1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
28 #define ENABLE_UNUSED_CODE 0 // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
30 #define ENABLE_RESERVED_CODE 0 // reserved for later use
32 #define CHUNK_ID_LEN 4 // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE -1 // do not write chunk size
36 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
39 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
62 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
185 -1, SAVE_CONF_ALWAYS,
186 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
217 &li.can_move_into_acid_bits, ~0 // default: everything can
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 // default: always deadly
226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
227 &li.em_explodes_by_fire, FALSE
231 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
232 &li.score[SC_TIME_BONUS], 1
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
237 &li.auto_exit_sokoban, FALSE
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
242 &li.auto_count_gems, FALSE
246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
247 &li.solved_by_one_player, FALSE
251 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
252 &li.time_score_base, 1
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
257 &li.rate_time_over_score, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
262 &li.bd_intermission, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
267 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
272 &li.bd_pal_timing, FALSE
276 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
277 &li.bd_cycle_delay_ms, 200
281 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
282 &li.bd_cycle_delay_c64, 0
286 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
287 &li.bd_hatching_delay_cycles, 21
291 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
292 &li.bd_hatching_delay_seconds, 2
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
297 &li.bd_line_shifting_borders, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
302 &li.bd_scan_first_and_last_row, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
307 &li.bd_short_explosions, TRUE
311 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
312 &li.bd_cave_random_seed_c64, 0
316 TYPE_INTEGER, CONF_VALUE_32_BIT(3),
317 &li.bd_color_b, GD_C64_COLOR(0)
321 TYPE_INTEGER, CONF_VALUE_32_BIT(4),
322 &li.bd_color_0, GD_C64_COLOR(0)
326 TYPE_INTEGER, CONF_VALUE_32_BIT(5),
327 &li.bd_color_1, GD_C64_COLOR(8)
331 TYPE_INTEGER, CONF_VALUE_32_BIT(6),
332 &li.bd_color_2, GD_C64_COLOR(11)
336 TYPE_INTEGER, CONF_VALUE_32_BIT(7),
337 &li.bd_color_3, GD_C64_COLOR(1)
341 TYPE_INTEGER, CONF_VALUE_32_BIT(8),
342 &li.bd_color_4, GD_C64_COLOR(5)
346 TYPE_INTEGER, CONF_VALUE_32_BIT(9),
347 &li.bd_color_5, GD_C64_COLOR(6)
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
359 // (these values are the same for each player)
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
363 &li.block_last_field, FALSE // default case for EM levels
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
368 &li.sp_block_last_field, TRUE // default case for SP levels
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
373 &li.instant_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
378 &li.can_pass_to_walkable, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
383 &li.block_snap_field, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
388 &li.continuous_snapping, TRUE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
393 &li.shifted_relocation, FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
398 &li.lazy_relocation, FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
403 &li.finish_dig_collect, TRUE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
408 &li.keep_walkable_ce, FALSE
411 // (these values are different for each player)
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[0], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[0], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[0], EL_PLAYER_1
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[0], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[0], EL_PLAYER_1
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[0], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[0], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[1], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[1], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[1], EL_PLAYER_2
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[1], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[1], EL_PLAYER_2
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[1], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[1], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
529 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
534 &li.initial_player_gravity[2], FALSE
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
539 &li.use_start_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
544 &li.start_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
549 &li.use_artwork_element[2], FALSE
553 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
554 &li.artwork_element[2], EL_PLAYER_3
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
559 &li.use_explosion_element[2], FALSE
563 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
564 &li.explosion_element[2], EL_PLAYER_3
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
569 &li.use_initial_inventory[2], FALSE
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
574 &li.initial_inventory_size[2], 1
578 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
579 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
585 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
586 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
591 &li.initial_player_gravity[3], FALSE
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
596 &li.use_start_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
601 &li.start_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
606 &li.use_artwork_element[3], FALSE
610 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
611 &li.artwork_element[3], EL_PLAYER_4
615 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
616 &li.use_explosion_element[3], FALSE
620 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
621 &li.explosion_element[3], EL_PLAYER_4
625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
626 &li.use_initial_inventory[3], FALSE
630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
631 &li.initial_inventory_size[3], 1
635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
636 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
640 // (these values are only valid for BD style levels)
641 // (some values for BD style amoeba following below)
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.bd_diagonal_movements, FALSE
649 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
650 &li.bd_topmost_player_active, TRUE
654 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
655 &li.bd_pushing_prob, 25
659 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
660 &li.bd_pushing_prob_with_sweet, 100
664 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
665 &li.bd_push_mega_rock_with_sweet, FALSE
669 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
670 &li.bd_snap_element, EL_EMPTY
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_sand_looks_like, EL_BDX_SAND_1
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BDX_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BDX_ROCK
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND_EXTRA], 20
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
698 &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BDX_DIAMOND
707 EL_BDX_FIREFLY_1, -1,
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_1_explodes_to, EL_BDX_EXPLODING_1
713 EL_BDX_FIREFLY_2, -1,
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BDX_EXPLODING_1
719 EL_BDX_BUTTERFLY_1, -1,
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_1_explodes_to, EL_BDX_DIAMOND_GROWING_1
725 EL_BDX_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BDX_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BDX_ROCK_GROWING_1
737 EL_BDX_DRAGONFLY, -1,
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BDX_EXPLODING_1
743 EL_BDX_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BDX_DIAMOND
749 EL_BDX_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BDX_WALL
755 EL_BDX_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BDX_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BDX_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BDX_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BDX_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BDX_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BDX_MAGIC_WALL, -1,
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_time, 999
792 EL_BDX_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_diamond_to, EL_BDX_ROCK_FALLING
797 EL_BDX_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_rock_to, EL_BDX_DIAMOND_FALLING
802 EL_BDX_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_mega_rock_to, EL_BDX_NITRO_PACK_FALLING
807 EL_BDX_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nut_to, EL_BDX_NUT_FALLING
812 EL_BDX_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_nitro_pack_to, EL_BDX_MEGA_ROCK_FALLING
817 EL_BDX_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
822 EL_BDX_MAGIC_WALL, -1,
823 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
824 &li.bd_magic_wall_flying_rock_to, EL_BDX_FLYING_DIAMOND_FLYING
829 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
830 &li.bd_clock_extra_time, 30
834 EL_BDX_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
836 &li.bd_voodoo_collects_diamonds, FALSE
839 EL_BDX_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
841 &li.bd_voodoo_hurt_kills_player, FALSE
844 EL_BDX_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
846 &li.bd_voodoo_dies_by_rock, FALSE
849 EL_BDX_VOODOO_DOLL, -1,
850 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
851 &li.bd_voodoo_vanish_by_explosion, TRUE
854 EL_BDX_VOODOO_DOLL, -1,
855 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
856 &li.bd_voodoo_penalty_time, 30
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.bd_slime_is_predictable, TRUE
866 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
867 &li.bd_slime_permeability_rate, 100
871 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
872 &li.bd_slime_permeability_bits_c64, 0
876 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
877 &li.bd_slime_random_seed_c64, -1
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
882 &li.bd_slime_eats_element_1, EL_BDX_DIAMOND
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
887 &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
892 &li.bd_slime_eats_element_2, EL_BDX_ROCK
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
897 &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
902 &li.bd_slime_eats_element_3, EL_BDX_NUT
906 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
907 &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
912 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
913 &li.bd_acid_eats_element, EL_BDX_SAND_1
917 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
918 &li.bd_acid_spread_rate, 3
922 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
923 &li.bd_acid_turns_to_element, EL_BDX_EXPLODING_3
928 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
929 &li.bd_biter_move_delay, 0
933 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
934 &li.bd_biter_eats_element, EL_BDX_DIAMOND
939 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
940 &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
944 EL_BDX_EXPANDABLE_WALL_ANY, -1,
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
946 &li.bd_change_expanding_wall, FALSE
949 EL_BDX_EXPANDABLE_WALL_ANY, -1,
950 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
951 &li.bd_expanding_wall_looks_like, EL_BDX_WALL
955 EL_BDX_REPLICATOR, -1,
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
957 &li.bd_replicators_active, TRUE
960 EL_BDX_REPLICATOR, -1,
961 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
962 &li.bd_replicator_create_delay, 4
966 EL_BDX_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
968 &li.bd_conveyor_belts_active, TRUE
971 EL_BDX_CONVEYOR_LEFT, -1,
972 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
973 &li.bd_conveyor_belts_changed, FALSE
978 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
979 &li.bd_water_cannot_flow_down, FALSE
984 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
985 &li.bd_nut_content, EL_BDX_NUT_BREAKING_1
989 EL_BDX_PNEUMATIC_HAMMER, -1,
990 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
991 &li.bd_hammer_walls_break_delay, 5
994 EL_BDX_PNEUMATIC_HAMMER, -1,
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
996 &li.bd_hammer_walls_reappear, FALSE
999 EL_BDX_PNEUMATIC_HAMMER, -1,
1000 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1001 &li.bd_hammer_walls_reappear_delay, 100
1005 EL_BDX_ROCKET_LAUNCHER, -1,
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1007 &li.bd_infinite_rockets, FALSE
1011 EL_BDX_SKELETON, -1,
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1013 &li.bd_num_skeletons_needed_for_pot, 5
1016 EL_BDX_SKELETON, -1,
1017 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1018 &li.bd_skeleton_worth_num_diamonds, 0
1022 EL_BDX_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1024 &li.bd_creatures_start_backwards, FALSE
1027 EL_BDX_CREATURE_SWITCH, -1,
1028 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1029 &li.bd_creatures_turn_on_hatching, FALSE
1032 EL_BDX_CREATURE_SWITCH, -1,
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 &li.bd_creatures_auto_turn_delay, 0
1038 EL_BDX_GRAVITY_SWITCH, -1,
1039 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1040 &li.bd_gravity_direction, GD_MV_DOWN
1043 EL_BDX_GRAVITY_SWITCH, -1,
1044 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1045 &li.bd_gravity_switch_active, FALSE
1048 EL_BDX_GRAVITY_SWITCH, -1,
1049 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1050 &li.bd_gravity_switch_delay, 10
1053 EL_BDX_GRAVITY_SWITCH, -1,
1054 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1055 &li.bd_gravity_affects_all, TRUE
1058 // (the following values are related to various game elements)
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 &li.score[SC_EMERALD], 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1069 &li.score[SC_DIAMOND], 10
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.score[SC_BUG], 10
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1081 &li.score[SC_SPACESHIP], 10
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1087 &li.score[SC_PACMAN], 10
1092 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1093 &li.score[SC_NUT], 10
1098 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1099 &li.score[SC_DYNAMITE], 10
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1105 &li.score[SC_KEY], 10
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 &li.score[SC_PEARL], 10
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 &li.score[SC_CRYSTAL], 10
1122 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1123 &li.amoeba_content, EL_DIAMOND
1127 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1128 &li.amoeba_speed, 10
1132 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1133 &li.grow_into_diggable, TRUE
1137 EL_BDX_AMOEBA_1, -1,
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1139 &li.bd_amoeba_1_threshold_too_big, 200
1142 EL_BDX_AMOEBA_1, -1,
1143 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1144 &li.bd_amoeba_1_slow_growth_time, 200
1147 EL_BDX_AMOEBA_1, -1,
1148 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1149 &li.bd_amoeba_1_content_too_big, EL_BDX_ROCK
1152 EL_BDX_AMOEBA_1, -1,
1153 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
1154 &li.bd_amoeba_1_content_enclosed, EL_BDX_DIAMOND
1157 EL_BDX_AMOEBA_1, -1,
1158 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1159 &li.bd_amoeba_1_slow_growth_rate, 3
1162 EL_BDX_AMOEBA_1, -1,
1163 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1164 &li.bd_amoeba_1_fast_growth_rate, 25
1167 EL_BDX_AMOEBA_1, -1,
1168 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1169 &li.bd_amoeba_wait_for_hatching, FALSE
1172 EL_BDX_AMOEBA_1, -1,
1173 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1174 &li.bd_amoeba_start_immediately, TRUE
1178 EL_BDX_AMOEBA_2, -1,
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1180 &li.bd_amoeba_2_threshold_too_big, 200
1183 EL_BDX_AMOEBA_2, -1,
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1185 &li.bd_amoeba_2_slow_growth_time, 200
1188 EL_BDX_AMOEBA_2, -1,
1189 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1190 &li.bd_amoeba_2_content_too_big, EL_BDX_ROCK
1193 EL_BDX_AMOEBA_2, -1,
1194 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
1195 &li.bd_amoeba_2_content_enclosed, EL_BDX_DIAMOND
1198 EL_BDX_AMOEBA_2, -1,
1199 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1200 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1203 EL_BDX_AMOEBA_2, -1,
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1205 &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1208 EL_BDX_AMOEBA_2, -1,
1209 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1210 &li.bd_amoeba_2_slow_growth_rate, 3
1213 EL_BDX_AMOEBA_2, -1,
1214 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1215 &li.bd_amoeba_2_fast_growth_rate, 25
1218 EL_BDX_AMOEBA_2, -1,
1219 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1220 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1225 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1226 &li.yamyam_content, EL_ROCK, NULL,
1227 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1231 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1232 &li.score[SC_YAMYAM], 10
1237 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1238 &li.score[SC_ROBOT], 10
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1255 &li.time_magic_wall, 10
1259 EL_GAME_OF_LIFE, -1,
1260 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1261 &li.game_of_life[0], 2
1264 EL_GAME_OF_LIFE, -1,
1265 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1266 &li.game_of_life[1], 3
1269 EL_GAME_OF_LIFE, -1,
1270 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1271 &li.game_of_life[2], 3
1274 EL_GAME_OF_LIFE, -1,
1275 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1276 &li.game_of_life[3], 3
1279 EL_GAME_OF_LIFE, -1,
1280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1281 &li.use_life_bugs, FALSE
1286 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1291 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1296 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1301 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1306 EL_TIMEGATE_SWITCH, -1,
1307 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1308 &li.time_timegate, 10
1312 EL_LIGHT_SWITCH_ACTIVE, -1,
1313 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1318 EL_SHIELD_NORMAL, -1,
1319 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1320 &li.shield_normal_time, 10
1323 EL_SHIELD_NORMAL, -1,
1324 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1325 &li.score[SC_SHIELD], 10
1329 EL_SHIELD_DEADLY, -1,
1330 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1331 &li.shield_deadly_time, 10
1334 EL_SHIELD_DEADLY, -1,
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1336 &li.score[SC_SHIELD], 10
1341 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1347 &li.extra_time_score, 10
1351 EL_TIME_ORB_FULL, -1,
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1353 &li.time_orb_time, 10
1356 EL_TIME_ORB_FULL, -1,
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1358 &li.use_time_orb_bug, FALSE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1364 &li.use_spring_bug, FALSE
1369 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1370 &li.android_move_time, 10
1374 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1375 &li.android_clone_time, 10
1378 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1379 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1380 &li.android_clone_element[0], EL_EMPTY, NULL,
1381 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1385 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1386 &li.android_clone_element[0], EL_EMPTY, NULL,
1387 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1392 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1393 &li.lenses_score, 10
1397 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1402 EL_EMC_MAGNIFIER, -1,
1403 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1404 &li.magnify_score, 10
1407 EL_EMC_MAGNIFIER, -1,
1408 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1409 &li.magnify_time, 10
1413 EL_EMC_MAGIC_BALL, -1,
1414 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1418 EL_EMC_MAGIC_BALL, -1,
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1420 &li.ball_random, FALSE
1423 EL_EMC_MAGIC_BALL, -1,
1424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1425 &li.ball_active_initial, FALSE
1428 EL_EMC_MAGIC_BALL, -1,
1429 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1430 &li.ball_content, EL_EMPTY, NULL,
1431 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1435 EL_SOKOBAN_FIELD_EMPTY, -1,
1436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1437 &li.sb_fields_needed, TRUE
1441 EL_SOKOBAN_OBJECT, -1,
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1443 &li.sb_objects_needed, TRUE
1448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1449 &li.mm_laser_red, FALSE
1453 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1454 &li.mm_laser_green, FALSE
1458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1459 &li.mm_laser_blue, TRUE
1464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1465 &li.df_laser_red, TRUE
1469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1470 &li.df_laser_green, TRUE
1474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1475 &li.df_laser_blue, FALSE
1479 EL_MM_FUSE_ACTIVE, -1,
1480 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1481 &li.mm_time_fuse, 25
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1486 &li.mm_time_bomb, 75
1490 EL_MM_GRAY_BALL, -1,
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1492 &li.mm_time_ball, 75
1495 EL_MM_GRAY_BALL, -1,
1496 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1497 &li.mm_ball_choice_mode, ANIM_RANDOM
1500 EL_MM_GRAY_BALL, -1,
1501 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1502 &li.mm_ball_content, EL_EMPTY, NULL,
1503 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1506 EL_MM_GRAY_BALL, -1,
1507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1508 &li.rotate_mm_ball_content, TRUE
1511 EL_MM_GRAY_BALL, -1,
1512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1513 &li.explode_mm_ball, FALSE
1517 EL_MM_STEEL_BLOCK, -1,
1518 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1519 &li.mm_time_block, 75
1522 EL_MM_LIGHTBALL, -1,
1523 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1524 &li.score[SC_ELEM_BONUS], 10
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1538 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1539 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1543 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1544 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1549 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1550 &xx_envelope.autowrap, FALSE
1554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1555 &xx_envelope.centered, FALSE
1560 TYPE_STRING, CONF_VALUE_BYTES(1),
1561 &xx_envelope.text, -1, NULL,
1562 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1563 &xx_default_string_empty[0]
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1577 TYPE_STRING, CONF_VALUE_BYTES(1),
1578 &xx_ei.description[0], -1,
1579 &yy_ei.description[0],
1580 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1581 &xx_default_description[0]
1586 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1587 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1590 #if ENABLE_RESERVED_CODE
1591 // (reserved for later use)
1594 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1595 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1602 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1603 &xx_ei.use_gfx_element, FALSE,
1604 &yy_ei.use_gfx_element
1608 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1609 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1610 &yy_ei.gfx_element_initial
1615 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1616 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1617 &yy_ei.access_direction
1622 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1623 &xx_ei.collect_score_initial, 10,
1624 &yy_ei.collect_score_initial
1628 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1629 &xx_ei.collect_count_initial, 1,
1630 &yy_ei.collect_count_initial
1635 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1636 &xx_ei.ce_value_fixed_initial, 0,
1637 &yy_ei.ce_value_fixed_initial
1641 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1642 &xx_ei.ce_value_random_initial, 0,
1643 &yy_ei.ce_value_random_initial
1647 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1648 &xx_ei.use_last_ce_value, FALSE,
1649 &yy_ei.use_last_ce_value
1654 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1655 &xx_ei.push_delay_fixed, 8,
1656 &yy_ei.push_delay_fixed
1660 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1661 &xx_ei.push_delay_random, 8,
1662 &yy_ei.push_delay_random
1666 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1667 &xx_ei.drop_delay_fixed, 0,
1668 &yy_ei.drop_delay_fixed
1672 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1673 &xx_ei.drop_delay_random, 0,
1674 &yy_ei.drop_delay_random
1678 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1679 &xx_ei.move_delay_fixed, 0,
1680 &yy_ei.move_delay_fixed
1684 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1685 &xx_ei.move_delay_random, 0,
1686 &yy_ei.move_delay_random
1690 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1691 &xx_ei.step_delay_fixed, 0,
1692 &yy_ei.step_delay_fixed
1696 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1697 &xx_ei.step_delay_random, 0,
1698 &yy_ei.step_delay_random
1703 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1704 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1709 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1710 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1711 &yy_ei.move_direction_initial
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1716 &xx_ei.move_stepsize, TILEX / 8,
1717 &yy_ei.move_stepsize
1722 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1723 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1724 &yy_ei.move_enter_element
1728 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1729 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1730 &yy_ei.move_leave_element
1734 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1735 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1736 &yy_ei.move_leave_type
1741 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1742 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1743 &yy_ei.slippery_type
1748 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1749 &xx_ei.explosion_type, EXPLODES_3X3,
1750 &yy_ei.explosion_type
1754 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1755 &xx_ei.explosion_delay, 16,
1756 &yy_ei.explosion_delay
1760 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1761 &xx_ei.ignition_delay, 8,
1762 &yy_ei.ignition_delay
1767 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1768 &xx_ei.content, EL_EMPTY_SPACE,
1770 &xx_num_contents, 1, 1
1773 // ---------- "num_change_pages" must be the last entry ---------------------
1776 -1, SAVE_CONF_ALWAYS,
1777 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1778 &xx_ei.num_change_pages, 1,
1779 &yy_ei.num_change_pages
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1792 // ---------- "current_change_page" must be the first entry -----------------
1795 -1, SAVE_CONF_ALWAYS,
1796 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1797 &xx_current_change_page, -1
1800 // ---------- (the remaining entries can be in any order) -------------------
1804 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1805 &xx_change.can_change, FALSE
1810 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1811 &xx_event_bits[0], 0
1815 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1816 &xx_event_bits[1], 0
1821 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1822 &xx_change.trigger_player, CH_PLAYER_ANY
1826 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1827 &xx_change.trigger_side, CH_SIDE_ANY
1831 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1832 &xx_change.trigger_page, CH_PAGE_ANY
1837 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1838 &xx_change.target_element, EL_EMPTY_SPACE
1843 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1844 &xx_change.delay_fixed, 0
1848 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1849 &xx_change.delay_random, 0
1853 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1854 &xx_change.delay_frames, FRAMES_PER_SECOND
1859 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1860 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1865 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1866 &xx_change.explode, FALSE
1870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1871 &xx_change.use_target_content, FALSE
1875 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1876 &xx_change.only_if_complete, FALSE
1880 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1881 &xx_change.use_random_replace, FALSE
1885 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1886 &xx_change.random_percentage, 100
1890 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1891 &xx_change.replace_when, CP_WHEN_EMPTY
1896 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1897 &xx_change.has_action, FALSE
1901 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1902 &xx_change.action_type, CA_NO_ACTION
1906 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1907 &xx_change.action_mode, CA_MODE_UNDEFINED
1911 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1912 &xx_change.action_arg, CA_ARG_UNDEFINED
1917 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1918 &xx_change.action_element, EL_EMPTY_SPACE
1923 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1924 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1925 &xx_num_contents, 1, 1
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1939 TYPE_STRING, CONF_VALUE_BYTES(1),
1940 &xx_ei.description[0], -1, NULL,
1941 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1942 &xx_default_description[0]
1947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1948 &xx_ei.use_gfx_element, FALSE
1952 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1953 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1958 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1959 &xx_group.choice_mode, ANIM_RANDOM
1964 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1965 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1966 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1981 &xx_ei.use_gfx_element, FALSE
1985 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1986 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1996 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
2000 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
2001 &li.block_snap_field, TRUE
2005 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2006 &li.continuous_snapping, TRUE
2010 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2011 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2015 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2016 &li.use_start_element[0], FALSE
2020 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2021 &li.start_element[0], EL_PLAYER_1
2025 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2026 &li.use_artwork_element[0], FALSE
2030 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2031 &li.artwork_element[0], EL_PLAYER_1
2035 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2036 &li.use_explosion_element[0], FALSE
2040 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2041 &li.explosion_element[0], EL_PLAYER_1
2056 filetype_id_list[] =
2058 { LEVEL_FILE_TYPE_RND, "RND" },
2059 { LEVEL_FILE_TYPE_BD, "BD" },
2060 { LEVEL_FILE_TYPE_EM, "EM" },
2061 { LEVEL_FILE_TYPE_SP, "SP" },
2062 { LEVEL_FILE_TYPE_DX, "DX" },
2063 { LEVEL_FILE_TYPE_SB, "SB" },
2064 { LEVEL_FILE_TYPE_DC, "DC" },
2065 { LEVEL_FILE_TYPE_MM, "MM" },
2066 { LEVEL_FILE_TYPE_MM, "DF" },
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2075 static boolean check_special_flags(char *flag)
2077 if (strEqual(options.special_flags, flag) ||
2078 strEqual(leveldir_current->special_flags, flag))
2084 static struct DateInfo getCurrentDate(void)
2086 time_t epoch_seconds = time(NULL);
2087 struct tm *now = localtime(&epoch_seconds);
2088 struct DateInfo date;
2090 date.year = now->tm_year + 1900;
2091 date.month = now->tm_mon + 1;
2092 date.day = now->tm_mday;
2094 date.src = DATE_SRC_CLOCK;
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2103 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104 change->has_event[i] = FALSE;
2107 static void resetEventBits(void)
2111 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112 xx_event_bits[i] = 0;
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2119 /* important: only change event flag if corresponding event bit is set
2120 (this is because all xx_event_bits[] values are loaded separately,
2121 and all xx_event_bits[] values are set back to zero before loading
2122 another value xx_event_bits[x] (each value representing 32 flags)) */
2124 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126 change->has_event[i] = TRUE;
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2133 /* in contrast to the above function setEventFlagsFromEventBits(), it
2134 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135 depending on the corresponding change->has_event[i] values here, as
2136 all xx_event_bits[] values are reset in resetEventBits() before */
2138 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139 if (change->has_event[i])
2140 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2145 static char description[MAX_ELEMENT_NAME_LEN + 1];
2146 char *default_description = (ei->custom_description != NULL ?
2147 ei->custom_description :
2148 ei->editor_description);
2151 // always start with reliable default values
2152 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153 description[i] = '\0';
2155 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2158 return &description[0];
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2163 char *default_description = getDefaultElementDescription(ei);
2166 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167 ei->description[i] = default_description[i];
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2174 for (i = 0; conf[i].data_type != -1; i++)
2176 int default_value = conf[i].default_value;
2177 int data_type = conf[i].data_type;
2178 int conf_type = conf[i].conf_type;
2179 int byte_mask = conf_type & CONF_MASK_BYTES;
2181 if (byte_mask == CONF_MASK_MULTI_BYTES)
2183 int default_num_entities = conf[i].default_num_entities;
2184 int max_num_entities = conf[i].max_num_entities;
2186 *(int *)(conf[i].num_entities) = default_num_entities;
2188 if (data_type == TYPE_STRING)
2190 char *default_string = conf[i].default_string;
2191 char *string = (char *)(conf[i].value);
2193 strncpy(string, default_string, max_num_entities);
2195 else if (data_type == TYPE_ELEMENT_LIST)
2197 int *element_array = (int *)(conf[i].value);
2200 for (j = 0; j < max_num_entities; j++)
2201 element_array[j] = default_value;
2203 else if (data_type == TYPE_CONTENT_LIST)
2205 struct Content *content = (struct Content *)(conf[i].value);
2208 for (c = 0; c < max_num_entities; c++)
2209 for (y = 0; y < 3; y++)
2210 for (x = 0; x < 3; x++)
2211 content[c].e[x][y] = default_value;
2214 else // constant size configuration data (1, 2 or 4 bytes)
2216 if (data_type == TYPE_BOOLEAN)
2217 *(boolean *)(conf[i].value) = default_value;
2219 *(int *) (conf[i].value) = default_value;
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2228 for (i = 0; conf[i].data_type != -1; i++)
2230 int data_type = conf[i].data_type;
2231 int conf_type = conf[i].conf_type;
2232 int byte_mask = conf_type & CONF_MASK_BYTES;
2234 if (byte_mask == CONF_MASK_MULTI_BYTES)
2236 int max_num_entities = conf[i].max_num_entities;
2238 if (data_type == TYPE_STRING)
2240 char *string = (char *)(conf[i].value);
2241 char *string_copy = (char *)(conf[i].value_copy);
2243 strncpy(string_copy, string, max_num_entities);
2245 else if (data_type == TYPE_ELEMENT_LIST)
2247 int *element_array = (int *)(conf[i].value);
2248 int *element_array_copy = (int *)(conf[i].value_copy);
2251 for (j = 0; j < max_num_entities; j++)
2252 element_array_copy[j] = element_array[j];
2254 else if (data_type == TYPE_CONTENT_LIST)
2256 struct Content *content = (struct Content *)(conf[i].value);
2257 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2260 for (c = 0; c < max_num_entities; c++)
2261 for (y = 0; y < 3; y++)
2262 for (x = 0; x < 3; x++)
2263 content_copy[c].e[x][y] = content[c].e[x][y];
2266 else // constant size configuration data (1, 2 or 4 bytes)
2268 if (data_type == TYPE_BOOLEAN)
2269 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2271 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2280 xx_ei = *ei_from; // copy element data into temporary buffer
2281 yy_ei = *ei_to; // copy element data into temporary buffer
2283 copyConfigFromConfigList(chunk_config_CUSX_base);
2288 // ---------- reinitialize and copy change pages ----------
2290 ei_to->num_change_pages = ei_from->num_change_pages;
2291 ei_to->current_change_page = ei_from->current_change_page;
2293 setElementChangePages(ei_to, ei_to->num_change_pages);
2295 for (i = 0; i < ei_to->num_change_pages; i++)
2296 ei_to->change_page[i] = ei_from->change_page[i];
2298 // ---------- copy group element info ----------
2299 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2300 *ei_to->group = *ei_from->group;
2302 // mark this custom element as modified
2303 ei_to->modified_settings = TRUE;
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2308 int change_page_size = sizeof(struct ElementChangeInfo);
2310 ei->num_change_pages = MAX(1, change_pages);
2313 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2315 if (ei->current_change_page >= ei->num_change_pages)
2316 ei->current_change_page = ei->num_change_pages - 1;
2318 ei->change = &ei->change_page[ei->current_change_page];
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2323 xx_change = *change; // copy change data into temporary buffer
2325 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2327 *change = xx_change;
2329 resetEventFlags(change);
2331 change->direct_action = 0;
2332 change->other_action = 0;
2334 change->pre_change_function = NULL;
2335 change->change_function = NULL;
2336 change->post_change_function = NULL;
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2341 boolean add_border = FALSE;
2344 int x2 = STD_LEV_FIELDX - 1;
2345 int y2 = STD_LEV_FIELDY - 1;
2348 li = *level; // copy level data into temporary buffer
2349 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350 *level = li; // copy temporary buffer back to level data
2352 setLevelInfoToDefaults_BD();
2353 setLevelInfoToDefaults_EM();
2354 setLevelInfoToDefaults_SP();
2355 setLevelInfoToDefaults_MM();
2357 level->native_bd_level = &native_bd_level;
2358 level->native_em_level = &native_em_level;
2359 level->native_sp_level = &native_sp_level;
2360 level->native_mm_level = &native_mm_level;
2362 level->file_version = FILE_VERSION_ACTUAL;
2363 level->game_version = GAME_VERSION_ACTUAL;
2365 level->creation_date = getCurrentDate();
2367 level->encoding_16bit_field = TRUE;
2368 level->encoding_16bit_yamyam = TRUE;
2369 level->encoding_16bit_amoeba = TRUE;
2371 // clear level name and level author string buffers
2372 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373 level->name[i] = '\0';
2374 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375 level->author[i] = '\0';
2377 // set level name and level author to default values
2378 strcpy(level->name, NAMELESS_LEVEL_NAME);
2379 strcpy(level->author, ANONYMOUS_NAME);
2381 // set default game engine type
2382 level->game_engine_type = setup.default_game_engine_type;
2384 // some game engines should have a default playfield with border elements
2385 if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386 level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387 level->game_engine_type == GAME_ENGINE_TYPE_SP)
2396 // set level playfield to playable default level with player and exit
2397 for (x = 0; x < MAX_LEV_FIELDX; x++)
2399 for (y = 0; y < MAX_LEV_FIELDY; y++)
2401 if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402 y == 0 || y == STD_LEV_FIELDY - 1))
2403 level->field[x][y] = getEngineElement(EL_STEELWALL);
2405 level->field[x][y] = getEngineElement(EL_SAND);
2409 level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410 level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2412 BorderElement = getEngineElement(EL_STEELWALL);
2414 // detect custom elements when loading them
2415 level->file_has_custom_elements = FALSE;
2417 // set random colors for BD style levels according to preferred color type
2418 SetRandomLevelColors_BD(setup.bd_default_color_type);
2420 // set default color type and colors for BD style level colors
2421 SetDefaultLevelColorType_BD();
2422 SetDefaultLevelColors_BD();
2424 // set all bug compatibility flags to "false" => do not emulate this bug
2425 level->use_action_after_change_bug = FALSE;
2427 if (leveldir_current)
2429 // try to determine better author name than 'anonymous'
2430 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2432 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2437 switch (LEVELCLASS(leveldir_current))
2439 case LEVELCLASS_TUTORIAL:
2440 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2443 case LEVELCLASS_CONTRIB:
2444 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2448 case LEVELCLASS_PRIVATE:
2449 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2454 // keep default value
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2463 static boolean clipboard_elements_initialized = FALSE;
2466 InitElementPropertiesStatic();
2468 li = *level; // copy level data into temporary buffer
2469 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470 *level = li; // copy temporary buffer back to level data
2472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2475 struct ElementInfo *ei = &element_info[element];
2477 if (element == EL_MM_GRAY_BALL)
2479 struct LevelInfo_MM *level_mm = level->native_mm_level;
2482 for (j = 0; j < level->num_mm_ball_contents; j++)
2483 level->mm_ball_content[j] =
2484 map_element_MM_to_RND(level_mm->ball_content[j]);
2487 // never initialize clipboard elements after the very first time
2488 // (to be able to use clipboard elements between several levels)
2489 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2492 if (IS_ENVELOPE(element))
2494 int envelope_nr = element - EL_ENVELOPE_1;
2496 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2498 level->envelope[envelope_nr] = xx_envelope;
2501 if (IS_CUSTOM_ELEMENT(element) ||
2502 IS_GROUP_ELEMENT(element) ||
2503 IS_INTERNAL_ELEMENT(element))
2505 xx_ei = *ei; // copy element data into temporary buffer
2507 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2512 setElementChangePages(ei, 1);
2513 setElementChangeInfoToDefaults(ei->change);
2515 if (IS_CUSTOM_ELEMENT(element) ||
2516 IS_GROUP_ELEMENT(element))
2518 setElementDescriptionToDefault(ei);
2520 ei->modified_settings = FALSE;
2523 if (IS_CUSTOM_ELEMENT(element) ||
2524 IS_INTERNAL_ELEMENT(element))
2526 // internal values used in level editor
2528 ei->access_type = 0;
2529 ei->access_layer = 0;
2530 ei->access_protected = 0;
2531 ei->walk_to_action = 0;
2532 ei->smash_targets = 0;
2535 ei->can_explode_by_fire = FALSE;
2536 ei->can_explode_smashed = FALSE;
2537 ei->can_explode_impact = FALSE;
2539 ei->current_change_page = 0;
2542 if (IS_GROUP_ELEMENT(element) ||
2543 IS_INTERNAL_ELEMENT(element))
2545 struct ElementGroupInfo *group;
2547 // initialize memory for list of elements in group
2548 if (ei->group == NULL)
2549 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2553 xx_group = *group; // copy group data into temporary buffer
2555 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2560 if (IS_EMPTY_ELEMENT(element) ||
2561 IS_INTERNAL_ELEMENT(element))
2563 xx_ei = *ei; // copy element data into temporary buffer
2565 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2571 clipboard_elements_initialized = TRUE;
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575 boolean level_info_only,
2576 boolean reset_file_status)
2578 setLevelInfoToDefaults_Level(level);
2580 if (!level_info_only)
2581 setLevelInfoToDefaults_Elements(level);
2583 if (reset_file_status)
2585 level->no_valid_file = FALSE;
2586 level->no_level_file = FALSE;
2589 level->changed = FALSE;
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2594 level_file_info->nr = 0;
2595 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596 level_file_info->packed = FALSE;
2598 setString(&level_file_info->basename, NULL);
2599 setString(&level_file_info->filename, NULL);
2602 int getMappedElement_SB(int, boolean);
2604 static void ActivateLevelTemplate(void)
2608 if (check_special_flags("load_xsb_to_ces"))
2610 // fill smaller playfields with padding "beyond border wall" elements
2611 if (level.fieldx < level_template.fieldx ||
2612 level.fieldy < level_template.fieldy)
2614 short field[level.fieldx][level.fieldy];
2615 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2620 // copy old playfield (which is smaller than the visible area)
2621 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622 field[x][y] = level.field[x][y];
2624 // fill new, larger playfield with "beyond border wall" elements
2625 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626 level.field[x][y] = getMappedElement_SB('_', TRUE);
2628 // copy the old playfield to the middle of the new playfield
2629 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2632 level.fieldx = new_fieldx;
2633 level.fieldy = new_fieldy;
2637 // Currently there is no special action needed to activate the template
2638 // data, because 'element_info' property settings overwrite the original
2639 // level data, while all other variables do not change.
2641 // Exception: 'from_level_template' elements in the original level playfield
2642 // are overwritten with the corresponding elements at the same position in
2643 // playfield from the level template.
2645 for (x = 0; x < level.fieldx; x++)
2646 for (y = 0; y < level.fieldy; y++)
2647 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648 level.field[x][y] = level_template.field[x][y];
2650 if (check_special_flags("load_xsb_to_ces"))
2652 struct LevelInfo level_backup = level;
2654 // overwrite all individual level settings from template level settings
2655 level = level_template;
2657 // restore level file info
2658 level.file_info = level_backup.file_info;
2660 // restore playfield size
2661 level.fieldx = level_backup.fieldx;
2662 level.fieldy = level_backup.fieldy;
2664 // restore playfield content
2665 for (x = 0; x < level.fieldx; x++)
2666 for (y = 0; y < level.fieldy; y++)
2667 level.field[x][y] = level_backup.field[x][y];
2669 // restore name and author from individual level
2670 strcpy(level.name, level_backup.name);
2671 strcpy(level.author, level_backup.author);
2673 // restore flag "use_custom_template"
2674 level.use_custom_template = level_backup.use_custom_template;
2678 static boolean checkForPackageFromBasename_BD(char *basename)
2680 // check for native BD level file extensions
2681 if (!strSuffixLower(basename, ".bd") &&
2682 !strSuffixLower(basename, ".bdr") &&
2683 !strSuffixLower(basename, ".brc") &&
2684 !strSuffixLower(basename, ".gds"))
2687 // check for standard single-level BD files (like "001.bd")
2688 if (strSuffixLower(basename, ".bd") &&
2689 strlen(basename) == 6 &&
2690 basename[0] >= '0' && basename[0] <= '9' &&
2691 basename[1] >= '0' && basename[1] <= '9' &&
2692 basename[2] >= '0' && basename[2] <= '9')
2695 // this is a level package in native BD file format
2699 static char *getLevelFilenameFromBasename(char *basename)
2701 static char *filename = NULL;
2703 checked_free(filename);
2705 filename = getPath2(getCurrentLevelDir(), basename);
2710 static int getFileTypeFromBasename(char *basename)
2712 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2714 static char *filename = NULL;
2715 struct stat file_status;
2717 // ---------- try to determine file type from filename ----------
2719 // check for typical filename of a Supaplex level package file
2720 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2721 return LEVEL_FILE_TYPE_SP;
2723 // check for typical filename of a Diamond Caves II level package file
2724 if (strSuffixLower(basename, ".dc") ||
2725 strSuffixLower(basename, ".dc2"))
2726 return LEVEL_FILE_TYPE_DC;
2728 // check for typical filename of a Sokoban level package file
2729 if (strSuffixLower(basename, ".xsb") &&
2730 strchr(basename, '%') == NULL)
2731 return LEVEL_FILE_TYPE_SB;
2733 // check for typical filename of a Boulder Dash (GDash) level package file
2734 if (checkForPackageFromBasename_BD(basename))
2735 return LEVEL_FILE_TYPE_BD;
2737 // ---------- try to determine file type from filesize ----------
2739 checked_free(filename);
2740 filename = getPath2(getCurrentLevelDir(), basename);
2742 if (stat(filename, &file_status) == 0)
2744 // check for typical filesize of a Supaplex level package file
2745 if (file_status.st_size == 170496)
2746 return LEVEL_FILE_TYPE_SP;
2749 return LEVEL_FILE_TYPE_UNKNOWN;
2752 static int getFileTypeFromMagicBytes(char *filename, int type)
2756 if ((file = openFile(filename, MODE_READ)))
2758 char chunk_name[CHUNK_ID_LEN + 1];
2760 getFileChunkBE(file, chunk_name, NULL);
2762 if (strEqual(chunk_name, "MMII") ||
2763 strEqual(chunk_name, "MIRR"))
2764 type = LEVEL_FILE_TYPE_MM;
2772 static boolean checkForPackageFromBasename(char *basename)
2774 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2775 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2777 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2780 static char *getSingleLevelBasenameExt(int nr, char *extension)
2782 static char basename[MAX_FILENAME_LEN];
2785 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2787 sprintf(basename, "%03d.%s", nr, extension);
2792 static char *getSingleLevelBasename(int nr)
2794 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2797 static char *getPackedLevelBasename(int type)
2799 static char basename[MAX_FILENAME_LEN];
2800 char *directory = getCurrentLevelDir();
2802 DirectoryEntry *dir_entry;
2804 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2806 if ((dir = openDirectory(directory)) == NULL)
2808 Warn("cannot read current level directory '%s'", directory);
2813 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2815 char *entry_basename = dir_entry->basename;
2816 int entry_type = getFileTypeFromBasename(entry_basename);
2818 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2820 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2823 strcpy(basename, entry_basename);
2830 closeDirectory(dir);
2835 static char *getSingleLevelFilename(int nr)
2837 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2840 #if ENABLE_UNUSED_CODE
2841 static char *getPackedLevelFilename(int type)
2843 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2847 char *getDefaultLevelFilename(int nr)
2849 return getSingleLevelFilename(nr);
2852 #if ENABLE_UNUSED_CODE
2853 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2857 lfi->packed = FALSE;
2859 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2860 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2864 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2865 int type, char *format, ...)
2867 static char basename[MAX_FILENAME_LEN];
2870 va_start(ap, format);
2871 vsprintf(basename, format, ap);
2875 lfi->packed = FALSE;
2877 setString(&lfi->basename, basename);
2878 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2881 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2887 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2888 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2891 static int getFiletypeFromID(char *filetype_id)
2893 char *filetype_id_lower;
2894 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2897 if (filetype_id == NULL)
2898 return LEVEL_FILE_TYPE_UNKNOWN;
2900 filetype_id_lower = getStringToLower(filetype_id);
2902 for (i = 0; filetype_id_list[i].id != NULL; i++)
2904 char *id_lower = getStringToLower(filetype_id_list[i].id);
2906 if (strEqual(filetype_id_lower, id_lower))
2907 filetype = filetype_id_list[i].filetype;
2911 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2915 free(filetype_id_lower);
2920 char *getLocalLevelTemplateFilename(void)
2922 return getDefaultLevelFilename(-1);
2925 char *getGlobalLevelTemplateFilename(void)
2927 // global variable "leveldir_current" must be modified in the loop below
2928 LevelDirTree *leveldir_current_last = leveldir_current;
2929 char *filename = NULL;
2931 // check for template level in path from current to topmost tree node
2933 while (leveldir_current != NULL)
2935 filename = getDefaultLevelFilename(-1);
2937 if (fileExists(filename))
2940 leveldir_current = leveldir_current->node_parent;
2943 // restore global variable "leveldir_current" modified in above loop
2944 leveldir_current = leveldir_current_last;
2949 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2953 // special case: level number is negative => check for level template file
2956 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2957 getSingleLevelBasename(-1));
2959 // replace local level template filename with global template filename
2960 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2962 // no fallback if template file not existing
2966 // special case: check for file name/pattern specified in "levelinfo.conf"
2967 if (leveldir_current->level_filename != NULL)
2969 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2971 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2972 leveldir_current->level_filename, nr);
2974 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2976 if (fileExists(lfi->filename))
2979 else if (leveldir_current->level_filetype != NULL)
2981 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2983 // check for specified native level file with standard file name
2984 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2985 "%03d.%s", nr, LEVELFILE_EXTENSION);
2986 if (fileExists(lfi->filename))
2990 // check for native Rocks'n'Diamonds level file
2991 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2992 "%03d.%s", nr, LEVELFILE_EXTENSION);
2993 if (fileExists(lfi->filename))
2996 // check for native Boulder Dash level file
2997 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2998 if (fileExists(lfi->filename))
3001 // check for Emerald Mine level file (V1)
3002 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
3003 'a' + (nr / 10) % 26, '0' + nr % 10);
3004 if (fileExists(lfi->filename))
3006 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3007 'A' + (nr / 10) % 26, '0' + nr % 10);
3008 if (fileExists(lfi->filename))
3011 // check for Emerald Mine level file (V2 to V5)
3012 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3013 if (fileExists(lfi->filename))
3016 // check for Emerald Mine level file (V6 / single mode)
3017 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3018 if (fileExists(lfi->filename))
3020 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3021 if (fileExists(lfi->filename))
3024 // check for Emerald Mine level file (V6 / teamwork mode)
3025 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3026 if (fileExists(lfi->filename))
3028 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3029 if (fileExists(lfi->filename))
3032 // check for various packed level file formats
3033 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3034 if (fileExists(lfi->filename))
3037 // no known level file found -- use default values (and fail later)
3038 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3039 "%03d.%s", nr, LEVELFILE_EXTENSION);
3042 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3044 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3045 lfi->type = getFileTypeFromBasename(lfi->basename);
3047 if (lfi->type == LEVEL_FILE_TYPE_RND)
3048 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3051 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3053 // always start with reliable default values
3054 setFileInfoToDefaults(level_file_info);
3056 level_file_info->nr = nr; // set requested level number
3058 determineLevelFileInfo_Filename(level_file_info);
3059 determineLevelFileInfo_Filetype(level_file_info);
3062 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3063 struct LevelFileInfo *lfi_to)
3065 lfi_to->nr = lfi_from->nr;
3066 lfi_to->type = lfi_from->type;
3067 lfi_to->packed = lfi_from->packed;
3069 setString(&lfi_to->basename, lfi_from->basename);
3070 setString(&lfi_to->filename, lfi_from->filename);
3073 // ----------------------------------------------------------------------------
3074 // functions for loading R'n'D level
3075 // ----------------------------------------------------------------------------
3077 int getMappedElement(int element)
3079 // remap some (historic, now obsolete) elements
3083 case EL_PLAYER_OBSOLETE:
3084 element = EL_PLAYER_1;
3087 case EL_KEY_OBSOLETE:
3091 case EL_EM_KEY_1_FILE_OBSOLETE:
3092 element = EL_EM_KEY_1;
3095 case EL_EM_KEY_2_FILE_OBSOLETE:
3096 element = EL_EM_KEY_2;
3099 case EL_EM_KEY_3_FILE_OBSOLETE:
3100 element = EL_EM_KEY_3;
3103 case EL_EM_KEY_4_FILE_OBSOLETE:
3104 element = EL_EM_KEY_4;
3107 case EL_ENVELOPE_OBSOLETE:
3108 element = EL_ENVELOPE_1;
3116 if (element >= NUM_FILE_ELEMENTS)
3118 Warn("invalid level element %d", element);
3120 element = EL_UNKNOWN;
3128 static int getMappedElementByVersion(int element, int game_version)
3130 // remap some elements due to certain game version
3132 if (game_version <= VERSION_IDENT(2,2,0,0))
3134 // map game font elements
3135 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3136 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3137 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3138 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3141 if (game_version < VERSION_IDENT(3,0,0,0))
3143 // map Supaplex gravity tube elements
3144 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3145 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3146 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3147 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3154 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3156 level->file_version = getFileVersion(file);
3157 level->game_version = getFileVersion(file);
3162 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3164 level->creation_date.year = getFile16BitBE(file);
3165 level->creation_date.month = getFile8Bit(file);
3166 level->creation_date.day = getFile8Bit(file);
3168 level->creation_date.src = DATE_SRC_LEVELFILE;
3173 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3175 int initial_player_stepsize;
3176 int initial_player_gravity;
3179 level->fieldx = getFile8Bit(file);
3180 level->fieldy = getFile8Bit(file);
3182 level->time = getFile16BitBE(file);
3183 level->gems_needed = getFile16BitBE(file);
3185 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3186 level->name[i] = getFile8Bit(file);
3187 level->name[MAX_LEVEL_NAME_LEN] = 0;
3189 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3190 level->score[i] = getFile8Bit(file);
3192 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3193 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3194 for (y = 0; y < 3; y++)
3195 for (x = 0; x < 3; x++)
3196 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3198 level->amoeba_speed = getFile8Bit(file);
3199 level->time_magic_wall = getFile8Bit(file);
3200 level->time_wheel = getFile8Bit(file);
3201 level->amoeba_content = getMappedElement(getFile8Bit(file));
3203 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3206 for (i = 0; i < MAX_PLAYERS; i++)
3207 level->initial_player_stepsize[i] = initial_player_stepsize;
3209 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3211 for (i = 0; i < MAX_PLAYERS; i++)
3212 level->initial_player_gravity[i] = initial_player_gravity;
3214 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3217 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3219 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3220 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221 level->can_move_into_acid_bits = getFile32BitBE(file);
3222 level->dont_collide_with_bits = getFile8Bit(file);
3224 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3227 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3228 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3229 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3231 level->game_engine_type = getFile8Bit(file);
3233 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3238 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3242 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3243 level->name[i] = getFile8Bit(file);
3244 level->name[MAX_LEVEL_NAME_LEN] = 0;
3249 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3253 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3254 level->author[i] = getFile8Bit(file);
3255 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3260 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3263 int chunk_size_expected = level->fieldx * level->fieldy;
3265 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3266 stored with 16-bit encoding (and should be twice as big then).
3267 Even worse, playfield data was stored 16-bit when only yamyam content
3268 contained 16-bit elements and vice versa. */
3270 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3271 chunk_size_expected *= 2;
3273 if (chunk_size_expected != chunk_size)
3275 ReadUnusedBytesFromFile(file, chunk_size);
3276 return chunk_size_expected;
3279 for (y = 0; y < level->fieldy; y++)
3280 for (x = 0; x < level->fieldx; x++)
3281 level->field[x][y] =
3282 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3287 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3290 int header_size = 4;
3291 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3292 int chunk_size_expected = header_size + content_size;
3294 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3295 stored with 16-bit encoding (and should be twice as big then).
3296 Even worse, playfield data was stored 16-bit when only yamyam content
3297 contained 16-bit elements and vice versa. */
3299 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3300 chunk_size_expected += content_size;
3302 if (chunk_size_expected != chunk_size)
3304 ReadUnusedBytesFromFile(file, chunk_size);
3305 return chunk_size_expected;
3309 level->num_yamyam_contents = getFile8Bit(file);
3313 // correct invalid number of content fields -- should never happen
3314 if (level->num_yamyam_contents < 1 ||
3315 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3316 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3318 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3319 for (y = 0; y < 3; y++)
3320 for (x = 0; x < 3; x++)
3321 level->yamyam_content[i].e[x][y] =
3322 getMappedElement(level->encoding_16bit_field ?
3323 getFile16BitBE(file) : getFile8Bit(file));
3327 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3332 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3334 element = getMappedElement(getFile16BitBE(file));
3335 num_contents = getFile8Bit(file);
3337 getFile8Bit(file); // content x size (unused)
3338 getFile8Bit(file); // content y size (unused)
3340 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3342 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3343 for (y = 0; y < 3; y++)
3344 for (x = 0; x < 3; x++)
3345 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3347 // correct invalid number of content fields -- should never happen
3348 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3349 num_contents = STD_ELEMENT_CONTENTS;
3351 if (element == EL_YAMYAM)
3353 level->num_yamyam_contents = num_contents;
3355 for (i = 0; i < num_contents; i++)
3356 for (y = 0; y < 3; y++)
3357 for (x = 0; x < 3; x++)
3358 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3360 else if (element == EL_BD_AMOEBA)
3362 level->amoeba_content = content_array[0][0][0];
3366 Warn("cannot load content for element '%d'", element);
3372 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3378 int chunk_size_expected;
3380 element = getMappedElement(getFile16BitBE(file));
3381 if (!IS_ENVELOPE(element))
3382 element = EL_ENVELOPE_1;
3384 envelope_nr = element - EL_ENVELOPE_1;
3386 envelope_len = getFile16BitBE(file);
3388 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3389 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3391 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3393 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3394 if (chunk_size_expected != chunk_size)
3396 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3397 return chunk_size_expected;
3400 for (i = 0; i < envelope_len; i++)
3401 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3406 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3408 int num_changed_custom_elements = getFile16BitBE(file);
3409 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3412 if (chunk_size_expected != chunk_size)
3414 ReadUnusedBytesFromFile(file, chunk_size - 2);
3415 return chunk_size_expected;
3418 for (i = 0; i < num_changed_custom_elements; i++)
3420 int element = getMappedElement(getFile16BitBE(file));
3421 int properties = getFile32BitBE(file);
3423 if (IS_CUSTOM_ELEMENT(element))
3424 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3426 Warn("invalid custom element number %d", element);
3428 // older game versions that wrote level files with CUS1 chunks used
3429 // different default push delay values (not yet stored in level file)
3430 element_info[element].push_delay_fixed = 2;
3431 element_info[element].push_delay_random = 8;
3434 level->file_has_custom_elements = TRUE;
3439 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3441 int num_changed_custom_elements = getFile16BitBE(file);
3442 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3445 if (chunk_size_expected != chunk_size)
3447 ReadUnusedBytesFromFile(file, chunk_size - 2);
3448 return chunk_size_expected;
3451 for (i = 0; i < num_changed_custom_elements; i++)
3453 int element = getMappedElement(getFile16BitBE(file));
3454 int custom_target_element = getMappedElement(getFile16BitBE(file));
3456 if (IS_CUSTOM_ELEMENT(element))
3457 element_info[element].change->target_element = custom_target_element;
3459 Warn("invalid custom element number %d", element);
3462 level->file_has_custom_elements = TRUE;
3467 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3469 int num_changed_custom_elements = getFile16BitBE(file);
3470 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3473 if (chunk_size_expected != chunk_size)
3475 ReadUnusedBytesFromFile(file, chunk_size - 2);
3476 return chunk_size_expected;
3479 for (i = 0; i < num_changed_custom_elements; i++)
3481 int element = getMappedElement(getFile16BitBE(file));
3482 struct ElementInfo *ei = &element_info[element];
3483 unsigned int event_bits;
3485 if (!IS_CUSTOM_ELEMENT(element))
3487 Warn("invalid custom element number %d", element);
3489 element = EL_INTERNAL_DUMMY;
3492 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3493 ei->description[j] = getFile8Bit(file);
3494 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3496 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3498 // some free bytes for future properties and padding
3499 ReadUnusedBytesFromFile(file, 7);
3501 ei->use_gfx_element = getFile8Bit(file);
3502 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3504 ei->collect_score_initial = getFile8Bit(file);
3505 ei->collect_count_initial = getFile8Bit(file);
3507 ei->push_delay_fixed = getFile16BitBE(file);
3508 ei->push_delay_random = getFile16BitBE(file);
3509 ei->move_delay_fixed = getFile16BitBE(file);
3510 ei->move_delay_random = getFile16BitBE(file);
3512 ei->move_pattern = getFile16BitBE(file);
3513 ei->move_direction_initial = getFile8Bit(file);
3514 ei->move_stepsize = getFile8Bit(file);
3516 for (y = 0; y < 3; y++)
3517 for (x = 0; x < 3; x++)
3518 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3520 // bits 0 - 31 of "has_event[]"
3521 event_bits = getFile32BitBE(file);
3522 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3523 if (event_bits & (1u << j))
3524 ei->change->has_event[j] = TRUE;
3526 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3528 ei->change->delay_fixed = getFile16BitBE(file);
3529 ei->change->delay_random = getFile16BitBE(file);
3530 ei->change->delay_frames = getFile16BitBE(file);
3532 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3534 ei->change->explode = getFile8Bit(file);
3535 ei->change->use_target_content = getFile8Bit(file);
3536 ei->change->only_if_complete = getFile8Bit(file);
3537 ei->change->use_random_replace = getFile8Bit(file);
3539 ei->change->random_percentage = getFile8Bit(file);
3540 ei->change->replace_when = getFile8Bit(file);
3542 for (y = 0; y < 3; y++)
3543 for (x = 0; x < 3; x++)
3544 ei->change->target_content.e[x][y] =
3545 getMappedElement(getFile16BitBE(file));
3547 ei->slippery_type = getFile8Bit(file);
3549 // some free bytes for future properties and padding
3550 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3552 // mark that this custom element has been modified
3553 ei->modified_settings = TRUE;
3556 level->file_has_custom_elements = TRUE;
3561 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3563 struct ElementInfo *ei;
3564 int chunk_size_expected;
3568 // ---------- custom element base property values (96 bytes) ----------------
3570 element = getMappedElement(getFile16BitBE(file));
3572 if (!IS_CUSTOM_ELEMENT(element))
3574 Warn("invalid custom element number %d", element);
3576 ReadUnusedBytesFromFile(file, chunk_size - 2);
3581 ei = &element_info[element];
3583 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3584 ei->description[i] = getFile8Bit(file);
3585 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3587 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3589 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3591 ei->num_change_pages = getFile8Bit(file);
3593 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3594 if (chunk_size_expected != chunk_size)
3596 ReadUnusedBytesFromFile(file, chunk_size - 43);
3597 return chunk_size_expected;
3600 ei->ce_value_fixed_initial = getFile16BitBE(file);
3601 ei->ce_value_random_initial = getFile16BitBE(file);
3602 ei->use_last_ce_value = getFile8Bit(file);
3604 ei->use_gfx_element = getFile8Bit(file);
3605 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3607 ei->collect_score_initial = getFile8Bit(file);
3608 ei->collect_count_initial = getFile8Bit(file);
3610 ei->drop_delay_fixed = getFile8Bit(file);
3611 ei->push_delay_fixed = getFile8Bit(file);
3612 ei->drop_delay_random = getFile8Bit(file);
3613 ei->push_delay_random = getFile8Bit(file);
3614 ei->move_delay_fixed = getFile16BitBE(file);
3615 ei->move_delay_random = getFile16BitBE(file);
3617 // bits 0 - 15 of "move_pattern" ...
3618 ei->move_pattern = getFile16BitBE(file);
3619 ei->move_direction_initial = getFile8Bit(file);
3620 ei->move_stepsize = getFile8Bit(file);
3622 ei->slippery_type = getFile8Bit(file);
3624 for (y = 0; y < 3; y++)
3625 for (x = 0; x < 3; x++)
3626 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3628 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3629 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3630 ei->move_leave_type = getFile8Bit(file);
3632 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3633 ei->move_pattern |= (getFile16BitBE(file) << 16);
3635 ei->access_direction = getFile8Bit(file);
3637 ei->explosion_delay = getFile8Bit(file);
3638 ei->ignition_delay = getFile8Bit(file);
3639 ei->explosion_type = getFile8Bit(file);
3641 // some free bytes for future custom property values and padding
3642 ReadUnusedBytesFromFile(file, 1);
3644 // ---------- change page property values (48 bytes) ------------------------
3646 setElementChangePages(ei, ei->num_change_pages);
3648 for (i = 0; i < ei->num_change_pages; i++)
3650 struct ElementChangeInfo *change = &ei->change_page[i];
3651 unsigned int event_bits;
3653 // always start with reliable default values
3654 setElementChangeInfoToDefaults(change);
3656 // bits 0 - 31 of "has_event[]" ...
3657 event_bits = getFile32BitBE(file);
3658 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3659 if (event_bits & (1u << j))
3660 change->has_event[j] = TRUE;
3662 change->target_element = getMappedElement(getFile16BitBE(file));
3664 change->delay_fixed = getFile16BitBE(file);
3665 change->delay_random = getFile16BitBE(file);
3666 change->delay_frames = getFile16BitBE(file);
3668 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3670 change->explode = getFile8Bit(file);
3671 change->use_target_content = getFile8Bit(file);
3672 change->only_if_complete = getFile8Bit(file);
3673 change->use_random_replace = getFile8Bit(file);
3675 change->random_percentage = getFile8Bit(file);
3676 change->replace_when = getFile8Bit(file);
3678 for (y = 0; y < 3; y++)
3679 for (x = 0; x < 3; x++)
3680 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3682 change->can_change = getFile8Bit(file);
3684 change->trigger_side = getFile8Bit(file);
3686 change->trigger_player = getFile8Bit(file);
3687 change->trigger_page = getFile8Bit(file);
3689 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3690 CH_PAGE_ANY : (1 << change->trigger_page));
3692 change->has_action = getFile8Bit(file);
3693 change->action_type = getFile8Bit(file);
3694 change->action_mode = getFile8Bit(file);
3695 change->action_arg = getFile16BitBE(file);
3697 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3698 event_bits = getFile8Bit(file);
3699 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3700 if (event_bits & (1u << (j - 32)))
3701 change->has_event[j] = TRUE;
3704 // mark this custom element as modified
3705 ei->modified_settings = TRUE;
3707 level->file_has_custom_elements = TRUE;
3712 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3714 struct ElementInfo *ei;
3715 struct ElementGroupInfo *group;
3719 element = getMappedElement(getFile16BitBE(file));
3721 if (!IS_GROUP_ELEMENT(element))
3723 Warn("invalid group element number %d", element);
3725 ReadUnusedBytesFromFile(file, chunk_size - 2);
3730 ei = &element_info[element];
3732 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3733 ei->description[i] = getFile8Bit(file);
3734 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3736 group = element_info[element].group;
3738 group->num_elements = getFile8Bit(file);
3740 ei->use_gfx_element = getFile8Bit(file);
3741 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3743 group->choice_mode = getFile8Bit(file);
3745 // some free bytes for future values and padding
3746 ReadUnusedBytesFromFile(file, 3);
3748 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3749 group->element[i] = getMappedElement(getFile16BitBE(file));
3751 // mark this group element as modified
3752 element_info[element].modified_settings = TRUE;
3754 level->file_has_custom_elements = TRUE;
3759 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3760 int element, int real_element)
3762 int micro_chunk_size = 0;
3763 int conf_type = getFile8Bit(file);
3764 int byte_mask = conf_type & CONF_MASK_BYTES;
3765 boolean element_found = FALSE;
3768 micro_chunk_size += 1;
3770 if (byte_mask == CONF_MASK_MULTI_BYTES)
3772 int num_bytes = getFile16BitBE(file);
3773 byte *buffer = checked_malloc(num_bytes);
3775 ReadBytesFromFile(file, buffer, num_bytes);
3777 for (i = 0; conf[i].data_type != -1; i++)
3779 if (conf[i].element == element &&
3780 conf[i].conf_type == conf_type)
3782 int data_type = conf[i].data_type;
3783 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3784 int max_num_entities = conf[i].max_num_entities;
3786 if (num_entities > max_num_entities)
3788 Warn("truncating number of entities for element %d from %d to %d",
3789 element, num_entities, max_num_entities);
3791 num_entities = max_num_entities;
3794 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3795 data_type == TYPE_CONTENT_LIST))
3797 // for element and content lists, zero entities are not allowed
3798 Warn("found empty list of entities for element %d", element);
3800 // do not set "num_entities" here to prevent reading behind buffer
3802 *(int *)(conf[i].num_entities) = 1; // at least one is required
3806 *(int *)(conf[i].num_entities) = num_entities;
3809 element_found = TRUE;
3811 if (data_type == TYPE_STRING)
3813 char *string = (char *)(conf[i].value);
3816 for (j = 0; j < max_num_entities; j++)
3817 string[j] = (j < num_entities ? buffer[j] : '\0');
3819 else if (data_type == TYPE_ELEMENT_LIST)
3821 int *element_array = (int *)(conf[i].value);
3824 for (j = 0; j < num_entities; j++)
3826 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3828 else if (data_type == TYPE_CONTENT_LIST)
3830 struct Content *content= (struct Content *)(conf[i].value);
3833 for (c = 0; c < num_entities; c++)
3834 for (y = 0; y < 3; y++)
3835 for (x = 0; x < 3; x++)
3836 content[c].e[x][y] =
3837 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3840 element_found = FALSE;
3846 checked_free(buffer);
3848 micro_chunk_size += 2 + num_bytes;
3850 else // constant size configuration data (1, 2 or 4 bytes)
3852 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3853 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3854 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3856 for (i = 0; conf[i].data_type != -1; i++)
3858 if (conf[i].element == element &&
3859 conf[i].conf_type == conf_type)
3861 int data_type = conf[i].data_type;
3863 if (data_type == TYPE_ELEMENT)
3864 value = getMappedElement(value);
3866 if (data_type == TYPE_BOOLEAN)
3867 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3869 *(int *) (conf[i].value) = value;
3871 element_found = TRUE;
3877 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3882 char *error_conf_chunk_bytes =
3883 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3884 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3885 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3886 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3887 int error_element = real_element;
3889 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3890 error_conf_chunk_bytes, error_conf_chunk_token,
3891 error_element, EL_NAME(error_element));
3894 return micro_chunk_size;
3897 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3899 int real_chunk_size = 0;
3901 li = *level; // copy level data into temporary buffer
3903 while (!checkEndOfFile(file))
3905 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3907 if (real_chunk_size >= chunk_size)
3911 *level = li; // copy temporary buffer back to level data
3913 return real_chunk_size;
3916 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3918 int real_chunk_size = 0;
3920 li = *level; // copy level data into temporary buffer
3922 while (!checkEndOfFile(file))
3924 int element = getMappedElement(getFile16BitBE(file));
3926 real_chunk_size += 2;
3927 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3929 if (real_chunk_size >= chunk_size)
3933 *level = li; // copy temporary buffer back to level data
3935 return real_chunk_size;
3938 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3940 int real_chunk_size = 0;
3942 li = *level; // copy level data into temporary buffer
3944 while (!checkEndOfFile(file))
3946 int element = getMappedElement(getFile16BitBE(file));
3948 real_chunk_size += 2;
3949 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3951 if (real_chunk_size >= chunk_size)
3955 *level = li; // copy temporary buffer back to level data
3957 return real_chunk_size;
3960 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3962 int element = getMappedElement(getFile16BitBE(file));
3963 int envelope_nr = element - EL_ENVELOPE_1;
3964 int real_chunk_size = 2;
3966 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3968 while (!checkEndOfFile(file))
3970 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3973 if (real_chunk_size >= chunk_size)
3977 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3979 return real_chunk_size;
3982 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3984 int element = getMappedElement(getFile16BitBE(file));
3985 int real_chunk_size = 2;
3986 struct ElementInfo *ei = &element_info[element];
3989 xx_ei = *ei; // copy element data into temporary buffer
3991 xx_ei.num_change_pages = -1;
3993 while (!checkEndOfFile(file))
3995 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3997 if (xx_ei.num_change_pages != -1)
4000 if (real_chunk_size >= chunk_size)
4006 if (ei->num_change_pages == -1)
4008 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4011 ei->num_change_pages = 1;
4013 setElementChangePages(ei, 1);
4014 setElementChangeInfoToDefaults(ei->change);
4016 return real_chunk_size;
4019 // initialize number of change pages stored for this custom element
4020 setElementChangePages(ei, ei->num_change_pages);
4021 for (i = 0; i < ei->num_change_pages; i++)
4022 setElementChangeInfoToDefaults(&ei->change_page[i]);
4024 // start with reading properties for the first change page
4025 xx_current_change_page = 0;
4027 while (!checkEndOfFile(file))
4029 // level file might contain invalid change page number
4030 if (xx_current_change_page >= ei->num_change_pages)
4033 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4035 xx_change = *change; // copy change data into temporary buffer
4037 resetEventBits(); // reset bits; change page might have changed
4039 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4042 *change = xx_change;
4044 setEventFlagsFromEventBits(change);
4046 if (real_chunk_size >= chunk_size)
4050 level->file_has_custom_elements = TRUE;
4052 return real_chunk_size;
4055 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4057 int element = getMappedElement(getFile16BitBE(file));
4058 int real_chunk_size = 2;
4059 struct ElementInfo *ei = &element_info[element];
4060 struct ElementGroupInfo *group = ei->group;
4065 xx_ei = *ei; // copy element data into temporary buffer
4066 xx_group = *group; // copy group data into temporary buffer
4068 while (!checkEndOfFile(file))
4070 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4073 if (real_chunk_size >= chunk_size)
4080 level->file_has_custom_elements = TRUE;
4082 return real_chunk_size;
4085 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4087 int element = getMappedElement(getFile16BitBE(file));
4088 int real_chunk_size = 2;
4089 struct ElementInfo *ei = &element_info[element];
4091 xx_ei = *ei; // copy element data into temporary buffer
4093 while (!checkEndOfFile(file))
4095 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4098 if (real_chunk_size >= chunk_size)
4104 level->file_has_custom_elements = TRUE;
4106 return real_chunk_size;
4109 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4110 struct LevelFileInfo *level_file_info,
4111 boolean level_info_only)
4113 char *filename = level_file_info->filename;
4114 char cookie[MAX_LINE_LEN];
4115 char chunk_name[CHUNK_ID_LEN + 1];
4119 if (!(file = openFile(filename, MODE_READ)))
4121 level->no_valid_file = TRUE;
4122 level->no_level_file = TRUE;
4124 if (level_info_only)
4127 Warn("cannot read level '%s' -- using empty level", filename);
4129 if (!setup.editor.use_template_for_new_levels)
4132 // if level file not found, try to initialize level data from template
4133 filename = getGlobalLevelTemplateFilename();
4135 if (!(file = openFile(filename, MODE_READ)))
4138 // default: for empty levels, use level template for custom elements
4139 level->use_custom_template = TRUE;
4141 level->no_valid_file = FALSE;
4144 getFileChunkBE(file, chunk_name, NULL);
4145 if (strEqual(chunk_name, "RND1"))
4147 getFile32BitBE(file); // not used
4149 getFileChunkBE(file, chunk_name, NULL);
4150 if (!strEqual(chunk_name, "CAVE"))
4152 level->no_valid_file = TRUE;
4154 Warn("unknown format of level file '%s'", filename);
4161 else // check for pre-2.0 file format with cookie string
4163 strcpy(cookie, chunk_name);
4164 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4166 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4167 cookie[strlen(cookie) - 1] = '\0';
4169 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4171 level->no_valid_file = TRUE;
4173 Warn("unknown format of level file '%s'", filename);
4180 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4182 level->no_valid_file = TRUE;
4184 Warn("unsupported version of level file '%s'", filename);
4191 // pre-2.0 level files have no game version, so use file version here
4192 level->game_version = level->file_version;
4195 if (level->file_version < FILE_VERSION_1_2)
4197 // level files from versions before 1.2.0 without chunk structure
4198 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4199 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4207 int (*loader)(File *, int, struct LevelInfo *);
4211 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4212 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4213 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4214 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4215 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4216 { "INFO", -1, LoadLevel_INFO },
4217 { "BODY", -1, LoadLevel_BODY },
4218 { "CONT", -1, LoadLevel_CONT },
4219 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4220 { "CNT3", -1, LoadLevel_CNT3 },
4221 { "CUS1", -1, LoadLevel_CUS1 },
4222 { "CUS2", -1, LoadLevel_CUS2 },
4223 { "CUS3", -1, LoadLevel_CUS3 },
4224 { "CUS4", -1, LoadLevel_CUS4 },
4225 { "GRP1", -1, LoadLevel_GRP1 },
4226 { "CONF", -1, LoadLevel_CONF },
4227 { "ELEM", -1, LoadLevel_ELEM },
4228 { "NOTE", -1, LoadLevel_NOTE },
4229 { "CUSX", -1, LoadLevel_CUSX },
4230 { "GRPX", -1, LoadLevel_GRPX },
4231 { "EMPX", -1, LoadLevel_EMPX },
4236 while (getFileChunkBE(file, chunk_name, &chunk_size))
4240 while (chunk_info[i].name != NULL &&
4241 !strEqual(chunk_name, chunk_info[i].name))
4244 if (chunk_info[i].name == NULL)
4246 Warn("unknown chunk '%s' in level file '%s'",
4247 chunk_name, filename);
4249 ReadUnusedBytesFromFile(file, chunk_size);
4251 else if (chunk_info[i].size != -1 &&
4252 chunk_info[i].size != chunk_size)
4254 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4255 chunk_size, chunk_name, filename);
4257 ReadUnusedBytesFromFile(file, chunk_size);
4261 // call function to load this level chunk
4262 int chunk_size_expected =
4263 (chunk_info[i].loader)(file, chunk_size, level);
4265 if (chunk_size_expected < 0)
4267 Warn("error reading chunk '%s' in level file '%s'",
4268 chunk_name, filename);
4273 // the size of some chunks cannot be checked before reading other
4274 // chunks first (like "HEAD" and "BODY") that contain some header
4275 // information, so check them here
4276 if (chunk_size_expected != chunk_size)
4278 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4279 chunk_size, chunk_name, filename);
4291 // ----------------------------------------------------------------------------
4292 // functions for loading BD level
4293 // ----------------------------------------------------------------------------
4295 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4296 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4298 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4300 struct LevelInfo_BD *level_bd = level->native_bd_level;
4301 GdCave *cave = NULL; // will be changed below
4302 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4303 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4306 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4308 // cave and map newly allocated when set to defaults above
4309 cave = level_bd->cave;
4312 cave->intermission = level->bd_intermission;
4315 cave->level_time[0] = level->time;
4316 cave->level_diamonds[0] = level->gems_needed;
4319 cave->scheduling = level->bd_scheduling_type;
4320 cave->pal_timing = level->bd_pal_timing;
4321 cave->level_speed[0] = level->bd_cycle_delay_ms;
4322 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4323 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4324 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4327 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4328 cave->diamond_value = level->score[SC_EMERALD];
4329 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4331 // compatibility settings
4332 cave->lineshift = level->bd_line_shifting_borders;
4333 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4334 cave->short_explosions = level->bd_short_explosions;
4336 // player properties
4337 cave->diagonal_movements = level->bd_diagonal_movements;
4338 cave->active_is_first_found = level->bd_topmost_player_active;
4339 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4340 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4341 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4342 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4344 // element properties
4345 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4346 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4347 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4348 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4349 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4350 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4351 cave->level_magic_wall_time[0] = level->bd_magic_wall_time;
4352 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4353 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4354 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4355 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4357 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4358 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4359 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4360 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4361 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4362 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4363 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4365 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4366 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4367 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4368 cave->level_amoeba_threshold[0] = level->bd_amoeba_1_threshold_too_big;
4369 cave->level_amoeba_time[0] = level->bd_amoeba_1_slow_growth_time;
4370 cave->amoeba_growth_prob = level->bd_amoeba_1_slow_growth_rate * 10000;
4371 cave->amoeba_fast_growth_prob = level->bd_amoeba_1_fast_growth_rate * 10000;
4372 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4373 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4374 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4375 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4377 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4378 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4379 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4380 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4381 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4382 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4384 cave->slime_predictable = level->bd_slime_is_predictable;
4385 cave->slime_correct_random = level->bd_slime_correct_random;
4386 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4387 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4388 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4389 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4390 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4391 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4392 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4393 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4394 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4395 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4397 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4398 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4399 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4401 cave->biter_delay_frame = level->bd_biter_move_delay;
4402 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4404 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4406 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4408 cave->replicators_active = level->bd_replicators_active;
4409 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4411 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4412 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4414 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4416 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4418 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4419 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4420 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4422 cave->infinite_rockets = level->bd_infinite_rockets;
4424 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4425 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4427 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4428 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4430 cave->creatures_backwards = level->bd_creatures_start_backwards;
4431 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4432 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4434 cave->gravity = level->bd_gravity_direction;
4435 cave->gravity_switch_active = level->bd_gravity_switch_active;
4436 cave->gravity_change_time = level->bd_gravity_switch_delay;
4437 cave->gravity_affects_all = level->bd_gravity_affects_all;
4439 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4440 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4441 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4442 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4444 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4445 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4446 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4447 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4448 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4449 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4451 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4452 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4453 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4454 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4456 cave->colorb = level->bd_color_b;
4457 cave->color0 = level->bd_color_0;
4458 cave->color1 = level->bd_color_1;
4459 cave->color2 = level->bd_color_2;
4460 cave->color3 = level->bd_color_3;
4461 cave->color4 = level->bd_color_4;
4462 cave->color5 = level->bd_color_5;
4465 strncpy(cave->name, level->name, sizeof(GdString));
4466 cave->name[sizeof(GdString) - 1] = '\0';
4468 // playfield elements
4469 for (x = 0; x < cave->w; x++)
4470 for (y = 0; y < cave->h; y++)
4471 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4474 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4476 struct LevelInfo_BD *level_bd = level->native_bd_level;
4477 GdCave *cave = level_bd->cave;
4478 int bd_level_nr = level_bd->level_nr;
4481 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4482 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4485 level->bd_intermission = cave->intermission;
4488 level->time = cave->level_time[bd_level_nr];
4489 level->gems_needed = cave->level_diamonds[bd_level_nr];
4492 level->bd_scheduling_type = cave->scheduling;
4493 level->bd_pal_timing = cave->pal_timing;
4494 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4495 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4496 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4497 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4500 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4501 level->score[SC_EMERALD] = cave->diamond_value;
4502 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4504 // compatibility settings
4505 level->bd_line_shifting_borders = cave->lineshift;
4506 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4507 level->bd_short_explosions = cave->short_explosions;
4509 // player properties
4510 level->bd_diagonal_movements = cave->diagonal_movements;
4511 level->bd_topmost_player_active = cave->active_is_first_found;
4512 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4513 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4514 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4515 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4517 // element properties
4518 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4519 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4520 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4521 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4522 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4523 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4524 level->bd_magic_wall_time = cave->level_magic_wall_time[bd_level_nr];
4525 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4526 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4527 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4528 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4530 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4531 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4532 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4533 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4534 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4535 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4536 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4538 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4539 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4540 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4541 level->bd_amoeba_1_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4542 level->bd_amoeba_1_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4543 level->bd_amoeba_1_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4544 level->bd_amoeba_1_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4545 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4546 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4547 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4548 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4550 level->bd_amoeba_1_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4551 level->bd_amoeba_1_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4552 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4553 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4554 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4555 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4557 level->bd_slime_is_predictable = cave->slime_predictable;
4558 level->bd_slime_correct_random = cave->slime_correct_random;
4559 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4560 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4561 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4562 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4563 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4564 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4565 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4566 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4567 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4568 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4570 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4571 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4572 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4574 level->bd_biter_move_delay = cave->biter_delay_frame;
4575 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4577 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4579 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4581 level->bd_replicators_active = cave->replicators_active;
4582 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4584 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4585 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4587 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4589 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4591 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4592 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4593 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4595 level->bd_infinite_rockets = cave->infinite_rockets;
4597 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4598 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4600 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4601 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4603 level->bd_creatures_start_backwards = cave->creatures_backwards;
4604 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4605 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4607 level->bd_gravity_direction = cave->gravity;
4608 level->bd_gravity_switch_active = cave->gravity_switch_active;
4609 level->bd_gravity_switch_delay = cave->gravity_change_time;
4610 level->bd_gravity_affects_all = cave->gravity_affects_all;
4612 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4613 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4614 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4615 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4617 level->bd_firefly_1_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4618 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4619 level->bd_butterfly_1_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4620 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4621 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4622 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4624 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4625 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4626 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4627 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4629 level->bd_color_b = cave->colorb;
4630 level->bd_color_0 = cave->color0;
4631 level->bd_color_1 = cave->color1;
4632 level->bd_color_2 = cave->color2;
4633 level->bd_color_3 = cave->color3;
4634 level->bd_color_4 = cave->color4;
4635 level->bd_color_5 = cave->color5;
4637 // set default color type and colors for BD style level colors
4638 SetDefaultLevelColorType_BD();
4639 SetDefaultLevelColors_BD();
4642 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4644 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4645 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4647 // playfield elements
4648 for (x = 0; x < level->fieldx; x++)
4649 for (y = 0; y < level->fieldy; y++)
4650 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4652 checked_free(cave_name);
4655 static void setTapeInfoToDefaults(void);
4657 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4659 struct LevelInfo_BD *level_bd = level->native_bd_level;
4660 GdCave *cave = level_bd->cave;
4661 GdReplay *replay = level_bd->replay;
4667 // always start with reliable default values
4668 setTapeInfoToDefaults();
4670 tape.level_nr = level_nr; // (currently not used)
4671 tape.random_seed = replay->seed;
4673 TapeSetDateFromIsoDateString(replay->date);
4676 tape.pos[tape.counter].delay = 0;
4678 tape.bd_replay = TRUE;
4680 // all time calculations only used to display approximate tape time
4681 int cave_speed = cave->speed;
4682 int milliseconds_game = 0;
4683 int milliseconds_elapsed = 20;
4685 for (i = 0; i < replay->movements->len; i++)
4687 int replay_action = replay->movements->data[i];
4688 int tape_action = map_action_BD_to_RND(replay_action);
4689 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4690 boolean success = 0;
4694 success = TapeAddAction(action);
4696 milliseconds_game += milliseconds_elapsed;
4698 if (milliseconds_game >= cave_speed)
4700 milliseconds_game -= cave_speed;
4707 tape.pos[tape.counter].delay = 0;
4708 tape.pos[tape.counter].action[0] = 0;
4712 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4718 TapeHaltRecording();
4720 if (!replay->success)
4721 Warn("BD replay is marked as not successful");
4725 // ----------------------------------------------------------------------------
4726 // functions for loading EM level
4727 // ----------------------------------------------------------------------------
4729 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4731 static int ball_xy[8][2] =
4742 struct LevelInfo_EM *level_em = level->native_em_level;
4743 struct CAVE *cav = level_em->cav;
4746 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4747 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4749 cav->time_seconds = level->time;
4750 cav->gems_needed = level->gems_needed;
4752 cav->emerald_score = level->score[SC_EMERALD];
4753 cav->diamond_score = level->score[SC_DIAMOND];
4754 cav->alien_score = level->score[SC_ROBOT];
4755 cav->tank_score = level->score[SC_SPACESHIP];
4756 cav->bug_score = level->score[SC_BUG];
4757 cav->eater_score = level->score[SC_YAMYAM];
4758 cav->nut_score = level->score[SC_NUT];
4759 cav->dynamite_score = level->score[SC_DYNAMITE];
4760 cav->key_score = level->score[SC_KEY];
4761 cav->exit_score = level->score[SC_TIME_BONUS];
4763 cav->num_eater_arrays = level->num_yamyam_contents;
4765 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4766 for (y = 0; y < 3; y++)
4767 for (x = 0; x < 3; x++)
4768 cav->eater_array[i][y * 3 + x] =
4769 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4771 cav->amoeba_time = level->amoeba_speed;
4772 cav->wonderwall_time = level->time_magic_wall;
4773 cav->wheel_time = level->time_wheel;
4775 cav->android_move_time = level->android_move_time;
4776 cav->android_clone_time = level->android_clone_time;
4777 cav->ball_random = level->ball_random;
4778 cav->ball_active = level->ball_active_initial;
4779 cav->ball_time = level->ball_time;
4780 cav->num_ball_arrays = level->num_ball_contents;
4782 cav->lenses_score = level->lenses_score;
4783 cav->magnify_score = level->magnify_score;
4784 cav->slurp_score = level->slurp_score;
4786 cav->lenses_time = level->lenses_time;
4787 cav->magnify_time = level->magnify_time;
4789 cav->wind_time = 9999;
4790 cav->wind_direction =
4791 map_direction_RND_to_EM(level->wind_direction_initial);
4793 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4794 for (j = 0; j < 8; j++)
4795 cav->ball_array[i][j] =
4796 map_element_RND_to_EM_cave(level->ball_content[i].
4797 e[ball_xy[j][0]][ball_xy[j][1]]);
4799 map_android_clone_elements_RND_to_EM(level);
4801 // first fill the complete playfield with the empty space element
4802 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4803 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4804 cav->cave[x][y] = Cblank;
4806 // then copy the real level contents from level file into the playfield
4807 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4809 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4811 if (level->field[x][y] == EL_AMOEBA_DEAD)
4812 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4814 cav->cave[x][y] = new_element;
4817 for (i = 0; i < MAX_PLAYERS; i++)
4819 cav->player_x[i] = -1;
4820 cav->player_y[i] = -1;
4823 // initialize player positions and delete players from the playfield
4824 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4826 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4828 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4830 cav->player_x[player_nr] = x;
4831 cav->player_y[player_nr] = y;
4833 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4838 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4840 static int ball_xy[8][2] =
4851 struct LevelInfo_EM *level_em = level->native_em_level;
4852 struct CAVE *cav = level_em->cav;
4855 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4856 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4858 level->time = cav->time_seconds;
4859 level->gems_needed = cav->gems_needed;
4861 sprintf(level->name, "Level %d", level->file_info.nr);
4863 level->score[SC_EMERALD] = cav->emerald_score;
4864 level->score[SC_DIAMOND] = cav->diamond_score;
4865 level->score[SC_ROBOT] = cav->alien_score;
4866 level->score[SC_SPACESHIP] = cav->tank_score;
4867 level->score[SC_BUG] = cav->bug_score;
4868 level->score[SC_YAMYAM] = cav->eater_score;
4869 level->score[SC_NUT] = cav->nut_score;
4870 level->score[SC_DYNAMITE] = cav->dynamite_score;
4871 level->score[SC_KEY] = cav->key_score;
4872 level->score[SC_TIME_BONUS] = cav->exit_score;
4874 level->num_yamyam_contents = cav->num_eater_arrays;
4876 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4877 for (y = 0; y < 3; y++)
4878 for (x = 0; x < 3; x++)
4879 level->yamyam_content[i].e[x][y] =
4880 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4882 level->amoeba_speed = cav->amoeba_time;
4883 level->time_magic_wall = cav->wonderwall_time;
4884 level->time_wheel = cav->wheel_time;
4886 level->android_move_time = cav->android_move_time;
4887 level->android_clone_time = cav->android_clone_time;
4888 level->ball_random = cav->ball_random;
4889 level->ball_active_initial = cav->ball_active;
4890 level->ball_time = cav->ball_time;
4891 level->num_ball_contents = cav->num_ball_arrays;
4893 level->lenses_score = cav->lenses_score;
4894 level->magnify_score = cav->magnify_score;
4895 level->slurp_score = cav->slurp_score;
4897 level->lenses_time = cav->lenses_time;
4898 level->magnify_time = cav->magnify_time;
4900 level->wind_direction_initial =
4901 map_direction_EM_to_RND(cav->wind_direction);
4903 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4904 for (j = 0; j < 8; j++)
4905 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4906 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4908 map_android_clone_elements_EM_to_RND(level);
4910 // convert the playfield (some elements need special treatment)
4911 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4913 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4915 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4916 new_element = EL_AMOEBA_DEAD;
4918 level->field[x][y] = new_element;
4921 for (i = 0; i < MAX_PLAYERS; i++)
4923 // in case of all players set to the same field, use the first player
4924 int nr = MAX_PLAYERS - i - 1;
4925 int jx = cav->player_x[nr];
4926 int jy = cav->player_y[nr];
4928 if (jx != -1 && jy != -1)
4929 level->field[jx][jy] = EL_PLAYER_1 + nr;
4932 // time score is counted for each 10 seconds left in Emerald Mine levels
4933 level->time_score_base = 10;
4937 // ----------------------------------------------------------------------------
4938 // functions for loading SP level
4939 // ----------------------------------------------------------------------------
4941 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4943 struct LevelInfo_SP *level_sp = level->native_sp_level;
4944 LevelInfoType *header = &level_sp->header;
4947 level_sp->width = level->fieldx;
4948 level_sp->height = level->fieldy;
4950 for (x = 0; x < level->fieldx; x++)
4951 for (y = 0; y < level->fieldy; y++)
4952 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4954 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4956 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4957 header->LevelTitle[i] = level->name[i];
4958 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4960 header->InfotronsNeeded = level->gems_needed;
4962 header->SpecialPortCount = 0;
4964 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4966 boolean gravity_port_found = FALSE;
4967 boolean gravity_port_valid = FALSE;
4968 int gravity_port_flag;
4969 int gravity_port_base_element;
4970 int element = level->field[x][y];
4972 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4973 element <= EL_SP_GRAVITY_ON_PORT_UP)
4975 gravity_port_found = TRUE;
4976 gravity_port_valid = TRUE;
4977 gravity_port_flag = 1;
4978 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4980 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4981 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4983 gravity_port_found = TRUE;
4984 gravity_port_valid = TRUE;
4985 gravity_port_flag = 0;
4986 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4988 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4989 element <= EL_SP_GRAVITY_PORT_UP)
4991 // change R'n'D style gravity inverting special port to normal port
4992 // (there are no gravity inverting ports in native Supaplex engine)
4994 gravity_port_found = TRUE;
4995 gravity_port_valid = FALSE;
4996 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4999 if (gravity_port_found)
5001 if (gravity_port_valid &&
5002 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5004 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5006 port->PortLocation = (y * level->fieldx + x) * 2;
5007 port->Gravity = gravity_port_flag;
5009 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5011 header->SpecialPortCount++;
5015 // change special gravity port to normal port
5017 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5020 level_sp->playfield[x][y] = element - EL_SP_START;
5025 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5027 struct LevelInfo_SP *level_sp = level->native_sp_level;
5028 LevelInfoType *header = &level_sp->header;
5029 boolean num_invalid_elements = 0;
5032 level->fieldx = level_sp->width;
5033 level->fieldy = level_sp->height;
5035 for (x = 0; x < level->fieldx; x++)
5037 for (y = 0; y < level->fieldy; y++)
5039 int element_old = level_sp->playfield[x][y];
5040 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5042 if (element_new == EL_UNKNOWN)
5044 num_invalid_elements++;
5046 Debug("level:native:SP", "invalid element %d at position %d, %d",
5050 level->field[x][y] = element_new;
5054 if (num_invalid_elements > 0)
5055 Warn("found %d invalid elements%s", num_invalid_elements,
5056 (!options.debug ? " (use '--debug' for more details)" : ""));
5058 for (i = 0; i < MAX_PLAYERS; i++)
5059 level->initial_player_gravity[i] =
5060 (header->InitialGravity == 1 ? TRUE : FALSE);
5062 // skip leading spaces
5063 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5064 if (header->LevelTitle[i] != ' ')
5068 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5069 level->name[j] = header->LevelTitle[i];
5070 level->name[j] = '\0';
5072 // cut trailing spaces
5074 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5075 level->name[j - 1] = '\0';
5077 level->gems_needed = header->InfotronsNeeded;
5079 for (i = 0; i < header->SpecialPortCount; i++)
5081 SpecialPortType *port = &header->SpecialPort[i];
5082 int port_location = port->PortLocation;
5083 int gravity = port->Gravity;
5084 int port_x, port_y, port_element;
5086 port_x = (port_location / 2) % level->fieldx;
5087 port_y = (port_location / 2) / level->fieldx;
5089 if (port_x < 0 || port_x >= level->fieldx ||
5090 port_y < 0 || port_y >= level->fieldy)
5092 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5097 port_element = level->field[port_x][port_y];
5099 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5100 port_element > EL_SP_GRAVITY_PORT_UP)
5102 Warn("no special port at position (%d, %d)", port_x, port_y);
5107 // change previous (wrong) gravity inverting special port to either
5108 // gravity enabling special port or gravity disabling special port
5109 level->field[port_x][port_y] +=
5110 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5111 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5114 // change special gravity ports without database entries to normal ports
5115 for (x = 0; x < level->fieldx; x++)
5116 for (y = 0; y < level->fieldy; y++)
5117 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5118 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5119 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5121 level->time = 0; // no time limit
5122 level->amoeba_speed = 0;
5123 level->time_magic_wall = 0;
5124 level->time_wheel = 0;
5125 level->amoeba_content = EL_EMPTY;
5127 // original Supaplex does not use score values -- rate by playing time
5128 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5129 level->score[i] = 0;
5131 level->rate_time_over_score = TRUE;
5133 // there are no yamyams in supaplex levels
5134 for (i = 0; i < level->num_yamyam_contents; i++)
5135 for (x = 0; x < 3; x++)
5136 for (y = 0; y < 3; y++)
5137 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5140 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5142 struct LevelInfo_SP *level_sp = level->native_sp_level;
5143 struct DemoInfo_SP *demo = &level_sp->demo;
5146 // always start with reliable default values
5147 demo->is_available = FALSE;
5150 if (TAPE_IS_EMPTY(tape))
5153 demo->level_nr = tape.level_nr; // (currently not used)
5155 level_sp->header.DemoRandomSeed = tape.random_seed;
5159 for (i = 0; i < tape.length; i++)
5161 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5162 int demo_repeat = tape.pos[i].delay;
5163 int demo_entries = (demo_repeat + 15) / 16;
5165 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5167 Warn("tape truncated: size exceeds maximum SP demo size %d",
5173 for (j = 0; j < demo_repeat / 16; j++)
5174 demo->data[demo->length++] = 0xf0 | demo_action;
5176 if (demo_repeat % 16)
5177 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5180 demo->is_available = TRUE;
5183 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5185 struct LevelInfo_SP *level_sp = level->native_sp_level;
5186 struct DemoInfo_SP *demo = &level_sp->demo;
5187 char *filename = level->file_info.filename;
5190 // always start with reliable default values
5191 setTapeInfoToDefaults();
5193 if (!demo->is_available)
5196 tape.level_nr = demo->level_nr; // (currently not used)
5197 tape.random_seed = level_sp->header.DemoRandomSeed;
5199 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5202 tape.pos[tape.counter].delay = 0;
5204 for (i = 0; i < demo->length; i++)
5206 int demo_action = demo->data[i] & 0x0f;
5207 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5208 int tape_action = map_key_SP_to_RND(demo_action);
5209 int tape_repeat = demo_repeat + 1;
5210 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5211 boolean success = 0;
5214 for (j = 0; j < tape_repeat; j++)
5215 success = TapeAddAction(action);
5219 Warn("SP demo truncated: size exceeds maximum tape size %d",
5226 TapeHaltRecording();
5230 // ----------------------------------------------------------------------------
5231 // functions for loading MM level
5232 // ----------------------------------------------------------------------------
5234 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5236 struct LevelInfo_MM *level_mm = level->native_mm_level;
5239 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5240 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5242 level_mm->time = level->time;
5243 level_mm->kettles_needed = level->gems_needed;
5244 level_mm->auto_count_kettles = level->auto_count_gems;
5246 level_mm->mm_laser_red = level->mm_laser_red;
5247 level_mm->mm_laser_green = level->mm_laser_green;
5248 level_mm->mm_laser_blue = level->mm_laser_blue;
5250 level_mm->df_laser_red = level->df_laser_red;
5251 level_mm->df_laser_green = level->df_laser_green;
5252 level_mm->df_laser_blue = level->df_laser_blue;
5254 strcpy(level_mm->name, level->name);
5255 strcpy(level_mm->author, level->author);
5257 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5258 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5259 level_mm->score[SC_KEY] = level->score[SC_KEY];
5260 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5261 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5263 level_mm->amoeba_speed = level->amoeba_speed;
5264 level_mm->time_fuse = level->mm_time_fuse;
5265 level_mm->time_bomb = level->mm_time_bomb;
5266 level_mm->time_ball = level->mm_time_ball;
5267 level_mm->time_block = level->mm_time_block;
5269 level_mm->num_ball_contents = level->num_mm_ball_contents;
5270 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5271 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5272 level_mm->explode_ball = level->explode_mm_ball;
5274 for (i = 0; i < level->num_mm_ball_contents; i++)
5275 level_mm->ball_content[i] =
5276 map_element_RND_to_MM(level->mm_ball_content[i]);
5278 for (x = 0; x < level->fieldx; x++)
5279 for (y = 0; y < level->fieldy; y++)
5281 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5284 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5286 struct LevelInfo_MM *level_mm = level->native_mm_level;
5289 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5290 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5292 level->time = level_mm->time;
5293 level->gems_needed = level_mm->kettles_needed;
5294 level->auto_count_gems = level_mm->auto_count_kettles;
5296 level->mm_laser_red = level_mm->mm_laser_red;
5297 level->mm_laser_green = level_mm->mm_laser_green;
5298 level->mm_laser_blue = level_mm->mm_laser_blue;
5300 level->df_laser_red = level_mm->df_laser_red;
5301 level->df_laser_green = level_mm->df_laser_green;
5302 level->df_laser_blue = level_mm->df_laser_blue;
5304 strcpy(level->name, level_mm->name);
5306 // only overwrite author from 'levelinfo.conf' if author defined in level
5307 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5308 strcpy(level->author, level_mm->author);
5310 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5311 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5312 level->score[SC_KEY] = level_mm->score[SC_KEY];
5313 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5314 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5316 level->amoeba_speed = level_mm->amoeba_speed;
5317 level->mm_time_fuse = level_mm->time_fuse;
5318 level->mm_time_bomb = level_mm->time_bomb;
5319 level->mm_time_ball = level_mm->time_ball;
5320 level->mm_time_block = level_mm->time_block;
5322 level->num_mm_ball_contents = level_mm->num_ball_contents;
5323 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5324 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5325 level->explode_mm_ball = level_mm->explode_ball;
5327 for (i = 0; i < level->num_mm_ball_contents; i++)
5328 level->mm_ball_content[i] =
5329 map_element_MM_to_RND(level_mm->ball_content[i]);
5331 for (x = 0; x < level->fieldx; x++)
5332 for (y = 0; y < level->fieldy; y++)
5333 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5337 // ----------------------------------------------------------------------------
5338 // functions for loading DC level
5339 // ----------------------------------------------------------------------------
5341 #define DC_LEVEL_HEADER_SIZE 344
5343 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5346 static int last_data_encoded;
5350 int diff_hi, diff_lo;
5351 int data_hi, data_lo;
5352 unsigned short data_decoded;
5356 last_data_encoded = 0;
5363 diff = data_encoded - last_data_encoded;
5364 diff_hi = diff & ~0xff;
5365 diff_lo = diff & 0xff;
5369 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5370 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5371 data_hi = data_hi & 0xff00;
5373 data_decoded = data_hi | data_lo;
5375 last_data_encoded = data_encoded;
5377 offset1 = (offset1 + 1) % 31;
5378 offset2 = offset2 & 0xff;
5380 return data_decoded;
5383 static int getMappedElement_DC(int element)
5391 // 0x0117 - 0x036e: (?)
5394 // 0x042d - 0x0684: (?)
5410 element = EL_CRYSTAL;
5413 case 0x0e77: // quicksand (boulder)
5414 element = EL_QUICKSAND_FAST_FULL;
5417 case 0x0e99: // slow quicksand (boulder)
5418 element = EL_QUICKSAND_FULL;
5422 element = EL_EM_EXIT_OPEN;
5426 element = EL_EM_EXIT_CLOSED;
5430 element = EL_EM_STEEL_EXIT_OPEN;
5434 element = EL_EM_STEEL_EXIT_CLOSED;
5437 case 0x0f4f: // dynamite (lit 1)
5438 element = EL_EM_DYNAMITE_ACTIVE;
5441 case 0x0f57: // dynamite (lit 2)
5442 element = EL_EM_DYNAMITE_ACTIVE;
5445 case 0x0f5f: // dynamite (lit 3)
5446 element = EL_EM_DYNAMITE_ACTIVE;
5449 case 0x0f67: // dynamite (lit 4)
5450 element = EL_EM_DYNAMITE_ACTIVE;
5457 element = EL_AMOEBA_WET;
5461 element = EL_AMOEBA_DROP;
5465 element = EL_DC_MAGIC_WALL;
5469 element = EL_SPACESHIP_UP;
5473 element = EL_SPACESHIP_DOWN;
5477 element = EL_SPACESHIP_LEFT;
5481 element = EL_SPACESHIP_RIGHT;
5485 element = EL_BUG_UP;
5489 element = EL_BUG_DOWN;
5493 element = EL_BUG_LEFT;
5497 element = EL_BUG_RIGHT;
5501 element = EL_MOLE_UP;
5505 element = EL_MOLE_DOWN;
5509 element = EL_MOLE_LEFT;
5513 element = EL_MOLE_RIGHT;
5521 element = EL_YAMYAM_UP;
5525 element = EL_SWITCHGATE_OPEN;
5529 element = EL_SWITCHGATE_CLOSED;
5533 element = EL_DC_SWITCHGATE_SWITCH_UP;
5537 element = EL_TIMEGATE_CLOSED;
5540 case 0x144c: // conveyor belt switch (green)
5541 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5544 case 0x144f: // conveyor belt switch (red)
5545 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5548 case 0x1452: // conveyor belt switch (blue)
5549 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5553 element = EL_CONVEYOR_BELT_3_MIDDLE;
5557 element = EL_CONVEYOR_BELT_3_LEFT;
5561 element = EL_CONVEYOR_BELT_3_RIGHT;
5565 element = EL_CONVEYOR_BELT_1_MIDDLE;
5569 element = EL_CONVEYOR_BELT_1_LEFT;
5573 element = EL_CONVEYOR_BELT_1_RIGHT;
5577 element = EL_CONVEYOR_BELT_4_MIDDLE;
5581 element = EL_CONVEYOR_BELT_4_LEFT;
5585 element = EL_CONVEYOR_BELT_4_RIGHT;
5589 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5593 element = EL_EXPANDABLE_WALL_VERTICAL;
5597 element = EL_EXPANDABLE_WALL_ANY;
5600 case 0x14ce: // growing steel wall (left/right)
5601 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5604 case 0x14df: // growing steel wall (up/down)
5605 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5608 case 0x14e8: // growing steel wall (up/down/left/right)
5609 element = EL_EXPANDABLE_STEELWALL_ANY;
5613 element = EL_SHIELD_DEADLY;
5617 element = EL_EXTRA_TIME;
5625 element = EL_EMPTY_SPACE;
5628 case 0x1578: // quicksand (empty)
5629 element = EL_QUICKSAND_FAST_EMPTY;
5632 case 0x1579: // slow quicksand (empty)
5633 element = EL_QUICKSAND_EMPTY;
5643 element = EL_EM_DYNAMITE;
5646 case 0x15a1: // key (red)
5647 element = EL_EM_KEY_1;
5650 case 0x15a2: // key (yellow)
5651 element = EL_EM_KEY_2;
5654 case 0x15a3: // key (blue)
5655 element = EL_EM_KEY_4;
5658 case 0x15a4: // key (green)
5659 element = EL_EM_KEY_3;
5662 case 0x15a5: // key (white)
5663 element = EL_DC_KEY_WHITE;
5667 element = EL_WALL_SLIPPERY;
5674 case 0x15a8: // wall (not round)
5678 case 0x15a9: // (blue)
5679 element = EL_CHAR_A;
5682 case 0x15aa: // (blue)
5683 element = EL_CHAR_B;
5686 case 0x15ab: // (blue)
5687 element = EL_CHAR_C;
5690 case 0x15ac: // (blue)
5691 element = EL_CHAR_D;
5694 case 0x15ad: // (blue)
5695 element = EL_CHAR_E;
5698 case 0x15ae: // (blue)
5699 element = EL_CHAR_F;
5702 case 0x15af: // (blue)
5703 element = EL_CHAR_G;
5706 case 0x15b0: // (blue)
5707 element = EL_CHAR_H;
5710 case 0x15b1: // (blue)
5711 element = EL_CHAR_I;
5714 case 0x15b2: // (blue)
5715 element = EL_CHAR_J;
5718 case 0x15b3: // (blue)
5719 element = EL_CHAR_K;
5722 case 0x15b4: // (blue)
5723 element = EL_CHAR_L;
5726 case 0x15b5: // (blue)
5727 element = EL_CHAR_M;
5730 case 0x15b6: // (blue)
5731 element = EL_CHAR_N;
5734 case 0x15b7: // (blue)
5735 element = EL_CHAR_O;
5738 case 0x15b8: // (blue)
5739 element = EL_CHAR_P;
5742 case 0x15b9: // (blue)
5743 element = EL_CHAR_Q;
5746 case 0x15ba: // (blue)
5747 element = EL_CHAR_R;
5750 case 0x15bb: // (blue)
5751 element = EL_CHAR_S;
5754 case 0x15bc: // (blue)
5755 element = EL_CHAR_T;
5758 case 0x15bd: // (blue)
5759 element = EL_CHAR_U;
5762 case 0x15be: // (blue)
5763 element = EL_CHAR_V;
5766 case 0x15bf: // (blue)
5767 element = EL_CHAR_W;
5770 case 0x15c0: // (blue)
5771 element = EL_CHAR_X;
5774 case 0x15c1: // (blue)
5775 element = EL_CHAR_Y;
5778 case 0x15c2: // (blue)
5779 element = EL_CHAR_Z;
5782 case 0x15c3: // (blue)
5783 element = EL_CHAR_AUMLAUT;
5786 case 0x15c4: // (blue)
5787 element = EL_CHAR_OUMLAUT;
5790 case 0x15c5: // (blue)
5791 element = EL_CHAR_UUMLAUT;
5794 case 0x15c6: // (blue)
5795 element = EL_CHAR_0;
5798 case 0x15c7: // (blue)
5799 element = EL_CHAR_1;
5802 case 0x15c8: // (blue)
5803 element = EL_CHAR_2;
5806 case 0x15c9: // (blue)
5807 element = EL_CHAR_3;
5810 case 0x15ca: // (blue)
5811 element = EL_CHAR_4;
5814 case 0x15cb: // (blue)
5815 element = EL_CHAR_5;
5818 case 0x15cc: // (blue)
5819 element = EL_CHAR_6;
5822 case 0x15cd: // (blue)
5823 element = EL_CHAR_7;
5826 case 0x15ce: // (blue)
5827 element = EL_CHAR_8;
5830 case 0x15cf: // (blue)
5831 element = EL_CHAR_9;
5834 case 0x15d0: // (blue)
5835 element = EL_CHAR_PERIOD;
5838 case 0x15d1: // (blue)
5839 element = EL_CHAR_EXCLAM;
5842 case 0x15d2: // (blue)
5843 element = EL_CHAR_COLON;
5846 case 0x15d3: // (blue)
5847 element = EL_CHAR_LESS;
5850 case 0x15d4: // (blue)
5851 element = EL_CHAR_GREATER;
5854 case 0x15d5: // (blue)
5855 element = EL_CHAR_QUESTION;
5858 case 0x15d6: // (blue)
5859 element = EL_CHAR_COPYRIGHT;
5862 case 0x15d7: // (blue)
5863 element = EL_CHAR_UP;
5866 case 0x15d8: // (blue)
5867 element = EL_CHAR_DOWN;
5870 case 0x15d9: // (blue)
5871 element = EL_CHAR_BUTTON;
5874 case 0x15da: // (blue)
5875 element = EL_CHAR_PLUS;
5878 case 0x15db: // (blue)
5879 element = EL_CHAR_MINUS;
5882 case 0x15dc: // (blue)
5883 element = EL_CHAR_APOSTROPHE;
5886 case 0x15dd: // (blue)
5887 element = EL_CHAR_PARENLEFT;
5890 case 0x15de: // (blue)
5891 element = EL_CHAR_PARENRIGHT;
5894 case 0x15df: // (green)
5895 element = EL_CHAR_A;
5898 case 0x15e0: // (green)
5899 element = EL_CHAR_B;
5902 case 0x15e1: // (green)
5903 element = EL_CHAR_C;
5906 case 0x15e2: // (green)
5907 element = EL_CHAR_D;
5910 case 0x15e3: // (green)
5911 element = EL_CHAR_E;
5914 case 0x15e4: // (green)
5915 element = EL_CHAR_F;
5918 case 0x15e5: // (green)
5919 element = EL_CHAR_G;
5922 case 0x15e6: // (green)
5923 element = EL_CHAR_H;
5926 case 0x15e7: // (green)
5927 element = EL_CHAR_I;
5930 case 0x15e8: // (green)
5931 element = EL_CHAR_J;
5934 case 0x15e9: // (green)
5935 element = EL_CHAR_K;
5938 case 0x15ea: // (green)
5939 element = EL_CHAR_L;
5942 case 0x15eb: // (green)
5943 element = EL_CHAR_M;
5946 case 0x15ec: // (green)
5947 element = EL_CHAR_N;
5950 case 0x15ed: // (green)
5951 element = EL_CHAR_O;
5954 case 0x15ee: // (green)
5955 element = EL_CHAR_P;
5958 case 0x15ef: // (green)
5959 element = EL_CHAR_Q;
5962 case 0x15f0: // (green)
5963 element = EL_CHAR_R;
5966 case 0x15f1: // (green)
5967 element = EL_CHAR_S;
5970 case 0x15f2: // (green)
5971 element = EL_CHAR_T;
5974 case 0x15f3: // (green)
5975 element = EL_CHAR_U;
5978 case 0x15f4: // (green)
5979 element = EL_CHAR_V;
5982 case 0x15f5: // (green)
5983 element = EL_CHAR_W;
5986 case 0x15f6: // (green)
5987 element = EL_CHAR_X;
5990 case 0x15f7: // (green)
5991 element = EL_CHAR_Y;
5994 case 0x15f8: // (green)
5995 element = EL_CHAR_Z;
5998 case 0x15f9: // (green)
5999 element = EL_CHAR_AUMLAUT;
6002 case 0x15fa: // (green)
6003 element = EL_CHAR_OUMLAUT;
6006 case 0x15fb: // (green)
6007 element = EL_CHAR_UUMLAUT;
6010 case 0x15fc: // (green)
6011 element = EL_CHAR_0;
6014 case 0x15fd: // (green)
6015 element = EL_CHAR_1;
6018 case 0x15fe: // (green)
6019 element = EL_CHAR_2;
6022 case 0x15ff: // (green)
6023 element = EL_CHAR_3;
6026 case 0x1600: // (green)
6027 element = EL_CHAR_4;
6030 case 0x1601: // (green)
6031 element = EL_CHAR_5;
6034 case 0x1602: // (green)
6035 element = EL_CHAR_6;
6038 case 0x1603: // (green)
6039 element = EL_CHAR_7;
6042 case 0x1604: // (green)
6043 element = EL_CHAR_8;
6046 case 0x1605: // (green)
6047 element = EL_CHAR_9;
6050 case 0x1606: // (green)
6051 element = EL_CHAR_PERIOD;
6054 case 0x1607: // (green)
6055 element = EL_CHAR_EXCLAM;
6058 case 0x1608: // (green)
6059 element = EL_CHAR_COLON;
6062 case 0x1609: // (green)
6063 element = EL_CHAR_LESS;
6066 case 0x160a: // (green)
6067 element = EL_CHAR_GREATER;
6070 case 0x160b: // (green)
6071 element = EL_CHAR_QUESTION;
6074 case 0x160c: // (green)
6075 element = EL_CHAR_COPYRIGHT;
6078 case 0x160d: // (green)
6079 element = EL_CHAR_UP;
6082 case 0x160e: // (green)
6083 element = EL_CHAR_DOWN;
6086 case 0x160f: // (green)
6087 element = EL_CHAR_BUTTON;
6090 case 0x1610: // (green)
6091 element = EL_CHAR_PLUS;
6094 case 0x1611: // (green)
6095 element = EL_CHAR_MINUS;
6098 case 0x1612: // (green)
6099 element = EL_CHAR_APOSTROPHE;
6102 case 0x1613: // (green)
6103 element = EL_CHAR_PARENLEFT;
6106 case 0x1614: // (green)
6107 element = EL_CHAR_PARENRIGHT;
6110 case 0x1615: // (blue steel)
6111 element = EL_STEEL_CHAR_A;
6114 case 0x1616: // (blue steel)
6115 element = EL_STEEL_CHAR_B;
6118 case 0x1617: // (blue steel)
6119 element = EL_STEEL_CHAR_C;
6122 case 0x1618: // (blue steel)
6123 element = EL_STEEL_CHAR_D;
6126 case 0x1619: // (blue steel)
6127 element = EL_STEEL_CHAR_E;
6130 case 0x161a: // (blue steel)
6131 element = EL_STEEL_CHAR_F;
6134 case 0x161b: // (blue steel)
6135 element = EL_STEEL_CHAR_G;
6138 case 0x161c: // (blue steel)
6139 element = EL_STEEL_CHAR_H;
6142 case 0x161d: // (blue steel)
6143 element = EL_STEEL_CHAR_I;
6146 case 0x161e: // (blue steel)
6147 element = EL_STEEL_CHAR_J;
6150 case 0x161f: // (blue steel)
6151 element = EL_STEEL_CHAR_K;
6154 case 0x1620: // (blue steel)
6155 element = EL_STEEL_CHAR_L;
6158 case 0x1621: // (blue steel)
6159 element = EL_STEEL_CHAR_M;
6162 case 0x1622: // (blue steel)
6163 element = EL_STEEL_CHAR_N;
6166 case 0x1623: // (blue steel)
6167 element = EL_STEEL_CHAR_O;
6170 case 0x1624: // (blue steel)
6171 element = EL_STEEL_CHAR_P;
6174 case 0x1625: // (blue steel)
6175 element = EL_STEEL_CHAR_Q;
6178 case 0x1626: // (blue steel)
6179 element = EL_STEEL_CHAR_R;
6182 case 0x1627: // (blue steel)
6183 element = EL_STEEL_CHAR_S;
6186 case 0x1628: // (blue steel)
6187 element = EL_STEEL_CHAR_T;
6190 case 0x1629: // (blue steel)
6191 element = EL_STEEL_CHAR_U;
6194 case 0x162a: // (blue steel)
6195 element = EL_STEEL_CHAR_V;
6198 case 0x162b: // (blue steel)
6199 element = EL_STEEL_CHAR_W;
6202 case 0x162c: // (blue steel)
6203 element = EL_STEEL_CHAR_X;
6206 case 0x162d: // (blue steel)
6207 element = EL_STEEL_CHAR_Y;
6210 case 0x162e: // (blue steel)
6211 element = EL_STEEL_CHAR_Z;
6214 case 0x162f: // (blue steel)
6215 element = EL_STEEL_CHAR_AUMLAUT;
6218 case 0x1630: // (blue steel)
6219 element = EL_STEEL_CHAR_OUMLAUT;
6222 case 0x1631: // (blue steel)
6223 element = EL_STEEL_CHAR_UUMLAUT;
6226 case 0x1632: // (blue steel)
6227 element = EL_STEEL_CHAR_0;
6230 case 0x1633: // (blue steel)
6231 element = EL_STEEL_CHAR_1;
6234 case 0x1634: // (blue steel)
6235 element = EL_STEEL_CHAR_2;
6238 case 0x1635: // (blue steel)
6239 element = EL_STEEL_CHAR_3;
6242 case 0x1636: // (blue steel)
6243 element = EL_STEEL_CHAR_4;
6246 case 0x1637: // (blue steel)
6247 element = EL_STEEL_CHAR_5;
6250 case 0x1638: // (blue steel)
6251 element = EL_STEEL_CHAR_6;
6254 case 0x1639: // (blue steel)
6255 element = EL_STEEL_CHAR_7;
6258 case 0x163a: // (blue steel)
6259 element = EL_STEEL_CHAR_8;
6262 case 0x163b: // (blue steel)
6263 element = EL_STEEL_CHAR_9;
6266 case 0x163c: // (blue steel)
6267 element = EL_STEEL_CHAR_PERIOD;
6270 case 0x163d: // (blue steel)
6271 element = EL_STEEL_CHAR_EXCLAM;
6274 case 0x163e: // (blue steel)
6275 element = EL_STEEL_CHAR_COLON;
6278 case 0x163f: // (blue steel)
6279 element = EL_STEEL_CHAR_LESS;
6282 case 0x1640: // (blue steel)
6283 element = EL_STEEL_CHAR_GREATER;
6286 case 0x1641: // (blue steel)
6287 element = EL_STEEL_CHAR_QUESTION;
6290 case 0x1642: // (blue steel)
6291 element = EL_STEEL_CHAR_COPYRIGHT;
6294 case 0x1643: // (blue steel)
6295 element = EL_STEEL_CHAR_UP;
6298 case 0x1644: // (blue steel)
6299 element = EL_STEEL_CHAR_DOWN;
6302 case 0x1645: // (blue steel)
6303 element = EL_STEEL_CHAR_BUTTON;
6306 case 0x1646: // (blue steel)
6307 element = EL_STEEL_CHAR_PLUS;
6310 case 0x1647: // (blue steel)
6311 element = EL_STEEL_CHAR_MINUS;
6314 case 0x1648: // (blue steel)
6315 element = EL_STEEL_CHAR_APOSTROPHE;
6318 case 0x1649: // (blue steel)
6319 element = EL_STEEL_CHAR_PARENLEFT;
6322 case 0x164a: // (blue steel)
6323 element = EL_STEEL_CHAR_PARENRIGHT;
6326 case 0x164b: // (green steel)
6327 element = EL_STEEL_CHAR_A;
6330 case 0x164c: // (green steel)
6331 element = EL_STEEL_CHAR_B;
6334 case 0x164d: // (green steel)
6335 element = EL_STEEL_CHAR_C;
6338 case 0x164e: // (green steel)
6339 element = EL_STEEL_CHAR_D;
6342 case 0x164f: // (green steel)
6343 element = EL_STEEL_CHAR_E;
6346 case 0x1650: // (green steel)
6347 element = EL_STEEL_CHAR_F;
6350 case 0x1651: // (green steel)
6351 element = EL_STEEL_CHAR_G;
6354 case 0x1652: // (green steel)
6355 element = EL_STEEL_CHAR_H;
6358 case 0x1653: // (green steel)
6359 element = EL_STEEL_CHAR_I;
6362 case 0x1654: // (green steel)
6363 element = EL_STEEL_CHAR_J;
6366 case 0x1655: // (green steel)
6367 element = EL_STEEL_CHAR_K;
6370 case 0x1656: // (green steel)
6371 element = EL_STEEL_CHAR_L;
6374 case 0x1657: // (green steel)
6375 element = EL_STEEL_CHAR_M;
6378 case 0x1658: // (green steel)
6379 element = EL_STEEL_CHAR_N;
6382 case 0x1659: // (green steel)
6383 element = EL_STEEL_CHAR_O;
6386 case 0x165a: // (green steel)
6387 element = EL_STEEL_CHAR_P;
6390 case 0x165b: // (green steel)
6391 element = EL_STEEL_CHAR_Q;
6394 case 0x165c: // (green steel)
6395 element = EL_STEEL_CHAR_R;
6398 case 0x165d: // (green steel)
6399 element = EL_STEEL_CHAR_S;
6402 case 0x165e: // (green steel)
6403 element = EL_STEEL_CHAR_T;
6406 case 0x165f: // (green steel)
6407 element = EL_STEEL_CHAR_U;
6410 case 0x1660: // (green steel)
6411 element = EL_STEEL_CHAR_V;
6414 case 0x1661: // (green steel)
6415 element = EL_STEEL_CHAR_W;
6418 case 0x1662: // (green steel)
6419 element = EL_STEEL_CHAR_X;
6422 case 0x1663: // (green steel)
6423 element = EL_STEEL_CHAR_Y;
6426 case 0x1664: // (green steel)
6427 element = EL_STEEL_CHAR_Z;
6430 case 0x1665: // (green steel)
6431 element = EL_STEEL_CHAR_AUMLAUT;
6434 case 0x1666: // (green steel)
6435 element = EL_STEEL_CHAR_OUMLAUT;
6438 case 0x1667: // (green steel)
6439 element = EL_STEEL_CHAR_UUMLAUT;
6442 case 0x1668: // (green steel)
6443 element = EL_STEEL_CHAR_0;
6446 case 0x1669: // (green steel)
6447 element = EL_STEEL_CHAR_1;
6450 case 0x166a: // (green steel)
6451 element = EL_STEEL_CHAR_2;
6454 case 0x166b: // (green steel)
6455 element = EL_STEEL_CHAR_3;
6458 case 0x166c: // (green steel)
6459 element = EL_STEEL_CHAR_4;
6462 case 0x166d: // (green steel)
6463 element = EL_STEEL_CHAR_5;
6466 case 0x166e: // (green steel)
6467 element = EL_STEEL_CHAR_6;
6470 case 0x166f: // (green steel)
6471 element = EL_STEEL_CHAR_7;
6474 case 0x1670: // (green steel)
6475 element = EL_STEEL_CHAR_8;
6478 case 0x1671: // (green steel)
6479 element = EL_STEEL_CHAR_9;
6482 case 0x1672: // (green steel)
6483 element = EL_STEEL_CHAR_PERIOD;
6486 case 0x1673: // (green steel)
6487 element = EL_STEEL_CHAR_EXCLAM;
6490 case 0x1674: // (green steel)
6491 element = EL_STEEL_CHAR_COLON;
6494 case 0x1675: // (green steel)
6495 element = EL_STEEL_CHAR_LESS;
6498 case 0x1676: // (green steel)
6499 element = EL_STEEL_CHAR_GREATER;
6502 case 0x1677: // (green steel)
6503 element = EL_STEEL_CHAR_QUESTION;
6506 case 0x1678: // (green steel)
6507 element = EL_STEEL_CHAR_COPYRIGHT;
6510 case 0x1679: // (green steel)
6511 element = EL_STEEL_CHAR_UP;
6514 case 0x167a: // (green steel)
6515 element = EL_STEEL_CHAR_DOWN;
6518 case 0x167b: // (green steel)
6519 element = EL_STEEL_CHAR_BUTTON;
6522 case 0x167c: // (green steel)
6523 element = EL_STEEL_CHAR_PLUS;
6526 case 0x167d: // (green steel)
6527 element = EL_STEEL_CHAR_MINUS;
6530 case 0x167e: // (green steel)
6531 element = EL_STEEL_CHAR_APOSTROPHE;
6534 case 0x167f: // (green steel)
6535 element = EL_STEEL_CHAR_PARENLEFT;
6538 case 0x1680: // (green steel)
6539 element = EL_STEEL_CHAR_PARENRIGHT;
6542 case 0x1681: // gate (red)
6543 element = EL_EM_GATE_1;
6546 case 0x1682: // secret gate (red)
6547 element = EL_EM_GATE_1_GRAY;
6550 case 0x1683: // gate (yellow)
6551 element = EL_EM_GATE_2;
6554 case 0x1684: // secret gate (yellow)
6555 element = EL_EM_GATE_2_GRAY;
6558 case 0x1685: // gate (blue)
6559 element = EL_EM_GATE_4;
6562 case 0x1686: // secret gate (blue)
6563 element = EL_EM_GATE_4_GRAY;
6566 case 0x1687: // gate (green)
6567 element = EL_EM_GATE_3;
6570 case 0x1688: // secret gate (green)
6571 element = EL_EM_GATE_3_GRAY;
6574 case 0x1689: // gate (white)
6575 element = EL_DC_GATE_WHITE;
6578 case 0x168a: // secret gate (white)
6579 element = EL_DC_GATE_WHITE_GRAY;
6582 case 0x168b: // secret gate (no key)
6583 element = EL_DC_GATE_FAKE_GRAY;
6587 element = EL_ROBOT_WHEEL;
6591 element = EL_DC_TIMEGATE_SWITCH;
6595 element = EL_ACID_POOL_BOTTOM;
6599 element = EL_ACID_POOL_TOPLEFT;
6603 element = EL_ACID_POOL_TOPRIGHT;
6607 element = EL_ACID_POOL_BOTTOMLEFT;
6611 element = EL_ACID_POOL_BOTTOMRIGHT;
6615 element = EL_STEELWALL;
6619 element = EL_STEELWALL_SLIPPERY;
6622 case 0x1695: // steel wall (not round)
6623 element = EL_STEELWALL;
6626 case 0x1696: // steel wall (left)
6627 element = EL_DC_STEELWALL_1_LEFT;
6630 case 0x1697: // steel wall (bottom)
6631 element = EL_DC_STEELWALL_1_BOTTOM;
6634 case 0x1698: // steel wall (right)
6635 element = EL_DC_STEELWALL_1_RIGHT;
6638 case 0x1699: // steel wall (top)
6639 element = EL_DC_STEELWALL_1_TOP;
6642 case 0x169a: // steel wall (left/bottom)
6643 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6646 case 0x169b: // steel wall (right/bottom)
6647 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6650 case 0x169c: // steel wall (right/top)
6651 element = EL_DC_STEELWALL_1_TOPRIGHT;
6654 case 0x169d: // steel wall (left/top)
6655 element = EL_DC_STEELWALL_1_TOPLEFT;
6658 case 0x169e: // steel wall (right/bottom small)
6659 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6662 case 0x169f: // steel wall (left/bottom small)
6663 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6666 case 0x16a0: // steel wall (right/top small)
6667 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6670 case 0x16a1: // steel wall (left/top small)
6671 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6674 case 0x16a2: // steel wall (left/right)
6675 element = EL_DC_STEELWALL_1_VERTICAL;
6678 case 0x16a3: // steel wall (top/bottom)
6679 element = EL_DC_STEELWALL_1_HORIZONTAL;
6682 case 0x16a4: // steel wall 2 (left end)
6683 element = EL_DC_STEELWALL_2_LEFT;
6686 case 0x16a5: // steel wall 2 (right end)
6687 element = EL_DC_STEELWALL_2_RIGHT;
6690 case 0x16a6: // steel wall 2 (top end)
6691 element = EL_DC_STEELWALL_2_TOP;
6694 case 0x16a7: // steel wall 2 (bottom end)
6695 element = EL_DC_STEELWALL_2_BOTTOM;
6698 case 0x16a8: // steel wall 2 (left/right)
6699 element = EL_DC_STEELWALL_2_HORIZONTAL;
6702 case 0x16a9: // steel wall 2 (up/down)
6703 element = EL_DC_STEELWALL_2_VERTICAL;
6706 case 0x16aa: // steel wall 2 (mid)
6707 element = EL_DC_STEELWALL_2_MIDDLE;
6711 element = EL_SIGN_EXCLAMATION;
6715 element = EL_SIGN_RADIOACTIVITY;
6719 element = EL_SIGN_STOP;
6723 element = EL_SIGN_WHEELCHAIR;
6727 element = EL_SIGN_PARKING;
6731 element = EL_SIGN_NO_ENTRY;
6735 element = EL_SIGN_HEART;
6739 element = EL_SIGN_GIVE_WAY;
6743 element = EL_SIGN_ENTRY_FORBIDDEN;
6747 element = EL_SIGN_EMERGENCY_EXIT;
6751 element = EL_SIGN_YIN_YANG;
6755 element = EL_WALL_EMERALD;
6759 element = EL_WALL_DIAMOND;
6763 element = EL_WALL_PEARL;
6767 element = EL_WALL_CRYSTAL;
6771 element = EL_INVISIBLE_WALL;
6775 element = EL_INVISIBLE_STEELWALL;
6779 // EL_INVISIBLE_SAND
6782 element = EL_LIGHT_SWITCH;
6786 element = EL_ENVELOPE_1;
6790 if (element >= 0x0117 && element <= 0x036e) // (?)
6791 element = EL_DIAMOND;
6792 else if (element >= 0x042d && element <= 0x0684) // (?)
6793 element = EL_EMERALD;
6794 else if (element >= 0x157c && element <= 0x158b)
6796 else if (element >= 0x1590 && element <= 0x159f)
6797 element = EL_DC_LANDMINE;
6798 else if (element >= 0x16bc && element <= 0x16cb)
6799 element = EL_INVISIBLE_SAND;
6802 Warn("unknown Diamond Caves element 0x%04x", element);
6804 element = EL_UNKNOWN;
6809 return getMappedElement(element);
6812 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6814 byte header[DC_LEVEL_HEADER_SIZE];
6816 int envelope_header_pos = 62;
6817 int envelope_content_pos = 94;
6818 int level_name_pos = 251;
6819 int level_author_pos = 292;
6820 int envelope_header_len;
6821 int envelope_content_len;
6823 int level_author_len;
6825 int num_yamyam_contents;
6828 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6830 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6832 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6834 header[i * 2 + 0] = header_word >> 8;
6835 header[i * 2 + 1] = header_word & 0xff;
6838 // read some values from level header to check level decoding integrity
6839 fieldx = header[6] | (header[7] << 8);
6840 fieldy = header[8] | (header[9] << 8);
6841 num_yamyam_contents = header[60] | (header[61] << 8);
6843 // do some simple sanity checks to ensure that level was correctly decoded
6844 if (fieldx < 1 || fieldx > 256 ||
6845 fieldy < 1 || fieldy > 256 ||
6846 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6848 level->no_valid_file = TRUE;
6850 Warn("cannot decode level from stream -- using empty level");
6855 // maximum envelope header size is 31 bytes
6856 envelope_header_len = header[envelope_header_pos];
6857 // maximum envelope content size is 110 (156?) bytes
6858 envelope_content_len = header[envelope_content_pos];
6860 // maximum level title size is 40 bytes
6861 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6862 // maximum level author size is 30 (51?) bytes
6863 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6867 for (i = 0; i < envelope_header_len; i++)
6868 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6869 level->envelope[0].text[envelope_size++] =
6870 header[envelope_header_pos + 1 + i];
6872 if (envelope_header_len > 0 && envelope_content_len > 0)
6874 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6875 level->envelope[0].text[envelope_size++] = '\n';
6876 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6877 level->envelope[0].text[envelope_size++] = '\n';
6880 for (i = 0; i < envelope_content_len; i++)
6881 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6882 level->envelope[0].text[envelope_size++] =
6883 header[envelope_content_pos + 1 + i];
6885 level->envelope[0].text[envelope_size] = '\0';
6887 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6888 level->envelope[0].ysize = 10;
6889 level->envelope[0].autowrap = TRUE;
6890 level->envelope[0].centered = TRUE;
6892 for (i = 0; i < level_name_len; i++)
6893 level->name[i] = header[level_name_pos + 1 + i];
6894 level->name[level_name_len] = '\0';
6896 for (i = 0; i < level_author_len; i++)
6897 level->author[i] = header[level_author_pos + 1 + i];
6898 level->author[level_author_len] = '\0';
6900 num_yamyam_contents = header[60] | (header[61] << 8);
6901 level->num_yamyam_contents =
6902 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6904 for (i = 0; i < num_yamyam_contents; i++)
6906 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6908 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6909 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6911 if (i < MAX_ELEMENT_CONTENTS)
6912 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6916 fieldx = header[6] | (header[7] << 8);
6917 fieldy = header[8] | (header[9] << 8);
6918 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6919 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6921 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6923 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6924 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6926 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6927 level->field[x][y] = getMappedElement_DC(element_dc);
6930 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6931 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6932 level->field[x][y] = EL_PLAYER_1;
6934 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6935 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6936 level->field[x][y] = EL_PLAYER_2;
6938 level->gems_needed = header[18] | (header[19] << 8);
6940 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6941 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6942 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6943 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6944 level->score[SC_NUT] = header[28] | (header[29] << 8);
6945 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6946 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6947 level->score[SC_BUG] = header[34] | (header[35] << 8);
6948 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6949 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6950 level->score[SC_KEY] = header[40] | (header[41] << 8);
6951 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6953 level->time = header[44] | (header[45] << 8);
6955 level->amoeba_speed = header[46] | (header[47] << 8);
6956 level->time_light = header[48] | (header[49] << 8);
6957 level->time_timegate = header[50] | (header[51] << 8);
6958 level->time_wheel = header[52] | (header[53] << 8);
6959 level->time_magic_wall = header[54] | (header[55] << 8);
6960 level->extra_time = header[56] | (header[57] << 8);
6961 level->shield_normal_time = header[58] | (header[59] << 8);
6963 // shield and extra time elements do not have a score
6964 level->score[SC_SHIELD] = 0;
6965 level->extra_time_score = 0;
6967 // set time for normal and deadly shields to the same value
6968 level->shield_deadly_time = level->shield_normal_time;
6970 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6971 // can slip down from flat walls, like normal walls and steel walls
6972 level->em_slippery_gems = TRUE;
6974 // time score is counted for each 10 seconds left in Diamond Caves levels
6975 level->time_score_base = 10;
6978 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6979 struct LevelFileInfo *level_file_info,
6980 boolean level_info_only)
6982 char *filename = level_file_info->filename;
6984 int num_magic_bytes = 8;
6985 char magic_bytes[num_magic_bytes + 1];
6986 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6988 if (!(file = openFile(filename, MODE_READ)))
6990 level->no_valid_file = TRUE;
6992 if (!level_info_only)
6993 Warn("cannot read level '%s' -- using empty level", filename);
6998 // fseek(file, 0x0000, SEEK_SET);
7000 if (level_file_info->packed)
7002 // read "magic bytes" from start of file
7003 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7004 magic_bytes[0] = '\0';
7006 // check "magic bytes" for correct file format
7007 if (!strPrefix(magic_bytes, "DC2"))
7009 level->no_valid_file = TRUE;
7011 Warn("unknown DC level file '%s' -- using empty level", filename);
7016 if (strPrefix(magic_bytes, "DC2Win95") ||
7017 strPrefix(magic_bytes, "DC2Win98"))
7019 int position_first_level = 0x00fa;
7020 int extra_bytes = 4;
7023 // advance file stream to first level inside the level package
7024 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7026 // each block of level data is followed by block of non-level data
7027 num_levels_to_skip *= 2;
7029 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7030 while (num_levels_to_skip >= 0)
7032 // advance file stream to next level inside the level package
7033 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7035 level->no_valid_file = TRUE;
7037 Warn("cannot fseek in file '%s' -- using empty level", filename);
7042 // skip apparently unused extra bytes following each level
7043 ReadUnusedBytesFromFile(file, extra_bytes);
7045 // read size of next level in level package
7046 skip_bytes = getFile32BitLE(file);
7048 num_levels_to_skip--;
7053 level->no_valid_file = TRUE;
7055 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7061 LoadLevelFromFileStream_DC(file, level);
7067 // ----------------------------------------------------------------------------
7068 // functions for loading SB level
7069 // ----------------------------------------------------------------------------
7071 int getMappedElement_SB(int element_ascii, boolean use_ces)
7079 sb_element_mapping[] =
7081 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7082 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7083 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7084 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7085 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7086 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7087 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7088 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7095 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7096 if (element_ascii == sb_element_mapping[i].ascii)
7097 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7099 return EL_UNDEFINED;
7102 static void SetLevelSettings_SB(struct LevelInfo *level)
7106 level->use_step_counter = TRUE;
7109 level->score[SC_TIME_BONUS] = 0;
7110 level->time_score_base = 1;
7111 level->rate_time_over_score = TRUE;
7114 level->auto_exit_sokoban = TRUE;
7117 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7118 struct LevelFileInfo *level_file_info,
7119 boolean level_info_only)
7121 char *filename = level_file_info->filename;
7122 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7123 char last_comment[MAX_LINE_LEN];
7124 char level_name[MAX_LINE_LEN];
7127 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7128 boolean read_continued_line = FALSE;
7129 boolean reading_playfield = FALSE;
7130 boolean got_valid_playfield_line = FALSE;
7131 boolean invalid_playfield_char = FALSE;
7132 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7133 int file_level_nr = 0;
7134 int x = 0, y = 0; // initialized to make compilers happy
7136 last_comment[0] = '\0';
7137 level_name[0] = '\0';
7139 if (!(file = openFile(filename, MODE_READ)))
7141 level->no_valid_file = TRUE;
7143 if (!level_info_only)
7144 Warn("cannot read level '%s' -- using empty level", filename);
7149 while (!checkEndOfFile(file))
7151 // level successfully read, but next level may follow here
7152 if (!got_valid_playfield_line && reading_playfield)
7154 // read playfield from single level file -- skip remaining file
7155 if (!level_file_info->packed)
7158 if (file_level_nr >= num_levels_to_skip)
7163 last_comment[0] = '\0';
7164 level_name[0] = '\0';
7166 reading_playfield = FALSE;
7169 got_valid_playfield_line = FALSE;
7171 // read next line of input file
7172 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7175 // cut trailing line break (this can be newline and/or carriage return)
7176 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7177 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7180 // copy raw input line for later use (mainly debugging output)
7181 strcpy(line_raw, line);
7183 if (read_continued_line)
7185 // append new line to existing line, if there is enough space
7186 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7187 strcat(previous_line, line_ptr);
7189 strcpy(line, previous_line); // copy storage buffer to line
7191 read_continued_line = FALSE;
7194 // if the last character is '\', continue at next line
7195 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7197 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7198 strcpy(previous_line, line); // copy line to storage buffer
7200 read_continued_line = TRUE;
7206 if (line[0] == '\0')
7209 // extract comment text from comment line
7212 for (line_ptr = line; *line_ptr; line_ptr++)
7213 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7216 strcpy(last_comment, line_ptr);
7221 // extract level title text from line containing level title
7222 if (line[0] == '\'')
7224 strcpy(level_name, &line[1]);
7226 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7227 level_name[strlen(level_name) - 1] = '\0';
7232 // skip lines containing only spaces (or empty lines)
7233 for (line_ptr = line; *line_ptr; line_ptr++)
7234 if (*line_ptr != ' ')
7236 if (*line_ptr == '\0')
7239 // at this point, we have found a line containing part of a playfield
7241 got_valid_playfield_line = TRUE;
7243 if (!reading_playfield)
7245 reading_playfield = TRUE;
7246 invalid_playfield_char = FALSE;
7248 for (x = 0; x < MAX_LEV_FIELDX; x++)
7249 for (y = 0; y < MAX_LEV_FIELDY; y++)
7250 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7255 // start with topmost tile row
7259 // skip playfield line if larger row than allowed
7260 if (y >= MAX_LEV_FIELDY)
7263 // start with leftmost tile column
7266 // read playfield elements from line
7267 for (line_ptr = line; *line_ptr; line_ptr++)
7269 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7271 // stop parsing playfield line if larger column than allowed
7272 if (x >= MAX_LEV_FIELDX)
7275 if (mapped_sb_element == EL_UNDEFINED)
7277 invalid_playfield_char = TRUE;
7282 level->field[x][y] = mapped_sb_element;
7284 // continue with next tile column
7287 level->fieldx = MAX(x, level->fieldx);
7290 if (invalid_playfield_char)
7292 // if first playfield line, treat invalid lines as comment lines
7294 reading_playfield = FALSE;
7299 // continue with next tile row
7307 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7308 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7310 if (!reading_playfield)
7312 level->no_valid_file = TRUE;
7314 Warn("cannot read level '%s' -- using empty level", filename);
7319 if (*level_name != '\0')
7321 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7322 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7324 else if (*last_comment != '\0')
7326 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7327 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7331 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7334 // set all empty fields beyond the border walls to invisible steel wall
7335 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7337 if ((x == 0 || x == level->fieldx - 1 ||
7338 y == 0 || y == level->fieldy - 1) &&
7339 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7340 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7341 level->field, level->fieldx, level->fieldy);
7344 // set special level settings for Sokoban levels
7345 SetLevelSettings_SB(level);
7347 if (load_xsb_to_ces)
7349 // special global settings can now be set in level template
7350 level->use_custom_template = TRUE;
7355 // -------------------------------------------------------------------------
7356 // functions for handling native levels
7357 // -------------------------------------------------------------------------
7359 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7360 struct LevelFileInfo *level_file_info,
7361 boolean level_info_only)
7365 // determine position of requested level inside level package
7366 if (level_file_info->packed)
7367 pos = level_file_info->nr - leveldir_current->first_level;
7369 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7370 level->no_valid_file = TRUE;
7373 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7374 struct LevelFileInfo *level_file_info,
7375 boolean level_info_only)
7377 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7378 level->no_valid_file = TRUE;
7381 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7382 struct LevelFileInfo *level_file_info,
7383 boolean level_info_only)
7387 // determine position of requested level inside level package
7388 if (level_file_info->packed)
7389 pos = level_file_info->nr - leveldir_current->first_level;
7391 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7392 level->no_valid_file = TRUE;
7395 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7396 struct LevelFileInfo *level_file_info,
7397 boolean level_info_only)
7399 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7400 level->no_valid_file = TRUE;
7403 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7405 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7406 CopyNativeLevel_RND_to_BD(level);
7407 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7408 CopyNativeLevel_RND_to_EM(level);
7409 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7410 CopyNativeLevel_RND_to_SP(level);
7411 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7412 CopyNativeLevel_RND_to_MM(level);
7415 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7417 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7418 CopyNativeLevel_BD_to_RND(level);
7419 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7420 CopyNativeLevel_EM_to_RND(level);
7421 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7422 CopyNativeLevel_SP_to_RND(level);
7423 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7424 CopyNativeLevel_MM_to_RND(level);
7427 void SaveNativeLevel(struct LevelInfo *level)
7429 // saving native level files only supported for some game engines
7430 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7431 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7434 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7435 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7436 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7437 char *filename = getLevelFilenameFromBasename(basename);
7439 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7442 boolean success = FALSE;
7444 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7446 CopyNativeLevel_RND_to_BD(level);
7447 // CopyNativeTape_RND_to_BD(level);
7449 success = SaveNativeLevel_BD(filename);
7451 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7453 CopyNativeLevel_RND_to_SP(level);
7454 CopyNativeTape_RND_to_SP(level);
7456 success = SaveNativeLevel_SP(filename);
7460 Request("Native level file saved!", REQ_CONFIRM);
7462 Request("Failed to save native level file!", REQ_CONFIRM);
7466 // ----------------------------------------------------------------------------
7467 // functions for loading generic level
7468 // ----------------------------------------------------------------------------
7470 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7471 struct LevelFileInfo *level_file_info,
7472 boolean level_info_only)
7474 // always start with reliable default values
7475 setLevelInfoToDefaults(level, level_info_only, TRUE);
7477 switch (level_file_info->type)
7479 case LEVEL_FILE_TYPE_RND:
7480 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7483 case LEVEL_FILE_TYPE_BD:
7484 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7485 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7488 case LEVEL_FILE_TYPE_EM:
7489 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7490 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7493 case LEVEL_FILE_TYPE_SP:
7494 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7495 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7498 case LEVEL_FILE_TYPE_MM:
7499 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7500 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7503 case LEVEL_FILE_TYPE_DC:
7504 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7507 case LEVEL_FILE_TYPE_SB:
7508 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7512 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7516 // if level file is invalid, restore level structure to default values
7517 if (level->no_valid_file)
7518 setLevelInfoToDefaults(level, level_info_only, FALSE);
7520 if (check_special_flags("use_native_bd_game_engine"))
7521 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7523 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7524 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7526 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7527 CopyNativeLevel_Native_to_RND(level);
7530 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7532 static struct LevelFileInfo level_file_info;
7534 // always start with reliable default values
7535 setFileInfoToDefaults(&level_file_info);
7537 level_file_info.nr = 0; // unknown level number
7538 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7540 setString(&level_file_info.filename, filename);
7542 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7545 static void LoadLevel_InitVersion(struct LevelInfo *level)
7549 if (leveldir_current == NULL) // only when dumping level
7552 // all engine modifications also valid for levels which use latest engine
7553 if (level->game_version < VERSION_IDENT(3,2,0,5))
7555 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7556 level->time_score_base = 10;
7559 if (leveldir_current->latest_engine)
7561 // ---------- use latest game engine --------------------------------------
7563 /* For all levels which are forced to use the latest game engine version
7564 (normally all but user contributed, private and undefined levels), set
7565 the game engine version to the actual version; this allows for actual
7566 corrections in the game engine to take effect for existing, converted
7567 levels (from "classic" or other existing games) to make the emulation
7568 of the corresponding game more accurate, while (hopefully) not breaking
7569 existing levels created from other players. */
7571 level->game_version = GAME_VERSION_ACTUAL;
7573 /* Set special EM style gems behaviour: EM style gems slip down from
7574 normal, steel and growing wall. As this is a more fundamental change,
7575 it seems better to set the default behaviour to "off" (as it is more
7576 natural) and make it configurable in the level editor (as a property
7577 of gem style elements). Already existing converted levels (neither
7578 private nor contributed levels) are changed to the new behaviour. */
7580 if (level->file_version < FILE_VERSION_2_0)
7581 level->em_slippery_gems = TRUE;
7586 // ---------- use game engine the level was created with --------------------
7588 /* For all levels which are not forced to use the latest game engine
7589 version (normally user contributed, private and undefined levels),
7590 use the version of the game engine the levels were created for.
7592 Since 2.0.1, the game engine version is now directly stored
7593 in the level file (chunk "VERS"), so there is no need anymore
7594 to set the game version from the file version (except for old,
7595 pre-2.0 levels, where the game version is still taken from the
7596 file format version used to store the level -- see above). */
7598 // player was faster than enemies in 1.0.0 and before
7599 if (level->file_version == FILE_VERSION_1_0)
7600 for (i = 0; i < MAX_PLAYERS; i++)
7601 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7603 // default behaviour for EM style gems was "slippery" only in 2.0.1
7604 if (level->game_version == VERSION_IDENT(2,0,1,0))
7605 level->em_slippery_gems = TRUE;
7607 // springs could be pushed over pits before (pre-release version) 2.2.0
7608 if (level->game_version < VERSION_IDENT(2,2,0,0))
7609 level->use_spring_bug = TRUE;
7611 if (level->game_version < VERSION_IDENT(3,2,0,5))
7613 // time orb caused limited time in endless time levels before 3.2.0-5
7614 level->use_time_orb_bug = TRUE;
7616 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7617 level->block_snap_field = FALSE;
7619 // extra time score was same value as time left score before 3.2.0-5
7620 level->extra_time_score = level->score[SC_TIME_BONUS];
7623 if (level->game_version < VERSION_IDENT(3,2,0,7))
7625 // default behaviour for snapping was "not continuous" before 3.2.0-7
7626 level->continuous_snapping = FALSE;
7629 // only few elements were able to actively move into acid before 3.1.0
7630 // trigger settings did not exist before 3.1.0; set to default "any"
7631 if (level->game_version < VERSION_IDENT(3,1,0,0))
7633 // correct "can move into acid" settings (all zero in old levels)
7635 level->can_move_into_acid_bits = 0; // nothing can move into acid
7636 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7638 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7639 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7640 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7641 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7643 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7644 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7646 // correct trigger settings (stored as zero == "none" in old levels)
7648 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7650 int element = EL_CUSTOM_START + i;
7651 struct ElementInfo *ei = &element_info[element];
7653 for (j = 0; j < ei->num_change_pages; j++)
7655 struct ElementChangeInfo *change = &ei->change_page[j];
7657 change->trigger_player = CH_PLAYER_ANY;
7658 change->trigger_page = CH_PAGE_ANY;
7663 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7665 int element = EL_CUSTOM_256;
7666 struct ElementInfo *ei = &element_info[element];
7667 struct ElementChangeInfo *change = &ei->change_page[0];
7669 /* This is needed to fix a problem that was caused by a bugfix in function
7670 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7671 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7672 not replace walkable elements, but instead just placed the player on it,
7673 without placing the Sokoban field under the player). Unfortunately, this
7674 breaks "Snake Bite" style levels when the snake is halfway through a door
7675 that just closes (the snake head is still alive and can be moved in this
7676 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7677 player (without Sokoban element) which then gets killed as designed). */
7679 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7680 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7681 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7682 change->target_element = EL_PLAYER_1;
7685 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7686 if (level->game_version < VERSION_IDENT(3,2,5,0))
7688 /* This is needed to fix a problem that was caused by a bugfix in function
7689 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7690 corrects the behaviour when a custom element changes to another custom
7691 element with a higher element number that has change actions defined.
7692 Normally, only one change per frame is allowed for custom elements.
7693 Therefore, it is checked if a custom element already changed in the
7694 current frame; if it did, subsequent changes are suppressed.
7695 Unfortunately, this is only checked for element changes, but not for
7696 change actions, which are still executed. As the function above loops
7697 through all custom elements from lower to higher, an element change
7698 resulting in a lower CE number won't be checked again, while a target
7699 element with a higher number will also be checked, and potential change
7700 actions will get executed for this CE, too (which is wrong), while
7701 further changes are ignored (which is correct). As this bugfix breaks
7702 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7703 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7704 behaviour for existing levels and tapes that make use of this bug */
7706 level->use_action_after_change_bug = TRUE;
7709 // not centering level after relocating player was default only in 3.2.3
7710 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7711 level->shifted_relocation = TRUE;
7713 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7714 if (level->game_version < VERSION_IDENT(3,2,6,0))
7715 level->em_explodes_by_fire = TRUE;
7717 // levels were solved by the first player entering an exit up to 4.1.0.0
7718 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7719 level->solved_by_one_player = TRUE;
7721 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7722 if (level->game_version < VERSION_IDENT(4,1,1,1))
7723 level->use_life_bugs = TRUE;
7725 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7726 if (level->game_version < VERSION_IDENT(4,1,1,1))
7727 level->sb_objects_needed = FALSE;
7729 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7730 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7731 level->finish_dig_collect = FALSE;
7733 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7734 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7735 level->keep_walkable_ce = TRUE;
7738 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7740 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7743 // check if this level is (not) a Sokoban level
7744 for (y = 0; y < level->fieldy; y++)
7745 for (x = 0; x < level->fieldx; x++)
7746 if (!IS_SB_ELEMENT(Tile[x][y]))
7747 is_sokoban_level = FALSE;
7749 if (is_sokoban_level)
7751 // set special level settings for Sokoban levels
7752 SetLevelSettings_SB(level);
7756 static void LoadLevel_InitSettings(struct LevelInfo *level)
7758 // adjust level settings for (non-native) Sokoban-style levels
7759 LoadLevel_InitSettings_SB(level);
7761 // rename levels with title "nameless level" or if renaming is forced
7762 if (leveldir_current->empty_level_name != NULL &&
7763 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7764 leveldir_current->force_level_name))
7765 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7766 leveldir_current->empty_level_name, level_nr);
7769 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7773 // map elements that have changed in newer versions
7774 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7775 level->game_version);
7776 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7777 for (x = 0; x < 3; x++)
7778 for (y = 0; y < 3; y++)
7779 level->yamyam_content[i].e[x][y] =
7780 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7781 level->game_version);
7785 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7789 // map custom element change events that have changed in newer versions
7790 // (these following values were accidentally changed in version 3.0.1)
7791 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7792 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7794 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7796 int element = EL_CUSTOM_START + i;
7798 // order of checking and copying events to be mapped is important
7799 // (do not change the start and end value -- they are constant)
7800 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7802 if (HAS_CHANGE_EVENT(element, j - 2))
7804 SET_CHANGE_EVENT(element, j - 2, FALSE);
7805 SET_CHANGE_EVENT(element, j, TRUE);
7809 // order of checking and copying events to be mapped is important
7810 // (do not change the start and end value -- they are constant)
7811 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7813 if (HAS_CHANGE_EVENT(element, j - 1))
7815 SET_CHANGE_EVENT(element, j - 1, FALSE);
7816 SET_CHANGE_EVENT(element, j, TRUE);
7822 // initialize "can_change" field for old levels with only one change page
7823 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7825 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7827 int element = EL_CUSTOM_START + i;
7829 if (CAN_CHANGE(element))
7830 element_info[element].change->can_change = TRUE;
7834 // correct custom element values (for old levels without these options)
7835 if (level->game_version < VERSION_IDENT(3,1,1,0))
7837 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7839 int element = EL_CUSTOM_START + i;
7840 struct ElementInfo *ei = &element_info[element];
7842 if (ei->access_direction == MV_NO_DIRECTION)
7843 ei->access_direction = MV_ALL_DIRECTIONS;
7847 // correct custom element values (fix invalid values for all versions)
7850 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7852 int element = EL_CUSTOM_START + i;
7853 struct ElementInfo *ei = &element_info[element];
7855 for (j = 0; j < ei->num_change_pages; j++)
7857 struct ElementChangeInfo *change = &ei->change_page[j];
7859 if (change->trigger_player == CH_PLAYER_NONE)
7860 change->trigger_player = CH_PLAYER_ANY;
7862 if (change->trigger_side == CH_SIDE_NONE)
7863 change->trigger_side = CH_SIDE_ANY;
7868 // initialize "can_explode" field for old levels which did not store this
7869 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7870 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7872 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7874 int element = EL_CUSTOM_START + i;
7876 if (EXPLODES_1X1_OLD(element))
7877 element_info[element].explosion_type = EXPLODES_1X1;
7879 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7880 EXPLODES_SMASHED(element) ||
7881 EXPLODES_IMPACT(element)));
7885 // correct previously hard-coded move delay values for maze runner style
7886 if (level->game_version < VERSION_IDENT(3,1,1,0))
7888 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7890 int element = EL_CUSTOM_START + i;
7892 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7894 // previously hard-coded and therefore ignored
7895 element_info[element].move_delay_fixed = 9;
7896 element_info[element].move_delay_random = 0;
7901 // set some other uninitialized values of custom elements in older levels
7902 if (level->game_version < VERSION_IDENT(3,1,0,0))
7904 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7906 int element = EL_CUSTOM_START + i;
7908 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7910 element_info[element].explosion_delay = 17;
7911 element_info[element].ignition_delay = 8;
7915 // set mouse click change events to work for left/middle/right mouse button
7916 if (level->game_version < VERSION_IDENT(4,2,3,0))
7918 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7920 int element = EL_CUSTOM_START + i;
7921 struct ElementInfo *ei = &element_info[element];
7923 for (j = 0; j < ei->num_change_pages; j++)
7925 struct ElementChangeInfo *change = &ei->change_page[j];
7927 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7928 change->has_event[CE_PRESSED_BY_MOUSE] ||
7929 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7930 change->has_event[CE_MOUSE_PRESSED_ON_X])
7931 change->trigger_side = CH_SIDE_ANY;
7937 static void LoadLevel_InitElements(struct LevelInfo *level)
7939 LoadLevel_InitStandardElements(level);
7941 if (level->file_has_custom_elements)
7942 LoadLevel_InitCustomElements(level);
7944 // initialize element properties for level editor etc.
7945 InitElementPropertiesEngine(level->game_version);
7946 InitElementPropertiesGfxElement();
7949 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7953 // map elements that have changed in newer versions
7954 for (y = 0; y < level->fieldy; y++)
7955 for (x = 0; x < level->fieldx; x++)
7956 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7957 level->game_version);
7959 // clear unused playfield data (nicer if level gets resized in editor)
7960 for (x = 0; x < MAX_LEV_FIELDX; x++)
7961 for (y = 0; y < MAX_LEV_FIELDY; y++)
7962 if (x >= level->fieldx || y >= level->fieldy)
7963 level->field[x][y] = EL_EMPTY;
7965 // copy elements to runtime playfield array
7966 for (x = 0; x < MAX_LEV_FIELDX; x++)
7967 for (y = 0; y < MAX_LEV_FIELDY; y++)
7968 Tile[x][y] = level->field[x][y];
7970 // initialize level size variables for faster access
7971 lev_fieldx = level->fieldx;
7972 lev_fieldy = level->fieldy;
7974 // determine border element for this level
7975 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7976 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7981 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7983 struct LevelFileInfo *level_file_info = &level->file_info;
7985 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7986 CopyNativeLevel_RND_to_Native(level);
7989 static void LoadLevelTemplate_LoadAndInit(void)
7991 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7993 LoadLevel_InitVersion(&level_template);
7994 LoadLevel_InitElements(&level_template);
7995 LoadLevel_InitSettings(&level_template);
7997 ActivateLevelTemplate();
8000 void LoadLevelTemplate(int nr)
8002 if (!fileExists(getGlobalLevelTemplateFilename()))
8004 Warn("no level template found for this level");
8009 setLevelFileInfo(&level_template.file_info, nr);
8011 LoadLevelTemplate_LoadAndInit();
8014 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8016 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8018 LoadLevelTemplate_LoadAndInit();
8021 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8023 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8025 if (level.use_custom_template)
8027 if (network_level != NULL)
8028 LoadNetworkLevelTemplate(network_level);
8030 LoadLevelTemplate(-1);
8033 LoadLevel_InitVersion(&level);
8034 LoadLevel_InitElements(&level);
8035 LoadLevel_InitPlayfield(&level);
8036 LoadLevel_InitSettings(&level);
8038 LoadLevel_InitNativeEngines(&level);
8041 void LoadLevel(int nr)
8043 SetLevelSetInfo(leveldir_current->identifier, nr);
8045 setLevelFileInfo(&level.file_info, nr);
8047 LoadLevel_LoadAndInit(NULL);
8050 void LoadLevelInfoOnly(int nr)
8052 setLevelFileInfo(&level.file_info, nr);
8054 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8057 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8059 SetLevelSetInfo(network_level->leveldir_identifier,
8060 network_level->file_info.nr);
8062 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8064 LoadLevel_LoadAndInit(network_level);
8067 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8071 chunk_size += putFileVersion(file, level->file_version);
8072 chunk_size += putFileVersion(file, level->game_version);
8077 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8081 chunk_size += putFile16BitBE(file, level->creation_date.year);
8082 chunk_size += putFile8Bit(file, level->creation_date.month);
8083 chunk_size += putFile8Bit(file, level->creation_date.day);
8088 #if ENABLE_HISTORIC_CHUNKS
8089 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8093 putFile8Bit(file, level->fieldx);
8094 putFile8Bit(file, level->fieldy);
8096 putFile16BitBE(file, level->time);
8097 putFile16BitBE(file, level->gems_needed);
8099 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8100 putFile8Bit(file, level->name[i]);
8102 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8103 putFile8Bit(file, level->score[i]);
8105 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8106 for (y = 0; y < 3; y++)
8107 for (x = 0; x < 3; x++)
8108 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8109 level->yamyam_content[i].e[x][y]));
8110 putFile8Bit(file, level->amoeba_speed);
8111 putFile8Bit(file, level->time_magic_wall);
8112 putFile8Bit(file, level->time_wheel);
8113 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8114 level->amoeba_content));
8115 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8116 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8117 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8118 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8120 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8122 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8123 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8124 putFile32BitBE(file, level->can_move_into_acid_bits);
8125 putFile8Bit(file, level->dont_collide_with_bits);
8127 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8128 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8130 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8131 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8132 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8134 putFile8Bit(file, level->game_engine_type);
8136 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8140 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8145 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8146 chunk_size += putFile8Bit(file, level->name[i]);
8151 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8156 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8157 chunk_size += putFile8Bit(file, level->author[i]);
8162 #if ENABLE_HISTORIC_CHUNKS
8163 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8168 for (y = 0; y < level->fieldy; y++)
8169 for (x = 0; x < level->fieldx; x++)
8170 if (level->encoding_16bit_field)
8171 chunk_size += putFile16BitBE(file, level->field[x][y]);
8173 chunk_size += putFile8Bit(file, level->field[x][y]);
8179 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8184 for (y = 0; y < level->fieldy; y++)
8185 for (x = 0; x < level->fieldx; x++)
8186 chunk_size += putFile16BitBE(file, level->field[x][y]);
8191 #if ENABLE_HISTORIC_CHUNKS
8192 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8196 putFile8Bit(file, EL_YAMYAM);
8197 putFile8Bit(file, level->num_yamyam_contents);
8198 putFile8Bit(file, 0);
8199 putFile8Bit(file, 0);
8201 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8202 for (y = 0; y < 3; y++)
8203 for (x = 0; x < 3; x++)
8204 if (level->encoding_16bit_field)
8205 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8207 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8211 #if ENABLE_HISTORIC_CHUNKS
8212 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8215 int num_contents, content_xsize, content_ysize;
8216 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8218 if (element == EL_YAMYAM)
8220 num_contents = level->num_yamyam_contents;
8224 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8225 for (y = 0; y < 3; y++)
8226 for (x = 0; x < 3; x++)
8227 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8229 else if (element == EL_BD_AMOEBA)
8235 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8236 for (y = 0; y < 3; y++)
8237 for (x = 0; x < 3; x++)
8238 content_array[i][x][y] = EL_EMPTY;
8239 content_array[0][0][0] = level->amoeba_content;
8243 // chunk header already written -- write empty chunk data
8244 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8246 Warn("cannot save content for element '%d'", element);
8251 putFile16BitBE(file, element);
8252 putFile8Bit(file, num_contents);
8253 putFile8Bit(file, content_xsize);
8254 putFile8Bit(file, content_ysize);
8256 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8258 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8259 for (y = 0; y < 3; y++)
8260 for (x = 0; x < 3; x++)
8261 putFile16BitBE(file, content_array[i][x][y]);
8265 #if ENABLE_HISTORIC_CHUNKS
8266 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8268 int envelope_nr = element - EL_ENVELOPE_1;
8269 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8273 chunk_size += putFile16BitBE(file, element);
8274 chunk_size += putFile16BitBE(file, envelope_len);
8275 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8276 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8278 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8279 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8281 for (i = 0; i < envelope_len; i++)
8282 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8288 #if ENABLE_HISTORIC_CHUNKS
8289 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8290 int num_changed_custom_elements)
8294 putFile16BitBE(file, num_changed_custom_elements);
8296 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8298 int element = EL_CUSTOM_START + i;
8300 struct ElementInfo *ei = &element_info[element];
8302 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8304 if (check < num_changed_custom_elements)
8306 putFile16BitBE(file, element);
8307 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8314 if (check != num_changed_custom_elements) // should not happen
8315 Warn("inconsistent number of custom element properties");
8319 #if ENABLE_HISTORIC_CHUNKS
8320 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8321 int num_changed_custom_elements)
8325 putFile16BitBE(file, num_changed_custom_elements);
8327 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8329 int element = EL_CUSTOM_START + i;
8331 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8333 if (check < num_changed_custom_elements)
8335 putFile16BitBE(file, element);
8336 putFile16BitBE(file, element_info[element].change->target_element);
8343 if (check != num_changed_custom_elements) // should not happen
8344 Warn("inconsistent number of custom target elements");
8348 #if ENABLE_HISTORIC_CHUNKS
8349 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8350 int num_changed_custom_elements)
8352 int i, j, x, y, check = 0;
8354 putFile16BitBE(file, num_changed_custom_elements);
8356 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8358 int element = EL_CUSTOM_START + i;
8359 struct ElementInfo *ei = &element_info[element];
8361 if (ei->modified_settings)
8363 if (check < num_changed_custom_elements)
8365 putFile16BitBE(file, element);
8367 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8368 putFile8Bit(file, ei->description[j]);
8370 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8372 // some free bytes for future properties and padding
8373 WriteUnusedBytesToFile(file, 7);
8375 putFile8Bit(file, ei->use_gfx_element);
8376 putFile16BitBE(file, ei->gfx_element_initial);
8378 putFile8Bit(file, ei->collect_score_initial);
8379 putFile8Bit(file, ei->collect_count_initial);
8381 putFile16BitBE(file, ei->push_delay_fixed);
8382 putFile16BitBE(file, ei->push_delay_random);
8383 putFile16BitBE(file, ei->move_delay_fixed);
8384 putFile16BitBE(file, ei->move_delay_random);
8386 putFile16BitBE(file, ei->move_pattern);
8387 putFile8Bit(file, ei->move_direction_initial);
8388 putFile8Bit(file, ei->move_stepsize);
8390 for (y = 0; y < 3; y++)
8391 for (x = 0; x < 3; x++)
8392 putFile16BitBE(file, ei->content.e[x][y]);
8394 putFile32BitBE(file, ei->change->events);
8396 putFile16BitBE(file, ei->change->target_element);
8398 putFile16BitBE(file, ei->change->delay_fixed);
8399 putFile16BitBE(file, ei->change->delay_random);
8400 putFile16BitBE(file, ei->change->delay_frames);
8402 putFile16BitBE(file, ei->change->initial_trigger_element);
8404 putFile8Bit(file, ei->change->explode);
8405 putFile8Bit(file, ei->change->use_target_content);
8406 putFile8Bit(file, ei->change->only_if_complete);
8407 putFile8Bit(file, ei->change->use_random_replace);
8409 putFile8Bit(file, ei->change->random_percentage);
8410 putFile8Bit(file, ei->change->replace_when);
8412 for (y = 0; y < 3; y++)
8413 for (x = 0; x < 3; x++)
8414 putFile16BitBE(file, ei->change->content.e[x][y]);
8416 putFile8Bit(file, ei->slippery_type);
8418 // some free bytes for future properties and padding
8419 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8426 if (check != num_changed_custom_elements) // should not happen
8427 Warn("inconsistent number of custom element properties");
8431 #if ENABLE_HISTORIC_CHUNKS
8432 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8434 struct ElementInfo *ei = &element_info[element];
8437 // ---------- custom element base property values (96 bytes) ----------------
8439 putFile16BitBE(file, element);
8441 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8442 putFile8Bit(file, ei->description[i]);
8444 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8446 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8448 putFile8Bit(file, ei->num_change_pages);
8450 putFile16BitBE(file, ei->ce_value_fixed_initial);
8451 putFile16BitBE(file, ei->ce_value_random_initial);
8452 putFile8Bit(file, ei->use_last_ce_value);
8454 putFile8Bit(file, ei->use_gfx_element);
8455 putFile16BitBE(file, ei->gfx_element_initial);
8457 putFile8Bit(file, ei->collect_score_initial);
8458 putFile8Bit(file, ei->collect_count_initial);
8460 putFile8Bit(file, ei->drop_delay_fixed);
8461 putFile8Bit(file, ei->push_delay_fixed);
8462 putFile8Bit(file, ei->drop_delay_random);
8463 putFile8Bit(file, ei->push_delay_random);
8464 putFile16BitBE(file, ei->move_delay_fixed);
8465 putFile16BitBE(file, ei->move_delay_random);
8467 // bits 0 - 15 of "move_pattern" ...
8468 putFile16BitBE(file, ei->move_pattern & 0xffff);
8469 putFile8Bit(file, ei->move_direction_initial);
8470 putFile8Bit(file, ei->move_stepsize);
8472 putFile8Bit(file, ei->slippery_type);
8474 for (y = 0; y < 3; y++)
8475 for (x = 0; x < 3; x++)
8476 putFile16BitBE(file, ei->content.e[x][y]);
8478 putFile16BitBE(file, ei->move_enter_element);
8479 putFile16BitBE(file, ei->move_leave_element);
8480 putFile8Bit(file, ei->move_leave_type);
8482 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8483 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8485 putFile8Bit(file, ei->access_direction);
8487 putFile8Bit(file, ei->explosion_delay);
8488 putFile8Bit(file, ei->ignition_delay);
8489 putFile8Bit(file, ei->explosion_type);
8491 // some free bytes for future custom property values and padding
8492 WriteUnusedBytesToFile(file, 1);
8494 // ---------- change page property values (48 bytes) ------------------------
8496 for (i = 0; i < ei->num_change_pages; i++)
8498 struct ElementChangeInfo *change = &ei->change_page[i];
8499 unsigned int event_bits;
8501 // bits 0 - 31 of "has_event[]" ...
8503 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8504 if (change->has_event[j])
8505 event_bits |= (1u << j);
8506 putFile32BitBE(file, event_bits);
8508 putFile16BitBE(file, change->target_element);
8510 putFile16BitBE(file, change->delay_fixed);
8511 putFile16BitBE(file, change->delay_random);
8512 putFile16BitBE(file, change->delay_frames);
8514 putFile16BitBE(file, change->initial_trigger_element);
8516 putFile8Bit(file, change->explode);
8517 putFile8Bit(file, change->use_target_content);
8518 putFile8Bit(file, change->only_if_complete);
8519 putFile8Bit(file, change->use_random_replace);
8521 putFile8Bit(file, change->random_percentage);
8522 putFile8Bit(file, change->replace_when);
8524 for (y = 0; y < 3; y++)
8525 for (x = 0; x < 3; x++)
8526 putFile16BitBE(file, change->target_content.e[x][y]);
8528 putFile8Bit(file, change->can_change);
8530 putFile8Bit(file, change->trigger_side);
8532 putFile8Bit(file, change->trigger_player);
8533 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8534 log_2(change->trigger_page)));
8536 putFile8Bit(file, change->has_action);
8537 putFile8Bit(file, change->action_type);
8538 putFile8Bit(file, change->action_mode);
8539 putFile16BitBE(file, change->action_arg);
8541 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8543 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8544 if (change->has_event[j])
8545 event_bits |= (1u << (j - 32));
8546 putFile8Bit(file, event_bits);
8551 #if ENABLE_HISTORIC_CHUNKS
8552 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8554 struct ElementInfo *ei = &element_info[element];
8555 struct ElementGroupInfo *group = ei->group;
8558 putFile16BitBE(file, element);
8560 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8561 putFile8Bit(file, ei->description[i]);
8563 putFile8Bit(file, group->num_elements);
8565 putFile8Bit(file, ei->use_gfx_element);
8566 putFile16BitBE(file, ei->gfx_element_initial);
8568 putFile8Bit(file, group->choice_mode);
8570 // some free bytes for future values and padding
8571 WriteUnusedBytesToFile(file, 3);
8573 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8574 putFile16BitBE(file, group->element[i]);
8578 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8579 boolean write_element)
8581 int save_type = entry->save_type;
8582 int data_type = entry->data_type;
8583 int conf_type = entry->conf_type;
8584 int byte_mask = conf_type & CONF_MASK_BYTES;
8585 int element = entry->element;
8586 int default_value = entry->default_value;
8588 boolean modified = FALSE;
8590 if (byte_mask != CONF_MASK_MULTI_BYTES)
8592 void *value_ptr = entry->value;
8593 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8596 // check if any settings have been modified before saving them
8597 if (value != default_value)
8600 // do not save if explicitly told or if unmodified default settings
8601 if ((save_type == SAVE_CONF_NEVER) ||
8602 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8606 num_bytes += putFile16BitBE(file, element);
8608 num_bytes += putFile8Bit(file, conf_type);
8609 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8610 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8611 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8614 else if (data_type == TYPE_STRING)
8616 char *default_string = entry->default_string;
8617 char *string = (char *)(entry->value);
8618 int string_length = strlen(string);
8621 // check if any settings have been modified before saving them
8622 if (!strEqual(string, default_string))
8625 // do not save if explicitly told or if unmodified default settings
8626 if ((save_type == SAVE_CONF_NEVER) ||
8627 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8631 num_bytes += putFile16BitBE(file, element);
8633 num_bytes += putFile8Bit(file, conf_type);
8634 num_bytes += putFile16BitBE(file, string_length);
8636 for (i = 0; i < string_length; i++)
8637 num_bytes += putFile8Bit(file, string[i]);
8639 else if (data_type == TYPE_ELEMENT_LIST)
8641 int *element_array = (int *)(entry->value);
8642 int num_elements = *(int *)(entry->num_entities);
8645 // check if any settings have been modified before saving them
8646 for (i = 0; i < num_elements; i++)
8647 if (element_array[i] != default_value)
8650 // do not save if explicitly told or if unmodified default settings
8651 if ((save_type == SAVE_CONF_NEVER) ||
8652 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8656 num_bytes += putFile16BitBE(file, element);
8658 num_bytes += putFile8Bit(file, conf_type);
8659 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8661 for (i = 0; i < num_elements; i++)
8662 num_bytes += putFile16BitBE(file, element_array[i]);
8664 else if (data_type == TYPE_CONTENT_LIST)
8666 struct Content *content = (struct Content *)(entry->value);
8667 int num_contents = *(int *)(entry->num_entities);
8670 // check if any settings have been modified before saving them
8671 for (i = 0; i < num_contents; i++)
8672 for (y = 0; y < 3; y++)
8673 for (x = 0; x < 3; x++)
8674 if (content[i].e[x][y] != default_value)
8677 // do not save if explicitly told or if unmodified default settings
8678 if ((save_type == SAVE_CONF_NEVER) ||
8679 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8683 num_bytes += putFile16BitBE(file, element);
8685 num_bytes += putFile8Bit(file, conf_type);
8686 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8688 for (i = 0; i < num_contents; i++)
8689 for (y = 0; y < 3; y++)
8690 for (x = 0; x < 3; x++)
8691 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8697 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8702 li = *level; // copy level data into temporary buffer
8704 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8705 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8710 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8715 li = *level; // copy level data into temporary buffer
8717 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8718 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8723 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8725 int envelope_nr = element - EL_ENVELOPE_1;
8729 chunk_size += putFile16BitBE(file, element);
8731 // copy envelope data into temporary buffer
8732 xx_envelope = level->envelope[envelope_nr];
8734 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8735 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8740 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8742 struct ElementInfo *ei = &element_info[element];
8746 chunk_size += putFile16BitBE(file, element);
8748 xx_ei = *ei; // copy element data into temporary buffer
8750 // set default description string for this specific element
8751 strcpy(xx_default_description, getDefaultElementDescription(ei));
8753 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8754 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8756 for (i = 0; i < ei->num_change_pages; i++)
8758 struct ElementChangeInfo *change = &ei->change_page[i];
8760 xx_current_change_page = i;
8762 xx_change = *change; // copy change data into temporary buffer
8765 setEventBitsFromEventFlags(change);
8767 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8768 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8775 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8777 struct ElementInfo *ei = &element_info[element];
8778 struct ElementGroupInfo *group = ei->group;
8782 chunk_size += putFile16BitBE(file, element);
8784 xx_ei = *ei; // copy element data into temporary buffer
8785 xx_group = *group; // copy group data into temporary buffer
8787 // set default description string for this specific element
8788 strcpy(xx_default_description, getDefaultElementDescription(ei));
8790 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8791 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8796 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8798 struct ElementInfo *ei = &element_info[element];
8802 chunk_size += putFile16BitBE(file, element);
8804 xx_ei = *ei; // copy element data into temporary buffer
8806 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8807 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8812 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8813 boolean save_as_template)
8819 if (!(file = fopen(filename, MODE_WRITE)))
8821 Warn("cannot save level file '%s'", filename);
8826 level->file_version = FILE_VERSION_ACTUAL;
8827 level->game_version = GAME_VERSION_ACTUAL;
8829 level->creation_date = getCurrentDate();
8831 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8832 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8834 chunk_size = SaveLevel_VERS(NULL, level);
8835 putFileChunkBE(file, "VERS", chunk_size);
8836 SaveLevel_VERS(file, level);
8838 chunk_size = SaveLevel_DATE(NULL, level);
8839 putFileChunkBE(file, "DATE", chunk_size);
8840 SaveLevel_DATE(file, level);
8842 chunk_size = SaveLevel_NAME(NULL, level);
8843 putFileChunkBE(file, "NAME", chunk_size);
8844 SaveLevel_NAME(file, level);
8846 chunk_size = SaveLevel_AUTH(NULL, level);
8847 putFileChunkBE(file, "AUTH", chunk_size);
8848 SaveLevel_AUTH(file, level);
8850 chunk_size = SaveLevel_INFO(NULL, level);
8851 putFileChunkBE(file, "INFO", chunk_size);
8852 SaveLevel_INFO(file, level);
8854 chunk_size = SaveLevel_BODY(NULL, level);
8855 putFileChunkBE(file, "BODY", chunk_size);
8856 SaveLevel_BODY(file, level);
8858 chunk_size = SaveLevel_ELEM(NULL, level);
8859 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8861 putFileChunkBE(file, "ELEM", chunk_size);
8862 SaveLevel_ELEM(file, level);
8865 for (i = 0; i < NUM_ENVELOPES; i++)
8867 int element = EL_ENVELOPE_1 + i;
8869 chunk_size = SaveLevel_NOTE(NULL, level, element);
8870 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8872 putFileChunkBE(file, "NOTE", chunk_size);
8873 SaveLevel_NOTE(file, level, element);
8877 // if not using template level, check for non-default custom/group elements
8878 if (!level->use_custom_template || save_as_template)
8880 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8882 int element = EL_CUSTOM_START + i;
8884 chunk_size = SaveLevel_CUSX(NULL, level, element);
8885 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8887 putFileChunkBE(file, "CUSX", chunk_size);
8888 SaveLevel_CUSX(file, level, element);
8892 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8894 int element = EL_GROUP_START + i;
8896 chunk_size = SaveLevel_GRPX(NULL, level, element);
8897 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8899 putFileChunkBE(file, "GRPX", chunk_size);
8900 SaveLevel_GRPX(file, level, element);
8904 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8906 int element = GET_EMPTY_ELEMENT(i);
8908 chunk_size = SaveLevel_EMPX(NULL, level, element);
8909 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8911 putFileChunkBE(file, "EMPX", chunk_size);
8912 SaveLevel_EMPX(file, level, element);
8919 SetFilePermissions(filename, PERMS_PRIVATE);
8922 void SaveLevel(int nr)
8924 char *filename = getDefaultLevelFilename(nr);
8926 SaveLevelFromFilename(&level, filename, FALSE);
8929 void SaveLevelTemplate(void)
8931 char *filename = getLocalLevelTemplateFilename();
8933 SaveLevelFromFilename(&level, filename, TRUE);
8936 boolean SaveLevelChecked(int nr)
8938 char *filename = getDefaultLevelFilename(nr);
8939 boolean new_level = !fileExists(filename);
8940 boolean level_saved = FALSE;
8942 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8947 Request("Level saved!", REQ_CONFIRM);
8955 void DumpLevel(struct LevelInfo *level)
8957 if (level->no_level_file || level->no_valid_file)
8959 Warn("cannot dump -- no valid level file found");
8965 Print("Level xxx (file version %08d, game version %08d)\n",
8966 level->file_version, level->game_version);
8969 Print("Level author: '%s'\n", level->author);
8970 Print("Level title: '%s'\n", level->name);
8972 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8974 Print("Level time: %d seconds\n", level->time);
8975 Print("Gems needed: %d\n", level->gems_needed);
8977 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8978 Print("Time for wheel: %d seconds\n", level->time_wheel);
8979 Print("Time for light: %d seconds\n", level->time_light);
8980 Print("Time for timegate: %d seconds\n", level->time_timegate);
8982 Print("Amoeba speed: %d\n", level->amoeba_speed);
8985 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8986 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8987 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8988 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8989 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8990 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8996 for (i = 0; i < NUM_ENVELOPES; i++)
8998 char *text = level->envelope[i].text;
8999 int text_len = strlen(text);
9000 boolean has_text = FALSE;
9002 for (j = 0; j < text_len; j++)
9003 if (text[j] != ' ' && text[j] != '\n')
9009 Print("Envelope %d:\n'%s'\n", i + 1, text);
9017 void DumpLevels(void)
9019 static LevelDirTree *dumplevel_leveldir = NULL;
9021 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9022 global.dumplevel_leveldir);
9024 if (dumplevel_leveldir == NULL)
9025 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9027 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9028 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9029 Fail("no such level number: %d", global.dumplevel_level_nr);
9031 leveldir_current = dumplevel_leveldir;
9033 LoadLevel(global.dumplevel_level_nr);
9040 // ============================================================================
9041 // tape file functions
9042 // ============================================================================
9044 static void setTapeInfoToDefaults(void)
9048 // always start with reliable default values (empty tape)
9051 // default values (also for pre-1.2 tapes) with only the first player
9052 tape.player_participates[0] = TRUE;
9053 for (i = 1; i < MAX_PLAYERS; i++)
9054 tape.player_participates[i] = FALSE;
9056 // at least one (default: the first) player participates in every tape
9057 tape.num_participating_players = 1;
9059 tape.property_bits = TAPE_PROPERTY_NONE;
9061 tape.level_nr = level_nr;
9063 tape.changed = FALSE;
9064 tape.solved = FALSE;
9066 tape.recording = FALSE;
9067 tape.playing = FALSE;
9068 tape.pausing = FALSE;
9070 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9071 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9073 tape.no_info_chunk = TRUE;
9074 tape.no_valid_file = FALSE;
9077 static int getTapePosSize(struct TapeInfo *tape)
9079 int tape_pos_size = 0;
9081 if (tape->use_key_actions)
9082 tape_pos_size += tape->num_participating_players;
9084 if (tape->use_mouse_actions)
9085 tape_pos_size += 3; // x and y position and mouse button mask
9087 tape_pos_size += 1; // tape action delay value
9089 return tape_pos_size;
9092 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9094 tape->use_key_actions = FALSE;
9095 tape->use_mouse_actions = FALSE;
9097 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9098 tape->use_key_actions = TRUE;
9100 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9101 tape->use_mouse_actions = TRUE;
9104 static int getTapeActionValue(struct TapeInfo *tape)
9106 return (tape->use_key_actions &&
9107 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9108 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9109 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9110 TAPE_ACTIONS_DEFAULT);
9113 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9115 tape->file_version = getFileVersion(file);
9116 tape->game_version = getFileVersion(file);
9121 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9125 tape->random_seed = getFile32BitBE(file);
9126 tape->date = getFile32BitBE(file);
9127 tape->length = getFile32BitBE(file);
9129 // read header fields that are new since version 1.2
9130 if (tape->file_version >= FILE_VERSION_1_2)
9132 byte store_participating_players = getFile8Bit(file);
9135 // since version 1.2, tapes store which players participate in the tape
9136 tape->num_participating_players = 0;
9137 for (i = 0; i < MAX_PLAYERS; i++)
9139 tape->player_participates[i] = FALSE;
9141 if (store_participating_players & (1 << i))
9143 tape->player_participates[i] = TRUE;
9144 tape->num_participating_players++;
9148 setTapeActionFlags(tape, getFile8Bit(file));
9150 tape->property_bits = getFile8Bit(file);
9151 tape->solved = getFile8Bit(file);
9153 engine_version = getFileVersion(file);
9154 if (engine_version > 0)
9155 tape->engine_version = engine_version;
9157 tape->engine_version = tape->game_version;
9163 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9165 tape->scr_fieldx = getFile8Bit(file);
9166 tape->scr_fieldy = getFile8Bit(file);
9171 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9173 char *level_identifier = NULL;
9174 int level_identifier_size;
9177 tape->no_info_chunk = FALSE;
9179 level_identifier_size = getFile16BitBE(file);
9181 level_identifier = checked_malloc(level_identifier_size);
9183 for (i = 0; i < level_identifier_size; i++)
9184 level_identifier[i] = getFile8Bit(file);
9186 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9187 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9189 checked_free(level_identifier);
9191 tape->level_nr = getFile16BitBE(file);
9193 chunk_size = 2 + level_identifier_size + 2;
9198 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9201 int tape_pos_size = getTapePosSize(tape);
9202 int chunk_size_expected = tape_pos_size * tape->length;
9204 if (chunk_size_expected != chunk_size)
9206 ReadUnusedBytesFromFile(file, chunk_size);
9207 return chunk_size_expected;
9210 for (i = 0; i < tape->length; i++)
9212 if (i >= MAX_TAPE_LEN)
9214 Warn("tape truncated -- size exceeds maximum tape size %d",
9217 // tape too large; read and ignore remaining tape data from this chunk
9218 for (;i < tape->length; i++)
9219 ReadUnusedBytesFromFile(file, tape_pos_size);
9224 if (tape->use_key_actions)
9226 for (j = 0; j < MAX_PLAYERS; j++)
9228 tape->pos[i].action[j] = MV_NONE;
9230 if (tape->player_participates[j])
9231 tape->pos[i].action[j] = getFile8Bit(file);
9235 if (tape->use_mouse_actions)
9237 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9238 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9239 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9242 tape->pos[i].delay = getFile8Bit(file);
9244 if (tape->file_version == FILE_VERSION_1_0)
9246 // eliminate possible diagonal moves in old tapes
9247 // this is only for backward compatibility
9249 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9250 byte action = tape->pos[i].action[0];
9251 int k, num_moves = 0;
9253 for (k = 0; k < 4; k++)
9255 if (action & joy_dir[k])
9257 tape->pos[i + num_moves].action[0] = joy_dir[k];
9259 tape->pos[i + num_moves].delay = 0;
9268 tape->length += num_moves;
9271 else if (tape->file_version < FILE_VERSION_2_0)
9273 // convert pre-2.0 tapes to new tape format
9275 if (tape->pos[i].delay > 1)
9278 tape->pos[i + 1] = tape->pos[i];
9279 tape->pos[i + 1].delay = 1;
9282 for (j = 0; j < MAX_PLAYERS; j++)
9283 tape->pos[i].action[j] = MV_NONE;
9284 tape->pos[i].delay--;
9291 if (checkEndOfFile(file))
9295 if (i != tape->length)
9296 chunk_size = tape_pos_size * i;
9301 static void LoadTape_SokobanSolution(char *filename)
9304 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9306 if (!(file = openFile(filename, MODE_READ)))
9308 tape.no_valid_file = TRUE;
9313 while (!checkEndOfFile(file))
9315 unsigned char c = getByteFromFile(file);
9317 if (checkEndOfFile(file))
9324 tape.pos[tape.length].action[0] = MV_UP;
9325 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9331 tape.pos[tape.length].action[0] = MV_DOWN;
9332 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9338 tape.pos[tape.length].action[0] = MV_LEFT;
9339 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9345 tape.pos[tape.length].action[0] = MV_RIGHT;
9346 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9354 // ignore white-space characters
9358 tape.no_valid_file = TRUE;
9360 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9368 if (tape.no_valid_file)
9371 tape.length_frames = GetTapeLengthFrames();
9372 tape.length_seconds = GetTapeLengthSeconds();
9375 void LoadTapeFromFilename(char *filename)
9377 char cookie[MAX_LINE_LEN];
9378 char chunk_name[CHUNK_ID_LEN + 1];
9382 // always start with reliable default values
9383 setTapeInfoToDefaults();
9385 if (strSuffix(filename, ".sln"))
9387 LoadTape_SokobanSolution(filename);
9392 if (!(file = openFile(filename, MODE_READ)))
9394 tape.no_valid_file = TRUE;
9399 getFileChunkBE(file, chunk_name, NULL);
9400 if (strEqual(chunk_name, "RND1"))
9402 getFile32BitBE(file); // not used
9404 getFileChunkBE(file, chunk_name, NULL);
9405 if (!strEqual(chunk_name, "TAPE"))
9407 tape.no_valid_file = TRUE;
9409 Warn("unknown format of tape file '%s'", filename);
9416 else // check for pre-2.0 file format with cookie string
9418 strcpy(cookie, chunk_name);
9419 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9421 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9422 cookie[strlen(cookie) - 1] = '\0';
9424 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9426 tape.no_valid_file = TRUE;
9428 Warn("unknown format of tape file '%s'", filename);
9435 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9437 tape.no_valid_file = TRUE;
9439 Warn("unsupported version of tape file '%s'", filename);
9446 // pre-2.0 tape files have no game version, so use file version here
9447 tape.game_version = tape.file_version;
9450 if (tape.file_version < FILE_VERSION_1_2)
9452 // tape files from versions before 1.2.0 without chunk structure
9453 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9454 LoadTape_BODY(file, 2 * tape.length, &tape);
9462 int (*loader)(File *, int, struct TapeInfo *);
9466 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9467 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9468 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9469 { "INFO", -1, LoadTape_INFO },
9470 { "BODY", -1, LoadTape_BODY },
9474 while (getFileChunkBE(file, chunk_name, &chunk_size))
9478 while (chunk_info[i].name != NULL &&
9479 !strEqual(chunk_name, chunk_info[i].name))
9482 if (chunk_info[i].name == NULL)
9484 Warn("unknown chunk '%s' in tape file '%s'",
9485 chunk_name, filename);
9487 ReadUnusedBytesFromFile(file, chunk_size);
9489 else if (chunk_info[i].size != -1 &&
9490 chunk_info[i].size != chunk_size)
9492 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9493 chunk_size, chunk_name, filename);
9495 ReadUnusedBytesFromFile(file, chunk_size);
9499 // call function to load this tape chunk
9500 int chunk_size_expected =
9501 (chunk_info[i].loader)(file, chunk_size, &tape);
9503 // the size of some chunks cannot be checked before reading other
9504 // chunks first (like "HEAD" and "BODY") that contain some header
9505 // information, so check them here
9506 if (chunk_size_expected != chunk_size)
9508 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9509 chunk_size, chunk_name, filename);
9517 tape.length_frames = GetTapeLengthFrames();
9518 tape.length_seconds = GetTapeLengthSeconds();
9521 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9523 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9525 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9526 tape.engine_version);
9530 void LoadTape(int nr)
9532 char *filename = getTapeFilename(nr);
9534 LoadTapeFromFilename(filename);
9537 void LoadSolutionTape(int nr)
9539 char *filename = getSolutionTapeFilename(nr);
9541 LoadTapeFromFilename(filename);
9543 if (TAPE_IS_EMPTY(tape))
9545 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9546 level.native_bd_level->replay != NULL)
9547 CopyNativeTape_BD_to_RND(&level);
9548 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9549 level.native_sp_level->demo.is_available)
9550 CopyNativeTape_SP_to_RND(&level);
9554 void LoadScoreTape(char *score_tape_basename, int nr)
9556 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9558 LoadTapeFromFilename(filename);
9561 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9563 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9565 LoadTapeFromFilename(filename);
9568 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9570 // chunk required for team mode tapes with non-default screen size
9571 return (tape->num_participating_players > 1 &&
9572 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9573 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9576 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9578 putFileVersion(file, tape->file_version);
9579 putFileVersion(file, tape->game_version);
9582 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9585 byte store_participating_players = 0;
9587 // set bits for participating players for compact storage
9588 for (i = 0; i < MAX_PLAYERS; i++)
9589 if (tape->player_participates[i])
9590 store_participating_players |= (1 << i);
9592 putFile32BitBE(file, tape->random_seed);
9593 putFile32BitBE(file, tape->date);
9594 putFile32BitBE(file, tape->length);
9596 putFile8Bit(file, store_participating_players);
9598 putFile8Bit(file, getTapeActionValue(tape));
9600 putFile8Bit(file, tape->property_bits);
9601 putFile8Bit(file, tape->solved);
9603 putFileVersion(file, tape->engine_version);
9606 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9608 putFile8Bit(file, tape->scr_fieldx);
9609 putFile8Bit(file, tape->scr_fieldy);
9612 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9614 int level_identifier_size = strlen(tape->level_identifier) + 1;
9617 putFile16BitBE(file, level_identifier_size);
9619 for (i = 0; i < level_identifier_size; i++)
9620 putFile8Bit(file, tape->level_identifier[i]);
9622 putFile16BitBE(file, tape->level_nr);
9625 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9629 for (i = 0; i < tape->length; i++)
9631 if (tape->use_key_actions)
9633 for (j = 0; j < MAX_PLAYERS; j++)
9634 if (tape->player_participates[j])
9635 putFile8Bit(file, tape->pos[i].action[j]);
9638 if (tape->use_mouse_actions)
9640 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9641 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9642 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9645 putFile8Bit(file, tape->pos[i].delay);
9649 void SaveTapeToFilename(char *filename)
9653 int info_chunk_size;
9654 int body_chunk_size;
9656 if (!(file = fopen(filename, MODE_WRITE)))
9658 Warn("cannot save level recording file '%s'", filename);
9663 tape_pos_size = getTapePosSize(&tape);
9665 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9666 body_chunk_size = tape_pos_size * tape.length;
9668 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9669 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9671 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9672 SaveTape_VERS(file, &tape);
9674 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9675 SaveTape_HEAD(file, &tape);
9677 if (checkSaveTape_SCRN(&tape))
9679 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9680 SaveTape_SCRN(file, &tape);
9683 putFileChunkBE(file, "INFO", info_chunk_size);
9684 SaveTape_INFO(file, &tape);
9686 putFileChunkBE(file, "BODY", body_chunk_size);
9687 SaveTape_BODY(file, &tape);
9691 SetFilePermissions(filename, PERMS_PRIVATE);
9694 static void SaveTapeExt(char *filename)
9698 tape.file_version = FILE_VERSION_ACTUAL;
9699 tape.game_version = GAME_VERSION_ACTUAL;
9701 tape.num_participating_players = 0;
9703 // count number of participating players
9704 for (i = 0; i < MAX_PLAYERS; i++)
9705 if (tape.player_participates[i])
9706 tape.num_participating_players++;
9708 SaveTapeToFilename(filename);
9710 tape.changed = FALSE;
9713 void SaveTape(int nr)
9715 char *filename = getTapeFilename(nr);
9717 InitTapeDirectory(leveldir_current->subdir);
9719 SaveTapeExt(filename);
9722 void SaveScoreTape(int nr)
9724 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9726 // used instead of "leveldir_current->subdir" (for network games)
9727 InitScoreTapeDirectory(levelset.identifier, nr);
9729 SaveTapeExt(filename);
9732 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9733 unsigned int req_state_added)
9735 char *filename = getTapeFilename(nr);
9736 boolean new_tape = !fileExists(filename);
9737 boolean tape_saved = FALSE;
9739 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9744 Request(msg_saved, REQ_CONFIRM | req_state_added);
9752 boolean SaveTapeChecked(int nr)
9754 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9757 boolean SaveTapeChecked_LevelSolved(int nr)
9759 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9760 "Level solved! Tape saved!", REQ_STAY_OPEN);
9763 void DumpTape(struct TapeInfo *tape)
9765 int tape_frame_counter;
9768 if (tape->no_valid_file)
9770 Warn("cannot dump -- no valid tape file found");
9777 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9778 tape->level_nr, tape->file_version, tape->game_version);
9779 Print(" (effective engine version %08d)\n",
9780 tape->engine_version);
9781 Print("Level series identifier: '%s'\n", tape->level_identifier);
9783 Print("Solution tape: %s\n",
9784 tape->solved ? "yes" :
9785 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9787 Print("Special tape properties: ");
9788 if (tape->property_bits == TAPE_PROPERTY_NONE)
9790 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9791 Print("[em_random_bug]");
9792 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9793 Print("[game_speed]");
9794 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9796 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9797 Print("[single_step]");
9798 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9799 Print("[snapshot]");
9800 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9801 Print("[replayed]");
9802 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9803 Print("[tas_keys]");
9804 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9805 Print("[small_graphics]");
9808 int year2 = tape->date / 10000;
9809 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9810 int month_index_raw = (tape->date / 100) % 100;
9811 int month_index = month_index_raw % 12; // prevent invalid index
9812 int month = month_index + 1;
9813 int day = tape->date % 100;
9815 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9819 tape_frame_counter = 0;
9821 for (i = 0; i < tape->length; i++)
9823 if (i >= MAX_TAPE_LEN)
9828 for (j = 0; j < MAX_PLAYERS; j++)
9830 if (tape->player_participates[j])
9832 int action = tape->pos[i].action[j];
9834 Print("%d:%02x ", j, action);
9835 Print("[%c%c%c%c|%c%c] - ",
9836 (action & JOY_LEFT ? '<' : ' '),
9837 (action & JOY_RIGHT ? '>' : ' '),
9838 (action & JOY_UP ? '^' : ' '),
9839 (action & JOY_DOWN ? 'v' : ' '),
9840 (action & JOY_BUTTON_1 ? '1' : ' '),
9841 (action & JOY_BUTTON_2 ? '2' : ' '));
9845 Print("(%03d) ", tape->pos[i].delay);
9846 Print("[%05d]\n", tape_frame_counter);
9848 tape_frame_counter += tape->pos[i].delay;
9854 void DumpTapes(void)
9856 static LevelDirTree *dumptape_leveldir = NULL;
9858 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9859 global.dumptape_leveldir);
9861 if (dumptape_leveldir == NULL)
9862 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9864 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9865 global.dumptape_level_nr > dumptape_leveldir->last_level)
9866 Fail("no such level number: %d", global.dumptape_level_nr);
9868 leveldir_current = dumptape_leveldir;
9870 if (options.mytapes)
9871 LoadTape(global.dumptape_level_nr);
9873 LoadSolutionTape(global.dumptape_level_nr);
9881 // ============================================================================
9882 // score file functions
9883 // ============================================================================
9885 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9889 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9891 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9892 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9893 scores->entry[i].score = 0;
9894 scores->entry[i].time = 0;
9896 scores->entry[i].id = -1;
9897 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9898 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9899 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9900 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9901 strcpy(scores->entry[i].country_code, "??");
9904 scores->num_entries = 0;
9905 scores->last_added = -1;
9906 scores->last_added_local = -1;
9908 scores->updated = FALSE;
9909 scores->uploaded = FALSE;
9910 scores->tape_downloaded = FALSE;
9911 scores->force_last_added = FALSE;
9913 // The following values are intentionally not reset here:
9917 // - continue_playing
9918 // - continue_on_return
9921 static void setScoreInfoToDefaults(void)
9923 setScoreInfoToDefaultsExt(&scores);
9926 static void setServerScoreInfoToDefaults(void)
9928 setScoreInfoToDefaultsExt(&server_scores);
9931 static void LoadScore_OLD(int nr)
9934 char *filename = getScoreFilename(nr);
9935 char cookie[MAX_LINE_LEN];
9936 char line[MAX_LINE_LEN];
9940 if (!(file = fopen(filename, MODE_READ)))
9943 // check file identifier
9944 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9946 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9947 cookie[strlen(cookie) - 1] = '\0';
9949 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9951 Warn("unknown format of score file '%s'", filename);
9958 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9960 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9961 Warn("fscanf() failed; %s", strerror(errno));
9963 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9966 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9967 line[strlen(line) - 1] = '\0';
9969 for (line_ptr = line; *line_ptr; line_ptr++)
9971 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9973 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9974 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9983 static void ConvertScore_OLD(void)
9985 // only convert score to time for levels that rate playing time over score
9986 if (!level.rate_time_over_score)
9989 // convert old score to playing time for score-less levels (like Supaplex)
9990 int time_final_max = 999;
9993 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9995 int score = scores.entry[i].score;
9997 if (score > 0 && score < time_final_max)
9998 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10002 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10004 scores->file_version = getFileVersion(file);
10005 scores->game_version = getFileVersion(file);
10010 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10012 char *level_identifier = NULL;
10013 int level_identifier_size;
10016 level_identifier_size = getFile16BitBE(file);
10018 level_identifier = checked_malloc(level_identifier_size);
10020 for (i = 0; i < level_identifier_size; i++)
10021 level_identifier[i] = getFile8Bit(file);
10023 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10024 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10026 checked_free(level_identifier);
10028 scores->level_nr = getFile16BitBE(file);
10029 scores->num_entries = getFile16BitBE(file);
10031 chunk_size = 2 + level_identifier_size + 2 + 2;
10036 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10040 for (i = 0; i < scores->num_entries; i++)
10042 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10043 scores->entry[i].name[j] = getFile8Bit(file);
10045 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10048 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10053 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10057 for (i = 0; i < scores->num_entries; i++)
10058 scores->entry[i].score = getFile16BitBE(file);
10060 chunk_size = scores->num_entries * 2;
10065 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10069 for (i = 0; i < scores->num_entries; i++)
10070 scores->entry[i].score = getFile32BitBE(file);
10072 chunk_size = scores->num_entries * 4;
10077 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10081 for (i = 0; i < scores->num_entries; i++)
10082 scores->entry[i].time = getFile32BitBE(file);
10084 chunk_size = scores->num_entries * 4;
10089 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10093 for (i = 0; i < scores->num_entries; i++)
10095 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10096 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10098 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10101 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10106 void LoadScore(int nr)
10108 char *filename = getScoreFilename(nr);
10109 char cookie[MAX_LINE_LEN];
10110 char chunk_name[CHUNK_ID_LEN + 1];
10112 boolean old_score_file_format = FALSE;
10115 // always start with reliable default values
10116 setScoreInfoToDefaults();
10118 if (!(file = openFile(filename, MODE_READ)))
10121 getFileChunkBE(file, chunk_name, NULL);
10122 if (strEqual(chunk_name, "RND1"))
10124 getFile32BitBE(file); // not used
10126 getFileChunkBE(file, chunk_name, NULL);
10127 if (!strEqual(chunk_name, "SCOR"))
10129 Warn("unknown format of score file '%s'", filename);
10136 else // check for old file format with cookie string
10138 strcpy(cookie, chunk_name);
10139 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10141 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10142 cookie[strlen(cookie) - 1] = '\0';
10144 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10146 Warn("unknown format of score file '%s'", filename);
10153 old_score_file_format = TRUE;
10156 if (old_score_file_format)
10158 // score files from versions before 4.2.4.0 without chunk structure
10161 // convert score to time, if possible (mainly for Supaplex levels)
10162 ConvertScore_OLD();
10170 int (*loader)(File *, int, struct ScoreInfo *);
10174 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10175 { "INFO", -1, LoadScore_INFO },
10176 { "NAME", -1, LoadScore_NAME },
10177 { "SCOR", -1, LoadScore_SCOR },
10178 { "SC4R", -1, LoadScore_SC4R },
10179 { "TIME", -1, LoadScore_TIME },
10180 { "TAPE", -1, LoadScore_TAPE },
10185 while (getFileChunkBE(file, chunk_name, &chunk_size))
10189 while (chunk_info[i].name != NULL &&
10190 !strEqual(chunk_name, chunk_info[i].name))
10193 if (chunk_info[i].name == NULL)
10195 Warn("unknown chunk '%s' in score file '%s'",
10196 chunk_name, filename);
10198 ReadUnusedBytesFromFile(file, chunk_size);
10200 else if (chunk_info[i].size != -1 &&
10201 chunk_info[i].size != chunk_size)
10203 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10204 chunk_size, chunk_name, filename);
10206 ReadUnusedBytesFromFile(file, chunk_size);
10210 // call function to load this score chunk
10211 int chunk_size_expected =
10212 (chunk_info[i].loader)(file, chunk_size, &scores);
10214 // the size of some chunks cannot be checked before reading other
10215 // chunks first (like "HEAD" and "BODY") that contain some header
10216 // information, so check them here
10217 if (chunk_size_expected != chunk_size)
10219 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10220 chunk_size, chunk_name, filename);
10229 #if ENABLE_HISTORIC_CHUNKS
10230 void SaveScore_OLD(int nr)
10233 char *filename = getScoreFilename(nr);
10236 // used instead of "leveldir_current->subdir" (for network games)
10237 InitScoreDirectory(levelset.identifier);
10239 if (!(file = fopen(filename, MODE_WRITE)))
10241 Warn("cannot save score for level %d", nr);
10246 fprintf(file, "%s\n\n", SCORE_COOKIE);
10248 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10249 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10253 SetFilePermissions(filename, PERMS_PRIVATE);
10257 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10259 putFileVersion(file, scores->file_version);
10260 putFileVersion(file, scores->game_version);
10263 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10265 int level_identifier_size = strlen(scores->level_identifier) + 1;
10268 putFile16BitBE(file, level_identifier_size);
10270 for (i = 0; i < level_identifier_size; i++)
10271 putFile8Bit(file, scores->level_identifier[i]);
10273 putFile16BitBE(file, scores->level_nr);
10274 putFile16BitBE(file, scores->num_entries);
10277 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10281 for (i = 0; i < scores->num_entries; i++)
10283 int name_size = strlen(scores->entry[i].name);
10285 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10286 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10290 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10294 for (i = 0; i < scores->num_entries; i++)
10295 putFile16BitBE(file, scores->entry[i].score);
10298 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10302 for (i = 0; i < scores->num_entries; i++)
10303 putFile32BitBE(file, scores->entry[i].score);
10306 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10310 for (i = 0; i < scores->num_entries; i++)
10311 putFile32BitBE(file, scores->entry[i].time);
10314 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10318 for (i = 0; i < scores->num_entries; i++)
10320 int size = strlen(scores->entry[i].tape_basename);
10322 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10323 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10327 static void SaveScoreToFilename(char *filename)
10330 int info_chunk_size;
10331 int name_chunk_size;
10332 int scor_chunk_size;
10333 int sc4r_chunk_size;
10334 int time_chunk_size;
10335 int tape_chunk_size;
10336 boolean has_large_score_values;
10339 if (!(file = fopen(filename, MODE_WRITE)))
10341 Warn("cannot save score file '%s'", filename);
10346 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10347 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10348 scor_chunk_size = scores.num_entries * 2;
10349 sc4r_chunk_size = scores.num_entries * 4;
10350 time_chunk_size = scores.num_entries * 4;
10351 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10353 has_large_score_values = FALSE;
10354 for (i = 0; i < scores.num_entries; i++)
10355 if (scores.entry[i].score > 0xffff)
10356 has_large_score_values = TRUE;
10358 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10359 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10361 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10362 SaveScore_VERS(file, &scores);
10364 putFileChunkBE(file, "INFO", info_chunk_size);
10365 SaveScore_INFO(file, &scores);
10367 putFileChunkBE(file, "NAME", name_chunk_size);
10368 SaveScore_NAME(file, &scores);
10370 if (has_large_score_values)
10372 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10373 SaveScore_SC4R(file, &scores);
10377 putFileChunkBE(file, "SCOR", scor_chunk_size);
10378 SaveScore_SCOR(file, &scores);
10381 putFileChunkBE(file, "TIME", time_chunk_size);
10382 SaveScore_TIME(file, &scores);
10384 putFileChunkBE(file, "TAPE", tape_chunk_size);
10385 SaveScore_TAPE(file, &scores);
10389 SetFilePermissions(filename, PERMS_PRIVATE);
10392 void SaveScore(int nr)
10394 char *filename = getScoreFilename(nr);
10397 // used instead of "leveldir_current->subdir" (for network games)
10398 InitScoreDirectory(levelset.identifier);
10400 scores.file_version = FILE_VERSION_ACTUAL;
10401 scores.game_version = GAME_VERSION_ACTUAL;
10403 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10404 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10405 scores.level_nr = level_nr;
10407 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10408 if (scores.entry[i].score == 0 &&
10409 scores.entry[i].time == 0 &&
10410 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10413 scores.num_entries = i;
10415 if (scores.num_entries == 0)
10418 SaveScoreToFilename(filename);
10421 static void LoadServerScoreFromCache(int nr)
10423 struct ScoreEntry score_entry;
10432 { &score_entry.score, FALSE, 0 },
10433 { &score_entry.time, FALSE, 0 },
10434 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10435 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10436 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10437 { &score_entry.id, FALSE, 0 },
10438 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10439 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10440 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10441 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10445 char *filename = getScoreCacheFilename(nr);
10446 SetupFileHash *score_hash = loadSetupFileHash(filename);
10449 server_scores.num_entries = 0;
10451 if (score_hash == NULL)
10454 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10456 score_entry = server_scores.entry[i];
10458 for (j = 0; score_mapping[j].value != NULL; j++)
10462 sprintf(token, "%02d.%d", i, j);
10464 char *value = getHashEntry(score_hash, token);
10469 if (score_mapping[j].is_string)
10471 char *score_value = (char *)score_mapping[j].value;
10472 int value_size = score_mapping[j].string_size;
10474 strncpy(score_value, value, value_size);
10475 score_value[value_size] = '\0';
10479 int *score_value = (int *)score_mapping[j].value;
10481 *score_value = atoi(value);
10484 server_scores.num_entries = i + 1;
10487 server_scores.entry[i] = score_entry;
10490 freeSetupFileHash(score_hash);
10493 void LoadServerScore(int nr, boolean download_score)
10495 if (!setup.use_api_server)
10498 // always start with reliable default values
10499 setServerScoreInfoToDefaults();
10501 // 1st step: load server scores from cache file (which may not exist)
10502 // (this should prevent reading it while the thread is writing to it)
10503 LoadServerScoreFromCache(nr);
10505 if (download_score && runtime.use_api_server)
10507 // 2nd step: download server scores from score server to cache file
10508 // (as thread, as it might time out if the server is not reachable)
10509 ApiGetScoreAsThread(nr);
10513 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10515 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10517 // if score tape not uploaded, ask for uploading missing tapes later
10518 if (!setup.has_remaining_tapes)
10519 setup.ask_for_remaining_tapes = TRUE;
10521 setup.provide_uploading_tapes = TRUE;
10522 setup.has_remaining_tapes = TRUE;
10524 SaveSetup_ServerSetup();
10527 void SaveServerScore(int nr, boolean tape_saved)
10529 if (!runtime.use_api_server)
10531 PrepareScoreTapesForUpload(leveldir_current->subdir);
10536 ApiAddScoreAsThread(nr, tape_saved, NULL);
10539 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10540 char *score_tape_filename)
10542 if (!runtime.use_api_server)
10545 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10548 void LoadLocalAndServerScore(int nr, boolean download_score)
10550 int last_added_local = scores.last_added_local;
10551 boolean force_last_added = scores.force_last_added;
10553 // needed if only showing server scores
10554 setScoreInfoToDefaults();
10556 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10559 // restore last added local score entry (before merging server scores)
10560 scores.last_added = scores.last_added_local = last_added_local;
10562 if (setup.use_api_server &&
10563 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10565 // load server scores from cache file and trigger update from server
10566 LoadServerScore(nr, download_score);
10568 // merge local scores with scores from server
10569 MergeServerScore();
10572 if (force_last_added)
10573 scores.force_last_added = force_last_added;
10577 // ============================================================================
10578 // setup file functions
10579 // ============================================================================
10581 #define TOKEN_STR_PLAYER_PREFIX "player_"
10584 static struct TokenInfo global_setup_tokens[] =
10588 &setup.player_name, "player_name"
10592 &setup.multiple_users, "multiple_users"
10596 &setup.sound, "sound"
10600 &setup.sound_loops, "repeating_sound_loops"
10604 &setup.sound_music, "background_music"
10608 &setup.sound_simple, "simple_sound_effects"
10612 &setup.toons, "toons"
10616 &setup.global_animations, "global_animations"
10620 &setup.scroll_delay, "scroll_delay"
10624 &setup.forced_scroll_delay, "forced_scroll_delay"
10628 &setup.scroll_delay_value, "scroll_delay_value"
10632 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10636 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10640 &setup.fade_screens, "fade_screens"
10644 &setup.autorecord, "automatic_tape_recording"
10648 &setup.autorecord_after_replay, "autorecord_after_replay"
10652 &setup.auto_pause_on_start, "auto_pause_on_start"
10656 &setup.show_titlescreen, "show_titlescreen"
10660 &setup.quick_doors, "quick_doors"
10664 &setup.team_mode, "team_mode"
10668 &setup.handicap, "handicap"
10672 &setup.skip_levels, "skip_levels"
10675 TYPE_SWITCH_3_STATES,
10676 &setup.allow_skipping_levels, "allow_skipping_levels"
10680 &setup.increment_levels, "increment_levels"
10684 &setup.auto_play_next_level, "auto_play_next_level"
10688 &setup.count_score_after_game, "count_score_after_game"
10692 &setup.show_scores_after_game, "show_scores_after_game"
10696 &setup.time_limit, "time_limit"
10700 &setup.fullscreen, "fullscreen"
10704 &setup.window_scaling_percent, "window_scaling_percent"
10708 &setup.window_scaling_quality, "window_scaling_quality"
10712 &setup.screen_rendering_mode, "screen_rendering_mode"
10716 &setup.vsync_mode, "vsync_mode"
10720 &setup.ask_on_escape, "ask_on_escape"
10724 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10728 &setup.ask_on_game_over, "ask_on_game_over"
10732 &setup.ask_on_quit_game, "ask_on_quit_game"
10736 &setup.ask_on_quit_program, "ask_on_quit_program"
10740 &setup.quick_switch, "quick_player_switch"
10744 &setup.input_on_focus, "input_on_focus"
10748 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10752 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10756 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10760 &setup.game_speed_extended, "game_speed_extended"
10764 &setup.game_frame_delay, "game_frame_delay"
10768 &setup.default_game_engine_type, "default_game_engine_type"
10772 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10776 &setup.bd_skip_hatching, "bd_skip_hatching"
10780 &setup.bd_scroll_delay, "bd_scroll_delay"
10784 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10787 TYPE_SWITCH_3_STATES,
10788 &setup.bd_smooth_movements, "bd_smooth_movements"
10791 TYPE_SWITCH_3_STATES,
10792 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10795 TYPE_SWITCH_3_STATES,
10796 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10799 TYPE_SWITCH_3_STATES,
10800 &setup.bd_skip_falling_sounds, "bd_skip_falling_sounds"
10804 &setup.bd_palette_c64, "bd_palette_c64"
10808 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10812 &setup.bd_palette_atari, "bd_palette_atari"
10816 &setup.bd_default_color_type, "bd_default_color_type"
10820 &setup.bd_random_colors, "bd_random_colors"
10824 &setup.sp_show_border_elements, "sp_show_border_elements"
10828 &setup.small_game_graphics, "small_game_graphics"
10832 &setup.show_load_save_buttons, "show_load_save_buttons"
10836 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10840 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10844 &setup.graphics_set, "graphics_set"
10848 &setup.sounds_set, "sounds_set"
10852 &setup.music_set, "music_set"
10855 TYPE_SWITCH_3_STATES,
10856 &setup.override_level_graphics, "override_level_graphics"
10859 TYPE_SWITCH_3_STATES,
10860 &setup.override_level_sounds, "override_level_sounds"
10863 TYPE_SWITCH_3_STATES,
10864 &setup.override_level_music, "override_level_music"
10868 &setup.volume_simple, "volume_simple"
10872 &setup.volume_loops, "volume_loops"
10876 &setup.volume_music, "volume_music"
10880 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10884 &setup.network_mode, "network_mode"
10888 &setup.network_player_nr, "network_player"
10892 &setup.network_server_hostname, "network_server_hostname"
10896 &setup.touch.control_type, "touch.control_type"
10900 &setup.touch.move_distance, "touch.move_distance"
10904 &setup.touch.drop_distance, "touch.drop_distance"
10908 &setup.touch.transparency, "touch.transparency"
10912 &setup.touch.draw_outlined, "touch.draw_outlined"
10916 &setup.touch.draw_pressed, "touch.draw_pressed"
10920 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10924 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10928 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10932 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10936 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10940 static struct TokenInfo auto_setup_tokens[] =
10944 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10948 static struct TokenInfo server_setup_tokens[] =
10952 &setup.player_uuid, "player_uuid"
10956 &setup.player_version, "player_version"
10960 &setup.use_api_server, TEST_PREFIX "use_api_server"
10964 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10968 &setup.api_server_password, TEST_PREFIX "api_server_password"
10972 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10976 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10980 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10984 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10988 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10992 static struct TokenInfo editor_setup_tokens[] =
10996 &setup.editor.el_classic, "editor.el_classic"
11000 &setup.editor.el_custom, "editor.el_custom"
11004 &setup.editor.el_user_defined, "editor.el_user_defined"
11008 &setup.editor.el_dynamic, "editor.el_dynamic"
11012 &setup.editor.el_headlines, "editor.el_headlines"
11016 &setup.editor.show_element_token, "editor.show_element_token"
11020 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11024 static struct TokenInfo editor_cascade_setup_tokens[] =
11028 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11032 &setup.editor_cascade.el_bdx, "editor.cascade.el_bdx"
11036 &setup.editor_cascade.el_bdx_effects, "editor.cascade.el_bdx_effects"
11040 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11044 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11048 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11052 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11056 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11060 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11064 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11068 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11072 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11076 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11080 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11084 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11088 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11092 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11096 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11100 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11104 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11108 static struct TokenInfo shortcut_setup_tokens[] =
11112 &setup.shortcut.save_game, "shortcut.save_game"
11116 &setup.shortcut.load_game, "shortcut.load_game"
11120 &setup.shortcut.restart_game, "shortcut.restart_game"
11124 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11128 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11132 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11136 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11140 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11144 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11148 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11152 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11156 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11160 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11164 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11168 &setup.shortcut.tape_record, "shortcut.tape_record"
11172 &setup.shortcut.tape_play, "shortcut.tape_play"
11176 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11180 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11184 &setup.shortcut.sound_music, "shortcut.sound_music"
11188 &setup.shortcut.snap_left, "shortcut.snap_left"
11192 &setup.shortcut.snap_right, "shortcut.snap_right"
11196 &setup.shortcut.snap_up, "shortcut.snap_up"
11200 &setup.shortcut.snap_down, "shortcut.snap_down"
11204 &setup.shortcut.speed_fast, "shortcut.speed_fast"
11208 &setup.shortcut.speed_slow, "shortcut.speed_slow"
11212 static struct SetupInputInfo setup_input;
11213 static struct TokenInfo player_setup_tokens[] =
11217 &setup_input.use_joystick, ".use_joystick"
11221 &setup_input.joy.device_name, ".joy.device_name"
11225 &setup_input.joy.xleft, ".joy.xleft"
11229 &setup_input.joy.xmiddle, ".joy.xmiddle"
11233 &setup_input.joy.xright, ".joy.xright"
11237 &setup_input.joy.yupper, ".joy.yupper"
11241 &setup_input.joy.ymiddle, ".joy.ymiddle"
11245 &setup_input.joy.ylower, ".joy.ylower"
11249 &setup_input.joy.snap, ".joy.snap_field"
11253 &setup_input.joy.drop, ".joy.place_bomb"
11257 &setup_input.key.left, ".key.move_left"
11261 &setup_input.key.right, ".key.move_right"
11265 &setup_input.key.up, ".key.move_up"
11269 &setup_input.key.down, ".key.move_down"
11273 &setup_input.key.snap, ".key.snap_field"
11277 &setup_input.key.drop, ".key.place_bomb"
11281 static struct TokenInfo system_setup_tokens[] =
11285 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11289 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11293 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11297 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11301 static struct TokenInfo internal_setup_tokens[] =
11305 &setup.internal.program_title, "program_title"
11309 &setup.internal.program_version, "program_version"
11313 &setup.internal.program_author, "program_author"
11317 &setup.internal.program_email, "program_email"
11321 &setup.internal.program_website, "program_website"
11325 &setup.internal.program_copyright, "program_copyright"
11329 &setup.internal.program_company, "program_company"
11333 &setup.internal.program_icon_file, "program_icon_file"
11337 &setup.internal.default_graphics_set, "default_graphics_set"
11341 &setup.internal.default_sounds_set, "default_sounds_set"
11345 &setup.internal.default_music_set, "default_music_set"
11349 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11353 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11357 &setup.internal.fallback_music_file, "fallback_music_file"
11361 &setup.internal.default_level_series, "default_level_series"
11365 &setup.internal.default_window_width, "default_window_width"
11369 &setup.internal.default_window_height, "default_window_height"
11373 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11377 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11381 &setup.internal.create_user_levelset, "create_user_levelset"
11385 &setup.internal.info_screens_from_main, "info_screens_from_main"
11389 &setup.internal.menu_game, "menu_game"
11393 &setup.internal.menu_engines, "menu_engines"
11397 &setup.internal.menu_editor, "menu_editor"
11401 &setup.internal.menu_graphics, "menu_graphics"
11405 &setup.internal.menu_sound, "menu_sound"
11409 &setup.internal.menu_artwork, "menu_artwork"
11413 &setup.internal.menu_input, "menu_input"
11417 &setup.internal.menu_touch, "menu_touch"
11421 &setup.internal.menu_shortcuts, "menu_shortcuts"
11425 &setup.internal.menu_exit, "menu_exit"
11429 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11433 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11437 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11441 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11445 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11449 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11453 &setup.internal.menu_shortcuts_speed, "menu_shortcuts_speed"
11457 &setup.internal.info_title, "info_title"
11461 &setup.internal.info_elements, "info_elements"
11465 &setup.internal.info_music, "info_music"
11469 &setup.internal.info_credits, "info_credits"
11473 &setup.internal.info_program, "info_program"
11477 &setup.internal.info_version, "info_version"
11481 &setup.internal.info_levelset, "info_levelset"
11485 &setup.internal.info_exit, "info_exit"
11489 static struct TokenInfo debug_setup_tokens[] =
11493 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11497 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11501 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11505 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11509 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11513 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11517 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11521 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11525 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11529 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11533 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11537 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11541 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11545 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11549 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11553 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11557 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11561 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11565 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11569 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11573 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11576 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11580 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11583 TYPE_SWITCH_3_STATES,
11584 &setup.debug.xsn_mode, "debug.xsn_mode"
11588 &setup.debug.xsn_percent, "debug.xsn_percent"
11592 static struct TokenInfo options_setup_tokens[] =
11596 &setup.options.verbose, "options.verbose"
11600 &setup.options.debug, "options.debug"
11604 &setup.options.debug_mode, "options.debug_mode"
11608 static void setSetupInfoToDefaults(struct SetupInfo *si)
11612 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11614 si->multiple_users = TRUE;
11617 si->sound_loops = TRUE;
11618 si->sound_music = TRUE;
11619 si->sound_simple = TRUE;
11621 si->global_animations = TRUE;
11622 si->scroll_delay = TRUE;
11623 si->forced_scroll_delay = FALSE;
11624 si->scroll_delay_value = STD_SCROLL_DELAY;
11625 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11626 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11627 si->fade_screens = TRUE;
11628 si->autorecord = TRUE;
11629 si->autorecord_after_replay = TRUE;
11630 si->auto_pause_on_start = FALSE;
11631 si->show_titlescreen = TRUE;
11632 si->quick_doors = FALSE;
11633 si->team_mode = FALSE;
11634 si->handicap = TRUE;
11635 si->skip_levels = TRUE;
11636 si->allow_skipping_levels = STATE_ASK;
11637 si->increment_levels = TRUE;
11638 si->auto_play_next_level = TRUE;
11639 si->count_score_after_game = TRUE;
11640 si->show_scores_after_game = TRUE;
11641 si->time_limit = TRUE;
11642 si->fullscreen = FALSE;
11643 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11644 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11645 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11646 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11647 si->ask_on_escape = TRUE;
11648 si->ask_on_escape_editor = TRUE;
11649 si->ask_on_game_over = TRUE;
11650 si->ask_on_quit_game = TRUE;
11651 si->ask_on_quit_program = TRUE;
11652 si->quick_switch = FALSE;
11653 si->input_on_focus = FALSE;
11654 si->prefer_aga_graphics = TRUE;
11655 si->prefer_lowpass_sounds = FALSE;
11656 si->prefer_extra_panel_items = TRUE;
11657 si->game_speed_extended = FALSE;
11658 si->game_frame_delay = GAME_FRAME_DELAY;
11659 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11660 si->bd_skip_uncovering = FALSE;
11661 si->bd_skip_hatching = FALSE;
11662 si->bd_scroll_delay = TRUE;
11663 si->bd_show_invisible_outbox = FALSE;
11664 si->bd_smooth_movements = STATE_AUTO;
11665 si->bd_pushing_graphics = STATE_TRUE;
11666 si->bd_up_down_graphics = STATE_TRUE;
11667 si->bd_skip_falling_sounds = STATE_AUTO;
11668 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11669 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11670 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11671 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11672 si->bd_random_colors = FALSE;
11673 si->sp_show_border_elements = FALSE;
11674 si->small_game_graphics = FALSE;
11675 si->show_load_save_buttons = FALSE;
11676 si->show_undo_redo_buttons = FALSE;
11677 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11679 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11680 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11681 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11683 si->override_level_graphics = STATE_FALSE;
11684 si->override_level_sounds = STATE_FALSE;
11685 si->override_level_music = STATE_FALSE;
11687 si->volume_simple = 100; // percent
11688 si->volume_loops = 100; // percent
11689 si->volume_music = 100; // percent
11690 si->audio_sample_rate_44100 = FALSE;
11692 si->network_mode = FALSE;
11693 si->network_player_nr = 0; // first player
11694 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11696 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11697 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11698 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11699 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11700 si->touch.draw_outlined = TRUE;
11701 si->touch.draw_pressed = TRUE;
11703 for (i = 0; i < 2; i++)
11705 char *default_grid_button[6][2] =
11711 { "111222", " vv " },
11712 { "111222", " vv " }
11714 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11715 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11716 int min_xsize = MIN(6, grid_xsize);
11717 int min_ysize = MIN(6, grid_ysize);
11718 int startx = grid_xsize - min_xsize;
11719 int starty = grid_ysize - min_ysize;
11722 // virtual buttons grid can only be set to defaults if video is initialized
11723 // (this will be repeated if virtual buttons are not loaded from setup file)
11724 if (video.initialized)
11726 si->touch.grid_xsize[i] = grid_xsize;
11727 si->touch.grid_ysize[i] = grid_ysize;
11731 si->touch.grid_xsize[i] = -1;
11732 si->touch.grid_ysize[i] = -1;
11735 for (x = 0; x < MAX_GRID_XSIZE; x++)
11736 for (y = 0; y < MAX_GRID_YSIZE; y++)
11737 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11739 for (x = 0; x < min_xsize; x++)
11740 for (y = 0; y < min_ysize; y++)
11741 si->touch.grid_button[i][x][starty + y] =
11742 default_grid_button[y][0][x];
11744 for (x = 0; x < min_xsize; x++)
11745 for (y = 0; y < min_ysize; y++)
11746 si->touch.grid_button[i][startx + x][starty + y] =
11747 default_grid_button[y][1][x];
11750 si->touch.grid_initialized = video.initialized;
11752 si->touch.overlay_buttons = FALSE;
11754 si->editor.el_boulderdash = TRUE;
11755 si->editor.el_boulderdash_native = TRUE;
11756 si->editor.el_boulderdash_effects = TRUE;
11757 si->editor.el_emerald_mine = TRUE;
11758 si->editor.el_emerald_mine_club = TRUE;
11759 si->editor.el_more = TRUE;
11760 si->editor.el_sokoban = TRUE;
11761 si->editor.el_supaplex = TRUE;
11762 si->editor.el_diamond_caves = TRUE;
11763 si->editor.el_dx_boulderdash = TRUE;
11765 si->editor.el_mirror_magic = TRUE;
11766 si->editor.el_deflektor = TRUE;
11768 si->editor.el_chars = TRUE;
11769 si->editor.el_steel_chars = TRUE;
11771 si->editor.el_classic = TRUE;
11772 si->editor.el_custom = TRUE;
11774 si->editor.el_user_defined = FALSE;
11775 si->editor.el_dynamic = TRUE;
11777 si->editor.el_headlines = TRUE;
11779 si->editor.show_element_token = FALSE;
11781 si->editor.show_read_only_warning = TRUE;
11783 si->editor.use_template_for_new_levels = TRUE;
11785 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11786 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11787 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11788 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11789 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11791 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11792 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11793 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11794 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11795 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11797 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11798 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11799 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11800 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11801 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11802 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11804 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11805 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11806 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11808 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11809 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11810 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11811 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11813 si->shortcut.speed_fast = DEFAULT_KEY_SPEED_FAST;
11814 si->shortcut.speed_slow = DEFAULT_KEY_SPEED_SLOW;
11816 for (i = 0; i < MAX_PLAYERS; i++)
11818 si->input[i].use_joystick = FALSE;
11819 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11820 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11821 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11822 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11823 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11824 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11825 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11826 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11827 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11828 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11829 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11830 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11831 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11832 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11833 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11836 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11837 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11838 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11839 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11841 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11842 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11843 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11844 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11845 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11846 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11847 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11849 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11851 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11852 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11853 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11855 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11856 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11857 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11859 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11860 si->internal.choose_from_top_leveldir = FALSE;
11861 si->internal.show_scaling_in_title = TRUE;
11862 si->internal.create_user_levelset = TRUE;
11863 si->internal.info_screens_from_main = FALSE;
11865 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11866 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11868 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11869 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11870 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11871 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11872 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11873 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11874 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11875 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11876 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11877 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11879 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11880 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11881 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11882 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11883 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11884 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11885 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11886 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11887 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11888 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11890 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11891 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11893 si->debug.show_frames_per_second = FALSE;
11895 si->debug.xsn_mode = STATE_AUTO;
11896 si->debug.xsn_percent = 0;
11898 si->options.verbose = FALSE;
11899 si->options.debug = FALSE;
11900 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11902 #if defined(PLATFORM_ANDROID)
11903 si->fullscreen = TRUE;
11904 si->touch.overlay_buttons = TRUE;
11907 setHideSetupEntry(&setup.debug.xsn_mode);
11910 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11912 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11915 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11917 si->player_uuid = NULL; // (will be set later)
11918 si->player_version = 1; // (will be set later)
11920 si->use_api_server = TRUE;
11921 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11922 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11923 si->ask_for_uploading_tapes = TRUE;
11924 si->ask_for_remaining_tapes = FALSE;
11925 si->provide_uploading_tapes = TRUE;
11926 si->ask_for_using_api_server = TRUE;
11927 si->has_remaining_tapes = FALSE;
11930 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11932 si->editor_cascade.el_bd = TRUE;
11933 si->editor_cascade.el_bdx = TRUE;
11934 si->editor_cascade.el_bdx_effects = FALSE;
11935 si->editor_cascade.el_em = TRUE;
11936 si->editor_cascade.el_emc = TRUE;
11937 si->editor_cascade.el_rnd = TRUE;
11938 si->editor_cascade.el_sb = TRUE;
11939 si->editor_cascade.el_sp = TRUE;
11940 si->editor_cascade.el_dc = TRUE;
11941 si->editor_cascade.el_dx = TRUE;
11943 si->editor_cascade.el_mm = TRUE;
11944 si->editor_cascade.el_df = TRUE;
11946 si->editor_cascade.el_chars = FALSE;
11947 si->editor_cascade.el_steel_chars = FALSE;
11948 si->editor_cascade.el_ce = FALSE;
11949 si->editor_cascade.el_ge = FALSE;
11950 si->editor_cascade.el_es = FALSE;
11951 si->editor_cascade.el_ref = FALSE;
11952 si->editor_cascade.el_user = FALSE;
11953 si->editor_cascade.el_dynamic = FALSE;
11956 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11958 static char *getHideSetupToken(void *setup_value)
11960 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11962 if (setup_value != NULL)
11963 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11965 return hide_setup_token;
11968 void setHideSetupEntry(void *setup_value)
11970 char *hide_setup_token = getHideSetupToken(setup_value);
11972 if (hide_setup_hash == NULL)
11973 hide_setup_hash = newSetupFileHash();
11975 if (setup_value != NULL)
11976 setHashEntry(hide_setup_hash, hide_setup_token, "");
11979 void removeHideSetupEntry(void *setup_value)
11981 char *hide_setup_token = getHideSetupToken(setup_value);
11983 if (setup_value != NULL)
11984 removeHashEntry(hide_setup_hash, hide_setup_token);
11987 boolean hideSetupEntry(void *setup_value)
11989 char *hide_setup_token = getHideSetupToken(setup_value);
11991 return (setup_value != NULL &&
11992 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11995 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11996 struct TokenInfo *token_info,
11997 int token_nr, char *token_text)
11999 char *token_hide_text = getStringCat2(token_text, ".hide");
12000 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12002 // set the value of this setup option in the setup option structure
12003 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12005 // check if this setup option should be hidden in the setup menu
12006 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12007 setHideSetupEntry(token_info[token_nr].value);
12009 free(token_hide_text);
12012 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12013 struct TokenInfo *token_info,
12016 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12017 token_info[token_nr].text);
12020 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12024 if (!setup_file_hash)
12027 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12028 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12030 setup.touch.grid_initialized = TRUE;
12031 for (i = 0; i < 2; i++)
12033 int grid_xsize = setup.touch.grid_xsize[i];
12034 int grid_ysize = setup.touch.grid_ysize[i];
12037 // if virtual buttons are not loaded from setup file, repeat initializing
12038 // virtual buttons grid with default values later when video is initialized
12039 if (grid_xsize == -1 ||
12042 setup.touch.grid_initialized = FALSE;
12047 for (y = 0; y < grid_ysize; y++)
12049 char token_string[MAX_LINE_LEN];
12051 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12053 char *value_string = getHashEntry(setup_file_hash, token_string);
12055 if (value_string == NULL)
12058 for (x = 0; x < grid_xsize; x++)
12060 char c = value_string[x];
12062 setup.touch.grid_button[i][x][y] =
12063 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12068 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12069 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12071 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12072 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12074 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12078 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12080 setup_input = setup.input[pnr];
12081 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12083 char full_token[100];
12085 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12086 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12089 setup.input[pnr] = setup_input;
12092 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12093 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12095 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12096 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12098 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12099 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12101 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12102 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12104 setHideRelatedSetupEntries();
12107 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12111 if (!setup_file_hash)
12114 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12115 setSetupInfo(auto_setup_tokens, i,
12116 getHashEntry(setup_file_hash,
12117 auto_setup_tokens[i].text));
12120 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12124 if (!setup_file_hash)
12127 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12128 setSetupInfo(server_setup_tokens, i,
12129 getHashEntry(setup_file_hash,
12130 server_setup_tokens[i].text));
12133 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12137 if (!setup_file_hash)
12140 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12141 setSetupInfo(editor_cascade_setup_tokens, i,
12142 getHashEntry(setup_file_hash,
12143 editor_cascade_setup_tokens[i].text));
12146 void LoadUserNames(void)
12148 int last_user_nr = user.nr;
12151 if (global.user_names != NULL)
12153 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12154 checked_free(global.user_names[i]);
12156 checked_free(global.user_names);
12159 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12161 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12165 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12167 if (setup_file_hash)
12169 char *player_name = getHashEntry(setup_file_hash, "player_name");
12171 global.user_names[i] = getFixedUserName(player_name);
12173 freeSetupFileHash(setup_file_hash);
12176 if (global.user_names[i] == NULL)
12177 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12180 user.nr = last_user_nr;
12183 void LoadSetupFromFilename(char *filename)
12185 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12187 if (setup_file_hash)
12189 decodeSetupFileHash_Default(setup_file_hash);
12191 freeSetupFileHash(setup_file_hash);
12195 Debug("setup", "using default setup values");
12199 static void LoadSetup_SpecialPostProcessing(void)
12201 char *player_name_new;
12203 // needed to work around problems with fixed length strings
12204 player_name_new = getFixedUserName(setup.player_name);
12205 free(setup.player_name);
12206 setup.player_name = player_name_new;
12208 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12209 if (setup.scroll_delay == FALSE)
12211 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12212 setup.scroll_delay = TRUE; // now always "on"
12215 // make sure that scroll delay value stays inside valid range
12216 setup.scroll_delay_value =
12217 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12220 void LoadSetup_Default(void)
12224 // always start with reliable default values
12225 setSetupInfoToDefaults(&setup);
12227 // try to load setup values from default setup file
12228 filename = getDefaultSetupFilename();
12230 if (fileExists(filename))
12231 LoadSetupFromFilename(filename);
12233 // try to load setup values from platform setup file
12234 filename = getPlatformSetupFilename();
12236 if (fileExists(filename))
12237 LoadSetupFromFilename(filename);
12239 // try to load setup values from user setup file
12240 filename = getSetupFilename();
12242 LoadSetupFromFilename(filename);
12244 LoadSetup_SpecialPostProcessing();
12247 void LoadSetup_AutoSetup(void)
12249 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12250 SetupFileHash *setup_file_hash = NULL;
12252 // always start with reliable default values
12253 setSetupInfoToDefaults_AutoSetup(&setup);
12255 setup_file_hash = loadSetupFileHash(filename);
12257 if (setup_file_hash)
12259 decodeSetupFileHash_AutoSetup(setup_file_hash);
12261 freeSetupFileHash(setup_file_hash);
12267 void LoadSetup_ServerSetup(void)
12269 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12270 SetupFileHash *setup_file_hash = NULL;
12272 // always start with reliable default values
12273 setSetupInfoToDefaults_ServerSetup(&setup);
12275 setup_file_hash = loadSetupFileHash(filename);
12277 if (setup_file_hash)
12279 decodeSetupFileHash_ServerSetup(setup_file_hash);
12281 freeSetupFileHash(setup_file_hash);
12286 if (setup.player_uuid == NULL)
12288 // player UUID does not yet exist in setup file
12289 setup.player_uuid = getStringCopy(getUUID());
12290 setup.player_version = 2;
12292 SaveSetup_ServerSetup();
12296 void LoadSetup_EditorCascade(void)
12298 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12299 SetupFileHash *setup_file_hash = NULL;
12301 // always start with reliable default values
12302 setSetupInfoToDefaults_EditorCascade(&setup);
12304 setup_file_hash = loadSetupFileHash(filename);
12306 if (setup_file_hash)
12308 decodeSetupFileHash_EditorCascade(setup_file_hash);
12310 freeSetupFileHash(setup_file_hash);
12316 void LoadSetup(void)
12318 LoadSetup_Default();
12319 LoadSetup_AutoSetup();
12320 LoadSetup_ServerSetup();
12321 LoadSetup_EditorCascade();
12324 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12325 char *mapping_line)
12327 char mapping_guid[MAX_LINE_LEN];
12328 char *mapping_start, *mapping_end;
12330 // get GUID from game controller mapping line: copy complete line
12331 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12332 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12334 // get GUID from game controller mapping line: cut after GUID part
12335 mapping_start = strchr(mapping_guid, ',');
12336 if (mapping_start != NULL)
12337 *mapping_start = '\0';
12339 // cut newline from game controller mapping line
12340 mapping_end = strchr(mapping_line, '\n');
12341 if (mapping_end != NULL)
12342 *mapping_end = '\0';
12344 // add mapping entry to game controller mappings hash
12345 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12348 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12353 if (!(file = fopen(filename, MODE_READ)))
12355 Warn("cannot read game controller mappings file '%s'", filename);
12360 while (!feof(file))
12362 char line[MAX_LINE_LEN];
12364 if (!fgets(line, MAX_LINE_LEN, file))
12367 addGameControllerMappingToHash(mappings_hash, line);
12373 void SaveSetup_Default(void)
12375 char *filename = getSetupFilename();
12379 InitUserDataDirectory();
12381 if (!(file = fopen(filename, MODE_WRITE)))
12383 Warn("cannot write setup file '%s'", filename);
12388 fprintFileHeader(file, SETUP_FILENAME);
12390 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12392 // just to make things nicer :)
12393 if (global_setup_tokens[i].value == &setup.multiple_users ||
12394 global_setup_tokens[i].value == &setup.sound ||
12395 global_setup_tokens[i].value == &setup.graphics_set ||
12396 global_setup_tokens[i].value == &setup.volume_simple ||
12397 global_setup_tokens[i].value == &setup.network_mode ||
12398 global_setup_tokens[i].value == &setup.touch.control_type ||
12399 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12400 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12401 fprintf(file, "\n");
12403 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12406 for (i = 0; i < 2; i++)
12408 int grid_xsize = setup.touch.grid_xsize[i];
12409 int grid_ysize = setup.touch.grid_ysize[i];
12412 fprintf(file, "\n");
12414 for (y = 0; y < grid_ysize; y++)
12416 char token_string[MAX_LINE_LEN];
12417 char value_string[MAX_LINE_LEN];
12419 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12421 for (x = 0; x < grid_xsize; x++)
12423 char c = setup.touch.grid_button[i][x][y];
12425 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12428 value_string[grid_xsize] = '\0';
12430 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12434 fprintf(file, "\n");
12435 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12436 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12438 fprintf(file, "\n");
12439 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12440 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12442 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12446 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12447 fprintf(file, "\n");
12449 setup_input = setup.input[pnr];
12450 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12451 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12454 fprintf(file, "\n");
12455 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12456 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12458 // (internal setup values not saved to user setup file)
12460 fprintf(file, "\n");
12461 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12462 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12463 setup.debug.xsn_mode != STATE_AUTO)
12464 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12466 fprintf(file, "\n");
12467 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12468 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12472 SetFilePermissions(filename, PERMS_PRIVATE);
12475 void SaveSetup_AutoSetup(void)
12477 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12481 InitUserDataDirectory();
12483 if (!(file = fopen(filename, MODE_WRITE)))
12485 Warn("cannot write auto setup file '%s'", filename);
12492 fprintFileHeader(file, AUTOSETUP_FILENAME);
12494 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12495 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12499 SetFilePermissions(filename, PERMS_PRIVATE);
12504 void SaveSetup_ServerSetup(void)
12506 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12510 InitUserDataDirectory();
12512 if (!(file = fopen(filename, MODE_WRITE)))
12514 Warn("cannot write server setup file '%s'", filename);
12521 fprintFileHeader(file, SERVERSETUP_FILENAME);
12523 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12525 // just to make things nicer :)
12526 if (server_setup_tokens[i].value == &setup.use_api_server)
12527 fprintf(file, "\n");
12529 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12534 SetFilePermissions(filename, PERMS_PRIVATE);
12539 void SaveSetup_EditorCascade(void)
12541 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12545 InitUserDataDirectory();
12547 if (!(file = fopen(filename, MODE_WRITE)))
12549 Warn("cannot write editor cascade state file '%s'", filename);
12556 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12558 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12559 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12563 SetFilePermissions(filename, PERMS_PRIVATE);
12568 void SaveSetup(void)
12570 SaveSetup_Default();
12571 SaveSetup_AutoSetup();
12572 SaveSetup_ServerSetup();
12573 SaveSetup_EditorCascade();
12576 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12581 if (!(file = fopen(filename, MODE_WRITE)))
12583 Warn("cannot write game controller mappings file '%s'", filename);
12588 BEGIN_HASH_ITERATION(mappings_hash, itr)
12590 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12592 END_HASH_ITERATION(mappings_hash, itr)
12597 void SaveSetup_AddGameControllerMapping(char *mapping)
12599 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12600 SetupFileHash *mappings_hash = newSetupFileHash();
12602 InitUserDataDirectory();
12604 // load existing personal game controller mappings
12605 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12607 // add new mapping to personal game controller mappings
12608 addGameControllerMappingToHash(mappings_hash, mapping);
12610 // save updated personal game controller mappings
12611 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12613 freeSetupFileHash(mappings_hash);
12617 void LoadCustomElementDescriptions(void)
12619 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12620 SetupFileHash *setup_file_hash;
12623 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12625 if (element_info[i].custom_description != NULL)
12627 free(element_info[i].custom_description);
12628 element_info[i].custom_description = NULL;
12632 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12635 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12637 char *token = getStringCat2(element_info[i].token_name, ".name");
12638 char *value = getHashEntry(setup_file_hash, token);
12641 element_info[i].custom_description = getStringCopy(value);
12646 freeSetupFileHash(setup_file_hash);
12649 static int getElementFromToken(char *token)
12651 char *value = getHashEntry(element_token_hash, token);
12654 return atoi(value);
12656 Warn("unknown element token '%s'", token);
12658 return EL_UNDEFINED;
12661 void FreeGlobalAnimEventInfo(void)
12663 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12665 if (gaei->event_list == NULL)
12670 for (i = 0; i < gaei->num_event_lists; i++)
12672 checked_free(gaei->event_list[i]->event_value);
12673 checked_free(gaei->event_list[i]);
12676 checked_free(gaei->event_list);
12678 gaei->event_list = NULL;
12679 gaei->num_event_lists = 0;
12682 static int AddGlobalAnimEventList(void)
12684 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12685 int list_pos = gaei->num_event_lists++;
12687 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12688 sizeof(struct GlobalAnimEventListInfo *));
12690 gaei->event_list[list_pos] =
12691 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12693 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12695 gaeli->event_value = NULL;
12696 gaeli->num_event_values = 0;
12701 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12703 // do not add empty global animation events
12704 if (event_value == ANIM_EVENT_NONE)
12707 // if list position is undefined, create new list
12708 if (list_pos == ANIM_EVENT_UNDEFINED)
12709 list_pos = AddGlobalAnimEventList();
12711 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12712 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12713 int value_pos = gaeli->num_event_values++;
12715 gaeli->event_value = checked_realloc(gaeli->event_value,
12716 gaeli->num_event_values * sizeof(int *));
12718 gaeli->event_value[value_pos] = event_value;
12723 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12725 if (list_pos == ANIM_EVENT_UNDEFINED)
12726 return ANIM_EVENT_NONE;
12728 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12729 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12731 return gaeli->event_value[value_pos];
12734 int GetGlobalAnimEventValueCount(int list_pos)
12736 if (list_pos == ANIM_EVENT_UNDEFINED)
12739 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12740 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12742 return gaeli->num_event_values;
12745 // This function checks if a string <s> of the format "string1, string2, ..."
12746 // exactly contains a string <s_contained>.
12748 static boolean string_has_parameter(char *s, char *s_contained)
12752 if (s == NULL || s_contained == NULL)
12755 if (strlen(s_contained) > strlen(s))
12758 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12760 char next_char = s[strlen(s_contained)];
12762 // check if next character is delimiter or whitespace
12763 if (next_char == ',' || next_char == '\0' ||
12764 next_char == ' ' || next_char == '\t')
12768 // check if string contains another parameter string after a comma
12769 substring = strchr(s, ',');
12770 if (substring == NULL) // string does not contain a comma
12773 // advance string pointer to next character after the comma
12776 // skip potential whitespaces after the comma
12777 while (*substring == ' ' || *substring == '\t')
12780 return string_has_parameter(substring, s_contained);
12783 static int get_anim_parameter_value_ce(char *s)
12786 char *pattern_1 = "ce_change:custom_";
12787 char *pattern_2 = ".page_";
12788 int pattern_1_len = strlen(pattern_1);
12789 char *matching_char = strstr(s_ptr, pattern_1);
12790 int result = ANIM_EVENT_NONE;
12792 if (matching_char == NULL)
12793 return ANIM_EVENT_NONE;
12795 result = ANIM_EVENT_CE_CHANGE;
12797 s_ptr = matching_char + pattern_1_len;
12799 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12800 if (*s_ptr >= '0' && *s_ptr <= '9')
12802 int gic_ce_nr = (*s_ptr++ - '0');
12804 if (*s_ptr >= '0' && *s_ptr <= '9')
12806 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12808 if (*s_ptr >= '0' && *s_ptr <= '9')
12809 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12812 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12813 return ANIM_EVENT_NONE;
12815 // custom element stored as 0 to 255
12818 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12822 // invalid custom element number specified
12824 return ANIM_EVENT_NONE;
12827 // check for change page number ("page_X" or "page_XX") (optional)
12828 if (strPrefix(s_ptr, pattern_2))
12830 s_ptr += strlen(pattern_2);
12832 if (*s_ptr >= '0' && *s_ptr <= '9')
12834 int gic_page_nr = (*s_ptr++ - '0');
12836 if (*s_ptr >= '0' && *s_ptr <= '9')
12837 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12839 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12840 return ANIM_EVENT_NONE;
12842 // change page stored as 1 to 32 (0 means "all change pages")
12844 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12848 // invalid animation part number specified
12850 return ANIM_EVENT_NONE;
12854 // discard result if next character is neither delimiter nor whitespace
12855 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12856 *s_ptr == ' ' || *s_ptr == '\t'))
12857 return ANIM_EVENT_NONE;
12862 static int get_anim_parameter_value(char *s)
12864 int event_value[] =
12872 char *pattern_1[] =
12880 char *pattern_2 = ".part_";
12881 char *matching_char = NULL;
12883 int pattern_1_len = 0;
12884 int result = ANIM_EVENT_NONE;
12887 result = get_anim_parameter_value_ce(s);
12889 if (result != ANIM_EVENT_NONE)
12892 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12894 matching_char = strstr(s_ptr, pattern_1[i]);
12895 pattern_1_len = strlen(pattern_1[i]);
12896 result = event_value[i];
12898 if (matching_char != NULL)
12902 if (matching_char == NULL)
12903 return ANIM_EVENT_NONE;
12905 s_ptr = matching_char + pattern_1_len;
12907 // check for main animation number ("anim_X" or "anim_XX")
12908 if (*s_ptr >= '0' && *s_ptr <= '9')
12910 int gic_anim_nr = (*s_ptr++ - '0');
12912 if (*s_ptr >= '0' && *s_ptr <= '9')
12913 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12915 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12916 return ANIM_EVENT_NONE;
12918 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12922 // invalid main animation number specified
12924 return ANIM_EVENT_NONE;
12927 // check for animation part number ("part_X" or "part_XX") (optional)
12928 if (strPrefix(s_ptr, pattern_2))
12930 s_ptr += strlen(pattern_2);
12932 if (*s_ptr >= '0' && *s_ptr <= '9')
12934 int gic_part_nr = (*s_ptr++ - '0');
12936 if (*s_ptr >= '0' && *s_ptr <= '9')
12937 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12939 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12940 return ANIM_EVENT_NONE;
12942 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12946 // invalid animation part number specified
12948 return ANIM_EVENT_NONE;
12952 // discard result if next character is neither delimiter nor whitespace
12953 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12954 *s_ptr == ' ' || *s_ptr == '\t'))
12955 return ANIM_EVENT_NONE;
12960 static int get_anim_parameter_values(char *s)
12962 int list_pos = ANIM_EVENT_UNDEFINED;
12963 int event_value = ANIM_EVENT_DEFAULT;
12965 if (string_has_parameter(s, "any"))
12966 event_value |= ANIM_EVENT_ANY;
12968 if (string_has_parameter(s, "click:self") ||
12969 string_has_parameter(s, "click") ||
12970 string_has_parameter(s, "self"))
12971 event_value |= ANIM_EVENT_SELF;
12973 if (string_has_parameter(s, "unclick:any"))
12974 event_value |= ANIM_EVENT_UNCLICK_ANY;
12976 // if animation event found, add it to global animation event list
12977 if (event_value != ANIM_EVENT_NONE)
12978 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12982 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12983 event_value = get_anim_parameter_value(s);
12985 // if animation event found, add it to global animation event list
12986 if (event_value != ANIM_EVENT_NONE)
12987 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12989 // continue with next part of the string, starting with next comma
12990 s = strchr(s + 1, ',');
12996 static int get_anim_action_parameter_value(char *token)
12998 // check most common default case first to massively speed things up
12999 if (strEqual(token, ARG_UNDEFINED))
13000 return ANIM_EVENT_ACTION_NONE;
13002 int result = getImageIDFromToken(token);
13006 char *gfx_token = getStringCat2("gfx.", token);
13008 result = getImageIDFromToken(gfx_token);
13010 checked_free(gfx_token);
13015 Key key = getKeyFromX11KeyName(token);
13017 if (key != KSYM_UNDEFINED)
13018 result = -(int)key;
13025 result = get_hash_from_string(token); // unsigned int => int
13026 result = ABS(result); // may be negative now
13027 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13029 setHashEntry(anim_url_hash, int2str(result, 0), token);
13034 result = ANIM_EVENT_ACTION_NONE;
13039 int get_parameter_value(char *value_raw, char *suffix, int type)
13041 char *value = getStringToLower(value_raw);
13042 int result = 0; // probably a save default value
13044 if (strEqual(suffix, ".direction"))
13046 result = (strEqual(value, "left") ? MV_LEFT :
13047 strEqual(value, "right") ? MV_RIGHT :
13048 strEqual(value, "up") ? MV_UP :
13049 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13051 else if (strEqual(suffix, ".position"))
13053 result = (strEqual(value, "left") ? POS_LEFT :
13054 strEqual(value, "right") ? POS_RIGHT :
13055 strEqual(value, "top") ? POS_TOP :
13056 strEqual(value, "upper") ? POS_UPPER :
13057 strEqual(value, "middle") ? POS_MIDDLE :
13058 strEqual(value, "lower") ? POS_LOWER :
13059 strEqual(value, "bottom") ? POS_BOTTOM :
13060 strEqual(value, "any") ? POS_ANY :
13061 strEqual(value, "ce") ? POS_CE :
13062 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13063 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13065 else if (strEqual(suffix, ".align"))
13067 result = (strEqual(value, "left") ? ALIGN_LEFT :
13068 strEqual(value, "right") ? ALIGN_RIGHT :
13069 strEqual(value, "center") ? ALIGN_CENTER :
13070 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13072 else if (strEqual(suffix, ".valign"))
13074 result = (strEqual(value, "top") ? VALIGN_TOP :
13075 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13076 strEqual(value, "middle") ? VALIGN_MIDDLE :
13077 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13079 else if (strEqual(suffix, ".anim_mode"))
13081 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13082 string_has_parameter(value, "loop") ? ANIM_LOOP :
13083 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13084 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13085 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13086 string_has_parameter(value, "random") ? ANIM_RANDOM :
13087 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13088 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13089 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13090 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13091 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13092 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13093 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13094 string_has_parameter(value, "all") ? ANIM_ALL :
13095 string_has_parameter(value, "tiled") ? ANIM_TILED :
13096 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13099 if (string_has_parameter(value, "once"))
13100 result |= ANIM_ONCE;
13102 if (string_has_parameter(value, "reverse"))
13103 result |= ANIM_REVERSE;
13105 if (string_has_parameter(value, "opaque_player"))
13106 result |= ANIM_OPAQUE_PLAYER;
13108 if (string_has_parameter(value, "static_panel"))
13109 result |= ANIM_STATIC_PANEL;
13111 else if (strEqual(suffix, ".init_event") ||
13112 strEqual(suffix, ".anim_event"))
13114 result = get_anim_parameter_values(value);
13116 else if (strEqual(suffix, ".init_delay_action") ||
13117 strEqual(suffix, ".anim_delay_action") ||
13118 strEqual(suffix, ".post_delay_action") ||
13119 strEqual(suffix, ".init_event_action") ||
13120 strEqual(suffix, ".anim_event_action"))
13122 result = get_anim_action_parameter_value(value_raw);
13124 else if (strEqual(suffix, ".class"))
13126 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13127 get_hash_from_string(value));
13129 else if (strEqual(suffix, ".style"))
13131 result = STYLE_DEFAULT;
13133 if (string_has_parameter(value, "accurate_borders"))
13134 result |= STYLE_ACCURATE_BORDERS;
13136 if (string_has_parameter(value, "inner_corners"))
13137 result |= STYLE_INNER_CORNERS;
13139 if (string_has_parameter(value, "reverse"))
13140 result |= STYLE_REVERSE;
13142 if (string_has_parameter(value, "leftmost_position"))
13143 result |= STYLE_LEFTMOST_POSITION;
13145 if (string_has_parameter(value, "block_clicks"))
13146 result |= STYLE_BLOCK;
13148 if (string_has_parameter(value, "passthrough_clicks"))
13149 result |= STYLE_PASSTHROUGH;
13151 if (string_has_parameter(value, "multiple_actions"))
13152 result |= STYLE_MULTIPLE_ACTIONS;
13154 if (string_has_parameter(value, "consume_ce_event"))
13155 result |= STYLE_CONSUME_CE_EVENT;
13157 else if (strEqual(suffix, ".fade_mode"))
13159 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13160 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13161 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13162 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13163 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13164 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13165 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13166 FADE_MODE_DEFAULT);
13168 else if (strEqual(suffix, ".auto_delay_unit"))
13170 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13171 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13172 AUTO_DELAY_UNIT_DEFAULT);
13174 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13176 result = gfx.get_font_from_token_function(value);
13178 else // generic parameter of type integer or boolean
13180 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13181 type == TYPE_INTEGER ? get_integer_from_string(value) :
13182 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13183 ARG_UNDEFINED_VALUE);
13191 static int get_token_parameter_value(char *token, char *value_raw)
13195 if (token == NULL || value_raw == NULL)
13196 return ARG_UNDEFINED_VALUE;
13198 suffix = strrchr(token, '.');
13199 if (suffix == NULL)
13202 if (strEqual(suffix, ".element"))
13203 return getElementFromToken(value_raw);
13205 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13206 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13209 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13210 boolean ignore_defaults)
13214 for (i = 0; image_config_vars[i].token != NULL; i++)
13216 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13218 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13219 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13223 *image_config_vars[i].value =
13224 get_token_parameter_value(image_config_vars[i].token, value);
13228 void InitMenuDesignSettings_Static(void)
13230 // always start with reliable default values from static default config
13231 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13234 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13238 // the following initializes hierarchical values from static configuration
13240 // special case: initialize "ARG_DEFAULT" values in static default config
13241 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13242 titlescreen_initial_first_default.fade_mode =
13243 title_initial_first_default.fade_mode;
13244 titlescreen_initial_first_default.fade_delay =
13245 title_initial_first_default.fade_delay;
13246 titlescreen_initial_first_default.post_delay =
13247 title_initial_first_default.post_delay;
13248 titlescreen_initial_first_default.auto_delay =
13249 title_initial_first_default.auto_delay;
13250 titlescreen_initial_first_default.auto_delay_unit =
13251 title_initial_first_default.auto_delay_unit;
13252 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13253 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13254 titlescreen_first_default.post_delay = title_first_default.post_delay;
13255 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13256 titlescreen_first_default.auto_delay_unit =
13257 title_first_default.auto_delay_unit;
13258 titlemessage_initial_first_default.fade_mode =
13259 title_initial_first_default.fade_mode;
13260 titlemessage_initial_first_default.fade_delay =
13261 title_initial_first_default.fade_delay;
13262 titlemessage_initial_first_default.post_delay =
13263 title_initial_first_default.post_delay;
13264 titlemessage_initial_first_default.auto_delay =
13265 title_initial_first_default.auto_delay;
13266 titlemessage_initial_first_default.auto_delay_unit =
13267 title_initial_first_default.auto_delay_unit;
13268 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13269 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13270 titlemessage_first_default.post_delay = title_first_default.post_delay;
13271 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13272 titlemessage_first_default.auto_delay_unit =
13273 title_first_default.auto_delay_unit;
13275 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13276 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13277 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13278 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13279 titlescreen_initial_default.auto_delay_unit =
13280 title_initial_default.auto_delay_unit;
13281 titlescreen_default.fade_mode = title_default.fade_mode;
13282 titlescreen_default.fade_delay = title_default.fade_delay;
13283 titlescreen_default.post_delay = title_default.post_delay;
13284 titlescreen_default.auto_delay = title_default.auto_delay;
13285 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13286 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13287 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13288 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13289 titlemessage_initial_default.auto_delay_unit =
13290 title_initial_default.auto_delay_unit;
13291 titlemessage_default.fade_mode = title_default.fade_mode;
13292 titlemessage_default.fade_delay = title_default.fade_delay;
13293 titlemessage_default.post_delay = title_default.post_delay;
13294 titlemessage_default.auto_delay = title_default.auto_delay;
13295 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13297 // special case: initialize "ARG_DEFAULT" values in static default config
13298 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13299 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13301 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13302 titlescreen_first[i] = titlescreen_first_default;
13303 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13304 titlemessage_first[i] = titlemessage_first_default;
13306 titlescreen_initial[i] = titlescreen_initial_default;
13307 titlescreen[i] = titlescreen_default;
13308 titlemessage_initial[i] = titlemessage_initial_default;
13309 titlemessage[i] = titlemessage_default;
13312 // special case: initialize "ARG_DEFAULT" values in static default config
13313 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13314 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13316 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13319 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13320 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13321 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13324 // special case: initialize "ARG_DEFAULT" values in static default config
13325 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13326 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13328 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13329 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13330 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13332 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13335 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13339 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13343 struct XY *dst, *src;
13345 game_buttons_xy[] =
13347 { &game.button.save, &game.button.stop },
13348 { &game.button.pause2, &game.button.pause },
13349 { &game.button.load, &game.button.play },
13350 { &game.button.undo, &game.button.stop },
13351 { &game.button.redo, &game.button.play },
13357 // special case: initialize later added SETUP list size from LEVELS value
13358 if (menu.list_size[GAME_MODE_SETUP] == -1)
13359 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13361 // set default position for snapshot buttons to stop/pause/play buttons
13362 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13363 if ((*game_buttons_xy[i].dst).x == -1 &&
13364 (*game_buttons_xy[i].dst).y == -1)
13365 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13367 // --------------------------------------------------------------------------
13368 // dynamic viewports (including playfield margins, borders and alignments)
13369 // --------------------------------------------------------------------------
13371 // dynamic viewports currently only supported for landscape mode
13372 int display_width = MAX(video.display_width, video.display_height);
13373 int display_height = MIN(video.display_width, video.display_height);
13375 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13377 struct RectWithBorder *vp_window = &viewport.window[i];
13378 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13379 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13380 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13381 boolean dynamic_window_width = (vp_window->min_width != -1);
13382 boolean dynamic_window_height = (vp_window->min_height != -1);
13383 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13384 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13386 // adjust window size if min/max width/height is specified
13388 if (vp_window->min_width != -1)
13390 int window_width = display_width;
13392 // when using static window height, use aspect ratio of display
13393 if (vp_window->min_height == -1)
13394 window_width = vp_window->height * display_width / display_height;
13396 vp_window->width = MAX(vp_window->min_width, window_width);
13399 if (vp_window->min_height != -1)
13401 int window_height = display_height;
13403 // when using static window width, use aspect ratio of display
13404 if (vp_window->min_width == -1)
13405 window_height = vp_window->width * display_height / display_width;
13407 vp_window->height = MAX(vp_window->min_height, window_height);
13410 if (vp_window->max_width != -1)
13411 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13413 if (vp_window->max_height != -1)
13414 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13416 int playfield_width = vp_window->width;
13417 int playfield_height = vp_window->height;
13419 // adjust playfield size and position according to specified margins
13421 playfield_width -= vp_playfield->margin_left;
13422 playfield_width -= vp_playfield->margin_right;
13424 playfield_height -= vp_playfield->margin_top;
13425 playfield_height -= vp_playfield->margin_bottom;
13427 // adjust playfield size if min/max width/height is specified
13429 if (vp_playfield->min_width != -1)
13430 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13432 if (vp_playfield->min_height != -1)
13433 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13435 if (vp_playfield->max_width != -1)
13436 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13438 if (vp_playfield->max_height != -1)
13439 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13441 // adjust playfield position according to specified alignment
13443 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13444 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13445 else if (vp_playfield->align == ALIGN_CENTER)
13446 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13447 else if (vp_playfield->align == ALIGN_RIGHT)
13448 vp_playfield->x += playfield_width - vp_playfield->width;
13450 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13451 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13452 else if (vp_playfield->valign == VALIGN_MIDDLE)
13453 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13454 else if (vp_playfield->valign == VALIGN_BOTTOM)
13455 vp_playfield->y += playfield_height - vp_playfield->height;
13457 vp_playfield->x += vp_playfield->margin_left;
13458 vp_playfield->y += vp_playfield->margin_top;
13460 // adjust individual playfield borders if only default border is specified
13462 if (vp_playfield->border_left == -1)
13463 vp_playfield->border_left = vp_playfield->border_size;
13464 if (vp_playfield->border_right == -1)
13465 vp_playfield->border_right = vp_playfield->border_size;
13466 if (vp_playfield->border_top == -1)
13467 vp_playfield->border_top = vp_playfield->border_size;
13468 if (vp_playfield->border_bottom == -1)
13469 vp_playfield->border_bottom = vp_playfield->border_size;
13471 // set dynamic playfield borders if borders are specified as undefined
13472 // (but only if window size was dynamic and playfield size was static)
13474 if (dynamic_window_width && !dynamic_playfield_width)
13476 if (vp_playfield->border_left == -1)
13478 vp_playfield->border_left = (vp_playfield->x -
13479 vp_playfield->margin_left);
13480 vp_playfield->x -= vp_playfield->border_left;
13481 vp_playfield->width += vp_playfield->border_left;
13484 if (vp_playfield->border_right == -1)
13486 vp_playfield->border_right = (vp_window->width -
13488 vp_playfield->width -
13489 vp_playfield->margin_right);
13490 vp_playfield->width += vp_playfield->border_right;
13494 if (dynamic_window_height && !dynamic_playfield_height)
13496 if (vp_playfield->border_top == -1)
13498 vp_playfield->border_top = (vp_playfield->y -
13499 vp_playfield->margin_top);
13500 vp_playfield->y -= vp_playfield->border_top;
13501 vp_playfield->height += vp_playfield->border_top;
13504 if (vp_playfield->border_bottom == -1)
13506 vp_playfield->border_bottom = (vp_window->height -
13508 vp_playfield->height -
13509 vp_playfield->margin_bottom);
13510 vp_playfield->height += vp_playfield->border_bottom;
13514 // adjust playfield size to be a multiple of a defined alignment tile size
13516 int align_size = vp_playfield->align_size;
13517 int playfield_xtiles = vp_playfield->width / align_size;
13518 int playfield_ytiles = vp_playfield->height / align_size;
13519 int playfield_width_corrected = playfield_xtiles * align_size;
13520 int playfield_height_corrected = playfield_ytiles * align_size;
13521 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13522 i == GFX_SPECIAL_ARG_EDITOR);
13524 if (is_playfield_mode &&
13525 dynamic_playfield_width &&
13526 vp_playfield->width != playfield_width_corrected)
13528 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13530 vp_playfield->width = playfield_width_corrected;
13532 if (vp_playfield->align == ALIGN_LEFT)
13534 vp_playfield->border_left += playfield_xdiff;
13536 else if (vp_playfield->align == ALIGN_RIGHT)
13538 vp_playfield->border_right += playfield_xdiff;
13540 else if (vp_playfield->align == ALIGN_CENTER)
13542 int border_left_diff = playfield_xdiff / 2;
13543 int border_right_diff = playfield_xdiff - border_left_diff;
13545 vp_playfield->border_left += border_left_diff;
13546 vp_playfield->border_right += border_right_diff;
13550 if (is_playfield_mode &&
13551 dynamic_playfield_height &&
13552 vp_playfield->height != playfield_height_corrected)
13554 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13556 vp_playfield->height = playfield_height_corrected;
13558 if (vp_playfield->valign == VALIGN_TOP)
13560 vp_playfield->border_top += playfield_ydiff;
13562 else if (vp_playfield->align == VALIGN_BOTTOM)
13564 vp_playfield->border_right += playfield_ydiff;
13566 else if (vp_playfield->align == VALIGN_MIDDLE)
13568 int border_top_diff = playfield_ydiff / 2;
13569 int border_bottom_diff = playfield_ydiff - border_top_diff;
13571 vp_playfield->border_top += border_top_diff;
13572 vp_playfield->border_bottom += border_bottom_diff;
13576 // adjust door positions according to specified alignment
13578 for (j = 0; j < 2; j++)
13580 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13582 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13583 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13584 else if (vp_door->align == ALIGN_CENTER)
13585 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13586 else if (vp_door->align == ALIGN_RIGHT)
13587 vp_door->x += vp_window->width - vp_door->width;
13589 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13590 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13591 else if (vp_door->valign == VALIGN_MIDDLE)
13592 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13593 else if (vp_door->valign == VALIGN_BOTTOM)
13594 vp_door->y += vp_window->height - vp_door->height;
13599 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13603 struct XYTileSize *dst, *src;
13606 editor_buttons_xy[] =
13609 &editor.button.element_left, &editor.palette.element_left,
13610 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13613 &editor.button.element_middle, &editor.palette.element_middle,
13614 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13617 &editor.button.element_right, &editor.palette.element_right,
13618 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13625 // set default position for element buttons to element graphics
13626 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13628 if ((*editor_buttons_xy[i].dst).x == -1 &&
13629 (*editor_buttons_xy[i].dst).y == -1)
13631 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13633 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13635 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13639 // adjust editor palette rows and columns if specified to be dynamic
13641 if (editor.palette.cols == -1)
13643 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13644 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13645 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13647 editor.palette.cols = (vp_width - sc_width) / bt_width;
13649 if (editor.palette.x == -1)
13651 int palette_width = editor.palette.cols * bt_width + sc_width;
13653 editor.palette.x = (vp_width - palette_width) / 2;
13657 if (editor.palette.rows == -1)
13659 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13660 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13661 int tx_height = getFontHeight(FONT_TEXT_2);
13663 editor.palette.rows = (vp_height - tx_height) / bt_height;
13665 if (editor.palette.y == -1)
13667 int palette_height = editor.palette.rows * bt_height + tx_height;
13669 editor.palette.y = (vp_height - palette_height) / 2;
13674 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13675 boolean initialize)
13677 // special case: check if network and preview player positions are redefined,
13678 // to compare this later against the main menu level preview being redefined
13679 struct TokenIntPtrInfo menu_config_players[] =
13681 { "main.network_players.x", &menu.main.network_players.redefined },
13682 { "main.network_players.y", &menu.main.network_players.redefined },
13683 { "main.preview_players.x", &menu.main.preview_players.redefined },
13684 { "main.preview_players.y", &menu.main.preview_players.redefined },
13685 { "preview.x", &preview.redefined },
13686 { "preview.y", &preview.redefined }
13692 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13693 *menu_config_players[i].value = FALSE;
13697 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13698 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13699 *menu_config_players[i].value = TRUE;
13703 static void InitMenuDesignSettings_PreviewPlayers(void)
13705 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13708 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13710 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13713 static void LoadMenuDesignSettingsFromFilename(char *filename)
13715 static struct TitleFadingInfo tfi;
13716 static struct TitleMessageInfo tmi;
13717 static struct TokenInfo title_tokens[] =
13719 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13720 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13721 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13722 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13723 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13727 static struct TokenInfo titlemessage_tokens[] =
13729 { TYPE_INTEGER, &tmi.x, ".x" },
13730 { TYPE_INTEGER, &tmi.y, ".y" },
13731 { TYPE_INTEGER, &tmi.width, ".width" },
13732 { TYPE_INTEGER, &tmi.height, ".height" },
13733 { TYPE_INTEGER, &tmi.chars, ".chars" },
13734 { TYPE_INTEGER, &tmi.lines, ".lines" },
13735 { TYPE_INTEGER, &tmi.align, ".align" },
13736 { TYPE_INTEGER, &tmi.valign, ".valign" },
13737 { TYPE_INTEGER, &tmi.font, ".font" },
13738 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13739 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13740 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13741 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13742 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13743 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13744 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13745 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13746 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13752 struct TitleFadingInfo *info;
13757 // initialize first titles from "enter screen" definitions, if defined
13758 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13759 { &title_first_default, "menu.enter_screen.TITLE" },
13761 // initialize title screens from "next screen" definitions, if defined
13762 { &title_initial_default, "menu.next_screen.TITLE" },
13763 { &title_default, "menu.next_screen.TITLE" },
13769 struct TitleMessageInfo *array;
13772 titlemessage_arrays[] =
13774 // initialize first titles from "enter screen" definitions, if defined
13775 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13776 { titlescreen_first, "menu.enter_screen.TITLE" },
13777 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13778 { titlemessage_first, "menu.enter_screen.TITLE" },
13780 // initialize titles from "next screen" definitions, if defined
13781 { titlescreen_initial, "menu.next_screen.TITLE" },
13782 { titlescreen, "menu.next_screen.TITLE" },
13783 { titlemessage_initial, "menu.next_screen.TITLE" },
13784 { titlemessage, "menu.next_screen.TITLE" },
13786 // overwrite titles with title definitions, if defined
13787 { titlescreen_initial_first, "[title_initial]" },
13788 { titlescreen_first, "[title]" },
13789 { titlemessage_initial_first, "[title_initial]" },
13790 { titlemessage_first, "[title]" },
13792 { titlescreen_initial, "[title_initial]" },
13793 { titlescreen, "[title]" },
13794 { titlemessage_initial, "[title_initial]" },
13795 { titlemessage, "[title]" },
13797 // overwrite titles with title screen/message definitions, if defined
13798 { titlescreen_initial_first, "[titlescreen_initial]" },
13799 { titlescreen_first, "[titlescreen]" },
13800 { titlemessage_initial_first, "[titlemessage_initial]" },
13801 { titlemessage_first, "[titlemessage]" },
13803 { titlescreen_initial, "[titlescreen_initial]" },
13804 { titlescreen, "[titlescreen]" },
13805 { titlemessage_initial, "[titlemessage_initial]" },
13806 { titlemessage, "[titlemessage]" },
13810 SetupFileHash *setup_file_hash;
13813 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13816 // the following initializes hierarchical values from dynamic configuration
13818 // special case: initialize with default values that may be overwritten
13819 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13820 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13822 struct TokenIntPtrInfo menu_config[] =
13824 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13825 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13826 { "menu.list_size", &menu.list_size[i] }
13829 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13831 char *token = menu_config[j].token;
13832 char *value = getHashEntry(setup_file_hash, token);
13835 *menu_config[j].value = get_integer_from_string(value);
13839 // special case: initialize with default values that may be overwritten
13840 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13841 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13843 struct TokenIntPtrInfo menu_config[] =
13845 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13846 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13847 { "menu.list_size.INFO", &menu.list_size_info[i] },
13848 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13849 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13852 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13854 char *token = menu_config[j].token;
13855 char *value = getHashEntry(setup_file_hash, token);
13858 *menu_config[j].value = get_integer_from_string(value);
13862 // special case: initialize with default values that may be overwritten
13863 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13864 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13866 struct TokenIntPtrInfo menu_config[] =
13868 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13869 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13872 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13874 char *token = menu_config[j].token;
13875 char *value = getHashEntry(setup_file_hash, token);
13878 *menu_config[j].value = get_integer_from_string(value);
13882 // special case: initialize with default values that may be overwritten
13883 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13884 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13886 struct TokenIntPtrInfo menu_config[] =
13888 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13889 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13890 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13891 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13892 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13893 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13894 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13895 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13896 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13897 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13900 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13902 char *token = menu_config[j].token;
13903 char *value = getHashEntry(setup_file_hash, token);
13906 *menu_config[j].value = get_integer_from_string(value);
13910 // special case: initialize with default values that may be overwritten
13911 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13912 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13914 struct TokenIntPtrInfo menu_config[] =
13916 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13917 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13918 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13919 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13920 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13921 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13922 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13923 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13924 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13927 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13929 char *token = menu_config[j].token;
13930 char *value = getHashEntry(setup_file_hash, token);
13933 *menu_config[j].value = get_token_parameter_value(token, value);
13937 // special case: initialize with default values that may be overwritten
13938 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13939 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13943 char *token_prefix;
13944 struct RectWithBorder *struct_ptr;
13948 { "viewport.window", &viewport.window[i] },
13949 { "viewport.playfield", &viewport.playfield[i] },
13950 { "viewport.door_1", &viewport.door_1[i] },
13951 { "viewport.door_2", &viewport.door_2[i] }
13954 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13956 struct TokenIntPtrInfo vp_config[] =
13958 { ".x", &vp_struct[j].struct_ptr->x },
13959 { ".y", &vp_struct[j].struct_ptr->y },
13960 { ".width", &vp_struct[j].struct_ptr->width },
13961 { ".height", &vp_struct[j].struct_ptr->height },
13962 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13963 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13964 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13965 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13966 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13967 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13968 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13969 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13970 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13971 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13972 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13973 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13974 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13975 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13976 { ".align", &vp_struct[j].struct_ptr->align },
13977 { ".valign", &vp_struct[j].struct_ptr->valign }
13980 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13982 char *token = getStringCat2(vp_struct[j].token_prefix,
13983 vp_config[k].token);
13984 char *value = getHashEntry(setup_file_hash, token);
13987 *vp_config[k].value = get_token_parameter_value(token, value);
13994 // special case: initialize with default values that may be overwritten
13995 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13996 for (i = 0; title_info[i].info != NULL; i++)
13998 struct TitleFadingInfo *info = title_info[i].info;
13999 char *base_token = title_info[i].text;
14001 for (j = 0; title_tokens[j].type != -1; j++)
14003 char *token = getStringCat2(base_token, title_tokens[j].text);
14004 char *value = getHashEntry(setup_file_hash, token);
14008 int parameter_value = get_token_parameter_value(token, value);
14012 *(int *)title_tokens[j].value = (int)parameter_value;
14021 // special case: initialize with default values that may be overwritten
14022 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14023 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14025 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14026 char *base_token = titlemessage_arrays[i].text;
14028 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14030 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14031 char *value = getHashEntry(setup_file_hash, token);
14035 int parameter_value = get_token_parameter_value(token, value);
14037 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14041 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14042 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14044 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14054 // read (and overwrite with) values that may be specified in config file
14055 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14057 // special case: check if network and preview player positions are redefined
14058 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14060 freeSetupFileHash(setup_file_hash);
14063 void LoadMenuDesignSettings(void)
14065 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14067 InitMenuDesignSettings_Static();
14068 InitMenuDesignSettings_SpecialPreProcessing();
14069 InitMenuDesignSettings_PreviewPlayers();
14071 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14073 // first look for special settings configured in level series config
14074 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14076 if (fileExists(filename_base))
14077 LoadMenuDesignSettingsFromFilename(filename_base);
14080 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14082 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14083 LoadMenuDesignSettingsFromFilename(filename_local);
14085 InitMenuDesignSettings_SpecialPostProcessing();
14088 void LoadMenuDesignSettings_AfterGraphics(void)
14090 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14093 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14094 boolean ignore_defaults)
14098 for (i = 0; sound_config_vars[i].token != NULL; i++)
14100 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14102 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14103 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14107 *sound_config_vars[i].value =
14108 get_token_parameter_value(sound_config_vars[i].token, value);
14112 void InitSoundSettings_Static(void)
14114 // always start with reliable default values from static default config
14115 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14118 static void LoadSoundSettingsFromFilename(char *filename)
14120 SetupFileHash *setup_file_hash;
14122 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14125 // read (and overwrite with) values that may be specified in config file
14126 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14128 freeSetupFileHash(setup_file_hash);
14131 void LoadSoundSettings(void)
14133 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14135 InitSoundSettings_Static();
14137 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14139 // first look for special settings configured in level series config
14140 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14142 if (fileExists(filename_base))
14143 LoadSoundSettingsFromFilename(filename_base);
14146 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14148 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14149 LoadSoundSettingsFromFilename(filename_local);
14152 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14154 char *filename = getEditorSetupFilename();
14155 SetupFileList *setup_file_list, *list;
14156 SetupFileHash *element_hash;
14157 int num_unknown_tokens = 0;
14160 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14163 element_hash = newSetupFileHash();
14165 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14166 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14168 // determined size may be larger than needed (due to unknown elements)
14170 for (list = setup_file_list; list != NULL; list = list->next)
14173 // add space for up to 3 more elements for padding that may be needed
14174 *num_elements += 3;
14176 // free memory for old list of elements, if needed
14177 checked_free(*elements);
14179 // allocate memory for new list of elements
14180 *elements = checked_malloc(*num_elements * sizeof(int));
14183 for (list = setup_file_list; list != NULL; list = list->next)
14185 char *value = getHashEntry(element_hash, list->token);
14187 if (value == NULL) // try to find obsolete token mapping
14189 char *mapped_token = get_mapped_token(list->token);
14191 if (mapped_token != NULL)
14193 value = getHashEntry(element_hash, mapped_token);
14195 free(mapped_token);
14201 (*elements)[(*num_elements)++] = atoi(value);
14205 if (num_unknown_tokens == 0)
14208 Warn("unknown token(s) found in config file:");
14209 Warn("- config file: '%s'", filename);
14211 num_unknown_tokens++;
14214 Warn("- token: '%s'", list->token);
14218 if (num_unknown_tokens > 0)
14221 while (*num_elements % 4) // pad with empty elements, if needed
14222 (*elements)[(*num_elements)++] = EL_EMPTY;
14224 freeSetupFileList(setup_file_list);
14225 freeSetupFileHash(element_hash);
14228 for (i = 0; i < *num_elements; i++)
14229 Debug("editor", "element '%s' [%d]\n",
14230 element_info[(*elements)[i]].token_name, (*elements)[i]);
14234 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14237 SetupFileHash *setup_file_hash = NULL;
14238 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14239 char *filename_music, *filename_prefix, *filename_info;
14245 token_to_value_ptr[] =
14247 { "title_header", &tmp_music_file_info.title_header },
14248 { "artist_header", &tmp_music_file_info.artist_header },
14249 { "album_header", &tmp_music_file_info.album_header },
14250 { "year_header", &tmp_music_file_info.year_header },
14251 { "played_header", &tmp_music_file_info.played_header },
14253 { "title", &tmp_music_file_info.title },
14254 { "artist", &tmp_music_file_info.artist },
14255 { "album", &tmp_music_file_info.album },
14256 { "year", &tmp_music_file_info.year },
14257 { "played", &tmp_music_file_info.played },
14263 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14264 getCustomMusicFilename(basename));
14266 if (filename_music == NULL)
14269 // ---------- try to replace file extension ----------
14271 filename_prefix = getStringCopy(filename_music);
14272 if (strrchr(filename_prefix, '.') != NULL)
14273 *strrchr(filename_prefix, '.') = '\0';
14274 filename_info = getStringCat2(filename_prefix, ".txt");
14276 if (fileExists(filename_info))
14277 setup_file_hash = loadSetupFileHash(filename_info);
14279 free(filename_prefix);
14280 free(filename_info);
14282 if (setup_file_hash == NULL)
14284 // ---------- try to add file extension ----------
14286 filename_prefix = getStringCopy(filename_music);
14287 filename_info = getStringCat2(filename_prefix, ".txt");
14289 if (fileExists(filename_info))
14290 setup_file_hash = loadSetupFileHash(filename_info);
14292 free(filename_prefix);
14293 free(filename_info);
14296 if (setup_file_hash == NULL)
14299 // ---------- music file info found ----------
14301 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14303 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14305 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14307 *token_to_value_ptr[i].value_ptr =
14308 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14311 tmp_music_file_info.basename = getStringCopy(basename);
14312 tmp_music_file_info.music = music;
14313 tmp_music_file_info.is_sound = is_sound;
14315 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14316 *new_music_file_info = tmp_music_file_info;
14318 return new_music_file_info;
14321 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14323 return get_music_file_info_ext(basename, music, FALSE);
14326 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14328 return get_music_file_info_ext(basename, sound, TRUE);
14331 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14332 char *basename, boolean is_sound)
14334 for (; list != NULL; list = list->next)
14335 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14341 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14343 return music_info_listed_ext(list, basename, FALSE);
14346 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14348 return music_info_listed_ext(list, basename, TRUE);
14351 void LoadMusicInfo(void)
14353 int num_music_noconf = getMusicListSize_NoConf();
14354 int num_music = getMusicListSize();
14355 int num_sounds = getSoundListSize();
14356 struct FileInfo *music, *sound;
14357 struct MusicFileInfo *next, **new;
14361 while (music_file_info != NULL)
14363 next = music_file_info->next;
14365 checked_free(music_file_info->basename);
14367 checked_free(music_file_info->title_header);
14368 checked_free(music_file_info->artist_header);
14369 checked_free(music_file_info->album_header);
14370 checked_free(music_file_info->year_header);
14371 checked_free(music_file_info->played_header);
14373 checked_free(music_file_info->title);
14374 checked_free(music_file_info->artist);
14375 checked_free(music_file_info->album);
14376 checked_free(music_file_info->year);
14377 checked_free(music_file_info->played);
14379 free(music_file_info);
14381 music_file_info = next;
14384 new = &music_file_info;
14386 // get (configured or unconfigured) music file info for all levels
14387 for (i = leveldir_current->first_level;
14388 i <= leveldir_current->last_level; i++)
14392 if (levelset.music[i] != MUS_UNDEFINED)
14394 // get music file info for configured level music
14395 music_nr = levelset.music[i];
14397 else if (num_music_noconf > 0)
14399 // get music file info for unconfigured level music
14400 int level_pos = i - leveldir_current->first_level;
14402 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14409 char *basename = getMusicInfoEntryFilename(music_nr);
14411 if (basename == NULL)
14414 if (!music_info_listed(music_file_info, basename))
14416 *new = get_music_file_info(basename, music_nr);
14419 new = &(*new)->next;
14423 // get music file info for all remaining configured music files
14424 for (i = 0; i < num_music; i++)
14426 music = getMusicListEntry(i);
14428 if (music->filename == NULL)
14431 if (strEqual(music->filename, UNDEFINED_FILENAME))
14434 // a configured file may be not recognized as music
14435 if (!FileIsMusic(music->filename))
14438 if (!music_info_listed(music_file_info, music->filename))
14440 *new = get_music_file_info(music->filename, i);
14443 new = &(*new)->next;
14447 // get sound file info for all configured sound files
14448 for (i = 0; i < num_sounds; i++)
14450 sound = getSoundListEntry(i);
14452 if (sound->filename == NULL)
14455 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14458 // a configured file may be not recognized as sound
14459 if (!FileIsSound(sound->filename))
14462 if (!sound_info_listed(music_file_info, sound->filename))
14464 *new = get_sound_file_info(sound->filename, i);
14466 new = &(*new)->next;
14470 // add pointers to previous list nodes
14472 struct MusicFileInfo *node = music_file_info;
14474 while (node != NULL)
14477 node->next->prev = node;
14483 static void add_helpanim_entry(int element, int action, int direction,
14484 int delay, int *num_list_entries)
14486 struct HelpAnimInfo *new_list_entry;
14487 (*num_list_entries)++;
14490 checked_realloc(helpanim_info,
14491 *num_list_entries * sizeof(struct HelpAnimInfo));
14492 new_list_entry = &helpanim_info[*num_list_entries - 1];
14494 new_list_entry->element = element;
14495 new_list_entry->action = action;
14496 new_list_entry->direction = direction;
14497 new_list_entry->delay = delay;
14500 static void print_unknown_token(char *filename, char *token, int token_nr)
14505 Warn("unknown token(s) found in config file:");
14506 Warn("- config file: '%s'", filename);
14509 Warn("- token: '%s'", token);
14512 static void print_unknown_token_end(int token_nr)
14518 void LoadHelpAnimInfo(void)
14520 char *filename = getHelpAnimFilename();
14521 SetupFileList *setup_file_list = NULL, *list;
14522 SetupFileHash *element_hash, *action_hash, *direction_hash;
14523 int num_list_entries = 0;
14524 int num_unknown_tokens = 0;
14527 if (fileExists(filename))
14528 setup_file_list = loadSetupFileList(filename);
14530 if (setup_file_list == NULL)
14532 // use reliable default values from static configuration
14533 SetupFileList *insert_ptr;
14535 insert_ptr = setup_file_list =
14536 newSetupFileList(helpanim_config[0].token,
14537 helpanim_config[0].value);
14539 for (i = 1; helpanim_config[i].token; i++)
14540 insert_ptr = addListEntry(insert_ptr,
14541 helpanim_config[i].token,
14542 helpanim_config[i].value);
14545 element_hash = newSetupFileHash();
14546 action_hash = newSetupFileHash();
14547 direction_hash = newSetupFileHash();
14549 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14550 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14552 for (i = 0; i < NUM_ACTIONS; i++)
14553 setHashEntry(action_hash, element_action_info[i].suffix,
14554 i_to_a(element_action_info[i].value));
14556 // do not store direction index (bit) here, but direction value!
14557 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14558 setHashEntry(direction_hash, element_direction_info[i].suffix,
14559 i_to_a(1 << element_direction_info[i].value));
14561 for (list = setup_file_list; list != NULL; list = list->next)
14563 char *element_token, *action_token, *direction_token;
14564 char *element_value, *action_value, *direction_value;
14565 int delay = atoi(list->value);
14567 if (strEqual(list->token, "end"))
14569 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14574 /* first try to break element into element/action/direction parts;
14575 if this does not work, also accept combined "element[.act][.dir]"
14576 elements (like "dynamite.active"), which are unique elements */
14578 if (strchr(list->token, '.') == NULL) // token contains no '.'
14580 element_value = getHashEntry(element_hash, list->token);
14581 if (element_value != NULL) // element found
14582 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14583 &num_list_entries);
14586 // no further suffixes found -- this is not an element
14587 print_unknown_token(filename, list->token, num_unknown_tokens++);
14593 // token has format "<prefix>.<something>"
14595 action_token = strchr(list->token, '.'); // suffix may be action ...
14596 direction_token = action_token; // ... or direction
14598 element_token = getStringCopy(list->token);
14599 *strchr(element_token, '.') = '\0';
14601 element_value = getHashEntry(element_hash, element_token);
14603 if (element_value == NULL) // this is no element
14605 element_value = getHashEntry(element_hash, list->token);
14606 if (element_value != NULL) // combined element found
14607 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14608 &num_list_entries);
14610 print_unknown_token(filename, list->token, num_unknown_tokens++);
14612 free(element_token);
14617 action_value = getHashEntry(action_hash, action_token);
14619 if (action_value != NULL) // action found
14621 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14622 &num_list_entries);
14624 free(element_token);
14629 direction_value = getHashEntry(direction_hash, direction_token);
14631 if (direction_value != NULL) // direction found
14633 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14634 &num_list_entries);
14636 free(element_token);
14641 if (strchr(action_token + 1, '.') == NULL)
14643 // no further suffixes found -- this is not an action nor direction
14645 element_value = getHashEntry(element_hash, list->token);
14646 if (element_value != NULL) // combined element found
14647 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14648 &num_list_entries);
14650 print_unknown_token(filename, list->token, num_unknown_tokens++);
14652 free(element_token);
14657 // token has format "<prefix>.<suffix>.<something>"
14659 direction_token = strchr(action_token + 1, '.');
14661 action_token = getStringCopy(action_token);
14662 *strchr(action_token + 1, '.') = '\0';
14664 action_value = getHashEntry(action_hash, action_token);
14666 if (action_value == NULL) // this is no action
14668 element_value = getHashEntry(element_hash, list->token);
14669 if (element_value != NULL) // combined element found
14670 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14671 &num_list_entries);
14673 print_unknown_token(filename, list->token, num_unknown_tokens++);
14675 free(element_token);
14676 free(action_token);
14681 direction_value = getHashEntry(direction_hash, direction_token);
14683 if (direction_value != NULL) // direction found
14685 add_helpanim_entry(atoi(element_value), atoi(action_value),
14686 atoi(direction_value), delay, &num_list_entries);
14688 free(element_token);
14689 free(action_token);
14694 // this is no direction
14696 element_value = getHashEntry(element_hash, list->token);
14697 if (element_value != NULL) // combined element found
14698 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14699 &num_list_entries);
14701 print_unknown_token(filename, list->token, num_unknown_tokens++);
14703 free(element_token);
14704 free(action_token);
14707 print_unknown_token_end(num_unknown_tokens);
14709 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14710 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14712 freeSetupFileList(setup_file_list);
14713 freeSetupFileHash(element_hash);
14714 freeSetupFileHash(action_hash);
14715 freeSetupFileHash(direction_hash);
14718 for (i = 0; i < num_list_entries; i++)
14719 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14720 EL_NAME(helpanim_info[i].element),
14721 helpanim_info[i].element,
14722 helpanim_info[i].action,
14723 helpanim_info[i].direction,
14724 helpanim_info[i].delay);
14728 void LoadHelpTextInfo(void)
14730 char *filename = getHelpTextFilename();
14733 if (helptext_info != NULL)
14735 freeSetupFileHash(helptext_info);
14736 helptext_info = NULL;
14739 if (fileExists(filename))
14740 helptext_info = loadSetupFileHash(filename);
14742 if (helptext_info == NULL)
14744 // use reliable default values from static configuration
14745 helptext_info = newSetupFileHash();
14747 for (i = 0; helptext_config[i].token; i++)
14748 setHashEntry(helptext_info,
14749 helptext_config[i].token,
14750 helptext_config[i].value);
14754 BEGIN_HASH_ITERATION(helptext_info, itr)
14756 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14757 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14759 END_HASH_ITERATION(hash, itr)
14764 // ----------------------------------------------------------------------------
14766 // ----------------------------------------------------------------------------
14768 #define MAX_NUM_CONVERT_LEVELS 1000
14770 void ConvertLevels(void)
14772 static LevelDirTree *convert_leveldir = NULL;
14773 static int convert_level_nr = -1;
14774 static int num_levels_handled = 0;
14775 static int num_levels_converted = 0;
14776 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14779 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14780 global.convert_leveldir);
14782 if (convert_leveldir == NULL)
14783 Fail("no such level identifier: '%s'", global.convert_leveldir);
14785 leveldir_current = convert_leveldir;
14787 if (global.convert_level_nr != -1)
14789 convert_leveldir->first_level = global.convert_level_nr;
14790 convert_leveldir->last_level = global.convert_level_nr;
14793 convert_level_nr = convert_leveldir->first_level;
14795 PrintLine("=", 79);
14796 Print("Converting levels\n");
14797 PrintLine("-", 79);
14798 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14799 Print("Level series name: '%s'\n", convert_leveldir->name);
14800 Print("Level series author: '%s'\n", convert_leveldir->author);
14801 Print("Number of levels: %d\n", convert_leveldir->levels);
14802 PrintLine("=", 79);
14805 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14806 levels_failed[i] = FALSE;
14808 while (convert_level_nr <= convert_leveldir->last_level)
14810 char *level_filename;
14813 level_nr = convert_level_nr++;
14815 Print("Level %03d: ", level_nr);
14817 LoadLevel(level_nr);
14818 if (level.no_level_file || level.no_valid_file)
14820 Print("(no level)\n");
14824 Print("converting level ... ");
14827 // special case: conversion of some EMC levels as requested by ACME
14828 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14831 level_filename = getDefaultLevelFilename(level_nr);
14832 new_level = !fileExists(level_filename);
14836 SaveLevel(level_nr);
14838 num_levels_converted++;
14840 Print("converted.\n");
14844 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14845 levels_failed[level_nr] = TRUE;
14847 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14850 num_levels_handled++;
14854 PrintLine("=", 79);
14855 Print("Number of levels handled: %d\n", num_levels_handled);
14856 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14857 (num_levels_handled ?
14858 num_levels_converted * 100 / num_levels_handled : 0));
14859 PrintLine("-", 79);
14860 Print("Summary (for automatic parsing by scripts):\n");
14861 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14862 convert_leveldir->identifier, num_levels_converted,
14863 num_levels_handled,
14864 (num_levels_handled ?
14865 num_levels_converted * 100 / num_levels_handled : 0));
14867 if (num_levels_handled != num_levels_converted)
14869 Print(", FAILED:");
14870 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14871 if (levels_failed[i])
14876 PrintLine("=", 79);
14878 CloseAllAndExit(0);
14882 // ----------------------------------------------------------------------------
14883 // create and save images for use in level sketches (raw BMP format)
14884 // ----------------------------------------------------------------------------
14886 void CreateLevelSketchImages(void)
14892 InitElementPropertiesGfxElement();
14894 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14895 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14897 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14899 int element = getMappedElement(i);
14900 char basename1[16];
14901 char basename2[16];
14905 sprintf(basename1, "%04d.bmp", i);
14906 sprintf(basename2, "%04ds.bmp", i);
14908 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14909 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14911 DrawSizedElement(0, 0, element, TILESIZE);
14912 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14914 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14915 Fail("cannot save level sketch image file '%s'", filename1);
14917 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14918 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14920 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14921 Fail("cannot save level sketch image file '%s'", filename2);
14926 // create corresponding SQL statements (for normal and small images)
14929 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14930 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14933 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14934 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14936 // optional: create content for forum level sketch demonstration post
14938 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14941 FreeBitmap(bitmap1);
14942 FreeBitmap(bitmap2);
14945 fprintf(stderr, "\n");
14947 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14949 CloseAllAndExit(0);
14953 // ----------------------------------------------------------------------------
14954 // create and save images for element collecting animations (raw BMP format)
14955 // ----------------------------------------------------------------------------
14957 static boolean createCollectImage(int element)
14959 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14962 void CreateCollectElementImages(void)
14966 int anim_frames = num_steps - 1;
14967 int tile_size = TILESIZE;
14968 int anim_width = tile_size * anim_frames;
14969 int anim_height = tile_size;
14970 int num_collect_images = 0;
14971 int pos_collect_images = 0;
14973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14974 if (createCollectImage(i))
14975 num_collect_images++;
14977 Info("Creating %d element collecting animation images ...",
14978 num_collect_images);
14980 int dst_width = anim_width * 2;
14981 int dst_height = anim_height * num_collect_images / 2;
14982 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14983 char *basename_bmp = "RocksCollect.bmp";
14984 char *basename_png = "RocksCollect.png";
14985 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14986 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14987 int len_filename_bmp = strlen(filename_bmp);
14988 int len_filename_png = strlen(filename_png);
14989 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14990 char cmd_convert[max_command_len];
14992 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14996 // force using RGBA surface for destination bitmap
14997 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14998 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15000 dst_bitmap->surface =
15001 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15003 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15005 if (!createCollectImage(i))
15008 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15009 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15010 int graphic = el2img(i);
15011 char *token_name = element_info[i].token_name;
15012 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15013 Bitmap *src_bitmap;
15016 Info("- creating collecting image for '%s' ...", token_name);
15018 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15020 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15021 tile_size, tile_size, 0, 0);
15023 // force using RGBA surface for temporary bitmap (using transparent black)
15024 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15025 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15027 tmp_bitmap->surface =
15028 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15030 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15032 for (j = 0; j < anim_frames; j++)
15034 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15035 int frame_size = frame_size_final * num_steps;
15036 int offset = (tile_size - frame_size_final) / 2;
15037 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15039 while (frame_size > frame_size_final)
15043 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15045 FreeBitmap(frame_bitmap);
15047 frame_bitmap = half_bitmap;
15050 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15051 frame_size_final, frame_size_final,
15052 dst_x + j * tile_size + offset, dst_y + offset);
15054 FreeBitmap(frame_bitmap);
15057 tmp_bitmap->surface_masked = NULL;
15059 FreeBitmap(tmp_bitmap);
15061 pos_collect_images++;
15064 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15065 Fail("cannot save element collecting image file '%s'", filename_bmp);
15067 FreeBitmap(dst_bitmap);
15069 Info("Converting image file from BMP to PNG ...");
15071 if (system(cmd_convert) != 0)
15072 Fail("converting image file failed");
15074 unlink(filename_bmp);
15078 CloseAllAndExit(0);
15082 // ----------------------------------------------------------------------------
15083 // create and save images for custom and group elements (raw BMP format)
15084 // ----------------------------------------------------------------------------
15086 void CreateCustomElementImages(char *directory)
15088 char *src_basename = "RocksCE-template.ilbm";
15089 char *dst_basename = "RocksCE.bmp";
15090 char *src_filename = getPath2(directory, src_basename);
15091 char *dst_filename = getPath2(directory, dst_basename);
15092 Bitmap *src_bitmap;
15094 int yoffset_ce = 0;
15095 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15098 InitVideoDefaults();
15100 ReCreateBitmap(&backbuffer, video.width, video.height);
15102 src_bitmap = LoadImage(src_filename);
15104 bitmap = CreateBitmap(TILEX * 16 * 2,
15105 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15108 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15115 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15116 TILEX * x, TILEY * y + yoffset_ce);
15118 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15120 TILEX * x + TILEX * 16,
15121 TILEY * y + yoffset_ce);
15123 for (j = 2; j >= 0; j--)
15127 BlitBitmap(src_bitmap, bitmap,
15128 TILEX + c * 7, 0, 6, 10,
15129 TILEX * x + 6 + j * 7,
15130 TILEY * y + 11 + yoffset_ce);
15132 BlitBitmap(src_bitmap, bitmap,
15133 TILEX + c * 8, TILEY, 6, 10,
15134 TILEX * 16 + TILEX * x + 6 + j * 8,
15135 TILEY * y + 10 + yoffset_ce);
15141 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15148 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15149 TILEX * x, TILEY * y + yoffset_ge);
15151 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15153 TILEX * x + TILEX * 16,
15154 TILEY * y + yoffset_ge);
15156 for (j = 1; j >= 0; j--)
15160 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15161 TILEX * x + 6 + j * 10,
15162 TILEY * y + 11 + yoffset_ge);
15164 BlitBitmap(src_bitmap, bitmap,
15165 TILEX + c * 8, TILEY + 12, 6, 10,
15166 TILEX * 16 + TILEX * x + 10 + j * 8,
15167 TILEY * y + 10 + yoffset_ge);
15173 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15174 Fail("cannot save CE graphics file '%s'", dst_filename);
15176 FreeBitmap(bitmap);
15178 CloseAllAndExit(0);