1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
28 #define ENABLE_UNUSED_CODE 0 // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
30 #define ENABLE_RESERVED_CODE 0 // reserved for later use
32 #define CHUNK_ID_LEN 4 // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE -1 // do not write chunk size
36 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
39 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
62 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
185 -1, SAVE_CONF_ALWAYS,
186 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
217 &li.can_move_into_acid_bits, ~0 // default: everything can
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 // default: always deadly
226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
227 &li.em_explodes_by_fire, FALSE
231 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
232 &li.score[SC_TIME_BONUS], 1
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
237 &li.auto_exit_sokoban, FALSE
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
242 &li.auto_count_gems, FALSE
246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
247 &li.solved_by_one_player, FALSE
251 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
252 &li.time_score_base, 1
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
257 &li.rate_time_over_score, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
262 &li.bd_intermission, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
267 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
272 &li.bd_pal_timing, FALSE
276 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
277 &li.bd_cycle_delay_ms, 200
281 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
282 &li.bd_cycle_delay_c64, 0
286 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
287 &li.bd_hatching_delay_cycles, 21
291 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
292 &li.bd_hatching_delay_seconds, 2
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
297 &li.bd_line_shifting_borders, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
302 &li.bd_scan_first_and_last_row, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
307 &li.bd_short_explosions, TRUE
311 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
312 &li.bd_cave_random_seed_c64, 0
316 TYPE_INTEGER, CONF_VALUE_32_BIT(3),
317 &li.bd_color_b, GD_C64_COLOR(0)
321 TYPE_INTEGER, CONF_VALUE_32_BIT(4),
322 &li.bd_color_0, GD_C64_COLOR(0)
326 TYPE_INTEGER, CONF_VALUE_32_BIT(5),
327 &li.bd_color_1, GD_C64_COLOR(8)
331 TYPE_INTEGER, CONF_VALUE_32_BIT(6),
332 &li.bd_color_2, GD_C64_COLOR(11)
336 TYPE_INTEGER, CONF_VALUE_32_BIT(7),
337 &li.bd_color_3, GD_C64_COLOR(1)
341 TYPE_INTEGER, CONF_VALUE_32_BIT(8),
342 &li.bd_color_4, GD_C64_COLOR(5)
346 TYPE_INTEGER, CONF_VALUE_32_BIT(9),
347 &li.bd_color_5, GD_C64_COLOR(6)
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
359 // (these values are the same for each player)
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
363 &li.block_last_field, FALSE // default case for EM levels
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
368 &li.sp_block_last_field, TRUE // default case for SP levels
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
373 &li.instant_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
378 &li.can_pass_to_walkable, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
383 &li.block_snap_field, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
388 &li.continuous_snapping, TRUE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
393 &li.shifted_relocation, FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
398 &li.lazy_relocation, FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
403 &li.finish_dig_collect, TRUE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
408 &li.keep_walkable_ce, FALSE
411 // (these values are different for each player)
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[0], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[0], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[0], EL_PLAYER_1
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[0], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[0], EL_PLAYER_1
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[0], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[0], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[1], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[1], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[1], EL_PLAYER_2
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[1], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[1], EL_PLAYER_2
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[1], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[1], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
529 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
534 &li.initial_player_gravity[2], FALSE
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
539 &li.use_start_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
544 &li.start_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
549 &li.use_artwork_element[2], FALSE
553 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
554 &li.artwork_element[2], EL_PLAYER_3
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
559 &li.use_explosion_element[2], FALSE
563 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
564 &li.explosion_element[2], EL_PLAYER_3
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
569 &li.use_initial_inventory[2], FALSE
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
574 &li.initial_inventory_size[2], 1
578 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
579 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
585 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
586 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
591 &li.initial_player_gravity[3], FALSE
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
596 &li.use_start_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
601 &li.start_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
606 &li.use_artwork_element[3], FALSE
610 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
611 &li.artwork_element[3], EL_PLAYER_4
615 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
616 &li.use_explosion_element[3], FALSE
620 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
621 &li.explosion_element[3], EL_PLAYER_4
625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
626 &li.use_initial_inventory[3], FALSE
630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
631 &li.initial_inventory_size[3], 1
635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
636 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
640 // (these values are only valid for BD style levels)
641 // (some values for BD style amoeba following below)
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.bd_diagonal_movements, FALSE
649 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
650 &li.bd_topmost_player_active, TRUE
654 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
655 &li.bd_pushing_prob, 25
659 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
660 &li.bd_pushing_prob_with_sweet, 100
664 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
665 &li.bd_push_mega_rock_with_sweet, FALSE
669 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
670 &li.bd_snap_element, EL_EMPTY
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_sand_looks_like, EL_BD_SAND
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BD_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BD_ROCK
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND_EXTRA], 20
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
698 &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BD_DIAMOND
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_explodes_to, EL_BD_EXPLODING_1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BD_EXPLODING_1
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_explodes_to, EL_BD_DIAMOND_GROWING_1
725 EL_BD_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BD_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BD_ROCK_GROWING_1
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BD_EXPLODING_1
743 EL_BD_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BD_DIAMOND
749 EL_BD_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BD_WALL
755 EL_BD_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BD_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BD_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BD_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BD_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BD_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BD_MAGIC_WALL, -1,
788 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
792 EL_BD_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
797 EL_BD_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
802 EL_BD_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
807 EL_BD_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
812 EL_BD_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
817 EL_BD_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
824 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
825 &li.bd_clock_extra_time, 30
829 EL_BD_VOODOO_DOLL, -1,
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
831 &li.bd_voodoo_collects_diamonds, FALSE
834 EL_BD_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
836 &li.bd_voodoo_hurt_kills_player, FALSE
839 EL_BD_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
841 &li.bd_voodoo_dies_by_rock, FALSE
844 EL_BD_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
846 &li.bd_voodoo_vanish_by_explosion, TRUE
849 EL_BD_VOODOO_DOLL, -1,
850 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
851 &li.bd_voodoo_penalty_time, 30
856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
857 &li.bd_slime_is_predictable, TRUE
861 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
862 &li.bd_slime_permeability_rate, 100
866 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
867 &li.bd_slime_permeability_bits_c64, 0
871 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
872 &li.bd_slime_random_seed_c64, -1
876 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
877 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
882 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
887 &li.bd_slime_eats_element_2, EL_BD_ROCK
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
892 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
897 &li.bd_slime_eats_element_3, EL_BD_NUT
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
902 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
907 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
908 &li.bd_acid_eats_element, EL_BD_SAND
912 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
913 &li.bd_acid_spread_rate, 3
917 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
918 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
923 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
924 &li.bd_biter_move_delay, 0
928 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
929 &li.bd_biter_eats_element, EL_BD_DIAMOND
934 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
935 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
939 EL_BD_EXPANDABLE_WALL_ANY, -1,
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
941 &li.bd_change_expanding_wall, FALSE
944 EL_BD_EXPANDABLE_WALL_ANY, -1,
945 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
946 &li.bd_expanding_wall_looks_like, EL_BD_WALL
950 EL_BD_REPLICATOR, -1,
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &li.bd_replicators_active, TRUE
955 EL_BD_REPLICATOR, -1,
956 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
957 &li.bd_replicator_create_delay, 4
961 EL_BD_CONVEYOR_LEFT, -1,
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
963 &li.bd_conveyor_belts_active, TRUE
966 EL_BD_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
968 &li.bd_conveyor_belts_changed, FALSE
973 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
974 &li.bd_water_cannot_flow_down, FALSE
979 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
980 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
984 EL_BD_PNEUMATIC_HAMMER, -1,
985 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
986 &li.bd_hammer_walls_break_delay, 5
989 EL_BD_PNEUMATIC_HAMMER, -1,
990 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
991 &li.bd_hammer_walls_reappear, FALSE
994 EL_BD_PNEUMATIC_HAMMER, -1,
995 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
996 &li.bd_hammer_walls_reappear_delay, 100
1000 EL_BD_ROCKET_LAUNCHER, -1,
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1002 &li.bd_infinite_rockets, FALSE
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1008 &li.bd_num_skeletons_needed_for_pot, 5
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1013 &li.bd_skeleton_worth_num_diamonds, 0
1017 EL_BD_CREATURE_SWITCH, -1,
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1019 &li.bd_creatures_start_backwards, FALSE
1022 EL_BD_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1024 &li.bd_creatures_turn_on_hatching, FALSE
1027 EL_BD_CREATURE_SWITCH, -1,
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1029 &li.bd_creatures_auto_turn_delay, 0
1033 EL_BD_GRAVITY_SWITCH, -1,
1034 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1035 &li.bd_gravity_direction, GD_MV_DOWN
1038 EL_BD_GRAVITY_SWITCH, -1,
1039 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1040 &li.bd_gravity_switch_active, FALSE
1043 EL_BD_GRAVITY_SWITCH, -1,
1044 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1045 &li.bd_gravity_switch_delay, 10
1048 EL_BD_GRAVITY_SWITCH, -1,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1050 &li.bd_gravity_affects_all, TRUE
1053 // (the following values are related to various game elements)
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.score[SC_EMERALD], 10
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.score[SC_DIAMOND], 10
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1070 &li.score[SC_BUG], 10
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1076 &li.score[SC_SPACESHIP], 10
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1082 &li.score[SC_PACMAN], 10
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1088 &li.score[SC_NUT], 10
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1094 &li.score[SC_DYNAMITE], 10
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1100 &li.score[SC_KEY], 10
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1106 &li.score[SC_PEARL], 10
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1112 &li.score[SC_CRYSTAL], 10
1115 // (amoeba values used by R'n'D game engine only)
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1119 &li.amoeba_content, EL_DIAMOND
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1124 &li.amoeba_speed, 10
1128 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1129 &li.grow_into_diggable, TRUE
1131 // (amoeba values used by BD game engine only)
1134 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1135 &li.bd_amoeba_wait_for_hatching, FALSE
1139 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1140 &li.bd_amoeba_start_immediately, TRUE
1144 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1145 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1150 &li.bd_amoeba_threshold_too_big, 200
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1155 &li.bd_amoeba_slow_growth_time, 200
1159 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1160 &li.bd_amoeba_slow_growth_rate, 3
1164 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1165 &li.bd_amoeba_fast_growth_rate, 25
1169 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1170 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1175 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1181 &li.bd_amoeba_2_threshold_too_big, 200
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1186 &li.bd_amoeba_2_slow_growth_time, 200
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1191 &li.bd_amoeba_2_slow_growth_rate, 3
1195 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1196 &li.bd_amoeba_2_fast_growth_rate, 25
1200 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1201 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1205 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1206 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1211 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1215 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1216 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1221 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1222 &li.yamyam_content, EL_ROCK, NULL,
1223 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1227 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1228 &li.score[SC_YAMYAM], 10
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1234 &li.score[SC_ROBOT], 10
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1244 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1250 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1251 &li.time_magic_wall, 10
1255 EL_GAME_OF_LIFE, -1,
1256 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1257 &li.game_of_life[0], 2
1260 EL_GAME_OF_LIFE, -1,
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1262 &li.game_of_life[1], 3
1265 EL_GAME_OF_LIFE, -1,
1266 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1267 &li.game_of_life[2], 3
1270 EL_GAME_OF_LIFE, -1,
1271 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1272 &li.game_of_life[3], 3
1275 EL_GAME_OF_LIFE, -1,
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1277 &li.use_life_bugs, FALSE
1282 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1287 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1292 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1297 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1302 EL_TIMEGATE_SWITCH, -1,
1303 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1304 &li.time_timegate, 10
1308 EL_LIGHT_SWITCH_ACTIVE, -1,
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1314 EL_SHIELD_NORMAL, -1,
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1316 &li.shield_normal_time, 10
1319 EL_SHIELD_NORMAL, -1,
1320 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1321 &li.score[SC_SHIELD], 10
1325 EL_SHIELD_DEADLY, -1,
1326 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1327 &li.shield_deadly_time, 10
1330 EL_SHIELD_DEADLY, -1,
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1332 &li.score[SC_SHIELD], 10
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1343 &li.extra_time_score, 10
1347 EL_TIME_ORB_FULL, -1,
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1349 &li.time_orb_time, 10
1352 EL_TIME_ORB_FULL, -1,
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1354 &li.use_time_orb_bug, FALSE
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1360 &li.use_spring_bug, FALSE
1365 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1366 &li.android_move_time, 10
1370 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1371 &li.android_clone_time, 10
1374 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1375 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1376 &li.android_clone_element[0], EL_EMPTY, NULL,
1377 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1381 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1382 &li.android_clone_element[0], EL_EMPTY, NULL,
1383 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1388 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1389 &li.lenses_score, 10
1393 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1398 EL_EMC_MAGNIFIER, -1,
1399 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1400 &li.magnify_score, 10
1403 EL_EMC_MAGNIFIER, -1,
1404 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1405 &li.magnify_time, 10
1409 EL_EMC_MAGIC_BALL, -1,
1410 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1414 EL_EMC_MAGIC_BALL, -1,
1415 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1416 &li.ball_random, FALSE
1419 EL_EMC_MAGIC_BALL, -1,
1420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1421 &li.ball_active_initial, FALSE
1424 EL_EMC_MAGIC_BALL, -1,
1425 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1426 &li.ball_content, EL_EMPTY, NULL,
1427 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1431 EL_SOKOBAN_FIELD_EMPTY, -1,
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1433 &li.sb_fields_needed, TRUE
1437 EL_SOKOBAN_OBJECT, -1,
1438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1439 &li.sb_objects_needed, TRUE
1444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1445 &li.mm_laser_red, FALSE
1449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1450 &li.mm_laser_green, FALSE
1454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1455 &li.mm_laser_blue, TRUE
1460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1461 &li.df_laser_red, TRUE
1465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1466 &li.df_laser_green, TRUE
1470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1471 &li.df_laser_blue, FALSE
1475 EL_MM_FUSE_ACTIVE, -1,
1476 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1477 &li.mm_time_fuse, 25
1481 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1482 &li.mm_time_bomb, 75
1486 EL_MM_GRAY_BALL, -1,
1487 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1488 &li.mm_time_ball, 75
1491 EL_MM_GRAY_BALL, -1,
1492 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1493 &li.mm_ball_choice_mode, ANIM_RANDOM
1496 EL_MM_GRAY_BALL, -1,
1497 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1498 &li.mm_ball_content, EL_EMPTY, NULL,
1499 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1502 EL_MM_GRAY_BALL, -1,
1503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1504 &li.rotate_mm_ball_content, TRUE
1507 EL_MM_GRAY_BALL, -1,
1508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1509 &li.explode_mm_ball, FALSE
1513 EL_MM_STEEL_BLOCK, -1,
1514 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1515 &li.mm_time_block, 75
1518 EL_MM_LIGHTBALL, -1,
1519 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1520 &li.score[SC_ELEM_BONUS], 10
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1534 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1535 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1539 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1540 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1545 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1546 &xx_envelope.autowrap, FALSE
1550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1551 &xx_envelope.centered, FALSE
1556 TYPE_STRING, CONF_VALUE_BYTES(1),
1557 &xx_envelope.text, -1, NULL,
1558 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1559 &xx_default_string_empty[0]
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1573 TYPE_STRING, CONF_VALUE_BYTES(1),
1574 &xx_ei.description[0], -1,
1575 &yy_ei.description[0],
1576 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1577 &xx_default_description[0]
1582 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1583 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1586 #if ENABLE_RESERVED_CODE
1587 // (reserved for later use)
1590 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1591 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1599 &xx_ei.use_gfx_element, FALSE,
1600 &yy_ei.use_gfx_element
1604 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1605 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1606 &yy_ei.gfx_element_initial
1611 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1612 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1613 &yy_ei.access_direction
1618 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1619 &xx_ei.collect_score_initial, 10,
1620 &yy_ei.collect_score_initial
1624 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1625 &xx_ei.collect_count_initial, 1,
1626 &yy_ei.collect_count_initial
1631 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1632 &xx_ei.ce_value_fixed_initial, 0,
1633 &yy_ei.ce_value_fixed_initial
1637 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1638 &xx_ei.ce_value_random_initial, 0,
1639 &yy_ei.ce_value_random_initial
1643 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1644 &xx_ei.use_last_ce_value, FALSE,
1645 &yy_ei.use_last_ce_value
1650 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1651 &xx_ei.push_delay_fixed, 8,
1652 &yy_ei.push_delay_fixed
1656 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1657 &xx_ei.push_delay_random, 8,
1658 &yy_ei.push_delay_random
1662 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1663 &xx_ei.drop_delay_fixed, 0,
1664 &yy_ei.drop_delay_fixed
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1669 &xx_ei.drop_delay_random, 0,
1670 &yy_ei.drop_delay_random
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1675 &xx_ei.move_delay_fixed, 0,
1676 &yy_ei.move_delay_fixed
1680 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1681 &xx_ei.move_delay_random, 0,
1682 &yy_ei.move_delay_random
1686 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1687 &xx_ei.step_delay_fixed, 0,
1688 &yy_ei.step_delay_fixed
1692 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1693 &xx_ei.step_delay_random, 0,
1694 &yy_ei.step_delay_random
1699 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1700 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1705 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1706 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1707 &yy_ei.move_direction_initial
1711 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1712 &xx_ei.move_stepsize, TILEX / 8,
1713 &yy_ei.move_stepsize
1718 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1719 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1720 &yy_ei.move_enter_element
1724 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1725 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1726 &yy_ei.move_leave_element
1730 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1731 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1732 &yy_ei.move_leave_type
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1738 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1739 &yy_ei.slippery_type
1744 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1745 &xx_ei.explosion_type, EXPLODES_3X3,
1746 &yy_ei.explosion_type
1750 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1751 &xx_ei.explosion_delay, 16,
1752 &yy_ei.explosion_delay
1756 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1757 &xx_ei.ignition_delay, 8,
1758 &yy_ei.ignition_delay
1763 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1764 &xx_ei.content, EL_EMPTY_SPACE,
1766 &xx_num_contents, 1, 1
1769 // ---------- "num_change_pages" must be the last entry ---------------------
1772 -1, SAVE_CONF_ALWAYS,
1773 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1774 &xx_ei.num_change_pages, 1,
1775 &yy_ei.num_change_pages
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1788 // ---------- "current_change_page" must be the first entry -----------------
1791 -1, SAVE_CONF_ALWAYS,
1792 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1793 &xx_current_change_page, -1
1796 // ---------- (the remaining entries can be in any order) -------------------
1800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1801 &xx_change.can_change, FALSE
1806 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1807 &xx_event_bits[0], 0
1811 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1812 &xx_event_bits[1], 0
1817 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1818 &xx_change.trigger_player, CH_PLAYER_ANY
1822 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1823 &xx_change.trigger_side, CH_SIDE_ANY
1827 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1828 &xx_change.trigger_page, CH_PAGE_ANY
1833 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1834 &xx_change.target_element, EL_EMPTY_SPACE
1839 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1840 &xx_change.delay_fixed, 0
1844 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1845 &xx_change.delay_random, 0
1849 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1850 &xx_change.delay_frames, FRAMES_PER_SECOND
1855 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1856 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1862 &xx_change.explode, FALSE
1866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1867 &xx_change.use_target_content, FALSE
1871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1872 &xx_change.only_if_complete, FALSE
1876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1877 &xx_change.use_random_replace, FALSE
1881 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1882 &xx_change.random_percentage, 100
1886 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1887 &xx_change.replace_when, CP_WHEN_EMPTY
1892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1893 &xx_change.has_action, FALSE
1897 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1898 &xx_change.action_type, CA_NO_ACTION
1902 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1903 &xx_change.action_mode, CA_MODE_UNDEFINED
1907 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1908 &xx_change.action_arg, CA_ARG_UNDEFINED
1913 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1914 &xx_change.action_element, EL_EMPTY_SPACE
1919 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1920 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1921 &xx_num_contents, 1, 1
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1935 TYPE_STRING, CONF_VALUE_BYTES(1),
1936 &xx_ei.description[0], -1, NULL,
1937 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1938 &xx_default_description[0]
1943 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1944 &xx_ei.use_gfx_element, FALSE
1948 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1949 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1954 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1955 &xx_group.choice_mode, ANIM_RANDOM
1960 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1961 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1962 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1977 &xx_ei.use_gfx_element, FALSE
1981 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1982 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1992 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1997 &li.block_snap_field, TRUE
2001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2002 &li.continuous_snapping, TRUE
2006 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2007 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2011 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2012 &li.use_start_element[0], FALSE
2016 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2017 &li.start_element[0], EL_PLAYER_1
2021 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2022 &li.use_artwork_element[0], FALSE
2026 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2027 &li.artwork_element[0], EL_PLAYER_1
2031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2032 &li.use_explosion_element[0], FALSE
2036 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2037 &li.explosion_element[0], EL_PLAYER_1
2052 filetype_id_list[] =
2054 { LEVEL_FILE_TYPE_RND, "RND" },
2055 { LEVEL_FILE_TYPE_BD, "BD" },
2056 { LEVEL_FILE_TYPE_EM, "EM" },
2057 { LEVEL_FILE_TYPE_SP, "SP" },
2058 { LEVEL_FILE_TYPE_DX, "DX" },
2059 { LEVEL_FILE_TYPE_SB, "SB" },
2060 { LEVEL_FILE_TYPE_DC, "DC" },
2061 { LEVEL_FILE_TYPE_MM, "MM" },
2062 { LEVEL_FILE_TYPE_MM, "DF" },
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2071 static boolean check_special_flags(char *flag)
2073 if (strEqual(options.special_flags, flag) ||
2074 strEqual(leveldir_current->special_flags, flag))
2080 static struct DateInfo getCurrentDate(void)
2082 time_t epoch_seconds = time(NULL);
2083 struct tm *now = localtime(&epoch_seconds);
2084 struct DateInfo date;
2086 date.year = now->tm_year + 1900;
2087 date.month = now->tm_mon + 1;
2088 date.day = now->tm_mday;
2090 date.src = DATE_SRC_CLOCK;
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2099 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100 change->has_event[i] = FALSE;
2103 static void resetEventBits(void)
2107 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108 xx_event_bits[i] = 0;
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2115 /* important: only change event flag if corresponding event bit is set
2116 (this is because all xx_event_bits[] values are loaded separately,
2117 and all xx_event_bits[] values are set back to zero before loading
2118 another value xx_event_bits[x] (each value representing 32 flags)) */
2120 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122 change->has_event[i] = TRUE;
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2129 /* in contrast to the above function setEventFlagsFromEventBits(), it
2130 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131 depending on the corresponding change->has_event[i] values here, as
2132 all xx_event_bits[] values are reset in resetEventBits() before */
2134 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135 if (change->has_event[i])
2136 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2141 static char description[MAX_ELEMENT_NAME_LEN + 1];
2142 char *default_description = (ei->custom_description != NULL ?
2143 ei->custom_description :
2144 ei->editor_description);
2147 // always start with reliable default values
2148 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149 description[i] = '\0';
2151 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2154 return &description[0];
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2159 char *default_description = getDefaultElementDescription(ei);
2162 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163 ei->description[i] = default_description[i];
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2170 for (i = 0; conf[i].data_type != -1; i++)
2172 int default_value = conf[i].default_value;
2173 int data_type = conf[i].data_type;
2174 int conf_type = conf[i].conf_type;
2175 int byte_mask = conf_type & CONF_MASK_BYTES;
2177 if (byte_mask == CONF_MASK_MULTI_BYTES)
2179 int default_num_entities = conf[i].default_num_entities;
2180 int max_num_entities = conf[i].max_num_entities;
2182 *(int *)(conf[i].num_entities) = default_num_entities;
2184 if (data_type == TYPE_STRING)
2186 char *default_string = conf[i].default_string;
2187 char *string = (char *)(conf[i].value);
2189 strncpy(string, default_string, max_num_entities);
2191 else if (data_type == TYPE_ELEMENT_LIST)
2193 int *element_array = (int *)(conf[i].value);
2196 for (j = 0; j < max_num_entities; j++)
2197 element_array[j] = default_value;
2199 else if (data_type == TYPE_CONTENT_LIST)
2201 struct Content *content = (struct Content *)(conf[i].value);
2204 for (c = 0; c < max_num_entities; c++)
2205 for (y = 0; y < 3; y++)
2206 for (x = 0; x < 3; x++)
2207 content[c].e[x][y] = default_value;
2210 else // constant size configuration data (1, 2 or 4 bytes)
2212 if (data_type == TYPE_BOOLEAN)
2213 *(boolean *)(conf[i].value) = default_value;
2215 *(int *) (conf[i].value) = default_value;
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2224 for (i = 0; conf[i].data_type != -1; i++)
2226 int data_type = conf[i].data_type;
2227 int conf_type = conf[i].conf_type;
2228 int byte_mask = conf_type & CONF_MASK_BYTES;
2230 if (byte_mask == CONF_MASK_MULTI_BYTES)
2232 int max_num_entities = conf[i].max_num_entities;
2234 if (data_type == TYPE_STRING)
2236 char *string = (char *)(conf[i].value);
2237 char *string_copy = (char *)(conf[i].value_copy);
2239 strncpy(string_copy, string, max_num_entities);
2241 else if (data_type == TYPE_ELEMENT_LIST)
2243 int *element_array = (int *)(conf[i].value);
2244 int *element_array_copy = (int *)(conf[i].value_copy);
2247 for (j = 0; j < max_num_entities; j++)
2248 element_array_copy[j] = element_array[j];
2250 else if (data_type == TYPE_CONTENT_LIST)
2252 struct Content *content = (struct Content *)(conf[i].value);
2253 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2256 for (c = 0; c < max_num_entities; c++)
2257 for (y = 0; y < 3; y++)
2258 for (x = 0; x < 3; x++)
2259 content_copy[c].e[x][y] = content[c].e[x][y];
2262 else // constant size configuration data (1, 2 or 4 bytes)
2264 if (data_type == TYPE_BOOLEAN)
2265 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2267 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2276 xx_ei = *ei_from; // copy element data into temporary buffer
2277 yy_ei = *ei_to; // copy element data into temporary buffer
2279 copyConfigFromConfigList(chunk_config_CUSX_base);
2284 // ---------- reinitialize and copy change pages ----------
2286 ei_to->num_change_pages = ei_from->num_change_pages;
2287 ei_to->current_change_page = ei_from->current_change_page;
2289 setElementChangePages(ei_to, ei_to->num_change_pages);
2291 for (i = 0; i < ei_to->num_change_pages; i++)
2292 ei_to->change_page[i] = ei_from->change_page[i];
2294 // ---------- copy group element info ----------
2295 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2296 *ei_to->group = *ei_from->group;
2298 // mark this custom element as modified
2299 ei_to->modified_settings = TRUE;
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2304 int change_page_size = sizeof(struct ElementChangeInfo);
2306 ei->num_change_pages = MAX(1, change_pages);
2309 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2311 if (ei->current_change_page >= ei->num_change_pages)
2312 ei->current_change_page = ei->num_change_pages - 1;
2314 ei->change = &ei->change_page[ei->current_change_page];
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2319 xx_change = *change; // copy change data into temporary buffer
2321 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2323 *change = xx_change;
2325 resetEventFlags(change);
2327 change->direct_action = 0;
2328 change->other_action = 0;
2330 change->pre_change_function = NULL;
2331 change->change_function = NULL;
2332 change->post_change_function = NULL;
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2337 boolean add_border = FALSE;
2340 int x2 = STD_LEV_FIELDX - 1;
2341 int y2 = STD_LEV_FIELDY - 1;
2344 li = *level; // copy level data into temporary buffer
2345 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2346 *level = li; // copy temporary buffer back to level data
2348 setLevelInfoToDefaults_BD();
2349 setLevelInfoToDefaults_EM();
2350 setLevelInfoToDefaults_SP();
2351 setLevelInfoToDefaults_MM();
2353 level->native_bd_level = &native_bd_level;
2354 level->native_em_level = &native_em_level;
2355 level->native_sp_level = &native_sp_level;
2356 level->native_mm_level = &native_mm_level;
2358 level->file_version = FILE_VERSION_ACTUAL;
2359 level->game_version = GAME_VERSION_ACTUAL;
2361 level->creation_date = getCurrentDate();
2363 level->encoding_16bit_field = TRUE;
2364 level->encoding_16bit_yamyam = TRUE;
2365 level->encoding_16bit_amoeba = TRUE;
2367 // clear level name and level author string buffers
2368 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2369 level->name[i] = '\0';
2370 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2371 level->author[i] = '\0';
2373 // set level name and level author to default values
2374 strcpy(level->name, NAMELESS_LEVEL_NAME);
2375 strcpy(level->author, ANONYMOUS_NAME);
2377 // set default game engine type
2378 level->game_engine_type = setup.default_game_engine_type;
2380 // some game engines should have a default playfield with border elements
2381 if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2382 level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2383 level->game_engine_type == GAME_ENGINE_TYPE_SP)
2392 // set level playfield to playable default level with player and exit
2393 for (x = 0; x < MAX_LEV_FIELDX; x++)
2395 for (y = 0; y < MAX_LEV_FIELDY; y++)
2397 if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2398 y == 0 || y == STD_LEV_FIELDY - 1))
2399 level->field[x][y] = getEngineElement(EL_STEELWALL);
2401 level->field[x][y] = getEngineElement(EL_SAND);
2405 level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2406 level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2408 BorderElement = getEngineElement(EL_STEELWALL);
2410 // detect custom elements when loading them
2411 level->file_has_custom_elements = FALSE;
2413 // set random colors for BD style levels according to preferred color type
2414 SetRandomLevelColors_BD(setup.bd_default_color_type);
2416 // set default color type and colors for BD style level colors
2417 SetDefaultLevelColorType_BD();
2418 SetDefaultLevelColors_BD();
2420 // set all bug compatibility flags to "false" => do not emulate this bug
2421 level->use_action_after_change_bug = FALSE;
2423 if (leveldir_current)
2425 // try to determine better author name than 'anonymous'
2426 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2428 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2429 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2433 switch (LEVELCLASS(leveldir_current))
2435 case LEVELCLASS_TUTORIAL:
2436 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2439 case LEVELCLASS_CONTRIB:
2440 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2441 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2444 case LEVELCLASS_PRIVATE:
2445 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2446 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2450 // keep default value
2457 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2459 static boolean clipboard_elements_initialized = FALSE;
2462 InitElementPropertiesStatic();
2464 li = *level; // copy level data into temporary buffer
2465 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2466 *level = li; // copy temporary buffer back to level data
2468 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2471 struct ElementInfo *ei = &element_info[element];
2473 if (element == EL_MM_GRAY_BALL)
2475 struct LevelInfo_MM *level_mm = level->native_mm_level;
2478 for (j = 0; j < level->num_mm_ball_contents; j++)
2479 level->mm_ball_content[j] =
2480 map_element_MM_to_RND(level_mm->ball_content[j]);
2483 // never initialize clipboard elements after the very first time
2484 // (to be able to use clipboard elements between several levels)
2485 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2488 if (IS_ENVELOPE(element))
2490 int envelope_nr = element - EL_ENVELOPE_1;
2492 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2494 level->envelope[envelope_nr] = xx_envelope;
2497 if (IS_CUSTOM_ELEMENT(element) ||
2498 IS_GROUP_ELEMENT(element) ||
2499 IS_INTERNAL_ELEMENT(element))
2501 xx_ei = *ei; // copy element data into temporary buffer
2503 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508 setElementChangePages(ei, 1);
2509 setElementChangeInfoToDefaults(ei->change);
2511 if (IS_CUSTOM_ELEMENT(element) ||
2512 IS_GROUP_ELEMENT(element))
2514 setElementDescriptionToDefault(ei);
2516 ei->modified_settings = FALSE;
2519 if (IS_CUSTOM_ELEMENT(element) ||
2520 IS_INTERNAL_ELEMENT(element))
2522 // internal values used in level editor
2524 ei->access_type = 0;
2525 ei->access_layer = 0;
2526 ei->access_protected = 0;
2527 ei->walk_to_action = 0;
2528 ei->smash_targets = 0;
2531 ei->can_explode_by_fire = FALSE;
2532 ei->can_explode_smashed = FALSE;
2533 ei->can_explode_impact = FALSE;
2535 ei->current_change_page = 0;
2538 if (IS_GROUP_ELEMENT(element) ||
2539 IS_INTERNAL_ELEMENT(element))
2541 struct ElementGroupInfo *group;
2543 // initialize memory for list of elements in group
2544 if (ei->group == NULL)
2545 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2549 xx_group = *group; // copy group data into temporary buffer
2551 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556 if (IS_EMPTY_ELEMENT(element) ||
2557 IS_INTERNAL_ELEMENT(element))
2559 xx_ei = *ei; // copy element data into temporary buffer
2561 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2567 clipboard_elements_initialized = TRUE;
2570 static void setLevelInfoToDefaults(struct LevelInfo *level,
2571 boolean level_info_only,
2572 boolean reset_file_status)
2574 setLevelInfoToDefaults_Level(level);
2576 if (!level_info_only)
2577 setLevelInfoToDefaults_Elements(level);
2579 if (reset_file_status)
2581 level->no_valid_file = FALSE;
2582 level->no_level_file = FALSE;
2585 level->changed = FALSE;
2588 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2590 level_file_info->nr = 0;
2591 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2592 level_file_info->packed = FALSE;
2594 setString(&level_file_info->basename, NULL);
2595 setString(&level_file_info->filename, NULL);
2598 int getMappedElement_SB(int, boolean);
2600 static void ActivateLevelTemplate(void)
2604 if (check_special_flags("load_xsb_to_ces"))
2606 // fill smaller playfields with padding "beyond border wall" elements
2607 if (level.fieldx < level_template.fieldx ||
2608 level.fieldy < level_template.fieldy)
2610 short field[level.fieldx][level.fieldy];
2611 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2612 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2613 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2614 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2616 // copy old playfield (which is smaller than the visible area)
2617 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2618 field[x][y] = level.field[x][y];
2620 // fill new, larger playfield with "beyond border wall" elements
2621 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2622 level.field[x][y] = getMappedElement_SB('_', TRUE);
2624 // copy the old playfield to the middle of the new playfield
2625 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2626 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2628 level.fieldx = new_fieldx;
2629 level.fieldy = new_fieldy;
2633 // Currently there is no special action needed to activate the template
2634 // data, because 'element_info' property settings overwrite the original
2635 // level data, while all other variables do not change.
2637 // Exception: 'from_level_template' elements in the original level playfield
2638 // are overwritten with the corresponding elements at the same position in
2639 // playfield from the level template.
2641 for (x = 0; x < level.fieldx; x++)
2642 for (y = 0; y < level.fieldy; y++)
2643 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2644 level.field[x][y] = level_template.field[x][y];
2646 if (check_special_flags("load_xsb_to_ces"))
2648 struct LevelInfo level_backup = level;
2650 // overwrite all individual level settings from template level settings
2651 level = level_template;
2653 // restore level file info
2654 level.file_info = level_backup.file_info;
2656 // restore playfield size
2657 level.fieldx = level_backup.fieldx;
2658 level.fieldy = level_backup.fieldy;
2660 // restore playfield content
2661 for (x = 0; x < level.fieldx; x++)
2662 for (y = 0; y < level.fieldy; y++)
2663 level.field[x][y] = level_backup.field[x][y];
2665 // restore name and author from individual level
2666 strcpy(level.name, level_backup.name);
2667 strcpy(level.author, level_backup.author);
2669 // restore flag "use_custom_template"
2670 level.use_custom_template = level_backup.use_custom_template;
2674 static boolean checkForPackageFromBasename_BD(char *basename)
2676 // check for native BD level file extensions
2677 if (!strSuffixLower(basename, ".bd") &&
2678 !strSuffixLower(basename, ".bdr") &&
2679 !strSuffixLower(basename, ".brc") &&
2680 !strSuffixLower(basename, ".gds"))
2683 // check for standard single-level BD files (like "001.bd")
2684 if (strSuffixLower(basename, ".bd") &&
2685 strlen(basename) == 6 &&
2686 basename[0] >= '0' && basename[0] <= '9' &&
2687 basename[1] >= '0' && basename[1] <= '9' &&
2688 basename[2] >= '0' && basename[2] <= '9')
2691 // this is a level package in native BD file format
2695 static char *getLevelFilenameFromBasename(char *basename)
2697 static char *filename = NULL;
2699 checked_free(filename);
2701 filename = getPath2(getCurrentLevelDir(), basename);
2706 static int getFileTypeFromBasename(char *basename)
2708 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2710 static char *filename = NULL;
2711 struct stat file_status;
2713 // ---------- try to determine file type from filename ----------
2715 // check for typical filename of a Supaplex level package file
2716 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2717 return LEVEL_FILE_TYPE_SP;
2719 // check for typical filename of a Diamond Caves II level package file
2720 if (strSuffixLower(basename, ".dc") ||
2721 strSuffixLower(basename, ".dc2"))
2722 return LEVEL_FILE_TYPE_DC;
2724 // check for typical filename of a Sokoban level package file
2725 if (strSuffixLower(basename, ".xsb") &&
2726 strchr(basename, '%') == NULL)
2727 return LEVEL_FILE_TYPE_SB;
2729 // check for typical filename of a Boulder Dash (GDash) level package file
2730 if (checkForPackageFromBasename_BD(basename))
2731 return LEVEL_FILE_TYPE_BD;
2733 // ---------- try to determine file type from filesize ----------
2735 checked_free(filename);
2736 filename = getPath2(getCurrentLevelDir(), basename);
2738 if (stat(filename, &file_status) == 0)
2740 // check for typical filesize of a Supaplex level package file
2741 if (file_status.st_size == 170496)
2742 return LEVEL_FILE_TYPE_SP;
2745 return LEVEL_FILE_TYPE_UNKNOWN;
2748 static int getFileTypeFromMagicBytes(char *filename, int type)
2752 if ((file = openFile(filename, MODE_READ)))
2754 char chunk_name[CHUNK_ID_LEN + 1];
2756 getFileChunkBE(file, chunk_name, NULL);
2758 if (strEqual(chunk_name, "MMII") ||
2759 strEqual(chunk_name, "MIRR"))
2760 type = LEVEL_FILE_TYPE_MM;
2768 static boolean checkForPackageFromBasename(char *basename)
2770 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2771 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2773 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2776 static char *getSingleLevelBasenameExt(int nr, char *extension)
2778 static char basename[MAX_FILENAME_LEN];
2781 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2783 sprintf(basename, "%03d.%s", nr, extension);
2788 static char *getSingleLevelBasename(int nr)
2790 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2793 static char *getPackedLevelBasename(int type)
2795 static char basename[MAX_FILENAME_LEN];
2796 char *directory = getCurrentLevelDir();
2798 DirectoryEntry *dir_entry;
2800 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2802 if ((dir = openDirectory(directory)) == NULL)
2804 Warn("cannot read current level directory '%s'", directory);
2809 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2811 char *entry_basename = dir_entry->basename;
2812 int entry_type = getFileTypeFromBasename(entry_basename);
2814 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2816 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2819 strcpy(basename, entry_basename);
2826 closeDirectory(dir);
2831 static char *getSingleLevelFilename(int nr)
2833 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2836 #if ENABLE_UNUSED_CODE
2837 static char *getPackedLevelFilename(int type)
2839 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2843 char *getDefaultLevelFilename(int nr)
2845 return getSingleLevelFilename(nr);
2848 #if ENABLE_UNUSED_CODE
2849 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2853 lfi->packed = FALSE;
2855 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2856 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2860 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2861 int type, char *format, ...)
2863 static char basename[MAX_FILENAME_LEN];
2866 va_start(ap, format);
2867 vsprintf(basename, format, ap);
2871 lfi->packed = FALSE;
2873 setString(&lfi->basename, basename);
2874 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2877 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2883 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2884 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2887 static int getFiletypeFromID(char *filetype_id)
2889 char *filetype_id_lower;
2890 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2893 if (filetype_id == NULL)
2894 return LEVEL_FILE_TYPE_UNKNOWN;
2896 filetype_id_lower = getStringToLower(filetype_id);
2898 for (i = 0; filetype_id_list[i].id != NULL; i++)
2900 char *id_lower = getStringToLower(filetype_id_list[i].id);
2902 if (strEqual(filetype_id_lower, id_lower))
2903 filetype = filetype_id_list[i].filetype;
2907 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2911 free(filetype_id_lower);
2916 char *getLocalLevelTemplateFilename(void)
2918 return getDefaultLevelFilename(-1);
2921 char *getGlobalLevelTemplateFilename(void)
2923 // global variable "leveldir_current" must be modified in the loop below
2924 LevelDirTree *leveldir_current_last = leveldir_current;
2925 char *filename = NULL;
2927 // check for template level in path from current to topmost tree node
2929 while (leveldir_current != NULL)
2931 filename = getDefaultLevelFilename(-1);
2933 if (fileExists(filename))
2936 leveldir_current = leveldir_current->node_parent;
2939 // restore global variable "leveldir_current" modified in above loop
2940 leveldir_current = leveldir_current_last;
2945 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2949 // special case: level number is negative => check for level template file
2952 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2953 getSingleLevelBasename(-1));
2955 // replace local level template filename with global template filename
2956 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2958 // no fallback if template file not existing
2962 // special case: check for file name/pattern specified in "levelinfo.conf"
2963 if (leveldir_current->level_filename != NULL)
2965 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2967 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2968 leveldir_current->level_filename, nr);
2970 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2972 if (fileExists(lfi->filename))
2975 else if (leveldir_current->level_filetype != NULL)
2977 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2979 // check for specified native level file with standard file name
2980 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2981 "%03d.%s", nr, LEVELFILE_EXTENSION);
2982 if (fileExists(lfi->filename))
2986 // check for native Rocks'n'Diamonds level file
2987 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2988 "%03d.%s", nr, LEVELFILE_EXTENSION);
2989 if (fileExists(lfi->filename))
2992 // check for native Boulder Dash level file
2993 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2994 if (fileExists(lfi->filename))
2997 // check for Emerald Mine level file (V1)
2998 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2999 'a' + (nr / 10) % 26, '0' + nr % 10);
3000 if (fileExists(lfi->filename))
3002 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
3003 'A' + (nr / 10) % 26, '0' + nr % 10);
3004 if (fileExists(lfi->filename))
3007 // check for Emerald Mine level file (V2 to V5)
3008 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
3009 if (fileExists(lfi->filename))
3012 // check for Emerald Mine level file (V6 / single mode)
3013 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3014 if (fileExists(lfi->filename))
3016 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3017 if (fileExists(lfi->filename))
3020 // check for Emerald Mine level file (V6 / teamwork mode)
3021 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3022 if (fileExists(lfi->filename))
3024 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3025 if (fileExists(lfi->filename))
3028 // check for various packed level file formats
3029 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3030 if (fileExists(lfi->filename))
3033 // no known level file found -- use default values (and fail later)
3034 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3035 "%03d.%s", nr, LEVELFILE_EXTENSION);
3038 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3040 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3041 lfi->type = getFileTypeFromBasename(lfi->basename);
3043 if (lfi->type == LEVEL_FILE_TYPE_RND)
3044 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3047 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3049 // always start with reliable default values
3050 setFileInfoToDefaults(level_file_info);
3052 level_file_info->nr = nr; // set requested level number
3054 determineLevelFileInfo_Filename(level_file_info);
3055 determineLevelFileInfo_Filetype(level_file_info);
3058 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3059 struct LevelFileInfo *lfi_to)
3061 lfi_to->nr = lfi_from->nr;
3062 lfi_to->type = lfi_from->type;
3063 lfi_to->packed = lfi_from->packed;
3065 setString(&lfi_to->basename, lfi_from->basename);
3066 setString(&lfi_to->filename, lfi_from->filename);
3069 // ----------------------------------------------------------------------------
3070 // functions for loading R'n'D level
3071 // ----------------------------------------------------------------------------
3073 int getMappedElement(int element)
3075 // remap some (historic, now obsolete) elements
3079 case EL_PLAYER_OBSOLETE:
3080 element = EL_PLAYER_1;
3083 case EL_KEY_OBSOLETE:
3087 case EL_EM_KEY_1_FILE_OBSOLETE:
3088 element = EL_EM_KEY_1;
3091 case EL_EM_KEY_2_FILE_OBSOLETE:
3092 element = EL_EM_KEY_2;
3095 case EL_EM_KEY_3_FILE_OBSOLETE:
3096 element = EL_EM_KEY_3;
3099 case EL_EM_KEY_4_FILE_OBSOLETE:
3100 element = EL_EM_KEY_4;
3103 case EL_ENVELOPE_OBSOLETE:
3104 element = EL_ENVELOPE_1;
3112 if (element >= NUM_FILE_ELEMENTS)
3114 Warn("invalid level element %d", element);
3116 element = EL_UNKNOWN;
3124 static int getMappedElementByVersion(int element, int game_version)
3126 // remap some elements due to certain game version
3128 if (game_version <= VERSION_IDENT(2,2,0,0))
3130 // map game font elements
3131 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3132 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3133 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3134 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3137 if (game_version < VERSION_IDENT(3,0,0,0))
3139 // map Supaplex gravity tube elements
3140 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3141 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3142 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3143 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3150 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3152 level->file_version = getFileVersion(file);
3153 level->game_version = getFileVersion(file);
3158 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3160 level->creation_date.year = getFile16BitBE(file);
3161 level->creation_date.month = getFile8Bit(file);
3162 level->creation_date.day = getFile8Bit(file);
3164 level->creation_date.src = DATE_SRC_LEVELFILE;
3169 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3171 int initial_player_stepsize;
3172 int initial_player_gravity;
3175 level->fieldx = getFile8Bit(file);
3176 level->fieldy = getFile8Bit(file);
3178 level->time = getFile16BitBE(file);
3179 level->gems_needed = getFile16BitBE(file);
3181 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3182 level->name[i] = getFile8Bit(file);
3183 level->name[MAX_LEVEL_NAME_LEN] = 0;
3185 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3186 level->score[i] = getFile8Bit(file);
3188 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3189 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3190 for (y = 0; y < 3; y++)
3191 for (x = 0; x < 3; x++)
3192 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3194 level->amoeba_speed = getFile8Bit(file);
3195 level->time_magic_wall = getFile8Bit(file);
3196 level->time_wheel = getFile8Bit(file);
3197 level->amoeba_content = getMappedElement(getFile8Bit(file));
3199 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3202 for (i = 0; i < MAX_PLAYERS; i++)
3203 level->initial_player_stepsize[i] = initial_player_stepsize;
3205 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3207 for (i = 0; i < MAX_PLAYERS; i++)
3208 level->initial_player_gravity[i] = initial_player_gravity;
3210 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3211 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3213 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3216 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3217 level->can_move_into_acid_bits = getFile32BitBE(file);
3218 level->dont_collide_with_bits = getFile8Bit(file);
3220 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3221 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3223 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3224 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3225 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3227 level->game_engine_type = getFile8Bit(file);
3229 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3234 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3238 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3239 level->name[i] = getFile8Bit(file);
3240 level->name[MAX_LEVEL_NAME_LEN] = 0;
3245 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3249 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3250 level->author[i] = getFile8Bit(file);
3251 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3256 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3259 int chunk_size_expected = level->fieldx * level->fieldy;
3261 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3262 stored with 16-bit encoding (and should be twice as big then).
3263 Even worse, playfield data was stored 16-bit when only yamyam content
3264 contained 16-bit elements and vice versa. */
3266 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3267 chunk_size_expected *= 2;
3269 if (chunk_size_expected != chunk_size)
3271 ReadUnusedBytesFromFile(file, chunk_size);
3272 return chunk_size_expected;
3275 for (y = 0; y < level->fieldy; y++)
3276 for (x = 0; x < level->fieldx; x++)
3277 level->field[x][y] =
3278 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3283 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3286 int header_size = 4;
3287 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3288 int chunk_size_expected = header_size + content_size;
3290 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3291 stored with 16-bit encoding (and should be twice as big then).
3292 Even worse, playfield data was stored 16-bit when only yamyam content
3293 contained 16-bit elements and vice versa. */
3295 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3296 chunk_size_expected += content_size;
3298 if (chunk_size_expected != chunk_size)
3300 ReadUnusedBytesFromFile(file, chunk_size);
3301 return chunk_size_expected;
3305 level->num_yamyam_contents = getFile8Bit(file);
3309 // correct invalid number of content fields -- should never happen
3310 if (level->num_yamyam_contents < 1 ||
3311 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3312 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3314 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3315 for (y = 0; y < 3; y++)
3316 for (x = 0; x < 3; x++)
3317 level->yamyam_content[i].e[x][y] =
3318 getMappedElement(level->encoding_16bit_field ?
3319 getFile16BitBE(file) : getFile8Bit(file));
3323 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3328 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3330 element = getMappedElement(getFile16BitBE(file));
3331 num_contents = getFile8Bit(file);
3333 getFile8Bit(file); // content x size (unused)
3334 getFile8Bit(file); // content y size (unused)
3336 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3338 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3339 for (y = 0; y < 3; y++)
3340 for (x = 0; x < 3; x++)
3341 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3343 // correct invalid number of content fields -- should never happen
3344 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3345 num_contents = STD_ELEMENT_CONTENTS;
3347 if (element == EL_YAMYAM)
3349 level->num_yamyam_contents = num_contents;
3351 for (i = 0; i < num_contents; i++)
3352 for (y = 0; y < 3; y++)
3353 for (x = 0; x < 3; x++)
3354 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3356 else if (element == EL_BD_AMOEBA)
3358 level->amoeba_content = content_array[0][0][0];
3362 Warn("cannot load content for element '%d'", element);
3368 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3374 int chunk_size_expected;
3376 element = getMappedElement(getFile16BitBE(file));
3377 if (!IS_ENVELOPE(element))
3378 element = EL_ENVELOPE_1;
3380 envelope_nr = element - EL_ENVELOPE_1;
3382 envelope_len = getFile16BitBE(file);
3384 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3385 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3387 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3389 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3390 if (chunk_size_expected != chunk_size)
3392 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3393 return chunk_size_expected;
3396 for (i = 0; i < envelope_len; i++)
3397 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3402 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3404 int num_changed_custom_elements = getFile16BitBE(file);
3405 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3408 if (chunk_size_expected != chunk_size)
3410 ReadUnusedBytesFromFile(file, chunk_size - 2);
3411 return chunk_size_expected;
3414 for (i = 0; i < num_changed_custom_elements; i++)
3416 int element = getMappedElement(getFile16BitBE(file));
3417 int properties = getFile32BitBE(file);
3419 if (IS_CUSTOM_ELEMENT(element))
3420 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3422 Warn("invalid custom element number %d", element);
3424 // older game versions that wrote level files with CUS1 chunks used
3425 // different default push delay values (not yet stored in level file)
3426 element_info[element].push_delay_fixed = 2;
3427 element_info[element].push_delay_random = 8;
3430 level->file_has_custom_elements = TRUE;
3435 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3437 int num_changed_custom_elements = getFile16BitBE(file);
3438 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3441 if (chunk_size_expected != chunk_size)
3443 ReadUnusedBytesFromFile(file, chunk_size - 2);
3444 return chunk_size_expected;
3447 for (i = 0; i < num_changed_custom_elements; i++)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int custom_target_element = getMappedElement(getFile16BitBE(file));
3452 if (IS_CUSTOM_ELEMENT(element))
3453 element_info[element].change->target_element = custom_target_element;
3455 Warn("invalid custom element number %d", element);
3458 level->file_has_custom_elements = TRUE;
3463 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3465 int num_changed_custom_elements = getFile16BitBE(file);
3466 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3469 if (chunk_size_expected != chunk_size)
3471 ReadUnusedBytesFromFile(file, chunk_size - 2);
3472 return chunk_size_expected;
3475 for (i = 0; i < num_changed_custom_elements; i++)
3477 int element = getMappedElement(getFile16BitBE(file));
3478 struct ElementInfo *ei = &element_info[element];
3479 unsigned int event_bits;
3481 if (!IS_CUSTOM_ELEMENT(element))
3483 Warn("invalid custom element number %d", element);
3485 element = EL_INTERNAL_DUMMY;
3488 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3489 ei->description[j] = getFile8Bit(file);
3490 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3492 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3494 // some free bytes for future properties and padding
3495 ReadUnusedBytesFromFile(file, 7);
3497 ei->use_gfx_element = getFile8Bit(file);
3498 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3500 ei->collect_score_initial = getFile8Bit(file);
3501 ei->collect_count_initial = getFile8Bit(file);
3503 ei->push_delay_fixed = getFile16BitBE(file);
3504 ei->push_delay_random = getFile16BitBE(file);
3505 ei->move_delay_fixed = getFile16BitBE(file);
3506 ei->move_delay_random = getFile16BitBE(file);
3508 ei->move_pattern = getFile16BitBE(file);
3509 ei->move_direction_initial = getFile8Bit(file);
3510 ei->move_stepsize = getFile8Bit(file);
3512 for (y = 0; y < 3; y++)
3513 for (x = 0; x < 3; x++)
3514 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3516 // bits 0 - 31 of "has_event[]"
3517 event_bits = getFile32BitBE(file);
3518 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3519 if (event_bits & (1u << j))
3520 ei->change->has_event[j] = TRUE;
3522 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3524 ei->change->delay_fixed = getFile16BitBE(file);
3525 ei->change->delay_random = getFile16BitBE(file);
3526 ei->change->delay_frames = getFile16BitBE(file);
3528 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3530 ei->change->explode = getFile8Bit(file);
3531 ei->change->use_target_content = getFile8Bit(file);
3532 ei->change->only_if_complete = getFile8Bit(file);
3533 ei->change->use_random_replace = getFile8Bit(file);
3535 ei->change->random_percentage = getFile8Bit(file);
3536 ei->change->replace_when = getFile8Bit(file);
3538 for (y = 0; y < 3; y++)
3539 for (x = 0; x < 3; x++)
3540 ei->change->target_content.e[x][y] =
3541 getMappedElement(getFile16BitBE(file));
3543 ei->slippery_type = getFile8Bit(file);
3545 // some free bytes for future properties and padding
3546 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3548 // mark that this custom element has been modified
3549 ei->modified_settings = TRUE;
3552 level->file_has_custom_elements = TRUE;
3557 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3559 struct ElementInfo *ei;
3560 int chunk_size_expected;
3564 // ---------- custom element base property values (96 bytes) ----------------
3566 element = getMappedElement(getFile16BitBE(file));
3568 if (!IS_CUSTOM_ELEMENT(element))
3570 Warn("invalid custom element number %d", element);
3572 ReadUnusedBytesFromFile(file, chunk_size - 2);
3577 ei = &element_info[element];
3579 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3580 ei->description[i] = getFile8Bit(file);
3581 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3583 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3585 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3587 ei->num_change_pages = getFile8Bit(file);
3589 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3590 if (chunk_size_expected != chunk_size)
3592 ReadUnusedBytesFromFile(file, chunk_size - 43);
3593 return chunk_size_expected;
3596 ei->ce_value_fixed_initial = getFile16BitBE(file);
3597 ei->ce_value_random_initial = getFile16BitBE(file);
3598 ei->use_last_ce_value = getFile8Bit(file);
3600 ei->use_gfx_element = getFile8Bit(file);
3601 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3603 ei->collect_score_initial = getFile8Bit(file);
3604 ei->collect_count_initial = getFile8Bit(file);
3606 ei->drop_delay_fixed = getFile8Bit(file);
3607 ei->push_delay_fixed = getFile8Bit(file);
3608 ei->drop_delay_random = getFile8Bit(file);
3609 ei->push_delay_random = getFile8Bit(file);
3610 ei->move_delay_fixed = getFile16BitBE(file);
3611 ei->move_delay_random = getFile16BitBE(file);
3613 // bits 0 - 15 of "move_pattern" ...
3614 ei->move_pattern = getFile16BitBE(file);
3615 ei->move_direction_initial = getFile8Bit(file);
3616 ei->move_stepsize = getFile8Bit(file);
3618 ei->slippery_type = getFile8Bit(file);
3620 for (y = 0; y < 3; y++)
3621 for (x = 0; x < 3; x++)
3622 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3624 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3625 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3626 ei->move_leave_type = getFile8Bit(file);
3628 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3629 ei->move_pattern |= (getFile16BitBE(file) << 16);
3631 ei->access_direction = getFile8Bit(file);
3633 ei->explosion_delay = getFile8Bit(file);
3634 ei->ignition_delay = getFile8Bit(file);
3635 ei->explosion_type = getFile8Bit(file);
3637 // some free bytes for future custom property values and padding
3638 ReadUnusedBytesFromFile(file, 1);
3640 // ---------- change page property values (48 bytes) ------------------------
3642 setElementChangePages(ei, ei->num_change_pages);
3644 for (i = 0; i < ei->num_change_pages; i++)
3646 struct ElementChangeInfo *change = &ei->change_page[i];
3647 unsigned int event_bits;
3649 // always start with reliable default values
3650 setElementChangeInfoToDefaults(change);
3652 // bits 0 - 31 of "has_event[]" ...
3653 event_bits = getFile32BitBE(file);
3654 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3655 if (event_bits & (1u << j))
3656 change->has_event[j] = TRUE;
3658 change->target_element = getMappedElement(getFile16BitBE(file));
3660 change->delay_fixed = getFile16BitBE(file);
3661 change->delay_random = getFile16BitBE(file);
3662 change->delay_frames = getFile16BitBE(file);
3664 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3666 change->explode = getFile8Bit(file);
3667 change->use_target_content = getFile8Bit(file);
3668 change->only_if_complete = getFile8Bit(file);
3669 change->use_random_replace = getFile8Bit(file);
3671 change->random_percentage = getFile8Bit(file);
3672 change->replace_when = getFile8Bit(file);
3674 for (y = 0; y < 3; y++)
3675 for (x = 0; x < 3; x++)
3676 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3678 change->can_change = getFile8Bit(file);
3680 change->trigger_side = getFile8Bit(file);
3682 change->trigger_player = getFile8Bit(file);
3683 change->trigger_page = getFile8Bit(file);
3685 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3686 CH_PAGE_ANY : (1 << change->trigger_page));
3688 change->has_action = getFile8Bit(file);
3689 change->action_type = getFile8Bit(file);
3690 change->action_mode = getFile8Bit(file);
3691 change->action_arg = getFile16BitBE(file);
3693 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3694 event_bits = getFile8Bit(file);
3695 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3696 if (event_bits & (1u << (j - 32)))
3697 change->has_event[j] = TRUE;
3700 // mark this custom element as modified
3701 ei->modified_settings = TRUE;
3703 level->file_has_custom_elements = TRUE;
3708 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3710 struct ElementInfo *ei;
3711 struct ElementGroupInfo *group;
3715 element = getMappedElement(getFile16BitBE(file));
3717 if (!IS_GROUP_ELEMENT(element))
3719 Warn("invalid group element number %d", element);
3721 ReadUnusedBytesFromFile(file, chunk_size - 2);
3726 ei = &element_info[element];
3728 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3729 ei->description[i] = getFile8Bit(file);
3730 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3732 group = element_info[element].group;
3734 group->num_elements = getFile8Bit(file);
3736 ei->use_gfx_element = getFile8Bit(file);
3737 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3739 group->choice_mode = getFile8Bit(file);
3741 // some free bytes for future values and padding
3742 ReadUnusedBytesFromFile(file, 3);
3744 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3745 group->element[i] = getMappedElement(getFile16BitBE(file));
3747 // mark this group element as modified
3748 element_info[element].modified_settings = TRUE;
3750 level->file_has_custom_elements = TRUE;
3755 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3756 int element, int real_element)
3758 int micro_chunk_size = 0;
3759 int conf_type = getFile8Bit(file);
3760 int byte_mask = conf_type & CONF_MASK_BYTES;
3761 boolean element_found = FALSE;
3764 micro_chunk_size += 1;
3766 if (byte_mask == CONF_MASK_MULTI_BYTES)
3768 int num_bytes = getFile16BitBE(file);
3769 byte *buffer = checked_malloc(num_bytes);
3771 ReadBytesFromFile(file, buffer, num_bytes);
3773 for (i = 0; conf[i].data_type != -1; i++)
3775 if (conf[i].element == element &&
3776 conf[i].conf_type == conf_type)
3778 int data_type = conf[i].data_type;
3779 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3780 int max_num_entities = conf[i].max_num_entities;
3782 if (num_entities > max_num_entities)
3784 Warn("truncating number of entities for element %d from %d to %d",
3785 element, num_entities, max_num_entities);
3787 num_entities = max_num_entities;
3790 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3791 data_type == TYPE_CONTENT_LIST))
3793 // for element and content lists, zero entities are not allowed
3794 Warn("found empty list of entities for element %d", element);
3796 // do not set "num_entities" here to prevent reading behind buffer
3798 *(int *)(conf[i].num_entities) = 1; // at least one is required
3802 *(int *)(conf[i].num_entities) = num_entities;
3805 element_found = TRUE;
3807 if (data_type == TYPE_STRING)
3809 char *string = (char *)(conf[i].value);
3812 for (j = 0; j < max_num_entities; j++)
3813 string[j] = (j < num_entities ? buffer[j] : '\0');
3815 else if (data_type == TYPE_ELEMENT_LIST)
3817 int *element_array = (int *)(conf[i].value);
3820 for (j = 0; j < num_entities; j++)
3822 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3824 else if (data_type == TYPE_CONTENT_LIST)
3826 struct Content *content= (struct Content *)(conf[i].value);
3829 for (c = 0; c < num_entities; c++)
3830 for (y = 0; y < 3; y++)
3831 for (x = 0; x < 3; x++)
3832 content[c].e[x][y] =
3833 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3836 element_found = FALSE;
3842 checked_free(buffer);
3844 micro_chunk_size += 2 + num_bytes;
3846 else // constant size configuration data (1, 2 or 4 bytes)
3848 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3849 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3850 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3852 for (i = 0; conf[i].data_type != -1; i++)
3854 if (conf[i].element == element &&
3855 conf[i].conf_type == conf_type)
3857 int data_type = conf[i].data_type;
3859 if (data_type == TYPE_ELEMENT)
3860 value = getMappedElement(value);
3862 if (data_type == TYPE_BOOLEAN)
3863 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3865 *(int *) (conf[i].value) = value;
3867 element_found = TRUE;
3873 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3878 char *error_conf_chunk_bytes =
3879 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3880 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3881 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3882 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3883 int error_element = real_element;
3885 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3886 error_conf_chunk_bytes, error_conf_chunk_token,
3887 error_element, EL_NAME(error_element));
3890 return micro_chunk_size;
3893 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3895 int real_chunk_size = 0;
3897 li = *level; // copy level data into temporary buffer
3899 while (!checkEndOfFile(file))
3901 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3903 if (real_chunk_size >= chunk_size)
3907 *level = li; // copy temporary buffer back to level data
3909 return real_chunk_size;
3912 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3914 int real_chunk_size = 0;
3916 li = *level; // copy level data into temporary buffer
3918 while (!checkEndOfFile(file))
3920 int element = getMappedElement(getFile16BitBE(file));
3922 real_chunk_size += 2;
3923 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3925 if (real_chunk_size >= chunk_size)
3929 *level = li; // copy temporary buffer back to level data
3931 return real_chunk_size;
3934 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3936 int real_chunk_size = 0;
3938 li = *level; // copy level data into temporary buffer
3940 while (!checkEndOfFile(file))
3942 int element = getMappedElement(getFile16BitBE(file));
3944 real_chunk_size += 2;
3945 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3947 if (real_chunk_size >= chunk_size)
3951 *level = li; // copy temporary buffer back to level data
3953 return real_chunk_size;
3956 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3958 int element = getMappedElement(getFile16BitBE(file));
3959 int envelope_nr = element - EL_ENVELOPE_1;
3960 int real_chunk_size = 2;
3962 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3964 while (!checkEndOfFile(file))
3966 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3969 if (real_chunk_size >= chunk_size)
3973 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3975 return real_chunk_size;
3978 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3980 int element = getMappedElement(getFile16BitBE(file));
3981 int real_chunk_size = 2;
3982 struct ElementInfo *ei = &element_info[element];
3985 xx_ei = *ei; // copy element data into temporary buffer
3987 xx_ei.num_change_pages = -1;
3989 while (!checkEndOfFile(file))
3991 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3993 if (xx_ei.num_change_pages != -1)
3996 if (real_chunk_size >= chunk_size)
4002 if (ei->num_change_pages == -1)
4004 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
4007 ei->num_change_pages = 1;
4009 setElementChangePages(ei, 1);
4010 setElementChangeInfoToDefaults(ei->change);
4012 return real_chunk_size;
4015 // initialize number of change pages stored for this custom element
4016 setElementChangePages(ei, ei->num_change_pages);
4017 for (i = 0; i < ei->num_change_pages; i++)
4018 setElementChangeInfoToDefaults(&ei->change_page[i]);
4020 // start with reading properties for the first change page
4021 xx_current_change_page = 0;
4023 while (!checkEndOfFile(file))
4025 // level file might contain invalid change page number
4026 if (xx_current_change_page >= ei->num_change_pages)
4029 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4031 xx_change = *change; // copy change data into temporary buffer
4033 resetEventBits(); // reset bits; change page might have changed
4035 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4038 *change = xx_change;
4040 setEventFlagsFromEventBits(change);
4042 if (real_chunk_size >= chunk_size)
4046 level->file_has_custom_elements = TRUE;
4048 return real_chunk_size;
4051 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4053 int element = getMappedElement(getFile16BitBE(file));
4054 int real_chunk_size = 2;
4055 struct ElementInfo *ei = &element_info[element];
4056 struct ElementGroupInfo *group = ei->group;
4061 xx_ei = *ei; // copy element data into temporary buffer
4062 xx_group = *group; // copy group data into temporary buffer
4064 while (!checkEndOfFile(file))
4066 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4069 if (real_chunk_size >= chunk_size)
4076 level->file_has_custom_elements = TRUE;
4078 return real_chunk_size;
4081 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4083 int element = getMappedElement(getFile16BitBE(file));
4084 int real_chunk_size = 2;
4085 struct ElementInfo *ei = &element_info[element];
4087 xx_ei = *ei; // copy element data into temporary buffer
4089 while (!checkEndOfFile(file))
4091 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4094 if (real_chunk_size >= chunk_size)
4100 level->file_has_custom_elements = TRUE;
4102 return real_chunk_size;
4105 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4106 struct LevelFileInfo *level_file_info,
4107 boolean level_info_only)
4109 char *filename = level_file_info->filename;
4110 char cookie[MAX_LINE_LEN];
4111 char chunk_name[CHUNK_ID_LEN + 1];
4115 if (!(file = openFile(filename, MODE_READ)))
4117 level->no_valid_file = TRUE;
4118 level->no_level_file = TRUE;
4120 if (level_info_only)
4123 Warn("cannot read level '%s' -- using empty level", filename);
4125 if (!setup.editor.use_template_for_new_levels)
4128 // if level file not found, try to initialize level data from template
4129 filename = getGlobalLevelTemplateFilename();
4131 if (!(file = openFile(filename, MODE_READ)))
4134 // default: for empty levels, use level template for custom elements
4135 level->use_custom_template = TRUE;
4137 level->no_valid_file = FALSE;
4140 getFileChunkBE(file, chunk_name, NULL);
4141 if (strEqual(chunk_name, "RND1"))
4143 getFile32BitBE(file); // not used
4145 getFileChunkBE(file, chunk_name, NULL);
4146 if (!strEqual(chunk_name, "CAVE"))
4148 level->no_valid_file = TRUE;
4150 Warn("unknown format of level file '%s'", filename);
4157 else // check for pre-2.0 file format with cookie string
4159 strcpy(cookie, chunk_name);
4160 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4162 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4163 cookie[strlen(cookie) - 1] = '\0';
4165 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4167 level->no_valid_file = TRUE;
4169 Warn("unknown format of level file '%s'", filename);
4176 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4178 level->no_valid_file = TRUE;
4180 Warn("unsupported version of level file '%s'", filename);
4187 // pre-2.0 level files have no game version, so use file version here
4188 level->game_version = level->file_version;
4191 if (level->file_version < FILE_VERSION_1_2)
4193 // level files from versions before 1.2.0 without chunk structure
4194 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4195 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4203 int (*loader)(File *, int, struct LevelInfo *);
4207 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4208 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4209 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4210 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4211 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4212 { "INFO", -1, LoadLevel_INFO },
4213 { "BODY", -1, LoadLevel_BODY },
4214 { "CONT", -1, LoadLevel_CONT },
4215 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4216 { "CNT3", -1, LoadLevel_CNT3 },
4217 { "CUS1", -1, LoadLevel_CUS1 },
4218 { "CUS2", -1, LoadLevel_CUS2 },
4219 { "CUS3", -1, LoadLevel_CUS3 },
4220 { "CUS4", -1, LoadLevel_CUS4 },
4221 { "GRP1", -1, LoadLevel_GRP1 },
4222 { "CONF", -1, LoadLevel_CONF },
4223 { "ELEM", -1, LoadLevel_ELEM },
4224 { "NOTE", -1, LoadLevel_NOTE },
4225 { "CUSX", -1, LoadLevel_CUSX },
4226 { "GRPX", -1, LoadLevel_GRPX },
4227 { "EMPX", -1, LoadLevel_EMPX },
4232 while (getFileChunkBE(file, chunk_name, &chunk_size))
4236 while (chunk_info[i].name != NULL &&
4237 !strEqual(chunk_name, chunk_info[i].name))
4240 if (chunk_info[i].name == NULL)
4242 Warn("unknown chunk '%s' in level file '%s'",
4243 chunk_name, filename);
4245 ReadUnusedBytesFromFile(file, chunk_size);
4247 else if (chunk_info[i].size != -1 &&
4248 chunk_info[i].size != chunk_size)
4250 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4251 chunk_size, chunk_name, filename);
4253 ReadUnusedBytesFromFile(file, chunk_size);
4257 // call function to load this level chunk
4258 int chunk_size_expected =
4259 (chunk_info[i].loader)(file, chunk_size, level);
4261 if (chunk_size_expected < 0)
4263 Warn("error reading chunk '%s' in level file '%s'",
4264 chunk_name, filename);
4269 // the size of some chunks cannot be checked before reading other
4270 // chunks first (like "HEAD" and "BODY") that contain some header
4271 // information, so check them here
4272 if (chunk_size_expected != chunk_size)
4274 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4275 chunk_size, chunk_name, filename);
4287 // ----------------------------------------------------------------------------
4288 // functions for loading BD level
4289 // ----------------------------------------------------------------------------
4291 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4292 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4294 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4296 struct LevelInfo_BD *level_bd = level->native_bd_level;
4297 GdCave *cave = NULL; // will be changed below
4298 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4299 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4302 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4304 // cave and map newly allocated when set to defaults above
4305 cave = level_bd->cave;
4308 cave->intermission = level->bd_intermission;
4311 cave->level_time[0] = level->time;
4312 cave->level_diamonds[0] = level->gems_needed;
4315 cave->scheduling = level->bd_scheduling_type;
4316 cave->pal_timing = level->bd_pal_timing;
4317 cave->level_speed[0] = level->bd_cycle_delay_ms;
4318 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4319 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4320 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4323 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4324 cave->diamond_value = level->score[SC_EMERALD];
4325 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4327 // compatibility settings
4328 cave->lineshift = level->bd_line_shifting_borders;
4329 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4330 cave->short_explosions = level->bd_short_explosions;
4332 // player properties
4333 cave->diagonal_movements = level->bd_diagonal_movements;
4334 cave->active_is_first_found = level->bd_topmost_player_active;
4335 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4336 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4337 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4338 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4340 // element properties
4341 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4342 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4343 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4344 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4345 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4346 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4347 cave->level_magic_wall_time[0] = level->time_magic_wall;
4348 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4349 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4350 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4351 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4353 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4354 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4355 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4356 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4357 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4358 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4359 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4361 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4362 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4363 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4364 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4365 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4366 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4367 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4368 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4369 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4370 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4371 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4373 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4374 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4375 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4376 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4377 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4378 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4380 cave->slime_predictable = level->bd_slime_is_predictable;
4381 cave->slime_correct_random = level->bd_slime_correct_random;
4382 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4383 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4384 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4385 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4386 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4387 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4388 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4389 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4390 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4391 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4393 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4394 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4395 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4397 cave->biter_delay_frame = level->bd_biter_move_delay;
4398 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4400 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4402 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4404 cave->replicators_active = level->bd_replicators_active;
4405 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4407 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4408 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4410 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4412 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4414 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4415 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4416 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4418 cave->infinite_rockets = level->bd_infinite_rockets;
4420 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4421 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4423 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4424 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4426 cave->creatures_backwards = level->bd_creatures_start_backwards;
4427 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4428 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4430 cave->gravity = level->bd_gravity_direction;
4431 cave->gravity_switch_active = level->bd_gravity_switch_active;
4432 cave->gravity_change_time = level->bd_gravity_switch_delay;
4433 cave->gravity_affects_all = level->bd_gravity_affects_all;
4435 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4436 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4437 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4438 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4440 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4441 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4442 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4443 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4444 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4445 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4447 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4448 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4449 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4450 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4452 cave->colorb = level->bd_color_b;
4453 cave->color0 = level->bd_color_0;
4454 cave->color1 = level->bd_color_1;
4455 cave->color2 = level->bd_color_2;
4456 cave->color3 = level->bd_color_3;
4457 cave->color4 = level->bd_color_4;
4458 cave->color5 = level->bd_color_5;
4461 strncpy(cave->name, level->name, sizeof(GdString));
4462 cave->name[sizeof(GdString) - 1] = '\0';
4464 // playfield elements
4465 for (x = 0; x < cave->w; x++)
4466 for (y = 0; y < cave->h; y++)
4467 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4470 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4472 struct LevelInfo_BD *level_bd = level->native_bd_level;
4473 GdCave *cave = level_bd->cave;
4474 int bd_level_nr = level_bd->level_nr;
4477 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4478 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4481 level->bd_intermission = cave->intermission;
4484 level->time = cave->level_time[bd_level_nr];
4485 level->gems_needed = cave->level_diamonds[bd_level_nr];
4488 level->bd_scheduling_type = cave->scheduling;
4489 level->bd_pal_timing = cave->pal_timing;
4490 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4491 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4492 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4493 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4496 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4497 level->score[SC_EMERALD] = cave->diamond_value;
4498 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4500 // compatibility settings
4501 level->bd_line_shifting_borders = cave->lineshift;
4502 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4503 level->bd_short_explosions = cave->short_explosions;
4505 // player properties
4506 level->bd_diagonal_movements = cave->diagonal_movements;
4507 level->bd_topmost_player_active = cave->active_is_first_found;
4508 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4509 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4510 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4511 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4513 // element properties
4514 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4515 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4516 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4517 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4518 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4519 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4520 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4521 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4522 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4523 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4524 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4526 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4527 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4528 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4529 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4530 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4531 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4532 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4534 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4535 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4536 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4537 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4538 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4539 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4540 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4541 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4542 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4543 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4544 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4546 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4547 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4548 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4549 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4550 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4551 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4553 level->bd_slime_is_predictable = cave->slime_predictable;
4554 level->bd_slime_correct_random = cave->slime_correct_random;
4555 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4556 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4557 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4558 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4559 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4560 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4561 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4562 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4563 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4564 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4566 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4567 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4568 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4570 level->bd_biter_move_delay = cave->biter_delay_frame;
4571 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4573 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4575 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4577 level->bd_replicators_active = cave->replicators_active;
4578 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4580 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4581 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4583 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4585 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4587 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4588 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4589 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4591 level->bd_infinite_rockets = cave->infinite_rockets;
4593 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4594 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4596 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4597 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4599 level->bd_creatures_start_backwards = cave->creatures_backwards;
4600 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4601 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4603 level->bd_gravity_direction = cave->gravity;
4604 level->bd_gravity_switch_active = cave->gravity_switch_active;
4605 level->bd_gravity_switch_delay = cave->gravity_change_time;
4606 level->bd_gravity_affects_all = cave->gravity_affects_all;
4608 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4609 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4610 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4611 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4613 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4614 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4615 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4616 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4617 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4618 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4620 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4621 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4622 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4623 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4625 level->bd_color_b = cave->colorb;
4626 level->bd_color_0 = cave->color0;
4627 level->bd_color_1 = cave->color1;
4628 level->bd_color_2 = cave->color2;
4629 level->bd_color_3 = cave->color3;
4630 level->bd_color_4 = cave->color4;
4631 level->bd_color_5 = cave->color5;
4633 // set default color type and colors for BD style level colors
4634 SetDefaultLevelColorType_BD();
4635 SetDefaultLevelColors_BD();
4638 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4640 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4641 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4643 // playfield elements
4644 for (x = 0; x < level->fieldx; x++)
4645 for (y = 0; y < level->fieldy; y++)
4646 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4648 checked_free(cave_name);
4651 static void setTapeInfoToDefaults(void);
4653 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4655 struct LevelInfo_BD *level_bd = level->native_bd_level;
4656 GdCave *cave = level_bd->cave;
4657 GdReplay *replay = level_bd->replay;
4663 // always start with reliable default values
4664 setTapeInfoToDefaults();
4666 tape.level_nr = level_nr; // (currently not used)
4667 tape.random_seed = replay->seed;
4669 TapeSetDateFromIsoDateString(replay->date);
4672 tape.pos[tape.counter].delay = 0;
4674 tape.bd_replay = TRUE;
4676 // all time calculations only used to display approximate tape time
4677 int cave_speed = cave->speed;
4678 int milliseconds_game = 0;
4679 int milliseconds_elapsed = 20;
4681 for (i = 0; i < replay->movements->len; i++)
4683 int replay_action = replay->movements->data[i];
4684 int tape_action = map_action_BD_to_RND(replay_action);
4685 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4686 boolean success = 0;
4690 success = TapeAddAction(action);
4692 milliseconds_game += milliseconds_elapsed;
4694 if (milliseconds_game >= cave_speed)
4696 milliseconds_game -= cave_speed;
4703 tape.pos[tape.counter].delay = 0;
4704 tape.pos[tape.counter].action[0] = 0;
4708 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4714 TapeHaltRecording();
4716 if (!replay->success)
4717 Warn("BD replay is marked as not successful");
4721 // ----------------------------------------------------------------------------
4722 // functions for loading EM level
4723 // ----------------------------------------------------------------------------
4725 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4727 static int ball_xy[8][2] =
4738 struct LevelInfo_EM *level_em = level->native_em_level;
4739 struct CAVE *cav = level_em->cav;
4742 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4743 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4745 cav->time_seconds = level->time;
4746 cav->gems_needed = level->gems_needed;
4748 cav->emerald_score = level->score[SC_EMERALD];
4749 cav->diamond_score = level->score[SC_DIAMOND];
4750 cav->alien_score = level->score[SC_ROBOT];
4751 cav->tank_score = level->score[SC_SPACESHIP];
4752 cav->bug_score = level->score[SC_BUG];
4753 cav->eater_score = level->score[SC_YAMYAM];
4754 cav->nut_score = level->score[SC_NUT];
4755 cav->dynamite_score = level->score[SC_DYNAMITE];
4756 cav->key_score = level->score[SC_KEY];
4757 cav->exit_score = level->score[SC_TIME_BONUS];
4759 cav->num_eater_arrays = level->num_yamyam_contents;
4761 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4762 for (y = 0; y < 3; y++)
4763 for (x = 0; x < 3; x++)
4764 cav->eater_array[i][y * 3 + x] =
4765 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4767 cav->amoeba_time = level->amoeba_speed;
4768 cav->wonderwall_time = level->time_magic_wall;
4769 cav->wheel_time = level->time_wheel;
4771 cav->android_move_time = level->android_move_time;
4772 cav->android_clone_time = level->android_clone_time;
4773 cav->ball_random = level->ball_random;
4774 cav->ball_active = level->ball_active_initial;
4775 cav->ball_time = level->ball_time;
4776 cav->num_ball_arrays = level->num_ball_contents;
4778 cav->lenses_score = level->lenses_score;
4779 cav->magnify_score = level->magnify_score;
4780 cav->slurp_score = level->slurp_score;
4782 cav->lenses_time = level->lenses_time;
4783 cav->magnify_time = level->magnify_time;
4785 cav->wind_time = 9999;
4786 cav->wind_direction =
4787 map_direction_RND_to_EM(level->wind_direction_initial);
4789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4790 for (j = 0; j < 8; j++)
4791 cav->ball_array[i][j] =
4792 map_element_RND_to_EM_cave(level->ball_content[i].
4793 e[ball_xy[j][0]][ball_xy[j][1]]);
4795 map_android_clone_elements_RND_to_EM(level);
4797 // first fill the complete playfield with the empty space element
4798 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4799 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4800 cav->cave[x][y] = Cblank;
4802 // then copy the real level contents from level file into the playfield
4803 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4805 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4807 if (level->field[x][y] == EL_AMOEBA_DEAD)
4808 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4810 cav->cave[x][y] = new_element;
4813 for (i = 0; i < MAX_PLAYERS; i++)
4815 cav->player_x[i] = -1;
4816 cav->player_y[i] = -1;
4819 // initialize player positions and delete players from the playfield
4820 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4822 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4824 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4826 cav->player_x[player_nr] = x;
4827 cav->player_y[player_nr] = y;
4829 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4834 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4836 static int ball_xy[8][2] =
4847 struct LevelInfo_EM *level_em = level->native_em_level;
4848 struct CAVE *cav = level_em->cav;
4851 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4852 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4854 level->time = cav->time_seconds;
4855 level->gems_needed = cav->gems_needed;
4857 sprintf(level->name, "Level %d", level->file_info.nr);
4859 level->score[SC_EMERALD] = cav->emerald_score;
4860 level->score[SC_DIAMOND] = cav->diamond_score;
4861 level->score[SC_ROBOT] = cav->alien_score;
4862 level->score[SC_SPACESHIP] = cav->tank_score;
4863 level->score[SC_BUG] = cav->bug_score;
4864 level->score[SC_YAMYAM] = cav->eater_score;
4865 level->score[SC_NUT] = cav->nut_score;
4866 level->score[SC_DYNAMITE] = cav->dynamite_score;
4867 level->score[SC_KEY] = cav->key_score;
4868 level->score[SC_TIME_BONUS] = cav->exit_score;
4870 level->num_yamyam_contents = cav->num_eater_arrays;
4872 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4873 for (y = 0; y < 3; y++)
4874 for (x = 0; x < 3; x++)
4875 level->yamyam_content[i].e[x][y] =
4876 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4878 level->amoeba_speed = cav->amoeba_time;
4879 level->time_magic_wall = cav->wonderwall_time;
4880 level->time_wheel = cav->wheel_time;
4882 level->android_move_time = cav->android_move_time;
4883 level->android_clone_time = cav->android_clone_time;
4884 level->ball_random = cav->ball_random;
4885 level->ball_active_initial = cav->ball_active;
4886 level->ball_time = cav->ball_time;
4887 level->num_ball_contents = cav->num_ball_arrays;
4889 level->lenses_score = cav->lenses_score;
4890 level->magnify_score = cav->magnify_score;
4891 level->slurp_score = cav->slurp_score;
4893 level->lenses_time = cav->lenses_time;
4894 level->magnify_time = cav->magnify_time;
4896 level->wind_direction_initial =
4897 map_direction_EM_to_RND(cav->wind_direction);
4899 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4900 for (j = 0; j < 8; j++)
4901 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4902 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4904 map_android_clone_elements_EM_to_RND(level);
4906 // convert the playfield (some elements need special treatment)
4907 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4909 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4911 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4912 new_element = EL_AMOEBA_DEAD;
4914 level->field[x][y] = new_element;
4917 for (i = 0; i < MAX_PLAYERS; i++)
4919 // in case of all players set to the same field, use the first player
4920 int nr = MAX_PLAYERS - i - 1;
4921 int jx = cav->player_x[nr];
4922 int jy = cav->player_y[nr];
4924 if (jx != -1 && jy != -1)
4925 level->field[jx][jy] = EL_PLAYER_1 + nr;
4928 // time score is counted for each 10 seconds left in Emerald Mine levels
4929 level->time_score_base = 10;
4933 // ----------------------------------------------------------------------------
4934 // functions for loading SP level
4935 // ----------------------------------------------------------------------------
4937 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4939 struct LevelInfo_SP *level_sp = level->native_sp_level;
4940 LevelInfoType *header = &level_sp->header;
4943 level_sp->width = level->fieldx;
4944 level_sp->height = level->fieldy;
4946 for (x = 0; x < level->fieldx; x++)
4947 for (y = 0; y < level->fieldy; y++)
4948 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4950 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4952 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4953 header->LevelTitle[i] = level->name[i];
4954 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4956 header->InfotronsNeeded = level->gems_needed;
4958 header->SpecialPortCount = 0;
4960 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4962 boolean gravity_port_found = FALSE;
4963 boolean gravity_port_valid = FALSE;
4964 int gravity_port_flag;
4965 int gravity_port_base_element;
4966 int element = level->field[x][y];
4968 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4969 element <= EL_SP_GRAVITY_ON_PORT_UP)
4971 gravity_port_found = TRUE;
4972 gravity_port_valid = TRUE;
4973 gravity_port_flag = 1;
4974 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4976 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4977 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4979 gravity_port_found = TRUE;
4980 gravity_port_valid = TRUE;
4981 gravity_port_flag = 0;
4982 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4984 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4985 element <= EL_SP_GRAVITY_PORT_UP)
4987 // change R'n'D style gravity inverting special port to normal port
4988 // (there are no gravity inverting ports in native Supaplex engine)
4990 gravity_port_found = TRUE;
4991 gravity_port_valid = FALSE;
4992 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4995 if (gravity_port_found)
4997 if (gravity_port_valid &&
4998 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
5000 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
5002 port->PortLocation = (y * level->fieldx + x) * 2;
5003 port->Gravity = gravity_port_flag;
5005 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5007 header->SpecialPortCount++;
5011 // change special gravity port to normal port
5013 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5016 level_sp->playfield[x][y] = element - EL_SP_START;
5021 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5023 struct LevelInfo_SP *level_sp = level->native_sp_level;
5024 LevelInfoType *header = &level_sp->header;
5025 boolean num_invalid_elements = 0;
5028 level->fieldx = level_sp->width;
5029 level->fieldy = level_sp->height;
5031 for (x = 0; x < level->fieldx; x++)
5033 for (y = 0; y < level->fieldy; y++)
5035 int element_old = level_sp->playfield[x][y];
5036 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5038 if (element_new == EL_UNKNOWN)
5040 num_invalid_elements++;
5042 Debug("level:native:SP", "invalid element %d at position %d, %d",
5046 level->field[x][y] = element_new;
5050 if (num_invalid_elements > 0)
5051 Warn("found %d invalid elements%s", num_invalid_elements,
5052 (!options.debug ? " (use '--debug' for more details)" : ""));
5054 for (i = 0; i < MAX_PLAYERS; i++)
5055 level->initial_player_gravity[i] =
5056 (header->InitialGravity == 1 ? TRUE : FALSE);
5058 // skip leading spaces
5059 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5060 if (header->LevelTitle[i] != ' ')
5064 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5065 level->name[j] = header->LevelTitle[i];
5066 level->name[j] = '\0';
5068 // cut trailing spaces
5070 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5071 level->name[j - 1] = '\0';
5073 level->gems_needed = header->InfotronsNeeded;
5075 for (i = 0; i < header->SpecialPortCount; i++)
5077 SpecialPortType *port = &header->SpecialPort[i];
5078 int port_location = port->PortLocation;
5079 int gravity = port->Gravity;
5080 int port_x, port_y, port_element;
5082 port_x = (port_location / 2) % level->fieldx;
5083 port_y = (port_location / 2) / level->fieldx;
5085 if (port_x < 0 || port_x >= level->fieldx ||
5086 port_y < 0 || port_y >= level->fieldy)
5088 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5093 port_element = level->field[port_x][port_y];
5095 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5096 port_element > EL_SP_GRAVITY_PORT_UP)
5098 Warn("no special port at position (%d, %d)", port_x, port_y);
5103 // change previous (wrong) gravity inverting special port to either
5104 // gravity enabling special port or gravity disabling special port
5105 level->field[port_x][port_y] +=
5106 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5107 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5110 // change special gravity ports without database entries to normal ports
5111 for (x = 0; x < level->fieldx; x++)
5112 for (y = 0; y < level->fieldy; y++)
5113 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5114 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5115 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5117 level->time = 0; // no time limit
5118 level->amoeba_speed = 0;
5119 level->time_magic_wall = 0;
5120 level->time_wheel = 0;
5121 level->amoeba_content = EL_EMPTY;
5123 // original Supaplex does not use score values -- rate by playing time
5124 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5125 level->score[i] = 0;
5127 level->rate_time_over_score = TRUE;
5129 // there are no yamyams in supaplex levels
5130 for (i = 0; i < level->num_yamyam_contents; i++)
5131 for (x = 0; x < 3; x++)
5132 for (y = 0; y < 3; y++)
5133 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5136 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5138 struct LevelInfo_SP *level_sp = level->native_sp_level;
5139 struct DemoInfo_SP *demo = &level_sp->demo;
5142 // always start with reliable default values
5143 demo->is_available = FALSE;
5146 if (TAPE_IS_EMPTY(tape))
5149 demo->level_nr = tape.level_nr; // (currently not used)
5151 level_sp->header.DemoRandomSeed = tape.random_seed;
5155 for (i = 0; i < tape.length; i++)
5157 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5158 int demo_repeat = tape.pos[i].delay;
5159 int demo_entries = (demo_repeat + 15) / 16;
5161 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5163 Warn("tape truncated: size exceeds maximum SP demo size %d",
5169 for (j = 0; j < demo_repeat / 16; j++)
5170 demo->data[demo->length++] = 0xf0 | demo_action;
5172 if (demo_repeat % 16)
5173 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5176 demo->is_available = TRUE;
5179 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5181 struct LevelInfo_SP *level_sp = level->native_sp_level;
5182 struct DemoInfo_SP *demo = &level_sp->demo;
5183 char *filename = level->file_info.filename;
5186 // always start with reliable default values
5187 setTapeInfoToDefaults();
5189 if (!demo->is_available)
5192 tape.level_nr = demo->level_nr; // (currently not used)
5193 tape.random_seed = level_sp->header.DemoRandomSeed;
5195 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5198 tape.pos[tape.counter].delay = 0;
5200 for (i = 0; i < demo->length; i++)
5202 int demo_action = demo->data[i] & 0x0f;
5203 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5204 int tape_action = map_key_SP_to_RND(demo_action);
5205 int tape_repeat = demo_repeat + 1;
5206 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5207 boolean success = 0;
5210 for (j = 0; j < tape_repeat; j++)
5211 success = TapeAddAction(action);
5215 Warn("SP demo truncated: size exceeds maximum tape size %d",
5222 TapeHaltRecording();
5226 // ----------------------------------------------------------------------------
5227 // functions for loading MM level
5228 // ----------------------------------------------------------------------------
5230 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5232 struct LevelInfo_MM *level_mm = level->native_mm_level;
5235 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5236 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5238 level_mm->time = level->time;
5239 level_mm->kettles_needed = level->gems_needed;
5240 level_mm->auto_count_kettles = level->auto_count_gems;
5242 level_mm->mm_laser_red = level->mm_laser_red;
5243 level_mm->mm_laser_green = level->mm_laser_green;
5244 level_mm->mm_laser_blue = level->mm_laser_blue;
5246 level_mm->df_laser_red = level->df_laser_red;
5247 level_mm->df_laser_green = level->df_laser_green;
5248 level_mm->df_laser_blue = level->df_laser_blue;
5250 strcpy(level_mm->name, level->name);
5251 strcpy(level_mm->author, level->author);
5253 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5254 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5255 level_mm->score[SC_KEY] = level->score[SC_KEY];
5256 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5257 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5259 level_mm->amoeba_speed = level->amoeba_speed;
5260 level_mm->time_fuse = level->mm_time_fuse;
5261 level_mm->time_bomb = level->mm_time_bomb;
5262 level_mm->time_ball = level->mm_time_ball;
5263 level_mm->time_block = level->mm_time_block;
5265 level_mm->num_ball_contents = level->num_mm_ball_contents;
5266 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5267 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5268 level_mm->explode_ball = level->explode_mm_ball;
5270 for (i = 0; i < level->num_mm_ball_contents; i++)
5271 level_mm->ball_content[i] =
5272 map_element_RND_to_MM(level->mm_ball_content[i]);
5274 for (x = 0; x < level->fieldx; x++)
5275 for (y = 0; y < level->fieldy; y++)
5277 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5280 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5282 struct LevelInfo_MM *level_mm = level->native_mm_level;
5285 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5286 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5288 level->time = level_mm->time;
5289 level->gems_needed = level_mm->kettles_needed;
5290 level->auto_count_gems = level_mm->auto_count_kettles;
5292 level->mm_laser_red = level_mm->mm_laser_red;
5293 level->mm_laser_green = level_mm->mm_laser_green;
5294 level->mm_laser_blue = level_mm->mm_laser_blue;
5296 level->df_laser_red = level_mm->df_laser_red;
5297 level->df_laser_green = level_mm->df_laser_green;
5298 level->df_laser_blue = level_mm->df_laser_blue;
5300 strcpy(level->name, level_mm->name);
5302 // only overwrite author from 'levelinfo.conf' if author defined in level
5303 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5304 strcpy(level->author, level_mm->author);
5306 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5307 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5308 level->score[SC_KEY] = level_mm->score[SC_KEY];
5309 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5310 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5312 level->amoeba_speed = level_mm->amoeba_speed;
5313 level->mm_time_fuse = level_mm->time_fuse;
5314 level->mm_time_bomb = level_mm->time_bomb;
5315 level->mm_time_ball = level_mm->time_ball;
5316 level->mm_time_block = level_mm->time_block;
5318 level->num_mm_ball_contents = level_mm->num_ball_contents;
5319 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5320 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5321 level->explode_mm_ball = level_mm->explode_ball;
5323 for (i = 0; i < level->num_mm_ball_contents; i++)
5324 level->mm_ball_content[i] =
5325 map_element_MM_to_RND(level_mm->ball_content[i]);
5327 for (x = 0; x < level->fieldx; x++)
5328 for (y = 0; y < level->fieldy; y++)
5329 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5333 // ----------------------------------------------------------------------------
5334 // functions for loading DC level
5335 // ----------------------------------------------------------------------------
5337 #define DC_LEVEL_HEADER_SIZE 344
5339 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5342 static int last_data_encoded;
5346 int diff_hi, diff_lo;
5347 int data_hi, data_lo;
5348 unsigned short data_decoded;
5352 last_data_encoded = 0;
5359 diff = data_encoded - last_data_encoded;
5360 diff_hi = diff & ~0xff;
5361 diff_lo = diff & 0xff;
5365 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5366 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5367 data_hi = data_hi & 0xff00;
5369 data_decoded = data_hi | data_lo;
5371 last_data_encoded = data_encoded;
5373 offset1 = (offset1 + 1) % 31;
5374 offset2 = offset2 & 0xff;
5376 return data_decoded;
5379 static int getMappedElement_DC(int element)
5387 // 0x0117 - 0x036e: (?)
5390 // 0x042d - 0x0684: (?)
5406 element = EL_CRYSTAL;
5409 case 0x0e77: // quicksand (boulder)
5410 element = EL_QUICKSAND_FAST_FULL;
5413 case 0x0e99: // slow quicksand (boulder)
5414 element = EL_QUICKSAND_FULL;
5418 element = EL_EM_EXIT_OPEN;
5422 element = EL_EM_EXIT_CLOSED;
5426 element = EL_EM_STEEL_EXIT_OPEN;
5430 element = EL_EM_STEEL_EXIT_CLOSED;
5433 case 0x0f4f: // dynamite (lit 1)
5434 element = EL_EM_DYNAMITE_ACTIVE;
5437 case 0x0f57: // dynamite (lit 2)
5438 element = EL_EM_DYNAMITE_ACTIVE;
5441 case 0x0f5f: // dynamite (lit 3)
5442 element = EL_EM_DYNAMITE_ACTIVE;
5445 case 0x0f67: // dynamite (lit 4)
5446 element = EL_EM_DYNAMITE_ACTIVE;
5453 element = EL_AMOEBA_WET;
5457 element = EL_AMOEBA_DROP;
5461 element = EL_DC_MAGIC_WALL;
5465 element = EL_SPACESHIP_UP;
5469 element = EL_SPACESHIP_DOWN;
5473 element = EL_SPACESHIP_LEFT;
5477 element = EL_SPACESHIP_RIGHT;
5481 element = EL_BUG_UP;
5485 element = EL_BUG_DOWN;
5489 element = EL_BUG_LEFT;
5493 element = EL_BUG_RIGHT;
5497 element = EL_MOLE_UP;
5501 element = EL_MOLE_DOWN;
5505 element = EL_MOLE_LEFT;
5509 element = EL_MOLE_RIGHT;
5517 element = EL_YAMYAM_UP;
5521 element = EL_SWITCHGATE_OPEN;
5525 element = EL_SWITCHGATE_CLOSED;
5529 element = EL_DC_SWITCHGATE_SWITCH_UP;
5533 element = EL_TIMEGATE_CLOSED;
5536 case 0x144c: // conveyor belt switch (green)
5537 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5540 case 0x144f: // conveyor belt switch (red)
5541 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5544 case 0x1452: // conveyor belt switch (blue)
5545 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5549 element = EL_CONVEYOR_BELT_3_MIDDLE;
5553 element = EL_CONVEYOR_BELT_3_LEFT;
5557 element = EL_CONVEYOR_BELT_3_RIGHT;
5561 element = EL_CONVEYOR_BELT_1_MIDDLE;
5565 element = EL_CONVEYOR_BELT_1_LEFT;
5569 element = EL_CONVEYOR_BELT_1_RIGHT;
5573 element = EL_CONVEYOR_BELT_4_MIDDLE;
5577 element = EL_CONVEYOR_BELT_4_LEFT;
5581 element = EL_CONVEYOR_BELT_4_RIGHT;
5585 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5589 element = EL_EXPANDABLE_WALL_VERTICAL;
5593 element = EL_EXPANDABLE_WALL_ANY;
5596 case 0x14ce: // growing steel wall (left/right)
5597 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5600 case 0x14df: // growing steel wall (up/down)
5601 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5604 case 0x14e8: // growing steel wall (up/down/left/right)
5605 element = EL_EXPANDABLE_STEELWALL_ANY;
5609 element = EL_SHIELD_DEADLY;
5613 element = EL_EXTRA_TIME;
5621 element = EL_EMPTY_SPACE;
5624 case 0x1578: // quicksand (empty)
5625 element = EL_QUICKSAND_FAST_EMPTY;
5628 case 0x1579: // slow quicksand (empty)
5629 element = EL_QUICKSAND_EMPTY;
5639 element = EL_EM_DYNAMITE;
5642 case 0x15a1: // key (red)
5643 element = EL_EM_KEY_1;
5646 case 0x15a2: // key (yellow)
5647 element = EL_EM_KEY_2;
5650 case 0x15a3: // key (blue)
5651 element = EL_EM_KEY_4;
5654 case 0x15a4: // key (green)
5655 element = EL_EM_KEY_3;
5658 case 0x15a5: // key (white)
5659 element = EL_DC_KEY_WHITE;
5663 element = EL_WALL_SLIPPERY;
5670 case 0x15a8: // wall (not round)
5674 case 0x15a9: // (blue)
5675 element = EL_CHAR_A;
5678 case 0x15aa: // (blue)
5679 element = EL_CHAR_B;
5682 case 0x15ab: // (blue)
5683 element = EL_CHAR_C;
5686 case 0x15ac: // (blue)
5687 element = EL_CHAR_D;
5690 case 0x15ad: // (blue)
5691 element = EL_CHAR_E;
5694 case 0x15ae: // (blue)
5695 element = EL_CHAR_F;
5698 case 0x15af: // (blue)
5699 element = EL_CHAR_G;
5702 case 0x15b0: // (blue)
5703 element = EL_CHAR_H;
5706 case 0x15b1: // (blue)
5707 element = EL_CHAR_I;
5710 case 0x15b2: // (blue)
5711 element = EL_CHAR_J;
5714 case 0x15b3: // (blue)
5715 element = EL_CHAR_K;
5718 case 0x15b4: // (blue)
5719 element = EL_CHAR_L;
5722 case 0x15b5: // (blue)
5723 element = EL_CHAR_M;
5726 case 0x15b6: // (blue)
5727 element = EL_CHAR_N;
5730 case 0x15b7: // (blue)
5731 element = EL_CHAR_O;
5734 case 0x15b8: // (blue)
5735 element = EL_CHAR_P;
5738 case 0x15b9: // (blue)
5739 element = EL_CHAR_Q;
5742 case 0x15ba: // (blue)
5743 element = EL_CHAR_R;
5746 case 0x15bb: // (blue)
5747 element = EL_CHAR_S;
5750 case 0x15bc: // (blue)
5751 element = EL_CHAR_T;
5754 case 0x15bd: // (blue)
5755 element = EL_CHAR_U;
5758 case 0x15be: // (blue)
5759 element = EL_CHAR_V;
5762 case 0x15bf: // (blue)
5763 element = EL_CHAR_W;
5766 case 0x15c0: // (blue)
5767 element = EL_CHAR_X;
5770 case 0x15c1: // (blue)
5771 element = EL_CHAR_Y;
5774 case 0x15c2: // (blue)
5775 element = EL_CHAR_Z;
5778 case 0x15c3: // (blue)
5779 element = EL_CHAR_AUMLAUT;
5782 case 0x15c4: // (blue)
5783 element = EL_CHAR_OUMLAUT;
5786 case 0x15c5: // (blue)
5787 element = EL_CHAR_UUMLAUT;
5790 case 0x15c6: // (blue)
5791 element = EL_CHAR_0;
5794 case 0x15c7: // (blue)
5795 element = EL_CHAR_1;
5798 case 0x15c8: // (blue)
5799 element = EL_CHAR_2;
5802 case 0x15c9: // (blue)
5803 element = EL_CHAR_3;
5806 case 0x15ca: // (blue)
5807 element = EL_CHAR_4;
5810 case 0x15cb: // (blue)
5811 element = EL_CHAR_5;
5814 case 0x15cc: // (blue)
5815 element = EL_CHAR_6;
5818 case 0x15cd: // (blue)
5819 element = EL_CHAR_7;
5822 case 0x15ce: // (blue)
5823 element = EL_CHAR_8;
5826 case 0x15cf: // (blue)
5827 element = EL_CHAR_9;
5830 case 0x15d0: // (blue)
5831 element = EL_CHAR_PERIOD;
5834 case 0x15d1: // (blue)
5835 element = EL_CHAR_EXCLAM;
5838 case 0x15d2: // (blue)
5839 element = EL_CHAR_COLON;
5842 case 0x15d3: // (blue)
5843 element = EL_CHAR_LESS;
5846 case 0x15d4: // (blue)
5847 element = EL_CHAR_GREATER;
5850 case 0x15d5: // (blue)
5851 element = EL_CHAR_QUESTION;
5854 case 0x15d6: // (blue)
5855 element = EL_CHAR_COPYRIGHT;
5858 case 0x15d7: // (blue)
5859 element = EL_CHAR_UP;
5862 case 0x15d8: // (blue)
5863 element = EL_CHAR_DOWN;
5866 case 0x15d9: // (blue)
5867 element = EL_CHAR_BUTTON;
5870 case 0x15da: // (blue)
5871 element = EL_CHAR_PLUS;
5874 case 0x15db: // (blue)
5875 element = EL_CHAR_MINUS;
5878 case 0x15dc: // (blue)
5879 element = EL_CHAR_APOSTROPHE;
5882 case 0x15dd: // (blue)
5883 element = EL_CHAR_PARENLEFT;
5886 case 0x15de: // (blue)
5887 element = EL_CHAR_PARENRIGHT;
5890 case 0x15df: // (green)
5891 element = EL_CHAR_A;
5894 case 0x15e0: // (green)
5895 element = EL_CHAR_B;
5898 case 0x15e1: // (green)
5899 element = EL_CHAR_C;
5902 case 0x15e2: // (green)
5903 element = EL_CHAR_D;
5906 case 0x15e3: // (green)
5907 element = EL_CHAR_E;
5910 case 0x15e4: // (green)
5911 element = EL_CHAR_F;
5914 case 0x15e5: // (green)
5915 element = EL_CHAR_G;
5918 case 0x15e6: // (green)
5919 element = EL_CHAR_H;
5922 case 0x15e7: // (green)
5923 element = EL_CHAR_I;
5926 case 0x15e8: // (green)
5927 element = EL_CHAR_J;
5930 case 0x15e9: // (green)
5931 element = EL_CHAR_K;
5934 case 0x15ea: // (green)
5935 element = EL_CHAR_L;
5938 case 0x15eb: // (green)
5939 element = EL_CHAR_M;
5942 case 0x15ec: // (green)
5943 element = EL_CHAR_N;
5946 case 0x15ed: // (green)
5947 element = EL_CHAR_O;
5950 case 0x15ee: // (green)
5951 element = EL_CHAR_P;
5954 case 0x15ef: // (green)
5955 element = EL_CHAR_Q;
5958 case 0x15f0: // (green)
5959 element = EL_CHAR_R;
5962 case 0x15f1: // (green)
5963 element = EL_CHAR_S;
5966 case 0x15f2: // (green)
5967 element = EL_CHAR_T;
5970 case 0x15f3: // (green)
5971 element = EL_CHAR_U;
5974 case 0x15f4: // (green)
5975 element = EL_CHAR_V;
5978 case 0x15f5: // (green)
5979 element = EL_CHAR_W;
5982 case 0x15f6: // (green)
5983 element = EL_CHAR_X;
5986 case 0x15f7: // (green)
5987 element = EL_CHAR_Y;
5990 case 0x15f8: // (green)
5991 element = EL_CHAR_Z;
5994 case 0x15f9: // (green)
5995 element = EL_CHAR_AUMLAUT;
5998 case 0x15fa: // (green)
5999 element = EL_CHAR_OUMLAUT;
6002 case 0x15fb: // (green)
6003 element = EL_CHAR_UUMLAUT;
6006 case 0x15fc: // (green)
6007 element = EL_CHAR_0;
6010 case 0x15fd: // (green)
6011 element = EL_CHAR_1;
6014 case 0x15fe: // (green)
6015 element = EL_CHAR_2;
6018 case 0x15ff: // (green)
6019 element = EL_CHAR_3;
6022 case 0x1600: // (green)
6023 element = EL_CHAR_4;
6026 case 0x1601: // (green)
6027 element = EL_CHAR_5;
6030 case 0x1602: // (green)
6031 element = EL_CHAR_6;
6034 case 0x1603: // (green)
6035 element = EL_CHAR_7;
6038 case 0x1604: // (green)
6039 element = EL_CHAR_8;
6042 case 0x1605: // (green)
6043 element = EL_CHAR_9;
6046 case 0x1606: // (green)
6047 element = EL_CHAR_PERIOD;
6050 case 0x1607: // (green)
6051 element = EL_CHAR_EXCLAM;
6054 case 0x1608: // (green)
6055 element = EL_CHAR_COLON;
6058 case 0x1609: // (green)
6059 element = EL_CHAR_LESS;
6062 case 0x160a: // (green)
6063 element = EL_CHAR_GREATER;
6066 case 0x160b: // (green)
6067 element = EL_CHAR_QUESTION;
6070 case 0x160c: // (green)
6071 element = EL_CHAR_COPYRIGHT;
6074 case 0x160d: // (green)
6075 element = EL_CHAR_UP;
6078 case 0x160e: // (green)
6079 element = EL_CHAR_DOWN;
6082 case 0x160f: // (green)
6083 element = EL_CHAR_BUTTON;
6086 case 0x1610: // (green)
6087 element = EL_CHAR_PLUS;
6090 case 0x1611: // (green)
6091 element = EL_CHAR_MINUS;
6094 case 0x1612: // (green)
6095 element = EL_CHAR_APOSTROPHE;
6098 case 0x1613: // (green)
6099 element = EL_CHAR_PARENLEFT;
6102 case 0x1614: // (green)
6103 element = EL_CHAR_PARENRIGHT;
6106 case 0x1615: // (blue steel)
6107 element = EL_STEEL_CHAR_A;
6110 case 0x1616: // (blue steel)
6111 element = EL_STEEL_CHAR_B;
6114 case 0x1617: // (blue steel)
6115 element = EL_STEEL_CHAR_C;
6118 case 0x1618: // (blue steel)
6119 element = EL_STEEL_CHAR_D;
6122 case 0x1619: // (blue steel)
6123 element = EL_STEEL_CHAR_E;
6126 case 0x161a: // (blue steel)
6127 element = EL_STEEL_CHAR_F;
6130 case 0x161b: // (blue steel)
6131 element = EL_STEEL_CHAR_G;
6134 case 0x161c: // (blue steel)
6135 element = EL_STEEL_CHAR_H;
6138 case 0x161d: // (blue steel)
6139 element = EL_STEEL_CHAR_I;
6142 case 0x161e: // (blue steel)
6143 element = EL_STEEL_CHAR_J;
6146 case 0x161f: // (blue steel)
6147 element = EL_STEEL_CHAR_K;
6150 case 0x1620: // (blue steel)
6151 element = EL_STEEL_CHAR_L;
6154 case 0x1621: // (blue steel)
6155 element = EL_STEEL_CHAR_M;
6158 case 0x1622: // (blue steel)
6159 element = EL_STEEL_CHAR_N;
6162 case 0x1623: // (blue steel)
6163 element = EL_STEEL_CHAR_O;
6166 case 0x1624: // (blue steel)
6167 element = EL_STEEL_CHAR_P;
6170 case 0x1625: // (blue steel)
6171 element = EL_STEEL_CHAR_Q;
6174 case 0x1626: // (blue steel)
6175 element = EL_STEEL_CHAR_R;
6178 case 0x1627: // (blue steel)
6179 element = EL_STEEL_CHAR_S;
6182 case 0x1628: // (blue steel)
6183 element = EL_STEEL_CHAR_T;
6186 case 0x1629: // (blue steel)
6187 element = EL_STEEL_CHAR_U;
6190 case 0x162a: // (blue steel)
6191 element = EL_STEEL_CHAR_V;
6194 case 0x162b: // (blue steel)
6195 element = EL_STEEL_CHAR_W;
6198 case 0x162c: // (blue steel)
6199 element = EL_STEEL_CHAR_X;
6202 case 0x162d: // (blue steel)
6203 element = EL_STEEL_CHAR_Y;
6206 case 0x162e: // (blue steel)
6207 element = EL_STEEL_CHAR_Z;
6210 case 0x162f: // (blue steel)
6211 element = EL_STEEL_CHAR_AUMLAUT;
6214 case 0x1630: // (blue steel)
6215 element = EL_STEEL_CHAR_OUMLAUT;
6218 case 0x1631: // (blue steel)
6219 element = EL_STEEL_CHAR_UUMLAUT;
6222 case 0x1632: // (blue steel)
6223 element = EL_STEEL_CHAR_0;
6226 case 0x1633: // (blue steel)
6227 element = EL_STEEL_CHAR_1;
6230 case 0x1634: // (blue steel)
6231 element = EL_STEEL_CHAR_2;
6234 case 0x1635: // (blue steel)
6235 element = EL_STEEL_CHAR_3;
6238 case 0x1636: // (blue steel)
6239 element = EL_STEEL_CHAR_4;
6242 case 0x1637: // (blue steel)
6243 element = EL_STEEL_CHAR_5;
6246 case 0x1638: // (blue steel)
6247 element = EL_STEEL_CHAR_6;
6250 case 0x1639: // (blue steel)
6251 element = EL_STEEL_CHAR_7;
6254 case 0x163a: // (blue steel)
6255 element = EL_STEEL_CHAR_8;
6258 case 0x163b: // (blue steel)
6259 element = EL_STEEL_CHAR_9;
6262 case 0x163c: // (blue steel)
6263 element = EL_STEEL_CHAR_PERIOD;
6266 case 0x163d: // (blue steel)
6267 element = EL_STEEL_CHAR_EXCLAM;
6270 case 0x163e: // (blue steel)
6271 element = EL_STEEL_CHAR_COLON;
6274 case 0x163f: // (blue steel)
6275 element = EL_STEEL_CHAR_LESS;
6278 case 0x1640: // (blue steel)
6279 element = EL_STEEL_CHAR_GREATER;
6282 case 0x1641: // (blue steel)
6283 element = EL_STEEL_CHAR_QUESTION;
6286 case 0x1642: // (blue steel)
6287 element = EL_STEEL_CHAR_COPYRIGHT;
6290 case 0x1643: // (blue steel)
6291 element = EL_STEEL_CHAR_UP;
6294 case 0x1644: // (blue steel)
6295 element = EL_STEEL_CHAR_DOWN;
6298 case 0x1645: // (blue steel)
6299 element = EL_STEEL_CHAR_BUTTON;
6302 case 0x1646: // (blue steel)
6303 element = EL_STEEL_CHAR_PLUS;
6306 case 0x1647: // (blue steel)
6307 element = EL_STEEL_CHAR_MINUS;
6310 case 0x1648: // (blue steel)
6311 element = EL_STEEL_CHAR_APOSTROPHE;
6314 case 0x1649: // (blue steel)
6315 element = EL_STEEL_CHAR_PARENLEFT;
6318 case 0x164a: // (blue steel)
6319 element = EL_STEEL_CHAR_PARENRIGHT;
6322 case 0x164b: // (green steel)
6323 element = EL_STEEL_CHAR_A;
6326 case 0x164c: // (green steel)
6327 element = EL_STEEL_CHAR_B;
6330 case 0x164d: // (green steel)
6331 element = EL_STEEL_CHAR_C;
6334 case 0x164e: // (green steel)
6335 element = EL_STEEL_CHAR_D;
6338 case 0x164f: // (green steel)
6339 element = EL_STEEL_CHAR_E;
6342 case 0x1650: // (green steel)
6343 element = EL_STEEL_CHAR_F;
6346 case 0x1651: // (green steel)
6347 element = EL_STEEL_CHAR_G;
6350 case 0x1652: // (green steel)
6351 element = EL_STEEL_CHAR_H;
6354 case 0x1653: // (green steel)
6355 element = EL_STEEL_CHAR_I;
6358 case 0x1654: // (green steel)
6359 element = EL_STEEL_CHAR_J;
6362 case 0x1655: // (green steel)
6363 element = EL_STEEL_CHAR_K;
6366 case 0x1656: // (green steel)
6367 element = EL_STEEL_CHAR_L;
6370 case 0x1657: // (green steel)
6371 element = EL_STEEL_CHAR_M;
6374 case 0x1658: // (green steel)
6375 element = EL_STEEL_CHAR_N;
6378 case 0x1659: // (green steel)
6379 element = EL_STEEL_CHAR_O;
6382 case 0x165a: // (green steel)
6383 element = EL_STEEL_CHAR_P;
6386 case 0x165b: // (green steel)
6387 element = EL_STEEL_CHAR_Q;
6390 case 0x165c: // (green steel)
6391 element = EL_STEEL_CHAR_R;
6394 case 0x165d: // (green steel)
6395 element = EL_STEEL_CHAR_S;
6398 case 0x165e: // (green steel)
6399 element = EL_STEEL_CHAR_T;
6402 case 0x165f: // (green steel)
6403 element = EL_STEEL_CHAR_U;
6406 case 0x1660: // (green steel)
6407 element = EL_STEEL_CHAR_V;
6410 case 0x1661: // (green steel)
6411 element = EL_STEEL_CHAR_W;
6414 case 0x1662: // (green steel)
6415 element = EL_STEEL_CHAR_X;
6418 case 0x1663: // (green steel)
6419 element = EL_STEEL_CHAR_Y;
6422 case 0x1664: // (green steel)
6423 element = EL_STEEL_CHAR_Z;
6426 case 0x1665: // (green steel)
6427 element = EL_STEEL_CHAR_AUMLAUT;
6430 case 0x1666: // (green steel)
6431 element = EL_STEEL_CHAR_OUMLAUT;
6434 case 0x1667: // (green steel)
6435 element = EL_STEEL_CHAR_UUMLAUT;
6438 case 0x1668: // (green steel)
6439 element = EL_STEEL_CHAR_0;
6442 case 0x1669: // (green steel)
6443 element = EL_STEEL_CHAR_1;
6446 case 0x166a: // (green steel)
6447 element = EL_STEEL_CHAR_2;
6450 case 0x166b: // (green steel)
6451 element = EL_STEEL_CHAR_3;
6454 case 0x166c: // (green steel)
6455 element = EL_STEEL_CHAR_4;
6458 case 0x166d: // (green steel)
6459 element = EL_STEEL_CHAR_5;
6462 case 0x166e: // (green steel)
6463 element = EL_STEEL_CHAR_6;
6466 case 0x166f: // (green steel)
6467 element = EL_STEEL_CHAR_7;
6470 case 0x1670: // (green steel)
6471 element = EL_STEEL_CHAR_8;
6474 case 0x1671: // (green steel)
6475 element = EL_STEEL_CHAR_9;
6478 case 0x1672: // (green steel)
6479 element = EL_STEEL_CHAR_PERIOD;
6482 case 0x1673: // (green steel)
6483 element = EL_STEEL_CHAR_EXCLAM;
6486 case 0x1674: // (green steel)
6487 element = EL_STEEL_CHAR_COLON;
6490 case 0x1675: // (green steel)
6491 element = EL_STEEL_CHAR_LESS;
6494 case 0x1676: // (green steel)
6495 element = EL_STEEL_CHAR_GREATER;
6498 case 0x1677: // (green steel)
6499 element = EL_STEEL_CHAR_QUESTION;
6502 case 0x1678: // (green steel)
6503 element = EL_STEEL_CHAR_COPYRIGHT;
6506 case 0x1679: // (green steel)
6507 element = EL_STEEL_CHAR_UP;
6510 case 0x167a: // (green steel)
6511 element = EL_STEEL_CHAR_DOWN;
6514 case 0x167b: // (green steel)
6515 element = EL_STEEL_CHAR_BUTTON;
6518 case 0x167c: // (green steel)
6519 element = EL_STEEL_CHAR_PLUS;
6522 case 0x167d: // (green steel)
6523 element = EL_STEEL_CHAR_MINUS;
6526 case 0x167e: // (green steel)
6527 element = EL_STEEL_CHAR_APOSTROPHE;
6530 case 0x167f: // (green steel)
6531 element = EL_STEEL_CHAR_PARENLEFT;
6534 case 0x1680: // (green steel)
6535 element = EL_STEEL_CHAR_PARENRIGHT;
6538 case 0x1681: // gate (red)
6539 element = EL_EM_GATE_1;
6542 case 0x1682: // secret gate (red)
6543 element = EL_EM_GATE_1_GRAY;
6546 case 0x1683: // gate (yellow)
6547 element = EL_EM_GATE_2;
6550 case 0x1684: // secret gate (yellow)
6551 element = EL_EM_GATE_2_GRAY;
6554 case 0x1685: // gate (blue)
6555 element = EL_EM_GATE_4;
6558 case 0x1686: // secret gate (blue)
6559 element = EL_EM_GATE_4_GRAY;
6562 case 0x1687: // gate (green)
6563 element = EL_EM_GATE_3;
6566 case 0x1688: // secret gate (green)
6567 element = EL_EM_GATE_3_GRAY;
6570 case 0x1689: // gate (white)
6571 element = EL_DC_GATE_WHITE;
6574 case 0x168a: // secret gate (white)
6575 element = EL_DC_GATE_WHITE_GRAY;
6578 case 0x168b: // secret gate (no key)
6579 element = EL_DC_GATE_FAKE_GRAY;
6583 element = EL_ROBOT_WHEEL;
6587 element = EL_DC_TIMEGATE_SWITCH;
6591 element = EL_ACID_POOL_BOTTOM;
6595 element = EL_ACID_POOL_TOPLEFT;
6599 element = EL_ACID_POOL_TOPRIGHT;
6603 element = EL_ACID_POOL_BOTTOMLEFT;
6607 element = EL_ACID_POOL_BOTTOMRIGHT;
6611 element = EL_STEELWALL;
6615 element = EL_STEELWALL_SLIPPERY;
6618 case 0x1695: // steel wall (not round)
6619 element = EL_STEELWALL;
6622 case 0x1696: // steel wall (left)
6623 element = EL_DC_STEELWALL_1_LEFT;
6626 case 0x1697: // steel wall (bottom)
6627 element = EL_DC_STEELWALL_1_BOTTOM;
6630 case 0x1698: // steel wall (right)
6631 element = EL_DC_STEELWALL_1_RIGHT;
6634 case 0x1699: // steel wall (top)
6635 element = EL_DC_STEELWALL_1_TOP;
6638 case 0x169a: // steel wall (left/bottom)
6639 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6642 case 0x169b: // steel wall (right/bottom)
6643 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6646 case 0x169c: // steel wall (right/top)
6647 element = EL_DC_STEELWALL_1_TOPRIGHT;
6650 case 0x169d: // steel wall (left/top)
6651 element = EL_DC_STEELWALL_1_TOPLEFT;
6654 case 0x169e: // steel wall (right/bottom small)
6655 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6658 case 0x169f: // steel wall (left/bottom small)
6659 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6662 case 0x16a0: // steel wall (right/top small)
6663 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6666 case 0x16a1: // steel wall (left/top small)
6667 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6670 case 0x16a2: // steel wall (left/right)
6671 element = EL_DC_STEELWALL_1_VERTICAL;
6674 case 0x16a3: // steel wall (top/bottom)
6675 element = EL_DC_STEELWALL_1_HORIZONTAL;
6678 case 0x16a4: // steel wall 2 (left end)
6679 element = EL_DC_STEELWALL_2_LEFT;
6682 case 0x16a5: // steel wall 2 (right end)
6683 element = EL_DC_STEELWALL_2_RIGHT;
6686 case 0x16a6: // steel wall 2 (top end)
6687 element = EL_DC_STEELWALL_2_TOP;
6690 case 0x16a7: // steel wall 2 (bottom end)
6691 element = EL_DC_STEELWALL_2_BOTTOM;
6694 case 0x16a8: // steel wall 2 (left/right)
6695 element = EL_DC_STEELWALL_2_HORIZONTAL;
6698 case 0x16a9: // steel wall 2 (up/down)
6699 element = EL_DC_STEELWALL_2_VERTICAL;
6702 case 0x16aa: // steel wall 2 (mid)
6703 element = EL_DC_STEELWALL_2_MIDDLE;
6707 element = EL_SIGN_EXCLAMATION;
6711 element = EL_SIGN_RADIOACTIVITY;
6715 element = EL_SIGN_STOP;
6719 element = EL_SIGN_WHEELCHAIR;
6723 element = EL_SIGN_PARKING;
6727 element = EL_SIGN_NO_ENTRY;
6731 element = EL_SIGN_HEART;
6735 element = EL_SIGN_GIVE_WAY;
6739 element = EL_SIGN_ENTRY_FORBIDDEN;
6743 element = EL_SIGN_EMERGENCY_EXIT;
6747 element = EL_SIGN_YIN_YANG;
6751 element = EL_WALL_EMERALD;
6755 element = EL_WALL_DIAMOND;
6759 element = EL_WALL_PEARL;
6763 element = EL_WALL_CRYSTAL;
6767 element = EL_INVISIBLE_WALL;
6771 element = EL_INVISIBLE_STEELWALL;
6775 // EL_INVISIBLE_SAND
6778 element = EL_LIGHT_SWITCH;
6782 element = EL_ENVELOPE_1;
6786 if (element >= 0x0117 && element <= 0x036e) // (?)
6787 element = EL_DIAMOND;
6788 else if (element >= 0x042d && element <= 0x0684) // (?)
6789 element = EL_EMERALD;
6790 else if (element >= 0x157c && element <= 0x158b)
6792 else if (element >= 0x1590 && element <= 0x159f)
6793 element = EL_DC_LANDMINE;
6794 else if (element >= 0x16bc && element <= 0x16cb)
6795 element = EL_INVISIBLE_SAND;
6798 Warn("unknown Diamond Caves element 0x%04x", element);
6800 element = EL_UNKNOWN;
6805 return getMappedElement(element);
6808 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6810 byte header[DC_LEVEL_HEADER_SIZE];
6812 int envelope_header_pos = 62;
6813 int envelope_content_pos = 94;
6814 int level_name_pos = 251;
6815 int level_author_pos = 292;
6816 int envelope_header_len;
6817 int envelope_content_len;
6819 int level_author_len;
6821 int num_yamyam_contents;
6824 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6826 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6828 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6830 header[i * 2 + 0] = header_word >> 8;
6831 header[i * 2 + 1] = header_word & 0xff;
6834 // read some values from level header to check level decoding integrity
6835 fieldx = header[6] | (header[7] << 8);
6836 fieldy = header[8] | (header[9] << 8);
6837 num_yamyam_contents = header[60] | (header[61] << 8);
6839 // do some simple sanity checks to ensure that level was correctly decoded
6840 if (fieldx < 1 || fieldx > 256 ||
6841 fieldy < 1 || fieldy > 256 ||
6842 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6844 level->no_valid_file = TRUE;
6846 Warn("cannot decode level from stream -- using empty level");
6851 // maximum envelope header size is 31 bytes
6852 envelope_header_len = header[envelope_header_pos];
6853 // maximum envelope content size is 110 (156?) bytes
6854 envelope_content_len = header[envelope_content_pos];
6856 // maximum level title size is 40 bytes
6857 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6858 // maximum level author size is 30 (51?) bytes
6859 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6863 for (i = 0; i < envelope_header_len; i++)
6864 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6865 level->envelope[0].text[envelope_size++] =
6866 header[envelope_header_pos + 1 + i];
6868 if (envelope_header_len > 0 && envelope_content_len > 0)
6870 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6871 level->envelope[0].text[envelope_size++] = '\n';
6872 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6873 level->envelope[0].text[envelope_size++] = '\n';
6876 for (i = 0; i < envelope_content_len; i++)
6877 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6878 level->envelope[0].text[envelope_size++] =
6879 header[envelope_content_pos + 1 + i];
6881 level->envelope[0].text[envelope_size] = '\0';
6883 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6884 level->envelope[0].ysize = 10;
6885 level->envelope[0].autowrap = TRUE;
6886 level->envelope[0].centered = TRUE;
6888 for (i = 0; i < level_name_len; i++)
6889 level->name[i] = header[level_name_pos + 1 + i];
6890 level->name[level_name_len] = '\0';
6892 for (i = 0; i < level_author_len; i++)
6893 level->author[i] = header[level_author_pos + 1 + i];
6894 level->author[level_author_len] = '\0';
6896 num_yamyam_contents = header[60] | (header[61] << 8);
6897 level->num_yamyam_contents =
6898 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6900 for (i = 0; i < num_yamyam_contents; i++)
6902 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6904 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6905 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6907 if (i < MAX_ELEMENT_CONTENTS)
6908 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6912 fieldx = header[6] | (header[7] << 8);
6913 fieldy = header[8] | (header[9] << 8);
6914 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6915 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6917 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6919 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6920 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6922 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6923 level->field[x][y] = getMappedElement_DC(element_dc);
6926 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6927 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6928 level->field[x][y] = EL_PLAYER_1;
6930 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6931 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6932 level->field[x][y] = EL_PLAYER_2;
6934 level->gems_needed = header[18] | (header[19] << 8);
6936 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6937 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6938 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6939 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6940 level->score[SC_NUT] = header[28] | (header[29] << 8);
6941 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6942 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6943 level->score[SC_BUG] = header[34] | (header[35] << 8);
6944 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6945 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6946 level->score[SC_KEY] = header[40] | (header[41] << 8);
6947 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6949 level->time = header[44] | (header[45] << 8);
6951 level->amoeba_speed = header[46] | (header[47] << 8);
6952 level->time_light = header[48] | (header[49] << 8);
6953 level->time_timegate = header[50] | (header[51] << 8);
6954 level->time_wheel = header[52] | (header[53] << 8);
6955 level->time_magic_wall = header[54] | (header[55] << 8);
6956 level->extra_time = header[56] | (header[57] << 8);
6957 level->shield_normal_time = header[58] | (header[59] << 8);
6959 // shield and extra time elements do not have a score
6960 level->score[SC_SHIELD] = 0;
6961 level->extra_time_score = 0;
6963 // set time for normal and deadly shields to the same value
6964 level->shield_deadly_time = level->shield_normal_time;
6966 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6967 // can slip down from flat walls, like normal walls and steel walls
6968 level->em_slippery_gems = TRUE;
6970 // time score is counted for each 10 seconds left in Diamond Caves levels
6971 level->time_score_base = 10;
6974 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6975 struct LevelFileInfo *level_file_info,
6976 boolean level_info_only)
6978 char *filename = level_file_info->filename;
6980 int num_magic_bytes = 8;
6981 char magic_bytes[num_magic_bytes + 1];
6982 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6984 if (!(file = openFile(filename, MODE_READ)))
6986 level->no_valid_file = TRUE;
6988 if (!level_info_only)
6989 Warn("cannot read level '%s' -- using empty level", filename);
6994 // fseek(file, 0x0000, SEEK_SET);
6996 if (level_file_info->packed)
6998 // read "magic bytes" from start of file
6999 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
7000 magic_bytes[0] = '\0';
7002 // check "magic bytes" for correct file format
7003 if (!strPrefix(magic_bytes, "DC2"))
7005 level->no_valid_file = TRUE;
7007 Warn("unknown DC level file '%s' -- using empty level", filename);
7012 if (strPrefix(magic_bytes, "DC2Win95") ||
7013 strPrefix(magic_bytes, "DC2Win98"))
7015 int position_first_level = 0x00fa;
7016 int extra_bytes = 4;
7019 // advance file stream to first level inside the level package
7020 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7022 // each block of level data is followed by block of non-level data
7023 num_levels_to_skip *= 2;
7025 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7026 while (num_levels_to_skip >= 0)
7028 // advance file stream to next level inside the level package
7029 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7031 level->no_valid_file = TRUE;
7033 Warn("cannot fseek in file '%s' -- using empty level", filename);
7038 // skip apparently unused extra bytes following each level
7039 ReadUnusedBytesFromFile(file, extra_bytes);
7041 // read size of next level in level package
7042 skip_bytes = getFile32BitLE(file);
7044 num_levels_to_skip--;
7049 level->no_valid_file = TRUE;
7051 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7057 LoadLevelFromFileStream_DC(file, level);
7063 // ----------------------------------------------------------------------------
7064 // functions for loading SB level
7065 // ----------------------------------------------------------------------------
7067 int getMappedElement_SB(int element_ascii, boolean use_ces)
7075 sb_element_mapping[] =
7077 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7078 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7079 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7080 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7081 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7082 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7083 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7084 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7091 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7092 if (element_ascii == sb_element_mapping[i].ascii)
7093 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7095 return EL_UNDEFINED;
7098 static void SetLevelSettings_SB(struct LevelInfo *level)
7102 level->use_step_counter = TRUE;
7105 level->score[SC_TIME_BONUS] = 0;
7106 level->time_score_base = 1;
7107 level->rate_time_over_score = TRUE;
7110 level->auto_exit_sokoban = TRUE;
7113 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7114 struct LevelFileInfo *level_file_info,
7115 boolean level_info_only)
7117 char *filename = level_file_info->filename;
7118 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7119 char last_comment[MAX_LINE_LEN];
7120 char level_name[MAX_LINE_LEN];
7123 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7124 boolean read_continued_line = FALSE;
7125 boolean reading_playfield = FALSE;
7126 boolean got_valid_playfield_line = FALSE;
7127 boolean invalid_playfield_char = FALSE;
7128 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7129 int file_level_nr = 0;
7130 int x = 0, y = 0; // initialized to make compilers happy
7132 last_comment[0] = '\0';
7133 level_name[0] = '\0';
7135 if (!(file = openFile(filename, MODE_READ)))
7137 level->no_valid_file = TRUE;
7139 if (!level_info_only)
7140 Warn("cannot read level '%s' -- using empty level", filename);
7145 while (!checkEndOfFile(file))
7147 // level successfully read, but next level may follow here
7148 if (!got_valid_playfield_line && reading_playfield)
7150 // read playfield from single level file -- skip remaining file
7151 if (!level_file_info->packed)
7154 if (file_level_nr >= num_levels_to_skip)
7159 last_comment[0] = '\0';
7160 level_name[0] = '\0';
7162 reading_playfield = FALSE;
7165 got_valid_playfield_line = FALSE;
7167 // read next line of input file
7168 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7171 // cut trailing line break (this can be newline and/or carriage return)
7172 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7173 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7176 // copy raw input line for later use (mainly debugging output)
7177 strcpy(line_raw, line);
7179 if (read_continued_line)
7181 // append new line to existing line, if there is enough space
7182 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7183 strcat(previous_line, line_ptr);
7185 strcpy(line, previous_line); // copy storage buffer to line
7187 read_continued_line = FALSE;
7190 // if the last character is '\', continue at next line
7191 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7193 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7194 strcpy(previous_line, line); // copy line to storage buffer
7196 read_continued_line = TRUE;
7202 if (line[0] == '\0')
7205 // extract comment text from comment line
7208 for (line_ptr = line; *line_ptr; line_ptr++)
7209 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7212 strcpy(last_comment, line_ptr);
7217 // extract level title text from line containing level title
7218 if (line[0] == '\'')
7220 strcpy(level_name, &line[1]);
7222 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7223 level_name[strlen(level_name) - 1] = '\0';
7228 // skip lines containing only spaces (or empty lines)
7229 for (line_ptr = line; *line_ptr; line_ptr++)
7230 if (*line_ptr != ' ')
7232 if (*line_ptr == '\0')
7235 // at this point, we have found a line containing part of a playfield
7237 got_valid_playfield_line = TRUE;
7239 if (!reading_playfield)
7241 reading_playfield = TRUE;
7242 invalid_playfield_char = FALSE;
7244 for (x = 0; x < MAX_LEV_FIELDX; x++)
7245 for (y = 0; y < MAX_LEV_FIELDY; y++)
7246 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7251 // start with topmost tile row
7255 // skip playfield line if larger row than allowed
7256 if (y >= MAX_LEV_FIELDY)
7259 // start with leftmost tile column
7262 // read playfield elements from line
7263 for (line_ptr = line; *line_ptr; line_ptr++)
7265 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7267 // stop parsing playfield line if larger column than allowed
7268 if (x >= MAX_LEV_FIELDX)
7271 if (mapped_sb_element == EL_UNDEFINED)
7273 invalid_playfield_char = TRUE;
7278 level->field[x][y] = mapped_sb_element;
7280 // continue with next tile column
7283 level->fieldx = MAX(x, level->fieldx);
7286 if (invalid_playfield_char)
7288 // if first playfield line, treat invalid lines as comment lines
7290 reading_playfield = FALSE;
7295 // continue with next tile row
7303 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7304 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7306 if (!reading_playfield)
7308 level->no_valid_file = TRUE;
7310 Warn("cannot read level '%s' -- using empty level", filename);
7315 if (*level_name != '\0')
7317 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7318 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7320 else if (*last_comment != '\0')
7322 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7323 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7327 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7330 // set all empty fields beyond the border walls to invisible steel wall
7331 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7333 if ((x == 0 || x == level->fieldx - 1 ||
7334 y == 0 || y == level->fieldy - 1) &&
7335 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7336 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7337 level->field, level->fieldx, level->fieldy);
7340 // set special level settings for Sokoban levels
7341 SetLevelSettings_SB(level);
7343 if (load_xsb_to_ces)
7345 // special global settings can now be set in level template
7346 level->use_custom_template = TRUE;
7351 // -------------------------------------------------------------------------
7352 // functions for handling native levels
7353 // -------------------------------------------------------------------------
7355 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7356 struct LevelFileInfo *level_file_info,
7357 boolean level_info_only)
7361 // determine position of requested level inside level package
7362 if (level_file_info->packed)
7363 pos = level_file_info->nr - leveldir_current->first_level;
7365 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7366 level->no_valid_file = TRUE;
7369 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7370 struct LevelFileInfo *level_file_info,
7371 boolean level_info_only)
7373 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7374 level->no_valid_file = TRUE;
7377 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7378 struct LevelFileInfo *level_file_info,
7379 boolean level_info_only)
7383 // determine position of requested level inside level package
7384 if (level_file_info->packed)
7385 pos = level_file_info->nr - leveldir_current->first_level;
7387 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7388 level->no_valid_file = TRUE;
7391 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7392 struct LevelFileInfo *level_file_info,
7393 boolean level_info_only)
7395 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7396 level->no_valid_file = TRUE;
7399 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7401 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7402 CopyNativeLevel_RND_to_BD(level);
7403 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7404 CopyNativeLevel_RND_to_EM(level);
7405 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7406 CopyNativeLevel_RND_to_SP(level);
7407 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7408 CopyNativeLevel_RND_to_MM(level);
7411 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7413 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7414 CopyNativeLevel_BD_to_RND(level);
7415 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7416 CopyNativeLevel_EM_to_RND(level);
7417 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7418 CopyNativeLevel_SP_to_RND(level);
7419 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7420 CopyNativeLevel_MM_to_RND(level);
7423 void SaveNativeLevel(struct LevelInfo *level)
7425 // saving native level files only supported for some game engines
7426 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7427 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7430 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7431 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7432 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7433 char *filename = getLevelFilenameFromBasename(basename);
7435 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7438 boolean success = FALSE;
7440 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7442 CopyNativeLevel_RND_to_BD(level);
7443 // CopyNativeTape_RND_to_BD(level);
7445 success = SaveNativeLevel_BD(filename);
7447 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7449 CopyNativeLevel_RND_to_SP(level);
7450 CopyNativeTape_RND_to_SP(level);
7452 success = SaveNativeLevel_SP(filename);
7456 Request("Native level file saved!", REQ_CONFIRM);
7458 Request("Failed to save native level file!", REQ_CONFIRM);
7462 // ----------------------------------------------------------------------------
7463 // functions for loading generic level
7464 // ----------------------------------------------------------------------------
7466 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7467 struct LevelFileInfo *level_file_info,
7468 boolean level_info_only)
7470 // always start with reliable default values
7471 setLevelInfoToDefaults(level, level_info_only, TRUE);
7473 switch (level_file_info->type)
7475 case LEVEL_FILE_TYPE_RND:
7476 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7479 case LEVEL_FILE_TYPE_BD:
7480 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7481 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7484 case LEVEL_FILE_TYPE_EM:
7485 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7486 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7489 case LEVEL_FILE_TYPE_SP:
7490 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7491 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7494 case LEVEL_FILE_TYPE_MM:
7495 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7496 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7499 case LEVEL_FILE_TYPE_DC:
7500 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7503 case LEVEL_FILE_TYPE_SB:
7504 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7508 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7512 // if level file is invalid, restore level structure to default values
7513 if (level->no_valid_file)
7514 setLevelInfoToDefaults(level, level_info_only, FALSE);
7516 if (check_special_flags("use_native_bd_game_engine"))
7517 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7519 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7520 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7522 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7523 CopyNativeLevel_Native_to_RND(level);
7526 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7528 static struct LevelFileInfo level_file_info;
7530 // always start with reliable default values
7531 setFileInfoToDefaults(&level_file_info);
7533 level_file_info.nr = 0; // unknown level number
7534 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7536 setString(&level_file_info.filename, filename);
7538 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7541 static void LoadLevel_InitVersion(struct LevelInfo *level)
7545 if (leveldir_current == NULL) // only when dumping level
7548 // all engine modifications also valid for levels which use latest engine
7549 if (level->game_version < VERSION_IDENT(3,2,0,5))
7551 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7552 level->time_score_base = 10;
7555 if (leveldir_current->latest_engine)
7557 // ---------- use latest game engine --------------------------------------
7559 /* For all levels which are forced to use the latest game engine version
7560 (normally all but user contributed, private and undefined levels), set
7561 the game engine version to the actual version; this allows for actual
7562 corrections in the game engine to take effect for existing, converted
7563 levels (from "classic" or other existing games) to make the emulation
7564 of the corresponding game more accurate, while (hopefully) not breaking
7565 existing levels created from other players. */
7567 level->game_version = GAME_VERSION_ACTUAL;
7569 /* Set special EM style gems behaviour: EM style gems slip down from
7570 normal, steel and growing wall. As this is a more fundamental change,
7571 it seems better to set the default behaviour to "off" (as it is more
7572 natural) and make it configurable in the level editor (as a property
7573 of gem style elements). Already existing converted levels (neither
7574 private nor contributed levels) are changed to the new behaviour. */
7576 if (level->file_version < FILE_VERSION_2_0)
7577 level->em_slippery_gems = TRUE;
7582 // ---------- use game engine the level was created with --------------------
7584 /* For all levels which are not forced to use the latest game engine
7585 version (normally user contributed, private and undefined levels),
7586 use the version of the game engine the levels were created for.
7588 Since 2.0.1, the game engine version is now directly stored
7589 in the level file (chunk "VERS"), so there is no need anymore
7590 to set the game version from the file version (except for old,
7591 pre-2.0 levels, where the game version is still taken from the
7592 file format version used to store the level -- see above). */
7594 // player was faster than enemies in 1.0.0 and before
7595 if (level->file_version == FILE_VERSION_1_0)
7596 for (i = 0; i < MAX_PLAYERS; i++)
7597 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7599 // default behaviour for EM style gems was "slippery" only in 2.0.1
7600 if (level->game_version == VERSION_IDENT(2,0,1,0))
7601 level->em_slippery_gems = TRUE;
7603 // springs could be pushed over pits before (pre-release version) 2.2.0
7604 if (level->game_version < VERSION_IDENT(2,2,0,0))
7605 level->use_spring_bug = TRUE;
7607 if (level->game_version < VERSION_IDENT(3,2,0,5))
7609 // time orb caused limited time in endless time levels before 3.2.0-5
7610 level->use_time_orb_bug = TRUE;
7612 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7613 level->block_snap_field = FALSE;
7615 // extra time score was same value as time left score before 3.2.0-5
7616 level->extra_time_score = level->score[SC_TIME_BONUS];
7619 if (level->game_version < VERSION_IDENT(3,2,0,7))
7621 // default behaviour for snapping was "not continuous" before 3.2.0-7
7622 level->continuous_snapping = FALSE;
7625 // only few elements were able to actively move into acid before 3.1.0
7626 // trigger settings did not exist before 3.1.0; set to default "any"
7627 if (level->game_version < VERSION_IDENT(3,1,0,0))
7629 // correct "can move into acid" settings (all zero in old levels)
7631 level->can_move_into_acid_bits = 0; // nothing can move into acid
7632 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7634 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7635 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7636 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7637 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7639 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7640 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7642 // correct trigger settings (stored as zero == "none" in old levels)
7644 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7646 int element = EL_CUSTOM_START + i;
7647 struct ElementInfo *ei = &element_info[element];
7649 for (j = 0; j < ei->num_change_pages; j++)
7651 struct ElementChangeInfo *change = &ei->change_page[j];
7653 change->trigger_player = CH_PLAYER_ANY;
7654 change->trigger_page = CH_PAGE_ANY;
7659 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7661 int element = EL_CUSTOM_256;
7662 struct ElementInfo *ei = &element_info[element];
7663 struct ElementChangeInfo *change = &ei->change_page[0];
7665 /* This is needed to fix a problem that was caused by a bugfix in function
7666 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7667 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7668 not replace walkable elements, but instead just placed the player on it,
7669 without placing the Sokoban field under the player). Unfortunately, this
7670 breaks "Snake Bite" style levels when the snake is halfway through a door
7671 that just closes (the snake head is still alive and can be moved in this
7672 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7673 player (without Sokoban element) which then gets killed as designed). */
7675 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7676 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7677 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7678 change->target_element = EL_PLAYER_1;
7681 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7682 if (level->game_version < VERSION_IDENT(3,2,5,0))
7684 /* This is needed to fix a problem that was caused by a bugfix in function
7685 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7686 corrects the behaviour when a custom element changes to another custom
7687 element with a higher element number that has change actions defined.
7688 Normally, only one change per frame is allowed for custom elements.
7689 Therefore, it is checked if a custom element already changed in the
7690 current frame; if it did, subsequent changes are suppressed.
7691 Unfortunately, this is only checked for element changes, but not for
7692 change actions, which are still executed. As the function above loops
7693 through all custom elements from lower to higher, an element change
7694 resulting in a lower CE number won't be checked again, while a target
7695 element with a higher number will also be checked, and potential change
7696 actions will get executed for this CE, too (which is wrong), while
7697 further changes are ignored (which is correct). As this bugfix breaks
7698 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7699 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7700 behaviour for existing levels and tapes that make use of this bug */
7702 level->use_action_after_change_bug = TRUE;
7705 // not centering level after relocating player was default only in 3.2.3
7706 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7707 level->shifted_relocation = TRUE;
7709 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7710 if (level->game_version < VERSION_IDENT(3,2,6,0))
7711 level->em_explodes_by_fire = TRUE;
7713 // levels were solved by the first player entering an exit up to 4.1.0.0
7714 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7715 level->solved_by_one_player = TRUE;
7717 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7718 if (level->game_version < VERSION_IDENT(4,1,1,1))
7719 level->use_life_bugs = TRUE;
7721 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7722 if (level->game_version < VERSION_IDENT(4,1,1,1))
7723 level->sb_objects_needed = FALSE;
7725 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7726 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7727 level->finish_dig_collect = FALSE;
7729 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7730 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7731 level->keep_walkable_ce = TRUE;
7734 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7736 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7739 // check if this level is (not) a Sokoban level
7740 for (y = 0; y < level->fieldy; y++)
7741 for (x = 0; x < level->fieldx; x++)
7742 if (!IS_SB_ELEMENT(Tile[x][y]))
7743 is_sokoban_level = FALSE;
7745 if (is_sokoban_level)
7747 // set special level settings for Sokoban levels
7748 SetLevelSettings_SB(level);
7752 static void LoadLevel_InitSettings(struct LevelInfo *level)
7754 // adjust level settings for (non-native) Sokoban-style levels
7755 LoadLevel_InitSettings_SB(level);
7757 // rename levels with title "nameless level" or if renaming is forced
7758 if (leveldir_current->empty_level_name != NULL &&
7759 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7760 leveldir_current->force_level_name))
7761 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7762 leveldir_current->empty_level_name, level_nr);
7765 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7769 // map elements that have changed in newer versions
7770 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7771 level->game_version);
7772 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7773 for (x = 0; x < 3; x++)
7774 for (y = 0; y < 3; y++)
7775 level->yamyam_content[i].e[x][y] =
7776 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7777 level->game_version);
7781 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7785 // map custom element change events that have changed in newer versions
7786 // (these following values were accidentally changed in version 3.0.1)
7787 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7788 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7792 int element = EL_CUSTOM_START + i;
7794 // order of checking and copying events to be mapped is important
7795 // (do not change the start and end value -- they are constant)
7796 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7798 if (HAS_CHANGE_EVENT(element, j - 2))
7800 SET_CHANGE_EVENT(element, j - 2, FALSE);
7801 SET_CHANGE_EVENT(element, j, TRUE);
7805 // order of checking and copying events to be mapped is important
7806 // (do not change the start and end value -- they are constant)
7807 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7809 if (HAS_CHANGE_EVENT(element, j - 1))
7811 SET_CHANGE_EVENT(element, j - 1, FALSE);
7812 SET_CHANGE_EVENT(element, j, TRUE);
7818 // initialize "can_change" field for old levels with only one change page
7819 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7821 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7823 int element = EL_CUSTOM_START + i;
7825 if (CAN_CHANGE(element))
7826 element_info[element].change->can_change = TRUE;
7830 // correct custom element values (for old levels without these options)
7831 if (level->game_version < VERSION_IDENT(3,1,1,0))
7833 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7835 int element = EL_CUSTOM_START + i;
7836 struct ElementInfo *ei = &element_info[element];
7838 if (ei->access_direction == MV_NO_DIRECTION)
7839 ei->access_direction = MV_ALL_DIRECTIONS;
7843 // correct custom element values (fix invalid values for all versions)
7846 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7848 int element = EL_CUSTOM_START + i;
7849 struct ElementInfo *ei = &element_info[element];
7851 for (j = 0; j < ei->num_change_pages; j++)
7853 struct ElementChangeInfo *change = &ei->change_page[j];
7855 if (change->trigger_player == CH_PLAYER_NONE)
7856 change->trigger_player = CH_PLAYER_ANY;
7858 if (change->trigger_side == CH_SIDE_NONE)
7859 change->trigger_side = CH_SIDE_ANY;
7864 // initialize "can_explode" field for old levels which did not store this
7865 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7866 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7870 int element = EL_CUSTOM_START + i;
7872 if (EXPLODES_1X1_OLD(element))
7873 element_info[element].explosion_type = EXPLODES_1X1;
7875 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7876 EXPLODES_SMASHED(element) ||
7877 EXPLODES_IMPACT(element)));
7881 // correct previously hard-coded move delay values for maze runner style
7882 if (level->game_version < VERSION_IDENT(3,1,1,0))
7884 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7886 int element = EL_CUSTOM_START + i;
7888 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7890 // previously hard-coded and therefore ignored
7891 element_info[element].move_delay_fixed = 9;
7892 element_info[element].move_delay_random = 0;
7897 // set some other uninitialized values of custom elements in older levels
7898 if (level->game_version < VERSION_IDENT(3,1,0,0))
7900 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7902 int element = EL_CUSTOM_START + i;
7904 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7906 element_info[element].explosion_delay = 17;
7907 element_info[element].ignition_delay = 8;
7911 // set mouse click change events to work for left/middle/right mouse button
7912 if (level->game_version < VERSION_IDENT(4,2,3,0))
7914 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7916 int element = EL_CUSTOM_START + i;
7917 struct ElementInfo *ei = &element_info[element];
7919 for (j = 0; j < ei->num_change_pages; j++)
7921 struct ElementChangeInfo *change = &ei->change_page[j];
7923 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7924 change->has_event[CE_PRESSED_BY_MOUSE] ||
7925 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7926 change->has_event[CE_MOUSE_PRESSED_ON_X])
7927 change->trigger_side = CH_SIDE_ANY;
7933 static void LoadLevel_InitElements(struct LevelInfo *level)
7935 LoadLevel_InitStandardElements(level);
7937 if (level->file_has_custom_elements)
7938 LoadLevel_InitCustomElements(level);
7940 // initialize element properties for level editor etc.
7941 InitElementPropertiesEngine(level->game_version);
7942 InitElementPropertiesGfxElement();
7945 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7949 // map elements that have changed in newer versions
7950 for (y = 0; y < level->fieldy; y++)
7951 for (x = 0; x < level->fieldx; x++)
7952 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7953 level->game_version);
7955 // clear unused playfield data (nicer if level gets resized in editor)
7956 for (x = 0; x < MAX_LEV_FIELDX; x++)
7957 for (y = 0; y < MAX_LEV_FIELDY; y++)
7958 if (x >= level->fieldx || y >= level->fieldy)
7959 level->field[x][y] = EL_EMPTY;
7961 // copy elements to runtime playfield array
7962 for (x = 0; x < MAX_LEV_FIELDX; x++)
7963 for (y = 0; y < MAX_LEV_FIELDY; y++)
7964 Tile[x][y] = level->field[x][y];
7966 // initialize level size variables for faster access
7967 lev_fieldx = level->fieldx;
7968 lev_fieldy = level->fieldy;
7970 // determine border element for this level
7971 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7972 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7977 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7979 struct LevelFileInfo *level_file_info = &level->file_info;
7981 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7982 CopyNativeLevel_RND_to_Native(level);
7985 static void LoadLevelTemplate_LoadAndInit(void)
7987 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7989 LoadLevel_InitVersion(&level_template);
7990 LoadLevel_InitElements(&level_template);
7991 LoadLevel_InitSettings(&level_template);
7993 ActivateLevelTemplate();
7996 void LoadLevelTemplate(int nr)
7998 if (!fileExists(getGlobalLevelTemplateFilename()))
8000 Warn("no level template found for this level");
8005 setLevelFileInfo(&level_template.file_info, nr);
8007 LoadLevelTemplate_LoadAndInit();
8010 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8012 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8014 LoadLevelTemplate_LoadAndInit();
8017 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8019 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8021 if (level.use_custom_template)
8023 if (network_level != NULL)
8024 LoadNetworkLevelTemplate(network_level);
8026 LoadLevelTemplate(-1);
8029 LoadLevel_InitVersion(&level);
8030 LoadLevel_InitElements(&level);
8031 LoadLevel_InitPlayfield(&level);
8032 LoadLevel_InitSettings(&level);
8034 LoadLevel_InitNativeEngines(&level);
8037 void LoadLevel(int nr)
8039 SetLevelSetInfo(leveldir_current->identifier, nr);
8041 setLevelFileInfo(&level.file_info, nr);
8043 LoadLevel_LoadAndInit(NULL);
8046 void LoadLevelInfoOnly(int nr)
8048 setLevelFileInfo(&level.file_info, nr);
8050 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8053 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8055 SetLevelSetInfo(network_level->leveldir_identifier,
8056 network_level->file_info.nr);
8058 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8060 LoadLevel_LoadAndInit(network_level);
8063 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8067 chunk_size += putFileVersion(file, level->file_version);
8068 chunk_size += putFileVersion(file, level->game_version);
8073 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8077 chunk_size += putFile16BitBE(file, level->creation_date.year);
8078 chunk_size += putFile8Bit(file, level->creation_date.month);
8079 chunk_size += putFile8Bit(file, level->creation_date.day);
8084 #if ENABLE_HISTORIC_CHUNKS
8085 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8089 putFile8Bit(file, level->fieldx);
8090 putFile8Bit(file, level->fieldy);
8092 putFile16BitBE(file, level->time);
8093 putFile16BitBE(file, level->gems_needed);
8095 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8096 putFile8Bit(file, level->name[i]);
8098 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8099 putFile8Bit(file, level->score[i]);
8101 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8102 for (y = 0; y < 3; y++)
8103 for (x = 0; x < 3; x++)
8104 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8105 level->yamyam_content[i].e[x][y]));
8106 putFile8Bit(file, level->amoeba_speed);
8107 putFile8Bit(file, level->time_magic_wall);
8108 putFile8Bit(file, level->time_wheel);
8109 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8110 level->amoeba_content));
8111 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8112 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8113 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8114 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8116 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8118 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8119 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8120 putFile32BitBE(file, level->can_move_into_acid_bits);
8121 putFile8Bit(file, level->dont_collide_with_bits);
8123 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8124 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8126 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8127 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8128 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8130 putFile8Bit(file, level->game_engine_type);
8132 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8136 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8141 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8142 chunk_size += putFile8Bit(file, level->name[i]);
8147 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8152 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8153 chunk_size += putFile8Bit(file, level->author[i]);
8158 #if ENABLE_HISTORIC_CHUNKS
8159 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8164 for (y = 0; y < level->fieldy; y++)
8165 for (x = 0; x < level->fieldx; x++)
8166 if (level->encoding_16bit_field)
8167 chunk_size += putFile16BitBE(file, level->field[x][y]);
8169 chunk_size += putFile8Bit(file, level->field[x][y]);
8175 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8180 for (y = 0; y < level->fieldy; y++)
8181 for (x = 0; x < level->fieldx; x++)
8182 chunk_size += putFile16BitBE(file, level->field[x][y]);
8187 #if ENABLE_HISTORIC_CHUNKS
8188 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8192 putFile8Bit(file, EL_YAMYAM);
8193 putFile8Bit(file, level->num_yamyam_contents);
8194 putFile8Bit(file, 0);
8195 putFile8Bit(file, 0);
8197 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8198 for (y = 0; y < 3; y++)
8199 for (x = 0; x < 3; x++)
8200 if (level->encoding_16bit_field)
8201 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8203 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8207 #if ENABLE_HISTORIC_CHUNKS
8208 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8211 int num_contents, content_xsize, content_ysize;
8212 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8214 if (element == EL_YAMYAM)
8216 num_contents = level->num_yamyam_contents;
8220 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8221 for (y = 0; y < 3; y++)
8222 for (x = 0; x < 3; x++)
8223 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8225 else if (element == EL_BD_AMOEBA)
8231 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8232 for (y = 0; y < 3; y++)
8233 for (x = 0; x < 3; x++)
8234 content_array[i][x][y] = EL_EMPTY;
8235 content_array[0][0][0] = level->amoeba_content;
8239 // chunk header already written -- write empty chunk data
8240 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8242 Warn("cannot save content for element '%d'", element);
8247 putFile16BitBE(file, element);
8248 putFile8Bit(file, num_contents);
8249 putFile8Bit(file, content_xsize);
8250 putFile8Bit(file, content_ysize);
8252 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8254 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8255 for (y = 0; y < 3; y++)
8256 for (x = 0; x < 3; x++)
8257 putFile16BitBE(file, content_array[i][x][y]);
8261 #if ENABLE_HISTORIC_CHUNKS
8262 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8264 int envelope_nr = element - EL_ENVELOPE_1;
8265 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8269 chunk_size += putFile16BitBE(file, element);
8270 chunk_size += putFile16BitBE(file, envelope_len);
8271 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8272 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8274 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8275 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8277 for (i = 0; i < envelope_len; i++)
8278 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8284 #if ENABLE_HISTORIC_CHUNKS
8285 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8286 int num_changed_custom_elements)
8290 putFile16BitBE(file, num_changed_custom_elements);
8292 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8294 int element = EL_CUSTOM_START + i;
8296 struct ElementInfo *ei = &element_info[element];
8298 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8300 if (check < num_changed_custom_elements)
8302 putFile16BitBE(file, element);
8303 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8310 if (check != num_changed_custom_elements) // should not happen
8311 Warn("inconsistent number of custom element properties");
8315 #if ENABLE_HISTORIC_CHUNKS
8316 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8317 int num_changed_custom_elements)
8321 putFile16BitBE(file, num_changed_custom_elements);
8323 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8325 int element = EL_CUSTOM_START + i;
8327 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8329 if (check < num_changed_custom_elements)
8331 putFile16BitBE(file, element);
8332 putFile16BitBE(file, element_info[element].change->target_element);
8339 if (check != num_changed_custom_elements) // should not happen
8340 Warn("inconsistent number of custom target elements");
8344 #if ENABLE_HISTORIC_CHUNKS
8345 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8346 int num_changed_custom_elements)
8348 int i, j, x, y, check = 0;
8350 putFile16BitBE(file, num_changed_custom_elements);
8352 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8354 int element = EL_CUSTOM_START + i;
8355 struct ElementInfo *ei = &element_info[element];
8357 if (ei->modified_settings)
8359 if (check < num_changed_custom_elements)
8361 putFile16BitBE(file, element);
8363 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8364 putFile8Bit(file, ei->description[j]);
8366 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8368 // some free bytes for future properties and padding
8369 WriteUnusedBytesToFile(file, 7);
8371 putFile8Bit(file, ei->use_gfx_element);
8372 putFile16BitBE(file, ei->gfx_element_initial);
8374 putFile8Bit(file, ei->collect_score_initial);
8375 putFile8Bit(file, ei->collect_count_initial);
8377 putFile16BitBE(file, ei->push_delay_fixed);
8378 putFile16BitBE(file, ei->push_delay_random);
8379 putFile16BitBE(file, ei->move_delay_fixed);
8380 putFile16BitBE(file, ei->move_delay_random);
8382 putFile16BitBE(file, ei->move_pattern);
8383 putFile8Bit(file, ei->move_direction_initial);
8384 putFile8Bit(file, ei->move_stepsize);
8386 for (y = 0; y < 3; y++)
8387 for (x = 0; x < 3; x++)
8388 putFile16BitBE(file, ei->content.e[x][y]);
8390 putFile32BitBE(file, ei->change->events);
8392 putFile16BitBE(file, ei->change->target_element);
8394 putFile16BitBE(file, ei->change->delay_fixed);
8395 putFile16BitBE(file, ei->change->delay_random);
8396 putFile16BitBE(file, ei->change->delay_frames);
8398 putFile16BitBE(file, ei->change->initial_trigger_element);
8400 putFile8Bit(file, ei->change->explode);
8401 putFile8Bit(file, ei->change->use_target_content);
8402 putFile8Bit(file, ei->change->only_if_complete);
8403 putFile8Bit(file, ei->change->use_random_replace);
8405 putFile8Bit(file, ei->change->random_percentage);
8406 putFile8Bit(file, ei->change->replace_when);
8408 for (y = 0; y < 3; y++)
8409 for (x = 0; x < 3; x++)
8410 putFile16BitBE(file, ei->change->content.e[x][y]);
8412 putFile8Bit(file, ei->slippery_type);
8414 // some free bytes for future properties and padding
8415 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8422 if (check != num_changed_custom_elements) // should not happen
8423 Warn("inconsistent number of custom element properties");
8427 #if ENABLE_HISTORIC_CHUNKS
8428 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8430 struct ElementInfo *ei = &element_info[element];
8433 // ---------- custom element base property values (96 bytes) ----------------
8435 putFile16BitBE(file, element);
8437 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8438 putFile8Bit(file, ei->description[i]);
8440 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8442 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8444 putFile8Bit(file, ei->num_change_pages);
8446 putFile16BitBE(file, ei->ce_value_fixed_initial);
8447 putFile16BitBE(file, ei->ce_value_random_initial);
8448 putFile8Bit(file, ei->use_last_ce_value);
8450 putFile8Bit(file, ei->use_gfx_element);
8451 putFile16BitBE(file, ei->gfx_element_initial);
8453 putFile8Bit(file, ei->collect_score_initial);
8454 putFile8Bit(file, ei->collect_count_initial);
8456 putFile8Bit(file, ei->drop_delay_fixed);
8457 putFile8Bit(file, ei->push_delay_fixed);
8458 putFile8Bit(file, ei->drop_delay_random);
8459 putFile8Bit(file, ei->push_delay_random);
8460 putFile16BitBE(file, ei->move_delay_fixed);
8461 putFile16BitBE(file, ei->move_delay_random);
8463 // bits 0 - 15 of "move_pattern" ...
8464 putFile16BitBE(file, ei->move_pattern & 0xffff);
8465 putFile8Bit(file, ei->move_direction_initial);
8466 putFile8Bit(file, ei->move_stepsize);
8468 putFile8Bit(file, ei->slippery_type);
8470 for (y = 0; y < 3; y++)
8471 for (x = 0; x < 3; x++)
8472 putFile16BitBE(file, ei->content.e[x][y]);
8474 putFile16BitBE(file, ei->move_enter_element);
8475 putFile16BitBE(file, ei->move_leave_element);
8476 putFile8Bit(file, ei->move_leave_type);
8478 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8479 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8481 putFile8Bit(file, ei->access_direction);
8483 putFile8Bit(file, ei->explosion_delay);
8484 putFile8Bit(file, ei->ignition_delay);
8485 putFile8Bit(file, ei->explosion_type);
8487 // some free bytes for future custom property values and padding
8488 WriteUnusedBytesToFile(file, 1);
8490 // ---------- change page property values (48 bytes) ------------------------
8492 for (i = 0; i < ei->num_change_pages; i++)
8494 struct ElementChangeInfo *change = &ei->change_page[i];
8495 unsigned int event_bits;
8497 // bits 0 - 31 of "has_event[]" ...
8499 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8500 if (change->has_event[j])
8501 event_bits |= (1u << j);
8502 putFile32BitBE(file, event_bits);
8504 putFile16BitBE(file, change->target_element);
8506 putFile16BitBE(file, change->delay_fixed);
8507 putFile16BitBE(file, change->delay_random);
8508 putFile16BitBE(file, change->delay_frames);
8510 putFile16BitBE(file, change->initial_trigger_element);
8512 putFile8Bit(file, change->explode);
8513 putFile8Bit(file, change->use_target_content);
8514 putFile8Bit(file, change->only_if_complete);
8515 putFile8Bit(file, change->use_random_replace);
8517 putFile8Bit(file, change->random_percentage);
8518 putFile8Bit(file, change->replace_when);
8520 for (y = 0; y < 3; y++)
8521 for (x = 0; x < 3; x++)
8522 putFile16BitBE(file, change->target_content.e[x][y]);
8524 putFile8Bit(file, change->can_change);
8526 putFile8Bit(file, change->trigger_side);
8528 putFile8Bit(file, change->trigger_player);
8529 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8530 log_2(change->trigger_page)));
8532 putFile8Bit(file, change->has_action);
8533 putFile8Bit(file, change->action_type);
8534 putFile8Bit(file, change->action_mode);
8535 putFile16BitBE(file, change->action_arg);
8537 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8539 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8540 if (change->has_event[j])
8541 event_bits |= (1u << (j - 32));
8542 putFile8Bit(file, event_bits);
8547 #if ENABLE_HISTORIC_CHUNKS
8548 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8550 struct ElementInfo *ei = &element_info[element];
8551 struct ElementGroupInfo *group = ei->group;
8554 putFile16BitBE(file, element);
8556 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8557 putFile8Bit(file, ei->description[i]);
8559 putFile8Bit(file, group->num_elements);
8561 putFile8Bit(file, ei->use_gfx_element);
8562 putFile16BitBE(file, ei->gfx_element_initial);
8564 putFile8Bit(file, group->choice_mode);
8566 // some free bytes for future values and padding
8567 WriteUnusedBytesToFile(file, 3);
8569 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8570 putFile16BitBE(file, group->element[i]);
8574 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8575 boolean write_element)
8577 int save_type = entry->save_type;
8578 int data_type = entry->data_type;
8579 int conf_type = entry->conf_type;
8580 int byte_mask = conf_type & CONF_MASK_BYTES;
8581 int element = entry->element;
8582 int default_value = entry->default_value;
8584 boolean modified = FALSE;
8586 if (byte_mask != CONF_MASK_MULTI_BYTES)
8588 void *value_ptr = entry->value;
8589 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8592 // check if any settings have been modified before saving them
8593 if (value != default_value)
8596 // do not save if explicitly told or if unmodified default settings
8597 if ((save_type == SAVE_CONF_NEVER) ||
8598 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8602 num_bytes += putFile16BitBE(file, element);
8604 num_bytes += putFile8Bit(file, conf_type);
8605 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8606 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8607 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8610 else if (data_type == TYPE_STRING)
8612 char *default_string = entry->default_string;
8613 char *string = (char *)(entry->value);
8614 int string_length = strlen(string);
8617 // check if any settings have been modified before saving them
8618 if (!strEqual(string, default_string))
8621 // do not save if explicitly told or if unmodified default settings
8622 if ((save_type == SAVE_CONF_NEVER) ||
8623 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8627 num_bytes += putFile16BitBE(file, element);
8629 num_bytes += putFile8Bit(file, conf_type);
8630 num_bytes += putFile16BitBE(file, string_length);
8632 for (i = 0; i < string_length; i++)
8633 num_bytes += putFile8Bit(file, string[i]);
8635 else if (data_type == TYPE_ELEMENT_LIST)
8637 int *element_array = (int *)(entry->value);
8638 int num_elements = *(int *)(entry->num_entities);
8641 // check if any settings have been modified before saving them
8642 for (i = 0; i < num_elements; i++)
8643 if (element_array[i] != default_value)
8646 // do not save if explicitly told or if unmodified default settings
8647 if ((save_type == SAVE_CONF_NEVER) ||
8648 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8652 num_bytes += putFile16BitBE(file, element);
8654 num_bytes += putFile8Bit(file, conf_type);
8655 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8657 for (i = 0; i < num_elements; i++)
8658 num_bytes += putFile16BitBE(file, element_array[i]);
8660 else if (data_type == TYPE_CONTENT_LIST)
8662 struct Content *content = (struct Content *)(entry->value);
8663 int num_contents = *(int *)(entry->num_entities);
8666 // check if any settings have been modified before saving them
8667 for (i = 0; i < num_contents; i++)
8668 for (y = 0; y < 3; y++)
8669 for (x = 0; x < 3; x++)
8670 if (content[i].e[x][y] != default_value)
8673 // do not save if explicitly told or if unmodified default settings
8674 if ((save_type == SAVE_CONF_NEVER) ||
8675 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8679 num_bytes += putFile16BitBE(file, element);
8681 num_bytes += putFile8Bit(file, conf_type);
8682 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8684 for (i = 0; i < num_contents; i++)
8685 for (y = 0; y < 3; y++)
8686 for (x = 0; x < 3; x++)
8687 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8693 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8698 li = *level; // copy level data into temporary buffer
8700 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8701 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8706 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8711 li = *level; // copy level data into temporary buffer
8713 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8714 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8719 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8721 int envelope_nr = element - EL_ENVELOPE_1;
8725 chunk_size += putFile16BitBE(file, element);
8727 // copy envelope data into temporary buffer
8728 xx_envelope = level->envelope[envelope_nr];
8730 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8731 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8736 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8738 struct ElementInfo *ei = &element_info[element];
8742 chunk_size += putFile16BitBE(file, element);
8744 xx_ei = *ei; // copy element data into temporary buffer
8746 // set default description string for this specific element
8747 strcpy(xx_default_description, getDefaultElementDescription(ei));
8749 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8750 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8752 for (i = 0; i < ei->num_change_pages; i++)
8754 struct ElementChangeInfo *change = &ei->change_page[i];
8756 xx_current_change_page = i;
8758 xx_change = *change; // copy change data into temporary buffer
8761 setEventBitsFromEventFlags(change);
8763 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8764 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8771 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8773 struct ElementInfo *ei = &element_info[element];
8774 struct ElementGroupInfo *group = ei->group;
8778 chunk_size += putFile16BitBE(file, element);
8780 xx_ei = *ei; // copy element data into temporary buffer
8781 xx_group = *group; // copy group data into temporary buffer
8783 // set default description string for this specific element
8784 strcpy(xx_default_description, getDefaultElementDescription(ei));
8786 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8787 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8792 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8794 struct ElementInfo *ei = &element_info[element];
8798 chunk_size += putFile16BitBE(file, element);
8800 xx_ei = *ei; // copy element data into temporary buffer
8802 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8803 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8808 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8809 boolean save_as_template)
8815 if (!(file = fopen(filename, MODE_WRITE)))
8817 Warn("cannot save level file '%s'", filename);
8822 level->file_version = FILE_VERSION_ACTUAL;
8823 level->game_version = GAME_VERSION_ACTUAL;
8825 level->creation_date = getCurrentDate();
8827 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8828 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8830 chunk_size = SaveLevel_VERS(NULL, level);
8831 putFileChunkBE(file, "VERS", chunk_size);
8832 SaveLevel_VERS(file, level);
8834 chunk_size = SaveLevel_DATE(NULL, level);
8835 putFileChunkBE(file, "DATE", chunk_size);
8836 SaveLevel_DATE(file, level);
8838 chunk_size = SaveLevel_NAME(NULL, level);
8839 putFileChunkBE(file, "NAME", chunk_size);
8840 SaveLevel_NAME(file, level);
8842 chunk_size = SaveLevel_AUTH(NULL, level);
8843 putFileChunkBE(file, "AUTH", chunk_size);
8844 SaveLevel_AUTH(file, level);
8846 chunk_size = SaveLevel_INFO(NULL, level);
8847 putFileChunkBE(file, "INFO", chunk_size);
8848 SaveLevel_INFO(file, level);
8850 chunk_size = SaveLevel_BODY(NULL, level);
8851 putFileChunkBE(file, "BODY", chunk_size);
8852 SaveLevel_BODY(file, level);
8854 chunk_size = SaveLevel_ELEM(NULL, level);
8855 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8857 putFileChunkBE(file, "ELEM", chunk_size);
8858 SaveLevel_ELEM(file, level);
8861 for (i = 0; i < NUM_ENVELOPES; i++)
8863 int element = EL_ENVELOPE_1 + i;
8865 chunk_size = SaveLevel_NOTE(NULL, level, element);
8866 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8868 putFileChunkBE(file, "NOTE", chunk_size);
8869 SaveLevel_NOTE(file, level, element);
8873 // if not using template level, check for non-default custom/group elements
8874 if (!level->use_custom_template || save_as_template)
8876 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8878 int element = EL_CUSTOM_START + i;
8880 chunk_size = SaveLevel_CUSX(NULL, level, element);
8881 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8883 putFileChunkBE(file, "CUSX", chunk_size);
8884 SaveLevel_CUSX(file, level, element);
8888 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8890 int element = EL_GROUP_START + i;
8892 chunk_size = SaveLevel_GRPX(NULL, level, element);
8893 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8895 putFileChunkBE(file, "GRPX", chunk_size);
8896 SaveLevel_GRPX(file, level, element);
8900 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8902 int element = GET_EMPTY_ELEMENT(i);
8904 chunk_size = SaveLevel_EMPX(NULL, level, element);
8905 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8907 putFileChunkBE(file, "EMPX", chunk_size);
8908 SaveLevel_EMPX(file, level, element);
8915 SetFilePermissions(filename, PERMS_PRIVATE);
8918 void SaveLevel(int nr)
8920 char *filename = getDefaultLevelFilename(nr);
8922 SaveLevelFromFilename(&level, filename, FALSE);
8925 void SaveLevelTemplate(void)
8927 char *filename = getLocalLevelTemplateFilename();
8929 SaveLevelFromFilename(&level, filename, TRUE);
8932 boolean SaveLevelChecked(int nr)
8934 char *filename = getDefaultLevelFilename(nr);
8935 boolean new_level = !fileExists(filename);
8936 boolean level_saved = FALSE;
8938 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8943 Request("Level saved!", REQ_CONFIRM);
8951 void DumpLevel(struct LevelInfo *level)
8953 if (level->no_level_file || level->no_valid_file)
8955 Warn("cannot dump -- no valid level file found");
8961 Print("Level xxx (file version %08d, game version %08d)\n",
8962 level->file_version, level->game_version);
8965 Print("Level author: '%s'\n", level->author);
8966 Print("Level title: '%s'\n", level->name);
8968 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8970 Print("Level time: %d seconds\n", level->time);
8971 Print("Gems needed: %d\n", level->gems_needed);
8973 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8974 Print("Time for wheel: %d seconds\n", level->time_wheel);
8975 Print("Time for light: %d seconds\n", level->time_light);
8976 Print("Time for timegate: %d seconds\n", level->time_timegate);
8978 Print("Amoeba speed: %d\n", level->amoeba_speed);
8981 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8982 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8983 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8984 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8985 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8986 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8992 for (i = 0; i < NUM_ENVELOPES; i++)
8994 char *text = level->envelope[i].text;
8995 int text_len = strlen(text);
8996 boolean has_text = FALSE;
8998 for (j = 0; j < text_len; j++)
8999 if (text[j] != ' ' && text[j] != '\n')
9005 Print("Envelope %d:\n'%s'\n", i + 1, text);
9013 void DumpLevels(void)
9015 static LevelDirTree *dumplevel_leveldir = NULL;
9017 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9018 global.dumplevel_leveldir);
9020 if (dumplevel_leveldir == NULL)
9021 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9023 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9024 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9025 Fail("no such level number: %d", global.dumplevel_level_nr);
9027 leveldir_current = dumplevel_leveldir;
9029 LoadLevel(global.dumplevel_level_nr);
9036 // ============================================================================
9037 // tape file functions
9038 // ============================================================================
9040 static void setTapeInfoToDefaults(void)
9044 // always start with reliable default values (empty tape)
9047 // default values (also for pre-1.2 tapes) with only the first player
9048 tape.player_participates[0] = TRUE;
9049 for (i = 1; i < MAX_PLAYERS; i++)
9050 tape.player_participates[i] = FALSE;
9052 // at least one (default: the first) player participates in every tape
9053 tape.num_participating_players = 1;
9055 tape.property_bits = TAPE_PROPERTY_NONE;
9057 tape.level_nr = level_nr;
9059 tape.changed = FALSE;
9060 tape.solved = FALSE;
9062 tape.recording = FALSE;
9063 tape.playing = FALSE;
9064 tape.pausing = FALSE;
9066 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9067 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9069 tape.no_info_chunk = TRUE;
9070 tape.no_valid_file = FALSE;
9073 static int getTapePosSize(struct TapeInfo *tape)
9075 int tape_pos_size = 0;
9077 if (tape->use_key_actions)
9078 tape_pos_size += tape->num_participating_players;
9080 if (tape->use_mouse_actions)
9081 tape_pos_size += 3; // x and y position and mouse button mask
9083 tape_pos_size += 1; // tape action delay value
9085 return tape_pos_size;
9088 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9090 tape->use_key_actions = FALSE;
9091 tape->use_mouse_actions = FALSE;
9093 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9094 tape->use_key_actions = TRUE;
9096 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9097 tape->use_mouse_actions = TRUE;
9100 static int getTapeActionValue(struct TapeInfo *tape)
9102 return (tape->use_key_actions &&
9103 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9104 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9105 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9106 TAPE_ACTIONS_DEFAULT);
9109 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9111 tape->file_version = getFileVersion(file);
9112 tape->game_version = getFileVersion(file);
9117 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9121 tape->random_seed = getFile32BitBE(file);
9122 tape->date = getFile32BitBE(file);
9123 tape->length = getFile32BitBE(file);
9125 // read header fields that are new since version 1.2
9126 if (tape->file_version >= FILE_VERSION_1_2)
9128 byte store_participating_players = getFile8Bit(file);
9131 // since version 1.2, tapes store which players participate in the tape
9132 tape->num_participating_players = 0;
9133 for (i = 0; i < MAX_PLAYERS; i++)
9135 tape->player_participates[i] = FALSE;
9137 if (store_participating_players & (1 << i))
9139 tape->player_participates[i] = TRUE;
9140 tape->num_participating_players++;
9144 setTapeActionFlags(tape, getFile8Bit(file));
9146 tape->property_bits = getFile8Bit(file);
9147 tape->solved = getFile8Bit(file);
9149 engine_version = getFileVersion(file);
9150 if (engine_version > 0)
9151 tape->engine_version = engine_version;
9153 tape->engine_version = tape->game_version;
9159 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9161 tape->scr_fieldx = getFile8Bit(file);
9162 tape->scr_fieldy = getFile8Bit(file);
9167 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9169 char *level_identifier = NULL;
9170 int level_identifier_size;
9173 tape->no_info_chunk = FALSE;
9175 level_identifier_size = getFile16BitBE(file);
9177 level_identifier = checked_malloc(level_identifier_size);
9179 for (i = 0; i < level_identifier_size; i++)
9180 level_identifier[i] = getFile8Bit(file);
9182 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9183 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9185 checked_free(level_identifier);
9187 tape->level_nr = getFile16BitBE(file);
9189 chunk_size = 2 + level_identifier_size + 2;
9194 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9197 int tape_pos_size = getTapePosSize(tape);
9198 int chunk_size_expected = tape_pos_size * tape->length;
9200 if (chunk_size_expected != chunk_size)
9202 ReadUnusedBytesFromFile(file, chunk_size);
9203 return chunk_size_expected;
9206 for (i = 0; i < tape->length; i++)
9208 if (i >= MAX_TAPE_LEN)
9210 Warn("tape truncated -- size exceeds maximum tape size %d",
9213 // tape too large; read and ignore remaining tape data from this chunk
9214 for (;i < tape->length; i++)
9215 ReadUnusedBytesFromFile(file, tape_pos_size);
9220 if (tape->use_key_actions)
9222 for (j = 0; j < MAX_PLAYERS; j++)
9224 tape->pos[i].action[j] = MV_NONE;
9226 if (tape->player_participates[j])
9227 tape->pos[i].action[j] = getFile8Bit(file);
9231 if (tape->use_mouse_actions)
9233 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9234 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9235 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9238 tape->pos[i].delay = getFile8Bit(file);
9240 if (tape->file_version == FILE_VERSION_1_0)
9242 // eliminate possible diagonal moves in old tapes
9243 // this is only for backward compatibility
9245 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9246 byte action = tape->pos[i].action[0];
9247 int k, num_moves = 0;
9249 for (k = 0; k < 4; k++)
9251 if (action & joy_dir[k])
9253 tape->pos[i + num_moves].action[0] = joy_dir[k];
9255 tape->pos[i + num_moves].delay = 0;
9264 tape->length += num_moves;
9267 else if (tape->file_version < FILE_VERSION_2_0)
9269 // convert pre-2.0 tapes to new tape format
9271 if (tape->pos[i].delay > 1)
9274 tape->pos[i + 1] = tape->pos[i];
9275 tape->pos[i + 1].delay = 1;
9278 for (j = 0; j < MAX_PLAYERS; j++)
9279 tape->pos[i].action[j] = MV_NONE;
9280 tape->pos[i].delay--;
9287 if (checkEndOfFile(file))
9291 if (i != tape->length)
9292 chunk_size = tape_pos_size * i;
9297 static void LoadTape_SokobanSolution(char *filename)
9300 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9302 if (!(file = openFile(filename, MODE_READ)))
9304 tape.no_valid_file = TRUE;
9309 while (!checkEndOfFile(file))
9311 unsigned char c = getByteFromFile(file);
9313 if (checkEndOfFile(file))
9320 tape.pos[tape.length].action[0] = MV_UP;
9321 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9327 tape.pos[tape.length].action[0] = MV_DOWN;
9328 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9334 tape.pos[tape.length].action[0] = MV_LEFT;
9335 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9341 tape.pos[tape.length].action[0] = MV_RIGHT;
9342 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9350 // ignore white-space characters
9354 tape.no_valid_file = TRUE;
9356 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9364 if (tape.no_valid_file)
9367 tape.length_frames = GetTapeLengthFrames();
9368 tape.length_seconds = GetTapeLengthSeconds();
9371 void LoadTapeFromFilename(char *filename)
9373 char cookie[MAX_LINE_LEN];
9374 char chunk_name[CHUNK_ID_LEN + 1];
9378 // always start with reliable default values
9379 setTapeInfoToDefaults();
9381 if (strSuffix(filename, ".sln"))
9383 LoadTape_SokobanSolution(filename);
9388 if (!(file = openFile(filename, MODE_READ)))
9390 tape.no_valid_file = TRUE;
9395 getFileChunkBE(file, chunk_name, NULL);
9396 if (strEqual(chunk_name, "RND1"))
9398 getFile32BitBE(file); // not used
9400 getFileChunkBE(file, chunk_name, NULL);
9401 if (!strEqual(chunk_name, "TAPE"))
9403 tape.no_valid_file = TRUE;
9405 Warn("unknown format of tape file '%s'", filename);
9412 else // check for pre-2.0 file format with cookie string
9414 strcpy(cookie, chunk_name);
9415 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9417 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9418 cookie[strlen(cookie) - 1] = '\0';
9420 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9422 tape.no_valid_file = TRUE;
9424 Warn("unknown format of tape file '%s'", filename);
9431 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9433 tape.no_valid_file = TRUE;
9435 Warn("unsupported version of tape file '%s'", filename);
9442 // pre-2.0 tape files have no game version, so use file version here
9443 tape.game_version = tape.file_version;
9446 if (tape.file_version < FILE_VERSION_1_2)
9448 // tape files from versions before 1.2.0 without chunk structure
9449 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9450 LoadTape_BODY(file, 2 * tape.length, &tape);
9458 int (*loader)(File *, int, struct TapeInfo *);
9462 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9463 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9464 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9465 { "INFO", -1, LoadTape_INFO },
9466 { "BODY", -1, LoadTape_BODY },
9470 while (getFileChunkBE(file, chunk_name, &chunk_size))
9474 while (chunk_info[i].name != NULL &&
9475 !strEqual(chunk_name, chunk_info[i].name))
9478 if (chunk_info[i].name == NULL)
9480 Warn("unknown chunk '%s' in tape file '%s'",
9481 chunk_name, filename);
9483 ReadUnusedBytesFromFile(file, chunk_size);
9485 else if (chunk_info[i].size != -1 &&
9486 chunk_info[i].size != chunk_size)
9488 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9489 chunk_size, chunk_name, filename);
9491 ReadUnusedBytesFromFile(file, chunk_size);
9495 // call function to load this tape chunk
9496 int chunk_size_expected =
9497 (chunk_info[i].loader)(file, chunk_size, &tape);
9499 // the size of some chunks cannot be checked before reading other
9500 // chunks first (like "HEAD" and "BODY") that contain some header
9501 // information, so check them here
9502 if (chunk_size_expected != chunk_size)
9504 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9505 chunk_size, chunk_name, filename);
9513 tape.length_frames = GetTapeLengthFrames();
9514 tape.length_seconds = GetTapeLengthSeconds();
9517 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9519 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9521 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9522 tape.engine_version);
9526 void LoadTape(int nr)
9528 char *filename = getTapeFilename(nr);
9530 LoadTapeFromFilename(filename);
9533 void LoadSolutionTape(int nr)
9535 char *filename = getSolutionTapeFilename(nr);
9537 LoadTapeFromFilename(filename);
9539 if (TAPE_IS_EMPTY(tape))
9541 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9542 level.native_bd_level->replay != NULL)
9543 CopyNativeTape_BD_to_RND(&level);
9544 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9545 level.native_sp_level->demo.is_available)
9546 CopyNativeTape_SP_to_RND(&level);
9550 void LoadScoreTape(char *score_tape_basename, int nr)
9552 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9554 LoadTapeFromFilename(filename);
9557 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9559 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9561 LoadTapeFromFilename(filename);
9564 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9566 // chunk required for team mode tapes with non-default screen size
9567 return (tape->num_participating_players > 1 &&
9568 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9569 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9572 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9574 putFileVersion(file, tape->file_version);
9575 putFileVersion(file, tape->game_version);
9578 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9581 byte store_participating_players = 0;
9583 // set bits for participating players for compact storage
9584 for (i = 0; i < MAX_PLAYERS; i++)
9585 if (tape->player_participates[i])
9586 store_participating_players |= (1 << i);
9588 putFile32BitBE(file, tape->random_seed);
9589 putFile32BitBE(file, tape->date);
9590 putFile32BitBE(file, tape->length);
9592 putFile8Bit(file, store_participating_players);
9594 putFile8Bit(file, getTapeActionValue(tape));
9596 putFile8Bit(file, tape->property_bits);
9597 putFile8Bit(file, tape->solved);
9599 putFileVersion(file, tape->engine_version);
9602 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9604 putFile8Bit(file, tape->scr_fieldx);
9605 putFile8Bit(file, tape->scr_fieldy);
9608 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9610 int level_identifier_size = strlen(tape->level_identifier) + 1;
9613 putFile16BitBE(file, level_identifier_size);
9615 for (i = 0; i < level_identifier_size; i++)
9616 putFile8Bit(file, tape->level_identifier[i]);
9618 putFile16BitBE(file, tape->level_nr);
9621 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9625 for (i = 0; i < tape->length; i++)
9627 if (tape->use_key_actions)
9629 for (j = 0; j < MAX_PLAYERS; j++)
9630 if (tape->player_participates[j])
9631 putFile8Bit(file, tape->pos[i].action[j]);
9634 if (tape->use_mouse_actions)
9636 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9637 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9638 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9641 putFile8Bit(file, tape->pos[i].delay);
9645 void SaveTapeToFilename(char *filename)
9649 int info_chunk_size;
9650 int body_chunk_size;
9652 if (!(file = fopen(filename, MODE_WRITE)))
9654 Warn("cannot save level recording file '%s'", filename);
9659 tape_pos_size = getTapePosSize(&tape);
9661 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9662 body_chunk_size = tape_pos_size * tape.length;
9664 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9665 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9667 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9668 SaveTape_VERS(file, &tape);
9670 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9671 SaveTape_HEAD(file, &tape);
9673 if (checkSaveTape_SCRN(&tape))
9675 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9676 SaveTape_SCRN(file, &tape);
9679 putFileChunkBE(file, "INFO", info_chunk_size);
9680 SaveTape_INFO(file, &tape);
9682 putFileChunkBE(file, "BODY", body_chunk_size);
9683 SaveTape_BODY(file, &tape);
9687 SetFilePermissions(filename, PERMS_PRIVATE);
9690 static void SaveTapeExt(char *filename)
9694 tape.file_version = FILE_VERSION_ACTUAL;
9695 tape.game_version = GAME_VERSION_ACTUAL;
9697 tape.num_participating_players = 0;
9699 // count number of participating players
9700 for (i = 0; i < MAX_PLAYERS; i++)
9701 if (tape.player_participates[i])
9702 tape.num_participating_players++;
9704 SaveTapeToFilename(filename);
9706 tape.changed = FALSE;
9709 void SaveTape(int nr)
9711 char *filename = getTapeFilename(nr);
9713 InitTapeDirectory(leveldir_current->subdir);
9715 SaveTapeExt(filename);
9718 void SaveScoreTape(int nr)
9720 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9722 // used instead of "leveldir_current->subdir" (for network games)
9723 InitScoreTapeDirectory(levelset.identifier, nr);
9725 SaveTapeExt(filename);
9728 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9729 unsigned int req_state_added)
9731 char *filename = getTapeFilename(nr);
9732 boolean new_tape = !fileExists(filename);
9733 boolean tape_saved = FALSE;
9735 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9740 Request(msg_saved, REQ_CONFIRM | req_state_added);
9748 boolean SaveTapeChecked(int nr)
9750 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9753 boolean SaveTapeChecked_LevelSolved(int nr)
9755 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9756 "Level solved! Tape saved!", REQ_STAY_OPEN);
9759 void DumpTape(struct TapeInfo *tape)
9761 int tape_frame_counter;
9764 if (tape->no_valid_file)
9766 Warn("cannot dump -- no valid tape file found");
9773 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9774 tape->level_nr, tape->file_version, tape->game_version);
9775 Print(" (effective engine version %08d)\n",
9776 tape->engine_version);
9777 Print("Level series identifier: '%s'\n", tape->level_identifier);
9779 Print("Solution tape: %s\n",
9780 tape->solved ? "yes" :
9781 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9783 Print("Special tape properties: ");
9784 if (tape->property_bits == TAPE_PROPERTY_NONE)
9786 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9787 Print("[em_random_bug]");
9788 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9789 Print("[game_speed]");
9790 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9792 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9793 Print("[single_step]");
9794 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9795 Print("[snapshot]");
9796 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9797 Print("[replayed]");
9798 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9799 Print("[tas_keys]");
9800 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9801 Print("[small_graphics]");
9804 int year2 = tape->date / 10000;
9805 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9806 int month_index_raw = (tape->date / 100) % 100;
9807 int month_index = month_index_raw % 12; // prevent invalid index
9808 int month = month_index + 1;
9809 int day = tape->date % 100;
9811 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9815 tape_frame_counter = 0;
9817 for (i = 0; i < tape->length; i++)
9819 if (i >= MAX_TAPE_LEN)
9824 for (j = 0; j < MAX_PLAYERS; j++)
9826 if (tape->player_participates[j])
9828 int action = tape->pos[i].action[j];
9830 Print("%d:%02x ", j, action);
9831 Print("[%c%c%c%c|%c%c] - ",
9832 (action & JOY_LEFT ? '<' : ' '),
9833 (action & JOY_RIGHT ? '>' : ' '),
9834 (action & JOY_UP ? '^' : ' '),
9835 (action & JOY_DOWN ? 'v' : ' '),
9836 (action & JOY_BUTTON_1 ? '1' : ' '),
9837 (action & JOY_BUTTON_2 ? '2' : ' '));
9841 Print("(%03d) ", tape->pos[i].delay);
9842 Print("[%05d]\n", tape_frame_counter);
9844 tape_frame_counter += tape->pos[i].delay;
9850 void DumpTapes(void)
9852 static LevelDirTree *dumptape_leveldir = NULL;
9854 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9855 global.dumptape_leveldir);
9857 if (dumptape_leveldir == NULL)
9858 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9860 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9861 global.dumptape_level_nr > dumptape_leveldir->last_level)
9862 Fail("no such level number: %d", global.dumptape_level_nr);
9864 leveldir_current = dumptape_leveldir;
9866 if (options.mytapes)
9867 LoadTape(global.dumptape_level_nr);
9869 LoadSolutionTape(global.dumptape_level_nr);
9877 // ============================================================================
9878 // score file functions
9879 // ============================================================================
9881 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9885 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9887 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9888 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9889 scores->entry[i].score = 0;
9890 scores->entry[i].time = 0;
9892 scores->entry[i].id = -1;
9893 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9894 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9895 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9896 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9897 strcpy(scores->entry[i].country_code, "??");
9900 scores->num_entries = 0;
9901 scores->last_added = -1;
9902 scores->last_added_local = -1;
9904 scores->updated = FALSE;
9905 scores->uploaded = FALSE;
9906 scores->tape_downloaded = FALSE;
9907 scores->force_last_added = FALSE;
9909 // The following values are intentionally not reset here:
9913 // - continue_playing
9914 // - continue_on_return
9917 static void setScoreInfoToDefaults(void)
9919 setScoreInfoToDefaultsExt(&scores);
9922 static void setServerScoreInfoToDefaults(void)
9924 setScoreInfoToDefaultsExt(&server_scores);
9927 static void LoadScore_OLD(int nr)
9930 char *filename = getScoreFilename(nr);
9931 char cookie[MAX_LINE_LEN];
9932 char line[MAX_LINE_LEN];
9936 if (!(file = fopen(filename, MODE_READ)))
9939 // check file identifier
9940 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9942 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9943 cookie[strlen(cookie) - 1] = '\0';
9945 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9947 Warn("unknown format of score file '%s'", filename);
9954 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9956 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9957 Warn("fscanf() failed; %s", strerror(errno));
9959 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9962 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9963 line[strlen(line) - 1] = '\0';
9965 for (line_ptr = line; *line_ptr; line_ptr++)
9967 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9969 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9970 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9979 static void ConvertScore_OLD(void)
9981 // only convert score to time for levels that rate playing time over score
9982 if (!level.rate_time_over_score)
9985 // convert old score to playing time for score-less levels (like Supaplex)
9986 int time_final_max = 999;
9989 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9991 int score = scores.entry[i].score;
9993 if (score > 0 && score < time_final_max)
9994 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9998 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10000 scores->file_version = getFileVersion(file);
10001 scores->game_version = getFileVersion(file);
10006 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10008 char *level_identifier = NULL;
10009 int level_identifier_size;
10012 level_identifier_size = getFile16BitBE(file);
10014 level_identifier = checked_malloc(level_identifier_size);
10016 for (i = 0; i < level_identifier_size; i++)
10017 level_identifier[i] = getFile8Bit(file);
10019 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10020 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10022 checked_free(level_identifier);
10024 scores->level_nr = getFile16BitBE(file);
10025 scores->num_entries = getFile16BitBE(file);
10027 chunk_size = 2 + level_identifier_size + 2 + 2;
10032 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10036 for (i = 0; i < scores->num_entries; i++)
10038 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10039 scores->entry[i].name[j] = getFile8Bit(file);
10041 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10044 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10049 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10053 for (i = 0; i < scores->num_entries; i++)
10054 scores->entry[i].score = getFile16BitBE(file);
10056 chunk_size = scores->num_entries * 2;
10061 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10065 for (i = 0; i < scores->num_entries; i++)
10066 scores->entry[i].score = getFile32BitBE(file);
10068 chunk_size = scores->num_entries * 4;
10073 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10077 for (i = 0; i < scores->num_entries; i++)
10078 scores->entry[i].time = getFile32BitBE(file);
10080 chunk_size = scores->num_entries * 4;
10085 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10089 for (i = 0; i < scores->num_entries; i++)
10091 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10092 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10094 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10097 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10102 void LoadScore(int nr)
10104 char *filename = getScoreFilename(nr);
10105 char cookie[MAX_LINE_LEN];
10106 char chunk_name[CHUNK_ID_LEN + 1];
10108 boolean old_score_file_format = FALSE;
10111 // always start with reliable default values
10112 setScoreInfoToDefaults();
10114 if (!(file = openFile(filename, MODE_READ)))
10117 getFileChunkBE(file, chunk_name, NULL);
10118 if (strEqual(chunk_name, "RND1"))
10120 getFile32BitBE(file); // not used
10122 getFileChunkBE(file, chunk_name, NULL);
10123 if (!strEqual(chunk_name, "SCOR"))
10125 Warn("unknown format of score file '%s'", filename);
10132 else // check for old file format with cookie string
10134 strcpy(cookie, chunk_name);
10135 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10137 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10138 cookie[strlen(cookie) - 1] = '\0';
10140 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10142 Warn("unknown format of score file '%s'", filename);
10149 old_score_file_format = TRUE;
10152 if (old_score_file_format)
10154 // score files from versions before 4.2.4.0 without chunk structure
10157 // convert score to time, if possible (mainly for Supaplex levels)
10158 ConvertScore_OLD();
10166 int (*loader)(File *, int, struct ScoreInfo *);
10170 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10171 { "INFO", -1, LoadScore_INFO },
10172 { "NAME", -1, LoadScore_NAME },
10173 { "SCOR", -1, LoadScore_SCOR },
10174 { "SC4R", -1, LoadScore_SC4R },
10175 { "TIME", -1, LoadScore_TIME },
10176 { "TAPE", -1, LoadScore_TAPE },
10181 while (getFileChunkBE(file, chunk_name, &chunk_size))
10185 while (chunk_info[i].name != NULL &&
10186 !strEqual(chunk_name, chunk_info[i].name))
10189 if (chunk_info[i].name == NULL)
10191 Warn("unknown chunk '%s' in score file '%s'",
10192 chunk_name, filename);
10194 ReadUnusedBytesFromFile(file, chunk_size);
10196 else if (chunk_info[i].size != -1 &&
10197 chunk_info[i].size != chunk_size)
10199 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10200 chunk_size, chunk_name, filename);
10202 ReadUnusedBytesFromFile(file, chunk_size);
10206 // call function to load this score chunk
10207 int chunk_size_expected =
10208 (chunk_info[i].loader)(file, chunk_size, &scores);
10210 // the size of some chunks cannot be checked before reading other
10211 // chunks first (like "HEAD" and "BODY") that contain some header
10212 // information, so check them here
10213 if (chunk_size_expected != chunk_size)
10215 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10216 chunk_size, chunk_name, filename);
10225 #if ENABLE_HISTORIC_CHUNKS
10226 void SaveScore_OLD(int nr)
10229 char *filename = getScoreFilename(nr);
10232 // used instead of "leveldir_current->subdir" (for network games)
10233 InitScoreDirectory(levelset.identifier);
10235 if (!(file = fopen(filename, MODE_WRITE)))
10237 Warn("cannot save score for level %d", nr);
10242 fprintf(file, "%s\n\n", SCORE_COOKIE);
10244 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10245 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10249 SetFilePermissions(filename, PERMS_PRIVATE);
10253 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10255 putFileVersion(file, scores->file_version);
10256 putFileVersion(file, scores->game_version);
10259 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10261 int level_identifier_size = strlen(scores->level_identifier) + 1;
10264 putFile16BitBE(file, level_identifier_size);
10266 for (i = 0; i < level_identifier_size; i++)
10267 putFile8Bit(file, scores->level_identifier[i]);
10269 putFile16BitBE(file, scores->level_nr);
10270 putFile16BitBE(file, scores->num_entries);
10273 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10277 for (i = 0; i < scores->num_entries; i++)
10279 int name_size = strlen(scores->entry[i].name);
10281 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10282 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10286 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10290 for (i = 0; i < scores->num_entries; i++)
10291 putFile16BitBE(file, scores->entry[i].score);
10294 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10298 for (i = 0; i < scores->num_entries; i++)
10299 putFile32BitBE(file, scores->entry[i].score);
10302 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10306 for (i = 0; i < scores->num_entries; i++)
10307 putFile32BitBE(file, scores->entry[i].time);
10310 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10314 for (i = 0; i < scores->num_entries; i++)
10316 int size = strlen(scores->entry[i].tape_basename);
10318 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10319 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10323 static void SaveScoreToFilename(char *filename)
10326 int info_chunk_size;
10327 int name_chunk_size;
10328 int scor_chunk_size;
10329 int sc4r_chunk_size;
10330 int time_chunk_size;
10331 int tape_chunk_size;
10332 boolean has_large_score_values;
10335 if (!(file = fopen(filename, MODE_WRITE)))
10337 Warn("cannot save score file '%s'", filename);
10342 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10343 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10344 scor_chunk_size = scores.num_entries * 2;
10345 sc4r_chunk_size = scores.num_entries * 4;
10346 time_chunk_size = scores.num_entries * 4;
10347 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10349 has_large_score_values = FALSE;
10350 for (i = 0; i < scores.num_entries; i++)
10351 if (scores.entry[i].score > 0xffff)
10352 has_large_score_values = TRUE;
10354 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10355 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10357 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10358 SaveScore_VERS(file, &scores);
10360 putFileChunkBE(file, "INFO", info_chunk_size);
10361 SaveScore_INFO(file, &scores);
10363 putFileChunkBE(file, "NAME", name_chunk_size);
10364 SaveScore_NAME(file, &scores);
10366 if (has_large_score_values)
10368 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10369 SaveScore_SC4R(file, &scores);
10373 putFileChunkBE(file, "SCOR", scor_chunk_size);
10374 SaveScore_SCOR(file, &scores);
10377 putFileChunkBE(file, "TIME", time_chunk_size);
10378 SaveScore_TIME(file, &scores);
10380 putFileChunkBE(file, "TAPE", tape_chunk_size);
10381 SaveScore_TAPE(file, &scores);
10385 SetFilePermissions(filename, PERMS_PRIVATE);
10388 void SaveScore(int nr)
10390 char *filename = getScoreFilename(nr);
10393 // used instead of "leveldir_current->subdir" (for network games)
10394 InitScoreDirectory(levelset.identifier);
10396 scores.file_version = FILE_VERSION_ACTUAL;
10397 scores.game_version = GAME_VERSION_ACTUAL;
10399 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10400 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10401 scores.level_nr = level_nr;
10403 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10404 if (scores.entry[i].score == 0 &&
10405 scores.entry[i].time == 0 &&
10406 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10409 scores.num_entries = i;
10411 if (scores.num_entries == 0)
10414 SaveScoreToFilename(filename);
10417 static void LoadServerScoreFromCache(int nr)
10419 struct ScoreEntry score_entry;
10428 { &score_entry.score, FALSE, 0 },
10429 { &score_entry.time, FALSE, 0 },
10430 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10431 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10432 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10433 { &score_entry.id, FALSE, 0 },
10434 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10435 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10436 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10437 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10441 char *filename = getScoreCacheFilename(nr);
10442 SetupFileHash *score_hash = loadSetupFileHash(filename);
10445 server_scores.num_entries = 0;
10447 if (score_hash == NULL)
10450 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10452 score_entry = server_scores.entry[i];
10454 for (j = 0; score_mapping[j].value != NULL; j++)
10458 sprintf(token, "%02d.%d", i, j);
10460 char *value = getHashEntry(score_hash, token);
10465 if (score_mapping[j].is_string)
10467 char *score_value = (char *)score_mapping[j].value;
10468 int value_size = score_mapping[j].string_size;
10470 strncpy(score_value, value, value_size);
10471 score_value[value_size] = '\0';
10475 int *score_value = (int *)score_mapping[j].value;
10477 *score_value = atoi(value);
10480 server_scores.num_entries = i + 1;
10483 server_scores.entry[i] = score_entry;
10486 freeSetupFileHash(score_hash);
10489 void LoadServerScore(int nr, boolean download_score)
10491 if (!setup.use_api_server)
10494 // always start with reliable default values
10495 setServerScoreInfoToDefaults();
10497 // 1st step: load server scores from cache file (which may not exist)
10498 // (this should prevent reading it while the thread is writing to it)
10499 LoadServerScoreFromCache(nr);
10501 if (download_score && runtime.use_api_server)
10503 // 2nd step: download server scores from score server to cache file
10504 // (as thread, as it might time out if the server is not reachable)
10505 ApiGetScoreAsThread(nr);
10509 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10511 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10513 // if score tape not uploaded, ask for uploading missing tapes later
10514 if (!setup.has_remaining_tapes)
10515 setup.ask_for_remaining_tapes = TRUE;
10517 setup.provide_uploading_tapes = TRUE;
10518 setup.has_remaining_tapes = TRUE;
10520 SaveSetup_ServerSetup();
10523 void SaveServerScore(int nr, boolean tape_saved)
10525 if (!runtime.use_api_server)
10527 PrepareScoreTapesForUpload(leveldir_current->subdir);
10532 ApiAddScoreAsThread(nr, tape_saved, NULL);
10535 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10536 char *score_tape_filename)
10538 if (!runtime.use_api_server)
10541 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10544 void LoadLocalAndServerScore(int nr, boolean download_score)
10546 int last_added_local = scores.last_added_local;
10547 boolean force_last_added = scores.force_last_added;
10549 // needed if only showing server scores
10550 setScoreInfoToDefaults();
10552 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10555 // restore last added local score entry (before merging server scores)
10556 scores.last_added = scores.last_added_local = last_added_local;
10558 if (setup.use_api_server &&
10559 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10561 // load server scores from cache file and trigger update from server
10562 LoadServerScore(nr, download_score);
10564 // merge local scores with scores from server
10565 MergeServerScore();
10568 if (force_last_added)
10569 scores.force_last_added = force_last_added;
10573 // ============================================================================
10574 // setup file functions
10575 // ============================================================================
10577 #define TOKEN_STR_PLAYER_PREFIX "player_"
10580 static struct TokenInfo global_setup_tokens[] =
10584 &setup.player_name, "player_name"
10588 &setup.multiple_users, "multiple_users"
10592 &setup.sound, "sound"
10596 &setup.sound_loops, "repeating_sound_loops"
10600 &setup.sound_music, "background_music"
10604 &setup.sound_simple, "simple_sound_effects"
10608 &setup.toons, "toons"
10612 &setup.global_animations, "global_animations"
10616 &setup.scroll_delay, "scroll_delay"
10620 &setup.forced_scroll_delay, "forced_scroll_delay"
10624 &setup.scroll_delay_value, "scroll_delay_value"
10628 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10632 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10636 &setup.fade_screens, "fade_screens"
10640 &setup.autorecord, "automatic_tape_recording"
10644 &setup.autorecord_after_replay, "autorecord_after_replay"
10648 &setup.auto_pause_on_start, "auto_pause_on_start"
10652 &setup.show_titlescreen, "show_titlescreen"
10656 &setup.quick_doors, "quick_doors"
10660 &setup.team_mode, "team_mode"
10664 &setup.handicap, "handicap"
10668 &setup.skip_levels, "skip_levels"
10672 &setup.increment_levels, "increment_levels"
10676 &setup.auto_play_next_level, "auto_play_next_level"
10680 &setup.count_score_after_game, "count_score_after_game"
10684 &setup.show_scores_after_game, "show_scores_after_game"
10688 &setup.time_limit, "time_limit"
10692 &setup.fullscreen, "fullscreen"
10696 &setup.window_scaling_percent, "window_scaling_percent"
10700 &setup.window_scaling_quality, "window_scaling_quality"
10704 &setup.screen_rendering_mode, "screen_rendering_mode"
10708 &setup.vsync_mode, "vsync_mode"
10712 &setup.ask_on_escape, "ask_on_escape"
10716 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10720 &setup.ask_on_game_over, "ask_on_game_over"
10724 &setup.ask_on_quit_game, "ask_on_quit_game"
10728 &setup.ask_on_quit_program, "ask_on_quit_program"
10732 &setup.quick_switch, "quick_player_switch"
10736 &setup.input_on_focus, "input_on_focus"
10740 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10744 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10748 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10752 &setup.game_speed_extended, "game_speed_extended"
10756 &setup.game_frame_delay, "game_frame_delay"
10760 &setup.default_game_engine_type, "default_game_engine_type"
10764 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10768 &setup.bd_skip_hatching, "bd_skip_hatching"
10772 &setup.bd_scroll_delay, "bd_scroll_delay"
10776 &setup.bd_smooth_movements, "bd_smooth_movements"
10780 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10784 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10788 &setup.bd_skip_falling_sounds, "bd_skip_falling_sounds"
10792 &setup.bd_palette_c64, "bd_palette_c64"
10796 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10800 &setup.bd_palette_atari, "bd_palette_atari"
10804 &setup.bd_default_color_type, "bd_default_color_type"
10808 &setup.bd_random_colors, "bd_random_colors"
10812 &setup.sp_show_border_elements, "sp_show_border_elements"
10816 &setup.small_game_graphics, "small_game_graphics"
10820 &setup.show_load_save_buttons, "show_load_save_buttons"
10824 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10828 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10832 &setup.graphics_set, "graphics_set"
10836 &setup.sounds_set, "sounds_set"
10840 &setup.music_set, "music_set"
10844 &setup.override_level_graphics, "override_level_graphics"
10848 &setup.override_level_sounds, "override_level_sounds"
10852 &setup.override_level_music, "override_level_music"
10856 &setup.volume_simple, "volume_simple"
10860 &setup.volume_loops, "volume_loops"
10864 &setup.volume_music, "volume_music"
10868 &setup.network_mode, "network_mode"
10872 &setup.network_player_nr, "network_player"
10876 &setup.network_server_hostname, "network_server_hostname"
10880 &setup.touch.control_type, "touch.control_type"
10884 &setup.touch.move_distance, "touch.move_distance"
10888 &setup.touch.drop_distance, "touch.drop_distance"
10892 &setup.touch.transparency, "touch.transparency"
10896 &setup.touch.draw_outlined, "touch.draw_outlined"
10900 &setup.touch.draw_pressed, "touch.draw_pressed"
10904 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10908 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10912 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10916 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10920 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10924 static struct TokenInfo auto_setup_tokens[] =
10928 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10932 static struct TokenInfo server_setup_tokens[] =
10936 &setup.player_uuid, "player_uuid"
10940 &setup.player_version, "player_version"
10944 &setup.use_api_server, TEST_PREFIX "use_api_server"
10948 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10952 &setup.api_server_password, TEST_PREFIX "api_server_password"
10956 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10960 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10964 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10968 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10972 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10976 static struct TokenInfo editor_setup_tokens[] =
10980 &setup.editor.el_classic, "editor.el_classic"
10984 &setup.editor.el_custom, "editor.el_custom"
10988 &setup.editor.el_user_defined, "editor.el_user_defined"
10992 &setup.editor.el_dynamic, "editor.el_dynamic"
10996 &setup.editor.el_headlines, "editor.el_headlines"
11000 &setup.editor.show_element_token, "editor.show_element_token"
11004 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11008 static struct TokenInfo editor_cascade_setup_tokens[] =
11012 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11016 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
11020 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
11024 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11028 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11032 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11036 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11040 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11044 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11048 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11052 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11056 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11060 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11064 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11068 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11072 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11076 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11080 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11084 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11088 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11092 static struct TokenInfo shortcut_setup_tokens[] =
11096 &setup.shortcut.save_game, "shortcut.save_game"
11100 &setup.shortcut.load_game, "shortcut.load_game"
11104 &setup.shortcut.restart_game, "shortcut.restart_game"
11108 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11112 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11116 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11120 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11124 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11128 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11132 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11136 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11140 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11144 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11148 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11152 &setup.shortcut.tape_record, "shortcut.tape_record"
11156 &setup.shortcut.tape_play, "shortcut.tape_play"
11160 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11164 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11168 &setup.shortcut.sound_music, "shortcut.sound_music"
11172 &setup.shortcut.snap_left, "shortcut.snap_left"
11176 &setup.shortcut.snap_right, "shortcut.snap_right"
11180 &setup.shortcut.snap_up, "shortcut.snap_up"
11184 &setup.shortcut.snap_down, "shortcut.snap_down"
11188 static struct SetupInputInfo setup_input;
11189 static struct TokenInfo player_setup_tokens[] =
11193 &setup_input.use_joystick, ".use_joystick"
11197 &setup_input.joy.device_name, ".joy.device_name"
11201 &setup_input.joy.xleft, ".joy.xleft"
11205 &setup_input.joy.xmiddle, ".joy.xmiddle"
11209 &setup_input.joy.xright, ".joy.xright"
11213 &setup_input.joy.yupper, ".joy.yupper"
11217 &setup_input.joy.ymiddle, ".joy.ymiddle"
11221 &setup_input.joy.ylower, ".joy.ylower"
11225 &setup_input.joy.snap, ".joy.snap_field"
11229 &setup_input.joy.drop, ".joy.place_bomb"
11233 &setup_input.key.left, ".key.move_left"
11237 &setup_input.key.right, ".key.move_right"
11241 &setup_input.key.up, ".key.move_up"
11245 &setup_input.key.down, ".key.move_down"
11249 &setup_input.key.snap, ".key.snap_field"
11253 &setup_input.key.drop, ".key.place_bomb"
11257 static struct TokenInfo system_setup_tokens[] =
11261 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11265 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11269 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11273 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11277 static struct TokenInfo internal_setup_tokens[] =
11281 &setup.internal.program_title, "program_title"
11285 &setup.internal.program_version, "program_version"
11289 &setup.internal.program_author, "program_author"
11293 &setup.internal.program_email, "program_email"
11297 &setup.internal.program_website, "program_website"
11301 &setup.internal.program_copyright, "program_copyright"
11305 &setup.internal.program_company, "program_company"
11309 &setup.internal.program_icon_file, "program_icon_file"
11313 &setup.internal.default_graphics_set, "default_graphics_set"
11317 &setup.internal.default_sounds_set, "default_sounds_set"
11321 &setup.internal.default_music_set, "default_music_set"
11325 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11329 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11333 &setup.internal.fallback_music_file, "fallback_music_file"
11337 &setup.internal.default_level_series, "default_level_series"
11341 &setup.internal.default_window_width, "default_window_width"
11345 &setup.internal.default_window_height, "default_window_height"
11349 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11353 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11357 &setup.internal.create_user_levelset, "create_user_levelset"
11361 &setup.internal.info_screens_from_main, "info_screens_from_main"
11365 &setup.internal.menu_game, "menu_game"
11369 &setup.internal.menu_engines, "menu_engines"
11373 &setup.internal.menu_editor, "menu_editor"
11377 &setup.internal.menu_graphics, "menu_graphics"
11381 &setup.internal.menu_sound, "menu_sound"
11385 &setup.internal.menu_artwork, "menu_artwork"
11389 &setup.internal.menu_input, "menu_input"
11393 &setup.internal.menu_touch, "menu_touch"
11397 &setup.internal.menu_shortcuts, "menu_shortcuts"
11401 &setup.internal.menu_exit, "menu_exit"
11405 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11409 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11413 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11417 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11421 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11425 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11429 &setup.internal.info_title, "info_title"
11433 &setup.internal.info_elements, "info_elements"
11437 &setup.internal.info_music, "info_music"
11441 &setup.internal.info_credits, "info_credits"
11445 &setup.internal.info_program, "info_program"
11449 &setup.internal.info_version, "info_version"
11453 &setup.internal.info_levelset, "info_levelset"
11457 &setup.internal.info_exit, "info_exit"
11461 static struct TokenInfo debug_setup_tokens[] =
11465 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11469 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11473 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11477 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11481 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11485 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11489 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11493 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11497 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11501 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11505 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11509 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11513 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11517 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11521 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11525 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11529 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11533 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11537 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11541 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11545 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11548 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11552 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11556 &setup.debug.xsn_mode, "debug.xsn_mode"
11560 &setup.debug.xsn_percent, "debug.xsn_percent"
11564 static struct TokenInfo options_setup_tokens[] =
11568 &setup.options.verbose, "options.verbose"
11572 &setup.options.debug, "options.debug"
11576 &setup.options.debug_mode, "options.debug_mode"
11580 static void setSetupInfoToDefaults(struct SetupInfo *si)
11584 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11586 si->multiple_users = TRUE;
11589 si->sound_loops = TRUE;
11590 si->sound_music = TRUE;
11591 si->sound_simple = TRUE;
11593 si->global_animations = TRUE;
11594 si->scroll_delay = TRUE;
11595 si->forced_scroll_delay = FALSE;
11596 si->scroll_delay_value = STD_SCROLL_DELAY;
11597 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11598 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11599 si->fade_screens = TRUE;
11600 si->autorecord = TRUE;
11601 si->autorecord_after_replay = TRUE;
11602 si->auto_pause_on_start = FALSE;
11603 si->show_titlescreen = TRUE;
11604 si->quick_doors = FALSE;
11605 si->team_mode = FALSE;
11606 si->handicap = TRUE;
11607 si->skip_levels = TRUE;
11608 si->increment_levels = TRUE;
11609 si->auto_play_next_level = TRUE;
11610 si->count_score_after_game = TRUE;
11611 si->show_scores_after_game = TRUE;
11612 si->time_limit = TRUE;
11613 si->fullscreen = FALSE;
11614 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11615 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11616 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11617 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11618 si->ask_on_escape = TRUE;
11619 si->ask_on_escape_editor = TRUE;
11620 si->ask_on_game_over = TRUE;
11621 si->ask_on_quit_game = TRUE;
11622 si->ask_on_quit_program = TRUE;
11623 si->quick_switch = FALSE;
11624 si->input_on_focus = FALSE;
11625 si->prefer_aga_graphics = TRUE;
11626 si->prefer_lowpass_sounds = FALSE;
11627 si->prefer_extra_panel_items = TRUE;
11628 si->game_speed_extended = FALSE;
11629 si->game_frame_delay = GAME_FRAME_DELAY;
11630 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11631 si->bd_skip_uncovering = FALSE;
11632 si->bd_skip_hatching = FALSE;
11633 si->bd_scroll_delay = TRUE;
11634 si->bd_smooth_movements = AUTO;
11635 si->bd_pushing_graphics = TRUE;
11636 si->bd_up_down_graphics = TRUE;
11637 si->bd_skip_falling_sounds = AUTO;
11638 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11639 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11640 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11641 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11642 si->bd_random_colors = FALSE;
11643 si->sp_show_border_elements = FALSE;
11644 si->small_game_graphics = FALSE;
11645 si->show_load_save_buttons = FALSE;
11646 si->show_undo_redo_buttons = FALSE;
11647 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11649 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11650 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11651 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11653 si->override_level_graphics = FALSE;
11654 si->override_level_sounds = FALSE;
11655 si->override_level_music = FALSE;
11657 si->volume_simple = 100; // percent
11658 si->volume_loops = 100; // percent
11659 si->volume_music = 100; // percent
11661 si->network_mode = FALSE;
11662 si->network_player_nr = 0; // first player
11663 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11665 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11666 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11667 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11668 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11669 si->touch.draw_outlined = TRUE;
11670 si->touch.draw_pressed = TRUE;
11672 for (i = 0; i < 2; i++)
11674 char *default_grid_button[6][2] =
11680 { "111222", " vv " },
11681 { "111222", " vv " }
11683 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11684 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11685 int min_xsize = MIN(6, grid_xsize);
11686 int min_ysize = MIN(6, grid_ysize);
11687 int startx = grid_xsize - min_xsize;
11688 int starty = grid_ysize - min_ysize;
11691 // virtual buttons grid can only be set to defaults if video is initialized
11692 // (this will be repeated if virtual buttons are not loaded from setup file)
11693 if (video.initialized)
11695 si->touch.grid_xsize[i] = grid_xsize;
11696 si->touch.grid_ysize[i] = grid_ysize;
11700 si->touch.grid_xsize[i] = -1;
11701 si->touch.grid_ysize[i] = -1;
11704 for (x = 0; x < MAX_GRID_XSIZE; x++)
11705 for (y = 0; y < MAX_GRID_YSIZE; y++)
11706 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11708 for (x = 0; x < min_xsize; x++)
11709 for (y = 0; y < min_ysize; y++)
11710 si->touch.grid_button[i][x][starty + y] =
11711 default_grid_button[y][0][x];
11713 for (x = 0; x < min_xsize; x++)
11714 for (y = 0; y < min_ysize; y++)
11715 si->touch.grid_button[i][startx + x][starty + y] =
11716 default_grid_button[y][1][x];
11719 si->touch.grid_initialized = video.initialized;
11721 si->touch.overlay_buttons = FALSE;
11723 si->editor.el_boulderdash = TRUE;
11724 si->editor.el_boulderdash_native = TRUE;
11725 si->editor.el_boulderdash_effects = TRUE;
11726 si->editor.el_emerald_mine = TRUE;
11727 si->editor.el_emerald_mine_club = TRUE;
11728 si->editor.el_more = TRUE;
11729 si->editor.el_sokoban = TRUE;
11730 si->editor.el_supaplex = TRUE;
11731 si->editor.el_diamond_caves = TRUE;
11732 si->editor.el_dx_boulderdash = TRUE;
11734 si->editor.el_mirror_magic = TRUE;
11735 si->editor.el_deflektor = TRUE;
11737 si->editor.el_chars = TRUE;
11738 si->editor.el_steel_chars = TRUE;
11740 si->editor.el_classic = TRUE;
11741 si->editor.el_custom = TRUE;
11743 si->editor.el_user_defined = FALSE;
11744 si->editor.el_dynamic = TRUE;
11746 si->editor.el_headlines = TRUE;
11748 si->editor.show_element_token = FALSE;
11750 si->editor.show_read_only_warning = TRUE;
11752 si->editor.use_template_for_new_levels = TRUE;
11754 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11755 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11756 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11757 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11758 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11760 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11761 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11762 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11763 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11764 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11766 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11767 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11768 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11769 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11770 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11771 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11773 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11774 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11775 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11777 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11778 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11779 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11780 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11782 for (i = 0; i < MAX_PLAYERS; i++)
11784 si->input[i].use_joystick = FALSE;
11785 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11786 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11787 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11788 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11789 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11790 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11791 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11792 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11793 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11794 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11795 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11796 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11797 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11798 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11799 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11802 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11803 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11804 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11805 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11807 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11808 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11809 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11810 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11811 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11812 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11813 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11815 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11817 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11818 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11819 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11821 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11822 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11823 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11825 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11826 si->internal.choose_from_top_leveldir = FALSE;
11827 si->internal.show_scaling_in_title = TRUE;
11828 si->internal.create_user_levelset = TRUE;
11829 si->internal.info_screens_from_main = FALSE;
11831 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11832 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11834 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11835 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11836 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11837 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11838 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11839 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11840 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11841 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11842 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11843 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11845 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11846 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11847 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11848 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11849 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11850 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11851 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11852 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11853 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11854 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11856 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11857 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11859 si->debug.show_frames_per_second = FALSE;
11861 si->debug.xsn_mode = AUTO;
11862 si->debug.xsn_percent = 0;
11864 si->options.verbose = FALSE;
11865 si->options.debug = FALSE;
11866 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11868 #if defined(PLATFORM_ANDROID)
11869 si->fullscreen = TRUE;
11870 si->touch.overlay_buttons = TRUE;
11873 setHideSetupEntry(&setup.debug.xsn_mode);
11876 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11878 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11881 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11883 si->player_uuid = NULL; // (will be set later)
11884 si->player_version = 1; // (will be set later)
11886 si->use_api_server = TRUE;
11887 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11888 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11889 si->ask_for_uploading_tapes = TRUE;
11890 si->ask_for_remaining_tapes = FALSE;
11891 si->provide_uploading_tapes = TRUE;
11892 si->ask_for_using_api_server = TRUE;
11893 si->has_remaining_tapes = FALSE;
11896 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11898 si->editor_cascade.el_bd = TRUE;
11899 si->editor_cascade.el_bd_native = TRUE;
11900 si->editor_cascade.el_bd_effects = FALSE;
11901 si->editor_cascade.el_em = TRUE;
11902 si->editor_cascade.el_emc = TRUE;
11903 si->editor_cascade.el_rnd = TRUE;
11904 si->editor_cascade.el_sb = TRUE;
11905 si->editor_cascade.el_sp = TRUE;
11906 si->editor_cascade.el_dc = TRUE;
11907 si->editor_cascade.el_dx = TRUE;
11909 si->editor_cascade.el_mm = TRUE;
11910 si->editor_cascade.el_df = TRUE;
11912 si->editor_cascade.el_chars = FALSE;
11913 si->editor_cascade.el_steel_chars = FALSE;
11914 si->editor_cascade.el_ce = FALSE;
11915 si->editor_cascade.el_ge = FALSE;
11916 si->editor_cascade.el_es = FALSE;
11917 si->editor_cascade.el_ref = FALSE;
11918 si->editor_cascade.el_user = FALSE;
11919 si->editor_cascade.el_dynamic = FALSE;
11922 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11924 static char *getHideSetupToken(void *setup_value)
11926 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11928 if (setup_value != NULL)
11929 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11931 return hide_setup_token;
11934 void setHideSetupEntry(void *setup_value)
11936 char *hide_setup_token = getHideSetupToken(setup_value);
11938 if (hide_setup_hash == NULL)
11939 hide_setup_hash = newSetupFileHash();
11941 if (setup_value != NULL)
11942 setHashEntry(hide_setup_hash, hide_setup_token, "");
11945 void removeHideSetupEntry(void *setup_value)
11947 char *hide_setup_token = getHideSetupToken(setup_value);
11949 if (setup_value != NULL)
11950 removeHashEntry(hide_setup_hash, hide_setup_token);
11953 boolean hideSetupEntry(void *setup_value)
11955 char *hide_setup_token = getHideSetupToken(setup_value);
11957 return (setup_value != NULL &&
11958 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11961 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11962 struct TokenInfo *token_info,
11963 int token_nr, char *token_text)
11965 char *token_hide_text = getStringCat2(token_text, ".hide");
11966 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11968 // set the value of this setup option in the setup option structure
11969 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11971 // check if this setup option should be hidden in the setup menu
11972 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11973 setHideSetupEntry(token_info[token_nr].value);
11975 free(token_hide_text);
11978 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11979 struct TokenInfo *token_info,
11982 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11983 token_info[token_nr].text);
11986 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11990 if (!setup_file_hash)
11993 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11994 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11996 setup.touch.grid_initialized = TRUE;
11997 for (i = 0; i < 2; i++)
11999 int grid_xsize = setup.touch.grid_xsize[i];
12000 int grid_ysize = setup.touch.grid_ysize[i];
12003 // if virtual buttons are not loaded from setup file, repeat initializing
12004 // virtual buttons grid with default values later when video is initialized
12005 if (grid_xsize == -1 ||
12008 setup.touch.grid_initialized = FALSE;
12013 for (y = 0; y < grid_ysize; y++)
12015 char token_string[MAX_LINE_LEN];
12017 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12019 char *value_string = getHashEntry(setup_file_hash, token_string);
12021 if (value_string == NULL)
12024 for (x = 0; x < grid_xsize; x++)
12026 char c = value_string[x];
12028 setup.touch.grid_button[i][x][y] =
12029 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12034 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12035 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12037 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12038 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12040 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12044 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12046 setup_input = setup.input[pnr];
12047 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12049 char full_token[100];
12051 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12052 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12055 setup.input[pnr] = setup_input;
12058 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12059 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12061 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12062 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12064 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12065 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12067 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12068 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12070 setHideRelatedSetupEntries();
12073 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12077 if (!setup_file_hash)
12080 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12081 setSetupInfo(auto_setup_tokens, i,
12082 getHashEntry(setup_file_hash,
12083 auto_setup_tokens[i].text));
12086 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12090 if (!setup_file_hash)
12093 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12094 setSetupInfo(server_setup_tokens, i,
12095 getHashEntry(setup_file_hash,
12096 server_setup_tokens[i].text));
12099 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12103 if (!setup_file_hash)
12106 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12107 setSetupInfo(editor_cascade_setup_tokens, i,
12108 getHashEntry(setup_file_hash,
12109 editor_cascade_setup_tokens[i].text));
12112 void LoadUserNames(void)
12114 int last_user_nr = user.nr;
12117 if (global.user_names != NULL)
12119 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12120 checked_free(global.user_names[i]);
12122 checked_free(global.user_names);
12125 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12127 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12131 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12133 if (setup_file_hash)
12135 char *player_name = getHashEntry(setup_file_hash, "player_name");
12137 global.user_names[i] = getFixedUserName(player_name);
12139 freeSetupFileHash(setup_file_hash);
12142 if (global.user_names[i] == NULL)
12143 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12146 user.nr = last_user_nr;
12149 void LoadSetupFromFilename(char *filename)
12151 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12153 if (setup_file_hash)
12155 decodeSetupFileHash_Default(setup_file_hash);
12157 freeSetupFileHash(setup_file_hash);
12161 Debug("setup", "using default setup values");
12165 static void LoadSetup_SpecialPostProcessing(void)
12167 char *player_name_new;
12169 // needed to work around problems with fixed length strings
12170 player_name_new = getFixedUserName(setup.player_name);
12171 free(setup.player_name);
12172 setup.player_name = player_name_new;
12174 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12175 if (setup.scroll_delay == FALSE)
12177 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12178 setup.scroll_delay = TRUE; // now always "on"
12181 // make sure that scroll delay value stays inside valid range
12182 setup.scroll_delay_value =
12183 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12186 void LoadSetup_Default(void)
12190 // always start with reliable default values
12191 setSetupInfoToDefaults(&setup);
12193 // try to load setup values from default setup file
12194 filename = getDefaultSetupFilename();
12196 if (fileExists(filename))
12197 LoadSetupFromFilename(filename);
12199 // try to load setup values from platform setup file
12200 filename = getPlatformSetupFilename();
12202 if (fileExists(filename))
12203 LoadSetupFromFilename(filename);
12205 // try to load setup values from user setup file
12206 filename = getSetupFilename();
12208 LoadSetupFromFilename(filename);
12210 LoadSetup_SpecialPostProcessing();
12213 void LoadSetup_AutoSetup(void)
12215 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12216 SetupFileHash *setup_file_hash = NULL;
12218 // always start with reliable default values
12219 setSetupInfoToDefaults_AutoSetup(&setup);
12221 setup_file_hash = loadSetupFileHash(filename);
12223 if (setup_file_hash)
12225 decodeSetupFileHash_AutoSetup(setup_file_hash);
12227 freeSetupFileHash(setup_file_hash);
12233 void LoadSetup_ServerSetup(void)
12235 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12236 SetupFileHash *setup_file_hash = NULL;
12238 // always start with reliable default values
12239 setSetupInfoToDefaults_ServerSetup(&setup);
12241 setup_file_hash = loadSetupFileHash(filename);
12243 if (setup_file_hash)
12245 decodeSetupFileHash_ServerSetup(setup_file_hash);
12247 freeSetupFileHash(setup_file_hash);
12252 if (setup.player_uuid == NULL)
12254 // player UUID does not yet exist in setup file
12255 setup.player_uuid = getStringCopy(getUUID());
12256 setup.player_version = 2;
12258 SaveSetup_ServerSetup();
12262 void LoadSetup_EditorCascade(void)
12264 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12265 SetupFileHash *setup_file_hash = NULL;
12267 // always start with reliable default values
12268 setSetupInfoToDefaults_EditorCascade(&setup);
12270 setup_file_hash = loadSetupFileHash(filename);
12272 if (setup_file_hash)
12274 decodeSetupFileHash_EditorCascade(setup_file_hash);
12276 freeSetupFileHash(setup_file_hash);
12282 void LoadSetup(void)
12284 LoadSetup_Default();
12285 LoadSetup_AutoSetup();
12286 LoadSetup_ServerSetup();
12287 LoadSetup_EditorCascade();
12290 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12291 char *mapping_line)
12293 char mapping_guid[MAX_LINE_LEN];
12294 char *mapping_start, *mapping_end;
12296 // get GUID from game controller mapping line: copy complete line
12297 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12298 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12300 // get GUID from game controller mapping line: cut after GUID part
12301 mapping_start = strchr(mapping_guid, ',');
12302 if (mapping_start != NULL)
12303 *mapping_start = '\0';
12305 // cut newline from game controller mapping line
12306 mapping_end = strchr(mapping_line, '\n');
12307 if (mapping_end != NULL)
12308 *mapping_end = '\0';
12310 // add mapping entry to game controller mappings hash
12311 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12314 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12319 if (!(file = fopen(filename, MODE_READ)))
12321 Warn("cannot read game controller mappings file '%s'", filename);
12326 while (!feof(file))
12328 char line[MAX_LINE_LEN];
12330 if (!fgets(line, MAX_LINE_LEN, file))
12333 addGameControllerMappingToHash(mappings_hash, line);
12339 void SaveSetup_Default(void)
12341 char *filename = getSetupFilename();
12345 InitUserDataDirectory();
12347 if (!(file = fopen(filename, MODE_WRITE)))
12349 Warn("cannot write setup file '%s'", filename);
12354 fprintFileHeader(file, SETUP_FILENAME);
12356 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12358 // just to make things nicer :)
12359 if (global_setup_tokens[i].value == &setup.multiple_users ||
12360 global_setup_tokens[i].value == &setup.sound ||
12361 global_setup_tokens[i].value == &setup.graphics_set ||
12362 global_setup_tokens[i].value == &setup.volume_simple ||
12363 global_setup_tokens[i].value == &setup.network_mode ||
12364 global_setup_tokens[i].value == &setup.touch.control_type ||
12365 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12366 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12367 fprintf(file, "\n");
12369 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12372 for (i = 0; i < 2; i++)
12374 int grid_xsize = setup.touch.grid_xsize[i];
12375 int grid_ysize = setup.touch.grid_ysize[i];
12378 fprintf(file, "\n");
12380 for (y = 0; y < grid_ysize; y++)
12382 char token_string[MAX_LINE_LEN];
12383 char value_string[MAX_LINE_LEN];
12385 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12387 for (x = 0; x < grid_xsize; x++)
12389 char c = setup.touch.grid_button[i][x][y];
12391 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12394 value_string[grid_xsize] = '\0';
12396 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12400 fprintf(file, "\n");
12401 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12402 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12404 fprintf(file, "\n");
12405 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12406 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12408 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12412 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12413 fprintf(file, "\n");
12415 setup_input = setup.input[pnr];
12416 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12417 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12420 fprintf(file, "\n");
12421 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12422 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12424 // (internal setup values not saved to user setup file)
12426 fprintf(file, "\n");
12427 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12428 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12429 setup.debug.xsn_mode != AUTO)
12430 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12432 fprintf(file, "\n");
12433 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12434 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12438 SetFilePermissions(filename, PERMS_PRIVATE);
12441 void SaveSetup_AutoSetup(void)
12443 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12447 InitUserDataDirectory();
12449 if (!(file = fopen(filename, MODE_WRITE)))
12451 Warn("cannot write auto setup file '%s'", filename);
12458 fprintFileHeader(file, AUTOSETUP_FILENAME);
12460 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12461 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12465 SetFilePermissions(filename, PERMS_PRIVATE);
12470 void SaveSetup_ServerSetup(void)
12472 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12476 InitUserDataDirectory();
12478 if (!(file = fopen(filename, MODE_WRITE)))
12480 Warn("cannot write server setup file '%s'", filename);
12487 fprintFileHeader(file, SERVERSETUP_FILENAME);
12489 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12491 // just to make things nicer :)
12492 if (server_setup_tokens[i].value == &setup.use_api_server)
12493 fprintf(file, "\n");
12495 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12500 SetFilePermissions(filename, PERMS_PRIVATE);
12505 void SaveSetup_EditorCascade(void)
12507 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12511 InitUserDataDirectory();
12513 if (!(file = fopen(filename, MODE_WRITE)))
12515 Warn("cannot write editor cascade state file '%s'", filename);
12522 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12524 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12525 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12529 SetFilePermissions(filename, PERMS_PRIVATE);
12534 void SaveSetup(void)
12536 SaveSetup_Default();
12537 SaveSetup_AutoSetup();
12538 SaveSetup_ServerSetup();
12539 SaveSetup_EditorCascade();
12542 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12547 if (!(file = fopen(filename, MODE_WRITE)))
12549 Warn("cannot write game controller mappings file '%s'", filename);
12554 BEGIN_HASH_ITERATION(mappings_hash, itr)
12556 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12558 END_HASH_ITERATION(mappings_hash, itr)
12563 void SaveSetup_AddGameControllerMapping(char *mapping)
12565 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12566 SetupFileHash *mappings_hash = newSetupFileHash();
12568 InitUserDataDirectory();
12570 // load existing personal game controller mappings
12571 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12573 // add new mapping to personal game controller mappings
12574 addGameControllerMappingToHash(mappings_hash, mapping);
12576 // save updated personal game controller mappings
12577 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12579 freeSetupFileHash(mappings_hash);
12583 void LoadCustomElementDescriptions(void)
12585 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12586 SetupFileHash *setup_file_hash;
12589 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12591 if (element_info[i].custom_description != NULL)
12593 free(element_info[i].custom_description);
12594 element_info[i].custom_description = NULL;
12598 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12601 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12603 char *token = getStringCat2(element_info[i].token_name, ".name");
12604 char *value = getHashEntry(setup_file_hash, token);
12607 element_info[i].custom_description = getStringCopy(value);
12612 freeSetupFileHash(setup_file_hash);
12615 static int getElementFromToken(char *token)
12617 char *value = getHashEntry(element_token_hash, token);
12620 return atoi(value);
12622 Warn("unknown element token '%s'", token);
12624 return EL_UNDEFINED;
12627 void FreeGlobalAnimEventInfo(void)
12629 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12631 if (gaei->event_list == NULL)
12636 for (i = 0; i < gaei->num_event_lists; i++)
12638 checked_free(gaei->event_list[i]->event_value);
12639 checked_free(gaei->event_list[i]);
12642 checked_free(gaei->event_list);
12644 gaei->event_list = NULL;
12645 gaei->num_event_lists = 0;
12648 static int AddGlobalAnimEventList(void)
12650 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12651 int list_pos = gaei->num_event_lists++;
12653 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12654 sizeof(struct GlobalAnimEventListInfo *));
12656 gaei->event_list[list_pos] =
12657 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12659 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12661 gaeli->event_value = NULL;
12662 gaeli->num_event_values = 0;
12667 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12669 // do not add empty global animation events
12670 if (event_value == ANIM_EVENT_NONE)
12673 // if list position is undefined, create new list
12674 if (list_pos == ANIM_EVENT_UNDEFINED)
12675 list_pos = AddGlobalAnimEventList();
12677 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12678 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12679 int value_pos = gaeli->num_event_values++;
12681 gaeli->event_value = checked_realloc(gaeli->event_value,
12682 gaeli->num_event_values * sizeof(int *));
12684 gaeli->event_value[value_pos] = event_value;
12689 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12691 if (list_pos == ANIM_EVENT_UNDEFINED)
12692 return ANIM_EVENT_NONE;
12694 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12695 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12697 return gaeli->event_value[value_pos];
12700 int GetGlobalAnimEventValueCount(int list_pos)
12702 if (list_pos == ANIM_EVENT_UNDEFINED)
12705 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12706 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12708 return gaeli->num_event_values;
12711 // This function checks if a string <s> of the format "string1, string2, ..."
12712 // exactly contains a string <s_contained>.
12714 static boolean string_has_parameter(char *s, char *s_contained)
12718 if (s == NULL || s_contained == NULL)
12721 if (strlen(s_contained) > strlen(s))
12724 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12726 char next_char = s[strlen(s_contained)];
12728 // check if next character is delimiter or whitespace
12729 if (next_char == ',' || next_char == '\0' ||
12730 next_char == ' ' || next_char == '\t')
12734 // check if string contains another parameter string after a comma
12735 substring = strchr(s, ',');
12736 if (substring == NULL) // string does not contain a comma
12739 // advance string pointer to next character after the comma
12742 // skip potential whitespaces after the comma
12743 while (*substring == ' ' || *substring == '\t')
12746 return string_has_parameter(substring, s_contained);
12749 static int get_anim_parameter_value_ce(char *s)
12752 char *pattern_1 = "ce_change:custom_";
12753 char *pattern_2 = ".page_";
12754 int pattern_1_len = strlen(pattern_1);
12755 char *matching_char = strstr(s_ptr, pattern_1);
12756 int result = ANIM_EVENT_NONE;
12758 if (matching_char == NULL)
12759 return ANIM_EVENT_NONE;
12761 result = ANIM_EVENT_CE_CHANGE;
12763 s_ptr = matching_char + pattern_1_len;
12765 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12766 if (*s_ptr >= '0' && *s_ptr <= '9')
12768 int gic_ce_nr = (*s_ptr++ - '0');
12770 if (*s_ptr >= '0' && *s_ptr <= '9')
12772 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12774 if (*s_ptr >= '0' && *s_ptr <= '9')
12775 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12778 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12779 return ANIM_EVENT_NONE;
12781 // custom element stored as 0 to 255
12784 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12788 // invalid custom element number specified
12790 return ANIM_EVENT_NONE;
12793 // check for change page number ("page_X" or "page_XX") (optional)
12794 if (strPrefix(s_ptr, pattern_2))
12796 s_ptr += strlen(pattern_2);
12798 if (*s_ptr >= '0' && *s_ptr <= '9')
12800 int gic_page_nr = (*s_ptr++ - '0');
12802 if (*s_ptr >= '0' && *s_ptr <= '9')
12803 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12805 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12806 return ANIM_EVENT_NONE;
12808 // change page stored as 1 to 32 (0 means "all change pages")
12810 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12814 // invalid animation part number specified
12816 return ANIM_EVENT_NONE;
12820 // discard result if next character is neither delimiter nor whitespace
12821 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12822 *s_ptr == ' ' || *s_ptr == '\t'))
12823 return ANIM_EVENT_NONE;
12828 static int get_anim_parameter_value(char *s)
12830 int event_value[] =
12838 char *pattern_1[] =
12846 char *pattern_2 = ".part_";
12847 char *matching_char = NULL;
12849 int pattern_1_len = 0;
12850 int result = ANIM_EVENT_NONE;
12853 result = get_anim_parameter_value_ce(s);
12855 if (result != ANIM_EVENT_NONE)
12858 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12860 matching_char = strstr(s_ptr, pattern_1[i]);
12861 pattern_1_len = strlen(pattern_1[i]);
12862 result = event_value[i];
12864 if (matching_char != NULL)
12868 if (matching_char == NULL)
12869 return ANIM_EVENT_NONE;
12871 s_ptr = matching_char + pattern_1_len;
12873 // check for main animation number ("anim_X" or "anim_XX")
12874 if (*s_ptr >= '0' && *s_ptr <= '9')
12876 int gic_anim_nr = (*s_ptr++ - '0');
12878 if (*s_ptr >= '0' && *s_ptr <= '9')
12879 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12881 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12882 return ANIM_EVENT_NONE;
12884 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12888 // invalid main animation number specified
12890 return ANIM_EVENT_NONE;
12893 // check for animation part number ("part_X" or "part_XX") (optional)
12894 if (strPrefix(s_ptr, pattern_2))
12896 s_ptr += strlen(pattern_2);
12898 if (*s_ptr >= '0' && *s_ptr <= '9')
12900 int gic_part_nr = (*s_ptr++ - '0');
12902 if (*s_ptr >= '0' && *s_ptr <= '9')
12903 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12905 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12906 return ANIM_EVENT_NONE;
12908 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12912 // invalid animation part number specified
12914 return ANIM_EVENT_NONE;
12918 // discard result if next character is neither delimiter nor whitespace
12919 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12920 *s_ptr == ' ' || *s_ptr == '\t'))
12921 return ANIM_EVENT_NONE;
12926 static int get_anim_parameter_values(char *s)
12928 int list_pos = ANIM_EVENT_UNDEFINED;
12929 int event_value = ANIM_EVENT_DEFAULT;
12931 if (string_has_parameter(s, "any"))
12932 event_value |= ANIM_EVENT_ANY;
12934 if (string_has_parameter(s, "click:self") ||
12935 string_has_parameter(s, "click") ||
12936 string_has_parameter(s, "self"))
12937 event_value |= ANIM_EVENT_SELF;
12939 if (string_has_parameter(s, "unclick:any"))
12940 event_value |= ANIM_EVENT_UNCLICK_ANY;
12942 // if animation event found, add it to global animation event list
12943 if (event_value != ANIM_EVENT_NONE)
12944 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12948 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12949 event_value = get_anim_parameter_value(s);
12951 // if animation event found, add it to global animation event list
12952 if (event_value != ANIM_EVENT_NONE)
12953 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12955 // continue with next part of the string, starting with next comma
12956 s = strchr(s + 1, ',');
12962 static int get_anim_action_parameter_value(char *token)
12964 // check most common default case first to massively speed things up
12965 if (strEqual(token, ARG_UNDEFINED))
12966 return ANIM_EVENT_ACTION_NONE;
12968 int result = getImageIDFromToken(token);
12972 char *gfx_token = getStringCat2("gfx.", token);
12974 result = getImageIDFromToken(gfx_token);
12976 checked_free(gfx_token);
12981 Key key = getKeyFromX11KeyName(token);
12983 if (key != KSYM_UNDEFINED)
12984 result = -(int)key;
12991 result = get_hash_from_string(token); // unsigned int => int
12992 result = ABS(result); // may be negative now
12993 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12995 setHashEntry(anim_url_hash, int2str(result, 0), token);
13000 result = ANIM_EVENT_ACTION_NONE;
13005 int get_parameter_value(char *value_raw, char *suffix, int type)
13007 char *value = getStringToLower(value_raw);
13008 int result = 0; // probably a save default value
13010 if (strEqual(suffix, ".direction"))
13012 result = (strEqual(value, "left") ? MV_LEFT :
13013 strEqual(value, "right") ? MV_RIGHT :
13014 strEqual(value, "up") ? MV_UP :
13015 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13017 else if (strEqual(suffix, ".position"))
13019 result = (strEqual(value, "left") ? POS_LEFT :
13020 strEqual(value, "right") ? POS_RIGHT :
13021 strEqual(value, "top") ? POS_TOP :
13022 strEqual(value, "upper") ? POS_UPPER :
13023 strEqual(value, "middle") ? POS_MIDDLE :
13024 strEqual(value, "lower") ? POS_LOWER :
13025 strEqual(value, "bottom") ? POS_BOTTOM :
13026 strEqual(value, "any") ? POS_ANY :
13027 strEqual(value, "ce") ? POS_CE :
13028 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13029 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13031 else if (strEqual(suffix, ".align"))
13033 result = (strEqual(value, "left") ? ALIGN_LEFT :
13034 strEqual(value, "right") ? ALIGN_RIGHT :
13035 strEqual(value, "center") ? ALIGN_CENTER :
13036 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13038 else if (strEqual(suffix, ".valign"))
13040 result = (strEqual(value, "top") ? VALIGN_TOP :
13041 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13042 strEqual(value, "middle") ? VALIGN_MIDDLE :
13043 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13045 else if (strEqual(suffix, ".anim_mode"))
13047 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13048 string_has_parameter(value, "loop") ? ANIM_LOOP :
13049 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13050 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13051 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13052 string_has_parameter(value, "random") ? ANIM_RANDOM :
13053 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13054 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13055 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13056 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13057 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13058 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13059 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13060 string_has_parameter(value, "all") ? ANIM_ALL :
13061 string_has_parameter(value, "tiled") ? ANIM_TILED :
13062 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13065 if (string_has_parameter(value, "once"))
13066 result |= ANIM_ONCE;
13068 if (string_has_parameter(value, "reverse"))
13069 result |= ANIM_REVERSE;
13071 if (string_has_parameter(value, "opaque_player"))
13072 result |= ANIM_OPAQUE_PLAYER;
13074 if (string_has_parameter(value, "static_panel"))
13075 result |= ANIM_STATIC_PANEL;
13077 else if (strEqual(suffix, ".init_event") ||
13078 strEqual(suffix, ".anim_event"))
13080 result = get_anim_parameter_values(value);
13082 else if (strEqual(suffix, ".init_delay_action") ||
13083 strEqual(suffix, ".anim_delay_action") ||
13084 strEqual(suffix, ".post_delay_action") ||
13085 strEqual(suffix, ".init_event_action") ||
13086 strEqual(suffix, ".anim_event_action"))
13088 result = get_anim_action_parameter_value(value_raw);
13090 else if (strEqual(suffix, ".class"))
13092 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13093 get_hash_from_string(value));
13095 else if (strEqual(suffix, ".style"))
13097 result = STYLE_DEFAULT;
13099 if (string_has_parameter(value, "accurate_borders"))
13100 result |= STYLE_ACCURATE_BORDERS;
13102 if (string_has_parameter(value, "inner_corners"))
13103 result |= STYLE_INNER_CORNERS;
13105 if (string_has_parameter(value, "reverse"))
13106 result |= STYLE_REVERSE;
13108 if (string_has_parameter(value, "leftmost_position"))
13109 result |= STYLE_LEFTMOST_POSITION;
13111 if (string_has_parameter(value, "block_clicks"))
13112 result |= STYLE_BLOCK;
13114 if (string_has_parameter(value, "passthrough_clicks"))
13115 result |= STYLE_PASSTHROUGH;
13117 if (string_has_parameter(value, "multiple_actions"))
13118 result |= STYLE_MULTIPLE_ACTIONS;
13120 if (string_has_parameter(value, "consume_ce_event"))
13121 result |= STYLE_CONSUME_CE_EVENT;
13123 else if (strEqual(suffix, ".fade_mode"))
13125 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13126 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13127 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13128 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13129 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13130 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13131 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13132 FADE_MODE_DEFAULT);
13134 else if (strEqual(suffix, ".auto_delay_unit"))
13136 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13137 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13138 AUTO_DELAY_UNIT_DEFAULT);
13140 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13142 result = gfx.get_font_from_token_function(value);
13144 else // generic parameter of type integer or boolean
13146 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13147 type == TYPE_INTEGER ? get_integer_from_string(value) :
13148 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13149 ARG_UNDEFINED_VALUE);
13157 static int get_token_parameter_value(char *token, char *value_raw)
13161 if (token == NULL || value_raw == NULL)
13162 return ARG_UNDEFINED_VALUE;
13164 suffix = strrchr(token, '.');
13165 if (suffix == NULL)
13168 if (strEqual(suffix, ".element"))
13169 return getElementFromToken(value_raw);
13171 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13172 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13175 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13176 boolean ignore_defaults)
13180 for (i = 0; image_config_vars[i].token != NULL; i++)
13182 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13184 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13185 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13189 *image_config_vars[i].value =
13190 get_token_parameter_value(image_config_vars[i].token, value);
13194 void InitMenuDesignSettings_Static(void)
13196 // always start with reliable default values from static default config
13197 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13200 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13204 // the following initializes hierarchical values from static configuration
13206 // special case: initialize "ARG_DEFAULT" values in static default config
13207 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13208 titlescreen_initial_first_default.fade_mode =
13209 title_initial_first_default.fade_mode;
13210 titlescreen_initial_first_default.fade_delay =
13211 title_initial_first_default.fade_delay;
13212 titlescreen_initial_first_default.post_delay =
13213 title_initial_first_default.post_delay;
13214 titlescreen_initial_first_default.auto_delay =
13215 title_initial_first_default.auto_delay;
13216 titlescreen_initial_first_default.auto_delay_unit =
13217 title_initial_first_default.auto_delay_unit;
13218 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13219 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13220 titlescreen_first_default.post_delay = title_first_default.post_delay;
13221 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13222 titlescreen_first_default.auto_delay_unit =
13223 title_first_default.auto_delay_unit;
13224 titlemessage_initial_first_default.fade_mode =
13225 title_initial_first_default.fade_mode;
13226 titlemessage_initial_first_default.fade_delay =
13227 title_initial_first_default.fade_delay;
13228 titlemessage_initial_first_default.post_delay =
13229 title_initial_first_default.post_delay;
13230 titlemessage_initial_first_default.auto_delay =
13231 title_initial_first_default.auto_delay;
13232 titlemessage_initial_first_default.auto_delay_unit =
13233 title_initial_first_default.auto_delay_unit;
13234 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13235 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13236 titlemessage_first_default.post_delay = title_first_default.post_delay;
13237 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13238 titlemessage_first_default.auto_delay_unit =
13239 title_first_default.auto_delay_unit;
13241 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13242 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13243 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13244 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13245 titlescreen_initial_default.auto_delay_unit =
13246 title_initial_default.auto_delay_unit;
13247 titlescreen_default.fade_mode = title_default.fade_mode;
13248 titlescreen_default.fade_delay = title_default.fade_delay;
13249 titlescreen_default.post_delay = title_default.post_delay;
13250 titlescreen_default.auto_delay = title_default.auto_delay;
13251 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13252 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13253 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13254 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13255 titlemessage_initial_default.auto_delay_unit =
13256 title_initial_default.auto_delay_unit;
13257 titlemessage_default.fade_mode = title_default.fade_mode;
13258 titlemessage_default.fade_delay = title_default.fade_delay;
13259 titlemessage_default.post_delay = title_default.post_delay;
13260 titlemessage_default.auto_delay = title_default.auto_delay;
13261 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13263 // special case: initialize "ARG_DEFAULT" values in static default config
13264 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13265 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13267 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13268 titlescreen_first[i] = titlescreen_first_default;
13269 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13270 titlemessage_first[i] = titlemessage_first_default;
13272 titlescreen_initial[i] = titlescreen_initial_default;
13273 titlescreen[i] = titlescreen_default;
13274 titlemessage_initial[i] = titlemessage_initial_default;
13275 titlemessage[i] = titlemessage_default;
13278 // special case: initialize "ARG_DEFAULT" values in static default config
13279 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13280 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13282 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13285 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13286 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13287 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13290 // special case: initialize "ARG_DEFAULT" values in static default config
13291 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13292 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13294 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13295 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13296 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13298 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13301 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13305 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13309 struct XY *dst, *src;
13311 game_buttons_xy[] =
13313 { &game.button.save, &game.button.stop },
13314 { &game.button.pause2, &game.button.pause },
13315 { &game.button.load, &game.button.play },
13316 { &game.button.undo, &game.button.stop },
13317 { &game.button.redo, &game.button.play },
13323 // special case: initialize later added SETUP list size from LEVELS value
13324 if (menu.list_size[GAME_MODE_SETUP] == -1)
13325 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13327 // set default position for snapshot buttons to stop/pause/play buttons
13328 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13329 if ((*game_buttons_xy[i].dst).x == -1 &&
13330 (*game_buttons_xy[i].dst).y == -1)
13331 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13333 // --------------------------------------------------------------------------
13334 // dynamic viewports (including playfield margins, borders and alignments)
13335 // --------------------------------------------------------------------------
13337 // dynamic viewports currently only supported for landscape mode
13338 int display_width = MAX(video.display_width, video.display_height);
13339 int display_height = MIN(video.display_width, video.display_height);
13341 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13343 struct RectWithBorder *vp_window = &viewport.window[i];
13344 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13345 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13346 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13347 boolean dynamic_window_width = (vp_window->min_width != -1);
13348 boolean dynamic_window_height = (vp_window->min_height != -1);
13349 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13350 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13352 // adjust window size if min/max width/height is specified
13354 if (vp_window->min_width != -1)
13356 int window_width = display_width;
13358 // when using static window height, use aspect ratio of display
13359 if (vp_window->min_height == -1)
13360 window_width = vp_window->height * display_width / display_height;
13362 vp_window->width = MAX(vp_window->min_width, window_width);
13365 if (vp_window->min_height != -1)
13367 int window_height = display_height;
13369 // when using static window width, use aspect ratio of display
13370 if (vp_window->min_width == -1)
13371 window_height = vp_window->width * display_height / display_width;
13373 vp_window->height = MAX(vp_window->min_height, window_height);
13376 if (vp_window->max_width != -1)
13377 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13379 if (vp_window->max_height != -1)
13380 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13382 int playfield_width = vp_window->width;
13383 int playfield_height = vp_window->height;
13385 // adjust playfield size and position according to specified margins
13387 playfield_width -= vp_playfield->margin_left;
13388 playfield_width -= vp_playfield->margin_right;
13390 playfield_height -= vp_playfield->margin_top;
13391 playfield_height -= vp_playfield->margin_bottom;
13393 // adjust playfield size if min/max width/height is specified
13395 if (vp_playfield->min_width != -1)
13396 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13398 if (vp_playfield->min_height != -1)
13399 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13401 if (vp_playfield->max_width != -1)
13402 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13404 if (vp_playfield->max_height != -1)
13405 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13407 // adjust playfield position according to specified alignment
13409 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13410 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13411 else if (vp_playfield->align == ALIGN_CENTER)
13412 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13413 else if (vp_playfield->align == ALIGN_RIGHT)
13414 vp_playfield->x += playfield_width - vp_playfield->width;
13416 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13417 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13418 else if (vp_playfield->valign == VALIGN_MIDDLE)
13419 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13420 else if (vp_playfield->valign == VALIGN_BOTTOM)
13421 vp_playfield->y += playfield_height - vp_playfield->height;
13423 vp_playfield->x += vp_playfield->margin_left;
13424 vp_playfield->y += vp_playfield->margin_top;
13426 // adjust individual playfield borders if only default border is specified
13428 if (vp_playfield->border_left == -1)
13429 vp_playfield->border_left = vp_playfield->border_size;
13430 if (vp_playfield->border_right == -1)
13431 vp_playfield->border_right = vp_playfield->border_size;
13432 if (vp_playfield->border_top == -1)
13433 vp_playfield->border_top = vp_playfield->border_size;
13434 if (vp_playfield->border_bottom == -1)
13435 vp_playfield->border_bottom = vp_playfield->border_size;
13437 // set dynamic playfield borders if borders are specified as undefined
13438 // (but only if window size was dynamic and playfield size was static)
13440 if (dynamic_window_width && !dynamic_playfield_width)
13442 if (vp_playfield->border_left == -1)
13444 vp_playfield->border_left = (vp_playfield->x -
13445 vp_playfield->margin_left);
13446 vp_playfield->x -= vp_playfield->border_left;
13447 vp_playfield->width += vp_playfield->border_left;
13450 if (vp_playfield->border_right == -1)
13452 vp_playfield->border_right = (vp_window->width -
13454 vp_playfield->width -
13455 vp_playfield->margin_right);
13456 vp_playfield->width += vp_playfield->border_right;
13460 if (dynamic_window_height && !dynamic_playfield_height)
13462 if (vp_playfield->border_top == -1)
13464 vp_playfield->border_top = (vp_playfield->y -
13465 vp_playfield->margin_top);
13466 vp_playfield->y -= vp_playfield->border_top;
13467 vp_playfield->height += vp_playfield->border_top;
13470 if (vp_playfield->border_bottom == -1)
13472 vp_playfield->border_bottom = (vp_window->height -
13474 vp_playfield->height -
13475 vp_playfield->margin_bottom);
13476 vp_playfield->height += vp_playfield->border_bottom;
13480 // adjust playfield size to be a multiple of a defined alignment tile size
13482 int align_size = vp_playfield->align_size;
13483 int playfield_xtiles = vp_playfield->width / align_size;
13484 int playfield_ytiles = vp_playfield->height / align_size;
13485 int playfield_width_corrected = playfield_xtiles * align_size;
13486 int playfield_height_corrected = playfield_ytiles * align_size;
13487 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13488 i == GFX_SPECIAL_ARG_EDITOR);
13490 if (is_playfield_mode &&
13491 dynamic_playfield_width &&
13492 vp_playfield->width != playfield_width_corrected)
13494 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13496 vp_playfield->width = playfield_width_corrected;
13498 if (vp_playfield->align == ALIGN_LEFT)
13500 vp_playfield->border_left += playfield_xdiff;
13502 else if (vp_playfield->align == ALIGN_RIGHT)
13504 vp_playfield->border_right += playfield_xdiff;
13506 else if (vp_playfield->align == ALIGN_CENTER)
13508 int border_left_diff = playfield_xdiff / 2;
13509 int border_right_diff = playfield_xdiff - border_left_diff;
13511 vp_playfield->border_left += border_left_diff;
13512 vp_playfield->border_right += border_right_diff;
13516 if (is_playfield_mode &&
13517 dynamic_playfield_height &&
13518 vp_playfield->height != playfield_height_corrected)
13520 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13522 vp_playfield->height = playfield_height_corrected;
13524 if (vp_playfield->valign == VALIGN_TOP)
13526 vp_playfield->border_top += playfield_ydiff;
13528 else if (vp_playfield->align == VALIGN_BOTTOM)
13530 vp_playfield->border_right += playfield_ydiff;
13532 else if (vp_playfield->align == VALIGN_MIDDLE)
13534 int border_top_diff = playfield_ydiff / 2;
13535 int border_bottom_diff = playfield_ydiff - border_top_diff;
13537 vp_playfield->border_top += border_top_diff;
13538 vp_playfield->border_bottom += border_bottom_diff;
13542 // adjust door positions according to specified alignment
13544 for (j = 0; j < 2; j++)
13546 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13548 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13549 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13550 else if (vp_door->align == ALIGN_CENTER)
13551 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13552 else if (vp_door->align == ALIGN_RIGHT)
13553 vp_door->x += vp_window->width - vp_door->width;
13555 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13556 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13557 else if (vp_door->valign == VALIGN_MIDDLE)
13558 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13559 else if (vp_door->valign == VALIGN_BOTTOM)
13560 vp_door->y += vp_window->height - vp_door->height;
13565 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13569 struct XYTileSize *dst, *src;
13572 editor_buttons_xy[] =
13575 &editor.button.element_left, &editor.palette.element_left,
13576 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13579 &editor.button.element_middle, &editor.palette.element_middle,
13580 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13583 &editor.button.element_right, &editor.palette.element_right,
13584 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13591 // set default position for element buttons to element graphics
13592 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13594 if ((*editor_buttons_xy[i].dst).x == -1 &&
13595 (*editor_buttons_xy[i].dst).y == -1)
13597 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13599 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13601 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13605 // adjust editor palette rows and columns if specified to be dynamic
13607 if (editor.palette.cols == -1)
13609 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13610 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13611 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13613 editor.palette.cols = (vp_width - sc_width) / bt_width;
13615 if (editor.palette.x == -1)
13617 int palette_width = editor.palette.cols * bt_width + sc_width;
13619 editor.palette.x = (vp_width - palette_width) / 2;
13623 if (editor.palette.rows == -1)
13625 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13626 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13627 int tx_height = getFontHeight(FONT_TEXT_2);
13629 editor.palette.rows = (vp_height - tx_height) / bt_height;
13631 if (editor.palette.y == -1)
13633 int palette_height = editor.palette.rows * bt_height + tx_height;
13635 editor.palette.y = (vp_height - palette_height) / 2;
13640 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13641 boolean initialize)
13643 // special case: check if network and preview player positions are redefined,
13644 // to compare this later against the main menu level preview being redefined
13645 struct TokenIntPtrInfo menu_config_players[] =
13647 { "main.network_players.x", &menu.main.network_players.redefined },
13648 { "main.network_players.y", &menu.main.network_players.redefined },
13649 { "main.preview_players.x", &menu.main.preview_players.redefined },
13650 { "main.preview_players.y", &menu.main.preview_players.redefined },
13651 { "preview.x", &preview.redefined },
13652 { "preview.y", &preview.redefined }
13658 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13659 *menu_config_players[i].value = FALSE;
13663 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13664 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13665 *menu_config_players[i].value = TRUE;
13669 static void InitMenuDesignSettings_PreviewPlayers(void)
13671 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13674 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13676 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13679 static void LoadMenuDesignSettingsFromFilename(char *filename)
13681 static struct TitleFadingInfo tfi;
13682 static struct TitleMessageInfo tmi;
13683 static struct TokenInfo title_tokens[] =
13685 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13686 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13687 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13688 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13689 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13693 static struct TokenInfo titlemessage_tokens[] =
13695 { TYPE_INTEGER, &tmi.x, ".x" },
13696 { TYPE_INTEGER, &tmi.y, ".y" },
13697 { TYPE_INTEGER, &tmi.width, ".width" },
13698 { TYPE_INTEGER, &tmi.height, ".height" },
13699 { TYPE_INTEGER, &tmi.chars, ".chars" },
13700 { TYPE_INTEGER, &tmi.lines, ".lines" },
13701 { TYPE_INTEGER, &tmi.align, ".align" },
13702 { TYPE_INTEGER, &tmi.valign, ".valign" },
13703 { TYPE_INTEGER, &tmi.font, ".font" },
13704 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13705 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13706 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13707 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13708 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13709 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13710 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13711 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13712 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13718 struct TitleFadingInfo *info;
13723 // initialize first titles from "enter screen" definitions, if defined
13724 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13725 { &title_first_default, "menu.enter_screen.TITLE" },
13727 // initialize title screens from "next screen" definitions, if defined
13728 { &title_initial_default, "menu.next_screen.TITLE" },
13729 { &title_default, "menu.next_screen.TITLE" },
13735 struct TitleMessageInfo *array;
13738 titlemessage_arrays[] =
13740 // initialize first titles from "enter screen" definitions, if defined
13741 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13742 { titlescreen_first, "menu.enter_screen.TITLE" },
13743 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13744 { titlemessage_first, "menu.enter_screen.TITLE" },
13746 // initialize titles from "next screen" definitions, if defined
13747 { titlescreen_initial, "menu.next_screen.TITLE" },
13748 { titlescreen, "menu.next_screen.TITLE" },
13749 { titlemessage_initial, "menu.next_screen.TITLE" },
13750 { titlemessage, "menu.next_screen.TITLE" },
13752 // overwrite titles with title definitions, if defined
13753 { titlescreen_initial_first, "[title_initial]" },
13754 { titlescreen_first, "[title]" },
13755 { titlemessage_initial_first, "[title_initial]" },
13756 { titlemessage_first, "[title]" },
13758 { titlescreen_initial, "[title_initial]" },
13759 { titlescreen, "[title]" },
13760 { titlemessage_initial, "[title_initial]" },
13761 { titlemessage, "[title]" },
13763 // overwrite titles with title screen/message definitions, if defined
13764 { titlescreen_initial_first, "[titlescreen_initial]" },
13765 { titlescreen_first, "[titlescreen]" },
13766 { titlemessage_initial_first, "[titlemessage_initial]" },
13767 { titlemessage_first, "[titlemessage]" },
13769 { titlescreen_initial, "[titlescreen_initial]" },
13770 { titlescreen, "[titlescreen]" },
13771 { titlemessage_initial, "[titlemessage_initial]" },
13772 { titlemessage, "[titlemessage]" },
13776 SetupFileHash *setup_file_hash;
13779 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13782 // the following initializes hierarchical values from dynamic configuration
13784 // special case: initialize with default values that may be overwritten
13785 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13786 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13788 struct TokenIntPtrInfo menu_config[] =
13790 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13791 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13792 { "menu.list_size", &menu.list_size[i] }
13795 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13797 char *token = menu_config[j].token;
13798 char *value = getHashEntry(setup_file_hash, token);
13801 *menu_config[j].value = get_integer_from_string(value);
13805 // special case: initialize with default values that may be overwritten
13806 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13807 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13809 struct TokenIntPtrInfo menu_config[] =
13811 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13812 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13813 { "menu.list_size.INFO", &menu.list_size_info[i] },
13814 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13815 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13818 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13820 char *token = menu_config[j].token;
13821 char *value = getHashEntry(setup_file_hash, token);
13824 *menu_config[j].value = get_integer_from_string(value);
13828 // special case: initialize with default values that may be overwritten
13829 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13830 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13832 struct TokenIntPtrInfo menu_config[] =
13834 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13835 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13838 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13840 char *token = menu_config[j].token;
13841 char *value = getHashEntry(setup_file_hash, token);
13844 *menu_config[j].value = get_integer_from_string(value);
13848 // special case: initialize with default values that may be overwritten
13849 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13850 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13852 struct TokenIntPtrInfo menu_config[] =
13854 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13855 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13856 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13857 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13858 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13859 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13860 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13861 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13862 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13863 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13866 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13868 char *token = menu_config[j].token;
13869 char *value = getHashEntry(setup_file_hash, token);
13872 *menu_config[j].value = get_integer_from_string(value);
13876 // special case: initialize with default values that may be overwritten
13877 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13878 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13880 struct TokenIntPtrInfo menu_config[] =
13882 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13883 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13884 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13885 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13886 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13887 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13888 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13889 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13890 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13893 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13895 char *token = menu_config[j].token;
13896 char *value = getHashEntry(setup_file_hash, token);
13899 *menu_config[j].value = get_token_parameter_value(token, value);
13903 // special case: initialize with default values that may be overwritten
13904 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13905 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13909 char *token_prefix;
13910 struct RectWithBorder *struct_ptr;
13914 { "viewport.window", &viewport.window[i] },
13915 { "viewport.playfield", &viewport.playfield[i] },
13916 { "viewport.door_1", &viewport.door_1[i] },
13917 { "viewport.door_2", &viewport.door_2[i] }
13920 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13922 struct TokenIntPtrInfo vp_config[] =
13924 { ".x", &vp_struct[j].struct_ptr->x },
13925 { ".y", &vp_struct[j].struct_ptr->y },
13926 { ".width", &vp_struct[j].struct_ptr->width },
13927 { ".height", &vp_struct[j].struct_ptr->height },
13928 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13929 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13930 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13931 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13932 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13933 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13934 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13935 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13936 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13937 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13938 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13939 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13940 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13941 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13942 { ".align", &vp_struct[j].struct_ptr->align },
13943 { ".valign", &vp_struct[j].struct_ptr->valign }
13946 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13948 char *token = getStringCat2(vp_struct[j].token_prefix,
13949 vp_config[k].token);
13950 char *value = getHashEntry(setup_file_hash, token);
13953 *vp_config[k].value = get_token_parameter_value(token, value);
13960 // special case: initialize with default values that may be overwritten
13961 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13962 for (i = 0; title_info[i].info != NULL; i++)
13964 struct TitleFadingInfo *info = title_info[i].info;
13965 char *base_token = title_info[i].text;
13967 for (j = 0; title_tokens[j].type != -1; j++)
13969 char *token = getStringCat2(base_token, title_tokens[j].text);
13970 char *value = getHashEntry(setup_file_hash, token);
13974 int parameter_value = get_token_parameter_value(token, value);
13978 *(int *)title_tokens[j].value = (int)parameter_value;
13987 // special case: initialize with default values that may be overwritten
13988 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13989 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13991 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13992 char *base_token = titlemessage_arrays[i].text;
13994 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13996 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13997 char *value = getHashEntry(setup_file_hash, token);
14001 int parameter_value = get_token_parameter_value(token, value);
14003 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14007 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14008 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14010 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14020 // read (and overwrite with) values that may be specified in config file
14021 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14023 // special case: check if network and preview player positions are redefined
14024 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14026 freeSetupFileHash(setup_file_hash);
14029 void LoadMenuDesignSettings(void)
14031 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14033 InitMenuDesignSettings_Static();
14034 InitMenuDesignSettings_SpecialPreProcessing();
14035 InitMenuDesignSettings_PreviewPlayers();
14037 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14039 // first look for special settings configured in level series config
14040 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14042 if (fileExists(filename_base))
14043 LoadMenuDesignSettingsFromFilename(filename_base);
14046 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14048 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14049 LoadMenuDesignSettingsFromFilename(filename_local);
14051 InitMenuDesignSettings_SpecialPostProcessing();
14054 void LoadMenuDesignSettings_AfterGraphics(void)
14056 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14059 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14060 boolean ignore_defaults)
14064 for (i = 0; sound_config_vars[i].token != NULL; i++)
14066 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14068 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14069 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14073 *sound_config_vars[i].value =
14074 get_token_parameter_value(sound_config_vars[i].token, value);
14078 void InitSoundSettings_Static(void)
14080 // always start with reliable default values from static default config
14081 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14084 static void LoadSoundSettingsFromFilename(char *filename)
14086 SetupFileHash *setup_file_hash;
14088 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14091 // read (and overwrite with) values that may be specified in config file
14092 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14094 freeSetupFileHash(setup_file_hash);
14097 void LoadSoundSettings(void)
14099 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14101 InitSoundSettings_Static();
14103 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14105 // first look for special settings configured in level series config
14106 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14108 if (fileExists(filename_base))
14109 LoadSoundSettingsFromFilename(filename_base);
14112 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14114 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14115 LoadSoundSettingsFromFilename(filename_local);
14118 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14120 char *filename = getEditorSetupFilename();
14121 SetupFileList *setup_file_list, *list;
14122 SetupFileHash *element_hash;
14123 int num_unknown_tokens = 0;
14126 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14129 element_hash = newSetupFileHash();
14131 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14132 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14134 // determined size may be larger than needed (due to unknown elements)
14136 for (list = setup_file_list; list != NULL; list = list->next)
14139 // add space for up to 3 more elements for padding that may be needed
14140 *num_elements += 3;
14142 // free memory for old list of elements, if needed
14143 checked_free(*elements);
14145 // allocate memory for new list of elements
14146 *elements = checked_malloc(*num_elements * sizeof(int));
14149 for (list = setup_file_list; list != NULL; list = list->next)
14151 char *value = getHashEntry(element_hash, list->token);
14153 if (value == NULL) // try to find obsolete token mapping
14155 char *mapped_token = get_mapped_token(list->token);
14157 if (mapped_token != NULL)
14159 value = getHashEntry(element_hash, mapped_token);
14161 free(mapped_token);
14167 (*elements)[(*num_elements)++] = atoi(value);
14171 if (num_unknown_tokens == 0)
14174 Warn("unknown token(s) found in config file:");
14175 Warn("- config file: '%s'", filename);
14177 num_unknown_tokens++;
14180 Warn("- token: '%s'", list->token);
14184 if (num_unknown_tokens > 0)
14187 while (*num_elements % 4) // pad with empty elements, if needed
14188 (*elements)[(*num_elements)++] = EL_EMPTY;
14190 freeSetupFileList(setup_file_list);
14191 freeSetupFileHash(element_hash);
14194 for (i = 0; i < *num_elements; i++)
14195 Debug("editor", "element '%s' [%d]\n",
14196 element_info[(*elements)[i]].token_name, (*elements)[i]);
14200 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14203 SetupFileHash *setup_file_hash = NULL;
14204 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14205 char *filename_music, *filename_prefix, *filename_info;
14211 token_to_value_ptr[] =
14213 { "title_header", &tmp_music_file_info.title_header },
14214 { "artist_header", &tmp_music_file_info.artist_header },
14215 { "album_header", &tmp_music_file_info.album_header },
14216 { "year_header", &tmp_music_file_info.year_header },
14217 { "played_header", &tmp_music_file_info.played_header },
14219 { "title", &tmp_music_file_info.title },
14220 { "artist", &tmp_music_file_info.artist },
14221 { "album", &tmp_music_file_info.album },
14222 { "year", &tmp_music_file_info.year },
14223 { "played", &tmp_music_file_info.played },
14229 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14230 getCustomMusicFilename(basename));
14232 if (filename_music == NULL)
14235 // ---------- try to replace file extension ----------
14237 filename_prefix = getStringCopy(filename_music);
14238 if (strrchr(filename_prefix, '.') != NULL)
14239 *strrchr(filename_prefix, '.') = '\0';
14240 filename_info = getStringCat2(filename_prefix, ".txt");
14242 if (fileExists(filename_info))
14243 setup_file_hash = loadSetupFileHash(filename_info);
14245 free(filename_prefix);
14246 free(filename_info);
14248 if (setup_file_hash == NULL)
14250 // ---------- try to add file extension ----------
14252 filename_prefix = getStringCopy(filename_music);
14253 filename_info = getStringCat2(filename_prefix, ".txt");
14255 if (fileExists(filename_info))
14256 setup_file_hash = loadSetupFileHash(filename_info);
14258 free(filename_prefix);
14259 free(filename_info);
14262 if (setup_file_hash == NULL)
14265 // ---------- music file info found ----------
14267 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14269 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14271 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14273 *token_to_value_ptr[i].value_ptr =
14274 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14277 tmp_music_file_info.basename = getStringCopy(basename);
14278 tmp_music_file_info.music = music;
14279 tmp_music_file_info.is_sound = is_sound;
14281 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14282 *new_music_file_info = tmp_music_file_info;
14284 return new_music_file_info;
14287 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14289 return get_music_file_info_ext(basename, music, FALSE);
14292 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14294 return get_music_file_info_ext(basename, sound, TRUE);
14297 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14298 char *basename, boolean is_sound)
14300 for (; list != NULL; list = list->next)
14301 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14307 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14309 return music_info_listed_ext(list, basename, FALSE);
14312 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14314 return music_info_listed_ext(list, basename, TRUE);
14317 void LoadMusicInfo(void)
14319 int num_music_noconf = getMusicListSize_NoConf();
14320 int num_music = getMusicListSize();
14321 int num_sounds = getSoundListSize();
14322 struct FileInfo *music, *sound;
14323 struct MusicFileInfo *next, **new;
14327 while (music_file_info != NULL)
14329 next = music_file_info->next;
14331 checked_free(music_file_info->basename);
14333 checked_free(music_file_info->title_header);
14334 checked_free(music_file_info->artist_header);
14335 checked_free(music_file_info->album_header);
14336 checked_free(music_file_info->year_header);
14337 checked_free(music_file_info->played_header);
14339 checked_free(music_file_info->title);
14340 checked_free(music_file_info->artist);
14341 checked_free(music_file_info->album);
14342 checked_free(music_file_info->year);
14343 checked_free(music_file_info->played);
14345 free(music_file_info);
14347 music_file_info = next;
14350 new = &music_file_info;
14352 // get (configured or unconfigured) music file info for all levels
14353 for (i = leveldir_current->first_level;
14354 i <= leveldir_current->last_level; i++)
14358 if (levelset.music[i] != MUS_UNDEFINED)
14360 // get music file info for configured level music
14361 music_nr = levelset.music[i];
14363 else if (num_music_noconf > 0)
14365 // get music file info for unconfigured level music
14366 int level_pos = i - leveldir_current->first_level;
14368 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14375 char *basename = getMusicInfoEntryFilename(music_nr);
14377 if (basename == NULL)
14380 if (!music_info_listed(music_file_info, basename))
14382 *new = get_music_file_info(basename, music_nr);
14385 new = &(*new)->next;
14389 // get music file info for all remaining configured music files
14390 for (i = 0; i < num_music; i++)
14392 music = getMusicListEntry(i);
14394 if (music->filename == NULL)
14397 if (strEqual(music->filename, UNDEFINED_FILENAME))
14400 // a configured file may be not recognized as music
14401 if (!FileIsMusic(music->filename))
14404 if (!music_info_listed(music_file_info, music->filename))
14406 *new = get_music_file_info(music->filename, i);
14409 new = &(*new)->next;
14413 // get sound file info for all configured sound files
14414 for (i = 0; i < num_sounds; i++)
14416 sound = getSoundListEntry(i);
14418 if (sound->filename == NULL)
14421 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14424 // a configured file may be not recognized as sound
14425 if (!FileIsSound(sound->filename))
14428 if (!sound_info_listed(music_file_info, sound->filename))
14430 *new = get_sound_file_info(sound->filename, i);
14432 new = &(*new)->next;
14436 // add pointers to previous list nodes
14438 struct MusicFileInfo *node = music_file_info;
14440 while (node != NULL)
14443 node->next->prev = node;
14449 static void add_helpanim_entry(int element, int action, int direction,
14450 int delay, int *num_list_entries)
14452 struct HelpAnimInfo *new_list_entry;
14453 (*num_list_entries)++;
14456 checked_realloc(helpanim_info,
14457 *num_list_entries * sizeof(struct HelpAnimInfo));
14458 new_list_entry = &helpanim_info[*num_list_entries - 1];
14460 new_list_entry->element = element;
14461 new_list_entry->action = action;
14462 new_list_entry->direction = direction;
14463 new_list_entry->delay = delay;
14466 static void print_unknown_token(char *filename, char *token, int token_nr)
14471 Warn("unknown token(s) found in config file:");
14472 Warn("- config file: '%s'", filename);
14475 Warn("- token: '%s'", token);
14478 static void print_unknown_token_end(int token_nr)
14484 void LoadHelpAnimInfo(void)
14486 char *filename = getHelpAnimFilename();
14487 SetupFileList *setup_file_list = NULL, *list;
14488 SetupFileHash *element_hash, *action_hash, *direction_hash;
14489 int num_list_entries = 0;
14490 int num_unknown_tokens = 0;
14493 if (fileExists(filename))
14494 setup_file_list = loadSetupFileList(filename);
14496 if (setup_file_list == NULL)
14498 // use reliable default values from static configuration
14499 SetupFileList *insert_ptr;
14501 insert_ptr = setup_file_list =
14502 newSetupFileList(helpanim_config[0].token,
14503 helpanim_config[0].value);
14505 for (i = 1; helpanim_config[i].token; i++)
14506 insert_ptr = addListEntry(insert_ptr,
14507 helpanim_config[i].token,
14508 helpanim_config[i].value);
14511 element_hash = newSetupFileHash();
14512 action_hash = newSetupFileHash();
14513 direction_hash = newSetupFileHash();
14515 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14516 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14518 for (i = 0; i < NUM_ACTIONS; i++)
14519 setHashEntry(action_hash, element_action_info[i].suffix,
14520 i_to_a(element_action_info[i].value));
14522 // do not store direction index (bit) here, but direction value!
14523 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14524 setHashEntry(direction_hash, element_direction_info[i].suffix,
14525 i_to_a(1 << element_direction_info[i].value));
14527 for (list = setup_file_list; list != NULL; list = list->next)
14529 char *element_token, *action_token, *direction_token;
14530 char *element_value, *action_value, *direction_value;
14531 int delay = atoi(list->value);
14533 if (strEqual(list->token, "end"))
14535 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14540 /* first try to break element into element/action/direction parts;
14541 if this does not work, also accept combined "element[.act][.dir]"
14542 elements (like "dynamite.active"), which are unique elements */
14544 if (strchr(list->token, '.') == NULL) // token contains no '.'
14546 element_value = getHashEntry(element_hash, list->token);
14547 if (element_value != NULL) // element found
14548 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14549 &num_list_entries);
14552 // no further suffixes found -- this is not an element
14553 print_unknown_token(filename, list->token, num_unknown_tokens++);
14559 // token has format "<prefix>.<something>"
14561 action_token = strchr(list->token, '.'); // suffix may be action ...
14562 direction_token = action_token; // ... or direction
14564 element_token = getStringCopy(list->token);
14565 *strchr(element_token, '.') = '\0';
14567 element_value = getHashEntry(element_hash, element_token);
14569 if (element_value == NULL) // this is no element
14571 element_value = getHashEntry(element_hash, list->token);
14572 if (element_value != NULL) // combined element found
14573 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14574 &num_list_entries);
14576 print_unknown_token(filename, list->token, num_unknown_tokens++);
14578 free(element_token);
14583 action_value = getHashEntry(action_hash, action_token);
14585 if (action_value != NULL) // action found
14587 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14588 &num_list_entries);
14590 free(element_token);
14595 direction_value = getHashEntry(direction_hash, direction_token);
14597 if (direction_value != NULL) // direction found
14599 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14600 &num_list_entries);
14602 free(element_token);
14607 if (strchr(action_token + 1, '.') == NULL)
14609 // no further suffixes found -- this is not an action nor direction
14611 element_value = getHashEntry(element_hash, list->token);
14612 if (element_value != NULL) // combined element found
14613 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14614 &num_list_entries);
14616 print_unknown_token(filename, list->token, num_unknown_tokens++);
14618 free(element_token);
14623 // token has format "<prefix>.<suffix>.<something>"
14625 direction_token = strchr(action_token + 1, '.');
14627 action_token = getStringCopy(action_token);
14628 *strchr(action_token + 1, '.') = '\0';
14630 action_value = getHashEntry(action_hash, action_token);
14632 if (action_value == NULL) // this is no action
14634 element_value = getHashEntry(element_hash, list->token);
14635 if (element_value != NULL) // combined element found
14636 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14637 &num_list_entries);
14639 print_unknown_token(filename, list->token, num_unknown_tokens++);
14641 free(element_token);
14642 free(action_token);
14647 direction_value = getHashEntry(direction_hash, direction_token);
14649 if (direction_value != NULL) // direction found
14651 add_helpanim_entry(atoi(element_value), atoi(action_value),
14652 atoi(direction_value), delay, &num_list_entries);
14654 free(element_token);
14655 free(action_token);
14660 // this is no direction
14662 element_value = getHashEntry(element_hash, list->token);
14663 if (element_value != NULL) // combined element found
14664 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14665 &num_list_entries);
14667 print_unknown_token(filename, list->token, num_unknown_tokens++);
14669 free(element_token);
14670 free(action_token);
14673 print_unknown_token_end(num_unknown_tokens);
14675 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14676 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14678 freeSetupFileList(setup_file_list);
14679 freeSetupFileHash(element_hash);
14680 freeSetupFileHash(action_hash);
14681 freeSetupFileHash(direction_hash);
14684 for (i = 0; i < num_list_entries; i++)
14685 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14686 EL_NAME(helpanim_info[i].element),
14687 helpanim_info[i].element,
14688 helpanim_info[i].action,
14689 helpanim_info[i].direction,
14690 helpanim_info[i].delay);
14694 void LoadHelpTextInfo(void)
14696 char *filename = getHelpTextFilename();
14699 if (helptext_info != NULL)
14701 freeSetupFileHash(helptext_info);
14702 helptext_info = NULL;
14705 if (fileExists(filename))
14706 helptext_info = loadSetupFileHash(filename);
14708 if (helptext_info == NULL)
14710 // use reliable default values from static configuration
14711 helptext_info = newSetupFileHash();
14713 for (i = 0; helptext_config[i].token; i++)
14714 setHashEntry(helptext_info,
14715 helptext_config[i].token,
14716 helptext_config[i].value);
14720 BEGIN_HASH_ITERATION(helptext_info, itr)
14722 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14723 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14725 END_HASH_ITERATION(hash, itr)
14730 // ----------------------------------------------------------------------------
14732 // ----------------------------------------------------------------------------
14734 #define MAX_NUM_CONVERT_LEVELS 1000
14736 void ConvertLevels(void)
14738 static LevelDirTree *convert_leveldir = NULL;
14739 static int convert_level_nr = -1;
14740 static int num_levels_handled = 0;
14741 static int num_levels_converted = 0;
14742 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14745 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14746 global.convert_leveldir);
14748 if (convert_leveldir == NULL)
14749 Fail("no such level identifier: '%s'", global.convert_leveldir);
14751 leveldir_current = convert_leveldir;
14753 if (global.convert_level_nr != -1)
14755 convert_leveldir->first_level = global.convert_level_nr;
14756 convert_leveldir->last_level = global.convert_level_nr;
14759 convert_level_nr = convert_leveldir->first_level;
14761 PrintLine("=", 79);
14762 Print("Converting levels\n");
14763 PrintLine("-", 79);
14764 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14765 Print("Level series name: '%s'\n", convert_leveldir->name);
14766 Print("Level series author: '%s'\n", convert_leveldir->author);
14767 Print("Number of levels: %d\n", convert_leveldir->levels);
14768 PrintLine("=", 79);
14771 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14772 levels_failed[i] = FALSE;
14774 while (convert_level_nr <= convert_leveldir->last_level)
14776 char *level_filename;
14779 level_nr = convert_level_nr++;
14781 Print("Level %03d: ", level_nr);
14783 LoadLevel(level_nr);
14784 if (level.no_level_file || level.no_valid_file)
14786 Print("(no level)\n");
14790 Print("converting level ... ");
14793 // special case: conversion of some EMC levels as requested by ACME
14794 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14797 level_filename = getDefaultLevelFilename(level_nr);
14798 new_level = !fileExists(level_filename);
14802 SaveLevel(level_nr);
14804 num_levels_converted++;
14806 Print("converted.\n");
14810 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14811 levels_failed[level_nr] = TRUE;
14813 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14816 num_levels_handled++;
14820 PrintLine("=", 79);
14821 Print("Number of levels handled: %d\n", num_levels_handled);
14822 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14823 (num_levels_handled ?
14824 num_levels_converted * 100 / num_levels_handled : 0));
14825 PrintLine("-", 79);
14826 Print("Summary (for automatic parsing by scripts):\n");
14827 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14828 convert_leveldir->identifier, num_levels_converted,
14829 num_levels_handled,
14830 (num_levels_handled ?
14831 num_levels_converted * 100 / num_levels_handled : 0));
14833 if (num_levels_handled != num_levels_converted)
14835 Print(", FAILED:");
14836 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14837 if (levels_failed[i])
14842 PrintLine("=", 79);
14844 CloseAllAndExit(0);
14848 // ----------------------------------------------------------------------------
14849 // create and save images for use in level sketches (raw BMP format)
14850 // ----------------------------------------------------------------------------
14852 void CreateLevelSketchImages(void)
14858 InitElementPropertiesGfxElement();
14860 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14861 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14863 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14865 int element = getMappedElement(i);
14866 char basename1[16];
14867 char basename2[16];
14871 sprintf(basename1, "%04d.bmp", i);
14872 sprintf(basename2, "%04ds.bmp", i);
14874 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14875 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14877 DrawSizedElement(0, 0, element, TILESIZE);
14878 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14880 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14881 Fail("cannot save level sketch image file '%s'", filename1);
14883 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14884 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14886 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14887 Fail("cannot save level sketch image file '%s'", filename2);
14892 // create corresponding SQL statements (for normal and small images)
14895 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14896 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14899 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14900 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14902 // optional: create content for forum level sketch demonstration post
14904 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14907 FreeBitmap(bitmap1);
14908 FreeBitmap(bitmap2);
14911 fprintf(stderr, "\n");
14913 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14915 CloseAllAndExit(0);
14919 // ----------------------------------------------------------------------------
14920 // create and save images for element collecting animations (raw BMP format)
14921 // ----------------------------------------------------------------------------
14923 static boolean createCollectImage(int element)
14925 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14928 void CreateCollectElementImages(void)
14932 int anim_frames = num_steps - 1;
14933 int tile_size = TILESIZE;
14934 int anim_width = tile_size * anim_frames;
14935 int anim_height = tile_size;
14936 int num_collect_images = 0;
14937 int pos_collect_images = 0;
14939 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14940 if (createCollectImage(i))
14941 num_collect_images++;
14943 Info("Creating %d element collecting animation images ...",
14944 num_collect_images);
14946 int dst_width = anim_width * 2;
14947 int dst_height = anim_height * num_collect_images / 2;
14948 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14949 char *basename_bmp = "RocksCollect.bmp";
14950 char *basename_png = "RocksCollect.png";
14951 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14952 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14953 int len_filename_bmp = strlen(filename_bmp);
14954 int len_filename_png = strlen(filename_png);
14955 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14956 char cmd_convert[max_command_len];
14958 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14962 // force using RGBA surface for destination bitmap
14963 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14964 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14966 dst_bitmap->surface =
14967 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14969 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14971 if (!createCollectImage(i))
14974 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14975 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14976 int graphic = el2img(i);
14977 char *token_name = element_info[i].token_name;
14978 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14979 Bitmap *src_bitmap;
14982 Info("- creating collecting image for '%s' ...", token_name);
14984 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14986 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14987 tile_size, tile_size, 0, 0);
14989 // force using RGBA surface for temporary bitmap (using transparent black)
14990 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14991 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14993 tmp_bitmap->surface =
14994 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14996 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14998 for (j = 0; j < anim_frames; j++)
15000 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15001 int frame_size = frame_size_final * num_steps;
15002 int offset = (tile_size - frame_size_final) / 2;
15003 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15005 while (frame_size > frame_size_final)
15009 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15011 FreeBitmap(frame_bitmap);
15013 frame_bitmap = half_bitmap;
15016 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15017 frame_size_final, frame_size_final,
15018 dst_x + j * tile_size + offset, dst_y + offset);
15020 FreeBitmap(frame_bitmap);
15023 tmp_bitmap->surface_masked = NULL;
15025 FreeBitmap(tmp_bitmap);
15027 pos_collect_images++;
15030 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15031 Fail("cannot save element collecting image file '%s'", filename_bmp);
15033 FreeBitmap(dst_bitmap);
15035 Info("Converting image file from BMP to PNG ...");
15037 if (system(cmd_convert) != 0)
15038 Fail("converting image file failed");
15040 unlink(filename_bmp);
15044 CloseAllAndExit(0);
15048 // ----------------------------------------------------------------------------
15049 // create and save images for custom and group elements (raw BMP format)
15050 // ----------------------------------------------------------------------------
15052 void CreateCustomElementImages(char *directory)
15054 char *src_basename = "RocksCE-template.ilbm";
15055 char *dst_basename = "RocksCE.bmp";
15056 char *src_filename = getPath2(directory, src_basename);
15057 char *dst_filename = getPath2(directory, dst_basename);
15058 Bitmap *src_bitmap;
15060 int yoffset_ce = 0;
15061 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15064 InitVideoDefaults();
15066 ReCreateBitmap(&backbuffer, video.width, video.height);
15068 src_bitmap = LoadImage(src_filename);
15070 bitmap = CreateBitmap(TILEX * 16 * 2,
15071 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15074 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15081 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15082 TILEX * x, TILEY * y + yoffset_ce);
15084 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15086 TILEX * x + TILEX * 16,
15087 TILEY * y + yoffset_ce);
15089 for (j = 2; j >= 0; j--)
15093 BlitBitmap(src_bitmap, bitmap,
15094 TILEX + c * 7, 0, 6, 10,
15095 TILEX * x + 6 + j * 7,
15096 TILEY * y + 11 + yoffset_ce);
15098 BlitBitmap(src_bitmap, bitmap,
15099 TILEX + c * 8, TILEY, 6, 10,
15100 TILEX * 16 + TILEX * x + 6 + j * 8,
15101 TILEY * y + 10 + yoffset_ce);
15107 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15114 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15115 TILEX * x, TILEY * y + yoffset_ge);
15117 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15119 TILEX * x + TILEX * 16,
15120 TILEY * y + yoffset_ge);
15122 for (j = 1; j >= 0; j--)
15126 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15127 TILEX * x + 6 + j * 10,
15128 TILEY * y + 11 + yoffset_ge);
15130 BlitBitmap(src_bitmap, bitmap,
15131 TILEX + c * 8, TILEY + 12, 6, 10,
15132 TILEX * 16 + TILEX * x + 10 + j * 8,
15133 TILEY * y + 10 + yoffset_ge);
15139 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15140 Fail("cannot save CE graphics file '%s'", dst_filename);
15142 FreeBitmap(bitmap);
15144 CloseAllAndExit(0);