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"
10671 TYPE_SWITCH_3_STATES,
10672 &setup.allow_skipping_levels, "allow_skipping_levels"
10676 &setup.increment_levels, "increment_levels"
10680 &setup.auto_play_next_level, "auto_play_next_level"
10684 &setup.count_score_after_game, "count_score_after_game"
10688 &setup.show_scores_after_game, "show_scores_after_game"
10692 &setup.time_limit, "time_limit"
10696 &setup.fullscreen, "fullscreen"
10700 &setup.window_scaling_percent, "window_scaling_percent"
10704 &setup.window_scaling_quality, "window_scaling_quality"
10708 &setup.screen_rendering_mode, "screen_rendering_mode"
10712 &setup.vsync_mode, "vsync_mode"
10716 &setup.ask_on_escape, "ask_on_escape"
10720 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10724 &setup.ask_on_game_over, "ask_on_game_over"
10728 &setup.ask_on_quit_game, "ask_on_quit_game"
10732 &setup.ask_on_quit_program, "ask_on_quit_program"
10736 &setup.quick_switch, "quick_player_switch"
10740 &setup.input_on_focus, "input_on_focus"
10744 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10748 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10752 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10756 &setup.game_speed_extended, "game_speed_extended"
10760 &setup.game_frame_delay, "game_frame_delay"
10764 &setup.default_game_engine_type, "default_game_engine_type"
10768 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10772 &setup.bd_skip_hatching, "bd_skip_hatching"
10776 &setup.bd_scroll_delay, "bd_scroll_delay"
10780 &setup.bd_show_invisible_outbox, "bd_show_invisible_outbox"
10783 TYPE_SWITCH_3_STATES,
10784 &setup.bd_smooth_movements, "bd_smooth_movements"
10787 TYPE_SWITCH_3_STATES,
10788 &setup.bd_pushing_graphics, "bd_pushing_graphics"
10791 TYPE_SWITCH_3_STATES,
10792 &setup.bd_up_down_graphics, "bd_up_down_graphics"
10795 TYPE_SWITCH_3_STATES,
10796 &setup.bd_skip_falling_sounds, "bd_skip_falling_sounds"
10800 &setup.bd_palette_c64, "bd_palette_c64"
10804 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10808 &setup.bd_palette_atari, "bd_palette_atari"
10812 &setup.bd_default_color_type, "bd_default_color_type"
10816 &setup.bd_random_colors, "bd_random_colors"
10820 &setup.sp_show_border_elements, "sp_show_border_elements"
10824 &setup.small_game_graphics, "small_game_graphics"
10828 &setup.show_load_save_buttons, "show_load_save_buttons"
10832 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10836 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10840 &setup.graphics_set, "graphics_set"
10844 &setup.sounds_set, "sounds_set"
10848 &setup.music_set, "music_set"
10851 TYPE_SWITCH_3_STATES,
10852 &setup.override_level_graphics, "override_level_graphics"
10855 TYPE_SWITCH_3_STATES,
10856 &setup.override_level_sounds, "override_level_sounds"
10859 TYPE_SWITCH_3_STATES,
10860 &setup.override_level_music, "override_level_music"
10864 &setup.volume_simple, "volume_simple"
10868 &setup.volume_loops, "volume_loops"
10872 &setup.volume_music, "volume_music"
10876 &setup.audio_sample_rate_44100, "audio_sample_rate_44100"
10880 &setup.network_mode, "network_mode"
10884 &setup.network_player_nr, "network_player"
10888 &setup.network_server_hostname, "network_server_hostname"
10892 &setup.touch.control_type, "touch.control_type"
10896 &setup.touch.move_distance, "touch.move_distance"
10900 &setup.touch.drop_distance, "touch.drop_distance"
10904 &setup.touch.transparency, "touch.transparency"
10908 &setup.touch.draw_outlined, "touch.draw_outlined"
10912 &setup.touch.draw_pressed, "touch.draw_pressed"
10916 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10920 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10924 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10928 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10932 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10936 static struct TokenInfo auto_setup_tokens[] =
10940 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10944 static struct TokenInfo server_setup_tokens[] =
10948 &setup.player_uuid, "player_uuid"
10952 &setup.player_version, "player_version"
10956 &setup.use_api_server, TEST_PREFIX "use_api_server"
10960 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10964 &setup.api_server_password, TEST_PREFIX "api_server_password"
10968 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10972 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10976 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10980 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10984 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10988 static struct TokenInfo editor_setup_tokens[] =
10992 &setup.editor.el_classic, "editor.el_classic"
10996 &setup.editor.el_custom, "editor.el_custom"
11000 &setup.editor.el_user_defined, "editor.el_user_defined"
11004 &setup.editor.el_dynamic, "editor.el_dynamic"
11008 &setup.editor.el_headlines, "editor.el_headlines"
11012 &setup.editor.show_element_token, "editor.show_element_token"
11016 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
11020 static struct TokenInfo editor_cascade_setup_tokens[] =
11024 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
11028 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
11032 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
11036 &setup.editor_cascade.el_em, "editor.cascade.el_em"
11040 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
11044 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
11048 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
11052 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
11056 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11060 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11064 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11068 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11072 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11076 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11080 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11084 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11088 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11092 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11096 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11100 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11104 static struct TokenInfo shortcut_setup_tokens[] =
11108 &setup.shortcut.save_game, "shortcut.save_game"
11112 &setup.shortcut.load_game, "shortcut.load_game"
11116 &setup.shortcut.restart_game, "shortcut.restart_game"
11120 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11124 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11128 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11132 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11136 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11140 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11144 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11148 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11152 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11156 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11160 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11164 &setup.shortcut.tape_record, "shortcut.tape_record"
11168 &setup.shortcut.tape_play, "shortcut.tape_play"
11172 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11176 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11180 &setup.shortcut.sound_music, "shortcut.sound_music"
11184 &setup.shortcut.snap_left, "shortcut.snap_left"
11188 &setup.shortcut.snap_right, "shortcut.snap_right"
11192 &setup.shortcut.snap_up, "shortcut.snap_up"
11196 &setup.shortcut.snap_down, "shortcut.snap_down"
11200 &setup.shortcut.speed_fast, "shortcut.speed_fast"
11204 &setup.shortcut.speed_slow, "shortcut.speed_slow"
11208 static struct SetupInputInfo setup_input;
11209 static struct TokenInfo player_setup_tokens[] =
11213 &setup_input.use_joystick, ".use_joystick"
11217 &setup_input.joy.device_name, ".joy.device_name"
11221 &setup_input.joy.xleft, ".joy.xleft"
11225 &setup_input.joy.xmiddle, ".joy.xmiddle"
11229 &setup_input.joy.xright, ".joy.xright"
11233 &setup_input.joy.yupper, ".joy.yupper"
11237 &setup_input.joy.ymiddle, ".joy.ymiddle"
11241 &setup_input.joy.ylower, ".joy.ylower"
11245 &setup_input.joy.snap, ".joy.snap_field"
11249 &setup_input.joy.drop, ".joy.place_bomb"
11253 &setup_input.key.left, ".key.move_left"
11257 &setup_input.key.right, ".key.move_right"
11261 &setup_input.key.up, ".key.move_up"
11265 &setup_input.key.down, ".key.move_down"
11269 &setup_input.key.snap, ".key.snap_field"
11273 &setup_input.key.drop, ".key.place_bomb"
11277 static struct TokenInfo system_setup_tokens[] =
11281 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11285 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11289 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11293 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11297 static struct TokenInfo internal_setup_tokens[] =
11301 &setup.internal.program_title, "program_title"
11305 &setup.internal.program_version, "program_version"
11309 &setup.internal.program_author, "program_author"
11313 &setup.internal.program_email, "program_email"
11317 &setup.internal.program_website, "program_website"
11321 &setup.internal.program_copyright, "program_copyright"
11325 &setup.internal.program_company, "program_company"
11329 &setup.internal.program_icon_file, "program_icon_file"
11333 &setup.internal.default_graphics_set, "default_graphics_set"
11337 &setup.internal.default_sounds_set, "default_sounds_set"
11341 &setup.internal.default_music_set, "default_music_set"
11345 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11349 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11353 &setup.internal.fallback_music_file, "fallback_music_file"
11357 &setup.internal.default_level_series, "default_level_series"
11361 &setup.internal.default_window_width, "default_window_width"
11365 &setup.internal.default_window_height, "default_window_height"
11369 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11373 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11377 &setup.internal.create_user_levelset, "create_user_levelset"
11381 &setup.internal.info_screens_from_main, "info_screens_from_main"
11385 &setup.internal.menu_game, "menu_game"
11389 &setup.internal.menu_engines, "menu_engines"
11393 &setup.internal.menu_editor, "menu_editor"
11397 &setup.internal.menu_graphics, "menu_graphics"
11401 &setup.internal.menu_sound, "menu_sound"
11405 &setup.internal.menu_artwork, "menu_artwork"
11409 &setup.internal.menu_input, "menu_input"
11413 &setup.internal.menu_touch, "menu_touch"
11417 &setup.internal.menu_shortcuts, "menu_shortcuts"
11421 &setup.internal.menu_exit, "menu_exit"
11425 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11429 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11433 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11437 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11441 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11445 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11449 &setup.internal.menu_shortcuts_speed, "menu_shortcuts_speed"
11453 &setup.internal.info_title, "info_title"
11457 &setup.internal.info_elements, "info_elements"
11461 &setup.internal.info_music, "info_music"
11465 &setup.internal.info_credits, "info_credits"
11469 &setup.internal.info_program, "info_program"
11473 &setup.internal.info_version, "info_version"
11477 &setup.internal.info_levelset, "info_levelset"
11481 &setup.internal.info_exit, "info_exit"
11485 static struct TokenInfo debug_setup_tokens[] =
11489 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11493 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11497 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11501 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11505 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11509 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11513 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11517 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11521 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11525 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11529 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11533 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11537 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11541 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11545 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11549 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11553 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11557 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11561 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11565 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11569 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11572 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11576 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11579 TYPE_SWITCH_3_STATES,
11580 &setup.debug.xsn_mode, "debug.xsn_mode"
11584 &setup.debug.xsn_percent, "debug.xsn_percent"
11588 static struct TokenInfo options_setup_tokens[] =
11592 &setup.options.verbose, "options.verbose"
11596 &setup.options.debug, "options.debug"
11600 &setup.options.debug_mode, "options.debug_mode"
11604 static void setSetupInfoToDefaults(struct SetupInfo *si)
11608 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11610 si->multiple_users = TRUE;
11613 si->sound_loops = TRUE;
11614 si->sound_music = TRUE;
11615 si->sound_simple = TRUE;
11617 si->global_animations = TRUE;
11618 si->scroll_delay = TRUE;
11619 si->forced_scroll_delay = FALSE;
11620 si->scroll_delay_value = STD_SCROLL_DELAY;
11621 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11622 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11623 si->fade_screens = TRUE;
11624 si->autorecord = TRUE;
11625 si->autorecord_after_replay = TRUE;
11626 si->auto_pause_on_start = FALSE;
11627 si->show_titlescreen = TRUE;
11628 si->quick_doors = FALSE;
11629 si->team_mode = FALSE;
11630 si->handicap = TRUE;
11631 si->skip_levels = TRUE;
11632 si->allow_skipping_levels = MODE_ASK;
11633 si->increment_levels = TRUE;
11634 si->auto_play_next_level = TRUE;
11635 si->count_score_after_game = TRUE;
11636 si->show_scores_after_game = TRUE;
11637 si->time_limit = TRUE;
11638 si->fullscreen = FALSE;
11639 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11640 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11641 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11642 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11643 si->ask_on_escape = TRUE;
11644 si->ask_on_escape_editor = TRUE;
11645 si->ask_on_game_over = TRUE;
11646 si->ask_on_quit_game = TRUE;
11647 si->ask_on_quit_program = TRUE;
11648 si->quick_switch = FALSE;
11649 si->input_on_focus = FALSE;
11650 si->prefer_aga_graphics = TRUE;
11651 si->prefer_lowpass_sounds = FALSE;
11652 si->prefer_extra_panel_items = TRUE;
11653 si->game_speed_extended = FALSE;
11654 si->game_frame_delay = GAME_FRAME_DELAY;
11655 si->default_game_engine_type = GAME_ENGINE_TYPE_RND;
11656 si->bd_skip_uncovering = FALSE;
11657 si->bd_skip_hatching = FALSE;
11658 si->bd_scroll_delay = TRUE;
11659 si->bd_show_invisible_outbox = FALSE;
11660 si->bd_smooth_movements = MODE_AUTO;
11661 si->bd_pushing_graphics = MODE_TRUE;
11662 si->bd_up_down_graphics = MODE_TRUE;
11663 si->bd_skip_falling_sounds = MODE_AUTO;
11664 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11665 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11666 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11667 si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11668 si->bd_random_colors = FALSE;
11669 si->sp_show_border_elements = FALSE;
11670 si->small_game_graphics = FALSE;
11671 si->show_load_save_buttons = FALSE;
11672 si->show_undo_redo_buttons = FALSE;
11673 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11675 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11676 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11677 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11679 si->override_level_graphics = MODE_FALSE;
11680 si->override_level_sounds = MODE_FALSE;
11681 si->override_level_music = MODE_FALSE;
11683 si->volume_simple = 100; // percent
11684 si->volume_loops = 100; // percent
11685 si->volume_music = 100; // percent
11686 si->audio_sample_rate_44100 = FALSE;
11688 si->network_mode = FALSE;
11689 si->network_player_nr = 0; // first player
11690 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11692 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11693 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11694 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11695 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11696 si->touch.draw_outlined = TRUE;
11697 si->touch.draw_pressed = TRUE;
11699 for (i = 0; i < 2; i++)
11701 char *default_grid_button[6][2] =
11707 { "111222", " vv " },
11708 { "111222", " vv " }
11710 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11711 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11712 int min_xsize = MIN(6, grid_xsize);
11713 int min_ysize = MIN(6, grid_ysize);
11714 int startx = grid_xsize - min_xsize;
11715 int starty = grid_ysize - min_ysize;
11718 // virtual buttons grid can only be set to defaults if video is initialized
11719 // (this will be repeated if virtual buttons are not loaded from setup file)
11720 if (video.initialized)
11722 si->touch.grid_xsize[i] = grid_xsize;
11723 si->touch.grid_ysize[i] = grid_ysize;
11727 si->touch.grid_xsize[i] = -1;
11728 si->touch.grid_ysize[i] = -1;
11731 for (x = 0; x < MAX_GRID_XSIZE; x++)
11732 for (y = 0; y < MAX_GRID_YSIZE; y++)
11733 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11735 for (x = 0; x < min_xsize; x++)
11736 for (y = 0; y < min_ysize; y++)
11737 si->touch.grid_button[i][x][starty + y] =
11738 default_grid_button[y][0][x];
11740 for (x = 0; x < min_xsize; x++)
11741 for (y = 0; y < min_ysize; y++)
11742 si->touch.grid_button[i][startx + x][starty + y] =
11743 default_grid_button[y][1][x];
11746 si->touch.grid_initialized = video.initialized;
11748 si->touch.overlay_buttons = FALSE;
11750 si->editor.el_boulderdash = TRUE;
11751 si->editor.el_boulderdash_native = TRUE;
11752 si->editor.el_boulderdash_effects = TRUE;
11753 si->editor.el_emerald_mine = TRUE;
11754 si->editor.el_emerald_mine_club = TRUE;
11755 si->editor.el_more = TRUE;
11756 si->editor.el_sokoban = TRUE;
11757 si->editor.el_supaplex = TRUE;
11758 si->editor.el_diamond_caves = TRUE;
11759 si->editor.el_dx_boulderdash = TRUE;
11761 si->editor.el_mirror_magic = TRUE;
11762 si->editor.el_deflektor = TRUE;
11764 si->editor.el_chars = TRUE;
11765 si->editor.el_steel_chars = TRUE;
11767 si->editor.el_classic = TRUE;
11768 si->editor.el_custom = TRUE;
11770 si->editor.el_user_defined = FALSE;
11771 si->editor.el_dynamic = TRUE;
11773 si->editor.el_headlines = TRUE;
11775 si->editor.show_element_token = FALSE;
11777 si->editor.show_read_only_warning = TRUE;
11779 si->editor.use_template_for_new_levels = TRUE;
11781 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11782 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11783 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11784 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11785 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11787 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11788 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11789 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11790 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11791 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11793 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11794 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11795 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11796 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11797 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11798 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11800 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11801 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11802 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11804 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11805 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11806 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11807 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11809 si->shortcut.speed_fast = DEFAULT_KEY_SPEED_FAST;
11810 si->shortcut.speed_slow = DEFAULT_KEY_SPEED_SLOW;
11812 for (i = 0; i < MAX_PLAYERS; i++)
11814 si->input[i].use_joystick = FALSE;
11815 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11816 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11817 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11818 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11819 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11820 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11821 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11822 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11823 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11824 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11825 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11826 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11827 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11828 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11829 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11832 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11833 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11834 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11835 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11837 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11838 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11839 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11840 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11841 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11842 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11843 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11845 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11847 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11848 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11849 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11851 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11852 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11853 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11855 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11856 si->internal.choose_from_top_leveldir = FALSE;
11857 si->internal.show_scaling_in_title = TRUE;
11858 si->internal.create_user_levelset = TRUE;
11859 si->internal.info_screens_from_main = FALSE;
11861 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11862 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11864 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11865 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11866 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11867 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11868 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11869 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11870 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11871 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11872 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11873 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11875 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11876 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11877 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11878 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11879 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11880 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11881 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11882 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11883 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11884 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11886 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11887 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11889 si->debug.show_frames_per_second = FALSE;
11891 si->debug.xsn_mode = MODE_AUTO;
11892 si->debug.xsn_percent = 0;
11894 si->options.verbose = FALSE;
11895 si->options.debug = FALSE;
11896 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11898 #if defined(PLATFORM_ANDROID)
11899 si->fullscreen = TRUE;
11900 si->touch.overlay_buttons = TRUE;
11903 setHideSetupEntry(&setup.debug.xsn_mode);
11906 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11908 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11911 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11913 si->player_uuid = NULL; // (will be set later)
11914 si->player_version = 1; // (will be set later)
11916 si->use_api_server = TRUE;
11917 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11918 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11919 si->ask_for_uploading_tapes = TRUE;
11920 si->ask_for_remaining_tapes = FALSE;
11921 si->provide_uploading_tapes = TRUE;
11922 si->ask_for_using_api_server = TRUE;
11923 si->has_remaining_tapes = FALSE;
11926 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11928 si->editor_cascade.el_bd = TRUE;
11929 si->editor_cascade.el_bd_native = TRUE;
11930 si->editor_cascade.el_bd_effects = FALSE;
11931 si->editor_cascade.el_em = TRUE;
11932 si->editor_cascade.el_emc = TRUE;
11933 si->editor_cascade.el_rnd = TRUE;
11934 si->editor_cascade.el_sb = TRUE;
11935 si->editor_cascade.el_sp = TRUE;
11936 si->editor_cascade.el_dc = TRUE;
11937 si->editor_cascade.el_dx = TRUE;
11939 si->editor_cascade.el_mm = TRUE;
11940 si->editor_cascade.el_df = TRUE;
11942 si->editor_cascade.el_chars = FALSE;
11943 si->editor_cascade.el_steel_chars = FALSE;
11944 si->editor_cascade.el_ce = FALSE;
11945 si->editor_cascade.el_ge = FALSE;
11946 si->editor_cascade.el_es = FALSE;
11947 si->editor_cascade.el_ref = FALSE;
11948 si->editor_cascade.el_user = FALSE;
11949 si->editor_cascade.el_dynamic = FALSE;
11952 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11954 static char *getHideSetupToken(void *setup_value)
11956 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11958 if (setup_value != NULL)
11959 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11961 return hide_setup_token;
11964 void setHideSetupEntry(void *setup_value)
11966 char *hide_setup_token = getHideSetupToken(setup_value);
11968 if (hide_setup_hash == NULL)
11969 hide_setup_hash = newSetupFileHash();
11971 if (setup_value != NULL)
11972 setHashEntry(hide_setup_hash, hide_setup_token, "");
11975 void removeHideSetupEntry(void *setup_value)
11977 char *hide_setup_token = getHideSetupToken(setup_value);
11979 if (setup_value != NULL)
11980 removeHashEntry(hide_setup_hash, hide_setup_token);
11983 boolean hideSetupEntry(void *setup_value)
11985 char *hide_setup_token = getHideSetupToken(setup_value);
11987 return (setup_value != NULL &&
11988 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11991 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11992 struct TokenInfo *token_info,
11993 int token_nr, char *token_text)
11995 char *token_hide_text = getStringCat2(token_text, ".hide");
11996 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11998 // set the value of this setup option in the setup option structure
11999 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12001 // check if this setup option should be hidden in the setup menu
12002 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12003 setHideSetupEntry(token_info[token_nr].value);
12005 free(token_hide_text);
12008 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12009 struct TokenInfo *token_info,
12012 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12013 token_info[token_nr].text);
12016 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12020 if (!setup_file_hash)
12023 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12024 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12026 setup.touch.grid_initialized = TRUE;
12027 for (i = 0; i < 2; i++)
12029 int grid_xsize = setup.touch.grid_xsize[i];
12030 int grid_ysize = setup.touch.grid_ysize[i];
12033 // if virtual buttons are not loaded from setup file, repeat initializing
12034 // virtual buttons grid with default values later when video is initialized
12035 if (grid_xsize == -1 ||
12038 setup.touch.grid_initialized = FALSE;
12043 for (y = 0; y < grid_ysize; y++)
12045 char token_string[MAX_LINE_LEN];
12047 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12049 char *value_string = getHashEntry(setup_file_hash, token_string);
12051 if (value_string == NULL)
12054 for (x = 0; x < grid_xsize; x++)
12056 char c = value_string[x];
12058 setup.touch.grid_button[i][x][y] =
12059 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12064 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12065 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12067 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12068 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12070 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12074 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12076 setup_input = setup.input[pnr];
12077 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12079 char full_token[100];
12081 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12082 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12085 setup.input[pnr] = setup_input;
12088 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12089 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12091 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12092 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12094 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12095 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12097 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12098 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12100 setHideRelatedSetupEntries();
12103 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12107 if (!setup_file_hash)
12110 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12111 setSetupInfo(auto_setup_tokens, i,
12112 getHashEntry(setup_file_hash,
12113 auto_setup_tokens[i].text));
12116 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12120 if (!setup_file_hash)
12123 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12124 setSetupInfo(server_setup_tokens, i,
12125 getHashEntry(setup_file_hash,
12126 server_setup_tokens[i].text));
12129 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12133 if (!setup_file_hash)
12136 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12137 setSetupInfo(editor_cascade_setup_tokens, i,
12138 getHashEntry(setup_file_hash,
12139 editor_cascade_setup_tokens[i].text));
12142 void LoadUserNames(void)
12144 int last_user_nr = user.nr;
12147 if (global.user_names != NULL)
12149 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12150 checked_free(global.user_names[i]);
12152 checked_free(global.user_names);
12155 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12157 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12161 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12163 if (setup_file_hash)
12165 char *player_name = getHashEntry(setup_file_hash, "player_name");
12167 global.user_names[i] = getFixedUserName(player_name);
12169 freeSetupFileHash(setup_file_hash);
12172 if (global.user_names[i] == NULL)
12173 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12176 user.nr = last_user_nr;
12179 void LoadSetupFromFilename(char *filename)
12181 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12183 if (setup_file_hash)
12185 decodeSetupFileHash_Default(setup_file_hash);
12187 freeSetupFileHash(setup_file_hash);
12191 Debug("setup", "using default setup values");
12195 static void LoadSetup_SpecialPostProcessing(void)
12197 char *player_name_new;
12199 // needed to work around problems with fixed length strings
12200 player_name_new = getFixedUserName(setup.player_name);
12201 free(setup.player_name);
12202 setup.player_name = player_name_new;
12204 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12205 if (setup.scroll_delay == FALSE)
12207 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12208 setup.scroll_delay = TRUE; // now always "on"
12211 // make sure that scroll delay value stays inside valid range
12212 setup.scroll_delay_value =
12213 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12216 void LoadSetup_Default(void)
12220 // always start with reliable default values
12221 setSetupInfoToDefaults(&setup);
12223 // try to load setup values from default setup file
12224 filename = getDefaultSetupFilename();
12226 if (fileExists(filename))
12227 LoadSetupFromFilename(filename);
12229 // try to load setup values from platform setup file
12230 filename = getPlatformSetupFilename();
12232 if (fileExists(filename))
12233 LoadSetupFromFilename(filename);
12235 // try to load setup values from user setup file
12236 filename = getSetupFilename();
12238 LoadSetupFromFilename(filename);
12240 LoadSetup_SpecialPostProcessing();
12243 void LoadSetup_AutoSetup(void)
12245 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12246 SetupFileHash *setup_file_hash = NULL;
12248 // always start with reliable default values
12249 setSetupInfoToDefaults_AutoSetup(&setup);
12251 setup_file_hash = loadSetupFileHash(filename);
12253 if (setup_file_hash)
12255 decodeSetupFileHash_AutoSetup(setup_file_hash);
12257 freeSetupFileHash(setup_file_hash);
12263 void LoadSetup_ServerSetup(void)
12265 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12266 SetupFileHash *setup_file_hash = NULL;
12268 // always start with reliable default values
12269 setSetupInfoToDefaults_ServerSetup(&setup);
12271 setup_file_hash = loadSetupFileHash(filename);
12273 if (setup_file_hash)
12275 decodeSetupFileHash_ServerSetup(setup_file_hash);
12277 freeSetupFileHash(setup_file_hash);
12282 if (setup.player_uuid == NULL)
12284 // player UUID does not yet exist in setup file
12285 setup.player_uuid = getStringCopy(getUUID());
12286 setup.player_version = 2;
12288 SaveSetup_ServerSetup();
12292 void LoadSetup_EditorCascade(void)
12294 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12295 SetupFileHash *setup_file_hash = NULL;
12297 // always start with reliable default values
12298 setSetupInfoToDefaults_EditorCascade(&setup);
12300 setup_file_hash = loadSetupFileHash(filename);
12302 if (setup_file_hash)
12304 decodeSetupFileHash_EditorCascade(setup_file_hash);
12306 freeSetupFileHash(setup_file_hash);
12312 void LoadSetup(void)
12314 LoadSetup_Default();
12315 LoadSetup_AutoSetup();
12316 LoadSetup_ServerSetup();
12317 LoadSetup_EditorCascade();
12320 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12321 char *mapping_line)
12323 char mapping_guid[MAX_LINE_LEN];
12324 char *mapping_start, *mapping_end;
12326 // get GUID from game controller mapping line: copy complete line
12327 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12328 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12330 // get GUID from game controller mapping line: cut after GUID part
12331 mapping_start = strchr(mapping_guid, ',');
12332 if (mapping_start != NULL)
12333 *mapping_start = '\0';
12335 // cut newline from game controller mapping line
12336 mapping_end = strchr(mapping_line, '\n');
12337 if (mapping_end != NULL)
12338 *mapping_end = '\0';
12340 // add mapping entry to game controller mappings hash
12341 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12344 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12349 if (!(file = fopen(filename, MODE_READ)))
12351 Warn("cannot read game controller mappings file '%s'", filename);
12356 while (!feof(file))
12358 char line[MAX_LINE_LEN];
12360 if (!fgets(line, MAX_LINE_LEN, file))
12363 addGameControllerMappingToHash(mappings_hash, line);
12369 void SaveSetup_Default(void)
12371 char *filename = getSetupFilename();
12375 InitUserDataDirectory();
12377 if (!(file = fopen(filename, MODE_WRITE)))
12379 Warn("cannot write setup file '%s'", filename);
12384 fprintFileHeader(file, SETUP_FILENAME);
12386 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12388 // just to make things nicer :)
12389 if (global_setup_tokens[i].value == &setup.multiple_users ||
12390 global_setup_tokens[i].value == &setup.sound ||
12391 global_setup_tokens[i].value == &setup.graphics_set ||
12392 global_setup_tokens[i].value == &setup.volume_simple ||
12393 global_setup_tokens[i].value == &setup.network_mode ||
12394 global_setup_tokens[i].value == &setup.touch.control_type ||
12395 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12396 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12397 fprintf(file, "\n");
12399 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12402 for (i = 0; i < 2; i++)
12404 int grid_xsize = setup.touch.grid_xsize[i];
12405 int grid_ysize = setup.touch.grid_ysize[i];
12408 fprintf(file, "\n");
12410 for (y = 0; y < grid_ysize; y++)
12412 char token_string[MAX_LINE_LEN];
12413 char value_string[MAX_LINE_LEN];
12415 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12417 for (x = 0; x < grid_xsize; x++)
12419 char c = setup.touch.grid_button[i][x][y];
12421 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12424 value_string[grid_xsize] = '\0';
12426 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12430 fprintf(file, "\n");
12431 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12432 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12434 fprintf(file, "\n");
12435 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12436 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12438 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12442 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12443 fprintf(file, "\n");
12445 setup_input = setup.input[pnr];
12446 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12447 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12450 fprintf(file, "\n");
12451 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12452 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12454 // (internal setup values not saved to user setup file)
12456 fprintf(file, "\n");
12457 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12458 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12459 setup.debug.xsn_mode != MODE_AUTO)
12460 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12462 fprintf(file, "\n");
12463 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12464 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12468 SetFilePermissions(filename, PERMS_PRIVATE);
12471 void SaveSetup_AutoSetup(void)
12473 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12477 InitUserDataDirectory();
12479 if (!(file = fopen(filename, MODE_WRITE)))
12481 Warn("cannot write auto setup file '%s'", filename);
12488 fprintFileHeader(file, AUTOSETUP_FILENAME);
12490 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12491 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12495 SetFilePermissions(filename, PERMS_PRIVATE);
12500 void SaveSetup_ServerSetup(void)
12502 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12506 InitUserDataDirectory();
12508 if (!(file = fopen(filename, MODE_WRITE)))
12510 Warn("cannot write server setup file '%s'", filename);
12517 fprintFileHeader(file, SERVERSETUP_FILENAME);
12519 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12521 // just to make things nicer :)
12522 if (server_setup_tokens[i].value == &setup.use_api_server)
12523 fprintf(file, "\n");
12525 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12530 SetFilePermissions(filename, PERMS_PRIVATE);
12535 void SaveSetup_EditorCascade(void)
12537 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12541 InitUserDataDirectory();
12543 if (!(file = fopen(filename, MODE_WRITE)))
12545 Warn("cannot write editor cascade state file '%s'", filename);
12552 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12554 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12555 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12559 SetFilePermissions(filename, PERMS_PRIVATE);
12564 void SaveSetup(void)
12566 SaveSetup_Default();
12567 SaveSetup_AutoSetup();
12568 SaveSetup_ServerSetup();
12569 SaveSetup_EditorCascade();
12572 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12577 if (!(file = fopen(filename, MODE_WRITE)))
12579 Warn("cannot write game controller mappings file '%s'", filename);
12584 BEGIN_HASH_ITERATION(mappings_hash, itr)
12586 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12588 END_HASH_ITERATION(mappings_hash, itr)
12593 void SaveSetup_AddGameControllerMapping(char *mapping)
12595 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12596 SetupFileHash *mappings_hash = newSetupFileHash();
12598 InitUserDataDirectory();
12600 // load existing personal game controller mappings
12601 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12603 // add new mapping to personal game controller mappings
12604 addGameControllerMappingToHash(mappings_hash, mapping);
12606 // save updated personal game controller mappings
12607 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12609 freeSetupFileHash(mappings_hash);
12613 void LoadCustomElementDescriptions(void)
12615 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12616 SetupFileHash *setup_file_hash;
12619 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12621 if (element_info[i].custom_description != NULL)
12623 free(element_info[i].custom_description);
12624 element_info[i].custom_description = NULL;
12628 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12631 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12633 char *token = getStringCat2(element_info[i].token_name, ".name");
12634 char *value = getHashEntry(setup_file_hash, token);
12637 element_info[i].custom_description = getStringCopy(value);
12642 freeSetupFileHash(setup_file_hash);
12645 static int getElementFromToken(char *token)
12647 char *value = getHashEntry(element_token_hash, token);
12650 return atoi(value);
12652 Warn("unknown element token '%s'", token);
12654 return EL_UNDEFINED;
12657 void FreeGlobalAnimEventInfo(void)
12659 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12661 if (gaei->event_list == NULL)
12666 for (i = 0; i < gaei->num_event_lists; i++)
12668 checked_free(gaei->event_list[i]->event_value);
12669 checked_free(gaei->event_list[i]);
12672 checked_free(gaei->event_list);
12674 gaei->event_list = NULL;
12675 gaei->num_event_lists = 0;
12678 static int AddGlobalAnimEventList(void)
12680 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12681 int list_pos = gaei->num_event_lists++;
12683 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12684 sizeof(struct GlobalAnimEventListInfo *));
12686 gaei->event_list[list_pos] =
12687 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12689 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12691 gaeli->event_value = NULL;
12692 gaeli->num_event_values = 0;
12697 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12699 // do not add empty global animation events
12700 if (event_value == ANIM_EVENT_NONE)
12703 // if list position is undefined, create new list
12704 if (list_pos == ANIM_EVENT_UNDEFINED)
12705 list_pos = AddGlobalAnimEventList();
12707 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12708 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12709 int value_pos = gaeli->num_event_values++;
12711 gaeli->event_value = checked_realloc(gaeli->event_value,
12712 gaeli->num_event_values * sizeof(int *));
12714 gaeli->event_value[value_pos] = event_value;
12719 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12721 if (list_pos == ANIM_EVENT_UNDEFINED)
12722 return ANIM_EVENT_NONE;
12724 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12725 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12727 return gaeli->event_value[value_pos];
12730 int GetGlobalAnimEventValueCount(int list_pos)
12732 if (list_pos == ANIM_EVENT_UNDEFINED)
12735 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12736 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12738 return gaeli->num_event_values;
12741 // This function checks if a string <s> of the format "string1, string2, ..."
12742 // exactly contains a string <s_contained>.
12744 static boolean string_has_parameter(char *s, char *s_contained)
12748 if (s == NULL || s_contained == NULL)
12751 if (strlen(s_contained) > strlen(s))
12754 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12756 char next_char = s[strlen(s_contained)];
12758 // check if next character is delimiter or whitespace
12759 if (next_char == ',' || next_char == '\0' ||
12760 next_char == ' ' || next_char == '\t')
12764 // check if string contains another parameter string after a comma
12765 substring = strchr(s, ',');
12766 if (substring == NULL) // string does not contain a comma
12769 // advance string pointer to next character after the comma
12772 // skip potential whitespaces after the comma
12773 while (*substring == ' ' || *substring == '\t')
12776 return string_has_parameter(substring, s_contained);
12779 static int get_anim_parameter_value_ce(char *s)
12782 char *pattern_1 = "ce_change:custom_";
12783 char *pattern_2 = ".page_";
12784 int pattern_1_len = strlen(pattern_1);
12785 char *matching_char = strstr(s_ptr, pattern_1);
12786 int result = ANIM_EVENT_NONE;
12788 if (matching_char == NULL)
12789 return ANIM_EVENT_NONE;
12791 result = ANIM_EVENT_CE_CHANGE;
12793 s_ptr = matching_char + pattern_1_len;
12795 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12796 if (*s_ptr >= '0' && *s_ptr <= '9')
12798 int gic_ce_nr = (*s_ptr++ - '0');
12800 if (*s_ptr >= '0' && *s_ptr <= '9')
12802 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12804 if (*s_ptr >= '0' && *s_ptr <= '9')
12805 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12808 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12809 return ANIM_EVENT_NONE;
12811 // custom element stored as 0 to 255
12814 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12818 // invalid custom element number specified
12820 return ANIM_EVENT_NONE;
12823 // check for change page number ("page_X" or "page_XX") (optional)
12824 if (strPrefix(s_ptr, pattern_2))
12826 s_ptr += strlen(pattern_2);
12828 if (*s_ptr >= '0' && *s_ptr <= '9')
12830 int gic_page_nr = (*s_ptr++ - '0');
12832 if (*s_ptr >= '0' && *s_ptr <= '9')
12833 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12835 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12836 return ANIM_EVENT_NONE;
12838 // change page stored as 1 to 32 (0 means "all change pages")
12840 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12844 // invalid animation part number specified
12846 return ANIM_EVENT_NONE;
12850 // discard result if next character is neither delimiter nor whitespace
12851 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12852 *s_ptr == ' ' || *s_ptr == '\t'))
12853 return ANIM_EVENT_NONE;
12858 static int get_anim_parameter_value(char *s)
12860 int event_value[] =
12868 char *pattern_1[] =
12876 char *pattern_2 = ".part_";
12877 char *matching_char = NULL;
12879 int pattern_1_len = 0;
12880 int result = ANIM_EVENT_NONE;
12883 result = get_anim_parameter_value_ce(s);
12885 if (result != ANIM_EVENT_NONE)
12888 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12890 matching_char = strstr(s_ptr, pattern_1[i]);
12891 pattern_1_len = strlen(pattern_1[i]);
12892 result = event_value[i];
12894 if (matching_char != NULL)
12898 if (matching_char == NULL)
12899 return ANIM_EVENT_NONE;
12901 s_ptr = matching_char + pattern_1_len;
12903 // check for main animation number ("anim_X" or "anim_XX")
12904 if (*s_ptr >= '0' && *s_ptr <= '9')
12906 int gic_anim_nr = (*s_ptr++ - '0');
12908 if (*s_ptr >= '0' && *s_ptr <= '9')
12909 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12911 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12912 return ANIM_EVENT_NONE;
12914 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12918 // invalid main animation number specified
12920 return ANIM_EVENT_NONE;
12923 // check for animation part number ("part_X" or "part_XX") (optional)
12924 if (strPrefix(s_ptr, pattern_2))
12926 s_ptr += strlen(pattern_2);
12928 if (*s_ptr >= '0' && *s_ptr <= '9')
12930 int gic_part_nr = (*s_ptr++ - '0');
12932 if (*s_ptr >= '0' && *s_ptr <= '9')
12933 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12935 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12936 return ANIM_EVENT_NONE;
12938 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12942 // invalid animation part number specified
12944 return ANIM_EVENT_NONE;
12948 // discard result if next character is neither delimiter nor whitespace
12949 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12950 *s_ptr == ' ' || *s_ptr == '\t'))
12951 return ANIM_EVENT_NONE;
12956 static int get_anim_parameter_values(char *s)
12958 int list_pos = ANIM_EVENT_UNDEFINED;
12959 int event_value = ANIM_EVENT_DEFAULT;
12961 if (string_has_parameter(s, "any"))
12962 event_value |= ANIM_EVENT_ANY;
12964 if (string_has_parameter(s, "click:self") ||
12965 string_has_parameter(s, "click") ||
12966 string_has_parameter(s, "self"))
12967 event_value |= ANIM_EVENT_SELF;
12969 if (string_has_parameter(s, "unclick:any"))
12970 event_value |= ANIM_EVENT_UNCLICK_ANY;
12972 // if animation event found, add it to global animation event list
12973 if (event_value != ANIM_EVENT_NONE)
12974 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12978 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12979 event_value = get_anim_parameter_value(s);
12981 // if animation event found, add it to global animation event list
12982 if (event_value != ANIM_EVENT_NONE)
12983 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12985 // continue with next part of the string, starting with next comma
12986 s = strchr(s + 1, ',');
12992 static int get_anim_action_parameter_value(char *token)
12994 // check most common default case first to massively speed things up
12995 if (strEqual(token, ARG_UNDEFINED))
12996 return ANIM_EVENT_ACTION_NONE;
12998 int result = getImageIDFromToken(token);
13002 char *gfx_token = getStringCat2("gfx.", token);
13004 result = getImageIDFromToken(gfx_token);
13006 checked_free(gfx_token);
13011 Key key = getKeyFromX11KeyName(token);
13013 if (key != KSYM_UNDEFINED)
13014 result = -(int)key;
13021 result = get_hash_from_string(token); // unsigned int => int
13022 result = ABS(result); // may be negative now
13023 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13025 setHashEntry(anim_url_hash, int2str(result, 0), token);
13030 result = ANIM_EVENT_ACTION_NONE;
13035 int get_parameter_value(char *value_raw, char *suffix, int type)
13037 char *value = getStringToLower(value_raw);
13038 int result = 0; // probably a save default value
13040 if (strEqual(suffix, ".direction"))
13042 result = (strEqual(value, "left") ? MV_LEFT :
13043 strEqual(value, "right") ? MV_RIGHT :
13044 strEqual(value, "up") ? MV_UP :
13045 strEqual(value, "down") ? MV_DOWN : MV_NONE);
13047 else if (strEqual(suffix, ".position"))
13049 result = (strEqual(value, "left") ? POS_LEFT :
13050 strEqual(value, "right") ? POS_RIGHT :
13051 strEqual(value, "top") ? POS_TOP :
13052 strEqual(value, "upper") ? POS_UPPER :
13053 strEqual(value, "middle") ? POS_MIDDLE :
13054 strEqual(value, "lower") ? POS_LOWER :
13055 strEqual(value, "bottom") ? POS_BOTTOM :
13056 strEqual(value, "any") ? POS_ANY :
13057 strEqual(value, "ce") ? POS_CE :
13058 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13059 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
13061 else if (strEqual(suffix, ".align"))
13063 result = (strEqual(value, "left") ? ALIGN_LEFT :
13064 strEqual(value, "right") ? ALIGN_RIGHT :
13065 strEqual(value, "center") ? ALIGN_CENTER :
13066 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13068 else if (strEqual(suffix, ".valign"))
13070 result = (strEqual(value, "top") ? VALIGN_TOP :
13071 strEqual(value, "bottom") ? VALIGN_BOTTOM :
13072 strEqual(value, "middle") ? VALIGN_MIDDLE :
13073 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13075 else if (strEqual(suffix, ".anim_mode"))
13077 result = (string_has_parameter(value, "none") ? ANIM_NONE :
13078 string_has_parameter(value, "loop") ? ANIM_LOOP :
13079 string_has_parameter(value, "linear") ? ANIM_LINEAR :
13080 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
13081 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
13082 string_has_parameter(value, "random") ? ANIM_RANDOM :
13083 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13084 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13085 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13086 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13087 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13088 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13089 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13090 string_has_parameter(value, "all") ? ANIM_ALL :
13091 string_has_parameter(value, "tiled") ? ANIM_TILED :
13092 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13095 if (string_has_parameter(value, "once"))
13096 result |= ANIM_ONCE;
13098 if (string_has_parameter(value, "reverse"))
13099 result |= ANIM_REVERSE;
13101 if (string_has_parameter(value, "opaque_player"))
13102 result |= ANIM_OPAQUE_PLAYER;
13104 if (string_has_parameter(value, "static_panel"))
13105 result |= ANIM_STATIC_PANEL;
13107 else if (strEqual(suffix, ".init_event") ||
13108 strEqual(suffix, ".anim_event"))
13110 result = get_anim_parameter_values(value);
13112 else if (strEqual(suffix, ".init_delay_action") ||
13113 strEqual(suffix, ".anim_delay_action") ||
13114 strEqual(suffix, ".post_delay_action") ||
13115 strEqual(suffix, ".init_event_action") ||
13116 strEqual(suffix, ".anim_event_action"))
13118 result = get_anim_action_parameter_value(value_raw);
13120 else if (strEqual(suffix, ".class"))
13122 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13123 get_hash_from_string(value));
13125 else if (strEqual(suffix, ".style"))
13127 result = STYLE_DEFAULT;
13129 if (string_has_parameter(value, "accurate_borders"))
13130 result |= STYLE_ACCURATE_BORDERS;
13132 if (string_has_parameter(value, "inner_corners"))
13133 result |= STYLE_INNER_CORNERS;
13135 if (string_has_parameter(value, "reverse"))
13136 result |= STYLE_REVERSE;
13138 if (string_has_parameter(value, "leftmost_position"))
13139 result |= STYLE_LEFTMOST_POSITION;
13141 if (string_has_parameter(value, "block_clicks"))
13142 result |= STYLE_BLOCK;
13144 if (string_has_parameter(value, "passthrough_clicks"))
13145 result |= STYLE_PASSTHROUGH;
13147 if (string_has_parameter(value, "multiple_actions"))
13148 result |= STYLE_MULTIPLE_ACTIONS;
13150 if (string_has_parameter(value, "consume_ce_event"))
13151 result |= STYLE_CONSUME_CE_EVENT;
13153 else if (strEqual(suffix, ".fade_mode"))
13155 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13156 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13157 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13158 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13159 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13160 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13161 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13162 FADE_MODE_DEFAULT);
13164 else if (strEqual(suffix, ".auto_delay_unit"))
13166 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13167 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13168 AUTO_DELAY_UNIT_DEFAULT);
13170 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13172 result = gfx.get_font_from_token_function(value);
13174 else // generic parameter of type integer or boolean
13176 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13177 type == TYPE_INTEGER ? get_integer_from_string(value) :
13178 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13179 ARG_UNDEFINED_VALUE);
13187 static int get_token_parameter_value(char *token, char *value_raw)
13191 if (token == NULL || value_raw == NULL)
13192 return ARG_UNDEFINED_VALUE;
13194 suffix = strrchr(token, '.');
13195 if (suffix == NULL)
13198 if (strEqual(suffix, ".element"))
13199 return getElementFromToken(value_raw);
13201 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13202 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13205 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13206 boolean ignore_defaults)
13210 for (i = 0; image_config_vars[i].token != NULL; i++)
13212 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13214 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13215 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13219 *image_config_vars[i].value =
13220 get_token_parameter_value(image_config_vars[i].token, value);
13224 void InitMenuDesignSettings_Static(void)
13226 // always start with reliable default values from static default config
13227 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13230 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13234 // the following initializes hierarchical values from static configuration
13236 // special case: initialize "ARG_DEFAULT" values in static default config
13237 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13238 titlescreen_initial_first_default.fade_mode =
13239 title_initial_first_default.fade_mode;
13240 titlescreen_initial_first_default.fade_delay =
13241 title_initial_first_default.fade_delay;
13242 titlescreen_initial_first_default.post_delay =
13243 title_initial_first_default.post_delay;
13244 titlescreen_initial_first_default.auto_delay =
13245 title_initial_first_default.auto_delay;
13246 titlescreen_initial_first_default.auto_delay_unit =
13247 title_initial_first_default.auto_delay_unit;
13248 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13249 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13250 titlescreen_first_default.post_delay = title_first_default.post_delay;
13251 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13252 titlescreen_first_default.auto_delay_unit =
13253 title_first_default.auto_delay_unit;
13254 titlemessage_initial_first_default.fade_mode =
13255 title_initial_first_default.fade_mode;
13256 titlemessage_initial_first_default.fade_delay =
13257 title_initial_first_default.fade_delay;
13258 titlemessage_initial_first_default.post_delay =
13259 title_initial_first_default.post_delay;
13260 titlemessage_initial_first_default.auto_delay =
13261 title_initial_first_default.auto_delay;
13262 titlemessage_initial_first_default.auto_delay_unit =
13263 title_initial_first_default.auto_delay_unit;
13264 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13265 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13266 titlemessage_first_default.post_delay = title_first_default.post_delay;
13267 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13268 titlemessage_first_default.auto_delay_unit =
13269 title_first_default.auto_delay_unit;
13271 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13272 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13273 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13274 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13275 titlescreen_initial_default.auto_delay_unit =
13276 title_initial_default.auto_delay_unit;
13277 titlescreen_default.fade_mode = title_default.fade_mode;
13278 titlescreen_default.fade_delay = title_default.fade_delay;
13279 titlescreen_default.post_delay = title_default.post_delay;
13280 titlescreen_default.auto_delay = title_default.auto_delay;
13281 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13282 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13283 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13284 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13285 titlemessage_initial_default.auto_delay_unit =
13286 title_initial_default.auto_delay_unit;
13287 titlemessage_default.fade_mode = title_default.fade_mode;
13288 titlemessage_default.fade_delay = title_default.fade_delay;
13289 titlemessage_default.post_delay = title_default.post_delay;
13290 titlemessage_default.auto_delay = title_default.auto_delay;
13291 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13293 // special case: initialize "ARG_DEFAULT" values in static default config
13294 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13295 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13297 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13298 titlescreen_first[i] = titlescreen_first_default;
13299 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13300 titlemessage_first[i] = titlemessage_first_default;
13302 titlescreen_initial[i] = titlescreen_initial_default;
13303 titlescreen[i] = titlescreen_default;
13304 titlemessage_initial[i] = titlemessage_initial_default;
13305 titlemessage[i] = titlemessage_default;
13308 // special case: initialize "ARG_DEFAULT" values in static default config
13309 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13310 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13312 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13315 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13316 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13317 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13320 // special case: initialize "ARG_DEFAULT" values in static default config
13321 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13322 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13324 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13325 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13326 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13328 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13331 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13335 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13339 struct XY *dst, *src;
13341 game_buttons_xy[] =
13343 { &game.button.save, &game.button.stop },
13344 { &game.button.pause2, &game.button.pause },
13345 { &game.button.load, &game.button.play },
13346 { &game.button.undo, &game.button.stop },
13347 { &game.button.redo, &game.button.play },
13353 // special case: initialize later added SETUP list size from LEVELS value
13354 if (menu.list_size[GAME_MODE_SETUP] == -1)
13355 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13357 // set default position for snapshot buttons to stop/pause/play buttons
13358 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13359 if ((*game_buttons_xy[i].dst).x == -1 &&
13360 (*game_buttons_xy[i].dst).y == -1)
13361 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13363 // --------------------------------------------------------------------------
13364 // dynamic viewports (including playfield margins, borders and alignments)
13365 // --------------------------------------------------------------------------
13367 // dynamic viewports currently only supported for landscape mode
13368 int display_width = MAX(video.display_width, video.display_height);
13369 int display_height = MIN(video.display_width, video.display_height);
13371 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13373 struct RectWithBorder *vp_window = &viewport.window[i];
13374 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13375 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13376 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13377 boolean dynamic_window_width = (vp_window->min_width != -1);
13378 boolean dynamic_window_height = (vp_window->min_height != -1);
13379 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13380 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13382 // adjust window size if min/max width/height is specified
13384 if (vp_window->min_width != -1)
13386 int window_width = display_width;
13388 // when using static window height, use aspect ratio of display
13389 if (vp_window->min_height == -1)
13390 window_width = vp_window->height * display_width / display_height;
13392 vp_window->width = MAX(vp_window->min_width, window_width);
13395 if (vp_window->min_height != -1)
13397 int window_height = display_height;
13399 // when using static window width, use aspect ratio of display
13400 if (vp_window->min_width == -1)
13401 window_height = vp_window->width * display_height / display_width;
13403 vp_window->height = MAX(vp_window->min_height, window_height);
13406 if (vp_window->max_width != -1)
13407 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13409 if (vp_window->max_height != -1)
13410 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13412 int playfield_width = vp_window->width;
13413 int playfield_height = vp_window->height;
13415 // adjust playfield size and position according to specified margins
13417 playfield_width -= vp_playfield->margin_left;
13418 playfield_width -= vp_playfield->margin_right;
13420 playfield_height -= vp_playfield->margin_top;
13421 playfield_height -= vp_playfield->margin_bottom;
13423 // adjust playfield size if min/max width/height is specified
13425 if (vp_playfield->min_width != -1)
13426 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13428 if (vp_playfield->min_height != -1)
13429 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13431 if (vp_playfield->max_width != -1)
13432 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13434 if (vp_playfield->max_height != -1)
13435 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13437 // adjust playfield position according to specified alignment
13439 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13440 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13441 else if (vp_playfield->align == ALIGN_CENTER)
13442 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13443 else if (vp_playfield->align == ALIGN_RIGHT)
13444 vp_playfield->x += playfield_width - vp_playfield->width;
13446 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13447 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13448 else if (vp_playfield->valign == VALIGN_MIDDLE)
13449 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13450 else if (vp_playfield->valign == VALIGN_BOTTOM)
13451 vp_playfield->y += playfield_height - vp_playfield->height;
13453 vp_playfield->x += vp_playfield->margin_left;
13454 vp_playfield->y += vp_playfield->margin_top;
13456 // adjust individual playfield borders if only default border is specified
13458 if (vp_playfield->border_left == -1)
13459 vp_playfield->border_left = vp_playfield->border_size;
13460 if (vp_playfield->border_right == -1)
13461 vp_playfield->border_right = vp_playfield->border_size;
13462 if (vp_playfield->border_top == -1)
13463 vp_playfield->border_top = vp_playfield->border_size;
13464 if (vp_playfield->border_bottom == -1)
13465 vp_playfield->border_bottom = vp_playfield->border_size;
13467 // set dynamic playfield borders if borders are specified as undefined
13468 // (but only if window size was dynamic and playfield size was static)
13470 if (dynamic_window_width && !dynamic_playfield_width)
13472 if (vp_playfield->border_left == -1)
13474 vp_playfield->border_left = (vp_playfield->x -
13475 vp_playfield->margin_left);
13476 vp_playfield->x -= vp_playfield->border_left;
13477 vp_playfield->width += vp_playfield->border_left;
13480 if (vp_playfield->border_right == -1)
13482 vp_playfield->border_right = (vp_window->width -
13484 vp_playfield->width -
13485 vp_playfield->margin_right);
13486 vp_playfield->width += vp_playfield->border_right;
13490 if (dynamic_window_height && !dynamic_playfield_height)
13492 if (vp_playfield->border_top == -1)
13494 vp_playfield->border_top = (vp_playfield->y -
13495 vp_playfield->margin_top);
13496 vp_playfield->y -= vp_playfield->border_top;
13497 vp_playfield->height += vp_playfield->border_top;
13500 if (vp_playfield->border_bottom == -1)
13502 vp_playfield->border_bottom = (vp_window->height -
13504 vp_playfield->height -
13505 vp_playfield->margin_bottom);
13506 vp_playfield->height += vp_playfield->border_bottom;
13510 // adjust playfield size to be a multiple of a defined alignment tile size
13512 int align_size = vp_playfield->align_size;
13513 int playfield_xtiles = vp_playfield->width / align_size;
13514 int playfield_ytiles = vp_playfield->height / align_size;
13515 int playfield_width_corrected = playfield_xtiles * align_size;
13516 int playfield_height_corrected = playfield_ytiles * align_size;
13517 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13518 i == GFX_SPECIAL_ARG_EDITOR);
13520 if (is_playfield_mode &&
13521 dynamic_playfield_width &&
13522 vp_playfield->width != playfield_width_corrected)
13524 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13526 vp_playfield->width = playfield_width_corrected;
13528 if (vp_playfield->align == ALIGN_LEFT)
13530 vp_playfield->border_left += playfield_xdiff;
13532 else if (vp_playfield->align == ALIGN_RIGHT)
13534 vp_playfield->border_right += playfield_xdiff;
13536 else if (vp_playfield->align == ALIGN_CENTER)
13538 int border_left_diff = playfield_xdiff / 2;
13539 int border_right_diff = playfield_xdiff - border_left_diff;
13541 vp_playfield->border_left += border_left_diff;
13542 vp_playfield->border_right += border_right_diff;
13546 if (is_playfield_mode &&
13547 dynamic_playfield_height &&
13548 vp_playfield->height != playfield_height_corrected)
13550 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13552 vp_playfield->height = playfield_height_corrected;
13554 if (vp_playfield->valign == VALIGN_TOP)
13556 vp_playfield->border_top += playfield_ydiff;
13558 else if (vp_playfield->align == VALIGN_BOTTOM)
13560 vp_playfield->border_right += playfield_ydiff;
13562 else if (vp_playfield->align == VALIGN_MIDDLE)
13564 int border_top_diff = playfield_ydiff / 2;
13565 int border_bottom_diff = playfield_ydiff - border_top_diff;
13567 vp_playfield->border_top += border_top_diff;
13568 vp_playfield->border_bottom += border_bottom_diff;
13572 // adjust door positions according to specified alignment
13574 for (j = 0; j < 2; j++)
13576 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13578 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13579 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13580 else if (vp_door->align == ALIGN_CENTER)
13581 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13582 else if (vp_door->align == ALIGN_RIGHT)
13583 vp_door->x += vp_window->width - vp_door->width;
13585 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13586 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13587 else if (vp_door->valign == VALIGN_MIDDLE)
13588 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13589 else if (vp_door->valign == VALIGN_BOTTOM)
13590 vp_door->y += vp_window->height - vp_door->height;
13595 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13599 struct XYTileSize *dst, *src;
13602 editor_buttons_xy[] =
13605 &editor.button.element_left, &editor.palette.element_left,
13606 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13609 &editor.button.element_middle, &editor.palette.element_middle,
13610 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13613 &editor.button.element_right, &editor.palette.element_right,
13614 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13621 // set default position for element buttons to element graphics
13622 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13624 if ((*editor_buttons_xy[i].dst).x == -1 &&
13625 (*editor_buttons_xy[i].dst).y == -1)
13627 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13629 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13631 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13635 // adjust editor palette rows and columns if specified to be dynamic
13637 if (editor.palette.cols == -1)
13639 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13640 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13641 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13643 editor.palette.cols = (vp_width - sc_width) / bt_width;
13645 if (editor.palette.x == -1)
13647 int palette_width = editor.palette.cols * bt_width + sc_width;
13649 editor.palette.x = (vp_width - palette_width) / 2;
13653 if (editor.palette.rows == -1)
13655 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13656 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13657 int tx_height = getFontHeight(FONT_TEXT_2);
13659 editor.palette.rows = (vp_height - tx_height) / bt_height;
13661 if (editor.palette.y == -1)
13663 int palette_height = editor.palette.rows * bt_height + tx_height;
13665 editor.palette.y = (vp_height - palette_height) / 2;
13670 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13671 boolean initialize)
13673 // special case: check if network and preview player positions are redefined,
13674 // to compare this later against the main menu level preview being redefined
13675 struct TokenIntPtrInfo menu_config_players[] =
13677 { "main.network_players.x", &menu.main.network_players.redefined },
13678 { "main.network_players.y", &menu.main.network_players.redefined },
13679 { "main.preview_players.x", &menu.main.preview_players.redefined },
13680 { "main.preview_players.y", &menu.main.preview_players.redefined },
13681 { "preview.x", &preview.redefined },
13682 { "preview.y", &preview.redefined }
13688 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13689 *menu_config_players[i].value = FALSE;
13693 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13694 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13695 *menu_config_players[i].value = TRUE;
13699 static void InitMenuDesignSettings_PreviewPlayers(void)
13701 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13704 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13706 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13709 static void LoadMenuDesignSettingsFromFilename(char *filename)
13711 static struct TitleFadingInfo tfi;
13712 static struct TitleMessageInfo tmi;
13713 static struct TokenInfo title_tokens[] =
13715 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13716 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13717 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13718 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13719 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13723 static struct TokenInfo titlemessage_tokens[] =
13725 { TYPE_INTEGER, &tmi.x, ".x" },
13726 { TYPE_INTEGER, &tmi.y, ".y" },
13727 { TYPE_INTEGER, &tmi.width, ".width" },
13728 { TYPE_INTEGER, &tmi.height, ".height" },
13729 { TYPE_INTEGER, &tmi.chars, ".chars" },
13730 { TYPE_INTEGER, &tmi.lines, ".lines" },
13731 { TYPE_INTEGER, &tmi.align, ".align" },
13732 { TYPE_INTEGER, &tmi.valign, ".valign" },
13733 { TYPE_INTEGER, &tmi.font, ".font" },
13734 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13735 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13736 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13737 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13738 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13739 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13740 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13741 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13742 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13748 struct TitleFadingInfo *info;
13753 // initialize first titles from "enter screen" definitions, if defined
13754 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13755 { &title_first_default, "menu.enter_screen.TITLE" },
13757 // initialize title screens from "next screen" definitions, if defined
13758 { &title_initial_default, "menu.next_screen.TITLE" },
13759 { &title_default, "menu.next_screen.TITLE" },
13765 struct TitleMessageInfo *array;
13768 titlemessage_arrays[] =
13770 // initialize first titles from "enter screen" definitions, if defined
13771 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13772 { titlescreen_first, "menu.enter_screen.TITLE" },
13773 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13774 { titlemessage_first, "menu.enter_screen.TITLE" },
13776 // initialize titles from "next screen" definitions, if defined
13777 { titlescreen_initial, "menu.next_screen.TITLE" },
13778 { titlescreen, "menu.next_screen.TITLE" },
13779 { titlemessage_initial, "menu.next_screen.TITLE" },
13780 { titlemessage, "menu.next_screen.TITLE" },
13782 // overwrite titles with title definitions, if defined
13783 { titlescreen_initial_first, "[title_initial]" },
13784 { titlescreen_first, "[title]" },
13785 { titlemessage_initial_first, "[title_initial]" },
13786 { titlemessage_first, "[title]" },
13788 { titlescreen_initial, "[title_initial]" },
13789 { titlescreen, "[title]" },
13790 { titlemessage_initial, "[title_initial]" },
13791 { titlemessage, "[title]" },
13793 // overwrite titles with title screen/message definitions, if defined
13794 { titlescreen_initial_first, "[titlescreen_initial]" },
13795 { titlescreen_first, "[titlescreen]" },
13796 { titlemessage_initial_first, "[titlemessage_initial]" },
13797 { titlemessage_first, "[titlemessage]" },
13799 { titlescreen_initial, "[titlescreen_initial]" },
13800 { titlescreen, "[titlescreen]" },
13801 { titlemessage_initial, "[titlemessage_initial]" },
13802 { titlemessage, "[titlemessage]" },
13806 SetupFileHash *setup_file_hash;
13809 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13812 // the following initializes hierarchical values from dynamic configuration
13814 // special case: initialize with default values that may be overwritten
13815 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13816 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13818 struct TokenIntPtrInfo menu_config[] =
13820 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13821 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13822 { "menu.list_size", &menu.list_size[i] }
13825 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13827 char *token = menu_config[j].token;
13828 char *value = getHashEntry(setup_file_hash, token);
13831 *menu_config[j].value = get_integer_from_string(value);
13835 // special case: initialize with default values that may be overwritten
13836 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13837 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13839 struct TokenIntPtrInfo menu_config[] =
13841 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13842 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13843 { "menu.list_size.INFO", &menu.list_size_info[i] },
13844 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13845 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13848 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13850 char *token = menu_config[j].token;
13851 char *value = getHashEntry(setup_file_hash, token);
13854 *menu_config[j].value = get_integer_from_string(value);
13858 // special case: initialize with default values that may be overwritten
13859 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13860 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13862 struct TokenIntPtrInfo menu_config[] =
13864 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13865 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13868 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13870 char *token = menu_config[j].token;
13871 char *value = getHashEntry(setup_file_hash, token);
13874 *menu_config[j].value = get_integer_from_string(value);
13878 // special case: initialize with default values that may be overwritten
13879 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13880 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13882 struct TokenIntPtrInfo menu_config[] =
13884 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13885 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13886 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13887 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13888 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13889 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13890 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13891 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13892 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13893 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13896 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13898 char *token = menu_config[j].token;
13899 char *value = getHashEntry(setup_file_hash, token);
13902 *menu_config[j].value = get_integer_from_string(value);
13906 // special case: initialize with default values that may be overwritten
13907 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13908 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13910 struct TokenIntPtrInfo menu_config[] =
13912 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13913 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13914 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13915 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13916 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13917 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13918 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13919 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13920 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13923 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13925 char *token = menu_config[j].token;
13926 char *value = getHashEntry(setup_file_hash, token);
13929 *menu_config[j].value = get_token_parameter_value(token, value);
13933 // special case: initialize with default values that may be overwritten
13934 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13935 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13939 char *token_prefix;
13940 struct RectWithBorder *struct_ptr;
13944 { "viewport.window", &viewport.window[i] },
13945 { "viewport.playfield", &viewport.playfield[i] },
13946 { "viewport.door_1", &viewport.door_1[i] },
13947 { "viewport.door_2", &viewport.door_2[i] }
13950 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13952 struct TokenIntPtrInfo vp_config[] =
13954 { ".x", &vp_struct[j].struct_ptr->x },
13955 { ".y", &vp_struct[j].struct_ptr->y },
13956 { ".width", &vp_struct[j].struct_ptr->width },
13957 { ".height", &vp_struct[j].struct_ptr->height },
13958 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13959 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13960 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13961 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13962 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13963 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13964 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13965 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13966 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13967 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13968 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13969 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13970 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13971 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13972 { ".align", &vp_struct[j].struct_ptr->align },
13973 { ".valign", &vp_struct[j].struct_ptr->valign }
13976 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13978 char *token = getStringCat2(vp_struct[j].token_prefix,
13979 vp_config[k].token);
13980 char *value = getHashEntry(setup_file_hash, token);
13983 *vp_config[k].value = get_token_parameter_value(token, value);
13990 // special case: initialize with default values that may be overwritten
13991 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13992 for (i = 0; title_info[i].info != NULL; i++)
13994 struct TitleFadingInfo *info = title_info[i].info;
13995 char *base_token = title_info[i].text;
13997 for (j = 0; title_tokens[j].type != -1; j++)
13999 char *token = getStringCat2(base_token, title_tokens[j].text);
14000 char *value = getHashEntry(setup_file_hash, token);
14004 int parameter_value = get_token_parameter_value(token, value);
14008 *(int *)title_tokens[j].value = (int)parameter_value;
14017 // special case: initialize with default values that may be overwritten
14018 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14019 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14021 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14022 char *base_token = titlemessage_arrays[i].text;
14024 for (j = 0; titlemessage_tokens[j].type != -1; j++)
14026 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14027 char *value = getHashEntry(setup_file_hash, token);
14031 int parameter_value = get_token_parameter_value(token, value);
14033 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14037 if (titlemessage_tokens[j].type == TYPE_INTEGER)
14038 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
14040 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14050 // read (and overwrite with) values that may be specified in config file
14051 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14053 // special case: check if network and preview player positions are redefined
14054 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14056 freeSetupFileHash(setup_file_hash);
14059 void LoadMenuDesignSettings(void)
14061 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14063 InitMenuDesignSettings_Static();
14064 InitMenuDesignSettings_SpecialPreProcessing();
14065 InitMenuDesignSettings_PreviewPlayers();
14067 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14069 // first look for special settings configured in level series config
14070 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14072 if (fileExists(filename_base))
14073 LoadMenuDesignSettingsFromFilename(filename_base);
14076 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14078 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14079 LoadMenuDesignSettingsFromFilename(filename_local);
14081 InitMenuDesignSettings_SpecialPostProcessing();
14084 void LoadMenuDesignSettings_AfterGraphics(void)
14086 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14089 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14090 boolean ignore_defaults)
14094 for (i = 0; sound_config_vars[i].token != NULL; i++)
14096 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14098 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14099 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14103 *sound_config_vars[i].value =
14104 get_token_parameter_value(sound_config_vars[i].token, value);
14108 void InitSoundSettings_Static(void)
14110 // always start with reliable default values from static default config
14111 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14114 static void LoadSoundSettingsFromFilename(char *filename)
14116 SetupFileHash *setup_file_hash;
14118 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14121 // read (and overwrite with) values that may be specified in config file
14122 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14124 freeSetupFileHash(setup_file_hash);
14127 void LoadSoundSettings(void)
14129 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14131 InitSoundSettings_Static();
14133 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14135 // first look for special settings configured in level series config
14136 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14138 if (fileExists(filename_base))
14139 LoadSoundSettingsFromFilename(filename_base);
14142 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14144 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14145 LoadSoundSettingsFromFilename(filename_local);
14148 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14150 char *filename = getEditorSetupFilename();
14151 SetupFileList *setup_file_list, *list;
14152 SetupFileHash *element_hash;
14153 int num_unknown_tokens = 0;
14156 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14159 element_hash = newSetupFileHash();
14161 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14162 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14164 // determined size may be larger than needed (due to unknown elements)
14166 for (list = setup_file_list; list != NULL; list = list->next)
14169 // add space for up to 3 more elements for padding that may be needed
14170 *num_elements += 3;
14172 // free memory for old list of elements, if needed
14173 checked_free(*elements);
14175 // allocate memory for new list of elements
14176 *elements = checked_malloc(*num_elements * sizeof(int));
14179 for (list = setup_file_list; list != NULL; list = list->next)
14181 char *value = getHashEntry(element_hash, list->token);
14183 if (value == NULL) // try to find obsolete token mapping
14185 char *mapped_token = get_mapped_token(list->token);
14187 if (mapped_token != NULL)
14189 value = getHashEntry(element_hash, mapped_token);
14191 free(mapped_token);
14197 (*elements)[(*num_elements)++] = atoi(value);
14201 if (num_unknown_tokens == 0)
14204 Warn("unknown token(s) found in config file:");
14205 Warn("- config file: '%s'", filename);
14207 num_unknown_tokens++;
14210 Warn("- token: '%s'", list->token);
14214 if (num_unknown_tokens > 0)
14217 while (*num_elements % 4) // pad with empty elements, if needed
14218 (*elements)[(*num_elements)++] = EL_EMPTY;
14220 freeSetupFileList(setup_file_list);
14221 freeSetupFileHash(element_hash);
14224 for (i = 0; i < *num_elements; i++)
14225 Debug("editor", "element '%s' [%d]\n",
14226 element_info[(*elements)[i]].token_name, (*elements)[i]);
14230 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14233 SetupFileHash *setup_file_hash = NULL;
14234 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14235 char *filename_music, *filename_prefix, *filename_info;
14241 token_to_value_ptr[] =
14243 { "title_header", &tmp_music_file_info.title_header },
14244 { "artist_header", &tmp_music_file_info.artist_header },
14245 { "album_header", &tmp_music_file_info.album_header },
14246 { "year_header", &tmp_music_file_info.year_header },
14247 { "played_header", &tmp_music_file_info.played_header },
14249 { "title", &tmp_music_file_info.title },
14250 { "artist", &tmp_music_file_info.artist },
14251 { "album", &tmp_music_file_info.album },
14252 { "year", &tmp_music_file_info.year },
14253 { "played", &tmp_music_file_info.played },
14259 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14260 getCustomMusicFilename(basename));
14262 if (filename_music == NULL)
14265 // ---------- try to replace file extension ----------
14267 filename_prefix = getStringCopy(filename_music);
14268 if (strrchr(filename_prefix, '.') != NULL)
14269 *strrchr(filename_prefix, '.') = '\0';
14270 filename_info = getStringCat2(filename_prefix, ".txt");
14272 if (fileExists(filename_info))
14273 setup_file_hash = loadSetupFileHash(filename_info);
14275 free(filename_prefix);
14276 free(filename_info);
14278 if (setup_file_hash == NULL)
14280 // ---------- try to add file extension ----------
14282 filename_prefix = getStringCopy(filename_music);
14283 filename_info = getStringCat2(filename_prefix, ".txt");
14285 if (fileExists(filename_info))
14286 setup_file_hash = loadSetupFileHash(filename_info);
14288 free(filename_prefix);
14289 free(filename_info);
14292 if (setup_file_hash == NULL)
14295 // ---------- music file info found ----------
14297 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14299 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14301 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14303 *token_to_value_ptr[i].value_ptr =
14304 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14307 tmp_music_file_info.basename = getStringCopy(basename);
14308 tmp_music_file_info.music = music;
14309 tmp_music_file_info.is_sound = is_sound;
14311 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14312 *new_music_file_info = tmp_music_file_info;
14314 return new_music_file_info;
14317 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14319 return get_music_file_info_ext(basename, music, FALSE);
14322 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14324 return get_music_file_info_ext(basename, sound, TRUE);
14327 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14328 char *basename, boolean is_sound)
14330 for (; list != NULL; list = list->next)
14331 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14337 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14339 return music_info_listed_ext(list, basename, FALSE);
14342 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14344 return music_info_listed_ext(list, basename, TRUE);
14347 void LoadMusicInfo(void)
14349 int num_music_noconf = getMusicListSize_NoConf();
14350 int num_music = getMusicListSize();
14351 int num_sounds = getSoundListSize();
14352 struct FileInfo *music, *sound;
14353 struct MusicFileInfo *next, **new;
14357 while (music_file_info != NULL)
14359 next = music_file_info->next;
14361 checked_free(music_file_info->basename);
14363 checked_free(music_file_info->title_header);
14364 checked_free(music_file_info->artist_header);
14365 checked_free(music_file_info->album_header);
14366 checked_free(music_file_info->year_header);
14367 checked_free(music_file_info->played_header);
14369 checked_free(music_file_info->title);
14370 checked_free(music_file_info->artist);
14371 checked_free(music_file_info->album);
14372 checked_free(music_file_info->year);
14373 checked_free(music_file_info->played);
14375 free(music_file_info);
14377 music_file_info = next;
14380 new = &music_file_info;
14382 // get (configured or unconfigured) music file info for all levels
14383 for (i = leveldir_current->first_level;
14384 i <= leveldir_current->last_level; i++)
14388 if (levelset.music[i] != MUS_UNDEFINED)
14390 // get music file info for configured level music
14391 music_nr = levelset.music[i];
14393 else if (num_music_noconf > 0)
14395 // get music file info for unconfigured level music
14396 int level_pos = i - leveldir_current->first_level;
14398 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14405 char *basename = getMusicInfoEntryFilename(music_nr);
14407 if (basename == NULL)
14410 if (!music_info_listed(music_file_info, basename))
14412 *new = get_music_file_info(basename, music_nr);
14415 new = &(*new)->next;
14419 // get music file info for all remaining configured music files
14420 for (i = 0; i < num_music; i++)
14422 music = getMusicListEntry(i);
14424 if (music->filename == NULL)
14427 if (strEqual(music->filename, UNDEFINED_FILENAME))
14430 // a configured file may be not recognized as music
14431 if (!FileIsMusic(music->filename))
14434 if (!music_info_listed(music_file_info, music->filename))
14436 *new = get_music_file_info(music->filename, i);
14439 new = &(*new)->next;
14443 // get sound file info for all configured sound files
14444 for (i = 0; i < num_sounds; i++)
14446 sound = getSoundListEntry(i);
14448 if (sound->filename == NULL)
14451 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14454 // a configured file may be not recognized as sound
14455 if (!FileIsSound(sound->filename))
14458 if (!sound_info_listed(music_file_info, sound->filename))
14460 *new = get_sound_file_info(sound->filename, i);
14462 new = &(*new)->next;
14466 // add pointers to previous list nodes
14468 struct MusicFileInfo *node = music_file_info;
14470 while (node != NULL)
14473 node->next->prev = node;
14479 static void add_helpanim_entry(int element, int action, int direction,
14480 int delay, int *num_list_entries)
14482 struct HelpAnimInfo *new_list_entry;
14483 (*num_list_entries)++;
14486 checked_realloc(helpanim_info,
14487 *num_list_entries * sizeof(struct HelpAnimInfo));
14488 new_list_entry = &helpanim_info[*num_list_entries - 1];
14490 new_list_entry->element = element;
14491 new_list_entry->action = action;
14492 new_list_entry->direction = direction;
14493 new_list_entry->delay = delay;
14496 static void print_unknown_token(char *filename, char *token, int token_nr)
14501 Warn("unknown token(s) found in config file:");
14502 Warn("- config file: '%s'", filename);
14505 Warn("- token: '%s'", token);
14508 static void print_unknown_token_end(int token_nr)
14514 void LoadHelpAnimInfo(void)
14516 char *filename = getHelpAnimFilename();
14517 SetupFileList *setup_file_list = NULL, *list;
14518 SetupFileHash *element_hash, *action_hash, *direction_hash;
14519 int num_list_entries = 0;
14520 int num_unknown_tokens = 0;
14523 if (fileExists(filename))
14524 setup_file_list = loadSetupFileList(filename);
14526 if (setup_file_list == NULL)
14528 // use reliable default values from static configuration
14529 SetupFileList *insert_ptr;
14531 insert_ptr = setup_file_list =
14532 newSetupFileList(helpanim_config[0].token,
14533 helpanim_config[0].value);
14535 for (i = 1; helpanim_config[i].token; i++)
14536 insert_ptr = addListEntry(insert_ptr,
14537 helpanim_config[i].token,
14538 helpanim_config[i].value);
14541 element_hash = newSetupFileHash();
14542 action_hash = newSetupFileHash();
14543 direction_hash = newSetupFileHash();
14545 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14546 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14548 for (i = 0; i < NUM_ACTIONS; i++)
14549 setHashEntry(action_hash, element_action_info[i].suffix,
14550 i_to_a(element_action_info[i].value));
14552 // do not store direction index (bit) here, but direction value!
14553 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14554 setHashEntry(direction_hash, element_direction_info[i].suffix,
14555 i_to_a(1 << element_direction_info[i].value));
14557 for (list = setup_file_list; list != NULL; list = list->next)
14559 char *element_token, *action_token, *direction_token;
14560 char *element_value, *action_value, *direction_value;
14561 int delay = atoi(list->value);
14563 if (strEqual(list->token, "end"))
14565 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14570 /* first try to break element into element/action/direction parts;
14571 if this does not work, also accept combined "element[.act][.dir]"
14572 elements (like "dynamite.active"), which are unique elements */
14574 if (strchr(list->token, '.') == NULL) // token contains no '.'
14576 element_value = getHashEntry(element_hash, list->token);
14577 if (element_value != NULL) // element found
14578 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14579 &num_list_entries);
14582 // no further suffixes found -- this is not an element
14583 print_unknown_token(filename, list->token, num_unknown_tokens++);
14589 // token has format "<prefix>.<something>"
14591 action_token = strchr(list->token, '.'); // suffix may be action ...
14592 direction_token = action_token; // ... or direction
14594 element_token = getStringCopy(list->token);
14595 *strchr(element_token, '.') = '\0';
14597 element_value = getHashEntry(element_hash, element_token);
14599 if (element_value == NULL) // this is no element
14601 element_value = getHashEntry(element_hash, list->token);
14602 if (element_value != NULL) // combined element found
14603 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14604 &num_list_entries);
14606 print_unknown_token(filename, list->token, num_unknown_tokens++);
14608 free(element_token);
14613 action_value = getHashEntry(action_hash, action_token);
14615 if (action_value != NULL) // action found
14617 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14618 &num_list_entries);
14620 free(element_token);
14625 direction_value = getHashEntry(direction_hash, direction_token);
14627 if (direction_value != NULL) // direction found
14629 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14630 &num_list_entries);
14632 free(element_token);
14637 if (strchr(action_token + 1, '.') == NULL)
14639 // no further suffixes found -- this is not an action nor direction
14641 element_value = getHashEntry(element_hash, list->token);
14642 if (element_value != NULL) // combined element found
14643 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14644 &num_list_entries);
14646 print_unknown_token(filename, list->token, num_unknown_tokens++);
14648 free(element_token);
14653 // token has format "<prefix>.<suffix>.<something>"
14655 direction_token = strchr(action_token + 1, '.');
14657 action_token = getStringCopy(action_token);
14658 *strchr(action_token + 1, '.') = '\0';
14660 action_value = getHashEntry(action_hash, action_token);
14662 if (action_value == NULL) // this is no action
14664 element_value = getHashEntry(element_hash, list->token);
14665 if (element_value != NULL) // combined element found
14666 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14667 &num_list_entries);
14669 print_unknown_token(filename, list->token, num_unknown_tokens++);
14671 free(element_token);
14672 free(action_token);
14677 direction_value = getHashEntry(direction_hash, direction_token);
14679 if (direction_value != NULL) // direction found
14681 add_helpanim_entry(atoi(element_value), atoi(action_value),
14682 atoi(direction_value), delay, &num_list_entries);
14684 free(element_token);
14685 free(action_token);
14690 // this is no direction
14692 element_value = getHashEntry(element_hash, list->token);
14693 if (element_value != NULL) // combined element found
14694 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14695 &num_list_entries);
14697 print_unknown_token(filename, list->token, num_unknown_tokens++);
14699 free(element_token);
14700 free(action_token);
14703 print_unknown_token_end(num_unknown_tokens);
14705 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14706 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14708 freeSetupFileList(setup_file_list);
14709 freeSetupFileHash(element_hash);
14710 freeSetupFileHash(action_hash);
14711 freeSetupFileHash(direction_hash);
14714 for (i = 0; i < num_list_entries; i++)
14715 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14716 EL_NAME(helpanim_info[i].element),
14717 helpanim_info[i].element,
14718 helpanim_info[i].action,
14719 helpanim_info[i].direction,
14720 helpanim_info[i].delay);
14724 void LoadHelpTextInfo(void)
14726 char *filename = getHelpTextFilename();
14729 if (helptext_info != NULL)
14731 freeSetupFileHash(helptext_info);
14732 helptext_info = NULL;
14735 if (fileExists(filename))
14736 helptext_info = loadSetupFileHash(filename);
14738 if (helptext_info == NULL)
14740 // use reliable default values from static configuration
14741 helptext_info = newSetupFileHash();
14743 for (i = 0; helptext_config[i].token; i++)
14744 setHashEntry(helptext_info,
14745 helptext_config[i].token,
14746 helptext_config[i].value);
14750 BEGIN_HASH_ITERATION(helptext_info, itr)
14752 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14753 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14755 END_HASH_ITERATION(hash, itr)
14760 // ----------------------------------------------------------------------------
14762 // ----------------------------------------------------------------------------
14764 #define MAX_NUM_CONVERT_LEVELS 1000
14766 void ConvertLevels(void)
14768 static LevelDirTree *convert_leveldir = NULL;
14769 static int convert_level_nr = -1;
14770 static int num_levels_handled = 0;
14771 static int num_levels_converted = 0;
14772 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14775 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14776 global.convert_leveldir);
14778 if (convert_leveldir == NULL)
14779 Fail("no such level identifier: '%s'", global.convert_leveldir);
14781 leveldir_current = convert_leveldir;
14783 if (global.convert_level_nr != -1)
14785 convert_leveldir->first_level = global.convert_level_nr;
14786 convert_leveldir->last_level = global.convert_level_nr;
14789 convert_level_nr = convert_leveldir->first_level;
14791 PrintLine("=", 79);
14792 Print("Converting levels\n");
14793 PrintLine("-", 79);
14794 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14795 Print("Level series name: '%s'\n", convert_leveldir->name);
14796 Print("Level series author: '%s'\n", convert_leveldir->author);
14797 Print("Number of levels: %d\n", convert_leveldir->levels);
14798 PrintLine("=", 79);
14801 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14802 levels_failed[i] = FALSE;
14804 while (convert_level_nr <= convert_leveldir->last_level)
14806 char *level_filename;
14809 level_nr = convert_level_nr++;
14811 Print("Level %03d: ", level_nr);
14813 LoadLevel(level_nr);
14814 if (level.no_level_file || level.no_valid_file)
14816 Print("(no level)\n");
14820 Print("converting level ... ");
14823 // special case: conversion of some EMC levels as requested by ACME
14824 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14827 level_filename = getDefaultLevelFilename(level_nr);
14828 new_level = !fileExists(level_filename);
14832 SaveLevel(level_nr);
14834 num_levels_converted++;
14836 Print("converted.\n");
14840 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14841 levels_failed[level_nr] = TRUE;
14843 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14846 num_levels_handled++;
14850 PrintLine("=", 79);
14851 Print("Number of levels handled: %d\n", num_levels_handled);
14852 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14853 (num_levels_handled ?
14854 num_levels_converted * 100 / num_levels_handled : 0));
14855 PrintLine("-", 79);
14856 Print("Summary (for automatic parsing by scripts):\n");
14857 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14858 convert_leveldir->identifier, num_levels_converted,
14859 num_levels_handled,
14860 (num_levels_handled ?
14861 num_levels_converted * 100 / num_levels_handled : 0));
14863 if (num_levels_handled != num_levels_converted)
14865 Print(", FAILED:");
14866 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14867 if (levels_failed[i])
14872 PrintLine("=", 79);
14874 CloseAllAndExit(0);
14878 // ----------------------------------------------------------------------------
14879 // create and save images for use in level sketches (raw BMP format)
14880 // ----------------------------------------------------------------------------
14882 void CreateLevelSketchImages(void)
14888 InitElementPropertiesGfxElement();
14890 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14891 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14893 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14895 int element = getMappedElement(i);
14896 char basename1[16];
14897 char basename2[16];
14901 sprintf(basename1, "%04d.bmp", i);
14902 sprintf(basename2, "%04ds.bmp", i);
14904 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14905 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14907 DrawSizedElement(0, 0, element, TILESIZE);
14908 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14910 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14911 Fail("cannot save level sketch image file '%s'", filename1);
14913 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14914 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14916 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14917 Fail("cannot save level sketch image file '%s'", filename2);
14922 // create corresponding SQL statements (for normal and small images)
14925 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14926 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14929 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14930 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14932 // optional: create content for forum level sketch demonstration post
14934 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14937 FreeBitmap(bitmap1);
14938 FreeBitmap(bitmap2);
14941 fprintf(stderr, "\n");
14943 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14945 CloseAllAndExit(0);
14949 // ----------------------------------------------------------------------------
14950 // create and save images for element collecting animations (raw BMP format)
14951 // ----------------------------------------------------------------------------
14953 static boolean createCollectImage(int element)
14955 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14958 void CreateCollectElementImages(void)
14962 int anim_frames = num_steps - 1;
14963 int tile_size = TILESIZE;
14964 int anim_width = tile_size * anim_frames;
14965 int anim_height = tile_size;
14966 int num_collect_images = 0;
14967 int pos_collect_images = 0;
14969 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14970 if (createCollectImage(i))
14971 num_collect_images++;
14973 Info("Creating %d element collecting animation images ...",
14974 num_collect_images);
14976 int dst_width = anim_width * 2;
14977 int dst_height = anim_height * num_collect_images / 2;
14978 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14979 char *basename_bmp = "RocksCollect.bmp";
14980 char *basename_png = "RocksCollect.png";
14981 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14982 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14983 int len_filename_bmp = strlen(filename_bmp);
14984 int len_filename_png = strlen(filename_png);
14985 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14986 char cmd_convert[max_command_len];
14988 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14992 // force using RGBA surface for destination bitmap
14993 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14994 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14996 dst_bitmap->surface =
14997 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14999 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15001 if (!createCollectImage(i))
15004 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15005 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15006 int graphic = el2img(i);
15007 char *token_name = element_info[i].token_name;
15008 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15009 Bitmap *src_bitmap;
15012 Info("- creating collecting image for '%s' ...", token_name);
15014 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15016 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15017 tile_size, tile_size, 0, 0);
15019 // force using RGBA surface for temporary bitmap (using transparent black)
15020 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15021 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15023 tmp_bitmap->surface =
15024 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15026 tmp_bitmap->surface_masked = tmp_bitmap->surface;
15028 for (j = 0; j < anim_frames; j++)
15030 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15031 int frame_size = frame_size_final * num_steps;
15032 int offset = (tile_size - frame_size_final) / 2;
15033 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15035 while (frame_size > frame_size_final)
15039 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15041 FreeBitmap(frame_bitmap);
15043 frame_bitmap = half_bitmap;
15046 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15047 frame_size_final, frame_size_final,
15048 dst_x + j * tile_size + offset, dst_y + offset);
15050 FreeBitmap(frame_bitmap);
15053 tmp_bitmap->surface_masked = NULL;
15055 FreeBitmap(tmp_bitmap);
15057 pos_collect_images++;
15060 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15061 Fail("cannot save element collecting image file '%s'", filename_bmp);
15063 FreeBitmap(dst_bitmap);
15065 Info("Converting image file from BMP to PNG ...");
15067 if (system(cmd_convert) != 0)
15068 Fail("converting image file failed");
15070 unlink(filename_bmp);
15074 CloseAllAndExit(0);
15078 // ----------------------------------------------------------------------------
15079 // create and save images for custom and group elements (raw BMP format)
15080 // ----------------------------------------------------------------------------
15082 void CreateCustomElementImages(char *directory)
15084 char *src_basename = "RocksCE-template.ilbm";
15085 char *dst_basename = "RocksCE.bmp";
15086 char *src_filename = getPath2(directory, src_basename);
15087 char *dst_filename = getPath2(directory, dst_basename);
15088 Bitmap *src_bitmap;
15090 int yoffset_ce = 0;
15091 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15094 InitVideoDefaults();
15096 ReCreateBitmap(&backbuffer, video.width, video.height);
15098 src_bitmap = LoadImage(src_filename);
15100 bitmap = CreateBitmap(TILEX * 16 * 2,
15101 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15104 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15111 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15112 TILEX * x, TILEY * y + yoffset_ce);
15114 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15116 TILEX * x + TILEX * 16,
15117 TILEY * y + yoffset_ce);
15119 for (j = 2; j >= 0; j--)
15123 BlitBitmap(src_bitmap, bitmap,
15124 TILEX + c * 7, 0, 6, 10,
15125 TILEX * x + 6 + j * 7,
15126 TILEY * y + 11 + yoffset_ce);
15128 BlitBitmap(src_bitmap, bitmap,
15129 TILEX + c * 8, TILEY, 6, 10,
15130 TILEX * 16 + TILEX * x + 6 + j * 8,
15131 TILEY * y + 10 + yoffset_ce);
15137 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15144 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15145 TILEX * x, TILEY * y + yoffset_ge);
15147 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15149 TILEX * x + TILEX * 16,
15150 TILEY * y + yoffset_ge);
15152 for (j = 1; j >= 0; j--)
15156 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15157 TILEX * x + 6 + j * 10,
15158 TILEY * y + 11 + yoffset_ge);
15160 BlitBitmap(src_bitmap, bitmap,
15161 TILEX + c * 8, TILEY + 12, 6, 10,
15162 TILEX * 16 + TILEX * x + 10 + j * 8,
15163 TILEY * y + 10 + yoffset_ge);
15169 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15170 Fail("cannot save CE graphics file '%s'", dst_filename);
15172 FreeBitmap(bitmap);
15174 CloseAllAndExit(0);