1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
28 #define ENABLE_UNUSED_CODE 0 // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
30 #define ENABLE_RESERVED_CODE 0 // reserved for later use
32 #define CHUNK_ID_LEN 4 // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE -1 // do not write chunk size
36 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
39 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
62 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
185 -1, SAVE_CONF_ALWAYS,
186 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
217 &li.can_move_into_acid_bits, ~0 // default: everything can
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 // default: always deadly
226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
227 &li.em_explodes_by_fire, FALSE
231 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
232 &li.score[SC_TIME_BONUS], 1
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
237 &li.auto_exit_sokoban, FALSE
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
242 &li.auto_count_gems, FALSE
246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
247 &li.solved_by_one_player, FALSE
251 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
252 &li.time_score_base, 1
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
257 &li.rate_time_over_score, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
262 &li.bd_intermission, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
267 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
272 &li.bd_pal_timing, FALSE
276 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
277 &li.bd_cycle_delay_ms, 200
281 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
282 &li.bd_cycle_delay_c64, 0
286 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
287 &li.bd_hatching_delay_cycles, 21
291 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
292 &li.bd_hatching_delay_seconds, 2
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
297 &li.bd_line_shifting_borders, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
302 &li.bd_scan_first_and_last_row, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
307 &li.bd_short_explosions, TRUE
311 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
312 &li.bd_cave_random_seed_c64, 0
316 TYPE_INTEGER, CONF_VALUE_32_BIT(3),
317 &li.bd_color_b, GD_C64_COLOR(0)
321 TYPE_INTEGER, CONF_VALUE_32_BIT(4),
322 &li.bd_color_0, GD_C64_COLOR(0)
326 TYPE_INTEGER, CONF_VALUE_32_BIT(5),
327 &li.bd_color_1, GD_C64_COLOR(8)
331 TYPE_INTEGER, CONF_VALUE_32_BIT(6),
332 &li.bd_color_2, GD_C64_COLOR(11)
336 TYPE_INTEGER, CONF_VALUE_32_BIT(7),
337 &li.bd_color_3, GD_C64_COLOR(1)
341 TYPE_INTEGER, CONF_VALUE_32_BIT(8),
342 &li.bd_color_4, GD_C64_COLOR(5)
346 TYPE_INTEGER, CONF_VALUE_32_BIT(9),
347 &li.bd_color_5, GD_C64_COLOR(6)
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
359 // (these values are the same for each player)
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
363 &li.block_last_field, FALSE // default case for EM levels
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
368 &li.sp_block_last_field, TRUE // default case for SP levels
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
373 &li.instant_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
378 &li.can_pass_to_walkable, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
383 &li.block_snap_field, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
388 &li.continuous_snapping, TRUE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
393 &li.shifted_relocation, FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
398 &li.lazy_relocation, FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
403 &li.finish_dig_collect, TRUE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
408 &li.keep_walkable_ce, FALSE
411 // (these values are different for each player)
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[0], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[0], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[0], EL_PLAYER_1
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[0], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[0], EL_PLAYER_1
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[0], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[0], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[1], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[1], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[1], EL_PLAYER_2
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[1], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[1], EL_PLAYER_2
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[1], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[1], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
529 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
534 &li.initial_player_gravity[2], FALSE
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
539 &li.use_start_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
544 &li.start_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
549 &li.use_artwork_element[2], FALSE
553 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
554 &li.artwork_element[2], EL_PLAYER_3
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
559 &li.use_explosion_element[2], FALSE
563 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
564 &li.explosion_element[2], EL_PLAYER_3
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
569 &li.use_initial_inventory[2], FALSE
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
574 &li.initial_inventory_size[2], 1
578 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
579 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
585 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
586 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
591 &li.initial_player_gravity[3], FALSE
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
596 &li.use_start_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
601 &li.start_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
606 &li.use_artwork_element[3], FALSE
610 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
611 &li.artwork_element[3], EL_PLAYER_4
615 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
616 &li.use_explosion_element[3], FALSE
620 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
621 &li.explosion_element[3], EL_PLAYER_4
625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
626 &li.use_initial_inventory[3], FALSE
630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
631 &li.initial_inventory_size[3], 1
635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
636 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
640 // (these values are only valid for BD style levels)
641 // (some values for BD style amoeba following below)
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.bd_diagonal_movements, FALSE
649 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
650 &li.bd_topmost_player_active, TRUE
654 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
655 &li.bd_pushing_prob, 25
659 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
660 &li.bd_pushing_prob_with_sweet, 100
664 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
665 &li.bd_push_mega_rock_with_sweet, FALSE
669 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
670 &li.bd_snap_element, EL_EMPTY
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_sand_looks_like, EL_BD_SAND
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BD_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BD_ROCK
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND_EXTRA], 20
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
698 &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BD_DIAMOND
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_explodes_to, EL_BD_EXPLODING_1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BD_EXPLODING_1
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_explodes_to, EL_BD_DIAMOND_GROWING_1
725 EL_BD_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BD_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BD_ROCK_GROWING_1
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BD_EXPLODING_1
743 EL_BD_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BD_DIAMOND
749 EL_BD_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BD_WALL
755 EL_BD_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BD_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BD_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BD_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BD_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BD_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BD_MAGIC_WALL, -1,
788 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
792 EL_BD_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
797 EL_BD_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
802 EL_BD_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
807 EL_BD_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
812 EL_BD_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
817 EL_BD_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
824 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
825 &li.bd_clock_extra_time, 30
829 EL_BD_VOODOO_DOLL, -1,
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
831 &li.bd_voodoo_collects_diamonds, FALSE
834 EL_BD_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
836 &li.bd_voodoo_hurt_kills_player, FALSE
839 EL_BD_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
841 &li.bd_voodoo_dies_by_rock, FALSE
844 EL_BD_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
846 &li.bd_voodoo_vanish_by_explosion, TRUE
849 EL_BD_VOODOO_DOLL, -1,
850 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
851 &li.bd_voodoo_penalty_time, 30
856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
857 &li.bd_slime_is_predictable, TRUE
861 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
862 &li.bd_slime_permeability_rate, 100
866 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
867 &li.bd_slime_permeability_bits_c64, 0
871 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
872 &li.bd_slime_random_seed_c64, -1
876 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
877 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
882 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
887 &li.bd_slime_eats_element_2, EL_BD_ROCK
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
892 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
897 &li.bd_slime_eats_element_3, EL_BD_NUT
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
902 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
907 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
908 &li.bd_acid_eats_element, EL_BD_SAND
912 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
913 &li.bd_acid_spread_rate, 3
917 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
918 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
923 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
924 &li.bd_biter_move_delay, 0
928 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
929 &li.bd_biter_eats_element, EL_BD_DIAMOND
934 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
935 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
939 EL_BD_EXPANDABLE_WALL_ANY, -1,
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
941 &li.bd_change_expanding_wall, FALSE
944 EL_BD_EXPANDABLE_WALL_ANY, -1,
945 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
946 &li.bd_expanding_wall_looks_like, EL_BD_WALL
950 EL_BD_REPLICATOR, -1,
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &li.bd_replicators_active, TRUE
955 EL_BD_REPLICATOR, -1,
956 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
957 &li.bd_replicator_create_delay, 4
961 EL_BD_CONVEYOR_LEFT, -1,
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
963 &li.bd_conveyor_belts_active, TRUE
966 EL_BD_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
968 &li.bd_conveyor_belts_changed, FALSE
973 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
974 &li.bd_water_cannot_flow_down, FALSE
979 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
980 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
984 EL_BD_PNEUMATIC_HAMMER, -1,
985 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
986 &li.bd_hammer_walls_break_delay, 5
989 EL_BD_PNEUMATIC_HAMMER, -1,
990 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
991 &li.bd_hammer_walls_reappear, FALSE
994 EL_BD_PNEUMATIC_HAMMER, -1,
995 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
996 &li.bd_hammer_walls_reappear_delay, 100
1000 EL_BD_ROCKET_LAUNCHER, -1,
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1002 &li.bd_infinite_rockets, FALSE
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1008 &li.bd_num_skeletons_needed_for_pot, 5
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1013 &li.bd_skeleton_worth_num_diamonds, 0
1017 EL_BD_CREATURE_SWITCH, -1,
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1019 &li.bd_creatures_start_backwards, FALSE
1022 EL_BD_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1024 &li.bd_creatures_turn_on_hatching, FALSE
1027 EL_BD_CREATURE_SWITCH, -1,
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1029 &li.bd_creatures_auto_turn_delay, 0
1033 EL_BD_GRAVITY_SWITCH, -1,
1034 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1035 &li.bd_gravity_direction, GD_MV_DOWN
1038 EL_BD_GRAVITY_SWITCH, -1,
1039 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1040 &li.bd_gravity_switch_active, FALSE
1043 EL_BD_GRAVITY_SWITCH, -1,
1044 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1045 &li.bd_gravity_switch_delay, 10
1048 EL_BD_GRAVITY_SWITCH, -1,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1050 &li.bd_gravity_affects_all, TRUE
1053 // (the following values are related to various game elements)
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.score[SC_EMERALD], 10
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.score[SC_DIAMOND], 10
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1070 &li.score[SC_BUG], 10
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1076 &li.score[SC_SPACESHIP], 10
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1082 &li.score[SC_PACMAN], 10
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1088 &li.score[SC_NUT], 10
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1094 &li.score[SC_DYNAMITE], 10
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1100 &li.score[SC_KEY], 10
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1106 &li.score[SC_PEARL], 10
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1112 &li.score[SC_CRYSTAL], 10
1115 // (amoeba values used by R'n'D game engine only)
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1119 &li.amoeba_content, EL_DIAMOND
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1124 &li.amoeba_speed, 10
1128 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1129 &li.grow_into_diggable, TRUE
1131 // (amoeba values used by BD game engine only)
1134 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1135 &li.bd_amoeba_wait_for_hatching, FALSE
1139 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1140 &li.bd_amoeba_start_immediately, TRUE
1144 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1145 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1150 &li.bd_amoeba_threshold_too_big, 200
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1155 &li.bd_amoeba_slow_growth_time, 200
1159 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1160 &li.bd_amoeba_slow_growth_rate, 3
1164 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1165 &li.bd_amoeba_fast_growth_rate, 25
1169 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1170 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1175 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1181 &li.bd_amoeba_2_threshold_too_big, 200
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1186 &li.bd_amoeba_2_slow_growth_time, 200
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1191 &li.bd_amoeba_2_slow_growth_rate, 3
1195 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1196 &li.bd_amoeba_2_fast_growth_rate, 25
1200 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1201 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1205 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1206 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1211 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1215 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1216 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1221 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1222 &li.yamyam_content, EL_ROCK, NULL,
1223 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1227 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1228 &li.score[SC_YAMYAM], 10
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1234 &li.score[SC_ROBOT], 10
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1244 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1250 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1251 &li.time_magic_wall, 10
1255 EL_GAME_OF_LIFE, -1,
1256 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1257 &li.game_of_life[0], 2
1260 EL_GAME_OF_LIFE, -1,
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1262 &li.game_of_life[1], 3
1265 EL_GAME_OF_LIFE, -1,
1266 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1267 &li.game_of_life[2], 3
1270 EL_GAME_OF_LIFE, -1,
1271 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1272 &li.game_of_life[3], 3
1275 EL_GAME_OF_LIFE, -1,
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1277 &li.use_life_bugs, FALSE
1282 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1287 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1292 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1297 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1302 EL_TIMEGATE_SWITCH, -1,
1303 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1304 &li.time_timegate, 10
1308 EL_LIGHT_SWITCH_ACTIVE, -1,
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1314 EL_SHIELD_NORMAL, -1,
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1316 &li.shield_normal_time, 10
1319 EL_SHIELD_NORMAL, -1,
1320 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1321 &li.score[SC_SHIELD], 10
1325 EL_SHIELD_DEADLY, -1,
1326 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1327 &li.shield_deadly_time, 10
1330 EL_SHIELD_DEADLY, -1,
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1332 &li.score[SC_SHIELD], 10
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1343 &li.extra_time_score, 10
1347 EL_TIME_ORB_FULL, -1,
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1349 &li.time_orb_time, 10
1352 EL_TIME_ORB_FULL, -1,
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1354 &li.use_time_orb_bug, FALSE
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1360 &li.use_spring_bug, FALSE
1365 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1366 &li.android_move_time, 10
1370 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1371 &li.android_clone_time, 10
1374 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1375 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1376 &li.android_clone_element[0], EL_EMPTY, NULL,
1377 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1381 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1382 &li.android_clone_element[0], EL_EMPTY, NULL,
1383 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1388 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1389 &li.lenses_score, 10
1393 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1398 EL_EMC_MAGNIFIER, -1,
1399 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1400 &li.magnify_score, 10
1403 EL_EMC_MAGNIFIER, -1,
1404 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1405 &li.magnify_time, 10
1409 EL_EMC_MAGIC_BALL, -1,
1410 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1414 EL_EMC_MAGIC_BALL, -1,
1415 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1416 &li.ball_random, FALSE
1419 EL_EMC_MAGIC_BALL, -1,
1420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1421 &li.ball_active_initial, FALSE
1424 EL_EMC_MAGIC_BALL, -1,
1425 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1426 &li.ball_content, EL_EMPTY, NULL,
1427 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1431 EL_SOKOBAN_FIELD_EMPTY, -1,
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1433 &li.sb_fields_needed, TRUE
1437 EL_SOKOBAN_OBJECT, -1,
1438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1439 &li.sb_objects_needed, TRUE
1444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1445 &li.mm_laser_red, FALSE
1449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1450 &li.mm_laser_green, FALSE
1454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1455 &li.mm_laser_blue, TRUE
1460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1461 &li.df_laser_red, TRUE
1465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1466 &li.df_laser_green, TRUE
1470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1471 &li.df_laser_blue, FALSE
1475 EL_MM_FUSE_ACTIVE, -1,
1476 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1477 &li.mm_time_fuse, 25
1481 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1482 &li.mm_time_bomb, 75
1486 EL_MM_GRAY_BALL, -1,
1487 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1488 &li.mm_time_ball, 75
1491 EL_MM_GRAY_BALL, -1,
1492 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1493 &li.mm_ball_choice_mode, ANIM_RANDOM
1496 EL_MM_GRAY_BALL, -1,
1497 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1498 &li.mm_ball_content, EL_EMPTY, NULL,
1499 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1502 EL_MM_GRAY_BALL, -1,
1503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1504 &li.rotate_mm_ball_content, TRUE
1507 EL_MM_GRAY_BALL, -1,
1508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1509 &li.explode_mm_ball, FALSE
1513 EL_MM_STEEL_BLOCK, -1,
1514 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1515 &li.mm_time_block, 75
1518 EL_MM_LIGHTBALL, -1,
1519 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1520 &li.score[SC_ELEM_BONUS], 10
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1534 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1535 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1539 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1540 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1545 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1546 &xx_envelope.autowrap, FALSE
1550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1551 &xx_envelope.centered, FALSE
1556 TYPE_STRING, CONF_VALUE_BYTES(1),
1557 &xx_envelope.text, -1, NULL,
1558 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1559 &xx_default_string_empty[0]
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1573 TYPE_STRING, CONF_VALUE_BYTES(1),
1574 &xx_ei.description[0], -1,
1575 &yy_ei.description[0],
1576 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1577 &xx_default_description[0]
1582 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1583 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1586 #if ENABLE_RESERVED_CODE
1587 // (reserved for later use)
1590 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1591 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1599 &xx_ei.use_gfx_element, FALSE,
1600 &yy_ei.use_gfx_element
1604 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1605 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1606 &yy_ei.gfx_element_initial
1611 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1612 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1613 &yy_ei.access_direction
1618 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1619 &xx_ei.collect_score_initial, 10,
1620 &yy_ei.collect_score_initial
1624 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1625 &xx_ei.collect_count_initial, 1,
1626 &yy_ei.collect_count_initial
1631 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1632 &xx_ei.ce_value_fixed_initial, 0,
1633 &yy_ei.ce_value_fixed_initial
1637 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1638 &xx_ei.ce_value_random_initial, 0,
1639 &yy_ei.ce_value_random_initial
1643 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1644 &xx_ei.use_last_ce_value, FALSE,
1645 &yy_ei.use_last_ce_value
1650 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1651 &xx_ei.push_delay_fixed, 8,
1652 &yy_ei.push_delay_fixed
1656 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1657 &xx_ei.push_delay_random, 8,
1658 &yy_ei.push_delay_random
1662 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1663 &xx_ei.drop_delay_fixed, 0,
1664 &yy_ei.drop_delay_fixed
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1669 &xx_ei.drop_delay_random, 0,
1670 &yy_ei.drop_delay_random
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1675 &xx_ei.move_delay_fixed, 0,
1676 &yy_ei.move_delay_fixed
1680 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1681 &xx_ei.move_delay_random, 0,
1682 &yy_ei.move_delay_random
1686 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1687 &xx_ei.step_delay_fixed, 0,
1688 &yy_ei.step_delay_fixed
1692 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1693 &xx_ei.step_delay_random, 0,
1694 &yy_ei.step_delay_random
1699 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1700 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1705 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1706 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1707 &yy_ei.move_direction_initial
1711 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1712 &xx_ei.move_stepsize, TILEX / 8,
1713 &yy_ei.move_stepsize
1718 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1719 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1720 &yy_ei.move_enter_element
1724 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1725 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1726 &yy_ei.move_leave_element
1730 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1731 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1732 &yy_ei.move_leave_type
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1738 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1739 &yy_ei.slippery_type
1744 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1745 &xx_ei.explosion_type, EXPLODES_3X3,
1746 &yy_ei.explosion_type
1750 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1751 &xx_ei.explosion_delay, 16,
1752 &yy_ei.explosion_delay
1756 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1757 &xx_ei.ignition_delay, 8,
1758 &yy_ei.ignition_delay
1763 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1764 &xx_ei.content, EL_EMPTY_SPACE,
1766 &xx_num_contents, 1, 1
1769 // ---------- "num_change_pages" must be the last entry ---------------------
1772 -1, SAVE_CONF_ALWAYS,
1773 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1774 &xx_ei.num_change_pages, 1,
1775 &yy_ei.num_change_pages
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1788 // ---------- "current_change_page" must be the first entry -----------------
1791 -1, SAVE_CONF_ALWAYS,
1792 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1793 &xx_current_change_page, -1
1796 // ---------- (the remaining entries can be in any order) -------------------
1800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1801 &xx_change.can_change, FALSE
1806 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1807 &xx_event_bits[0], 0
1811 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1812 &xx_event_bits[1], 0
1817 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1818 &xx_change.trigger_player, CH_PLAYER_ANY
1822 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1823 &xx_change.trigger_side, CH_SIDE_ANY
1827 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1828 &xx_change.trigger_page, CH_PAGE_ANY
1833 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1834 &xx_change.target_element, EL_EMPTY_SPACE
1839 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1840 &xx_change.delay_fixed, 0
1844 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1845 &xx_change.delay_random, 0
1849 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1850 &xx_change.delay_frames, FRAMES_PER_SECOND
1855 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1856 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1862 &xx_change.explode, FALSE
1866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1867 &xx_change.use_target_content, FALSE
1871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1872 &xx_change.only_if_complete, FALSE
1876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1877 &xx_change.use_random_replace, FALSE
1881 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1882 &xx_change.random_percentage, 100
1886 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1887 &xx_change.replace_when, CP_WHEN_EMPTY
1892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1893 &xx_change.has_action, FALSE
1897 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1898 &xx_change.action_type, CA_NO_ACTION
1902 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1903 &xx_change.action_mode, CA_MODE_UNDEFINED
1907 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1908 &xx_change.action_arg, CA_ARG_UNDEFINED
1913 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1914 &xx_change.action_element, EL_EMPTY_SPACE
1919 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1920 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1921 &xx_num_contents, 1, 1
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1935 TYPE_STRING, CONF_VALUE_BYTES(1),
1936 &xx_ei.description[0], -1, NULL,
1937 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1938 &xx_default_description[0]
1943 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1944 &xx_ei.use_gfx_element, FALSE
1948 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1949 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1954 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1955 &xx_group.choice_mode, ANIM_RANDOM
1960 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1961 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1962 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1977 &xx_ei.use_gfx_element, FALSE
1981 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1982 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1992 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1997 &li.block_snap_field, TRUE
2001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2002 &li.continuous_snapping, TRUE
2006 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2007 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2011 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2012 &li.use_start_element[0], FALSE
2016 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2017 &li.start_element[0], EL_PLAYER_1
2021 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2022 &li.use_artwork_element[0], FALSE
2026 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2027 &li.artwork_element[0], EL_PLAYER_1
2031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2032 &li.use_explosion_element[0], FALSE
2036 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2037 &li.explosion_element[0], EL_PLAYER_1
2052 filetype_id_list[] =
2054 { LEVEL_FILE_TYPE_RND, "RND" },
2055 { LEVEL_FILE_TYPE_BD, "BD" },
2056 { LEVEL_FILE_TYPE_EM, "EM" },
2057 { LEVEL_FILE_TYPE_SP, "SP" },
2058 { LEVEL_FILE_TYPE_DX, "DX" },
2059 { LEVEL_FILE_TYPE_SB, "SB" },
2060 { LEVEL_FILE_TYPE_DC, "DC" },
2061 { LEVEL_FILE_TYPE_MM, "MM" },
2062 { LEVEL_FILE_TYPE_MM, "DF" },
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2071 static boolean check_special_flags(char *flag)
2073 if (strEqual(options.special_flags, flag) ||
2074 strEqual(leveldir_current->special_flags, flag))
2080 static struct DateInfo getCurrentDate(void)
2082 time_t epoch_seconds = time(NULL);
2083 struct tm *now = localtime(&epoch_seconds);
2084 struct DateInfo date;
2086 date.year = now->tm_year + 1900;
2087 date.month = now->tm_mon + 1;
2088 date.day = now->tm_mday;
2090 date.src = DATE_SRC_CLOCK;
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2099 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100 change->has_event[i] = FALSE;
2103 static void resetEventBits(void)
2107 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108 xx_event_bits[i] = 0;
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2115 /* important: only change event flag if corresponding event bit is set
2116 (this is because all xx_event_bits[] values are loaded separately,
2117 and all xx_event_bits[] values are set back to zero before loading
2118 another value xx_event_bits[x] (each value representing 32 flags)) */
2120 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122 change->has_event[i] = TRUE;
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2129 /* in contrast to the above function setEventFlagsFromEventBits(), it
2130 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131 depending on the corresponding change->has_event[i] values here, as
2132 all xx_event_bits[] values are reset in resetEventBits() before */
2134 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135 if (change->has_event[i])
2136 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2141 static char description[MAX_ELEMENT_NAME_LEN + 1];
2142 char *default_description = (ei->custom_description != NULL ?
2143 ei->custom_description :
2144 ei->editor_description);
2147 // always start with reliable default values
2148 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149 description[i] = '\0';
2151 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2154 return &description[0];
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2159 char *default_description = getDefaultElementDescription(ei);
2162 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163 ei->description[i] = default_description[i];
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2170 for (i = 0; conf[i].data_type != -1; i++)
2172 int default_value = conf[i].default_value;
2173 int data_type = conf[i].data_type;
2174 int conf_type = conf[i].conf_type;
2175 int byte_mask = conf_type & CONF_MASK_BYTES;
2177 if (byte_mask == CONF_MASK_MULTI_BYTES)
2179 int default_num_entities = conf[i].default_num_entities;
2180 int max_num_entities = conf[i].max_num_entities;
2182 *(int *)(conf[i].num_entities) = default_num_entities;
2184 if (data_type == TYPE_STRING)
2186 char *default_string = conf[i].default_string;
2187 char *string = (char *)(conf[i].value);
2189 strncpy(string, default_string, max_num_entities);
2191 else if (data_type == TYPE_ELEMENT_LIST)
2193 int *element_array = (int *)(conf[i].value);
2196 for (j = 0; j < max_num_entities; j++)
2197 element_array[j] = default_value;
2199 else if (data_type == TYPE_CONTENT_LIST)
2201 struct Content *content = (struct Content *)(conf[i].value);
2204 for (c = 0; c < max_num_entities; c++)
2205 for (y = 0; y < 3; y++)
2206 for (x = 0; x < 3; x++)
2207 content[c].e[x][y] = default_value;
2210 else // constant size configuration data (1, 2 or 4 bytes)
2212 if (data_type == TYPE_BOOLEAN)
2213 *(boolean *)(conf[i].value) = default_value;
2215 *(int *) (conf[i].value) = default_value;
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2224 for (i = 0; conf[i].data_type != -1; i++)
2226 int data_type = conf[i].data_type;
2227 int conf_type = conf[i].conf_type;
2228 int byte_mask = conf_type & CONF_MASK_BYTES;
2230 if (byte_mask == CONF_MASK_MULTI_BYTES)
2232 int max_num_entities = conf[i].max_num_entities;
2234 if (data_type == TYPE_STRING)
2236 char *string = (char *)(conf[i].value);
2237 char *string_copy = (char *)(conf[i].value_copy);
2239 strncpy(string_copy, string, max_num_entities);
2241 else if (data_type == TYPE_ELEMENT_LIST)
2243 int *element_array = (int *)(conf[i].value);
2244 int *element_array_copy = (int *)(conf[i].value_copy);
2247 for (j = 0; j < max_num_entities; j++)
2248 element_array_copy[j] = element_array[j];
2250 else if (data_type == TYPE_CONTENT_LIST)
2252 struct Content *content = (struct Content *)(conf[i].value);
2253 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2256 for (c = 0; c < max_num_entities; c++)
2257 for (y = 0; y < 3; y++)
2258 for (x = 0; x < 3; x++)
2259 content_copy[c].e[x][y] = content[c].e[x][y];
2262 else // constant size configuration data (1, 2 or 4 bytes)
2264 if (data_type == TYPE_BOOLEAN)
2265 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2267 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2276 xx_ei = *ei_from; // copy element data into temporary buffer
2277 yy_ei = *ei_to; // copy element data into temporary buffer
2279 copyConfigFromConfigList(chunk_config_CUSX_base);
2284 // ---------- reinitialize and copy change pages ----------
2286 ei_to->num_change_pages = ei_from->num_change_pages;
2287 ei_to->current_change_page = ei_from->current_change_page;
2289 setElementChangePages(ei_to, ei_to->num_change_pages);
2291 for (i = 0; i < ei_to->num_change_pages; i++)
2292 ei_to->change_page[i] = ei_from->change_page[i];
2294 // ---------- copy group element info ----------
2295 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2296 *ei_to->group = *ei_from->group;
2298 // mark this custom element as modified
2299 ei_to->modified_settings = TRUE;
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2304 int change_page_size = sizeof(struct ElementChangeInfo);
2306 ei->num_change_pages = MAX(1, change_pages);
2309 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2311 if (ei->current_change_page >= ei->num_change_pages)
2312 ei->current_change_page = ei->num_change_pages - 1;
2314 ei->change = &ei->change_page[ei->current_change_page];
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2319 xx_change = *change; // copy change data into temporary buffer
2321 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2323 *change = xx_change;
2325 resetEventFlags(change);
2327 change->direct_action = 0;
2328 change->other_action = 0;
2330 change->pre_change_function = NULL;
2331 change->change_function = NULL;
2332 change->post_change_function = NULL;
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2339 li = *level; // copy level data into temporary buffer
2340 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2341 *level = li; // copy temporary buffer back to level data
2343 setLevelInfoToDefaults_BD();
2344 setLevelInfoToDefaults_EM();
2345 setLevelInfoToDefaults_SP();
2346 setLevelInfoToDefaults_MM();
2348 level->native_bd_level = &native_bd_level;
2349 level->native_em_level = &native_em_level;
2350 level->native_sp_level = &native_sp_level;
2351 level->native_mm_level = &native_mm_level;
2353 level->file_version = FILE_VERSION_ACTUAL;
2354 level->game_version = GAME_VERSION_ACTUAL;
2356 level->creation_date = getCurrentDate();
2358 level->encoding_16bit_field = TRUE;
2359 level->encoding_16bit_yamyam = TRUE;
2360 level->encoding_16bit_amoeba = TRUE;
2362 // clear level name and level author string buffers
2363 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2364 level->name[i] = '\0';
2365 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2366 level->author[i] = '\0';
2368 // set level name and level author to default values
2369 strcpy(level->name, NAMELESS_LEVEL_NAME);
2370 strcpy(level->author, ANONYMOUS_NAME);
2372 // set default game engine type
2373 level->game_engine_type = setup.default_game_engine_type;
2375 // set level playfield to playable default level with player and exit
2376 for (x = 0; x < MAX_LEV_FIELDX; x++)
2377 for (y = 0; y < MAX_LEV_FIELDY; y++)
2378 level->field[x][y] = EL_SAND;
2380 level->field[0][0] = EL_PLAYER_1;
2381 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2383 BorderElement = EL_STEELWALL;
2385 // detect custom elements when loading them
2386 level->file_has_custom_elements = FALSE;
2388 // set random colors for BD style levels according to preferred color type
2389 SetRandomLevelColors_BD(setup.bd_default_color_type);
2391 // set default color type and colors for BD style level colors
2392 SetDefaultLevelColorType_BD();
2393 SetDefaultLevelColors_BD();
2395 // set all bug compatibility flags to "false" => do not emulate this bug
2396 level->use_action_after_change_bug = FALSE;
2398 if (leveldir_current)
2400 // try to determine better author name than 'anonymous'
2401 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2403 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2404 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2408 switch (LEVELCLASS(leveldir_current))
2410 case LEVELCLASS_TUTORIAL:
2411 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2414 case LEVELCLASS_CONTRIB:
2415 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2416 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2419 case LEVELCLASS_PRIVATE:
2420 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2421 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2425 // keep default value
2432 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2434 static boolean clipboard_elements_initialized = FALSE;
2437 InitElementPropertiesStatic();
2439 li = *level; // copy level data into temporary buffer
2440 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2441 *level = li; // copy temporary buffer back to level data
2443 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2446 struct ElementInfo *ei = &element_info[element];
2448 if (element == EL_MM_GRAY_BALL)
2450 struct LevelInfo_MM *level_mm = level->native_mm_level;
2453 for (j = 0; j < level->num_mm_ball_contents; j++)
2454 level->mm_ball_content[j] =
2455 map_element_MM_to_RND(level_mm->ball_content[j]);
2458 // never initialize clipboard elements after the very first time
2459 // (to be able to use clipboard elements between several levels)
2460 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2463 if (IS_ENVELOPE(element))
2465 int envelope_nr = element - EL_ENVELOPE_1;
2467 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2469 level->envelope[envelope_nr] = xx_envelope;
2472 if (IS_CUSTOM_ELEMENT(element) ||
2473 IS_GROUP_ELEMENT(element) ||
2474 IS_INTERNAL_ELEMENT(element))
2476 xx_ei = *ei; // copy element data into temporary buffer
2478 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2483 setElementChangePages(ei, 1);
2484 setElementChangeInfoToDefaults(ei->change);
2486 if (IS_CUSTOM_ELEMENT(element) ||
2487 IS_GROUP_ELEMENT(element))
2489 setElementDescriptionToDefault(ei);
2491 ei->modified_settings = FALSE;
2494 if (IS_CUSTOM_ELEMENT(element) ||
2495 IS_INTERNAL_ELEMENT(element))
2497 // internal values used in level editor
2499 ei->access_type = 0;
2500 ei->access_layer = 0;
2501 ei->access_protected = 0;
2502 ei->walk_to_action = 0;
2503 ei->smash_targets = 0;
2506 ei->can_explode_by_fire = FALSE;
2507 ei->can_explode_smashed = FALSE;
2508 ei->can_explode_impact = FALSE;
2510 ei->current_change_page = 0;
2513 if (IS_GROUP_ELEMENT(element) ||
2514 IS_INTERNAL_ELEMENT(element))
2516 struct ElementGroupInfo *group;
2518 // initialize memory for list of elements in group
2519 if (ei->group == NULL)
2520 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2524 xx_group = *group; // copy group data into temporary buffer
2526 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2531 if (IS_EMPTY_ELEMENT(element) ||
2532 IS_INTERNAL_ELEMENT(element))
2534 xx_ei = *ei; // copy element data into temporary buffer
2536 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2542 clipboard_elements_initialized = TRUE;
2545 static void setLevelInfoToDefaults(struct LevelInfo *level,
2546 boolean level_info_only,
2547 boolean reset_file_status)
2549 setLevelInfoToDefaults_Level(level);
2551 if (!level_info_only)
2552 setLevelInfoToDefaults_Elements(level);
2554 if (reset_file_status)
2556 level->no_valid_file = FALSE;
2557 level->no_level_file = FALSE;
2560 level->changed = FALSE;
2563 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2565 level_file_info->nr = 0;
2566 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2567 level_file_info->packed = FALSE;
2569 setString(&level_file_info->basename, NULL);
2570 setString(&level_file_info->filename, NULL);
2573 int getMappedElement_SB(int, boolean);
2575 static void ActivateLevelTemplate(void)
2579 if (check_special_flags("load_xsb_to_ces"))
2581 // fill smaller playfields with padding "beyond border wall" elements
2582 if (level.fieldx < level_template.fieldx ||
2583 level.fieldy < level_template.fieldy)
2585 short field[level.fieldx][level.fieldy];
2586 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2587 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2588 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2589 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2591 // copy old playfield (which is smaller than the visible area)
2592 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2593 field[x][y] = level.field[x][y];
2595 // fill new, larger playfield with "beyond border wall" elements
2596 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2597 level.field[x][y] = getMappedElement_SB('_', TRUE);
2599 // copy the old playfield to the middle of the new playfield
2600 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2601 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2603 level.fieldx = new_fieldx;
2604 level.fieldy = new_fieldy;
2608 // Currently there is no special action needed to activate the template
2609 // data, because 'element_info' property settings overwrite the original
2610 // level data, while all other variables do not change.
2612 // Exception: 'from_level_template' elements in the original level playfield
2613 // are overwritten with the corresponding elements at the same position in
2614 // playfield from the level template.
2616 for (x = 0; x < level.fieldx; x++)
2617 for (y = 0; y < level.fieldy; y++)
2618 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2619 level.field[x][y] = level_template.field[x][y];
2621 if (check_special_flags("load_xsb_to_ces"))
2623 struct LevelInfo level_backup = level;
2625 // overwrite all individual level settings from template level settings
2626 level = level_template;
2628 // restore level file info
2629 level.file_info = level_backup.file_info;
2631 // restore playfield size
2632 level.fieldx = level_backup.fieldx;
2633 level.fieldy = level_backup.fieldy;
2635 // restore playfield content
2636 for (x = 0; x < level.fieldx; x++)
2637 for (y = 0; y < level.fieldy; y++)
2638 level.field[x][y] = level_backup.field[x][y];
2640 // restore name and author from individual level
2641 strcpy(level.name, level_backup.name);
2642 strcpy(level.author, level_backup.author);
2644 // restore flag "use_custom_template"
2645 level.use_custom_template = level_backup.use_custom_template;
2649 static boolean checkForPackageFromBasename_BD(char *basename)
2651 // check for native BD level file extensions
2652 if (!strSuffixLower(basename, ".bd") &&
2653 !strSuffixLower(basename, ".bdr") &&
2654 !strSuffixLower(basename, ".brc") &&
2655 !strSuffixLower(basename, ".gds"))
2658 // check for standard single-level BD files (like "001.bd")
2659 if (strSuffixLower(basename, ".bd") &&
2660 strlen(basename) == 6 &&
2661 basename[0] >= '0' && basename[0] <= '9' &&
2662 basename[1] >= '0' && basename[1] <= '9' &&
2663 basename[2] >= '0' && basename[2] <= '9')
2666 // this is a level package in native BD file format
2670 static char *getLevelFilenameFromBasename(char *basename)
2672 static char *filename = NULL;
2674 checked_free(filename);
2676 filename = getPath2(getCurrentLevelDir(), basename);
2681 static int getFileTypeFromBasename(char *basename)
2683 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2685 static char *filename = NULL;
2686 struct stat file_status;
2688 // ---------- try to determine file type from filename ----------
2690 // check for typical filename of a Supaplex level package file
2691 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2692 return LEVEL_FILE_TYPE_SP;
2694 // check for typical filename of a Diamond Caves II level package file
2695 if (strSuffixLower(basename, ".dc") ||
2696 strSuffixLower(basename, ".dc2"))
2697 return LEVEL_FILE_TYPE_DC;
2699 // check for typical filename of a Sokoban level package file
2700 if (strSuffixLower(basename, ".xsb") &&
2701 strchr(basename, '%') == NULL)
2702 return LEVEL_FILE_TYPE_SB;
2704 // check for typical filename of a Boulder Dash (GDash) level package file
2705 if (checkForPackageFromBasename_BD(basename))
2706 return LEVEL_FILE_TYPE_BD;
2708 // ---------- try to determine file type from filesize ----------
2710 checked_free(filename);
2711 filename = getPath2(getCurrentLevelDir(), basename);
2713 if (stat(filename, &file_status) == 0)
2715 // check for typical filesize of a Supaplex level package file
2716 if (file_status.st_size == 170496)
2717 return LEVEL_FILE_TYPE_SP;
2720 return LEVEL_FILE_TYPE_UNKNOWN;
2723 static int getFileTypeFromMagicBytes(char *filename, int type)
2727 if ((file = openFile(filename, MODE_READ)))
2729 char chunk_name[CHUNK_ID_LEN + 1];
2731 getFileChunkBE(file, chunk_name, NULL);
2733 if (strEqual(chunk_name, "MMII") ||
2734 strEqual(chunk_name, "MIRR"))
2735 type = LEVEL_FILE_TYPE_MM;
2743 static boolean checkForPackageFromBasename(char *basename)
2745 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2746 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2748 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2751 static char *getSingleLevelBasenameExt(int nr, char *extension)
2753 static char basename[MAX_FILENAME_LEN];
2756 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2758 sprintf(basename, "%03d.%s", nr, extension);
2763 static char *getSingleLevelBasename(int nr)
2765 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2768 static char *getPackedLevelBasename(int type)
2770 static char basename[MAX_FILENAME_LEN];
2771 char *directory = getCurrentLevelDir();
2773 DirectoryEntry *dir_entry;
2775 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2777 if ((dir = openDirectory(directory)) == NULL)
2779 Warn("cannot read current level directory '%s'", directory);
2784 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2786 char *entry_basename = dir_entry->basename;
2787 int entry_type = getFileTypeFromBasename(entry_basename);
2789 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2791 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2794 strcpy(basename, entry_basename);
2801 closeDirectory(dir);
2806 static char *getSingleLevelFilename(int nr)
2808 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2811 #if ENABLE_UNUSED_CODE
2812 static char *getPackedLevelFilename(int type)
2814 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2818 char *getDefaultLevelFilename(int nr)
2820 return getSingleLevelFilename(nr);
2823 #if ENABLE_UNUSED_CODE
2824 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2828 lfi->packed = FALSE;
2830 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2831 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2835 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2836 int type, char *format, ...)
2838 static char basename[MAX_FILENAME_LEN];
2841 va_start(ap, format);
2842 vsprintf(basename, format, ap);
2846 lfi->packed = FALSE;
2848 setString(&lfi->basename, basename);
2849 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2852 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2858 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2859 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2862 static int getFiletypeFromID(char *filetype_id)
2864 char *filetype_id_lower;
2865 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2868 if (filetype_id == NULL)
2869 return LEVEL_FILE_TYPE_UNKNOWN;
2871 filetype_id_lower = getStringToLower(filetype_id);
2873 for (i = 0; filetype_id_list[i].id != NULL; i++)
2875 char *id_lower = getStringToLower(filetype_id_list[i].id);
2877 if (strEqual(filetype_id_lower, id_lower))
2878 filetype = filetype_id_list[i].filetype;
2882 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2886 free(filetype_id_lower);
2891 char *getLocalLevelTemplateFilename(void)
2893 return getDefaultLevelFilename(-1);
2896 char *getGlobalLevelTemplateFilename(void)
2898 // global variable "leveldir_current" must be modified in the loop below
2899 LevelDirTree *leveldir_current_last = leveldir_current;
2900 char *filename = NULL;
2902 // check for template level in path from current to topmost tree node
2904 while (leveldir_current != NULL)
2906 filename = getDefaultLevelFilename(-1);
2908 if (fileExists(filename))
2911 leveldir_current = leveldir_current->node_parent;
2914 // restore global variable "leveldir_current" modified in above loop
2915 leveldir_current = leveldir_current_last;
2920 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2924 // special case: level number is negative => check for level template file
2927 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2928 getSingleLevelBasename(-1));
2930 // replace local level template filename with global template filename
2931 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2933 // no fallback if template file not existing
2937 // special case: check for file name/pattern specified in "levelinfo.conf"
2938 if (leveldir_current->level_filename != NULL)
2940 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2942 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2943 leveldir_current->level_filename, nr);
2945 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2947 if (fileExists(lfi->filename))
2950 else if (leveldir_current->level_filetype != NULL)
2952 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2954 // check for specified native level file with standard file name
2955 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2956 "%03d.%s", nr, LEVELFILE_EXTENSION);
2957 if (fileExists(lfi->filename))
2961 // check for native Rocks'n'Diamonds level file
2962 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2963 "%03d.%s", nr, LEVELFILE_EXTENSION);
2964 if (fileExists(lfi->filename))
2967 // check for native Boulder Dash level file
2968 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2969 if (fileExists(lfi->filename))
2972 // check for Emerald Mine level file (V1)
2973 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2974 'a' + (nr / 10) % 26, '0' + nr % 10);
2975 if (fileExists(lfi->filename))
2977 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2978 'A' + (nr / 10) % 26, '0' + nr % 10);
2979 if (fileExists(lfi->filename))
2982 // check for Emerald Mine level file (V2 to V5)
2983 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2984 if (fileExists(lfi->filename))
2987 // check for Emerald Mine level file (V6 / single mode)
2988 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2989 if (fileExists(lfi->filename))
2991 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2992 if (fileExists(lfi->filename))
2995 // check for Emerald Mine level file (V6 / teamwork mode)
2996 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2997 if (fileExists(lfi->filename))
2999 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3000 if (fileExists(lfi->filename))
3003 // check for various packed level file formats
3004 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3005 if (fileExists(lfi->filename))
3008 // no known level file found -- use default values (and fail later)
3009 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3010 "%03d.%s", nr, LEVELFILE_EXTENSION);
3013 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3015 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3016 lfi->type = getFileTypeFromBasename(lfi->basename);
3018 if (lfi->type == LEVEL_FILE_TYPE_RND)
3019 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3022 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3024 // always start with reliable default values
3025 setFileInfoToDefaults(level_file_info);
3027 level_file_info->nr = nr; // set requested level number
3029 determineLevelFileInfo_Filename(level_file_info);
3030 determineLevelFileInfo_Filetype(level_file_info);
3033 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3034 struct LevelFileInfo *lfi_to)
3036 lfi_to->nr = lfi_from->nr;
3037 lfi_to->type = lfi_from->type;
3038 lfi_to->packed = lfi_from->packed;
3040 setString(&lfi_to->basename, lfi_from->basename);
3041 setString(&lfi_to->filename, lfi_from->filename);
3044 // ----------------------------------------------------------------------------
3045 // functions for loading R'n'D level
3046 // ----------------------------------------------------------------------------
3048 int getMappedElement(int element)
3050 // remap some (historic, now obsolete) elements
3054 case EL_PLAYER_OBSOLETE:
3055 element = EL_PLAYER_1;
3058 case EL_KEY_OBSOLETE:
3062 case EL_EM_KEY_1_FILE_OBSOLETE:
3063 element = EL_EM_KEY_1;
3066 case EL_EM_KEY_2_FILE_OBSOLETE:
3067 element = EL_EM_KEY_2;
3070 case EL_EM_KEY_3_FILE_OBSOLETE:
3071 element = EL_EM_KEY_3;
3074 case EL_EM_KEY_4_FILE_OBSOLETE:
3075 element = EL_EM_KEY_4;
3078 case EL_ENVELOPE_OBSOLETE:
3079 element = EL_ENVELOPE_1;
3087 if (element >= NUM_FILE_ELEMENTS)
3089 Warn("invalid level element %d", element);
3091 element = EL_UNKNOWN;
3099 static int getMappedElementByVersion(int element, int game_version)
3101 // remap some elements due to certain game version
3103 if (game_version <= VERSION_IDENT(2,2,0,0))
3105 // map game font elements
3106 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3107 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3108 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3109 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3112 if (game_version < VERSION_IDENT(3,0,0,0))
3114 // map Supaplex gravity tube elements
3115 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3116 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3117 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3118 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3125 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3127 level->file_version = getFileVersion(file);
3128 level->game_version = getFileVersion(file);
3133 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3135 level->creation_date.year = getFile16BitBE(file);
3136 level->creation_date.month = getFile8Bit(file);
3137 level->creation_date.day = getFile8Bit(file);
3139 level->creation_date.src = DATE_SRC_LEVELFILE;
3144 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3146 int initial_player_stepsize;
3147 int initial_player_gravity;
3150 level->fieldx = getFile8Bit(file);
3151 level->fieldy = getFile8Bit(file);
3153 level->time = getFile16BitBE(file);
3154 level->gems_needed = getFile16BitBE(file);
3156 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3157 level->name[i] = getFile8Bit(file);
3158 level->name[MAX_LEVEL_NAME_LEN] = 0;
3160 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3161 level->score[i] = getFile8Bit(file);
3163 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3164 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3165 for (y = 0; y < 3; y++)
3166 for (x = 0; x < 3; x++)
3167 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3169 level->amoeba_speed = getFile8Bit(file);
3170 level->time_magic_wall = getFile8Bit(file);
3171 level->time_wheel = getFile8Bit(file);
3172 level->amoeba_content = getMappedElement(getFile8Bit(file));
3174 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3177 for (i = 0; i < MAX_PLAYERS; i++)
3178 level->initial_player_stepsize[i] = initial_player_stepsize;
3180 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3182 for (i = 0; i < MAX_PLAYERS; i++)
3183 level->initial_player_gravity[i] = initial_player_gravity;
3185 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3186 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3188 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3190 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3191 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3192 level->can_move_into_acid_bits = getFile32BitBE(file);
3193 level->dont_collide_with_bits = getFile8Bit(file);
3195 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3196 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3198 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3199 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3200 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3202 level->game_engine_type = getFile8Bit(file);
3204 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3209 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3213 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3214 level->name[i] = getFile8Bit(file);
3215 level->name[MAX_LEVEL_NAME_LEN] = 0;
3220 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3224 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3225 level->author[i] = getFile8Bit(file);
3226 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3231 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3234 int chunk_size_expected = level->fieldx * level->fieldy;
3236 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3237 stored with 16-bit encoding (and should be twice as big then).
3238 Even worse, playfield data was stored 16-bit when only yamyam content
3239 contained 16-bit elements and vice versa. */
3241 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3242 chunk_size_expected *= 2;
3244 if (chunk_size_expected != chunk_size)
3246 ReadUnusedBytesFromFile(file, chunk_size);
3247 return chunk_size_expected;
3250 for (y = 0; y < level->fieldy; y++)
3251 for (x = 0; x < level->fieldx; x++)
3252 level->field[x][y] =
3253 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3258 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3261 int header_size = 4;
3262 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3263 int chunk_size_expected = header_size + content_size;
3265 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3266 stored with 16-bit encoding (and should be twice as big then).
3267 Even worse, playfield data was stored 16-bit when only yamyam content
3268 contained 16-bit elements and vice versa. */
3270 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3271 chunk_size_expected += content_size;
3273 if (chunk_size_expected != chunk_size)
3275 ReadUnusedBytesFromFile(file, chunk_size);
3276 return chunk_size_expected;
3280 level->num_yamyam_contents = getFile8Bit(file);
3284 // correct invalid number of content fields -- should never happen
3285 if (level->num_yamyam_contents < 1 ||
3286 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3287 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3289 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3290 for (y = 0; y < 3; y++)
3291 for (x = 0; x < 3; x++)
3292 level->yamyam_content[i].e[x][y] =
3293 getMappedElement(level->encoding_16bit_field ?
3294 getFile16BitBE(file) : getFile8Bit(file));
3298 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3303 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3305 element = getMappedElement(getFile16BitBE(file));
3306 num_contents = getFile8Bit(file);
3308 getFile8Bit(file); // content x size (unused)
3309 getFile8Bit(file); // content y size (unused)
3311 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3313 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3314 for (y = 0; y < 3; y++)
3315 for (x = 0; x < 3; x++)
3316 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3318 // correct invalid number of content fields -- should never happen
3319 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3320 num_contents = STD_ELEMENT_CONTENTS;
3322 if (element == EL_YAMYAM)
3324 level->num_yamyam_contents = num_contents;
3326 for (i = 0; i < num_contents; i++)
3327 for (y = 0; y < 3; y++)
3328 for (x = 0; x < 3; x++)
3329 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3331 else if (element == EL_BD_AMOEBA)
3333 level->amoeba_content = content_array[0][0][0];
3337 Warn("cannot load content for element '%d'", element);
3343 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3349 int chunk_size_expected;
3351 element = getMappedElement(getFile16BitBE(file));
3352 if (!IS_ENVELOPE(element))
3353 element = EL_ENVELOPE_1;
3355 envelope_nr = element - EL_ENVELOPE_1;
3357 envelope_len = getFile16BitBE(file);
3359 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3360 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3362 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3364 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3365 if (chunk_size_expected != chunk_size)
3367 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3368 return chunk_size_expected;
3371 for (i = 0; i < envelope_len; i++)
3372 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3377 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3379 int num_changed_custom_elements = getFile16BitBE(file);
3380 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3383 if (chunk_size_expected != chunk_size)
3385 ReadUnusedBytesFromFile(file, chunk_size - 2);
3386 return chunk_size_expected;
3389 for (i = 0; i < num_changed_custom_elements; i++)
3391 int element = getMappedElement(getFile16BitBE(file));
3392 int properties = getFile32BitBE(file);
3394 if (IS_CUSTOM_ELEMENT(element))
3395 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3397 Warn("invalid custom element number %d", element);
3399 // older game versions that wrote level files with CUS1 chunks used
3400 // different default push delay values (not yet stored in level file)
3401 element_info[element].push_delay_fixed = 2;
3402 element_info[element].push_delay_random = 8;
3405 level->file_has_custom_elements = TRUE;
3410 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3412 int num_changed_custom_elements = getFile16BitBE(file);
3413 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3416 if (chunk_size_expected != chunk_size)
3418 ReadUnusedBytesFromFile(file, chunk_size - 2);
3419 return chunk_size_expected;
3422 for (i = 0; i < num_changed_custom_elements; i++)
3424 int element = getMappedElement(getFile16BitBE(file));
3425 int custom_target_element = getMappedElement(getFile16BitBE(file));
3427 if (IS_CUSTOM_ELEMENT(element))
3428 element_info[element].change->target_element = custom_target_element;
3430 Warn("invalid custom element number %d", element);
3433 level->file_has_custom_elements = TRUE;
3438 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3440 int num_changed_custom_elements = getFile16BitBE(file);
3441 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3444 if (chunk_size_expected != chunk_size)
3446 ReadUnusedBytesFromFile(file, chunk_size - 2);
3447 return chunk_size_expected;
3450 for (i = 0; i < num_changed_custom_elements; i++)
3452 int element = getMappedElement(getFile16BitBE(file));
3453 struct ElementInfo *ei = &element_info[element];
3454 unsigned int event_bits;
3456 if (!IS_CUSTOM_ELEMENT(element))
3458 Warn("invalid custom element number %d", element);
3460 element = EL_INTERNAL_DUMMY;
3463 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3464 ei->description[j] = getFile8Bit(file);
3465 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3467 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3469 // some free bytes for future properties and padding
3470 ReadUnusedBytesFromFile(file, 7);
3472 ei->use_gfx_element = getFile8Bit(file);
3473 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3475 ei->collect_score_initial = getFile8Bit(file);
3476 ei->collect_count_initial = getFile8Bit(file);
3478 ei->push_delay_fixed = getFile16BitBE(file);
3479 ei->push_delay_random = getFile16BitBE(file);
3480 ei->move_delay_fixed = getFile16BitBE(file);
3481 ei->move_delay_random = getFile16BitBE(file);
3483 ei->move_pattern = getFile16BitBE(file);
3484 ei->move_direction_initial = getFile8Bit(file);
3485 ei->move_stepsize = getFile8Bit(file);
3487 for (y = 0; y < 3; y++)
3488 for (x = 0; x < 3; x++)
3489 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3491 // bits 0 - 31 of "has_event[]"
3492 event_bits = getFile32BitBE(file);
3493 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3494 if (event_bits & (1u << j))
3495 ei->change->has_event[j] = TRUE;
3497 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3499 ei->change->delay_fixed = getFile16BitBE(file);
3500 ei->change->delay_random = getFile16BitBE(file);
3501 ei->change->delay_frames = getFile16BitBE(file);
3503 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3505 ei->change->explode = getFile8Bit(file);
3506 ei->change->use_target_content = getFile8Bit(file);
3507 ei->change->only_if_complete = getFile8Bit(file);
3508 ei->change->use_random_replace = getFile8Bit(file);
3510 ei->change->random_percentage = getFile8Bit(file);
3511 ei->change->replace_when = getFile8Bit(file);
3513 for (y = 0; y < 3; y++)
3514 for (x = 0; x < 3; x++)
3515 ei->change->target_content.e[x][y] =
3516 getMappedElement(getFile16BitBE(file));
3518 ei->slippery_type = getFile8Bit(file);
3520 // some free bytes for future properties and padding
3521 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3523 // mark that this custom element has been modified
3524 ei->modified_settings = TRUE;
3527 level->file_has_custom_elements = TRUE;
3532 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3534 struct ElementInfo *ei;
3535 int chunk_size_expected;
3539 // ---------- custom element base property values (96 bytes) ----------------
3541 element = getMappedElement(getFile16BitBE(file));
3543 if (!IS_CUSTOM_ELEMENT(element))
3545 Warn("invalid custom element number %d", element);
3547 ReadUnusedBytesFromFile(file, chunk_size - 2);
3552 ei = &element_info[element];
3554 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3555 ei->description[i] = getFile8Bit(file);
3556 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3558 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3560 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3562 ei->num_change_pages = getFile8Bit(file);
3564 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3565 if (chunk_size_expected != chunk_size)
3567 ReadUnusedBytesFromFile(file, chunk_size - 43);
3568 return chunk_size_expected;
3571 ei->ce_value_fixed_initial = getFile16BitBE(file);
3572 ei->ce_value_random_initial = getFile16BitBE(file);
3573 ei->use_last_ce_value = getFile8Bit(file);
3575 ei->use_gfx_element = getFile8Bit(file);
3576 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3578 ei->collect_score_initial = getFile8Bit(file);
3579 ei->collect_count_initial = getFile8Bit(file);
3581 ei->drop_delay_fixed = getFile8Bit(file);
3582 ei->push_delay_fixed = getFile8Bit(file);
3583 ei->drop_delay_random = getFile8Bit(file);
3584 ei->push_delay_random = getFile8Bit(file);
3585 ei->move_delay_fixed = getFile16BitBE(file);
3586 ei->move_delay_random = getFile16BitBE(file);
3588 // bits 0 - 15 of "move_pattern" ...
3589 ei->move_pattern = getFile16BitBE(file);
3590 ei->move_direction_initial = getFile8Bit(file);
3591 ei->move_stepsize = getFile8Bit(file);
3593 ei->slippery_type = getFile8Bit(file);
3595 for (y = 0; y < 3; y++)
3596 for (x = 0; x < 3; x++)
3597 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3599 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3600 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3601 ei->move_leave_type = getFile8Bit(file);
3603 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3604 ei->move_pattern |= (getFile16BitBE(file) << 16);
3606 ei->access_direction = getFile8Bit(file);
3608 ei->explosion_delay = getFile8Bit(file);
3609 ei->ignition_delay = getFile8Bit(file);
3610 ei->explosion_type = getFile8Bit(file);
3612 // some free bytes for future custom property values and padding
3613 ReadUnusedBytesFromFile(file, 1);
3615 // ---------- change page property values (48 bytes) ------------------------
3617 setElementChangePages(ei, ei->num_change_pages);
3619 for (i = 0; i < ei->num_change_pages; i++)
3621 struct ElementChangeInfo *change = &ei->change_page[i];
3622 unsigned int event_bits;
3624 // always start with reliable default values
3625 setElementChangeInfoToDefaults(change);
3627 // bits 0 - 31 of "has_event[]" ...
3628 event_bits = getFile32BitBE(file);
3629 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3630 if (event_bits & (1u << j))
3631 change->has_event[j] = TRUE;
3633 change->target_element = getMappedElement(getFile16BitBE(file));
3635 change->delay_fixed = getFile16BitBE(file);
3636 change->delay_random = getFile16BitBE(file);
3637 change->delay_frames = getFile16BitBE(file);
3639 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3641 change->explode = getFile8Bit(file);
3642 change->use_target_content = getFile8Bit(file);
3643 change->only_if_complete = getFile8Bit(file);
3644 change->use_random_replace = getFile8Bit(file);
3646 change->random_percentage = getFile8Bit(file);
3647 change->replace_when = getFile8Bit(file);
3649 for (y = 0; y < 3; y++)
3650 for (x = 0; x < 3; x++)
3651 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3653 change->can_change = getFile8Bit(file);
3655 change->trigger_side = getFile8Bit(file);
3657 change->trigger_player = getFile8Bit(file);
3658 change->trigger_page = getFile8Bit(file);
3660 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3661 CH_PAGE_ANY : (1 << change->trigger_page));
3663 change->has_action = getFile8Bit(file);
3664 change->action_type = getFile8Bit(file);
3665 change->action_mode = getFile8Bit(file);
3666 change->action_arg = getFile16BitBE(file);
3668 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3669 event_bits = getFile8Bit(file);
3670 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3671 if (event_bits & (1u << (j - 32)))
3672 change->has_event[j] = TRUE;
3675 // mark this custom element as modified
3676 ei->modified_settings = TRUE;
3678 level->file_has_custom_elements = TRUE;
3683 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3685 struct ElementInfo *ei;
3686 struct ElementGroupInfo *group;
3690 element = getMappedElement(getFile16BitBE(file));
3692 if (!IS_GROUP_ELEMENT(element))
3694 Warn("invalid group element number %d", element);
3696 ReadUnusedBytesFromFile(file, chunk_size - 2);
3701 ei = &element_info[element];
3703 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3704 ei->description[i] = getFile8Bit(file);
3705 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3707 group = element_info[element].group;
3709 group->num_elements = getFile8Bit(file);
3711 ei->use_gfx_element = getFile8Bit(file);
3712 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3714 group->choice_mode = getFile8Bit(file);
3716 // some free bytes for future values and padding
3717 ReadUnusedBytesFromFile(file, 3);
3719 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3720 group->element[i] = getMappedElement(getFile16BitBE(file));
3722 // mark this group element as modified
3723 element_info[element].modified_settings = TRUE;
3725 level->file_has_custom_elements = TRUE;
3730 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3731 int element, int real_element)
3733 int micro_chunk_size = 0;
3734 int conf_type = getFile8Bit(file);
3735 int byte_mask = conf_type & CONF_MASK_BYTES;
3736 boolean element_found = FALSE;
3739 micro_chunk_size += 1;
3741 if (byte_mask == CONF_MASK_MULTI_BYTES)
3743 int num_bytes = getFile16BitBE(file);
3744 byte *buffer = checked_malloc(num_bytes);
3746 ReadBytesFromFile(file, buffer, num_bytes);
3748 for (i = 0; conf[i].data_type != -1; i++)
3750 if (conf[i].element == element &&
3751 conf[i].conf_type == conf_type)
3753 int data_type = conf[i].data_type;
3754 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3755 int max_num_entities = conf[i].max_num_entities;
3757 if (num_entities > max_num_entities)
3759 Warn("truncating number of entities for element %d from %d to %d",
3760 element, num_entities, max_num_entities);
3762 num_entities = max_num_entities;
3765 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3766 data_type == TYPE_CONTENT_LIST))
3768 // for element and content lists, zero entities are not allowed
3769 Warn("found empty list of entities for element %d", element);
3771 // do not set "num_entities" here to prevent reading behind buffer
3773 *(int *)(conf[i].num_entities) = 1; // at least one is required
3777 *(int *)(conf[i].num_entities) = num_entities;
3780 element_found = TRUE;
3782 if (data_type == TYPE_STRING)
3784 char *string = (char *)(conf[i].value);
3787 for (j = 0; j < max_num_entities; j++)
3788 string[j] = (j < num_entities ? buffer[j] : '\0');
3790 else if (data_type == TYPE_ELEMENT_LIST)
3792 int *element_array = (int *)(conf[i].value);
3795 for (j = 0; j < num_entities; j++)
3797 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3799 else if (data_type == TYPE_CONTENT_LIST)
3801 struct Content *content= (struct Content *)(conf[i].value);
3804 for (c = 0; c < num_entities; c++)
3805 for (y = 0; y < 3; y++)
3806 for (x = 0; x < 3; x++)
3807 content[c].e[x][y] =
3808 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3811 element_found = FALSE;
3817 checked_free(buffer);
3819 micro_chunk_size += 2 + num_bytes;
3821 else // constant size configuration data (1, 2 or 4 bytes)
3823 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3824 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3825 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3827 for (i = 0; conf[i].data_type != -1; i++)
3829 if (conf[i].element == element &&
3830 conf[i].conf_type == conf_type)
3832 int data_type = conf[i].data_type;
3834 if (data_type == TYPE_ELEMENT)
3835 value = getMappedElement(value);
3837 if (data_type == TYPE_BOOLEAN)
3838 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3840 *(int *) (conf[i].value) = value;
3842 element_found = TRUE;
3848 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3853 char *error_conf_chunk_bytes =
3854 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3855 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3856 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3857 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3858 int error_element = real_element;
3860 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3861 error_conf_chunk_bytes, error_conf_chunk_token,
3862 error_element, EL_NAME(error_element));
3865 return micro_chunk_size;
3868 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3870 int real_chunk_size = 0;
3872 li = *level; // copy level data into temporary buffer
3874 while (!checkEndOfFile(file))
3876 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3878 if (real_chunk_size >= chunk_size)
3882 *level = li; // copy temporary buffer back to level data
3884 return real_chunk_size;
3887 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3889 int real_chunk_size = 0;
3891 li = *level; // copy level data into temporary buffer
3893 while (!checkEndOfFile(file))
3895 int element = getMappedElement(getFile16BitBE(file));
3897 real_chunk_size += 2;
3898 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3900 if (real_chunk_size >= chunk_size)
3904 *level = li; // copy temporary buffer back to level data
3906 return real_chunk_size;
3909 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3911 int real_chunk_size = 0;
3913 li = *level; // copy level data into temporary buffer
3915 while (!checkEndOfFile(file))
3917 int element = getMappedElement(getFile16BitBE(file));
3919 real_chunk_size += 2;
3920 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3922 if (real_chunk_size >= chunk_size)
3926 *level = li; // copy temporary buffer back to level data
3928 return real_chunk_size;
3931 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3933 int element = getMappedElement(getFile16BitBE(file));
3934 int envelope_nr = element - EL_ENVELOPE_1;
3935 int real_chunk_size = 2;
3937 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3939 while (!checkEndOfFile(file))
3941 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3944 if (real_chunk_size >= chunk_size)
3948 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3950 return real_chunk_size;
3953 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3955 int element = getMappedElement(getFile16BitBE(file));
3956 int real_chunk_size = 2;
3957 struct ElementInfo *ei = &element_info[element];
3960 xx_ei = *ei; // copy element data into temporary buffer
3962 xx_ei.num_change_pages = -1;
3964 while (!checkEndOfFile(file))
3966 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3968 if (xx_ei.num_change_pages != -1)
3971 if (real_chunk_size >= chunk_size)
3977 if (ei->num_change_pages == -1)
3979 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3982 ei->num_change_pages = 1;
3984 setElementChangePages(ei, 1);
3985 setElementChangeInfoToDefaults(ei->change);
3987 return real_chunk_size;
3990 // initialize number of change pages stored for this custom element
3991 setElementChangePages(ei, ei->num_change_pages);
3992 for (i = 0; i < ei->num_change_pages; i++)
3993 setElementChangeInfoToDefaults(&ei->change_page[i]);
3995 // start with reading properties for the first change page
3996 xx_current_change_page = 0;
3998 while (!checkEndOfFile(file))
4000 // level file might contain invalid change page number
4001 if (xx_current_change_page >= ei->num_change_pages)
4004 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4006 xx_change = *change; // copy change data into temporary buffer
4008 resetEventBits(); // reset bits; change page might have changed
4010 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4013 *change = xx_change;
4015 setEventFlagsFromEventBits(change);
4017 if (real_chunk_size >= chunk_size)
4021 level->file_has_custom_elements = TRUE;
4023 return real_chunk_size;
4026 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4028 int element = getMappedElement(getFile16BitBE(file));
4029 int real_chunk_size = 2;
4030 struct ElementInfo *ei = &element_info[element];
4031 struct ElementGroupInfo *group = ei->group;
4036 xx_ei = *ei; // copy element data into temporary buffer
4037 xx_group = *group; // copy group data into temporary buffer
4039 while (!checkEndOfFile(file))
4041 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4044 if (real_chunk_size >= chunk_size)
4051 level->file_has_custom_elements = TRUE;
4053 return real_chunk_size;
4056 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4058 int element = getMappedElement(getFile16BitBE(file));
4059 int real_chunk_size = 2;
4060 struct ElementInfo *ei = &element_info[element];
4062 xx_ei = *ei; // copy element data into temporary buffer
4064 while (!checkEndOfFile(file))
4066 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4069 if (real_chunk_size >= chunk_size)
4075 level->file_has_custom_elements = TRUE;
4077 return real_chunk_size;
4080 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4081 struct LevelFileInfo *level_file_info,
4082 boolean level_info_only)
4084 char *filename = level_file_info->filename;
4085 char cookie[MAX_LINE_LEN];
4086 char chunk_name[CHUNK_ID_LEN + 1];
4090 if (!(file = openFile(filename, MODE_READ)))
4092 level->no_valid_file = TRUE;
4093 level->no_level_file = TRUE;
4095 if (level_info_only)
4098 Warn("cannot read level '%s' -- using empty level", filename);
4100 if (!setup.editor.use_template_for_new_levels)
4103 // if level file not found, try to initialize level data from template
4104 filename = getGlobalLevelTemplateFilename();
4106 if (!(file = openFile(filename, MODE_READ)))
4109 // default: for empty levels, use level template for custom elements
4110 level->use_custom_template = TRUE;
4112 level->no_valid_file = FALSE;
4115 getFileChunkBE(file, chunk_name, NULL);
4116 if (strEqual(chunk_name, "RND1"))
4118 getFile32BitBE(file); // not used
4120 getFileChunkBE(file, chunk_name, NULL);
4121 if (!strEqual(chunk_name, "CAVE"))
4123 level->no_valid_file = TRUE;
4125 Warn("unknown format of level file '%s'", filename);
4132 else // check for pre-2.0 file format with cookie string
4134 strcpy(cookie, chunk_name);
4135 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4137 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4138 cookie[strlen(cookie) - 1] = '\0';
4140 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4142 level->no_valid_file = TRUE;
4144 Warn("unknown format of level file '%s'", filename);
4151 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4153 level->no_valid_file = TRUE;
4155 Warn("unsupported version of level file '%s'", filename);
4162 // pre-2.0 level files have no game version, so use file version here
4163 level->game_version = level->file_version;
4166 if (level->file_version < FILE_VERSION_1_2)
4168 // level files from versions before 1.2.0 without chunk structure
4169 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4170 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4178 int (*loader)(File *, int, struct LevelInfo *);
4182 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4183 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4184 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4185 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4186 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4187 { "INFO", -1, LoadLevel_INFO },
4188 { "BODY", -1, LoadLevel_BODY },
4189 { "CONT", -1, LoadLevel_CONT },
4190 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4191 { "CNT3", -1, LoadLevel_CNT3 },
4192 { "CUS1", -1, LoadLevel_CUS1 },
4193 { "CUS2", -1, LoadLevel_CUS2 },
4194 { "CUS3", -1, LoadLevel_CUS3 },
4195 { "CUS4", -1, LoadLevel_CUS4 },
4196 { "GRP1", -1, LoadLevel_GRP1 },
4197 { "CONF", -1, LoadLevel_CONF },
4198 { "ELEM", -1, LoadLevel_ELEM },
4199 { "NOTE", -1, LoadLevel_NOTE },
4200 { "CUSX", -1, LoadLevel_CUSX },
4201 { "GRPX", -1, LoadLevel_GRPX },
4202 { "EMPX", -1, LoadLevel_EMPX },
4207 while (getFileChunkBE(file, chunk_name, &chunk_size))
4211 while (chunk_info[i].name != NULL &&
4212 !strEqual(chunk_name, chunk_info[i].name))
4215 if (chunk_info[i].name == NULL)
4217 Warn("unknown chunk '%s' in level file '%s'",
4218 chunk_name, filename);
4220 ReadUnusedBytesFromFile(file, chunk_size);
4222 else if (chunk_info[i].size != -1 &&
4223 chunk_info[i].size != chunk_size)
4225 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4226 chunk_size, chunk_name, filename);
4228 ReadUnusedBytesFromFile(file, chunk_size);
4232 // call function to load this level chunk
4233 int chunk_size_expected =
4234 (chunk_info[i].loader)(file, chunk_size, level);
4236 if (chunk_size_expected < 0)
4238 Warn("error reading chunk '%s' in level file '%s'",
4239 chunk_name, filename);
4244 // the size of some chunks cannot be checked before reading other
4245 // chunks first (like "HEAD" and "BODY") that contain some header
4246 // information, so check them here
4247 if (chunk_size_expected != chunk_size)
4249 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4250 chunk_size, chunk_name, filename);
4262 // ----------------------------------------------------------------------------
4263 // functions for loading BD level
4264 // ----------------------------------------------------------------------------
4266 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4267 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4269 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4271 struct LevelInfo_BD *level_bd = level->native_bd_level;
4272 GdCave *cave = NULL; // will be changed below
4273 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4274 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4277 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4279 // cave and map newly allocated when set to defaults above
4280 cave = level_bd->cave;
4283 cave->intermission = level->bd_intermission;
4286 cave->level_time[0] = level->time;
4287 cave->level_diamonds[0] = level->gems_needed;
4290 cave->scheduling = level->bd_scheduling_type;
4291 cave->pal_timing = level->bd_pal_timing;
4292 cave->level_speed[0] = level->bd_cycle_delay_ms;
4293 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4294 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4295 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4298 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4299 cave->diamond_value = level->score[SC_EMERALD];
4300 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4302 // compatibility settings
4303 cave->lineshift = level->bd_line_shifting_borders;
4304 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4305 cave->short_explosions = level->bd_short_explosions;
4307 // player properties
4308 cave->diagonal_movements = level->bd_diagonal_movements;
4309 cave->active_is_first_found = level->bd_topmost_player_active;
4310 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4311 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4312 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4313 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4315 // element properties
4316 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4317 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4318 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4319 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4320 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4321 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4322 cave->level_magic_wall_time[0] = level->time_magic_wall;
4323 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4324 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4325 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4326 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4328 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4329 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4330 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4331 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4332 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4333 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4334 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4336 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4337 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4338 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4339 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4340 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4341 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4342 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4343 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4344 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4345 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4346 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4348 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4349 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4350 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4351 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4352 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4353 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4355 cave->slime_predictable = level->bd_slime_is_predictable;
4356 cave->slime_correct_random = level->bd_slime_correct_random;
4357 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4358 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4359 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4360 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4361 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4362 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4363 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4364 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4365 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4366 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4368 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4369 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4370 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4372 cave->biter_delay_frame = level->bd_biter_move_delay;
4373 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4375 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4377 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4379 cave->replicators_active = level->bd_replicators_active;
4380 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4382 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4383 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4385 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4387 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4389 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4390 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4391 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4393 cave->infinite_rockets = level->bd_infinite_rockets;
4395 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4396 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4398 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4399 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4401 cave->creatures_backwards = level->bd_creatures_start_backwards;
4402 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4403 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4405 cave->gravity = level->bd_gravity_direction;
4406 cave->gravity_switch_active = level->bd_gravity_switch_active;
4407 cave->gravity_change_time = level->bd_gravity_switch_delay;
4408 cave->gravity_affects_all = level->bd_gravity_affects_all;
4410 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4411 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4412 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4413 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4415 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4416 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4417 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4418 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4419 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4420 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4422 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4423 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4424 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4425 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4427 cave->colorb = level->bd_color_b;
4428 cave->color0 = level->bd_color_0;
4429 cave->color1 = level->bd_color_1;
4430 cave->color2 = level->bd_color_2;
4431 cave->color3 = level->bd_color_3;
4432 cave->color4 = level->bd_color_4;
4433 cave->color5 = level->bd_color_5;
4436 strncpy(cave->name, level->name, sizeof(GdString));
4437 cave->name[sizeof(GdString) - 1] = '\0';
4439 // playfield elements
4440 for (x = 0; x < cave->w; x++)
4441 for (y = 0; y < cave->h; y++)
4442 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4445 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4447 struct LevelInfo_BD *level_bd = level->native_bd_level;
4448 GdCave *cave = level_bd->cave;
4449 int bd_level_nr = level_bd->level_nr;
4452 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4453 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4456 level->bd_intermission = cave->intermission;
4459 level->time = cave->level_time[bd_level_nr];
4460 level->gems_needed = cave->level_diamonds[bd_level_nr];
4463 level->bd_scheduling_type = cave->scheduling;
4464 level->bd_pal_timing = cave->pal_timing;
4465 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4466 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4467 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4468 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4471 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4472 level->score[SC_EMERALD] = cave->diamond_value;
4473 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4475 // compatibility settings
4476 level->bd_line_shifting_borders = cave->lineshift;
4477 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4478 level->bd_short_explosions = cave->short_explosions;
4480 // player properties
4481 level->bd_diagonal_movements = cave->diagonal_movements;
4482 level->bd_topmost_player_active = cave->active_is_first_found;
4483 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4484 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4485 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4486 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4488 // element properties
4489 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4490 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4491 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4492 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4493 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4494 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4495 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4496 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4497 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4498 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4499 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4501 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4502 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4503 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4504 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4505 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4506 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4507 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4509 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4510 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4511 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4512 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4513 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4514 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4515 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4516 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4517 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4518 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4519 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4521 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4522 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4523 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4524 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4525 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4526 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4528 level->bd_slime_is_predictable = cave->slime_predictable;
4529 level->bd_slime_correct_random = cave->slime_correct_random;
4530 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4531 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4532 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4533 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4534 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4535 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4536 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4537 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4538 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4539 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4541 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4542 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4543 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4545 level->bd_biter_move_delay = cave->biter_delay_frame;
4546 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4548 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4550 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4552 level->bd_replicators_active = cave->replicators_active;
4553 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4555 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4556 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4558 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4560 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4562 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4563 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4564 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4566 level->bd_infinite_rockets = cave->infinite_rockets;
4568 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4569 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4571 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4572 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4574 level->bd_creatures_start_backwards = cave->creatures_backwards;
4575 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4576 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4578 level->bd_gravity_direction = cave->gravity;
4579 level->bd_gravity_switch_active = cave->gravity_switch_active;
4580 level->bd_gravity_switch_delay = cave->gravity_change_time;
4581 level->bd_gravity_affects_all = cave->gravity_affects_all;
4583 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4584 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4585 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4586 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4588 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4589 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4590 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4591 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4592 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4593 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4595 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4596 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4597 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4598 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4600 level->bd_color_b = cave->colorb;
4601 level->bd_color_0 = cave->color0;
4602 level->bd_color_1 = cave->color1;
4603 level->bd_color_2 = cave->color2;
4604 level->bd_color_3 = cave->color3;
4605 level->bd_color_4 = cave->color4;
4606 level->bd_color_5 = cave->color5;
4608 // set default color type and colors for BD style level colors
4609 SetDefaultLevelColorType_BD();
4610 SetDefaultLevelColors_BD();
4613 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4615 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4616 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4618 // playfield elements
4619 for (x = 0; x < level->fieldx; x++)
4620 for (y = 0; y < level->fieldy; y++)
4621 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4623 checked_free(cave_name);
4626 static void setTapeInfoToDefaults(void);
4628 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4630 struct LevelInfo_BD *level_bd = level->native_bd_level;
4631 GdCave *cave = level_bd->cave;
4632 GdReplay *replay = level_bd->replay;
4638 // always start with reliable default values
4639 setTapeInfoToDefaults();
4641 tape.level_nr = level_nr; // (currently not used)
4642 tape.random_seed = replay->seed;
4644 TapeSetDateFromIsoDateString(replay->date);
4647 tape.pos[tape.counter].delay = 0;
4649 tape.bd_replay = TRUE;
4651 // all time calculations only used to display approximate tape time
4652 int cave_speed = cave->speed;
4653 int milliseconds_game = 0;
4654 int milliseconds_elapsed = 20;
4656 for (i = 0; i < replay->movements->len; i++)
4658 int replay_action = replay->movements->data[i];
4659 int tape_action = map_action_BD_to_RND(replay_action);
4660 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4661 boolean success = 0;
4665 success = TapeAddAction(action);
4667 milliseconds_game += milliseconds_elapsed;
4669 if (milliseconds_game >= cave_speed)
4671 milliseconds_game -= cave_speed;
4678 tape.pos[tape.counter].delay = 0;
4679 tape.pos[tape.counter].action[0] = 0;
4683 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4689 TapeHaltRecording();
4691 if (!replay->success)
4692 Warn("BD replay is marked as not successful");
4696 // ----------------------------------------------------------------------------
4697 // functions for loading EM level
4698 // ----------------------------------------------------------------------------
4700 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4702 static int ball_xy[8][2] =
4713 struct LevelInfo_EM *level_em = level->native_em_level;
4714 struct CAVE *cav = level_em->cav;
4717 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4718 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4720 cav->time_seconds = level->time;
4721 cav->gems_needed = level->gems_needed;
4723 cav->emerald_score = level->score[SC_EMERALD];
4724 cav->diamond_score = level->score[SC_DIAMOND];
4725 cav->alien_score = level->score[SC_ROBOT];
4726 cav->tank_score = level->score[SC_SPACESHIP];
4727 cav->bug_score = level->score[SC_BUG];
4728 cav->eater_score = level->score[SC_YAMYAM];
4729 cav->nut_score = level->score[SC_NUT];
4730 cav->dynamite_score = level->score[SC_DYNAMITE];
4731 cav->key_score = level->score[SC_KEY];
4732 cav->exit_score = level->score[SC_TIME_BONUS];
4734 cav->num_eater_arrays = level->num_yamyam_contents;
4736 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4737 for (y = 0; y < 3; y++)
4738 for (x = 0; x < 3; x++)
4739 cav->eater_array[i][y * 3 + x] =
4740 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4742 cav->amoeba_time = level->amoeba_speed;
4743 cav->wonderwall_time = level->time_magic_wall;
4744 cav->wheel_time = level->time_wheel;
4746 cav->android_move_time = level->android_move_time;
4747 cav->android_clone_time = level->android_clone_time;
4748 cav->ball_random = level->ball_random;
4749 cav->ball_active = level->ball_active_initial;
4750 cav->ball_time = level->ball_time;
4751 cav->num_ball_arrays = level->num_ball_contents;
4753 cav->lenses_score = level->lenses_score;
4754 cav->magnify_score = level->magnify_score;
4755 cav->slurp_score = level->slurp_score;
4757 cav->lenses_time = level->lenses_time;
4758 cav->magnify_time = level->magnify_time;
4760 cav->wind_time = 9999;
4761 cav->wind_direction =
4762 map_direction_RND_to_EM(level->wind_direction_initial);
4764 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4765 for (j = 0; j < 8; j++)
4766 cav->ball_array[i][j] =
4767 map_element_RND_to_EM_cave(level->ball_content[i].
4768 e[ball_xy[j][0]][ball_xy[j][1]]);
4770 map_android_clone_elements_RND_to_EM(level);
4772 // first fill the complete playfield with the empty space element
4773 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4774 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4775 cav->cave[x][y] = Cblank;
4777 // then copy the real level contents from level file into the playfield
4778 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4780 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4782 if (level->field[x][y] == EL_AMOEBA_DEAD)
4783 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4785 cav->cave[x][y] = new_element;
4788 for (i = 0; i < MAX_PLAYERS; i++)
4790 cav->player_x[i] = -1;
4791 cav->player_y[i] = -1;
4794 // initialize player positions and delete players from the playfield
4795 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4797 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4799 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4801 cav->player_x[player_nr] = x;
4802 cav->player_y[player_nr] = y;
4804 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4809 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4811 static int ball_xy[8][2] =
4822 struct LevelInfo_EM *level_em = level->native_em_level;
4823 struct CAVE *cav = level_em->cav;
4826 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4827 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4829 level->time = cav->time_seconds;
4830 level->gems_needed = cav->gems_needed;
4832 sprintf(level->name, "Level %d", level->file_info.nr);
4834 level->score[SC_EMERALD] = cav->emerald_score;
4835 level->score[SC_DIAMOND] = cav->diamond_score;
4836 level->score[SC_ROBOT] = cav->alien_score;
4837 level->score[SC_SPACESHIP] = cav->tank_score;
4838 level->score[SC_BUG] = cav->bug_score;
4839 level->score[SC_YAMYAM] = cav->eater_score;
4840 level->score[SC_NUT] = cav->nut_score;
4841 level->score[SC_DYNAMITE] = cav->dynamite_score;
4842 level->score[SC_KEY] = cav->key_score;
4843 level->score[SC_TIME_BONUS] = cav->exit_score;
4845 level->num_yamyam_contents = cav->num_eater_arrays;
4847 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4848 for (y = 0; y < 3; y++)
4849 for (x = 0; x < 3; x++)
4850 level->yamyam_content[i].e[x][y] =
4851 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4853 level->amoeba_speed = cav->amoeba_time;
4854 level->time_magic_wall = cav->wonderwall_time;
4855 level->time_wheel = cav->wheel_time;
4857 level->android_move_time = cav->android_move_time;
4858 level->android_clone_time = cav->android_clone_time;
4859 level->ball_random = cav->ball_random;
4860 level->ball_active_initial = cav->ball_active;
4861 level->ball_time = cav->ball_time;
4862 level->num_ball_contents = cav->num_ball_arrays;
4864 level->lenses_score = cav->lenses_score;
4865 level->magnify_score = cav->magnify_score;
4866 level->slurp_score = cav->slurp_score;
4868 level->lenses_time = cav->lenses_time;
4869 level->magnify_time = cav->magnify_time;
4871 level->wind_direction_initial =
4872 map_direction_EM_to_RND(cav->wind_direction);
4874 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4875 for (j = 0; j < 8; j++)
4876 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4877 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4879 map_android_clone_elements_EM_to_RND(level);
4881 // convert the playfield (some elements need special treatment)
4882 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4884 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4886 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4887 new_element = EL_AMOEBA_DEAD;
4889 level->field[x][y] = new_element;
4892 for (i = 0; i < MAX_PLAYERS; i++)
4894 // in case of all players set to the same field, use the first player
4895 int nr = MAX_PLAYERS - i - 1;
4896 int jx = cav->player_x[nr];
4897 int jy = cav->player_y[nr];
4899 if (jx != -1 && jy != -1)
4900 level->field[jx][jy] = EL_PLAYER_1 + nr;
4903 // time score is counted for each 10 seconds left in Emerald Mine levels
4904 level->time_score_base = 10;
4908 // ----------------------------------------------------------------------------
4909 // functions for loading SP level
4910 // ----------------------------------------------------------------------------
4912 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4914 struct LevelInfo_SP *level_sp = level->native_sp_level;
4915 LevelInfoType *header = &level_sp->header;
4918 level_sp->width = level->fieldx;
4919 level_sp->height = level->fieldy;
4921 for (x = 0; x < level->fieldx; x++)
4922 for (y = 0; y < level->fieldy; y++)
4923 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4925 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4927 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4928 header->LevelTitle[i] = level->name[i];
4929 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4931 header->InfotronsNeeded = level->gems_needed;
4933 header->SpecialPortCount = 0;
4935 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4937 boolean gravity_port_found = FALSE;
4938 boolean gravity_port_valid = FALSE;
4939 int gravity_port_flag;
4940 int gravity_port_base_element;
4941 int element = level->field[x][y];
4943 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4944 element <= EL_SP_GRAVITY_ON_PORT_UP)
4946 gravity_port_found = TRUE;
4947 gravity_port_valid = TRUE;
4948 gravity_port_flag = 1;
4949 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4951 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4952 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4954 gravity_port_found = TRUE;
4955 gravity_port_valid = TRUE;
4956 gravity_port_flag = 0;
4957 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4959 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4960 element <= EL_SP_GRAVITY_PORT_UP)
4962 // change R'n'D style gravity inverting special port to normal port
4963 // (there are no gravity inverting ports in native Supaplex engine)
4965 gravity_port_found = TRUE;
4966 gravity_port_valid = FALSE;
4967 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4970 if (gravity_port_found)
4972 if (gravity_port_valid &&
4973 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4975 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4977 port->PortLocation = (y * level->fieldx + x) * 2;
4978 port->Gravity = gravity_port_flag;
4980 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4982 header->SpecialPortCount++;
4986 // change special gravity port to normal port
4988 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4991 level_sp->playfield[x][y] = element - EL_SP_START;
4996 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4998 struct LevelInfo_SP *level_sp = level->native_sp_level;
4999 LevelInfoType *header = &level_sp->header;
5000 boolean num_invalid_elements = 0;
5003 level->fieldx = level_sp->width;
5004 level->fieldy = level_sp->height;
5006 for (x = 0; x < level->fieldx; x++)
5008 for (y = 0; y < level->fieldy; y++)
5010 int element_old = level_sp->playfield[x][y];
5011 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5013 if (element_new == EL_UNKNOWN)
5015 num_invalid_elements++;
5017 Debug("level:native:SP", "invalid element %d at position %d, %d",
5021 level->field[x][y] = element_new;
5025 if (num_invalid_elements > 0)
5026 Warn("found %d invalid elements%s", num_invalid_elements,
5027 (!options.debug ? " (use '--debug' for more details)" : ""));
5029 for (i = 0; i < MAX_PLAYERS; i++)
5030 level->initial_player_gravity[i] =
5031 (header->InitialGravity == 1 ? TRUE : FALSE);
5033 // skip leading spaces
5034 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5035 if (header->LevelTitle[i] != ' ')
5039 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5040 level->name[j] = header->LevelTitle[i];
5041 level->name[j] = '\0';
5043 // cut trailing spaces
5045 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5046 level->name[j - 1] = '\0';
5048 level->gems_needed = header->InfotronsNeeded;
5050 for (i = 0; i < header->SpecialPortCount; i++)
5052 SpecialPortType *port = &header->SpecialPort[i];
5053 int port_location = port->PortLocation;
5054 int gravity = port->Gravity;
5055 int port_x, port_y, port_element;
5057 port_x = (port_location / 2) % level->fieldx;
5058 port_y = (port_location / 2) / level->fieldx;
5060 if (port_x < 0 || port_x >= level->fieldx ||
5061 port_y < 0 || port_y >= level->fieldy)
5063 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5068 port_element = level->field[port_x][port_y];
5070 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5071 port_element > EL_SP_GRAVITY_PORT_UP)
5073 Warn("no special port at position (%d, %d)", port_x, port_y);
5078 // change previous (wrong) gravity inverting special port to either
5079 // gravity enabling special port or gravity disabling special port
5080 level->field[port_x][port_y] +=
5081 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5082 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5085 // change special gravity ports without database entries to normal ports
5086 for (x = 0; x < level->fieldx; x++)
5087 for (y = 0; y < level->fieldy; y++)
5088 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5089 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5090 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5092 level->time = 0; // no time limit
5093 level->amoeba_speed = 0;
5094 level->time_magic_wall = 0;
5095 level->time_wheel = 0;
5096 level->amoeba_content = EL_EMPTY;
5098 // original Supaplex does not use score values -- rate by playing time
5099 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5100 level->score[i] = 0;
5102 level->rate_time_over_score = TRUE;
5104 // there are no yamyams in supaplex levels
5105 for (i = 0; i < level->num_yamyam_contents; i++)
5106 for (x = 0; x < 3; x++)
5107 for (y = 0; y < 3; y++)
5108 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5111 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5113 struct LevelInfo_SP *level_sp = level->native_sp_level;
5114 struct DemoInfo_SP *demo = &level_sp->demo;
5117 // always start with reliable default values
5118 demo->is_available = FALSE;
5121 if (TAPE_IS_EMPTY(tape))
5124 demo->level_nr = tape.level_nr; // (currently not used)
5126 level_sp->header.DemoRandomSeed = tape.random_seed;
5130 for (i = 0; i < tape.length; i++)
5132 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5133 int demo_repeat = tape.pos[i].delay;
5134 int demo_entries = (demo_repeat + 15) / 16;
5136 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5138 Warn("tape truncated: size exceeds maximum SP demo size %d",
5144 for (j = 0; j < demo_repeat / 16; j++)
5145 demo->data[demo->length++] = 0xf0 | demo_action;
5147 if (demo_repeat % 16)
5148 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5151 demo->is_available = TRUE;
5154 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5156 struct LevelInfo_SP *level_sp = level->native_sp_level;
5157 struct DemoInfo_SP *demo = &level_sp->demo;
5158 char *filename = level->file_info.filename;
5161 // always start with reliable default values
5162 setTapeInfoToDefaults();
5164 if (!demo->is_available)
5167 tape.level_nr = demo->level_nr; // (currently not used)
5168 tape.random_seed = level_sp->header.DemoRandomSeed;
5170 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5173 tape.pos[tape.counter].delay = 0;
5175 for (i = 0; i < demo->length; i++)
5177 int demo_action = demo->data[i] & 0x0f;
5178 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5179 int tape_action = map_key_SP_to_RND(demo_action);
5180 int tape_repeat = demo_repeat + 1;
5181 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5182 boolean success = 0;
5185 for (j = 0; j < tape_repeat; j++)
5186 success = TapeAddAction(action);
5190 Warn("SP demo truncated: size exceeds maximum tape size %d",
5197 TapeHaltRecording();
5201 // ----------------------------------------------------------------------------
5202 // functions for loading MM level
5203 // ----------------------------------------------------------------------------
5205 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5207 struct LevelInfo_MM *level_mm = level->native_mm_level;
5210 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5211 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5213 level_mm->time = level->time;
5214 level_mm->kettles_needed = level->gems_needed;
5215 level_mm->auto_count_kettles = level->auto_count_gems;
5217 level_mm->mm_laser_red = level->mm_laser_red;
5218 level_mm->mm_laser_green = level->mm_laser_green;
5219 level_mm->mm_laser_blue = level->mm_laser_blue;
5221 level_mm->df_laser_red = level->df_laser_red;
5222 level_mm->df_laser_green = level->df_laser_green;
5223 level_mm->df_laser_blue = level->df_laser_blue;
5225 strcpy(level_mm->name, level->name);
5226 strcpy(level_mm->author, level->author);
5228 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5229 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5230 level_mm->score[SC_KEY] = level->score[SC_KEY];
5231 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5232 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5234 level_mm->amoeba_speed = level->amoeba_speed;
5235 level_mm->time_fuse = level->mm_time_fuse;
5236 level_mm->time_bomb = level->mm_time_bomb;
5237 level_mm->time_ball = level->mm_time_ball;
5238 level_mm->time_block = level->mm_time_block;
5240 level_mm->num_ball_contents = level->num_mm_ball_contents;
5241 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5242 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5243 level_mm->explode_ball = level->explode_mm_ball;
5245 for (i = 0; i < level->num_mm_ball_contents; i++)
5246 level_mm->ball_content[i] =
5247 map_element_RND_to_MM(level->mm_ball_content[i]);
5249 for (x = 0; x < level->fieldx; x++)
5250 for (y = 0; y < level->fieldy; y++)
5252 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5255 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5257 struct LevelInfo_MM *level_mm = level->native_mm_level;
5260 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5261 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5263 level->time = level_mm->time;
5264 level->gems_needed = level_mm->kettles_needed;
5265 level->auto_count_gems = level_mm->auto_count_kettles;
5267 level->mm_laser_red = level_mm->mm_laser_red;
5268 level->mm_laser_green = level_mm->mm_laser_green;
5269 level->mm_laser_blue = level_mm->mm_laser_blue;
5271 level->df_laser_red = level_mm->df_laser_red;
5272 level->df_laser_green = level_mm->df_laser_green;
5273 level->df_laser_blue = level_mm->df_laser_blue;
5275 strcpy(level->name, level_mm->name);
5277 // only overwrite author from 'levelinfo.conf' if author defined in level
5278 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5279 strcpy(level->author, level_mm->author);
5281 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5282 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5283 level->score[SC_KEY] = level_mm->score[SC_KEY];
5284 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5285 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5287 level->amoeba_speed = level_mm->amoeba_speed;
5288 level->mm_time_fuse = level_mm->time_fuse;
5289 level->mm_time_bomb = level_mm->time_bomb;
5290 level->mm_time_ball = level_mm->time_ball;
5291 level->mm_time_block = level_mm->time_block;
5293 level->num_mm_ball_contents = level_mm->num_ball_contents;
5294 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5295 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5296 level->explode_mm_ball = level_mm->explode_ball;
5298 for (i = 0; i < level->num_mm_ball_contents; i++)
5299 level->mm_ball_content[i] =
5300 map_element_MM_to_RND(level_mm->ball_content[i]);
5302 for (x = 0; x < level->fieldx; x++)
5303 for (y = 0; y < level->fieldy; y++)
5304 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5308 // ----------------------------------------------------------------------------
5309 // functions for loading DC level
5310 // ----------------------------------------------------------------------------
5312 #define DC_LEVEL_HEADER_SIZE 344
5314 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5317 static int last_data_encoded;
5321 int diff_hi, diff_lo;
5322 int data_hi, data_lo;
5323 unsigned short data_decoded;
5327 last_data_encoded = 0;
5334 diff = data_encoded - last_data_encoded;
5335 diff_hi = diff & ~0xff;
5336 diff_lo = diff & 0xff;
5340 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5341 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5342 data_hi = data_hi & 0xff00;
5344 data_decoded = data_hi | data_lo;
5346 last_data_encoded = data_encoded;
5348 offset1 = (offset1 + 1) % 31;
5349 offset2 = offset2 & 0xff;
5351 return data_decoded;
5354 static int getMappedElement_DC(int element)
5362 // 0x0117 - 0x036e: (?)
5365 // 0x042d - 0x0684: (?)
5381 element = EL_CRYSTAL;
5384 case 0x0e77: // quicksand (boulder)
5385 element = EL_QUICKSAND_FAST_FULL;
5388 case 0x0e99: // slow quicksand (boulder)
5389 element = EL_QUICKSAND_FULL;
5393 element = EL_EM_EXIT_OPEN;
5397 element = EL_EM_EXIT_CLOSED;
5401 element = EL_EM_STEEL_EXIT_OPEN;
5405 element = EL_EM_STEEL_EXIT_CLOSED;
5408 case 0x0f4f: // dynamite (lit 1)
5409 element = EL_EM_DYNAMITE_ACTIVE;
5412 case 0x0f57: // dynamite (lit 2)
5413 element = EL_EM_DYNAMITE_ACTIVE;
5416 case 0x0f5f: // dynamite (lit 3)
5417 element = EL_EM_DYNAMITE_ACTIVE;
5420 case 0x0f67: // dynamite (lit 4)
5421 element = EL_EM_DYNAMITE_ACTIVE;
5428 element = EL_AMOEBA_WET;
5432 element = EL_AMOEBA_DROP;
5436 element = EL_DC_MAGIC_WALL;
5440 element = EL_SPACESHIP_UP;
5444 element = EL_SPACESHIP_DOWN;
5448 element = EL_SPACESHIP_LEFT;
5452 element = EL_SPACESHIP_RIGHT;
5456 element = EL_BUG_UP;
5460 element = EL_BUG_DOWN;
5464 element = EL_BUG_LEFT;
5468 element = EL_BUG_RIGHT;
5472 element = EL_MOLE_UP;
5476 element = EL_MOLE_DOWN;
5480 element = EL_MOLE_LEFT;
5484 element = EL_MOLE_RIGHT;
5492 element = EL_YAMYAM_UP;
5496 element = EL_SWITCHGATE_OPEN;
5500 element = EL_SWITCHGATE_CLOSED;
5504 element = EL_DC_SWITCHGATE_SWITCH_UP;
5508 element = EL_TIMEGATE_CLOSED;
5511 case 0x144c: // conveyor belt switch (green)
5512 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5515 case 0x144f: // conveyor belt switch (red)
5516 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5519 case 0x1452: // conveyor belt switch (blue)
5520 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5524 element = EL_CONVEYOR_BELT_3_MIDDLE;
5528 element = EL_CONVEYOR_BELT_3_LEFT;
5532 element = EL_CONVEYOR_BELT_3_RIGHT;
5536 element = EL_CONVEYOR_BELT_1_MIDDLE;
5540 element = EL_CONVEYOR_BELT_1_LEFT;
5544 element = EL_CONVEYOR_BELT_1_RIGHT;
5548 element = EL_CONVEYOR_BELT_4_MIDDLE;
5552 element = EL_CONVEYOR_BELT_4_LEFT;
5556 element = EL_CONVEYOR_BELT_4_RIGHT;
5560 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5564 element = EL_EXPANDABLE_WALL_VERTICAL;
5568 element = EL_EXPANDABLE_WALL_ANY;
5571 case 0x14ce: // growing steel wall (left/right)
5572 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5575 case 0x14df: // growing steel wall (up/down)
5576 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5579 case 0x14e8: // growing steel wall (up/down/left/right)
5580 element = EL_EXPANDABLE_STEELWALL_ANY;
5584 element = EL_SHIELD_DEADLY;
5588 element = EL_EXTRA_TIME;
5596 element = EL_EMPTY_SPACE;
5599 case 0x1578: // quicksand (empty)
5600 element = EL_QUICKSAND_FAST_EMPTY;
5603 case 0x1579: // slow quicksand (empty)
5604 element = EL_QUICKSAND_EMPTY;
5614 element = EL_EM_DYNAMITE;
5617 case 0x15a1: // key (red)
5618 element = EL_EM_KEY_1;
5621 case 0x15a2: // key (yellow)
5622 element = EL_EM_KEY_2;
5625 case 0x15a3: // key (blue)
5626 element = EL_EM_KEY_4;
5629 case 0x15a4: // key (green)
5630 element = EL_EM_KEY_3;
5633 case 0x15a5: // key (white)
5634 element = EL_DC_KEY_WHITE;
5638 element = EL_WALL_SLIPPERY;
5645 case 0x15a8: // wall (not round)
5649 case 0x15a9: // (blue)
5650 element = EL_CHAR_A;
5653 case 0x15aa: // (blue)
5654 element = EL_CHAR_B;
5657 case 0x15ab: // (blue)
5658 element = EL_CHAR_C;
5661 case 0x15ac: // (blue)
5662 element = EL_CHAR_D;
5665 case 0x15ad: // (blue)
5666 element = EL_CHAR_E;
5669 case 0x15ae: // (blue)
5670 element = EL_CHAR_F;
5673 case 0x15af: // (blue)
5674 element = EL_CHAR_G;
5677 case 0x15b0: // (blue)
5678 element = EL_CHAR_H;
5681 case 0x15b1: // (blue)
5682 element = EL_CHAR_I;
5685 case 0x15b2: // (blue)
5686 element = EL_CHAR_J;
5689 case 0x15b3: // (blue)
5690 element = EL_CHAR_K;
5693 case 0x15b4: // (blue)
5694 element = EL_CHAR_L;
5697 case 0x15b5: // (blue)
5698 element = EL_CHAR_M;
5701 case 0x15b6: // (blue)
5702 element = EL_CHAR_N;
5705 case 0x15b7: // (blue)
5706 element = EL_CHAR_O;
5709 case 0x15b8: // (blue)
5710 element = EL_CHAR_P;
5713 case 0x15b9: // (blue)
5714 element = EL_CHAR_Q;
5717 case 0x15ba: // (blue)
5718 element = EL_CHAR_R;
5721 case 0x15bb: // (blue)
5722 element = EL_CHAR_S;
5725 case 0x15bc: // (blue)
5726 element = EL_CHAR_T;
5729 case 0x15bd: // (blue)
5730 element = EL_CHAR_U;
5733 case 0x15be: // (blue)
5734 element = EL_CHAR_V;
5737 case 0x15bf: // (blue)
5738 element = EL_CHAR_W;
5741 case 0x15c0: // (blue)
5742 element = EL_CHAR_X;
5745 case 0x15c1: // (blue)
5746 element = EL_CHAR_Y;
5749 case 0x15c2: // (blue)
5750 element = EL_CHAR_Z;
5753 case 0x15c3: // (blue)
5754 element = EL_CHAR_AUMLAUT;
5757 case 0x15c4: // (blue)
5758 element = EL_CHAR_OUMLAUT;
5761 case 0x15c5: // (blue)
5762 element = EL_CHAR_UUMLAUT;
5765 case 0x15c6: // (blue)
5766 element = EL_CHAR_0;
5769 case 0x15c7: // (blue)
5770 element = EL_CHAR_1;
5773 case 0x15c8: // (blue)
5774 element = EL_CHAR_2;
5777 case 0x15c9: // (blue)
5778 element = EL_CHAR_3;
5781 case 0x15ca: // (blue)
5782 element = EL_CHAR_4;
5785 case 0x15cb: // (blue)
5786 element = EL_CHAR_5;
5789 case 0x15cc: // (blue)
5790 element = EL_CHAR_6;
5793 case 0x15cd: // (blue)
5794 element = EL_CHAR_7;
5797 case 0x15ce: // (blue)
5798 element = EL_CHAR_8;
5801 case 0x15cf: // (blue)
5802 element = EL_CHAR_9;
5805 case 0x15d0: // (blue)
5806 element = EL_CHAR_PERIOD;
5809 case 0x15d1: // (blue)
5810 element = EL_CHAR_EXCLAM;
5813 case 0x15d2: // (blue)
5814 element = EL_CHAR_COLON;
5817 case 0x15d3: // (blue)
5818 element = EL_CHAR_LESS;
5821 case 0x15d4: // (blue)
5822 element = EL_CHAR_GREATER;
5825 case 0x15d5: // (blue)
5826 element = EL_CHAR_QUESTION;
5829 case 0x15d6: // (blue)
5830 element = EL_CHAR_COPYRIGHT;
5833 case 0x15d7: // (blue)
5834 element = EL_CHAR_UP;
5837 case 0x15d8: // (blue)
5838 element = EL_CHAR_DOWN;
5841 case 0x15d9: // (blue)
5842 element = EL_CHAR_BUTTON;
5845 case 0x15da: // (blue)
5846 element = EL_CHAR_PLUS;
5849 case 0x15db: // (blue)
5850 element = EL_CHAR_MINUS;
5853 case 0x15dc: // (blue)
5854 element = EL_CHAR_APOSTROPHE;
5857 case 0x15dd: // (blue)
5858 element = EL_CHAR_PARENLEFT;
5861 case 0x15de: // (blue)
5862 element = EL_CHAR_PARENRIGHT;
5865 case 0x15df: // (green)
5866 element = EL_CHAR_A;
5869 case 0x15e0: // (green)
5870 element = EL_CHAR_B;
5873 case 0x15e1: // (green)
5874 element = EL_CHAR_C;
5877 case 0x15e2: // (green)
5878 element = EL_CHAR_D;
5881 case 0x15e3: // (green)
5882 element = EL_CHAR_E;
5885 case 0x15e4: // (green)
5886 element = EL_CHAR_F;
5889 case 0x15e5: // (green)
5890 element = EL_CHAR_G;
5893 case 0x15e6: // (green)
5894 element = EL_CHAR_H;
5897 case 0x15e7: // (green)
5898 element = EL_CHAR_I;
5901 case 0x15e8: // (green)
5902 element = EL_CHAR_J;
5905 case 0x15e9: // (green)
5906 element = EL_CHAR_K;
5909 case 0x15ea: // (green)
5910 element = EL_CHAR_L;
5913 case 0x15eb: // (green)
5914 element = EL_CHAR_M;
5917 case 0x15ec: // (green)
5918 element = EL_CHAR_N;
5921 case 0x15ed: // (green)
5922 element = EL_CHAR_O;
5925 case 0x15ee: // (green)
5926 element = EL_CHAR_P;
5929 case 0x15ef: // (green)
5930 element = EL_CHAR_Q;
5933 case 0x15f0: // (green)
5934 element = EL_CHAR_R;
5937 case 0x15f1: // (green)
5938 element = EL_CHAR_S;
5941 case 0x15f2: // (green)
5942 element = EL_CHAR_T;
5945 case 0x15f3: // (green)
5946 element = EL_CHAR_U;
5949 case 0x15f4: // (green)
5950 element = EL_CHAR_V;
5953 case 0x15f5: // (green)
5954 element = EL_CHAR_W;
5957 case 0x15f6: // (green)
5958 element = EL_CHAR_X;
5961 case 0x15f7: // (green)
5962 element = EL_CHAR_Y;
5965 case 0x15f8: // (green)
5966 element = EL_CHAR_Z;
5969 case 0x15f9: // (green)
5970 element = EL_CHAR_AUMLAUT;
5973 case 0x15fa: // (green)
5974 element = EL_CHAR_OUMLAUT;
5977 case 0x15fb: // (green)
5978 element = EL_CHAR_UUMLAUT;
5981 case 0x15fc: // (green)
5982 element = EL_CHAR_0;
5985 case 0x15fd: // (green)
5986 element = EL_CHAR_1;
5989 case 0x15fe: // (green)
5990 element = EL_CHAR_2;
5993 case 0x15ff: // (green)
5994 element = EL_CHAR_3;
5997 case 0x1600: // (green)
5998 element = EL_CHAR_4;
6001 case 0x1601: // (green)
6002 element = EL_CHAR_5;
6005 case 0x1602: // (green)
6006 element = EL_CHAR_6;
6009 case 0x1603: // (green)
6010 element = EL_CHAR_7;
6013 case 0x1604: // (green)
6014 element = EL_CHAR_8;
6017 case 0x1605: // (green)
6018 element = EL_CHAR_9;
6021 case 0x1606: // (green)
6022 element = EL_CHAR_PERIOD;
6025 case 0x1607: // (green)
6026 element = EL_CHAR_EXCLAM;
6029 case 0x1608: // (green)
6030 element = EL_CHAR_COLON;
6033 case 0x1609: // (green)
6034 element = EL_CHAR_LESS;
6037 case 0x160a: // (green)
6038 element = EL_CHAR_GREATER;
6041 case 0x160b: // (green)
6042 element = EL_CHAR_QUESTION;
6045 case 0x160c: // (green)
6046 element = EL_CHAR_COPYRIGHT;
6049 case 0x160d: // (green)
6050 element = EL_CHAR_UP;
6053 case 0x160e: // (green)
6054 element = EL_CHAR_DOWN;
6057 case 0x160f: // (green)
6058 element = EL_CHAR_BUTTON;
6061 case 0x1610: // (green)
6062 element = EL_CHAR_PLUS;
6065 case 0x1611: // (green)
6066 element = EL_CHAR_MINUS;
6069 case 0x1612: // (green)
6070 element = EL_CHAR_APOSTROPHE;
6073 case 0x1613: // (green)
6074 element = EL_CHAR_PARENLEFT;
6077 case 0x1614: // (green)
6078 element = EL_CHAR_PARENRIGHT;
6081 case 0x1615: // (blue steel)
6082 element = EL_STEEL_CHAR_A;
6085 case 0x1616: // (blue steel)
6086 element = EL_STEEL_CHAR_B;
6089 case 0x1617: // (blue steel)
6090 element = EL_STEEL_CHAR_C;
6093 case 0x1618: // (blue steel)
6094 element = EL_STEEL_CHAR_D;
6097 case 0x1619: // (blue steel)
6098 element = EL_STEEL_CHAR_E;
6101 case 0x161a: // (blue steel)
6102 element = EL_STEEL_CHAR_F;
6105 case 0x161b: // (blue steel)
6106 element = EL_STEEL_CHAR_G;
6109 case 0x161c: // (blue steel)
6110 element = EL_STEEL_CHAR_H;
6113 case 0x161d: // (blue steel)
6114 element = EL_STEEL_CHAR_I;
6117 case 0x161e: // (blue steel)
6118 element = EL_STEEL_CHAR_J;
6121 case 0x161f: // (blue steel)
6122 element = EL_STEEL_CHAR_K;
6125 case 0x1620: // (blue steel)
6126 element = EL_STEEL_CHAR_L;
6129 case 0x1621: // (blue steel)
6130 element = EL_STEEL_CHAR_M;
6133 case 0x1622: // (blue steel)
6134 element = EL_STEEL_CHAR_N;
6137 case 0x1623: // (blue steel)
6138 element = EL_STEEL_CHAR_O;
6141 case 0x1624: // (blue steel)
6142 element = EL_STEEL_CHAR_P;
6145 case 0x1625: // (blue steel)
6146 element = EL_STEEL_CHAR_Q;
6149 case 0x1626: // (blue steel)
6150 element = EL_STEEL_CHAR_R;
6153 case 0x1627: // (blue steel)
6154 element = EL_STEEL_CHAR_S;
6157 case 0x1628: // (blue steel)
6158 element = EL_STEEL_CHAR_T;
6161 case 0x1629: // (blue steel)
6162 element = EL_STEEL_CHAR_U;
6165 case 0x162a: // (blue steel)
6166 element = EL_STEEL_CHAR_V;
6169 case 0x162b: // (blue steel)
6170 element = EL_STEEL_CHAR_W;
6173 case 0x162c: // (blue steel)
6174 element = EL_STEEL_CHAR_X;
6177 case 0x162d: // (blue steel)
6178 element = EL_STEEL_CHAR_Y;
6181 case 0x162e: // (blue steel)
6182 element = EL_STEEL_CHAR_Z;
6185 case 0x162f: // (blue steel)
6186 element = EL_STEEL_CHAR_AUMLAUT;
6189 case 0x1630: // (blue steel)
6190 element = EL_STEEL_CHAR_OUMLAUT;
6193 case 0x1631: // (blue steel)
6194 element = EL_STEEL_CHAR_UUMLAUT;
6197 case 0x1632: // (blue steel)
6198 element = EL_STEEL_CHAR_0;
6201 case 0x1633: // (blue steel)
6202 element = EL_STEEL_CHAR_1;
6205 case 0x1634: // (blue steel)
6206 element = EL_STEEL_CHAR_2;
6209 case 0x1635: // (blue steel)
6210 element = EL_STEEL_CHAR_3;
6213 case 0x1636: // (blue steel)
6214 element = EL_STEEL_CHAR_4;
6217 case 0x1637: // (blue steel)
6218 element = EL_STEEL_CHAR_5;
6221 case 0x1638: // (blue steel)
6222 element = EL_STEEL_CHAR_6;
6225 case 0x1639: // (blue steel)
6226 element = EL_STEEL_CHAR_7;
6229 case 0x163a: // (blue steel)
6230 element = EL_STEEL_CHAR_8;
6233 case 0x163b: // (blue steel)
6234 element = EL_STEEL_CHAR_9;
6237 case 0x163c: // (blue steel)
6238 element = EL_STEEL_CHAR_PERIOD;
6241 case 0x163d: // (blue steel)
6242 element = EL_STEEL_CHAR_EXCLAM;
6245 case 0x163e: // (blue steel)
6246 element = EL_STEEL_CHAR_COLON;
6249 case 0x163f: // (blue steel)
6250 element = EL_STEEL_CHAR_LESS;
6253 case 0x1640: // (blue steel)
6254 element = EL_STEEL_CHAR_GREATER;
6257 case 0x1641: // (blue steel)
6258 element = EL_STEEL_CHAR_QUESTION;
6261 case 0x1642: // (blue steel)
6262 element = EL_STEEL_CHAR_COPYRIGHT;
6265 case 0x1643: // (blue steel)
6266 element = EL_STEEL_CHAR_UP;
6269 case 0x1644: // (blue steel)
6270 element = EL_STEEL_CHAR_DOWN;
6273 case 0x1645: // (blue steel)
6274 element = EL_STEEL_CHAR_BUTTON;
6277 case 0x1646: // (blue steel)
6278 element = EL_STEEL_CHAR_PLUS;
6281 case 0x1647: // (blue steel)
6282 element = EL_STEEL_CHAR_MINUS;
6285 case 0x1648: // (blue steel)
6286 element = EL_STEEL_CHAR_APOSTROPHE;
6289 case 0x1649: // (blue steel)
6290 element = EL_STEEL_CHAR_PARENLEFT;
6293 case 0x164a: // (blue steel)
6294 element = EL_STEEL_CHAR_PARENRIGHT;
6297 case 0x164b: // (green steel)
6298 element = EL_STEEL_CHAR_A;
6301 case 0x164c: // (green steel)
6302 element = EL_STEEL_CHAR_B;
6305 case 0x164d: // (green steel)
6306 element = EL_STEEL_CHAR_C;
6309 case 0x164e: // (green steel)
6310 element = EL_STEEL_CHAR_D;
6313 case 0x164f: // (green steel)
6314 element = EL_STEEL_CHAR_E;
6317 case 0x1650: // (green steel)
6318 element = EL_STEEL_CHAR_F;
6321 case 0x1651: // (green steel)
6322 element = EL_STEEL_CHAR_G;
6325 case 0x1652: // (green steel)
6326 element = EL_STEEL_CHAR_H;
6329 case 0x1653: // (green steel)
6330 element = EL_STEEL_CHAR_I;
6333 case 0x1654: // (green steel)
6334 element = EL_STEEL_CHAR_J;
6337 case 0x1655: // (green steel)
6338 element = EL_STEEL_CHAR_K;
6341 case 0x1656: // (green steel)
6342 element = EL_STEEL_CHAR_L;
6345 case 0x1657: // (green steel)
6346 element = EL_STEEL_CHAR_M;
6349 case 0x1658: // (green steel)
6350 element = EL_STEEL_CHAR_N;
6353 case 0x1659: // (green steel)
6354 element = EL_STEEL_CHAR_O;
6357 case 0x165a: // (green steel)
6358 element = EL_STEEL_CHAR_P;
6361 case 0x165b: // (green steel)
6362 element = EL_STEEL_CHAR_Q;
6365 case 0x165c: // (green steel)
6366 element = EL_STEEL_CHAR_R;
6369 case 0x165d: // (green steel)
6370 element = EL_STEEL_CHAR_S;
6373 case 0x165e: // (green steel)
6374 element = EL_STEEL_CHAR_T;
6377 case 0x165f: // (green steel)
6378 element = EL_STEEL_CHAR_U;
6381 case 0x1660: // (green steel)
6382 element = EL_STEEL_CHAR_V;
6385 case 0x1661: // (green steel)
6386 element = EL_STEEL_CHAR_W;
6389 case 0x1662: // (green steel)
6390 element = EL_STEEL_CHAR_X;
6393 case 0x1663: // (green steel)
6394 element = EL_STEEL_CHAR_Y;
6397 case 0x1664: // (green steel)
6398 element = EL_STEEL_CHAR_Z;
6401 case 0x1665: // (green steel)
6402 element = EL_STEEL_CHAR_AUMLAUT;
6405 case 0x1666: // (green steel)
6406 element = EL_STEEL_CHAR_OUMLAUT;
6409 case 0x1667: // (green steel)
6410 element = EL_STEEL_CHAR_UUMLAUT;
6413 case 0x1668: // (green steel)
6414 element = EL_STEEL_CHAR_0;
6417 case 0x1669: // (green steel)
6418 element = EL_STEEL_CHAR_1;
6421 case 0x166a: // (green steel)
6422 element = EL_STEEL_CHAR_2;
6425 case 0x166b: // (green steel)
6426 element = EL_STEEL_CHAR_3;
6429 case 0x166c: // (green steel)
6430 element = EL_STEEL_CHAR_4;
6433 case 0x166d: // (green steel)
6434 element = EL_STEEL_CHAR_5;
6437 case 0x166e: // (green steel)
6438 element = EL_STEEL_CHAR_6;
6441 case 0x166f: // (green steel)
6442 element = EL_STEEL_CHAR_7;
6445 case 0x1670: // (green steel)
6446 element = EL_STEEL_CHAR_8;
6449 case 0x1671: // (green steel)
6450 element = EL_STEEL_CHAR_9;
6453 case 0x1672: // (green steel)
6454 element = EL_STEEL_CHAR_PERIOD;
6457 case 0x1673: // (green steel)
6458 element = EL_STEEL_CHAR_EXCLAM;
6461 case 0x1674: // (green steel)
6462 element = EL_STEEL_CHAR_COLON;
6465 case 0x1675: // (green steel)
6466 element = EL_STEEL_CHAR_LESS;
6469 case 0x1676: // (green steel)
6470 element = EL_STEEL_CHAR_GREATER;
6473 case 0x1677: // (green steel)
6474 element = EL_STEEL_CHAR_QUESTION;
6477 case 0x1678: // (green steel)
6478 element = EL_STEEL_CHAR_COPYRIGHT;
6481 case 0x1679: // (green steel)
6482 element = EL_STEEL_CHAR_UP;
6485 case 0x167a: // (green steel)
6486 element = EL_STEEL_CHAR_DOWN;
6489 case 0x167b: // (green steel)
6490 element = EL_STEEL_CHAR_BUTTON;
6493 case 0x167c: // (green steel)
6494 element = EL_STEEL_CHAR_PLUS;
6497 case 0x167d: // (green steel)
6498 element = EL_STEEL_CHAR_MINUS;
6501 case 0x167e: // (green steel)
6502 element = EL_STEEL_CHAR_APOSTROPHE;
6505 case 0x167f: // (green steel)
6506 element = EL_STEEL_CHAR_PARENLEFT;
6509 case 0x1680: // (green steel)
6510 element = EL_STEEL_CHAR_PARENRIGHT;
6513 case 0x1681: // gate (red)
6514 element = EL_EM_GATE_1;
6517 case 0x1682: // secret gate (red)
6518 element = EL_EM_GATE_1_GRAY;
6521 case 0x1683: // gate (yellow)
6522 element = EL_EM_GATE_2;
6525 case 0x1684: // secret gate (yellow)
6526 element = EL_EM_GATE_2_GRAY;
6529 case 0x1685: // gate (blue)
6530 element = EL_EM_GATE_4;
6533 case 0x1686: // secret gate (blue)
6534 element = EL_EM_GATE_4_GRAY;
6537 case 0x1687: // gate (green)
6538 element = EL_EM_GATE_3;
6541 case 0x1688: // secret gate (green)
6542 element = EL_EM_GATE_3_GRAY;
6545 case 0x1689: // gate (white)
6546 element = EL_DC_GATE_WHITE;
6549 case 0x168a: // secret gate (white)
6550 element = EL_DC_GATE_WHITE_GRAY;
6553 case 0x168b: // secret gate (no key)
6554 element = EL_DC_GATE_FAKE_GRAY;
6558 element = EL_ROBOT_WHEEL;
6562 element = EL_DC_TIMEGATE_SWITCH;
6566 element = EL_ACID_POOL_BOTTOM;
6570 element = EL_ACID_POOL_TOPLEFT;
6574 element = EL_ACID_POOL_TOPRIGHT;
6578 element = EL_ACID_POOL_BOTTOMLEFT;
6582 element = EL_ACID_POOL_BOTTOMRIGHT;
6586 element = EL_STEELWALL;
6590 element = EL_STEELWALL_SLIPPERY;
6593 case 0x1695: // steel wall (not round)
6594 element = EL_STEELWALL;
6597 case 0x1696: // steel wall (left)
6598 element = EL_DC_STEELWALL_1_LEFT;
6601 case 0x1697: // steel wall (bottom)
6602 element = EL_DC_STEELWALL_1_BOTTOM;
6605 case 0x1698: // steel wall (right)
6606 element = EL_DC_STEELWALL_1_RIGHT;
6609 case 0x1699: // steel wall (top)
6610 element = EL_DC_STEELWALL_1_TOP;
6613 case 0x169a: // steel wall (left/bottom)
6614 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6617 case 0x169b: // steel wall (right/bottom)
6618 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6621 case 0x169c: // steel wall (right/top)
6622 element = EL_DC_STEELWALL_1_TOPRIGHT;
6625 case 0x169d: // steel wall (left/top)
6626 element = EL_DC_STEELWALL_1_TOPLEFT;
6629 case 0x169e: // steel wall (right/bottom small)
6630 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6633 case 0x169f: // steel wall (left/bottom small)
6634 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6637 case 0x16a0: // steel wall (right/top small)
6638 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6641 case 0x16a1: // steel wall (left/top small)
6642 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6645 case 0x16a2: // steel wall (left/right)
6646 element = EL_DC_STEELWALL_1_VERTICAL;
6649 case 0x16a3: // steel wall (top/bottom)
6650 element = EL_DC_STEELWALL_1_HORIZONTAL;
6653 case 0x16a4: // steel wall 2 (left end)
6654 element = EL_DC_STEELWALL_2_LEFT;
6657 case 0x16a5: // steel wall 2 (right end)
6658 element = EL_DC_STEELWALL_2_RIGHT;
6661 case 0x16a6: // steel wall 2 (top end)
6662 element = EL_DC_STEELWALL_2_TOP;
6665 case 0x16a7: // steel wall 2 (bottom end)
6666 element = EL_DC_STEELWALL_2_BOTTOM;
6669 case 0x16a8: // steel wall 2 (left/right)
6670 element = EL_DC_STEELWALL_2_HORIZONTAL;
6673 case 0x16a9: // steel wall 2 (up/down)
6674 element = EL_DC_STEELWALL_2_VERTICAL;
6677 case 0x16aa: // steel wall 2 (mid)
6678 element = EL_DC_STEELWALL_2_MIDDLE;
6682 element = EL_SIGN_EXCLAMATION;
6686 element = EL_SIGN_RADIOACTIVITY;
6690 element = EL_SIGN_STOP;
6694 element = EL_SIGN_WHEELCHAIR;
6698 element = EL_SIGN_PARKING;
6702 element = EL_SIGN_NO_ENTRY;
6706 element = EL_SIGN_HEART;
6710 element = EL_SIGN_GIVE_WAY;
6714 element = EL_SIGN_ENTRY_FORBIDDEN;
6718 element = EL_SIGN_EMERGENCY_EXIT;
6722 element = EL_SIGN_YIN_YANG;
6726 element = EL_WALL_EMERALD;
6730 element = EL_WALL_DIAMOND;
6734 element = EL_WALL_PEARL;
6738 element = EL_WALL_CRYSTAL;
6742 element = EL_INVISIBLE_WALL;
6746 element = EL_INVISIBLE_STEELWALL;
6750 // EL_INVISIBLE_SAND
6753 element = EL_LIGHT_SWITCH;
6757 element = EL_ENVELOPE_1;
6761 if (element >= 0x0117 && element <= 0x036e) // (?)
6762 element = EL_DIAMOND;
6763 else if (element >= 0x042d && element <= 0x0684) // (?)
6764 element = EL_EMERALD;
6765 else if (element >= 0x157c && element <= 0x158b)
6767 else if (element >= 0x1590 && element <= 0x159f)
6768 element = EL_DC_LANDMINE;
6769 else if (element >= 0x16bc && element <= 0x16cb)
6770 element = EL_INVISIBLE_SAND;
6773 Warn("unknown Diamond Caves element 0x%04x", element);
6775 element = EL_UNKNOWN;
6780 return getMappedElement(element);
6783 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6785 byte header[DC_LEVEL_HEADER_SIZE];
6787 int envelope_header_pos = 62;
6788 int envelope_content_pos = 94;
6789 int level_name_pos = 251;
6790 int level_author_pos = 292;
6791 int envelope_header_len;
6792 int envelope_content_len;
6794 int level_author_len;
6796 int num_yamyam_contents;
6799 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6801 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6803 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6805 header[i * 2 + 0] = header_word >> 8;
6806 header[i * 2 + 1] = header_word & 0xff;
6809 // read some values from level header to check level decoding integrity
6810 fieldx = header[6] | (header[7] << 8);
6811 fieldy = header[8] | (header[9] << 8);
6812 num_yamyam_contents = header[60] | (header[61] << 8);
6814 // do some simple sanity checks to ensure that level was correctly decoded
6815 if (fieldx < 1 || fieldx > 256 ||
6816 fieldy < 1 || fieldy > 256 ||
6817 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6819 level->no_valid_file = TRUE;
6821 Warn("cannot decode level from stream -- using empty level");
6826 // maximum envelope header size is 31 bytes
6827 envelope_header_len = header[envelope_header_pos];
6828 // maximum envelope content size is 110 (156?) bytes
6829 envelope_content_len = header[envelope_content_pos];
6831 // maximum level title size is 40 bytes
6832 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6833 // maximum level author size is 30 (51?) bytes
6834 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6838 for (i = 0; i < envelope_header_len; i++)
6839 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6840 level->envelope[0].text[envelope_size++] =
6841 header[envelope_header_pos + 1 + i];
6843 if (envelope_header_len > 0 && envelope_content_len > 0)
6845 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6846 level->envelope[0].text[envelope_size++] = '\n';
6847 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6848 level->envelope[0].text[envelope_size++] = '\n';
6851 for (i = 0; i < envelope_content_len; i++)
6852 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6853 level->envelope[0].text[envelope_size++] =
6854 header[envelope_content_pos + 1 + i];
6856 level->envelope[0].text[envelope_size] = '\0';
6858 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6859 level->envelope[0].ysize = 10;
6860 level->envelope[0].autowrap = TRUE;
6861 level->envelope[0].centered = TRUE;
6863 for (i = 0; i < level_name_len; i++)
6864 level->name[i] = header[level_name_pos + 1 + i];
6865 level->name[level_name_len] = '\0';
6867 for (i = 0; i < level_author_len; i++)
6868 level->author[i] = header[level_author_pos + 1 + i];
6869 level->author[level_author_len] = '\0';
6871 num_yamyam_contents = header[60] | (header[61] << 8);
6872 level->num_yamyam_contents =
6873 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6875 for (i = 0; i < num_yamyam_contents; i++)
6877 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6879 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6880 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6882 if (i < MAX_ELEMENT_CONTENTS)
6883 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6887 fieldx = header[6] | (header[7] << 8);
6888 fieldy = header[8] | (header[9] << 8);
6889 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6890 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6892 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6894 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6895 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6897 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6898 level->field[x][y] = getMappedElement_DC(element_dc);
6901 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6902 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6903 level->field[x][y] = EL_PLAYER_1;
6905 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6906 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6907 level->field[x][y] = EL_PLAYER_2;
6909 level->gems_needed = header[18] | (header[19] << 8);
6911 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6912 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6913 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6914 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6915 level->score[SC_NUT] = header[28] | (header[29] << 8);
6916 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6917 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6918 level->score[SC_BUG] = header[34] | (header[35] << 8);
6919 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6920 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6921 level->score[SC_KEY] = header[40] | (header[41] << 8);
6922 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6924 level->time = header[44] | (header[45] << 8);
6926 level->amoeba_speed = header[46] | (header[47] << 8);
6927 level->time_light = header[48] | (header[49] << 8);
6928 level->time_timegate = header[50] | (header[51] << 8);
6929 level->time_wheel = header[52] | (header[53] << 8);
6930 level->time_magic_wall = header[54] | (header[55] << 8);
6931 level->extra_time = header[56] | (header[57] << 8);
6932 level->shield_normal_time = header[58] | (header[59] << 8);
6934 // shield and extra time elements do not have a score
6935 level->score[SC_SHIELD] = 0;
6936 level->extra_time_score = 0;
6938 // set time for normal and deadly shields to the same value
6939 level->shield_deadly_time = level->shield_normal_time;
6941 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6942 // can slip down from flat walls, like normal walls and steel walls
6943 level->em_slippery_gems = TRUE;
6945 // time score is counted for each 10 seconds left in Diamond Caves levels
6946 level->time_score_base = 10;
6949 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6950 struct LevelFileInfo *level_file_info,
6951 boolean level_info_only)
6953 char *filename = level_file_info->filename;
6955 int num_magic_bytes = 8;
6956 char magic_bytes[num_magic_bytes + 1];
6957 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6959 if (!(file = openFile(filename, MODE_READ)))
6961 level->no_valid_file = TRUE;
6963 if (!level_info_only)
6964 Warn("cannot read level '%s' -- using empty level", filename);
6969 // fseek(file, 0x0000, SEEK_SET);
6971 if (level_file_info->packed)
6973 // read "magic bytes" from start of file
6974 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6975 magic_bytes[0] = '\0';
6977 // check "magic bytes" for correct file format
6978 if (!strPrefix(magic_bytes, "DC2"))
6980 level->no_valid_file = TRUE;
6982 Warn("unknown DC level file '%s' -- using empty level", filename);
6987 if (strPrefix(magic_bytes, "DC2Win95") ||
6988 strPrefix(magic_bytes, "DC2Win98"))
6990 int position_first_level = 0x00fa;
6991 int extra_bytes = 4;
6994 // advance file stream to first level inside the level package
6995 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6997 // each block of level data is followed by block of non-level data
6998 num_levels_to_skip *= 2;
7000 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7001 while (num_levels_to_skip >= 0)
7003 // advance file stream to next level inside the level package
7004 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7006 level->no_valid_file = TRUE;
7008 Warn("cannot fseek in file '%s' -- using empty level", filename);
7013 // skip apparently unused extra bytes following each level
7014 ReadUnusedBytesFromFile(file, extra_bytes);
7016 // read size of next level in level package
7017 skip_bytes = getFile32BitLE(file);
7019 num_levels_to_skip--;
7024 level->no_valid_file = TRUE;
7026 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7032 LoadLevelFromFileStream_DC(file, level);
7038 // ----------------------------------------------------------------------------
7039 // functions for loading SB level
7040 // ----------------------------------------------------------------------------
7042 int getMappedElement_SB(int element_ascii, boolean use_ces)
7050 sb_element_mapping[] =
7052 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7053 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7054 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7055 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7056 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7057 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7058 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7059 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7066 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7067 if (element_ascii == sb_element_mapping[i].ascii)
7068 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7070 return EL_UNDEFINED;
7073 static void SetLevelSettings_SB(struct LevelInfo *level)
7077 level->use_step_counter = TRUE;
7080 level->score[SC_TIME_BONUS] = 0;
7081 level->time_score_base = 1;
7082 level->rate_time_over_score = TRUE;
7085 level->auto_exit_sokoban = TRUE;
7088 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7089 struct LevelFileInfo *level_file_info,
7090 boolean level_info_only)
7092 char *filename = level_file_info->filename;
7093 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7094 char last_comment[MAX_LINE_LEN];
7095 char level_name[MAX_LINE_LEN];
7098 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7099 boolean read_continued_line = FALSE;
7100 boolean reading_playfield = FALSE;
7101 boolean got_valid_playfield_line = FALSE;
7102 boolean invalid_playfield_char = FALSE;
7103 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7104 int file_level_nr = 0;
7105 int x = 0, y = 0; // initialized to make compilers happy
7107 last_comment[0] = '\0';
7108 level_name[0] = '\0';
7110 if (!(file = openFile(filename, MODE_READ)))
7112 level->no_valid_file = TRUE;
7114 if (!level_info_only)
7115 Warn("cannot read level '%s' -- using empty level", filename);
7120 while (!checkEndOfFile(file))
7122 // level successfully read, but next level may follow here
7123 if (!got_valid_playfield_line && reading_playfield)
7125 // read playfield from single level file -- skip remaining file
7126 if (!level_file_info->packed)
7129 if (file_level_nr >= num_levels_to_skip)
7134 last_comment[0] = '\0';
7135 level_name[0] = '\0';
7137 reading_playfield = FALSE;
7140 got_valid_playfield_line = FALSE;
7142 // read next line of input file
7143 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7146 // cut trailing line break (this can be newline and/or carriage return)
7147 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7148 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7151 // copy raw input line for later use (mainly debugging output)
7152 strcpy(line_raw, line);
7154 if (read_continued_line)
7156 // append new line to existing line, if there is enough space
7157 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7158 strcat(previous_line, line_ptr);
7160 strcpy(line, previous_line); // copy storage buffer to line
7162 read_continued_line = FALSE;
7165 // if the last character is '\', continue at next line
7166 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7168 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7169 strcpy(previous_line, line); // copy line to storage buffer
7171 read_continued_line = TRUE;
7177 if (line[0] == '\0')
7180 // extract comment text from comment line
7183 for (line_ptr = line; *line_ptr; line_ptr++)
7184 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7187 strcpy(last_comment, line_ptr);
7192 // extract level title text from line containing level title
7193 if (line[0] == '\'')
7195 strcpy(level_name, &line[1]);
7197 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7198 level_name[strlen(level_name) - 1] = '\0';
7203 // skip lines containing only spaces (or empty lines)
7204 for (line_ptr = line; *line_ptr; line_ptr++)
7205 if (*line_ptr != ' ')
7207 if (*line_ptr == '\0')
7210 // at this point, we have found a line containing part of a playfield
7212 got_valid_playfield_line = TRUE;
7214 if (!reading_playfield)
7216 reading_playfield = TRUE;
7217 invalid_playfield_char = FALSE;
7219 for (x = 0; x < MAX_LEV_FIELDX; x++)
7220 for (y = 0; y < MAX_LEV_FIELDY; y++)
7221 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7226 // start with topmost tile row
7230 // skip playfield line if larger row than allowed
7231 if (y >= MAX_LEV_FIELDY)
7234 // start with leftmost tile column
7237 // read playfield elements from line
7238 for (line_ptr = line; *line_ptr; line_ptr++)
7240 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7242 // stop parsing playfield line if larger column than allowed
7243 if (x >= MAX_LEV_FIELDX)
7246 if (mapped_sb_element == EL_UNDEFINED)
7248 invalid_playfield_char = TRUE;
7253 level->field[x][y] = mapped_sb_element;
7255 // continue with next tile column
7258 level->fieldx = MAX(x, level->fieldx);
7261 if (invalid_playfield_char)
7263 // if first playfield line, treat invalid lines as comment lines
7265 reading_playfield = FALSE;
7270 // continue with next tile row
7278 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7279 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7281 if (!reading_playfield)
7283 level->no_valid_file = TRUE;
7285 Warn("cannot read level '%s' -- using empty level", filename);
7290 if (*level_name != '\0')
7292 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7293 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7295 else if (*last_comment != '\0')
7297 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7298 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7302 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7305 // set all empty fields beyond the border walls to invisible steel wall
7306 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7308 if ((x == 0 || x == level->fieldx - 1 ||
7309 y == 0 || y == level->fieldy - 1) &&
7310 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7311 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7312 level->field, level->fieldx, level->fieldy);
7315 // set special level settings for Sokoban levels
7316 SetLevelSettings_SB(level);
7318 if (load_xsb_to_ces)
7320 // special global settings can now be set in level template
7321 level->use_custom_template = TRUE;
7326 // -------------------------------------------------------------------------
7327 // functions for handling native levels
7328 // -------------------------------------------------------------------------
7330 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7331 struct LevelFileInfo *level_file_info,
7332 boolean level_info_only)
7336 // determine position of requested level inside level package
7337 if (level_file_info->packed)
7338 pos = level_file_info->nr - leveldir_current->first_level;
7340 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7341 level->no_valid_file = TRUE;
7344 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7345 struct LevelFileInfo *level_file_info,
7346 boolean level_info_only)
7348 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7349 level->no_valid_file = TRUE;
7352 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7353 struct LevelFileInfo *level_file_info,
7354 boolean level_info_only)
7358 // determine position of requested level inside level package
7359 if (level_file_info->packed)
7360 pos = level_file_info->nr - leveldir_current->first_level;
7362 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7363 level->no_valid_file = TRUE;
7366 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7367 struct LevelFileInfo *level_file_info,
7368 boolean level_info_only)
7370 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7371 level->no_valid_file = TRUE;
7374 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7376 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7377 CopyNativeLevel_RND_to_BD(level);
7378 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7379 CopyNativeLevel_RND_to_EM(level);
7380 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7381 CopyNativeLevel_RND_to_SP(level);
7382 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7383 CopyNativeLevel_RND_to_MM(level);
7386 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7388 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7389 CopyNativeLevel_BD_to_RND(level);
7390 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7391 CopyNativeLevel_EM_to_RND(level);
7392 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7393 CopyNativeLevel_SP_to_RND(level);
7394 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7395 CopyNativeLevel_MM_to_RND(level);
7398 void SaveNativeLevel(struct LevelInfo *level)
7400 // saving native level files only supported for some game engines
7401 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7402 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7405 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7406 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7407 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7408 char *filename = getLevelFilenameFromBasename(basename);
7410 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7413 boolean success = FALSE;
7415 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7417 CopyNativeLevel_RND_to_BD(level);
7418 // CopyNativeTape_RND_to_BD(level);
7420 success = SaveNativeLevel_BD(filename);
7422 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7424 CopyNativeLevel_RND_to_SP(level);
7425 CopyNativeTape_RND_to_SP(level);
7427 success = SaveNativeLevel_SP(filename);
7431 Request("Native level file saved!", REQ_CONFIRM);
7433 Request("Failed to save native level file!", REQ_CONFIRM);
7437 // ----------------------------------------------------------------------------
7438 // functions for loading generic level
7439 // ----------------------------------------------------------------------------
7441 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7442 struct LevelFileInfo *level_file_info,
7443 boolean level_info_only)
7445 // always start with reliable default values
7446 setLevelInfoToDefaults(level, level_info_only, TRUE);
7448 switch (level_file_info->type)
7450 case LEVEL_FILE_TYPE_RND:
7451 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7454 case LEVEL_FILE_TYPE_BD:
7455 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7456 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7459 case LEVEL_FILE_TYPE_EM:
7460 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7461 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7464 case LEVEL_FILE_TYPE_SP:
7465 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7466 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7469 case LEVEL_FILE_TYPE_MM:
7470 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7471 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7474 case LEVEL_FILE_TYPE_DC:
7475 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7478 case LEVEL_FILE_TYPE_SB:
7479 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7483 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7487 // if level file is invalid, restore level structure to default values
7488 if (level->no_valid_file)
7489 setLevelInfoToDefaults(level, level_info_only, FALSE);
7491 if (check_special_flags("use_native_bd_game_engine"))
7492 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7494 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7495 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7497 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7498 CopyNativeLevel_Native_to_RND(level);
7501 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7503 static struct LevelFileInfo level_file_info;
7505 // always start with reliable default values
7506 setFileInfoToDefaults(&level_file_info);
7508 level_file_info.nr = 0; // unknown level number
7509 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7511 setString(&level_file_info.filename, filename);
7513 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7516 static void LoadLevel_InitVersion(struct LevelInfo *level)
7520 if (leveldir_current == NULL) // only when dumping level
7523 // all engine modifications also valid for levels which use latest engine
7524 if (level->game_version < VERSION_IDENT(3,2,0,5))
7526 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7527 level->time_score_base = 10;
7530 if (leveldir_current->latest_engine)
7532 // ---------- use latest game engine --------------------------------------
7534 /* For all levels which are forced to use the latest game engine version
7535 (normally all but user contributed, private and undefined levels), set
7536 the game engine version to the actual version; this allows for actual
7537 corrections in the game engine to take effect for existing, converted
7538 levels (from "classic" or other existing games) to make the emulation
7539 of the corresponding game more accurate, while (hopefully) not breaking
7540 existing levels created from other players. */
7542 level->game_version = GAME_VERSION_ACTUAL;
7544 /* Set special EM style gems behaviour: EM style gems slip down from
7545 normal, steel and growing wall. As this is a more fundamental change,
7546 it seems better to set the default behaviour to "off" (as it is more
7547 natural) and make it configurable in the level editor (as a property
7548 of gem style elements). Already existing converted levels (neither
7549 private nor contributed levels) are changed to the new behaviour. */
7551 if (level->file_version < FILE_VERSION_2_0)
7552 level->em_slippery_gems = TRUE;
7557 // ---------- use game engine the level was created with --------------------
7559 /* For all levels which are not forced to use the latest game engine
7560 version (normally user contributed, private and undefined levels),
7561 use the version of the game engine the levels were created for.
7563 Since 2.0.1, the game engine version is now directly stored
7564 in the level file (chunk "VERS"), so there is no need anymore
7565 to set the game version from the file version (except for old,
7566 pre-2.0 levels, where the game version is still taken from the
7567 file format version used to store the level -- see above). */
7569 // player was faster than enemies in 1.0.0 and before
7570 if (level->file_version == FILE_VERSION_1_0)
7571 for (i = 0; i < MAX_PLAYERS; i++)
7572 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7574 // default behaviour for EM style gems was "slippery" only in 2.0.1
7575 if (level->game_version == VERSION_IDENT(2,0,1,0))
7576 level->em_slippery_gems = TRUE;
7578 // springs could be pushed over pits before (pre-release version) 2.2.0
7579 if (level->game_version < VERSION_IDENT(2,2,0,0))
7580 level->use_spring_bug = TRUE;
7582 if (level->game_version < VERSION_IDENT(3,2,0,5))
7584 // time orb caused limited time in endless time levels before 3.2.0-5
7585 level->use_time_orb_bug = TRUE;
7587 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7588 level->block_snap_field = FALSE;
7590 // extra time score was same value as time left score before 3.2.0-5
7591 level->extra_time_score = level->score[SC_TIME_BONUS];
7594 if (level->game_version < VERSION_IDENT(3,2,0,7))
7596 // default behaviour for snapping was "not continuous" before 3.2.0-7
7597 level->continuous_snapping = FALSE;
7600 // only few elements were able to actively move into acid before 3.1.0
7601 // trigger settings did not exist before 3.1.0; set to default "any"
7602 if (level->game_version < VERSION_IDENT(3,1,0,0))
7604 // correct "can move into acid" settings (all zero in old levels)
7606 level->can_move_into_acid_bits = 0; // nothing can move into acid
7607 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7609 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7610 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7611 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7612 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7614 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7615 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7617 // correct trigger settings (stored as zero == "none" in old levels)
7619 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7621 int element = EL_CUSTOM_START + i;
7622 struct ElementInfo *ei = &element_info[element];
7624 for (j = 0; j < ei->num_change_pages; j++)
7626 struct ElementChangeInfo *change = &ei->change_page[j];
7628 change->trigger_player = CH_PLAYER_ANY;
7629 change->trigger_page = CH_PAGE_ANY;
7634 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7636 int element = EL_CUSTOM_256;
7637 struct ElementInfo *ei = &element_info[element];
7638 struct ElementChangeInfo *change = &ei->change_page[0];
7640 /* This is needed to fix a problem that was caused by a bugfix in function
7641 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7642 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7643 not replace walkable elements, but instead just placed the player on it,
7644 without placing the Sokoban field under the player). Unfortunately, this
7645 breaks "Snake Bite" style levels when the snake is halfway through a door
7646 that just closes (the snake head is still alive and can be moved in this
7647 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7648 player (without Sokoban element) which then gets killed as designed). */
7650 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7651 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7652 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7653 change->target_element = EL_PLAYER_1;
7656 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7657 if (level->game_version < VERSION_IDENT(3,2,5,0))
7659 /* This is needed to fix a problem that was caused by a bugfix in function
7660 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7661 corrects the behaviour when a custom element changes to another custom
7662 element with a higher element number that has change actions defined.
7663 Normally, only one change per frame is allowed for custom elements.
7664 Therefore, it is checked if a custom element already changed in the
7665 current frame; if it did, subsequent changes are suppressed.
7666 Unfortunately, this is only checked for element changes, but not for
7667 change actions, which are still executed. As the function above loops
7668 through all custom elements from lower to higher, an element change
7669 resulting in a lower CE number won't be checked again, while a target
7670 element with a higher number will also be checked, and potential change
7671 actions will get executed for this CE, too (which is wrong), while
7672 further changes are ignored (which is correct). As this bugfix breaks
7673 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7674 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7675 behaviour for existing levels and tapes that make use of this bug */
7677 level->use_action_after_change_bug = TRUE;
7680 // not centering level after relocating player was default only in 3.2.3
7681 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7682 level->shifted_relocation = TRUE;
7684 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7685 if (level->game_version < VERSION_IDENT(3,2,6,0))
7686 level->em_explodes_by_fire = TRUE;
7688 // levels were solved by the first player entering an exit up to 4.1.0.0
7689 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7690 level->solved_by_one_player = TRUE;
7692 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7693 if (level->game_version < VERSION_IDENT(4,1,1,1))
7694 level->use_life_bugs = TRUE;
7696 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7697 if (level->game_version < VERSION_IDENT(4,1,1,1))
7698 level->sb_objects_needed = FALSE;
7700 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7701 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7702 level->finish_dig_collect = FALSE;
7704 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7705 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7706 level->keep_walkable_ce = TRUE;
7709 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7711 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7714 // check if this level is (not) a Sokoban level
7715 for (y = 0; y < level->fieldy; y++)
7716 for (x = 0; x < level->fieldx; x++)
7717 if (!IS_SB_ELEMENT(Tile[x][y]))
7718 is_sokoban_level = FALSE;
7720 if (is_sokoban_level)
7722 // set special level settings for Sokoban levels
7723 SetLevelSettings_SB(level);
7727 static void LoadLevel_InitSettings(struct LevelInfo *level)
7729 // adjust level settings for (non-native) Sokoban-style levels
7730 LoadLevel_InitSettings_SB(level);
7732 // rename levels with title "nameless level" or if renaming is forced
7733 if (leveldir_current->empty_level_name != NULL &&
7734 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7735 leveldir_current->force_level_name))
7736 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7737 leveldir_current->empty_level_name, level_nr);
7740 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7744 // map elements that have changed in newer versions
7745 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7746 level->game_version);
7747 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7748 for (x = 0; x < 3; x++)
7749 for (y = 0; y < 3; y++)
7750 level->yamyam_content[i].e[x][y] =
7751 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7752 level->game_version);
7756 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7760 // map custom element change events that have changed in newer versions
7761 // (these following values were accidentally changed in version 3.0.1)
7762 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7763 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7765 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7767 int element = EL_CUSTOM_START + i;
7769 // order of checking and copying events to be mapped is important
7770 // (do not change the start and end value -- they are constant)
7771 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7773 if (HAS_CHANGE_EVENT(element, j - 2))
7775 SET_CHANGE_EVENT(element, j - 2, FALSE);
7776 SET_CHANGE_EVENT(element, j, TRUE);
7780 // order of checking and copying events to be mapped is important
7781 // (do not change the start and end value -- they are constant)
7782 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7784 if (HAS_CHANGE_EVENT(element, j - 1))
7786 SET_CHANGE_EVENT(element, j - 1, FALSE);
7787 SET_CHANGE_EVENT(element, j, TRUE);
7793 // initialize "can_change" field for old levels with only one change page
7794 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7796 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7798 int element = EL_CUSTOM_START + i;
7800 if (CAN_CHANGE(element))
7801 element_info[element].change->can_change = TRUE;
7805 // correct custom element values (for old levels without these options)
7806 if (level->game_version < VERSION_IDENT(3,1,1,0))
7808 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7810 int element = EL_CUSTOM_START + i;
7811 struct ElementInfo *ei = &element_info[element];
7813 if (ei->access_direction == MV_NO_DIRECTION)
7814 ei->access_direction = MV_ALL_DIRECTIONS;
7818 // correct custom element values (fix invalid values for all versions)
7821 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7823 int element = EL_CUSTOM_START + i;
7824 struct ElementInfo *ei = &element_info[element];
7826 for (j = 0; j < ei->num_change_pages; j++)
7828 struct ElementChangeInfo *change = &ei->change_page[j];
7830 if (change->trigger_player == CH_PLAYER_NONE)
7831 change->trigger_player = CH_PLAYER_ANY;
7833 if (change->trigger_side == CH_SIDE_NONE)
7834 change->trigger_side = CH_SIDE_ANY;
7839 // initialize "can_explode" field for old levels which did not store this
7840 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7841 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7843 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7845 int element = EL_CUSTOM_START + i;
7847 if (EXPLODES_1X1_OLD(element))
7848 element_info[element].explosion_type = EXPLODES_1X1;
7850 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7851 EXPLODES_SMASHED(element) ||
7852 EXPLODES_IMPACT(element)));
7856 // correct previously hard-coded move delay values for maze runner style
7857 if (level->game_version < VERSION_IDENT(3,1,1,0))
7859 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7861 int element = EL_CUSTOM_START + i;
7863 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7865 // previously hard-coded and therefore ignored
7866 element_info[element].move_delay_fixed = 9;
7867 element_info[element].move_delay_random = 0;
7872 // set some other uninitialized values of custom elements in older levels
7873 if (level->game_version < VERSION_IDENT(3,1,0,0))
7875 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7877 int element = EL_CUSTOM_START + i;
7879 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7881 element_info[element].explosion_delay = 17;
7882 element_info[element].ignition_delay = 8;
7886 // set mouse click change events to work for left/middle/right mouse button
7887 if (level->game_version < VERSION_IDENT(4,2,3,0))
7889 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7891 int element = EL_CUSTOM_START + i;
7892 struct ElementInfo *ei = &element_info[element];
7894 for (j = 0; j < ei->num_change_pages; j++)
7896 struct ElementChangeInfo *change = &ei->change_page[j];
7898 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7899 change->has_event[CE_PRESSED_BY_MOUSE] ||
7900 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7901 change->has_event[CE_MOUSE_PRESSED_ON_X])
7902 change->trigger_side = CH_SIDE_ANY;
7908 static void LoadLevel_InitElements(struct LevelInfo *level)
7910 LoadLevel_InitStandardElements(level);
7912 if (level->file_has_custom_elements)
7913 LoadLevel_InitCustomElements(level);
7915 // initialize element properties for level editor etc.
7916 InitElementPropertiesEngine(level->game_version);
7917 InitElementPropertiesGfxElement();
7920 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7924 // map elements that have changed in newer versions
7925 for (y = 0; y < level->fieldy; y++)
7926 for (x = 0; x < level->fieldx; x++)
7927 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7928 level->game_version);
7930 // clear unused playfield data (nicer if level gets resized in editor)
7931 for (x = 0; x < MAX_LEV_FIELDX; x++)
7932 for (y = 0; y < MAX_LEV_FIELDY; y++)
7933 if (x >= level->fieldx || y >= level->fieldy)
7934 level->field[x][y] = EL_EMPTY;
7936 // copy elements to runtime playfield array
7937 for (x = 0; x < MAX_LEV_FIELDX; x++)
7938 for (y = 0; y < MAX_LEV_FIELDY; y++)
7939 Tile[x][y] = level->field[x][y];
7941 // initialize level size variables for faster access
7942 lev_fieldx = level->fieldx;
7943 lev_fieldy = level->fieldy;
7945 // determine border element for this level
7946 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7947 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7952 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7954 struct LevelFileInfo *level_file_info = &level->file_info;
7956 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7957 CopyNativeLevel_RND_to_Native(level);
7960 static void LoadLevelTemplate_LoadAndInit(void)
7962 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7964 LoadLevel_InitVersion(&level_template);
7965 LoadLevel_InitElements(&level_template);
7966 LoadLevel_InitSettings(&level_template);
7968 ActivateLevelTemplate();
7971 void LoadLevelTemplate(int nr)
7973 if (!fileExists(getGlobalLevelTemplateFilename()))
7975 Warn("no level template found for this level");
7980 setLevelFileInfo(&level_template.file_info, nr);
7982 LoadLevelTemplate_LoadAndInit();
7985 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7987 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7989 LoadLevelTemplate_LoadAndInit();
7992 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7994 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7996 if (level.use_custom_template)
7998 if (network_level != NULL)
7999 LoadNetworkLevelTemplate(network_level);
8001 LoadLevelTemplate(-1);
8004 LoadLevel_InitVersion(&level);
8005 LoadLevel_InitElements(&level);
8006 LoadLevel_InitPlayfield(&level);
8007 LoadLevel_InitSettings(&level);
8009 LoadLevel_InitNativeEngines(&level);
8012 void LoadLevel(int nr)
8014 SetLevelSetInfo(leveldir_current->identifier, nr);
8016 setLevelFileInfo(&level.file_info, nr);
8018 LoadLevel_LoadAndInit(NULL);
8021 void LoadLevelInfoOnly(int nr)
8023 setLevelFileInfo(&level.file_info, nr);
8025 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8028 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8030 SetLevelSetInfo(network_level->leveldir_identifier,
8031 network_level->file_info.nr);
8033 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8035 LoadLevel_LoadAndInit(network_level);
8038 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8042 chunk_size += putFileVersion(file, level->file_version);
8043 chunk_size += putFileVersion(file, level->game_version);
8048 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8052 chunk_size += putFile16BitBE(file, level->creation_date.year);
8053 chunk_size += putFile8Bit(file, level->creation_date.month);
8054 chunk_size += putFile8Bit(file, level->creation_date.day);
8059 #if ENABLE_HISTORIC_CHUNKS
8060 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8064 putFile8Bit(file, level->fieldx);
8065 putFile8Bit(file, level->fieldy);
8067 putFile16BitBE(file, level->time);
8068 putFile16BitBE(file, level->gems_needed);
8070 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8071 putFile8Bit(file, level->name[i]);
8073 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8074 putFile8Bit(file, level->score[i]);
8076 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8077 for (y = 0; y < 3; y++)
8078 for (x = 0; x < 3; x++)
8079 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8080 level->yamyam_content[i].e[x][y]));
8081 putFile8Bit(file, level->amoeba_speed);
8082 putFile8Bit(file, level->time_magic_wall);
8083 putFile8Bit(file, level->time_wheel);
8084 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8085 level->amoeba_content));
8086 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8087 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8088 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8089 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8091 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8093 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8094 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8095 putFile32BitBE(file, level->can_move_into_acid_bits);
8096 putFile8Bit(file, level->dont_collide_with_bits);
8098 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8099 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8101 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8102 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8103 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8105 putFile8Bit(file, level->game_engine_type);
8107 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8111 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8116 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8117 chunk_size += putFile8Bit(file, level->name[i]);
8122 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8127 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8128 chunk_size += putFile8Bit(file, level->author[i]);
8133 #if ENABLE_HISTORIC_CHUNKS
8134 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8139 for (y = 0; y < level->fieldy; y++)
8140 for (x = 0; x < level->fieldx; x++)
8141 if (level->encoding_16bit_field)
8142 chunk_size += putFile16BitBE(file, level->field[x][y]);
8144 chunk_size += putFile8Bit(file, level->field[x][y]);
8150 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8155 for (y = 0; y < level->fieldy; y++)
8156 for (x = 0; x < level->fieldx; x++)
8157 chunk_size += putFile16BitBE(file, level->field[x][y]);
8162 #if ENABLE_HISTORIC_CHUNKS
8163 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8167 putFile8Bit(file, EL_YAMYAM);
8168 putFile8Bit(file, level->num_yamyam_contents);
8169 putFile8Bit(file, 0);
8170 putFile8Bit(file, 0);
8172 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8173 for (y = 0; y < 3; y++)
8174 for (x = 0; x < 3; x++)
8175 if (level->encoding_16bit_field)
8176 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8178 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8182 #if ENABLE_HISTORIC_CHUNKS
8183 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8186 int num_contents, content_xsize, content_ysize;
8187 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8189 if (element == EL_YAMYAM)
8191 num_contents = level->num_yamyam_contents;
8195 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8196 for (y = 0; y < 3; y++)
8197 for (x = 0; x < 3; x++)
8198 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8200 else if (element == EL_BD_AMOEBA)
8206 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8207 for (y = 0; y < 3; y++)
8208 for (x = 0; x < 3; x++)
8209 content_array[i][x][y] = EL_EMPTY;
8210 content_array[0][0][0] = level->amoeba_content;
8214 // chunk header already written -- write empty chunk data
8215 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8217 Warn("cannot save content for element '%d'", element);
8222 putFile16BitBE(file, element);
8223 putFile8Bit(file, num_contents);
8224 putFile8Bit(file, content_xsize);
8225 putFile8Bit(file, content_ysize);
8227 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8229 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8230 for (y = 0; y < 3; y++)
8231 for (x = 0; x < 3; x++)
8232 putFile16BitBE(file, content_array[i][x][y]);
8236 #if ENABLE_HISTORIC_CHUNKS
8237 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8239 int envelope_nr = element - EL_ENVELOPE_1;
8240 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8244 chunk_size += putFile16BitBE(file, element);
8245 chunk_size += putFile16BitBE(file, envelope_len);
8246 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8247 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8249 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8250 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8252 for (i = 0; i < envelope_len; i++)
8253 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8259 #if ENABLE_HISTORIC_CHUNKS
8260 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8261 int num_changed_custom_elements)
8265 putFile16BitBE(file, num_changed_custom_elements);
8267 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8269 int element = EL_CUSTOM_START + i;
8271 struct ElementInfo *ei = &element_info[element];
8273 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8275 if (check < num_changed_custom_elements)
8277 putFile16BitBE(file, element);
8278 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8285 if (check != num_changed_custom_elements) // should not happen
8286 Warn("inconsistent number of custom element properties");
8290 #if ENABLE_HISTORIC_CHUNKS
8291 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8292 int num_changed_custom_elements)
8296 putFile16BitBE(file, num_changed_custom_elements);
8298 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8300 int element = EL_CUSTOM_START + i;
8302 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8304 if (check < num_changed_custom_elements)
8306 putFile16BitBE(file, element);
8307 putFile16BitBE(file, element_info[element].change->target_element);
8314 if (check != num_changed_custom_elements) // should not happen
8315 Warn("inconsistent number of custom target elements");
8319 #if ENABLE_HISTORIC_CHUNKS
8320 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8321 int num_changed_custom_elements)
8323 int i, j, x, y, check = 0;
8325 putFile16BitBE(file, num_changed_custom_elements);
8327 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8329 int element = EL_CUSTOM_START + i;
8330 struct ElementInfo *ei = &element_info[element];
8332 if (ei->modified_settings)
8334 if (check < num_changed_custom_elements)
8336 putFile16BitBE(file, element);
8338 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8339 putFile8Bit(file, ei->description[j]);
8341 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8343 // some free bytes for future properties and padding
8344 WriteUnusedBytesToFile(file, 7);
8346 putFile8Bit(file, ei->use_gfx_element);
8347 putFile16BitBE(file, ei->gfx_element_initial);
8349 putFile8Bit(file, ei->collect_score_initial);
8350 putFile8Bit(file, ei->collect_count_initial);
8352 putFile16BitBE(file, ei->push_delay_fixed);
8353 putFile16BitBE(file, ei->push_delay_random);
8354 putFile16BitBE(file, ei->move_delay_fixed);
8355 putFile16BitBE(file, ei->move_delay_random);
8357 putFile16BitBE(file, ei->move_pattern);
8358 putFile8Bit(file, ei->move_direction_initial);
8359 putFile8Bit(file, ei->move_stepsize);
8361 for (y = 0; y < 3; y++)
8362 for (x = 0; x < 3; x++)
8363 putFile16BitBE(file, ei->content.e[x][y]);
8365 putFile32BitBE(file, ei->change->events);
8367 putFile16BitBE(file, ei->change->target_element);
8369 putFile16BitBE(file, ei->change->delay_fixed);
8370 putFile16BitBE(file, ei->change->delay_random);
8371 putFile16BitBE(file, ei->change->delay_frames);
8373 putFile16BitBE(file, ei->change->initial_trigger_element);
8375 putFile8Bit(file, ei->change->explode);
8376 putFile8Bit(file, ei->change->use_target_content);
8377 putFile8Bit(file, ei->change->only_if_complete);
8378 putFile8Bit(file, ei->change->use_random_replace);
8380 putFile8Bit(file, ei->change->random_percentage);
8381 putFile8Bit(file, ei->change->replace_when);
8383 for (y = 0; y < 3; y++)
8384 for (x = 0; x < 3; x++)
8385 putFile16BitBE(file, ei->change->content.e[x][y]);
8387 putFile8Bit(file, ei->slippery_type);
8389 // some free bytes for future properties and padding
8390 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8397 if (check != num_changed_custom_elements) // should not happen
8398 Warn("inconsistent number of custom element properties");
8402 #if ENABLE_HISTORIC_CHUNKS
8403 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8405 struct ElementInfo *ei = &element_info[element];
8408 // ---------- custom element base property values (96 bytes) ----------------
8410 putFile16BitBE(file, element);
8412 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8413 putFile8Bit(file, ei->description[i]);
8415 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8417 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8419 putFile8Bit(file, ei->num_change_pages);
8421 putFile16BitBE(file, ei->ce_value_fixed_initial);
8422 putFile16BitBE(file, ei->ce_value_random_initial);
8423 putFile8Bit(file, ei->use_last_ce_value);
8425 putFile8Bit(file, ei->use_gfx_element);
8426 putFile16BitBE(file, ei->gfx_element_initial);
8428 putFile8Bit(file, ei->collect_score_initial);
8429 putFile8Bit(file, ei->collect_count_initial);
8431 putFile8Bit(file, ei->drop_delay_fixed);
8432 putFile8Bit(file, ei->push_delay_fixed);
8433 putFile8Bit(file, ei->drop_delay_random);
8434 putFile8Bit(file, ei->push_delay_random);
8435 putFile16BitBE(file, ei->move_delay_fixed);
8436 putFile16BitBE(file, ei->move_delay_random);
8438 // bits 0 - 15 of "move_pattern" ...
8439 putFile16BitBE(file, ei->move_pattern & 0xffff);
8440 putFile8Bit(file, ei->move_direction_initial);
8441 putFile8Bit(file, ei->move_stepsize);
8443 putFile8Bit(file, ei->slippery_type);
8445 for (y = 0; y < 3; y++)
8446 for (x = 0; x < 3; x++)
8447 putFile16BitBE(file, ei->content.e[x][y]);
8449 putFile16BitBE(file, ei->move_enter_element);
8450 putFile16BitBE(file, ei->move_leave_element);
8451 putFile8Bit(file, ei->move_leave_type);
8453 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8454 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8456 putFile8Bit(file, ei->access_direction);
8458 putFile8Bit(file, ei->explosion_delay);
8459 putFile8Bit(file, ei->ignition_delay);
8460 putFile8Bit(file, ei->explosion_type);
8462 // some free bytes for future custom property values and padding
8463 WriteUnusedBytesToFile(file, 1);
8465 // ---------- change page property values (48 bytes) ------------------------
8467 for (i = 0; i < ei->num_change_pages; i++)
8469 struct ElementChangeInfo *change = &ei->change_page[i];
8470 unsigned int event_bits;
8472 // bits 0 - 31 of "has_event[]" ...
8474 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8475 if (change->has_event[j])
8476 event_bits |= (1u << j);
8477 putFile32BitBE(file, event_bits);
8479 putFile16BitBE(file, change->target_element);
8481 putFile16BitBE(file, change->delay_fixed);
8482 putFile16BitBE(file, change->delay_random);
8483 putFile16BitBE(file, change->delay_frames);
8485 putFile16BitBE(file, change->initial_trigger_element);
8487 putFile8Bit(file, change->explode);
8488 putFile8Bit(file, change->use_target_content);
8489 putFile8Bit(file, change->only_if_complete);
8490 putFile8Bit(file, change->use_random_replace);
8492 putFile8Bit(file, change->random_percentage);
8493 putFile8Bit(file, change->replace_when);
8495 for (y = 0; y < 3; y++)
8496 for (x = 0; x < 3; x++)
8497 putFile16BitBE(file, change->target_content.e[x][y]);
8499 putFile8Bit(file, change->can_change);
8501 putFile8Bit(file, change->trigger_side);
8503 putFile8Bit(file, change->trigger_player);
8504 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8505 log_2(change->trigger_page)));
8507 putFile8Bit(file, change->has_action);
8508 putFile8Bit(file, change->action_type);
8509 putFile8Bit(file, change->action_mode);
8510 putFile16BitBE(file, change->action_arg);
8512 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8514 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8515 if (change->has_event[j])
8516 event_bits |= (1u << (j - 32));
8517 putFile8Bit(file, event_bits);
8522 #if ENABLE_HISTORIC_CHUNKS
8523 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8525 struct ElementInfo *ei = &element_info[element];
8526 struct ElementGroupInfo *group = ei->group;
8529 putFile16BitBE(file, element);
8531 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8532 putFile8Bit(file, ei->description[i]);
8534 putFile8Bit(file, group->num_elements);
8536 putFile8Bit(file, ei->use_gfx_element);
8537 putFile16BitBE(file, ei->gfx_element_initial);
8539 putFile8Bit(file, group->choice_mode);
8541 // some free bytes for future values and padding
8542 WriteUnusedBytesToFile(file, 3);
8544 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8545 putFile16BitBE(file, group->element[i]);
8549 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8550 boolean write_element)
8552 int save_type = entry->save_type;
8553 int data_type = entry->data_type;
8554 int conf_type = entry->conf_type;
8555 int byte_mask = conf_type & CONF_MASK_BYTES;
8556 int element = entry->element;
8557 int default_value = entry->default_value;
8559 boolean modified = FALSE;
8561 if (byte_mask != CONF_MASK_MULTI_BYTES)
8563 void *value_ptr = entry->value;
8564 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8567 // check if any settings have been modified before saving them
8568 if (value != default_value)
8571 // do not save if explicitly told or if unmodified default settings
8572 if ((save_type == SAVE_CONF_NEVER) ||
8573 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8577 num_bytes += putFile16BitBE(file, element);
8579 num_bytes += putFile8Bit(file, conf_type);
8580 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8581 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8582 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8585 else if (data_type == TYPE_STRING)
8587 char *default_string = entry->default_string;
8588 char *string = (char *)(entry->value);
8589 int string_length = strlen(string);
8592 // check if any settings have been modified before saving them
8593 if (!strEqual(string, default_string))
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 += putFile16BitBE(file, string_length);
8607 for (i = 0; i < string_length; i++)
8608 num_bytes += putFile8Bit(file, string[i]);
8610 else if (data_type == TYPE_ELEMENT_LIST)
8612 int *element_array = (int *)(entry->value);
8613 int num_elements = *(int *)(entry->num_entities);
8616 // check if any settings have been modified before saving them
8617 for (i = 0; i < num_elements; i++)
8618 if (element_array[i] != default_value)
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, num_elements * CONF_ELEMENT_NUM_BYTES);
8632 for (i = 0; i < num_elements; i++)
8633 num_bytes += putFile16BitBE(file, element_array[i]);
8635 else if (data_type == TYPE_CONTENT_LIST)
8637 struct Content *content = (struct Content *)(entry->value);
8638 int num_contents = *(int *)(entry->num_entities);
8641 // check if any settings have been modified before saving them
8642 for (i = 0; i < num_contents; i++)
8643 for (y = 0; y < 3; y++)
8644 for (x = 0; x < 3; x++)
8645 if (content[i].e[x][y] != default_value)
8648 // do not save if explicitly told or if unmodified default settings
8649 if ((save_type == SAVE_CONF_NEVER) ||
8650 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8654 num_bytes += putFile16BitBE(file, element);
8656 num_bytes += putFile8Bit(file, conf_type);
8657 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8659 for (i = 0; i < num_contents; i++)
8660 for (y = 0; y < 3; y++)
8661 for (x = 0; x < 3; x++)
8662 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8668 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8673 li = *level; // copy level data into temporary buffer
8675 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8676 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8681 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8686 li = *level; // copy level data into temporary buffer
8688 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8689 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8694 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8696 int envelope_nr = element - EL_ENVELOPE_1;
8700 chunk_size += putFile16BitBE(file, element);
8702 // copy envelope data into temporary buffer
8703 xx_envelope = level->envelope[envelope_nr];
8705 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8706 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8711 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8713 struct ElementInfo *ei = &element_info[element];
8717 chunk_size += putFile16BitBE(file, element);
8719 xx_ei = *ei; // copy element data into temporary buffer
8721 // set default description string for this specific element
8722 strcpy(xx_default_description, getDefaultElementDescription(ei));
8724 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8725 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8727 for (i = 0; i < ei->num_change_pages; i++)
8729 struct ElementChangeInfo *change = &ei->change_page[i];
8731 xx_current_change_page = i;
8733 xx_change = *change; // copy change data into temporary buffer
8736 setEventBitsFromEventFlags(change);
8738 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8739 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8746 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8748 struct ElementInfo *ei = &element_info[element];
8749 struct ElementGroupInfo *group = ei->group;
8753 chunk_size += putFile16BitBE(file, element);
8755 xx_ei = *ei; // copy element data into temporary buffer
8756 xx_group = *group; // copy group data into temporary buffer
8758 // set default description string for this specific element
8759 strcpy(xx_default_description, getDefaultElementDescription(ei));
8761 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8762 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8767 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8769 struct ElementInfo *ei = &element_info[element];
8773 chunk_size += putFile16BitBE(file, element);
8775 xx_ei = *ei; // copy element data into temporary buffer
8777 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8778 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8783 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8784 boolean save_as_template)
8790 if (!(file = fopen(filename, MODE_WRITE)))
8792 Warn("cannot save level file '%s'", filename);
8797 level->file_version = FILE_VERSION_ACTUAL;
8798 level->game_version = GAME_VERSION_ACTUAL;
8800 level->creation_date = getCurrentDate();
8802 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8803 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8805 chunk_size = SaveLevel_VERS(NULL, level);
8806 putFileChunkBE(file, "VERS", chunk_size);
8807 SaveLevel_VERS(file, level);
8809 chunk_size = SaveLevel_DATE(NULL, level);
8810 putFileChunkBE(file, "DATE", chunk_size);
8811 SaveLevel_DATE(file, level);
8813 chunk_size = SaveLevel_NAME(NULL, level);
8814 putFileChunkBE(file, "NAME", chunk_size);
8815 SaveLevel_NAME(file, level);
8817 chunk_size = SaveLevel_AUTH(NULL, level);
8818 putFileChunkBE(file, "AUTH", chunk_size);
8819 SaveLevel_AUTH(file, level);
8821 chunk_size = SaveLevel_INFO(NULL, level);
8822 putFileChunkBE(file, "INFO", chunk_size);
8823 SaveLevel_INFO(file, level);
8825 chunk_size = SaveLevel_BODY(NULL, level);
8826 putFileChunkBE(file, "BODY", chunk_size);
8827 SaveLevel_BODY(file, level);
8829 chunk_size = SaveLevel_ELEM(NULL, level);
8830 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8832 putFileChunkBE(file, "ELEM", chunk_size);
8833 SaveLevel_ELEM(file, level);
8836 for (i = 0; i < NUM_ENVELOPES; i++)
8838 int element = EL_ENVELOPE_1 + i;
8840 chunk_size = SaveLevel_NOTE(NULL, level, element);
8841 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8843 putFileChunkBE(file, "NOTE", chunk_size);
8844 SaveLevel_NOTE(file, level, element);
8848 // if not using template level, check for non-default custom/group elements
8849 if (!level->use_custom_template || save_as_template)
8851 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8853 int element = EL_CUSTOM_START + i;
8855 chunk_size = SaveLevel_CUSX(NULL, level, element);
8856 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8858 putFileChunkBE(file, "CUSX", chunk_size);
8859 SaveLevel_CUSX(file, level, element);
8863 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8865 int element = EL_GROUP_START + i;
8867 chunk_size = SaveLevel_GRPX(NULL, level, element);
8868 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8870 putFileChunkBE(file, "GRPX", chunk_size);
8871 SaveLevel_GRPX(file, level, element);
8875 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8877 int element = GET_EMPTY_ELEMENT(i);
8879 chunk_size = SaveLevel_EMPX(NULL, level, element);
8880 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8882 putFileChunkBE(file, "EMPX", chunk_size);
8883 SaveLevel_EMPX(file, level, element);
8890 SetFilePermissions(filename, PERMS_PRIVATE);
8893 void SaveLevel(int nr)
8895 char *filename = getDefaultLevelFilename(nr);
8897 SaveLevelFromFilename(&level, filename, FALSE);
8900 void SaveLevelTemplate(void)
8902 char *filename = getLocalLevelTemplateFilename();
8904 SaveLevelFromFilename(&level, filename, TRUE);
8907 boolean SaveLevelChecked(int nr)
8909 char *filename = getDefaultLevelFilename(nr);
8910 boolean new_level = !fileExists(filename);
8911 boolean level_saved = FALSE;
8913 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8918 Request("Level saved!", REQ_CONFIRM);
8926 void DumpLevel(struct LevelInfo *level)
8928 if (level->no_level_file || level->no_valid_file)
8930 Warn("cannot dump -- no valid level file found");
8936 Print("Level xxx (file version %08d, game version %08d)\n",
8937 level->file_version, level->game_version);
8940 Print("Level author: '%s'\n", level->author);
8941 Print("Level title: '%s'\n", level->name);
8943 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8945 Print("Level time: %d seconds\n", level->time);
8946 Print("Gems needed: %d\n", level->gems_needed);
8948 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8949 Print("Time for wheel: %d seconds\n", level->time_wheel);
8950 Print("Time for light: %d seconds\n", level->time_light);
8951 Print("Time for timegate: %d seconds\n", level->time_timegate);
8953 Print("Amoeba speed: %d\n", level->amoeba_speed);
8956 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8957 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8958 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8959 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8960 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8961 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8967 for (i = 0; i < NUM_ENVELOPES; i++)
8969 char *text = level->envelope[i].text;
8970 int text_len = strlen(text);
8971 boolean has_text = FALSE;
8973 for (j = 0; j < text_len; j++)
8974 if (text[j] != ' ' && text[j] != '\n')
8980 Print("Envelope %d:\n'%s'\n", i + 1, text);
8988 void DumpLevels(void)
8990 static LevelDirTree *dumplevel_leveldir = NULL;
8992 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8993 global.dumplevel_leveldir);
8995 if (dumplevel_leveldir == NULL)
8996 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8998 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8999 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9000 Fail("no such level number: %d", global.dumplevel_level_nr);
9002 leveldir_current = dumplevel_leveldir;
9004 LoadLevel(global.dumplevel_level_nr);
9011 // ============================================================================
9012 // tape file functions
9013 // ============================================================================
9015 static void setTapeInfoToDefaults(void)
9019 // always start with reliable default values (empty tape)
9022 // default values (also for pre-1.2 tapes) with only the first player
9023 tape.player_participates[0] = TRUE;
9024 for (i = 1; i < MAX_PLAYERS; i++)
9025 tape.player_participates[i] = FALSE;
9027 // at least one (default: the first) player participates in every tape
9028 tape.num_participating_players = 1;
9030 tape.property_bits = TAPE_PROPERTY_NONE;
9032 tape.level_nr = level_nr;
9034 tape.changed = FALSE;
9035 tape.solved = FALSE;
9037 tape.recording = FALSE;
9038 tape.playing = FALSE;
9039 tape.pausing = FALSE;
9041 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9042 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9044 tape.no_info_chunk = TRUE;
9045 tape.no_valid_file = FALSE;
9048 static int getTapePosSize(struct TapeInfo *tape)
9050 int tape_pos_size = 0;
9052 if (tape->use_key_actions)
9053 tape_pos_size += tape->num_participating_players;
9055 if (tape->use_mouse_actions)
9056 tape_pos_size += 3; // x and y position and mouse button mask
9058 tape_pos_size += 1; // tape action delay value
9060 return tape_pos_size;
9063 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9065 tape->use_key_actions = FALSE;
9066 tape->use_mouse_actions = FALSE;
9068 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9069 tape->use_key_actions = TRUE;
9071 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9072 tape->use_mouse_actions = TRUE;
9075 static int getTapeActionValue(struct TapeInfo *tape)
9077 return (tape->use_key_actions &&
9078 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9079 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9080 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9081 TAPE_ACTIONS_DEFAULT);
9084 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9086 tape->file_version = getFileVersion(file);
9087 tape->game_version = getFileVersion(file);
9092 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9096 tape->random_seed = getFile32BitBE(file);
9097 tape->date = getFile32BitBE(file);
9098 tape->length = getFile32BitBE(file);
9100 // read header fields that are new since version 1.2
9101 if (tape->file_version >= FILE_VERSION_1_2)
9103 byte store_participating_players = getFile8Bit(file);
9106 // since version 1.2, tapes store which players participate in the tape
9107 tape->num_participating_players = 0;
9108 for (i = 0; i < MAX_PLAYERS; i++)
9110 tape->player_participates[i] = FALSE;
9112 if (store_participating_players & (1 << i))
9114 tape->player_participates[i] = TRUE;
9115 tape->num_participating_players++;
9119 setTapeActionFlags(tape, getFile8Bit(file));
9121 tape->property_bits = getFile8Bit(file);
9122 tape->solved = getFile8Bit(file);
9124 engine_version = getFileVersion(file);
9125 if (engine_version > 0)
9126 tape->engine_version = engine_version;
9128 tape->engine_version = tape->game_version;
9134 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9136 tape->scr_fieldx = getFile8Bit(file);
9137 tape->scr_fieldy = getFile8Bit(file);
9142 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9144 char *level_identifier = NULL;
9145 int level_identifier_size;
9148 tape->no_info_chunk = FALSE;
9150 level_identifier_size = getFile16BitBE(file);
9152 level_identifier = checked_malloc(level_identifier_size);
9154 for (i = 0; i < level_identifier_size; i++)
9155 level_identifier[i] = getFile8Bit(file);
9157 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9158 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9160 checked_free(level_identifier);
9162 tape->level_nr = getFile16BitBE(file);
9164 chunk_size = 2 + level_identifier_size + 2;
9169 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9172 int tape_pos_size = getTapePosSize(tape);
9173 int chunk_size_expected = tape_pos_size * tape->length;
9175 if (chunk_size_expected != chunk_size)
9177 ReadUnusedBytesFromFile(file, chunk_size);
9178 return chunk_size_expected;
9181 for (i = 0; i < tape->length; i++)
9183 if (i >= MAX_TAPE_LEN)
9185 Warn("tape truncated -- size exceeds maximum tape size %d",
9188 // tape too large; read and ignore remaining tape data from this chunk
9189 for (;i < tape->length; i++)
9190 ReadUnusedBytesFromFile(file, tape_pos_size);
9195 if (tape->use_key_actions)
9197 for (j = 0; j < MAX_PLAYERS; j++)
9199 tape->pos[i].action[j] = MV_NONE;
9201 if (tape->player_participates[j])
9202 tape->pos[i].action[j] = getFile8Bit(file);
9206 if (tape->use_mouse_actions)
9208 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9209 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9210 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9213 tape->pos[i].delay = getFile8Bit(file);
9215 if (tape->file_version == FILE_VERSION_1_0)
9217 // eliminate possible diagonal moves in old tapes
9218 // this is only for backward compatibility
9220 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9221 byte action = tape->pos[i].action[0];
9222 int k, num_moves = 0;
9224 for (k = 0; k < 4; k++)
9226 if (action & joy_dir[k])
9228 tape->pos[i + num_moves].action[0] = joy_dir[k];
9230 tape->pos[i + num_moves].delay = 0;
9239 tape->length += num_moves;
9242 else if (tape->file_version < FILE_VERSION_2_0)
9244 // convert pre-2.0 tapes to new tape format
9246 if (tape->pos[i].delay > 1)
9249 tape->pos[i + 1] = tape->pos[i];
9250 tape->pos[i + 1].delay = 1;
9253 for (j = 0; j < MAX_PLAYERS; j++)
9254 tape->pos[i].action[j] = MV_NONE;
9255 tape->pos[i].delay--;
9262 if (checkEndOfFile(file))
9266 if (i != tape->length)
9267 chunk_size = tape_pos_size * i;
9272 static void LoadTape_SokobanSolution(char *filename)
9275 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9277 if (!(file = openFile(filename, MODE_READ)))
9279 tape.no_valid_file = TRUE;
9284 while (!checkEndOfFile(file))
9286 unsigned char c = getByteFromFile(file);
9288 if (checkEndOfFile(file))
9295 tape.pos[tape.length].action[0] = MV_UP;
9296 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9302 tape.pos[tape.length].action[0] = MV_DOWN;
9303 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9309 tape.pos[tape.length].action[0] = MV_LEFT;
9310 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9316 tape.pos[tape.length].action[0] = MV_RIGHT;
9317 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9325 // ignore white-space characters
9329 tape.no_valid_file = TRUE;
9331 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9339 if (tape.no_valid_file)
9342 tape.length_frames = GetTapeLengthFrames();
9343 tape.length_seconds = GetTapeLengthSeconds();
9346 void LoadTapeFromFilename(char *filename)
9348 char cookie[MAX_LINE_LEN];
9349 char chunk_name[CHUNK_ID_LEN + 1];
9353 // always start with reliable default values
9354 setTapeInfoToDefaults();
9356 if (strSuffix(filename, ".sln"))
9358 LoadTape_SokobanSolution(filename);
9363 if (!(file = openFile(filename, MODE_READ)))
9365 tape.no_valid_file = TRUE;
9370 getFileChunkBE(file, chunk_name, NULL);
9371 if (strEqual(chunk_name, "RND1"))
9373 getFile32BitBE(file); // not used
9375 getFileChunkBE(file, chunk_name, NULL);
9376 if (!strEqual(chunk_name, "TAPE"))
9378 tape.no_valid_file = TRUE;
9380 Warn("unknown format of tape file '%s'", filename);
9387 else // check for pre-2.0 file format with cookie string
9389 strcpy(cookie, chunk_name);
9390 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9392 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9393 cookie[strlen(cookie) - 1] = '\0';
9395 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9397 tape.no_valid_file = TRUE;
9399 Warn("unknown format of tape file '%s'", filename);
9406 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9408 tape.no_valid_file = TRUE;
9410 Warn("unsupported version of tape file '%s'", filename);
9417 // pre-2.0 tape files have no game version, so use file version here
9418 tape.game_version = tape.file_version;
9421 if (tape.file_version < FILE_VERSION_1_2)
9423 // tape files from versions before 1.2.0 without chunk structure
9424 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9425 LoadTape_BODY(file, 2 * tape.length, &tape);
9433 int (*loader)(File *, int, struct TapeInfo *);
9437 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9438 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9439 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9440 { "INFO", -1, LoadTape_INFO },
9441 { "BODY", -1, LoadTape_BODY },
9445 while (getFileChunkBE(file, chunk_name, &chunk_size))
9449 while (chunk_info[i].name != NULL &&
9450 !strEqual(chunk_name, chunk_info[i].name))
9453 if (chunk_info[i].name == NULL)
9455 Warn("unknown chunk '%s' in tape file '%s'",
9456 chunk_name, filename);
9458 ReadUnusedBytesFromFile(file, chunk_size);
9460 else if (chunk_info[i].size != -1 &&
9461 chunk_info[i].size != chunk_size)
9463 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9464 chunk_size, chunk_name, filename);
9466 ReadUnusedBytesFromFile(file, chunk_size);
9470 // call function to load this tape chunk
9471 int chunk_size_expected =
9472 (chunk_info[i].loader)(file, chunk_size, &tape);
9474 // the size of some chunks cannot be checked before reading other
9475 // chunks first (like "HEAD" and "BODY") that contain some header
9476 // information, so check them here
9477 if (chunk_size_expected != chunk_size)
9479 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9480 chunk_size, chunk_name, filename);
9488 tape.length_frames = GetTapeLengthFrames();
9489 tape.length_seconds = GetTapeLengthSeconds();
9492 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9494 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9496 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9497 tape.engine_version);
9501 void LoadTape(int nr)
9503 char *filename = getTapeFilename(nr);
9505 LoadTapeFromFilename(filename);
9508 void LoadSolutionTape(int nr)
9510 char *filename = getSolutionTapeFilename(nr);
9512 LoadTapeFromFilename(filename);
9514 if (TAPE_IS_EMPTY(tape))
9516 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9517 level.native_bd_level->replay != NULL)
9518 CopyNativeTape_BD_to_RND(&level);
9519 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9520 level.native_sp_level->demo.is_available)
9521 CopyNativeTape_SP_to_RND(&level);
9525 void LoadScoreTape(char *score_tape_basename, int nr)
9527 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9529 LoadTapeFromFilename(filename);
9532 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9534 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9536 LoadTapeFromFilename(filename);
9539 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9541 // chunk required for team mode tapes with non-default screen size
9542 return (tape->num_participating_players > 1 &&
9543 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9544 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9547 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9549 putFileVersion(file, tape->file_version);
9550 putFileVersion(file, tape->game_version);
9553 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9556 byte store_participating_players = 0;
9558 // set bits for participating players for compact storage
9559 for (i = 0; i < MAX_PLAYERS; i++)
9560 if (tape->player_participates[i])
9561 store_participating_players |= (1 << i);
9563 putFile32BitBE(file, tape->random_seed);
9564 putFile32BitBE(file, tape->date);
9565 putFile32BitBE(file, tape->length);
9567 putFile8Bit(file, store_participating_players);
9569 putFile8Bit(file, getTapeActionValue(tape));
9571 putFile8Bit(file, tape->property_bits);
9572 putFile8Bit(file, tape->solved);
9574 putFileVersion(file, tape->engine_version);
9577 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9579 putFile8Bit(file, tape->scr_fieldx);
9580 putFile8Bit(file, tape->scr_fieldy);
9583 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9585 int level_identifier_size = strlen(tape->level_identifier) + 1;
9588 putFile16BitBE(file, level_identifier_size);
9590 for (i = 0; i < level_identifier_size; i++)
9591 putFile8Bit(file, tape->level_identifier[i]);
9593 putFile16BitBE(file, tape->level_nr);
9596 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9600 for (i = 0; i < tape->length; i++)
9602 if (tape->use_key_actions)
9604 for (j = 0; j < MAX_PLAYERS; j++)
9605 if (tape->player_participates[j])
9606 putFile8Bit(file, tape->pos[i].action[j]);
9609 if (tape->use_mouse_actions)
9611 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9612 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9613 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9616 putFile8Bit(file, tape->pos[i].delay);
9620 void SaveTapeToFilename(char *filename)
9624 int info_chunk_size;
9625 int body_chunk_size;
9627 if (!(file = fopen(filename, MODE_WRITE)))
9629 Warn("cannot save level recording file '%s'", filename);
9634 tape_pos_size = getTapePosSize(&tape);
9636 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9637 body_chunk_size = tape_pos_size * tape.length;
9639 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9640 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9642 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9643 SaveTape_VERS(file, &tape);
9645 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9646 SaveTape_HEAD(file, &tape);
9648 if (checkSaveTape_SCRN(&tape))
9650 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9651 SaveTape_SCRN(file, &tape);
9654 putFileChunkBE(file, "INFO", info_chunk_size);
9655 SaveTape_INFO(file, &tape);
9657 putFileChunkBE(file, "BODY", body_chunk_size);
9658 SaveTape_BODY(file, &tape);
9662 SetFilePermissions(filename, PERMS_PRIVATE);
9665 static void SaveTapeExt(char *filename)
9669 tape.file_version = FILE_VERSION_ACTUAL;
9670 tape.game_version = GAME_VERSION_ACTUAL;
9672 tape.num_participating_players = 0;
9674 // count number of participating players
9675 for (i = 0; i < MAX_PLAYERS; i++)
9676 if (tape.player_participates[i])
9677 tape.num_participating_players++;
9679 SaveTapeToFilename(filename);
9681 tape.changed = FALSE;
9684 void SaveTape(int nr)
9686 char *filename = getTapeFilename(nr);
9688 InitTapeDirectory(leveldir_current->subdir);
9690 SaveTapeExt(filename);
9693 void SaveScoreTape(int nr)
9695 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9697 // used instead of "leveldir_current->subdir" (for network games)
9698 InitScoreTapeDirectory(levelset.identifier, nr);
9700 SaveTapeExt(filename);
9703 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9704 unsigned int req_state_added)
9706 char *filename = getTapeFilename(nr);
9707 boolean new_tape = !fileExists(filename);
9708 boolean tape_saved = FALSE;
9710 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9715 Request(msg_saved, REQ_CONFIRM | req_state_added);
9723 boolean SaveTapeChecked(int nr)
9725 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9728 boolean SaveTapeChecked_LevelSolved(int nr)
9730 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9731 "Level solved! Tape saved!", REQ_STAY_OPEN);
9734 void DumpTape(struct TapeInfo *tape)
9736 int tape_frame_counter;
9739 if (tape->no_valid_file)
9741 Warn("cannot dump -- no valid tape file found");
9748 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9749 tape->level_nr, tape->file_version, tape->game_version);
9750 Print(" (effective engine version %08d)\n",
9751 tape->engine_version);
9752 Print("Level series identifier: '%s'\n", tape->level_identifier);
9754 Print("Solution tape: %s\n",
9755 tape->solved ? "yes" :
9756 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9758 Print("Special tape properties: ");
9759 if (tape->property_bits == TAPE_PROPERTY_NONE)
9761 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9762 Print("[em_random_bug]");
9763 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9764 Print("[game_speed]");
9765 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9767 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9768 Print("[single_step]");
9769 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9770 Print("[snapshot]");
9771 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9772 Print("[replayed]");
9773 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9774 Print("[tas_keys]");
9775 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9776 Print("[small_graphics]");
9779 int year2 = tape->date / 10000;
9780 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9781 int month_index_raw = (tape->date / 100) % 100;
9782 int month_index = month_index_raw % 12; // prevent invalid index
9783 int month = month_index + 1;
9784 int day = tape->date % 100;
9786 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9790 tape_frame_counter = 0;
9792 for (i = 0; i < tape->length; i++)
9794 if (i >= MAX_TAPE_LEN)
9799 for (j = 0; j < MAX_PLAYERS; j++)
9801 if (tape->player_participates[j])
9803 int action = tape->pos[i].action[j];
9805 Print("%d:%02x ", j, action);
9806 Print("[%c%c%c%c|%c%c] - ",
9807 (action & JOY_LEFT ? '<' : ' '),
9808 (action & JOY_RIGHT ? '>' : ' '),
9809 (action & JOY_UP ? '^' : ' '),
9810 (action & JOY_DOWN ? 'v' : ' '),
9811 (action & JOY_BUTTON_1 ? '1' : ' '),
9812 (action & JOY_BUTTON_2 ? '2' : ' '));
9816 Print("(%03d) ", tape->pos[i].delay);
9817 Print("[%05d]\n", tape_frame_counter);
9819 tape_frame_counter += tape->pos[i].delay;
9825 void DumpTapes(void)
9827 static LevelDirTree *dumptape_leveldir = NULL;
9829 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9830 global.dumptape_leveldir);
9832 if (dumptape_leveldir == NULL)
9833 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9835 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9836 global.dumptape_level_nr > dumptape_leveldir->last_level)
9837 Fail("no such level number: %d", global.dumptape_level_nr);
9839 leveldir_current = dumptape_leveldir;
9841 if (options.mytapes)
9842 LoadTape(global.dumptape_level_nr);
9844 LoadSolutionTape(global.dumptape_level_nr);
9852 // ============================================================================
9853 // score file functions
9854 // ============================================================================
9856 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9860 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9862 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9863 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9864 scores->entry[i].score = 0;
9865 scores->entry[i].time = 0;
9867 scores->entry[i].id = -1;
9868 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9869 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9870 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9871 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9872 strcpy(scores->entry[i].country_code, "??");
9875 scores->num_entries = 0;
9876 scores->last_added = -1;
9877 scores->last_added_local = -1;
9879 scores->updated = FALSE;
9880 scores->uploaded = FALSE;
9881 scores->tape_downloaded = FALSE;
9882 scores->force_last_added = FALSE;
9884 // The following values are intentionally not reset here:
9888 // - continue_playing
9889 // - continue_on_return
9892 static void setScoreInfoToDefaults(void)
9894 setScoreInfoToDefaultsExt(&scores);
9897 static void setServerScoreInfoToDefaults(void)
9899 setScoreInfoToDefaultsExt(&server_scores);
9902 static void LoadScore_OLD(int nr)
9905 char *filename = getScoreFilename(nr);
9906 char cookie[MAX_LINE_LEN];
9907 char line[MAX_LINE_LEN];
9911 if (!(file = fopen(filename, MODE_READ)))
9914 // check file identifier
9915 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9917 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9918 cookie[strlen(cookie) - 1] = '\0';
9920 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9922 Warn("unknown format of score file '%s'", filename);
9929 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9931 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9932 Warn("fscanf() failed; %s", strerror(errno));
9934 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9937 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9938 line[strlen(line) - 1] = '\0';
9940 for (line_ptr = line; *line_ptr; line_ptr++)
9942 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9944 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9945 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9954 static void ConvertScore_OLD(void)
9956 // only convert score to time for levels that rate playing time over score
9957 if (!level.rate_time_over_score)
9960 // convert old score to playing time for score-less levels (like Supaplex)
9961 int time_final_max = 999;
9964 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9966 int score = scores.entry[i].score;
9968 if (score > 0 && score < time_final_max)
9969 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9973 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9975 scores->file_version = getFileVersion(file);
9976 scores->game_version = getFileVersion(file);
9981 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9983 char *level_identifier = NULL;
9984 int level_identifier_size;
9987 level_identifier_size = getFile16BitBE(file);
9989 level_identifier = checked_malloc(level_identifier_size);
9991 for (i = 0; i < level_identifier_size; i++)
9992 level_identifier[i] = getFile8Bit(file);
9994 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9995 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9997 checked_free(level_identifier);
9999 scores->level_nr = getFile16BitBE(file);
10000 scores->num_entries = getFile16BitBE(file);
10002 chunk_size = 2 + level_identifier_size + 2 + 2;
10007 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10011 for (i = 0; i < scores->num_entries; i++)
10013 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10014 scores->entry[i].name[j] = getFile8Bit(file);
10016 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10019 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10024 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10028 for (i = 0; i < scores->num_entries; i++)
10029 scores->entry[i].score = getFile16BitBE(file);
10031 chunk_size = scores->num_entries * 2;
10036 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10040 for (i = 0; i < scores->num_entries; i++)
10041 scores->entry[i].score = getFile32BitBE(file);
10043 chunk_size = scores->num_entries * 4;
10048 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10052 for (i = 0; i < scores->num_entries; i++)
10053 scores->entry[i].time = getFile32BitBE(file);
10055 chunk_size = scores->num_entries * 4;
10060 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10064 for (i = 0; i < scores->num_entries; i++)
10066 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10067 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10069 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10072 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10077 void LoadScore(int nr)
10079 char *filename = getScoreFilename(nr);
10080 char cookie[MAX_LINE_LEN];
10081 char chunk_name[CHUNK_ID_LEN + 1];
10083 boolean old_score_file_format = FALSE;
10086 // always start with reliable default values
10087 setScoreInfoToDefaults();
10089 if (!(file = openFile(filename, MODE_READ)))
10092 getFileChunkBE(file, chunk_name, NULL);
10093 if (strEqual(chunk_name, "RND1"))
10095 getFile32BitBE(file); // not used
10097 getFileChunkBE(file, chunk_name, NULL);
10098 if (!strEqual(chunk_name, "SCOR"))
10100 Warn("unknown format of score file '%s'", filename);
10107 else // check for old file format with cookie string
10109 strcpy(cookie, chunk_name);
10110 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10112 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10113 cookie[strlen(cookie) - 1] = '\0';
10115 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10117 Warn("unknown format of score file '%s'", filename);
10124 old_score_file_format = TRUE;
10127 if (old_score_file_format)
10129 // score files from versions before 4.2.4.0 without chunk structure
10132 // convert score to time, if possible (mainly for Supaplex levels)
10133 ConvertScore_OLD();
10141 int (*loader)(File *, int, struct ScoreInfo *);
10145 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10146 { "INFO", -1, LoadScore_INFO },
10147 { "NAME", -1, LoadScore_NAME },
10148 { "SCOR", -1, LoadScore_SCOR },
10149 { "SC4R", -1, LoadScore_SC4R },
10150 { "TIME", -1, LoadScore_TIME },
10151 { "TAPE", -1, LoadScore_TAPE },
10156 while (getFileChunkBE(file, chunk_name, &chunk_size))
10160 while (chunk_info[i].name != NULL &&
10161 !strEqual(chunk_name, chunk_info[i].name))
10164 if (chunk_info[i].name == NULL)
10166 Warn("unknown chunk '%s' in score file '%s'",
10167 chunk_name, filename);
10169 ReadUnusedBytesFromFile(file, chunk_size);
10171 else if (chunk_info[i].size != -1 &&
10172 chunk_info[i].size != chunk_size)
10174 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10175 chunk_size, chunk_name, filename);
10177 ReadUnusedBytesFromFile(file, chunk_size);
10181 // call function to load this score chunk
10182 int chunk_size_expected =
10183 (chunk_info[i].loader)(file, chunk_size, &scores);
10185 // the size of some chunks cannot be checked before reading other
10186 // chunks first (like "HEAD" and "BODY") that contain some header
10187 // information, so check them here
10188 if (chunk_size_expected != chunk_size)
10190 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10191 chunk_size, chunk_name, filename);
10200 #if ENABLE_HISTORIC_CHUNKS
10201 void SaveScore_OLD(int nr)
10204 char *filename = getScoreFilename(nr);
10207 // used instead of "leveldir_current->subdir" (for network games)
10208 InitScoreDirectory(levelset.identifier);
10210 if (!(file = fopen(filename, MODE_WRITE)))
10212 Warn("cannot save score for level %d", nr);
10217 fprintf(file, "%s\n\n", SCORE_COOKIE);
10219 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10220 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10224 SetFilePermissions(filename, PERMS_PRIVATE);
10228 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10230 putFileVersion(file, scores->file_version);
10231 putFileVersion(file, scores->game_version);
10234 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10236 int level_identifier_size = strlen(scores->level_identifier) + 1;
10239 putFile16BitBE(file, level_identifier_size);
10241 for (i = 0; i < level_identifier_size; i++)
10242 putFile8Bit(file, scores->level_identifier[i]);
10244 putFile16BitBE(file, scores->level_nr);
10245 putFile16BitBE(file, scores->num_entries);
10248 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10252 for (i = 0; i < scores->num_entries; i++)
10254 int name_size = strlen(scores->entry[i].name);
10256 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10257 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10261 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10265 for (i = 0; i < scores->num_entries; i++)
10266 putFile16BitBE(file, scores->entry[i].score);
10269 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10273 for (i = 0; i < scores->num_entries; i++)
10274 putFile32BitBE(file, scores->entry[i].score);
10277 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10281 for (i = 0; i < scores->num_entries; i++)
10282 putFile32BitBE(file, scores->entry[i].time);
10285 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10289 for (i = 0; i < scores->num_entries; i++)
10291 int size = strlen(scores->entry[i].tape_basename);
10293 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10294 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10298 static void SaveScoreToFilename(char *filename)
10301 int info_chunk_size;
10302 int name_chunk_size;
10303 int scor_chunk_size;
10304 int sc4r_chunk_size;
10305 int time_chunk_size;
10306 int tape_chunk_size;
10307 boolean has_large_score_values;
10310 if (!(file = fopen(filename, MODE_WRITE)))
10312 Warn("cannot save score file '%s'", filename);
10317 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10318 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10319 scor_chunk_size = scores.num_entries * 2;
10320 sc4r_chunk_size = scores.num_entries * 4;
10321 time_chunk_size = scores.num_entries * 4;
10322 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10324 has_large_score_values = FALSE;
10325 for (i = 0; i < scores.num_entries; i++)
10326 if (scores.entry[i].score > 0xffff)
10327 has_large_score_values = TRUE;
10329 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10330 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10332 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10333 SaveScore_VERS(file, &scores);
10335 putFileChunkBE(file, "INFO", info_chunk_size);
10336 SaveScore_INFO(file, &scores);
10338 putFileChunkBE(file, "NAME", name_chunk_size);
10339 SaveScore_NAME(file, &scores);
10341 if (has_large_score_values)
10343 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10344 SaveScore_SC4R(file, &scores);
10348 putFileChunkBE(file, "SCOR", scor_chunk_size);
10349 SaveScore_SCOR(file, &scores);
10352 putFileChunkBE(file, "TIME", time_chunk_size);
10353 SaveScore_TIME(file, &scores);
10355 putFileChunkBE(file, "TAPE", tape_chunk_size);
10356 SaveScore_TAPE(file, &scores);
10360 SetFilePermissions(filename, PERMS_PRIVATE);
10363 void SaveScore(int nr)
10365 char *filename = getScoreFilename(nr);
10368 // used instead of "leveldir_current->subdir" (for network games)
10369 InitScoreDirectory(levelset.identifier);
10371 scores.file_version = FILE_VERSION_ACTUAL;
10372 scores.game_version = GAME_VERSION_ACTUAL;
10374 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10375 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10376 scores.level_nr = level_nr;
10378 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10379 if (scores.entry[i].score == 0 &&
10380 scores.entry[i].time == 0 &&
10381 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10384 scores.num_entries = i;
10386 if (scores.num_entries == 0)
10389 SaveScoreToFilename(filename);
10392 static void LoadServerScoreFromCache(int nr)
10394 struct ScoreEntry score_entry;
10403 { &score_entry.score, FALSE, 0 },
10404 { &score_entry.time, FALSE, 0 },
10405 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10406 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10407 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10408 { &score_entry.id, FALSE, 0 },
10409 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10410 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10411 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10412 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10416 char *filename = getScoreCacheFilename(nr);
10417 SetupFileHash *score_hash = loadSetupFileHash(filename);
10420 server_scores.num_entries = 0;
10422 if (score_hash == NULL)
10425 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10427 score_entry = server_scores.entry[i];
10429 for (j = 0; score_mapping[j].value != NULL; j++)
10433 sprintf(token, "%02d.%d", i, j);
10435 char *value = getHashEntry(score_hash, token);
10440 if (score_mapping[j].is_string)
10442 char *score_value = (char *)score_mapping[j].value;
10443 int value_size = score_mapping[j].string_size;
10445 strncpy(score_value, value, value_size);
10446 score_value[value_size] = '\0';
10450 int *score_value = (int *)score_mapping[j].value;
10452 *score_value = atoi(value);
10455 server_scores.num_entries = i + 1;
10458 server_scores.entry[i] = score_entry;
10461 freeSetupFileHash(score_hash);
10464 void LoadServerScore(int nr, boolean download_score)
10466 if (!setup.use_api_server)
10469 // always start with reliable default values
10470 setServerScoreInfoToDefaults();
10472 // 1st step: load server scores from cache file (which may not exist)
10473 // (this should prevent reading it while the thread is writing to it)
10474 LoadServerScoreFromCache(nr);
10476 if (download_score && runtime.use_api_server)
10478 // 2nd step: download server scores from score server to cache file
10479 // (as thread, as it might time out if the server is not reachable)
10480 ApiGetScoreAsThread(nr);
10484 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10486 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10488 // if score tape not uploaded, ask for uploading missing tapes later
10489 if (!setup.has_remaining_tapes)
10490 setup.ask_for_remaining_tapes = TRUE;
10492 setup.provide_uploading_tapes = TRUE;
10493 setup.has_remaining_tapes = TRUE;
10495 SaveSetup_ServerSetup();
10498 void SaveServerScore(int nr, boolean tape_saved)
10500 if (!runtime.use_api_server)
10502 PrepareScoreTapesForUpload(leveldir_current->subdir);
10507 ApiAddScoreAsThread(nr, tape_saved, NULL);
10510 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10511 char *score_tape_filename)
10513 if (!runtime.use_api_server)
10516 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10519 void LoadLocalAndServerScore(int nr, boolean download_score)
10521 int last_added_local = scores.last_added_local;
10522 boolean force_last_added = scores.force_last_added;
10524 // needed if only showing server scores
10525 setScoreInfoToDefaults();
10527 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10530 // restore last added local score entry (before merging server scores)
10531 scores.last_added = scores.last_added_local = last_added_local;
10533 if (setup.use_api_server &&
10534 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10536 // load server scores from cache file and trigger update from server
10537 LoadServerScore(nr, download_score);
10539 // merge local scores with scores from server
10540 MergeServerScore();
10543 if (force_last_added)
10544 scores.force_last_added = force_last_added;
10548 // ============================================================================
10549 // setup file functions
10550 // ============================================================================
10552 #define TOKEN_STR_PLAYER_PREFIX "player_"
10555 static struct TokenInfo global_setup_tokens[] =
10559 &setup.player_name, "player_name"
10563 &setup.multiple_users, "multiple_users"
10567 &setup.sound, "sound"
10571 &setup.sound_loops, "repeating_sound_loops"
10575 &setup.sound_music, "background_music"
10579 &setup.sound_simple, "simple_sound_effects"
10583 &setup.toons, "toons"
10587 &setup.global_animations, "global_animations"
10591 &setup.scroll_delay, "scroll_delay"
10595 &setup.forced_scroll_delay, "forced_scroll_delay"
10599 &setup.scroll_delay_value, "scroll_delay_value"
10603 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10607 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10611 &setup.fade_screens, "fade_screens"
10615 &setup.autorecord, "automatic_tape_recording"
10619 &setup.autorecord_after_replay, "autorecord_after_replay"
10623 &setup.auto_pause_on_start, "auto_pause_on_start"
10627 &setup.show_titlescreen, "show_titlescreen"
10631 &setup.quick_doors, "quick_doors"
10635 &setup.team_mode, "team_mode"
10639 &setup.handicap, "handicap"
10643 &setup.skip_levels, "skip_levels"
10647 &setup.increment_levels, "increment_levels"
10651 &setup.auto_play_next_level, "auto_play_next_level"
10655 &setup.count_score_after_game, "count_score_after_game"
10659 &setup.show_scores_after_game, "show_scores_after_game"
10663 &setup.time_limit, "time_limit"
10667 &setup.fullscreen, "fullscreen"
10671 &setup.window_scaling_percent, "window_scaling_percent"
10675 &setup.window_scaling_quality, "window_scaling_quality"
10679 &setup.screen_rendering_mode, "screen_rendering_mode"
10683 &setup.vsync_mode, "vsync_mode"
10687 &setup.ask_on_escape, "ask_on_escape"
10691 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10695 &setup.ask_on_game_over, "ask_on_game_over"
10699 &setup.ask_on_quit_game, "ask_on_quit_game"
10703 &setup.ask_on_quit_program, "ask_on_quit_program"
10707 &setup.quick_switch, "quick_player_switch"
10711 &setup.input_on_focus, "input_on_focus"
10715 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10719 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10723 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10727 &setup.game_speed_extended, "game_speed_extended"
10731 &setup.game_frame_delay, "game_frame_delay"
10735 &setup.default_game_engine_type, "default_game_engine_type"
10739 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10743 &setup.bd_skip_hatching, "bd_skip_hatching"
10747 &setup.bd_scroll_delay, "bd_scroll_delay"
10751 &setup.bd_smooth_movements, "bd_smooth_movements"
10755 &setup.bd_palette_c64, "bd_palette_c64"
10759 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10763 &setup.bd_palette_atari, "bd_palette_atari"
10767 &setup.bd_default_color_type, "bd_default_color_type"
10771 &setup.sp_show_border_elements, "sp_show_border_elements"
10775 &setup.small_game_graphics, "small_game_graphics"
10779 &setup.show_load_save_buttons, "show_load_save_buttons"
10783 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10787 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10791 &setup.graphics_set, "graphics_set"
10795 &setup.sounds_set, "sounds_set"
10799 &setup.music_set, "music_set"
10803 &setup.override_level_graphics, "override_level_graphics"
10807 &setup.override_level_sounds, "override_level_sounds"
10811 &setup.override_level_music, "override_level_music"
10815 &setup.volume_simple, "volume_simple"
10819 &setup.volume_loops, "volume_loops"
10823 &setup.volume_music, "volume_music"
10827 &setup.network_mode, "network_mode"
10831 &setup.network_player_nr, "network_player"
10835 &setup.network_server_hostname, "network_server_hostname"
10839 &setup.touch.control_type, "touch.control_type"
10843 &setup.touch.move_distance, "touch.move_distance"
10847 &setup.touch.drop_distance, "touch.drop_distance"
10851 &setup.touch.transparency, "touch.transparency"
10855 &setup.touch.draw_outlined, "touch.draw_outlined"
10859 &setup.touch.draw_pressed, "touch.draw_pressed"
10863 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10867 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10871 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10875 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10879 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10883 static struct TokenInfo auto_setup_tokens[] =
10887 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10891 static struct TokenInfo server_setup_tokens[] =
10895 &setup.player_uuid, "player_uuid"
10899 &setup.player_version, "player_version"
10903 &setup.use_api_server, TEST_PREFIX "use_api_server"
10907 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10911 &setup.api_server_password, TEST_PREFIX "api_server_password"
10915 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10919 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10923 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10927 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10931 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10935 static struct TokenInfo editor_setup_tokens[] =
10939 &setup.editor.el_classic, "editor.el_classic"
10943 &setup.editor.el_custom, "editor.el_custom"
10947 &setup.editor.el_user_defined, "editor.el_user_defined"
10951 &setup.editor.el_dynamic, "editor.el_dynamic"
10955 &setup.editor.el_headlines, "editor.el_headlines"
10959 &setup.editor.show_element_token, "editor.show_element_token"
10963 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10967 static struct TokenInfo editor_cascade_setup_tokens[] =
10971 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10975 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10979 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10983 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10987 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10991 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10995 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10999 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11003 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11007 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11011 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11015 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11019 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11023 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11027 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11031 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11035 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11039 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11043 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11047 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11051 static struct TokenInfo shortcut_setup_tokens[] =
11055 &setup.shortcut.save_game, "shortcut.save_game"
11059 &setup.shortcut.load_game, "shortcut.load_game"
11063 &setup.shortcut.restart_game, "shortcut.restart_game"
11067 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11071 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11075 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11079 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11083 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11087 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11091 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11095 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11099 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11103 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11107 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11111 &setup.shortcut.tape_record, "shortcut.tape_record"
11115 &setup.shortcut.tape_play, "shortcut.tape_play"
11119 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11123 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11127 &setup.shortcut.sound_music, "shortcut.sound_music"
11131 &setup.shortcut.snap_left, "shortcut.snap_left"
11135 &setup.shortcut.snap_right, "shortcut.snap_right"
11139 &setup.shortcut.snap_up, "shortcut.snap_up"
11143 &setup.shortcut.snap_down, "shortcut.snap_down"
11147 static struct SetupInputInfo setup_input;
11148 static struct TokenInfo player_setup_tokens[] =
11152 &setup_input.use_joystick, ".use_joystick"
11156 &setup_input.joy.device_name, ".joy.device_name"
11160 &setup_input.joy.xleft, ".joy.xleft"
11164 &setup_input.joy.xmiddle, ".joy.xmiddle"
11168 &setup_input.joy.xright, ".joy.xright"
11172 &setup_input.joy.yupper, ".joy.yupper"
11176 &setup_input.joy.ymiddle, ".joy.ymiddle"
11180 &setup_input.joy.ylower, ".joy.ylower"
11184 &setup_input.joy.snap, ".joy.snap_field"
11188 &setup_input.joy.drop, ".joy.place_bomb"
11192 &setup_input.key.left, ".key.move_left"
11196 &setup_input.key.right, ".key.move_right"
11200 &setup_input.key.up, ".key.move_up"
11204 &setup_input.key.down, ".key.move_down"
11208 &setup_input.key.snap, ".key.snap_field"
11212 &setup_input.key.drop, ".key.place_bomb"
11216 static struct TokenInfo system_setup_tokens[] =
11220 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11224 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11228 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11232 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11236 static struct TokenInfo internal_setup_tokens[] =
11240 &setup.internal.program_title, "program_title"
11244 &setup.internal.program_version, "program_version"
11248 &setup.internal.program_author, "program_author"
11252 &setup.internal.program_email, "program_email"
11256 &setup.internal.program_website, "program_website"
11260 &setup.internal.program_copyright, "program_copyright"
11264 &setup.internal.program_company, "program_company"
11268 &setup.internal.program_icon_file, "program_icon_file"
11272 &setup.internal.default_graphics_set, "default_graphics_set"
11276 &setup.internal.default_sounds_set, "default_sounds_set"
11280 &setup.internal.default_music_set, "default_music_set"
11284 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11288 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11292 &setup.internal.fallback_music_file, "fallback_music_file"
11296 &setup.internal.default_level_series, "default_level_series"
11300 &setup.internal.default_window_width, "default_window_width"
11304 &setup.internal.default_window_height, "default_window_height"
11308 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11312 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11316 &setup.internal.create_user_levelset, "create_user_levelset"
11320 &setup.internal.info_screens_from_main, "info_screens_from_main"
11324 &setup.internal.menu_game, "menu_game"
11328 &setup.internal.menu_engines, "menu_engines"
11332 &setup.internal.menu_editor, "menu_editor"
11336 &setup.internal.menu_graphics, "menu_graphics"
11340 &setup.internal.menu_sound, "menu_sound"
11344 &setup.internal.menu_artwork, "menu_artwork"
11348 &setup.internal.menu_input, "menu_input"
11352 &setup.internal.menu_touch, "menu_touch"
11356 &setup.internal.menu_shortcuts, "menu_shortcuts"
11360 &setup.internal.menu_exit, "menu_exit"
11364 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11368 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11372 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11376 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11380 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11384 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11388 &setup.internal.info_title, "info_title"
11392 &setup.internal.info_elements, "info_elements"
11396 &setup.internal.info_music, "info_music"
11400 &setup.internal.info_credits, "info_credits"
11404 &setup.internal.info_program, "info_program"
11408 &setup.internal.info_version, "info_version"
11412 &setup.internal.info_levelset, "info_levelset"
11416 &setup.internal.info_exit, "info_exit"
11420 static struct TokenInfo debug_setup_tokens[] =
11424 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11428 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11432 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11436 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11440 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11444 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11448 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11452 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11456 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11460 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11464 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11468 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11472 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11476 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11480 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11484 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11488 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11492 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11496 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11500 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11504 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11507 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11511 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11515 &setup.debug.xsn_mode, "debug.xsn_mode"
11519 &setup.debug.xsn_percent, "debug.xsn_percent"
11523 static struct TokenInfo options_setup_tokens[] =
11527 &setup.options.verbose, "options.verbose"
11531 &setup.options.debug, "options.debug"
11535 &setup.options.debug_mode, "options.debug_mode"
11539 static void setSetupInfoToDefaults(struct SetupInfo *si)
11543 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11545 si->multiple_users = TRUE;
11548 si->sound_loops = TRUE;
11549 si->sound_music = TRUE;
11550 si->sound_simple = TRUE;
11552 si->global_animations = TRUE;
11553 si->scroll_delay = TRUE;
11554 si->forced_scroll_delay = FALSE;
11555 si->scroll_delay_value = STD_SCROLL_DELAY;
11556 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11557 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11558 si->fade_screens = TRUE;
11559 si->autorecord = TRUE;
11560 si->autorecord_after_replay = TRUE;
11561 si->auto_pause_on_start = FALSE;
11562 si->show_titlescreen = TRUE;
11563 si->quick_doors = FALSE;
11564 si->team_mode = FALSE;
11565 si->handicap = TRUE;
11566 si->skip_levels = TRUE;
11567 si->increment_levels = TRUE;
11568 si->auto_play_next_level = TRUE;
11569 si->count_score_after_game = TRUE;
11570 si->show_scores_after_game = TRUE;
11571 si->time_limit = TRUE;
11572 si->fullscreen = FALSE;
11573 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11574 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11575 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11576 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11577 si->ask_on_escape = TRUE;
11578 si->ask_on_escape_editor = TRUE;
11579 si->ask_on_game_over = TRUE;
11580 si->ask_on_quit_game = TRUE;
11581 si->ask_on_quit_program = TRUE;
11582 si->quick_switch = FALSE;
11583 si->input_on_focus = FALSE;
11584 si->prefer_aga_graphics = TRUE;
11585 si->prefer_lowpass_sounds = FALSE;
11586 si->prefer_extra_panel_items = TRUE;
11587 si->game_speed_extended = FALSE;
11588 si->game_frame_delay = GAME_FRAME_DELAY;
11589 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11590 si->bd_skip_uncovering = FALSE;
11591 si->bd_skip_hatching = FALSE;
11592 si->bd_scroll_delay = TRUE;
11593 si->bd_smooth_movements = AUTO;
11594 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11595 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11596 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11597 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11598 si->sp_show_border_elements = FALSE;
11599 si->small_game_graphics = FALSE;
11600 si->show_load_save_buttons = FALSE;
11601 si->show_undo_redo_buttons = FALSE;
11602 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11604 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11605 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11606 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11608 si->override_level_graphics = FALSE;
11609 si->override_level_sounds = FALSE;
11610 si->override_level_music = FALSE;
11612 si->volume_simple = 100; // percent
11613 si->volume_loops = 100; // percent
11614 si->volume_music = 100; // percent
11616 si->network_mode = FALSE;
11617 si->network_player_nr = 0; // first player
11618 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11620 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11621 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11622 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11623 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11624 si->touch.draw_outlined = TRUE;
11625 si->touch.draw_pressed = TRUE;
11627 for (i = 0; i < 2; i++)
11629 char *default_grid_button[6][2] =
11635 { "111222", " vv " },
11636 { "111222", " vv " }
11638 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11639 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11640 int min_xsize = MIN(6, grid_xsize);
11641 int min_ysize = MIN(6, grid_ysize);
11642 int startx = grid_xsize - min_xsize;
11643 int starty = grid_ysize - min_ysize;
11646 // virtual buttons grid can only be set to defaults if video is initialized
11647 // (this will be repeated if virtual buttons are not loaded from setup file)
11648 if (video.initialized)
11650 si->touch.grid_xsize[i] = grid_xsize;
11651 si->touch.grid_ysize[i] = grid_ysize;
11655 si->touch.grid_xsize[i] = -1;
11656 si->touch.grid_ysize[i] = -1;
11659 for (x = 0; x < MAX_GRID_XSIZE; x++)
11660 for (y = 0; y < MAX_GRID_YSIZE; y++)
11661 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11663 for (x = 0; x < min_xsize; x++)
11664 for (y = 0; y < min_ysize; y++)
11665 si->touch.grid_button[i][x][starty + y] =
11666 default_grid_button[y][0][x];
11668 for (x = 0; x < min_xsize; x++)
11669 for (y = 0; y < min_ysize; y++)
11670 si->touch.grid_button[i][startx + x][starty + y] =
11671 default_grid_button[y][1][x];
11674 si->touch.grid_initialized = video.initialized;
11676 si->touch.overlay_buttons = FALSE;
11678 si->editor.el_boulderdash = TRUE;
11679 si->editor.el_boulderdash_native = TRUE;
11680 si->editor.el_boulderdash_effects = TRUE;
11681 si->editor.el_emerald_mine = TRUE;
11682 si->editor.el_emerald_mine_club = TRUE;
11683 si->editor.el_more = TRUE;
11684 si->editor.el_sokoban = TRUE;
11685 si->editor.el_supaplex = TRUE;
11686 si->editor.el_diamond_caves = TRUE;
11687 si->editor.el_dx_boulderdash = TRUE;
11689 si->editor.el_mirror_magic = TRUE;
11690 si->editor.el_deflektor = TRUE;
11692 si->editor.el_chars = TRUE;
11693 si->editor.el_steel_chars = TRUE;
11695 si->editor.el_classic = TRUE;
11696 si->editor.el_custom = TRUE;
11698 si->editor.el_user_defined = FALSE;
11699 si->editor.el_dynamic = TRUE;
11701 si->editor.el_headlines = TRUE;
11703 si->editor.show_element_token = FALSE;
11705 si->editor.show_read_only_warning = TRUE;
11707 si->editor.use_template_for_new_levels = TRUE;
11709 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11710 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11711 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11712 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11713 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11715 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11716 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11717 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11718 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11719 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11721 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11722 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11723 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11724 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11725 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11726 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11728 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11729 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11730 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11732 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11733 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11734 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11735 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11737 for (i = 0; i < MAX_PLAYERS; i++)
11739 si->input[i].use_joystick = FALSE;
11740 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11741 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11742 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11743 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11744 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11745 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11746 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11747 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11748 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11749 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11750 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11751 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11752 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11753 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11754 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11757 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11758 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11759 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11760 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11762 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11763 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11764 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11765 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11766 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11767 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11768 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11770 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11772 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11773 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11774 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11776 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11777 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11778 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11780 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11781 si->internal.choose_from_top_leveldir = FALSE;
11782 si->internal.show_scaling_in_title = TRUE;
11783 si->internal.create_user_levelset = TRUE;
11784 si->internal.info_screens_from_main = FALSE;
11786 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11787 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11789 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11790 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11791 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11792 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11793 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11794 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11795 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11796 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11797 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11798 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11800 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11801 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11802 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11803 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11804 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11805 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11806 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11807 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11808 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11809 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11811 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11812 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11814 si->debug.show_frames_per_second = FALSE;
11816 si->debug.xsn_mode = AUTO;
11817 si->debug.xsn_percent = 0;
11819 si->options.verbose = FALSE;
11820 si->options.debug = FALSE;
11821 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11823 #if defined(PLATFORM_ANDROID)
11824 si->fullscreen = TRUE;
11825 si->touch.overlay_buttons = TRUE;
11828 setHideSetupEntry(&setup.debug.xsn_mode);
11831 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11833 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11836 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11838 si->player_uuid = NULL; // (will be set later)
11839 si->player_version = 1; // (will be set later)
11841 si->use_api_server = TRUE;
11842 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11843 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11844 si->ask_for_uploading_tapes = TRUE;
11845 si->ask_for_remaining_tapes = FALSE;
11846 si->provide_uploading_tapes = TRUE;
11847 si->ask_for_using_api_server = TRUE;
11848 si->has_remaining_tapes = FALSE;
11851 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11853 si->editor_cascade.el_bd = TRUE;
11854 si->editor_cascade.el_bd_native = TRUE;
11855 si->editor_cascade.el_bd_effects = FALSE;
11856 si->editor_cascade.el_em = TRUE;
11857 si->editor_cascade.el_emc = TRUE;
11858 si->editor_cascade.el_rnd = TRUE;
11859 si->editor_cascade.el_sb = TRUE;
11860 si->editor_cascade.el_sp = TRUE;
11861 si->editor_cascade.el_dc = TRUE;
11862 si->editor_cascade.el_dx = TRUE;
11864 si->editor_cascade.el_mm = TRUE;
11865 si->editor_cascade.el_df = TRUE;
11867 si->editor_cascade.el_chars = FALSE;
11868 si->editor_cascade.el_steel_chars = FALSE;
11869 si->editor_cascade.el_ce = FALSE;
11870 si->editor_cascade.el_ge = FALSE;
11871 si->editor_cascade.el_es = FALSE;
11872 si->editor_cascade.el_ref = FALSE;
11873 si->editor_cascade.el_user = FALSE;
11874 si->editor_cascade.el_dynamic = FALSE;
11877 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11879 static char *getHideSetupToken(void *setup_value)
11881 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11883 if (setup_value != NULL)
11884 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11886 return hide_setup_token;
11889 void setHideSetupEntry(void *setup_value)
11891 char *hide_setup_token = getHideSetupToken(setup_value);
11893 if (hide_setup_hash == NULL)
11894 hide_setup_hash = newSetupFileHash();
11896 if (setup_value != NULL)
11897 setHashEntry(hide_setup_hash, hide_setup_token, "");
11900 void removeHideSetupEntry(void *setup_value)
11902 char *hide_setup_token = getHideSetupToken(setup_value);
11904 if (setup_value != NULL)
11905 removeHashEntry(hide_setup_hash, hide_setup_token);
11908 boolean hideSetupEntry(void *setup_value)
11910 char *hide_setup_token = getHideSetupToken(setup_value);
11912 return (setup_value != NULL &&
11913 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11916 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11917 struct TokenInfo *token_info,
11918 int token_nr, char *token_text)
11920 char *token_hide_text = getStringCat2(token_text, ".hide");
11921 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11923 // set the value of this setup option in the setup option structure
11924 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11926 // check if this setup option should be hidden in the setup menu
11927 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11928 setHideSetupEntry(token_info[token_nr].value);
11930 free(token_hide_text);
11933 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11934 struct TokenInfo *token_info,
11937 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11938 token_info[token_nr].text);
11941 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11945 if (!setup_file_hash)
11948 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11949 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11951 setup.touch.grid_initialized = TRUE;
11952 for (i = 0; i < 2; i++)
11954 int grid_xsize = setup.touch.grid_xsize[i];
11955 int grid_ysize = setup.touch.grid_ysize[i];
11958 // if virtual buttons are not loaded from setup file, repeat initializing
11959 // virtual buttons grid with default values later when video is initialized
11960 if (grid_xsize == -1 ||
11963 setup.touch.grid_initialized = FALSE;
11968 for (y = 0; y < grid_ysize; y++)
11970 char token_string[MAX_LINE_LEN];
11972 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11974 char *value_string = getHashEntry(setup_file_hash, token_string);
11976 if (value_string == NULL)
11979 for (x = 0; x < grid_xsize; x++)
11981 char c = value_string[x];
11983 setup.touch.grid_button[i][x][y] =
11984 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11989 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11990 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11992 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11993 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11995 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11999 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12001 setup_input = setup.input[pnr];
12002 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12004 char full_token[100];
12006 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12007 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12010 setup.input[pnr] = setup_input;
12013 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12014 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12016 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12017 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12019 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12020 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12022 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12023 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12025 setHideRelatedSetupEntries();
12028 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12032 if (!setup_file_hash)
12035 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12036 setSetupInfo(auto_setup_tokens, i,
12037 getHashEntry(setup_file_hash,
12038 auto_setup_tokens[i].text));
12041 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12045 if (!setup_file_hash)
12048 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12049 setSetupInfo(server_setup_tokens, i,
12050 getHashEntry(setup_file_hash,
12051 server_setup_tokens[i].text));
12054 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12058 if (!setup_file_hash)
12061 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12062 setSetupInfo(editor_cascade_setup_tokens, i,
12063 getHashEntry(setup_file_hash,
12064 editor_cascade_setup_tokens[i].text));
12067 void LoadUserNames(void)
12069 int last_user_nr = user.nr;
12072 if (global.user_names != NULL)
12074 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12075 checked_free(global.user_names[i]);
12077 checked_free(global.user_names);
12080 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12082 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12086 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12088 if (setup_file_hash)
12090 char *player_name = getHashEntry(setup_file_hash, "player_name");
12092 global.user_names[i] = getFixedUserName(player_name);
12094 freeSetupFileHash(setup_file_hash);
12097 if (global.user_names[i] == NULL)
12098 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12101 user.nr = last_user_nr;
12104 void LoadSetupFromFilename(char *filename)
12106 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12108 if (setup_file_hash)
12110 decodeSetupFileHash_Default(setup_file_hash);
12112 freeSetupFileHash(setup_file_hash);
12116 Debug("setup", "using default setup values");
12120 static void LoadSetup_SpecialPostProcessing(void)
12122 char *player_name_new;
12124 // needed to work around problems with fixed length strings
12125 player_name_new = getFixedUserName(setup.player_name);
12126 free(setup.player_name);
12127 setup.player_name = player_name_new;
12129 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12130 if (setup.scroll_delay == FALSE)
12132 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12133 setup.scroll_delay = TRUE; // now always "on"
12136 // make sure that scroll delay value stays inside valid range
12137 setup.scroll_delay_value =
12138 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12141 void LoadSetup_Default(void)
12145 // always start with reliable default values
12146 setSetupInfoToDefaults(&setup);
12148 // try to load setup values from default setup file
12149 filename = getDefaultSetupFilename();
12151 if (fileExists(filename))
12152 LoadSetupFromFilename(filename);
12154 // try to load setup values from platform setup file
12155 filename = getPlatformSetupFilename();
12157 if (fileExists(filename))
12158 LoadSetupFromFilename(filename);
12160 // try to load setup values from user setup file
12161 filename = getSetupFilename();
12163 LoadSetupFromFilename(filename);
12165 LoadSetup_SpecialPostProcessing();
12168 void LoadSetup_AutoSetup(void)
12170 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12171 SetupFileHash *setup_file_hash = NULL;
12173 // always start with reliable default values
12174 setSetupInfoToDefaults_AutoSetup(&setup);
12176 setup_file_hash = loadSetupFileHash(filename);
12178 if (setup_file_hash)
12180 decodeSetupFileHash_AutoSetup(setup_file_hash);
12182 freeSetupFileHash(setup_file_hash);
12188 void LoadSetup_ServerSetup(void)
12190 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12191 SetupFileHash *setup_file_hash = NULL;
12193 // always start with reliable default values
12194 setSetupInfoToDefaults_ServerSetup(&setup);
12196 setup_file_hash = loadSetupFileHash(filename);
12198 if (setup_file_hash)
12200 decodeSetupFileHash_ServerSetup(setup_file_hash);
12202 freeSetupFileHash(setup_file_hash);
12207 if (setup.player_uuid == NULL)
12209 // player UUID does not yet exist in setup file
12210 setup.player_uuid = getStringCopy(getUUID());
12211 setup.player_version = 2;
12213 SaveSetup_ServerSetup();
12217 void LoadSetup_EditorCascade(void)
12219 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12220 SetupFileHash *setup_file_hash = NULL;
12222 // always start with reliable default values
12223 setSetupInfoToDefaults_EditorCascade(&setup);
12225 setup_file_hash = loadSetupFileHash(filename);
12227 if (setup_file_hash)
12229 decodeSetupFileHash_EditorCascade(setup_file_hash);
12231 freeSetupFileHash(setup_file_hash);
12237 void LoadSetup(void)
12239 LoadSetup_Default();
12240 LoadSetup_AutoSetup();
12241 LoadSetup_ServerSetup();
12242 LoadSetup_EditorCascade();
12245 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12246 char *mapping_line)
12248 char mapping_guid[MAX_LINE_LEN];
12249 char *mapping_start, *mapping_end;
12251 // get GUID from game controller mapping line: copy complete line
12252 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12253 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12255 // get GUID from game controller mapping line: cut after GUID part
12256 mapping_start = strchr(mapping_guid, ',');
12257 if (mapping_start != NULL)
12258 *mapping_start = '\0';
12260 // cut newline from game controller mapping line
12261 mapping_end = strchr(mapping_line, '\n');
12262 if (mapping_end != NULL)
12263 *mapping_end = '\0';
12265 // add mapping entry to game controller mappings hash
12266 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12269 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12274 if (!(file = fopen(filename, MODE_READ)))
12276 Warn("cannot read game controller mappings file '%s'", filename);
12281 while (!feof(file))
12283 char line[MAX_LINE_LEN];
12285 if (!fgets(line, MAX_LINE_LEN, file))
12288 addGameControllerMappingToHash(mappings_hash, line);
12294 void SaveSetup_Default(void)
12296 char *filename = getSetupFilename();
12300 InitUserDataDirectory();
12302 if (!(file = fopen(filename, MODE_WRITE)))
12304 Warn("cannot write setup file '%s'", filename);
12309 fprintFileHeader(file, SETUP_FILENAME);
12311 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12313 // just to make things nicer :)
12314 if (global_setup_tokens[i].value == &setup.multiple_users ||
12315 global_setup_tokens[i].value == &setup.sound ||
12316 global_setup_tokens[i].value == &setup.graphics_set ||
12317 global_setup_tokens[i].value == &setup.volume_simple ||
12318 global_setup_tokens[i].value == &setup.network_mode ||
12319 global_setup_tokens[i].value == &setup.touch.control_type ||
12320 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12321 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12322 fprintf(file, "\n");
12324 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12327 for (i = 0; i < 2; i++)
12329 int grid_xsize = setup.touch.grid_xsize[i];
12330 int grid_ysize = setup.touch.grid_ysize[i];
12333 fprintf(file, "\n");
12335 for (y = 0; y < grid_ysize; y++)
12337 char token_string[MAX_LINE_LEN];
12338 char value_string[MAX_LINE_LEN];
12340 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12342 for (x = 0; x < grid_xsize; x++)
12344 char c = setup.touch.grid_button[i][x][y];
12346 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12349 value_string[grid_xsize] = '\0';
12351 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12355 fprintf(file, "\n");
12356 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12357 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12359 fprintf(file, "\n");
12360 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12361 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12363 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12367 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12368 fprintf(file, "\n");
12370 setup_input = setup.input[pnr];
12371 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12372 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12375 fprintf(file, "\n");
12376 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12377 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12379 // (internal setup values not saved to user setup file)
12381 fprintf(file, "\n");
12382 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12383 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12384 setup.debug.xsn_mode != AUTO)
12385 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12387 fprintf(file, "\n");
12388 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12389 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12393 SetFilePermissions(filename, PERMS_PRIVATE);
12396 void SaveSetup_AutoSetup(void)
12398 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12402 InitUserDataDirectory();
12404 if (!(file = fopen(filename, MODE_WRITE)))
12406 Warn("cannot write auto setup file '%s'", filename);
12413 fprintFileHeader(file, AUTOSETUP_FILENAME);
12415 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12416 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12420 SetFilePermissions(filename, PERMS_PRIVATE);
12425 void SaveSetup_ServerSetup(void)
12427 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12431 InitUserDataDirectory();
12433 if (!(file = fopen(filename, MODE_WRITE)))
12435 Warn("cannot write server setup file '%s'", filename);
12442 fprintFileHeader(file, SERVERSETUP_FILENAME);
12444 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12446 // just to make things nicer :)
12447 if (server_setup_tokens[i].value == &setup.use_api_server)
12448 fprintf(file, "\n");
12450 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12455 SetFilePermissions(filename, PERMS_PRIVATE);
12460 void SaveSetup_EditorCascade(void)
12462 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12466 InitUserDataDirectory();
12468 if (!(file = fopen(filename, MODE_WRITE)))
12470 Warn("cannot write editor cascade state file '%s'", filename);
12477 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12479 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12480 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12484 SetFilePermissions(filename, PERMS_PRIVATE);
12489 void SaveSetup(void)
12491 SaveSetup_Default();
12492 SaveSetup_AutoSetup();
12493 SaveSetup_ServerSetup();
12494 SaveSetup_EditorCascade();
12497 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12502 if (!(file = fopen(filename, MODE_WRITE)))
12504 Warn("cannot write game controller mappings file '%s'", filename);
12509 BEGIN_HASH_ITERATION(mappings_hash, itr)
12511 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12513 END_HASH_ITERATION(mappings_hash, itr)
12518 void SaveSetup_AddGameControllerMapping(char *mapping)
12520 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12521 SetupFileHash *mappings_hash = newSetupFileHash();
12523 InitUserDataDirectory();
12525 // load existing personal game controller mappings
12526 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12528 // add new mapping to personal game controller mappings
12529 addGameControllerMappingToHash(mappings_hash, mapping);
12531 // save updated personal game controller mappings
12532 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12534 freeSetupFileHash(mappings_hash);
12538 void LoadCustomElementDescriptions(void)
12540 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12541 SetupFileHash *setup_file_hash;
12544 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12546 if (element_info[i].custom_description != NULL)
12548 free(element_info[i].custom_description);
12549 element_info[i].custom_description = NULL;
12553 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12556 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12558 char *token = getStringCat2(element_info[i].token_name, ".name");
12559 char *value = getHashEntry(setup_file_hash, token);
12562 element_info[i].custom_description = getStringCopy(value);
12567 freeSetupFileHash(setup_file_hash);
12570 static int getElementFromToken(char *token)
12572 char *value = getHashEntry(element_token_hash, token);
12575 return atoi(value);
12577 Warn("unknown element token '%s'", token);
12579 return EL_UNDEFINED;
12582 void FreeGlobalAnimEventInfo(void)
12584 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12586 if (gaei->event_list == NULL)
12591 for (i = 0; i < gaei->num_event_lists; i++)
12593 checked_free(gaei->event_list[i]->event_value);
12594 checked_free(gaei->event_list[i]);
12597 checked_free(gaei->event_list);
12599 gaei->event_list = NULL;
12600 gaei->num_event_lists = 0;
12603 static int AddGlobalAnimEventList(void)
12605 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12606 int list_pos = gaei->num_event_lists++;
12608 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12609 sizeof(struct GlobalAnimEventListInfo *));
12611 gaei->event_list[list_pos] =
12612 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12614 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12616 gaeli->event_value = NULL;
12617 gaeli->num_event_values = 0;
12622 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12624 // do not add empty global animation events
12625 if (event_value == ANIM_EVENT_NONE)
12628 // if list position is undefined, create new list
12629 if (list_pos == ANIM_EVENT_UNDEFINED)
12630 list_pos = AddGlobalAnimEventList();
12632 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12633 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12634 int value_pos = gaeli->num_event_values++;
12636 gaeli->event_value = checked_realloc(gaeli->event_value,
12637 gaeli->num_event_values * sizeof(int *));
12639 gaeli->event_value[value_pos] = event_value;
12644 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12646 if (list_pos == ANIM_EVENT_UNDEFINED)
12647 return ANIM_EVENT_NONE;
12649 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12650 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12652 return gaeli->event_value[value_pos];
12655 int GetGlobalAnimEventValueCount(int list_pos)
12657 if (list_pos == ANIM_EVENT_UNDEFINED)
12660 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12661 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12663 return gaeli->num_event_values;
12666 // This function checks if a string <s> of the format "string1, string2, ..."
12667 // exactly contains a string <s_contained>.
12669 static boolean string_has_parameter(char *s, char *s_contained)
12673 if (s == NULL || s_contained == NULL)
12676 if (strlen(s_contained) > strlen(s))
12679 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12681 char next_char = s[strlen(s_contained)];
12683 // check if next character is delimiter or whitespace
12684 if (next_char == ',' || next_char == '\0' ||
12685 next_char == ' ' || next_char == '\t')
12689 // check if string contains another parameter string after a comma
12690 substring = strchr(s, ',');
12691 if (substring == NULL) // string does not contain a comma
12694 // advance string pointer to next character after the comma
12697 // skip potential whitespaces after the comma
12698 while (*substring == ' ' || *substring == '\t')
12701 return string_has_parameter(substring, s_contained);
12704 static int get_anim_parameter_value_ce(char *s)
12707 char *pattern_1 = "ce_change:custom_";
12708 char *pattern_2 = ".page_";
12709 int pattern_1_len = strlen(pattern_1);
12710 char *matching_char = strstr(s_ptr, pattern_1);
12711 int result = ANIM_EVENT_NONE;
12713 if (matching_char == NULL)
12714 return ANIM_EVENT_NONE;
12716 result = ANIM_EVENT_CE_CHANGE;
12718 s_ptr = matching_char + pattern_1_len;
12720 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12721 if (*s_ptr >= '0' && *s_ptr <= '9')
12723 int gic_ce_nr = (*s_ptr++ - '0');
12725 if (*s_ptr >= '0' && *s_ptr <= '9')
12727 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12729 if (*s_ptr >= '0' && *s_ptr <= '9')
12730 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12733 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12734 return ANIM_EVENT_NONE;
12736 // custom element stored as 0 to 255
12739 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12743 // invalid custom element number specified
12745 return ANIM_EVENT_NONE;
12748 // check for change page number ("page_X" or "page_XX") (optional)
12749 if (strPrefix(s_ptr, pattern_2))
12751 s_ptr += strlen(pattern_2);
12753 if (*s_ptr >= '0' && *s_ptr <= '9')
12755 int gic_page_nr = (*s_ptr++ - '0');
12757 if (*s_ptr >= '0' && *s_ptr <= '9')
12758 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12760 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12761 return ANIM_EVENT_NONE;
12763 // change page stored as 1 to 32 (0 means "all change pages")
12765 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12769 // invalid animation part number specified
12771 return ANIM_EVENT_NONE;
12775 // discard result if next character is neither delimiter nor whitespace
12776 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12777 *s_ptr == ' ' || *s_ptr == '\t'))
12778 return ANIM_EVENT_NONE;
12783 static int get_anim_parameter_value(char *s)
12785 int event_value[] =
12793 char *pattern_1[] =
12801 char *pattern_2 = ".part_";
12802 char *matching_char = NULL;
12804 int pattern_1_len = 0;
12805 int result = ANIM_EVENT_NONE;
12808 result = get_anim_parameter_value_ce(s);
12810 if (result != ANIM_EVENT_NONE)
12813 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12815 matching_char = strstr(s_ptr, pattern_1[i]);
12816 pattern_1_len = strlen(pattern_1[i]);
12817 result = event_value[i];
12819 if (matching_char != NULL)
12823 if (matching_char == NULL)
12824 return ANIM_EVENT_NONE;
12826 s_ptr = matching_char + pattern_1_len;
12828 // check for main animation number ("anim_X" or "anim_XX")
12829 if (*s_ptr >= '0' && *s_ptr <= '9')
12831 int gic_anim_nr = (*s_ptr++ - '0');
12833 if (*s_ptr >= '0' && *s_ptr <= '9')
12834 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12836 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12837 return ANIM_EVENT_NONE;
12839 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12843 // invalid main animation number specified
12845 return ANIM_EVENT_NONE;
12848 // check for animation part number ("part_X" or "part_XX") (optional)
12849 if (strPrefix(s_ptr, pattern_2))
12851 s_ptr += strlen(pattern_2);
12853 if (*s_ptr >= '0' && *s_ptr <= '9')
12855 int gic_part_nr = (*s_ptr++ - '0');
12857 if (*s_ptr >= '0' && *s_ptr <= '9')
12858 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12860 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12861 return ANIM_EVENT_NONE;
12863 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12867 // invalid animation part number specified
12869 return ANIM_EVENT_NONE;
12873 // discard result if next character is neither delimiter nor whitespace
12874 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12875 *s_ptr == ' ' || *s_ptr == '\t'))
12876 return ANIM_EVENT_NONE;
12881 static int get_anim_parameter_values(char *s)
12883 int list_pos = ANIM_EVENT_UNDEFINED;
12884 int event_value = ANIM_EVENT_DEFAULT;
12886 if (string_has_parameter(s, "any"))
12887 event_value |= ANIM_EVENT_ANY;
12889 if (string_has_parameter(s, "click:self") ||
12890 string_has_parameter(s, "click") ||
12891 string_has_parameter(s, "self"))
12892 event_value |= ANIM_EVENT_SELF;
12894 if (string_has_parameter(s, "unclick:any"))
12895 event_value |= ANIM_EVENT_UNCLICK_ANY;
12897 // if animation event found, add it to global animation event list
12898 if (event_value != ANIM_EVENT_NONE)
12899 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12903 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12904 event_value = get_anim_parameter_value(s);
12906 // if animation event found, add it to global animation event list
12907 if (event_value != ANIM_EVENT_NONE)
12908 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12910 // continue with next part of the string, starting with next comma
12911 s = strchr(s + 1, ',');
12917 static int get_anim_action_parameter_value(char *token)
12919 // check most common default case first to massively speed things up
12920 if (strEqual(token, ARG_UNDEFINED))
12921 return ANIM_EVENT_ACTION_NONE;
12923 int result = getImageIDFromToken(token);
12927 char *gfx_token = getStringCat2("gfx.", token);
12929 result = getImageIDFromToken(gfx_token);
12931 checked_free(gfx_token);
12936 Key key = getKeyFromX11KeyName(token);
12938 if (key != KSYM_UNDEFINED)
12939 result = -(int)key;
12946 result = get_hash_from_string(token); // unsigned int => int
12947 result = ABS(result); // may be negative now
12948 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12950 setHashEntry(anim_url_hash, int2str(result, 0), token);
12955 result = ANIM_EVENT_ACTION_NONE;
12960 int get_parameter_value(char *value_raw, char *suffix, int type)
12962 char *value = getStringToLower(value_raw);
12963 int result = 0; // probably a save default value
12965 if (strEqual(suffix, ".direction"))
12967 result = (strEqual(value, "left") ? MV_LEFT :
12968 strEqual(value, "right") ? MV_RIGHT :
12969 strEqual(value, "up") ? MV_UP :
12970 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12972 else if (strEqual(suffix, ".position"))
12974 result = (strEqual(value, "left") ? POS_LEFT :
12975 strEqual(value, "right") ? POS_RIGHT :
12976 strEqual(value, "top") ? POS_TOP :
12977 strEqual(value, "upper") ? POS_UPPER :
12978 strEqual(value, "middle") ? POS_MIDDLE :
12979 strEqual(value, "lower") ? POS_LOWER :
12980 strEqual(value, "bottom") ? POS_BOTTOM :
12981 strEqual(value, "any") ? POS_ANY :
12982 strEqual(value, "ce") ? POS_CE :
12983 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12984 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12986 else if (strEqual(suffix, ".align"))
12988 result = (strEqual(value, "left") ? ALIGN_LEFT :
12989 strEqual(value, "right") ? ALIGN_RIGHT :
12990 strEqual(value, "center") ? ALIGN_CENTER :
12991 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12993 else if (strEqual(suffix, ".valign"))
12995 result = (strEqual(value, "top") ? VALIGN_TOP :
12996 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12997 strEqual(value, "middle") ? VALIGN_MIDDLE :
12998 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13000 else if (strEqual(suffix, ".anim_mode"))
13002 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13003 string_has_parameter(value, "loop") ? ANIM_LOOP :
13004 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13005 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13006 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13007 string_has_parameter(value, "random") ? ANIM_RANDOM :
13008 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13009 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13010 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13011 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13012 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13013 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13014 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13015 string_has_parameter(value, "all") ? ANIM_ALL :
13016 string_has_parameter(value, "tiled") ? ANIM_TILED :
13017 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13020 if (string_has_parameter(value, "once"))
13021 result |= ANIM_ONCE;
13023 if (string_has_parameter(value, "reverse"))
13024 result |= ANIM_REVERSE;
13026 if (string_has_parameter(value, "opaque_player"))
13027 result |= ANIM_OPAQUE_PLAYER;
13029 if (string_has_parameter(value, "static_panel"))
13030 result |= ANIM_STATIC_PANEL;
13032 else if (strEqual(suffix, ".init_event") ||
13033 strEqual(suffix, ".anim_event"))
13035 result = get_anim_parameter_values(value);
13037 else if (strEqual(suffix, ".init_delay_action") ||
13038 strEqual(suffix, ".anim_delay_action") ||
13039 strEqual(suffix, ".post_delay_action") ||
13040 strEqual(suffix, ".init_event_action") ||
13041 strEqual(suffix, ".anim_event_action"))
13043 result = get_anim_action_parameter_value(value_raw);
13045 else if (strEqual(suffix, ".class"))
13047 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13048 get_hash_from_string(value));
13050 else if (strEqual(suffix, ".style"))
13052 result = STYLE_DEFAULT;
13054 if (string_has_parameter(value, "accurate_borders"))
13055 result |= STYLE_ACCURATE_BORDERS;
13057 if (string_has_parameter(value, "inner_corners"))
13058 result |= STYLE_INNER_CORNERS;
13060 if (string_has_parameter(value, "reverse"))
13061 result |= STYLE_REVERSE;
13063 if (string_has_parameter(value, "leftmost_position"))
13064 result |= STYLE_LEFTMOST_POSITION;
13066 if (string_has_parameter(value, "block_clicks"))
13067 result |= STYLE_BLOCK;
13069 if (string_has_parameter(value, "passthrough_clicks"))
13070 result |= STYLE_PASSTHROUGH;
13072 if (string_has_parameter(value, "multiple_actions"))
13073 result |= STYLE_MULTIPLE_ACTIONS;
13075 if (string_has_parameter(value, "consume_ce_event"))
13076 result |= STYLE_CONSUME_CE_EVENT;
13078 else if (strEqual(suffix, ".fade_mode"))
13080 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13081 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13082 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13083 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13084 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13085 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13086 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13087 FADE_MODE_DEFAULT);
13089 else if (strEqual(suffix, ".auto_delay_unit"))
13091 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13092 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13093 AUTO_DELAY_UNIT_DEFAULT);
13095 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13097 result = gfx.get_font_from_token_function(value);
13099 else // generic parameter of type integer or boolean
13101 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13102 type == TYPE_INTEGER ? get_integer_from_string(value) :
13103 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13104 ARG_UNDEFINED_VALUE);
13112 static int get_token_parameter_value(char *token, char *value_raw)
13116 if (token == NULL || value_raw == NULL)
13117 return ARG_UNDEFINED_VALUE;
13119 suffix = strrchr(token, '.');
13120 if (suffix == NULL)
13123 if (strEqual(suffix, ".element"))
13124 return getElementFromToken(value_raw);
13126 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13127 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13130 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13131 boolean ignore_defaults)
13135 for (i = 0; image_config_vars[i].token != NULL; i++)
13137 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13139 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13140 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13144 *image_config_vars[i].value =
13145 get_token_parameter_value(image_config_vars[i].token, value);
13149 void InitMenuDesignSettings_Static(void)
13151 // always start with reliable default values from static default config
13152 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13155 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13159 // the following initializes hierarchical values from static configuration
13161 // special case: initialize "ARG_DEFAULT" values in static default config
13162 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13163 titlescreen_initial_first_default.fade_mode =
13164 title_initial_first_default.fade_mode;
13165 titlescreen_initial_first_default.fade_delay =
13166 title_initial_first_default.fade_delay;
13167 titlescreen_initial_first_default.post_delay =
13168 title_initial_first_default.post_delay;
13169 titlescreen_initial_first_default.auto_delay =
13170 title_initial_first_default.auto_delay;
13171 titlescreen_initial_first_default.auto_delay_unit =
13172 title_initial_first_default.auto_delay_unit;
13173 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13174 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13175 titlescreen_first_default.post_delay = title_first_default.post_delay;
13176 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13177 titlescreen_first_default.auto_delay_unit =
13178 title_first_default.auto_delay_unit;
13179 titlemessage_initial_first_default.fade_mode =
13180 title_initial_first_default.fade_mode;
13181 titlemessage_initial_first_default.fade_delay =
13182 title_initial_first_default.fade_delay;
13183 titlemessage_initial_first_default.post_delay =
13184 title_initial_first_default.post_delay;
13185 titlemessage_initial_first_default.auto_delay =
13186 title_initial_first_default.auto_delay;
13187 titlemessage_initial_first_default.auto_delay_unit =
13188 title_initial_first_default.auto_delay_unit;
13189 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13190 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13191 titlemessage_first_default.post_delay = title_first_default.post_delay;
13192 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13193 titlemessage_first_default.auto_delay_unit =
13194 title_first_default.auto_delay_unit;
13196 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13197 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13198 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13199 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13200 titlescreen_initial_default.auto_delay_unit =
13201 title_initial_default.auto_delay_unit;
13202 titlescreen_default.fade_mode = title_default.fade_mode;
13203 titlescreen_default.fade_delay = title_default.fade_delay;
13204 titlescreen_default.post_delay = title_default.post_delay;
13205 titlescreen_default.auto_delay = title_default.auto_delay;
13206 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13207 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13208 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13209 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13210 titlemessage_initial_default.auto_delay_unit =
13211 title_initial_default.auto_delay_unit;
13212 titlemessage_default.fade_mode = title_default.fade_mode;
13213 titlemessage_default.fade_delay = title_default.fade_delay;
13214 titlemessage_default.post_delay = title_default.post_delay;
13215 titlemessage_default.auto_delay = title_default.auto_delay;
13216 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13218 // special case: initialize "ARG_DEFAULT" values in static default config
13219 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13220 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13222 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13223 titlescreen_first[i] = titlescreen_first_default;
13224 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13225 titlemessage_first[i] = titlemessage_first_default;
13227 titlescreen_initial[i] = titlescreen_initial_default;
13228 titlescreen[i] = titlescreen_default;
13229 titlemessage_initial[i] = titlemessage_initial_default;
13230 titlemessage[i] = titlemessage_default;
13233 // special case: initialize "ARG_DEFAULT" values in static default config
13234 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13235 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13237 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13240 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13241 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13242 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13245 // special case: initialize "ARG_DEFAULT" values in static default config
13246 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13247 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13249 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13250 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13251 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13253 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13256 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13260 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13264 struct XY *dst, *src;
13266 game_buttons_xy[] =
13268 { &game.button.save, &game.button.stop },
13269 { &game.button.pause2, &game.button.pause },
13270 { &game.button.load, &game.button.play },
13271 { &game.button.undo, &game.button.stop },
13272 { &game.button.redo, &game.button.play },
13278 // special case: initialize later added SETUP list size from LEVELS value
13279 if (menu.list_size[GAME_MODE_SETUP] == -1)
13280 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13282 // set default position for snapshot buttons to stop/pause/play buttons
13283 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13284 if ((*game_buttons_xy[i].dst).x == -1 &&
13285 (*game_buttons_xy[i].dst).y == -1)
13286 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13288 // --------------------------------------------------------------------------
13289 // dynamic viewports (including playfield margins, borders and alignments)
13290 // --------------------------------------------------------------------------
13292 // dynamic viewports currently only supported for landscape mode
13293 int display_width = MAX(video.display_width, video.display_height);
13294 int display_height = MIN(video.display_width, video.display_height);
13296 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13298 struct RectWithBorder *vp_window = &viewport.window[i];
13299 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13300 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13301 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13302 boolean dynamic_window_width = (vp_window->min_width != -1);
13303 boolean dynamic_window_height = (vp_window->min_height != -1);
13304 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13305 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13307 // adjust window size if min/max width/height is specified
13309 if (vp_window->min_width != -1)
13311 int window_width = display_width;
13313 // when using static window height, use aspect ratio of display
13314 if (vp_window->min_height == -1)
13315 window_width = vp_window->height * display_width / display_height;
13317 vp_window->width = MAX(vp_window->min_width, window_width);
13320 if (vp_window->min_height != -1)
13322 int window_height = display_height;
13324 // when using static window width, use aspect ratio of display
13325 if (vp_window->min_width == -1)
13326 window_height = vp_window->width * display_height / display_width;
13328 vp_window->height = MAX(vp_window->min_height, window_height);
13331 if (vp_window->max_width != -1)
13332 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13334 if (vp_window->max_height != -1)
13335 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13337 int playfield_width = vp_window->width;
13338 int playfield_height = vp_window->height;
13340 // adjust playfield size and position according to specified margins
13342 playfield_width -= vp_playfield->margin_left;
13343 playfield_width -= vp_playfield->margin_right;
13345 playfield_height -= vp_playfield->margin_top;
13346 playfield_height -= vp_playfield->margin_bottom;
13348 // adjust playfield size if min/max width/height is specified
13350 if (vp_playfield->min_width != -1)
13351 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13353 if (vp_playfield->min_height != -1)
13354 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13356 if (vp_playfield->max_width != -1)
13357 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13359 if (vp_playfield->max_height != -1)
13360 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13362 // adjust playfield position according to specified alignment
13364 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13365 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13366 else if (vp_playfield->align == ALIGN_CENTER)
13367 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13368 else if (vp_playfield->align == ALIGN_RIGHT)
13369 vp_playfield->x += playfield_width - vp_playfield->width;
13371 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13372 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13373 else if (vp_playfield->valign == VALIGN_MIDDLE)
13374 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13375 else if (vp_playfield->valign == VALIGN_BOTTOM)
13376 vp_playfield->y += playfield_height - vp_playfield->height;
13378 vp_playfield->x += vp_playfield->margin_left;
13379 vp_playfield->y += vp_playfield->margin_top;
13381 // adjust individual playfield borders if only default border is specified
13383 if (vp_playfield->border_left == -1)
13384 vp_playfield->border_left = vp_playfield->border_size;
13385 if (vp_playfield->border_right == -1)
13386 vp_playfield->border_right = vp_playfield->border_size;
13387 if (vp_playfield->border_top == -1)
13388 vp_playfield->border_top = vp_playfield->border_size;
13389 if (vp_playfield->border_bottom == -1)
13390 vp_playfield->border_bottom = vp_playfield->border_size;
13392 // set dynamic playfield borders if borders are specified as undefined
13393 // (but only if window size was dynamic and playfield size was static)
13395 if (dynamic_window_width && !dynamic_playfield_width)
13397 if (vp_playfield->border_left == -1)
13399 vp_playfield->border_left = (vp_playfield->x -
13400 vp_playfield->margin_left);
13401 vp_playfield->x -= vp_playfield->border_left;
13402 vp_playfield->width += vp_playfield->border_left;
13405 if (vp_playfield->border_right == -1)
13407 vp_playfield->border_right = (vp_window->width -
13409 vp_playfield->width -
13410 vp_playfield->margin_right);
13411 vp_playfield->width += vp_playfield->border_right;
13415 if (dynamic_window_height && !dynamic_playfield_height)
13417 if (vp_playfield->border_top == -1)
13419 vp_playfield->border_top = (vp_playfield->y -
13420 vp_playfield->margin_top);
13421 vp_playfield->y -= vp_playfield->border_top;
13422 vp_playfield->height += vp_playfield->border_top;
13425 if (vp_playfield->border_bottom == -1)
13427 vp_playfield->border_bottom = (vp_window->height -
13429 vp_playfield->height -
13430 vp_playfield->margin_bottom);
13431 vp_playfield->height += vp_playfield->border_bottom;
13435 // adjust playfield size to be a multiple of a defined alignment tile size
13437 int align_size = vp_playfield->align_size;
13438 int playfield_xtiles = vp_playfield->width / align_size;
13439 int playfield_ytiles = vp_playfield->height / align_size;
13440 int playfield_width_corrected = playfield_xtiles * align_size;
13441 int playfield_height_corrected = playfield_ytiles * align_size;
13442 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13443 i == GFX_SPECIAL_ARG_EDITOR);
13445 if (is_playfield_mode &&
13446 dynamic_playfield_width &&
13447 vp_playfield->width != playfield_width_corrected)
13449 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13451 vp_playfield->width = playfield_width_corrected;
13453 if (vp_playfield->align == ALIGN_LEFT)
13455 vp_playfield->border_left += playfield_xdiff;
13457 else if (vp_playfield->align == ALIGN_RIGHT)
13459 vp_playfield->border_right += playfield_xdiff;
13461 else if (vp_playfield->align == ALIGN_CENTER)
13463 int border_left_diff = playfield_xdiff / 2;
13464 int border_right_diff = playfield_xdiff - border_left_diff;
13466 vp_playfield->border_left += border_left_diff;
13467 vp_playfield->border_right += border_right_diff;
13471 if (is_playfield_mode &&
13472 dynamic_playfield_height &&
13473 vp_playfield->height != playfield_height_corrected)
13475 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13477 vp_playfield->height = playfield_height_corrected;
13479 if (vp_playfield->valign == VALIGN_TOP)
13481 vp_playfield->border_top += playfield_ydiff;
13483 else if (vp_playfield->align == VALIGN_BOTTOM)
13485 vp_playfield->border_right += playfield_ydiff;
13487 else if (vp_playfield->align == VALIGN_MIDDLE)
13489 int border_top_diff = playfield_ydiff / 2;
13490 int border_bottom_diff = playfield_ydiff - border_top_diff;
13492 vp_playfield->border_top += border_top_diff;
13493 vp_playfield->border_bottom += border_bottom_diff;
13497 // adjust door positions according to specified alignment
13499 for (j = 0; j < 2; j++)
13501 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13503 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13504 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13505 else if (vp_door->align == ALIGN_CENTER)
13506 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13507 else if (vp_door->align == ALIGN_RIGHT)
13508 vp_door->x += vp_window->width - vp_door->width;
13510 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13511 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13512 else if (vp_door->valign == VALIGN_MIDDLE)
13513 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13514 else if (vp_door->valign == VALIGN_BOTTOM)
13515 vp_door->y += vp_window->height - vp_door->height;
13520 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13524 struct XYTileSize *dst, *src;
13527 editor_buttons_xy[] =
13530 &editor.button.element_left, &editor.palette.element_left,
13531 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13534 &editor.button.element_middle, &editor.palette.element_middle,
13535 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13538 &editor.button.element_right, &editor.palette.element_right,
13539 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13546 // set default position for element buttons to element graphics
13547 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13549 if ((*editor_buttons_xy[i].dst).x == -1 &&
13550 (*editor_buttons_xy[i].dst).y == -1)
13552 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13554 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13556 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13560 // adjust editor palette rows and columns if specified to be dynamic
13562 if (editor.palette.cols == -1)
13564 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13565 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13566 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13568 editor.palette.cols = (vp_width - sc_width) / bt_width;
13570 if (editor.palette.x == -1)
13572 int palette_width = editor.palette.cols * bt_width + sc_width;
13574 editor.palette.x = (vp_width - palette_width) / 2;
13578 if (editor.palette.rows == -1)
13580 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13581 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13582 int tx_height = getFontHeight(FONT_TEXT_2);
13584 editor.palette.rows = (vp_height - tx_height) / bt_height;
13586 if (editor.palette.y == -1)
13588 int palette_height = editor.palette.rows * bt_height + tx_height;
13590 editor.palette.y = (vp_height - palette_height) / 2;
13595 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13596 boolean initialize)
13598 // special case: check if network and preview player positions are redefined,
13599 // to compare this later against the main menu level preview being redefined
13600 struct TokenIntPtrInfo menu_config_players[] =
13602 { "main.network_players.x", &menu.main.network_players.redefined },
13603 { "main.network_players.y", &menu.main.network_players.redefined },
13604 { "main.preview_players.x", &menu.main.preview_players.redefined },
13605 { "main.preview_players.y", &menu.main.preview_players.redefined },
13606 { "preview.x", &preview.redefined },
13607 { "preview.y", &preview.redefined }
13613 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13614 *menu_config_players[i].value = FALSE;
13618 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13619 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13620 *menu_config_players[i].value = TRUE;
13624 static void InitMenuDesignSettings_PreviewPlayers(void)
13626 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13629 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13631 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13634 static void LoadMenuDesignSettingsFromFilename(char *filename)
13636 static struct TitleFadingInfo tfi;
13637 static struct TitleMessageInfo tmi;
13638 static struct TokenInfo title_tokens[] =
13640 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13641 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13642 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13643 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13644 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13648 static struct TokenInfo titlemessage_tokens[] =
13650 { TYPE_INTEGER, &tmi.x, ".x" },
13651 { TYPE_INTEGER, &tmi.y, ".y" },
13652 { TYPE_INTEGER, &tmi.width, ".width" },
13653 { TYPE_INTEGER, &tmi.height, ".height" },
13654 { TYPE_INTEGER, &tmi.chars, ".chars" },
13655 { TYPE_INTEGER, &tmi.lines, ".lines" },
13656 { TYPE_INTEGER, &tmi.align, ".align" },
13657 { TYPE_INTEGER, &tmi.valign, ".valign" },
13658 { TYPE_INTEGER, &tmi.font, ".font" },
13659 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13660 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13661 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13662 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13663 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13664 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13665 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13666 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13667 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13673 struct TitleFadingInfo *info;
13678 // initialize first titles from "enter screen" definitions, if defined
13679 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13680 { &title_first_default, "menu.enter_screen.TITLE" },
13682 // initialize title screens from "next screen" definitions, if defined
13683 { &title_initial_default, "menu.next_screen.TITLE" },
13684 { &title_default, "menu.next_screen.TITLE" },
13690 struct TitleMessageInfo *array;
13693 titlemessage_arrays[] =
13695 // initialize first titles from "enter screen" definitions, if defined
13696 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13697 { titlescreen_first, "menu.enter_screen.TITLE" },
13698 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13699 { titlemessage_first, "menu.enter_screen.TITLE" },
13701 // initialize titles from "next screen" definitions, if defined
13702 { titlescreen_initial, "menu.next_screen.TITLE" },
13703 { titlescreen, "menu.next_screen.TITLE" },
13704 { titlemessage_initial, "menu.next_screen.TITLE" },
13705 { titlemessage, "menu.next_screen.TITLE" },
13707 // overwrite titles with title definitions, if defined
13708 { titlescreen_initial_first, "[title_initial]" },
13709 { titlescreen_first, "[title]" },
13710 { titlemessage_initial_first, "[title_initial]" },
13711 { titlemessage_first, "[title]" },
13713 { titlescreen_initial, "[title_initial]" },
13714 { titlescreen, "[title]" },
13715 { titlemessage_initial, "[title_initial]" },
13716 { titlemessage, "[title]" },
13718 // overwrite titles with title screen/message definitions, if defined
13719 { titlescreen_initial_first, "[titlescreen_initial]" },
13720 { titlescreen_first, "[titlescreen]" },
13721 { titlemessage_initial_first, "[titlemessage_initial]" },
13722 { titlemessage_first, "[titlemessage]" },
13724 { titlescreen_initial, "[titlescreen_initial]" },
13725 { titlescreen, "[titlescreen]" },
13726 { titlemessage_initial, "[titlemessage_initial]" },
13727 { titlemessage, "[titlemessage]" },
13731 SetupFileHash *setup_file_hash;
13734 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13737 // the following initializes hierarchical values from dynamic configuration
13739 // special case: initialize with default values that may be overwritten
13740 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13741 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13743 struct TokenIntPtrInfo menu_config[] =
13745 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13746 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13747 { "menu.list_size", &menu.list_size[i] }
13750 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13752 char *token = menu_config[j].token;
13753 char *value = getHashEntry(setup_file_hash, token);
13756 *menu_config[j].value = get_integer_from_string(value);
13760 // special case: initialize with default values that may be overwritten
13761 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13762 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13764 struct TokenIntPtrInfo menu_config[] =
13766 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13767 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13768 { "menu.list_size.INFO", &menu.list_size_info[i] },
13769 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13770 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13773 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13775 char *token = menu_config[j].token;
13776 char *value = getHashEntry(setup_file_hash, token);
13779 *menu_config[j].value = get_integer_from_string(value);
13783 // special case: initialize with default values that may be overwritten
13784 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13785 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13787 struct TokenIntPtrInfo menu_config[] =
13789 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13790 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13793 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13795 char *token = menu_config[j].token;
13796 char *value = getHashEntry(setup_file_hash, token);
13799 *menu_config[j].value = get_integer_from_string(value);
13803 // special case: initialize with default values that may be overwritten
13804 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13805 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13807 struct TokenIntPtrInfo menu_config[] =
13809 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13810 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13811 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13812 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13813 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13814 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13815 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13816 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13817 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13818 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13821 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13823 char *token = menu_config[j].token;
13824 char *value = getHashEntry(setup_file_hash, token);
13827 *menu_config[j].value = get_integer_from_string(value);
13831 // special case: initialize with default values that may be overwritten
13832 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13833 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13835 struct TokenIntPtrInfo menu_config[] =
13837 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13838 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13839 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13840 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13841 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13842 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13843 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13844 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13845 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
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_token_parameter_value(token, value);
13858 // special case: initialize with default values that may be overwritten
13859 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13860 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13864 char *token_prefix;
13865 struct RectWithBorder *struct_ptr;
13869 { "viewport.window", &viewport.window[i] },
13870 { "viewport.playfield", &viewport.playfield[i] },
13871 { "viewport.door_1", &viewport.door_1[i] },
13872 { "viewport.door_2", &viewport.door_2[i] }
13875 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13877 struct TokenIntPtrInfo vp_config[] =
13879 { ".x", &vp_struct[j].struct_ptr->x },
13880 { ".y", &vp_struct[j].struct_ptr->y },
13881 { ".width", &vp_struct[j].struct_ptr->width },
13882 { ".height", &vp_struct[j].struct_ptr->height },
13883 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13884 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13885 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13886 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13887 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13888 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13889 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13890 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13891 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13892 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13893 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13894 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13895 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13896 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13897 { ".align", &vp_struct[j].struct_ptr->align },
13898 { ".valign", &vp_struct[j].struct_ptr->valign }
13901 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13903 char *token = getStringCat2(vp_struct[j].token_prefix,
13904 vp_config[k].token);
13905 char *value = getHashEntry(setup_file_hash, token);
13908 *vp_config[k].value = get_token_parameter_value(token, value);
13915 // special case: initialize with default values that may be overwritten
13916 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13917 for (i = 0; title_info[i].info != NULL; i++)
13919 struct TitleFadingInfo *info = title_info[i].info;
13920 char *base_token = title_info[i].text;
13922 for (j = 0; title_tokens[j].type != -1; j++)
13924 char *token = getStringCat2(base_token, title_tokens[j].text);
13925 char *value = getHashEntry(setup_file_hash, token);
13929 int parameter_value = get_token_parameter_value(token, value);
13933 *(int *)title_tokens[j].value = (int)parameter_value;
13942 // special case: initialize with default values that may be overwritten
13943 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13944 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13946 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13947 char *base_token = titlemessage_arrays[i].text;
13949 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13951 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13952 char *value = getHashEntry(setup_file_hash, token);
13956 int parameter_value = get_token_parameter_value(token, value);
13958 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13962 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13963 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13965 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13975 // read (and overwrite with) values that may be specified in config file
13976 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13978 // special case: check if network and preview player positions are redefined
13979 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13981 freeSetupFileHash(setup_file_hash);
13984 void LoadMenuDesignSettings(void)
13986 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13988 InitMenuDesignSettings_Static();
13989 InitMenuDesignSettings_SpecialPreProcessing();
13990 InitMenuDesignSettings_PreviewPlayers();
13992 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13994 // first look for special settings configured in level series config
13995 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13997 if (fileExists(filename_base))
13998 LoadMenuDesignSettingsFromFilename(filename_base);
14001 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14003 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14004 LoadMenuDesignSettingsFromFilename(filename_local);
14006 InitMenuDesignSettings_SpecialPostProcessing();
14009 void LoadMenuDesignSettings_AfterGraphics(void)
14011 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14014 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14015 boolean ignore_defaults)
14019 for (i = 0; sound_config_vars[i].token != NULL; i++)
14021 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14023 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14024 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14028 *sound_config_vars[i].value =
14029 get_token_parameter_value(sound_config_vars[i].token, value);
14033 void InitSoundSettings_Static(void)
14035 // always start with reliable default values from static default config
14036 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14039 static void LoadSoundSettingsFromFilename(char *filename)
14041 SetupFileHash *setup_file_hash;
14043 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14046 // read (and overwrite with) values that may be specified in config file
14047 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14049 freeSetupFileHash(setup_file_hash);
14052 void LoadSoundSettings(void)
14054 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14056 InitSoundSettings_Static();
14058 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14060 // first look for special settings configured in level series config
14061 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14063 if (fileExists(filename_base))
14064 LoadSoundSettingsFromFilename(filename_base);
14067 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14069 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14070 LoadSoundSettingsFromFilename(filename_local);
14073 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14075 char *filename = getEditorSetupFilename();
14076 SetupFileList *setup_file_list, *list;
14077 SetupFileHash *element_hash;
14078 int num_unknown_tokens = 0;
14081 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14084 element_hash = newSetupFileHash();
14086 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14087 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14089 // determined size may be larger than needed (due to unknown elements)
14091 for (list = setup_file_list; list != NULL; list = list->next)
14094 // add space for up to 3 more elements for padding that may be needed
14095 *num_elements += 3;
14097 // free memory for old list of elements, if needed
14098 checked_free(*elements);
14100 // allocate memory for new list of elements
14101 *elements = checked_malloc(*num_elements * sizeof(int));
14104 for (list = setup_file_list; list != NULL; list = list->next)
14106 char *value = getHashEntry(element_hash, list->token);
14108 if (value == NULL) // try to find obsolete token mapping
14110 char *mapped_token = get_mapped_token(list->token);
14112 if (mapped_token != NULL)
14114 value = getHashEntry(element_hash, mapped_token);
14116 free(mapped_token);
14122 (*elements)[(*num_elements)++] = atoi(value);
14126 if (num_unknown_tokens == 0)
14129 Warn("unknown token(s) found in config file:");
14130 Warn("- config file: '%s'", filename);
14132 num_unknown_tokens++;
14135 Warn("- token: '%s'", list->token);
14139 if (num_unknown_tokens > 0)
14142 while (*num_elements % 4) // pad with empty elements, if needed
14143 (*elements)[(*num_elements)++] = EL_EMPTY;
14145 freeSetupFileList(setup_file_list);
14146 freeSetupFileHash(element_hash);
14149 for (i = 0; i < *num_elements; i++)
14150 Debug("editor", "element '%s' [%d]\n",
14151 element_info[(*elements)[i]].token_name, (*elements)[i]);
14155 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14158 SetupFileHash *setup_file_hash = NULL;
14159 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14160 char *filename_music, *filename_prefix, *filename_info;
14166 token_to_value_ptr[] =
14168 { "title_header", &tmp_music_file_info.title_header },
14169 { "artist_header", &tmp_music_file_info.artist_header },
14170 { "album_header", &tmp_music_file_info.album_header },
14171 { "year_header", &tmp_music_file_info.year_header },
14172 { "played_header", &tmp_music_file_info.played_header },
14174 { "title", &tmp_music_file_info.title },
14175 { "artist", &tmp_music_file_info.artist },
14176 { "album", &tmp_music_file_info.album },
14177 { "year", &tmp_music_file_info.year },
14178 { "played", &tmp_music_file_info.played },
14184 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14185 getCustomMusicFilename(basename));
14187 if (filename_music == NULL)
14190 // ---------- try to replace file extension ----------
14192 filename_prefix = getStringCopy(filename_music);
14193 if (strrchr(filename_prefix, '.') != NULL)
14194 *strrchr(filename_prefix, '.') = '\0';
14195 filename_info = getStringCat2(filename_prefix, ".txt");
14197 if (fileExists(filename_info))
14198 setup_file_hash = loadSetupFileHash(filename_info);
14200 free(filename_prefix);
14201 free(filename_info);
14203 if (setup_file_hash == NULL)
14205 // ---------- try to add file extension ----------
14207 filename_prefix = getStringCopy(filename_music);
14208 filename_info = getStringCat2(filename_prefix, ".txt");
14210 if (fileExists(filename_info))
14211 setup_file_hash = loadSetupFileHash(filename_info);
14213 free(filename_prefix);
14214 free(filename_info);
14217 if (setup_file_hash == NULL)
14220 // ---------- music file info found ----------
14222 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14224 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14226 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14228 *token_to_value_ptr[i].value_ptr =
14229 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14232 tmp_music_file_info.basename = getStringCopy(basename);
14233 tmp_music_file_info.music = music;
14234 tmp_music_file_info.is_sound = is_sound;
14236 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14237 *new_music_file_info = tmp_music_file_info;
14239 return new_music_file_info;
14242 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14244 return get_music_file_info_ext(basename, music, FALSE);
14247 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14249 return get_music_file_info_ext(basename, sound, TRUE);
14252 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14253 char *basename, boolean is_sound)
14255 for (; list != NULL; list = list->next)
14256 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14262 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14264 return music_info_listed_ext(list, basename, FALSE);
14267 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14269 return music_info_listed_ext(list, basename, TRUE);
14272 void LoadMusicInfo(void)
14274 int num_music_noconf = getMusicListSize_NoConf();
14275 int num_music = getMusicListSize();
14276 int num_sounds = getSoundListSize();
14277 struct FileInfo *music, *sound;
14278 struct MusicFileInfo *next, **new;
14282 while (music_file_info != NULL)
14284 next = music_file_info->next;
14286 checked_free(music_file_info->basename);
14288 checked_free(music_file_info->title_header);
14289 checked_free(music_file_info->artist_header);
14290 checked_free(music_file_info->album_header);
14291 checked_free(music_file_info->year_header);
14292 checked_free(music_file_info->played_header);
14294 checked_free(music_file_info->title);
14295 checked_free(music_file_info->artist);
14296 checked_free(music_file_info->album);
14297 checked_free(music_file_info->year);
14298 checked_free(music_file_info->played);
14300 free(music_file_info);
14302 music_file_info = next;
14305 new = &music_file_info;
14307 // get (configured or unconfigured) music file info for all levels
14308 for (i = leveldir_current->first_level;
14309 i <= leveldir_current->last_level; i++)
14313 if (levelset.music[i] != MUS_UNDEFINED)
14315 // get music file info for configured level music
14316 music_nr = levelset.music[i];
14318 else if (num_music_noconf > 0)
14320 // get music file info for unconfigured level music
14321 int level_pos = i - leveldir_current->first_level;
14323 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14330 char *basename = getMusicInfoEntryFilename(music_nr);
14332 if (basename == NULL)
14335 if (!music_info_listed(music_file_info, basename))
14337 *new = get_music_file_info(basename, music_nr);
14340 new = &(*new)->next;
14344 // get music file info for all remaining configured music files
14345 for (i = 0; i < num_music; i++)
14347 music = getMusicListEntry(i);
14349 if (music->filename == NULL)
14352 if (strEqual(music->filename, UNDEFINED_FILENAME))
14355 // a configured file may be not recognized as music
14356 if (!FileIsMusic(music->filename))
14359 if (!music_info_listed(music_file_info, music->filename))
14361 *new = get_music_file_info(music->filename, i);
14364 new = &(*new)->next;
14368 // get sound file info for all configured sound files
14369 for (i = 0; i < num_sounds; i++)
14371 sound = getSoundListEntry(i);
14373 if (sound->filename == NULL)
14376 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14379 // a configured file may be not recognized as sound
14380 if (!FileIsSound(sound->filename))
14383 if (!sound_info_listed(music_file_info, sound->filename))
14385 *new = get_sound_file_info(sound->filename, i);
14387 new = &(*new)->next;
14391 // add pointers to previous list nodes
14393 struct MusicFileInfo *node = music_file_info;
14395 while (node != NULL)
14398 node->next->prev = node;
14404 static void add_helpanim_entry(int element, int action, int direction,
14405 int delay, int *num_list_entries)
14407 struct HelpAnimInfo *new_list_entry;
14408 (*num_list_entries)++;
14411 checked_realloc(helpanim_info,
14412 *num_list_entries * sizeof(struct HelpAnimInfo));
14413 new_list_entry = &helpanim_info[*num_list_entries - 1];
14415 new_list_entry->element = element;
14416 new_list_entry->action = action;
14417 new_list_entry->direction = direction;
14418 new_list_entry->delay = delay;
14421 static void print_unknown_token(char *filename, char *token, int token_nr)
14426 Warn("unknown token(s) found in config file:");
14427 Warn("- config file: '%s'", filename);
14430 Warn("- token: '%s'", token);
14433 static void print_unknown_token_end(int token_nr)
14439 void LoadHelpAnimInfo(void)
14441 char *filename = getHelpAnimFilename();
14442 SetupFileList *setup_file_list = NULL, *list;
14443 SetupFileHash *element_hash, *action_hash, *direction_hash;
14444 int num_list_entries = 0;
14445 int num_unknown_tokens = 0;
14448 if (fileExists(filename))
14449 setup_file_list = loadSetupFileList(filename);
14451 if (setup_file_list == NULL)
14453 // use reliable default values from static configuration
14454 SetupFileList *insert_ptr;
14456 insert_ptr = setup_file_list =
14457 newSetupFileList(helpanim_config[0].token,
14458 helpanim_config[0].value);
14460 for (i = 1; helpanim_config[i].token; i++)
14461 insert_ptr = addListEntry(insert_ptr,
14462 helpanim_config[i].token,
14463 helpanim_config[i].value);
14466 element_hash = newSetupFileHash();
14467 action_hash = newSetupFileHash();
14468 direction_hash = newSetupFileHash();
14470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14471 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14473 for (i = 0; i < NUM_ACTIONS; i++)
14474 setHashEntry(action_hash, element_action_info[i].suffix,
14475 i_to_a(element_action_info[i].value));
14477 // do not store direction index (bit) here, but direction value!
14478 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14479 setHashEntry(direction_hash, element_direction_info[i].suffix,
14480 i_to_a(1 << element_direction_info[i].value));
14482 for (list = setup_file_list; list != NULL; list = list->next)
14484 char *element_token, *action_token, *direction_token;
14485 char *element_value, *action_value, *direction_value;
14486 int delay = atoi(list->value);
14488 if (strEqual(list->token, "end"))
14490 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14495 /* first try to break element into element/action/direction parts;
14496 if this does not work, also accept combined "element[.act][.dir]"
14497 elements (like "dynamite.active"), which are unique elements */
14499 if (strchr(list->token, '.') == NULL) // token contains no '.'
14501 element_value = getHashEntry(element_hash, list->token);
14502 if (element_value != NULL) // element found
14503 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14504 &num_list_entries);
14507 // no further suffixes found -- this is not an element
14508 print_unknown_token(filename, list->token, num_unknown_tokens++);
14514 // token has format "<prefix>.<something>"
14516 action_token = strchr(list->token, '.'); // suffix may be action ...
14517 direction_token = action_token; // ... or direction
14519 element_token = getStringCopy(list->token);
14520 *strchr(element_token, '.') = '\0';
14522 element_value = getHashEntry(element_hash, element_token);
14524 if (element_value == NULL) // this is no element
14526 element_value = getHashEntry(element_hash, list->token);
14527 if (element_value != NULL) // combined element found
14528 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14529 &num_list_entries);
14531 print_unknown_token(filename, list->token, num_unknown_tokens++);
14533 free(element_token);
14538 action_value = getHashEntry(action_hash, action_token);
14540 if (action_value != NULL) // action found
14542 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14543 &num_list_entries);
14545 free(element_token);
14550 direction_value = getHashEntry(direction_hash, direction_token);
14552 if (direction_value != NULL) // direction found
14554 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14555 &num_list_entries);
14557 free(element_token);
14562 if (strchr(action_token + 1, '.') == NULL)
14564 // no further suffixes found -- this is not an action nor direction
14566 element_value = getHashEntry(element_hash, list->token);
14567 if (element_value != NULL) // combined element found
14568 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14569 &num_list_entries);
14571 print_unknown_token(filename, list->token, num_unknown_tokens++);
14573 free(element_token);
14578 // token has format "<prefix>.<suffix>.<something>"
14580 direction_token = strchr(action_token + 1, '.');
14582 action_token = getStringCopy(action_token);
14583 *strchr(action_token + 1, '.') = '\0';
14585 action_value = getHashEntry(action_hash, action_token);
14587 if (action_value == NULL) // this is no action
14589 element_value = getHashEntry(element_hash, list->token);
14590 if (element_value != NULL) // combined element found
14591 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14592 &num_list_entries);
14594 print_unknown_token(filename, list->token, num_unknown_tokens++);
14596 free(element_token);
14597 free(action_token);
14602 direction_value = getHashEntry(direction_hash, direction_token);
14604 if (direction_value != NULL) // direction found
14606 add_helpanim_entry(atoi(element_value), atoi(action_value),
14607 atoi(direction_value), delay, &num_list_entries);
14609 free(element_token);
14610 free(action_token);
14615 // this is no direction
14617 element_value = getHashEntry(element_hash, list->token);
14618 if (element_value != NULL) // combined element found
14619 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14620 &num_list_entries);
14622 print_unknown_token(filename, list->token, num_unknown_tokens++);
14624 free(element_token);
14625 free(action_token);
14628 print_unknown_token_end(num_unknown_tokens);
14630 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14631 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14633 freeSetupFileList(setup_file_list);
14634 freeSetupFileHash(element_hash);
14635 freeSetupFileHash(action_hash);
14636 freeSetupFileHash(direction_hash);
14639 for (i = 0; i < num_list_entries; i++)
14640 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14641 EL_NAME(helpanim_info[i].element),
14642 helpanim_info[i].element,
14643 helpanim_info[i].action,
14644 helpanim_info[i].direction,
14645 helpanim_info[i].delay);
14649 void LoadHelpTextInfo(void)
14651 char *filename = getHelpTextFilename();
14654 if (helptext_info != NULL)
14656 freeSetupFileHash(helptext_info);
14657 helptext_info = NULL;
14660 if (fileExists(filename))
14661 helptext_info = loadSetupFileHash(filename);
14663 if (helptext_info == NULL)
14665 // use reliable default values from static configuration
14666 helptext_info = newSetupFileHash();
14668 for (i = 0; helptext_config[i].token; i++)
14669 setHashEntry(helptext_info,
14670 helptext_config[i].token,
14671 helptext_config[i].value);
14675 BEGIN_HASH_ITERATION(helptext_info, itr)
14677 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14678 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14680 END_HASH_ITERATION(hash, itr)
14685 // ----------------------------------------------------------------------------
14687 // ----------------------------------------------------------------------------
14689 #define MAX_NUM_CONVERT_LEVELS 1000
14691 void ConvertLevels(void)
14693 static LevelDirTree *convert_leveldir = NULL;
14694 static int convert_level_nr = -1;
14695 static int num_levels_handled = 0;
14696 static int num_levels_converted = 0;
14697 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14700 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14701 global.convert_leveldir);
14703 if (convert_leveldir == NULL)
14704 Fail("no such level identifier: '%s'", global.convert_leveldir);
14706 leveldir_current = convert_leveldir;
14708 if (global.convert_level_nr != -1)
14710 convert_leveldir->first_level = global.convert_level_nr;
14711 convert_leveldir->last_level = global.convert_level_nr;
14714 convert_level_nr = convert_leveldir->first_level;
14716 PrintLine("=", 79);
14717 Print("Converting levels\n");
14718 PrintLine("-", 79);
14719 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14720 Print("Level series name: '%s'\n", convert_leveldir->name);
14721 Print("Level series author: '%s'\n", convert_leveldir->author);
14722 Print("Number of levels: %d\n", convert_leveldir->levels);
14723 PrintLine("=", 79);
14726 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14727 levels_failed[i] = FALSE;
14729 while (convert_level_nr <= convert_leveldir->last_level)
14731 char *level_filename;
14734 level_nr = convert_level_nr++;
14736 Print("Level %03d: ", level_nr);
14738 LoadLevel(level_nr);
14739 if (level.no_level_file || level.no_valid_file)
14741 Print("(no level)\n");
14745 Print("converting level ... ");
14748 // special case: conversion of some EMC levels as requested by ACME
14749 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14752 level_filename = getDefaultLevelFilename(level_nr);
14753 new_level = !fileExists(level_filename);
14757 SaveLevel(level_nr);
14759 num_levels_converted++;
14761 Print("converted.\n");
14765 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14766 levels_failed[level_nr] = TRUE;
14768 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14771 num_levels_handled++;
14775 PrintLine("=", 79);
14776 Print("Number of levels handled: %d\n", num_levels_handled);
14777 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14778 (num_levels_handled ?
14779 num_levels_converted * 100 / num_levels_handled : 0));
14780 PrintLine("-", 79);
14781 Print("Summary (for automatic parsing by scripts):\n");
14782 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14783 convert_leveldir->identifier, num_levels_converted,
14784 num_levels_handled,
14785 (num_levels_handled ?
14786 num_levels_converted * 100 / num_levels_handled : 0));
14788 if (num_levels_handled != num_levels_converted)
14790 Print(", FAILED:");
14791 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14792 if (levels_failed[i])
14797 PrintLine("=", 79);
14799 CloseAllAndExit(0);
14803 // ----------------------------------------------------------------------------
14804 // create and save images for use in level sketches (raw BMP format)
14805 // ----------------------------------------------------------------------------
14807 void CreateLevelSketchImages(void)
14813 InitElementPropertiesGfxElement();
14815 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14816 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14818 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14820 int element = getMappedElement(i);
14821 char basename1[16];
14822 char basename2[16];
14826 sprintf(basename1, "%04d.bmp", i);
14827 sprintf(basename2, "%04ds.bmp", i);
14829 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14830 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14832 DrawSizedElement(0, 0, element, TILESIZE);
14833 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14835 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14836 Fail("cannot save level sketch image file '%s'", filename1);
14838 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14839 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14841 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14842 Fail("cannot save level sketch image file '%s'", filename2);
14847 // create corresponding SQL statements (for normal and small images)
14850 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14851 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14854 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14855 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14857 // optional: create content for forum level sketch demonstration post
14859 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14862 FreeBitmap(bitmap1);
14863 FreeBitmap(bitmap2);
14866 fprintf(stderr, "\n");
14868 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14870 CloseAllAndExit(0);
14874 // ----------------------------------------------------------------------------
14875 // create and save images for element collecting animations (raw BMP format)
14876 // ----------------------------------------------------------------------------
14878 static boolean createCollectImage(int element)
14880 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14883 void CreateCollectElementImages(void)
14887 int anim_frames = num_steps - 1;
14888 int tile_size = TILESIZE;
14889 int anim_width = tile_size * anim_frames;
14890 int anim_height = tile_size;
14891 int num_collect_images = 0;
14892 int pos_collect_images = 0;
14894 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14895 if (createCollectImage(i))
14896 num_collect_images++;
14898 Info("Creating %d element collecting animation images ...",
14899 num_collect_images);
14901 int dst_width = anim_width * 2;
14902 int dst_height = anim_height * num_collect_images / 2;
14903 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14904 char *basename_bmp = "RocksCollect.bmp";
14905 char *basename_png = "RocksCollect.png";
14906 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14907 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14908 int len_filename_bmp = strlen(filename_bmp);
14909 int len_filename_png = strlen(filename_png);
14910 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14911 char cmd_convert[max_command_len];
14913 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14917 // force using RGBA surface for destination bitmap
14918 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14919 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14921 dst_bitmap->surface =
14922 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14924 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14926 if (!createCollectImage(i))
14929 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14930 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14931 int graphic = el2img(i);
14932 char *token_name = element_info[i].token_name;
14933 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14934 Bitmap *src_bitmap;
14937 Info("- creating collecting image for '%s' ...", token_name);
14939 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14941 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14942 tile_size, tile_size, 0, 0);
14944 // force using RGBA surface for temporary bitmap (using transparent black)
14945 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14946 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14948 tmp_bitmap->surface =
14949 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14951 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14953 for (j = 0; j < anim_frames; j++)
14955 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14956 int frame_size = frame_size_final * num_steps;
14957 int offset = (tile_size - frame_size_final) / 2;
14958 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14960 while (frame_size > frame_size_final)
14964 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14966 FreeBitmap(frame_bitmap);
14968 frame_bitmap = half_bitmap;
14971 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14972 frame_size_final, frame_size_final,
14973 dst_x + j * tile_size + offset, dst_y + offset);
14975 FreeBitmap(frame_bitmap);
14978 tmp_bitmap->surface_masked = NULL;
14980 FreeBitmap(tmp_bitmap);
14982 pos_collect_images++;
14985 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14986 Fail("cannot save element collecting image file '%s'", filename_bmp);
14988 FreeBitmap(dst_bitmap);
14990 Info("Converting image file from BMP to PNG ...");
14992 if (system(cmd_convert) != 0)
14993 Fail("converting image file failed");
14995 unlink(filename_bmp);
14999 CloseAllAndExit(0);
15003 // ----------------------------------------------------------------------------
15004 // create and save images for custom and group elements (raw BMP format)
15005 // ----------------------------------------------------------------------------
15007 void CreateCustomElementImages(char *directory)
15009 char *src_basename = "RocksCE-template.ilbm";
15010 char *dst_basename = "RocksCE.bmp";
15011 char *src_filename = getPath2(directory, src_basename);
15012 char *dst_filename = getPath2(directory, dst_basename);
15013 Bitmap *src_bitmap;
15015 int yoffset_ce = 0;
15016 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15019 InitVideoDefaults();
15021 ReCreateBitmap(&backbuffer, video.width, video.height);
15023 src_bitmap = LoadImage(src_filename);
15025 bitmap = CreateBitmap(TILEX * 16 * 2,
15026 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15036 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15037 TILEX * x, TILEY * y + yoffset_ce);
15039 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15041 TILEX * x + TILEX * 16,
15042 TILEY * y + yoffset_ce);
15044 for (j = 2; j >= 0; j--)
15048 BlitBitmap(src_bitmap, bitmap,
15049 TILEX + c * 7, 0, 6, 10,
15050 TILEX * x + 6 + j * 7,
15051 TILEY * y + 11 + yoffset_ce);
15053 BlitBitmap(src_bitmap, bitmap,
15054 TILEX + c * 8, TILEY, 6, 10,
15055 TILEX * 16 + TILEX * x + 6 + j * 8,
15056 TILEY * y + 10 + yoffset_ce);
15062 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15069 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15070 TILEX * x, TILEY * y + yoffset_ge);
15072 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15074 TILEX * x + TILEX * 16,
15075 TILEY * y + yoffset_ge);
15077 for (j = 1; j >= 0; j--)
15081 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15082 TILEX * x + 6 + j * 10,
15083 TILEY * y + 11 + yoffset_ge);
15085 BlitBitmap(src_bitmap, bitmap,
15086 TILEX + c * 8, TILEY + 12, 6, 10,
15087 TILEX * 16 + TILEX * x + 10 + j * 8,
15088 TILEY * y + 10 + yoffset_ge);
15094 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15095 Fail("cannot save CE graphics file '%s'", dst_filename);
15097 FreeBitmap(bitmap);
15099 CloseAllAndExit(0);