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)
2337 boolean add_border = FALSE;
2340 int x2 = STD_LEV_FIELDX - 1;
2341 int y2 = STD_LEV_FIELDY - 1;
2344 li = *level; // copy level data into temporary buffer
2345 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2346 *level = li; // copy temporary buffer back to level data
2348 setLevelInfoToDefaults_BD();
2349 setLevelInfoToDefaults_EM();
2350 setLevelInfoToDefaults_SP();
2351 setLevelInfoToDefaults_MM();
2353 level->native_bd_level = &native_bd_level;
2354 level->native_em_level = &native_em_level;
2355 level->native_sp_level = &native_sp_level;
2356 level->native_mm_level = &native_mm_level;
2358 level->file_version = FILE_VERSION_ACTUAL;
2359 level->game_version = GAME_VERSION_ACTUAL;
2361 level->creation_date = getCurrentDate();
2363 level->encoding_16bit_field = TRUE;
2364 level->encoding_16bit_yamyam = TRUE;
2365 level->encoding_16bit_amoeba = TRUE;
2367 // clear level name and level author string buffers
2368 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2369 level->name[i] = '\0';
2370 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2371 level->author[i] = '\0';
2373 // set level name and level author to default values
2374 strcpy(level->name, NAMELESS_LEVEL_NAME);
2375 strcpy(level->author, ANONYMOUS_NAME);
2377 // set default game engine type
2378 level->game_engine_type = setup.default_game_engine_type;
2380 // some game engines should have a default playfield with border elements
2381 if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2382 level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2383 level->game_engine_type == GAME_ENGINE_TYPE_SP)
2392 // set level playfield to playable default level with player and exit
2393 for (x = 0; x < MAX_LEV_FIELDX; x++)
2395 for (y = 0; y < MAX_LEV_FIELDY; y++)
2397 if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2398 y == 0 || y == STD_LEV_FIELDY - 1))
2399 level->field[x][y] = getEngineElement(EL_STEELWALL);
2401 level->field[x][y] = getEngineElement(EL_SAND);
2405 level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2406 level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2408 BorderElement = getEngineElement(EL_STEELWALL);
2410 // detect custom elements when loading them
2411 level->file_has_custom_elements = FALSE;
2413 // set random colors for BD style levels according to preferred color type
2414 SetRandomLevelColors_BD(setup.bd_default_color_type);
2416 // set default color type and colors for BD style level colors
2417 SetDefaultLevelColorType_BD();
2418 SetDefaultLevelColors_BD();
2420 // set all bug compatibility flags to "false" => do not emulate this bug
2421 level->use_action_after_change_bug = FALSE;
2423 if (leveldir_current)
2425 // try to determine better author name than 'anonymous'
2426 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2428 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2429 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2433 switch (LEVELCLASS(leveldir_current))
2435 case LEVELCLASS_TUTORIAL:
2436 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2439 case LEVELCLASS_CONTRIB:
2440 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2441 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2444 case LEVELCLASS_PRIVATE:
2445 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2446 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2450 // keep default value
2457 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2459 static boolean clipboard_elements_initialized = FALSE;
2462 InitElementPropertiesStatic();
2464 li = *level; // copy level data into temporary buffer
2465 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2466 *level = li; // copy temporary buffer back to level data
2468 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2471 struct ElementInfo *ei = &element_info[element];
2473 if (element == EL_MM_GRAY_BALL)
2475 struct LevelInfo_MM *level_mm = level->native_mm_level;
2478 for (j = 0; j < level->num_mm_ball_contents; j++)
2479 level->mm_ball_content[j] =
2480 map_element_MM_to_RND(level_mm->ball_content[j]);
2483 // never initialize clipboard elements after the very first time
2484 // (to be able to use clipboard elements between several levels)
2485 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2488 if (IS_ENVELOPE(element))
2490 int envelope_nr = element - EL_ENVELOPE_1;
2492 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2494 level->envelope[envelope_nr] = xx_envelope;
2497 if (IS_CUSTOM_ELEMENT(element) ||
2498 IS_GROUP_ELEMENT(element) ||
2499 IS_INTERNAL_ELEMENT(element))
2501 xx_ei = *ei; // copy element data into temporary buffer
2503 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508 setElementChangePages(ei, 1);
2509 setElementChangeInfoToDefaults(ei->change);
2511 if (IS_CUSTOM_ELEMENT(element) ||
2512 IS_GROUP_ELEMENT(element))
2514 setElementDescriptionToDefault(ei);
2516 ei->modified_settings = FALSE;
2519 if (IS_CUSTOM_ELEMENT(element) ||
2520 IS_INTERNAL_ELEMENT(element))
2522 // internal values used in level editor
2524 ei->access_type = 0;
2525 ei->access_layer = 0;
2526 ei->access_protected = 0;
2527 ei->walk_to_action = 0;
2528 ei->smash_targets = 0;
2531 ei->can_explode_by_fire = FALSE;
2532 ei->can_explode_smashed = FALSE;
2533 ei->can_explode_impact = FALSE;
2535 ei->current_change_page = 0;
2538 if (IS_GROUP_ELEMENT(element) ||
2539 IS_INTERNAL_ELEMENT(element))
2541 struct ElementGroupInfo *group;
2543 // initialize memory for list of elements in group
2544 if (ei->group == NULL)
2545 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2549 xx_group = *group; // copy group data into temporary buffer
2551 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556 if (IS_EMPTY_ELEMENT(element) ||
2557 IS_INTERNAL_ELEMENT(element))
2559 xx_ei = *ei; // copy element data into temporary buffer
2561 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2567 clipboard_elements_initialized = TRUE;
2570 static void setLevelInfoToDefaults(struct LevelInfo *level,
2571 boolean level_info_only,
2572 boolean reset_file_status)
2574 setLevelInfoToDefaults_Level(level);
2576 if (!level_info_only)
2577 setLevelInfoToDefaults_Elements(level);
2579 if (reset_file_status)
2581 level->no_valid_file = FALSE;
2582 level->no_level_file = FALSE;
2585 level->changed = FALSE;
2588 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2590 level_file_info->nr = 0;
2591 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2592 level_file_info->packed = FALSE;
2594 setString(&level_file_info->basename, NULL);
2595 setString(&level_file_info->filename, NULL);
2598 int getMappedElement_SB(int, boolean);
2600 static void ActivateLevelTemplate(void)
2604 if (check_special_flags("load_xsb_to_ces"))
2606 // fill smaller playfields with padding "beyond border wall" elements
2607 if (level.fieldx < level_template.fieldx ||
2608 level.fieldy < level_template.fieldy)
2610 short field[level.fieldx][level.fieldy];
2611 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2612 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2613 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2614 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2616 // copy old playfield (which is smaller than the visible area)
2617 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2618 field[x][y] = level.field[x][y];
2620 // fill new, larger playfield with "beyond border wall" elements
2621 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2622 level.field[x][y] = getMappedElement_SB('_', TRUE);
2624 // copy the old playfield to the middle of the new playfield
2625 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2626 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2628 level.fieldx = new_fieldx;
2629 level.fieldy = new_fieldy;
2633 // Currently there is no special action needed to activate the template
2634 // data, because 'element_info' property settings overwrite the original
2635 // level data, while all other variables do not change.
2637 // Exception: 'from_level_template' elements in the original level playfield
2638 // are overwritten with the corresponding elements at the same position in
2639 // playfield from the level template.
2641 for (x = 0; x < level.fieldx; x++)
2642 for (y = 0; y < level.fieldy; y++)
2643 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2644 level.field[x][y] = level_template.field[x][y];
2646 if (check_special_flags("load_xsb_to_ces"))
2648 struct LevelInfo level_backup = level;
2650 // overwrite all individual level settings from template level settings
2651 level = level_template;
2653 // restore level file info
2654 level.file_info = level_backup.file_info;
2656 // restore playfield size
2657 level.fieldx = level_backup.fieldx;
2658 level.fieldy = level_backup.fieldy;
2660 // restore playfield content
2661 for (x = 0; x < level.fieldx; x++)
2662 for (y = 0; y < level.fieldy; y++)
2663 level.field[x][y] = level_backup.field[x][y];
2665 // restore name and author from individual level
2666 strcpy(level.name, level_backup.name);
2667 strcpy(level.author, level_backup.author);
2669 // restore flag "use_custom_template"
2670 level.use_custom_template = level_backup.use_custom_template;
2674 static boolean checkForPackageFromBasename_BD(char *basename)
2676 // check for native BD level file extensions
2677 if (!strSuffixLower(basename, ".bd") &&
2678 !strSuffixLower(basename, ".bdr") &&
2679 !strSuffixLower(basename, ".brc") &&
2680 !strSuffixLower(basename, ".gds"))
2683 // check for standard single-level BD files (like "001.bd")
2684 if (strSuffixLower(basename, ".bd") &&
2685 strlen(basename) == 6 &&
2686 basename[0] >= '0' && basename[0] <= '9' &&
2687 basename[1] >= '0' && basename[1] <= '9' &&
2688 basename[2] >= '0' && basename[2] <= '9')
2691 // this is a level package in native BD file format
2695 static char *getLevelFilenameFromBasename(char *basename)
2697 static char *filename = NULL;
2699 checked_free(filename);
2701 filename = getPath2(getCurrentLevelDir(), basename);
2706 static int getFileTypeFromBasename(char *basename)
2708 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2710 static char *filename = NULL;
2711 struct stat file_status;
2713 // ---------- try to determine file type from filename ----------
2715 // check for typical filename of a Supaplex level package file
2716 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2717 return LEVEL_FILE_TYPE_SP;
2719 // check for typical filename of a Diamond Caves II level package file
2720 if (strSuffixLower(basename, ".dc") ||
2721 strSuffixLower(basename, ".dc2"))
2722 return LEVEL_FILE_TYPE_DC;
2724 // check for typical filename of a Sokoban level package file
2725 if (strSuffixLower(basename, ".xsb") &&
2726 strchr(basename, '%') == NULL)
2727 return LEVEL_FILE_TYPE_SB;
2729 // check for typical filename of a Boulder Dash (GDash) level package file
2730 if (checkForPackageFromBasename_BD(basename))
2731 return LEVEL_FILE_TYPE_BD;
2733 // ---------- try to determine file type from filesize ----------
2735 checked_free(filename);
2736 filename = getPath2(getCurrentLevelDir(), basename);
2738 if (stat(filename, &file_status) == 0)
2740 // check for typical filesize of a Supaplex level package file
2741 if (file_status.st_size == 170496)
2742 return LEVEL_FILE_TYPE_SP;
2745 return LEVEL_FILE_TYPE_UNKNOWN;
2748 static int getFileTypeFromMagicBytes(char *filename, int type)
2752 if ((file = openFile(filename, MODE_READ)))
2754 char chunk_name[CHUNK_ID_LEN + 1];
2756 getFileChunkBE(file, chunk_name, NULL);
2758 if (strEqual(chunk_name, "MMII") ||
2759 strEqual(chunk_name, "MIRR"))
2760 type = LEVEL_FILE_TYPE_MM;
2768 static boolean checkForPackageFromBasename(char *basename)
2770 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2771 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2773 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2776 static char *getSingleLevelBasenameExt(int nr, char *extension)
2778 static char basename[MAX_FILENAME_LEN];
2781 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2783 sprintf(basename, "%03d.%s", nr, extension);
2788 static char *getSingleLevelBasename(int nr)
2790 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2793 static char *getPackedLevelBasename(int type)
2795 static char basename[MAX_FILENAME_LEN];
2796 char *directory = getCurrentLevelDir();
2798 DirectoryEntry *dir_entry;
2800 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2802 if ((dir = openDirectory(directory)) == NULL)
2804 Warn("cannot read current level directory '%s'", directory);
2809 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2811 char *entry_basename = dir_entry->basename;
2812 int entry_type = getFileTypeFromBasename(entry_basename);
2814 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2816 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2819 strcpy(basename, entry_basename);
2826 closeDirectory(dir);
2831 static char *getSingleLevelFilename(int nr)
2833 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2836 #if ENABLE_UNUSED_CODE
2837 static char *getPackedLevelFilename(int type)
2839 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2843 char *getDefaultLevelFilename(int nr)
2845 return getSingleLevelFilename(nr);
2848 #if ENABLE_UNUSED_CODE
2849 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2853 lfi->packed = FALSE;
2855 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2856 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2860 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2861 int type, char *format, ...)
2863 static char basename[MAX_FILENAME_LEN];
2866 va_start(ap, format);
2867 vsprintf(basename, format, ap);
2871 lfi->packed = FALSE;
2873 setString(&lfi->basename, basename);
2874 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2877 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2883 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2884 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2887 static int getFiletypeFromID(char *filetype_id)
2889 char *filetype_id_lower;
2890 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2893 if (filetype_id == NULL)
2894 return LEVEL_FILE_TYPE_UNKNOWN;
2896 filetype_id_lower = getStringToLower(filetype_id);
2898 for (i = 0; filetype_id_list[i].id != NULL; i++)
2900 char *id_lower = getStringToLower(filetype_id_list[i].id);
2902 if (strEqual(filetype_id_lower, id_lower))
2903 filetype = filetype_id_list[i].filetype;
2907 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2911 free(filetype_id_lower);
2916 char *getLocalLevelTemplateFilename(void)
2918 return getDefaultLevelFilename(-1);
2921 char *getGlobalLevelTemplateFilename(void)
2923 // global variable "leveldir_current" must be modified in the loop below
2924 LevelDirTree *leveldir_current_last = leveldir_current;
2925 char *filename = NULL;
2927 // check for template level in path from current to topmost tree node
2929 while (leveldir_current != NULL)
2931 filename = getDefaultLevelFilename(-1);
2933 if (fileExists(filename))
2936 leveldir_current = leveldir_current->node_parent;
2939 // restore global variable "leveldir_current" modified in above loop
2940 leveldir_current = leveldir_current_last;
2945 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2949 // special case: level number is negative => check for level template file
2952 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2953 getSingleLevelBasename(-1));
2955 // replace local level template filename with global template filename
2956 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2958 // no fallback if template file not existing
2962 // special case: check for file name/pattern specified in "levelinfo.conf"
2963 if (leveldir_current->level_filename != NULL)
2965 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2967 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2968 leveldir_current->level_filename, nr);
2970 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2972 if (fileExists(lfi->filename))
2975 else if (leveldir_current->level_filetype != NULL)
2977 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2979 // check for specified native level file with standard file name
2980 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2981 "%03d.%s", nr, LEVELFILE_EXTENSION);
2982 if (fileExists(lfi->filename))
2986 // check for native Rocks'n'Diamonds level file
2987 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2988 "%03d.%s", nr, LEVELFILE_EXTENSION);
2989 if (fileExists(lfi->filename))
2992 // check for native Boulder Dash level file
2993 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2994 if (fileExists(lfi->filename))
2997 // check for Emerald Mine level file (V1)
2998 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2999 'a' + (nr / 10) % 26, '0' + nr % 10);
3000 if (fileExists(lfi->filename))
3002 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3003 'A' + (nr / 10) % 26, '0' + nr % 10);
3004 if (fileExists(lfi->filename))
3007 // check for Emerald Mine level file (V2 to V5)
3008 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3009 if (fileExists(lfi->filename))
3012 // check for Emerald Mine level file (V6 / single mode)
3013 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3014 if (fileExists(lfi->filename))
3016 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3017 if (fileExists(lfi->filename))
3020 // check for Emerald Mine level file (V6 / teamwork mode)
3021 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3022 if (fileExists(lfi->filename))
3024 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3025 if (fileExists(lfi->filename))
3028 // check for various packed level file formats
3029 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3030 if (fileExists(lfi->filename))
3033 // no known level file found -- use default values (and fail later)
3034 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3035 "%03d.%s", nr, LEVELFILE_EXTENSION);
3038 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3040 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3041 lfi->type = getFileTypeFromBasename(lfi->basename);
3043 if (lfi->type == LEVEL_FILE_TYPE_RND)
3044 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3047 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3049 // always start with reliable default values
3050 setFileInfoToDefaults(level_file_info);
3052 level_file_info->nr = nr; // set requested level number
3054 determineLevelFileInfo_Filename(level_file_info);
3055 determineLevelFileInfo_Filetype(level_file_info);
3058 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3059 struct LevelFileInfo *lfi_to)
3061 lfi_to->nr = lfi_from->nr;
3062 lfi_to->type = lfi_from->type;
3063 lfi_to->packed = lfi_from->packed;
3065 setString(&lfi_to->basename, lfi_from->basename);
3066 setString(&lfi_to->filename, lfi_from->filename);
3069 // ----------------------------------------------------------------------------
3070 // functions for loading R'n'D level
3071 // ----------------------------------------------------------------------------
3073 int getMappedElement(int element)
3075 // remap some (historic, now obsolete) elements
3079 case EL_PLAYER_OBSOLETE:
3080 element = EL_PLAYER_1;
3083 case EL_KEY_OBSOLETE:
3087 case EL_EM_KEY_1_FILE_OBSOLETE:
3088 element = EL_EM_KEY_1;
3091 case EL_EM_KEY_2_FILE_OBSOLETE:
3092 element = EL_EM_KEY_2;
3095 case EL_EM_KEY_3_FILE_OBSOLETE:
3096 element = EL_EM_KEY_3;
3099 case EL_EM_KEY_4_FILE_OBSOLETE:
3100 element = EL_EM_KEY_4;
3103 case EL_ENVELOPE_OBSOLETE:
3104 element = EL_ENVELOPE_1;
3112 if (element >= NUM_FILE_ELEMENTS)
3114 Warn("invalid level element %d", element);
3116 element = EL_UNKNOWN;
3124 static int getMappedElementByVersion(int element, int game_version)
3126 // remap some elements due to certain game version
3128 if (game_version <= VERSION_IDENT(2,2,0,0))
3130 // map game font elements
3131 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3132 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3133 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3134 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3137 if (game_version < VERSION_IDENT(3,0,0,0))
3139 // map Supaplex gravity tube elements
3140 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3141 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3142 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3143 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3150 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3152 level->file_version = getFileVersion(file);
3153 level->game_version = getFileVersion(file);
3158 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3160 level->creation_date.year = getFile16BitBE(file);
3161 level->creation_date.month = getFile8Bit(file);
3162 level->creation_date.day = getFile8Bit(file);
3164 level->creation_date.src = DATE_SRC_LEVELFILE;
3169 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3171 int initial_player_stepsize;
3172 int initial_player_gravity;
3175 level->fieldx = getFile8Bit(file);
3176 level->fieldy = getFile8Bit(file);
3178 level->time = getFile16BitBE(file);
3179 level->gems_needed = getFile16BitBE(file);
3181 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3182 level->name[i] = getFile8Bit(file);
3183 level->name[MAX_LEVEL_NAME_LEN] = 0;
3185 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3186 level->score[i] = getFile8Bit(file);
3188 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3189 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3190 for (y = 0; y < 3; y++)
3191 for (x = 0; x < 3; x++)
3192 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3194 level->amoeba_speed = getFile8Bit(file);
3195 level->time_magic_wall = getFile8Bit(file);
3196 level->time_wheel = getFile8Bit(file);
3197 level->amoeba_content = getMappedElement(getFile8Bit(file));
3199 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3202 for (i = 0; i < MAX_PLAYERS; i++)
3203 level->initial_player_stepsize[i] = initial_player_stepsize;
3205 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3207 for (i = 0; i < MAX_PLAYERS; i++)
3208 level->initial_player_gravity[i] = initial_player_gravity;
3210 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3211 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3213 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3216 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3217 level->can_move_into_acid_bits = getFile32BitBE(file);
3218 level->dont_collide_with_bits = getFile8Bit(file);
3220 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3223 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3224 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3227 level->game_engine_type = getFile8Bit(file);
3229 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3234 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3238 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3239 level->name[i] = getFile8Bit(file);
3240 level->name[MAX_LEVEL_NAME_LEN] = 0;
3245 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3249 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3250 level->author[i] = getFile8Bit(file);
3251 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3256 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3259 int chunk_size_expected = level->fieldx * level->fieldy;
3261 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3262 stored with 16-bit encoding (and should be twice as big then).
3263 Even worse, playfield data was stored 16-bit when only yamyam content
3264 contained 16-bit elements and vice versa. */
3266 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3267 chunk_size_expected *= 2;
3269 if (chunk_size_expected != chunk_size)
3271 ReadUnusedBytesFromFile(file, chunk_size);
3272 return chunk_size_expected;
3275 for (y = 0; y < level->fieldy; y++)
3276 for (x = 0; x < level->fieldx; x++)
3277 level->field[x][y] =
3278 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3283 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3286 int header_size = 4;
3287 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3288 int chunk_size_expected = header_size + content_size;
3290 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3291 stored with 16-bit encoding (and should be twice as big then).
3292 Even worse, playfield data was stored 16-bit when only yamyam content
3293 contained 16-bit elements and vice versa. */
3295 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3296 chunk_size_expected += content_size;
3298 if (chunk_size_expected != chunk_size)
3300 ReadUnusedBytesFromFile(file, chunk_size);
3301 return chunk_size_expected;
3305 level->num_yamyam_contents = getFile8Bit(file);
3309 // correct invalid number of content fields -- should never happen
3310 if (level->num_yamyam_contents < 1 ||
3311 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3312 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3314 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3315 for (y = 0; y < 3; y++)
3316 for (x = 0; x < 3; x++)
3317 level->yamyam_content[i].e[x][y] =
3318 getMappedElement(level->encoding_16bit_field ?
3319 getFile16BitBE(file) : getFile8Bit(file));
3323 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3328 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3330 element = getMappedElement(getFile16BitBE(file));
3331 num_contents = getFile8Bit(file);
3333 getFile8Bit(file); // content x size (unused)
3334 getFile8Bit(file); // content y size (unused)
3336 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3338 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3339 for (y = 0; y < 3; y++)
3340 for (x = 0; x < 3; x++)
3341 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3343 // correct invalid number of content fields -- should never happen
3344 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3345 num_contents = STD_ELEMENT_CONTENTS;
3347 if (element == EL_YAMYAM)
3349 level->num_yamyam_contents = num_contents;
3351 for (i = 0; i < num_contents; i++)
3352 for (y = 0; y < 3; y++)
3353 for (x = 0; x < 3; x++)
3354 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3356 else if (element == EL_BD_AMOEBA)
3358 level->amoeba_content = content_array[0][0][0];
3362 Warn("cannot load content for element '%d'", element);
3368 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3374 int chunk_size_expected;
3376 element = getMappedElement(getFile16BitBE(file));
3377 if (!IS_ENVELOPE(element))
3378 element = EL_ENVELOPE_1;
3380 envelope_nr = element - EL_ENVELOPE_1;
3382 envelope_len = getFile16BitBE(file);
3384 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3385 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3387 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3389 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3390 if (chunk_size_expected != chunk_size)
3392 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3393 return chunk_size_expected;
3396 for (i = 0; i < envelope_len; i++)
3397 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3402 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3404 int num_changed_custom_elements = getFile16BitBE(file);
3405 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3408 if (chunk_size_expected != chunk_size)
3410 ReadUnusedBytesFromFile(file, chunk_size - 2);
3411 return chunk_size_expected;
3414 for (i = 0; i < num_changed_custom_elements; i++)
3416 int element = getMappedElement(getFile16BitBE(file));
3417 int properties = getFile32BitBE(file);
3419 if (IS_CUSTOM_ELEMENT(element))
3420 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3422 Warn("invalid custom element number %d", element);
3424 // older game versions that wrote level files with CUS1 chunks used
3425 // different default push delay values (not yet stored in level file)
3426 element_info[element].push_delay_fixed = 2;
3427 element_info[element].push_delay_random = 8;
3430 level->file_has_custom_elements = TRUE;
3435 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3437 int num_changed_custom_elements = getFile16BitBE(file);
3438 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3441 if (chunk_size_expected != chunk_size)
3443 ReadUnusedBytesFromFile(file, chunk_size - 2);
3444 return chunk_size_expected;
3447 for (i = 0; i < num_changed_custom_elements; i++)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int custom_target_element = getMappedElement(getFile16BitBE(file));
3452 if (IS_CUSTOM_ELEMENT(element))
3453 element_info[element].change->target_element = custom_target_element;
3455 Warn("invalid custom element number %d", element);
3458 level->file_has_custom_elements = TRUE;
3463 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3465 int num_changed_custom_elements = getFile16BitBE(file);
3466 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3469 if (chunk_size_expected != chunk_size)
3471 ReadUnusedBytesFromFile(file, chunk_size - 2);
3472 return chunk_size_expected;
3475 for (i = 0; i < num_changed_custom_elements; i++)
3477 int element = getMappedElement(getFile16BitBE(file));
3478 struct ElementInfo *ei = &element_info[element];
3479 unsigned int event_bits;
3481 if (!IS_CUSTOM_ELEMENT(element))
3483 Warn("invalid custom element number %d", element);
3485 element = EL_INTERNAL_DUMMY;
3488 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3489 ei->description[j] = getFile8Bit(file);
3490 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3492 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3494 // some free bytes for future properties and padding
3495 ReadUnusedBytesFromFile(file, 7);
3497 ei->use_gfx_element = getFile8Bit(file);
3498 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3500 ei->collect_score_initial = getFile8Bit(file);
3501 ei->collect_count_initial = getFile8Bit(file);
3503 ei->push_delay_fixed = getFile16BitBE(file);
3504 ei->push_delay_random = getFile16BitBE(file);
3505 ei->move_delay_fixed = getFile16BitBE(file);
3506 ei->move_delay_random = getFile16BitBE(file);
3508 ei->move_pattern = getFile16BitBE(file);
3509 ei->move_direction_initial = getFile8Bit(file);
3510 ei->move_stepsize = getFile8Bit(file);
3512 for (y = 0; y < 3; y++)
3513 for (x = 0; x < 3; x++)
3514 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3516 // bits 0 - 31 of "has_event[]"
3517 event_bits = getFile32BitBE(file);
3518 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3519 if (event_bits & (1u << j))
3520 ei->change->has_event[j] = TRUE;
3522 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3524 ei->change->delay_fixed = getFile16BitBE(file);
3525 ei->change->delay_random = getFile16BitBE(file);
3526 ei->change->delay_frames = getFile16BitBE(file);
3528 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3530 ei->change->explode = getFile8Bit(file);
3531 ei->change->use_target_content = getFile8Bit(file);
3532 ei->change->only_if_complete = getFile8Bit(file);
3533 ei->change->use_random_replace = getFile8Bit(file);
3535 ei->change->random_percentage = getFile8Bit(file);
3536 ei->change->replace_when = getFile8Bit(file);
3538 for (y = 0; y < 3; y++)
3539 for (x = 0; x < 3; x++)
3540 ei->change->target_content.e[x][y] =
3541 getMappedElement(getFile16BitBE(file));
3543 ei->slippery_type = getFile8Bit(file);
3545 // some free bytes for future properties and padding
3546 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3548 // mark that this custom element has been modified
3549 ei->modified_settings = TRUE;
3552 level->file_has_custom_elements = TRUE;
3557 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3559 struct ElementInfo *ei;
3560 int chunk_size_expected;
3564 // ---------- custom element base property values (96 bytes) ----------------
3566 element = getMappedElement(getFile16BitBE(file));
3568 if (!IS_CUSTOM_ELEMENT(element))
3570 Warn("invalid custom element number %d", element);
3572 ReadUnusedBytesFromFile(file, chunk_size - 2);
3577 ei = &element_info[element];
3579 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3580 ei->description[i] = getFile8Bit(file);
3581 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3583 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3585 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3587 ei->num_change_pages = getFile8Bit(file);
3589 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3590 if (chunk_size_expected != chunk_size)
3592 ReadUnusedBytesFromFile(file, chunk_size - 43);
3593 return chunk_size_expected;
3596 ei->ce_value_fixed_initial = getFile16BitBE(file);
3597 ei->ce_value_random_initial = getFile16BitBE(file);
3598 ei->use_last_ce_value = getFile8Bit(file);
3600 ei->use_gfx_element = getFile8Bit(file);
3601 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3603 ei->collect_score_initial = getFile8Bit(file);
3604 ei->collect_count_initial = getFile8Bit(file);
3606 ei->drop_delay_fixed = getFile8Bit(file);
3607 ei->push_delay_fixed = getFile8Bit(file);
3608 ei->drop_delay_random = getFile8Bit(file);
3609 ei->push_delay_random = getFile8Bit(file);
3610 ei->move_delay_fixed = getFile16BitBE(file);
3611 ei->move_delay_random = getFile16BitBE(file);
3613 // bits 0 - 15 of "move_pattern" ...
3614 ei->move_pattern = getFile16BitBE(file);
3615 ei->move_direction_initial = getFile8Bit(file);
3616 ei->move_stepsize = getFile8Bit(file);
3618 ei->slippery_type = getFile8Bit(file);
3620 for (y = 0; y < 3; y++)
3621 for (x = 0; x < 3; x++)
3622 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3624 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3625 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3626 ei->move_leave_type = getFile8Bit(file);
3628 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3629 ei->move_pattern |= (getFile16BitBE(file) << 16);
3631 ei->access_direction = getFile8Bit(file);
3633 ei->explosion_delay = getFile8Bit(file);
3634 ei->ignition_delay = getFile8Bit(file);
3635 ei->explosion_type = getFile8Bit(file);
3637 // some free bytes for future custom property values and padding
3638 ReadUnusedBytesFromFile(file, 1);
3640 // ---------- change page property values (48 bytes) ------------------------
3642 setElementChangePages(ei, ei->num_change_pages);
3644 for (i = 0; i < ei->num_change_pages; i++)
3646 struct ElementChangeInfo *change = &ei->change_page[i];
3647 unsigned int event_bits;
3649 // always start with reliable default values
3650 setElementChangeInfoToDefaults(change);
3652 // bits 0 - 31 of "has_event[]" ...
3653 event_bits = getFile32BitBE(file);
3654 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3655 if (event_bits & (1u << j))
3656 change->has_event[j] = TRUE;
3658 change->target_element = getMappedElement(getFile16BitBE(file));
3660 change->delay_fixed = getFile16BitBE(file);
3661 change->delay_random = getFile16BitBE(file);
3662 change->delay_frames = getFile16BitBE(file);
3664 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3666 change->explode = getFile8Bit(file);
3667 change->use_target_content = getFile8Bit(file);
3668 change->only_if_complete = getFile8Bit(file);
3669 change->use_random_replace = getFile8Bit(file);
3671 change->random_percentage = getFile8Bit(file);
3672 change->replace_when = getFile8Bit(file);
3674 for (y = 0; y < 3; y++)
3675 for (x = 0; x < 3; x++)
3676 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3678 change->can_change = getFile8Bit(file);
3680 change->trigger_side = getFile8Bit(file);
3682 change->trigger_player = getFile8Bit(file);
3683 change->trigger_page = getFile8Bit(file);
3685 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3686 CH_PAGE_ANY : (1 << change->trigger_page));
3688 change->has_action = getFile8Bit(file);
3689 change->action_type = getFile8Bit(file);
3690 change->action_mode = getFile8Bit(file);
3691 change->action_arg = getFile16BitBE(file);
3693 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3694 event_bits = getFile8Bit(file);
3695 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3696 if (event_bits & (1u << (j - 32)))
3697 change->has_event[j] = TRUE;
3700 // mark this custom element as modified
3701 ei->modified_settings = TRUE;
3703 level->file_has_custom_elements = TRUE;
3708 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3710 struct ElementInfo *ei;
3711 struct ElementGroupInfo *group;
3715 element = getMappedElement(getFile16BitBE(file));
3717 if (!IS_GROUP_ELEMENT(element))
3719 Warn("invalid group element number %d", element);
3721 ReadUnusedBytesFromFile(file, chunk_size - 2);
3726 ei = &element_info[element];
3728 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3729 ei->description[i] = getFile8Bit(file);
3730 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3732 group = element_info[element].group;
3734 group->num_elements = getFile8Bit(file);
3736 ei->use_gfx_element = getFile8Bit(file);
3737 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3739 group->choice_mode = getFile8Bit(file);
3741 // some free bytes for future values and padding
3742 ReadUnusedBytesFromFile(file, 3);
3744 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3745 group->element[i] = getMappedElement(getFile16BitBE(file));
3747 // mark this group element as modified
3748 element_info[element].modified_settings = TRUE;
3750 level->file_has_custom_elements = TRUE;
3755 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3756 int element, int real_element)
3758 int micro_chunk_size = 0;
3759 int conf_type = getFile8Bit(file);
3760 int byte_mask = conf_type & CONF_MASK_BYTES;
3761 boolean element_found = FALSE;
3764 micro_chunk_size += 1;
3766 if (byte_mask == CONF_MASK_MULTI_BYTES)
3768 int num_bytes = getFile16BitBE(file);
3769 byte *buffer = checked_malloc(num_bytes);
3771 ReadBytesFromFile(file, buffer, num_bytes);
3773 for (i = 0; conf[i].data_type != -1; i++)
3775 if (conf[i].element == element &&
3776 conf[i].conf_type == conf_type)
3778 int data_type = conf[i].data_type;
3779 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3780 int max_num_entities = conf[i].max_num_entities;
3782 if (num_entities > max_num_entities)
3784 Warn("truncating number of entities for element %d from %d to %d",
3785 element, num_entities, max_num_entities);
3787 num_entities = max_num_entities;
3790 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3791 data_type == TYPE_CONTENT_LIST))
3793 // for element and content lists, zero entities are not allowed
3794 Warn("found empty list of entities for element %d", element);
3796 // do not set "num_entities" here to prevent reading behind buffer
3798 *(int *)(conf[i].num_entities) = 1; // at least one is required
3802 *(int *)(conf[i].num_entities) = num_entities;
3805 element_found = TRUE;
3807 if (data_type == TYPE_STRING)
3809 char *string = (char *)(conf[i].value);
3812 for (j = 0; j < max_num_entities; j++)
3813 string[j] = (j < num_entities ? buffer[j] : '\0');
3815 else if (data_type == TYPE_ELEMENT_LIST)
3817 int *element_array = (int *)(conf[i].value);
3820 for (j = 0; j < num_entities; j++)
3822 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3824 else if (data_type == TYPE_CONTENT_LIST)
3826 struct Content *content= (struct Content *)(conf[i].value);
3829 for (c = 0; c < num_entities; c++)
3830 for (y = 0; y < 3; y++)
3831 for (x = 0; x < 3; x++)
3832 content[c].e[x][y] =
3833 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3836 element_found = FALSE;
3842 checked_free(buffer);
3844 micro_chunk_size += 2 + num_bytes;
3846 else // constant size configuration data (1, 2 or 4 bytes)
3848 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3849 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3850 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3852 for (i = 0; conf[i].data_type != -1; i++)
3854 if (conf[i].element == element &&
3855 conf[i].conf_type == conf_type)
3857 int data_type = conf[i].data_type;
3859 if (data_type == TYPE_ELEMENT)
3860 value = getMappedElement(value);
3862 if (data_type == TYPE_BOOLEAN)
3863 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3865 *(int *) (conf[i].value) = value;
3867 element_found = TRUE;
3873 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3878 char *error_conf_chunk_bytes =
3879 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3880 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3881 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3882 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3883 int error_element = real_element;
3885 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3886 error_conf_chunk_bytes, error_conf_chunk_token,
3887 error_element, EL_NAME(error_element));
3890 return micro_chunk_size;
3893 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3895 int real_chunk_size = 0;
3897 li = *level; // copy level data into temporary buffer
3899 while (!checkEndOfFile(file))
3901 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3903 if (real_chunk_size >= chunk_size)
3907 *level = li; // copy temporary buffer back to level data
3909 return real_chunk_size;
3912 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3914 int real_chunk_size = 0;
3916 li = *level; // copy level data into temporary buffer
3918 while (!checkEndOfFile(file))
3920 int element = getMappedElement(getFile16BitBE(file));
3922 real_chunk_size += 2;
3923 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3925 if (real_chunk_size >= chunk_size)
3929 *level = li; // copy temporary buffer back to level data
3931 return real_chunk_size;
3934 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3936 int real_chunk_size = 0;
3938 li = *level; // copy level data into temporary buffer
3940 while (!checkEndOfFile(file))
3942 int element = getMappedElement(getFile16BitBE(file));
3944 real_chunk_size += 2;
3945 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3947 if (real_chunk_size >= chunk_size)
3951 *level = li; // copy temporary buffer back to level data
3953 return real_chunk_size;
3956 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3958 int element = getMappedElement(getFile16BitBE(file));
3959 int envelope_nr = element - EL_ENVELOPE_1;
3960 int real_chunk_size = 2;
3962 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3964 while (!checkEndOfFile(file))
3966 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3969 if (real_chunk_size >= chunk_size)
3973 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3975 return real_chunk_size;
3978 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3980 int element = getMappedElement(getFile16BitBE(file));
3981 int real_chunk_size = 2;
3982 struct ElementInfo *ei = &element_info[element];
3985 xx_ei = *ei; // copy element data into temporary buffer
3987 xx_ei.num_change_pages = -1;
3989 while (!checkEndOfFile(file))
3991 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3993 if (xx_ei.num_change_pages != -1)
3996 if (real_chunk_size >= chunk_size)
4002 if (ei->num_change_pages == -1)
4004 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4007 ei->num_change_pages = 1;
4009 setElementChangePages(ei, 1);
4010 setElementChangeInfoToDefaults(ei->change);
4012 return real_chunk_size;
4015 // initialize number of change pages stored for this custom element
4016 setElementChangePages(ei, ei->num_change_pages);
4017 for (i = 0; i < ei->num_change_pages; i++)
4018 setElementChangeInfoToDefaults(&ei->change_page[i]);
4020 // start with reading properties for the first change page
4021 xx_current_change_page = 0;
4023 while (!checkEndOfFile(file))
4025 // level file might contain invalid change page number
4026 if (xx_current_change_page >= ei->num_change_pages)
4029 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4031 xx_change = *change; // copy change data into temporary buffer
4033 resetEventBits(); // reset bits; change page might have changed
4035 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4038 *change = xx_change;
4040 setEventFlagsFromEventBits(change);
4042 if (real_chunk_size >= chunk_size)
4046 level->file_has_custom_elements = TRUE;
4048 return real_chunk_size;
4051 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4053 int element = getMappedElement(getFile16BitBE(file));
4054 int real_chunk_size = 2;
4055 struct ElementInfo *ei = &element_info[element];
4056 struct ElementGroupInfo *group = ei->group;
4061 xx_ei = *ei; // copy element data into temporary buffer
4062 xx_group = *group; // copy group data into temporary buffer
4064 while (!checkEndOfFile(file))
4066 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4069 if (real_chunk_size >= chunk_size)
4076 level->file_has_custom_elements = TRUE;
4078 return real_chunk_size;
4081 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4083 int element = getMappedElement(getFile16BitBE(file));
4084 int real_chunk_size = 2;
4085 struct ElementInfo *ei = &element_info[element];
4087 xx_ei = *ei; // copy element data into temporary buffer
4089 while (!checkEndOfFile(file))
4091 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4094 if (real_chunk_size >= chunk_size)
4100 level->file_has_custom_elements = TRUE;
4102 return real_chunk_size;
4105 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4106 struct LevelFileInfo *level_file_info,
4107 boolean level_info_only)
4109 char *filename = level_file_info->filename;
4110 char cookie[MAX_LINE_LEN];
4111 char chunk_name[CHUNK_ID_LEN + 1];
4115 if (!(file = openFile(filename, MODE_READ)))
4117 level->no_valid_file = TRUE;
4118 level->no_level_file = TRUE;
4120 if (level_info_only)
4123 Warn("cannot read level '%s' -- using empty level", filename);
4125 if (!setup.editor.use_template_for_new_levels)
4128 // if level file not found, try to initialize level data from template
4129 filename = getGlobalLevelTemplateFilename();
4131 if (!(file = openFile(filename, MODE_READ)))
4134 // default: for empty levels, use level template for custom elements
4135 level->use_custom_template = TRUE;
4137 level->no_valid_file = FALSE;
4140 getFileChunkBE(file, chunk_name, NULL);
4141 if (strEqual(chunk_name, "RND1"))
4143 getFile32BitBE(file); // not used
4145 getFileChunkBE(file, chunk_name, NULL);
4146 if (!strEqual(chunk_name, "CAVE"))
4148 level->no_valid_file = TRUE;
4150 Warn("unknown format of level file '%s'", filename);
4157 else // check for pre-2.0 file format with cookie string
4159 strcpy(cookie, chunk_name);
4160 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4162 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4163 cookie[strlen(cookie) - 1] = '\0';
4165 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4167 level->no_valid_file = TRUE;
4169 Warn("unknown format of level file '%s'", filename);
4176 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4178 level->no_valid_file = TRUE;
4180 Warn("unsupported version of level file '%s'", filename);
4187 // pre-2.0 level files have no game version, so use file version here
4188 level->game_version = level->file_version;
4191 if (level->file_version < FILE_VERSION_1_2)
4193 // level files from versions before 1.2.0 without chunk structure
4194 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4195 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4203 int (*loader)(File *, int, struct LevelInfo *);
4207 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4208 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4209 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4210 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4211 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4212 { "INFO", -1, LoadLevel_INFO },
4213 { "BODY", -1, LoadLevel_BODY },
4214 { "CONT", -1, LoadLevel_CONT },
4215 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4216 { "CNT3", -1, LoadLevel_CNT3 },
4217 { "CUS1", -1, LoadLevel_CUS1 },
4218 { "CUS2", -1, LoadLevel_CUS2 },
4219 { "CUS3", -1, LoadLevel_CUS3 },
4220 { "CUS4", -1, LoadLevel_CUS4 },
4221 { "GRP1", -1, LoadLevel_GRP1 },
4222 { "CONF", -1, LoadLevel_CONF },
4223 { "ELEM", -1, LoadLevel_ELEM },
4224 { "NOTE", -1, LoadLevel_NOTE },
4225 { "CUSX", -1, LoadLevel_CUSX },
4226 { "GRPX", -1, LoadLevel_GRPX },
4227 { "EMPX", -1, LoadLevel_EMPX },
4232 while (getFileChunkBE(file, chunk_name, &chunk_size))
4236 while (chunk_info[i].name != NULL &&
4237 !strEqual(chunk_name, chunk_info[i].name))
4240 if (chunk_info[i].name == NULL)
4242 Warn("unknown chunk '%s' in level file '%s'",
4243 chunk_name, filename);
4245 ReadUnusedBytesFromFile(file, chunk_size);
4247 else if (chunk_info[i].size != -1 &&
4248 chunk_info[i].size != chunk_size)
4250 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4251 chunk_size, chunk_name, filename);
4253 ReadUnusedBytesFromFile(file, chunk_size);
4257 // call function to load this level chunk
4258 int chunk_size_expected =
4259 (chunk_info[i].loader)(file, chunk_size, level);
4261 if (chunk_size_expected < 0)
4263 Warn("error reading chunk '%s' in level file '%s'",
4264 chunk_name, filename);
4269 // the size of some chunks cannot be checked before reading other
4270 // chunks first (like "HEAD" and "BODY") that contain some header
4271 // information, so check them here
4272 if (chunk_size_expected != chunk_size)
4274 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4275 chunk_size, chunk_name, filename);
4287 // ----------------------------------------------------------------------------
4288 // functions for loading BD level
4289 // ----------------------------------------------------------------------------
4291 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4292 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4294 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4296 struct LevelInfo_BD *level_bd = level->native_bd_level;
4297 GdCave *cave = NULL; // will be changed below
4298 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4299 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4302 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4304 // cave and map newly allocated when set to defaults above
4305 cave = level_bd->cave;
4308 cave->intermission = level->bd_intermission;
4311 cave->level_time[0] = level->time;
4312 cave->level_diamonds[0] = level->gems_needed;
4315 cave->scheduling = level->bd_scheduling_type;
4316 cave->pal_timing = level->bd_pal_timing;
4317 cave->level_speed[0] = level->bd_cycle_delay_ms;
4318 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4319 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4320 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4323 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4324 cave->diamond_value = level->score[SC_EMERALD];
4325 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4327 // compatibility settings
4328 cave->lineshift = level->bd_line_shifting_borders;
4329 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4330 cave->short_explosions = level->bd_short_explosions;
4332 // player properties
4333 cave->diagonal_movements = level->bd_diagonal_movements;
4334 cave->active_is_first_found = level->bd_topmost_player_active;
4335 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4336 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4337 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4338 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4340 // element properties
4341 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4342 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4343 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4344 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4345 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4346 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4347 cave->level_magic_wall_time[0] = level->time_magic_wall;
4348 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4349 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4350 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4351 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4353 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4354 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4355 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4356 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4357 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4358 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4359 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4361 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4362 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4363 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4364 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4365 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4366 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4367 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4368 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4369 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4370 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4371 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4373 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4374 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4375 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4376 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4377 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4378 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4380 cave->slime_predictable = level->bd_slime_is_predictable;
4381 cave->slime_correct_random = level->bd_slime_correct_random;
4382 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4383 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4384 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4385 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4386 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4387 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4388 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4389 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4390 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4391 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4393 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4394 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4395 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4397 cave->biter_delay_frame = level->bd_biter_move_delay;
4398 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4400 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4402 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4404 cave->replicators_active = level->bd_replicators_active;
4405 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4407 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4408 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4410 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4412 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4414 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4415 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4416 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4418 cave->infinite_rockets = level->bd_infinite_rockets;
4420 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4421 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4423 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4424 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4426 cave->creatures_backwards = level->bd_creatures_start_backwards;
4427 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4428 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4430 cave->gravity = level->bd_gravity_direction;
4431 cave->gravity_switch_active = level->bd_gravity_switch_active;
4432 cave->gravity_change_time = level->bd_gravity_switch_delay;
4433 cave->gravity_affects_all = level->bd_gravity_affects_all;
4435 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4436 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4437 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4438 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4440 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4441 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4442 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4443 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4444 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4445 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4447 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4448 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4449 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4450 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4452 cave->colorb = level->bd_color_b;
4453 cave->color0 = level->bd_color_0;
4454 cave->color1 = level->bd_color_1;
4455 cave->color2 = level->bd_color_2;
4456 cave->color3 = level->bd_color_3;
4457 cave->color4 = level->bd_color_4;
4458 cave->color5 = level->bd_color_5;
4461 strncpy(cave->name, level->name, sizeof(GdString));
4462 cave->name[sizeof(GdString) - 1] = '\0';
4464 // playfield elements
4465 for (x = 0; x < cave->w; x++)
4466 for (y = 0; y < cave->h; y++)
4467 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4470 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4472 struct LevelInfo_BD *level_bd = level->native_bd_level;
4473 GdCave *cave = level_bd->cave;
4474 int bd_level_nr = level_bd->level_nr;
4477 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4478 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4481 level->bd_intermission = cave->intermission;
4484 level->time = cave->level_time[bd_level_nr];
4485 level->gems_needed = cave->level_diamonds[bd_level_nr];
4488 level->bd_scheduling_type = cave->scheduling;
4489 level->bd_pal_timing = cave->pal_timing;
4490 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4491 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4492 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4493 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4496 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4497 level->score[SC_EMERALD] = cave->diamond_value;
4498 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4500 // compatibility settings
4501 level->bd_line_shifting_borders = cave->lineshift;
4502 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4503 level->bd_short_explosions = cave->short_explosions;
4505 // player properties
4506 level->bd_diagonal_movements = cave->diagonal_movements;
4507 level->bd_topmost_player_active = cave->active_is_first_found;
4508 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4509 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4510 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4511 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4513 // element properties
4514 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4515 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4516 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4517 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4518 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4519 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4520 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4521 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4522 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4523 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4524 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4526 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4527 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4528 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4529 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4530 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4531 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4532 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4534 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4535 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4536 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4537 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4538 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4539 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4540 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4541 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4542 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4543 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4544 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4546 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4547 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4548 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4549 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4550 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4551 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4553 level->bd_slime_is_predictable = cave->slime_predictable;
4554 level->bd_slime_correct_random = cave->slime_correct_random;
4555 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4556 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4557 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4558 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4559 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4560 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4561 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4562 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4563 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4564 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4566 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4567 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4568 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4570 level->bd_biter_move_delay = cave->biter_delay_frame;
4571 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4573 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4575 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4577 level->bd_replicators_active = cave->replicators_active;
4578 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4580 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4581 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4583 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4585 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4587 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4588 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4589 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4591 level->bd_infinite_rockets = cave->infinite_rockets;
4593 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4594 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4596 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4597 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4599 level->bd_creatures_start_backwards = cave->creatures_backwards;
4600 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4601 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4603 level->bd_gravity_direction = cave->gravity;
4604 level->bd_gravity_switch_active = cave->gravity_switch_active;
4605 level->bd_gravity_switch_delay = cave->gravity_change_time;
4606 level->bd_gravity_affects_all = cave->gravity_affects_all;
4608 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4609 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4610 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4611 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4613 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4614 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4615 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4616 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4617 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4618 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4620 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4621 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4622 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4623 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4625 level->bd_color_b = cave->colorb;
4626 level->bd_color_0 = cave->color0;
4627 level->bd_color_1 = cave->color1;
4628 level->bd_color_2 = cave->color2;
4629 level->bd_color_3 = cave->color3;
4630 level->bd_color_4 = cave->color4;
4631 level->bd_color_5 = cave->color5;
4633 // set default color type and colors for BD style level colors
4634 SetDefaultLevelColorType_BD();
4635 SetDefaultLevelColors_BD();
4638 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4640 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4641 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4643 // playfield elements
4644 for (x = 0; x < level->fieldx; x++)
4645 for (y = 0; y < level->fieldy; y++)
4646 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4648 checked_free(cave_name);
4651 static void setTapeInfoToDefaults(void);
4653 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4655 struct LevelInfo_BD *level_bd = level->native_bd_level;
4656 GdCave *cave = level_bd->cave;
4657 GdReplay *replay = level_bd->replay;
4663 // always start with reliable default values
4664 setTapeInfoToDefaults();
4666 tape.level_nr = level_nr; // (currently not used)
4667 tape.random_seed = replay->seed;
4669 TapeSetDateFromIsoDateString(replay->date);
4672 tape.pos[tape.counter].delay = 0;
4674 tape.bd_replay = TRUE;
4676 // all time calculations only used to display approximate tape time
4677 int cave_speed = cave->speed;
4678 int milliseconds_game = 0;
4679 int milliseconds_elapsed = 20;
4681 for (i = 0; i < replay->movements->len; i++)
4683 int replay_action = replay->movements->data[i];
4684 int tape_action = map_action_BD_to_RND(replay_action);
4685 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4686 boolean success = 0;
4690 success = TapeAddAction(action);
4692 milliseconds_game += milliseconds_elapsed;
4694 if (milliseconds_game >= cave_speed)
4696 milliseconds_game -= cave_speed;
4703 tape.pos[tape.counter].delay = 0;
4704 tape.pos[tape.counter].action[0] = 0;
4708 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4714 TapeHaltRecording();
4716 if (!replay->success)
4717 Warn("BD replay is marked as not successful");
4721 // ----------------------------------------------------------------------------
4722 // functions for loading EM level
4723 // ----------------------------------------------------------------------------
4725 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4727 static int ball_xy[8][2] =
4738 struct LevelInfo_EM *level_em = level->native_em_level;
4739 struct CAVE *cav = level_em->cav;
4742 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4743 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4745 cav->time_seconds = level->time;
4746 cav->gems_needed = level->gems_needed;
4748 cav->emerald_score = level->score[SC_EMERALD];
4749 cav->diamond_score = level->score[SC_DIAMOND];
4750 cav->alien_score = level->score[SC_ROBOT];
4751 cav->tank_score = level->score[SC_SPACESHIP];
4752 cav->bug_score = level->score[SC_BUG];
4753 cav->eater_score = level->score[SC_YAMYAM];
4754 cav->nut_score = level->score[SC_NUT];
4755 cav->dynamite_score = level->score[SC_DYNAMITE];
4756 cav->key_score = level->score[SC_KEY];
4757 cav->exit_score = level->score[SC_TIME_BONUS];
4759 cav->num_eater_arrays = level->num_yamyam_contents;
4761 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4762 for (y = 0; y < 3; y++)
4763 for (x = 0; x < 3; x++)
4764 cav->eater_array[i][y * 3 + x] =
4765 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4767 cav->amoeba_time = level->amoeba_speed;
4768 cav->wonderwall_time = level->time_magic_wall;
4769 cav->wheel_time = level->time_wheel;
4771 cav->android_move_time = level->android_move_time;
4772 cav->android_clone_time = level->android_clone_time;
4773 cav->ball_random = level->ball_random;
4774 cav->ball_active = level->ball_active_initial;
4775 cav->ball_time = level->ball_time;
4776 cav->num_ball_arrays = level->num_ball_contents;
4778 cav->lenses_score = level->lenses_score;
4779 cav->magnify_score = level->magnify_score;
4780 cav->slurp_score = level->slurp_score;
4782 cav->lenses_time = level->lenses_time;
4783 cav->magnify_time = level->magnify_time;
4785 cav->wind_time = 9999;
4786 cav->wind_direction =
4787 map_direction_RND_to_EM(level->wind_direction_initial);
4789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4790 for (j = 0; j < 8; j++)
4791 cav->ball_array[i][j] =
4792 map_element_RND_to_EM_cave(level->ball_content[i].
4793 e[ball_xy[j][0]][ball_xy[j][1]]);
4795 map_android_clone_elements_RND_to_EM(level);
4797 // first fill the complete playfield with the empty space element
4798 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4799 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4800 cav->cave[x][y] = Cblank;
4802 // then copy the real level contents from level file into the playfield
4803 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4805 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4807 if (level->field[x][y] == EL_AMOEBA_DEAD)
4808 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4810 cav->cave[x][y] = new_element;
4813 for (i = 0; i < MAX_PLAYERS; i++)
4815 cav->player_x[i] = -1;
4816 cav->player_y[i] = -1;
4819 // initialize player positions and delete players from the playfield
4820 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4822 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4824 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4826 cav->player_x[player_nr] = x;
4827 cav->player_y[player_nr] = y;
4829 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4834 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4836 static int ball_xy[8][2] =
4847 struct LevelInfo_EM *level_em = level->native_em_level;
4848 struct CAVE *cav = level_em->cav;
4851 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4852 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4854 level->time = cav->time_seconds;
4855 level->gems_needed = cav->gems_needed;
4857 sprintf(level->name, "Level %d", level->file_info.nr);
4859 level->score[SC_EMERALD] = cav->emerald_score;
4860 level->score[SC_DIAMOND] = cav->diamond_score;
4861 level->score[SC_ROBOT] = cav->alien_score;
4862 level->score[SC_SPACESHIP] = cav->tank_score;
4863 level->score[SC_BUG] = cav->bug_score;
4864 level->score[SC_YAMYAM] = cav->eater_score;
4865 level->score[SC_NUT] = cav->nut_score;
4866 level->score[SC_DYNAMITE] = cav->dynamite_score;
4867 level->score[SC_KEY] = cav->key_score;
4868 level->score[SC_TIME_BONUS] = cav->exit_score;
4870 level->num_yamyam_contents = cav->num_eater_arrays;
4872 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4873 for (y = 0; y < 3; y++)
4874 for (x = 0; x < 3; x++)
4875 level->yamyam_content[i].e[x][y] =
4876 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4878 level->amoeba_speed = cav->amoeba_time;
4879 level->time_magic_wall = cav->wonderwall_time;
4880 level->time_wheel = cav->wheel_time;
4882 level->android_move_time = cav->android_move_time;
4883 level->android_clone_time = cav->android_clone_time;
4884 level->ball_random = cav->ball_random;
4885 level->ball_active_initial = cav->ball_active;
4886 level->ball_time = cav->ball_time;
4887 level->num_ball_contents = cav->num_ball_arrays;
4889 level->lenses_score = cav->lenses_score;
4890 level->magnify_score = cav->magnify_score;
4891 level->slurp_score = cav->slurp_score;
4893 level->lenses_time = cav->lenses_time;
4894 level->magnify_time = cav->magnify_time;
4896 level->wind_direction_initial =
4897 map_direction_EM_to_RND(cav->wind_direction);
4899 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4900 for (j = 0; j < 8; j++)
4901 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4902 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4904 map_android_clone_elements_EM_to_RND(level);
4906 // convert the playfield (some elements need special treatment)
4907 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4909 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4911 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4912 new_element = EL_AMOEBA_DEAD;
4914 level->field[x][y] = new_element;
4917 for (i = 0; i < MAX_PLAYERS; i++)
4919 // in case of all players set to the same field, use the first player
4920 int nr = MAX_PLAYERS - i - 1;
4921 int jx = cav->player_x[nr];
4922 int jy = cav->player_y[nr];
4924 if (jx != -1 && jy != -1)
4925 level->field[jx][jy] = EL_PLAYER_1 + nr;
4928 // time score is counted for each 10 seconds left in Emerald Mine levels
4929 level->time_score_base = 10;
4933 // ----------------------------------------------------------------------------
4934 // functions for loading SP level
4935 // ----------------------------------------------------------------------------
4937 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4939 struct LevelInfo_SP *level_sp = level->native_sp_level;
4940 LevelInfoType *header = &level_sp->header;
4943 level_sp->width = level->fieldx;
4944 level_sp->height = level->fieldy;
4946 for (x = 0; x < level->fieldx; x++)
4947 for (y = 0; y < level->fieldy; y++)
4948 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4950 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4952 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4953 header->LevelTitle[i] = level->name[i];
4954 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4956 header->InfotronsNeeded = level->gems_needed;
4958 header->SpecialPortCount = 0;
4960 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4962 boolean gravity_port_found = FALSE;
4963 boolean gravity_port_valid = FALSE;
4964 int gravity_port_flag;
4965 int gravity_port_base_element;
4966 int element = level->field[x][y];
4968 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4969 element <= EL_SP_GRAVITY_ON_PORT_UP)
4971 gravity_port_found = TRUE;
4972 gravity_port_valid = TRUE;
4973 gravity_port_flag = 1;
4974 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4976 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4977 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4979 gravity_port_found = TRUE;
4980 gravity_port_valid = TRUE;
4981 gravity_port_flag = 0;
4982 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4984 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4985 element <= EL_SP_GRAVITY_PORT_UP)
4987 // change R'n'D style gravity inverting special port to normal port
4988 // (there are no gravity inverting ports in native Supaplex engine)
4990 gravity_port_found = TRUE;
4991 gravity_port_valid = FALSE;
4992 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4995 if (gravity_port_found)
4997 if (gravity_port_valid &&
4998 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5000 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5002 port->PortLocation = (y * level->fieldx + x) * 2;
5003 port->Gravity = gravity_port_flag;
5005 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5007 header->SpecialPortCount++;
5011 // change special gravity port to normal port
5013 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5016 level_sp->playfield[x][y] = element - EL_SP_START;
5021 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5023 struct LevelInfo_SP *level_sp = level->native_sp_level;
5024 LevelInfoType *header = &level_sp->header;
5025 boolean num_invalid_elements = 0;
5028 level->fieldx = level_sp->width;
5029 level->fieldy = level_sp->height;
5031 for (x = 0; x < level->fieldx; x++)
5033 for (y = 0; y < level->fieldy; y++)
5035 int element_old = level_sp->playfield[x][y];
5036 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5038 if (element_new == EL_UNKNOWN)
5040 num_invalid_elements++;
5042 Debug("level:native:SP", "invalid element %d at position %d, %d",
5046 level->field[x][y] = element_new;
5050 if (num_invalid_elements > 0)
5051 Warn("found %d invalid elements%s", num_invalid_elements,
5052 (!options.debug ? " (use '--debug' for more details)" : ""));
5054 for (i = 0; i < MAX_PLAYERS; i++)
5055 level->initial_player_gravity[i] =
5056 (header->InitialGravity == 1 ? TRUE : FALSE);
5058 // skip leading spaces
5059 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5060 if (header->LevelTitle[i] != ' ')
5064 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5065 level->name[j] = header->LevelTitle[i];
5066 level->name[j] = '\0';
5068 // cut trailing spaces
5070 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5071 level->name[j - 1] = '\0';
5073 level->gems_needed = header->InfotronsNeeded;
5075 for (i = 0; i < header->SpecialPortCount; i++)
5077 SpecialPortType *port = &header->SpecialPort[i];
5078 int port_location = port->PortLocation;
5079 int gravity = port->Gravity;
5080 int port_x, port_y, port_element;
5082 port_x = (port_location / 2) % level->fieldx;
5083 port_y = (port_location / 2) / level->fieldx;
5085 if (port_x < 0 || port_x >= level->fieldx ||
5086 port_y < 0 || port_y >= level->fieldy)
5088 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5093 port_element = level->field[port_x][port_y];
5095 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5096 port_element > EL_SP_GRAVITY_PORT_UP)
5098 Warn("no special port at position (%d, %d)", port_x, port_y);
5103 // change previous (wrong) gravity inverting special port to either
5104 // gravity enabling special port or gravity disabling special port
5105 level->field[port_x][port_y] +=
5106 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5107 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5110 // change special gravity ports without database entries to normal ports
5111 for (x = 0; x < level->fieldx; x++)
5112 for (y = 0; y < level->fieldy; y++)
5113 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5114 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5115 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5117 level->time = 0; // no time limit
5118 level->amoeba_speed = 0;
5119 level->time_magic_wall = 0;
5120 level->time_wheel = 0;
5121 level->amoeba_content = EL_EMPTY;
5123 // original Supaplex does not use score values -- rate by playing time
5124 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5125 level->score[i] = 0;
5127 level->rate_time_over_score = TRUE;
5129 // there are no yamyams in supaplex levels
5130 for (i = 0; i < level->num_yamyam_contents; i++)
5131 for (x = 0; x < 3; x++)
5132 for (y = 0; y < 3; y++)
5133 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5136 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5138 struct LevelInfo_SP *level_sp = level->native_sp_level;
5139 struct DemoInfo_SP *demo = &level_sp->demo;
5142 // always start with reliable default values
5143 demo->is_available = FALSE;
5146 if (TAPE_IS_EMPTY(tape))
5149 demo->level_nr = tape.level_nr; // (currently not used)
5151 level_sp->header.DemoRandomSeed = tape.random_seed;
5155 for (i = 0; i < tape.length; i++)
5157 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5158 int demo_repeat = tape.pos[i].delay;
5159 int demo_entries = (demo_repeat + 15) / 16;
5161 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5163 Warn("tape truncated: size exceeds maximum SP demo size %d",
5169 for (j = 0; j < demo_repeat / 16; j++)
5170 demo->data[demo->length++] = 0xf0 | demo_action;
5172 if (demo_repeat % 16)
5173 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5176 demo->is_available = TRUE;
5179 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5181 struct LevelInfo_SP *level_sp = level->native_sp_level;
5182 struct DemoInfo_SP *demo = &level_sp->demo;
5183 char *filename = level->file_info.filename;
5186 // always start with reliable default values
5187 setTapeInfoToDefaults();
5189 if (!demo->is_available)
5192 tape.level_nr = demo->level_nr; // (currently not used)
5193 tape.random_seed = level_sp->header.DemoRandomSeed;
5195 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5198 tape.pos[tape.counter].delay = 0;
5200 for (i = 0; i < demo->length; i++)
5202 int demo_action = demo->data[i] & 0x0f;
5203 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5204 int tape_action = map_key_SP_to_RND(demo_action);
5205 int tape_repeat = demo_repeat + 1;
5206 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5207 boolean success = 0;
5210 for (j = 0; j < tape_repeat; j++)
5211 success = TapeAddAction(action);
5215 Warn("SP demo truncated: size exceeds maximum tape size %d",
5222 TapeHaltRecording();
5226 // ----------------------------------------------------------------------------
5227 // functions for loading MM level
5228 // ----------------------------------------------------------------------------
5230 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5232 struct LevelInfo_MM *level_mm = level->native_mm_level;
5235 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5236 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5238 level_mm->time = level->time;
5239 level_mm->kettles_needed = level->gems_needed;
5240 level_mm->auto_count_kettles = level->auto_count_gems;
5242 level_mm->mm_laser_red = level->mm_laser_red;
5243 level_mm->mm_laser_green = level->mm_laser_green;
5244 level_mm->mm_laser_blue = level->mm_laser_blue;
5246 level_mm->df_laser_red = level->df_laser_red;
5247 level_mm->df_laser_green = level->df_laser_green;
5248 level_mm->df_laser_blue = level->df_laser_blue;
5250 strcpy(level_mm->name, level->name);
5251 strcpy(level_mm->author, level->author);
5253 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5254 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5255 level_mm->score[SC_KEY] = level->score[SC_KEY];
5256 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5257 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5259 level_mm->amoeba_speed = level->amoeba_speed;
5260 level_mm->time_fuse = level->mm_time_fuse;
5261 level_mm->time_bomb = level->mm_time_bomb;
5262 level_mm->time_ball = level->mm_time_ball;
5263 level_mm->time_block = level->mm_time_block;
5265 level_mm->num_ball_contents = level->num_mm_ball_contents;
5266 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5267 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5268 level_mm->explode_ball = level->explode_mm_ball;
5270 for (i = 0; i < level->num_mm_ball_contents; i++)
5271 level_mm->ball_content[i] =
5272 map_element_RND_to_MM(level->mm_ball_content[i]);
5274 for (x = 0; x < level->fieldx; x++)
5275 for (y = 0; y < level->fieldy; y++)
5277 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5280 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5282 struct LevelInfo_MM *level_mm = level->native_mm_level;
5285 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5286 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5288 level->time = level_mm->time;
5289 level->gems_needed = level_mm->kettles_needed;
5290 level->auto_count_gems = level_mm->auto_count_kettles;
5292 level->mm_laser_red = level_mm->mm_laser_red;
5293 level->mm_laser_green = level_mm->mm_laser_green;
5294 level->mm_laser_blue = level_mm->mm_laser_blue;
5296 level->df_laser_red = level_mm->df_laser_red;
5297 level->df_laser_green = level_mm->df_laser_green;
5298 level->df_laser_blue = level_mm->df_laser_blue;
5300 strcpy(level->name, level_mm->name);
5302 // only overwrite author from 'levelinfo.conf' if author defined in level
5303 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5304 strcpy(level->author, level_mm->author);
5306 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5307 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5308 level->score[SC_KEY] = level_mm->score[SC_KEY];
5309 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5310 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5312 level->amoeba_speed = level_mm->amoeba_speed;
5313 level->mm_time_fuse = level_mm->time_fuse;
5314 level->mm_time_bomb = level_mm->time_bomb;
5315 level->mm_time_ball = level_mm->time_ball;
5316 level->mm_time_block = level_mm->time_block;
5318 level->num_mm_ball_contents = level_mm->num_ball_contents;
5319 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5320 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5321 level->explode_mm_ball = level_mm->explode_ball;
5323 for (i = 0; i < level->num_mm_ball_contents; i++)
5324 level->mm_ball_content[i] =
5325 map_element_MM_to_RND(level_mm->ball_content[i]);
5327 for (x = 0; x < level->fieldx; x++)
5328 for (y = 0; y < level->fieldy; y++)
5329 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5333 // ----------------------------------------------------------------------------
5334 // functions for loading DC level
5335 // ----------------------------------------------------------------------------
5337 #define DC_LEVEL_HEADER_SIZE 344
5339 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5342 static int last_data_encoded;
5346 int diff_hi, diff_lo;
5347 int data_hi, data_lo;
5348 unsigned short data_decoded;
5352 last_data_encoded = 0;
5359 diff = data_encoded - last_data_encoded;
5360 diff_hi = diff & ~0xff;
5361 diff_lo = diff & 0xff;
5365 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5366 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5367 data_hi = data_hi & 0xff00;
5369 data_decoded = data_hi | data_lo;
5371 last_data_encoded = data_encoded;
5373 offset1 = (offset1 + 1) % 31;
5374 offset2 = offset2 & 0xff;
5376 return data_decoded;
5379 static int getMappedElement_DC(int element)
5387 // 0x0117 - 0x036e: (?)
5390 // 0x042d - 0x0684: (?)
5406 element = EL_CRYSTAL;
5409 case 0x0e77: // quicksand (boulder)
5410 element = EL_QUICKSAND_FAST_FULL;
5413 case 0x0e99: // slow quicksand (boulder)
5414 element = EL_QUICKSAND_FULL;
5418 element = EL_EM_EXIT_OPEN;
5422 element = EL_EM_EXIT_CLOSED;
5426 element = EL_EM_STEEL_EXIT_OPEN;
5430 element = EL_EM_STEEL_EXIT_CLOSED;
5433 case 0x0f4f: // dynamite (lit 1)
5434 element = EL_EM_DYNAMITE_ACTIVE;
5437 case 0x0f57: // dynamite (lit 2)
5438 element = EL_EM_DYNAMITE_ACTIVE;
5441 case 0x0f5f: // dynamite (lit 3)
5442 element = EL_EM_DYNAMITE_ACTIVE;
5445 case 0x0f67: // dynamite (lit 4)
5446 element = EL_EM_DYNAMITE_ACTIVE;
5453 element = EL_AMOEBA_WET;
5457 element = EL_AMOEBA_DROP;
5461 element = EL_DC_MAGIC_WALL;
5465 element = EL_SPACESHIP_UP;
5469 element = EL_SPACESHIP_DOWN;
5473 element = EL_SPACESHIP_LEFT;
5477 element = EL_SPACESHIP_RIGHT;
5481 element = EL_BUG_UP;
5485 element = EL_BUG_DOWN;
5489 element = EL_BUG_LEFT;
5493 element = EL_BUG_RIGHT;
5497 element = EL_MOLE_UP;
5501 element = EL_MOLE_DOWN;
5505 element = EL_MOLE_LEFT;
5509 element = EL_MOLE_RIGHT;
5517 element = EL_YAMYAM_UP;
5521 element = EL_SWITCHGATE_OPEN;
5525 element = EL_SWITCHGATE_CLOSED;
5529 element = EL_DC_SWITCHGATE_SWITCH_UP;
5533 element = EL_TIMEGATE_CLOSED;
5536 case 0x144c: // conveyor belt switch (green)
5537 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5540 case 0x144f: // conveyor belt switch (red)
5541 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5544 case 0x1452: // conveyor belt switch (blue)
5545 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5549 element = EL_CONVEYOR_BELT_3_MIDDLE;
5553 element = EL_CONVEYOR_BELT_3_LEFT;
5557 element = EL_CONVEYOR_BELT_3_RIGHT;
5561 element = EL_CONVEYOR_BELT_1_MIDDLE;
5565 element = EL_CONVEYOR_BELT_1_LEFT;
5569 element = EL_CONVEYOR_BELT_1_RIGHT;
5573 element = EL_CONVEYOR_BELT_4_MIDDLE;
5577 element = EL_CONVEYOR_BELT_4_LEFT;
5581 element = EL_CONVEYOR_BELT_4_RIGHT;
5585 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5589 element = EL_EXPANDABLE_WALL_VERTICAL;
5593 element = EL_EXPANDABLE_WALL_ANY;
5596 case 0x14ce: // growing steel wall (left/right)
5597 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5600 case 0x14df: // growing steel wall (up/down)
5601 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5604 case 0x14e8: // growing steel wall (up/down/left/right)
5605 element = EL_EXPANDABLE_STEELWALL_ANY;
5609 element = EL_SHIELD_DEADLY;
5613 element = EL_EXTRA_TIME;
5621 element = EL_EMPTY_SPACE;
5624 case 0x1578: // quicksand (empty)
5625 element = EL_QUICKSAND_FAST_EMPTY;
5628 case 0x1579: // slow quicksand (empty)
5629 element = EL_QUICKSAND_EMPTY;
5639 element = EL_EM_DYNAMITE;
5642 case 0x15a1: // key (red)
5643 element = EL_EM_KEY_1;
5646 case 0x15a2: // key (yellow)
5647 element = EL_EM_KEY_2;
5650 case 0x15a3: // key (blue)
5651 element = EL_EM_KEY_4;
5654 case 0x15a4: // key (green)
5655 element = EL_EM_KEY_3;
5658 case 0x15a5: // key (white)
5659 element = EL_DC_KEY_WHITE;
5663 element = EL_WALL_SLIPPERY;
5670 case 0x15a8: // wall (not round)
5674 case 0x15a9: // (blue)
5675 element = EL_CHAR_A;
5678 case 0x15aa: // (blue)
5679 element = EL_CHAR_B;
5682 case 0x15ab: // (blue)
5683 element = EL_CHAR_C;
5686 case 0x15ac: // (blue)
5687 element = EL_CHAR_D;
5690 case 0x15ad: // (blue)
5691 element = EL_CHAR_E;
5694 case 0x15ae: // (blue)
5695 element = EL_CHAR_F;
5698 case 0x15af: // (blue)
5699 element = EL_CHAR_G;
5702 case 0x15b0: // (blue)
5703 element = EL_CHAR_H;
5706 case 0x15b1: // (blue)
5707 element = EL_CHAR_I;
5710 case 0x15b2: // (blue)
5711 element = EL_CHAR_J;
5714 case 0x15b3: // (blue)
5715 element = EL_CHAR_K;
5718 case 0x15b4: // (blue)
5719 element = EL_CHAR_L;
5722 case 0x15b5: // (blue)
5723 element = EL_CHAR_M;
5726 case 0x15b6: // (blue)
5727 element = EL_CHAR_N;
5730 case 0x15b7: // (blue)
5731 element = EL_CHAR_O;
5734 case 0x15b8: // (blue)
5735 element = EL_CHAR_P;
5738 case 0x15b9: // (blue)
5739 element = EL_CHAR_Q;
5742 case 0x15ba: // (blue)
5743 element = EL_CHAR_R;
5746 case 0x15bb: // (blue)
5747 element = EL_CHAR_S;
5750 case 0x15bc: // (blue)
5751 element = EL_CHAR_T;
5754 case 0x15bd: // (blue)
5755 element = EL_CHAR_U;
5758 case 0x15be: // (blue)
5759 element = EL_CHAR_V;
5762 case 0x15bf: // (blue)
5763 element = EL_CHAR_W;
5766 case 0x15c0: // (blue)
5767 element = EL_CHAR_X;
5770 case 0x15c1: // (blue)
5771 element = EL_CHAR_Y;
5774 case 0x15c2: // (blue)
5775 element = EL_CHAR_Z;
5778 case 0x15c3: // (blue)
5779 element = EL_CHAR_AUMLAUT;
5782 case 0x15c4: // (blue)
5783 element = EL_CHAR_OUMLAUT;
5786 case 0x15c5: // (blue)
5787 element = EL_CHAR_UUMLAUT;
5790 case 0x15c6: // (blue)
5791 element = EL_CHAR_0;
5794 case 0x15c7: // (blue)
5795 element = EL_CHAR_1;
5798 case 0x15c8: // (blue)
5799 element = EL_CHAR_2;
5802 case 0x15c9: // (blue)
5803 element = EL_CHAR_3;
5806 case 0x15ca: // (blue)
5807 element = EL_CHAR_4;
5810 case 0x15cb: // (blue)
5811 element = EL_CHAR_5;
5814 case 0x15cc: // (blue)
5815 element = EL_CHAR_6;
5818 case 0x15cd: // (blue)
5819 element = EL_CHAR_7;
5822 case 0x15ce: // (blue)
5823 element = EL_CHAR_8;
5826 case 0x15cf: // (blue)
5827 element = EL_CHAR_9;
5830 case 0x15d0: // (blue)
5831 element = EL_CHAR_PERIOD;
5834 case 0x15d1: // (blue)
5835 element = EL_CHAR_EXCLAM;
5838 case 0x15d2: // (blue)
5839 element = EL_CHAR_COLON;
5842 case 0x15d3: // (blue)
5843 element = EL_CHAR_LESS;
5846 case 0x15d4: // (blue)
5847 element = EL_CHAR_GREATER;
5850 case 0x15d5: // (blue)
5851 element = EL_CHAR_QUESTION;
5854 case 0x15d6: // (blue)
5855 element = EL_CHAR_COPYRIGHT;
5858 case 0x15d7: // (blue)
5859 element = EL_CHAR_UP;
5862 case 0x15d8: // (blue)
5863 element = EL_CHAR_DOWN;
5866 case 0x15d9: // (blue)
5867 element = EL_CHAR_BUTTON;
5870 case 0x15da: // (blue)
5871 element = EL_CHAR_PLUS;
5874 case 0x15db: // (blue)
5875 element = EL_CHAR_MINUS;
5878 case 0x15dc: // (blue)
5879 element = EL_CHAR_APOSTROPHE;
5882 case 0x15dd: // (blue)
5883 element = EL_CHAR_PARENLEFT;
5886 case 0x15de: // (blue)
5887 element = EL_CHAR_PARENRIGHT;
5890 case 0x15df: // (green)
5891 element = EL_CHAR_A;
5894 case 0x15e0: // (green)
5895 element = EL_CHAR_B;
5898 case 0x15e1: // (green)
5899 element = EL_CHAR_C;
5902 case 0x15e2: // (green)
5903 element = EL_CHAR_D;
5906 case 0x15e3: // (green)
5907 element = EL_CHAR_E;
5910 case 0x15e4: // (green)
5911 element = EL_CHAR_F;
5914 case 0x15e5: // (green)
5915 element = EL_CHAR_G;
5918 case 0x15e6: // (green)
5919 element = EL_CHAR_H;
5922 case 0x15e7: // (green)
5923 element = EL_CHAR_I;
5926 case 0x15e8: // (green)
5927 element = EL_CHAR_J;
5930 case 0x15e9: // (green)
5931 element = EL_CHAR_K;
5934 case 0x15ea: // (green)
5935 element = EL_CHAR_L;
5938 case 0x15eb: // (green)
5939 element = EL_CHAR_M;
5942 case 0x15ec: // (green)
5943 element = EL_CHAR_N;
5946 case 0x15ed: // (green)
5947 element = EL_CHAR_O;
5950 case 0x15ee: // (green)
5951 element = EL_CHAR_P;
5954 case 0x15ef: // (green)
5955 element = EL_CHAR_Q;
5958 case 0x15f0: // (green)
5959 element = EL_CHAR_R;
5962 case 0x15f1: // (green)
5963 element = EL_CHAR_S;
5966 case 0x15f2: // (green)
5967 element = EL_CHAR_T;
5970 case 0x15f3: // (green)
5971 element = EL_CHAR_U;
5974 case 0x15f4: // (green)
5975 element = EL_CHAR_V;
5978 case 0x15f5: // (green)
5979 element = EL_CHAR_W;
5982 case 0x15f6: // (green)
5983 element = EL_CHAR_X;
5986 case 0x15f7: // (green)
5987 element = EL_CHAR_Y;
5990 case 0x15f8: // (green)
5991 element = EL_CHAR_Z;
5994 case 0x15f9: // (green)
5995 element = EL_CHAR_AUMLAUT;
5998 case 0x15fa: // (green)
5999 element = EL_CHAR_OUMLAUT;
6002 case 0x15fb: // (green)
6003 element = EL_CHAR_UUMLAUT;
6006 case 0x15fc: // (green)
6007 element = EL_CHAR_0;
6010 case 0x15fd: // (green)
6011 element = EL_CHAR_1;
6014 case 0x15fe: // (green)
6015 element = EL_CHAR_2;
6018 case 0x15ff: // (green)
6019 element = EL_CHAR_3;
6022 case 0x1600: // (green)
6023 element = EL_CHAR_4;
6026 case 0x1601: // (green)
6027 element = EL_CHAR_5;
6030 case 0x1602: // (green)
6031 element = EL_CHAR_6;
6034 case 0x1603: // (green)
6035 element = EL_CHAR_7;
6038 case 0x1604: // (green)
6039 element = EL_CHAR_8;
6042 case 0x1605: // (green)
6043 element = EL_CHAR_9;
6046 case 0x1606: // (green)
6047 element = EL_CHAR_PERIOD;
6050 case 0x1607: // (green)
6051 element = EL_CHAR_EXCLAM;
6054 case 0x1608: // (green)
6055 element = EL_CHAR_COLON;
6058 case 0x1609: // (green)
6059 element = EL_CHAR_LESS;
6062 case 0x160a: // (green)
6063 element = EL_CHAR_GREATER;
6066 case 0x160b: // (green)
6067 element = EL_CHAR_QUESTION;
6070 case 0x160c: // (green)
6071 element = EL_CHAR_COPYRIGHT;
6074 case 0x160d: // (green)
6075 element = EL_CHAR_UP;
6078 case 0x160e: // (green)
6079 element = EL_CHAR_DOWN;
6082 case 0x160f: // (green)
6083 element = EL_CHAR_BUTTON;
6086 case 0x1610: // (green)
6087 element = EL_CHAR_PLUS;
6090 case 0x1611: // (green)
6091 element = EL_CHAR_MINUS;
6094 case 0x1612: // (green)
6095 element = EL_CHAR_APOSTROPHE;
6098 case 0x1613: // (green)
6099 element = EL_CHAR_PARENLEFT;
6102 case 0x1614: // (green)
6103 element = EL_CHAR_PARENRIGHT;
6106 case 0x1615: // (blue steel)
6107 element = EL_STEEL_CHAR_A;
6110 case 0x1616: // (blue steel)
6111 element = EL_STEEL_CHAR_B;
6114 case 0x1617: // (blue steel)
6115 element = EL_STEEL_CHAR_C;
6118 case 0x1618: // (blue steel)
6119 element = EL_STEEL_CHAR_D;
6122 case 0x1619: // (blue steel)
6123 element = EL_STEEL_CHAR_E;
6126 case 0x161a: // (blue steel)
6127 element = EL_STEEL_CHAR_F;
6130 case 0x161b: // (blue steel)
6131 element = EL_STEEL_CHAR_G;
6134 case 0x161c: // (blue steel)
6135 element = EL_STEEL_CHAR_H;
6138 case 0x161d: // (blue steel)
6139 element = EL_STEEL_CHAR_I;
6142 case 0x161e: // (blue steel)
6143 element = EL_STEEL_CHAR_J;
6146 case 0x161f: // (blue steel)
6147 element = EL_STEEL_CHAR_K;
6150 case 0x1620: // (blue steel)
6151 element = EL_STEEL_CHAR_L;
6154 case 0x1621: // (blue steel)
6155 element = EL_STEEL_CHAR_M;
6158 case 0x1622: // (blue steel)
6159 element = EL_STEEL_CHAR_N;
6162 case 0x1623: // (blue steel)
6163 element = EL_STEEL_CHAR_O;
6166 case 0x1624: // (blue steel)
6167 element = EL_STEEL_CHAR_P;
6170 case 0x1625: // (blue steel)
6171 element = EL_STEEL_CHAR_Q;
6174 case 0x1626: // (blue steel)
6175 element = EL_STEEL_CHAR_R;
6178 case 0x1627: // (blue steel)
6179 element = EL_STEEL_CHAR_S;
6182 case 0x1628: // (blue steel)
6183 element = EL_STEEL_CHAR_T;
6186 case 0x1629: // (blue steel)
6187 element = EL_STEEL_CHAR_U;
6190 case 0x162a: // (blue steel)
6191 element = EL_STEEL_CHAR_V;
6194 case 0x162b: // (blue steel)
6195 element = EL_STEEL_CHAR_W;
6198 case 0x162c: // (blue steel)
6199 element = EL_STEEL_CHAR_X;
6202 case 0x162d: // (blue steel)
6203 element = EL_STEEL_CHAR_Y;
6206 case 0x162e: // (blue steel)
6207 element = EL_STEEL_CHAR_Z;
6210 case 0x162f: // (blue steel)
6211 element = EL_STEEL_CHAR_AUMLAUT;
6214 case 0x1630: // (blue steel)
6215 element = EL_STEEL_CHAR_OUMLAUT;
6218 case 0x1631: // (blue steel)
6219 element = EL_STEEL_CHAR_UUMLAUT;
6222 case 0x1632: // (blue steel)
6223 element = EL_STEEL_CHAR_0;
6226 case 0x1633: // (blue steel)
6227 element = EL_STEEL_CHAR_1;
6230 case 0x1634: // (blue steel)
6231 element = EL_STEEL_CHAR_2;
6234 case 0x1635: // (blue steel)
6235 element = EL_STEEL_CHAR_3;
6238 case 0x1636: // (blue steel)
6239 element = EL_STEEL_CHAR_4;
6242 case 0x1637: // (blue steel)
6243 element = EL_STEEL_CHAR_5;
6246 case 0x1638: // (blue steel)
6247 element = EL_STEEL_CHAR_6;
6250 case 0x1639: // (blue steel)
6251 element = EL_STEEL_CHAR_7;
6254 case 0x163a: // (blue steel)
6255 element = EL_STEEL_CHAR_8;
6258 case 0x163b: // (blue steel)
6259 element = EL_STEEL_CHAR_9;
6262 case 0x163c: // (blue steel)
6263 element = EL_STEEL_CHAR_PERIOD;
6266 case 0x163d: // (blue steel)
6267 element = EL_STEEL_CHAR_EXCLAM;
6270 case 0x163e: // (blue steel)
6271 element = EL_STEEL_CHAR_COLON;
6274 case 0x163f: // (blue steel)
6275 element = EL_STEEL_CHAR_LESS;
6278 case 0x1640: // (blue steel)
6279 element = EL_STEEL_CHAR_GREATER;
6282 case 0x1641: // (blue steel)
6283 element = EL_STEEL_CHAR_QUESTION;
6286 case 0x1642: // (blue steel)
6287 element = EL_STEEL_CHAR_COPYRIGHT;
6290 case 0x1643: // (blue steel)
6291 element = EL_STEEL_CHAR_UP;
6294 case 0x1644: // (blue steel)
6295 element = EL_STEEL_CHAR_DOWN;
6298 case 0x1645: // (blue steel)
6299 element = EL_STEEL_CHAR_BUTTON;
6302 case 0x1646: // (blue steel)
6303 element = EL_STEEL_CHAR_PLUS;
6306 case 0x1647: // (blue steel)
6307 element = EL_STEEL_CHAR_MINUS;
6310 case 0x1648: // (blue steel)
6311 element = EL_STEEL_CHAR_APOSTROPHE;
6314 case 0x1649: // (blue steel)
6315 element = EL_STEEL_CHAR_PARENLEFT;
6318 case 0x164a: // (blue steel)
6319 element = EL_STEEL_CHAR_PARENRIGHT;
6322 case 0x164b: // (green steel)
6323 element = EL_STEEL_CHAR_A;
6326 case 0x164c: // (green steel)
6327 element = EL_STEEL_CHAR_B;
6330 case 0x164d: // (green steel)
6331 element = EL_STEEL_CHAR_C;
6334 case 0x164e: // (green steel)
6335 element = EL_STEEL_CHAR_D;
6338 case 0x164f: // (green steel)
6339 element = EL_STEEL_CHAR_E;
6342 case 0x1650: // (green steel)
6343 element = EL_STEEL_CHAR_F;
6346 case 0x1651: // (green steel)
6347 element = EL_STEEL_CHAR_G;
6350 case 0x1652: // (green steel)
6351 element = EL_STEEL_CHAR_H;
6354 case 0x1653: // (green steel)
6355 element = EL_STEEL_CHAR_I;
6358 case 0x1654: // (green steel)
6359 element = EL_STEEL_CHAR_J;
6362 case 0x1655: // (green steel)
6363 element = EL_STEEL_CHAR_K;
6366 case 0x1656: // (green steel)
6367 element = EL_STEEL_CHAR_L;
6370 case 0x1657: // (green steel)
6371 element = EL_STEEL_CHAR_M;
6374 case 0x1658: // (green steel)
6375 element = EL_STEEL_CHAR_N;
6378 case 0x1659: // (green steel)
6379 element = EL_STEEL_CHAR_O;
6382 case 0x165a: // (green steel)
6383 element = EL_STEEL_CHAR_P;
6386 case 0x165b: // (green steel)
6387 element = EL_STEEL_CHAR_Q;
6390 case 0x165c: // (green steel)
6391 element = EL_STEEL_CHAR_R;
6394 case 0x165d: // (green steel)
6395 element = EL_STEEL_CHAR_S;
6398 case 0x165e: // (green steel)
6399 element = EL_STEEL_CHAR_T;
6402 case 0x165f: // (green steel)
6403 element = EL_STEEL_CHAR_U;
6406 case 0x1660: // (green steel)
6407 element = EL_STEEL_CHAR_V;
6410 case 0x1661: // (green steel)
6411 element = EL_STEEL_CHAR_W;
6414 case 0x1662: // (green steel)
6415 element = EL_STEEL_CHAR_X;
6418 case 0x1663: // (green steel)
6419 element = EL_STEEL_CHAR_Y;
6422 case 0x1664: // (green steel)
6423 element = EL_STEEL_CHAR_Z;
6426 case 0x1665: // (green steel)
6427 element = EL_STEEL_CHAR_AUMLAUT;
6430 case 0x1666: // (green steel)
6431 element = EL_STEEL_CHAR_OUMLAUT;
6434 case 0x1667: // (green steel)
6435 element = EL_STEEL_CHAR_UUMLAUT;
6438 case 0x1668: // (green steel)
6439 element = EL_STEEL_CHAR_0;
6442 case 0x1669: // (green steel)
6443 element = EL_STEEL_CHAR_1;
6446 case 0x166a: // (green steel)
6447 element = EL_STEEL_CHAR_2;
6450 case 0x166b: // (green steel)
6451 element = EL_STEEL_CHAR_3;
6454 case 0x166c: // (green steel)
6455 element = EL_STEEL_CHAR_4;
6458 case 0x166d: // (green steel)
6459 element = EL_STEEL_CHAR_5;
6462 case 0x166e: // (green steel)
6463 element = EL_STEEL_CHAR_6;
6466 case 0x166f: // (green steel)
6467 element = EL_STEEL_CHAR_7;
6470 case 0x1670: // (green steel)
6471 element = EL_STEEL_CHAR_8;
6474 case 0x1671: // (green steel)
6475 element = EL_STEEL_CHAR_9;
6478 case 0x1672: // (green steel)
6479 element = EL_STEEL_CHAR_PERIOD;
6482 case 0x1673: // (green steel)
6483 element = EL_STEEL_CHAR_EXCLAM;
6486 case 0x1674: // (green steel)
6487 element = EL_STEEL_CHAR_COLON;
6490 case 0x1675: // (green steel)
6491 element = EL_STEEL_CHAR_LESS;
6494 case 0x1676: // (green steel)
6495 element = EL_STEEL_CHAR_GREATER;
6498 case 0x1677: // (green steel)
6499 element = EL_STEEL_CHAR_QUESTION;
6502 case 0x1678: // (green steel)
6503 element = EL_STEEL_CHAR_COPYRIGHT;
6506 case 0x1679: // (green steel)
6507 element = EL_STEEL_CHAR_UP;
6510 case 0x167a: // (green steel)
6511 element = EL_STEEL_CHAR_DOWN;
6514 case 0x167b: // (green steel)
6515 element = EL_STEEL_CHAR_BUTTON;
6518 case 0x167c: // (green steel)
6519 element = EL_STEEL_CHAR_PLUS;
6522 case 0x167d: // (green steel)
6523 element = EL_STEEL_CHAR_MINUS;
6526 case 0x167e: // (green steel)
6527 element = EL_STEEL_CHAR_APOSTROPHE;
6530 case 0x167f: // (green steel)
6531 element = EL_STEEL_CHAR_PARENLEFT;
6534 case 0x1680: // (green steel)
6535 element = EL_STEEL_CHAR_PARENRIGHT;
6538 case 0x1681: // gate (red)
6539 element = EL_EM_GATE_1;
6542 case 0x1682: // secret gate (red)
6543 element = EL_EM_GATE_1_GRAY;
6546 case 0x1683: // gate (yellow)
6547 element = EL_EM_GATE_2;
6550 case 0x1684: // secret gate (yellow)
6551 element = EL_EM_GATE_2_GRAY;
6554 case 0x1685: // gate (blue)
6555 element = EL_EM_GATE_4;
6558 case 0x1686: // secret gate (blue)
6559 element = EL_EM_GATE_4_GRAY;
6562 case 0x1687: // gate (green)
6563 element = EL_EM_GATE_3;
6566 case 0x1688: // secret gate (green)
6567 element = EL_EM_GATE_3_GRAY;
6570 case 0x1689: // gate (white)
6571 element = EL_DC_GATE_WHITE;
6574 case 0x168a: // secret gate (white)
6575 element = EL_DC_GATE_WHITE_GRAY;
6578 case 0x168b: // secret gate (no key)
6579 element = EL_DC_GATE_FAKE_GRAY;
6583 element = EL_ROBOT_WHEEL;
6587 element = EL_DC_TIMEGATE_SWITCH;
6591 element = EL_ACID_POOL_BOTTOM;
6595 element = EL_ACID_POOL_TOPLEFT;
6599 element = EL_ACID_POOL_TOPRIGHT;
6603 element = EL_ACID_POOL_BOTTOMLEFT;
6607 element = EL_ACID_POOL_BOTTOMRIGHT;
6611 element = EL_STEELWALL;
6615 element = EL_STEELWALL_SLIPPERY;
6618 case 0x1695: // steel wall (not round)
6619 element = EL_STEELWALL;
6622 case 0x1696: // steel wall (left)
6623 element = EL_DC_STEELWALL_1_LEFT;
6626 case 0x1697: // steel wall (bottom)
6627 element = EL_DC_STEELWALL_1_BOTTOM;
6630 case 0x1698: // steel wall (right)
6631 element = EL_DC_STEELWALL_1_RIGHT;
6634 case 0x1699: // steel wall (top)
6635 element = EL_DC_STEELWALL_1_TOP;
6638 case 0x169a: // steel wall (left/bottom)
6639 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6642 case 0x169b: // steel wall (right/bottom)
6643 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6646 case 0x169c: // steel wall (right/top)
6647 element = EL_DC_STEELWALL_1_TOPRIGHT;
6650 case 0x169d: // steel wall (left/top)
6651 element = EL_DC_STEELWALL_1_TOPLEFT;
6654 case 0x169e: // steel wall (right/bottom small)
6655 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6658 case 0x169f: // steel wall (left/bottom small)
6659 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6662 case 0x16a0: // steel wall (right/top small)
6663 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6666 case 0x16a1: // steel wall (left/top small)
6667 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6670 case 0x16a2: // steel wall (left/right)
6671 element = EL_DC_STEELWALL_1_VERTICAL;
6674 case 0x16a3: // steel wall (top/bottom)
6675 element = EL_DC_STEELWALL_1_HORIZONTAL;
6678 case 0x16a4: // steel wall 2 (left end)
6679 element = EL_DC_STEELWALL_2_LEFT;
6682 case 0x16a5: // steel wall 2 (right end)
6683 element = EL_DC_STEELWALL_2_RIGHT;
6686 case 0x16a6: // steel wall 2 (top end)
6687 element = EL_DC_STEELWALL_2_TOP;
6690 case 0x16a7: // steel wall 2 (bottom end)
6691 element = EL_DC_STEELWALL_2_BOTTOM;
6694 case 0x16a8: // steel wall 2 (left/right)
6695 element = EL_DC_STEELWALL_2_HORIZONTAL;
6698 case 0x16a9: // steel wall 2 (up/down)
6699 element = EL_DC_STEELWALL_2_VERTICAL;
6702 case 0x16aa: // steel wall 2 (mid)
6703 element = EL_DC_STEELWALL_2_MIDDLE;
6707 element = EL_SIGN_EXCLAMATION;
6711 element = EL_SIGN_RADIOACTIVITY;
6715 element = EL_SIGN_STOP;
6719 element = EL_SIGN_WHEELCHAIR;
6723 element = EL_SIGN_PARKING;
6727 element = EL_SIGN_NO_ENTRY;
6731 element = EL_SIGN_HEART;
6735 element = EL_SIGN_GIVE_WAY;
6739 element = EL_SIGN_ENTRY_FORBIDDEN;
6743 element = EL_SIGN_EMERGENCY_EXIT;
6747 element = EL_SIGN_YIN_YANG;
6751 element = EL_WALL_EMERALD;
6755 element = EL_WALL_DIAMOND;
6759 element = EL_WALL_PEARL;
6763 element = EL_WALL_CRYSTAL;
6767 element = EL_INVISIBLE_WALL;
6771 element = EL_INVISIBLE_STEELWALL;
6775 // EL_INVISIBLE_SAND
6778 element = EL_LIGHT_SWITCH;
6782 element = EL_ENVELOPE_1;
6786 if (element >= 0x0117 && element <= 0x036e) // (?)
6787 element = EL_DIAMOND;
6788 else if (element >= 0x042d && element <= 0x0684) // (?)
6789 element = EL_EMERALD;
6790 else if (element >= 0x157c && element <= 0x158b)
6792 else if (element >= 0x1590 && element <= 0x159f)
6793 element = EL_DC_LANDMINE;
6794 else if (element >= 0x16bc && element <= 0x16cb)
6795 element = EL_INVISIBLE_SAND;
6798 Warn("unknown Diamond Caves element 0x%04x", element);
6800 element = EL_UNKNOWN;
6805 return getMappedElement(element);
6808 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6810 byte header[DC_LEVEL_HEADER_SIZE];
6812 int envelope_header_pos = 62;
6813 int envelope_content_pos = 94;
6814 int level_name_pos = 251;
6815 int level_author_pos = 292;
6816 int envelope_header_len;
6817 int envelope_content_len;
6819 int level_author_len;
6821 int num_yamyam_contents;
6824 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6826 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6828 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6830 header[i * 2 + 0] = header_word >> 8;
6831 header[i * 2 + 1] = header_word & 0xff;
6834 // read some values from level header to check level decoding integrity
6835 fieldx = header[6] | (header[7] << 8);
6836 fieldy = header[8] | (header[9] << 8);
6837 num_yamyam_contents = header[60] | (header[61] << 8);
6839 // do some simple sanity checks to ensure that level was correctly decoded
6840 if (fieldx < 1 || fieldx > 256 ||
6841 fieldy < 1 || fieldy > 256 ||
6842 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6844 level->no_valid_file = TRUE;
6846 Warn("cannot decode level from stream -- using empty level");
6851 // maximum envelope header size is 31 bytes
6852 envelope_header_len = header[envelope_header_pos];
6853 // maximum envelope content size is 110 (156?) bytes
6854 envelope_content_len = header[envelope_content_pos];
6856 // maximum level title size is 40 bytes
6857 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6858 // maximum level author size is 30 (51?) bytes
6859 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6863 for (i = 0; i < envelope_header_len; i++)
6864 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6865 level->envelope[0].text[envelope_size++] =
6866 header[envelope_header_pos + 1 + i];
6868 if (envelope_header_len > 0 && envelope_content_len > 0)
6870 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6871 level->envelope[0].text[envelope_size++] = '\n';
6872 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6873 level->envelope[0].text[envelope_size++] = '\n';
6876 for (i = 0; i < envelope_content_len; i++)
6877 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878 level->envelope[0].text[envelope_size++] =
6879 header[envelope_content_pos + 1 + i];
6881 level->envelope[0].text[envelope_size] = '\0';
6883 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6884 level->envelope[0].ysize = 10;
6885 level->envelope[0].autowrap = TRUE;
6886 level->envelope[0].centered = TRUE;
6888 for (i = 0; i < level_name_len; i++)
6889 level->name[i] = header[level_name_pos + 1 + i];
6890 level->name[level_name_len] = '\0';
6892 for (i = 0; i < level_author_len; i++)
6893 level->author[i] = header[level_author_pos + 1 + i];
6894 level->author[level_author_len] = '\0';
6896 num_yamyam_contents = header[60] | (header[61] << 8);
6897 level->num_yamyam_contents =
6898 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6900 for (i = 0; i < num_yamyam_contents; i++)
6902 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6904 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6905 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6907 if (i < MAX_ELEMENT_CONTENTS)
6908 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6912 fieldx = header[6] | (header[7] << 8);
6913 fieldy = header[8] | (header[9] << 8);
6914 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6915 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6917 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6919 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6920 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6922 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6923 level->field[x][y] = getMappedElement_DC(element_dc);
6926 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6927 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6928 level->field[x][y] = EL_PLAYER_1;
6930 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6931 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6932 level->field[x][y] = EL_PLAYER_2;
6934 level->gems_needed = header[18] | (header[19] << 8);
6936 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6937 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6938 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6939 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6940 level->score[SC_NUT] = header[28] | (header[29] << 8);
6941 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6942 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6943 level->score[SC_BUG] = header[34] | (header[35] << 8);
6944 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6945 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6946 level->score[SC_KEY] = header[40] | (header[41] << 8);
6947 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6949 level->time = header[44] | (header[45] << 8);
6951 level->amoeba_speed = header[46] | (header[47] << 8);
6952 level->time_light = header[48] | (header[49] << 8);
6953 level->time_timegate = header[50] | (header[51] << 8);
6954 level->time_wheel = header[52] | (header[53] << 8);
6955 level->time_magic_wall = header[54] | (header[55] << 8);
6956 level->extra_time = header[56] | (header[57] << 8);
6957 level->shield_normal_time = header[58] | (header[59] << 8);
6959 // shield and extra time elements do not have a score
6960 level->score[SC_SHIELD] = 0;
6961 level->extra_time_score = 0;
6963 // set time for normal and deadly shields to the same value
6964 level->shield_deadly_time = level->shield_normal_time;
6966 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6967 // can slip down from flat walls, like normal walls and steel walls
6968 level->em_slippery_gems = TRUE;
6970 // time score is counted for each 10 seconds left in Diamond Caves levels
6971 level->time_score_base = 10;
6974 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6975 struct LevelFileInfo *level_file_info,
6976 boolean level_info_only)
6978 char *filename = level_file_info->filename;
6980 int num_magic_bytes = 8;
6981 char magic_bytes[num_magic_bytes + 1];
6982 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6984 if (!(file = openFile(filename, MODE_READ)))
6986 level->no_valid_file = TRUE;
6988 if (!level_info_only)
6989 Warn("cannot read level '%s' -- using empty level", filename);
6994 // fseek(file, 0x0000, SEEK_SET);
6996 if (level_file_info->packed)
6998 // read "magic bytes" from start of file
6999 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7000 magic_bytes[0] = '\0';
7002 // check "magic bytes" for correct file format
7003 if (!strPrefix(magic_bytes, "DC2"))
7005 level->no_valid_file = TRUE;
7007 Warn("unknown DC level file '%s' -- using empty level", filename);
7012 if (strPrefix(magic_bytes, "DC2Win95") ||
7013 strPrefix(magic_bytes, "DC2Win98"))
7015 int position_first_level = 0x00fa;
7016 int extra_bytes = 4;
7019 // advance file stream to first level inside the level package
7020 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7022 // each block of level data is followed by block of non-level data
7023 num_levels_to_skip *= 2;
7025 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7026 while (num_levels_to_skip >= 0)
7028 // advance file stream to next level inside the level package
7029 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7031 level->no_valid_file = TRUE;
7033 Warn("cannot fseek in file '%s' -- using empty level", filename);
7038 // skip apparently unused extra bytes following each level
7039 ReadUnusedBytesFromFile(file, extra_bytes);
7041 // read size of next level in level package
7042 skip_bytes = getFile32BitLE(file);
7044 num_levels_to_skip--;
7049 level->no_valid_file = TRUE;
7051 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7057 LoadLevelFromFileStream_DC(file, level);
7063 // ----------------------------------------------------------------------------
7064 // functions for loading SB level
7065 // ----------------------------------------------------------------------------
7067 int getMappedElement_SB(int element_ascii, boolean use_ces)
7075 sb_element_mapping[] =
7077 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7078 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7079 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7080 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7081 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7082 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7083 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7084 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7091 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7092 if (element_ascii == sb_element_mapping[i].ascii)
7093 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7095 return EL_UNDEFINED;
7098 static void SetLevelSettings_SB(struct LevelInfo *level)
7102 level->use_step_counter = TRUE;
7105 level->score[SC_TIME_BONUS] = 0;
7106 level->time_score_base = 1;
7107 level->rate_time_over_score = TRUE;
7110 level->auto_exit_sokoban = TRUE;
7113 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7114 struct LevelFileInfo *level_file_info,
7115 boolean level_info_only)
7117 char *filename = level_file_info->filename;
7118 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7119 char last_comment[MAX_LINE_LEN];
7120 char level_name[MAX_LINE_LEN];
7123 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7124 boolean read_continued_line = FALSE;
7125 boolean reading_playfield = FALSE;
7126 boolean got_valid_playfield_line = FALSE;
7127 boolean invalid_playfield_char = FALSE;
7128 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7129 int file_level_nr = 0;
7130 int x = 0, y = 0; // initialized to make compilers happy
7132 last_comment[0] = '\0';
7133 level_name[0] = '\0';
7135 if (!(file = openFile(filename, MODE_READ)))
7137 level->no_valid_file = TRUE;
7139 if (!level_info_only)
7140 Warn("cannot read level '%s' -- using empty level", filename);
7145 while (!checkEndOfFile(file))
7147 // level successfully read, but next level may follow here
7148 if (!got_valid_playfield_line && reading_playfield)
7150 // read playfield from single level file -- skip remaining file
7151 if (!level_file_info->packed)
7154 if (file_level_nr >= num_levels_to_skip)
7159 last_comment[0] = '\0';
7160 level_name[0] = '\0';
7162 reading_playfield = FALSE;
7165 got_valid_playfield_line = FALSE;
7167 // read next line of input file
7168 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7171 // cut trailing line break (this can be newline and/or carriage return)
7172 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7173 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7176 // copy raw input line for later use (mainly debugging output)
7177 strcpy(line_raw, line);
7179 if (read_continued_line)
7181 // append new line to existing line, if there is enough space
7182 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7183 strcat(previous_line, line_ptr);
7185 strcpy(line, previous_line); // copy storage buffer to line
7187 read_continued_line = FALSE;
7190 // if the last character is '\', continue at next line
7191 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7193 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7194 strcpy(previous_line, line); // copy line to storage buffer
7196 read_continued_line = TRUE;
7202 if (line[0] == '\0')
7205 // extract comment text from comment line
7208 for (line_ptr = line; *line_ptr; line_ptr++)
7209 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7212 strcpy(last_comment, line_ptr);
7217 // extract level title text from line containing level title
7218 if (line[0] == '\'')
7220 strcpy(level_name, &line[1]);
7222 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7223 level_name[strlen(level_name) - 1] = '\0';
7228 // skip lines containing only spaces (or empty lines)
7229 for (line_ptr = line; *line_ptr; line_ptr++)
7230 if (*line_ptr != ' ')
7232 if (*line_ptr == '\0')
7235 // at this point, we have found a line containing part of a playfield
7237 got_valid_playfield_line = TRUE;
7239 if (!reading_playfield)
7241 reading_playfield = TRUE;
7242 invalid_playfield_char = FALSE;
7244 for (x = 0; x < MAX_LEV_FIELDX; x++)
7245 for (y = 0; y < MAX_LEV_FIELDY; y++)
7246 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7251 // start with topmost tile row
7255 // skip playfield line if larger row than allowed
7256 if (y >= MAX_LEV_FIELDY)
7259 // start with leftmost tile column
7262 // read playfield elements from line
7263 for (line_ptr = line; *line_ptr; line_ptr++)
7265 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7267 // stop parsing playfield line if larger column than allowed
7268 if (x >= MAX_LEV_FIELDX)
7271 if (mapped_sb_element == EL_UNDEFINED)
7273 invalid_playfield_char = TRUE;
7278 level->field[x][y] = mapped_sb_element;
7280 // continue with next tile column
7283 level->fieldx = MAX(x, level->fieldx);
7286 if (invalid_playfield_char)
7288 // if first playfield line, treat invalid lines as comment lines
7290 reading_playfield = FALSE;
7295 // continue with next tile row
7303 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7304 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7306 if (!reading_playfield)
7308 level->no_valid_file = TRUE;
7310 Warn("cannot read level '%s' -- using empty level", filename);
7315 if (*level_name != '\0')
7317 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7318 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7320 else if (*last_comment != '\0')
7322 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7323 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7327 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7330 // set all empty fields beyond the border walls to invisible steel wall
7331 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7333 if ((x == 0 || x == level->fieldx - 1 ||
7334 y == 0 || y == level->fieldy - 1) &&
7335 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7336 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7337 level->field, level->fieldx, level->fieldy);
7340 // set special level settings for Sokoban levels
7341 SetLevelSettings_SB(level);
7343 if (load_xsb_to_ces)
7345 // special global settings can now be set in level template
7346 level->use_custom_template = TRUE;
7351 // -------------------------------------------------------------------------
7352 // functions for handling native levels
7353 // -------------------------------------------------------------------------
7355 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7356 struct LevelFileInfo *level_file_info,
7357 boolean level_info_only)
7361 // determine position of requested level inside level package
7362 if (level_file_info->packed)
7363 pos = level_file_info->nr - leveldir_current->first_level;
7365 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7366 level->no_valid_file = TRUE;
7369 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7370 struct LevelFileInfo *level_file_info,
7371 boolean level_info_only)
7373 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7374 level->no_valid_file = TRUE;
7377 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7378 struct LevelFileInfo *level_file_info,
7379 boolean level_info_only)
7383 // determine position of requested level inside level package
7384 if (level_file_info->packed)
7385 pos = level_file_info->nr - leveldir_current->first_level;
7387 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7388 level->no_valid_file = TRUE;
7391 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7392 struct LevelFileInfo *level_file_info,
7393 boolean level_info_only)
7395 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7396 level->no_valid_file = TRUE;
7399 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7401 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7402 CopyNativeLevel_RND_to_BD(level);
7403 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7404 CopyNativeLevel_RND_to_EM(level);
7405 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7406 CopyNativeLevel_RND_to_SP(level);
7407 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7408 CopyNativeLevel_RND_to_MM(level);
7411 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7413 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7414 CopyNativeLevel_BD_to_RND(level);
7415 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7416 CopyNativeLevel_EM_to_RND(level);
7417 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7418 CopyNativeLevel_SP_to_RND(level);
7419 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7420 CopyNativeLevel_MM_to_RND(level);
7423 void SaveNativeLevel(struct LevelInfo *level)
7425 // saving native level files only supported for some game engines
7426 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7427 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7430 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7431 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7432 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7433 char *filename = getLevelFilenameFromBasename(basename);
7435 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7438 boolean success = FALSE;
7440 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7442 CopyNativeLevel_RND_to_BD(level);
7443 // CopyNativeTape_RND_to_BD(level);
7445 success = SaveNativeLevel_BD(filename);
7447 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7449 CopyNativeLevel_RND_to_SP(level);
7450 CopyNativeTape_RND_to_SP(level);
7452 success = SaveNativeLevel_SP(filename);
7456 Request("Native level file saved!", REQ_CONFIRM);
7458 Request("Failed to save native level file!", REQ_CONFIRM);
7462 // ----------------------------------------------------------------------------
7463 // functions for loading generic level
7464 // ----------------------------------------------------------------------------
7466 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7467 struct LevelFileInfo *level_file_info,
7468 boolean level_info_only)
7470 // always start with reliable default values
7471 setLevelInfoToDefaults(level, level_info_only, TRUE);
7473 switch (level_file_info->type)
7475 case LEVEL_FILE_TYPE_RND:
7476 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7479 case LEVEL_FILE_TYPE_BD:
7480 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7481 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7484 case LEVEL_FILE_TYPE_EM:
7485 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7486 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7489 case LEVEL_FILE_TYPE_SP:
7490 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7491 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7494 case LEVEL_FILE_TYPE_MM:
7495 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7496 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7499 case LEVEL_FILE_TYPE_DC:
7500 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7503 case LEVEL_FILE_TYPE_SB:
7504 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7508 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7512 // if level file is invalid, restore level structure to default values
7513 if (level->no_valid_file)
7514 setLevelInfoToDefaults(level, level_info_only, FALSE);
7516 if (check_special_flags("use_native_bd_game_engine"))
7517 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7519 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7520 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7522 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7523 CopyNativeLevel_Native_to_RND(level);
7526 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7528 static struct LevelFileInfo level_file_info;
7530 // always start with reliable default values
7531 setFileInfoToDefaults(&level_file_info);
7533 level_file_info.nr = 0; // unknown level number
7534 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7536 setString(&level_file_info.filename, filename);
7538 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7541 static void LoadLevel_InitVersion(struct LevelInfo *level)
7545 if (leveldir_current == NULL) // only when dumping level
7548 // all engine modifications also valid for levels which use latest engine
7549 if (level->game_version < VERSION_IDENT(3,2,0,5))
7551 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7552 level->time_score_base = 10;
7555 if (leveldir_current->latest_engine)
7557 // ---------- use latest game engine --------------------------------------
7559 /* For all levels which are forced to use the latest game engine version
7560 (normally all but user contributed, private and undefined levels), set
7561 the game engine version to the actual version; this allows for actual
7562 corrections in the game engine to take effect for existing, converted
7563 levels (from "classic" or other existing games) to make the emulation
7564 of the corresponding game more accurate, while (hopefully) not breaking
7565 existing levels created from other players. */
7567 level->game_version = GAME_VERSION_ACTUAL;
7569 /* Set special EM style gems behaviour: EM style gems slip down from
7570 normal, steel and growing wall. As this is a more fundamental change,
7571 it seems better to set the default behaviour to "off" (as it is more
7572 natural) and make it configurable in the level editor (as a property
7573 of gem style elements). Already existing converted levels (neither
7574 private nor contributed levels) are changed to the new behaviour. */
7576 if (level->file_version < FILE_VERSION_2_0)
7577 level->em_slippery_gems = TRUE;
7582 // ---------- use game engine the level was created with --------------------
7584 /* For all levels which are not forced to use the latest game engine
7585 version (normally user contributed, private and undefined levels),
7586 use the version of the game engine the levels were created for.
7588 Since 2.0.1, the game engine version is now directly stored
7589 in the level file (chunk "VERS"), so there is no need anymore
7590 to set the game version from the file version (except for old,
7591 pre-2.0 levels, where the game version is still taken from the
7592 file format version used to store the level -- see above). */
7594 // player was faster than enemies in 1.0.0 and before
7595 if (level->file_version == FILE_VERSION_1_0)
7596 for (i = 0; i < MAX_PLAYERS; i++)
7597 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7599 // default behaviour for EM style gems was "slippery" only in 2.0.1
7600 if (level->game_version == VERSION_IDENT(2,0,1,0))
7601 level->em_slippery_gems = TRUE;
7603 // springs could be pushed over pits before (pre-release version) 2.2.0
7604 if (level->game_version < VERSION_IDENT(2,2,0,0))
7605 level->use_spring_bug = TRUE;
7607 if (level->game_version < VERSION_IDENT(3,2,0,5))
7609 // time orb caused limited time in endless time levels before 3.2.0-5
7610 level->use_time_orb_bug = TRUE;
7612 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7613 level->block_snap_field = FALSE;
7615 // extra time score was same value as time left score before 3.2.0-5
7616 level->extra_time_score = level->score[SC_TIME_BONUS];
7619 if (level->game_version < VERSION_IDENT(3,2,0,7))
7621 // default behaviour for snapping was "not continuous" before 3.2.0-7
7622 level->continuous_snapping = FALSE;
7625 // only few elements were able to actively move into acid before 3.1.0
7626 // trigger settings did not exist before 3.1.0; set to default "any"
7627 if (level->game_version < VERSION_IDENT(3,1,0,0))
7629 // correct "can move into acid" settings (all zero in old levels)
7631 level->can_move_into_acid_bits = 0; // nothing can move into acid
7632 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7634 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7635 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7636 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7637 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7639 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7640 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7642 // correct trigger settings (stored as zero == "none" in old levels)
7644 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7646 int element = EL_CUSTOM_START + i;
7647 struct ElementInfo *ei = &element_info[element];
7649 for (j = 0; j < ei->num_change_pages; j++)
7651 struct ElementChangeInfo *change = &ei->change_page[j];
7653 change->trigger_player = CH_PLAYER_ANY;
7654 change->trigger_page = CH_PAGE_ANY;
7659 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7661 int element = EL_CUSTOM_256;
7662 struct ElementInfo *ei = &element_info[element];
7663 struct ElementChangeInfo *change = &ei->change_page[0];
7665 /* This is needed to fix a problem that was caused by a bugfix in function
7666 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7667 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7668 not replace walkable elements, but instead just placed the player on it,
7669 without placing the Sokoban field under the player). Unfortunately, this
7670 breaks "Snake Bite" style levels when the snake is halfway through a door
7671 that just closes (the snake head is still alive and can be moved in this
7672 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7673 player (without Sokoban element) which then gets killed as designed). */
7675 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7676 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7677 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7678 change->target_element = EL_PLAYER_1;
7681 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7682 if (level->game_version < VERSION_IDENT(3,2,5,0))
7684 /* This is needed to fix a problem that was caused by a bugfix in function
7685 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7686 corrects the behaviour when a custom element changes to another custom
7687 element with a higher element number that has change actions defined.
7688 Normally, only one change per frame is allowed for custom elements.
7689 Therefore, it is checked if a custom element already changed in the
7690 current frame; if it did, subsequent changes are suppressed.
7691 Unfortunately, this is only checked for element changes, but not for
7692 change actions, which are still executed. As the function above loops
7693 through all custom elements from lower to higher, an element change
7694 resulting in a lower CE number won't be checked again, while a target
7695 element with a higher number will also be checked, and potential change
7696 actions will get executed for this CE, too (which is wrong), while
7697 further changes are ignored (which is correct). As this bugfix breaks
7698 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7699 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7700 behaviour for existing levels and tapes that make use of this bug */
7702 level->use_action_after_change_bug = TRUE;
7705 // not centering level after relocating player was default only in 3.2.3
7706 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7707 level->shifted_relocation = TRUE;
7709 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7710 if (level->game_version < VERSION_IDENT(3,2,6,0))
7711 level->em_explodes_by_fire = TRUE;
7713 // levels were solved by the first player entering an exit up to 4.1.0.0
7714 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7715 level->solved_by_one_player = TRUE;
7717 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7718 if (level->game_version < VERSION_IDENT(4,1,1,1))
7719 level->use_life_bugs = TRUE;
7721 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7722 if (level->game_version < VERSION_IDENT(4,1,1,1))
7723 level->sb_objects_needed = FALSE;
7725 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7726 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7727 level->finish_dig_collect = FALSE;
7729 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7730 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7731 level->keep_walkable_ce = TRUE;
7734 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7736 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7739 // check if this level is (not) a Sokoban level
7740 for (y = 0; y < level->fieldy; y++)
7741 for (x = 0; x < level->fieldx; x++)
7742 if (!IS_SB_ELEMENT(Tile[x][y]))
7743 is_sokoban_level = FALSE;
7745 if (is_sokoban_level)
7747 // set special level settings for Sokoban levels
7748 SetLevelSettings_SB(level);
7752 static void LoadLevel_InitSettings(struct LevelInfo *level)
7754 // adjust level settings for (non-native) Sokoban-style levels
7755 LoadLevel_InitSettings_SB(level);
7757 // rename levels with title "nameless level" or if renaming is forced
7758 if (leveldir_current->empty_level_name != NULL &&
7759 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7760 leveldir_current->force_level_name))
7761 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7762 leveldir_current->empty_level_name, level_nr);
7765 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7769 // map elements that have changed in newer versions
7770 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7771 level->game_version);
7772 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7773 for (x = 0; x < 3; x++)
7774 for (y = 0; y < 3; y++)
7775 level->yamyam_content[i].e[x][y] =
7776 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7777 level->game_version);
7781 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7785 // map custom element change events that have changed in newer versions
7786 // (these following values were accidentally changed in version 3.0.1)
7787 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7788 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7792 int element = EL_CUSTOM_START + i;
7794 // order of checking and copying events to be mapped is important
7795 // (do not change the start and end value -- they are constant)
7796 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7798 if (HAS_CHANGE_EVENT(element, j - 2))
7800 SET_CHANGE_EVENT(element, j - 2, FALSE);
7801 SET_CHANGE_EVENT(element, j, TRUE);
7805 // order of checking and copying events to be mapped is important
7806 // (do not change the start and end value -- they are constant)
7807 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7809 if (HAS_CHANGE_EVENT(element, j - 1))
7811 SET_CHANGE_EVENT(element, j - 1, FALSE);
7812 SET_CHANGE_EVENT(element, j, TRUE);
7818 // initialize "can_change" field for old levels with only one change page
7819 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7821 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7823 int element = EL_CUSTOM_START + i;
7825 if (CAN_CHANGE(element))
7826 element_info[element].change->can_change = TRUE;
7830 // correct custom element values (for old levels without these options)
7831 if (level->game_version < VERSION_IDENT(3,1,1,0))
7833 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7835 int element = EL_CUSTOM_START + i;
7836 struct ElementInfo *ei = &element_info[element];
7838 if (ei->access_direction == MV_NO_DIRECTION)
7839 ei->access_direction = MV_ALL_DIRECTIONS;
7843 // correct custom element values (fix invalid values for all versions)
7846 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7848 int element = EL_CUSTOM_START + i;
7849 struct ElementInfo *ei = &element_info[element];
7851 for (j = 0; j < ei->num_change_pages; j++)
7853 struct ElementChangeInfo *change = &ei->change_page[j];
7855 if (change->trigger_player == CH_PLAYER_NONE)
7856 change->trigger_player = CH_PLAYER_ANY;
7858 if (change->trigger_side == CH_SIDE_NONE)
7859 change->trigger_side = CH_SIDE_ANY;
7864 // initialize "can_explode" field for old levels which did not store this
7865 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7866 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7870 int element = EL_CUSTOM_START + i;
7872 if (EXPLODES_1X1_OLD(element))
7873 element_info[element].explosion_type = EXPLODES_1X1;
7875 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7876 EXPLODES_SMASHED(element) ||
7877 EXPLODES_IMPACT(element)));
7881 // correct previously hard-coded move delay values for maze runner style
7882 if (level->game_version < VERSION_IDENT(3,1,1,0))
7884 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7886 int element = EL_CUSTOM_START + i;
7888 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7890 // previously hard-coded and therefore ignored
7891 element_info[element].move_delay_fixed = 9;
7892 element_info[element].move_delay_random = 0;
7897 // set some other uninitialized values of custom elements in older levels
7898 if (level->game_version < VERSION_IDENT(3,1,0,0))
7900 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7902 int element = EL_CUSTOM_START + i;
7904 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7906 element_info[element].explosion_delay = 17;
7907 element_info[element].ignition_delay = 8;
7911 // set mouse click change events to work for left/middle/right mouse button
7912 if (level->game_version < VERSION_IDENT(4,2,3,0))
7914 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7916 int element = EL_CUSTOM_START + i;
7917 struct ElementInfo *ei = &element_info[element];
7919 for (j = 0; j < ei->num_change_pages; j++)
7921 struct ElementChangeInfo *change = &ei->change_page[j];
7923 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7924 change->has_event[CE_PRESSED_BY_MOUSE] ||
7925 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7926 change->has_event[CE_MOUSE_PRESSED_ON_X])
7927 change->trigger_side = CH_SIDE_ANY;
7933 static void LoadLevel_InitElements(struct LevelInfo *level)
7935 LoadLevel_InitStandardElements(level);
7937 if (level->file_has_custom_elements)
7938 LoadLevel_InitCustomElements(level);
7940 // initialize element properties for level editor etc.
7941 InitElementPropertiesEngine(level->game_version);
7942 InitElementPropertiesGfxElement();
7945 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7949 // map elements that have changed in newer versions
7950 for (y = 0; y < level->fieldy; y++)
7951 for (x = 0; x < level->fieldx; x++)
7952 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7953 level->game_version);
7955 // clear unused playfield data (nicer if level gets resized in editor)
7956 for (x = 0; x < MAX_LEV_FIELDX; x++)
7957 for (y = 0; y < MAX_LEV_FIELDY; y++)
7958 if (x >= level->fieldx || y >= level->fieldy)
7959 level->field[x][y] = EL_EMPTY;
7961 // copy elements to runtime playfield array
7962 for (x = 0; x < MAX_LEV_FIELDX; x++)
7963 for (y = 0; y < MAX_LEV_FIELDY; y++)
7964 Tile[x][y] = level->field[x][y];
7966 // initialize level size variables for faster access
7967 lev_fieldx = level->fieldx;
7968 lev_fieldy = level->fieldy;
7970 // determine border element for this level
7971 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7972 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7977 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7979 struct LevelFileInfo *level_file_info = &level->file_info;
7981 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7982 CopyNativeLevel_RND_to_Native(level);
7985 static void LoadLevelTemplate_LoadAndInit(void)
7987 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7989 LoadLevel_InitVersion(&level_template);
7990 LoadLevel_InitElements(&level_template);
7991 LoadLevel_InitSettings(&level_template);
7993 ActivateLevelTemplate();
7996 void LoadLevelTemplate(int nr)
7998 if (!fileExists(getGlobalLevelTemplateFilename()))
8000 Warn("no level template found for this level");
8005 setLevelFileInfo(&level_template.file_info, nr);
8007 LoadLevelTemplate_LoadAndInit();
8010 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8012 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8014 LoadLevelTemplate_LoadAndInit();
8017 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8019 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8021 if (level.use_custom_template)
8023 if (network_level != NULL)
8024 LoadNetworkLevelTemplate(network_level);
8026 LoadLevelTemplate(-1);
8029 LoadLevel_InitVersion(&level);
8030 LoadLevel_InitElements(&level);
8031 LoadLevel_InitPlayfield(&level);
8032 LoadLevel_InitSettings(&level);
8034 LoadLevel_InitNativeEngines(&level);
8037 void LoadLevel(int nr)
8039 SetLevelSetInfo(leveldir_current->identifier, nr);
8041 setLevelFileInfo(&level.file_info, nr);
8043 LoadLevel_LoadAndInit(NULL);
8046 void LoadLevelInfoOnly(int nr)
8048 setLevelFileInfo(&level.file_info, nr);
8050 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8053 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8055 SetLevelSetInfo(network_level->leveldir_identifier,
8056 network_level->file_info.nr);
8058 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8060 LoadLevel_LoadAndInit(network_level);
8063 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8067 chunk_size += putFileVersion(file, level->file_version);
8068 chunk_size += putFileVersion(file, level->game_version);
8073 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8077 chunk_size += putFile16BitBE(file, level->creation_date.year);
8078 chunk_size += putFile8Bit(file, level->creation_date.month);
8079 chunk_size += putFile8Bit(file, level->creation_date.day);
8084 #if ENABLE_HISTORIC_CHUNKS
8085 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8089 putFile8Bit(file, level->fieldx);
8090 putFile8Bit(file, level->fieldy);
8092 putFile16BitBE(file, level->time);
8093 putFile16BitBE(file, level->gems_needed);
8095 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8096 putFile8Bit(file, level->name[i]);
8098 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8099 putFile8Bit(file, level->score[i]);
8101 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8102 for (y = 0; y < 3; y++)
8103 for (x = 0; x < 3; x++)
8104 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8105 level->yamyam_content[i].e[x][y]));
8106 putFile8Bit(file, level->amoeba_speed);
8107 putFile8Bit(file, level->time_magic_wall);
8108 putFile8Bit(file, level->time_wheel);
8109 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8110 level->amoeba_content));
8111 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8112 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8113 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8114 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8116 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8118 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8119 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8120 putFile32BitBE(file, level->can_move_into_acid_bits);
8121 putFile8Bit(file, level->dont_collide_with_bits);
8123 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8124 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8126 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8127 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8128 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8130 putFile8Bit(file, level->game_engine_type);
8132 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8136 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8141 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8142 chunk_size += putFile8Bit(file, level->name[i]);
8147 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8152 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8153 chunk_size += putFile8Bit(file, level->author[i]);
8158 #if ENABLE_HISTORIC_CHUNKS
8159 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8164 for (y = 0; y < level->fieldy; y++)
8165 for (x = 0; x < level->fieldx; x++)
8166 if (level->encoding_16bit_field)
8167 chunk_size += putFile16BitBE(file, level->field[x][y]);
8169 chunk_size += putFile8Bit(file, level->field[x][y]);
8175 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8180 for (y = 0; y < level->fieldy; y++)
8181 for (x = 0; x < level->fieldx; x++)
8182 chunk_size += putFile16BitBE(file, level->field[x][y]);
8187 #if ENABLE_HISTORIC_CHUNKS
8188 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8192 putFile8Bit(file, EL_YAMYAM);
8193 putFile8Bit(file, level->num_yamyam_contents);
8194 putFile8Bit(file, 0);
8195 putFile8Bit(file, 0);
8197 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8198 for (y = 0; y < 3; y++)
8199 for (x = 0; x < 3; x++)
8200 if (level->encoding_16bit_field)
8201 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8203 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8207 #if ENABLE_HISTORIC_CHUNKS
8208 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8211 int num_contents, content_xsize, content_ysize;
8212 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8214 if (element == EL_YAMYAM)
8216 num_contents = level->num_yamyam_contents;
8220 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8221 for (y = 0; y < 3; y++)
8222 for (x = 0; x < 3; x++)
8223 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8225 else if (element == EL_BD_AMOEBA)
8231 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8232 for (y = 0; y < 3; y++)
8233 for (x = 0; x < 3; x++)
8234 content_array[i][x][y] = EL_EMPTY;
8235 content_array[0][0][0] = level->amoeba_content;
8239 // chunk header already written -- write empty chunk data
8240 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8242 Warn("cannot save content for element '%d'", element);
8247 putFile16BitBE(file, element);
8248 putFile8Bit(file, num_contents);
8249 putFile8Bit(file, content_xsize);
8250 putFile8Bit(file, content_ysize);
8252 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8254 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8255 for (y = 0; y < 3; y++)
8256 for (x = 0; x < 3; x++)
8257 putFile16BitBE(file, content_array[i][x][y]);
8261 #if ENABLE_HISTORIC_CHUNKS
8262 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8264 int envelope_nr = element - EL_ENVELOPE_1;
8265 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8269 chunk_size += putFile16BitBE(file, element);
8270 chunk_size += putFile16BitBE(file, envelope_len);
8271 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8272 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8274 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8275 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8277 for (i = 0; i < envelope_len; i++)
8278 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CUS1(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 struct ElementInfo *ei = &element_info[element];
8298 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8300 if (check < num_changed_custom_elements)
8302 putFile16BitBE(file, element);
8303 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8310 if (check != num_changed_custom_elements) // should not happen
8311 Warn("inconsistent number of custom element properties");
8315 #if ENABLE_HISTORIC_CHUNKS
8316 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8317 int num_changed_custom_elements)
8321 putFile16BitBE(file, num_changed_custom_elements);
8323 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8325 int element = EL_CUSTOM_START + i;
8327 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8329 if (check < num_changed_custom_elements)
8331 putFile16BitBE(file, element);
8332 putFile16BitBE(file, element_info[element].change->target_element);
8339 if (check != num_changed_custom_elements) // should not happen
8340 Warn("inconsistent number of custom target elements");
8344 #if ENABLE_HISTORIC_CHUNKS
8345 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8346 int num_changed_custom_elements)
8348 int i, j, x, y, check = 0;
8350 putFile16BitBE(file, num_changed_custom_elements);
8352 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8354 int element = EL_CUSTOM_START + i;
8355 struct ElementInfo *ei = &element_info[element];
8357 if (ei->modified_settings)
8359 if (check < num_changed_custom_elements)
8361 putFile16BitBE(file, element);
8363 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8364 putFile8Bit(file, ei->description[j]);
8366 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8368 // some free bytes for future properties and padding
8369 WriteUnusedBytesToFile(file, 7);
8371 putFile8Bit(file, ei->use_gfx_element);
8372 putFile16BitBE(file, ei->gfx_element_initial);
8374 putFile8Bit(file, ei->collect_score_initial);
8375 putFile8Bit(file, ei->collect_count_initial);
8377 putFile16BitBE(file, ei->push_delay_fixed);
8378 putFile16BitBE(file, ei->push_delay_random);
8379 putFile16BitBE(file, ei->move_delay_fixed);
8380 putFile16BitBE(file, ei->move_delay_random);
8382 putFile16BitBE(file, ei->move_pattern);
8383 putFile8Bit(file, ei->move_direction_initial);
8384 putFile8Bit(file, ei->move_stepsize);
8386 for (y = 0; y < 3; y++)
8387 for (x = 0; x < 3; x++)
8388 putFile16BitBE(file, ei->content.e[x][y]);
8390 putFile32BitBE(file, ei->change->events);
8392 putFile16BitBE(file, ei->change->target_element);
8394 putFile16BitBE(file, ei->change->delay_fixed);
8395 putFile16BitBE(file, ei->change->delay_random);
8396 putFile16BitBE(file, ei->change->delay_frames);
8398 putFile16BitBE(file, ei->change->initial_trigger_element);
8400 putFile8Bit(file, ei->change->explode);
8401 putFile8Bit(file, ei->change->use_target_content);
8402 putFile8Bit(file, ei->change->only_if_complete);
8403 putFile8Bit(file, ei->change->use_random_replace);
8405 putFile8Bit(file, ei->change->random_percentage);
8406 putFile8Bit(file, ei->change->replace_when);
8408 for (y = 0; y < 3; y++)
8409 for (x = 0; x < 3; x++)
8410 putFile16BitBE(file, ei->change->content.e[x][y]);
8412 putFile8Bit(file, ei->slippery_type);
8414 // some free bytes for future properties and padding
8415 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8422 if (check != num_changed_custom_elements) // should not happen
8423 Warn("inconsistent number of custom element properties");
8427 #if ENABLE_HISTORIC_CHUNKS
8428 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8430 struct ElementInfo *ei = &element_info[element];
8433 // ---------- custom element base property values (96 bytes) ----------------
8435 putFile16BitBE(file, element);
8437 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8438 putFile8Bit(file, ei->description[i]);
8440 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8442 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8444 putFile8Bit(file, ei->num_change_pages);
8446 putFile16BitBE(file, ei->ce_value_fixed_initial);
8447 putFile16BitBE(file, ei->ce_value_random_initial);
8448 putFile8Bit(file, ei->use_last_ce_value);
8450 putFile8Bit(file, ei->use_gfx_element);
8451 putFile16BitBE(file, ei->gfx_element_initial);
8453 putFile8Bit(file, ei->collect_score_initial);
8454 putFile8Bit(file, ei->collect_count_initial);
8456 putFile8Bit(file, ei->drop_delay_fixed);
8457 putFile8Bit(file, ei->push_delay_fixed);
8458 putFile8Bit(file, ei->drop_delay_random);
8459 putFile8Bit(file, ei->push_delay_random);
8460 putFile16BitBE(file, ei->move_delay_fixed);
8461 putFile16BitBE(file, ei->move_delay_random);
8463 // bits 0 - 15 of "move_pattern" ...
8464 putFile16BitBE(file, ei->move_pattern & 0xffff);
8465 putFile8Bit(file, ei->move_direction_initial);
8466 putFile8Bit(file, ei->move_stepsize);
8468 putFile8Bit(file, ei->slippery_type);
8470 for (y = 0; y < 3; y++)
8471 for (x = 0; x < 3; x++)
8472 putFile16BitBE(file, ei->content.e[x][y]);
8474 putFile16BitBE(file, ei->move_enter_element);
8475 putFile16BitBE(file, ei->move_leave_element);
8476 putFile8Bit(file, ei->move_leave_type);
8478 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8479 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8481 putFile8Bit(file, ei->access_direction);
8483 putFile8Bit(file, ei->explosion_delay);
8484 putFile8Bit(file, ei->ignition_delay);
8485 putFile8Bit(file, ei->explosion_type);
8487 // some free bytes for future custom property values and padding
8488 WriteUnusedBytesToFile(file, 1);
8490 // ---------- change page property values (48 bytes) ------------------------
8492 for (i = 0; i < ei->num_change_pages; i++)
8494 struct ElementChangeInfo *change = &ei->change_page[i];
8495 unsigned int event_bits;
8497 // bits 0 - 31 of "has_event[]" ...
8499 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8500 if (change->has_event[j])
8501 event_bits |= (1u << j);
8502 putFile32BitBE(file, event_bits);
8504 putFile16BitBE(file, change->target_element);
8506 putFile16BitBE(file, change->delay_fixed);
8507 putFile16BitBE(file, change->delay_random);
8508 putFile16BitBE(file, change->delay_frames);
8510 putFile16BitBE(file, change->initial_trigger_element);
8512 putFile8Bit(file, change->explode);
8513 putFile8Bit(file, change->use_target_content);
8514 putFile8Bit(file, change->only_if_complete);
8515 putFile8Bit(file, change->use_random_replace);
8517 putFile8Bit(file, change->random_percentage);
8518 putFile8Bit(file, change->replace_when);
8520 for (y = 0; y < 3; y++)
8521 for (x = 0; x < 3; x++)
8522 putFile16BitBE(file, change->target_content.e[x][y]);
8524 putFile8Bit(file, change->can_change);
8526 putFile8Bit(file, change->trigger_side);
8528 putFile8Bit(file, change->trigger_player);
8529 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8530 log_2(change->trigger_page)));
8532 putFile8Bit(file, change->has_action);
8533 putFile8Bit(file, change->action_type);
8534 putFile8Bit(file, change->action_mode);
8535 putFile16BitBE(file, change->action_arg);
8537 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8539 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8540 if (change->has_event[j])
8541 event_bits |= (1u << (j - 32));
8542 putFile8Bit(file, event_bits);
8547 #if ENABLE_HISTORIC_CHUNKS
8548 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8550 struct ElementInfo *ei = &element_info[element];
8551 struct ElementGroupInfo *group = ei->group;
8554 putFile16BitBE(file, element);
8556 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8557 putFile8Bit(file, ei->description[i]);
8559 putFile8Bit(file, group->num_elements);
8561 putFile8Bit(file, ei->use_gfx_element);
8562 putFile16BitBE(file, ei->gfx_element_initial);
8564 putFile8Bit(file, group->choice_mode);
8566 // some free bytes for future values and padding
8567 WriteUnusedBytesToFile(file, 3);
8569 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8570 putFile16BitBE(file, group->element[i]);
8574 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8575 boolean write_element)
8577 int save_type = entry->save_type;
8578 int data_type = entry->data_type;
8579 int conf_type = entry->conf_type;
8580 int byte_mask = conf_type & CONF_MASK_BYTES;
8581 int element = entry->element;
8582 int default_value = entry->default_value;
8584 boolean modified = FALSE;
8586 if (byte_mask != CONF_MASK_MULTI_BYTES)
8588 void *value_ptr = entry->value;
8589 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8592 // check if any settings have been modified before saving them
8593 if (value != default_value)
8596 // do not save if explicitly told or if unmodified default settings
8597 if ((save_type == SAVE_CONF_NEVER) ||
8598 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8602 num_bytes += putFile16BitBE(file, element);
8604 num_bytes += putFile8Bit(file, conf_type);
8605 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8606 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8607 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8610 else if (data_type == TYPE_STRING)
8612 char *default_string = entry->default_string;
8613 char *string = (char *)(entry->value);
8614 int string_length = strlen(string);
8617 // check if any settings have been modified before saving them
8618 if (!strEqual(string, default_string))
8621 // do not save if explicitly told or if unmodified default settings
8622 if ((save_type == SAVE_CONF_NEVER) ||
8623 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8627 num_bytes += putFile16BitBE(file, element);
8629 num_bytes += putFile8Bit(file, conf_type);
8630 num_bytes += putFile16BitBE(file, string_length);
8632 for (i = 0; i < string_length; i++)
8633 num_bytes += putFile8Bit(file, string[i]);
8635 else if (data_type == TYPE_ELEMENT_LIST)
8637 int *element_array = (int *)(entry->value);
8638 int num_elements = *(int *)(entry->num_entities);
8641 // check if any settings have been modified before saving them
8642 for (i = 0; i < num_elements; i++)
8643 if (element_array[i] != default_value)
8646 // do not save if explicitly told or if unmodified default settings
8647 if ((save_type == SAVE_CONF_NEVER) ||
8648 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8652 num_bytes += putFile16BitBE(file, element);
8654 num_bytes += putFile8Bit(file, conf_type);
8655 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8657 for (i = 0; i < num_elements; i++)
8658 num_bytes += putFile16BitBE(file, element_array[i]);
8660 else if (data_type == TYPE_CONTENT_LIST)
8662 struct Content *content = (struct Content *)(entry->value);
8663 int num_contents = *(int *)(entry->num_entities);
8666 // check if any settings have been modified before saving them
8667 for (i = 0; i < num_contents; i++)
8668 for (y = 0; y < 3; y++)
8669 for (x = 0; x < 3; x++)
8670 if (content[i].e[x][y] != default_value)
8673 // do not save if explicitly told or if unmodified default settings
8674 if ((save_type == SAVE_CONF_NEVER) ||
8675 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8679 num_bytes += putFile16BitBE(file, element);
8681 num_bytes += putFile8Bit(file, conf_type);
8682 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8684 for (i = 0; i < num_contents; i++)
8685 for (y = 0; y < 3; y++)
8686 for (x = 0; x < 3; x++)
8687 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8693 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8698 li = *level; // copy level data into temporary buffer
8700 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8701 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8706 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8711 li = *level; // copy level data into temporary buffer
8713 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8714 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8719 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8721 int envelope_nr = element - EL_ENVELOPE_1;
8725 chunk_size += putFile16BitBE(file, element);
8727 // copy envelope data into temporary buffer
8728 xx_envelope = level->envelope[envelope_nr];
8730 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8731 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8736 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8738 struct ElementInfo *ei = &element_info[element];
8742 chunk_size += putFile16BitBE(file, element);
8744 xx_ei = *ei; // copy element data into temporary buffer
8746 // set default description string for this specific element
8747 strcpy(xx_default_description, getDefaultElementDescription(ei));
8749 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8750 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8752 for (i = 0; i < ei->num_change_pages; i++)
8754 struct ElementChangeInfo *change = &ei->change_page[i];
8756 xx_current_change_page = i;
8758 xx_change = *change; // copy change data into temporary buffer
8761 setEventBitsFromEventFlags(change);
8763 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8764 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8771 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8773 struct ElementInfo *ei = &element_info[element];
8774 struct ElementGroupInfo *group = ei->group;
8778 chunk_size += putFile16BitBE(file, element);
8780 xx_ei = *ei; // copy element data into temporary buffer
8781 xx_group = *group; // copy group data into temporary buffer
8783 // set default description string for this specific element
8784 strcpy(xx_default_description, getDefaultElementDescription(ei));
8786 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8787 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8792 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8794 struct ElementInfo *ei = &element_info[element];
8798 chunk_size += putFile16BitBE(file, element);
8800 xx_ei = *ei; // copy element data into temporary buffer
8802 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8803 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8808 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8809 boolean save_as_template)
8815 if (!(file = fopen(filename, MODE_WRITE)))
8817 Warn("cannot save level file '%s'", filename);
8822 level->file_version = FILE_VERSION_ACTUAL;
8823 level->game_version = GAME_VERSION_ACTUAL;
8825 level->creation_date = getCurrentDate();
8827 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8828 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8830 chunk_size = SaveLevel_VERS(NULL, level);
8831 putFileChunkBE(file, "VERS", chunk_size);
8832 SaveLevel_VERS(file, level);
8834 chunk_size = SaveLevel_DATE(NULL, level);
8835 putFileChunkBE(file, "DATE", chunk_size);
8836 SaveLevel_DATE(file, level);
8838 chunk_size = SaveLevel_NAME(NULL, level);
8839 putFileChunkBE(file, "NAME", chunk_size);
8840 SaveLevel_NAME(file, level);
8842 chunk_size = SaveLevel_AUTH(NULL, level);
8843 putFileChunkBE(file, "AUTH", chunk_size);
8844 SaveLevel_AUTH(file, level);
8846 chunk_size = SaveLevel_INFO(NULL, level);
8847 putFileChunkBE(file, "INFO", chunk_size);
8848 SaveLevel_INFO(file, level);
8850 chunk_size = SaveLevel_BODY(NULL, level);
8851 putFileChunkBE(file, "BODY", chunk_size);
8852 SaveLevel_BODY(file, level);
8854 chunk_size = SaveLevel_ELEM(NULL, level);
8855 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8857 putFileChunkBE(file, "ELEM", chunk_size);
8858 SaveLevel_ELEM(file, level);
8861 for (i = 0; i < NUM_ENVELOPES; i++)
8863 int element = EL_ENVELOPE_1 + i;
8865 chunk_size = SaveLevel_NOTE(NULL, level, element);
8866 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8868 putFileChunkBE(file, "NOTE", chunk_size);
8869 SaveLevel_NOTE(file, level, element);
8873 // if not using template level, check for non-default custom/group elements
8874 if (!level->use_custom_template || save_as_template)
8876 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8878 int element = EL_CUSTOM_START + i;
8880 chunk_size = SaveLevel_CUSX(NULL, level, element);
8881 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8883 putFileChunkBE(file, "CUSX", chunk_size);
8884 SaveLevel_CUSX(file, level, element);
8888 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8890 int element = EL_GROUP_START + i;
8892 chunk_size = SaveLevel_GRPX(NULL, level, element);
8893 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8895 putFileChunkBE(file, "GRPX", chunk_size);
8896 SaveLevel_GRPX(file, level, element);
8900 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8902 int element = GET_EMPTY_ELEMENT(i);
8904 chunk_size = SaveLevel_EMPX(NULL, level, element);
8905 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8907 putFileChunkBE(file, "EMPX", chunk_size);
8908 SaveLevel_EMPX(file, level, element);
8915 SetFilePermissions(filename, PERMS_PRIVATE);
8918 void SaveLevel(int nr)
8920 char *filename = getDefaultLevelFilename(nr);
8922 SaveLevelFromFilename(&level, filename, FALSE);
8925 void SaveLevelTemplate(void)
8927 char *filename = getLocalLevelTemplateFilename();
8929 SaveLevelFromFilename(&level, filename, TRUE);
8932 boolean SaveLevelChecked(int nr)
8934 char *filename = getDefaultLevelFilename(nr);
8935 boolean new_level = !fileExists(filename);
8936 boolean level_saved = FALSE;
8938 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8943 Request("Level saved!", REQ_CONFIRM);
8951 void DumpLevel(struct LevelInfo *level)
8953 if (level->no_level_file || level->no_valid_file)
8955 Warn("cannot dump -- no valid level file found");
8961 Print("Level xxx (file version %08d, game version %08d)\n",
8962 level->file_version, level->game_version);
8965 Print("Level author: '%s'\n", level->author);
8966 Print("Level title: '%s'\n", level->name);
8968 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8970 Print("Level time: %d seconds\n", level->time);
8971 Print("Gems needed: %d\n", level->gems_needed);
8973 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8974 Print("Time for wheel: %d seconds\n", level->time_wheel);
8975 Print("Time for light: %d seconds\n", level->time_light);
8976 Print("Time for timegate: %d seconds\n", level->time_timegate);
8978 Print("Amoeba speed: %d\n", level->amoeba_speed);
8981 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8982 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8983 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8984 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8985 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8986 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8992 for (i = 0; i < NUM_ENVELOPES; i++)
8994 char *text = level->envelope[i].text;
8995 int text_len = strlen(text);
8996 boolean has_text = FALSE;
8998 for (j = 0; j < text_len; j++)
8999 if (text[j] != ' ' && text[j] != '\n')
9005 Print("Envelope %d:\n'%s'\n", i + 1, text);
9013 void DumpLevels(void)
9015 static LevelDirTree *dumplevel_leveldir = NULL;
9017 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9018 global.dumplevel_leveldir);
9020 if (dumplevel_leveldir == NULL)
9021 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9023 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9024 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9025 Fail("no such level number: %d", global.dumplevel_level_nr);
9027 leveldir_current = dumplevel_leveldir;
9029 LoadLevel(global.dumplevel_level_nr);
9036 // ============================================================================
9037 // tape file functions
9038 // ============================================================================
9040 static void setTapeInfoToDefaults(void)
9044 // always start with reliable default values (empty tape)
9047 // default values (also for pre-1.2 tapes) with only the first player
9048 tape.player_participates[0] = TRUE;
9049 for (i = 1; i < MAX_PLAYERS; i++)
9050 tape.player_participates[i] = FALSE;
9052 // at least one (default: the first) player participates in every tape
9053 tape.num_participating_players = 1;
9055 tape.property_bits = TAPE_PROPERTY_NONE;
9057 tape.level_nr = level_nr;
9059 tape.changed = FALSE;
9060 tape.solved = FALSE;
9062 tape.recording = FALSE;
9063 tape.playing = FALSE;
9064 tape.pausing = FALSE;
9066 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9067 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9069 tape.no_info_chunk = TRUE;
9070 tape.no_valid_file = FALSE;
9073 static int getTapePosSize(struct TapeInfo *tape)
9075 int tape_pos_size = 0;
9077 if (tape->use_key_actions)
9078 tape_pos_size += tape->num_participating_players;
9080 if (tape->use_mouse_actions)
9081 tape_pos_size += 3; // x and y position and mouse button mask
9083 tape_pos_size += 1; // tape action delay value
9085 return tape_pos_size;
9088 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9090 tape->use_key_actions = FALSE;
9091 tape->use_mouse_actions = FALSE;
9093 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9094 tape->use_key_actions = TRUE;
9096 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9097 tape->use_mouse_actions = TRUE;
9100 static int getTapeActionValue(struct TapeInfo *tape)
9102 return (tape->use_key_actions &&
9103 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9104 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9105 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9106 TAPE_ACTIONS_DEFAULT);
9109 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9111 tape->file_version = getFileVersion(file);
9112 tape->game_version = getFileVersion(file);
9117 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9121 tape->random_seed = getFile32BitBE(file);
9122 tape->date = getFile32BitBE(file);
9123 tape->length = getFile32BitBE(file);
9125 // read header fields that are new since version 1.2
9126 if (tape->file_version >= FILE_VERSION_1_2)
9128 byte store_participating_players = getFile8Bit(file);
9131 // since version 1.2, tapes store which players participate in the tape
9132 tape->num_participating_players = 0;
9133 for (i = 0; i < MAX_PLAYERS; i++)
9135 tape->player_participates[i] = FALSE;
9137 if (store_participating_players & (1 << i))
9139 tape->player_participates[i] = TRUE;
9140 tape->num_participating_players++;
9144 setTapeActionFlags(tape, getFile8Bit(file));
9146 tape->property_bits = getFile8Bit(file);
9147 tape->solved = getFile8Bit(file);
9149 engine_version = getFileVersion(file);
9150 if (engine_version > 0)
9151 tape->engine_version = engine_version;
9153 tape->engine_version = tape->game_version;
9159 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9161 tape->scr_fieldx = getFile8Bit(file);
9162 tape->scr_fieldy = getFile8Bit(file);
9167 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9169 char *level_identifier = NULL;
9170 int level_identifier_size;
9173 tape->no_info_chunk = FALSE;
9175 level_identifier_size = getFile16BitBE(file);
9177 level_identifier = checked_malloc(level_identifier_size);
9179 for (i = 0; i < level_identifier_size; i++)
9180 level_identifier[i] = getFile8Bit(file);
9182 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9183 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9185 checked_free(level_identifier);
9187 tape->level_nr = getFile16BitBE(file);
9189 chunk_size = 2 + level_identifier_size + 2;
9194 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9197 int tape_pos_size = getTapePosSize(tape);
9198 int chunk_size_expected = tape_pos_size * tape->length;
9200 if (chunk_size_expected != chunk_size)
9202 ReadUnusedBytesFromFile(file, chunk_size);
9203 return chunk_size_expected;
9206 for (i = 0; i < tape->length; i++)
9208 if (i >= MAX_TAPE_LEN)
9210 Warn("tape truncated -- size exceeds maximum tape size %d",
9213 // tape too large; read and ignore remaining tape data from this chunk
9214 for (;i < tape->length; i++)
9215 ReadUnusedBytesFromFile(file, tape_pos_size);
9220 if (tape->use_key_actions)
9222 for (j = 0; j < MAX_PLAYERS; j++)
9224 tape->pos[i].action[j] = MV_NONE;
9226 if (tape->player_participates[j])
9227 tape->pos[i].action[j] = getFile8Bit(file);
9231 if (tape->use_mouse_actions)
9233 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9234 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9235 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9238 tape->pos[i].delay = getFile8Bit(file);
9240 if (tape->file_version == FILE_VERSION_1_0)
9242 // eliminate possible diagonal moves in old tapes
9243 // this is only for backward compatibility
9245 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9246 byte action = tape->pos[i].action[0];
9247 int k, num_moves = 0;
9249 for (k = 0; k < 4; k++)
9251 if (action & joy_dir[k])
9253 tape->pos[i + num_moves].action[0] = joy_dir[k];
9255 tape->pos[i + num_moves].delay = 0;
9264 tape->length += num_moves;
9267 else if (tape->file_version < FILE_VERSION_2_0)
9269 // convert pre-2.0 tapes to new tape format
9271 if (tape->pos[i].delay > 1)
9274 tape->pos[i + 1] = tape->pos[i];
9275 tape->pos[i + 1].delay = 1;
9278 for (j = 0; j < MAX_PLAYERS; j++)
9279 tape->pos[i].action[j] = MV_NONE;
9280 tape->pos[i].delay--;
9287 if (checkEndOfFile(file))
9291 if (i != tape->length)
9292 chunk_size = tape_pos_size * i;
9297 static void LoadTape_SokobanSolution(char *filename)
9300 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9302 if (!(file = openFile(filename, MODE_READ)))
9304 tape.no_valid_file = TRUE;
9309 while (!checkEndOfFile(file))
9311 unsigned char c = getByteFromFile(file);
9313 if (checkEndOfFile(file))
9320 tape.pos[tape.length].action[0] = MV_UP;
9321 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9327 tape.pos[tape.length].action[0] = MV_DOWN;
9328 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9334 tape.pos[tape.length].action[0] = MV_LEFT;
9335 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9341 tape.pos[tape.length].action[0] = MV_RIGHT;
9342 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9350 // ignore white-space characters
9354 tape.no_valid_file = TRUE;
9356 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9364 if (tape.no_valid_file)
9367 tape.length_frames = GetTapeLengthFrames();
9368 tape.length_seconds = GetTapeLengthSeconds();
9371 void LoadTapeFromFilename(char *filename)
9373 char cookie[MAX_LINE_LEN];
9374 char chunk_name[CHUNK_ID_LEN + 1];
9378 // always start with reliable default values
9379 setTapeInfoToDefaults();
9381 if (strSuffix(filename, ".sln"))
9383 LoadTape_SokobanSolution(filename);
9388 if (!(file = openFile(filename, MODE_READ)))
9390 tape.no_valid_file = TRUE;
9395 getFileChunkBE(file, chunk_name, NULL);
9396 if (strEqual(chunk_name, "RND1"))
9398 getFile32BitBE(file); // not used
9400 getFileChunkBE(file, chunk_name, NULL);
9401 if (!strEqual(chunk_name, "TAPE"))
9403 tape.no_valid_file = TRUE;
9405 Warn("unknown format of tape file '%s'", filename);
9412 else // check for pre-2.0 file format with cookie string
9414 strcpy(cookie, chunk_name);
9415 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9417 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9418 cookie[strlen(cookie) - 1] = '\0';
9420 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9422 tape.no_valid_file = TRUE;
9424 Warn("unknown format of tape file '%s'", filename);
9431 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9433 tape.no_valid_file = TRUE;
9435 Warn("unsupported version of tape file '%s'", filename);
9442 // pre-2.0 tape files have no game version, so use file version here
9443 tape.game_version = tape.file_version;
9446 if (tape.file_version < FILE_VERSION_1_2)
9448 // tape files from versions before 1.2.0 without chunk structure
9449 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9450 LoadTape_BODY(file, 2 * tape.length, &tape);
9458 int (*loader)(File *, int, struct TapeInfo *);
9462 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9463 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9464 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9465 { "INFO", -1, LoadTape_INFO },
9466 { "BODY", -1, LoadTape_BODY },
9470 while (getFileChunkBE(file, chunk_name, &chunk_size))
9474 while (chunk_info[i].name != NULL &&
9475 !strEqual(chunk_name, chunk_info[i].name))
9478 if (chunk_info[i].name == NULL)
9480 Warn("unknown chunk '%s' in tape file '%s'",
9481 chunk_name, filename);
9483 ReadUnusedBytesFromFile(file, chunk_size);
9485 else if (chunk_info[i].size != -1 &&
9486 chunk_info[i].size != chunk_size)
9488 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9489 chunk_size, chunk_name, filename);
9491 ReadUnusedBytesFromFile(file, chunk_size);
9495 // call function to load this tape chunk
9496 int chunk_size_expected =
9497 (chunk_info[i].loader)(file, chunk_size, &tape);
9499 // the size of some chunks cannot be checked before reading other
9500 // chunks first (like "HEAD" and "BODY") that contain some header
9501 // information, so check them here
9502 if (chunk_size_expected != chunk_size)
9504 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9505 chunk_size, chunk_name, filename);
9513 tape.length_frames = GetTapeLengthFrames();
9514 tape.length_seconds = GetTapeLengthSeconds();
9517 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9519 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9521 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9522 tape.engine_version);
9526 void LoadTape(int nr)
9528 char *filename = getTapeFilename(nr);
9530 LoadTapeFromFilename(filename);
9533 void LoadSolutionTape(int nr)
9535 char *filename = getSolutionTapeFilename(nr);
9537 LoadTapeFromFilename(filename);
9539 if (TAPE_IS_EMPTY(tape))
9541 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9542 level.native_bd_level->replay != NULL)
9543 CopyNativeTape_BD_to_RND(&level);
9544 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9545 level.native_sp_level->demo.is_available)
9546 CopyNativeTape_SP_to_RND(&level);
9550 void LoadScoreTape(char *score_tape_basename, int nr)
9552 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9554 LoadTapeFromFilename(filename);
9557 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9559 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9561 LoadTapeFromFilename(filename);
9564 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9566 // chunk required for team mode tapes with non-default screen size
9567 return (tape->num_participating_players > 1 &&
9568 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9569 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9572 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9574 putFileVersion(file, tape->file_version);
9575 putFileVersion(file, tape->game_version);
9578 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9581 byte store_participating_players = 0;
9583 // set bits for participating players for compact storage
9584 for (i = 0; i < MAX_PLAYERS; i++)
9585 if (tape->player_participates[i])
9586 store_participating_players |= (1 << i);
9588 putFile32BitBE(file, tape->random_seed);
9589 putFile32BitBE(file, tape->date);
9590 putFile32BitBE(file, tape->length);
9592 putFile8Bit(file, store_participating_players);
9594 putFile8Bit(file, getTapeActionValue(tape));
9596 putFile8Bit(file, tape->property_bits);
9597 putFile8Bit(file, tape->solved);
9599 putFileVersion(file, tape->engine_version);
9602 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9604 putFile8Bit(file, tape->scr_fieldx);
9605 putFile8Bit(file, tape->scr_fieldy);
9608 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9610 int level_identifier_size = strlen(tape->level_identifier) + 1;
9613 putFile16BitBE(file, level_identifier_size);
9615 for (i = 0; i < level_identifier_size; i++)
9616 putFile8Bit(file, tape->level_identifier[i]);
9618 putFile16BitBE(file, tape->level_nr);
9621 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9625 for (i = 0; i < tape->length; i++)
9627 if (tape->use_key_actions)
9629 for (j = 0; j < MAX_PLAYERS; j++)
9630 if (tape->player_participates[j])
9631 putFile8Bit(file, tape->pos[i].action[j]);
9634 if (tape->use_mouse_actions)
9636 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9637 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9638 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9641 putFile8Bit(file, tape->pos[i].delay);
9645 void SaveTapeToFilename(char *filename)
9649 int info_chunk_size;
9650 int body_chunk_size;
9652 if (!(file = fopen(filename, MODE_WRITE)))
9654 Warn("cannot save level recording file '%s'", filename);
9659 tape_pos_size = getTapePosSize(&tape);
9661 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9662 body_chunk_size = tape_pos_size * tape.length;
9664 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9665 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9667 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9668 SaveTape_VERS(file, &tape);
9670 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9671 SaveTape_HEAD(file, &tape);
9673 if (checkSaveTape_SCRN(&tape))
9675 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9676 SaveTape_SCRN(file, &tape);
9679 putFileChunkBE(file, "INFO", info_chunk_size);
9680 SaveTape_INFO(file, &tape);
9682 putFileChunkBE(file, "BODY", body_chunk_size);
9683 SaveTape_BODY(file, &tape);
9687 SetFilePermissions(filename, PERMS_PRIVATE);
9690 static void SaveTapeExt(char *filename)
9694 tape.file_version = FILE_VERSION_ACTUAL;
9695 tape.game_version = GAME_VERSION_ACTUAL;
9697 tape.num_participating_players = 0;
9699 // count number of participating players
9700 for (i = 0; i < MAX_PLAYERS; i++)
9701 if (tape.player_participates[i])
9702 tape.num_participating_players++;
9704 SaveTapeToFilename(filename);
9706 tape.changed = FALSE;
9709 void SaveTape(int nr)
9711 char *filename = getTapeFilename(nr);
9713 InitTapeDirectory(leveldir_current->subdir);
9715 SaveTapeExt(filename);
9718 void SaveScoreTape(int nr)
9720 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9722 // used instead of "leveldir_current->subdir" (for network games)
9723 InitScoreTapeDirectory(levelset.identifier, nr);
9725 SaveTapeExt(filename);
9728 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9729 unsigned int req_state_added)
9731 char *filename = getTapeFilename(nr);
9732 boolean new_tape = !fileExists(filename);
9733 boolean tape_saved = FALSE;
9735 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9740 Request(msg_saved, REQ_CONFIRM | req_state_added);
9748 boolean SaveTapeChecked(int nr)
9750 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9753 boolean SaveTapeChecked_LevelSolved(int nr)
9755 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9756 "Level solved! Tape saved!", REQ_STAY_OPEN);
9759 void DumpTape(struct TapeInfo *tape)
9761 int tape_frame_counter;
9764 if (tape->no_valid_file)
9766 Warn("cannot dump -- no valid tape file found");
9773 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9774 tape->level_nr, tape->file_version, tape->game_version);
9775 Print(" (effective engine version %08d)\n",
9776 tape->engine_version);
9777 Print("Level series identifier: '%s'\n", tape->level_identifier);
9779 Print("Solution tape: %s\n",
9780 tape->solved ? "yes" :
9781 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9783 Print("Special tape properties: ");
9784 if (tape->property_bits == TAPE_PROPERTY_NONE)
9786 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9787 Print("[em_random_bug]");
9788 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9789 Print("[game_speed]");
9790 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9792 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9793 Print("[single_step]");
9794 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9795 Print("[snapshot]");
9796 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9797 Print("[replayed]");
9798 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9799 Print("[tas_keys]");
9800 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9801 Print("[small_graphics]");
9804 int year2 = tape->date / 10000;
9805 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9806 int month_index_raw = (tape->date / 100) % 100;
9807 int month_index = month_index_raw % 12; // prevent invalid index
9808 int month = month_index + 1;
9809 int day = tape->date % 100;
9811 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9815 tape_frame_counter = 0;
9817 for (i = 0; i < tape->length; i++)
9819 if (i >= MAX_TAPE_LEN)
9824 for (j = 0; j < MAX_PLAYERS; j++)
9826 if (tape->player_participates[j])
9828 int action = tape->pos[i].action[j];
9830 Print("%d:%02x ", j, action);
9831 Print("[%c%c%c%c|%c%c] - ",
9832 (action & JOY_LEFT ? '<' : ' '),
9833 (action & JOY_RIGHT ? '>' : ' '),
9834 (action & JOY_UP ? '^' : ' '),
9835 (action & JOY_DOWN ? 'v' : ' '),
9836 (action & JOY_BUTTON_1 ? '1' : ' '),
9837 (action & JOY_BUTTON_2 ? '2' : ' '));
9841 Print("(%03d) ", tape->pos[i].delay);
9842 Print("[%05d]\n", tape_frame_counter);
9844 tape_frame_counter += tape->pos[i].delay;
9850 void DumpTapes(void)
9852 static LevelDirTree *dumptape_leveldir = NULL;
9854 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9855 global.dumptape_leveldir);
9857 if (dumptape_leveldir == NULL)
9858 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9860 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9861 global.dumptape_level_nr > dumptape_leveldir->last_level)
9862 Fail("no such level number: %d", global.dumptape_level_nr);
9864 leveldir_current = dumptape_leveldir;
9866 if (options.mytapes)
9867 LoadTape(global.dumptape_level_nr);
9869 LoadSolutionTape(global.dumptape_level_nr);
9877 // ============================================================================
9878 // score file functions
9879 // ============================================================================
9881 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9885 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9887 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9888 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9889 scores->entry[i].score = 0;
9890 scores->entry[i].time = 0;
9892 scores->entry[i].id = -1;
9893 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9894 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9895 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9896 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9897 strcpy(scores->entry[i].country_code, "??");
9900 scores->num_entries = 0;
9901 scores->last_added = -1;
9902 scores->last_added_local = -1;
9904 scores->updated = FALSE;
9905 scores->uploaded = FALSE;
9906 scores->tape_downloaded = FALSE;
9907 scores->force_last_added = FALSE;
9909 // The following values are intentionally not reset here:
9913 // - continue_playing
9914 // - continue_on_return
9917 static void setScoreInfoToDefaults(void)
9919 setScoreInfoToDefaultsExt(&scores);
9922 static void setServerScoreInfoToDefaults(void)
9924 setScoreInfoToDefaultsExt(&server_scores);
9927 static void LoadScore_OLD(int nr)
9930 char *filename = getScoreFilename(nr);
9931 char cookie[MAX_LINE_LEN];
9932 char line[MAX_LINE_LEN];
9936 if (!(file = fopen(filename, MODE_READ)))
9939 // check file identifier
9940 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9942 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9943 cookie[strlen(cookie) - 1] = '\0';
9945 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9947 Warn("unknown format of score file '%s'", filename);
9954 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9956 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9957 Warn("fscanf() failed; %s", strerror(errno));
9959 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9962 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9963 line[strlen(line) - 1] = '\0';
9965 for (line_ptr = line; *line_ptr; line_ptr++)
9967 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9969 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9970 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9979 static void ConvertScore_OLD(void)
9981 // only convert score to time for levels that rate playing time over score
9982 if (!level.rate_time_over_score)
9985 // convert old score to playing time for score-less levels (like Supaplex)
9986 int time_final_max = 999;
9989 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9991 int score = scores.entry[i].score;
9993 if (score > 0 && score < time_final_max)
9994 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9998 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10000 scores->file_version = getFileVersion(file);
10001 scores->game_version = getFileVersion(file);
10006 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10008 char *level_identifier = NULL;
10009 int level_identifier_size;
10012 level_identifier_size = getFile16BitBE(file);
10014 level_identifier = checked_malloc(level_identifier_size);
10016 for (i = 0; i < level_identifier_size; i++)
10017 level_identifier[i] = getFile8Bit(file);
10019 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10020 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10022 checked_free(level_identifier);
10024 scores->level_nr = getFile16BitBE(file);
10025 scores->num_entries = getFile16BitBE(file);
10027 chunk_size = 2 + level_identifier_size + 2 + 2;
10032 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10036 for (i = 0; i < scores->num_entries; i++)
10038 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10039 scores->entry[i].name[j] = getFile8Bit(file);
10041 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10044 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10049 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10053 for (i = 0; i < scores->num_entries; i++)
10054 scores->entry[i].score = getFile16BitBE(file);
10056 chunk_size = scores->num_entries * 2;
10061 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10065 for (i = 0; i < scores->num_entries; i++)
10066 scores->entry[i].score = getFile32BitBE(file);
10068 chunk_size = scores->num_entries * 4;
10073 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10077 for (i = 0; i < scores->num_entries; i++)
10078 scores->entry[i].time = getFile32BitBE(file);
10080 chunk_size = scores->num_entries * 4;
10085 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10089 for (i = 0; i < scores->num_entries; i++)
10091 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10092 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10094 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10097 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10102 void LoadScore(int nr)
10104 char *filename = getScoreFilename(nr);
10105 char cookie[MAX_LINE_LEN];
10106 char chunk_name[CHUNK_ID_LEN + 1];
10108 boolean old_score_file_format = FALSE;
10111 // always start with reliable default values
10112 setScoreInfoToDefaults();
10114 if (!(file = openFile(filename, MODE_READ)))
10117 getFileChunkBE(file, chunk_name, NULL);
10118 if (strEqual(chunk_name, "RND1"))
10120 getFile32BitBE(file); // not used
10122 getFileChunkBE(file, chunk_name, NULL);
10123 if (!strEqual(chunk_name, "SCOR"))
10125 Warn("unknown format of score file '%s'", filename);
10132 else // check for old file format with cookie string
10134 strcpy(cookie, chunk_name);
10135 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10137 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10138 cookie[strlen(cookie) - 1] = '\0';
10140 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10142 Warn("unknown format of score file '%s'", filename);
10149 old_score_file_format = TRUE;
10152 if (old_score_file_format)
10154 // score files from versions before 4.2.4.0 without chunk structure
10157 // convert score to time, if possible (mainly for Supaplex levels)
10158 ConvertScore_OLD();
10166 int (*loader)(File *, int, struct ScoreInfo *);
10170 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10171 { "INFO", -1, LoadScore_INFO },
10172 { "NAME", -1, LoadScore_NAME },
10173 { "SCOR", -1, LoadScore_SCOR },
10174 { "SC4R", -1, LoadScore_SC4R },
10175 { "TIME", -1, LoadScore_TIME },
10176 { "TAPE", -1, LoadScore_TAPE },
10181 while (getFileChunkBE(file, chunk_name, &chunk_size))
10185 while (chunk_info[i].name != NULL &&
10186 !strEqual(chunk_name, chunk_info[i].name))
10189 if (chunk_info[i].name == NULL)
10191 Warn("unknown chunk '%s' in score file '%s'",
10192 chunk_name, filename);
10194 ReadUnusedBytesFromFile(file, chunk_size);
10196 else if (chunk_info[i].size != -1 &&
10197 chunk_info[i].size != chunk_size)
10199 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10200 chunk_size, chunk_name, filename);
10202 ReadUnusedBytesFromFile(file, chunk_size);
10206 // call function to load this score chunk
10207 int chunk_size_expected =
10208 (chunk_info[i].loader)(file, chunk_size, &scores);
10210 // the size of some chunks cannot be checked before reading other
10211 // chunks first (like "HEAD" and "BODY") that contain some header
10212 // information, so check them here
10213 if (chunk_size_expected != chunk_size)
10215 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10216 chunk_size, chunk_name, filename);
10225 #if ENABLE_HISTORIC_CHUNKS
10226 void SaveScore_OLD(int nr)
10229 char *filename = getScoreFilename(nr);
10232 // used instead of "leveldir_current->subdir" (for network games)
10233 InitScoreDirectory(levelset.identifier);
10235 if (!(file = fopen(filename, MODE_WRITE)))
10237 Warn("cannot save score for level %d", nr);
10242 fprintf(file, "%s\n\n", SCORE_COOKIE);
10244 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10245 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10249 SetFilePermissions(filename, PERMS_PRIVATE);
10253 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10255 putFileVersion(file, scores->file_version);
10256 putFileVersion(file, scores->game_version);
10259 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10261 int level_identifier_size = strlen(scores->level_identifier) + 1;
10264 putFile16BitBE(file, level_identifier_size);
10266 for (i = 0; i < level_identifier_size; i++)
10267 putFile8Bit(file, scores->level_identifier[i]);
10269 putFile16BitBE(file, scores->level_nr);
10270 putFile16BitBE(file, scores->num_entries);
10273 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10277 for (i = 0; i < scores->num_entries; i++)
10279 int name_size = strlen(scores->entry[i].name);
10281 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10282 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10286 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10290 for (i = 0; i < scores->num_entries; i++)
10291 putFile16BitBE(file, scores->entry[i].score);
10294 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10298 for (i = 0; i < scores->num_entries; i++)
10299 putFile32BitBE(file, scores->entry[i].score);
10302 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10306 for (i = 0; i < scores->num_entries; i++)
10307 putFile32BitBE(file, scores->entry[i].time);
10310 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10314 for (i = 0; i < scores->num_entries; i++)
10316 int size = strlen(scores->entry[i].tape_basename);
10318 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10319 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10323 static void SaveScoreToFilename(char *filename)
10326 int info_chunk_size;
10327 int name_chunk_size;
10328 int scor_chunk_size;
10329 int sc4r_chunk_size;
10330 int time_chunk_size;
10331 int tape_chunk_size;
10332 boolean has_large_score_values;
10335 if (!(file = fopen(filename, MODE_WRITE)))
10337 Warn("cannot save score file '%s'", filename);
10342 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10343 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10344 scor_chunk_size = scores.num_entries * 2;
10345 sc4r_chunk_size = scores.num_entries * 4;
10346 time_chunk_size = scores.num_entries * 4;
10347 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10349 has_large_score_values = FALSE;
10350 for (i = 0; i < scores.num_entries; i++)
10351 if (scores.entry[i].score > 0xffff)
10352 has_large_score_values = TRUE;
10354 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10355 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10357 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10358 SaveScore_VERS(file, &scores);
10360 putFileChunkBE(file, "INFO", info_chunk_size);
10361 SaveScore_INFO(file, &scores);
10363 putFileChunkBE(file, "NAME", name_chunk_size);
10364 SaveScore_NAME(file, &scores);
10366 if (has_large_score_values)
10368 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10369 SaveScore_SC4R(file, &scores);
10373 putFileChunkBE(file, "SCOR", scor_chunk_size);
10374 SaveScore_SCOR(file, &scores);
10377 putFileChunkBE(file, "TIME", time_chunk_size);
10378 SaveScore_TIME(file, &scores);
10380 putFileChunkBE(file, "TAPE", tape_chunk_size);
10381 SaveScore_TAPE(file, &scores);
10385 SetFilePermissions(filename, PERMS_PRIVATE);
10388 void SaveScore(int nr)
10390 char *filename = getScoreFilename(nr);
10393 // used instead of "leveldir_current->subdir" (for network games)
10394 InitScoreDirectory(levelset.identifier);
10396 scores.file_version = FILE_VERSION_ACTUAL;
10397 scores.game_version = GAME_VERSION_ACTUAL;
10399 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10400 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10401 scores.level_nr = level_nr;
10403 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10404 if (scores.entry[i].score == 0 &&
10405 scores.entry[i].time == 0 &&
10406 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10409 scores.num_entries = i;
10411 if (scores.num_entries == 0)
10414 SaveScoreToFilename(filename);
10417 static void LoadServerScoreFromCache(int nr)
10419 struct ScoreEntry score_entry;
10428 { &score_entry.score, FALSE, 0 },
10429 { &score_entry.time, FALSE, 0 },
10430 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10431 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10432 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10433 { &score_entry.id, FALSE, 0 },
10434 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10435 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10436 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10437 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10441 char *filename = getScoreCacheFilename(nr);
10442 SetupFileHash *score_hash = loadSetupFileHash(filename);
10445 server_scores.num_entries = 0;
10447 if (score_hash == NULL)
10450 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10452 score_entry = server_scores.entry[i];
10454 for (j = 0; score_mapping[j].value != NULL; j++)
10458 sprintf(token, "%02d.%d", i, j);
10460 char *value = getHashEntry(score_hash, token);
10465 if (score_mapping[j].is_string)
10467 char *score_value = (char *)score_mapping[j].value;
10468 int value_size = score_mapping[j].string_size;
10470 strncpy(score_value, value, value_size);
10471 score_value[value_size] = '\0';
10475 int *score_value = (int *)score_mapping[j].value;
10477 *score_value = atoi(value);
10480 server_scores.num_entries = i + 1;
10483 server_scores.entry[i] = score_entry;
10486 freeSetupFileHash(score_hash);
10489 void LoadServerScore(int nr, boolean download_score)
10491 if (!setup.use_api_server)
10494 // always start with reliable default values
10495 setServerScoreInfoToDefaults();
10497 // 1st step: load server scores from cache file (which may not exist)
10498 // (this should prevent reading it while the thread is writing to it)
10499 LoadServerScoreFromCache(nr);
10501 if (download_score && runtime.use_api_server)
10503 // 2nd step: download server scores from score server to cache file
10504 // (as thread, as it might time out if the server is not reachable)
10505 ApiGetScoreAsThread(nr);
10509 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10511 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10513 // if score tape not uploaded, ask for uploading missing tapes later
10514 if (!setup.has_remaining_tapes)
10515 setup.ask_for_remaining_tapes = TRUE;
10517 setup.provide_uploading_tapes = TRUE;
10518 setup.has_remaining_tapes = TRUE;
10520 SaveSetup_ServerSetup();
10523 void SaveServerScore(int nr, boolean tape_saved)
10525 if (!runtime.use_api_server)
10527 PrepareScoreTapesForUpload(leveldir_current->subdir);
10532 ApiAddScoreAsThread(nr, tape_saved, NULL);
10535 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10536 char *score_tape_filename)
10538 if (!runtime.use_api_server)
10541 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10544 void LoadLocalAndServerScore(int nr, boolean download_score)
10546 int last_added_local = scores.last_added_local;
10547 boolean force_last_added = scores.force_last_added;
10549 // needed if only showing server scores
10550 setScoreInfoToDefaults();
10552 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10555 // restore last added local score entry (before merging server scores)
10556 scores.last_added = scores.last_added_local = last_added_local;
10558 if (setup.use_api_server &&
10559 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10561 // load server scores from cache file and trigger update from server
10562 LoadServerScore(nr, download_score);
10564 // merge local scores with scores from server
10565 MergeServerScore();
10568 if (force_last_added)
10569 scores.force_last_added = force_last_added;
10573 // ============================================================================
10574 // setup file functions
10575 // ============================================================================
10577 #define TOKEN_STR_PLAYER_PREFIX "player_"
10580 static struct TokenInfo global_setup_tokens[] =
10584 &setup.player_name, "player_name"
10588 &setup.multiple_users, "multiple_users"
10592 &setup.sound, "sound"
10596 &setup.sound_loops, "repeating_sound_loops"
10600 &setup.sound_music, "background_music"
10604 &setup.sound_simple, "simple_sound_effects"
10608 &setup.toons, "toons"
10612 &setup.global_animations, "global_animations"
10616 &setup.scroll_delay, "scroll_delay"
10620 &setup.forced_scroll_delay, "forced_scroll_delay"
10624 &setup.scroll_delay_value, "scroll_delay_value"
10628 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10632 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10636 &setup.fade_screens, "fade_screens"
10640 &setup.autorecord, "automatic_tape_recording"
10644 &setup.autorecord_after_replay, "autorecord_after_replay"
10648 &setup.auto_pause_on_start, "auto_pause_on_start"
10652 &setup.show_titlescreen, "show_titlescreen"
10656 &setup.quick_doors, "quick_doors"
10660 &setup.team_mode, "team_mode"
10664 &setup.handicap, "handicap"
10668 &setup.skip_levels, "skip_levels"
10672 &setup.increment_levels, "increment_levels"
10676 &setup.auto_play_next_level, "auto_play_next_level"
10680 &setup.count_score_after_game, "count_score_after_game"
10684 &setup.show_scores_after_game, "show_scores_after_game"
10688 &setup.time_limit, "time_limit"
10692 &setup.fullscreen, "fullscreen"
10696 &setup.window_scaling_percent, "window_scaling_percent"
10700 &setup.window_scaling_quality, "window_scaling_quality"
10704 &setup.screen_rendering_mode, "screen_rendering_mode"
10708 &setup.vsync_mode, "vsync_mode"
10712 &setup.ask_on_escape, "ask_on_escape"
10716 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10720 &setup.ask_on_game_over, "ask_on_game_over"
10724 &setup.ask_on_quit_game, "ask_on_quit_game"
10728 &setup.ask_on_quit_program, "ask_on_quit_program"
10732 &setup.quick_switch, "quick_player_switch"
10736 &setup.input_on_focus, "input_on_focus"
10740 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10744 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10748 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10752 &setup.game_speed_extended, "game_speed_extended"
10756 &setup.game_frame_delay, "game_frame_delay"
10760 &setup.default_game_engine_type, "default_game_engine_type"
10764 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10768 &setup.bd_skip_hatching, "bd_skip_hatching"
10772 &setup.bd_scroll_delay, "bd_scroll_delay"
10776 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10780 &setup.bd_smooth_movements, "bd_smooth_movements"
10784 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10788 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10792 &setup.bd_skip_falling_sounds, "bd_skip_falling_sounds"
10796 &setup.bd_palette_c64, "bd_palette_c64"
10800 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10804 &setup.bd_palette_atari, "bd_palette_atari"
10808 &setup.bd_default_color_type, "bd_default_color_type"
10812 &setup.bd_random_colors, "bd_random_colors"
10816 &setup.sp_show_border_elements, "sp_show_border_elements"
10820 &setup.small_game_graphics, "small_game_graphics"
10824 &setup.show_load_save_buttons, "show_load_save_buttons"
10828 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10832 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10836 &setup.graphics_set, "graphics_set"
10840 &setup.sounds_set, "sounds_set"
10844 &setup.music_set, "music_set"
10848 &setup.override_level_graphics, "override_level_graphics"
10852 &setup.override_level_sounds, "override_level_sounds"
10856 &setup.override_level_music, "override_level_music"
10860 &setup.volume_simple, "volume_simple"
10864 &setup.volume_loops, "volume_loops"
10868 &setup.volume_music, "volume_music"
10872 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10876 &setup.network_mode, "network_mode"
10880 &setup.network_player_nr, "network_player"
10884 &setup.network_server_hostname, "network_server_hostname"
10888 &setup.touch.control_type, "touch.control_type"
10892 &setup.touch.move_distance, "touch.move_distance"
10896 &setup.touch.drop_distance, "touch.drop_distance"
10900 &setup.touch.transparency, "touch.transparency"
10904 &setup.touch.draw_outlined, "touch.draw_outlined"
10908 &setup.touch.draw_pressed, "touch.draw_pressed"
10912 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10916 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10920 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10924 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10928 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10932 static struct TokenInfo auto_setup_tokens[] =
10936 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10940 static struct TokenInfo server_setup_tokens[] =
10944 &setup.player_uuid, "player_uuid"
10948 &setup.player_version, "player_version"
10952 &setup.use_api_server, TEST_PREFIX "use_api_server"
10956 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10960 &setup.api_server_password, TEST_PREFIX "api_server_password"
10964 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10968 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10972 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10976 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10980 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10984 static struct TokenInfo editor_setup_tokens[] =
10988 &setup.editor.el_classic, "editor.el_classic"
10992 &setup.editor.el_custom, "editor.el_custom"
10996 &setup.editor.el_user_defined, "editor.el_user_defined"
11000 &setup.editor.el_dynamic, "editor.el_dynamic"
11004 &setup.editor.el_headlines, "editor.el_headlines"
11008 &setup.editor.show_element_token, "editor.show_element_token"
11012 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11016 static struct TokenInfo editor_cascade_setup_tokens[] =
11020 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11024 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
11028 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
11032 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11036 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11040 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11044 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11048 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11052 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11056 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11060 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11064 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11068 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11072 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11076 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11080 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11084 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11088 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11092 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11096 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11100 static struct TokenInfo shortcut_setup_tokens[] =
11104 &setup.shortcut.save_game, "shortcut.save_game"
11108 &setup.shortcut.load_game, "shortcut.load_game"
11112 &setup.shortcut.restart_game, "shortcut.restart_game"
11116 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11120 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11124 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11128 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11132 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11136 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11140 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11144 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11148 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11152 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11156 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11160 &setup.shortcut.tape_record, "shortcut.tape_record"
11164 &setup.shortcut.tape_play, "shortcut.tape_play"
11168 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11172 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11176 &setup.shortcut.sound_music, "shortcut.sound_music"
11180 &setup.shortcut.snap_left, "shortcut.snap_left"
11184 &setup.shortcut.snap_right, "shortcut.snap_right"
11188 &setup.shortcut.snap_up, "shortcut.snap_up"
11192 &setup.shortcut.snap_down, "shortcut.snap_down"
11196 static struct SetupInputInfo setup_input;
11197 static struct TokenInfo player_setup_tokens[] =
11201 &setup_input.use_joystick, ".use_joystick"
11205 &setup_input.joy.device_name, ".joy.device_name"
11209 &setup_input.joy.xleft, ".joy.xleft"
11213 &setup_input.joy.xmiddle, ".joy.xmiddle"
11217 &setup_input.joy.xright, ".joy.xright"
11221 &setup_input.joy.yupper, ".joy.yupper"
11225 &setup_input.joy.ymiddle, ".joy.ymiddle"
11229 &setup_input.joy.ylower, ".joy.ylower"
11233 &setup_input.joy.snap, ".joy.snap_field"
11237 &setup_input.joy.drop, ".joy.place_bomb"
11241 &setup_input.key.left, ".key.move_left"
11245 &setup_input.key.right, ".key.move_right"
11249 &setup_input.key.up, ".key.move_up"
11253 &setup_input.key.down, ".key.move_down"
11257 &setup_input.key.snap, ".key.snap_field"
11261 &setup_input.key.drop, ".key.place_bomb"
11265 static struct TokenInfo system_setup_tokens[] =
11269 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11273 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11277 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11281 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11285 static struct TokenInfo internal_setup_tokens[] =
11289 &setup.internal.program_title, "program_title"
11293 &setup.internal.program_version, "program_version"
11297 &setup.internal.program_author, "program_author"
11301 &setup.internal.program_email, "program_email"
11305 &setup.internal.program_website, "program_website"
11309 &setup.internal.program_copyright, "program_copyright"
11313 &setup.internal.program_company, "program_company"
11317 &setup.internal.program_icon_file, "program_icon_file"
11321 &setup.internal.default_graphics_set, "default_graphics_set"
11325 &setup.internal.default_sounds_set, "default_sounds_set"
11329 &setup.internal.default_music_set, "default_music_set"
11333 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11337 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11341 &setup.internal.fallback_music_file, "fallback_music_file"
11345 &setup.internal.default_level_series, "default_level_series"
11349 &setup.internal.default_window_width, "default_window_width"
11353 &setup.internal.default_window_height, "default_window_height"
11357 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11361 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11365 &setup.internal.create_user_levelset, "create_user_levelset"
11369 &setup.internal.info_screens_from_main, "info_screens_from_main"
11373 &setup.internal.menu_game, "menu_game"
11377 &setup.internal.menu_engines, "menu_engines"
11381 &setup.internal.menu_editor, "menu_editor"
11385 &setup.internal.menu_graphics, "menu_graphics"
11389 &setup.internal.menu_sound, "menu_sound"
11393 &setup.internal.menu_artwork, "menu_artwork"
11397 &setup.internal.menu_input, "menu_input"
11401 &setup.internal.menu_touch, "menu_touch"
11405 &setup.internal.menu_shortcuts, "menu_shortcuts"
11409 &setup.internal.menu_exit, "menu_exit"
11413 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11417 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11421 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11425 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11429 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11433 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11437 &setup.internal.info_title, "info_title"
11441 &setup.internal.info_elements, "info_elements"
11445 &setup.internal.info_music, "info_music"
11449 &setup.internal.info_credits, "info_credits"
11453 &setup.internal.info_program, "info_program"
11457 &setup.internal.info_version, "info_version"
11461 &setup.internal.info_levelset, "info_levelset"
11465 &setup.internal.info_exit, "info_exit"
11469 static struct TokenInfo debug_setup_tokens[] =
11473 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11477 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11481 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11485 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11489 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11493 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11497 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11501 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11505 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11509 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11513 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11517 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11521 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11525 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11529 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11533 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11537 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11541 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11545 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11549 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11553 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11556 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11560 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11564 &setup.debug.xsn_mode, "debug.xsn_mode"
11568 &setup.debug.xsn_percent, "debug.xsn_percent"
11572 static struct TokenInfo options_setup_tokens[] =
11576 &setup.options.verbose, "options.verbose"
11580 &setup.options.debug, "options.debug"
11584 &setup.options.debug_mode, "options.debug_mode"
11588 static void setSetupInfoToDefaults(struct SetupInfo *si)
11592 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11594 si->multiple_users = TRUE;
11597 si->sound_loops = TRUE;
11598 si->sound_music = TRUE;
11599 si->sound_simple = TRUE;
11601 si->global_animations = TRUE;
11602 si->scroll_delay = TRUE;
11603 si->forced_scroll_delay = FALSE;
11604 si->scroll_delay_value = STD_SCROLL_DELAY;
11605 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11606 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11607 si->fade_screens = TRUE;
11608 si->autorecord = TRUE;
11609 si->autorecord_after_replay = TRUE;
11610 si->auto_pause_on_start = FALSE;
11611 si->show_titlescreen = TRUE;
11612 si->quick_doors = FALSE;
11613 si->team_mode = FALSE;
11614 si->handicap = TRUE;
11615 si->skip_levels = TRUE;
11616 si->increment_levels = TRUE;
11617 si->auto_play_next_level = TRUE;
11618 si->count_score_after_game = TRUE;
11619 si->show_scores_after_game = TRUE;
11620 si->time_limit = TRUE;
11621 si->fullscreen = FALSE;
11622 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11623 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11624 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11625 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11626 si->ask_on_escape = TRUE;
11627 si->ask_on_escape_editor = TRUE;
11628 si->ask_on_game_over = TRUE;
11629 si->ask_on_quit_game = TRUE;
11630 si->ask_on_quit_program = TRUE;
11631 si->quick_switch = FALSE;
11632 si->input_on_focus = FALSE;
11633 si->prefer_aga_graphics = TRUE;
11634 si->prefer_lowpass_sounds = FALSE;
11635 si->prefer_extra_panel_items = TRUE;
11636 si->game_speed_extended = FALSE;
11637 si->game_frame_delay = GAME_FRAME_DELAY;
11638 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11639 si->bd_skip_uncovering = FALSE;
11640 si->bd_skip_hatching = FALSE;
11641 si->bd_scroll_delay = TRUE;
11642 si->bd_show_invisible_outbox = FALSE;
11643 si->bd_smooth_movements = AUTO;
11644 si->bd_pushing_graphics = TRUE;
11645 si->bd_up_down_graphics = TRUE;
11646 si->bd_skip_falling_sounds = AUTO;
11647 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11648 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11649 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11650 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11651 si->bd_random_colors = FALSE;
11652 si->sp_show_border_elements = FALSE;
11653 si->small_game_graphics = FALSE;
11654 si->show_load_save_buttons = FALSE;
11655 si->show_undo_redo_buttons = FALSE;
11656 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11658 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11659 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11660 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11662 si->override_level_graphics = FALSE;
11663 si->override_level_sounds = FALSE;
11664 si->override_level_music = FALSE;
11666 si->volume_simple = 100; // percent
11667 si->volume_loops = 100; // percent
11668 si->volume_music = 100; // percent
11669 si->audio_sample_rate_44100 = FALSE;
11671 si->network_mode = FALSE;
11672 si->network_player_nr = 0; // first player
11673 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11675 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11676 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11677 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11678 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11679 si->touch.draw_outlined = TRUE;
11680 si->touch.draw_pressed = TRUE;
11682 for (i = 0; i < 2; i++)
11684 char *default_grid_button[6][2] =
11690 { "111222", " vv " },
11691 { "111222", " vv " }
11693 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11694 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11695 int min_xsize = MIN(6, grid_xsize);
11696 int min_ysize = MIN(6, grid_ysize);
11697 int startx = grid_xsize - min_xsize;
11698 int starty = grid_ysize - min_ysize;
11701 // virtual buttons grid can only be set to defaults if video is initialized
11702 // (this will be repeated if virtual buttons are not loaded from setup file)
11703 if (video.initialized)
11705 si->touch.grid_xsize[i] = grid_xsize;
11706 si->touch.grid_ysize[i] = grid_ysize;
11710 si->touch.grid_xsize[i] = -1;
11711 si->touch.grid_ysize[i] = -1;
11714 for (x = 0; x < MAX_GRID_XSIZE; x++)
11715 for (y = 0; y < MAX_GRID_YSIZE; y++)
11716 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11718 for (x = 0; x < min_xsize; x++)
11719 for (y = 0; y < min_ysize; y++)
11720 si->touch.grid_button[i][x][starty + y] =
11721 default_grid_button[y][0][x];
11723 for (x = 0; x < min_xsize; x++)
11724 for (y = 0; y < min_ysize; y++)
11725 si->touch.grid_button[i][startx + x][starty + y] =
11726 default_grid_button[y][1][x];
11729 si->touch.grid_initialized = video.initialized;
11731 si->touch.overlay_buttons = FALSE;
11733 si->editor.el_boulderdash = TRUE;
11734 si->editor.el_boulderdash_native = TRUE;
11735 si->editor.el_boulderdash_effects = TRUE;
11736 si->editor.el_emerald_mine = TRUE;
11737 si->editor.el_emerald_mine_club = TRUE;
11738 si->editor.el_more = TRUE;
11739 si->editor.el_sokoban = TRUE;
11740 si->editor.el_supaplex = TRUE;
11741 si->editor.el_diamond_caves = TRUE;
11742 si->editor.el_dx_boulderdash = TRUE;
11744 si->editor.el_mirror_magic = TRUE;
11745 si->editor.el_deflektor = TRUE;
11747 si->editor.el_chars = TRUE;
11748 si->editor.el_steel_chars = TRUE;
11750 si->editor.el_classic = TRUE;
11751 si->editor.el_custom = TRUE;
11753 si->editor.el_user_defined = FALSE;
11754 si->editor.el_dynamic = TRUE;
11756 si->editor.el_headlines = TRUE;
11758 si->editor.show_element_token = FALSE;
11760 si->editor.show_read_only_warning = TRUE;
11762 si->editor.use_template_for_new_levels = TRUE;
11764 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11765 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11766 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11767 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11768 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11770 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11771 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11772 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11773 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11774 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11776 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11777 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11778 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11779 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11780 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11781 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11783 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11784 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11785 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11787 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11788 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11789 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11790 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11792 for (i = 0; i < MAX_PLAYERS; i++)
11794 si->input[i].use_joystick = FALSE;
11795 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11796 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11797 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11798 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11799 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11800 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11801 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11802 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11803 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11804 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11805 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11806 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11807 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11808 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11809 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11812 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11813 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11814 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11815 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11817 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11818 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11819 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11820 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11821 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11822 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11823 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11825 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11827 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11828 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11829 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11831 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11832 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11833 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11835 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11836 si->internal.choose_from_top_leveldir = FALSE;
11837 si->internal.show_scaling_in_title = TRUE;
11838 si->internal.create_user_levelset = TRUE;
11839 si->internal.info_screens_from_main = FALSE;
11841 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11842 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11844 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11845 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11846 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11847 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11848 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11849 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11850 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11851 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11852 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11853 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11855 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11856 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11857 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11858 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11859 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11860 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11861 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11862 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11863 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11864 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11866 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11867 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11869 si->debug.show_frames_per_second = FALSE;
11871 si->debug.xsn_mode = AUTO;
11872 si->debug.xsn_percent = 0;
11874 si->options.verbose = FALSE;
11875 si->options.debug = FALSE;
11876 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11878 #if defined(PLATFORM_ANDROID)
11879 si->fullscreen = TRUE;
11880 si->touch.overlay_buttons = TRUE;
11883 setHideSetupEntry(&setup.debug.xsn_mode);
11886 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11888 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11891 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11893 si->player_uuid = NULL; // (will be set later)
11894 si->player_version = 1; // (will be set later)
11896 si->use_api_server = TRUE;
11897 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11898 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11899 si->ask_for_uploading_tapes = TRUE;
11900 si->ask_for_remaining_tapes = FALSE;
11901 si->provide_uploading_tapes = TRUE;
11902 si->ask_for_using_api_server = TRUE;
11903 si->has_remaining_tapes = FALSE;
11906 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11908 si->editor_cascade.el_bd = TRUE;
11909 si->editor_cascade.el_bd_native = TRUE;
11910 si->editor_cascade.el_bd_effects = FALSE;
11911 si->editor_cascade.el_em = TRUE;
11912 si->editor_cascade.el_emc = TRUE;
11913 si->editor_cascade.el_rnd = TRUE;
11914 si->editor_cascade.el_sb = TRUE;
11915 si->editor_cascade.el_sp = TRUE;
11916 si->editor_cascade.el_dc = TRUE;
11917 si->editor_cascade.el_dx = TRUE;
11919 si->editor_cascade.el_mm = TRUE;
11920 si->editor_cascade.el_df = TRUE;
11922 si->editor_cascade.el_chars = FALSE;
11923 si->editor_cascade.el_steel_chars = FALSE;
11924 si->editor_cascade.el_ce = FALSE;
11925 si->editor_cascade.el_ge = FALSE;
11926 si->editor_cascade.el_es = FALSE;
11927 si->editor_cascade.el_ref = FALSE;
11928 si->editor_cascade.el_user = FALSE;
11929 si->editor_cascade.el_dynamic = FALSE;
11932 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11934 static char *getHideSetupToken(void *setup_value)
11936 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11938 if (setup_value != NULL)
11939 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11941 return hide_setup_token;
11944 void setHideSetupEntry(void *setup_value)
11946 char *hide_setup_token = getHideSetupToken(setup_value);
11948 if (hide_setup_hash == NULL)
11949 hide_setup_hash = newSetupFileHash();
11951 if (setup_value != NULL)
11952 setHashEntry(hide_setup_hash, hide_setup_token, "");
11955 void removeHideSetupEntry(void *setup_value)
11957 char *hide_setup_token = getHideSetupToken(setup_value);
11959 if (setup_value != NULL)
11960 removeHashEntry(hide_setup_hash, hide_setup_token);
11963 boolean hideSetupEntry(void *setup_value)
11965 char *hide_setup_token = getHideSetupToken(setup_value);
11967 return (setup_value != NULL &&
11968 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11971 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11972 struct TokenInfo *token_info,
11973 int token_nr, char *token_text)
11975 char *token_hide_text = getStringCat2(token_text, ".hide");
11976 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11978 // set the value of this setup option in the setup option structure
11979 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11981 // check if this setup option should be hidden in the setup menu
11982 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11983 setHideSetupEntry(token_info[token_nr].value);
11985 free(token_hide_text);
11988 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11989 struct TokenInfo *token_info,
11992 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11993 token_info[token_nr].text);
11996 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12000 if (!setup_file_hash)
12003 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12004 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12006 setup.touch.grid_initialized = TRUE;
12007 for (i = 0; i < 2; i++)
12009 int grid_xsize = setup.touch.grid_xsize[i];
12010 int grid_ysize = setup.touch.grid_ysize[i];
12013 // if virtual buttons are not loaded from setup file, repeat initializing
12014 // virtual buttons grid with default values later when video is initialized
12015 if (grid_xsize == -1 ||
12018 setup.touch.grid_initialized = FALSE;
12023 for (y = 0; y < grid_ysize; y++)
12025 char token_string[MAX_LINE_LEN];
12027 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12029 char *value_string = getHashEntry(setup_file_hash, token_string);
12031 if (value_string == NULL)
12034 for (x = 0; x < grid_xsize; x++)
12036 char c = value_string[x];
12038 setup.touch.grid_button[i][x][y] =
12039 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12044 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12045 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12047 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12048 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12050 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12054 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12056 setup_input = setup.input[pnr];
12057 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12059 char full_token[100];
12061 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12062 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12065 setup.input[pnr] = setup_input;
12068 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12069 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12071 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12072 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12074 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12075 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12077 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12078 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12080 setHideRelatedSetupEntries();
12083 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12087 if (!setup_file_hash)
12090 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12091 setSetupInfo(auto_setup_tokens, i,
12092 getHashEntry(setup_file_hash,
12093 auto_setup_tokens[i].text));
12096 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12100 if (!setup_file_hash)
12103 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12104 setSetupInfo(server_setup_tokens, i,
12105 getHashEntry(setup_file_hash,
12106 server_setup_tokens[i].text));
12109 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12113 if (!setup_file_hash)
12116 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12117 setSetupInfo(editor_cascade_setup_tokens, i,
12118 getHashEntry(setup_file_hash,
12119 editor_cascade_setup_tokens[i].text));
12122 void LoadUserNames(void)
12124 int last_user_nr = user.nr;
12127 if (global.user_names != NULL)
12129 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12130 checked_free(global.user_names[i]);
12132 checked_free(global.user_names);
12135 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12137 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12141 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12143 if (setup_file_hash)
12145 char *player_name = getHashEntry(setup_file_hash, "player_name");
12147 global.user_names[i] = getFixedUserName(player_name);
12149 freeSetupFileHash(setup_file_hash);
12152 if (global.user_names[i] == NULL)
12153 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12156 user.nr = last_user_nr;
12159 void LoadSetupFromFilename(char *filename)
12161 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12163 if (setup_file_hash)
12165 decodeSetupFileHash_Default(setup_file_hash);
12167 freeSetupFileHash(setup_file_hash);
12171 Debug("setup", "using default setup values");
12175 static void LoadSetup_SpecialPostProcessing(void)
12177 char *player_name_new;
12179 // needed to work around problems with fixed length strings
12180 player_name_new = getFixedUserName(setup.player_name);
12181 free(setup.player_name);
12182 setup.player_name = player_name_new;
12184 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12185 if (setup.scroll_delay == FALSE)
12187 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12188 setup.scroll_delay = TRUE; // now always "on"
12191 // make sure that scroll delay value stays inside valid range
12192 setup.scroll_delay_value =
12193 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12196 void LoadSetup_Default(void)
12200 // always start with reliable default values
12201 setSetupInfoToDefaults(&setup);
12203 // try to load setup values from default setup file
12204 filename = getDefaultSetupFilename();
12206 if (fileExists(filename))
12207 LoadSetupFromFilename(filename);
12209 // try to load setup values from platform setup file
12210 filename = getPlatformSetupFilename();
12212 if (fileExists(filename))
12213 LoadSetupFromFilename(filename);
12215 // try to load setup values from user setup file
12216 filename = getSetupFilename();
12218 LoadSetupFromFilename(filename);
12220 LoadSetup_SpecialPostProcessing();
12223 void LoadSetup_AutoSetup(void)
12225 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12226 SetupFileHash *setup_file_hash = NULL;
12228 // always start with reliable default values
12229 setSetupInfoToDefaults_AutoSetup(&setup);
12231 setup_file_hash = loadSetupFileHash(filename);
12233 if (setup_file_hash)
12235 decodeSetupFileHash_AutoSetup(setup_file_hash);
12237 freeSetupFileHash(setup_file_hash);
12243 void LoadSetup_ServerSetup(void)
12245 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12246 SetupFileHash *setup_file_hash = NULL;
12248 // always start with reliable default values
12249 setSetupInfoToDefaults_ServerSetup(&setup);
12251 setup_file_hash = loadSetupFileHash(filename);
12253 if (setup_file_hash)
12255 decodeSetupFileHash_ServerSetup(setup_file_hash);
12257 freeSetupFileHash(setup_file_hash);
12262 if (setup.player_uuid == NULL)
12264 // player UUID does not yet exist in setup file
12265 setup.player_uuid = getStringCopy(getUUID());
12266 setup.player_version = 2;
12268 SaveSetup_ServerSetup();
12272 void LoadSetup_EditorCascade(void)
12274 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12275 SetupFileHash *setup_file_hash = NULL;
12277 // always start with reliable default values
12278 setSetupInfoToDefaults_EditorCascade(&setup);
12280 setup_file_hash = loadSetupFileHash(filename);
12282 if (setup_file_hash)
12284 decodeSetupFileHash_EditorCascade(setup_file_hash);
12286 freeSetupFileHash(setup_file_hash);
12292 void LoadSetup(void)
12294 LoadSetup_Default();
12295 LoadSetup_AutoSetup();
12296 LoadSetup_ServerSetup();
12297 LoadSetup_EditorCascade();
12300 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12301 char *mapping_line)
12303 char mapping_guid[MAX_LINE_LEN];
12304 char *mapping_start, *mapping_end;
12306 // get GUID from game controller mapping line: copy complete line
12307 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12308 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12310 // get GUID from game controller mapping line: cut after GUID part
12311 mapping_start = strchr(mapping_guid, ',');
12312 if (mapping_start != NULL)
12313 *mapping_start = '\0';
12315 // cut newline from game controller mapping line
12316 mapping_end = strchr(mapping_line, '\n');
12317 if (mapping_end != NULL)
12318 *mapping_end = '\0';
12320 // add mapping entry to game controller mappings hash
12321 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12324 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12329 if (!(file = fopen(filename, MODE_READ)))
12331 Warn("cannot read game controller mappings file '%s'", filename);
12336 while (!feof(file))
12338 char line[MAX_LINE_LEN];
12340 if (!fgets(line, MAX_LINE_LEN, file))
12343 addGameControllerMappingToHash(mappings_hash, line);
12349 void SaveSetup_Default(void)
12351 char *filename = getSetupFilename();
12355 InitUserDataDirectory();
12357 if (!(file = fopen(filename, MODE_WRITE)))
12359 Warn("cannot write setup file '%s'", filename);
12364 fprintFileHeader(file, SETUP_FILENAME);
12366 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12368 // just to make things nicer :)
12369 if (global_setup_tokens[i].value == &setup.multiple_users ||
12370 global_setup_tokens[i].value == &setup.sound ||
12371 global_setup_tokens[i].value == &setup.graphics_set ||
12372 global_setup_tokens[i].value == &setup.volume_simple ||
12373 global_setup_tokens[i].value == &setup.network_mode ||
12374 global_setup_tokens[i].value == &setup.touch.control_type ||
12375 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12376 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12377 fprintf(file, "\n");
12379 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12382 for (i = 0; i < 2; i++)
12384 int grid_xsize = setup.touch.grid_xsize[i];
12385 int grid_ysize = setup.touch.grid_ysize[i];
12388 fprintf(file, "\n");
12390 for (y = 0; y < grid_ysize; y++)
12392 char token_string[MAX_LINE_LEN];
12393 char value_string[MAX_LINE_LEN];
12395 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12397 for (x = 0; x < grid_xsize; x++)
12399 char c = setup.touch.grid_button[i][x][y];
12401 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12404 value_string[grid_xsize] = '\0';
12406 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12410 fprintf(file, "\n");
12411 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12412 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12414 fprintf(file, "\n");
12415 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12416 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12418 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12422 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12423 fprintf(file, "\n");
12425 setup_input = setup.input[pnr];
12426 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12427 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12430 fprintf(file, "\n");
12431 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12432 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12434 // (internal setup values not saved to user setup file)
12436 fprintf(file, "\n");
12437 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12438 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12439 setup.debug.xsn_mode != AUTO)
12440 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12442 fprintf(file, "\n");
12443 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12444 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12448 SetFilePermissions(filename, PERMS_PRIVATE);
12451 void SaveSetup_AutoSetup(void)
12453 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12457 InitUserDataDirectory();
12459 if (!(file = fopen(filename, MODE_WRITE)))
12461 Warn("cannot write auto setup file '%s'", filename);
12468 fprintFileHeader(file, AUTOSETUP_FILENAME);
12470 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12471 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12475 SetFilePermissions(filename, PERMS_PRIVATE);
12480 void SaveSetup_ServerSetup(void)
12482 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12486 InitUserDataDirectory();
12488 if (!(file = fopen(filename, MODE_WRITE)))
12490 Warn("cannot write server setup file '%s'", filename);
12497 fprintFileHeader(file, SERVERSETUP_FILENAME);
12499 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12501 // just to make things nicer :)
12502 if (server_setup_tokens[i].value == &setup.use_api_server)
12503 fprintf(file, "\n");
12505 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12510 SetFilePermissions(filename, PERMS_PRIVATE);
12515 void SaveSetup_EditorCascade(void)
12517 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12521 InitUserDataDirectory();
12523 if (!(file = fopen(filename, MODE_WRITE)))
12525 Warn("cannot write editor cascade state file '%s'", filename);
12532 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12534 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12535 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12539 SetFilePermissions(filename, PERMS_PRIVATE);
12544 void SaveSetup(void)
12546 SaveSetup_Default();
12547 SaveSetup_AutoSetup();
12548 SaveSetup_ServerSetup();
12549 SaveSetup_EditorCascade();
12552 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12557 if (!(file = fopen(filename, MODE_WRITE)))
12559 Warn("cannot write game controller mappings file '%s'", filename);
12564 BEGIN_HASH_ITERATION(mappings_hash, itr)
12566 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12568 END_HASH_ITERATION(mappings_hash, itr)
12573 void SaveSetup_AddGameControllerMapping(char *mapping)
12575 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12576 SetupFileHash *mappings_hash = newSetupFileHash();
12578 InitUserDataDirectory();
12580 // load existing personal game controller mappings
12581 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12583 // add new mapping to personal game controller mappings
12584 addGameControllerMappingToHash(mappings_hash, mapping);
12586 // save updated personal game controller mappings
12587 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12589 freeSetupFileHash(mappings_hash);
12593 void LoadCustomElementDescriptions(void)
12595 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12596 SetupFileHash *setup_file_hash;
12599 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12601 if (element_info[i].custom_description != NULL)
12603 free(element_info[i].custom_description);
12604 element_info[i].custom_description = NULL;
12608 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12611 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12613 char *token = getStringCat2(element_info[i].token_name, ".name");
12614 char *value = getHashEntry(setup_file_hash, token);
12617 element_info[i].custom_description = getStringCopy(value);
12622 freeSetupFileHash(setup_file_hash);
12625 static int getElementFromToken(char *token)
12627 char *value = getHashEntry(element_token_hash, token);
12630 return atoi(value);
12632 Warn("unknown element token '%s'", token);
12634 return EL_UNDEFINED;
12637 void FreeGlobalAnimEventInfo(void)
12639 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12641 if (gaei->event_list == NULL)
12646 for (i = 0; i < gaei->num_event_lists; i++)
12648 checked_free(gaei->event_list[i]->event_value);
12649 checked_free(gaei->event_list[i]);
12652 checked_free(gaei->event_list);
12654 gaei->event_list = NULL;
12655 gaei->num_event_lists = 0;
12658 static int AddGlobalAnimEventList(void)
12660 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12661 int list_pos = gaei->num_event_lists++;
12663 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12664 sizeof(struct GlobalAnimEventListInfo *));
12666 gaei->event_list[list_pos] =
12667 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12669 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12671 gaeli->event_value = NULL;
12672 gaeli->num_event_values = 0;
12677 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12679 // do not add empty global animation events
12680 if (event_value == ANIM_EVENT_NONE)
12683 // if list position is undefined, create new list
12684 if (list_pos == ANIM_EVENT_UNDEFINED)
12685 list_pos = AddGlobalAnimEventList();
12687 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12688 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12689 int value_pos = gaeli->num_event_values++;
12691 gaeli->event_value = checked_realloc(gaeli->event_value,
12692 gaeli->num_event_values * sizeof(int *));
12694 gaeli->event_value[value_pos] = event_value;
12699 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12701 if (list_pos == ANIM_EVENT_UNDEFINED)
12702 return ANIM_EVENT_NONE;
12704 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12705 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12707 return gaeli->event_value[value_pos];
12710 int GetGlobalAnimEventValueCount(int list_pos)
12712 if (list_pos == ANIM_EVENT_UNDEFINED)
12715 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12716 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12718 return gaeli->num_event_values;
12721 // This function checks if a string <s> of the format "string1, string2, ..."
12722 // exactly contains a string <s_contained>.
12724 static boolean string_has_parameter(char *s, char *s_contained)
12728 if (s == NULL || s_contained == NULL)
12731 if (strlen(s_contained) > strlen(s))
12734 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12736 char next_char = s[strlen(s_contained)];
12738 // check if next character is delimiter or whitespace
12739 if (next_char == ',' || next_char == '\0' ||
12740 next_char == ' ' || next_char == '\t')
12744 // check if string contains another parameter string after a comma
12745 substring = strchr(s, ',');
12746 if (substring == NULL) // string does not contain a comma
12749 // advance string pointer to next character after the comma
12752 // skip potential whitespaces after the comma
12753 while (*substring == ' ' || *substring == '\t')
12756 return string_has_parameter(substring, s_contained);
12759 static int get_anim_parameter_value_ce(char *s)
12762 char *pattern_1 = "ce_change:custom_";
12763 char *pattern_2 = ".page_";
12764 int pattern_1_len = strlen(pattern_1);
12765 char *matching_char = strstr(s_ptr, pattern_1);
12766 int result = ANIM_EVENT_NONE;
12768 if (matching_char == NULL)
12769 return ANIM_EVENT_NONE;
12771 result = ANIM_EVENT_CE_CHANGE;
12773 s_ptr = matching_char + pattern_1_len;
12775 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12776 if (*s_ptr >= '0' && *s_ptr <= '9')
12778 int gic_ce_nr = (*s_ptr++ - '0');
12780 if (*s_ptr >= '0' && *s_ptr <= '9')
12782 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12784 if (*s_ptr >= '0' && *s_ptr <= '9')
12785 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12788 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12789 return ANIM_EVENT_NONE;
12791 // custom element stored as 0 to 255
12794 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12798 // invalid custom element number specified
12800 return ANIM_EVENT_NONE;
12803 // check for change page number ("page_X" or "page_XX") (optional)
12804 if (strPrefix(s_ptr, pattern_2))
12806 s_ptr += strlen(pattern_2);
12808 if (*s_ptr >= '0' && *s_ptr <= '9')
12810 int gic_page_nr = (*s_ptr++ - '0');
12812 if (*s_ptr >= '0' && *s_ptr <= '9')
12813 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12815 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12816 return ANIM_EVENT_NONE;
12818 // change page stored as 1 to 32 (0 means "all change pages")
12820 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12824 // invalid animation part number specified
12826 return ANIM_EVENT_NONE;
12830 // discard result if next character is neither delimiter nor whitespace
12831 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12832 *s_ptr == ' ' || *s_ptr == '\t'))
12833 return ANIM_EVENT_NONE;
12838 static int get_anim_parameter_value(char *s)
12840 int event_value[] =
12848 char *pattern_1[] =
12856 char *pattern_2 = ".part_";
12857 char *matching_char = NULL;
12859 int pattern_1_len = 0;
12860 int result = ANIM_EVENT_NONE;
12863 result = get_anim_parameter_value_ce(s);
12865 if (result != ANIM_EVENT_NONE)
12868 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12870 matching_char = strstr(s_ptr, pattern_1[i]);
12871 pattern_1_len = strlen(pattern_1[i]);
12872 result = event_value[i];
12874 if (matching_char != NULL)
12878 if (matching_char == NULL)
12879 return ANIM_EVENT_NONE;
12881 s_ptr = matching_char + pattern_1_len;
12883 // check for main animation number ("anim_X" or "anim_XX")
12884 if (*s_ptr >= '0' && *s_ptr <= '9')
12886 int gic_anim_nr = (*s_ptr++ - '0');
12888 if (*s_ptr >= '0' && *s_ptr <= '9')
12889 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12891 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12892 return ANIM_EVENT_NONE;
12894 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12898 // invalid main animation number specified
12900 return ANIM_EVENT_NONE;
12903 // check for animation part number ("part_X" or "part_XX") (optional)
12904 if (strPrefix(s_ptr, pattern_2))
12906 s_ptr += strlen(pattern_2);
12908 if (*s_ptr >= '0' && *s_ptr <= '9')
12910 int gic_part_nr = (*s_ptr++ - '0');
12912 if (*s_ptr >= '0' && *s_ptr <= '9')
12913 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12915 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12916 return ANIM_EVENT_NONE;
12918 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12922 // invalid animation part number specified
12924 return ANIM_EVENT_NONE;
12928 // discard result if next character is neither delimiter nor whitespace
12929 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12930 *s_ptr == ' ' || *s_ptr == '\t'))
12931 return ANIM_EVENT_NONE;
12936 static int get_anim_parameter_values(char *s)
12938 int list_pos = ANIM_EVENT_UNDEFINED;
12939 int event_value = ANIM_EVENT_DEFAULT;
12941 if (string_has_parameter(s, "any"))
12942 event_value |= ANIM_EVENT_ANY;
12944 if (string_has_parameter(s, "click:self") ||
12945 string_has_parameter(s, "click") ||
12946 string_has_parameter(s, "self"))
12947 event_value |= ANIM_EVENT_SELF;
12949 if (string_has_parameter(s, "unclick:any"))
12950 event_value |= ANIM_EVENT_UNCLICK_ANY;
12952 // if animation event found, add it to global animation event list
12953 if (event_value != ANIM_EVENT_NONE)
12954 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12958 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12959 event_value = get_anim_parameter_value(s);
12961 // if animation event found, add it to global animation event list
12962 if (event_value != ANIM_EVENT_NONE)
12963 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12965 // continue with next part of the string, starting with next comma
12966 s = strchr(s + 1, ',');
12972 static int get_anim_action_parameter_value(char *token)
12974 // check most common default case first to massively speed things up
12975 if (strEqual(token, ARG_UNDEFINED))
12976 return ANIM_EVENT_ACTION_NONE;
12978 int result = getImageIDFromToken(token);
12982 char *gfx_token = getStringCat2("gfx.", token);
12984 result = getImageIDFromToken(gfx_token);
12986 checked_free(gfx_token);
12991 Key key = getKeyFromX11KeyName(token);
12993 if (key != KSYM_UNDEFINED)
12994 result = -(int)key;
13001 result = get_hash_from_string(token); // unsigned int => int
13002 result = ABS(result); // may be negative now
13003 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13005 setHashEntry(anim_url_hash, int2str(result, 0), token);
13010 result = ANIM_EVENT_ACTION_NONE;
13015 int get_parameter_value(char *value_raw, char *suffix, int type)
13017 char *value = getStringToLower(value_raw);
13018 int result = 0; // probably a save default value
13020 if (strEqual(suffix, ".direction"))
13022 result = (strEqual(value, "left") ? MV_LEFT :
13023 strEqual(value, "right") ? MV_RIGHT :
13024 strEqual(value, "up") ? MV_UP :
13025 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13027 else if (strEqual(suffix, ".position"))
13029 result = (strEqual(value, "left") ? POS_LEFT :
13030 strEqual(value, "right") ? POS_RIGHT :
13031 strEqual(value, "top") ? POS_TOP :
13032 strEqual(value, "upper") ? POS_UPPER :
13033 strEqual(value, "middle") ? POS_MIDDLE :
13034 strEqual(value, "lower") ? POS_LOWER :
13035 strEqual(value, "bottom") ? POS_BOTTOM :
13036 strEqual(value, "any") ? POS_ANY :
13037 strEqual(value, "ce") ? POS_CE :
13038 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13039 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13041 else if (strEqual(suffix, ".align"))
13043 result = (strEqual(value, "left") ? ALIGN_LEFT :
13044 strEqual(value, "right") ? ALIGN_RIGHT :
13045 strEqual(value, "center") ? ALIGN_CENTER :
13046 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13048 else if (strEqual(suffix, ".valign"))
13050 result = (strEqual(value, "top") ? VALIGN_TOP :
13051 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13052 strEqual(value, "middle") ? VALIGN_MIDDLE :
13053 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13055 else if (strEqual(suffix, ".anim_mode"))
13057 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13058 string_has_parameter(value, "loop") ? ANIM_LOOP :
13059 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13060 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13061 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13062 string_has_parameter(value, "random") ? ANIM_RANDOM :
13063 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13064 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13065 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13066 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13067 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13068 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13069 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13070 string_has_parameter(value, "all") ? ANIM_ALL :
13071 string_has_parameter(value, "tiled") ? ANIM_TILED :
13072 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13075 if (string_has_parameter(value, "once"))
13076 result |= ANIM_ONCE;
13078 if (string_has_parameter(value, "reverse"))
13079 result |= ANIM_REVERSE;
13081 if (string_has_parameter(value, "opaque_player"))
13082 result |= ANIM_OPAQUE_PLAYER;
13084 if (string_has_parameter(value, "static_panel"))
13085 result |= ANIM_STATIC_PANEL;
13087 else if (strEqual(suffix, ".init_event") ||
13088 strEqual(suffix, ".anim_event"))
13090 result = get_anim_parameter_values(value);
13092 else if (strEqual(suffix, ".init_delay_action") ||
13093 strEqual(suffix, ".anim_delay_action") ||
13094 strEqual(suffix, ".post_delay_action") ||
13095 strEqual(suffix, ".init_event_action") ||
13096 strEqual(suffix, ".anim_event_action"))
13098 result = get_anim_action_parameter_value(value_raw);
13100 else if (strEqual(suffix, ".class"))
13102 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13103 get_hash_from_string(value));
13105 else if (strEqual(suffix, ".style"))
13107 result = STYLE_DEFAULT;
13109 if (string_has_parameter(value, "accurate_borders"))
13110 result |= STYLE_ACCURATE_BORDERS;
13112 if (string_has_parameter(value, "inner_corners"))
13113 result |= STYLE_INNER_CORNERS;
13115 if (string_has_parameter(value, "reverse"))
13116 result |= STYLE_REVERSE;
13118 if (string_has_parameter(value, "leftmost_position"))
13119 result |= STYLE_LEFTMOST_POSITION;
13121 if (string_has_parameter(value, "block_clicks"))
13122 result |= STYLE_BLOCK;
13124 if (string_has_parameter(value, "passthrough_clicks"))
13125 result |= STYLE_PASSTHROUGH;
13127 if (string_has_parameter(value, "multiple_actions"))
13128 result |= STYLE_MULTIPLE_ACTIONS;
13130 if (string_has_parameter(value, "consume_ce_event"))
13131 result |= STYLE_CONSUME_CE_EVENT;
13133 else if (strEqual(suffix, ".fade_mode"))
13135 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13136 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13137 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13138 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13139 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13140 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13141 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13142 FADE_MODE_DEFAULT);
13144 else if (strEqual(suffix, ".auto_delay_unit"))
13146 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13147 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13148 AUTO_DELAY_UNIT_DEFAULT);
13150 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13152 result = gfx.get_font_from_token_function(value);
13154 else // generic parameter of type integer or boolean
13156 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13157 type == TYPE_INTEGER ? get_integer_from_string(value) :
13158 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13159 ARG_UNDEFINED_VALUE);
13167 static int get_token_parameter_value(char *token, char *value_raw)
13171 if (token == NULL || value_raw == NULL)
13172 return ARG_UNDEFINED_VALUE;
13174 suffix = strrchr(token, '.');
13175 if (suffix == NULL)
13178 if (strEqual(suffix, ".element"))
13179 return getElementFromToken(value_raw);
13181 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13182 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13185 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13186 boolean ignore_defaults)
13190 for (i = 0; image_config_vars[i].token != NULL; i++)
13192 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13194 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13195 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13199 *image_config_vars[i].value =
13200 get_token_parameter_value(image_config_vars[i].token, value);
13204 void InitMenuDesignSettings_Static(void)
13206 // always start with reliable default values from static default config
13207 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13210 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13214 // the following initializes hierarchical values from static configuration
13216 // special case: initialize "ARG_DEFAULT" values in static default config
13217 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13218 titlescreen_initial_first_default.fade_mode =
13219 title_initial_first_default.fade_mode;
13220 titlescreen_initial_first_default.fade_delay =
13221 title_initial_first_default.fade_delay;
13222 titlescreen_initial_first_default.post_delay =
13223 title_initial_first_default.post_delay;
13224 titlescreen_initial_first_default.auto_delay =
13225 title_initial_first_default.auto_delay;
13226 titlescreen_initial_first_default.auto_delay_unit =
13227 title_initial_first_default.auto_delay_unit;
13228 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13229 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13230 titlescreen_first_default.post_delay = title_first_default.post_delay;
13231 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13232 titlescreen_first_default.auto_delay_unit =
13233 title_first_default.auto_delay_unit;
13234 titlemessage_initial_first_default.fade_mode =
13235 title_initial_first_default.fade_mode;
13236 titlemessage_initial_first_default.fade_delay =
13237 title_initial_first_default.fade_delay;
13238 titlemessage_initial_first_default.post_delay =
13239 title_initial_first_default.post_delay;
13240 titlemessage_initial_first_default.auto_delay =
13241 title_initial_first_default.auto_delay;
13242 titlemessage_initial_first_default.auto_delay_unit =
13243 title_initial_first_default.auto_delay_unit;
13244 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13245 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13246 titlemessage_first_default.post_delay = title_first_default.post_delay;
13247 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13248 titlemessage_first_default.auto_delay_unit =
13249 title_first_default.auto_delay_unit;
13251 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13252 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13253 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13254 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13255 titlescreen_initial_default.auto_delay_unit =
13256 title_initial_default.auto_delay_unit;
13257 titlescreen_default.fade_mode = title_default.fade_mode;
13258 titlescreen_default.fade_delay = title_default.fade_delay;
13259 titlescreen_default.post_delay = title_default.post_delay;
13260 titlescreen_default.auto_delay = title_default.auto_delay;
13261 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13262 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13263 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13264 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13265 titlemessage_initial_default.auto_delay_unit =
13266 title_initial_default.auto_delay_unit;
13267 titlemessage_default.fade_mode = title_default.fade_mode;
13268 titlemessage_default.fade_delay = title_default.fade_delay;
13269 titlemessage_default.post_delay = title_default.post_delay;
13270 titlemessage_default.auto_delay = title_default.auto_delay;
13271 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13273 // special case: initialize "ARG_DEFAULT" values in static default config
13274 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13275 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13277 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13278 titlescreen_first[i] = titlescreen_first_default;
13279 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13280 titlemessage_first[i] = titlemessage_first_default;
13282 titlescreen_initial[i] = titlescreen_initial_default;
13283 titlescreen[i] = titlescreen_default;
13284 titlemessage_initial[i] = titlemessage_initial_default;
13285 titlemessage[i] = titlemessage_default;
13288 // special case: initialize "ARG_DEFAULT" values in static default config
13289 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13290 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13292 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13295 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13296 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13297 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13300 // special case: initialize "ARG_DEFAULT" values in static default config
13301 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13302 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13304 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13305 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13306 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13308 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13311 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13315 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13319 struct XY *dst, *src;
13321 game_buttons_xy[] =
13323 { &game.button.save, &game.button.stop },
13324 { &game.button.pause2, &game.button.pause },
13325 { &game.button.load, &game.button.play },
13326 { &game.button.undo, &game.button.stop },
13327 { &game.button.redo, &game.button.play },
13333 // special case: initialize later added SETUP list size from LEVELS value
13334 if (menu.list_size[GAME_MODE_SETUP] == -1)
13335 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13337 // set default position for snapshot buttons to stop/pause/play buttons
13338 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13339 if ((*game_buttons_xy[i].dst).x == -1 &&
13340 (*game_buttons_xy[i].dst).y == -1)
13341 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13343 // --------------------------------------------------------------------------
13344 // dynamic viewports (including playfield margins, borders and alignments)
13345 // --------------------------------------------------------------------------
13347 // dynamic viewports currently only supported for landscape mode
13348 int display_width = MAX(video.display_width, video.display_height);
13349 int display_height = MIN(video.display_width, video.display_height);
13351 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13353 struct RectWithBorder *vp_window = &viewport.window[i];
13354 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13355 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13356 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13357 boolean dynamic_window_width = (vp_window->min_width != -1);
13358 boolean dynamic_window_height = (vp_window->min_height != -1);
13359 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13360 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13362 // adjust window size if min/max width/height is specified
13364 if (vp_window->min_width != -1)
13366 int window_width = display_width;
13368 // when using static window height, use aspect ratio of display
13369 if (vp_window->min_height == -1)
13370 window_width = vp_window->height * display_width / display_height;
13372 vp_window->width = MAX(vp_window->min_width, window_width);
13375 if (vp_window->min_height != -1)
13377 int window_height = display_height;
13379 // when using static window width, use aspect ratio of display
13380 if (vp_window->min_width == -1)
13381 window_height = vp_window->width * display_height / display_width;
13383 vp_window->height = MAX(vp_window->min_height, window_height);
13386 if (vp_window->max_width != -1)
13387 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13389 if (vp_window->max_height != -1)
13390 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13392 int playfield_width = vp_window->width;
13393 int playfield_height = vp_window->height;
13395 // adjust playfield size and position according to specified margins
13397 playfield_width -= vp_playfield->margin_left;
13398 playfield_width -= vp_playfield->margin_right;
13400 playfield_height -= vp_playfield->margin_top;
13401 playfield_height -= vp_playfield->margin_bottom;
13403 // adjust playfield size if min/max width/height is specified
13405 if (vp_playfield->min_width != -1)
13406 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13408 if (vp_playfield->min_height != -1)
13409 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13411 if (vp_playfield->max_width != -1)
13412 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13414 if (vp_playfield->max_height != -1)
13415 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13417 // adjust playfield position according to specified alignment
13419 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13420 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13421 else if (vp_playfield->align == ALIGN_CENTER)
13422 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13423 else if (vp_playfield->align == ALIGN_RIGHT)
13424 vp_playfield->x += playfield_width - vp_playfield->width;
13426 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13427 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13428 else if (vp_playfield->valign == VALIGN_MIDDLE)
13429 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13430 else if (vp_playfield->valign == VALIGN_BOTTOM)
13431 vp_playfield->y += playfield_height - vp_playfield->height;
13433 vp_playfield->x += vp_playfield->margin_left;
13434 vp_playfield->y += vp_playfield->margin_top;
13436 // adjust individual playfield borders if only default border is specified
13438 if (vp_playfield->border_left == -1)
13439 vp_playfield->border_left = vp_playfield->border_size;
13440 if (vp_playfield->border_right == -1)
13441 vp_playfield->border_right = vp_playfield->border_size;
13442 if (vp_playfield->border_top == -1)
13443 vp_playfield->border_top = vp_playfield->border_size;
13444 if (vp_playfield->border_bottom == -1)
13445 vp_playfield->border_bottom = vp_playfield->border_size;
13447 // set dynamic playfield borders if borders are specified as undefined
13448 // (but only if window size was dynamic and playfield size was static)
13450 if (dynamic_window_width && !dynamic_playfield_width)
13452 if (vp_playfield->border_left == -1)
13454 vp_playfield->border_left = (vp_playfield->x -
13455 vp_playfield->margin_left);
13456 vp_playfield->x -= vp_playfield->border_left;
13457 vp_playfield->width += vp_playfield->border_left;
13460 if (vp_playfield->border_right == -1)
13462 vp_playfield->border_right = (vp_window->width -
13464 vp_playfield->width -
13465 vp_playfield->margin_right);
13466 vp_playfield->width += vp_playfield->border_right;
13470 if (dynamic_window_height && !dynamic_playfield_height)
13472 if (vp_playfield->border_top == -1)
13474 vp_playfield->border_top = (vp_playfield->y -
13475 vp_playfield->margin_top);
13476 vp_playfield->y -= vp_playfield->border_top;
13477 vp_playfield->height += vp_playfield->border_top;
13480 if (vp_playfield->border_bottom == -1)
13482 vp_playfield->border_bottom = (vp_window->height -
13484 vp_playfield->height -
13485 vp_playfield->margin_bottom);
13486 vp_playfield->height += vp_playfield->border_bottom;
13490 // adjust playfield size to be a multiple of a defined alignment tile size
13492 int align_size = vp_playfield->align_size;
13493 int playfield_xtiles = vp_playfield->width / align_size;
13494 int playfield_ytiles = vp_playfield->height / align_size;
13495 int playfield_width_corrected = playfield_xtiles * align_size;
13496 int playfield_height_corrected = playfield_ytiles * align_size;
13497 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13498 i == GFX_SPECIAL_ARG_EDITOR);
13500 if (is_playfield_mode &&
13501 dynamic_playfield_width &&
13502 vp_playfield->width != playfield_width_corrected)
13504 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13506 vp_playfield->width = playfield_width_corrected;
13508 if (vp_playfield->align == ALIGN_LEFT)
13510 vp_playfield->border_left += playfield_xdiff;
13512 else if (vp_playfield->align == ALIGN_RIGHT)
13514 vp_playfield->border_right += playfield_xdiff;
13516 else if (vp_playfield->align == ALIGN_CENTER)
13518 int border_left_diff = playfield_xdiff / 2;
13519 int border_right_diff = playfield_xdiff - border_left_diff;
13521 vp_playfield->border_left += border_left_diff;
13522 vp_playfield->border_right += border_right_diff;
13526 if (is_playfield_mode &&
13527 dynamic_playfield_height &&
13528 vp_playfield->height != playfield_height_corrected)
13530 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13532 vp_playfield->height = playfield_height_corrected;
13534 if (vp_playfield->valign == VALIGN_TOP)
13536 vp_playfield->border_top += playfield_ydiff;
13538 else if (vp_playfield->align == VALIGN_BOTTOM)
13540 vp_playfield->border_right += playfield_ydiff;
13542 else if (vp_playfield->align == VALIGN_MIDDLE)
13544 int border_top_diff = playfield_ydiff / 2;
13545 int border_bottom_diff = playfield_ydiff - border_top_diff;
13547 vp_playfield->border_top += border_top_diff;
13548 vp_playfield->border_bottom += border_bottom_diff;
13552 // adjust door positions according to specified alignment
13554 for (j = 0; j < 2; j++)
13556 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13558 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13559 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13560 else if (vp_door->align == ALIGN_CENTER)
13561 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13562 else if (vp_door->align == ALIGN_RIGHT)
13563 vp_door->x += vp_window->width - vp_door->width;
13565 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13566 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13567 else if (vp_door->valign == VALIGN_MIDDLE)
13568 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13569 else if (vp_door->valign == VALIGN_BOTTOM)
13570 vp_door->y += vp_window->height - vp_door->height;
13575 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13579 struct XYTileSize *dst, *src;
13582 editor_buttons_xy[] =
13585 &editor.button.element_left, &editor.palette.element_left,
13586 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13589 &editor.button.element_middle, &editor.palette.element_middle,
13590 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13593 &editor.button.element_right, &editor.palette.element_right,
13594 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13601 // set default position for element buttons to element graphics
13602 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13604 if ((*editor_buttons_xy[i].dst).x == -1 &&
13605 (*editor_buttons_xy[i].dst).y == -1)
13607 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13609 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13611 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13615 // adjust editor palette rows and columns if specified to be dynamic
13617 if (editor.palette.cols == -1)
13619 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13620 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13621 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13623 editor.palette.cols = (vp_width - sc_width) / bt_width;
13625 if (editor.palette.x == -1)
13627 int palette_width = editor.palette.cols * bt_width + sc_width;
13629 editor.palette.x = (vp_width - palette_width) / 2;
13633 if (editor.palette.rows == -1)
13635 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13636 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13637 int tx_height = getFontHeight(FONT_TEXT_2);
13639 editor.palette.rows = (vp_height - tx_height) / bt_height;
13641 if (editor.palette.y == -1)
13643 int palette_height = editor.palette.rows * bt_height + tx_height;
13645 editor.palette.y = (vp_height - palette_height) / 2;
13650 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13651 boolean initialize)
13653 // special case: check if network and preview player positions are redefined,
13654 // to compare this later against the main menu level preview being redefined
13655 struct TokenIntPtrInfo menu_config_players[] =
13657 { "main.network_players.x", &menu.main.network_players.redefined },
13658 { "main.network_players.y", &menu.main.network_players.redefined },
13659 { "main.preview_players.x", &menu.main.preview_players.redefined },
13660 { "main.preview_players.y", &menu.main.preview_players.redefined },
13661 { "preview.x", &preview.redefined },
13662 { "preview.y", &preview.redefined }
13668 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13669 *menu_config_players[i].value = FALSE;
13673 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13674 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13675 *menu_config_players[i].value = TRUE;
13679 static void InitMenuDesignSettings_PreviewPlayers(void)
13681 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13684 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13686 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13689 static void LoadMenuDesignSettingsFromFilename(char *filename)
13691 static struct TitleFadingInfo tfi;
13692 static struct TitleMessageInfo tmi;
13693 static struct TokenInfo title_tokens[] =
13695 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13696 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13697 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13698 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13699 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13703 static struct TokenInfo titlemessage_tokens[] =
13705 { TYPE_INTEGER, &tmi.x, ".x" },
13706 { TYPE_INTEGER, &tmi.y, ".y" },
13707 { TYPE_INTEGER, &tmi.width, ".width" },
13708 { TYPE_INTEGER, &tmi.height, ".height" },
13709 { TYPE_INTEGER, &tmi.chars, ".chars" },
13710 { TYPE_INTEGER, &tmi.lines, ".lines" },
13711 { TYPE_INTEGER, &tmi.align, ".align" },
13712 { TYPE_INTEGER, &tmi.valign, ".valign" },
13713 { TYPE_INTEGER, &tmi.font, ".font" },
13714 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13715 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13716 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13717 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13718 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13719 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13720 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13721 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13722 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13728 struct TitleFadingInfo *info;
13733 // initialize first titles from "enter screen" definitions, if defined
13734 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13735 { &title_first_default, "menu.enter_screen.TITLE" },
13737 // initialize title screens from "next screen" definitions, if defined
13738 { &title_initial_default, "menu.next_screen.TITLE" },
13739 { &title_default, "menu.next_screen.TITLE" },
13745 struct TitleMessageInfo *array;
13748 titlemessage_arrays[] =
13750 // initialize first titles from "enter screen" definitions, if defined
13751 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13752 { titlescreen_first, "menu.enter_screen.TITLE" },
13753 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13754 { titlemessage_first, "menu.enter_screen.TITLE" },
13756 // initialize titles from "next screen" definitions, if defined
13757 { titlescreen_initial, "menu.next_screen.TITLE" },
13758 { titlescreen, "menu.next_screen.TITLE" },
13759 { titlemessage_initial, "menu.next_screen.TITLE" },
13760 { titlemessage, "menu.next_screen.TITLE" },
13762 // overwrite titles with title definitions, if defined
13763 { titlescreen_initial_first, "[title_initial]" },
13764 { titlescreen_first, "[title]" },
13765 { titlemessage_initial_first, "[title_initial]" },
13766 { titlemessage_first, "[title]" },
13768 { titlescreen_initial, "[title_initial]" },
13769 { titlescreen, "[title]" },
13770 { titlemessage_initial, "[title_initial]" },
13771 { titlemessage, "[title]" },
13773 // overwrite titles with title screen/message definitions, if defined
13774 { titlescreen_initial_first, "[titlescreen_initial]" },
13775 { titlescreen_first, "[titlescreen]" },
13776 { titlemessage_initial_first, "[titlemessage_initial]" },
13777 { titlemessage_first, "[titlemessage]" },
13779 { titlescreen_initial, "[titlescreen_initial]" },
13780 { titlescreen, "[titlescreen]" },
13781 { titlemessage_initial, "[titlemessage_initial]" },
13782 { titlemessage, "[titlemessage]" },
13786 SetupFileHash *setup_file_hash;
13789 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13792 // the following initializes hierarchical values from dynamic configuration
13794 // special case: initialize with default values that may be overwritten
13795 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13796 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13798 struct TokenIntPtrInfo menu_config[] =
13800 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13801 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13802 { "menu.list_size", &menu.list_size[i] }
13805 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13807 char *token = menu_config[j].token;
13808 char *value = getHashEntry(setup_file_hash, token);
13811 *menu_config[j].value = get_integer_from_string(value);
13815 // special case: initialize with default values that may be overwritten
13816 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13817 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13819 struct TokenIntPtrInfo menu_config[] =
13821 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13822 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13823 { "menu.list_size.INFO", &menu.list_size_info[i] },
13824 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13825 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13828 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13830 char *token = menu_config[j].token;
13831 char *value = getHashEntry(setup_file_hash, token);
13834 *menu_config[j].value = get_integer_from_string(value);
13838 // special case: initialize with default values that may be overwritten
13839 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13840 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13842 struct TokenIntPtrInfo menu_config[] =
13844 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13845 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13848 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13850 char *token = menu_config[j].token;
13851 char *value = getHashEntry(setup_file_hash, token);
13854 *menu_config[j].value = get_integer_from_string(value);
13858 // special case: initialize with default values that may be overwritten
13859 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13860 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13862 struct TokenIntPtrInfo menu_config[] =
13864 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13865 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13866 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13867 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13868 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13869 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13870 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13871 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13872 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13873 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13876 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13878 char *token = menu_config[j].token;
13879 char *value = getHashEntry(setup_file_hash, token);
13882 *menu_config[j].value = get_integer_from_string(value);
13886 // special case: initialize with default values that may be overwritten
13887 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13888 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13890 struct TokenIntPtrInfo menu_config[] =
13892 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13893 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13894 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13895 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13896 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13897 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13898 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13899 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13900 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13903 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13905 char *token = menu_config[j].token;
13906 char *value = getHashEntry(setup_file_hash, token);
13909 *menu_config[j].value = get_token_parameter_value(token, value);
13913 // special case: initialize with default values that may be overwritten
13914 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13915 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13919 char *token_prefix;
13920 struct RectWithBorder *struct_ptr;
13924 { "viewport.window", &viewport.window[i] },
13925 { "viewport.playfield", &viewport.playfield[i] },
13926 { "viewport.door_1", &viewport.door_1[i] },
13927 { "viewport.door_2", &viewport.door_2[i] }
13930 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13932 struct TokenIntPtrInfo vp_config[] =
13934 { ".x", &vp_struct[j].struct_ptr->x },
13935 { ".y", &vp_struct[j].struct_ptr->y },
13936 { ".width", &vp_struct[j].struct_ptr->width },
13937 { ".height", &vp_struct[j].struct_ptr->height },
13938 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13939 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13940 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13941 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13942 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13943 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13944 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13945 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13946 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13947 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13948 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13949 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13950 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13951 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13952 { ".align", &vp_struct[j].struct_ptr->align },
13953 { ".valign", &vp_struct[j].struct_ptr->valign }
13956 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13958 char *token = getStringCat2(vp_struct[j].token_prefix,
13959 vp_config[k].token);
13960 char *value = getHashEntry(setup_file_hash, token);
13963 *vp_config[k].value = get_token_parameter_value(token, value);
13970 // special case: initialize with default values that may be overwritten
13971 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13972 for (i = 0; title_info[i].info != NULL; i++)
13974 struct TitleFadingInfo *info = title_info[i].info;
13975 char *base_token = title_info[i].text;
13977 for (j = 0; title_tokens[j].type != -1; j++)
13979 char *token = getStringCat2(base_token, title_tokens[j].text);
13980 char *value = getHashEntry(setup_file_hash, token);
13984 int parameter_value = get_token_parameter_value(token, value);
13988 *(int *)title_tokens[j].value = (int)parameter_value;
13997 // special case: initialize with default values that may be overwritten
13998 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13999 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14001 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14002 char *base_token = titlemessage_arrays[i].text;
14004 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14006 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14007 char *value = getHashEntry(setup_file_hash, token);
14011 int parameter_value = get_token_parameter_value(token, value);
14013 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14017 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14018 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14020 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14030 // read (and overwrite with) values that may be specified in config file
14031 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14033 // special case: check if network and preview player positions are redefined
14034 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14036 freeSetupFileHash(setup_file_hash);
14039 void LoadMenuDesignSettings(void)
14041 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14043 InitMenuDesignSettings_Static();
14044 InitMenuDesignSettings_SpecialPreProcessing();
14045 InitMenuDesignSettings_PreviewPlayers();
14047 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14049 // first look for special settings configured in level series config
14050 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14052 if (fileExists(filename_base))
14053 LoadMenuDesignSettingsFromFilename(filename_base);
14056 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14058 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14059 LoadMenuDesignSettingsFromFilename(filename_local);
14061 InitMenuDesignSettings_SpecialPostProcessing();
14064 void LoadMenuDesignSettings_AfterGraphics(void)
14066 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14069 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14070 boolean ignore_defaults)
14074 for (i = 0; sound_config_vars[i].token != NULL; i++)
14076 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14078 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14079 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14083 *sound_config_vars[i].value =
14084 get_token_parameter_value(sound_config_vars[i].token, value);
14088 void InitSoundSettings_Static(void)
14090 // always start with reliable default values from static default config
14091 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14094 static void LoadSoundSettingsFromFilename(char *filename)
14096 SetupFileHash *setup_file_hash;
14098 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14101 // read (and overwrite with) values that may be specified in config file
14102 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14104 freeSetupFileHash(setup_file_hash);
14107 void LoadSoundSettings(void)
14109 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14111 InitSoundSettings_Static();
14113 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14115 // first look for special settings configured in level series config
14116 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14118 if (fileExists(filename_base))
14119 LoadSoundSettingsFromFilename(filename_base);
14122 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14124 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14125 LoadSoundSettingsFromFilename(filename_local);
14128 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14130 char *filename = getEditorSetupFilename();
14131 SetupFileList *setup_file_list, *list;
14132 SetupFileHash *element_hash;
14133 int num_unknown_tokens = 0;
14136 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14139 element_hash = newSetupFileHash();
14141 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14142 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14144 // determined size may be larger than needed (due to unknown elements)
14146 for (list = setup_file_list; list != NULL; list = list->next)
14149 // add space for up to 3 more elements for padding that may be needed
14150 *num_elements += 3;
14152 // free memory for old list of elements, if needed
14153 checked_free(*elements);
14155 // allocate memory for new list of elements
14156 *elements = checked_malloc(*num_elements * sizeof(int));
14159 for (list = setup_file_list; list != NULL; list = list->next)
14161 char *value = getHashEntry(element_hash, list->token);
14163 if (value == NULL) // try to find obsolete token mapping
14165 char *mapped_token = get_mapped_token(list->token);
14167 if (mapped_token != NULL)
14169 value = getHashEntry(element_hash, mapped_token);
14171 free(mapped_token);
14177 (*elements)[(*num_elements)++] = atoi(value);
14181 if (num_unknown_tokens == 0)
14184 Warn("unknown token(s) found in config file:");
14185 Warn("- config file: '%s'", filename);
14187 num_unknown_tokens++;
14190 Warn("- token: '%s'", list->token);
14194 if (num_unknown_tokens > 0)
14197 while (*num_elements % 4) // pad with empty elements, if needed
14198 (*elements)[(*num_elements)++] = EL_EMPTY;
14200 freeSetupFileList(setup_file_list);
14201 freeSetupFileHash(element_hash);
14204 for (i = 0; i < *num_elements; i++)
14205 Debug("editor", "element '%s' [%d]\n",
14206 element_info[(*elements)[i]].token_name, (*elements)[i]);
14210 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14213 SetupFileHash *setup_file_hash = NULL;
14214 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14215 char *filename_music, *filename_prefix, *filename_info;
14221 token_to_value_ptr[] =
14223 { "title_header", &tmp_music_file_info.title_header },
14224 { "artist_header", &tmp_music_file_info.artist_header },
14225 { "album_header", &tmp_music_file_info.album_header },
14226 { "year_header", &tmp_music_file_info.year_header },
14227 { "played_header", &tmp_music_file_info.played_header },
14229 { "title", &tmp_music_file_info.title },
14230 { "artist", &tmp_music_file_info.artist },
14231 { "album", &tmp_music_file_info.album },
14232 { "year", &tmp_music_file_info.year },
14233 { "played", &tmp_music_file_info.played },
14239 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14240 getCustomMusicFilename(basename));
14242 if (filename_music == NULL)
14245 // ---------- try to replace file extension ----------
14247 filename_prefix = getStringCopy(filename_music);
14248 if (strrchr(filename_prefix, '.') != NULL)
14249 *strrchr(filename_prefix, '.') = '\0';
14250 filename_info = getStringCat2(filename_prefix, ".txt");
14252 if (fileExists(filename_info))
14253 setup_file_hash = loadSetupFileHash(filename_info);
14255 free(filename_prefix);
14256 free(filename_info);
14258 if (setup_file_hash == NULL)
14260 // ---------- try to add file extension ----------
14262 filename_prefix = getStringCopy(filename_music);
14263 filename_info = getStringCat2(filename_prefix, ".txt");
14265 if (fileExists(filename_info))
14266 setup_file_hash = loadSetupFileHash(filename_info);
14268 free(filename_prefix);
14269 free(filename_info);
14272 if (setup_file_hash == NULL)
14275 // ---------- music file info found ----------
14277 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14279 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14281 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14283 *token_to_value_ptr[i].value_ptr =
14284 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14287 tmp_music_file_info.basename = getStringCopy(basename);
14288 tmp_music_file_info.music = music;
14289 tmp_music_file_info.is_sound = is_sound;
14291 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14292 *new_music_file_info = tmp_music_file_info;
14294 return new_music_file_info;
14297 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14299 return get_music_file_info_ext(basename, music, FALSE);
14302 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14304 return get_music_file_info_ext(basename, sound, TRUE);
14307 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14308 char *basename, boolean is_sound)
14310 for (; list != NULL; list = list->next)
14311 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14317 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14319 return music_info_listed_ext(list, basename, FALSE);
14322 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14324 return music_info_listed_ext(list, basename, TRUE);
14327 void LoadMusicInfo(void)
14329 int num_music_noconf = getMusicListSize_NoConf();
14330 int num_music = getMusicListSize();
14331 int num_sounds = getSoundListSize();
14332 struct FileInfo *music, *sound;
14333 struct MusicFileInfo *next, **new;
14337 while (music_file_info != NULL)
14339 next = music_file_info->next;
14341 checked_free(music_file_info->basename);
14343 checked_free(music_file_info->title_header);
14344 checked_free(music_file_info->artist_header);
14345 checked_free(music_file_info->album_header);
14346 checked_free(music_file_info->year_header);
14347 checked_free(music_file_info->played_header);
14349 checked_free(music_file_info->title);
14350 checked_free(music_file_info->artist);
14351 checked_free(music_file_info->album);
14352 checked_free(music_file_info->year);
14353 checked_free(music_file_info->played);
14355 free(music_file_info);
14357 music_file_info = next;
14360 new = &music_file_info;
14362 // get (configured or unconfigured) music file info for all levels
14363 for (i = leveldir_current->first_level;
14364 i <= leveldir_current->last_level; i++)
14368 if (levelset.music[i] != MUS_UNDEFINED)
14370 // get music file info for configured level music
14371 music_nr = levelset.music[i];
14373 else if (num_music_noconf > 0)
14375 // get music file info for unconfigured level music
14376 int level_pos = i - leveldir_current->first_level;
14378 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14385 char *basename = getMusicInfoEntryFilename(music_nr);
14387 if (basename == NULL)
14390 if (!music_info_listed(music_file_info, basename))
14392 *new = get_music_file_info(basename, music_nr);
14395 new = &(*new)->next;
14399 // get music file info for all remaining configured music files
14400 for (i = 0; i < num_music; i++)
14402 music = getMusicListEntry(i);
14404 if (music->filename == NULL)
14407 if (strEqual(music->filename, UNDEFINED_FILENAME))
14410 // a configured file may be not recognized as music
14411 if (!FileIsMusic(music->filename))
14414 if (!music_info_listed(music_file_info, music->filename))
14416 *new = get_music_file_info(music->filename, i);
14419 new = &(*new)->next;
14423 // get sound file info for all configured sound files
14424 for (i = 0; i < num_sounds; i++)
14426 sound = getSoundListEntry(i);
14428 if (sound->filename == NULL)
14431 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14434 // a configured file may be not recognized as sound
14435 if (!FileIsSound(sound->filename))
14438 if (!sound_info_listed(music_file_info, sound->filename))
14440 *new = get_sound_file_info(sound->filename, i);
14442 new = &(*new)->next;
14446 // add pointers to previous list nodes
14448 struct MusicFileInfo *node = music_file_info;
14450 while (node != NULL)
14453 node->next->prev = node;
14459 static void add_helpanim_entry(int element, int action, int direction,
14460 int delay, int *num_list_entries)
14462 struct HelpAnimInfo *new_list_entry;
14463 (*num_list_entries)++;
14466 checked_realloc(helpanim_info,
14467 *num_list_entries * sizeof(struct HelpAnimInfo));
14468 new_list_entry = &helpanim_info[*num_list_entries - 1];
14470 new_list_entry->element = element;
14471 new_list_entry->action = action;
14472 new_list_entry->direction = direction;
14473 new_list_entry->delay = delay;
14476 static void print_unknown_token(char *filename, char *token, int token_nr)
14481 Warn("unknown token(s) found in config file:");
14482 Warn("- config file: '%s'", filename);
14485 Warn("- token: '%s'", token);
14488 static void print_unknown_token_end(int token_nr)
14494 void LoadHelpAnimInfo(void)
14496 char *filename = getHelpAnimFilename();
14497 SetupFileList *setup_file_list = NULL, *list;
14498 SetupFileHash *element_hash, *action_hash, *direction_hash;
14499 int num_list_entries = 0;
14500 int num_unknown_tokens = 0;
14503 if (fileExists(filename))
14504 setup_file_list = loadSetupFileList(filename);
14506 if (setup_file_list == NULL)
14508 // use reliable default values from static configuration
14509 SetupFileList *insert_ptr;
14511 insert_ptr = setup_file_list =
14512 newSetupFileList(helpanim_config[0].token,
14513 helpanim_config[0].value);
14515 for (i = 1; helpanim_config[i].token; i++)
14516 insert_ptr = addListEntry(insert_ptr,
14517 helpanim_config[i].token,
14518 helpanim_config[i].value);
14521 element_hash = newSetupFileHash();
14522 action_hash = newSetupFileHash();
14523 direction_hash = newSetupFileHash();
14525 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14526 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14528 for (i = 0; i < NUM_ACTIONS; i++)
14529 setHashEntry(action_hash, element_action_info[i].suffix,
14530 i_to_a(element_action_info[i].value));
14532 // do not store direction index (bit) here, but direction value!
14533 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14534 setHashEntry(direction_hash, element_direction_info[i].suffix,
14535 i_to_a(1 << element_direction_info[i].value));
14537 for (list = setup_file_list; list != NULL; list = list->next)
14539 char *element_token, *action_token, *direction_token;
14540 char *element_value, *action_value, *direction_value;
14541 int delay = atoi(list->value);
14543 if (strEqual(list->token, "end"))
14545 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14550 /* first try to break element into element/action/direction parts;
14551 if this does not work, also accept combined "element[.act][.dir]"
14552 elements (like "dynamite.active"), which are unique elements */
14554 if (strchr(list->token, '.') == NULL) // token contains no '.'
14556 element_value = getHashEntry(element_hash, list->token);
14557 if (element_value != NULL) // element found
14558 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14559 &num_list_entries);
14562 // no further suffixes found -- this is not an element
14563 print_unknown_token(filename, list->token, num_unknown_tokens++);
14569 // token has format "<prefix>.<something>"
14571 action_token = strchr(list->token, '.'); // suffix may be action ...
14572 direction_token = action_token; // ... or direction
14574 element_token = getStringCopy(list->token);
14575 *strchr(element_token, '.') = '\0';
14577 element_value = getHashEntry(element_hash, element_token);
14579 if (element_value == NULL) // this is no element
14581 element_value = getHashEntry(element_hash, list->token);
14582 if (element_value != NULL) // combined element found
14583 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14584 &num_list_entries);
14586 print_unknown_token(filename, list->token, num_unknown_tokens++);
14588 free(element_token);
14593 action_value = getHashEntry(action_hash, action_token);
14595 if (action_value != NULL) // action found
14597 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14598 &num_list_entries);
14600 free(element_token);
14605 direction_value = getHashEntry(direction_hash, direction_token);
14607 if (direction_value != NULL) // direction found
14609 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14610 &num_list_entries);
14612 free(element_token);
14617 if (strchr(action_token + 1, '.') == NULL)
14619 // no further suffixes found -- this is not an action nor direction
14621 element_value = getHashEntry(element_hash, list->token);
14622 if (element_value != NULL) // combined element found
14623 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14624 &num_list_entries);
14626 print_unknown_token(filename, list->token, num_unknown_tokens++);
14628 free(element_token);
14633 // token has format "<prefix>.<suffix>.<something>"
14635 direction_token = strchr(action_token + 1, '.');
14637 action_token = getStringCopy(action_token);
14638 *strchr(action_token + 1, '.') = '\0';
14640 action_value = getHashEntry(action_hash, action_token);
14642 if (action_value == NULL) // this is no action
14644 element_value = getHashEntry(element_hash, list->token);
14645 if (element_value != NULL) // combined element found
14646 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14647 &num_list_entries);
14649 print_unknown_token(filename, list->token, num_unknown_tokens++);
14651 free(element_token);
14652 free(action_token);
14657 direction_value = getHashEntry(direction_hash, direction_token);
14659 if (direction_value != NULL) // direction found
14661 add_helpanim_entry(atoi(element_value), atoi(action_value),
14662 atoi(direction_value), delay, &num_list_entries);
14664 free(element_token);
14665 free(action_token);
14670 // this is no direction
14672 element_value = getHashEntry(element_hash, list->token);
14673 if (element_value != NULL) // combined element found
14674 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14675 &num_list_entries);
14677 print_unknown_token(filename, list->token, num_unknown_tokens++);
14679 free(element_token);
14680 free(action_token);
14683 print_unknown_token_end(num_unknown_tokens);
14685 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14686 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14688 freeSetupFileList(setup_file_list);
14689 freeSetupFileHash(element_hash);
14690 freeSetupFileHash(action_hash);
14691 freeSetupFileHash(direction_hash);
14694 for (i = 0; i < num_list_entries; i++)
14695 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14696 EL_NAME(helpanim_info[i].element),
14697 helpanim_info[i].element,
14698 helpanim_info[i].action,
14699 helpanim_info[i].direction,
14700 helpanim_info[i].delay);
14704 void LoadHelpTextInfo(void)
14706 char *filename = getHelpTextFilename();
14709 if (helptext_info != NULL)
14711 freeSetupFileHash(helptext_info);
14712 helptext_info = NULL;
14715 if (fileExists(filename))
14716 helptext_info = loadSetupFileHash(filename);
14718 if (helptext_info == NULL)
14720 // use reliable default values from static configuration
14721 helptext_info = newSetupFileHash();
14723 for (i = 0; helptext_config[i].token; i++)
14724 setHashEntry(helptext_info,
14725 helptext_config[i].token,
14726 helptext_config[i].value);
14730 BEGIN_HASH_ITERATION(helptext_info, itr)
14732 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14733 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14735 END_HASH_ITERATION(hash, itr)
14740 // ----------------------------------------------------------------------------
14742 // ----------------------------------------------------------------------------
14744 #define MAX_NUM_CONVERT_LEVELS 1000
14746 void ConvertLevels(void)
14748 static LevelDirTree *convert_leveldir = NULL;
14749 static int convert_level_nr = -1;
14750 static int num_levels_handled = 0;
14751 static int num_levels_converted = 0;
14752 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14755 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14756 global.convert_leveldir);
14758 if (convert_leveldir == NULL)
14759 Fail("no such level identifier: '%s'", global.convert_leveldir);
14761 leveldir_current = convert_leveldir;
14763 if (global.convert_level_nr != -1)
14765 convert_leveldir->first_level = global.convert_level_nr;
14766 convert_leveldir->last_level = global.convert_level_nr;
14769 convert_level_nr = convert_leveldir->first_level;
14771 PrintLine("=", 79);
14772 Print("Converting levels\n");
14773 PrintLine("-", 79);
14774 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14775 Print("Level series name: '%s'\n", convert_leveldir->name);
14776 Print("Level series author: '%s'\n", convert_leveldir->author);
14777 Print("Number of levels: %d\n", convert_leveldir->levels);
14778 PrintLine("=", 79);
14781 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14782 levels_failed[i] = FALSE;
14784 while (convert_level_nr <= convert_leveldir->last_level)
14786 char *level_filename;
14789 level_nr = convert_level_nr++;
14791 Print("Level %03d: ", level_nr);
14793 LoadLevel(level_nr);
14794 if (level.no_level_file || level.no_valid_file)
14796 Print("(no level)\n");
14800 Print("converting level ... ");
14803 // special case: conversion of some EMC levels as requested by ACME
14804 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14807 level_filename = getDefaultLevelFilename(level_nr);
14808 new_level = !fileExists(level_filename);
14812 SaveLevel(level_nr);
14814 num_levels_converted++;
14816 Print("converted.\n");
14820 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14821 levels_failed[level_nr] = TRUE;
14823 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14826 num_levels_handled++;
14830 PrintLine("=", 79);
14831 Print("Number of levels handled: %d\n", num_levels_handled);
14832 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14833 (num_levels_handled ?
14834 num_levels_converted * 100 / num_levels_handled : 0));
14835 PrintLine("-", 79);
14836 Print("Summary (for automatic parsing by scripts):\n");
14837 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14838 convert_leveldir->identifier, num_levels_converted,
14839 num_levels_handled,
14840 (num_levels_handled ?
14841 num_levels_converted * 100 / num_levels_handled : 0));
14843 if (num_levels_handled != num_levels_converted)
14845 Print(", FAILED:");
14846 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14847 if (levels_failed[i])
14852 PrintLine("=", 79);
14854 CloseAllAndExit(0);
14858 // ----------------------------------------------------------------------------
14859 // create and save images for use in level sketches (raw BMP format)
14860 // ----------------------------------------------------------------------------
14862 void CreateLevelSketchImages(void)
14868 InitElementPropertiesGfxElement();
14870 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14871 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14873 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14875 int element = getMappedElement(i);
14876 char basename1[16];
14877 char basename2[16];
14881 sprintf(basename1, "%04d.bmp", i);
14882 sprintf(basename2, "%04ds.bmp", i);
14884 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14885 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14887 DrawSizedElement(0, 0, element, TILESIZE);
14888 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14890 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14891 Fail("cannot save level sketch image file '%s'", filename1);
14893 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14894 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14896 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14897 Fail("cannot save level sketch image file '%s'", filename2);
14902 // create corresponding SQL statements (for normal and small images)
14905 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14906 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14909 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14910 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14912 // optional: create content for forum level sketch demonstration post
14914 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14917 FreeBitmap(bitmap1);
14918 FreeBitmap(bitmap2);
14921 fprintf(stderr, "\n");
14923 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14925 CloseAllAndExit(0);
14929 // ----------------------------------------------------------------------------
14930 // create and save images for element collecting animations (raw BMP format)
14931 // ----------------------------------------------------------------------------
14933 static boolean createCollectImage(int element)
14935 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14938 void CreateCollectElementImages(void)
14942 int anim_frames = num_steps - 1;
14943 int tile_size = TILESIZE;
14944 int anim_width = tile_size * anim_frames;
14945 int anim_height = tile_size;
14946 int num_collect_images = 0;
14947 int pos_collect_images = 0;
14949 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14950 if (createCollectImage(i))
14951 num_collect_images++;
14953 Info("Creating %d element collecting animation images ...",
14954 num_collect_images);
14956 int dst_width = anim_width * 2;
14957 int dst_height = anim_height * num_collect_images / 2;
14958 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14959 char *basename_bmp = "RocksCollect.bmp";
14960 char *basename_png = "RocksCollect.png";
14961 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14962 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14963 int len_filename_bmp = strlen(filename_bmp);
14964 int len_filename_png = strlen(filename_png);
14965 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14966 char cmd_convert[max_command_len];
14968 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14972 // force using RGBA surface for destination bitmap
14973 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14974 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14976 dst_bitmap->surface =
14977 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14979 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14981 if (!createCollectImage(i))
14984 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14985 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14986 int graphic = el2img(i);
14987 char *token_name = element_info[i].token_name;
14988 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14989 Bitmap *src_bitmap;
14992 Info("- creating collecting image for '%s' ...", token_name);
14994 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14996 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14997 tile_size, tile_size, 0, 0);
14999 // force using RGBA surface for temporary bitmap (using transparent black)
15000 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15001 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15003 tmp_bitmap->surface =
15004 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15006 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15008 for (j = 0; j < anim_frames; j++)
15010 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15011 int frame_size = frame_size_final * num_steps;
15012 int offset = (tile_size - frame_size_final) / 2;
15013 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15015 while (frame_size > frame_size_final)
15019 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15021 FreeBitmap(frame_bitmap);
15023 frame_bitmap = half_bitmap;
15026 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15027 frame_size_final, frame_size_final,
15028 dst_x + j * tile_size + offset, dst_y + offset);
15030 FreeBitmap(frame_bitmap);
15033 tmp_bitmap->surface_masked = NULL;
15035 FreeBitmap(tmp_bitmap);
15037 pos_collect_images++;
15040 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15041 Fail("cannot save element collecting image file '%s'", filename_bmp);
15043 FreeBitmap(dst_bitmap);
15045 Info("Converting image file from BMP to PNG ...");
15047 if (system(cmd_convert) != 0)
15048 Fail("converting image file failed");
15050 unlink(filename_bmp);
15054 CloseAllAndExit(0);
15058 // ----------------------------------------------------------------------------
15059 // create and save images for custom and group elements (raw BMP format)
15060 // ----------------------------------------------------------------------------
15062 void CreateCustomElementImages(char *directory)
15064 char *src_basename = "RocksCE-template.ilbm";
15065 char *dst_basename = "RocksCE.bmp";
15066 char *src_filename = getPath2(directory, src_basename);
15067 char *dst_filename = getPath2(directory, dst_basename);
15068 Bitmap *src_bitmap;
15070 int yoffset_ce = 0;
15071 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15074 InitVideoDefaults();
15076 ReCreateBitmap(&backbuffer, video.width, video.height);
15078 src_bitmap = LoadImage(src_filename);
15080 bitmap = CreateBitmap(TILEX * 16 * 2,
15081 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15084 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15091 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15092 TILEX * x, TILEY * y + yoffset_ce);
15094 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15096 TILEX * x + TILEX * 16,
15097 TILEY * y + yoffset_ce);
15099 for (j = 2; j >= 0; j--)
15103 BlitBitmap(src_bitmap, bitmap,
15104 TILEX + c * 7, 0, 6, 10,
15105 TILEX * x + 6 + j * 7,
15106 TILEY * y + 11 + yoffset_ce);
15108 BlitBitmap(src_bitmap, bitmap,
15109 TILEX + c * 8, TILEY, 6, 10,
15110 TILEX * 16 + TILEX * x + 6 + j * 8,
15111 TILEY * y + 10 + yoffset_ce);
15117 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15124 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15125 TILEX * x, TILEY * y + yoffset_ge);
15127 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15129 TILEX * x + TILEX * 16,
15130 TILEY * y + yoffset_ge);
15132 for (j = 1; j >= 0; j--)
15136 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15137 TILEX * x + 6 + j * 10,
15138 TILEY * y + 11 + yoffset_ge);
15140 BlitBitmap(src_bitmap, bitmap,
15141 TILEX + c * 8, TILEY + 12, 6, 10,
15142 TILEX * 16 + TILEX * x + 10 + j * 8,
15143 TILEY * y + 10 + yoffset_ge);
15149 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15150 Fail("cannot save CE graphics file '%s'", dst_filename);
15152 FreeBitmap(bitmap);
15154 CloseAllAndExit(0);