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_BD_SAND
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BD_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BD_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_BD_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BD_DIAMOND
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_explodes_to, EL_BD_EXPLODING_1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BD_EXPLODING_1
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_explodes_to, EL_BD_DIAMOND_GROWING_1
725 EL_BD_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BD_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BD_ROCK_GROWING_1
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BD_EXPLODING_1
743 EL_BD_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BD_DIAMOND
749 EL_BD_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BD_WALL
755 EL_BD_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BD_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BD_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BD_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BD_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BD_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BD_MAGIC_WALL, -1,
788 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
792 EL_BD_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
797 EL_BD_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
802 EL_BD_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
807 EL_BD_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
812 EL_BD_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
817 EL_BD_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
824 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
825 &li.bd_clock_extra_time, 30
829 EL_BD_VOODOO_DOLL, -1,
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
831 &li.bd_voodoo_collects_diamonds, FALSE
834 EL_BD_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
836 &li.bd_voodoo_hurt_kills_player, FALSE
839 EL_BD_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
841 &li.bd_voodoo_dies_by_rock, FALSE
844 EL_BD_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
846 &li.bd_voodoo_vanish_by_explosion, TRUE
849 EL_BD_VOODOO_DOLL, -1,
850 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
851 &li.bd_voodoo_penalty_time, 30
856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
857 &li.bd_slime_is_predictable, TRUE
861 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
862 &li.bd_slime_permeability_rate, 100
866 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
867 &li.bd_slime_permeability_bits_c64, 0
871 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
872 &li.bd_slime_random_seed_c64, -1
876 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
877 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
882 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
887 &li.bd_slime_eats_element_2, EL_BD_ROCK
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
892 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
897 &li.bd_slime_eats_element_3, EL_BD_NUT
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
902 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
907 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
908 &li.bd_acid_eats_element, EL_BD_SAND
912 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
913 &li.bd_acid_spread_rate, 3
917 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
918 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
923 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
924 &li.bd_biter_move_delay, 0
928 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
929 &li.bd_biter_eats_element, EL_BD_DIAMOND
934 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
935 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
939 EL_BD_EXPANDABLE_WALL_ANY, -1,
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
941 &li.bd_change_expanding_wall, FALSE
944 EL_BD_EXPANDABLE_WALL_ANY, -1,
945 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
946 &li.bd_expanding_wall_looks_like, EL_BD_WALL
950 EL_BD_REPLICATOR, -1,
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &li.bd_replicators_active, TRUE
955 EL_BD_REPLICATOR, -1,
956 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
957 &li.bd_replicator_create_delay, 4
961 EL_BD_CONVEYOR_LEFT, -1,
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
963 &li.bd_conveyor_belts_active, TRUE
966 EL_BD_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
968 &li.bd_conveyor_belts_changed, FALSE
973 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
974 &li.bd_water_cannot_flow_down, FALSE
979 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
980 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
984 EL_BD_PNEUMATIC_HAMMER, -1,
985 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
986 &li.bd_hammer_walls_break_delay, 5
989 EL_BD_PNEUMATIC_HAMMER, -1,
990 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
991 &li.bd_hammer_walls_reappear, FALSE
994 EL_BD_PNEUMATIC_HAMMER, -1,
995 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
996 &li.bd_hammer_walls_reappear_delay, 100
1000 EL_BD_ROCKET_LAUNCHER, -1,
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1002 &li.bd_infinite_rockets, FALSE
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1008 &li.bd_num_skeletons_needed_for_pot, 5
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1013 &li.bd_skeleton_worth_num_diamonds, 0
1017 EL_BD_CREATURE_SWITCH, -1,
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1019 &li.bd_creatures_start_backwards, FALSE
1022 EL_BD_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1024 &li.bd_creatures_turn_on_hatching, FALSE
1027 EL_BD_CREATURE_SWITCH, -1,
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1029 &li.bd_creatures_auto_turn_delay, 0
1033 EL_BD_GRAVITY_SWITCH, -1,
1034 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1035 &li.bd_gravity_direction, GD_MV_DOWN
1038 EL_BD_GRAVITY_SWITCH, -1,
1039 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1040 &li.bd_gravity_switch_active, FALSE
1043 EL_BD_GRAVITY_SWITCH, -1,
1044 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1045 &li.bd_gravity_switch_delay, 10
1048 EL_BD_GRAVITY_SWITCH, -1,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1050 &li.bd_gravity_affects_all, TRUE
1053 // (the following values are related to various game elements)
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.score[SC_EMERALD], 10
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.score[SC_DIAMOND], 10
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1070 &li.score[SC_BUG], 10
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1076 &li.score[SC_SPACESHIP], 10
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1082 &li.score[SC_PACMAN], 10
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1088 &li.score[SC_NUT], 10
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1094 &li.score[SC_DYNAMITE], 10
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1100 &li.score[SC_KEY], 10
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1106 &li.score[SC_PEARL], 10
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1112 &li.score[SC_CRYSTAL], 10
1115 // (amoeba values used by R'n'D game engine only)
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1119 &li.amoeba_content, EL_DIAMOND
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1124 &li.amoeba_speed, 10
1128 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1129 &li.grow_into_diggable, TRUE
1131 // (amoeba values used by BD game engine only)
1134 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1135 &li.bd_amoeba_wait_for_hatching, FALSE
1139 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1140 &li.bd_amoeba_start_immediately, TRUE
1144 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1145 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1150 &li.bd_amoeba_threshold_too_big, 200
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1155 &li.bd_amoeba_slow_growth_time, 200
1159 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1160 &li.bd_amoeba_slow_growth_rate, 3
1164 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1165 &li.bd_amoeba_fast_growth_rate, 25
1169 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1170 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1175 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1181 &li.bd_amoeba_2_threshold_too_big, 200
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1186 &li.bd_amoeba_2_slow_growth_time, 200
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1191 &li.bd_amoeba_2_slow_growth_rate, 3
1195 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1196 &li.bd_amoeba_2_fast_growth_rate, 25
1200 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1201 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1205 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1206 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1211 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1215 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1216 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1221 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1222 &li.yamyam_content, EL_ROCK, NULL,
1223 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1227 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1228 &li.score[SC_YAMYAM], 10
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1234 &li.score[SC_ROBOT], 10
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1244 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1250 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1251 &li.time_magic_wall, 10
1255 EL_GAME_OF_LIFE, -1,
1256 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1257 &li.game_of_life[0], 2
1260 EL_GAME_OF_LIFE, -1,
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1262 &li.game_of_life[1], 3
1265 EL_GAME_OF_LIFE, -1,
1266 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1267 &li.game_of_life[2], 3
1270 EL_GAME_OF_LIFE, -1,
1271 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1272 &li.game_of_life[3], 3
1275 EL_GAME_OF_LIFE, -1,
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1277 &li.use_life_bugs, FALSE
1282 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1287 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1292 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1297 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1302 EL_TIMEGATE_SWITCH, -1,
1303 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1304 &li.time_timegate, 10
1308 EL_LIGHT_SWITCH_ACTIVE, -1,
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1314 EL_SHIELD_NORMAL, -1,
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1316 &li.shield_normal_time, 10
1319 EL_SHIELD_NORMAL, -1,
1320 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1321 &li.score[SC_SHIELD], 10
1325 EL_SHIELD_DEADLY, -1,
1326 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1327 &li.shield_deadly_time, 10
1330 EL_SHIELD_DEADLY, -1,
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1332 &li.score[SC_SHIELD], 10
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1343 &li.extra_time_score, 10
1347 EL_TIME_ORB_FULL, -1,
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1349 &li.time_orb_time, 10
1352 EL_TIME_ORB_FULL, -1,
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1354 &li.use_time_orb_bug, FALSE
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1360 &li.use_spring_bug, FALSE
1365 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1366 &li.android_move_time, 10
1370 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1371 &li.android_clone_time, 10
1374 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1375 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1376 &li.android_clone_element[0], EL_EMPTY, NULL,
1377 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1381 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1382 &li.android_clone_element[0], EL_EMPTY, NULL,
1383 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1388 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1389 &li.lenses_score, 10
1393 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1398 EL_EMC_MAGNIFIER, -1,
1399 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1400 &li.magnify_score, 10
1403 EL_EMC_MAGNIFIER, -1,
1404 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1405 &li.magnify_time, 10
1409 EL_EMC_MAGIC_BALL, -1,
1410 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1414 EL_EMC_MAGIC_BALL, -1,
1415 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1416 &li.ball_random, FALSE
1419 EL_EMC_MAGIC_BALL, -1,
1420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1421 &li.ball_active_initial, FALSE
1424 EL_EMC_MAGIC_BALL, -1,
1425 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1426 &li.ball_content, EL_EMPTY, NULL,
1427 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1431 EL_SOKOBAN_FIELD_EMPTY, -1,
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1433 &li.sb_fields_needed, TRUE
1437 EL_SOKOBAN_OBJECT, -1,
1438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1439 &li.sb_objects_needed, TRUE
1444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1445 &li.mm_laser_red, FALSE
1449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1450 &li.mm_laser_green, FALSE
1454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1455 &li.mm_laser_blue, TRUE
1460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1461 &li.df_laser_red, TRUE
1465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1466 &li.df_laser_green, TRUE
1470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1471 &li.df_laser_blue, FALSE
1475 EL_MM_FUSE_ACTIVE, -1,
1476 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1477 &li.mm_time_fuse, 25
1481 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1482 &li.mm_time_bomb, 75
1486 EL_MM_GRAY_BALL, -1,
1487 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1488 &li.mm_time_ball, 75
1491 EL_MM_GRAY_BALL, -1,
1492 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1493 &li.mm_ball_choice_mode, ANIM_RANDOM
1496 EL_MM_GRAY_BALL, -1,
1497 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1498 &li.mm_ball_content, EL_EMPTY, NULL,
1499 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1502 EL_MM_GRAY_BALL, -1,
1503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1504 &li.rotate_mm_ball_content, TRUE
1507 EL_MM_GRAY_BALL, -1,
1508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1509 &li.explode_mm_ball, FALSE
1513 EL_MM_STEEL_BLOCK, -1,
1514 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1515 &li.mm_time_block, 75
1518 EL_MM_LIGHTBALL, -1,
1519 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1520 &li.score[SC_ELEM_BONUS], 10
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1534 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1535 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1539 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1540 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1545 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1546 &xx_envelope.autowrap, FALSE
1550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1551 &xx_envelope.centered, FALSE
1556 TYPE_STRING, CONF_VALUE_BYTES(1),
1557 &xx_envelope.text, -1, NULL,
1558 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1559 &xx_default_string_empty[0]
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1573 TYPE_STRING, CONF_VALUE_BYTES(1),
1574 &xx_ei.description[0], -1,
1575 &yy_ei.description[0],
1576 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1577 &xx_default_description[0]
1582 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1583 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1586 #if ENABLE_RESERVED_CODE
1587 // (reserved for later use)
1590 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1591 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1599 &xx_ei.use_gfx_element, FALSE,
1600 &yy_ei.use_gfx_element
1604 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1605 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1606 &yy_ei.gfx_element_initial
1611 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1612 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1613 &yy_ei.access_direction
1618 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1619 &xx_ei.collect_score_initial, 10,
1620 &yy_ei.collect_score_initial
1624 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1625 &xx_ei.collect_count_initial, 1,
1626 &yy_ei.collect_count_initial
1631 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1632 &xx_ei.ce_value_fixed_initial, 0,
1633 &yy_ei.ce_value_fixed_initial
1637 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1638 &xx_ei.ce_value_random_initial, 0,
1639 &yy_ei.ce_value_random_initial
1643 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1644 &xx_ei.use_last_ce_value, FALSE,
1645 &yy_ei.use_last_ce_value
1650 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1651 &xx_ei.push_delay_fixed, 8,
1652 &yy_ei.push_delay_fixed
1656 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1657 &xx_ei.push_delay_random, 8,
1658 &yy_ei.push_delay_random
1662 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1663 &xx_ei.drop_delay_fixed, 0,
1664 &yy_ei.drop_delay_fixed
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1669 &xx_ei.drop_delay_random, 0,
1670 &yy_ei.drop_delay_random
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1675 &xx_ei.move_delay_fixed, 0,
1676 &yy_ei.move_delay_fixed
1680 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1681 &xx_ei.move_delay_random, 0,
1682 &yy_ei.move_delay_random
1686 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1687 &xx_ei.step_delay_fixed, 0,
1688 &yy_ei.step_delay_fixed
1692 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1693 &xx_ei.step_delay_random, 0,
1694 &yy_ei.step_delay_random
1699 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1700 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1705 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1706 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1707 &yy_ei.move_direction_initial
1711 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1712 &xx_ei.move_stepsize, TILEX / 8,
1713 &yy_ei.move_stepsize
1718 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1719 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1720 &yy_ei.move_enter_element
1724 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1725 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1726 &yy_ei.move_leave_element
1730 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1731 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1732 &yy_ei.move_leave_type
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1738 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1739 &yy_ei.slippery_type
1744 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1745 &xx_ei.explosion_type, EXPLODES_3X3,
1746 &yy_ei.explosion_type
1750 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1751 &xx_ei.explosion_delay, 16,
1752 &yy_ei.explosion_delay
1756 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1757 &xx_ei.ignition_delay, 8,
1758 &yy_ei.ignition_delay
1763 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1764 &xx_ei.content, EL_EMPTY_SPACE,
1766 &xx_num_contents, 1, 1
1769 // ---------- "num_change_pages" must be the last entry ---------------------
1772 -1, SAVE_CONF_ALWAYS,
1773 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1774 &xx_ei.num_change_pages, 1,
1775 &yy_ei.num_change_pages
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1788 // ---------- "current_change_page" must be the first entry -----------------
1791 -1, SAVE_CONF_ALWAYS,
1792 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1793 &xx_current_change_page, -1
1796 // ---------- (the remaining entries can be in any order) -------------------
1800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1801 &xx_change.can_change, FALSE
1806 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1807 &xx_event_bits[0], 0
1811 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1812 &xx_event_bits[1], 0
1817 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1818 &xx_change.trigger_player, CH_PLAYER_ANY
1822 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1823 &xx_change.trigger_side, CH_SIDE_ANY
1827 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1828 &xx_change.trigger_page, CH_PAGE_ANY
1833 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1834 &xx_change.target_element, EL_EMPTY_SPACE
1839 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1840 &xx_change.delay_fixed, 0
1844 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1845 &xx_change.delay_random, 0
1849 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1850 &xx_change.delay_frames, FRAMES_PER_SECOND
1855 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1856 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1862 &xx_change.explode, FALSE
1866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1867 &xx_change.use_target_content, FALSE
1871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1872 &xx_change.only_if_complete, FALSE
1876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1877 &xx_change.use_random_replace, FALSE
1881 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1882 &xx_change.random_percentage, 100
1886 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1887 &xx_change.replace_when, CP_WHEN_EMPTY
1892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1893 &xx_change.has_action, FALSE
1897 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1898 &xx_change.action_type, CA_NO_ACTION
1902 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1903 &xx_change.action_mode, CA_MODE_UNDEFINED
1907 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1908 &xx_change.action_arg, CA_ARG_UNDEFINED
1913 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1914 &xx_change.action_element, EL_EMPTY_SPACE
1919 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1920 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1921 &xx_num_contents, 1, 1
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1935 TYPE_STRING, CONF_VALUE_BYTES(1),
1936 &xx_ei.description[0], -1, NULL,
1937 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1938 &xx_default_description[0]
1943 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1944 &xx_ei.use_gfx_element, FALSE
1948 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1949 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1954 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1955 &xx_group.choice_mode, ANIM_RANDOM
1960 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1961 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1962 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1977 &xx_ei.use_gfx_element, FALSE
1981 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1982 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1992 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1997 &li.block_snap_field, TRUE
2001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2002 &li.continuous_snapping, TRUE
2006 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2007 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2011 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2012 &li.use_start_element[0], FALSE
2016 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2017 &li.start_element[0], EL_PLAYER_1
2021 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2022 &li.use_artwork_element[0], FALSE
2026 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2027 &li.artwork_element[0], EL_PLAYER_1
2031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2032 &li.use_explosion_element[0], FALSE
2036 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2037 &li.explosion_element[0], EL_PLAYER_1
2052 filetype_id_list[] =
2054 { LEVEL_FILE_TYPE_RND, "RND" },
2055 { LEVEL_FILE_TYPE_BD, "BD" },
2056 { LEVEL_FILE_TYPE_EM, "EM" },
2057 { LEVEL_FILE_TYPE_SP, "SP" },
2058 { LEVEL_FILE_TYPE_DX, "DX" },
2059 { LEVEL_FILE_TYPE_SB, "SB" },
2060 { LEVEL_FILE_TYPE_DC, "DC" },
2061 { LEVEL_FILE_TYPE_MM, "MM" },
2062 { LEVEL_FILE_TYPE_MM, "DF" },
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2071 static boolean check_special_flags(char *flag)
2073 if (strEqual(options.special_flags, flag) ||
2074 strEqual(leveldir_current->special_flags, flag))
2080 static struct DateInfo getCurrentDate(void)
2082 time_t epoch_seconds = time(NULL);
2083 struct tm *now = localtime(&epoch_seconds);
2084 struct DateInfo date;
2086 date.year = now->tm_year + 1900;
2087 date.month = now->tm_mon + 1;
2088 date.day = now->tm_mday;
2090 date.src = DATE_SRC_CLOCK;
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2099 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100 change->has_event[i] = FALSE;
2103 static void resetEventBits(void)
2107 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108 xx_event_bits[i] = 0;
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2115 /* important: only change event flag if corresponding event bit is set
2116 (this is because all xx_event_bits[] values are loaded separately,
2117 and all xx_event_bits[] values are set back to zero before loading
2118 another value xx_event_bits[x] (each value representing 32 flags)) */
2120 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122 change->has_event[i] = TRUE;
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2129 /* in contrast to the above function setEventFlagsFromEventBits(), it
2130 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131 depending on the corresponding change->has_event[i] values here, as
2132 all xx_event_bits[] values are reset in resetEventBits() before */
2134 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135 if (change->has_event[i])
2136 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2141 static char description[MAX_ELEMENT_NAME_LEN + 1];
2142 char *default_description = (ei->custom_description != NULL ?
2143 ei->custom_description :
2144 ei->editor_description);
2147 // always start with reliable default values
2148 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149 description[i] = '\0';
2151 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2154 return &description[0];
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2159 char *default_description = getDefaultElementDescription(ei);
2162 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163 ei->description[i] = default_description[i];
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2170 for (i = 0; conf[i].data_type != -1; i++)
2172 int default_value = conf[i].default_value;
2173 int data_type = conf[i].data_type;
2174 int conf_type = conf[i].conf_type;
2175 int byte_mask = conf_type & CONF_MASK_BYTES;
2177 if (byte_mask == CONF_MASK_MULTI_BYTES)
2179 int default_num_entities = conf[i].default_num_entities;
2180 int max_num_entities = conf[i].max_num_entities;
2182 *(int *)(conf[i].num_entities) = default_num_entities;
2184 if (data_type == TYPE_STRING)
2186 char *default_string = conf[i].default_string;
2187 char *string = (char *)(conf[i].value);
2189 strncpy(string, default_string, max_num_entities);
2191 else if (data_type == TYPE_ELEMENT_LIST)
2193 int *element_array = (int *)(conf[i].value);
2196 for (j = 0; j < max_num_entities; j++)
2197 element_array[j] = default_value;
2199 else if (data_type == TYPE_CONTENT_LIST)
2201 struct Content *content = (struct Content *)(conf[i].value);
2204 for (c = 0; c < max_num_entities; c++)
2205 for (y = 0; y < 3; y++)
2206 for (x = 0; x < 3; x++)
2207 content[c].e[x][y] = default_value;
2210 else // constant size configuration data (1, 2 or 4 bytes)
2212 if (data_type == TYPE_BOOLEAN)
2213 *(boolean *)(conf[i].value) = default_value;
2215 *(int *) (conf[i].value) = default_value;
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2224 for (i = 0; conf[i].data_type != -1; i++)
2226 int data_type = conf[i].data_type;
2227 int conf_type = conf[i].conf_type;
2228 int byte_mask = conf_type & CONF_MASK_BYTES;
2230 if (byte_mask == CONF_MASK_MULTI_BYTES)
2232 int max_num_entities = conf[i].max_num_entities;
2234 if (data_type == TYPE_STRING)
2236 char *string = (char *)(conf[i].value);
2237 char *string_copy = (char *)(conf[i].value_copy);
2239 strncpy(string_copy, string, max_num_entities);
2241 else if (data_type == TYPE_ELEMENT_LIST)
2243 int *element_array = (int *)(conf[i].value);
2244 int *element_array_copy = (int *)(conf[i].value_copy);
2247 for (j = 0; j < max_num_entities; j++)
2248 element_array_copy[j] = element_array[j];
2250 else if (data_type == TYPE_CONTENT_LIST)
2252 struct Content *content = (struct Content *)(conf[i].value);
2253 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2256 for (c = 0; c < max_num_entities; c++)
2257 for (y = 0; y < 3; y++)
2258 for (x = 0; x < 3; x++)
2259 content_copy[c].e[x][y] = content[c].e[x][y];
2262 else // constant size configuration data (1, 2 or 4 bytes)
2264 if (data_type == TYPE_BOOLEAN)
2265 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2267 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2276 xx_ei = *ei_from; // copy element data into temporary buffer
2277 yy_ei = *ei_to; // copy element data into temporary buffer
2279 copyConfigFromConfigList(chunk_config_CUSX_base);
2284 // ---------- reinitialize and copy change pages ----------
2286 ei_to->num_change_pages = ei_from->num_change_pages;
2287 ei_to->current_change_page = ei_from->current_change_page;
2289 setElementChangePages(ei_to, ei_to->num_change_pages);
2291 for (i = 0; i < ei_to->num_change_pages; i++)
2292 ei_to->change_page[i] = ei_from->change_page[i];
2294 // ---------- copy group element info ----------
2295 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2296 *ei_to->group = *ei_from->group;
2298 // mark this custom element as modified
2299 ei_to->modified_settings = TRUE;
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2304 int change_page_size = sizeof(struct ElementChangeInfo);
2306 ei->num_change_pages = MAX(1, change_pages);
2309 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2311 if (ei->current_change_page >= ei->num_change_pages)
2312 ei->current_change_page = ei->num_change_pages - 1;
2314 ei->change = &ei->change_page[ei->current_change_page];
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2319 xx_change = *change; // copy change data into temporary buffer
2321 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2323 *change = xx_change;
2325 resetEventFlags(change);
2327 change->direct_action = 0;
2328 change->other_action = 0;
2330 change->pre_change_function = NULL;
2331 change->change_function = NULL;
2332 change->post_change_function = NULL;
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2339 li = *level; // copy level data into temporary buffer
2340 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2341 *level = li; // copy temporary buffer back to level data
2343 setLevelInfoToDefaults_BD();
2344 setLevelInfoToDefaults_EM();
2345 setLevelInfoToDefaults_SP();
2346 setLevelInfoToDefaults_MM();
2348 level->native_bd_level = &native_bd_level;
2349 level->native_em_level = &native_em_level;
2350 level->native_sp_level = &native_sp_level;
2351 level->native_mm_level = &native_mm_level;
2353 level->file_version = FILE_VERSION_ACTUAL;
2354 level->game_version = GAME_VERSION_ACTUAL;
2356 level->creation_date = getCurrentDate();
2358 level->encoding_16bit_field = TRUE;
2359 level->encoding_16bit_yamyam = TRUE;
2360 level->encoding_16bit_amoeba = TRUE;
2362 // clear level name and level author string buffers
2363 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2364 level->name[i] = '\0';
2365 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2366 level->author[i] = '\0';
2368 // set level name and level author to default values
2369 strcpy(level->name, NAMELESS_LEVEL_NAME);
2370 strcpy(level->author, ANONYMOUS_NAME);
2372 // set level playfield to playable default level with player and exit
2373 for (x = 0; x < MAX_LEV_FIELDX; x++)
2374 for (y = 0; y < MAX_LEV_FIELDY; y++)
2375 level->field[x][y] = EL_SAND;
2377 level->field[0][0] = EL_PLAYER_1;
2378 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2380 BorderElement = EL_STEELWALL;
2382 // detect custom elements when loading them
2383 level->file_has_custom_elements = FALSE;
2385 // set default color type and colors for BD style level colors
2386 SetDefaultLevelColorType_BD();
2387 SetDefaultLevelColors_BD();
2389 // set all bug compatibility flags to "false" => do not emulate this bug
2390 level->use_action_after_change_bug = FALSE;
2392 if (leveldir_current)
2394 // try to determine better author name than 'anonymous'
2395 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2397 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2398 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2402 switch (LEVELCLASS(leveldir_current))
2404 case LEVELCLASS_TUTORIAL:
2405 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2408 case LEVELCLASS_CONTRIB:
2409 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2410 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2413 case LEVELCLASS_PRIVATE:
2414 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2415 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2419 // keep default value
2426 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2428 static boolean clipboard_elements_initialized = FALSE;
2431 InitElementPropertiesStatic();
2433 li = *level; // copy level data into temporary buffer
2434 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2435 *level = li; // copy temporary buffer back to level data
2437 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2440 struct ElementInfo *ei = &element_info[element];
2442 if (element == EL_MM_GRAY_BALL)
2444 struct LevelInfo_MM *level_mm = level->native_mm_level;
2447 for (j = 0; j < level->num_mm_ball_contents; j++)
2448 level->mm_ball_content[j] =
2449 map_element_MM_to_RND(level_mm->ball_content[j]);
2452 // never initialize clipboard elements after the very first time
2453 // (to be able to use clipboard elements between several levels)
2454 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2457 if (IS_ENVELOPE(element))
2459 int envelope_nr = element - EL_ENVELOPE_1;
2461 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2463 level->envelope[envelope_nr] = xx_envelope;
2466 if (IS_CUSTOM_ELEMENT(element) ||
2467 IS_GROUP_ELEMENT(element) ||
2468 IS_INTERNAL_ELEMENT(element))
2470 xx_ei = *ei; // copy element data into temporary buffer
2472 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2477 setElementChangePages(ei, 1);
2478 setElementChangeInfoToDefaults(ei->change);
2480 if (IS_CUSTOM_ELEMENT(element) ||
2481 IS_GROUP_ELEMENT(element))
2483 setElementDescriptionToDefault(ei);
2485 ei->modified_settings = FALSE;
2488 if (IS_CUSTOM_ELEMENT(element) ||
2489 IS_INTERNAL_ELEMENT(element))
2491 // internal values used in level editor
2493 ei->access_type = 0;
2494 ei->access_layer = 0;
2495 ei->access_protected = 0;
2496 ei->walk_to_action = 0;
2497 ei->smash_targets = 0;
2500 ei->can_explode_by_fire = FALSE;
2501 ei->can_explode_smashed = FALSE;
2502 ei->can_explode_impact = FALSE;
2504 ei->current_change_page = 0;
2507 if (IS_GROUP_ELEMENT(element) ||
2508 IS_INTERNAL_ELEMENT(element))
2510 struct ElementGroupInfo *group;
2512 // initialize memory for list of elements in group
2513 if (ei->group == NULL)
2514 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2518 xx_group = *group; // copy group data into temporary buffer
2520 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2525 if (IS_EMPTY_ELEMENT(element) ||
2526 IS_INTERNAL_ELEMENT(element))
2528 xx_ei = *ei; // copy element data into temporary buffer
2530 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2536 clipboard_elements_initialized = TRUE;
2539 static void setLevelInfoToDefaults(struct LevelInfo *level,
2540 boolean level_info_only,
2541 boolean reset_file_status)
2543 setLevelInfoToDefaults_Level(level);
2545 if (!level_info_only)
2546 setLevelInfoToDefaults_Elements(level);
2548 if (reset_file_status)
2550 level->no_valid_file = FALSE;
2551 level->no_level_file = FALSE;
2554 level->changed = FALSE;
2557 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2559 level_file_info->nr = 0;
2560 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2561 level_file_info->packed = FALSE;
2563 setString(&level_file_info->basename, NULL);
2564 setString(&level_file_info->filename, NULL);
2567 int getMappedElement_SB(int, boolean);
2569 static void ActivateLevelTemplate(void)
2573 if (check_special_flags("load_xsb_to_ces"))
2575 // fill smaller playfields with padding "beyond border wall" elements
2576 if (level.fieldx < level_template.fieldx ||
2577 level.fieldy < level_template.fieldy)
2579 short field[level.fieldx][level.fieldy];
2580 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2581 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2582 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2583 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2585 // copy old playfield (which is smaller than the visible area)
2586 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2587 field[x][y] = level.field[x][y];
2589 // fill new, larger playfield with "beyond border wall" elements
2590 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2591 level.field[x][y] = getMappedElement_SB('_', TRUE);
2593 // copy the old playfield to the middle of the new playfield
2594 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2595 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2597 level.fieldx = new_fieldx;
2598 level.fieldy = new_fieldy;
2602 // Currently there is no special action needed to activate the template
2603 // data, because 'element_info' property settings overwrite the original
2604 // level data, while all other variables do not change.
2606 // Exception: 'from_level_template' elements in the original level playfield
2607 // are overwritten with the corresponding elements at the same position in
2608 // playfield from the level template.
2610 for (x = 0; x < level.fieldx; x++)
2611 for (y = 0; y < level.fieldy; y++)
2612 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2613 level.field[x][y] = level_template.field[x][y];
2615 if (check_special_flags("load_xsb_to_ces"))
2617 struct LevelInfo level_backup = level;
2619 // overwrite all individual level settings from template level settings
2620 level = level_template;
2622 // restore level file info
2623 level.file_info = level_backup.file_info;
2625 // restore playfield size
2626 level.fieldx = level_backup.fieldx;
2627 level.fieldy = level_backup.fieldy;
2629 // restore playfield content
2630 for (x = 0; x < level.fieldx; x++)
2631 for (y = 0; y < level.fieldy; y++)
2632 level.field[x][y] = level_backup.field[x][y];
2634 // restore name and author from individual level
2635 strcpy(level.name, level_backup.name);
2636 strcpy(level.author, level_backup.author);
2638 // restore flag "use_custom_template"
2639 level.use_custom_template = level_backup.use_custom_template;
2643 static boolean checkForPackageFromBasename_BD(char *basename)
2645 // check for native BD level file extensions
2646 if (!strSuffixLower(basename, ".bd") &&
2647 !strSuffixLower(basename, ".bdr") &&
2648 !strSuffixLower(basename, ".brc") &&
2649 !strSuffixLower(basename, ".gds"))
2652 // check for standard single-level BD files (like "001.bd")
2653 if (strSuffixLower(basename, ".bd") &&
2654 strlen(basename) == 6 &&
2655 basename[0] >= '0' && basename[0] <= '9' &&
2656 basename[1] >= '0' && basename[1] <= '9' &&
2657 basename[2] >= '0' && basename[2] <= '9')
2660 // this is a level package in native BD file format
2664 static char *getLevelFilenameFromBasename(char *basename)
2666 static char *filename = NULL;
2668 checked_free(filename);
2670 filename = getPath2(getCurrentLevelDir(), basename);
2675 static int getFileTypeFromBasename(char *basename)
2677 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2679 static char *filename = NULL;
2680 struct stat file_status;
2682 // ---------- try to determine file type from filename ----------
2684 // check for typical filename of a Supaplex level package file
2685 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2686 return LEVEL_FILE_TYPE_SP;
2688 // check for typical filename of a Diamond Caves II level package file
2689 if (strSuffixLower(basename, ".dc") ||
2690 strSuffixLower(basename, ".dc2"))
2691 return LEVEL_FILE_TYPE_DC;
2693 // check for typical filename of a Sokoban level package file
2694 if (strSuffixLower(basename, ".xsb") &&
2695 strchr(basename, '%') == NULL)
2696 return LEVEL_FILE_TYPE_SB;
2698 // check for typical filename of a Boulder Dash (GDash) level package file
2699 if (checkForPackageFromBasename_BD(basename))
2700 return LEVEL_FILE_TYPE_BD;
2702 // ---------- try to determine file type from filesize ----------
2704 checked_free(filename);
2705 filename = getPath2(getCurrentLevelDir(), basename);
2707 if (stat(filename, &file_status) == 0)
2709 // check for typical filesize of a Supaplex level package file
2710 if (file_status.st_size == 170496)
2711 return LEVEL_FILE_TYPE_SP;
2714 return LEVEL_FILE_TYPE_UNKNOWN;
2717 static int getFileTypeFromMagicBytes(char *filename, int type)
2721 if ((file = openFile(filename, MODE_READ)))
2723 char chunk_name[CHUNK_ID_LEN + 1];
2725 getFileChunkBE(file, chunk_name, NULL);
2727 if (strEqual(chunk_name, "MMII") ||
2728 strEqual(chunk_name, "MIRR"))
2729 type = LEVEL_FILE_TYPE_MM;
2737 static boolean checkForPackageFromBasename(char *basename)
2739 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2740 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2742 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2745 static char *getSingleLevelBasenameExt(int nr, char *extension)
2747 static char basename[MAX_FILENAME_LEN];
2750 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2752 sprintf(basename, "%03d.%s", nr, extension);
2757 static char *getSingleLevelBasename(int nr)
2759 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2762 static char *getPackedLevelBasename(int type)
2764 static char basename[MAX_FILENAME_LEN];
2765 char *directory = getCurrentLevelDir();
2767 DirectoryEntry *dir_entry;
2769 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2771 if ((dir = openDirectory(directory)) == NULL)
2773 Warn("cannot read current level directory '%s'", directory);
2778 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2780 char *entry_basename = dir_entry->basename;
2781 int entry_type = getFileTypeFromBasename(entry_basename);
2783 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2785 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2788 strcpy(basename, entry_basename);
2795 closeDirectory(dir);
2800 static char *getSingleLevelFilename(int nr)
2802 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2805 #if ENABLE_UNUSED_CODE
2806 static char *getPackedLevelFilename(int type)
2808 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2812 char *getDefaultLevelFilename(int nr)
2814 return getSingleLevelFilename(nr);
2817 #if ENABLE_UNUSED_CODE
2818 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2822 lfi->packed = FALSE;
2824 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2825 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2829 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2830 int type, char *format, ...)
2832 static char basename[MAX_FILENAME_LEN];
2835 va_start(ap, format);
2836 vsprintf(basename, format, ap);
2840 lfi->packed = FALSE;
2842 setString(&lfi->basename, basename);
2843 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2846 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2852 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2853 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2856 static int getFiletypeFromID(char *filetype_id)
2858 char *filetype_id_lower;
2859 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2862 if (filetype_id == NULL)
2863 return LEVEL_FILE_TYPE_UNKNOWN;
2865 filetype_id_lower = getStringToLower(filetype_id);
2867 for (i = 0; filetype_id_list[i].id != NULL; i++)
2869 char *id_lower = getStringToLower(filetype_id_list[i].id);
2871 if (strEqual(filetype_id_lower, id_lower))
2872 filetype = filetype_id_list[i].filetype;
2876 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2880 free(filetype_id_lower);
2885 char *getLocalLevelTemplateFilename(void)
2887 return getDefaultLevelFilename(-1);
2890 char *getGlobalLevelTemplateFilename(void)
2892 // global variable "leveldir_current" must be modified in the loop below
2893 LevelDirTree *leveldir_current_last = leveldir_current;
2894 char *filename = NULL;
2896 // check for template level in path from current to topmost tree node
2898 while (leveldir_current != NULL)
2900 filename = getDefaultLevelFilename(-1);
2902 if (fileExists(filename))
2905 leveldir_current = leveldir_current->node_parent;
2908 // restore global variable "leveldir_current" modified in above loop
2909 leveldir_current = leveldir_current_last;
2914 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2918 // special case: level number is negative => check for level template file
2921 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2922 getSingleLevelBasename(-1));
2924 // replace local level template filename with global template filename
2925 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2927 // no fallback if template file not existing
2931 // special case: check for file name/pattern specified in "levelinfo.conf"
2932 if (leveldir_current->level_filename != NULL)
2934 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2936 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2937 leveldir_current->level_filename, nr);
2939 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2941 if (fileExists(lfi->filename))
2944 else if (leveldir_current->level_filetype != NULL)
2946 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2948 // check for specified native level file with standard file name
2949 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2950 "%03d.%s", nr, LEVELFILE_EXTENSION);
2951 if (fileExists(lfi->filename))
2955 // check for native Rocks'n'Diamonds level file
2956 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2957 "%03d.%s", nr, LEVELFILE_EXTENSION);
2958 if (fileExists(lfi->filename))
2961 // check for native Boulder Dash level file
2962 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2963 if (fileExists(lfi->filename))
2966 // check for Emerald Mine level file (V1)
2967 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2968 'a' + (nr / 10) % 26, '0' + nr % 10);
2969 if (fileExists(lfi->filename))
2971 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2972 'A' + (nr / 10) % 26, '0' + nr % 10);
2973 if (fileExists(lfi->filename))
2976 // check for Emerald Mine level file (V2 to V5)
2977 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2978 if (fileExists(lfi->filename))
2981 // check for Emerald Mine level file (V6 / single mode)
2982 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2983 if (fileExists(lfi->filename))
2985 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2986 if (fileExists(lfi->filename))
2989 // check for Emerald Mine level file (V6 / teamwork mode)
2990 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2991 if (fileExists(lfi->filename))
2993 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2994 if (fileExists(lfi->filename))
2997 // check for various packed level file formats
2998 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2999 if (fileExists(lfi->filename))
3002 // no known level file found -- use default values (and fail later)
3003 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3004 "%03d.%s", nr, LEVELFILE_EXTENSION);
3007 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3009 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3010 lfi->type = getFileTypeFromBasename(lfi->basename);
3012 if (lfi->type == LEVEL_FILE_TYPE_RND)
3013 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3016 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3018 // always start with reliable default values
3019 setFileInfoToDefaults(level_file_info);
3021 level_file_info->nr = nr; // set requested level number
3023 determineLevelFileInfo_Filename(level_file_info);
3024 determineLevelFileInfo_Filetype(level_file_info);
3027 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3028 struct LevelFileInfo *lfi_to)
3030 lfi_to->nr = lfi_from->nr;
3031 lfi_to->type = lfi_from->type;
3032 lfi_to->packed = lfi_from->packed;
3034 setString(&lfi_to->basename, lfi_from->basename);
3035 setString(&lfi_to->filename, lfi_from->filename);
3038 // ----------------------------------------------------------------------------
3039 // functions for loading R'n'D level
3040 // ----------------------------------------------------------------------------
3042 int getMappedElement(int element)
3044 // remap some (historic, now obsolete) elements
3048 case EL_PLAYER_OBSOLETE:
3049 element = EL_PLAYER_1;
3052 case EL_KEY_OBSOLETE:
3056 case EL_EM_KEY_1_FILE_OBSOLETE:
3057 element = EL_EM_KEY_1;
3060 case EL_EM_KEY_2_FILE_OBSOLETE:
3061 element = EL_EM_KEY_2;
3064 case EL_EM_KEY_3_FILE_OBSOLETE:
3065 element = EL_EM_KEY_3;
3068 case EL_EM_KEY_4_FILE_OBSOLETE:
3069 element = EL_EM_KEY_4;
3072 case EL_ENVELOPE_OBSOLETE:
3073 element = EL_ENVELOPE_1;
3081 if (element >= NUM_FILE_ELEMENTS)
3083 Warn("invalid level element %d", element);
3085 element = EL_UNKNOWN;
3093 static int getMappedElementByVersion(int element, int game_version)
3095 // remap some elements due to certain game version
3097 if (game_version <= VERSION_IDENT(2,2,0,0))
3099 // map game font elements
3100 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3101 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3102 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3103 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3106 if (game_version < VERSION_IDENT(3,0,0,0))
3108 // map Supaplex gravity tube elements
3109 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3110 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3111 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3112 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3119 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3121 level->file_version = getFileVersion(file);
3122 level->game_version = getFileVersion(file);
3127 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3129 level->creation_date.year = getFile16BitBE(file);
3130 level->creation_date.month = getFile8Bit(file);
3131 level->creation_date.day = getFile8Bit(file);
3133 level->creation_date.src = DATE_SRC_LEVELFILE;
3138 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3140 int initial_player_stepsize;
3141 int initial_player_gravity;
3144 level->fieldx = getFile8Bit(file);
3145 level->fieldy = getFile8Bit(file);
3147 level->time = getFile16BitBE(file);
3148 level->gems_needed = getFile16BitBE(file);
3150 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3151 level->name[i] = getFile8Bit(file);
3152 level->name[MAX_LEVEL_NAME_LEN] = 0;
3154 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3155 level->score[i] = getFile8Bit(file);
3157 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3158 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3159 for (y = 0; y < 3; y++)
3160 for (x = 0; x < 3; x++)
3161 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3163 level->amoeba_speed = getFile8Bit(file);
3164 level->time_magic_wall = getFile8Bit(file);
3165 level->time_wheel = getFile8Bit(file);
3166 level->amoeba_content = getMappedElement(getFile8Bit(file));
3168 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3171 for (i = 0; i < MAX_PLAYERS; i++)
3172 level->initial_player_stepsize[i] = initial_player_stepsize;
3174 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3176 for (i = 0; i < MAX_PLAYERS; i++)
3177 level->initial_player_gravity[i] = initial_player_gravity;
3179 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3180 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3182 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3184 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3185 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3186 level->can_move_into_acid_bits = getFile32BitBE(file);
3187 level->dont_collide_with_bits = getFile8Bit(file);
3189 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3190 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3192 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3193 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3194 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3196 level->game_engine_type = getFile8Bit(file);
3198 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3203 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3207 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3208 level->name[i] = getFile8Bit(file);
3209 level->name[MAX_LEVEL_NAME_LEN] = 0;
3214 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3218 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3219 level->author[i] = getFile8Bit(file);
3220 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3225 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3228 int chunk_size_expected = level->fieldx * level->fieldy;
3230 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3231 stored with 16-bit encoding (and should be twice as big then).
3232 Even worse, playfield data was stored 16-bit when only yamyam content
3233 contained 16-bit elements and vice versa. */
3235 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3236 chunk_size_expected *= 2;
3238 if (chunk_size_expected != chunk_size)
3240 ReadUnusedBytesFromFile(file, chunk_size);
3241 return chunk_size_expected;
3244 for (y = 0; y < level->fieldy; y++)
3245 for (x = 0; x < level->fieldx; x++)
3246 level->field[x][y] =
3247 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3252 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3255 int header_size = 4;
3256 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3257 int chunk_size_expected = header_size + content_size;
3259 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3260 stored with 16-bit encoding (and should be twice as big then).
3261 Even worse, playfield data was stored 16-bit when only yamyam content
3262 contained 16-bit elements and vice versa. */
3264 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3265 chunk_size_expected += content_size;
3267 if (chunk_size_expected != chunk_size)
3269 ReadUnusedBytesFromFile(file, chunk_size);
3270 return chunk_size_expected;
3274 level->num_yamyam_contents = getFile8Bit(file);
3278 // correct invalid number of content fields -- should never happen
3279 if (level->num_yamyam_contents < 1 ||
3280 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3281 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3283 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3284 for (y = 0; y < 3; y++)
3285 for (x = 0; x < 3; x++)
3286 level->yamyam_content[i].e[x][y] =
3287 getMappedElement(level->encoding_16bit_field ?
3288 getFile16BitBE(file) : getFile8Bit(file));
3292 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3297 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3299 element = getMappedElement(getFile16BitBE(file));
3300 num_contents = getFile8Bit(file);
3302 getFile8Bit(file); // content x size (unused)
3303 getFile8Bit(file); // content y size (unused)
3305 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3307 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3308 for (y = 0; y < 3; y++)
3309 for (x = 0; x < 3; x++)
3310 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3312 // correct invalid number of content fields -- should never happen
3313 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3314 num_contents = STD_ELEMENT_CONTENTS;
3316 if (element == EL_YAMYAM)
3318 level->num_yamyam_contents = num_contents;
3320 for (i = 0; i < num_contents; i++)
3321 for (y = 0; y < 3; y++)
3322 for (x = 0; x < 3; x++)
3323 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3325 else if (element == EL_BD_AMOEBA)
3327 level->amoeba_content = content_array[0][0][0];
3331 Warn("cannot load content for element '%d'", element);
3337 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3343 int chunk_size_expected;
3345 element = getMappedElement(getFile16BitBE(file));
3346 if (!IS_ENVELOPE(element))
3347 element = EL_ENVELOPE_1;
3349 envelope_nr = element - EL_ENVELOPE_1;
3351 envelope_len = getFile16BitBE(file);
3353 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3354 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3356 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3358 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3359 if (chunk_size_expected != chunk_size)
3361 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3362 return chunk_size_expected;
3365 for (i = 0; i < envelope_len; i++)
3366 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3371 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3373 int num_changed_custom_elements = getFile16BitBE(file);
3374 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3377 if (chunk_size_expected != chunk_size)
3379 ReadUnusedBytesFromFile(file, chunk_size - 2);
3380 return chunk_size_expected;
3383 for (i = 0; i < num_changed_custom_elements; i++)
3385 int element = getMappedElement(getFile16BitBE(file));
3386 int properties = getFile32BitBE(file);
3388 if (IS_CUSTOM_ELEMENT(element))
3389 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3391 Warn("invalid custom element number %d", element);
3393 // older game versions that wrote level files with CUS1 chunks used
3394 // different default push delay values (not yet stored in level file)
3395 element_info[element].push_delay_fixed = 2;
3396 element_info[element].push_delay_random = 8;
3399 level->file_has_custom_elements = TRUE;
3404 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3406 int num_changed_custom_elements = getFile16BitBE(file);
3407 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3410 if (chunk_size_expected != chunk_size)
3412 ReadUnusedBytesFromFile(file, chunk_size - 2);
3413 return chunk_size_expected;
3416 for (i = 0; i < num_changed_custom_elements; i++)
3418 int element = getMappedElement(getFile16BitBE(file));
3419 int custom_target_element = getMappedElement(getFile16BitBE(file));
3421 if (IS_CUSTOM_ELEMENT(element))
3422 element_info[element].change->target_element = custom_target_element;
3424 Warn("invalid custom element number %d", element);
3427 level->file_has_custom_elements = TRUE;
3432 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3434 int num_changed_custom_elements = getFile16BitBE(file);
3435 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3438 if (chunk_size_expected != chunk_size)
3440 ReadUnusedBytesFromFile(file, chunk_size - 2);
3441 return chunk_size_expected;
3444 for (i = 0; i < num_changed_custom_elements; i++)
3446 int element = getMappedElement(getFile16BitBE(file));
3447 struct ElementInfo *ei = &element_info[element];
3448 unsigned int event_bits;
3450 if (!IS_CUSTOM_ELEMENT(element))
3452 Warn("invalid custom element number %d", element);
3454 element = EL_INTERNAL_DUMMY;
3457 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3458 ei->description[j] = getFile8Bit(file);
3459 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3461 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3463 // some free bytes for future properties and padding
3464 ReadUnusedBytesFromFile(file, 7);
3466 ei->use_gfx_element = getFile8Bit(file);
3467 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3469 ei->collect_score_initial = getFile8Bit(file);
3470 ei->collect_count_initial = getFile8Bit(file);
3472 ei->push_delay_fixed = getFile16BitBE(file);
3473 ei->push_delay_random = getFile16BitBE(file);
3474 ei->move_delay_fixed = getFile16BitBE(file);
3475 ei->move_delay_random = getFile16BitBE(file);
3477 ei->move_pattern = getFile16BitBE(file);
3478 ei->move_direction_initial = getFile8Bit(file);
3479 ei->move_stepsize = getFile8Bit(file);
3481 for (y = 0; y < 3; y++)
3482 for (x = 0; x < 3; x++)
3483 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3485 // bits 0 - 31 of "has_event[]"
3486 event_bits = getFile32BitBE(file);
3487 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3488 if (event_bits & (1u << j))
3489 ei->change->has_event[j] = TRUE;
3491 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3493 ei->change->delay_fixed = getFile16BitBE(file);
3494 ei->change->delay_random = getFile16BitBE(file);
3495 ei->change->delay_frames = getFile16BitBE(file);
3497 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3499 ei->change->explode = getFile8Bit(file);
3500 ei->change->use_target_content = getFile8Bit(file);
3501 ei->change->only_if_complete = getFile8Bit(file);
3502 ei->change->use_random_replace = getFile8Bit(file);
3504 ei->change->random_percentage = getFile8Bit(file);
3505 ei->change->replace_when = getFile8Bit(file);
3507 for (y = 0; y < 3; y++)
3508 for (x = 0; x < 3; x++)
3509 ei->change->target_content.e[x][y] =
3510 getMappedElement(getFile16BitBE(file));
3512 ei->slippery_type = getFile8Bit(file);
3514 // some free bytes for future properties and padding
3515 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3517 // mark that this custom element has been modified
3518 ei->modified_settings = TRUE;
3521 level->file_has_custom_elements = TRUE;
3526 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3528 struct ElementInfo *ei;
3529 int chunk_size_expected;
3533 // ---------- custom element base property values (96 bytes) ----------------
3535 element = getMappedElement(getFile16BitBE(file));
3537 if (!IS_CUSTOM_ELEMENT(element))
3539 Warn("invalid custom element number %d", element);
3541 ReadUnusedBytesFromFile(file, chunk_size - 2);
3546 ei = &element_info[element];
3548 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3549 ei->description[i] = getFile8Bit(file);
3550 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3552 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3554 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3556 ei->num_change_pages = getFile8Bit(file);
3558 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3559 if (chunk_size_expected != chunk_size)
3561 ReadUnusedBytesFromFile(file, chunk_size - 43);
3562 return chunk_size_expected;
3565 ei->ce_value_fixed_initial = getFile16BitBE(file);
3566 ei->ce_value_random_initial = getFile16BitBE(file);
3567 ei->use_last_ce_value = getFile8Bit(file);
3569 ei->use_gfx_element = getFile8Bit(file);
3570 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3572 ei->collect_score_initial = getFile8Bit(file);
3573 ei->collect_count_initial = getFile8Bit(file);
3575 ei->drop_delay_fixed = getFile8Bit(file);
3576 ei->push_delay_fixed = getFile8Bit(file);
3577 ei->drop_delay_random = getFile8Bit(file);
3578 ei->push_delay_random = getFile8Bit(file);
3579 ei->move_delay_fixed = getFile16BitBE(file);
3580 ei->move_delay_random = getFile16BitBE(file);
3582 // bits 0 - 15 of "move_pattern" ...
3583 ei->move_pattern = getFile16BitBE(file);
3584 ei->move_direction_initial = getFile8Bit(file);
3585 ei->move_stepsize = getFile8Bit(file);
3587 ei->slippery_type = getFile8Bit(file);
3589 for (y = 0; y < 3; y++)
3590 for (x = 0; x < 3; x++)
3591 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3593 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3594 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3595 ei->move_leave_type = getFile8Bit(file);
3597 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3598 ei->move_pattern |= (getFile16BitBE(file) << 16);
3600 ei->access_direction = getFile8Bit(file);
3602 ei->explosion_delay = getFile8Bit(file);
3603 ei->ignition_delay = getFile8Bit(file);
3604 ei->explosion_type = getFile8Bit(file);
3606 // some free bytes for future custom property values and padding
3607 ReadUnusedBytesFromFile(file, 1);
3609 // ---------- change page property values (48 bytes) ------------------------
3611 setElementChangePages(ei, ei->num_change_pages);
3613 for (i = 0; i < ei->num_change_pages; i++)
3615 struct ElementChangeInfo *change = &ei->change_page[i];
3616 unsigned int event_bits;
3618 // always start with reliable default values
3619 setElementChangeInfoToDefaults(change);
3621 // bits 0 - 31 of "has_event[]" ...
3622 event_bits = getFile32BitBE(file);
3623 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3624 if (event_bits & (1u << j))
3625 change->has_event[j] = TRUE;
3627 change->target_element = getMappedElement(getFile16BitBE(file));
3629 change->delay_fixed = getFile16BitBE(file);
3630 change->delay_random = getFile16BitBE(file);
3631 change->delay_frames = getFile16BitBE(file);
3633 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3635 change->explode = getFile8Bit(file);
3636 change->use_target_content = getFile8Bit(file);
3637 change->only_if_complete = getFile8Bit(file);
3638 change->use_random_replace = getFile8Bit(file);
3640 change->random_percentage = getFile8Bit(file);
3641 change->replace_when = getFile8Bit(file);
3643 for (y = 0; y < 3; y++)
3644 for (x = 0; x < 3; x++)
3645 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3647 change->can_change = getFile8Bit(file);
3649 change->trigger_side = getFile8Bit(file);
3651 change->trigger_player = getFile8Bit(file);
3652 change->trigger_page = getFile8Bit(file);
3654 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3655 CH_PAGE_ANY : (1 << change->trigger_page));
3657 change->has_action = getFile8Bit(file);
3658 change->action_type = getFile8Bit(file);
3659 change->action_mode = getFile8Bit(file);
3660 change->action_arg = getFile16BitBE(file);
3662 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3663 event_bits = getFile8Bit(file);
3664 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3665 if (event_bits & (1u << (j - 32)))
3666 change->has_event[j] = TRUE;
3669 // mark this custom element as modified
3670 ei->modified_settings = TRUE;
3672 level->file_has_custom_elements = TRUE;
3677 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3679 struct ElementInfo *ei;
3680 struct ElementGroupInfo *group;
3684 element = getMappedElement(getFile16BitBE(file));
3686 if (!IS_GROUP_ELEMENT(element))
3688 Warn("invalid group element number %d", element);
3690 ReadUnusedBytesFromFile(file, chunk_size - 2);
3695 ei = &element_info[element];
3697 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3698 ei->description[i] = getFile8Bit(file);
3699 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3701 group = element_info[element].group;
3703 group->num_elements = getFile8Bit(file);
3705 ei->use_gfx_element = getFile8Bit(file);
3706 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3708 group->choice_mode = getFile8Bit(file);
3710 // some free bytes for future values and padding
3711 ReadUnusedBytesFromFile(file, 3);
3713 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3714 group->element[i] = getMappedElement(getFile16BitBE(file));
3716 // mark this group element as modified
3717 element_info[element].modified_settings = TRUE;
3719 level->file_has_custom_elements = TRUE;
3724 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3725 int element, int real_element)
3727 int micro_chunk_size = 0;
3728 int conf_type = getFile8Bit(file);
3729 int byte_mask = conf_type & CONF_MASK_BYTES;
3730 boolean element_found = FALSE;
3733 micro_chunk_size += 1;
3735 if (byte_mask == CONF_MASK_MULTI_BYTES)
3737 int num_bytes = getFile16BitBE(file);
3738 byte *buffer = checked_malloc(num_bytes);
3740 ReadBytesFromFile(file, buffer, num_bytes);
3742 for (i = 0; conf[i].data_type != -1; i++)
3744 if (conf[i].element == element &&
3745 conf[i].conf_type == conf_type)
3747 int data_type = conf[i].data_type;
3748 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3749 int max_num_entities = conf[i].max_num_entities;
3751 if (num_entities > max_num_entities)
3753 Warn("truncating number of entities for element %d from %d to %d",
3754 element, num_entities, max_num_entities);
3756 num_entities = max_num_entities;
3759 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3760 data_type == TYPE_CONTENT_LIST))
3762 // for element and content lists, zero entities are not allowed
3763 Warn("found empty list of entities for element %d", element);
3765 // do not set "num_entities" here to prevent reading behind buffer
3767 *(int *)(conf[i].num_entities) = 1; // at least one is required
3771 *(int *)(conf[i].num_entities) = num_entities;
3774 element_found = TRUE;
3776 if (data_type == TYPE_STRING)
3778 char *string = (char *)(conf[i].value);
3781 for (j = 0; j < max_num_entities; j++)
3782 string[j] = (j < num_entities ? buffer[j] : '\0');
3784 else if (data_type == TYPE_ELEMENT_LIST)
3786 int *element_array = (int *)(conf[i].value);
3789 for (j = 0; j < num_entities; j++)
3791 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3793 else if (data_type == TYPE_CONTENT_LIST)
3795 struct Content *content= (struct Content *)(conf[i].value);
3798 for (c = 0; c < num_entities; c++)
3799 for (y = 0; y < 3; y++)
3800 for (x = 0; x < 3; x++)
3801 content[c].e[x][y] =
3802 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3805 element_found = FALSE;
3811 checked_free(buffer);
3813 micro_chunk_size += 2 + num_bytes;
3815 else // constant size configuration data (1, 2 or 4 bytes)
3817 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3818 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3819 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3821 for (i = 0; conf[i].data_type != -1; i++)
3823 if (conf[i].element == element &&
3824 conf[i].conf_type == conf_type)
3826 int data_type = conf[i].data_type;
3828 if (data_type == TYPE_ELEMENT)
3829 value = getMappedElement(value);
3831 if (data_type == TYPE_BOOLEAN)
3832 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3834 *(int *) (conf[i].value) = value;
3836 element_found = TRUE;
3842 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3847 char *error_conf_chunk_bytes =
3848 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3849 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3850 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3851 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3852 int error_element = real_element;
3854 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3855 error_conf_chunk_bytes, error_conf_chunk_token,
3856 error_element, EL_NAME(error_element));
3859 return micro_chunk_size;
3862 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3864 int real_chunk_size = 0;
3866 li = *level; // copy level data into temporary buffer
3868 while (!checkEndOfFile(file))
3870 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3872 if (real_chunk_size >= chunk_size)
3876 *level = li; // copy temporary buffer back to level data
3878 return real_chunk_size;
3881 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3883 int real_chunk_size = 0;
3885 li = *level; // copy level data into temporary buffer
3887 while (!checkEndOfFile(file))
3889 int element = getMappedElement(getFile16BitBE(file));
3891 real_chunk_size += 2;
3892 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3894 if (real_chunk_size >= chunk_size)
3898 *level = li; // copy temporary buffer back to level data
3900 return real_chunk_size;
3903 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3905 int real_chunk_size = 0;
3907 li = *level; // copy level data into temporary buffer
3909 while (!checkEndOfFile(file))
3911 int element = getMappedElement(getFile16BitBE(file));
3913 real_chunk_size += 2;
3914 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3916 if (real_chunk_size >= chunk_size)
3920 *level = li; // copy temporary buffer back to level data
3922 return real_chunk_size;
3925 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3927 int element = getMappedElement(getFile16BitBE(file));
3928 int envelope_nr = element - EL_ENVELOPE_1;
3929 int real_chunk_size = 2;
3931 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3933 while (!checkEndOfFile(file))
3935 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3938 if (real_chunk_size >= chunk_size)
3942 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3944 return real_chunk_size;
3947 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3949 int element = getMappedElement(getFile16BitBE(file));
3950 int real_chunk_size = 2;
3951 struct ElementInfo *ei = &element_info[element];
3954 xx_ei = *ei; // copy element data into temporary buffer
3956 xx_ei.num_change_pages = -1;
3958 while (!checkEndOfFile(file))
3960 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3962 if (xx_ei.num_change_pages != -1)
3965 if (real_chunk_size >= chunk_size)
3971 if (ei->num_change_pages == -1)
3973 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3976 ei->num_change_pages = 1;
3978 setElementChangePages(ei, 1);
3979 setElementChangeInfoToDefaults(ei->change);
3981 return real_chunk_size;
3984 // initialize number of change pages stored for this custom element
3985 setElementChangePages(ei, ei->num_change_pages);
3986 for (i = 0; i < ei->num_change_pages; i++)
3987 setElementChangeInfoToDefaults(&ei->change_page[i]);
3989 // start with reading properties for the first change page
3990 xx_current_change_page = 0;
3992 while (!checkEndOfFile(file))
3994 // level file might contain invalid change page number
3995 if (xx_current_change_page >= ei->num_change_pages)
3998 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4000 xx_change = *change; // copy change data into temporary buffer
4002 resetEventBits(); // reset bits; change page might have changed
4004 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4007 *change = xx_change;
4009 setEventFlagsFromEventBits(change);
4011 if (real_chunk_size >= chunk_size)
4015 level->file_has_custom_elements = TRUE;
4017 return real_chunk_size;
4020 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4022 int element = getMappedElement(getFile16BitBE(file));
4023 int real_chunk_size = 2;
4024 struct ElementInfo *ei = &element_info[element];
4025 struct ElementGroupInfo *group = ei->group;
4030 xx_ei = *ei; // copy element data into temporary buffer
4031 xx_group = *group; // copy group data into temporary buffer
4033 while (!checkEndOfFile(file))
4035 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4038 if (real_chunk_size >= chunk_size)
4045 level->file_has_custom_elements = TRUE;
4047 return real_chunk_size;
4050 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4052 int element = getMappedElement(getFile16BitBE(file));
4053 int real_chunk_size = 2;
4054 struct ElementInfo *ei = &element_info[element];
4056 xx_ei = *ei; // copy element data into temporary buffer
4058 while (!checkEndOfFile(file))
4060 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4063 if (real_chunk_size >= chunk_size)
4069 level->file_has_custom_elements = TRUE;
4071 return real_chunk_size;
4074 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4075 struct LevelFileInfo *level_file_info,
4076 boolean level_info_only)
4078 char *filename = level_file_info->filename;
4079 char cookie[MAX_LINE_LEN];
4080 char chunk_name[CHUNK_ID_LEN + 1];
4084 if (!(file = openFile(filename, MODE_READ)))
4086 level->no_valid_file = TRUE;
4087 level->no_level_file = TRUE;
4089 if (level_info_only)
4092 Warn("cannot read level '%s' -- using empty level", filename);
4094 if (!setup.editor.use_template_for_new_levels)
4097 // if level file not found, try to initialize level data from template
4098 filename = getGlobalLevelTemplateFilename();
4100 if (!(file = openFile(filename, MODE_READ)))
4103 // default: for empty levels, use level template for custom elements
4104 level->use_custom_template = TRUE;
4106 level->no_valid_file = FALSE;
4109 getFileChunkBE(file, chunk_name, NULL);
4110 if (strEqual(chunk_name, "RND1"))
4112 getFile32BitBE(file); // not used
4114 getFileChunkBE(file, chunk_name, NULL);
4115 if (!strEqual(chunk_name, "CAVE"))
4117 level->no_valid_file = TRUE;
4119 Warn("unknown format of level file '%s'", filename);
4126 else // check for pre-2.0 file format with cookie string
4128 strcpy(cookie, chunk_name);
4129 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4131 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4132 cookie[strlen(cookie) - 1] = '\0';
4134 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4136 level->no_valid_file = TRUE;
4138 Warn("unknown format of level file '%s'", filename);
4145 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4147 level->no_valid_file = TRUE;
4149 Warn("unsupported version of level file '%s'", filename);
4156 // pre-2.0 level files have no game version, so use file version here
4157 level->game_version = level->file_version;
4160 if (level->file_version < FILE_VERSION_1_2)
4162 // level files from versions before 1.2.0 without chunk structure
4163 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4164 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4172 int (*loader)(File *, int, struct LevelInfo *);
4176 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4177 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4178 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4179 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4180 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4181 { "INFO", -1, LoadLevel_INFO },
4182 { "BODY", -1, LoadLevel_BODY },
4183 { "CONT", -1, LoadLevel_CONT },
4184 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4185 { "CNT3", -1, LoadLevel_CNT3 },
4186 { "CUS1", -1, LoadLevel_CUS1 },
4187 { "CUS2", -1, LoadLevel_CUS2 },
4188 { "CUS3", -1, LoadLevel_CUS3 },
4189 { "CUS4", -1, LoadLevel_CUS4 },
4190 { "GRP1", -1, LoadLevel_GRP1 },
4191 { "CONF", -1, LoadLevel_CONF },
4192 { "ELEM", -1, LoadLevel_ELEM },
4193 { "NOTE", -1, LoadLevel_NOTE },
4194 { "CUSX", -1, LoadLevel_CUSX },
4195 { "GRPX", -1, LoadLevel_GRPX },
4196 { "EMPX", -1, LoadLevel_EMPX },
4201 while (getFileChunkBE(file, chunk_name, &chunk_size))
4205 while (chunk_info[i].name != NULL &&
4206 !strEqual(chunk_name, chunk_info[i].name))
4209 if (chunk_info[i].name == NULL)
4211 Warn("unknown chunk '%s' in level file '%s'",
4212 chunk_name, filename);
4214 ReadUnusedBytesFromFile(file, chunk_size);
4216 else if (chunk_info[i].size != -1 &&
4217 chunk_info[i].size != chunk_size)
4219 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4220 chunk_size, chunk_name, filename);
4222 ReadUnusedBytesFromFile(file, chunk_size);
4226 // call function to load this level chunk
4227 int chunk_size_expected =
4228 (chunk_info[i].loader)(file, chunk_size, level);
4230 if (chunk_size_expected < 0)
4232 Warn("error reading chunk '%s' in level file '%s'",
4233 chunk_name, filename);
4238 // the size of some chunks cannot be checked before reading other
4239 // chunks first (like "HEAD" and "BODY") that contain some header
4240 // information, so check them here
4241 if (chunk_size_expected != chunk_size)
4243 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4244 chunk_size, chunk_name, filename);
4256 // ----------------------------------------------------------------------------
4257 // functions for loading BD level
4258 // ----------------------------------------------------------------------------
4260 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4261 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4263 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4265 struct LevelInfo_BD *level_bd = level->native_bd_level;
4266 GdCave *cave = NULL; // will be changed below
4267 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4268 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4271 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4273 // cave and map newly allocated when set to defaults above
4274 cave = level_bd->cave;
4277 cave->intermission = level->bd_intermission;
4280 cave->level_time[0] = level->time;
4281 cave->level_diamonds[0] = level->gems_needed;
4284 cave->scheduling = level->bd_scheduling_type;
4285 cave->pal_timing = level->bd_pal_timing;
4286 cave->level_speed[0] = level->bd_cycle_delay_ms;
4287 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4288 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4289 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4292 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4293 cave->diamond_value = level->score[SC_EMERALD];
4294 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4296 // compatibility settings
4297 cave->lineshift = level->bd_line_shifting_borders;
4298 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4299 cave->short_explosions = level->bd_short_explosions;
4301 // player properties
4302 cave->diagonal_movements = level->bd_diagonal_movements;
4303 cave->active_is_first_found = level->bd_topmost_player_active;
4304 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4305 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4306 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4307 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4309 // element properties
4310 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4311 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4312 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4313 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4314 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4315 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4316 cave->level_magic_wall_time[0] = level->time_magic_wall;
4317 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4318 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4319 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4320 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4322 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4323 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4324 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4325 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4326 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4327 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4328 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4330 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4331 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4332 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4333 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4334 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4335 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4336 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4337 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4338 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4339 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4340 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4342 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4343 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4344 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4345 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4346 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4347 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4349 cave->slime_predictable = level->bd_slime_is_predictable;
4350 cave->slime_correct_random = level->bd_slime_correct_random;
4351 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4352 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4353 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4354 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4355 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4356 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4357 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4358 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4359 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4360 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4362 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4363 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4364 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4366 cave->biter_delay_frame = level->bd_biter_move_delay;
4367 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4369 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4371 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4373 cave->replicators_active = level->bd_replicators_active;
4374 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4376 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4377 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4379 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4381 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4383 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4384 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4385 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4387 cave->infinite_rockets = level->bd_infinite_rockets;
4389 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4390 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4392 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4393 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4395 cave->creatures_backwards = level->bd_creatures_start_backwards;
4396 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4397 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4399 cave->gravity = level->bd_gravity_direction;
4400 cave->gravity_switch_active = level->bd_gravity_switch_active;
4401 cave->gravity_change_time = level->bd_gravity_switch_delay;
4402 cave->gravity_affects_all = level->bd_gravity_affects_all;
4404 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4405 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4406 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4407 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4409 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4410 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4411 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4412 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4413 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4414 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4416 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4417 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4418 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4419 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4421 cave->colorb = level->bd_color_b;
4422 cave->color0 = level->bd_color_0;
4423 cave->color1 = level->bd_color_1;
4424 cave->color2 = level->bd_color_2;
4425 cave->color3 = level->bd_color_3;
4426 cave->color4 = level->bd_color_4;
4427 cave->color5 = level->bd_color_5;
4430 strncpy(cave->name, level->name, sizeof(GdString));
4431 cave->name[sizeof(GdString) - 1] = '\0';
4433 // playfield elements
4434 for (x = 0; x < cave->w; x++)
4435 for (y = 0; y < cave->h; y++)
4436 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4439 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4441 struct LevelInfo_BD *level_bd = level->native_bd_level;
4442 GdCave *cave = level_bd->cave;
4443 int bd_level_nr = level_bd->level_nr;
4446 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4447 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4450 level->bd_intermission = cave->intermission;
4453 level->time = cave->level_time[bd_level_nr];
4454 level->gems_needed = cave->level_diamonds[bd_level_nr];
4457 level->bd_scheduling_type = cave->scheduling;
4458 level->bd_pal_timing = cave->pal_timing;
4459 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4460 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4461 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4462 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4465 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4466 level->score[SC_EMERALD] = cave->diamond_value;
4467 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4469 // compatibility settings
4470 level->bd_line_shifting_borders = cave->lineshift;
4471 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4472 level->bd_short_explosions = cave->short_explosions;
4474 // player properties
4475 level->bd_diagonal_movements = cave->diagonal_movements;
4476 level->bd_topmost_player_active = cave->active_is_first_found;
4477 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4478 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4479 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4480 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4482 // element properties
4483 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4484 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4485 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4486 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4487 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4488 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4489 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4490 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4491 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4492 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4493 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4495 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4496 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4497 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4498 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4499 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4500 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4501 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4503 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4504 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4505 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4506 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4507 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4508 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4509 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4510 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4511 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4512 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4513 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4515 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4516 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4517 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4518 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4519 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4520 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4522 level->bd_slime_is_predictable = cave->slime_predictable;
4523 level->bd_slime_correct_random = cave->slime_correct_random;
4524 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4525 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4526 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4527 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4528 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4529 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4530 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4531 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4532 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4533 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4535 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4536 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4537 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4539 level->bd_biter_move_delay = cave->biter_delay_frame;
4540 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4542 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4544 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4546 level->bd_replicators_active = cave->replicators_active;
4547 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4549 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4550 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4552 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4554 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4556 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4557 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4558 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4560 level->bd_infinite_rockets = cave->infinite_rockets;
4562 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4563 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4565 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4566 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4568 level->bd_creatures_start_backwards = cave->creatures_backwards;
4569 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4570 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4572 level->bd_gravity_direction = cave->gravity;
4573 level->bd_gravity_switch_active = cave->gravity_switch_active;
4574 level->bd_gravity_switch_delay = cave->gravity_change_time;
4575 level->bd_gravity_affects_all = cave->gravity_affects_all;
4577 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4578 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4579 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4580 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4582 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4583 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4584 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4585 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4586 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4587 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4589 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4590 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4591 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4592 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4594 level->bd_color_b = cave->colorb;
4595 level->bd_color_0 = cave->color0;
4596 level->bd_color_1 = cave->color1;
4597 level->bd_color_2 = cave->color2;
4598 level->bd_color_3 = cave->color3;
4599 level->bd_color_4 = cave->color4;
4600 level->bd_color_5 = cave->color5;
4602 // set default color type and colors for BD style level colors
4603 SetDefaultLevelColorType_BD();
4604 SetDefaultLevelColors_BD();
4607 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4609 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4610 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4612 // playfield elements
4613 for (x = 0; x < level->fieldx; x++)
4614 for (y = 0; y < level->fieldy; y++)
4615 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4617 checked_free(cave_name);
4620 static void setTapeInfoToDefaults(void);
4622 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4624 struct LevelInfo_BD *level_bd = level->native_bd_level;
4625 GdCave *cave = level_bd->cave;
4626 GdReplay *replay = level_bd->replay;
4632 // always start with reliable default values
4633 setTapeInfoToDefaults();
4635 tape.level_nr = level_nr; // (currently not used)
4636 tape.random_seed = replay->seed;
4638 TapeSetDateFromIsoDateString(replay->date);
4641 tape.pos[tape.counter].delay = 0;
4643 tape.bd_replay = TRUE;
4645 // all time calculations only used to display approximate tape time
4646 int cave_speed = cave->speed;
4647 int milliseconds_game = 0;
4648 int milliseconds_elapsed = 20;
4650 for (i = 0; i < replay->movements->len; i++)
4652 int replay_action = replay->movements->data[i];
4653 int tape_action = map_action_BD_to_RND(replay_action);
4654 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4655 boolean success = 0;
4659 success = TapeAddAction(action);
4661 milliseconds_game += milliseconds_elapsed;
4663 if (milliseconds_game >= cave_speed)
4665 milliseconds_game -= cave_speed;
4672 tape.pos[tape.counter].delay = 0;
4673 tape.pos[tape.counter].action[0] = 0;
4677 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4683 TapeHaltRecording();
4685 if (!replay->success)
4686 Warn("BD replay is marked as not successful");
4690 // ----------------------------------------------------------------------------
4691 // functions for loading EM level
4692 // ----------------------------------------------------------------------------
4694 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4696 static int ball_xy[8][2] =
4707 struct LevelInfo_EM *level_em = level->native_em_level;
4708 struct CAVE *cav = level_em->cav;
4711 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4712 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4714 cav->time_seconds = level->time;
4715 cav->gems_needed = level->gems_needed;
4717 cav->emerald_score = level->score[SC_EMERALD];
4718 cav->diamond_score = level->score[SC_DIAMOND];
4719 cav->alien_score = level->score[SC_ROBOT];
4720 cav->tank_score = level->score[SC_SPACESHIP];
4721 cav->bug_score = level->score[SC_BUG];
4722 cav->eater_score = level->score[SC_YAMYAM];
4723 cav->nut_score = level->score[SC_NUT];
4724 cav->dynamite_score = level->score[SC_DYNAMITE];
4725 cav->key_score = level->score[SC_KEY];
4726 cav->exit_score = level->score[SC_TIME_BONUS];
4728 cav->num_eater_arrays = level->num_yamyam_contents;
4730 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4731 for (y = 0; y < 3; y++)
4732 for (x = 0; x < 3; x++)
4733 cav->eater_array[i][y * 3 + x] =
4734 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4736 cav->amoeba_time = level->amoeba_speed;
4737 cav->wonderwall_time = level->time_magic_wall;
4738 cav->wheel_time = level->time_wheel;
4740 cav->android_move_time = level->android_move_time;
4741 cav->android_clone_time = level->android_clone_time;
4742 cav->ball_random = level->ball_random;
4743 cav->ball_active = level->ball_active_initial;
4744 cav->ball_time = level->ball_time;
4745 cav->num_ball_arrays = level->num_ball_contents;
4747 cav->lenses_score = level->lenses_score;
4748 cav->magnify_score = level->magnify_score;
4749 cav->slurp_score = level->slurp_score;
4751 cav->lenses_time = level->lenses_time;
4752 cav->magnify_time = level->magnify_time;
4754 cav->wind_time = 9999;
4755 cav->wind_direction =
4756 map_direction_RND_to_EM(level->wind_direction_initial);
4758 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4759 for (j = 0; j < 8; j++)
4760 cav->ball_array[i][j] =
4761 map_element_RND_to_EM_cave(level->ball_content[i].
4762 e[ball_xy[j][0]][ball_xy[j][1]]);
4764 map_android_clone_elements_RND_to_EM(level);
4766 // first fill the complete playfield with the empty space element
4767 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4768 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4769 cav->cave[x][y] = Cblank;
4771 // then copy the real level contents from level file into the playfield
4772 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4774 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4776 if (level->field[x][y] == EL_AMOEBA_DEAD)
4777 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4779 cav->cave[x][y] = new_element;
4782 for (i = 0; i < MAX_PLAYERS; i++)
4784 cav->player_x[i] = -1;
4785 cav->player_y[i] = -1;
4788 // initialize player positions and delete players from the playfield
4789 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4791 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4793 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4795 cav->player_x[player_nr] = x;
4796 cav->player_y[player_nr] = y;
4798 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4803 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4805 static int ball_xy[8][2] =
4816 struct LevelInfo_EM *level_em = level->native_em_level;
4817 struct CAVE *cav = level_em->cav;
4820 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4821 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4823 level->time = cav->time_seconds;
4824 level->gems_needed = cav->gems_needed;
4826 sprintf(level->name, "Level %d", level->file_info.nr);
4828 level->score[SC_EMERALD] = cav->emerald_score;
4829 level->score[SC_DIAMOND] = cav->diamond_score;
4830 level->score[SC_ROBOT] = cav->alien_score;
4831 level->score[SC_SPACESHIP] = cav->tank_score;
4832 level->score[SC_BUG] = cav->bug_score;
4833 level->score[SC_YAMYAM] = cav->eater_score;
4834 level->score[SC_NUT] = cav->nut_score;
4835 level->score[SC_DYNAMITE] = cav->dynamite_score;
4836 level->score[SC_KEY] = cav->key_score;
4837 level->score[SC_TIME_BONUS] = cav->exit_score;
4839 level->num_yamyam_contents = cav->num_eater_arrays;
4841 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4842 for (y = 0; y < 3; y++)
4843 for (x = 0; x < 3; x++)
4844 level->yamyam_content[i].e[x][y] =
4845 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4847 level->amoeba_speed = cav->amoeba_time;
4848 level->time_magic_wall = cav->wonderwall_time;
4849 level->time_wheel = cav->wheel_time;
4851 level->android_move_time = cav->android_move_time;
4852 level->android_clone_time = cav->android_clone_time;
4853 level->ball_random = cav->ball_random;
4854 level->ball_active_initial = cav->ball_active;
4855 level->ball_time = cav->ball_time;
4856 level->num_ball_contents = cav->num_ball_arrays;
4858 level->lenses_score = cav->lenses_score;
4859 level->magnify_score = cav->magnify_score;
4860 level->slurp_score = cav->slurp_score;
4862 level->lenses_time = cav->lenses_time;
4863 level->magnify_time = cav->magnify_time;
4865 level->wind_direction_initial =
4866 map_direction_EM_to_RND(cav->wind_direction);
4868 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4869 for (j = 0; j < 8; j++)
4870 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4871 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4873 map_android_clone_elements_EM_to_RND(level);
4875 // convert the playfield (some elements need special treatment)
4876 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4878 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4880 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4881 new_element = EL_AMOEBA_DEAD;
4883 level->field[x][y] = new_element;
4886 for (i = 0; i < MAX_PLAYERS; i++)
4888 // in case of all players set to the same field, use the first player
4889 int nr = MAX_PLAYERS - i - 1;
4890 int jx = cav->player_x[nr];
4891 int jy = cav->player_y[nr];
4893 if (jx != -1 && jy != -1)
4894 level->field[jx][jy] = EL_PLAYER_1 + nr;
4897 // time score is counted for each 10 seconds left in Emerald Mine levels
4898 level->time_score_base = 10;
4902 // ----------------------------------------------------------------------------
4903 // functions for loading SP level
4904 // ----------------------------------------------------------------------------
4906 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4908 struct LevelInfo_SP *level_sp = level->native_sp_level;
4909 LevelInfoType *header = &level_sp->header;
4912 level_sp->width = level->fieldx;
4913 level_sp->height = level->fieldy;
4915 for (x = 0; x < level->fieldx; x++)
4916 for (y = 0; y < level->fieldy; y++)
4917 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4919 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4921 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4922 header->LevelTitle[i] = level->name[i];
4923 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4925 header->InfotronsNeeded = level->gems_needed;
4927 header->SpecialPortCount = 0;
4929 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4931 boolean gravity_port_found = FALSE;
4932 boolean gravity_port_valid = FALSE;
4933 int gravity_port_flag;
4934 int gravity_port_base_element;
4935 int element = level->field[x][y];
4937 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4938 element <= EL_SP_GRAVITY_ON_PORT_UP)
4940 gravity_port_found = TRUE;
4941 gravity_port_valid = TRUE;
4942 gravity_port_flag = 1;
4943 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4945 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4946 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4948 gravity_port_found = TRUE;
4949 gravity_port_valid = TRUE;
4950 gravity_port_flag = 0;
4951 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4953 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4954 element <= EL_SP_GRAVITY_PORT_UP)
4956 // change R'n'D style gravity inverting special port to normal port
4957 // (there are no gravity inverting ports in native Supaplex engine)
4959 gravity_port_found = TRUE;
4960 gravity_port_valid = FALSE;
4961 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4964 if (gravity_port_found)
4966 if (gravity_port_valid &&
4967 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4969 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4971 port->PortLocation = (y * level->fieldx + x) * 2;
4972 port->Gravity = gravity_port_flag;
4974 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4976 header->SpecialPortCount++;
4980 // change special gravity port to normal port
4982 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4985 level_sp->playfield[x][y] = element - EL_SP_START;
4990 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4992 struct LevelInfo_SP *level_sp = level->native_sp_level;
4993 LevelInfoType *header = &level_sp->header;
4994 boolean num_invalid_elements = 0;
4997 level->fieldx = level_sp->width;
4998 level->fieldy = level_sp->height;
5000 for (x = 0; x < level->fieldx; x++)
5002 for (y = 0; y < level->fieldy; y++)
5004 int element_old = level_sp->playfield[x][y];
5005 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5007 if (element_new == EL_UNKNOWN)
5009 num_invalid_elements++;
5011 Debug("level:native:SP", "invalid element %d at position %d, %d",
5015 level->field[x][y] = element_new;
5019 if (num_invalid_elements > 0)
5020 Warn("found %d invalid elements%s", num_invalid_elements,
5021 (!options.debug ? " (use '--debug' for more details)" : ""));
5023 for (i = 0; i < MAX_PLAYERS; i++)
5024 level->initial_player_gravity[i] =
5025 (header->InitialGravity == 1 ? TRUE : FALSE);
5027 // skip leading spaces
5028 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5029 if (header->LevelTitle[i] != ' ')
5033 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5034 level->name[j] = header->LevelTitle[i];
5035 level->name[j] = '\0';
5037 // cut trailing spaces
5039 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5040 level->name[j - 1] = '\0';
5042 level->gems_needed = header->InfotronsNeeded;
5044 for (i = 0; i < header->SpecialPortCount; i++)
5046 SpecialPortType *port = &header->SpecialPort[i];
5047 int port_location = port->PortLocation;
5048 int gravity = port->Gravity;
5049 int port_x, port_y, port_element;
5051 port_x = (port_location / 2) % level->fieldx;
5052 port_y = (port_location / 2) / level->fieldx;
5054 if (port_x < 0 || port_x >= level->fieldx ||
5055 port_y < 0 || port_y >= level->fieldy)
5057 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5062 port_element = level->field[port_x][port_y];
5064 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5065 port_element > EL_SP_GRAVITY_PORT_UP)
5067 Warn("no special port at position (%d, %d)", port_x, port_y);
5072 // change previous (wrong) gravity inverting special port to either
5073 // gravity enabling special port or gravity disabling special port
5074 level->field[port_x][port_y] +=
5075 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5076 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5079 // change special gravity ports without database entries to normal ports
5080 for (x = 0; x < level->fieldx; x++)
5081 for (y = 0; y < level->fieldy; y++)
5082 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5083 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5084 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5086 level->time = 0; // no time limit
5087 level->amoeba_speed = 0;
5088 level->time_magic_wall = 0;
5089 level->time_wheel = 0;
5090 level->amoeba_content = EL_EMPTY;
5092 // original Supaplex does not use score values -- rate by playing time
5093 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5094 level->score[i] = 0;
5096 level->rate_time_over_score = TRUE;
5098 // there are no yamyams in supaplex levels
5099 for (i = 0; i < level->num_yamyam_contents; i++)
5100 for (x = 0; x < 3; x++)
5101 for (y = 0; y < 3; y++)
5102 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5105 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5107 struct LevelInfo_SP *level_sp = level->native_sp_level;
5108 struct DemoInfo_SP *demo = &level_sp->demo;
5111 // always start with reliable default values
5112 demo->is_available = FALSE;
5115 if (TAPE_IS_EMPTY(tape))
5118 demo->level_nr = tape.level_nr; // (currently not used)
5120 level_sp->header.DemoRandomSeed = tape.random_seed;
5124 for (i = 0; i < tape.length; i++)
5126 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5127 int demo_repeat = tape.pos[i].delay;
5128 int demo_entries = (demo_repeat + 15) / 16;
5130 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5132 Warn("tape truncated: size exceeds maximum SP demo size %d",
5138 for (j = 0; j < demo_repeat / 16; j++)
5139 demo->data[demo->length++] = 0xf0 | demo_action;
5141 if (demo_repeat % 16)
5142 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5145 demo->is_available = TRUE;
5148 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5150 struct LevelInfo_SP *level_sp = level->native_sp_level;
5151 struct DemoInfo_SP *demo = &level_sp->demo;
5152 char *filename = level->file_info.filename;
5155 // always start with reliable default values
5156 setTapeInfoToDefaults();
5158 if (!demo->is_available)
5161 tape.level_nr = demo->level_nr; // (currently not used)
5162 tape.random_seed = level_sp->header.DemoRandomSeed;
5164 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5167 tape.pos[tape.counter].delay = 0;
5169 for (i = 0; i < demo->length; i++)
5171 int demo_action = demo->data[i] & 0x0f;
5172 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5173 int tape_action = map_key_SP_to_RND(demo_action);
5174 int tape_repeat = demo_repeat + 1;
5175 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5176 boolean success = 0;
5179 for (j = 0; j < tape_repeat; j++)
5180 success = TapeAddAction(action);
5184 Warn("SP demo truncated: size exceeds maximum tape size %d",
5191 TapeHaltRecording();
5195 // ----------------------------------------------------------------------------
5196 // functions for loading MM level
5197 // ----------------------------------------------------------------------------
5199 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5201 struct LevelInfo_MM *level_mm = level->native_mm_level;
5204 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5205 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5207 level_mm->time = level->time;
5208 level_mm->kettles_needed = level->gems_needed;
5209 level_mm->auto_count_kettles = level->auto_count_gems;
5211 level_mm->mm_laser_red = level->mm_laser_red;
5212 level_mm->mm_laser_green = level->mm_laser_green;
5213 level_mm->mm_laser_blue = level->mm_laser_blue;
5215 level_mm->df_laser_red = level->df_laser_red;
5216 level_mm->df_laser_green = level->df_laser_green;
5217 level_mm->df_laser_blue = level->df_laser_blue;
5219 strcpy(level_mm->name, level->name);
5220 strcpy(level_mm->author, level->author);
5222 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5223 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5224 level_mm->score[SC_KEY] = level->score[SC_KEY];
5225 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5226 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5228 level_mm->amoeba_speed = level->amoeba_speed;
5229 level_mm->time_fuse = level->mm_time_fuse;
5230 level_mm->time_bomb = level->mm_time_bomb;
5231 level_mm->time_ball = level->mm_time_ball;
5232 level_mm->time_block = level->mm_time_block;
5234 level_mm->num_ball_contents = level->num_mm_ball_contents;
5235 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5236 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5237 level_mm->explode_ball = level->explode_mm_ball;
5239 for (i = 0; i < level->num_mm_ball_contents; i++)
5240 level_mm->ball_content[i] =
5241 map_element_RND_to_MM(level->mm_ball_content[i]);
5243 for (x = 0; x < level->fieldx; x++)
5244 for (y = 0; y < level->fieldy; y++)
5246 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5249 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5251 struct LevelInfo_MM *level_mm = level->native_mm_level;
5254 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5255 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5257 level->time = level_mm->time;
5258 level->gems_needed = level_mm->kettles_needed;
5259 level->auto_count_gems = level_mm->auto_count_kettles;
5261 level->mm_laser_red = level_mm->mm_laser_red;
5262 level->mm_laser_green = level_mm->mm_laser_green;
5263 level->mm_laser_blue = level_mm->mm_laser_blue;
5265 level->df_laser_red = level_mm->df_laser_red;
5266 level->df_laser_green = level_mm->df_laser_green;
5267 level->df_laser_blue = level_mm->df_laser_blue;
5269 strcpy(level->name, level_mm->name);
5271 // only overwrite author from 'levelinfo.conf' if author defined in level
5272 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5273 strcpy(level->author, level_mm->author);
5275 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5276 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5277 level->score[SC_KEY] = level_mm->score[SC_KEY];
5278 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5279 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5281 level->amoeba_speed = level_mm->amoeba_speed;
5282 level->mm_time_fuse = level_mm->time_fuse;
5283 level->mm_time_bomb = level_mm->time_bomb;
5284 level->mm_time_ball = level_mm->time_ball;
5285 level->mm_time_block = level_mm->time_block;
5287 level->num_mm_ball_contents = level_mm->num_ball_contents;
5288 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5289 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5290 level->explode_mm_ball = level_mm->explode_ball;
5292 for (i = 0; i < level->num_mm_ball_contents; i++)
5293 level->mm_ball_content[i] =
5294 map_element_MM_to_RND(level_mm->ball_content[i]);
5296 for (x = 0; x < level->fieldx; x++)
5297 for (y = 0; y < level->fieldy; y++)
5298 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5302 // ----------------------------------------------------------------------------
5303 // functions for loading DC level
5304 // ----------------------------------------------------------------------------
5306 #define DC_LEVEL_HEADER_SIZE 344
5308 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5311 static int last_data_encoded;
5315 int diff_hi, diff_lo;
5316 int data_hi, data_lo;
5317 unsigned short data_decoded;
5321 last_data_encoded = 0;
5328 diff = data_encoded - last_data_encoded;
5329 diff_hi = diff & ~0xff;
5330 diff_lo = diff & 0xff;
5334 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5335 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5336 data_hi = data_hi & 0xff00;
5338 data_decoded = data_hi | data_lo;
5340 last_data_encoded = data_encoded;
5342 offset1 = (offset1 + 1) % 31;
5343 offset2 = offset2 & 0xff;
5345 return data_decoded;
5348 static int getMappedElement_DC(int element)
5356 // 0x0117 - 0x036e: (?)
5359 // 0x042d - 0x0684: (?)
5375 element = EL_CRYSTAL;
5378 case 0x0e77: // quicksand (boulder)
5379 element = EL_QUICKSAND_FAST_FULL;
5382 case 0x0e99: // slow quicksand (boulder)
5383 element = EL_QUICKSAND_FULL;
5387 element = EL_EM_EXIT_OPEN;
5391 element = EL_EM_EXIT_CLOSED;
5395 element = EL_EM_STEEL_EXIT_OPEN;
5399 element = EL_EM_STEEL_EXIT_CLOSED;
5402 case 0x0f4f: // dynamite (lit 1)
5403 element = EL_EM_DYNAMITE_ACTIVE;
5406 case 0x0f57: // dynamite (lit 2)
5407 element = EL_EM_DYNAMITE_ACTIVE;
5410 case 0x0f5f: // dynamite (lit 3)
5411 element = EL_EM_DYNAMITE_ACTIVE;
5414 case 0x0f67: // dynamite (lit 4)
5415 element = EL_EM_DYNAMITE_ACTIVE;
5422 element = EL_AMOEBA_WET;
5426 element = EL_AMOEBA_DROP;
5430 element = EL_DC_MAGIC_WALL;
5434 element = EL_SPACESHIP_UP;
5438 element = EL_SPACESHIP_DOWN;
5442 element = EL_SPACESHIP_LEFT;
5446 element = EL_SPACESHIP_RIGHT;
5450 element = EL_BUG_UP;
5454 element = EL_BUG_DOWN;
5458 element = EL_BUG_LEFT;
5462 element = EL_BUG_RIGHT;
5466 element = EL_MOLE_UP;
5470 element = EL_MOLE_DOWN;
5474 element = EL_MOLE_LEFT;
5478 element = EL_MOLE_RIGHT;
5486 element = EL_YAMYAM_UP;
5490 element = EL_SWITCHGATE_OPEN;
5494 element = EL_SWITCHGATE_CLOSED;
5498 element = EL_DC_SWITCHGATE_SWITCH_UP;
5502 element = EL_TIMEGATE_CLOSED;
5505 case 0x144c: // conveyor belt switch (green)
5506 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5509 case 0x144f: // conveyor belt switch (red)
5510 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5513 case 0x1452: // conveyor belt switch (blue)
5514 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5518 element = EL_CONVEYOR_BELT_3_MIDDLE;
5522 element = EL_CONVEYOR_BELT_3_LEFT;
5526 element = EL_CONVEYOR_BELT_3_RIGHT;
5530 element = EL_CONVEYOR_BELT_1_MIDDLE;
5534 element = EL_CONVEYOR_BELT_1_LEFT;
5538 element = EL_CONVEYOR_BELT_1_RIGHT;
5542 element = EL_CONVEYOR_BELT_4_MIDDLE;
5546 element = EL_CONVEYOR_BELT_4_LEFT;
5550 element = EL_CONVEYOR_BELT_4_RIGHT;
5554 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5558 element = EL_EXPANDABLE_WALL_VERTICAL;
5562 element = EL_EXPANDABLE_WALL_ANY;
5565 case 0x14ce: // growing steel wall (left/right)
5566 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5569 case 0x14df: // growing steel wall (up/down)
5570 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5573 case 0x14e8: // growing steel wall (up/down/left/right)
5574 element = EL_EXPANDABLE_STEELWALL_ANY;
5578 element = EL_SHIELD_DEADLY;
5582 element = EL_EXTRA_TIME;
5590 element = EL_EMPTY_SPACE;
5593 case 0x1578: // quicksand (empty)
5594 element = EL_QUICKSAND_FAST_EMPTY;
5597 case 0x1579: // slow quicksand (empty)
5598 element = EL_QUICKSAND_EMPTY;
5608 element = EL_EM_DYNAMITE;
5611 case 0x15a1: // key (red)
5612 element = EL_EM_KEY_1;
5615 case 0x15a2: // key (yellow)
5616 element = EL_EM_KEY_2;
5619 case 0x15a3: // key (blue)
5620 element = EL_EM_KEY_4;
5623 case 0x15a4: // key (green)
5624 element = EL_EM_KEY_3;
5627 case 0x15a5: // key (white)
5628 element = EL_DC_KEY_WHITE;
5632 element = EL_WALL_SLIPPERY;
5639 case 0x15a8: // wall (not round)
5643 case 0x15a9: // (blue)
5644 element = EL_CHAR_A;
5647 case 0x15aa: // (blue)
5648 element = EL_CHAR_B;
5651 case 0x15ab: // (blue)
5652 element = EL_CHAR_C;
5655 case 0x15ac: // (blue)
5656 element = EL_CHAR_D;
5659 case 0x15ad: // (blue)
5660 element = EL_CHAR_E;
5663 case 0x15ae: // (blue)
5664 element = EL_CHAR_F;
5667 case 0x15af: // (blue)
5668 element = EL_CHAR_G;
5671 case 0x15b0: // (blue)
5672 element = EL_CHAR_H;
5675 case 0x15b1: // (blue)
5676 element = EL_CHAR_I;
5679 case 0x15b2: // (blue)
5680 element = EL_CHAR_J;
5683 case 0x15b3: // (blue)
5684 element = EL_CHAR_K;
5687 case 0x15b4: // (blue)
5688 element = EL_CHAR_L;
5691 case 0x15b5: // (blue)
5692 element = EL_CHAR_M;
5695 case 0x15b6: // (blue)
5696 element = EL_CHAR_N;
5699 case 0x15b7: // (blue)
5700 element = EL_CHAR_O;
5703 case 0x15b8: // (blue)
5704 element = EL_CHAR_P;
5707 case 0x15b9: // (blue)
5708 element = EL_CHAR_Q;
5711 case 0x15ba: // (blue)
5712 element = EL_CHAR_R;
5715 case 0x15bb: // (blue)
5716 element = EL_CHAR_S;
5719 case 0x15bc: // (blue)
5720 element = EL_CHAR_T;
5723 case 0x15bd: // (blue)
5724 element = EL_CHAR_U;
5727 case 0x15be: // (blue)
5728 element = EL_CHAR_V;
5731 case 0x15bf: // (blue)
5732 element = EL_CHAR_W;
5735 case 0x15c0: // (blue)
5736 element = EL_CHAR_X;
5739 case 0x15c1: // (blue)
5740 element = EL_CHAR_Y;
5743 case 0x15c2: // (blue)
5744 element = EL_CHAR_Z;
5747 case 0x15c3: // (blue)
5748 element = EL_CHAR_AUMLAUT;
5751 case 0x15c4: // (blue)
5752 element = EL_CHAR_OUMLAUT;
5755 case 0x15c5: // (blue)
5756 element = EL_CHAR_UUMLAUT;
5759 case 0x15c6: // (blue)
5760 element = EL_CHAR_0;
5763 case 0x15c7: // (blue)
5764 element = EL_CHAR_1;
5767 case 0x15c8: // (blue)
5768 element = EL_CHAR_2;
5771 case 0x15c9: // (blue)
5772 element = EL_CHAR_3;
5775 case 0x15ca: // (blue)
5776 element = EL_CHAR_4;
5779 case 0x15cb: // (blue)
5780 element = EL_CHAR_5;
5783 case 0x15cc: // (blue)
5784 element = EL_CHAR_6;
5787 case 0x15cd: // (blue)
5788 element = EL_CHAR_7;
5791 case 0x15ce: // (blue)
5792 element = EL_CHAR_8;
5795 case 0x15cf: // (blue)
5796 element = EL_CHAR_9;
5799 case 0x15d0: // (blue)
5800 element = EL_CHAR_PERIOD;
5803 case 0x15d1: // (blue)
5804 element = EL_CHAR_EXCLAM;
5807 case 0x15d2: // (blue)
5808 element = EL_CHAR_COLON;
5811 case 0x15d3: // (blue)
5812 element = EL_CHAR_LESS;
5815 case 0x15d4: // (blue)
5816 element = EL_CHAR_GREATER;
5819 case 0x15d5: // (blue)
5820 element = EL_CHAR_QUESTION;
5823 case 0x15d6: // (blue)
5824 element = EL_CHAR_COPYRIGHT;
5827 case 0x15d7: // (blue)
5828 element = EL_CHAR_UP;
5831 case 0x15d8: // (blue)
5832 element = EL_CHAR_DOWN;
5835 case 0x15d9: // (blue)
5836 element = EL_CHAR_BUTTON;
5839 case 0x15da: // (blue)
5840 element = EL_CHAR_PLUS;
5843 case 0x15db: // (blue)
5844 element = EL_CHAR_MINUS;
5847 case 0x15dc: // (blue)
5848 element = EL_CHAR_APOSTROPHE;
5851 case 0x15dd: // (blue)
5852 element = EL_CHAR_PARENLEFT;
5855 case 0x15de: // (blue)
5856 element = EL_CHAR_PARENRIGHT;
5859 case 0x15df: // (green)
5860 element = EL_CHAR_A;
5863 case 0x15e0: // (green)
5864 element = EL_CHAR_B;
5867 case 0x15e1: // (green)
5868 element = EL_CHAR_C;
5871 case 0x15e2: // (green)
5872 element = EL_CHAR_D;
5875 case 0x15e3: // (green)
5876 element = EL_CHAR_E;
5879 case 0x15e4: // (green)
5880 element = EL_CHAR_F;
5883 case 0x15e5: // (green)
5884 element = EL_CHAR_G;
5887 case 0x15e6: // (green)
5888 element = EL_CHAR_H;
5891 case 0x15e7: // (green)
5892 element = EL_CHAR_I;
5895 case 0x15e8: // (green)
5896 element = EL_CHAR_J;
5899 case 0x15e9: // (green)
5900 element = EL_CHAR_K;
5903 case 0x15ea: // (green)
5904 element = EL_CHAR_L;
5907 case 0x15eb: // (green)
5908 element = EL_CHAR_M;
5911 case 0x15ec: // (green)
5912 element = EL_CHAR_N;
5915 case 0x15ed: // (green)
5916 element = EL_CHAR_O;
5919 case 0x15ee: // (green)
5920 element = EL_CHAR_P;
5923 case 0x15ef: // (green)
5924 element = EL_CHAR_Q;
5927 case 0x15f0: // (green)
5928 element = EL_CHAR_R;
5931 case 0x15f1: // (green)
5932 element = EL_CHAR_S;
5935 case 0x15f2: // (green)
5936 element = EL_CHAR_T;
5939 case 0x15f3: // (green)
5940 element = EL_CHAR_U;
5943 case 0x15f4: // (green)
5944 element = EL_CHAR_V;
5947 case 0x15f5: // (green)
5948 element = EL_CHAR_W;
5951 case 0x15f6: // (green)
5952 element = EL_CHAR_X;
5955 case 0x15f7: // (green)
5956 element = EL_CHAR_Y;
5959 case 0x15f8: // (green)
5960 element = EL_CHAR_Z;
5963 case 0x15f9: // (green)
5964 element = EL_CHAR_AUMLAUT;
5967 case 0x15fa: // (green)
5968 element = EL_CHAR_OUMLAUT;
5971 case 0x15fb: // (green)
5972 element = EL_CHAR_UUMLAUT;
5975 case 0x15fc: // (green)
5976 element = EL_CHAR_0;
5979 case 0x15fd: // (green)
5980 element = EL_CHAR_1;
5983 case 0x15fe: // (green)
5984 element = EL_CHAR_2;
5987 case 0x15ff: // (green)
5988 element = EL_CHAR_3;
5991 case 0x1600: // (green)
5992 element = EL_CHAR_4;
5995 case 0x1601: // (green)
5996 element = EL_CHAR_5;
5999 case 0x1602: // (green)
6000 element = EL_CHAR_6;
6003 case 0x1603: // (green)
6004 element = EL_CHAR_7;
6007 case 0x1604: // (green)
6008 element = EL_CHAR_8;
6011 case 0x1605: // (green)
6012 element = EL_CHAR_9;
6015 case 0x1606: // (green)
6016 element = EL_CHAR_PERIOD;
6019 case 0x1607: // (green)
6020 element = EL_CHAR_EXCLAM;
6023 case 0x1608: // (green)
6024 element = EL_CHAR_COLON;
6027 case 0x1609: // (green)
6028 element = EL_CHAR_LESS;
6031 case 0x160a: // (green)
6032 element = EL_CHAR_GREATER;
6035 case 0x160b: // (green)
6036 element = EL_CHAR_QUESTION;
6039 case 0x160c: // (green)
6040 element = EL_CHAR_COPYRIGHT;
6043 case 0x160d: // (green)
6044 element = EL_CHAR_UP;
6047 case 0x160e: // (green)
6048 element = EL_CHAR_DOWN;
6051 case 0x160f: // (green)
6052 element = EL_CHAR_BUTTON;
6055 case 0x1610: // (green)
6056 element = EL_CHAR_PLUS;
6059 case 0x1611: // (green)
6060 element = EL_CHAR_MINUS;
6063 case 0x1612: // (green)
6064 element = EL_CHAR_APOSTROPHE;
6067 case 0x1613: // (green)
6068 element = EL_CHAR_PARENLEFT;
6071 case 0x1614: // (green)
6072 element = EL_CHAR_PARENRIGHT;
6075 case 0x1615: // (blue steel)
6076 element = EL_STEEL_CHAR_A;
6079 case 0x1616: // (blue steel)
6080 element = EL_STEEL_CHAR_B;
6083 case 0x1617: // (blue steel)
6084 element = EL_STEEL_CHAR_C;
6087 case 0x1618: // (blue steel)
6088 element = EL_STEEL_CHAR_D;
6091 case 0x1619: // (blue steel)
6092 element = EL_STEEL_CHAR_E;
6095 case 0x161a: // (blue steel)
6096 element = EL_STEEL_CHAR_F;
6099 case 0x161b: // (blue steel)
6100 element = EL_STEEL_CHAR_G;
6103 case 0x161c: // (blue steel)
6104 element = EL_STEEL_CHAR_H;
6107 case 0x161d: // (blue steel)
6108 element = EL_STEEL_CHAR_I;
6111 case 0x161e: // (blue steel)
6112 element = EL_STEEL_CHAR_J;
6115 case 0x161f: // (blue steel)
6116 element = EL_STEEL_CHAR_K;
6119 case 0x1620: // (blue steel)
6120 element = EL_STEEL_CHAR_L;
6123 case 0x1621: // (blue steel)
6124 element = EL_STEEL_CHAR_M;
6127 case 0x1622: // (blue steel)
6128 element = EL_STEEL_CHAR_N;
6131 case 0x1623: // (blue steel)
6132 element = EL_STEEL_CHAR_O;
6135 case 0x1624: // (blue steel)
6136 element = EL_STEEL_CHAR_P;
6139 case 0x1625: // (blue steel)
6140 element = EL_STEEL_CHAR_Q;
6143 case 0x1626: // (blue steel)
6144 element = EL_STEEL_CHAR_R;
6147 case 0x1627: // (blue steel)
6148 element = EL_STEEL_CHAR_S;
6151 case 0x1628: // (blue steel)
6152 element = EL_STEEL_CHAR_T;
6155 case 0x1629: // (blue steel)
6156 element = EL_STEEL_CHAR_U;
6159 case 0x162a: // (blue steel)
6160 element = EL_STEEL_CHAR_V;
6163 case 0x162b: // (blue steel)
6164 element = EL_STEEL_CHAR_W;
6167 case 0x162c: // (blue steel)
6168 element = EL_STEEL_CHAR_X;
6171 case 0x162d: // (blue steel)
6172 element = EL_STEEL_CHAR_Y;
6175 case 0x162e: // (blue steel)
6176 element = EL_STEEL_CHAR_Z;
6179 case 0x162f: // (blue steel)
6180 element = EL_STEEL_CHAR_AUMLAUT;
6183 case 0x1630: // (blue steel)
6184 element = EL_STEEL_CHAR_OUMLAUT;
6187 case 0x1631: // (blue steel)
6188 element = EL_STEEL_CHAR_UUMLAUT;
6191 case 0x1632: // (blue steel)
6192 element = EL_STEEL_CHAR_0;
6195 case 0x1633: // (blue steel)
6196 element = EL_STEEL_CHAR_1;
6199 case 0x1634: // (blue steel)
6200 element = EL_STEEL_CHAR_2;
6203 case 0x1635: // (blue steel)
6204 element = EL_STEEL_CHAR_3;
6207 case 0x1636: // (blue steel)
6208 element = EL_STEEL_CHAR_4;
6211 case 0x1637: // (blue steel)
6212 element = EL_STEEL_CHAR_5;
6215 case 0x1638: // (blue steel)
6216 element = EL_STEEL_CHAR_6;
6219 case 0x1639: // (blue steel)
6220 element = EL_STEEL_CHAR_7;
6223 case 0x163a: // (blue steel)
6224 element = EL_STEEL_CHAR_8;
6227 case 0x163b: // (blue steel)
6228 element = EL_STEEL_CHAR_9;
6231 case 0x163c: // (blue steel)
6232 element = EL_STEEL_CHAR_PERIOD;
6235 case 0x163d: // (blue steel)
6236 element = EL_STEEL_CHAR_EXCLAM;
6239 case 0x163e: // (blue steel)
6240 element = EL_STEEL_CHAR_COLON;
6243 case 0x163f: // (blue steel)
6244 element = EL_STEEL_CHAR_LESS;
6247 case 0x1640: // (blue steel)
6248 element = EL_STEEL_CHAR_GREATER;
6251 case 0x1641: // (blue steel)
6252 element = EL_STEEL_CHAR_QUESTION;
6255 case 0x1642: // (blue steel)
6256 element = EL_STEEL_CHAR_COPYRIGHT;
6259 case 0x1643: // (blue steel)
6260 element = EL_STEEL_CHAR_UP;
6263 case 0x1644: // (blue steel)
6264 element = EL_STEEL_CHAR_DOWN;
6267 case 0x1645: // (blue steel)
6268 element = EL_STEEL_CHAR_BUTTON;
6271 case 0x1646: // (blue steel)
6272 element = EL_STEEL_CHAR_PLUS;
6275 case 0x1647: // (blue steel)
6276 element = EL_STEEL_CHAR_MINUS;
6279 case 0x1648: // (blue steel)
6280 element = EL_STEEL_CHAR_APOSTROPHE;
6283 case 0x1649: // (blue steel)
6284 element = EL_STEEL_CHAR_PARENLEFT;
6287 case 0x164a: // (blue steel)
6288 element = EL_STEEL_CHAR_PARENRIGHT;
6291 case 0x164b: // (green steel)
6292 element = EL_STEEL_CHAR_A;
6295 case 0x164c: // (green steel)
6296 element = EL_STEEL_CHAR_B;
6299 case 0x164d: // (green steel)
6300 element = EL_STEEL_CHAR_C;
6303 case 0x164e: // (green steel)
6304 element = EL_STEEL_CHAR_D;
6307 case 0x164f: // (green steel)
6308 element = EL_STEEL_CHAR_E;
6311 case 0x1650: // (green steel)
6312 element = EL_STEEL_CHAR_F;
6315 case 0x1651: // (green steel)
6316 element = EL_STEEL_CHAR_G;
6319 case 0x1652: // (green steel)
6320 element = EL_STEEL_CHAR_H;
6323 case 0x1653: // (green steel)
6324 element = EL_STEEL_CHAR_I;
6327 case 0x1654: // (green steel)
6328 element = EL_STEEL_CHAR_J;
6331 case 0x1655: // (green steel)
6332 element = EL_STEEL_CHAR_K;
6335 case 0x1656: // (green steel)
6336 element = EL_STEEL_CHAR_L;
6339 case 0x1657: // (green steel)
6340 element = EL_STEEL_CHAR_M;
6343 case 0x1658: // (green steel)
6344 element = EL_STEEL_CHAR_N;
6347 case 0x1659: // (green steel)
6348 element = EL_STEEL_CHAR_O;
6351 case 0x165a: // (green steel)
6352 element = EL_STEEL_CHAR_P;
6355 case 0x165b: // (green steel)
6356 element = EL_STEEL_CHAR_Q;
6359 case 0x165c: // (green steel)
6360 element = EL_STEEL_CHAR_R;
6363 case 0x165d: // (green steel)
6364 element = EL_STEEL_CHAR_S;
6367 case 0x165e: // (green steel)
6368 element = EL_STEEL_CHAR_T;
6371 case 0x165f: // (green steel)
6372 element = EL_STEEL_CHAR_U;
6375 case 0x1660: // (green steel)
6376 element = EL_STEEL_CHAR_V;
6379 case 0x1661: // (green steel)
6380 element = EL_STEEL_CHAR_W;
6383 case 0x1662: // (green steel)
6384 element = EL_STEEL_CHAR_X;
6387 case 0x1663: // (green steel)
6388 element = EL_STEEL_CHAR_Y;
6391 case 0x1664: // (green steel)
6392 element = EL_STEEL_CHAR_Z;
6395 case 0x1665: // (green steel)
6396 element = EL_STEEL_CHAR_AUMLAUT;
6399 case 0x1666: // (green steel)
6400 element = EL_STEEL_CHAR_OUMLAUT;
6403 case 0x1667: // (green steel)
6404 element = EL_STEEL_CHAR_UUMLAUT;
6407 case 0x1668: // (green steel)
6408 element = EL_STEEL_CHAR_0;
6411 case 0x1669: // (green steel)
6412 element = EL_STEEL_CHAR_1;
6415 case 0x166a: // (green steel)
6416 element = EL_STEEL_CHAR_2;
6419 case 0x166b: // (green steel)
6420 element = EL_STEEL_CHAR_3;
6423 case 0x166c: // (green steel)
6424 element = EL_STEEL_CHAR_4;
6427 case 0x166d: // (green steel)
6428 element = EL_STEEL_CHAR_5;
6431 case 0x166e: // (green steel)
6432 element = EL_STEEL_CHAR_6;
6435 case 0x166f: // (green steel)
6436 element = EL_STEEL_CHAR_7;
6439 case 0x1670: // (green steel)
6440 element = EL_STEEL_CHAR_8;
6443 case 0x1671: // (green steel)
6444 element = EL_STEEL_CHAR_9;
6447 case 0x1672: // (green steel)
6448 element = EL_STEEL_CHAR_PERIOD;
6451 case 0x1673: // (green steel)
6452 element = EL_STEEL_CHAR_EXCLAM;
6455 case 0x1674: // (green steel)
6456 element = EL_STEEL_CHAR_COLON;
6459 case 0x1675: // (green steel)
6460 element = EL_STEEL_CHAR_LESS;
6463 case 0x1676: // (green steel)
6464 element = EL_STEEL_CHAR_GREATER;
6467 case 0x1677: // (green steel)
6468 element = EL_STEEL_CHAR_QUESTION;
6471 case 0x1678: // (green steel)
6472 element = EL_STEEL_CHAR_COPYRIGHT;
6475 case 0x1679: // (green steel)
6476 element = EL_STEEL_CHAR_UP;
6479 case 0x167a: // (green steel)
6480 element = EL_STEEL_CHAR_DOWN;
6483 case 0x167b: // (green steel)
6484 element = EL_STEEL_CHAR_BUTTON;
6487 case 0x167c: // (green steel)
6488 element = EL_STEEL_CHAR_PLUS;
6491 case 0x167d: // (green steel)
6492 element = EL_STEEL_CHAR_MINUS;
6495 case 0x167e: // (green steel)
6496 element = EL_STEEL_CHAR_APOSTROPHE;
6499 case 0x167f: // (green steel)
6500 element = EL_STEEL_CHAR_PARENLEFT;
6503 case 0x1680: // (green steel)
6504 element = EL_STEEL_CHAR_PARENRIGHT;
6507 case 0x1681: // gate (red)
6508 element = EL_EM_GATE_1;
6511 case 0x1682: // secret gate (red)
6512 element = EL_EM_GATE_1_GRAY;
6515 case 0x1683: // gate (yellow)
6516 element = EL_EM_GATE_2;
6519 case 0x1684: // secret gate (yellow)
6520 element = EL_EM_GATE_2_GRAY;
6523 case 0x1685: // gate (blue)
6524 element = EL_EM_GATE_4;
6527 case 0x1686: // secret gate (blue)
6528 element = EL_EM_GATE_4_GRAY;
6531 case 0x1687: // gate (green)
6532 element = EL_EM_GATE_3;
6535 case 0x1688: // secret gate (green)
6536 element = EL_EM_GATE_3_GRAY;
6539 case 0x1689: // gate (white)
6540 element = EL_DC_GATE_WHITE;
6543 case 0x168a: // secret gate (white)
6544 element = EL_DC_GATE_WHITE_GRAY;
6547 case 0x168b: // secret gate (no key)
6548 element = EL_DC_GATE_FAKE_GRAY;
6552 element = EL_ROBOT_WHEEL;
6556 element = EL_DC_TIMEGATE_SWITCH;
6560 element = EL_ACID_POOL_BOTTOM;
6564 element = EL_ACID_POOL_TOPLEFT;
6568 element = EL_ACID_POOL_TOPRIGHT;
6572 element = EL_ACID_POOL_BOTTOMLEFT;
6576 element = EL_ACID_POOL_BOTTOMRIGHT;
6580 element = EL_STEELWALL;
6584 element = EL_STEELWALL_SLIPPERY;
6587 case 0x1695: // steel wall (not round)
6588 element = EL_STEELWALL;
6591 case 0x1696: // steel wall (left)
6592 element = EL_DC_STEELWALL_1_LEFT;
6595 case 0x1697: // steel wall (bottom)
6596 element = EL_DC_STEELWALL_1_BOTTOM;
6599 case 0x1698: // steel wall (right)
6600 element = EL_DC_STEELWALL_1_RIGHT;
6603 case 0x1699: // steel wall (top)
6604 element = EL_DC_STEELWALL_1_TOP;
6607 case 0x169a: // steel wall (left/bottom)
6608 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6611 case 0x169b: // steel wall (right/bottom)
6612 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6615 case 0x169c: // steel wall (right/top)
6616 element = EL_DC_STEELWALL_1_TOPRIGHT;
6619 case 0x169d: // steel wall (left/top)
6620 element = EL_DC_STEELWALL_1_TOPLEFT;
6623 case 0x169e: // steel wall (right/bottom small)
6624 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6627 case 0x169f: // steel wall (left/bottom small)
6628 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6631 case 0x16a0: // steel wall (right/top small)
6632 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6635 case 0x16a1: // steel wall (left/top small)
6636 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6639 case 0x16a2: // steel wall (left/right)
6640 element = EL_DC_STEELWALL_1_VERTICAL;
6643 case 0x16a3: // steel wall (top/bottom)
6644 element = EL_DC_STEELWALL_1_HORIZONTAL;
6647 case 0x16a4: // steel wall 2 (left end)
6648 element = EL_DC_STEELWALL_2_LEFT;
6651 case 0x16a5: // steel wall 2 (right end)
6652 element = EL_DC_STEELWALL_2_RIGHT;
6655 case 0x16a6: // steel wall 2 (top end)
6656 element = EL_DC_STEELWALL_2_TOP;
6659 case 0x16a7: // steel wall 2 (bottom end)
6660 element = EL_DC_STEELWALL_2_BOTTOM;
6663 case 0x16a8: // steel wall 2 (left/right)
6664 element = EL_DC_STEELWALL_2_HORIZONTAL;
6667 case 0x16a9: // steel wall 2 (up/down)
6668 element = EL_DC_STEELWALL_2_VERTICAL;
6671 case 0x16aa: // steel wall 2 (mid)
6672 element = EL_DC_STEELWALL_2_MIDDLE;
6676 element = EL_SIGN_EXCLAMATION;
6680 element = EL_SIGN_RADIOACTIVITY;
6684 element = EL_SIGN_STOP;
6688 element = EL_SIGN_WHEELCHAIR;
6692 element = EL_SIGN_PARKING;
6696 element = EL_SIGN_NO_ENTRY;
6700 element = EL_SIGN_HEART;
6704 element = EL_SIGN_GIVE_WAY;
6708 element = EL_SIGN_ENTRY_FORBIDDEN;
6712 element = EL_SIGN_EMERGENCY_EXIT;
6716 element = EL_SIGN_YIN_YANG;
6720 element = EL_WALL_EMERALD;
6724 element = EL_WALL_DIAMOND;
6728 element = EL_WALL_PEARL;
6732 element = EL_WALL_CRYSTAL;
6736 element = EL_INVISIBLE_WALL;
6740 element = EL_INVISIBLE_STEELWALL;
6744 // EL_INVISIBLE_SAND
6747 element = EL_LIGHT_SWITCH;
6751 element = EL_ENVELOPE_1;
6755 if (element >= 0x0117 && element <= 0x036e) // (?)
6756 element = EL_DIAMOND;
6757 else if (element >= 0x042d && element <= 0x0684) // (?)
6758 element = EL_EMERALD;
6759 else if (element >= 0x157c && element <= 0x158b)
6761 else if (element >= 0x1590 && element <= 0x159f)
6762 element = EL_DC_LANDMINE;
6763 else if (element >= 0x16bc && element <= 0x16cb)
6764 element = EL_INVISIBLE_SAND;
6767 Warn("unknown Diamond Caves element 0x%04x", element);
6769 element = EL_UNKNOWN;
6774 return getMappedElement(element);
6777 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6779 byte header[DC_LEVEL_HEADER_SIZE];
6781 int envelope_header_pos = 62;
6782 int envelope_content_pos = 94;
6783 int level_name_pos = 251;
6784 int level_author_pos = 292;
6785 int envelope_header_len;
6786 int envelope_content_len;
6788 int level_author_len;
6790 int num_yamyam_contents;
6793 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6795 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6797 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6799 header[i * 2 + 0] = header_word >> 8;
6800 header[i * 2 + 1] = header_word & 0xff;
6803 // read some values from level header to check level decoding integrity
6804 fieldx = header[6] | (header[7] << 8);
6805 fieldy = header[8] | (header[9] << 8);
6806 num_yamyam_contents = header[60] | (header[61] << 8);
6808 // do some simple sanity checks to ensure that level was correctly decoded
6809 if (fieldx < 1 || fieldx > 256 ||
6810 fieldy < 1 || fieldy > 256 ||
6811 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6813 level->no_valid_file = TRUE;
6815 Warn("cannot decode level from stream -- using empty level");
6820 // maximum envelope header size is 31 bytes
6821 envelope_header_len = header[envelope_header_pos];
6822 // maximum envelope content size is 110 (156?) bytes
6823 envelope_content_len = header[envelope_content_pos];
6825 // maximum level title size is 40 bytes
6826 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6827 // maximum level author size is 30 (51?) bytes
6828 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6832 for (i = 0; i < envelope_header_len; i++)
6833 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6834 level->envelope[0].text[envelope_size++] =
6835 header[envelope_header_pos + 1 + i];
6837 if (envelope_header_len > 0 && envelope_content_len > 0)
6839 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6840 level->envelope[0].text[envelope_size++] = '\n';
6841 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6842 level->envelope[0].text[envelope_size++] = '\n';
6845 for (i = 0; i < envelope_content_len; i++)
6846 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6847 level->envelope[0].text[envelope_size++] =
6848 header[envelope_content_pos + 1 + i];
6850 level->envelope[0].text[envelope_size] = '\0';
6852 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6853 level->envelope[0].ysize = 10;
6854 level->envelope[0].autowrap = TRUE;
6855 level->envelope[0].centered = TRUE;
6857 for (i = 0; i < level_name_len; i++)
6858 level->name[i] = header[level_name_pos + 1 + i];
6859 level->name[level_name_len] = '\0';
6861 for (i = 0; i < level_author_len; i++)
6862 level->author[i] = header[level_author_pos + 1 + i];
6863 level->author[level_author_len] = '\0';
6865 num_yamyam_contents = header[60] | (header[61] << 8);
6866 level->num_yamyam_contents =
6867 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6869 for (i = 0; i < num_yamyam_contents; i++)
6871 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6873 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6874 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6876 if (i < MAX_ELEMENT_CONTENTS)
6877 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6881 fieldx = header[6] | (header[7] << 8);
6882 fieldy = header[8] | (header[9] << 8);
6883 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6884 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6886 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6888 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6889 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6891 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6892 level->field[x][y] = getMappedElement_DC(element_dc);
6895 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6896 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6897 level->field[x][y] = EL_PLAYER_1;
6899 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6900 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6901 level->field[x][y] = EL_PLAYER_2;
6903 level->gems_needed = header[18] | (header[19] << 8);
6905 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6906 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6907 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6908 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6909 level->score[SC_NUT] = header[28] | (header[29] << 8);
6910 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6911 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6912 level->score[SC_BUG] = header[34] | (header[35] << 8);
6913 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6914 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6915 level->score[SC_KEY] = header[40] | (header[41] << 8);
6916 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6918 level->time = header[44] | (header[45] << 8);
6920 level->amoeba_speed = header[46] | (header[47] << 8);
6921 level->time_light = header[48] | (header[49] << 8);
6922 level->time_timegate = header[50] | (header[51] << 8);
6923 level->time_wheel = header[52] | (header[53] << 8);
6924 level->time_magic_wall = header[54] | (header[55] << 8);
6925 level->extra_time = header[56] | (header[57] << 8);
6926 level->shield_normal_time = header[58] | (header[59] << 8);
6928 // shield and extra time elements do not have a score
6929 level->score[SC_SHIELD] = 0;
6930 level->extra_time_score = 0;
6932 // set time for normal and deadly shields to the same value
6933 level->shield_deadly_time = level->shield_normal_time;
6935 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6936 // can slip down from flat walls, like normal walls and steel walls
6937 level->em_slippery_gems = TRUE;
6939 // time score is counted for each 10 seconds left in Diamond Caves levels
6940 level->time_score_base = 10;
6943 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6944 struct LevelFileInfo *level_file_info,
6945 boolean level_info_only)
6947 char *filename = level_file_info->filename;
6949 int num_magic_bytes = 8;
6950 char magic_bytes[num_magic_bytes + 1];
6951 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6953 if (!(file = openFile(filename, MODE_READ)))
6955 level->no_valid_file = TRUE;
6957 if (!level_info_only)
6958 Warn("cannot read level '%s' -- using empty level", filename);
6963 // fseek(file, 0x0000, SEEK_SET);
6965 if (level_file_info->packed)
6967 // read "magic bytes" from start of file
6968 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6969 magic_bytes[0] = '\0';
6971 // check "magic bytes" for correct file format
6972 if (!strPrefix(magic_bytes, "DC2"))
6974 level->no_valid_file = TRUE;
6976 Warn("unknown DC level file '%s' -- using empty level", filename);
6981 if (strPrefix(magic_bytes, "DC2Win95") ||
6982 strPrefix(magic_bytes, "DC2Win98"))
6984 int position_first_level = 0x00fa;
6985 int extra_bytes = 4;
6988 // advance file stream to first level inside the level package
6989 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6991 // each block of level data is followed by block of non-level data
6992 num_levels_to_skip *= 2;
6994 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6995 while (num_levels_to_skip >= 0)
6997 // advance file stream to next level inside the level package
6998 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7000 level->no_valid_file = TRUE;
7002 Warn("cannot fseek in file '%s' -- using empty level", filename);
7007 // skip apparently unused extra bytes following each level
7008 ReadUnusedBytesFromFile(file, extra_bytes);
7010 // read size of next level in level package
7011 skip_bytes = getFile32BitLE(file);
7013 num_levels_to_skip--;
7018 level->no_valid_file = TRUE;
7020 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7026 LoadLevelFromFileStream_DC(file, level);
7032 // ----------------------------------------------------------------------------
7033 // functions for loading SB level
7034 // ----------------------------------------------------------------------------
7036 int getMappedElement_SB(int element_ascii, boolean use_ces)
7044 sb_element_mapping[] =
7046 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7047 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7048 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7049 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7050 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7051 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7052 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7053 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7060 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7061 if (element_ascii == sb_element_mapping[i].ascii)
7062 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7064 return EL_UNDEFINED;
7067 static void SetLevelSettings_SB(struct LevelInfo *level)
7071 level->use_step_counter = TRUE;
7074 level->score[SC_TIME_BONUS] = 0;
7075 level->time_score_base = 1;
7076 level->rate_time_over_score = TRUE;
7079 level->auto_exit_sokoban = TRUE;
7082 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7083 struct LevelFileInfo *level_file_info,
7084 boolean level_info_only)
7086 char *filename = level_file_info->filename;
7087 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7088 char last_comment[MAX_LINE_LEN];
7089 char level_name[MAX_LINE_LEN];
7092 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7093 boolean read_continued_line = FALSE;
7094 boolean reading_playfield = FALSE;
7095 boolean got_valid_playfield_line = FALSE;
7096 boolean invalid_playfield_char = FALSE;
7097 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7098 int file_level_nr = 0;
7099 int x = 0, y = 0; // initialized to make compilers happy
7101 last_comment[0] = '\0';
7102 level_name[0] = '\0';
7104 if (!(file = openFile(filename, MODE_READ)))
7106 level->no_valid_file = TRUE;
7108 if (!level_info_only)
7109 Warn("cannot read level '%s' -- using empty level", filename);
7114 while (!checkEndOfFile(file))
7116 // level successfully read, but next level may follow here
7117 if (!got_valid_playfield_line && reading_playfield)
7119 // read playfield from single level file -- skip remaining file
7120 if (!level_file_info->packed)
7123 if (file_level_nr >= num_levels_to_skip)
7128 last_comment[0] = '\0';
7129 level_name[0] = '\0';
7131 reading_playfield = FALSE;
7134 got_valid_playfield_line = FALSE;
7136 // read next line of input file
7137 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7140 // cut trailing line break (this can be newline and/or carriage return)
7141 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7142 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7145 // copy raw input line for later use (mainly debugging output)
7146 strcpy(line_raw, line);
7148 if (read_continued_line)
7150 // append new line to existing line, if there is enough space
7151 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7152 strcat(previous_line, line_ptr);
7154 strcpy(line, previous_line); // copy storage buffer to line
7156 read_continued_line = FALSE;
7159 // if the last character is '\', continue at next line
7160 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7162 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7163 strcpy(previous_line, line); // copy line to storage buffer
7165 read_continued_line = TRUE;
7171 if (line[0] == '\0')
7174 // extract comment text from comment line
7177 for (line_ptr = line; *line_ptr; line_ptr++)
7178 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7181 strcpy(last_comment, line_ptr);
7186 // extract level title text from line containing level title
7187 if (line[0] == '\'')
7189 strcpy(level_name, &line[1]);
7191 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7192 level_name[strlen(level_name) - 1] = '\0';
7197 // skip lines containing only spaces (or empty lines)
7198 for (line_ptr = line; *line_ptr; line_ptr++)
7199 if (*line_ptr != ' ')
7201 if (*line_ptr == '\0')
7204 // at this point, we have found a line containing part of a playfield
7206 got_valid_playfield_line = TRUE;
7208 if (!reading_playfield)
7210 reading_playfield = TRUE;
7211 invalid_playfield_char = FALSE;
7213 for (x = 0; x < MAX_LEV_FIELDX; x++)
7214 for (y = 0; y < MAX_LEV_FIELDY; y++)
7215 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7220 // start with topmost tile row
7224 // skip playfield line if larger row than allowed
7225 if (y >= MAX_LEV_FIELDY)
7228 // start with leftmost tile column
7231 // read playfield elements from line
7232 for (line_ptr = line; *line_ptr; line_ptr++)
7234 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7236 // stop parsing playfield line if larger column than allowed
7237 if (x >= MAX_LEV_FIELDX)
7240 if (mapped_sb_element == EL_UNDEFINED)
7242 invalid_playfield_char = TRUE;
7247 level->field[x][y] = mapped_sb_element;
7249 // continue with next tile column
7252 level->fieldx = MAX(x, level->fieldx);
7255 if (invalid_playfield_char)
7257 // if first playfield line, treat invalid lines as comment lines
7259 reading_playfield = FALSE;
7264 // continue with next tile row
7272 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7273 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7275 if (!reading_playfield)
7277 level->no_valid_file = TRUE;
7279 Warn("cannot read level '%s' -- using empty level", filename);
7284 if (*level_name != '\0')
7286 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7287 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7289 else if (*last_comment != '\0')
7291 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7292 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7296 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7299 // set all empty fields beyond the border walls to invisible steel wall
7300 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7302 if ((x == 0 || x == level->fieldx - 1 ||
7303 y == 0 || y == level->fieldy - 1) &&
7304 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7305 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7306 level->field, level->fieldx, level->fieldy);
7309 // set special level settings for Sokoban levels
7310 SetLevelSettings_SB(level);
7312 if (load_xsb_to_ces)
7314 // special global settings can now be set in level template
7315 level->use_custom_template = TRUE;
7320 // -------------------------------------------------------------------------
7321 // functions for handling native levels
7322 // -------------------------------------------------------------------------
7324 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7325 struct LevelFileInfo *level_file_info,
7326 boolean level_info_only)
7330 // determine position of requested level inside level package
7331 if (level_file_info->packed)
7332 pos = level_file_info->nr - leveldir_current->first_level;
7334 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7335 level->no_valid_file = TRUE;
7338 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7339 struct LevelFileInfo *level_file_info,
7340 boolean level_info_only)
7342 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7343 level->no_valid_file = TRUE;
7346 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7347 struct LevelFileInfo *level_file_info,
7348 boolean level_info_only)
7352 // determine position of requested level inside level package
7353 if (level_file_info->packed)
7354 pos = level_file_info->nr - leveldir_current->first_level;
7356 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7357 level->no_valid_file = TRUE;
7360 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7361 struct LevelFileInfo *level_file_info,
7362 boolean level_info_only)
7364 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7365 level->no_valid_file = TRUE;
7368 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7370 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7371 CopyNativeLevel_RND_to_BD(level);
7372 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7373 CopyNativeLevel_RND_to_EM(level);
7374 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7375 CopyNativeLevel_RND_to_SP(level);
7376 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7377 CopyNativeLevel_RND_to_MM(level);
7380 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7382 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7383 CopyNativeLevel_BD_to_RND(level);
7384 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7385 CopyNativeLevel_EM_to_RND(level);
7386 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7387 CopyNativeLevel_SP_to_RND(level);
7388 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7389 CopyNativeLevel_MM_to_RND(level);
7392 void SaveNativeLevel(struct LevelInfo *level)
7394 // saving native level files only supported for some game engines
7395 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7396 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7399 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7400 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7401 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7402 char *filename = getLevelFilenameFromBasename(basename);
7404 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7407 boolean success = FALSE;
7409 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7411 CopyNativeLevel_RND_to_BD(level);
7412 // CopyNativeTape_RND_to_BD(level);
7414 success = SaveNativeLevel_BD(filename);
7416 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7418 CopyNativeLevel_RND_to_SP(level);
7419 CopyNativeTape_RND_to_SP(level);
7421 success = SaveNativeLevel_SP(filename);
7425 Request("Native level file saved!", REQ_CONFIRM);
7427 Request("Failed to save native level file!", REQ_CONFIRM);
7431 // ----------------------------------------------------------------------------
7432 // functions for loading generic level
7433 // ----------------------------------------------------------------------------
7435 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7436 struct LevelFileInfo *level_file_info,
7437 boolean level_info_only)
7439 // always start with reliable default values
7440 setLevelInfoToDefaults(level, level_info_only, TRUE);
7442 switch (level_file_info->type)
7444 case LEVEL_FILE_TYPE_RND:
7445 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7448 case LEVEL_FILE_TYPE_BD:
7449 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7450 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7453 case LEVEL_FILE_TYPE_EM:
7454 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7455 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7458 case LEVEL_FILE_TYPE_SP:
7459 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7460 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7463 case LEVEL_FILE_TYPE_MM:
7464 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7465 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7468 case LEVEL_FILE_TYPE_DC:
7469 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7472 case LEVEL_FILE_TYPE_SB:
7473 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7477 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7481 // if level file is invalid, restore level structure to default values
7482 if (level->no_valid_file)
7483 setLevelInfoToDefaults(level, level_info_only, FALSE);
7485 if (check_special_flags("use_native_bd_game_engine"))
7486 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7488 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7489 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7491 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7492 CopyNativeLevel_Native_to_RND(level);
7495 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7497 static struct LevelFileInfo level_file_info;
7499 // always start with reliable default values
7500 setFileInfoToDefaults(&level_file_info);
7502 level_file_info.nr = 0; // unknown level number
7503 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7505 setString(&level_file_info.filename, filename);
7507 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7510 static void LoadLevel_InitVersion(struct LevelInfo *level)
7514 if (leveldir_current == NULL) // only when dumping level
7517 // all engine modifications also valid for levels which use latest engine
7518 if (level->game_version < VERSION_IDENT(3,2,0,5))
7520 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7521 level->time_score_base = 10;
7524 if (leveldir_current->latest_engine)
7526 // ---------- use latest game engine --------------------------------------
7528 /* For all levels which are forced to use the latest game engine version
7529 (normally all but user contributed, private and undefined levels), set
7530 the game engine version to the actual version; this allows for actual
7531 corrections in the game engine to take effect for existing, converted
7532 levels (from "classic" or other existing games) to make the emulation
7533 of the corresponding game more accurate, while (hopefully) not breaking
7534 existing levels created from other players. */
7536 level->game_version = GAME_VERSION_ACTUAL;
7538 /* Set special EM style gems behaviour: EM style gems slip down from
7539 normal, steel and growing wall. As this is a more fundamental change,
7540 it seems better to set the default behaviour to "off" (as it is more
7541 natural) and make it configurable in the level editor (as a property
7542 of gem style elements). Already existing converted levels (neither
7543 private nor contributed levels) are changed to the new behaviour. */
7545 if (level->file_version < FILE_VERSION_2_0)
7546 level->em_slippery_gems = TRUE;
7551 // ---------- use game engine the level was created with --------------------
7553 /* For all levels which are not forced to use the latest game engine
7554 version (normally user contributed, private and undefined levels),
7555 use the version of the game engine the levels were created for.
7557 Since 2.0.1, the game engine version is now directly stored
7558 in the level file (chunk "VERS"), so there is no need anymore
7559 to set the game version from the file version (except for old,
7560 pre-2.0 levels, where the game version is still taken from the
7561 file format version used to store the level -- see above). */
7563 // player was faster than enemies in 1.0.0 and before
7564 if (level->file_version == FILE_VERSION_1_0)
7565 for (i = 0; i < MAX_PLAYERS; i++)
7566 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7568 // default behaviour for EM style gems was "slippery" only in 2.0.1
7569 if (level->game_version == VERSION_IDENT(2,0,1,0))
7570 level->em_slippery_gems = TRUE;
7572 // springs could be pushed over pits before (pre-release version) 2.2.0
7573 if (level->game_version < VERSION_IDENT(2,2,0,0))
7574 level->use_spring_bug = TRUE;
7576 if (level->game_version < VERSION_IDENT(3,2,0,5))
7578 // time orb caused limited time in endless time levels before 3.2.0-5
7579 level->use_time_orb_bug = TRUE;
7581 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7582 level->block_snap_field = FALSE;
7584 // extra time score was same value as time left score before 3.2.0-5
7585 level->extra_time_score = level->score[SC_TIME_BONUS];
7588 if (level->game_version < VERSION_IDENT(3,2,0,7))
7590 // default behaviour for snapping was "not continuous" before 3.2.0-7
7591 level->continuous_snapping = FALSE;
7594 // only few elements were able to actively move into acid before 3.1.0
7595 // trigger settings did not exist before 3.1.0; set to default "any"
7596 if (level->game_version < VERSION_IDENT(3,1,0,0))
7598 // correct "can move into acid" settings (all zero in old levels)
7600 level->can_move_into_acid_bits = 0; // nothing can move into acid
7601 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7603 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7604 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7605 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7606 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7608 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7609 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7611 // correct trigger settings (stored as zero == "none" in old levels)
7613 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7615 int element = EL_CUSTOM_START + i;
7616 struct ElementInfo *ei = &element_info[element];
7618 for (j = 0; j < ei->num_change_pages; j++)
7620 struct ElementChangeInfo *change = &ei->change_page[j];
7622 change->trigger_player = CH_PLAYER_ANY;
7623 change->trigger_page = CH_PAGE_ANY;
7628 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7630 int element = EL_CUSTOM_256;
7631 struct ElementInfo *ei = &element_info[element];
7632 struct ElementChangeInfo *change = &ei->change_page[0];
7634 /* This is needed to fix a problem that was caused by a bugfix in function
7635 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7636 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7637 not replace walkable elements, but instead just placed the player on it,
7638 without placing the Sokoban field under the player). Unfortunately, this
7639 breaks "Snake Bite" style levels when the snake is halfway through a door
7640 that just closes (the snake head is still alive and can be moved in this
7641 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7642 player (without Sokoban element) which then gets killed as designed). */
7644 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7645 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7646 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7647 change->target_element = EL_PLAYER_1;
7650 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7651 if (level->game_version < VERSION_IDENT(3,2,5,0))
7653 /* This is needed to fix a problem that was caused by a bugfix in function
7654 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7655 corrects the behaviour when a custom element changes to another custom
7656 element with a higher element number that has change actions defined.
7657 Normally, only one change per frame is allowed for custom elements.
7658 Therefore, it is checked if a custom element already changed in the
7659 current frame; if it did, subsequent changes are suppressed.
7660 Unfortunately, this is only checked for element changes, but not for
7661 change actions, which are still executed. As the function above loops
7662 through all custom elements from lower to higher, an element change
7663 resulting in a lower CE number won't be checked again, while a target
7664 element with a higher number will also be checked, and potential change
7665 actions will get executed for this CE, too (which is wrong), while
7666 further changes are ignored (which is correct). As this bugfix breaks
7667 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7668 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7669 behaviour for existing levels and tapes that make use of this bug */
7671 level->use_action_after_change_bug = TRUE;
7674 // not centering level after relocating player was default only in 3.2.3
7675 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7676 level->shifted_relocation = TRUE;
7678 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7679 if (level->game_version < VERSION_IDENT(3,2,6,0))
7680 level->em_explodes_by_fire = TRUE;
7682 // levels were solved by the first player entering an exit up to 4.1.0.0
7683 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7684 level->solved_by_one_player = TRUE;
7686 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7687 if (level->game_version < VERSION_IDENT(4,1,1,1))
7688 level->use_life_bugs = TRUE;
7690 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7691 if (level->game_version < VERSION_IDENT(4,1,1,1))
7692 level->sb_objects_needed = FALSE;
7694 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7695 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7696 level->finish_dig_collect = FALSE;
7698 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7699 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7700 level->keep_walkable_ce = TRUE;
7703 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7705 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7708 // check if this level is (not) a Sokoban level
7709 for (y = 0; y < level->fieldy; y++)
7710 for (x = 0; x < level->fieldx; x++)
7711 if (!IS_SB_ELEMENT(Tile[x][y]))
7712 is_sokoban_level = FALSE;
7714 if (is_sokoban_level)
7716 // set special level settings for Sokoban levels
7717 SetLevelSettings_SB(level);
7721 static void LoadLevel_InitSettings(struct LevelInfo *level)
7723 // adjust level settings for (non-native) Sokoban-style levels
7724 LoadLevel_InitSettings_SB(level);
7726 // rename levels with title "nameless level" or if renaming is forced
7727 if (leveldir_current->empty_level_name != NULL &&
7728 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7729 leveldir_current->force_level_name))
7730 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7731 leveldir_current->empty_level_name, level_nr);
7734 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7738 // map elements that have changed in newer versions
7739 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7740 level->game_version);
7741 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7742 for (x = 0; x < 3; x++)
7743 for (y = 0; y < 3; y++)
7744 level->yamyam_content[i].e[x][y] =
7745 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7746 level->game_version);
7750 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7754 // map custom element change events that have changed in newer versions
7755 // (these following values were accidentally changed in version 3.0.1)
7756 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7757 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7759 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7761 int element = EL_CUSTOM_START + i;
7763 // order of checking and copying events to be mapped is important
7764 // (do not change the start and end value -- they are constant)
7765 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7767 if (HAS_CHANGE_EVENT(element, j - 2))
7769 SET_CHANGE_EVENT(element, j - 2, FALSE);
7770 SET_CHANGE_EVENT(element, j, TRUE);
7774 // order of checking and copying events to be mapped is important
7775 // (do not change the start and end value -- they are constant)
7776 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7778 if (HAS_CHANGE_EVENT(element, j - 1))
7780 SET_CHANGE_EVENT(element, j - 1, FALSE);
7781 SET_CHANGE_EVENT(element, j, TRUE);
7787 // initialize "can_change" field for old levels with only one change page
7788 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7792 int element = EL_CUSTOM_START + i;
7794 if (CAN_CHANGE(element))
7795 element_info[element].change->can_change = TRUE;
7799 // correct custom element values (for old levels without these options)
7800 if (level->game_version < VERSION_IDENT(3,1,1,0))
7802 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7804 int element = EL_CUSTOM_START + i;
7805 struct ElementInfo *ei = &element_info[element];
7807 if (ei->access_direction == MV_NO_DIRECTION)
7808 ei->access_direction = MV_ALL_DIRECTIONS;
7812 // correct custom element values (fix invalid values for all versions)
7815 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7817 int element = EL_CUSTOM_START + i;
7818 struct ElementInfo *ei = &element_info[element];
7820 for (j = 0; j < ei->num_change_pages; j++)
7822 struct ElementChangeInfo *change = &ei->change_page[j];
7824 if (change->trigger_player == CH_PLAYER_NONE)
7825 change->trigger_player = CH_PLAYER_ANY;
7827 if (change->trigger_side == CH_SIDE_NONE)
7828 change->trigger_side = CH_SIDE_ANY;
7833 // initialize "can_explode" field for old levels which did not store this
7834 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7835 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7837 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7839 int element = EL_CUSTOM_START + i;
7841 if (EXPLODES_1X1_OLD(element))
7842 element_info[element].explosion_type = EXPLODES_1X1;
7844 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7845 EXPLODES_SMASHED(element) ||
7846 EXPLODES_IMPACT(element)));
7850 // correct previously hard-coded move delay values for maze runner style
7851 if (level->game_version < VERSION_IDENT(3,1,1,0))
7853 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7855 int element = EL_CUSTOM_START + i;
7857 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7859 // previously hard-coded and therefore ignored
7860 element_info[element].move_delay_fixed = 9;
7861 element_info[element].move_delay_random = 0;
7866 // set some other uninitialized values of custom elements in older levels
7867 if (level->game_version < VERSION_IDENT(3,1,0,0))
7869 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7871 int element = EL_CUSTOM_START + i;
7873 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7875 element_info[element].explosion_delay = 17;
7876 element_info[element].ignition_delay = 8;
7880 // set mouse click change events to work for left/middle/right mouse button
7881 if (level->game_version < VERSION_IDENT(4,2,3,0))
7883 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7885 int element = EL_CUSTOM_START + i;
7886 struct ElementInfo *ei = &element_info[element];
7888 for (j = 0; j < ei->num_change_pages; j++)
7890 struct ElementChangeInfo *change = &ei->change_page[j];
7892 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7893 change->has_event[CE_PRESSED_BY_MOUSE] ||
7894 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7895 change->has_event[CE_MOUSE_PRESSED_ON_X])
7896 change->trigger_side = CH_SIDE_ANY;
7902 static void LoadLevel_InitElements(struct LevelInfo *level)
7904 LoadLevel_InitStandardElements(level);
7906 if (level->file_has_custom_elements)
7907 LoadLevel_InitCustomElements(level);
7909 // initialize element properties for level editor etc.
7910 InitElementPropertiesEngine(level->game_version);
7911 InitElementPropertiesGfxElement();
7914 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7918 // map elements that have changed in newer versions
7919 for (y = 0; y < level->fieldy; y++)
7920 for (x = 0; x < level->fieldx; x++)
7921 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7922 level->game_version);
7924 // clear unused playfield data (nicer if level gets resized in editor)
7925 for (x = 0; x < MAX_LEV_FIELDX; x++)
7926 for (y = 0; y < MAX_LEV_FIELDY; y++)
7927 if (x >= level->fieldx || y >= level->fieldy)
7928 level->field[x][y] = EL_EMPTY;
7930 // copy elements to runtime playfield array
7931 for (x = 0; x < MAX_LEV_FIELDX; x++)
7932 for (y = 0; y < MAX_LEV_FIELDY; y++)
7933 Tile[x][y] = level->field[x][y];
7935 // initialize level size variables for faster access
7936 lev_fieldx = level->fieldx;
7937 lev_fieldy = level->fieldy;
7939 // determine border element for this level
7940 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7941 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7946 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7948 struct LevelFileInfo *level_file_info = &level->file_info;
7950 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7951 CopyNativeLevel_RND_to_Native(level);
7954 static void LoadLevelTemplate_LoadAndInit(void)
7956 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7958 LoadLevel_InitVersion(&level_template);
7959 LoadLevel_InitElements(&level_template);
7960 LoadLevel_InitSettings(&level_template);
7962 ActivateLevelTemplate();
7965 void LoadLevelTemplate(int nr)
7967 if (!fileExists(getGlobalLevelTemplateFilename()))
7969 Warn("no level template found for this level");
7974 setLevelFileInfo(&level_template.file_info, nr);
7976 LoadLevelTemplate_LoadAndInit();
7979 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7981 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7983 LoadLevelTemplate_LoadAndInit();
7986 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7988 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7990 if (level.use_custom_template)
7992 if (network_level != NULL)
7993 LoadNetworkLevelTemplate(network_level);
7995 LoadLevelTemplate(-1);
7998 LoadLevel_InitVersion(&level);
7999 LoadLevel_InitElements(&level);
8000 LoadLevel_InitPlayfield(&level);
8001 LoadLevel_InitSettings(&level);
8003 LoadLevel_InitNativeEngines(&level);
8006 void LoadLevel(int nr)
8008 SetLevelSetInfo(leveldir_current->identifier, nr);
8010 setLevelFileInfo(&level.file_info, nr);
8012 LoadLevel_LoadAndInit(NULL);
8015 void LoadLevelInfoOnly(int nr)
8017 setLevelFileInfo(&level.file_info, nr);
8019 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8022 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8024 SetLevelSetInfo(network_level->leveldir_identifier,
8025 network_level->file_info.nr);
8027 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8029 LoadLevel_LoadAndInit(network_level);
8032 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8036 chunk_size += putFileVersion(file, level->file_version);
8037 chunk_size += putFileVersion(file, level->game_version);
8042 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8046 chunk_size += putFile16BitBE(file, level->creation_date.year);
8047 chunk_size += putFile8Bit(file, level->creation_date.month);
8048 chunk_size += putFile8Bit(file, level->creation_date.day);
8053 #if ENABLE_HISTORIC_CHUNKS
8054 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8058 putFile8Bit(file, level->fieldx);
8059 putFile8Bit(file, level->fieldy);
8061 putFile16BitBE(file, level->time);
8062 putFile16BitBE(file, level->gems_needed);
8064 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8065 putFile8Bit(file, level->name[i]);
8067 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8068 putFile8Bit(file, level->score[i]);
8070 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8071 for (y = 0; y < 3; y++)
8072 for (x = 0; x < 3; x++)
8073 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8074 level->yamyam_content[i].e[x][y]));
8075 putFile8Bit(file, level->amoeba_speed);
8076 putFile8Bit(file, level->time_magic_wall);
8077 putFile8Bit(file, level->time_wheel);
8078 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8079 level->amoeba_content));
8080 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8081 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8082 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8083 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8085 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8087 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8088 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8089 putFile32BitBE(file, level->can_move_into_acid_bits);
8090 putFile8Bit(file, level->dont_collide_with_bits);
8092 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8093 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8095 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8096 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8097 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8099 putFile8Bit(file, level->game_engine_type);
8101 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8105 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8110 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8111 chunk_size += putFile8Bit(file, level->name[i]);
8116 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8121 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8122 chunk_size += putFile8Bit(file, level->author[i]);
8127 #if ENABLE_HISTORIC_CHUNKS
8128 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8133 for (y = 0; y < level->fieldy; y++)
8134 for (x = 0; x < level->fieldx; x++)
8135 if (level->encoding_16bit_field)
8136 chunk_size += putFile16BitBE(file, level->field[x][y]);
8138 chunk_size += putFile8Bit(file, level->field[x][y]);
8144 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8149 for (y = 0; y < level->fieldy; y++)
8150 for (x = 0; x < level->fieldx; x++)
8151 chunk_size += putFile16BitBE(file, level->field[x][y]);
8156 #if ENABLE_HISTORIC_CHUNKS
8157 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8161 putFile8Bit(file, EL_YAMYAM);
8162 putFile8Bit(file, level->num_yamyam_contents);
8163 putFile8Bit(file, 0);
8164 putFile8Bit(file, 0);
8166 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8167 for (y = 0; y < 3; y++)
8168 for (x = 0; x < 3; x++)
8169 if (level->encoding_16bit_field)
8170 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8172 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8176 #if ENABLE_HISTORIC_CHUNKS
8177 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8180 int num_contents, content_xsize, content_ysize;
8181 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8183 if (element == EL_YAMYAM)
8185 num_contents = level->num_yamyam_contents;
8189 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8190 for (y = 0; y < 3; y++)
8191 for (x = 0; x < 3; x++)
8192 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8194 else if (element == EL_BD_AMOEBA)
8200 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8201 for (y = 0; y < 3; y++)
8202 for (x = 0; x < 3; x++)
8203 content_array[i][x][y] = EL_EMPTY;
8204 content_array[0][0][0] = level->amoeba_content;
8208 // chunk header already written -- write empty chunk data
8209 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8211 Warn("cannot save content for element '%d'", element);
8216 putFile16BitBE(file, element);
8217 putFile8Bit(file, num_contents);
8218 putFile8Bit(file, content_xsize);
8219 putFile8Bit(file, content_ysize);
8221 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8223 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8224 for (y = 0; y < 3; y++)
8225 for (x = 0; x < 3; x++)
8226 putFile16BitBE(file, content_array[i][x][y]);
8230 #if ENABLE_HISTORIC_CHUNKS
8231 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8233 int envelope_nr = element - EL_ENVELOPE_1;
8234 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8238 chunk_size += putFile16BitBE(file, element);
8239 chunk_size += putFile16BitBE(file, envelope_len);
8240 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8241 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8243 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8244 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8246 for (i = 0; i < envelope_len; i++)
8247 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8253 #if ENABLE_HISTORIC_CHUNKS
8254 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8255 int num_changed_custom_elements)
8259 putFile16BitBE(file, num_changed_custom_elements);
8261 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8263 int element = EL_CUSTOM_START + i;
8265 struct ElementInfo *ei = &element_info[element];
8267 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8269 if (check < num_changed_custom_elements)
8271 putFile16BitBE(file, element);
8272 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8279 if (check != num_changed_custom_elements) // should not happen
8280 Warn("inconsistent number of custom element properties");
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8286 int num_changed_custom_elements)
8290 putFile16BitBE(file, num_changed_custom_elements);
8292 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8294 int element = EL_CUSTOM_START + i;
8296 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8298 if (check < num_changed_custom_elements)
8300 putFile16BitBE(file, element);
8301 putFile16BitBE(file, element_info[element].change->target_element);
8308 if (check != num_changed_custom_elements) // should not happen
8309 Warn("inconsistent number of custom target elements");
8313 #if ENABLE_HISTORIC_CHUNKS
8314 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8315 int num_changed_custom_elements)
8317 int i, j, x, y, check = 0;
8319 putFile16BitBE(file, num_changed_custom_elements);
8321 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8323 int element = EL_CUSTOM_START + i;
8324 struct ElementInfo *ei = &element_info[element];
8326 if (ei->modified_settings)
8328 if (check < num_changed_custom_elements)
8330 putFile16BitBE(file, element);
8332 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8333 putFile8Bit(file, ei->description[j]);
8335 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8337 // some free bytes for future properties and padding
8338 WriteUnusedBytesToFile(file, 7);
8340 putFile8Bit(file, ei->use_gfx_element);
8341 putFile16BitBE(file, ei->gfx_element_initial);
8343 putFile8Bit(file, ei->collect_score_initial);
8344 putFile8Bit(file, ei->collect_count_initial);
8346 putFile16BitBE(file, ei->push_delay_fixed);
8347 putFile16BitBE(file, ei->push_delay_random);
8348 putFile16BitBE(file, ei->move_delay_fixed);
8349 putFile16BitBE(file, ei->move_delay_random);
8351 putFile16BitBE(file, ei->move_pattern);
8352 putFile8Bit(file, ei->move_direction_initial);
8353 putFile8Bit(file, ei->move_stepsize);
8355 for (y = 0; y < 3; y++)
8356 for (x = 0; x < 3; x++)
8357 putFile16BitBE(file, ei->content.e[x][y]);
8359 putFile32BitBE(file, ei->change->events);
8361 putFile16BitBE(file, ei->change->target_element);
8363 putFile16BitBE(file, ei->change->delay_fixed);
8364 putFile16BitBE(file, ei->change->delay_random);
8365 putFile16BitBE(file, ei->change->delay_frames);
8367 putFile16BitBE(file, ei->change->initial_trigger_element);
8369 putFile8Bit(file, ei->change->explode);
8370 putFile8Bit(file, ei->change->use_target_content);
8371 putFile8Bit(file, ei->change->only_if_complete);
8372 putFile8Bit(file, ei->change->use_random_replace);
8374 putFile8Bit(file, ei->change->random_percentage);
8375 putFile8Bit(file, ei->change->replace_when);
8377 for (y = 0; y < 3; y++)
8378 for (x = 0; x < 3; x++)
8379 putFile16BitBE(file, ei->change->content.e[x][y]);
8381 putFile8Bit(file, ei->slippery_type);
8383 // some free bytes for future properties and padding
8384 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8391 if (check != num_changed_custom_elements) // should not happen
8392 Warn("inconsistent number of custom element properties");
8396 #if ENABLE_HISTORIC_CHUNKS
8397 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8399 struct ElementInfo *ei = &element_info[element];
8402 // ---------- custom element base property values (96 bytes) ----------------
8404 putFile16BitBE(file, element);
8406 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8407 putFile8Bit(file, ei->description[i]);
8409 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8411 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8413 putFile8Bit(file, ei->num_change_pages);
8415 putFile16BitBE(file, ei->ce_value_fixed_initial);
8416 putFile16BitBE(file, ei->ce_value_random_initial);
8417 putFile8Bit(file, ei->use_last_ce_value);
8419 putFile8Bit(file, ei->use_gfx_element);
8420 putFile16BitBE(file, ei->gfx_element_initial);
8422 putFile8Bit(file, ei->collect_score_initial);
8423 putFile8Bit(file, ei->collect_count_initial);
8425 putFile8Bit(file, ei->drop_delay_fixed);
8426 putFile8Bit(file, ei->push_delay_fixed);
8427 putFile8Bit(file, ei->drop_delay_random);
8428 putFile8Bit(file, ei->push_delay_random);
8429 putFile16BitBE(file, ei->move_delay_fixed);
8430 putFile16BitBE(file, ei->move_delay_random);
8432 // bits 0 - 15 of "move_pattern" ...
8433 putFile16BitBE(file, ei->move_pattern & 0xffff);
8434 putFile8Bit(file, ei->move_direction_initial);
8435 putFile8Bit(file, ei->move_stepsize);
8437 putFile8Bit(file, ei->slippery_type);
8439 for (y = 0; y < 3; y++)
8440 for (x = 0; x < 3; x++)
8441 putFile16BitBE(file, ei->content.e[x][y]);
8443 putFile16BitBE(file, ei->move_enter_element);
8444 putFile16BitBE(file, ei->move_leave_element);
8445 putFile8Bit(file, ei->move_leave_type);
8447 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8448 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8450 putFile8Bit(file, ei->access_direction);
8452 putFile8Bit(file, ei->explosion_delay);
8453 putFile8Bit(file, ei->ignition_delay);
8454 putFile8Bit(file, ei->explosion_type);
8456 // some free bytes for future custom property values and padding
8457 WriteUnusedBytesToFile(file, 1);
8459 // ---------- change page property values (48 bytes) ------------------------
8461 for (i = 0; i < ei->num_change_pages; i++)
8463 struct ElementChangeInfo *change = &ei->change_page[i];
8464 unsigned int event_bits;
8466 // bits 0 - 31 of "has_event[]" ...
8468 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8469 if (change->has_event[j])
8470 event_bits |= (1u << j);
8471 putFile32BitBE(file, event_bits);
8473 putFile16BitBE(file, change->target_element);
8475 putFile16BitBE(file, change->delay_fixed);
8476 putFile16BitBE(file, change->delay_random);
8477 putFile16BitBE(file, change->delay_frames);
8479 putFile16BitBE(file, change->initial_trigger_element);
8481 putFile8Bit(file, change->explode);
8482 putFile8Bit(file, change->use_target_content);
8483 putFile8Bit(file, change->only_if_complete);
8484 putFile8Bit(file, change->use_random_replace);
8486 putFile8Bit(file, change->random_percentage);
8487 putFile8Bit(file, change->replace_when);
8489 for (y = 0; y < 3; y++)
8490 for (x = 0; x < 3; x++)
8491 putFile16BitBE(file, change->target_content.e[x][y]);
8493 putFile8Bit(file, change->can_change);
8495 putFile8Bit(file, change->trigger_side);
8497 putFile8Bit(file, change->trigger_player);
8498 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8499 log_2(change->trigger_page)));
8501 putFile8Bit(file, change->has_action);
8502 putFile8Bit(file, change->action_type);
8503 putFile8Bit(file, change->action_mode);
8504 putFile16BitBE(file, change->action_arg);
8506 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8508 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8509 if (change->has_event[j])
8510 event_bits |= (1u << (j - 32));
8511 putFile8Bit(file, event_bits);
8516 #if ENABLE_HISTORIC_CHUNKS
8517 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8519 struct ElementInfo *ei = &element_info[element];
8520 struct ElementGroupInfo *group = ei->group;
8523 putFile16BitBE(file, element);
8525 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8526 putFile8Bit(file, ei->description[i]);
8528 putFile8Bit(file, group->num_elements);
8530 putFile8Bit(file, ei->use_gfx_element);
8531 putFile16BitBE(file, ei->gfx_element_initial);
8533 putFile8Bit(file, group->choice_mode);
8535 // some free bytes for future values and padding
8536 WriteUnusedBytesToFile(file, 3);
8538 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8539 putFile16BitBE(file, group->element[i]);
8543 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8544 boolean write_element)
8546 int save_type = entry->save_type;
8547 int data_type = entry->data_type;
8548 int conf_type = entry->conf_type;
8549 int byte_mask = conf_type & CONF_MASK_BYTES;
8550 int element = entry->element;
8551 int default_value = entry->default_value;
8553 boolean modified = FALSE;
8555 if (byte_mask != CONF_MASK_MULTI_BYTES)
8557 void *value_ptr = entry->value;
8558 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8561 // check if any settings have been modified before saving them
8562 if (value != default_value)
8565 // do not save if explicitly told or if unmodified default settings
8566 if ((save_type == SAVE_CONF_NEVER) ||
8567 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8571 num_bytes += putFile16BitBE(file, element);
8573 num_bytes += putFile8Bit(file, conf_type);
8574 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8575 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8576 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8579 else if (data_type == TYPE_STRING)
8581 char *default_string = entry->default_string;
8582 char *string = (char *)(entry->value);
8583 int string_length = strlen(string);
8586 // check if any settings have been modified before saving them
8587 if (!strEqual(string, default_string))
8590 // do not save if explicitly told or if unmodified default settings
8591 if ((save_type == SAVE_CONF_NEVER) ||
8592 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8596 num_bytes += putFile16BitBE(file, element);
8598 num_bytes += putFile8Bit(file, conf_type);
8599 num_bytes += putFile16BitBE(file, string_length);
8601 for (i = 0; i < string_length; i++)
8602 num_bytes += putFile8Bit(file, string[i]);
8604 else if (data_type == TYPE_ELEMENT_LIST)
8606 int *element_array = (int *)(entry->value);
8607 int num_elements = *(int *)(entry->num_entities);
8610 // check if any settings have been modified before saving them
8611 for (i = 0; i < num_elements; i++)
8612 if (element_array[i] != default_value)
8615 // do not save if explicitly told or if unmodified default settings
8616 if ((save_type == SAVE_CONF_NEVER) ||
8617 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8621 num_bytes += putFile16BitBE(file, element);
8623 num_bytes += putFile8Bit(file, conf_type);
8624 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8626 for (i = 0; i < num_elements; i++)
8627 num_bytes += putFile16BitBE(file, element_array[i]);
8629 else if (data_type == TYPE_CONTENT_LIST)
8631 struct Content *content = (struct Content *)(entry->value);
8632 int num_contents = *(int *)(entry->num_entities);
8635 // check if any settings have been modified before saving them
8636 for (i = 0; i < num_contents; i++)
8637 for (y = 0; y < 3; y++)
8638 for (x = 0; x < 3; x++)
8639 if (content[i].e[x][y] != default_value)
8642 // do not save if explicitly told or if unmodified default settings
8643 if ((save_type == SAVE_CONF_NEVER) ||
8644 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8648 num_bytes += putFile16BitBE(file, element);
8650 num_bytes += putFile8Bit(file, conf_type);
8651 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8653 for (i = 0; i < num_contents; i++)
8654 for (y = 0; y < 3; y++)
8655 for (x = 0; x < 3; x++)
8656 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8662 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8667 li = *level; // copy level data into temporary buffer
8669 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8670 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8675 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8680 li = *level; // copy level data into temporary buffer
8682 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8683 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8688 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8690 int envelope_nr = element - EL_ENVELOPE_1;
8694 chunk_size += putFile16BitBE(file, element);
8696 // copy envelope data into temporary buffer
8697 xx_envelope = level->envelope[envelope_nr];
8699 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8700 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8705 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8707 struct ElementInfo *ei = &element_info[element];
8711 chunk_size += putFile16BitBE(file, element);
8713 xx_ei = *ei; // copy element data into temporary buffer
8715 // set default description string for this specific element
8716 strcpy(xx_default_description, getDefaultElementDescription(ei));
8718 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8719 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8721 for (i = 0; i < ei->num_change_pages; i++)
8723 struct ElementChangeInfo *change = &ei->change_page[i];
8725 xx_current_change_page = i;
8727 xx_change = *change; // copy change data into temporary buffer
8730 setEventBitsFromEventFlags(change);
8732 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8733 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8740 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8742 struct ElementInfo *ei = &element_info[element];
8743 struct ElementGroupInfo *group = ei->group;
8747 chunk_size += putFile16BitBE(file, element);
8749 xx_ei = *ei; // copy element data into temporary buffer
8750 xx_group = *group; // copy group data into temporary buffer
8752 // set default description string for this specific element
8753 strcpy(xx_default_description, getDefaultElementDescription(ei));
8755 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8756 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8761 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8763 struct ElementInfo *ei = &element_info[element];
8767 chunk_size += putFile16BitBE(file, element);
8769 xx_ei = *ei; // copy element data into temporary buffer
8771 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8772 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8777 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8778 boolean save_as_template)
8784 if (!(file = fopen(filename, MODE_WRITE)))
8786 Warn("cannot save level file '%s'", filename);
8791 level->file_version = FILE_VERSION_ACTUAL;
8792 level->game_version = GAME_VERSION_ACTUAL;
8794 level->creation_date = getCurrentDate();
8796 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8797 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8799 chunk_size = SaveLevel_VERS(NULL, level);
8800 putFileChunkBE(file, "VERS", chunk_size);
8801 SaveLevel_VERS(file, level);
8803 chunk_size = SaveLevel_DATE(NULL, level);
8804 putFileChunkBE(file, "DATE", chunk_size);
8805 SaveLevel_DATE(file, level);
8807 chunk_size = SaveLevel_NAME(NULL, level);
8808 putFileChunkBE(file, "NAME", chunk_size);
8809 SaveLevel_NAME(file, level);
8811 chunk_size = SaveLevel_AUTH(NULL, level);
8812 putFileChunkBE(file, "AUTH", chunk_size);
8813 SaveLevel_AUTH(file, level);
8815 chunk_size = SaveLevel_INFO(NULL, level);
8816 putFileChunkBE(file, "INFO", chunk_size);
8817 SaveLevel_INFO(file, level);
8819 chunk_size = SaveLevel_BODY(NULL, level);
8820 putFileChunkBE(file, "BODY", chunk_size);
8821 SaveLevel_BODY(file, level);
8823 chunk_size = SaveLevel_ELEM(NULL, level);
8824 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8826 putFileChunkBE(file, "ELEM", chunk_size);
8827 SaveLevel_ELEM(file, level);
8830 for (i = 0; i < NUM_ENVELOPES; i++)
8832 int element = EL_ENVELOPE_1 + i;
8834 chunk_size = SaveLevel_NOTE(NULL, level, element);
8835 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8837 putFileChunkBE(file, "NOTE", chunk_size);
8838 SaveLevel_NOTE(file, level, element);
8842 // if not using template level, check for non-default custom/group elements
8843 if (!level->use_custom_template || save_as_template)
8845 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8847 int element = EL_CUSTOM_START + i;
8849 chunk_size = SaveLevel_CUSX(NULL, level, element);
8850 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8852 putFileChunkBE(file, "CUSX", chunk_size);
8853 SaveLevel_CUSX(file, level, element);
8857 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8859 int element = EL_GROUP_START + i;
8861 chunk_size = SaveLevel_GRPX(NULL, level, element);
8862 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8864 putFileChunkBE(file, "GRPX", chunk_size);
8865 SaveLevel_GRPX(file, level, element);
8869 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8871 int element = GET_EMPTY_ELEMENT(i);
8873 chunk_size = SaveLevel_EMPX(NULL, level, element);
8874 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8876 putFileChunkBE(file, "EMPX", chunk_size);
8877 SaveLevel_EMPX(file, level, element);
8884 SetFilePermissions(filename, PERMS_PRIVATE);
8887 void SaveLevel(int nr)
8889 char *filename = getDefaultLevelFilename(nr);
8891 SaveLevelFromFilename(&level, filename, FALSE);
8894 void SaveLevelTemplate(void)
8896 char *filename = getLocalLevelTemplateFilename();
8898 SaveLevelFromFilename(&level, filename, TRUE);
8901 boolean SaveLevelChecked(int nr)
8903 char *filename = getDefaultLevelFilename(nr);
8904 boolean new_level = !fileExists(filename);
8905 boolean level_saved = FALSE;
8907 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8912 Request("Level saved!", REQ_CONFIRM);
8920 void DumpLevel(struct LevelInfo *level)
8922 if (level->no_level_file || level->no_valid_file)
8924 Warn("cannot dump -- no valid level file found");
8930 Print("Level xxx (file version %08d, game version %08d)\n",
8931 level->file_version, level->game_version);
8934 Print("Level author: '%s'\n", level->author);
8935 Print("Level title: '%s'\n", level->name);
8937 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8939 Print("Level time: %d seconds\n", level->time);
8940 Print("Gems needed: %d\n", level->gems_needed);
8942 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8943 Print("Time for wheel: %d seconds\n", level->time_wheel);
8944 Print("Time for light: %d seconds\n", level->time_light);
8945 Print("Time for timegate: %d seconds\n", level->time_timegate);
8947 Print("Amoeba speed: %d\n", level->amoeba_speed);
8950 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8951 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8952 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8953 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8954 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8955 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8961 for (i = 0; i < NUM_ENVELOPES; i++)
8963 char *text = level->envelope[i].text;
8964 int text_len = strlen(text);
8965 boolean has_text = FALSE;
8967 for (j = 0; j < text_len; j++)
8968 if (text[j] != ' ' && text[j] != '\n')
8974 Print("Envelope %d:\n'%s'\n", i + 1, text);
8982 void DumpLevels(void)
8984 static LevelDirTree *dumplevel_leveldir = NULL;
8986 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8987 global.dumplevel_leveldir);
8989 if (dumplevel_leveldir == NULL)
8990 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8992 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8993 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8994 Fail("no such level number: %d", global.dumplevel_level_nr);
8996 leveldir_current = dumplevel_leveldir;
8998 LoadLevel(global.dumplevel_level_nr);
9005 // ============================================================================
9006 // tape file functions
9007 // ============================================================================
9009 static void setTapeInfoToDefaults(void)
9013 // always start with reliable default values (empty tape)
9016 // default values (also for pre-1.2 tapes) with only the first player
9017 tape.player_participates[0] = TRUE;
9018 for (i = 1; i < MAX_PLAYERS; i++)
9019 tape.player_participates[i] = FALSE;
9021 // at least one (default: the first) player participates in every tape
9022 tape.num_participating_players = 1;
9024 tape.property_bits = TAPE_PROPERTY_NONE;
9026 tape.level_nr = level_nr;
9028 tape.changed = FALSE;
9029 tape.solved = FALSE;
9031 tape.recording = FALSE;
9032 tape.playing = FALSE;
9033 tape.pausing = FALSE;
9035 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9036 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9038 tape.no_info_chunk = TRUE;
9039 tape.no_valid_file = FALSE;
9042 static int getTapePosSize(struct TapeInfo *tape)
9044 int tape_pos_size = 0;
9046 if (tape->use_key_actions)
9047 tape_pos_size += tape->num_participating_players;
9049 if (tape->use_mouse_actions)
9050 tape_pos_size += 3; // x and y position and mouse button mask
9052 tape_pos_size += 1; // tape action delay value
9054 return tape_pos_size;
9057 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9059 tape->use_key_actions = FALSE;
9060 tape->use_mouse_actions = FALSE;
9062 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9063 tape->use_key_actions = TRUE;
9065 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9066 tape->use_mouse_actions = TRUE;
9069 static int getTapeActionValue(struct TapeInfo *tape)
9071 return (tape->use_key_actions &&
9072 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9073 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9074 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9075 TAPE_ACTIONS_DEFAULT);
9078 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9080 tape->file_version = getFileVersion(file);
9081 tape->game_version = getFileVersion(file);
9086 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9090 tape->random_seed = getFile32BitBE(file);
9091 tape->date = getFile32BitBE(file);
9092 tape->length = getFile32BitBE(file);
9094 // read header fields that are new since version 1.2
9095 if (tape->file_version >= FILE_VERSION_1_2)
9097 byte store_participating_players = getFile8Bit(file);
9100 // since version 1.2, tapes store which players participate in the tape
9101 tape->num_participating_players = 0;
9102 for (i = 0; i < MAX_PLAYERS; i++)
9104 tape->player_participates[i] = FALSE;
9106 if (store_participating_players & (1 << i))
9108 tape->player_participates[i] = TRUE;
9109 tape->num_participating_players++;
9113 setTapeActionFlags(tape, getFile8Bit(file));
9115 tape->property_bits = getFile8Bit(file);
9116 tape->solved = getFile8Bit(file);
9118 engine_version = getFileVersion(file);
9119 if (engine_version > 0)
9120 tape->engine_version = engine_version;
9122 tape->engine_version = tape->game_version;
9128 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9130 tape->scr_fieldx = getFile8Bit(file);
9131 tape->scr_fieldy = getFile8Bit(file);
9136 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9138 char *level_identifier = NULL;
9139 int level_identifier_size;
9142 tape->no_info_chunk = FALSE;
9144 level_identifier_size = getFile16BitBE(file);
9146 level_identifier = checked_malloc(level_identifier_size);
9148 for (i = 0; i < level_identifier_size; i++)
9149 level_identifier[i] = getFile8Bit(file);
9151 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9152 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9154 checked_free(level_identifier);
9156 tape->level_nr = getFile16BitBE(file);
9158 chunk_size = 2 + level_identifier_size + 2;
9163 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9166 int tape_pos_size = getTapePosSize(tape);
9167 int chunk_size_expected = tape_pos_size * tape->length;
9169 if (chunk_size_expected != chunk_size)
9171 ReadUnusedBytesFromFile(file, chunk_size);
9172 return chunk_size_expected;
9175 for (i = 0; i < tape->length; i++)
9177 if (i >= MAX_TAPE_LEN)
9179 Warn("tape truncated -- size exceeds maximum tape size %d",
9182 // tape too large; read and ignore remaining tape data from this chunk
9183 for (;i < tape->length; i++)
9184 ReadUnusedBytesFromFile(file, tape_pos_size);
9189 if (tape->use_key_actions)
9191 for (j = 0; j < MAX_PLAYERS; j++)
9193 tape->pos[i].action[j] = MV_NONE;
9195 if (tape->player_participates[j])
9196 tape->pos[i].action[j] = getFile8Bit(file);
9200 if (tape->use_mouse_actions)
9202 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9203 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9204 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9207 tape->pos[i].delay = getFile8Bit(file);
9209 if (tape->file_version == FILE_VERSION_1_0)
9211 // eliminate possible diagonal moves in old tapes
9212 // this is only for backward compatibility
9214 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9215 byte action = tape->pos[i].action[0];
9216 int k, num_moves = 0;
9218 for (k = 0; k < 4; k++)
9220 if (action & joy_dir[k])
9222 tape->pos[i + num_moves].action[0] = joy_dir[k];
9224 tape->pos[i + num_moves].delay = 0;
9233 tape->length += num_moves;
9236 else if (tape->file_version < FILE_VERSION_2_0)
9238 // convert pre-2.0 tapes to new tape format
9240 if (tape->pos[i].delay > 1)
9243 tape->pos[i + 1] = tape->pos[i];
9244 tape->pos[i + 1].delay = 1;
9247 for (j = 0; j < MAX_PLAYERS; j++)
9248 tape->pos[i].action[j] = MV_NONE;
9249 tape->pos[i].delay--;
9256 if (checkEndOfFile(file))
9260 if (i != tape->length)
9261 chunk_size = tape_pos_size * i;
9266 static void LoadTape_SokobanSolution(char *filename)
9269 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9271 if (!(file = openFile(filename, MODE_READ)))
9273 tape.no_valid_file = TRUE;
9278 while (!checkEndOfFile(file))
9280 unsigned char c = getByteFromFile(file);
9282 if (checkEndOfFile(file))
9289 tape.pos[tape.length].action[0] = MV_UP;
9290 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9296 tape.pos[tape.length].action[0] = MV_DOWN;
9297 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9303 tape.pos[tape.length].action[0] = MV_LEFT;
9304 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9310 tape.pos[tape.length].action[0] = MV_RIGHT;
9311 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9319 // ignore white-space characters
9323 tape.no_valid_file = TRUE;
9325 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9333 if (tape.no_valid_file)
9336 tape.length_frames = GetTapeLengthFrames();
9337 tape.length_seconds = GetTapeLengthSeconds();
9340 void LoadTapeFromFilename(char *filename)
9342 char cookie[MAX_LINE_LEN];
9343 char chunk_name[CHUNK_ID_LEN + 1];
9347 // always start with reliable default values
9348 setTapeInfoToDefaults();
9350 if (strSuffix(filename, ".sln"))
9352 LoadTape_SokobanSolution(filename);
9357 if (!(file = openFile(filename, MODE_READ)))
9359 tape.no_valid_file = TRUE;
9364 getFileChunkBE(file, chunk_name, NULL);
9365 if (strEqual(chunk_name, "RND1"))
9367 getFile32BitBE(file); // not used
9369 getFileChunkBE(file, chunk_name, NULL);
9370 if (!strEqual(chunk_name, "TAPE"))
9372 tape.no_valid_file = TRUE;
9374 Warn("unknown format of tape file '%s'", filename);
9381 else // check for pre-2.0 file format with cookie string
9383 strcpy(cookie, chunk_name);
9384 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9386 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9387 cookie[strlen(cookie) - 1] = '\0';
9389 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9391 tape.no_valid_file = TRUE;
9393 Warn("unknown format of tape file '%s'", filename);
9400 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9402 tape.no_valid_file = TRUE;
9404 Warn("unsupported version of tape file '%s'", filename);
9411 // pre-2.0 tape files have no game version, so use file version here
9412 tape.game_version = tape.file_version;
9415 if (tape.file_version < FILE_VERSION_1_2)
9417 // tape files from versions before 1.2.0 without chunk structure
9418 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9419 LoadTape_BODY(file, 2 * tape.length, &tape);
9427 int (*loader)(File *, int, struct TapeInfo *);
9431 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9432 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9433 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9434 { "INFO", -1, LoadTape_INFO },
9435 { "BODY", -1, LoadTape_BODY },
9439 while (getFileChunkBE(file, chunk_name, &chunk_size))
9443 while (chunk_info[i].name != NULL &&
9444 !strEqual(chunk_name, chunk_info[i].name))
9447 if (chunk_info[i].name == NULL)
9449 Warn("unknown chunk '%s' in tape file '%s'",
9450 chunk_name, filename);
9452 ReadUnusedBytesFromFile(file, chunk_size);
9454 else if (chunk_info[i].size != -1 &&
9455 chunk_info[i].size != chunk_size)
9457 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9458 chunk_size, chunk_name, filename);
9460 ReadUnusedBytesFromFile(file, chunk_size);
9464 // call function to load this tape chunk
9465 int chunk_size_expected =
9466 (chunk_info[i].loader)(file, chunk_size, &tape);
9468 // the size of some chunks cannot be checked before reading other
9469 // chunks first (like "HEAD" and "BODY") that contain some header
9470 // information, so check them here
9471 if (chunk_size_expected != chunk_size)
9473 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9474 chunk_size, chunk_name, filename);
9482 tape.length_frames = GetTapeLengthFrames();
9483 tape.length_seconds = GetTapeLengthSeconds();
9486 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9488 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9490 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9491 tape.engine_version);
9495 void LoadTape(int nr)
9497 char *filename = getTapeFilename(nr);
9499 LoadTapeFromFilename(filename);
9502 void LoadSolutionTape(int nr)
9504 char *filename = getSolutionTapeFilename(nr);
9506 LoadTapeFromFilename(filename);
9508 if (TAPE_IS_EMPTY(tape))
9510 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9511 level.native_bd_level->replay != NULL)
9512 CopyNativeTape_BD_to_RND(&level);
9513 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9514 level.native_sp_level->demo.is_available)
9515 CopyNativeTape_SP_to_RND(&level);
9519 void LoadScoreTape(char *score_tape_basename, int nr)
9521 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9523 LoadTapeFromFilename(filename);
9526 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9528 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9530 LoadTapeFromFilename(filename);
9533 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9535 // chunk required for team mode tapes with non-default screen size
9536 return (tape->num_participating_players > 1 &&
9537 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9538 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9541 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9543 putFileVersion(file, tape->file_version);
9544 putFileVersion(file, tape->game_version);
9547 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9550 byte store_participating_players = 0;
9552 // set bits for participating players for compact storage
9553 for (i = 0; i < MAX_PLAYERS; i++)
9554 if (tape->player_participates[i])
9555 store_participating_players |= (1 << i);
9557 putFile32BitBE(file, tape->random_seed);
9558 putFile32BitBE(file, tape->date);
9559 putFile32BitBE(file, tape->length);
9561 putFile8Bit(file, store_participating_players);
9563 putFile8Bit(file, getTapeActionValue(tape));
9565 putFile8Bit(file, tape->property_bits);
9566 putFile8Bit(file, tape->solved);
9568 putFileVersion(file, tape->engine_version);
9571 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9573 putFile8Bit(file, tape->scr_fieldx);
9574 putFile8Bit(file, tape->scr_fieldy);
9577 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9579 int level_identifier_size = strlen(tape->level_identifier) + 1;
9582 putFile16BitBE(file, level_identifier_size);
9584 for (i = 0; i < level_identifier_size; i++)
9585 putFile8Bit(file, tape->level_identifier[i]);
9587 putFile16BitBE(file, tape->level_nr);
9590 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9594 for (i = 0; i < tape->length; i++)
9596 if (tape->use_key_actions)
9598 for (j = 0; j < MAX_PLAYERS; j++)
9599 if (tape->player_participates[j])
9600 putFile8Bit(file, tape->pos[i].action[j]);
9603 if (tape->use_mouse_actions)
9605 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9606 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9607 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9610 putFile8Bit(file, tape->pos[i].delay);
9614 void SaveTapeToFilename(char *filename)
9618 int info_chunk_size;
9619 int body_chunk_size;
9621 if (!(file = fopen(filename, MODE_WRITE)))
9623 Warn("cannot save level recording file '%s'", filename);
9628 tape_pos_size = getTapePosSize(&tape);
9630 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9631 body_chunk_size = tape_pos_size * tape.length;
9633 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9634 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9636 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9637 SaveTape_VERS(file, &tape);
9639 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9640 SaveTape_HEAD(file, &tape);
9642 if (checkSaveTape_SCRN(&tape))
9644 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9645 SaveTape_SCRN(file, &tape);
9648 putFileChunkBE(file, "INFO", info_chunk_size);
9649 SaveTape_INFO(file, &tape);
9651 putFileChunkBE(file, "BODY", body_chunk_size);
9652 SaveTape_BODY(file, &tape);
9656 SetFilePermissions(filename, PERMS_PRIVATE);
9659 static void SaveTapeExt(char *filename)
9663 tape.file_version = FILE_VERSION_ACTUAL;
9664 tape.game_version = GAME_VERSION_ACTUAL;
9666 tape.num_participating_players = 0;
9668 // count number of participating players
9669 for (i = 0; i < MAX_PLAYERS; i++)
9670 if (tape.player_participates[i])
9671 tape.num_participating_players++;
9673 SaveTapeToFilename(filename);
9675 tape.changed = FALSE;
9678 void SaveTape(int nr)
9680 char *filename = getTapeFilename(nr);
9682 InitTapeDirectory(leveldir_current->subdir);
9684 SaveTapeExt(filename);
9687 void SaveScoreTape(int nr)
9689 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9691 // used instead of "leveldir_current->subdir" (for network games)
9692 InitScoreTapeDirectory(levelset.identifier, nr);
9694 SaveTapeExt(filename);
9697 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9698 unsigned int req_state_added)
9700 char *filename = getTapeFilename(nr);
9701 boolean new_tape = !fileExists(filename);
9702 boolean tape_saved = FALSE;
9704 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9709 Request(msg_saved, REQ_CONFIRM | req_state_added);
9717 boolean SaveTapeChecked(int nr)
9719 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9722 boolean SaveTapeChecked_LevelSolved(int nr)
9724 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9725 "Level solved! Tape saved!", REQ_STAY_OPEN);
9728 void DumpTape(struct TapeInfo *tape)
9730 int tape_frame_counter;
9733 if (tape->no_valid_file)
9735 Warn("cannot dump -- no valid tape file found");
9742 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9743 tape->level_nr, tape->file_version, tape->game_version);
9744 Print(" (effective engine version %08d)\n",
9745 tape->engine_version);
9746 Print("Level series identifier: '%s'\n", tape->level_identifier);
9748 Print("Solution tape: %s\n",
9749 tape->solved ? "yes" :
9750 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9752 Print("Special tape properties: ");
9753 if (tape->property_bits == TAPE_PROPERTY_NONE)
9755 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9756 Print("[em_random_bug]");
9757 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9758 Print("[game_speed]");
9759 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9761 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9762 Print("[single_step]");
9763 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9764 Print("[snapshot]");
9765 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9766 Print("[replayed]");
9767 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9768 Print("[tas_keys]");
9769 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9770 Print("[small_graphics]");
9773 int year2 = tape->date / 10000;
9774 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9775 int month_index_raw = (tape->date / 100) % 100;
9776 int month_index = month_index_raw % 12; // prevent invalid index
9777 int month = month_index + 1;
9778 int day = tape->date % 100;
9780 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9784 tape_frame_counter = 0;
9786 for (i = 0; i < tape->length; i++)
9788 if (i >= MAX_TAPE_LEN)
9793 for (j = 0; j < MAX_PLAYERS; j++)
9795 if (tape->player_participates[j])
9797 int action = tape->pos[i].action[j];
9799 Print("%d:%02x ", j, action);
9800 Print("[%c%c%c%c|%c%c] - ",
9801 (action & JOY_LEFT ? '<' : ' '),
9802 (action & JOY_RIGHT ? '>' : ' '),
9803 (action & JOY_UP ? '^' : ' '),
9804 (action & JOY_DOWN ? 'v' : ' '),
9805 (action & JOY_BUTTON_1 ? '1' : ' '),
9806 (action & JOY_BUTTON_2 ? '2' : ' '));
9810 Print("(%03d) ", tape->pos[i].delay);
9811 Print("[%05d]\n", tape_frame_counter);
9813 tape_frame_counter += tape->pos[i].delay;
9819 void DumpTapes(void)
9821 static LevelDirTree *dumptape_leveldir = NULL;
9823 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9824 global.dumptape_leveldir);
9826 if (dumptape_leveldir == NULL)
9827 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9829 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9830 global.dumptape_level_nr > dumptape_leveldir->last_level)
9831 Fail("no such level number: %d", global.dumptape_level_nr);
9833 leveldir_current = dumptape_leveldir;
9835 if (options.mytapes)
9836 LoadTape(global.dumptape_level_nr);
9838 LoadSolutionTape(global.dumptape_level_nr);
9846 // ============================================================================
9847 // score file functions
9848 // ============================================================================
9850 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9854 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9856 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9857 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9858 scores->entry[i].score = 0;
9859 scores->entry[i].time = 0;
9861 scores->entry[i].id = -1;
9862 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9863 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9864 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9865 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9866 strcpy(scores->entry[i].country_code, "??");
9869 scores->num_entries = 0;
9870 scores->last_added = -1;
9871 scores->last_added_local = -1;
9873 scores->updated = FALSE;
9874 scores->uploaded = FALSE;
9875 scores->tape_downloaded = FALSE;
9876 scores->force_last_added = FALSE;
9878 // The following values are intentionally not reset here:
9882 // - continue_playing
9883 // - continue_on_return
9886 static void setScoreInfoToDefaults(void)
9888 setScoreInfoToDefaultsExt(&scores);
9891 static void setServerScoreInfoToDefaults(void)
9893 setScoreInfoToDefaultsExt(&server_scores);
9896 static void LoadScore_OLD(int nr)
9899 char *filename = getScoreFilename(nr);
9900 char cookie[MAX_LINE_LEN];
9901 char line[MAX_LINE_LEN];
9905 if (!(file = fopen(filename, MODE_READ)))
9908 // check file identifier
9909 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9911 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9912 cookie[strlen(cookie) - 1] = '\0';
9914 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9916 Warn("unknown format of score file '%s'", filename);
9923 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9925 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9926 Warn("fscanf() failed; %s", strerror(errno));
9928 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9931 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9932 line[strlen(line) - 1] = '\0';
9934 for (line_ptr = line; *line_ptr; line_ptr++)
9936 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9938 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9939 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9948 static void ConvertScore_OLD(void)
9950 // only convert score to time for levels that rate playing time over score
9951 if (!level.rate_time_over_score)
9954 // convert old score to playing time for score-less levels (like Supaplex)
9955 int time_final_max = 999;
9958 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9960 int score = scores.entry[i].score;
9962 if (score > 0 && score < time_final_max)
9963 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9967 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9969 scores->file_version = getFileVersion(file);
9970 scores->game_version = getFileVersion(file);
9975 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9977 char *level_identifier = NULL;
9978 int level_identifier_size;
9981 level_identifier_size = getFile16BitBE(file);
9983 level_identifier = checked_malloc(level_identifier_size);
9985 for (i = 0; i < level_identifier_size; i++)
9986 level_identifier[i] = getFile8Bit(file);
9988 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9989 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9991 checked_free(level_identifier);
9993 scores->level_nr = getFile16BitBE(file);
9994 scores->num_entries = getFile16BitBE(file);
9996 chunk_size = 2 + level_identifier_size + 2 + 2;
10001 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10005 for (i = 0; i < scores->num_entries; i++)
10007 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10008 scores->entry[i].name[j] = getFile8Bit(file);
10010 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10013 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10018 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10022 for (i = 0; i < scores->num_entries; i++)
10023 scores->entry[i].score = getFile16BitBE(file);
10025 chunk_size = scores->num_entries * 2;
10030 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10034 for (i = 0; i < scores->num_entries; i++)
10035 scores->entry[i].score = getFile32BitBE(file);
10037 chunk_size = scores->num_entries * 4;
10042 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10046 for (i = 0; i < scores->num_entries; i++)
10047 scores->entry[i].time = getFile32BitBE(file);
10049 chunk_size = scores->num_entries * 4;
10054 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10058 for (i = 0; i < scores->num_entries; i++)
10060 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10061 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10063 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10066 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10071 void LoadScore(int nr)
10073 char *filename = getScoreFilename(nr);
10074 char cookie[MAX_LINE_LEN];
10075 char chunk_name[CHUNK_ID_LEN + 1];
10077 boolean old_score_file_format = FALSE;
10080 // always start with reliable default values
10081 setScoreInfoToDefaults();
10083 if (!(file = openFile(filename, MODE_READ)))
10086 getFileChunkBE(file, chunk_name, NULL);
10087 if (strEqual(chunk_name, "RND1"))
10089 getFile32BitBE(file); // not used
10091 getFileChunkBE(file, chunk_name, NULL);
10092 if (!strEqual(chunk_name, "SCOR"))
10094 Warn("unknown format of score file '%s'", filename);
10101 else // check for old file format with cookie string
10103 strcpy(cookie, chunk_name);
10104 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10106 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10107 cookie[strlen(cookie) - 1] = '\0';
10109 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10111 Warn("unknown format of score file '%s'", filename);
10118 old_score_file_format = TRUE;
10121 if (old_score_file_format)
10123 // score files from versions before 4.2.4.0 without chunk structure
10126 // convert score to time, if possible (mainly for Supaplex levels)
10127 ConvertScore_OLD();
10135 int (*loader)(File *, int, struct ScoreInfo *);
10139 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10140 { "INFO", -1, LoadScore_INFO },
10141 { "NAME", -1, LoadScore_NAME },
10142 { "SCOR", -1, LoadScore_SCOR },
10143 { "SC4R", -1, LoadScore_SC4R },
10144 { "TIME", -1, LoadScore_TIME },
10145 { "TAPE", -1, LoadScore_TAPE },
10150 while (getFileChunkBE(file, chunk_name, &chunk_size))
10154 while (chunk_info[i].name != NULL &&
10155 !strEqual(chunk_name, chunk_info[i].name))
10158 if (chunk_info[i].name == NULL)
10160 Warn("unknown chunk '%s' in score file '%s'",
10161 chunk_name, filename);
10163 ReadUnusedBytesFromFile(file, chunk_size);
10165 else if (chunk_info[i].size != -1 &&
10166 chunk_info[i].size != chunk_size)
10168 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10169 chunk_size, chunk_name, filename);
10171 ReadUnusedBytesFromFile(file, chunk_size);
10175 // call function to load this score chunk
10176 int chunk_size_expected =
10177 (chunk_info[i].loader)(file, chunk_size, &scores);
10179 // the size of some chunks cannot be checked before reading other
10180 // chunks first (like "HEAD" and "BODY") that contain some header
10181 // information, so check them here
10182 if (chunk_size_expected != chunk_size)
10184 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10185 chunk_size, chunk_name, filename);
10194 #if ENABLE_HISTORIC_CHUNKS
10195 void SaveScore_OLD(int nr)
10198 char *filename = getScoreFilename(nr);
10201 // used instead of "leveldir_current->subdir" (for network games)
10202 InitScoreDirectory(levelset.identifier);
10204 if (!(file = fopen(filename, MODE_WRITE)))
10206 Warn("cannot save score for level %d", nr);
10211 fprintf(file, "%s\n\n", SCORE_COOKIE);
10213 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10214 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10218 SetFilePermissions(filename, PERMS_PRIVATE);
10222 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10224 putFileVersion(file, scores->file_version);
10225 putFileVersion(file, scores->game_version);
10228 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10230 int level_identifier_size = strlen(scores->level_identifier) + 1;
10233 putFile16BitBE(file, level_identifier_size);
10235 for (i = 0; i < level_identifier_size; i++)
10236 putFile8Bit(file, scores->level_identifier[i]);
10238 putFile16BitBE(file, scores->level_nr);
10239 putFile16BitBE(file, scores->num_entries);
10242 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10246 for (i = 0; i < scores->num_entries; i++)
10248 int name_size = strlen(scores->entry[i].name);
10250 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10251 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10255 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10259 for (i = 0; i < scores->num_entries; i++)
10260 putFile16BitBE(file, scores->entry[i].score);
10263 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10267 for (i = 0; i < scores->num_entries; i++)
10268 putFile32BitBE(file, scores->entry[i].score);
10271 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10275 for (i = 0; i < scores->num_entries; i++)
10276 putFile32BitBE(file, scores->entry[i].time);
10279 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10283 for (i = 0; i < scores->num_entries; i++)
10285 int size = strlen(scores->entry[i].tape_basename);
10287 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10288 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10292 static void SaveScoreToFilename(char *filename)
10295 int info_chunk_size;
10296 int name_chunk_size;
10297 int scor_chunk_size;
10298 int sc4r_chunk_size;
10299 int time_chunk_size;
10300 int tape_chunk_size;
10301 boolean has_large_score_values;
10304 if (!(file = fopen(filename, MODE_WRITE)))
10306 Warn("cannot save score file '%s'", filename);
10311 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10312 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10313 scor_chunk_size = scores.num_entries * 2;
10314 sc4r_chunk_size = scores.num_entries * 4;
10315 time_chunk_size = scores.num_entries * 4;
10316 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10318 has_large_score_values = FALSE;
10319 for (i = 0; i < scores.num_entries; i++)
10320 if (scores.entry[i].score > 0xffff)
10321 has_large_score_values = TRUE;
10323 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10324 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10326 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10327 SaveScore_VERS(file, &scores);
10329 putFileChunkBE(file, "INFO", info_chunk_size);
10330 SaveScore_INFO(file, &scores);
10332 putFileChunkBE(file, "NAME", name_chunk_size);
10333 SaveScore_NAME(file, &scores);
10335 if (has_large_score_values)
10337 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10338 SaveScore_SC4R(file, &scores);
10342 putFileChunkBE(file, "SCOR", scor_chunk_size);
10343 SaveScore_SCOR(file, &scores);
10346 putFileChunkBE(file, "TIME", time_chunk_size);
10347 SaveScore_TIME(file, &scores);
10349 putFileChunkBE(file, "TAPE", tape_chunk_size);
10350 SaveScore_TAPE(file, &scores);
10354 SetFilePermissions(filename, PERMS_PRIVATE);
10357 void SaveScore(int nr)
10359 char *filename = getScoreFilename(nr);
10362 // used instead of "leveldir_current->subdir" (for network games)
10363 InitScoreDirectory(levelset.identifier);
10365 scores.file_version = FILE_VERSION_ACTUAL;
10366 scores.game_version = GAME_VERSION_ACTUAL;
10368 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10369 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10370 scores.level_nr = level_nr;
10372 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10373 if (scores.entry[i].score == 0 &&
10374 scores.entry[i].time == 0 &&
10375 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10378 scores.num_entries = i;
10380 if (scores.num_entries == 0)
10383 SaveScoreToFilename(filename);
10386 static void LoadServerScoreFromCache(int nr)
10388 struct ScoreEntry score_entry;
10397 { &score_entry.score, FALSE, 0 },
10398 { &score_entry.time, FALSE, 0 },
10399 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10400 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10401 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10402 { &score_entry.id, FALSE, 0 },
10403 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10404 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10405 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10406 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10410 char *filename = getScoreCacheFilename(nr);
10411 SetupFileHash *score_hash = loadSetupFileHash(filename);
10414 server_scores.num_entries = 0;
10416 if (score_hash == NULL)
10419 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10421 score_entry = server_scores.entry[i];
10423 for (j = 0; score_mapping[j].value != NULL; j++)
10427 sprintf(token, "%02d.%d", i, j);
10429 char *value = getHashEntry(score_hash, token);
10434 if (score_mapping[j].is_string)
10436 char *score_value = (char *)score_mapping[j].value;
10437 int value_size = score_mapping[j].string_size;
10439 strncpy(score_value, value, value_size);
10440 score_value[value_size] = '\0';
10444 int *score_value = (int *)score_mapping[j].value;
10446 *score_value = atoi(value);
10449 server_scores.num_entries = i + 1;
10452 server_scores.entry[i] = score_entry;
10455 freeSetupFileHash(score_hash);
10458 void LoadServerScore(int nr, boolean download_score)
10460 if (!setup.use_api_server)
10463 // always start with reliable default values
10464 setServerScoreInfoToDefaults();
10466 // 1st step: load server scores from cache file (which may not exist)
10467 // (this should prevent reading it while the thread is writing to it)
10468 LoadServerScoreFromCache(nr);
10470 if (download_score && runtime.use_api_server)
10472 // 2nd step: download server scores from score server to cache file
10473 // (as thread, as it might time out if the server is not reachable)
10474 ApiGetScoreAsThread(nr);
10478 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10480 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10482 // if score tape not uploaded, ask for uploading missing tapes later
10483 if (!setup.has_remaining_tapes)
10484 setup.ask_for_remaining_tapes = TRUE;
10486 setup.provide_uploading_tapes = TRUE;
10487 setup.has_remaining_tapes = TRUE;
10489 SaveSetup_ServerSetup();
10492 void SaveServerScore(int nr, boolean tape_saved)
10494 if (!runtime.use_api_server)
10496 PrepareScoreTapesForUpload(leveldir_current->subdir);
10501 ApiAddScoreAsThread(nr, tape_saved, NULL);
10504 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10505 char *score_tape_filename)
10507 if (!runtime.use_api_server)
10510 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10513 void LoadLocalAndServerScore(int nr, boolean download_score)
10515 int last_added_local = scores.last_added_local;
10516 boolean force_last_added = scores.force_last_added;
10518 // needed if only showing server scores
10519 setScoreInfoToDefaults();
10521 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10524 // restore last added local score entry (before merging server scores)
10525 scores.last_added = scores.last_added_local = last_added_local;
10527 if (setup.use_api_server &&
10528 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10530 // load server scores from cache file and trigger update from server
10531 LoadServerScore(nr, download_score);
10533 // merge local scores with scores from server
10534 MergeServerScore();
10537 if (force_last_added)
10538 scores.force_last_added = force_last_added;
10542 // ============================================================================
10543 // setup file functions
10544 // ============================================================================
10546 #define TOKEN_STR_PLAYER_PREFIX "player_"
10549 static struct TokenInfo global_setup_tokens[] =
10553 &setup.player_name, "player_name"
10557 &setup.multiple_users, "multiple_users"
10561 &setup.sound, "sound"
10565 &setup.sound_loops, "repeating_sound_loops"
10569 &setup.sound_music, "background_music"
10573 &setup.sound_simple, "simple_sound_effects"
10577 &setup.toons, "toons"
10581 &setup.global_animations, "global_animations"
10585 &setup.scroll_delay, "scroll_delay"
10589 &setup.forced_scroll_delay, "forced_scroll_delay"
10593 &setup.scroll_delay_value, "scroll_delay_value"
10597 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10601 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10605 &setup.fade_screens, "fade_screens"
10609 &setup.autorecord, "automatic_tape_recording"
10613 &setup.autorecord_after_replay, "autorecord_after_replay"
10617 &setup.auto_pause_on_start, "auto_pause_on_start"
10621 &setup.show_titlescreen, "show_titlescreen"
10625 &setup.quick_doors, "quick_doors"
10629 &setup.team_mode, "team_mode"
10633 &setup.handicap, "handicap"
10637 &setup.skip_levels, "skip_levels"
10641 &setup.increment_levels, "increment_levels"
10645 &setup.auto_play_next_level, "auto_play_next_level"
10649 &setup.count_score_after_game, "count_score_after_game"
10653 &setup.show_scores_after_game, "show_scores_after_game"
10657 &setup.time_limit, "time_limit"
10661 &setup.fullscreen, "fullscreen"
10665 &setup.window_scaling_percent, "window_scaling_percent"
10669 &setup.window_scaling_quality, "window_scaling_quality"
10673 &setup.screen_rendering_mode, "screen_rendering_mode"
10677 &setup.vsync_mode, "vsync_mode"
10681 &setup.ask_on_escape, "ask_on_escape"
10685 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10689 &setup.ask_on_game_over, "ask_on_game_over"
10693 &setup.ask_on_quit_game, "ask_on_quit_game"
10697 &setup.ask_on_quit_program, "ask_on_quit_program"
10701 &setup.quick_switch, "quick_player_switch"
10705 &setup.input_on_focus, "input_on_focus"
10709 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10713 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10717 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10721 &setup.game_speed_extended, "game_speed_extended"
10725 &setup.game_frame_delay, "game_frame_delay"
10729 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10733 &setup.bd_skip_hatching, "bd_skip_hatching"
10737 &setup.bd_scroll_delay, "bd_scroll_delay"
10741 &setup.bd_smooth_movements, "bd_smooth_movements"
10745 &setup.sp_show_border_elements, "sp_show_border_elements"
10749 &setup.small_game_graphics, "small_game_graphics"
10753 &setup.show_load_save_buttons, "show_load_save_buttons"
10757 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10761 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10765 &setup.graphics_set, "graphics_set"
10769 &setup.sounds_set, "sounds_set"
10773 &setup.music_set, "music_set"
10777 &setup.override_level_graphics, "override_level_graphics"
10781 &setup.override_level_sounds, "override_level_sounds"
10785 &setup.override_level_music, "override_level_music"
10789 &setup.volume_simple, "volume_simple"
10793 &setup.volume_loops, "volume_loops"
10797 &setup.volume_music, "volume_music"
10801 &setup.network_mode, "network_mode"
10805 &setup.network_player_nr, "network_player"
10809 &setup.network_server_hostname, "network_server_hostname"
10813 &setup.touch.control_type, "touch.control_type"
10817 &setup.touch.move_distance, "touch.move_distance"
10821 &setup.touch.drop_distance, "touch.drop_distance"
10825 &setup.touch.transparency, "touch.transparency"
10829 &setup.touch.draw_outlined, "touch.draw_outlined"
10833 &setup.touch.draw_pressed, "touch.draw_pressed"
10837 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10841 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10845 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10849 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10853 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10857 static struct TokenInfo auto_setup_tokens[] =
10861 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10865 static struct TokenInfo server_setup_tokens[] =
10869 &setup.player_uuid, "player_uuid"
10873 &setup.player_version, "player_version"
10877 &setup.use_api_server, TEST_PREFIX "use_api_server"
10881 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10885 &setup.api_server_password, TEST_PREFIX "api_server_password"
10889 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10893 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10897 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10901 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10905 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10909 static struct TokenInfo editor_setup_tokens[] =
10913 &setup.editor.el_classic, "editor.el_classic"
10917 &setup.editor.el_custom, "editor.el_custom"
10921 &setup.editor.el_user_defined, "editor.el_user_defined"
10925 &setup.editor.el_dynamic, "editor.el_dynamic"
10929 &setup.editor.el_headlines, "editor.el_headlines"
10933 &setup.editor.show_element_token, "editor.show_element_token"
10937 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10941 static struct TokenInfo editor_cascade_setup_tokens[] =
10945 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10949 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10953 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10957 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10961 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10965 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10969 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10973 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10977 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10981 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10985 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10989 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10993 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10997 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11001 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11005 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11009 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11013 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11017 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11021 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11025 static struct TokenInfo shortcut_setup_tokens[] =
11029 &setup.shortcut.save_game, "shortcut.save_game"
11033 &setup.shortcut.load_game, "shortcut.load_game"
11037 &setup.shortcut.restart_game, "shortcut.restart_game"
11041 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11045 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11049 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11053 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11057 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11061 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11065 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11069 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11073 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11077 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11081 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11085 &setup.shortcut.tape_record, "shortcut.tape_record"
11089 &setup.shortcut.tape_play, "shortcut.tape_play"
11093 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11097 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11101 &setup.shortcut.sound_music, "shortcut.sound_music"
11105 &setup.shortcut.snap_left, "shortcut.snap_left"
11109 &setup.shortcut.snap_right, "shortcut.snap_right"
11113 &setup.shortcut.snap_up, "shortcut.snap_up"
11117 &setup.shortcut.snap_down, "shortcut.snap_down"
11121 static struct SetupInputInfo setup_input;
11122 static struct TokenInfo player_setup_tokens[] =
11126 &setup_input.use_joystick, ".use_joystick"
11130 &setup_input.joy.device_name, ".joy.device_name"
11134 &setup_input.joy.xleft, ".joy.xleft"
11138 &setup_input.joy.xmiddle, ".joy.xmiddle"
11142 &setup_input.joy.xright, ".joy.xright"
11146 &setup_input.joy.yupper, ".joy.yupper"
11150 &setup_input.joy.ymiddle, ".joy.ymiddle"
11154 &setup_input.joy.ylower, ".joy.ylower"
11158 &setup_input.joy.snap, ".joy.snap_field"
11162 &setup_input.joy.drop, ".joy.place_bomb"
11166 &setup_input.key.left, ".key.move_left"
11170 &setup_input.key.right, ".key.move_right"
11174 &setup_input.key.up, ".key.move_up"
11178 &setup_input.key.down, ".key.move_down"
11182 &setup_input.key.snap, ".key.snap_field"
11186 &setup_input.key.drop, ".key.place_bomb"
11190 static struct TokenInfo system_setup_tokens[] =
11194 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11198 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11202 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11206 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11210 static struct TokenInfo internal_setup_tokens[] =
11214 &setup.internal.program_title, "program_title"
11218 &setup.internal.program_version, "program_version"
11222 &setup.internal.program_author, "program_author"
11226 &setup.internal.program_email, "program_email"
11230 &setup.internal.program_website, "program_website"
11234 &setup.internal.program_copyright, "program_copyright"
11238 &setup.internal.program_company, "program_company"
11242 &setup.internal.program_icon_file, "program_icon_file"
11246 &setup.internal.default_graphics_set, "default_graphics_set"
11250 &setup.internal.default_sounds_set, "default_sounds_set"
11254 &setup.internal.default_music_set, "default_music_set"
11258 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11262 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11266 &setup.internal.fallback_music_file, "fallback_music_file"
11270 &setup.internal.default_level_series, "default_level_series"
11274 &setup.internal.default_window_width, "default_window_width"
11278 &setup.internal.default_window_height, "default_window_height"
11282 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11286 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11290 &setup.internal.create_user_levelset, "create_user_levelset"
11294 &setup.internal.info_screens_from_main, "info_screens_from_main"
11298 &setup.internal.menu_game, "menu_game"
11302 &setup.internal.menu_engines, "menu_engines"
11306 &setup.internal.menu_editor, "menu_editor"
11310 &setup.internal.menu_graphics, "menu_graphics"
11314 &setup.internal.menu_sound, "menu_sound"
11318 &setup.internal.menu_artwork, "menu_artwork"
11322 &setup.internal.menu_input, "menu_input"
11326 &setup.internal.menu_touch, "menu_touch"
11330 &setup.internal.menu_shortcuts, "menu_shortcuts"
11334 &setup.internal.menu_exit, "menu_exit"
11338 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11342 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11346 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11350 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11354 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11358 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11362 &setup.internal.info_title, "info_title"
11366 &setup.internal.info_elements, "info_elements"
11370 &setup.internal.info_music, "info_music"
11374 &setup.internal.info_credits, "info_credits"
11378 &setup.internal.info_program, "info_program"
11382 &setup.internal.info_version, "info_version"
11386 &setup.internal.info_levelset, "info_levelset"
11390 &setup.internal.info_exit, "info_exit"
11394 static struct TokenInfo debug_setup_tokens[] =
11398 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11402 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11406 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11410 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11414 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11418 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11422 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11426 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11430 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11434 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11438 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11442 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11446 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11450 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11454 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11458 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11462 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11466 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11470 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11474 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11478 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11481 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11485 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11489 &setup.debug.xsn_mode, "debug.xsn_mode"
11493 &setup.debug.xsn_percent, "debug.xsn_percent"
11497 static struct TokenInfo options_setup_tokens[] =
11501 &setup.options.verbose, "options.verbose"
11505 &setup.options.debug, "options.debug"
11509 &setup.options.debug_mode, "options.debug_mode"
11513 static void setSetupInfoToDefaults(struct SetupInfo *si)
11517 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11519 si->multiple_users = TRUE;
11522 si->sound_loops = TRUE;
11523 si->sound_music = TRUE;
11524 si->sound_simple = TRUE;
11526 si->global_animations = TRUE;
11527 si->scroll_delay = TRUE;
11528 si->forced_scroll_delay = FALSE;
11529 si->scroll_delay_value = STD_SCROLL_DELAY;
11530 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11531 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11532 si->fade_screens = TRUE;
11533 si->autorecord = TRUE;
11534 si->autorecord_after_replay = TRUE;
11535 si->auto_pause_on_start = FALSE;
11536 si->show_titlescreen = TRUE;
11537 si->quick_doors = FALSE;
11538 si->team_mode = FALSE;
11539 si->handicap = TRUE;
11540 si->skip_levels = TRUE;
11541 si->increment_levels = TRUE;
11542 si->auto_play_next_level = TRUE;
11543 si->count_score_after_game = TRUE;
11544 si->show_scores_after_game = TRUE;
11545 si->time_limit = TRUE;
11546 si->fullscreen = FALSE;
11547 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11548 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11549 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11550 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11551 si->ask_on_escape = TRUE;
11552 si->ask_on_escape_editor = TRUE;
11553 si->ask_on_game_over = TRUE;
11554 si->ask_on_quit_game = TRUE;
11555 si->ask_on_quit_program = TRUE;
11556 si->quick_switch = FALSE;
11557 si->input_on_focus = FALSE;
11558 si->prefer_aga_graphics = TRUE;
11559 si->prefer_lowpass_sounds = FALSE;
11560 si->prefer_extra_panel_items = TRUE;
11561 si->game_speed_extended = FALSE;
11562 si->game_frame_delay = GAME_FRAME_DELAY;
11563 si->bd_skip_uncovering = FALSE;
11564 si->bd_skip_hatching = FALSE;
11565 si->bd_scroll_delay = TRUE;
11566 si->bd_smooth_movements = AUTO;
11567 si->sp_show_border_elements = FALSE;
11568 si->small_game_graphics = FALSE;
11569 si->show_load_save_buttons = FALSE;
11570 si->show_undo_redo_buttons = FALSE;
11571 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11573 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11574 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11575 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11577 si->override_level_graphics = FALSE;
11578 si->override_level_sounds = FALSE;
11579 si->override_level_music = FALSE;
11581 si->volume_simple = 100; // percent
11582 si->volume_loops = 100; // percent
11583 si->volume_music = 100; // percent
11585 si->network_mode = FALSE;
11586 si->network_player_nr = 0; // first player
11587 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11589 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11590 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11591 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11592 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11593 si->touch.draw_outlined = TRUE;
11594 si->touch.draw_pressed = TRUE;
11596 for (i = 0; i < 2; i++)
11598 char *default_grid_button[6][2] =
11604 { "111222", " vv " },
11605 { "111222", " vv " }
11607 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11608 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11609 int min_xsize = MIN(6, grid_xsize);
11610 int min_ysize = MIN(6, grid_ysize);
11611 int startx = grid_xsize - min_xsize;
11612 int starty = grid_ysize - min_ysize;
11615 // virtual buttons grid can only be set to defaults if video is initialized
11616 // (this will be repeated if virtual buttons are not loaded from setup file)
11617 if (video.initialized)
11619 si->touch.grid_xsize[i] = grid_xsize;
11620 si->touch.grid_ysize[i] = grid_ysize;
11624 si->touch.grid_xsize[i] = -1;
11625 si->touch.grid_ysize[i] = -1;
11628 for (x = 0; x < MAX_GRID_XSIZE; x++)
11629 for (y = 0; y < MAX_GRID_YSIZE; y++)
11630 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11632 for (x = 0; x < min_xsize; x++)
11633 for (y = 0; y < min_ysize; y++)
11634 si->touch.grid_button[i][x][starty + y] =
11635 default_grid_button[y][0][x];
11637 for (x = 0; x < min_xsize; x++)
11638 for (y = 0; y < min_ysize; y++)
11639 si->touch.grid_button[i][startx + x][starty + y] =
11640 default_grid_button[y][1][x];
11643 si->touch.grid_initialized = video.initialized;
11645 si->touch.overlay_buttons = FALSE;
11647 si->editor.el_boulderdash = TRUE;
11648 si->editor.el_boulderdash_native = TRUE;
11649 si->editor.el_boulderdash_effects = TRUE;
11650 si->editor.el_emerald_mine = TRUE;
11651 si->editor.el_emerald_mine_club = TRUE;
11652 si->editor.el_more = TRUE;
11653 si->editor.el_sokoban = TRUE;
11654 si->editor.el_supaplex = TRUE;
11655 si->editor.el_diamond_caves = TRUE;
11656 si->editor.el_dx_boulderdash = TRUE;
11658 si->editor.el_mirror_magic = TRUE;
11659 si->editor.el_deflektor = TRUE;
11661 si->editor.el_chars = TRUE;
11662 si->editor.el_steel_chars = TRUE;
11664 si->editor.el_classic = TRUE;
11665 si->editor.el_custom = TRUE;
11667 si->editor.el_user_defined = FALSE;
11668 si->editor.el_dynamic = TRUE;
11670 si->editor.el_headlines = TRUE;
11672 si->editor.show_element_token = FALSE;
11674 si->editor.show_read_only_warning = TRUE;
11676 si->editor.use_template_for_new_levels = TRUE;
11678 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11679 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11680 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11681 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11682 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11684 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11685 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11686 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11687 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11688 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11690 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11691 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11692 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11693 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11694 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11695 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11697 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11698 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11699 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11701 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11702 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11703 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11704 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11706 for (i = 0; i < MAX_PLAYERS; i++)
11708 si->input[i].use_joystick = FALSE;
11709 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11710 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11711 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11712 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11713 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11714 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11715 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11716 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11717 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11718 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11719 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11720 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11721 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11722 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11723 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11726 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11727 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11728 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11729 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11731 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11732 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11733 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11734 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11735 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11736 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11737 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11739 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11741 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11742 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11743 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11745 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11746 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11747 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11749 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11750 si->internal.choose_from_top_leveldir = FALSE;
11751 si->internal.show_scaling_in_title = TRUE;
11752 si->internal.create_user_levelset = TRUE;
11753 si->internal.info_screens_from_main = FALSE;
11755 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11756 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11758 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11759 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11760 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11761 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11762 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11763 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11764 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11765 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11766 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11767 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11769 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11770 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11771 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11772 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11773 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11774 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11775 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11776 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11777 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11778 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11780 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11781 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11783 si->debug.show_frames_per_second = FALSE;
11785 si->debug.xsn_mode = AUTO;
11786 si->debug.xsn_percent = 0;
11788 si->options.verbose = FALSE;
11789 si->options.debug = FALSE;
11790 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11792 #if defined(PLATFORM_ANDROID)
11793 si->fullscreen = TRUE;
11794 si->touch.overlay_buttons = TRUE;
11797 setHideSetupEntry(&setup.debug.xsn_mode);
11800 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11802 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11805 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11807 si->player_uuid = NULL; // (will be set later)
11808 si->player_version = 1; // (will be set later)
11810 si->use_api_server = TRUE;
11811 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11812 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11813 si->ask_for_uploading_tapes = TRUE;
11814 si->ask_for_remaining_tapes = FALSE;
11815 si->provide_uploading_tapes = TRUE;
11816 si->ask_for_using_api_server = TRUE;
11817 si->has_remaining_tapes = FALSE;
11820 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11822 si->editor_cascade.el_bd = TRUE;
11823 si->editor_cascade.el_bd_native = TRUE;
11824 si->editor_cascade.el_bd_effects = FALSE;
11825 si->editor_cascade.el_em = TRUE;
11826 si->editor_cascade.el_emc = TRUE;
11827 si->editor_cascade.el_rnd = TRUE;
11828 si->editor_cascade.el_sb = TRUE;
11829 si->editor_cascade.el_sp = TRUE;
11830 si->editor_cascade.el_dc = TRUE;
11831 si->editor_cascade.el_dx = TRUE;
11833 si->editor_cascade.el_mm = TRUE;
11834 si->editor_cascade.el_df = TRUE;
11836 si->editor_cascade.el_chars = FALSE;
11837 si->editor_cascade.el_steel_chars = FALSE;
11838 si->editor_cascade.el_ce = FALSE;
11839 si->editor_cascade.el_ge = FALSE;
11840 si->editor_cascade.el_es = FALSE;
11841 si->editor_cascade.el_ref = FALSE;
11842 si->editor_cascade.el_user = FALSE;
11843 si->editor_cascade.el_dynamic = FALSE;
11846 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11848 static char *getHideSetupToken(void *setup_value)
11850 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11852 if (setup_value != NULL)
11853 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11855 return hide_setup_token;
11858 void setHideSetupEntry(void *setup_value)
11860 char *hide_setup_token = getHideSetupToken(setup_value);
11862 if (hide_setup_hash == NULL)
11863 hide_setup_hash = newSetupFileHash();
11865 if (setup_value != NULL)
11866 setHashEntry(hide_setup_hash, hide_setup_token, "");
11869 void removeHideSetupEntry(void *setup_value)
11871 char *hide_setup_token = getHideSetupToken(setup_value);
11873 if (setup_value != NULL)
11874 removeHashEntry(hide_setup_hash, hide_setup_token);
11877 boolean hideSetupEntry(void *setup_value)
11879 char *hide_setup_token = getHideSetupToken(setup_value);
11881 return (setup_value != NULL &&
11882 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11885 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11886 struct TokenInfo *token_info,
11887 int token_nr, char *token_text)
11889 char *token_hide_text = getStringCat2(token_text, ".hide");
11890 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11892 // set the value of this setup option in the setup option structure
11893 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11895 // check if this setup option should be hidden in the setup menu
11896 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11897 setHideSetupEntry(token_info[token_nr].value);
11899 free(token_hide_text);
11902 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11903 struct TokenInfo *token_info,
11906 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11907 token_info[token_nr].text);
11910 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11914 if (!setup_file_hash)
11917 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11918 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11920 setup.touch.grid_initialized = TRUE;
11921 for (i = 0; i < 2; i++)
11923 int grid_xsize = setup.touch.grid_xsize[i];
11924 int grid_ysize = setup.touch.grid_ysize[i];
11927 // if virtual buttons are not loaded from setup file, repeat initializing
11928 // virtual buttons grid with default values later when video is initialized
11929 if (grid_xsize == -1 ||
11932 setup.touch.grid_initialized = FALSE;
11937 for (y = 0; y < grid_ysize; y++)
11939 char token_string[MAX_LINE_LEN];
11941 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11943 char *value_string = getHashEntry(setup_file_hash, token_string);
11945 if (value_string == NULL)
11948 for (x = 0; x < grid_xsize; x++)
11950 char c = value_string[x];
11952 setup.touch.grid_button[i][x][y] =
11953 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11958 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11959 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11961 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11962 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11964 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11968 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11970 setup_input = setup.input[pnr];
11971 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11973 char full_token[100];
11975 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11976 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11979 setup.input[pnr] = setup_input;
11982 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11983 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11985 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11986 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11988 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11989 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11991 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11992 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11994 setHideRelatedSetupEntries();
11997 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12001 if (!setup_file_hash)
12004 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12005 setSetupInfo(auto_setup_tokens, i,
12006 getHashEntry(setup_file_hash,
12007 auto_setup_tokens[i].text));
12010 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12014 if (!setup_file_hash)
12017 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12018 setSetupInfo(server_setup_tokens, i,
12019 getHashEntry(setup_file_hash,
12020 server_setup_tokens[i].text));
12023 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12027 if (!setup_file_hash)
12030 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12031 setSetupInfo(editor_cascade_setup_tokens, i,
12032 getHashEntry(setup_file_hash,
12033 editor_cascade_setup_tokens[i].text));
12036 void LoadUserNames(void)
12038 int last_user_nr = user.nr;
12041 if (global.user_names != NULL)
12043 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12044 checked_free(global.user_names[i]);
12046 checked_free(global.user_names);
12049 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12051 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12055 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12057 if (setup_file_hash)
12059 char *player_name = getHashEntry(setup_file_hash, "player_name");
12061 global.user_names[i] = getFixedUserName(player_name);
12063 freeSetupFileHash(setup_file_hash);
12066 if (global.user_names[i] == NULL)
12067 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12070 user.nr = last_user_nr;
12073 void LoadSetupFromFilename(char *filename)
12075 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12077 if (setup_file_hash)
12079 decodeSetupFileHash_Default(setup_file_hash);
12081 freeSetupFileHash(setup_file_hash);
12085 Debug("setup", "using default setup values");
12089 static void LoadSetup_SpecialPostProcessing(void)
12091 char *player_name_new;
12093 // needed to work around problems with fixed length strings
12094 player_name_new = getFixedUserName(setup.player_name);
12095 free(setup.player_name);
12096 setup.player_name = player_name_new;
12098 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12099 if (setup.scroll_delay == FALSE)
12101 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12102 setup.scroll_delay = TRUE; // now always "on"
12105 // make sure that scroll delay value stays inside valid range
12106 setup.scroll_delay_value =
12107 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12110 void LoadSetup_Default(void)
12114 // always start with reliable default values
12115 setSetupInfoToDefaults(&setup);
12117 // try to load setup values from default setup file
12118 filename = getDefaultSetupFilename();
12120 if (fileExists(filename))
12121 LoadSetupFromFilename(filename);
12123 // try to load setup values from platform setup file
12124 filename = getPlatformSetupFilename();
12126 if (fileExists(filename))
12127 LoadSetupFromFilename(filename);
12129 // try to load setup values from user setup file
12130 filename = getSetupFilename();
12132 LoadSetupFromFilename(filename);
12134 LoadSetup_SpecialPostProcessing();
12137 void LoadSetup_AutoSetup(void)
12139 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12140 SetupFileHash *setup_file_hash = NULL;
12142 // always start with reliable default values
12143 setSetupInfoToDefaults_AutoSetup(&setup);
12145 setup_file_hash = loadSetupFileHash(filename);
12147 if (setup_file_hash)
12149 decodeSetupFileHash_AutoSetup(setup_file_hash);
12151 freeSetupFileHash(setup_file_hash);
12157 void LoadSetup_ServerSetup(void)
12159 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12160 SetupFileHash *setup_file_hash = NULL;
12162 // always start with reliable default values
12163 setSetupInfoToDefaults_ServerSetup(&setup);
12165 setup_file_hash = loadSetupFileHash(filename);
12167 if (setup_file_hash)
12169 decodeSetupFileHash_ServerSetup(setup_file_hash);
12171 freeSetupFileHash(setup_file_hash);
12176 if (setup.player_uuid == NULL)
12178 // player UUID does not yet exist in setup file
12179 setup.player_uuid = getStringCopy(getUUID());
12180 setup.player_version = 2;
12182 SaveSetup_ServerSetup();
12186 void LoadSetup_EditorCascade(void)
12188 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12189 SetupFileHash *setup_file_hash = NULL;
12191 // always start with reliable default values
12192 setSetupInfoToDefaults_EditorCascade(&setup);
12194 setup_file_hash = loadSetupFileHash(filename);
12196 if (setup_file_hash)
12198 decodeSetupFileHash_EditorCascade(setup_file_hash);
12200 freeSetupFileHash(setup_file_hash);
12206 void LoadSetup(void)
12208 LoadSetup_Default();
12209 LoadSetup_AutoSetup();
12210 LoadSetup_ServerSetup();
12211 LoadSetup_EditorCascade();
12214 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12215 char *mapping_line)
12217 char mapping_guid[MAX_LINE_LEN];
12218 char *mapping_start, *mapping_end;
12220 // get GUID from game controller mapping line: copy complete line
12221 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12222 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12224 // get GUID from game controller mapping line: cut after GUID part
12225 mapping_start = strchr(mapping_guid, ',');
12226 if (mapping_start != NULL)
12227 *mapping_start = '\0';
12229 // cut newline from game controller mapping line
12230 mapping_end = strchr(mapping_line, '\n');
12231 if (mapping_end != NULL)
12232 *mapping_end = '\0';
12234 // add mapping entry to game controller mappings hash
12235 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12238 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12243 if (!(file = fopen(filename, MODE_READ)))
12245 Warn("cannot read game controller mappings file '%s'", filename);
12250 while (!feof(file))
12252 char line[MAX_LINE_LEN];
12254 if (!fgets(line, MAX_LINE_LEN, file))
12257 addGameControllerMappingToHash(mappings_hash, line);
12263 void SaveSetup_Default(void)
12265 char *filename = getSetupFilename();
12269 InitUserDataDirectory();
12271 if (!(file = fopen(filename, MODE_WRITE)))
12273 Warn("cannot write setup file '%s'", filename);
12278 fprintFileHeader(file, SETUP_FILENAME);
12280 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12282 // just to make things nicer :)
12283 if (global_setup_tokens[i].value == &setup.multiple_users ||
12284 global_setup_tokens[i].value == &setup.sound ||
12285 global_setup_tokens[i].value == &setup.graphics_set ||
12286 global_setup_tokens[i].value == &setup.volume_simple ||
12287 global_setup_tokens[i].value == &setup.network_mode ||
12288 global_setup_tokens[i].value == &setup.touch.control_type ||
12289 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12290 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12291 fprintf(file, "\n");
12293 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12296 for (i = 0; i < 2; i++)
12298 int grid_xsize = setup.touch.grid_xsize[i];
12299 int grid_ysize = setup.touch.grid_ysize[i];
12302 fprintf(file, "\n");
12304 for (y = 0; y < grid_ysize; y++)
12306 char token_string[MAX_LINE_LEN];
12307 char value_string[MAX_LINE_LEN];
12309 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12311 for (x = 0; x < grid_xsize; x++)
12313 char c = setup.touch.grid_button[i][x][y];
12315 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12318 value_string[grid_xsize] = '\0';
12320 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12324 fprintf(file, "\n");
12325 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12326 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12328 fprintf(file, "\n");
12329 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12330 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12332 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12336 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12337 fprintf(file, "\n");
12339 setup_input = setup.input[pnr];
12340 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12341 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12344 fprintf(file, "\n");
12345 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12346 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12348 // (internal setup values not saved to user setup file)
12350 fprintf(file, "\n");
12351 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12352 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12353 setup.debug.xsn_mode != AUTO)
12354 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12356 fprintf(file, "\n");
12357 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12358 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12362 SetFilePermissions(filename, PERMS_PRIVATE);
12365 void SaveSetup_AutoSetup(void)
12367 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12371 InitUserDataDirectory();
12373 if (!(file = fopen(filename, MODE_WRITE)))
12375 Warn("cannot write auto setup file '%s'", filename);
12382 fprintFileHeader(file, AUTOSETUP_FILENAME);
12384 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12385 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12389 SetFilePermissions(filename, PERMS_PRIVATE);
12394 void SaveSetup_ServerSetup(void)
12396 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12400 InitUserDataDirectory();
12402 if (!(file = fopen(filename, MODE_WRITE)))
12404 Warn("cannot write server setup file '%s'", filename);
12411 fprintFileHeader(file, SERVERSETUP_FILENAME);
12413 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12415 // just to make things nicer :)
12416 if (server_setup_tokens[i].value == &setup.use_api_server)
12417 fprintf(file, "\n");
12419 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12424 SetFilePermissions(filename, PERMS_PRIVATE);
12429 void SaveSetup_EditorCascade(void)
12431 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12435 InitUserDataDirectory();
12437 if (!(file = fopen(filename, MODE_WRITE)))
12439 Warn("cannot write editor cascade state file '%s'", filename);
12446 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12448 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12449 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12453 SetFilePermissions(filename, PERMS_PRIVATE);
12458 void SaveSetup(void)
12460 SaveSetup_Default();
12461 SaveSetup_AutoSetup();
12462 SaveSetup_ServerSetup();
12463 SaveSetup_EditorCascade();
12466 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12471 if (!(file = fopen(filename, MODE_WRITE)))
12473 Warn("cannot write game controller mappings file '%s'", filename);
12478 BEGIN_HASH_ITERATION(mappings_hash, itr)
12480 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12482 END_HASH_ITERATION(mappings_hash, itr)
12487 void SaveSetup_AddGameControllerMapping(char *mapping)
12489 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12490 SetupFileHash *mappings_hash = newSetupFileHash();
12492 InitUserDataDirectory();
12494 // load existing personal game controller mappings
12495 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12497 // add new mapping to personal game controller mappings
12498 addGameControllerMappingToHash(mappings_hash, mapping);
12500 // save updated personal game controller mappings
12501 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12503 freeSetupFileHash(mappings_hash);
12507 void LoadCustomElementDescriptions(void)
12509 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12510 SetupFileHash *setup_file_hash;
12513 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12515 if (element_info[i].custom_description != NULL)
12517 free(element_info[i].custom_description);
12518 element_info[i].custom_description = NULL;
12522 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12525 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12527 char *token = getStringCat2(element_info[i].token_name, ".name");
12528 char *value = getHashEntry(setup_file_hash, token);
12531 element_info[i].custom_description = getStringCopy(value);
12536 freeSetupFileHash(setup_file_hash);
12539 static int getElementFromToken(char *token)
12541 char *value = getHashEntry(element_token_hash, token);
12544 return atoi(value);
12546 Warn("unknown element token '%s'", token);
12548 return EL_UNDEFINED;
12551 void FreeGlobalAnimEventInfo(void)
12553 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12555 if (gaei->event_list == NULL)
12560 for (i = 0; i < gaei->num_event_lists; i++)
12562 checked_free(gaei->event_list[i]->event_value);
12563 checked_free(gaei->event_list[i]);
12566 checked_free(gaei->event_list);
12568 gaei->event_list = NULL;
12569 gaei->num_event_lists = 0;
12572 static int AddGlobalAnimEventList(void)
12574 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12575 int list_pos = gaei->num_event_lists++;
12577 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12578 sizeof(struct GlobalAnimEventListInfo *));
12580 gaei->event_list[list_pos] =
12581 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12583 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12585 gaeli->event_value = NULL;
12586 gaeli->num_event_values = 0;
12591 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12593 // do not add empty global animation events
12594 if (event_value == ANIM_EVENT_NONE)
12597 // if list position is undefined, create new list
12598 if (list_pos == ANIM_EVENT_UNDEFINED)
12599 list_pos = AddGlobalAnimEventList();
12601 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12602 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12603 int value_pos = gaeli->num_event_values++;
12605 gaeli->event_value = checked_realloc(gaeli->event_value,
12606 gaeli->num_event_values * sizeof(int *));
12608 gaeli->event_value[value_pos] = event_value;
12613 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12615 if (list_pos == ANIM_EVENT_UNDEFINED)
12616 return ANIM_EVENT_NONE;
12618 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12619 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12621 return gaeli->event_value[value_pos];
12624 int GetGlobalAnimEventValueCount(int list_pos)
12626 if (list_pos == ANIM_EVENT_UNDEFINED)
12629 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12630 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12632 return gaeli->num_event_values;
12635 // This function checks if a string <s> of the format "string1, string2, ..."
12636 // exactly contains a string <s_contained>.
12638 static boolean string_has_parameter(char *s, char *s_contained)
12642 if (s == NULL || s_contained == NULL)
12645 if (strlen(s_contained) > strlen(s))
12648 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12650 char next_char = s[strlen(s_contained)];
12652 // check if next character is delimiter or whitespace
12653 if (next_char == ',' || next_char == '\0' ||
12654 next_char == ' ' || next_char == '\t')
12658 // check if string contains another parameter string after a comma
12659 substring = strchr(s, ',');
12660 if (substring == NULL) // string does not contain a comma
12663 // advance string pointer to next character after the comma
12666 // skip potential whitespaces after the comma
12667 while (*substring == ' ' || *substring == '\t')
12670 return string_has_parameter(substring, s_contained);
12673 static int get_anim_parameter_value_ce(char *s)
12676 char *pattern_1 = "ce_change:custom_";
12677 char *pattern_2 = ".page_";
12678 int pattern_1_len = strlen(pattern_1);
12679 char *matching_char = strstr(s_ptr, pattern_1);
12680 int result = ANIM_EVENT_NONE;
12682 if (matching_char == NULL)
12683 return ANIM_EVENT_NONE;
12685 result = ANIM_EVENT_CE_CHANGE;
12687 s_ptr = matching_char + pattern_1_len;
12689 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12690 if (*s_ptr >= '0' && *s_ptr <= '9')
12692 int gic_ce_nr = (*s_ptr++ - '0');
12694 if (*s_ptr >= '0' && *s_ptr <= '9')
12696 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12698 if (*s_ptr >= '0' && *s_ptr <= '9')
12699 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12702 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12703 return ANIM_EVENT_NONE;
12705 // custom element stored as 0 to 255
12708 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12712 // invalid custom element number specified
12714 return ANIM_EVENT_NONE;
12717 // check for change page number ("page_X" or "page_XX") (optional)
12718 if (strPrefix(s_ptr, pattern_2))
12720 s_ptr += strlen(pattern_2);
12722 if (*s_ptr >= '0' && *s_ptr <= '9')
12724 int gic_page_nr = (*s_ptr++ - '0');
12726 if (*s_ptr >= '0' && *s_ptr <= '9')
12727 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12729 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12730 return ANIM_EVENT_NONE;
12732 // change page stored as 1 to 32 (0 means "all change pages")
12734 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12738 // invalid animation part number specified
12740 return ANIM_EVENT_NONE;
12744 // discard result if next character is neither delimiter nor whitespace
12745 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12746 *s_ptr == ' ' || *s_ptr == '\t'))
12747 return ANIM_EVENT_NONE;
12752 static int get_anim_parameter_value(char *s)
12754 int event_value[] =
12762 char *pattern_1[] =
12770 char *pattern_2 = ".part_";
12771 char *matching_char = NULL;
12773 int pattern_1_len = 0;
12774 int result = ANIM_EVENT_NONE;
12777 result = get_anim_parameter_value_ce(s);
12779 if (result != ANIM_EVENT_NONE)
12782 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12784 matching_char = strstr(s_ptr, pattern_1[i]);
12785 pattern_1_len = strlen(pattern_1[i]);
12786 result = event_value[i];
12788 if (matching_char != NULL)
12792 if (matching_char == NULL)
12793 return ANIM_EVENT_NONE;
12795 s_ptr = matching_char + pattern_1_len;
12797 // check for main animation number ("anim_X" or "anim_XX")
12798 if (*s_ptr >= '0' && *s_ptr <= '9')
12800 int gic_anim_nr = (*s_ptr++ - '0');
12802 if (*s_ptr >= '0' && *s_ptr <= '9')
12803 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12805 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12806 return ANIM_EVENT_NONE;
12808 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12812 // invalid main animation number specified
12814 return ANIM_EVENT_NONE;
12817 // check for animation part number ("part_X" or "part_XX") (optional)
12818 if (strPrefix(s_ptr, pattern_2))
12820 s_ptr += strlen(pattern_2);
12822 if (*s_ptr >= '0' && *s_ptr <= '9')
12824 int gic_part_nr = (*s_ptr++ - '0');
12826 if (*s_ptr >= '0' && *s_ptr <= '9')
12827 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12829 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12830 return ANIM_EVENT_NONE;
12832 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12836 // invalid animation part number specified
12838 return ANIM_EVENT_NONE;
12842 // discard result if next character is neither delimiter nor whitespace
12843 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12844 *s_ptr == ' ' || *s_ptr == '\t'))
12845 return ANIM_EVENT_NONE;
12850 static int get_anim_parameter_values(char *s)
12852 int list_pos = ANIM_EVENT_UNDEFINED;
12853 int event_value = ANIM_EVENT_DEFAULT;
12855 if (string_has_parameter(s, "any"))
12856 event_value |= ANIM_EVENT_ANY;
12858 if (string_has_parameter(s, "click:self") ||
12859 string_has_parameter(s, "click") ||
12860 string_has_parameter(s, "self"))
12861 event_value |= ANIM_EVENT_SELF;
12863 if (string_has_parameter(s, "unclick:any"))
12864 event_value |= ANIM_EVENT_UNCLICK_ANY;
12866 // if animation event found, add it to global animation event list
12867 if (event_value != ANIM_EVENT_NONE)
12868 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12872 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12873 event_value = get_anim_parameter_value(s);
12875 // if animation event found, add it to global animation event list
12876 if (event_value != ANIM_EVENT_NONE)
12877 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12879 // continue with next part of the string, starting with next comma
12880 s = strchr(s + 1, ',');
12886 static int get_anim_action_parameter_value(char *token)
12888 // check most common default case first to massively speed things up
12889 if (strEqual(token, ARG_UNDEFINED))
12890 return ANIM_EVENT_ACTION_NONE;
12892 int result = getImageIDFromToken(token);
12896 char *gfx_token = getStringCat2("gfx.", token);
12898 result = getImageIDFromToken(gfx_token);
12900 checked_free(gfx_token);
12905 Key key = getKeyFromX11KeyName(token);
12907 if (key != KSYM_UNDEFINED)
12908 result = -(int)key;
12915 result = get_hash_from_string(token); // unsigned int => int
12916 result = ABS(result); // may be negative now
12917 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12919 setHashEntry(anim_url_hash, int2str(result, 0), token);
12924 result = ANIM_EVENT_ACTION_NONE;
12929 int get_parameter_value(char *value_raw, char *suffix, int type)
12931 char *value = getStringToLower(value_raw);
12932 int result = 0; // probably a save default value
12934 if (strEqual(suffix, ".direction"))
12936 result = (strEqual(value, "left") ? MV_LEFT :
12937 strEqual(value, "right") ? MV_RIGHT :
12938 strEqual(value, "up") ? MV_UP :
12939 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12941 else if (strEqual(suffix, ".position"))
12943 result = (strEqual(value, "left") ? POS_LEFT :
12944 strEqual(value, "right") ? POS_RIGHT :
12945 strEqual(value, "top") ? POS_TOP :
12946 strEqual(value, "upper") ? POS_UPPER :
12947 strEqual(value, "middle") ? POS_MIDDLE :
12948 strEqual(value, "lower") ? POS_LOWER :
12949 strEqual(value, "bottom") ? POS_BOTTOM :
12950 strEqual(value, "any") ? POS_ANY :
12951 strEqual(value, "ce") ? POS_CE :
12952 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12953 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12955 else if (strEqual(suffix, ".align"))
12957 result = (strEqual(value, "left") ? ALIGN_LEFT :
12958 strEqual(value, "right") ? ALIGN_RIGHT :
12959 strEqual(value, "center") ? ALIGN_CENTER :
12960 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12962 else if (strEqual(suffix, ".valign"))
12964 result = (strEqual(value, "top") ? VALIGN_TOP :
12965 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12966 strEqual(value, "middle") ? VALIGN_MIDDLE :
12967 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12969 else if (strEqual(suffix, ".anim_mode"))
12971 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12972 string_has_parameter(value, "loop") ? ANIM_LOOP :
12973 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12974 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12975 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12976 string_has_parameter(value, "random") ? ANIM_RANDOM :
12977 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12978 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12979 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12980 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12981 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12982 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12983 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12984 string_has_parameter(value, "all") ? ANIM_ALL :
12985 string_has_parameter(value, "tiled") ? ANIM_TILED :
12986 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12989 if (string_has_parameter(value, "once"))
12990 result |= ANIM_ONCE;
12992 if (string_has_parameter(value, "reverse"))
12993 result |= ANIM_REVERSE;
12995 if (string_has_parameter(value, "opaque_player"))
12996 result |= ANIM_OPAQUE_PLAYER;
12998 if (string_has_parameter(value, "static_panel"))
12999 result |= ANIM_STATIC_PANEL;
13001 else if (strEqual(suffix, ".init_event") ||
13002 strEqual(suffix, ".anim_event"))
13004 result = get_anim_parameter_values(value);
13006 else if (strEqual(suffix, ".init_delay_action") ||
13007 strEqual(suffix, ".anim_delay_action") ||
13008 strEqual(suffix, ".post_delay_action") ||
13009 strEqual(suffix, ".init_event_action") ||
13010 strEqual(suffix, ".anim_event_action"))
13012 result = get_anim_action_parameter_value(value_raw);
13014 else if (strEqual(suffix, ".class"))
13016 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13017 get_hash_from_string(value));
13019 else if (strEqual(suffix, ".style"))
13021 result = STYLE_DEFAULT;
13023 if (string_has_parameter(value, "accurate_borders"))
13024 result |= STYLE_ACCURATE_BORDERS;
13026 if (string_has_parameter(value, "inner_corners"))
13027 result |= STYLE_INNER_CORNERS;
13029 if (string_has_parameter(value, "reverse"))
13030 result |= STYLE_REVERSE;
13032 if (string_has_parameter(value, "leftmost_position"))
13033 result |= STYLE_LEFTMOST_POSITION;
13035 if (string_has_parameter(value, "block_clicks"))
13036 result |= STYLE_BLOCK;
13038 if (string_has_parameter(value, "passthrough_clicks"))
13039 result |= STYLE_PASSTHROUGH;
13041 if (string_has_parameter(value, "multiple_actions"))
13042 result |= STYLE_MULTIPLE_ACTIONS;
13044 if (string_has_parameter(value, "consume_ce_event"))
13045 result |= STYLE_CONSUME_CE_EVENT;
13047 else if (strEqual(suffix, ".fade_mode"))
13049 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13050 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13051 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13052 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13053 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13054 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13055 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13056 FADE_MODE_DEFAULT);
13058 else if (strEqual(suffix, ".auto_delay_unit"))
13060 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13061 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13062 AUTO_DELAY_UNIT_DEFAULT);
13064 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13066 result = gfx.get_font_from_token_function(value);
13068 else // generic parameter of type integer or boolean
13070 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13071 type == TYPE_INTEGER ? get_integer_from_string(value) :
13072 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13073 ARG_UNDEFINED_VALUE);
13081 static int get_token_parameter_value(char *token, char *value_raw)
13085 if (token == NULL || value_raw == NULL)
13086 return ARG_UNDEFINED_VALUE;
13088 suffix = strrchr(token, '.');
13089 if (suffix == NULL)
13092 if (strEqual(suffix, ".element"))
13093 return getElementFromToken(value_raw);
13095 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13096 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13099 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13100 boolean ignore_defaults)
13104 for (i = 0; image_config_vars[i].token != NULL; i++)
13106 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13108 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13109 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13113 *image_config_vars[i].value =
13114 get_token_parameter_value(image_config_vars[i].token, value);
13118 void InitMenuDesignSettings_Static(void)
13120 // always start with reliable default values from static default config
13121 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13124 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13128 // the following initializes hierarchical values from static configuration
13130 // special case: initialize "ARG_DEFAULT" values in static default config
13131 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13132 titlescreen_initial_first_default.fade_mode =
13133 title_initial_first_default.fade_mode;
13134 titlescreen_initial_first_default.fade_delay =
13135 title_initial_first_default.fade_delay;
13136 titlescreen_initial_first_default.post_delay =
13137 title_initial_first_default.post_delay;
13138 titlescreen_initial_first_default.auto_delay =
13139 title_initial_first_default.auto_delay;
13140 titlescreen_initial_first_default.auto_delay_unit =
13141 title_initial_first_default.auto_delay_unit;
13142 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13143 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13144 titlescreen_first_default.post_delay = title_first_default.post_delay;
13145 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13146 titlescreen_first_default.auto_delay_unit =
13147 title_first_default.auto_delay_unit;
13148 titlemessage_initial_first_default.fade_mode =
13149 title_initial_first_default.fade_mode;
13150 titlemessage_initial_first_default.fade_delay =
13151 title_initial_first_default.fade_delay;
13152 titlemessage_initial_first_default.post_delay =
13153 title_initial_first_default.post_delay;
13154 titlemessage_initial_first_default.auto_delay =
13155 title_initial_first_default.auto_delay;
13156 titlemessage_initial_first_default.auto_delay_unit =
13157 title_initial_first_default.auto_delay_unit;
13158 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13159 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13160 titlemessage_first_default.post_delay = title_first_default.post_delay;
13161 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13162 titlemessage_first_default.auto_delay_unit =
13163 title_first_default.auto_delay_unit;
13165 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13166 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13167 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13168 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13169 titlescreen_initial_default.auto_delay_unit =
13170 title_initial_default.auto_delay_unit;
13171 titlescreen_default.fade_mode = title_default.fade_mode;
13172 titlescreen_default.fade_delay = title_default.fade_delay;
13173 titlescreen_default.post_delay = title_default.post_delay;
13174 titlescreen_default.auto_delay = title_default.auto_delay;
13175 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13176 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13177 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13178 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13179 titlemessage_initial_default.auto_delay_unit =
13180 title_initial_default.auto_delay_unit;
13181 titlemessage_default.fade_mode = title_default.fade_mode;
13182 titlemessage_default.fade_delay = title_default.fade_delay;
13183 titlemessage_default.post_delay = title_default.post_delay;
13184 titlemessage_default.auto_delay = title_default.auto_delay;
13185 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13187 // special case: initialize "ARG_DEFAULT" values in static default config
13188 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13189 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13191 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13192 titlescreen_first[i] = titlescreen_first_default;
13193 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13194 titlemessage_first[i] = titlemessage_first_default;
13196 titlescreen_initial[i] = titlescreen_initial_default;
13197 titlescreen[i] = titlescreen_default;
13198 titlemessage_initial[i] = titlemessage_initial_default;
13199 titlemessage[i] = titlemessage_default;
13202 // special case: initialize "ARG_DEFAULT" values in static default config
13203 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13204 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13206 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13209 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13210 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13211 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13214 // special case: initialize "ARG_DEFAULT" values in static default config
13215 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13216 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13218 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13219 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13220 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13222 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13225 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13229 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13233 struct XY *dst, *src;
13235 game_buttons_xy[] =
13237 { &game.button.save, &game.button.stop },
13238 { &game.button.pause2, &game.button.pause },
13239 { &game.button.load, &game.button.play },
13240 { &game.button.undo, &game.button.stop },
13241 { &game.button.redo, &game.button.play },
13247 // special case: initialize later added SETUP list size from LEVELS value
13248 if (menu.list_size[GAME_MODE_SETUP] == -1)
13249 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13251 // set default position for snapshot buttons to stop/pause/play buttons
13252 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13253 if ((*game_buttons_xy[i].dst).x == -1 &&
13254 (*game_buttons_xy[i].dst).y == -1)
13255 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13257 // --------------------------------------------------------------------------
13258 // dynamic viewports (including playfield margins, borders and alignments)
13259 // --------------------------------------------------------------------------
13261 // dynamic viewports currently only supported for landscape mode
13262 int display_width = MAX(video.display_width, video.display_height);
13263 int display_height = MIN(video.display_width, video.display_height);
13265 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13267 struct RectWithBorder *vp_window = &viewport.window[i];
13268 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13269 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13270 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13271 boolean dynamic_window_width = (vp_window->min_width != -1);
13272 boolean dynamic_window_height = (vp_window->min_height != -1);
13273 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13274 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13276 // adjust window size if min/max width/height is specified
13278 if (vp_window->min_width != -1)
13280 int window_width = display_width;
13282 // when using static window height, use aspect ratio of display
13283 if (vp_window->min_height == -1)
13284 window_width = vp_window->height * display_width / display_height;
13286 vp_window->width = MAX(vp_window->min_width, window_width);
13289 if (vp_window->min_height != -1)
13291 int window_height = display_height;
13293 // when using static window width, use aspect ratio of display
13294 if (vp_window->min_width == -1)
13295 window_height = vp_window->width * display_height / display_width;
13297 vp_window->height = MAX(vp_window->min_height, window_height);
13300 if (vp_window->max_width != -1)
13301 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13303 if (vp_window->max_height != -1)
13304 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13306 int playfield_width = vp_window->width;
13307 int playfield_height = vp_window->height;
13309 // adjust playfield size and position according to specified margins
13311 playfield_width -= vp_playfield->margin_left;
13312 playfield_width -= vp_playfield->margin_right;
13314 playfield_height -= vp_playfield->margin_top;
13315 playfield_height -= vp_playfield->margin_bottom;
13317 // adjust playfield size if min/max width/height is specified
13319 if (vp_playfield->min_width != -1)
13320 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13322 if (vp_playfield->min_height != -1)
13323 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13325 if (vp_playfield->max_width != -1)
13326 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13328 if (vp_playfield->max_height != -1)
13329 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13331 // adjust playfield position according to specified alignment
13333 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13334 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13335 else if (vp_playfield->align == ALIGN_CENTER)
13336 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13337 else if (vp_playfield->align == ALIGN_RIGHT)
13338 vp_playfield->x += playfield_width - vp_playfield->width;
13340 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13341 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13342 else if (vp_playfield->valign == VALIGN_MIDDLE)
13343 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13344 else if (vp_playfield->valign == VALIGN_BOTTOM)
13345 vp_playfield->y += playfield_height - vp_playfield->height;
13347 vp_playfield->x += vp_playfield->margin_left;
13348 vp_playfield->y += vp_playfield->margin_top;
13350 // adjust individual playfield borders if only default border is specified
13352 if (vp_playfield->border_left == -1)
13353 vp_playfield->border_left = vp_playfield->border_size;
13354 if (vp_playfield->border_right == -1)
13355 vp_playfield->border_right = vp_playfield->border_size;
13356 if (vp_playfield->border_top == -1)
13357 vp_playfield->border_top = vp_playfield->border_size;
13358 if (vp_playfield->border_bottom == -1)
13359 vp_playfield->border_bottom = vp_playfield->border_size;
13361 // set dynamic playfield borders if borders are specified as undefined
13362 // (but only if window size was dynamic and playfield size was static)
13364 if (dynamic_window_width && !dynamic_playfield_width)
13366 if (vp_playfield->border_left == -1)
13368 vp_playfield->border_left = (vp_playfield->x -
13369 vp_playfield->margin_left);
13370 vp_playfield->x -= vp_playfield->border_left;
13371 vp_playfield->width += vp_playfield->border_left;
13374 if (vp_playfield->border_right == -1)
13376 vp_playfield->border_right = (vp_window->width -
13378 vp_playfield->width -
13379 vp_playfield->margin_right);
13380 vp_playfield->width += vp_playfield->border_right;
13384 if (dynamic_window_height && !dynamic_playfield_height)
13386 if (vp_playfield->border_top == -1)
13388 vp_playfield->border_top = (vp_playfield->y -
13389 vp_playfield->margin_top);
13390 vp_playfield->y -= vp_playfield->border_top;
13391 vp_playfield->height += vp_playfield->border_top;
13394 if (vp_playfield->border_bottom == -1)
13396 vp_playfield->border_bottom = (vp_window->height -
13398 vp_playfield->height -
13399 vp_playfield->margin_bottom);
13400 vp_playfield->height += vp_playfield->border_bottom;
13404 // adjust playfield size to be a multiple of a defined alignment tile size
13406 int align_size = vp_playfield->align_size;
13407 int playfield_xtiles = vp_playfield->width / align_size;
13408 int playfield_ytiles = vp_playfield->height / align_size;
13409 int playfield_width_corrected = playfield_xtiles * align_size;
13410 int playfield_height_corrected = playfield_ytiles * align_size;
13411 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13412 i == GFX_SPECIAL_ARG_EDITOR);
13414 if (is_playfield_mode &&
13415 dynamic_playfield_width &&
13416 vp_playfield->width != playfield_width_corrected)
13418 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13420 vp_playfield->width = playfield_width_corrected;
13422 if (vp_playfield->align == ALIGN_LEFT)
13424 vp_playfield->border_left += playfield_xdiff;
13426 else if (vp_playfield->align == ALIGN_RIGHT)
13428 vp_playfield->border_right += playfield_xdiff;
13430 else if (vp_playfield->align == ALIGN_CENTER)
13432 int border_left_diff = playfield_xdiff / 2;
13433 int border_right_diff = playfield_xdiff - border_left_diff;
13435 vp_playfield->border_left += border_left_diff;
13436 vp_playfield->border_right += border_right_diff;
13440 if (is_playfield_mode &&
13441 dynamic_playfield_height &&
13442 vp_playfield->height != playfield_height_corrected)
13444 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13446 vp_playfield->height = playfield_height_corrected;
13448 if (vp_playfield->valign == VALIGN_TOP)
13450 vp_playfield->border_top += playfield_ydiff;
13452 else if (vp_playfield->align == VALIGN_BOTTOM)
13454 vp_playfield->border_right += playfield_ydiff;
13456 else if (vp_playfield->align == VALIGN_MIDDLE)
13458 int border_top_diff = playfield_ydiff / 2;
13459 int border_bottom_diff = playfield_ydiff - border_top_diff;
13461 vp_playfield->border_top += border_top_diff;
13462 vp_playfield->border_bottom += border_bottom_diff;
13466 // adjust door positions according to specified alignment
13468 for (j = 0; j < 2; j++)
13470 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13472 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13473 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13474 else if (vp_door->align == ALIGN_CENTER)
13475 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13476 else if (vp_door->align == ALIGN_RIGHT)
13477 vp_door->x += vp_window->width - vp_door->width;
13479 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13480 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13481 else if (vp_door->valign == VALIGN_MIDDLE)
13482 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13483 else if (vp_door->valign == VALIGN_BOTTOM)
13484 vp_door->y += vp_window->height - vp_door->height;
13489 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13493 struct XYTileSize *dst, *src;
13496 editor_buttons_xy[] =
13499 &editor.button.element_left, &editor.palette.element_left,
13500 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13503 &editor.button.element_middle, &editor.palette.element_middle,
13504 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13507 &editor.button.element_right, &editor.palette.element_right,
13508 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13515 // set default position for element buttons to element graphics
13516 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13518 if ((*editor_buttons_xy[i].dst).x == -1 &&
13519 (*editor_buttons_xy[i].dst).y == -1)
13521 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13523 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13525 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13529 // adjust editor palette rows and columns if specified to be dynamic
13531 if (editor.palette.cols == -1)
13533 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13534 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13535 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13537 editor.palette.cols = (vp_width - sc_width) / bt_width;
13539 if (editor.palette.x == -1)
13541 int palette_width = editor.palette.cols * bt_width + sc_width;
13543 editor.palette.x = (vp_width - palette_width) / 2;
13547 if (editor.palette.rows == -1)
13549 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13550 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13551 int tx_height = getFontHeight(FONT_TEXT_2);
13553 editor.palette.rows = (vp_height - tx_height) / bt_height;
13555 if (editor.palette.y == -1)
13557 int palette_height = editor.palette.rows * bt_height + tx_height;
13559 editor.palette.y = (vp_height - palette_height) / 2;
13564 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13565 boolean initialize)
13567 // special case: check if network and preview player positions are redefined,
13568 // to compare this later against the main menu level preview being redefined
13569 struct TokenIntPtrInfo menu_config_players[] =
13571 { "main.network_players.x", &menu.main.network_players.redefined },
13572 { "main.network_players.y", &menu.main.network_players.redefined },
13573 { "main.preview_players.x", &menu.main.preview_players.redefined },
13574 { "main.preview_players.y", &menu.main.preview_players.redefined },
13575 { "preview.x", &preview.redefined },
13576 { "preview.y", &preview.redefined }
13582 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13583 *menu_config_players[i].value = FALSE;
13587 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13588 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13589 *menu_config_players[i].value = TRUE;
13593 static void InitMenuDesignSettings_PreviewPlayers(void)
13595 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13598 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13600 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13603 static void LoadMenuDesignSettingsFromFilename(char *filename)
13605 static struct TitleFadingInfo tfi;
13606 static struct TitleMessageInfo tmi;
13607 static struct TokenInfo title_tokens[] =
13609 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13610 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13611 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13612 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13613 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13617 static struct TokenInfo titlemessage_tokens[] =
13619 { TYPE_INTEGER, &tmi.x, ".x" },
13620 { TYPE_INTEGER, &tmi.y, ".y" },
13621 { TYPE_INTEGER, &tmi.width, ".width" },
13622 { TYPE_INTEGER, &tmi.height, ".height" },
13623 { TYPE_INTEGER, &tmi.chars, ".chars" },
13624 { TYPE_INTEGER, &tmi.lines, ".lines" },
13625 { TYPE_INTEGER, &tmi.align, ".align" },
13626 { TYPE_INTEGER, &tmi.valign, ".valign" },
13627 { TYPE_INTEGER, &tmi.font, ".font" },
13628 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13629 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13630 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13631 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13632 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13633 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13634 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13635 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13636 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13642 struct TitleFadingInfo *info;
13647 // initialize first titles from "enter screen" definitions, if defined
13648 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13649 { &title_first_default, "menu.enter_screen.TITLE" },
13651 // initialize title screens from "next screen" definitions, if defined
13652 { &title_initial_default, "menu.next_screen.TITLE" },
13653 { &title_default, "menu.next_screen.TITLE" },
13659 struct TitleMessageInfo *array;
13662 titlemessage_arrays[] =
13664 // initialize first titles from "enter screen" definitions, if defined
13665 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13666 { titlescreen_first, "menu.enter_screen.TITLE" },
13667 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13668 { titlemessage_first, "menu.enter_screen.TITLE" },
13670 // initialize titles from "next screen" definitions, if defined
13671 { titlescreen_initial, "menu.next_screen.TITLE" },
13672 { titlescreen, "menu.next_screen.TITLE" },
13673 { titlemessage_initial, "menu.next_screen.TITLE" },
13674 { titlemessage, "menu.next_screen.TITLE" },
13676 // overwrite titles with title definitions, if defined
13677 { titlescreen_initial_first, "[title_initial]" },
13678 { titlescreen_first, "[title]" },
13679 { titlemessage_initial_first, "[title_initial]" },
13680 { titlemessage_first, "[title]" },
13682 { titlescreen_initial, "[title_initial]" },
13683 { titlescreen, "[title]" },
13684 { titlemessage_initial, "[title_initial]" },
13685 { titlemessage, "[title]" },
13687 // overwrite titles with title screen/message definitions, if defined
13688 { titlescreen_initial_first, "[titlescreen_initial]" },
13689 { titlescreen_first, "[titlescreen]" },
13690 { titlemessage_initial_first, "[titlemessage_initial]" },
13691 { titlemessage_first, "[titlemessage]" },
13693 { titlescreen_initial, "[titlescreen_initial]" },
13694 { titlescreen, "[titlescreen]" },
13695 { titlemessage_initial, "[titlemessage_initial]" },
13696 { titlemessage, "[titlemessage]" },
13700 SetupFileHash *setup_file_hash;
13703 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13706 // the following initializes hierarchical values from dynamic configuration
13708 // special case: initialize with default values that may be overwritten
13709 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13710 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13712 struct TokenIntPtrInfo menu_config[] =
13714 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13715 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13716 { "menu.list_size", &menu.list_size[i] }
13719 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13721 char *token = menu_config[j].token;
13722 char *value = getHashEntry(setup_file_hash, token);
13725 *menu_config[j].value = get_integer_from_string(value);
13729 // special case: initialize with default values that may be overwritten
13730 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13731 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13733 struct TokenIntPtrInfo menu_config[] =
13735 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13736 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13737 { "menu.list_size.INFO", &menu.list_size_info[i] },
13738 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13739 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13742 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13744 char *token = menu_config[j].token;
13745 char *value = getHashEntry(setup_file_hash, token);
13748 *menu_config[j].value = get_integer_from_string(value);
13752 // special case: initialize with default values that may be overwritten
13753 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13754 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13756 struct TokenIntPtrInfo menu_config[] =
13758 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13759 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13762 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13764 char *token = menu_config[j].token;
13765 char *value = getHashEntry(setup_file_hash, token);
13768 *menu_config[j].value = get_integer_from_string(value);
13772 // special case: initialize with default values that may be overwritten
13773 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13774 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13776 struct TokenIntPtrInfo menu_config[] =
13778 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13779 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13780 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13781 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13782 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13783 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13784 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13785 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13786 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13787 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13790 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13792 char *token = menu_config[j].token;
13793 char *value = getHashEntry(setup_file_hash, token);
13796 *menu_config[j].value = get_integer_from_string(value);
13800 // special case: initialize with default values that may be overwritten
13801 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13802 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13804 struct TokenIntPtrInfo menu_config[] =
13806 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13807 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13808 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13809 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13810 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13811 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13812 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13813 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13814 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13817 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13819 char *token = menu_config[j].token;
13820 char *value = getHashEntry(setup_file_hash, token);
13823 *menu_config[j].value = get_token_parameter_value(token, value);
13827 // special case: initialize with default values that may be overwritten
13828 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13829 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13833 char *token_prefix;
13834 struct RectWithBorder *struct_ptr;
13838 { "viewport.window", &viewport.window[i] },
13839 { "viewport.playfield", &viewport.playfield[i] },
13840 { "viewport.door_1", &viewport.door_1[i] },
13841 { "viewport.door_2", &viewport.door_2[i] }
13844 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13846 struct TokenIntPtrInfo vp_config[] =
13848 { ".x", &vp_struct[j].struct_ptr->x },
13849 { ".y", &vp_struct[j].struct_ptr->y },
13850 { ".width", &vp_struct[j].struct_ptr->width },
13851 { ".height", &vp_struct[j].struct_ptr->height },
13852 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13853 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13854 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13855 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13856 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13857 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13858 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13859 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13860 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13861 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13862 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13863 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13864 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13865 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13866 { ".align", &vp_struct[j].struct_ptr->align },
13867 { ".valign", &vp_struct[j].struct_ptr->valign }
13870 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13872 char *token = getStringCat2(vp_struct[j].token_prefix,
13873 vp_config[k].token);
13874 char *value = getHashEntry(setup_file_hash, token);
13877 *vp_config[k].value = get_token_parameter_value(token, value);
13884 // special case: initialize with default values that may be overwritten
13885 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13886 for (i = 0; title_info[i].info != NULL; i++)
13888 struct TitleFadingInfo *info = title_info[i].info;
13889 char *base_token = title_info[i].text;
13891 for (j = 0; title_tokens[j].type != -1; j++)
13893 char *token = getStringCat2(base_token, title_tokens[j].text);
13894 char *value = getHashEntry(setup_file_hash, token);
13898 int parameter_value = get_token_parameter_value(token, value);
13902 *(int *)title_tokens[j].value = (int)parameter_value;
13911 // special case: initialize with default values that may be overwritten
13912 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13913 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13915 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13916 char *base_token = titlemessage_arrays[i].text;
13918 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13920 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13921 char *value = getHashEntry(setup_file_hash, token);
13925 int parameter_value = get_token_parameter_value(token, value);
13927 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13931 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13932 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13934 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13944 // read (and overwrite with) values that may be specified in config file
13945 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13947 // special case: check if network and preview player positions are redefined
13948 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13950 freeSetupFileHash(setup_file_hash);
13953 void LoadMenuDesignSettings(void)
13955 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13957 InitMenuDesignSettings_Static();
13958 InitMenuDesignSettings_SpecialPreProcessing();
13959 InitMenuDesignSettings_PreviewPlayers();
13961 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13963 // first look for special settings configured in level series config
13964 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13966 if (fileExists(filename_base))
13967 LoadMenuDesignSettingsFromFilename(filename_base);
13970 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13972 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13973 LoadMenuDesignSettingsFromFilename(filename_local);
13975 InitMenuDesignSettings_SpecialPostProcessing();
13978 void LoadMenuDesignSettings_AfterGraphics(void)
13980 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13983 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13984 boolean ignore_defaults)
13988 for (i = 0; sound_config_vars[i].token != NULL; i++)
13990 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13992 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13993 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13997 *sound_config_vars[i].value =
13998 get_token_parameter_value(sound_config_vars[i].token, value);
14002 void InitSoundSettings_Static(void)
14004 // always start with reliable default values from static default config
14005 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14008 static void LoadSoundSettingsFromFilename(char *filename)
14010 SetupFileHash *setup_file_hash;
14012 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14015 // read (and overwrite with) values that may be specified in config file
14016 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14018 freeSetupFileHash(setup_file_hash);
14021 void LoadSoundSettings(void)
14023 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14025 InitSoundSettings_Static();
14027 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14029 // first look for special settings configured in level series config
14030 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14032 if (fileExists(filename_base))
14033 LoadSoundSettingsFromFilename(filename_base);
14036 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14038 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14039 LoadSoundSettingsFromFilename(filename_local);
14042 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14044 char *filename = getEditorSetupFilename();
14045 SetupFileList *setup_file_list, *list;
14046 SetupFileHash *element_hash;
14047 int num_unknown_tokens = 0;
14050 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14053 element_hash = newSetupFileHash();
14055 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14056 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14058 // determined size may be larger than needed (due to unknown elements)
14060 for (list = setup_file_list; list != NULL; list = list->next)
14063 // add space for up to 3 more elements for padding that may be needed
14064 *num_elements += 3;
14066 // free memory for old list of elements, if needed
14067 checked_free(*elements);
14069 // allocate memory for new list of elements
14070 *elements = checked_malloc(*num_elements * sizeof(int));
14073 for (list = setup_file_list; list != NULL; list = list->next)
14075 char *value = getHashEntry(element_hash, list->token);
14077 if (value == NULL) // try to find obsolete token mapping
14079 char *mapped_token = get_mapped_token(list->token);
14081 if (mapped_token != NULL)
14083 value = getHashEntry(element_hash, mapped_token);
14085 free(mapped_token);
14091 (*elements)[(*num_elements)++] = atoi(value);
14095 if (num_unknown_tokens == 0)
14098 Warn("unknown token(s) found in config file:");
14099 Warn("- config file: '%s'", filename);
14101 num_unknown_tokens++;
14104 Warn("- token: '%s'", list->token);
14108 if (num_unknown_tokens > 0)
14111 while (*num_elements % 4) // pad with empty elements, if needed
14112 (*elements)[(*num_elements)++] = EL_EMPTY;
14114 freeSetupFileList(setup_file_list);
14115 freeSetupFileHash(element_hash);
14118 for (i = 0; i < *num_elements; i++)
14119 Debug("editor", "element '%s' [%d]\n",
14120 element_info[(*elements)[i]].token_name, (*elements)[i]);
14124 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14127 SetupFileHash *setup_file_hash = NULL;
14128 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14129 char *filename_music, *filename_prefix, *filename_info;
14135 token_to_value_ptr[] =
14137 { "title_header", &tmp_music_file_info.title_header },
14138 { "artist_header", &tmp_music_file_info.artist_header },
14139 { "album_header", &tmp_music_file_info.album_header },
14140 { "year_header", &tmp_music_file_info.year_header },
14141 { "played_header", &tmp_music_file_info.played_header },
14143 { "title", &tmp_music_file_info.title },
14144 { "artist", &tmp_music_file_info.artist },
14145 { "album", &tmp_music_file_info.album },
14146 { "year", &tmp_music_file_info.year },
14147 { "played", &tmp_music_file_info.played },
14153 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14154 getCustomMusicFilename(basename));
14156 if (filename_music == NULL)
14159 // ---------- try to replace file extension ----------
14161 filename_prefix = getStringCopy(filename_music);
14162 if (strrchr(filename_prefix, '.') != NULL)
14163 *strrchr(filename_prefix, '.') = '\0';
14164 filename_info = getStringCat2(filename_prefix, ".txt");
14166 if (fileExists(filename_info))
14167 setup_file_hash = loadSetupFileHash(filename_info);
14169 free(filename_prefix);
14170 free(filename_info);
14172 if (setup_file_hash == NULL)
14174 // ---------- try to add file extension ----------
14176 filename_prefix = getStringCopy(filename_music);
14177 filename_info = getStringCat2(filename_prefix, ".txt");
14179 if (fileExists(filename_info))
14180 setup_file_hash = loadSetupFileHash(filename_info);
14182 free(filename_prefix);
14183 free(filename_info);
14186 if (setup_file_hash == NULL)
14189 // ---------- music file info found ----------
14191 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14193 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14195 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14197 *token_to_value_ptr[i].value_ptr =
14198 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14201 tmp_music_file_info.basename = getStringCopy(basename);
14202 tmp_music_file_info.music = music;
14203 tmp_music_file_info.is_sound = is_sound;
14205 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14206 *new_music_file_info = tmp_music_file_info;
14208 return new_music_file_info;
14211 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14213 return get_music_file_info_ext(basename, music, FALSE);
14216 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14218 return get_music_file_info_ext(basename, sound, TRUE);
14221 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14222 char *basename, boolean is_sound)
14224 for (; list != NULL; list = list->next)
14225 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14231 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14233 return music_info_listed_ext(list, basename, FALSE);
14236 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14238 return music_info_listed_ext(list, basename, TRUE);
14241 void LoadMusicInfo(void)
14243 int num_music_noconf = getMusicListSize_NoConf();
14244 int num_music = getMusicListSize();
14245 int num_sounds = getSoundListSize();
14246 struct FileInfo *music, *sound;
14247 struct MusicFileInfo *next, **new;
14251 while (music_file_info != NULL)
14253 next = music_file_info->next;
14255 checked_free(music_file_info->basename);
14257 checked_free(music_file_info->title_header);
14258 checked_free(music_file_info->artist_header);
14259 checked_free(music_file_info->album_header);
14260 checked_free(music_file_info->year_header);
14261 checked_free(music_file_info->played_header);
14263 checked_free(music_file_info->title);
14264 checked_free(music_file_info->artist);
14265 checked_free(music_file_info->album);
14266 checked_free(music_file_info->year);
14267 checked_free(music_file_info->played);
14269 free(music_file_info);
14271 music_file_info = next;
14274 new = &music_file_info;
14276 // get (configured or unconfigured) music file info for all levels
14277 for (i = leveldir_current->first_level;
14278 i <= leveldir_current->last_level; i++)
14282 if (levelset.music[i] != MUS_UNDEFINED)
14284 // get music file info for configured level music
14285 music_nr = levelset.music[i];
14287 else if (num_music_noconf > 0)
14289 // get music file info for unconfigured level music
14290 int level_pos = i - leveldir_current->first_level;
14292 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14299 char *basename = getMusicInfoEntryFilename(music_nr);
14301 if (basename == NULL)
14304 if (!music_info_listed(music_file_info, basename))
14306 *new = get_music_file_info(basename, music_nr);
14309 new = &(*new)->next;
14313 // get music file info for all remaining configured music files
14314 for (i = 0; i < num_music; i++)
14316 music = getMusicListEntry(i);
14318 if (music->filename == NULL)
14321 if (strEqual(music->filename, UNDEFINED_FILENAME))
14324 // a configured file may be not recognized as music
14325 if (!FileIsMusic(music->filename))
14328 if (!music_info_listed(music_file_info, music->filename))
14330 *new = get_music_file_info(music->filename, i);
14333 new = &(*new)->next;
14337 // get sound file info for all configured sound files
14338 for (i = 0; i < num_sounds; i++)
14340 sound = getSoundListEntry(i);
14342 if (sound->filename == NULL)
14345 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14348 // a configured file may be not recognized as sound
14349 if (!FileIsSound(sound->filename))
14352 if (!sound_info_listed(music_file_info, sound->filename))
14354 *new = get_sound_file_info(sound->filename, i);
14356 new = &(*new)->next;
14360 // add pointers to previous list nodes
14362 struct MusicFileInfo *node = music_file_info;
14364 while (node != NULL)
14367 node->next->prev = node;
14373 static void add_helpanim_entry(int element, int action, int direction,
14374 int delay, int *num_list_entries)
14376 struct HelpAnimInfo *new_list_entry;
14377 (*num_list_entries)++;
14380 checked_realloc(helpanim_info,
14381 *num_list_entries * sizeof(struct HelpAnimInfo));
14382 new_list_entry = &helpanim_info[*num_list_entries - 1];
14384 new_list_entry->element = element;
14385 new_list_entry->action = action;
14386 new_list_entry->direction = direction;
14387 new_list_entry->delay = delay;
14390 static void print_unknown_token(char *filename, char *token, int token_nr)
14395 Warn("unknown token(s) found in config file:");
14396 Warn("- config file: '%s'", filename);
14399 Warn("- token: '%s'", token);
14402 static void print_unknown_token_end(int token_nr)
14408 void LoadHelpAnimInfo(void)
14410 char *filename = getHelpAnimFilename();
14411 SetupFileList *setup_file_list = NULL, *list;
14412 SetupFileHash *element_hash, *action_hash, *direction_hash;
14413 int num_list_entries = 0;
14414 int num_unknown_tokens = 0;
14417 if (fileExists(filename))
14418 setup_file_list = loadSetupFileList(filename);
14420 if (setup_file_list == NULL)
14422 // use reliable default values from static configuration
14423 SetupFileList *insert_ptr;
14425 insert_ptr = setup_file_list =
14426 newSetupFileList(helpanim_config[0].token,
14427 helpanim_config[0].value);
14429 for (i = 1; helpanim_config[i].token; i++)
14430 insert_ptr = addListEntry(insert_ptr,
14431 helpanim_config[i].token,
14432 helpanim_config[i].value);
14435 element_hash = newSetupFileHash();
14436 action_hash = newSetupFileHash();
14437 direction_hash = newSetupFileHash();
14439 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14440 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14442 for (i = 0; i < NUM_ACTIONS; i++)
14443 setHashEntry(action_hash, element_action_info[i].suffix,
14444 i_to_a(element_action_info[i].value));
14446 // do not store direction index (bit) here, but direction value!
14447 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14448 setHashEntry(direction_hash, element_direction_info[i].suffix,
14449 i_to_a(1 << element_direction_info[i].value));
14451 for (list = setup_file_list; list != NULL; list = list->next)
14453 char *element_token, *action_token, *direction_token;
14454 char *element_value, *action_value, *direction_value;
14455 int delay = atoi(list->value);
14457 if (strEqual(list->token, "end"))
14459 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14464 /* first try to break element into element/action/direction parts;
14465 if this does not work, also accept combined "element[.act][.dir]"
14466 elements (like "dynamite.active"), which are unique elements */
14468 if (strchr(list->token, '.') == NULL) // token contains no '.'
14470 element_value = getHashEntry(element_hash, list->token);
14471 if (element_value != NULL) // element found
14472 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14473 &num_list_entries);
14476 // no further suffixes found -- this is not an element
14477 print_unknown_token(filename, list->token, num_unknown_tokens++);
14483 // token has format "<prefix>.<something>"
14485 action_token = strchr(list->token, '.'); // suffix may be action ...
14486 direction_token = action_token; // ... or direction
14488 element_token = getStringCopy(list->token);
14489 *strchr(element_token, '.') = '\0';
14491 element_value = getHashEntry(element_hash, element_token);
14493 if (element_value == NULL) // this is no element
14495 element_value = getHashEntry(element_hash, list->token);
14496 if (element_value != NULL) // combined element found
14497 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14498 &num_list_entries);
14500 print_unknown_token(filename, list->token, num_unknown_tokens++);
14502 free(element_token);
14507 action_value = getHashEntry(action_hash, action_token);
14509 if (action_value != NULL) // action found
14511 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14512 &num_list_entries);
14514 free(element_token);
14519 direction_value = getHashEntry(direction_hash, direction_token);
14521 if (direction_value != NULL) // direction found
14523 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14524 &num_list_entries);
14526 free(element_token);
14531 if (strchr(action_token + 1, '.') == NULL)
14533 // no further suffixes found -- this is not an action nor direction
14535 element_value = getHashEntry(element_hash, list->token);
14536 if (element_value != NULL) // combined element found
14537 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14538 &num_list_entries);
14540 print_unknown_token(filename, list->token, num_unknown_tokens++);
14542 free(element_token);
14547 // token has format "<prefix>.<suffix>.<something>"
14549 direction_token = strchr(action_token + 1, '.');
14551 action_token = getStringCopy(action_token);
14552 *strchr(action_token + 1, '.') = '\0';
14554 action_value = getHashEntry(action_hash, action_token);
14556 if (action_value == NULL) // this is no action
14558 element_value = getHashEntry(element_hash, list->token);
14559 if (element_value != NULL) // combined element found
14560 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14561 &num_list_entries);
14563 print_unknown_token(filename, list->token, num_unknown_tokens++);
14565 free(element_token);
14566 free(action_token);
14571 direction_value = getHashEntry(direction_hash, direction_token);
14573 if (direction_value != NULL) // direction found
14575 add_helpanim_entry(atoi(element_value), atoi(action_value),
14576 atoi(direction_value), delay, &num_list_entries);
14578 free(element_token);
14579 free(action_token);
14584 // this is no direction
14586 element_value = getHashEntry(element_hash, list->token);
14587 if (element_value != NULL) // combined element found
14588 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14589 &num_list_entries);
14591 print_unknown_token(filename, list->token, num_unknown_tokens++);
14593 free(element_token);
14594 free(action_token);
14597 print_unknown_token_end(num_unknown_tokens);
14599 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14600 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14602 freeSetupFileList(setup_file_list);
14603 freeSetupFileHash(element_hash);
14604 freeSetupFileHash(action_hash);
14605 freeSetupFileHash(direction_hash);
14608 for (i = 0; i < num_list_entries; i++)
14609 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14610 EL_NAME(helpanim_info[i].element),
14611 helpanim_info[i].element,
14612 helpanim_info[i].action,
14613 helpanim_info[i].direction,
14614 helpanim_info[i].delay);
14618 void LoadHelpTextInfo(void)
14620 char *filename = getHelpTextFilename();
14623 if (helptext_info != NULL)
14625 freeSetupFileHash(helptext_info);
14626 helptext_info = NULL;
14629 if (fileExists(filename))
14630 helptext_info = loadSetupFileHash(filename);
14632 if (helptext_info == NULL)
14634 // use reliable default values from static configuration
14635 helptext_info = newSetupFileHash();
14637 for (i = 0; helptext_config[i].token; i++)
14638 setHashEntry(helptext_info,
14639 helptext_config[i].token,
14640 helptext_config[i].value);
14644 BEGIN_HASH_ITERATION(helptext_info, itr)
14646 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14647 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14649 END_HASH_ITERATION(hash, itr)
14654 // ----------------------------------------------------------------------------
14656 // ----------------------------------------------------------------------------
14658 #define MAX_NUM_CONVERT_LEVELS 1000
14660 void ConvertLevels(void)
14662 static LevelDirTree *convert_leveldir = NULL;
14663 static int convert_level_nr = -1;
14664 static int num_levels_handled = 0;
14665 static int num_levels_converted = 0;
14666 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14669 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14670 global.convert_leveldir);
14672 if (convert_leveldir == NULL)
14673 Fail("no such level identifier: '%s'", global.convert_leveldir);
14675 leveldir_current = convert_leveldir;
14677 if (global.convert_level_nr != -1)
14679 convert_leveldir->first_level = global.convert_level_nr;
14680 convert_leveldir->last_level = global.convert_level_nr;
14683 convert_level_nr = convert_leveldir->first_level;
14685 PrintLine("=", 79);
14686 Print("Converting levels\n");
14687 PrintLine("-", 79);
14688 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14689 Print("Level series name: '%s'\n", convert_leveldir->name);
14690 Print("Level series author: '%s'\n", convert_leveldir->author);
14691 Print("Number of levels: %d\n", convert_leveldir->levels);
14692 PrintLine("=", 79);
14695 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14696 levels_failed[i] = FALSE;
14698 while (convert_level_nr <= convert_leveldir->last_level)
14700 char *level_filename;
14703 level_nr = convert_level_nr++;
14705 Print("Level %03d: ", level_nr);
14707 LoadLevel(level_nr);
14708 if (level.no_level_file || level.no_valid_file)
14710 Print("(no level)\n");
14714 Print("converting level ... ");
14717 // special case: conversion of some EMC levels as requested by ACME
14718 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14721 level_filename = getDefaultLevelFilename(level_nr);
14722 new_level = !fileExists(level_filename);
14726 SaveLevel(level_nr);
14728 num_levels_converted++;
14730 Print("converted.\n");
14734 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14735 levels_failed[level_nr] = TRUE;
14737 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14740 num_levels_handled++;
14744 PrintLine("=", 79);
14745 Print("Number of levels handled: %d\n", num_levels_handled);
14746 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14747 (num_levels_handled ?
14748 num_levels_converted * 100 / num_levels_handled : 0));
14749 PrintLine("-", 79);
14750 Print("Summary (for automatic parsing by scripts):\n");
14751 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14752 convert_leveldir->identifier, num_levels_converted,
14753 num_levels_handled,
14754 (num_levels_handled ?
14755 num_levels_converted * 100 / num_levels_handled : 0));
14757 if (num_levels_handled != num_levels_converted)
14759 Print(", FAILED:");
14760 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14761 if (levels_failed[i])
14766 PrintLine("=", 79);
14768 CloseAllAndExit(0);
14772 // ----------------------------------------------------------------------------
14773 // create and save images for use in level sketches (raw BMP format)
14774 // ----------------------------------------------------------------------------
14776 void CreateLevelSketchImages(void)
14782 InitElementPropertiesGfxElement();
14784 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14785 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14787 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14789 int element = getMappedElement(i);
14790 char basename1[16];
14791 char basename2[16];
14795 sprintf(basename1, "%04d.bmp", i);
14796 sprintf(basename2, "%04ds.bmp", i);
14798 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14799 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14801 DrawSizedElement(0, 0, element, TILESIZE);
14802 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14804 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14805 Fail("cannot save level sketch image file '%s'", filename1);
14807 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14808 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14810 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14811 Fail("cannot save level sketch image file '%s'", filename2);
14816 // create corresponding SQL statements (for normal and small images)
14819 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14820 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14823 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14824 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14826 // optional: create content for forum level sketch demonstration post
14828 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14831 FreeBitmap(bitmap1);
14832 FreeBitmap(bitmap2);
14835 fprintf(stderr, "\n");
14837 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14839 CloseAllAndExit(0);
14843 // ----------------------------------------------------------------------------
14844 // create and save images for element collecting animations (raw BMP format)
14845 // ----------------------------------------------------------------------------
14847 static boolean createCollectImage(int element)
14849 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14852 void CreateCollectElementImages(void)
14856 int anim_frames = num_steps - 1;
14857 int tile_size = TILESIZE;
14858 int anim_width = tile_size * anim_frames;
14859 int anim_height = tile_size;
14860 int num_collect_images = 0;
14861 int pos_collect_images = 0;
14863 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14864 if (createCollectImage(i))
14865 num_collect_images++;
14867 Info("Creating %d element collecting animation images ...",
14868 num_collect_images);
14870 int dst_width = anim_width * 2;
14871 int dst_height = anim_height * num_collect_images / 2;
14872 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14873 char *basename_bmp = "RocksCollect.bmp";
14874 char *basename_png = "RocksCollect.png";
14875 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14876 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14877 int len_filename_bmp = strlen(filename_bmp);
14878 int len_filename_png = strlen(filename_png);
14879 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14880 char cmd_convert[max_command_len];
14882 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14886 // force using RGBA surface for destination bitmap
14887 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14888 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14890 dst_bitmap->surface =
14891 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14893 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14895 if (!createCollectImage(i))
14898 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14899 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14900 int graphic = el2img(i);
14901 char *token_name = element_info[i].token_name;
14902 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14903 Bitmap *src_bitmap;
14906 Info("- creating collecting image for '%s' ...", token_name);
14908 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14910 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14911 tile_size, tile_size, 0, 0);
14913 // force using RGBA surface for temporary bitmap (using transparent black)
14914 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14915 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14917 tmp_bitmap->surface =
14918 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14920 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14922 for (j = 0; j < anim_frames; j++)
14924 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14925 int frame_size = frame_size_final * num_steps;
14926 int offset = (tile_size - frame_size_final) / 2;
14927 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14929 while (frame_size > frame_size_final)
14933 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14935 FreeBitmap(frame_bitmap);
14937 frame_bitmap = half_bitmap;
14940 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14941 frame_size_final, frame_size_final,
14942 dst_x + j * tile_size + offset, dst_y + offset);
14944 FreeBitmap(frame_bitmap);
14947 tmp_bitmap->surface_masked = NULL;
14949 FreeBitmap(tmp_bitmap);
14951 pos_collect_images++;
14954 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14955 Fail("cannot save element collecting image file '%s'", filename_bmp);
14957 FreeBitmap(dst_bitmap);
14959 Info("Converting image file from BMP to PNG ...");
14961 if (system(cmd_convert) != 0)
14962 Fail("converting image file failed");
14964 unlink(filename_bmp);
14968 CloseAllAndExit(0);
14972 // ----------------------------------------------------------------------------
14973 // create and save images for custom and group elements (raw BMP format)
14974 // ----------------------------------------------------------------------------
14976 void CreateCustomElementImages(char *directory)
14978 char *src_basename = "RocksCE-template.ilbm";
14979 char *dst_basename = "RocksCE.bmp";
14980 char *src_filename = getPath2(directory, src_basename);
14981 char *dst_filename = getPath2(directory, dst_basename);
14982 Bitmap *src_bitmap;
14984 int yoffset_ce = 0;
14985 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14988 InitVideoDefaults();
14990 ReCreateBitmap(&backbuffer, video.width, video.height);
14992 src_bitmap = LoadImage(src_filename);
14994 bitmap = CreateBitmap(TILEX * 16 * 2,
14995 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14998 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15005 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15006 TILEX * x, TILEY * y + yoffset_ce);
15008 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15010 TILEX * x + TILEX * 16,
15011 TILEY * y + yoffset_ce);
15013 for (j = 2; j >= 0; j--)
15017 BlitBitmap(src_bitmap, bitmap,
15018 TILEX + c * 7, 0, 6, 10,
15019 TILEX * x + 6 + j * 7,
15020 TILEY * y + 11 + yoffset_ce);
15022 BlitBitmap(src_bitmap, bitmap,
15023 TILEX + c * 8, TILEY, 6, 10,
15024 TILEX * 16 + TILEX * x + 6 + j * 8,
15025 TILEY * y + 10 + yoffset_ce);
15031 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15038 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15039 TILEX * x, TILEY * y + yoffset_ge);
15041 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15043 TILEX * x + TILEX * 16,
15044 TILEY * y + yoffset_ge);
15046 for (j = 1; j >= 0; j--)
15050 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15051 TILEX * x + 6 + j * 10,
15052 TILEY * y + 11 + yoffset_ge);
15054 BlitBitmap(src_bitmap, bitmap,
15055 TILEX + c * 8, TILEY + 12, 6, 10,
15056 TILEX * 16 + TILEX * x + 10 + j * 8,
15057 TILEY * y + 10 + yoffset_ge);
15063 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15064 Fail("cannot save CE graphics file '%s'", dst_filename);
15066 FreeBitmap(bitmap);
15068 CloseAllAndExit(0);