1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
28 #define ENABLE_UNUSED_CODE 0 // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
30 #define ENABLE_RESERVED_CODE 0 // reserved for later use
32 #define CHUNK_ID_LEN 4 // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE -1 // do not write chunk size
36 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
39 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
62 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
185 -1, SAVE_CONF_ALWAYS,
186 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
191 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
206 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
207 &li.em_slippery_gems, FALSE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
212 &li.use_custom_template, FALSE
216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
217 &li.can_move_into_acid_bits, ~0 // default: everything can
221 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
222 &li.dont_collide_with_bits, ~0 // default: always deadly
226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
227 &li.em_explodes_by_fire, FALSE
231 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
232 &li.score[SC_TIME_BONUS], 1
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
237 &li.auto_exit_sokoban, FALSE
241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
242 &li.auto_count_gems, FALSE
246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
247 &li.solved_by_one_player, FALSE
251 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
252 &li.time_score_base, 1
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
257 &li.rate_time_over_score, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
262 &li.bd_intermission, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
267 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
272 &li.bd_pal_timing, FALSE
276 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
277 &li.bd_cycle_delay_ms, 200
281 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
282 &li.bd_cycle_delay_c64, 0
286 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
287 &li.bd_hatching_delay_cycles, 21
291 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
292 &li.bd_hatching_delay_seconds, 2
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
297 &li.bd_line_shifting_borders, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
302 &li.bd_scan_first_and_last_row, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
307 &li.bd_short_explosions, TRUE
311 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
312 &li.bd_cave_random_seed_c64, 0
316 TYPE_INTEGER, CONF_VALUE_32_BIT(3),
317 &li.bd_color_b, GD_C64_COLOR(0)
321 TYPE_INTEGER, CONF_VALUE_32_BIT(4),
322 &li.bd_color_0, GD_C64_COLOR(0)
326 TYPE_INTEGER, CONF_VALUE_32_BIT(5),
327 &li.bd_color_1, GD_C64_COLOR(8)
331 TYPE_INTEGER, CONF_VALUE_32_BIT(6),
332 &li.bd_color_2, GD_C64_COLOR(11)
336 TYPE_INTEGER, CONF_VALUE_32_BIT(7),
337 &li.bd_color_3, GD_C64_COLOR(1)
341 TYPE_INTEGER, CONF_VALUE_32_BIT(8),
342 &li.bd_color_4, GD_C64_COLOR(5)
346 TYPE_INTEGER, CONF_VALUE_32_BIT(9),
347 &li.bd_color_5, GD_C64_COLOR(6)
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
359 // (these values are the same for each player)
362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
363 &li.block_last_field, FALSE // default case for EM levels
367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
368 &li.sp_block_last_field, TRUE // default case for SP levels
372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
373 &li.instant_relocation, FALSE
377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
378 &li.can_pass_to_walkable, FALSE
382 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
383 &li.block_snap_field, TRUE
387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
388 &li.continuous_snapping, TRUE
392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
393 &li.shifted_relocation, FALSE
397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
398 &li.lazy_relocation, FALSE
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
403 &li.finish_dig_collect, TRUE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
408 &li.keep_walkable_ce, FALSE
411 // (these values are different for each player)
414 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
420 &li.initial_player_gravity[0], FALSE
424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
425 &li.use_start_element[0], FALSE
429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
430 &li.start_element[0], EL_PLAYER_1
434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
435 &li.use_artwork_element[0], FALSE
439 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
440 &li.artwork_element[0], EL_PLAYER_1
444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
445 &li.use_explosion_element[0], FALSE
449 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
450 &li.explosion_element[0], EL_PLAYER_1
454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
455 &li.use_initial_inventory[0], FALSE
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
460 &li.initial_inventory_size[0], 1
464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
465 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
471 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
472 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
476 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
477 &li.initial_player_gravity[1], FALSE
481 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
482 &li.use_start_element[1], FALSE
486 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
487 &li.start_element[1], EL_PLAYER_2
491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
492 &li.use_artwork_element[1], FALSE
496 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
497 &li.artwork_element[1], EL_PLAYER_2
501 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
502 &li.use_explosion_element[1], FALSE
506 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
507 &li.explosion_element[1], EL_PLAYER_2
511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
512 &li.use_initial_inventory[1], FALSE
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
517 &li.initial_inventory_size[1], 1
521 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
522 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
528 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
529 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
533 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
534 &li.initial_player_gravity[2], FALSE
538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
539 &li.use_start_element[2], FALSE
543 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
544 &li.start_element[2], EL_PLAYER_3
548 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
549 &li.use_artwork_element[2], FALSE
553 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
554 &li.artwork_element[2], EL_PLAYER_3
558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
559 &li.use_explosion_element[2], FALSE
563 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
564 &li.explosion_element[2], EL_PLAYER_3
568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
569 &li.use_initial_inventory[2], FALSE
573 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
574 &li.initial_inventory_size[2], 1
578 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
579 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
585 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
586 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
590 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
591 &li.initial_player_gravity[3], FALSE
595 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
596 &li.use_start_element[3], FALSE
600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
601 &li.start_element[3], EL_PLAYER_4
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
606 &li.use_artwork_element[3], FALSE
610 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
611 &li.artwork_element[3], EL_PLAYER_4
615 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
616 &li.use_explosion_element[3], FALSE
620 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
621 &li.explosion_element[3], EL_PLAYER_4
625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
626 &li.use_initial_inventory[3], FALSE
630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
631 &li.initial_inventory_size[3], 1
635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
636 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
640 // (these values are only valid for BD style levels)
641 // (some values for BD style amoeba following below)
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
645 &li.bd_diagonal_movements, FALSE
649 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
650 &li.bd_topmost_player_active, TRUE
654 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
655 &li.bd_pushing_prob, 25
659 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
660 &li.bd_pushing_prob_with_sweet, 100
664 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
665 &li.bd_push_mega_rock_with_sweet, FALSE
669 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
670 &li.bd_snap_element, EL_EMPTY
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_sand_looks_like, EL_BD_SAND
681 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
682 &li.bd_rock_turns_to_on_falling, EL_BD_ROCK_FALLING
686 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
687 &li.bd_rock_turns_to_on_impact, EL_BD_ROCK
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND_EXTRA], 20
697 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
698 &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
703 &li.bd_diamond_turns_to_on_impact, EL_BD_DIAMOND
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_firefly_explodes_to, EL_BD_EXPLODING_1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_firefly_2_explodes_to, EL_BD_EXPLODING_1
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_butterfly_explodes_to, EL_BD_DIAMOND_GROWING_1
725 EL_BD_BUTTERFLY_2, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_butterfly_2_explodes_to, EL_BD_DIAMOND_GROWING_1
732 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
733 &li.bd_stonefly_explodes_to, EL_BD_ROCK_GROWING_1
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
739 &li.bd_dragonfly_explodes_to, EL_BD_EXPLODING_1
743 EL_BD_DIAMOND_GROWING_5, -1,
744 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
745 &li.bd_diamond_birth_turns_to, EL_BD_DIAMOND
749 EL_BD_BOMB_EXPLODING_4, -1,
750 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
751 &li.bd_bomb_explosion_turns_to, EL_BD_WALL
755 EL_BD_NITRO_PACK_EXPLODING_4, -1,
756 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
757 &li.bd_nitro_explosion_turns_to, EL_EMPTY
761 EL_BD_EXPLODING_5, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
763 &li.bd_explosion_turns_to, EL_EMPTY
767 EL_BD_MAGIC_WALL, -1,
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
769 &li.bd_magic_wall_wait_hatching, FALSE
772 EL_BD_MAGIC_WALL, -1,
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
774 &li.bd_magic_wall_stops_amoeba, TRUE
777 EL_BD_MAGIC_WALL, -1,
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
779 &li.bd_magic_wall_zero_infinite, TRUE
782 EL_BD_MAGIC_WALL, -1,
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
784 &li.bd_magic_wall_break_scan, FALSE
787 EL_BD_MAGIC_WALL, -1,
788 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
789 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
792 EL_BD_MAGIC_WALL, -1,
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
794 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
797 EL_BD_MAGIC_WALL, -1,
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
799 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
802 EL_BD_MAGIC_WALL, -1,
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
804 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
807 EL_BD_MAGIC_WALL, -1,
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
809 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
812 EL_BD_MAGIC_WALL, -1,
813 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
814 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
817 EL_BD_MAGIC_WALL, -1,
818 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
819 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
824 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
825 &li.bd_clock_extra_time, 30
829 EL_BD_VOODOO_DOLL, -1,
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
831 &li.bd_voodoo_collects_diamonds, FALSE
834 EL_BD_VOODOO_DOLL, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
836 &li.bd_voodoo_hurt_kills_player, FALSE
839 EL_BD_VOODOO_DOLL, -1,
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
841 &li.bd_voodoo_dies_by_rock, FALSE
844 EL_BD_VOODOO_DOLL, -1,
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
846 &li.bd_voodoo_vanish_by_explosion, TRUE
849 EL_BD_VOODOO_DOLL, -1,
850 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
851 &li.bd_voodoo_penalty_time, 30
856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
857 &li.bd_slime_is_predictable, TRUE
861 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
862 &li.bd_slime_permeability_rate, 100
866 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
867 &li.bd_slime_permeability_bits_c64, 0
871 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
872 &li.bd_slime_random_seed_c64, -1
876 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
877 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
882 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
887 &li.bd_slime_eats_element_2, EL_BD_ROCK
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
892 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
897 &li.bd_slime_eats_element_3, EL_BD_NUT
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
902 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
907 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
908 &li.bd_acid_eats_element, EL_BD_SAND
912 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
913 &li.bd_acid_spread_rate, 3
917 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
918 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
923 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
924 &li.bd_biter_move_delay, 0
928 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
929 &li.bd_biter_eats_element, EL_BD_DIAMOND
934 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
935 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
939 EL_BD_EXPANDABLE_WALL_ANY, -1,
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
941 &li.bd_change_expanding_wall, FALSE
944 EL_BD_EXPANDABLE_WALL_ANY, -1,
945 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
946 &li.bd_expanding_wall_looks_like, EL_BD_WALL
950 EL_BD_REPLICATOR, -1,
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &li.bd_replicators_active, TRUE
955 EL_BD_REPLICATOR, -1,
956 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
957 &li.bd_replicator_create_delay, 4
961 EL_BD_CONVEYOR_LEFT, -1,
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
963 &li.bd_conveyor_belts_active, TRUE
966 EL_BD_CONVEYOR_LEFT, -1,
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
968 &li.bd_conveyor_belts_changed, FALSE
973 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
974 &li.bd_water_cannot_flow_down, FALSE
979 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
980 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
984 EL_BD_PNEUMATIC_HAMMER, -1,
985 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
986 &li.bd_hammer_walls_break_delay, 5
989 EL_BD_PNEUMATIC_HAMMER, -1,
990 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
991 &li.bd_hammer_walls_reappear, FALSE
994 EL_BD_PNEUMATIC_HAMMER, -1,
995 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
996 &li.bd_hammer_walls_reappear_delay, 100
1000 EL_BD_ROCKET_LAUNCHER, -1,
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1002 &li.bd_infinite_rockets, FALSE
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1008 &li.bd_num_skeletons_needed_for_pot, 5
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1013 &li.bd_skeleton_worth_num_diamonds, 0
1017 EL_BD_CREATURE_SWITCH, -1,
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1019 &li.bd_creatures_start_backwards, FALSE
1022 EL_BD_CREATURE_SWITCH, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1024 &li.bd_creatures_turn_on_hatching, FALSE
1027 EL_BD_CREATURE_SWITCH, -1,
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1029 &li.bd_creatures_auto_turn_delay, 0
1033 EL_BD_GRAVITY_SWITCH, -1,
1034 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1035 &li.bd_gravity_direction, GD_MV_DOWN
1038 EL_BD_GRAVITY_SWITCH, -1,
1039 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1040 &li.bd_gravity_switch_active, FALSE
1043 EL_BD_GRAVITY_SWITCH, -1,
1044 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1045 &li.bd_gravity_switch_delay, 10
1048 EL_BD_GRAVITY_SWITCH, -1,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1050 &li.bd_gravity_affects_all, TRUE
1053 // (the following values are related to various game elements)
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.score[SC_EMERALD], 10
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.score[SC_DIAMOND], 10
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1070 &li.score[SC_BUG], 10
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1076 &li.score[SC_SPACESHIP], 10
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1082 &li.score[SC_PACMAN], 10
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1088 &li.score[SC_NUT], 10
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1094 &li.score[SC_DYNAMITE], 10
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1100 &li.score[SC_KEY], 10
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1106 &li.score[SC_PEARL], 10
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1112 &li.score[SC_CRYSTAL], 10
1115 // (amoeba values used by R'n'D game engine only)
1118 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1119 &li.amoeba_content, EL_DIAMOND
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1124 &li.amoeba_speed, 10
1128 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1129 &li.grow_into_diggable, TRUE
1131 // (amoeba values used by BD game engine only)
1134 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1135 &li.bd_amoeba_wait_for_hatching, FALSE
1139 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1140 &li.bd_amoeba_start_immediately, TRUE
1144 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1145 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1150 &li.bd_amoeba_threshold_too_big, 200
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1155 &li.bd_amoeba_slow_growth_time, 200
1159 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1160 &li.bd_amoeba_slow_growth_rate, 3
1164 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1165 &li.bd_amoeba_fast_growth_rate, 25
1169 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1170 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1175 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1181 &li.bd_amoeba_2_threshold_too_big, 200
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1186 &li.bd_amoeba_2_slow_growth_time, 200
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1191 &li.bd_amoeba_2_slow_growth_rate, 3
1195 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1196 &li.bd_amoeba_2_fast_growth_rate, 25
1200 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1201 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1205 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1206 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1211 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1215 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1216 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1221 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1222 &li.yamyam_content, EL_ROCK, NULL,
1223 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1227 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1228 &li.score[SC_YAMYAM], 10
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1234 &li.score[SC_ROBOT], 10
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1244 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1250 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1251 &li.time_magic_wall, 10
1255 EL_GAME_OF_LIFE, -1,
1256 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1257 &li.game_of_life[0], 2
1260 EL_GAME_OF_LIFE, -1,
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1262 &li.game_of_life[1], 3
1265 EL_GAME_OF_LIFE, -1,
1266 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1267 &li.game_of_life[2], 3
1270 EL_GAME_OF_LIFE, -1,
1271 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1272 &li.game_of_life[3], 3
1275 EL_GAME_OF_LIFE, -1,
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1277 &li.use_life_bugs, FALSE
1282 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1287 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1292 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1297 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1302 EL_TIMEGATE_SWITCH, -1,
1303 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1304 &li.time_timegate, 10
1308 EL_LIGHT_SWITCH_ACTIVE, -1,
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1314 EL_SHIELD_NORMAL, -1,
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1316 &li.shield_normal_time, 10
1319 EL_SHIELD_NORMAL, -1,
1320 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1321 &li.score[SC_SHIELD], 10
1325 EL_SHIELD_DEADLY, -1,
1326 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1327 &li.shield_deadly_time, 10
1330 EL_SHIELD_DEADLY, -1,
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1332 &li.score[SC_SHIELD], 10
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1343 &li.extra_time_score, 10
1347 EL_TIME_ORB_FULL, -1,
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1349 &li.time_orb_time, 10
1352 EL_TIME_ORB_FULL, -1,
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1354 &li.use_time_orb_bug, FALSE
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1360 &li.use_spring_bug, FALSE
1365 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1366 &li.android_move_time, 10
1370 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1371 &li.android_clone_time, 10
1374 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1375 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1376 &li.android_clone_element[0], EL_EMPTY, NULL,
1377 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1381 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1382 &li.android_clone_element[0], EL_EMPTY, NULL,
1383 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1388 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1389 &li.lenses_score, 10
1393 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1398 EL_EMC_MAGNIFIER, -1,
1399 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1400 &li.magnify_score, 10
1403 EL_EMC_MAGNIFIER, -1,
1404 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1405 &li.magnify_time, 10
1409 EL_EMC_MAGIC_BALL, -1,
1410 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1414 EL_EMC_MAGIC_BALL, -1,
1415 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1416 &li.ball_random, FALSE
1419 EL_EMC_MAGIC_BALL, -1,
1420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1421 &li.ball_active_initial, FALSE
1424 EL_EMC_MAGIC_BALL, -1,
1425 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1426 &li.ball_content, EL_EMPTY, NULL,
1427 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1431 EL_SOKOBAN_FIELD_EMPTY, -1,
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1433 &li.sb_fields_needed, TRUE
1437 EL_SOKOBAN_OBJECT, -1,
1438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1439 &li.sb_objects_needed, TRUE
1444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1445 &li.mm_laser_red, FALSE
1449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1450 &li.mm_laser_green, FALSE
1454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1455 &li.mm_laser_blue, TRUE
1460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1461 &li.df_laser_red, TRUE
1465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1466 &li.df_laser_green, TRUE
1470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1471 &li.df_laser_blue, FALSE
1475 EL_MM_FUSE_ACTIVE, -1,
1476 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1477 &li.mm_time_fuse, 25
1481 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1482 &li.mm_time_bomb, 75
1486 EL_MM_GRAY_BALL, -1,
1487 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1488 &li.mm_time_ball, 75
1491 EL_MM_GRAY_BALL, -1,
1492 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1493 &li.mm_ball_choice_mode, ANIM_RANDOM
1496 EL_MM_GRAY_BALL, -1,
1497 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1498 &li.mm_ball_content, EL_EMPTY, NULL,
1499 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1502 EL_MM_GRAY_BALL, -1,
1503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1504 &li.rotate_mm_ball_content, TRUE
1507 EL_MM_GRAY_BALL, -1,
1508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1509 &li.explode_mm_ball, FALSE
1513 EL_MM_STEEL_BLOCK, -1,
1514 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1515 &li.mm_time_block, 75
1518 EL_MM_LIGHTBALL, -1,
1519 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1520 &li.score[SC_ELEM_BONUS], 10
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1534 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1535 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1539 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1540 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1545 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1546 &xx_envelope.autowrap, FALSE
1550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1551 &xx_envelope.centered, FALSE
1556 TYPE_STRING, CONF_VALUE_BYTES(1),
1557 &xx_envelope.text, -1, NULL,
1558 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1559 &xx_default_string_empty[0]
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1573 TYPE_STRING, CONF_VALUE_BYTES(1),
1574 &xx_ei.description[0], -1,
1575 &yy_ei.description[0],
1576 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1577 &xx_default_description[0]
1582 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1583 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1586 #if ENABLE_RESERVED_CODE
1587 // (reserved for later use)
1590 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1591 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1599 &xx_ei.use_gfx_element, FALSE,
1600 &yy_ei.use_gfx_element
1604 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1605 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1606 &yy_ei.gfx_element_initial
1611 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1612 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1613 &yy_ei.access_direction
1618 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1619 &xx_ei.collect_score_initial, 10,
1620 &yy_ei.collect_score_initial
1624 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1625 &xx_ei.collect_count_initial, 1,
1626 &yy_ei.collect_count_initial
1631 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1632 &xx_ei.ce_value_fixed_initial, 0,
1633 &yy_ei.ce_value_fixed_initial
1637 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1638 &xx_ei.ce_value_random_initial, 0,
1639 &yy_ei.ce_value_random_initial
1643 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1644 &xx_ei.use_last_ce_value, FALSE,
1645 &yy_ei.use_last_ce_value
1650 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1651 &xx_ei.push_delay_fixed, 8,
1652 &yy_ei.push_delay_fixed
1656 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1657 &xx_ei.push_delay_random, 8,
1658 &yy_ei.push_delay_random
1662 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1663 &xx_ei.drop_delay_fixed, 0,
1664 &yy_ei.drop_delay_fixed
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1669 &xx_ei.drop_delay_random, 0,
1670 &yy_ei.drop_delay_random
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1675 &xx_ei.move_delay_fixed, 0,
1676 &yy_ei.move_delay_fixed
1680 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1681 &xx_ei.move_delay_random, 0,
1682 &yy_ei.move_delay_random
1686 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1687 &xx_ei.step_delay_fixed, 0,
1688 &yy_ei.step_delay_fixed
1692 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1693 &xx_ei.step_delay_random, 0,
1694 &yy_ei.step_delay_random
1699 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1700 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1705 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1706 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1707 &yy_ei.move_direction_initial
1711 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1712 &xx_ei.move_stepsize, TILEX / 8,
1713 &yy_ei.move_stepsize
1718 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1719 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1720 &yy_ei.move_enter_element
1724 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1725 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1726 &yy_ei.move_leave_element
1730 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1731 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1732 &yy_ei.move_leave_type
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1738 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1739 &yy_ei.slippery_type
1744 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1745 &xx_ei.explosion_type, EXPLODES_3X3,
1746 &yy_ei.explosion_type
1750 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1751 &xx_ei.explosion_delay, 16,
1752 &yy_ei.explosion_delay
1756 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1757 &xx_ei.ignition_delay, 8,
1758 &yy_ei.ignition_delay
1763 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1764 &xx_ei.content, EL_EMPTY_SPACE,
1766 &xx_num_contents, 1, 1
1769 // ---------- "num_change_pages" must be the last entry ---------------------
1772 -1, SAVE_CONF_ALWAYS,
1773 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1774 &xx_ei.num_change_pages, 1,
1775 &yy_ei.num_change_pages
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1788 // ---------- "current_change_page" must be the first entry -----------------
1791 -1, SAVE_CONF_ALWAYS,
1792 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1793 &xx_current_change_page, -1
1796 // ---------- (the remaining entries can be in any order) -------------------
1800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1801 &xx_change.can_change, FALSE
1806 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1807 &xx_event_bits[0], 0
1811 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1812 &xx_event_bits[1], 0
1817 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1818 &xx_change.trigger_player, CH_PLAYER_ANY
1822 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1823 &xx_change.trigger_side, CH_SIDE_ANY
1827 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1828 &xx_change.trigger_page, CH_PAGE_ANY
1833 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1834 &xx_change.target_element, EL_EMPTY_SPACE
1839 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1840 &xx_change.delay_fixed, 0
1844 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1845 &xx_change.delay_random, 0
1849 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1850 &xx_change.delay_frames, FRAMES_PER_SECOND
1855 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1856 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1862 &xx_change.explode, FALSE
1866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1867 &xx_change.use_target_content, FALSE
1871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1872 &xx_change.only_if_complete, FALSE
1876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1877 &xx_change.use_random_replace, FALSE
1881 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1882 &xx_change.random_percentage, 100
1886 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1887 &xx_change.replace_when, CP_WHEN_EMPTY
1892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1893 &xx_change.has_action, FALSE
1897 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1898 &xx_change.action_type, CA_NO_ACTION
1902 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1903 &xx_change.action_mode, CA_MODE_UNDEFINED
1907 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1908 &xx_change.action_arg, CA_ARG_UNDEFINED
1913 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1914 &xx_change.action_element, EL_EMPTY_SPACE
1919 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1920 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1921 &xx_num_contents, 1, 1
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1935 TYPE_STRING, CONF_VALUE_BYTES(1),
1936 &xx_ei.description[0], -1, NULL,
1937 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1938 &xx_default_description[0]
1943 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1944 &xx_ei.use_gfx_element, FALSE
1948 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1949 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1954 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1955 &xx_group.choice_mode, ANIM_RANDOM
1960 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1961 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1962 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1977 &xx_ei.use_gfx_element, FALSE
1981 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1982 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1992 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1997 &li.block_snap_field, TRUE
2001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
2002 &li.continuous_snapping, TRUE
2006 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
2007 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
2011 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
2012 &li.use_start_element[0], FALSE
2016 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
2017 &li.start_element[0], EL_PLAYER_1
2021 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
2022 &li.use_artwork_element[0], FALSE
2026 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
2027 &li.artwork_element[0], EL_PLAYER_1
2031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
2032 &li.use_explosion_element[0], FALSE
2036 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2037 &li.explosion_element[0], EL_PLAYER_1
2052 filetype_id_list[] =
2054 { LEVEL_FILE_TYPE_RND, "RND" },
2055 { LEVEL_FILE_TYPE_BD, "BD" },
2056 { LEVEL_FILE_TYPE_EM, "EM" },
2057 { LEVEL_FILE_TYPE_SP, "SP" },
2058 { LEVEL_FILE_TYPE_DX, "DX" },
2059 { LEVEL_FILE_TYPE_SB, "SB" },
2060 { LEVEL_FILE_TYPE_DC, "DC" },
2061 { LEVEL_FILE_TYPE_MM, "MM" },
2062 { LEVEL_FILE_TYPE_MM, "DF" },
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2071 static boolean check_special_flags(char *flag)
2073 if (strEqual(options.special_flags, flag) ||
2074 strEqual(leveldir_current->special_flags, flag))
2080 static struct DateInfo getCurrentDate(void)
2082 time_t epoch_seconds = time(NULL);
2083 struct tm *now = localtime(&epoch_seconds);
2084 struct DateInfo date;
2086 date.year = now->tm_year + 1900;
2087 date.month = now->tm_mon + 1;
2088 date.day = now->tm_mday;
2090 date.src = DATE_SRC_CLOCK;
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2099 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100 change->has_event[i] = FALSE;
2103 static void resetEventBits(void)
2107 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108 xx_event_bits[i] = 0;
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2115 /* important: only change event flag if corresponding event bit is set
2116 (this is because all xx_event_bits[] values are loaded separately,
2117 and all xx_event_bits[] values are set back to zero before loading
2118 another value xx_event_bits[x] (each value representing 32 flags)) */
2120 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122 change->has_event[i] = TRUE;
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2129 /* in contrast to the above function setEventFlagsFromEventBits(), it
2130 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131 depending on the corresponding change->has_event[i] values here, as
2132 all xx_event_bits[] values are reset in resetEventBits() before */
2134 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135 if (change->has_event[i])
2136 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2141 static char description[MAX_ELEMENT_NAME_LEN + 1];
2142 char *default_description = (ei->custom_description != NULL ?
2143 ei->custom_description :
2144 ei->editor_description);
2147 // always start with reliable default values
2148 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149 description[i] = '\0';
2151 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2154 return &description[0];
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2159 char *default_description = getDefaultElementDescription(ei);
2162 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163 ei->description[i] = default_description[i];
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2170 for (i = 0; conf[i].data_type != -1; i++)
2172 int default_value = conf[i].default_value;
2173 int data_type = conf[i].data_type;
2174 int conf_type = conf[i].conf_type;
2175 int byte_mask = conf_type & CONF_MASK_BYTES;
2177 if (byte_mask == CONF_MASK_MULTI_BYTES)
2179 int default_num_entities = conf[i].default_num_entities;
2180 int max_num_entities = conf[i].max_num_entities;
2182 *(int *)(conf[i].num_entities) = default_num_entities;
2184 if (data_type == TYPE_STRING)
2186 char *default_string = conf[i].default_string;
2187 char *string = (char *)(conf[i].value);
2189 strncpy(string, default_string, max_num_entities);
2191 else if (data_type == TYPE_ELEMENT_LIST)
2193 int *element_array = (int *)(conf[i].value);
2196 for (j = 0; j < max_num_entities; j++)
2197 element_array[j] = default_value;
2199 else if (data_type == TYPE_CONTENT_LIST)
2201 struct Content *content = (struct Content *)(conf[i].value);
2204 for (c = 0; c < max_num_entities; c++)
2205 for (y = 0; y < 3; y++)
2206 for (x = 0; x < 3; x++)
2207 content[c].e[x][y] = default_value;
2210 else // constant size configuration data (1, 2 or 4 bytes)
2212 if (data_type == TYPE_BOOLEAN)
2213 *(boolean *)(conf[i].value) = default_value;
2215 *(int *) (conf[i].value) = default_value;
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2224 for (i = 0; conf[i].data_type != -1; i++)
2226 int data_type = conf[i].data_type;
2227 int conf_type = conf[i].conf_type;
2228 int byte_mask = conf_type & CONF_MASK_BYTES;
2230 if (byte_mask == CONF_MASK_MULTI_BYTES)
2232 int max_num_entities = conf[i].max_num_entities;
2234 if (data_type == TYPE_STRING)
2236 char *string = (char *)(conf[i].value);
2237 char *string_copy = (char *)(conf[i].value_copy);
2239 strncpy(string_copy, string, max_num_entities);
2241 else if (data_type == TYPE_ELEMENT_LIST)
2243 int *element_array = (int *)(conf[i].value);
2244 int *element_array_copy = (int *)(conf[i].value_copy);
2247 for (j = 0; j < max_num_entities; j++)
2248 element_array_copy[j] = element_array[j];
2250 else if (data_type == TYPE_CONTENT_LIST)
2252 struct Content *content = (struct Content *)(conf[i].value);
2253 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2256 for (c = 0; c < max_num_entities; c++)
2257 for (y = 0; y < 3; y++)
2258 for (x = 0; x < 3; x++)
2259 content_copy[c].e[x][y] = content[c].e[x][y];
2262 else // constant size configuration data (1, 2 or 4 bytes)
2264 if (data_type == TYPE_BOOLEAN)
2265 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2267 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2276 xx_ei = *ei_from; // copy element data into temporary buffer
2277 yy_ei = *ei_to; // copy element data into temporary buffer
2279 copyConfigFromConfigList(chunk_config_CUSX_base);
2284 // ---------- reinitialize and copy change pages ----------
2286 ei_to->num_change_pages = ei_from->num_change_pages;
2287 ei_to->current_change_page = ei_from->current_change_page;
2289 setElementChangePages(ei_to, ei_to->num_change_pages);
2291 for (i = 0; i < ei_to->num_change_pages; i++)
2292 ei_to->change_page[i] = ei_from->change_page[i];
2294 // ---------- copy group element info ----------
2295 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2296 *ei_to->group = *ei_from->group;
2298 // mark this custom element as modified
2299 ei_to->modified_settings = TRUE;
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2304 int change_page_size = sizeof(struct ElementChangeInfo);
2306 ei->num_change_pages = MAX(1, change_pages);
2309 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2311 if (ei->current_change_page >= ei->num_change_pages)
2312 ei->current_change_page = ei->num_change_pages - 1;
2314 ei->change = &ei->change_page[ei->current_change_page];
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2319 xx_change = *change; // copy change data into temporary buffer
2321 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2323 *change = xx_change;
2325 resetEventFlags(change);
2327 change->direct_action = 0;
2328 change->other_action = 0;
2330 change->pre_change_function = NULL;
2331 change->change_function = NULL;
2332 change->post_change_function = NULL;
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2339 li = *level; // copy level data into temporary buffer
2340 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2341 *level = li; // copy temporary buffer back to level data
2343 setLevelInfoToDefaults_BD();
2344 setLevelInfoToDefaults_EM();
2345 setLevelInfoToDefaults_SP();
2346 setLevelInfoToDefaults_MM();
2348 level->native_bd_level = &native_bd_level;
2349 level->native_em_level = &native_em_level;
2350 level->native_sp_level = &native_sp_level;
2351 level->native_mm_level = &native_mm_level;
2353 level->file_version = FILE_VERSION_ACTUAL;
2354 level->game_version = GAME_VERSION_ACTUAL;
2356 level->creation_date = getCurrentDate();
2358 level->encoding_16bit_field = TRUE;
2359 level->encoding_16bit_yamyam = TRUE;
2360 level->encoding_16bit_amoeba = TRUE;
2362 // clear level name and level author string buffers
2363 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2364 level->name[i] = '\0';
2365 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2366 level->author[i] = '\0';
2368 // set level name and level author to default values
2369 strcpy(level->name, NAMELESS_LEVEL_NAME);
2370 strcpy(level->author, ANONYMOUS_NAME);
2372 // set level playfield to playable default level with player and exit
2373 for (x = 0; x < MAX_LEV_FIELDX; x++)
2374 for (y = 0; y < MAX_LEV_FIELDY; y++)
2375 level->field[x][y] = EL_SAND;
2377 level->field[0][0] = EL_PLAYER_1;
2378 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2380 BorderElement = EL_STEELWALL;
2382 // detect custom elements when loading them
2383 level->file_has_custom_elements = FALSE;
2385 // set random colors for BD style levels according to preferred color type
2386 SetRandomLevelColors_BD(setup.bd_color_type);
2388 // set default color type and colors for BD style level colors
2389 SetDefaultLevelColorType_BD();
2390 SetDefaultLevelColors_BD();
2392 // set all bug compatibility flags to "false" => do not emulate this bug
2393 level->use_action_after_change_bug = FALSE;
2395 if (leveldir_current)
2397 // try to determine better author name than 'anonymous'
2398 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2400 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2401 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2405 switch (LEVELCLASS(leveldir_current))
2407 case LEVELCLASS_TUTORIAL:
2408 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2411 case LEVELCLASS_CONTRIB:
2412 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2413 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2416 case LEVELCLASS_PRIVATE:
2417 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2418 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2422 // keep default value
2429 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2431 static boolean clipboard_elements_initialized = FALSE;
2434 InitElementPropertiesStatic();
2436 li = *level; // copy level data into temporary buffer
2437 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2438 *level = li; // copy temporary buffer back to level data
2440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2443 struct ElementInfo *ei = &element_info[element];
2445 if (element == EL_MM_GRAY_BALL)
2447 struct LevelInfo_MM *level_mm = level->native_mm_level;
2450 for (j = 0; j < level->num_mm_ball_contents; j++)
2451 level->mm_ball_content[j] =
2452 map_element_MM_to_RND(level_mm->ball_content[j]);
2455 // never initialize clipboard elements after the very first time
2456 // (to be able to use clipboard elements between several levels)
2457 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2460 if (IS_ENVELOPE(element))
2462 int envelope_nr = element - EL_ENVELOPE_1;
2464 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2466 level->envelope[envelope_nr] = xx_envelope;
2469 if (IS_CUSTOM_ELEMENT(element) ||
2470 IS_GROUP_ELEMENT(element) ||
2471 IS_INTERNAL_ELEMENT(element))
2473 xx_ei = *ei; // copy element data into temporary buffer
2475 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2480 setElementChangePages(ei, 1);
2481 setElementChangeInfoToDefaults(ei->change);
2483 if (IS_CUSTOM_ELEMENT(element) ||
2484 IS_GROUP_ELEMENT(element))
2486 setElementDescriptionToDefault(ei);
2488 ei->modified_settings = FALSE;
2491 if (IS_CUSTOM_ELEMENT(element) ||
2492 IS_INTERNAL_ELEMENT(element))
2494 // internal values used in level editor
2496 ei->access_type = 0;
2497 ei->access_layer = 0;
2498 ei->access_protected = 0;
2499 ei->walk_to_action = 0;
2500 ei->smash_targets = 0;
2503 ei->can_explode_by_fire = FALSE;
2504 ei->can_explode_smashed = FALSE;
2505 ei->can_explode_impact = FALSE;
2507 ei->current_change_page = 0;
2510 if (IS_GROUP_ELEMENT(element) ||
2511 IS_INTERNAL_ELEMENT(element))
2513 struct ElementGroupInfo *group;
2515 // initialize memory for list of elements in group
2516 if (ei->group == NULL)
2517 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2521 xx_group = *group; // copy group data into temporary buffer
2523 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2528 if (IS_EMPTY_ELEMENT(element) ||
2529 IS_INTERNAL_ELEMENT(element))
2531 xx_ei = *ei; // copy element data into temporary buffer
2533 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2539 clipboard_elements_initialized = TRUE;
2542 static void setLevelInfoToDefaults(struct LevelInfo *level,
2543 boolean level_info_only,
2544 boolean reset_file_status)
2546 setLevelInfoToDefaults_Level(level);
2548 if (!level_info_only)
2549 setLevelInfoToDefaults_Elements(level);
2551 if (reset_file_status)
2553 level->no_valid_file = FALSE;
2554 level->no_level_file = FALSE;
2557 level->changed = FALSE;
2560 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2562 level_file_info->nr = 0;
2563 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2564 level_file_info->packed = FALSE;
2566 setString(&level_file_info->basename, NULL);
2567 setString(&level_file_info->filename, NULL);
2570 int getMappedElement_SB(int, boolean);
2572 static void ActivateLevelTemplate(void)
2576 if (check_special_flags("load_xsb_to_ces"))
2578 // fill smaller playfields with padding "beyond border wall" elements
2579 if (level.fieldx < level_template.fieldx ||
2580 level.fieldy < level_template.fieldy)
2582 short field[level.fieldx][level.fieldy];
2583 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2584 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2585 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2586 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2588 // copy old playfield (which is smaller than the visible area)
2589 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2590 field[x][y] = level.field[x][y];
2592 // fill new, larger playfield with "beyond border wall" elements
2593 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2594 level.field[x][y] = getMappedElement_SB('_', TRUE);
2596 // copy the old playfield to the middle of the new playfield
2597 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2598 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2600 level.fieldx = new_fieldx;
2601 level.fieldy = new_fieldy;
2605 // Currently there is no special action needed to activate the template
2606 // data, because 'element_info' property settings overwrite the original
2607 // level data, while all other variables do not change.
2609 // Exception: 'from_level_template' elements in the original level playfield
2610 // are overwritten with the corresponding elements at the same position in
2611 // playfield from the level template.
2613 for (x = 0; x < level.fieldx; x++)
2614 for (y = 0; y < level.fieldy; y++)
2615 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2616 level.field[x][y] = level_template.field[x][y];
2618 if (check_special_flags("load_xsb_to_ces"))
2620 struct LevelInfo level_backup = level;
2622 // overwrite all individual level settings from template level settings
2623 level = level_template;
2625 // restore level file info
2626 level.file_info = level_backup.file_info;
2628 // restore playfield size
2629 level.fieldx = level_backup.fieldx;
2630 level.fieldy = level_backup.fieldy;
2632 // restore playfield content
2633 for (x = 0; x < level.fieldx; x++)
2634 for (y = 0; y < level.fieldy; y++)
2635 level.field[x][y] = level_backup.field[x][y];
2637 // restore name and author from individual level
2638 strcpy(level.name, level_backup.name);
2639 strcpy(level.author, level_backup.author);
2641 // restore flag "use_custom_template"
2642 level.use_custom_template = level_backup.use_custom_template;
2646 static boolean checkForPackageFromBasename_BD(char *basename)
2648 // check for native BD level file extensions
2649 if (!strSuffixLower(basename, ".bd") &&
2650 !strSuffixLower(basename, ".bdr") &&
2651 !strSuffixLower(basename, ".brc") &&
2652 !strSuffixLower(basename, ".gds"))
2655 // check for standard single-level BD files (like "001.bd")
2656 if (strSuffixLower(basename, ".bd") &&
2657 strlen(basename) == 6 &&
2658 basename[0] >= '0' && basename[0] <= '9' &&
2659 basename[1] >= '0' && basename[1] <= '9' &&
2660 basename[2] >= '0' && basename[2] <= '9')
2663 // this is a level package in native BD file format
2667 static char *getLevelFilenameFromBasename(char *basename)
2669 static char *filename = NULL;
2671 checked_free(filename);
2673 filename = getPath2(getCurrentLevelDir(), basename);
2678 static int getFileTypeFromBasename(char *basename)
2680 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2682 static char *filename = NULL;
2683 struct stat file_status;
2685 // ---------- try to determine file type from filename ----------
2687 // check for typical filename of a Supaplex level package file
2688 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2689 return LEVEL_FILE_TYPE_SP;
2691 // check for typical filename of a Diamond Caves II level package file
2692 if (strSuffixLower(basename, ".dc") ||
2693 strSuffixLower(basename, ".dc2"))
2694 return LEVEL_FILE_TYPE_DC;
2696 // check for typical filename of a Sokoban level package file
2697 if (strSuffixLower(basename, ".xsb") &&
2698 strchr(basename, '%') == NULL)
2699 return LEVEL_FILE_TYPE_SB;
2701 // check for typical filename of a Boulder Dash (GDash) level package file
2702 if (checkForPackageFromBasename_BD(basename))
2703 return LEVEL_FILE_TYPE_BD;
2705 // ---------- try to determine file type from filesize ----------
2707 checked_free(filename);
2708 filename = getPath2(getCurrentLevelDir(), basename);
2710 if (stat(filename, &file_status) == 0)
2712 // check for typical filesize of a Supaplex level package file
2713 if (file_status.st_size == 170496)
2714 return LEVEL_FILE_TYPE_SP;
2717 return LEVEL_FILE_TYPE_UNKNOWN;
2720 static int getFileTypeFromMagicBytes(char *filename, int type)
2724 if ((file = openFile(filename, MODE_READ)))
2726 char chunk_name[CHUNK_ID_LEN + 1];
2728 getFileChunkBE(file, chunk_name, NULL);
2730 if (strEqual(chunk_name, "MMII") ||
2731 strEqual(chunk_name, "MIRR"))
2732 type = LEVEL_FILE_TYPE_MM;
2740 static boolean checkForPackageFromBasename(char *basename)
2742 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2743 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2745 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2748 static char *getSingleLevelBasenameExt(int nr, char *extension)
2750 static char basename[MAX_FILENAME_LEN];
2753 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2755 sprintf(basename, "%03d.%s", nr, extension);
2760 static char *getSingleLevelBasename(int nr)
2762 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2765 static char *getPackedLevelBasename(int type)
2767 static char basename[MAX_FILENAME_LEN];
2768 char *directory = getCurrentLevelDir();
2770 DirectoryEntry *dir_entry;
2772 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2774 if ((dir = openDirectory(directory)) == NULL)
2776 Warn("cannot read current level directory '%s'", directory);
2781 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2783 char *entry_basename = dir_entry->basename;
2784 int entry_type = getFileTypeFromBasename(entry_basename);
2786 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2788 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2791 strcpy(basename, entry_basename);
2798 closeDirectory(dir);
2803 static char *getSingleLevelFilename(int nr)
2805 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2808 #if ENABLE_UNUSED_CODE
2809 static char *getPackedLevelFilename(int type)
2811 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2815 char *getDefaultLevelFilename(int nr)
2817 return getSingleLevelFilename(nr);
2820 #if ENABLE_UNUSED_CODE
2821 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2825 lfi->packed = FALSE;
2827 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2828 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2832 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2833 int type, char *format, ...)
2835 static char basename[MAX_FILENAME_LEN];
2838 va_start(ap, format);
2839 vsprintf(basename, format, ap);
2843 lfi->packed = FALSE;
2845 setString(&lfi->basename, basename);
2846 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2849 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2855 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2856 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2859 static int getFiletypeFromID(char *filetype_id)
2861 char *filetype_id_lower;
2862 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2865 if (filetype_id == NULL)
2866 return LEVEL_FILE_TYPE_UNKNOWN;
2868 filetype_id_lower = getStringToLower(filetype_id);
2870 for (i = 0; filetype_id_list[i].id != NULL; i++)
2872 char *id_lower = getStringToLower(filetype_id_list[i].id);
2874 if (strEqual(filetype_id_lower, id_lower))
2875 filetype = filetype_id_list[i].filetype;
2879 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2883 free(filetype_id_lower);
2888 char *getLocalLevelTemplateFilename(void)
2890 return getDefaultLevelFilename(-1);
2893 char *getGlobalLevelTemplateFilename(void)
2895 // global variable "leveldir_current" must be modified in the loop below
2896 LevelDirTree *leveldir_current_last = leveldir_current;
2897 char *filename = NULL;
2899 // check for template level in path from current to topmost tree node
2901 while (leveldir_current != NULL)
2903 filename = getDefaultLevelFilename(-1);
2905 if (fileExists(filename))
2908 leveldir_current = leveldir_current->node_parent;
2911 // restore global variable "leveldir_current" modified in above loop
2912 leveldir_current = leveldir_current_last;
2917 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2921 // special case: level number is negative => check for level template file
2924 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2925 getSingleLevelBasename(-1));
2927 // replace local level template filename with global template filename
2928 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2930 // no fallback if template file not existing
2934 // special case: check for file name/pattern specified in "levelinfo.conf"
2935 if (leveldir_current->level_filename != NULL)
2937 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2939 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2940 leveldir_current->level_filename, nr);
2942 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2944 if (fileExists(lfi->filename))
2947 else if (leveldir_current->level_filetype != NULL)
2949 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2951 // check for specified native level file with standard file name
2952 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2953 "%03d.%s", nr, LEVELFILE_EXTENSION);
2954 if (fileExists(lfi->filename))
2958 // check for native Rocks'n'Diamonds level file
2959 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2960 "%03d.%s", nr, LEVELFILE_EXTENSION);
2961 if (fileExists(lfi->filename))
2964 // check for native Boulder Dash level file
2965 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2966 if (fileExists(lfi->filename))
2969 // check for Emerald Mine level file (V1)
2970 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2971 'a' + (nr / 10) % 26, '0' + nr % 10);
2972 if (fileExists(lfi->filename))
2974 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2975 'A' + (nr / 10) % 26, '0' + nr % 10);
2976 if (fileExists(lfi->filename))
2979 // check for Emerald Mine level file (V2 to V5)
2980 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2981 if (fileExists(lfi->filename))
2984 // check for Emerald Mine level file (V6 / single mode)
2985 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2986 if (fileExists(lfi->filename))
2988 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2989 if (fileExists(lfi->filename))
2992 // check for Emerald Mine level file (V6 / teamwork mode)
2993 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2994 if (fileExists(lfi->filename))
2996 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2997 if (fileExists(lfi->filename))
3000 // check for various packed level file formats
3001 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3002 if (fileExists(lfi->filename))
3005 // no known level file found -- use default values (and fail later)
3006 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3007 "%03d.%s", nr, LEVELFILE_EXTENSION);
3010 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3012 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3013 lfi->type = getFileTypeFromBasename(lfi->basename);
3015 if (lfi->type == LEVEL_FILE_TYPE_RND)
3016 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3019 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3021 // always start with reliable default values
3022 setFileInfoToDefaults(level_file_info);
3024 level_file_info->nr = nr; // set requested level number
3026 determineLevelFileInfo_Filename(level_file_info);
3027 determineLevelFileInfo_Filetype(level_file_info);
3030 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3031 struct LevelFileInfo *lfi_to)
3033 lfi_to->nr = lfi_from->nr;
3034 lfi_to->type = lfi_from->type;
3035 lfi_to->packed = lfi_from->packed;
3037 setString(&lfi_to->basename, lfi_from->basename);
3038 setString(&lfi_to->filename, lfi_from->filename);
3041 // ----------------------------------------------------------------------------
3042 // functions for loading R'n'D level
3043 // ----------------------------------------------------------------------------
3045 int getMappedElement(int element)
3047 // remap some (historic, now obsolete) elements
3051 case EL_PLAYER_OBSOLETE:
3052 element = EL_PLAYER_1;
3055 case EL_KEY_OBSOLETE:
3059 case EL_EM_KEY_1_FILE_OBSOLETE:
3060 element = EL_EM_KEY_1;
3063 case EL_EM_KEY_2_FILE_OBSOLETE:
3064 element = EL_EM_KEY_2;
3067 case EL_EM_KEY_3_FILE_OBSOLETE:
3068 element = EL_EM_KEY_3;
3071 case EL_EM_KEY_4_FILE_OBSOLETE:
3072 element = EL_EM_KEY_4;
3075 case EL_ENVELOPE_OBSOLETE:
3076 element = EL_ENVELOPE_1;
3084 if (element >= NUM_FILE_ELEMENTS)
3086 Warn("invalid level element %d", element);
3088 element = EL_UNKNOWN;
3096 static int getMappedElementByVersion(int element, int game_version)
3098 // remap some elements due to certain game version
3100 if (game_version <= VERSION_IDENT(2,2,0,0))
3102 // map game font elements
3103 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3104 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3105 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3106 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3109 if (game_version < VERSION_IDENT(3,0,0,0))
3111 // map Supaplex gravity tube elements
3112 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3113 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3114 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3115 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3122 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3124 level->file_version = getFileVersion(file);
3125 level->game_version = getFileVersion(file);
3130 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3132 level->creation_date.year = getFile16BitBE(file);
3133 level->creation_date.month = getFile8Bit(file);
3134 level->creation_date.day = getFile8Bit(file);
3136 level->creation_date.src = DATE_SRC_LEVELFILE;
3141 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3143 int initial_player_stepsize;
3144 int initial_player_gravity;
3147 level->fieldx = getFile8Bit(file);
3148 level->fieldy = getFile8Bit(file);
3150 level->time = getFile16BitBE(file);
3151 level->gems_needed = getFile16BitBE(file);
3153 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3154 level->name[i] = getFile8Bit(file);
3155 level->name[MAX_LEVEL_NAME_LEN] = 0;
3157 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3158 level->score[i] = getFile8Bit(file);
3160 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3161 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3162 for (y = 0; y < 3; y++)
3163 for (x = 0; x < 3; x++)
3164 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3166 level->amoeba_speed = getFile8Bit(file);
3167 level->time_magic_wall = getFile8Bit(file);
3168 level->time_wheel = getFile8Bit(file);
3169 level->amoeba_content = getMappedElement(getFile8Bit(file));
3171 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3174 for (i = 0; i < MAX_PLAYERS; i++)
3175 level->initial_player_stepsize[i] = initial_player_stepsize;
3177 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3179 for (i = 0; i < MAX_PLAYERS; i++)
3180 level->initial_player_gravity[i] = initial_player_gravity;
3182 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3183 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3185 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3187 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3188 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3189 level->can_move_into_acid_bits = getFile32BitBE(file);
3190 level->dont_collide_with_bits = getFile8Bit(file);
3192 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3193 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3195 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3196 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3197 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3199 level->game_engine_type = getFile8Bit(file);
3201 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3206 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3210 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3211 level->name[i] = getFile8Bit(file);
3212 level->name[MAX_LEVEL_NAME_LEN] = 0;
3217 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3221 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3222 level->author[i] = getFile8Bit(file);
3223 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3228 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3231 int chunk_size_expected = level->fieldx * level->fieldy;
3233 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3234 stored with 16-bit encoding (and should be twice as big then).
3235 Even worse, playfield data was stored 16-bit when only yamyam content
3236 contained 16-bit elements and vice versa. */
3238 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3239 chunk_size_expected *= 2;
3241 if (chunk_size_expected != chunk_size)
3243 ReadUnusedBytesFromFile(file, chunk_size);
3244 return chunk_size_expected;
3247 for (y = 0; y < level->fieldy; y++)
3248 for (x = 0; x < level->fieldx; x++)
3249 level->field[x][y] =
3250 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3255 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3258 int header_size = 4;
3259 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3260 int chunk_size_expected = header_size + content_size;
3262 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3263 stored with 16-bit encoding (and should be twice as big then).
3264 Even worse, playfield data was stored 16-bit when only yamyam content
3265 contained 16-bit elements and vice versa. */
3267 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3268 chunk_size_expected += content_size;
3270 if (chunk_size_expected != chunk_size)
3272 ReadUnusedBytesFromFile(file, chunk_size);
3273 return chunk_size_expected;
3277 level->num_yamyam_contents = getFile8Bit(file);
3281 // correct invalid number of content fields -- should never happen
3282 if (level->num_yamyam_contents < 1 ||
3283 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3284 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3286 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3287 for (y = 0; y < 3; y++)
3288 for (x = 0; x < 3; x++)
3289 level->yamyam_content[i].e[x][y] =
3290 getMappedElement(level->encoding_16bit_field ?
3291 getFile16BitBE(file) : getFile8Bit(file));
3295 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3300 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3302 element = getMappedElement(getFile16BitBE(file));
3303 num_contents = getFile8Bit(file);
3305 getFile8Bit(file); // content x size (unused)
3306 getFile8Bit(file); // content y size (unused)
3308 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3310 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3311 for (y = 0; y < 3; y++)
3312 for (x = 0; x < 3; x++)
3313 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3315 // correct invalid number of content fields -- should never happen
3316 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3317 num_contents = STD_ELEMENT_CONTENTS;
3319 if (element == EL_YAMYAM)
3321 level->num_yamyam_contents = num_contents;
3323 for (i = 0; i < num_contents; i++)
3324 for (y = 0; y < 3; y++)
3325 for (x = 0; x < 3; x++)
3326 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3328 else if (element == EL_BD_AMOEBA)
3330 level->amoeba_content = content_array[0][0][0];
3334 Warn("cannot load content for element '%d'", element);
3340 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3346 int chunk_size_expected;
3348 element = getMappedElement(getFile16BitBE(file));
3349 if (!IS_ENVELOPE(element))
3350 element = EL_ENVELOPE_1;
3352 envelope_nr = element - EL_ENVELOPE_1;
3354 envelope_len = getFile16BitBE(file);
3356 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3357 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3359 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3361 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3362 if (chunk_size_expected != chunk_size)
3364 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3365 return chunk_size_expected;
3368 for (i = 0; i < envelope_len; i++)
3369 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3374 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3376 int num_changed_custom_elements = getFile16BitBE(file);
3377 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3380 if (chunk_size_expected != chunk_size)
3382 ReadUnusedBytesFromFile(file, chunk_size - 2);
3383 return chunk_size_expected;
3386 for (i = 0; i < num_changed_custom_elements; i++)
3388 int element = getMappedElement(getFile16BitBE(file));
3389 int properties = getFile32BitBE(file);
3391 if (IS_CUSTOM_ELEMENT(element))
3392 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3394 Warn("invalid custom element number %d", element);
3396 // older game versions that wrote level files with CUS1 chunks used
3397 // different default push delay values (not yet stored in level file)
3398 element_info[element].push_delay_fixed = 2;
3399 element_info[element].push_delay_random = 8;
3402 level->file_has_custom_elements = TRUE;
3407 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3409 int num_changed_custom_elements = getFile16BitBE(file);
3410 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3413 if (chunk_size_expected != chunk_size)
3415 ReadUnusedBytesFromFile(file, chunk_size - 2);
3416 return chunk_size_expected;
3419 for (i = 0; i < num_changed_custom_elements; i++)
3421 int element = getMappedElement(getFile16BitBE(file));
3422 int custom_target_element = getMappedElement(getFile16BitBE(file));
3424 if (IS_CUSTOM_ELEMENT(element))
3425 element_info[element].change->target_element = custom_target_element;
3427 Warn("invalid custom element number %d", element);
3430 level->file_has_custom_elements = TRUE;
3435 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3437 int num_changed_custom_elements = getFile16BitBE(file);
3438 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
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 struct ElementInfo *ei = &element_info[element];
3451 unsigned int event_bits;
3453 if (!IS_CUSTOM_ELEMENT(element))
3455 Warn("invalid custom element number %d", element);
3457 element = EL_INTERNAL_DUMMY;
3460 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3461 ei->description[j] = getFile8Bit(file);
3462 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3464 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3466 // some free bytes for future properties and padding
3467 ReadUnusedBytesFromFile(file, 7);
3469 ei->use_gfx_element = getFile8Bit(file);
3470 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3472 ei->collect_score_initial = getFile8Bit(file);
3473 ei->collect_count_initial = getFile8Bit(file);
3475 ei->push_delay_fixed = getFile16BitBE(file);
3476 ei->push_delay_random = getFile16BitBE(file);
3477 ei->move_delay_fixed = getFile16BitBE(file);
3478 ei->move_delay_random = getFile16BitBE(file);
3480 ei->move_pattern = getFile16BitBE(file);
3481 ei->move_direction_initial = getFile8Bit(file);
3482 ei->move_stepsize = getFile8Bit(file);
3484 for (y = 0; y < 3; y++)
3485 for (x = 0; x < 3; x++)
3486 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3488 // bits 0 - 31 of "has_event[]"
3489 event_bits = getFile32BitBE(file);
3490 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3491 if (event_bits & (1u << j))
3492 ei->change->has_event[j] = TRUE;
3494 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3496 ei->change->delay_fixed = getFile16BitBE(file);
3497 ei->change->delay_random = getFile16BitBE(file);
3498 ei->change->delay_frames = getFile16BitBE(file);
3500 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3502 ei->change->explode = getFile8Bit(file);
3503 ei->change->use_target_content = getFile8Bit(file);
3504 ei->change->only_if_complete = getFile8Bit(file);
3505 ei->change->use_random_replace = getFile8Bit(file);
3507 ei->change->random_percentage = getFile8Bit(file);
3508 ei->change->replace_when = getFile8Bit(file);
3510 for (y = 0; y < 3; y++)
3511 for (x = 0; x < 3; x++)
3512 ei->change->target_content.e[x][y] =
3513 getMappedElement(getFile16BitBE(file));
3515 ei->slippery_type = getFile8Bit(file);
3517 // some free bytes for future properties and padding
3518 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3520 // mark that this custom element has been modified
3521 ei->modified_settings = TRUE;
3524 level->file_has_custom_elements = TRUE;
3529 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3531 struct ElementInfo *ei;
3532 int chunk_size_expected;
3536 // ---------- custom element base property values (96 bytes) ----------------
3538 element = getMappedElement(getFile16BitBE(file));
3540 if (!IS_CUSTOM_ELEMENT(element))
3542 Warn("invalid custom element number %d", element);
3544 ReadUnusedBytesFromFile(file, chunk_size - 2);
3549 ei = &element_info[element];
3551 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3552 ei->description[i] = getFile8Bit(file);
3553 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3555 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3557 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3559 ei->num_change_pages = getFile8Bit(file);
3561 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3562 if (chunk_size_expected != chunk_size)
3564 ReadUnusedBytesFromFile(file, chunk_size - 43);
3565 return chunk_size_expected;
3568 ei->ce_value_fixed_initial = getFile16BitBE(file);
3569 ei->ce_value_random_initial = getFile16BitBE(file);
3570 ei->use_last_ce_value = getFile8Bit(file);
3572 ei->use_gfx_element = getFile8Bit(file);
3573 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3575 ei->collect_score_initial = getFile8Bit(file);
3576 ei->collect_count_initial = getFile8Bit(file);
3578 ei->drop_delay_fixed = getFile8Bit(file);
3579 ei->push_delay_fixed = getFile8Bit(file);
3580 ei->drop_delay_random = getFile8Bit(file);
3581 ei->push_delay_random = getFile8Bit(file);
3582 ei->move_delay_fixed = getFile16BitBE(file);
3583 ei->move_delay_random = getFile16BitBE(file);
3585 // bits 0 - 15 of "move_pattern" ...
3586 ei->move_pattern = getFile16BitBE(file);
3587 ei->move_direction_initial = getFile8Bit(file);
3588 ei->move_stepsize = getFile8Bit(file);
3590 ei->slippery_type = getFile8Bit(file);
3592 for (y = 0; y < 3; y++)
3593 for (x = 0; x < 3; x++)
3594 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3596 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3597 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3598 ei->move_leave_type = getFile8Bit(file);
3600 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3601 ei->move_pattern |= (getFile16BitBE(file) << 16);
3603 ei->access_direction = getFile8Bit(file);
3605 ei->explosion_delay = getFile8Bit(file);
3606 ei->ignition_delay = getFile8Bit(file);
3607 ei->explosion_type = getFile8Bit(file);
3609 // some free bytes for future custom property values and padding
3610 ReadUnusedBytesFromFile(file, 1);
3612 // ---------- change page property values (48 bytes) ------------------------
3614 setElementChangePages(ei, ei->num_change_pages);
3616 for (i = 0; i < ei->num_change_pages; i++)
3618 struct ElementChangeInfo *change = &ei->change_page[i];
3619 unsigned int event_bits;
3621 // always start with reliable default values
3622 setElementChangeInfoToDefaults(change);
3624 // bits 0 - 31 of "has_event[]" ...
3625 event_bits = getFile32BitBE(file);
3626 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3627 if (event_bits & (1u << j))
3628 change->has_event[j] = TRUE;
3630 change->target_element = getMappedElement(getFile16BitBE(file));
3632 change->delay_fixed = getFile16BitBE(file);
3633 change->delay_random = getFile16BitBE(file);
3634 change->delay_frames = getFile16BitBE(file);
3636 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3638 change->explode = getFile8Bit(file);
3639 change->use_target_content = getFile8Bit(file);
3640 change->only_if_complete = getFile8Bit(file);
3641 change->use_random_replace = getFile8Bit(file);
3643 change->random_percentage = getFile8Bit(file);
3644 change->replace_when = getFile8Bit(file);
3646 for (y = 0; y < 3; y++)
3647 for (x = 0; x < 3; x++)
3648 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3650 change->can_change = getFile8Bit(file);
3652 change->trigger_side = getFile8Bit(file);
3654 change->trigger_player = getFile8Bit(file);
3655 change->trigger_page = getFile8Bit(file);
3657 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3658 CH_PAGE_ANY : (1 << change->trigger_page));
3660 change->has_action = getFile8Bit(file);
3661 change->action_type = getFile8Bit(file);
3662 change->action_mode = getFile8Bit(file);
3663 change->action_arg = getFile16BitBE(file);
3665 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3666 event_bits = getFile8Bit(file);
3667 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3668 if (event_bits & (1u << (j - 32)))
3669 change->has_event[j] = TRUE;
3672 // mark this custom element as modified
3673 ei->modified_settings = TRUE;
3675 level->file_has_custom_elements = TRUE;
3680 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3682 struct ElementInfo *ei;
3683 struct ElementGroupInfo *group;
3687 element = getMappedElement(getFile16BitBE(file));
3689 if (!IS_GROUP_ELEMENT(element))
3691 Warn("invalid group element number %d", element);
3693 ReadUnusedBytesFromFile(file, chunk_size - 2);
3698 ei = &element_info[element];
3700 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3701 ei->description[i] = getFile8Bit(file);
3702 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3704 group = element_info[element].group;
3706 group->num_elements = getFile8Bit(file);
3708 ei->use_gfx_element = getFile8Bit(file);
3709 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3711 group->choice_mode = getFile8Bit(file);
3713 // some free bytes for future values and padding
3714 ReadUnusedBytesFromFile(file, 3);
3716 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3717 group->element[i] = getMappedElement(getFile16BitBE(file));
3719 // mark this group element as modified
3720 element_info[element].modified_settings = TRUE;
3722 level->file_has_custom_elements = TRUE;
3727 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3728 int element, int real_element)
3730 int micro_chunk_size = 0;
3731 int conf_type = getFile8Bit(file);
3732 int byte_mask = conf_type & CONF_MASK_BYTES;
3733 boolean element_found = FALSE;
3736 micro_chunk_size += 1;
3738 if (byte_mask == CONF_MASK_MULTI_BYTES)
3740 int num_bytes = getFile16BitBE(file);
3741 byte *buffer = checked_malloc(num_bytes);
3743 ReadBytesFromFile(file, buffer, num_bytes);
3745 for (i = 0; conf[i].data_type != -1; i++)
3747 if (conf[i].element == element &&
3748 conf[i].conf_type == conf_type)
3750 int data_type = conf[i].data_type;
3751 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3752 int max_num_entities = conf[i].max_num_entities;
3754 if (num_entities > max_num_entities)
3756 Warn("truncating number of entities for element %d from %d to %d",
3757 element, num_entities, max_num_entities);
3759 num_entities = max_num_entities;
3762 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3763 data_type == TYPE_CONTENT_LIST))
3765 // for element and content lists, zero entities are not allowed
3766 Warn("found empty list of entities for element %d", element);
3768 // do not set "num_entities" here to prevent reading behind buffer
3770 *(int *)(conf[i].num_entities) = 1; // at least one is required
3774 *(int *)(conf[i].num_entities) = num_entities;
3777 element_found = TRUE;
3779 if (data_type == TYPE_STRING)
3781 char *string = (char *)(conf[i].value);
3784 for (j = 0; j < max_num_entities; j++)
3785 string[j] = (j < num_entities ? buffer[j] : '\0');
3787 else if (data_type == TYPE_ELEMENT_LIST)
3789 int *element_array = (int *)(conf[i].value);
3792 for (j = 0; j < num_entities; j++)
3794 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3796 else if (data_type == TYPE_CONTENT_LIST)
3798 struct Content *content= (struct Content *)(conf[i].value);
3801 for (c = 0; c < num_entities; c++)
3802 for (y = 0; y < 3; y++)
3803 for (x = 0; x < 3; x++)
3804 content[c].e[x][y] =
3805 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3808 element_found = FALSE;
3814 checked_free(buffer);
3816 micro_chunk_size += 2 + num_bytes;
3818 else // constant size configuration data (1, 2 or 4 bytes)
3820 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3821 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3822 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3824 for (i = 0; conf[i].data_type != -1; i++)
3826 if (conf[i].element == element &&
3827 conf[i].conf_type == conf_type)
3829 int data_type = conf[i].data_type;
3831 if (data_type == TYPE_ELEMENT)
3832 value = getMappedElement(value);
3834 if (data_type == TYPE_BOOLEAN)
3835 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3837 *(int *) (conf[i].value) = value;
3839 element_found = TRUE;
3845 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3850 char *error_conf_chunk_bytes =
3851 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3852 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3853 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3854 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3855 int error_element = real_element;
3857 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3858 error_conf_chunk_bytes, error_conf_chunk_token,
3859 error_element, EL_NAME(error_element));
3862 return micro_chunk_size;
3865 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3867 int real_chunk_size = 0;
3869 li = *level; // copy level data into temporary buffer
3871 while (!checkEndOfFile(file))
3873 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3875 if (real_chunk_size >= chunk_size)
3879 *level = li; // copy temporary buffer back to level data
3881 return real_chunk_size;
3884 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3886 int real_chunk_size = 0;
3888 li = *level; // copy level data into temporary buffer
3890 while (!checkEndOfFile(file))
3892 int element = getMappedElement(getFile16BitBE(file));
3894 real_chunk_size += 2;
3895 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3897 if (real_chunk_size >= chunk_size)
3901 *level = li; // copy temporary buffer back to level data
3903 return real_chunk_size;
3906 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3908 int real_chunk_size = 0;
3910 li = *level; // copy level data into temporary buffer
3912 while (!checkEndOfFile(file))
3914 int element = getMappedElement(getFile16BitBE(file));
3916 real_chunk_size += 2;
3917 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3919 if (real_chunk_size >= chunk_size)
3923 *level = li; // copy temporary buffer back to level data
3925 return real_chunk_size;
3928 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3930 int element = getMappedElement(getFile16BitBE(file));
3931 int envelope_nr = element - EL_ENVELOPE_1;
3932 int real_chunk_size = 2;
3934 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3936 while (!checkEndOfFile(file))
3938 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3941 if (real_chunk_size >= chunk_size)
3945 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3947 return real_chunk_size;
3950 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3952 int element = getMappedElement(getFile16BitBE(file));
3953 int real_chunk_size = 2;
3954 struct ElementInfo *ei = &element_info[element];
3957 xx_ei = *ei; // copy element data into temporary buffer
3959 xx_ei.num_change_pages = -1;
3961 while (!checkEndOfFile(file))
3963 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3965 if (xx_ei.num_change_pages != -1)
3968 if (real_chunk_size >= chunk_size)
3974 if (ei->num_change_pages == -1)
3976 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3979 ei->num_change_pages = 1;
3981 setElementChangePages(ei, 1);
3982 setElementChangeInfoToDefaults(ei->change);
3984 return real_chunk_size;
3987 // initialize number of change pages stored for this custom element
3988 setElementChangePages(ei, ei->num_change_pages);
3989 for (i = 0; i < ei->num_change_pages; i++)
3990 setElementChangeInfoToDefaults(&ei->change_page[i]);
3992 // start with reading properties for the first change page
3993 xx_current_change_page = 0;
3995 while (!checkEndOfFile(file))
3997 // level file might contain invalid change page number
3998 if (xx_current_change_page >= ei->num_change_pages)
4001 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4003 xx_change = *change; // copy change data into temporary buffer
4005 resetEventBits(); // reset bits; change page might have changed
4007 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4010 *change = xx_change;
4012 setEventFlagsFromEventBits(change);
4014 if (real_chunk_size >= chunk_size)
4018 level->file_has_custom_elements = TRUE;
4020 return real_chunk_size;
4023 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4025 int element = getMappedElement(getFile16BitBE(file));
4026 int real_chunk_size = 2;
4027 struct ElementInfo *ei = &element_info[element];
4028 struct ElementGroupInfo *group = ei->group;
4033 xx_ei = *ei; // copy element data into temporary buffer
4034 xx_group = *group; // copy group data into temporary buffer
4036 while (!checkEndOfFile(file))
4038 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4041 if (real_chunk_size >= chunk_size)
4048 level->file_has_custom_elements = TRUE;
4050 return real_chunk_size;
4053 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4055 int element = getMappedElement(getFile16BitBE(file));
4056 int real_chunk_size = 2;
4057 struct ElementInfo *ei = &element_info[element];
4059 xx_ei = *ei; // copy element data into temporary buffer
4061 while (!checkEndOfFile(file))
4063 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4066 if (real_chunk_size >= chunk_size)
4072 level->file_has_custom_elements = TRUE;
4074 return real_chunk_size;
4077 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4078 struct LevelFileInfo *level_file_info,
4079 boolean level_info_only)
4081 char *filename = level_file_info->filename;
4082 char cookie[MAX_LINE_LEN];
4083 char chunk_name[CHUNK_ID_LEN + 1];
4087 if (!(file = openFile(filename, MODE_READ)))
4089 level->no_valid_file = TRUE;
4090 level->no_level_file = TRUE;
4092 if (level_info_only)
4095 Warn("cannot read level '%s' -- using empty level", filename);
4097 if (!setup.editor.use_template_for_new_levels)
4100 // if level file not found, try to initialize level data from template
4101 filename = getGlobalLevelTemplateFilename();
4103 if (!(file = openFile(filename, MODE_READ)))
4106 // default: for empty levels, use level template for custom elements
4107 level->use_custom_template = TRUE;
4109 level->no_valid_file = FALSE;
4112 getFileChunkBE(file, chunk_name, NULL);
4113 if (strEqual(chunk_name, "RND1"))
4115 getFile32BitBE(file); // not used
4117 getFileChunkBE(file, chunk_name, NULL);
4118 if (!strEqual(chunk_name, "CAVE"))
4120 level->no_valid_file = TRUE;
4122 Warn("unknown format of level file '%s'", filename);
4129 else // check for pre-2.0 file format with cookie string
4131 strcpy(cookie, chunk_name);
4132 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4134 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4135 cookie[strlen(cookie) - 1] = '\0';
4137 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4139 level->no_valid_file = TRUE;
4141 Warn("unknown format of level file '%s'", filename);
4148 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4150 level->no_valid_file = TRUE;
4152 Warn("unsupported version of level file '%s'", filename);
4159 // pre-2.0 level files have no game version, so use file version here
4160 level->game_version = level->file_version;
4163 if (level->file_version < FILE_VERSION_1_2)
4165 // level files from versions before 1.2.0 without chunk structure
4166 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4167 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4175 int (*loader)(File *, int, struct LevelInfo *);
4179 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4180 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4181 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4182 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4183 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4184 { "INFO", -1, LoadLevel_INFO },
4185 { "BODY", -1, LoadLevel_BODY },
4186 { "CONT", -1, LoadLevel_CONT },
4187 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4188 { "CNT3", -1, LoadLevel_CNT3 },
4189 { "CUS1", -1, LoadLevel_CUS1 },
4190 { "CUS2", -1, LoadLevel_CUS2 },
4191 { "CUS3", -1, LoadLevel_CUS3 },
4192 { "CUS4", -1, LoadLevel_CUS4 },
4193 { "GRP1", -1, LoadLevel_GRP1 },
4194 { "CONF", -1, LoadLevel_CONF },
4195 { "ELEM", -1, LoadLevel_ELEM },
4196 { "NOTE", -1, LoadLevel_NOTE },
4197 { "CUSX", -1, LoadLevel_CUSX },
4198 { "GRPX", -1, LoadLevel_GRPX },
4199 { "EMPX", -1, LoadLevel_EMPX },
4204 while (getFileChunkBE(file, chunk_name, &chunk_size))
4208 while (chunk_info[i].name != NULL &&
4209 !strEqual(chunk_name, chunk_info[i].name))
4212 if (chunk_info[i].name == NULL)
4214 Warn("unknown chunk '%s' in level file '%s'",
4215 chunk_name, filename);
4217 ReadUnusedBytesFromFile(file, chunk_size);
4219 else if (chunk_info[i].size != -1 &&
4220 chunk_info[i].size != chunk_size)
4222 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4223 chunk_size, chunk_name, filename);
4225 ReadUnusedBytesFromFile(file, chunk_size);
4229 // call function to load this level chunk
4230 int chunk_size_expected =
4231 (chunk_info[i].loader)(file, chunk_size, level);
4233 if (chunk_size_expected < 0)
4235 Warn("error reading chunk '%s' in level file '%s'",
4236 chunk_name, filename);
4241 // the size of some chunks cannot be checked before reading other
4242 // chunks first (like "HEAD" and "BODY") that contain some header
4243 // information, so check them here
4244 if (chunk_size_expected != chunk_size)
4246 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4247 chunk_size, chunk_name, filename);
4259 // ----------------------------------------------------------------------------
4260 // functions for loading BD level
4261 // ----------------------------------------------------------------------------
4263 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4264 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4266 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4268 struct LevelInfo_BD *level_bd = level->native_bd_level;
4269 GdCave *cave = NULL; // will be changed below
4270 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4271 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4274 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4276 // cave and map newly allocated when set to defaults above
4277 cave = level_bd->cave;
4280 cave->intermission = level->bd_intermission;
4283 cave->level_time[0] = level->time;
4284 cave->level_diamonds[0] = level->gems_needed;
4287 cave->scheduling = level->bd_scheduling_type;
4288 cave->pal_timing = level->bd_pal_timing;
4289 cave->level_speed[0] = level->bd_cycle_delay_ms;
4290 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4291 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4292 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4295 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4296 cave->diamond_value = level->score[SC_EMERALD];
4297 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4299 // compatibility settings
4300 cave->lineshift = level->bd_line_shifting_borders;
4301 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4302 cave->short_explosions = level->bd_short_explosions;
4304 // player properties
4305 cave->diagonal_movements = level->bd_diagonal_movements;
4306 cave->active_is_first_found = level->bd_topmost_player_active;
4307 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4308 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4309 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4310 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4312 // element properties
4313 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4314 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4315 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4316 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4317 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4318 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4319 cave->level_magic_wall_time[0] = level->time_magic_wall;
4320 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4321 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4322 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4323 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4325 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4326 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4327 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4328 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4329 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4330 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4331 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4333 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4334 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4335 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4336 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4337 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4338 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4339 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4340 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4341 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4342 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4343 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4345 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4346 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4347 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4348 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4349 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4350 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4352 cave->slime_predictable = level->bd_slime_is_predictable;
4353 cave->slime_correct_random = level->bd_slime_correct_random;
4354 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4355 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4356 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4357 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4358 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4359 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4360 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4361 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4362 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4363 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4365 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4366 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4367 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4369 cave->biter_delay_frame = level->bd_biter_move_delay;
4370 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4372 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4374 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4376 cave->replicators_active = level->bd_replicators_active;
4377 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4379 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4380 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4382 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4384 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4386 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4387 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4388 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4390 cave->infinite_rockets = level->bd_infinite_rockets;
4392 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4393 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4395 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4396 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4398 cave->creatures_backwards = level->bd_creatures_start_backwards;
4399 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4400 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4402 cave->gravity = level->bd_gravity_direction;
4403 cave->gravity_switch_active = level->bd_gravity_switch_active;
4404 cave->gravity_change_time = level->bd_gravity_switch_delay;
4405 cave->gravity_affects_all = level->bd_gravity_affects_all;
4407 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4408 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4409 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4410 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4412 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4413 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4414 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4415 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4416 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4417 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4419 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4420 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4421 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4422 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4424 cave->colorb = level->bd_color_b;
4425 cave->color0 = level->bd_color_0;
4426 cave->color1 = level->bd_color_1;
4427 cave->color2 = level->bd_color_2;
4428 cave->color3 = level->bd_color_3;
4429 cave->color4 = level->bd_color_4;
4430 cave->color5 = level->bd_color_5;
4433 strncpy(cave->name, level->name, sizeof(GdString));
4434 cave->name[sizeof(GdString) - 1] = '\0';
4436 // playfield elements
4437 for (x = 0; x < cave->w; x++)
4438 for (y = 0; y < cave->h; y++)
4439 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4442 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4444 struct LevelInfo_BD *level_bd = level->native_bd_level;
4445 GdCave *cave = level_bd->cave;
4446 int bd_level_nr = level_bd->level_nr;
4449 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4450 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4453 level->bd_intermission = cave->intermission;
4456 level->time = cave->level_time[bd_level_nr];
4457 level->gems_needed = cave->level_diamonds[bd_level_nr];
4460 level->bd_scheduling_type = cave->scheduling;
4461 level->bd_pal_timing = cave->pal_timing;
4462 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4463 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4464 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4465 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4468 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4469 level->score[SC_EMERALD] = cave->diamond_value;
4470 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4472 // compatibility settings
4473 level->bd_line_shifting_borders = cave->lineshift;
4474 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4475 level->bd_short_explosions = cave->short_explosions;
4477 // player properties
4478 level->bd_diagonal_movements = cave->diagonal_movements;
4479 level->bd_topmost_player_active = cave->active_is_first_found;
4480 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4481 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4482 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4483 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4485 // element properties
4486 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4487 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4488 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4489 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4490 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4491 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4492 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4493 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4494 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4495 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4496 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4498 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4499 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4500 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4501 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4502 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4503 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4504 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4506 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4507 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4508 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4509 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4510 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4511 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4512 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4513 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4514 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4515 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4516 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4518 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4519 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4520 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4521 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4522 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4523 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4525 level->bd_slime_is_predictable = cave->slime_predictable;
4526 level->bd_slime_correct_random = cave->slime_correct_random;
4527 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4528 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4529 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4530 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4531 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4532 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4533 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4534 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4535 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4536 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4538 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4539 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4540 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4542 level->bd_biter_move_delay = cave->biter_delay_frame;
4543 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4545 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4547 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4549 level->bd_replicators_active = cave->replicators_active;
4550 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4552 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4553 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4555 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4557 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4559 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4560 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4561 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4563 level->bd_infinite_rockets = cave->infinite_rockets;
4565 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4566 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4568 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4569 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4571 level->bd_creatures_start_backwards = cave->creatures_backwards;
4572 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4573 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4575 level->bd_gravity_direction = cave->gravity;
4576 level->bd_gravity_switch_active = cave->gravity_switch_active;
4577 level->bd_gravity_switch_delay = cave->gravity_change_time;
4578 level->bd_gravity_affects_all = cave->gravity_affects_all;
4580 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4581 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4582 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4583 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4585 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4586 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4587 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4588 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4589 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4590 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4592 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4593 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4594 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4595 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4597 level->bd_color_b = cave->colorb;
4598 level->bd_color_0 = cave->color0;
4599 level->bd_color_1 = cave->color1;
4600 level->bd_color_2 = cave->color2;
4601 level->bd_color_3 = cave->color3;
4602 level->bd_color_4 = cave->color4;
4603 level->bd_color_5 = cave->color5;
4605 // set default color type and colors for BD style level colors
4606 SetDefaultLevelColorType_BD();
4607 SetDefaultLevelColors_BD();
4610 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4612 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4613 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4615 // playfield elements
4616 for (x = 0; x < level->fieldx; x++)
4617 for (y = 0; y < level->fieldy; y++)
4618 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4620 checked_free(cave_name);
4623 static void setTapeInfoToDefaults(void);
4625 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4627 struct LevelInfo_BD *level_bd = level->native_bd_level;
4628 GdCave *cave = level_bd->cave;
4629 GdReplay *replay = level_bd->replay;
4635 // always start with reliable default values
4636 setTapeInfoToDefaults();
4638 tape.level_nr = level_nr; // (currently not used)
4639 tape.random_seed = replay->seed;
4641 TapeSetDateFromIsoDateString(replay->date);
4644 tape.pos[tape.counter].delay = 0;
4646 tape.bd_replay = TRUE;
4648 // all time calculations only used to display approximate tape time
4649 int cave_speed = cave->speed;
4650 int milliseconds_game = 0;
4651 int milliseconds_elapsed = 20;
4653 for (i = 0; i < replay->movements->len; i++)
4655 int replay_action = replay->movements->data[i];
4656 int tape_action = map_action_BD_to_RND(replay_action);
4657 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4658 boolean success = 0;
4662 success = TapeAddAction(action);
4664 milliseconds_game += milliseconds_elapsed;
4666 if (milliseconds_game >= cave_speed)
4668 milliseconds_game -= cave_speed;
4675 tape.pos[tape.counter].delay = 0;
4676 tape.pos[tape.counter].action[0] = 0;
4680 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4686 TapeHaltRecording();
4688 if (!replay->success)
4689 Warn("BD replay is marked as not successful");
4693 // ----------------------------------------------------------------------------
4694 // functions for loading EM level
4695 // ----------------------------------------------------------------------------
4697 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4699 static int ball_xy[8][2] =
4710 struct LevelInfo_EM *level_em = level->native_em_level;
4711 struct CAVE *cav = level_em->cav;
4714 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4715 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4717 cav->time_seconds = level->time;
4718 cav->gems_needed = level->gems_needed;
4720 cav->emerald_score = level->score[SC_EMERALD];
4721 cav->diamond_score = level->score[SC_DIAMOND];
4722 cav->alien_score = level->score[SC_ROBOT];
4723 cav->tank_score = level->score[SC_SPACESHIP];
4724 cav->bug_score = level->score[SC_BUG];
4725 cav->eater_score = level->score[SC_YAMYAM];
4726 cav->nut_score = level->score[SC_NUT];
4727 cav->dynamite_score = level->score[SC_DYNAMITE];
4728 cav->key_score = level->score[SC_KEY];
4729 cav->exit_score = level->score[SC_TIME_BONUS];
4731 cav->num_eater_arrays = level->num_yamyam_contents;
4733 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4734 for (y = 0; y < 3; y++)
4735 for (x = 0; x < 3; x++)
4736 cav->eater_array[i][y * 3 + x] =
4737 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4739 cav->amoeba_time = level->amoeba_speed;
4740 cav->wonderwall_time = level->time_magic_wall;
4741 cav->wheel_time = level->time_wheel;
4743 cav->android_move_time = level->android_move_time;
4744 cav->android_clone_time = level->android_clone_time;
4745 cav->ball_random = level->ball_random;
4746 cav->ball_active = level->ball_active_initial;
4747 cav->ball_time = level->ball_time;
4748 cav->num_ball_arrays = level->num_ball_contents;
4750 cav->lenses_score = level->lenses_score;
4751 cav->magnify_score = level->magnify_score;
4752 cav->slurp_score = level->slurp_score;
4754 cav->lenses_time = level->lenses_time;
4755 cav->magnify_time = level->magnify_time;
4757 cav->wind_time = 9999;
4758 cav->wind_direction =
4759 map_direction_RND_to_EM(level->wind_direction_initial);
4761 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4762 for (j = 0; j < 8; j++)
4763 cav->ball_array[i][j] =
4764 map_element_RND_to_EM_cave(level->ball_content[i].
4765 e[ball_xy[j][0]][ball_xy[j][1]]);
4767 map_android_clone_elements_RND_to_EM(level);
4769 // first fill the complete playfield with the empty space element
4770 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4771 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4772 cav->cave[x][y] = Cblank;
4774 // then copy the real level contents from level file into the playfield
4775 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4777 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4779 if (level->field[x][y] == EL_AMOEBA_DEAD)
4780 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4782 cav->cave[x][y] = new_element;
4785 for (i = 0; i < MAX_PLAYERS; i++)
4787 cav->player_x[i] = -1;
4788 cav->player_y[i] = -1;
4791 // initialize player positions and delete players from the playfield
4792 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4794 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4796 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4798 cav->player_x[player_nr] = x;
4799 cav->player_y[player_nr] = y;
4801 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4806 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4808 static int ball_xy[8][2] =
4819 struct LevelInfo_EM *level_em = level->native_em_level;
4820 struct CAVE *cav = level_em->cav;
4823 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4824 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4826 level->time = cav->time_seconds;
4827 level->gems_needed = cav->gems_needed;
4829 sprintf(level->name, "Level %d", level->file_info.nr);
4831 level->score[SC_EMERALD] = cav->emerald_score;
4832 level->score[SC_DIAMOND] = cav->diamond_score;
4833 level->score[SC_ROBOT] = cav->alien_score;
4834 level->score[SC_SPACESHIP] = cav->tank_score;
4835 level->score[SC_BUG] = cav->bug_score;
4836 level->score[SC_YAMYAM] = cav->eater_score;
4837 level->score[SC_NUT] = cav->nut_score;
4838 level->score[SC_DYNAMITE] = cav->dynamite_score;
4839 level->score[SC_KEY] = cav->key_score;
4840 level->score[SC_TIME_BONUS] = cav->exit_score;
4842 level->num_yamyam_contents = cav->num_eater_arrays;
4844 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4845 for (y = 0; y < 3; y++)
4846 for (x = 0; x < 3; x++)
4847 level->yamyam_content[i].e[x][y] =
4848 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4850 level->amoeba_speed = cav->amoeba_time;
4851 level->time_magic_wall = cav->wonderwall_time;
4852 level->time_wheel = cav->wheel_time;
4854 level->android_move_time = cav->android_move_time;
4855 level->android_clone_time = cav->android_clone_time;
4856 level->ball_random = cav->ball_random;
4857 level->ball_active_initial = cav->ball_active;
4858 level->ball_time = cav->ball_time;
4859 level->num_ball_contents = cav->num_ball_arrays;
4861 level->lenses_score = cav->lenses_score;
4862 level->magnify_score = cav->magnify_score;
4863 level->slurp_score = cav->slurp_score;
4865 level->lenses_time = cav->lenses_time;
4866 level->magnify_time = cav->magnify_time;
4868 level->wind_direction_initial =
4869 map_direction_EM_to_RND(cav->wind_direction);
4871 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4872 for (j = 0; j < 8; j++)
4873 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4874 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4876 map_android_clone_elements_EM_to_RND(level);
4878 // convert the playfield (some elements need special treatment)
4879 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4881 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4883 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4884 new_element = EL_AMOEBA_DEAD;
4886 level->field[x][y] = new_element;
4889 for (i = 0; i < MAX_PLAYERS; i++)
4891 // in case of all players set to the same field, use the first player
4892 int nr = MAX_PLAYERS - i - 1;
4893 int jx = cav->player_x[nr];
4894 int jy = cav->player_y[nr];
4896 if (jx != -1 && jy != -1)
4897 level->field[jx][jy] = EL_PLAYER_1 + nr;
4900 // time score is counted for each 10 seconds left in Emerald Mine levels
4901 level->time_score_base = 10;
4905 // ----------------------------------------------------------------------------
4906 // functions for loading SP level
4907 // ----------------------------------------------------------------------------
4909 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4911 struct LevelInfo_SP *level_sp = level->native_sp_level;
4912 LevelInfoType *header = &level_sp->header;
4915 level_sp->width = level->fieldx;
4916 level_sp->height = level->fieldy;
4918 for (x = 0; x < level->fieldx; x++)
4919 for (y = 0; y < level->fieldy; y++)
4920 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4922 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4924 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4925 header->LevelTitle[i] = level->name[i];
4926 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4928 header->InfotronsNeeded = level->gems_needed;
4930 header->SpecialPortCount = 0;
4932 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4934 boolean gravity_port_found = FALSE;
4935 boolean gravity_port_valid = FALSE;
4936 int gravity_port_flag;
4937 int gravity_port_base_element;
4938 int element = level->field[x][y];
4940 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4941 element <= EL_SP_GRAVITY_ON_PORT_UP)
4943 gravity_port_found = TRUE;
4944 gravity_port_valid = TRUE;
4945 gravity_port_flag = 1;
4946 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4948 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4949 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4951 gravity_port_found = TRUE;
4952 gravity_port_valid = TRUE;
4953 gravity_port_flag = 0;
4954 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4956 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4957 element <= EL_SP_GRAVITY_PORT_UP)
4959 // change R'n'D style gravity inverting special port to normal port
4960 // (there are no gravity inverting ports in native Supaplex engine)
4962 gravity_port_found = TRUE;
4963 gravity_port_valid = FALSE;
4964 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4967 if (gravity_port_found)
4969 if (gravity_port_valid &&
4970 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4972 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4974 port->PortLocation = (y * level->fieldx + x) * 2;
4975 port->Gravity = gravity_port_flag;
4977 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4979 header->SpecialPortCount++;
4983 // change special gravity port to normal port
4985 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4988 level_sp->playfield[x][y] = element - EL_SP_START;
4993 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4995 struct LevelInfo_SP *level_sp = level->native_sp_level;
4996 LevelInfoType *header = &level_sp->header;
4997 boolean num_invalid_elements = 0;
5000 level->fieldx = level_sp->width;
5001 level->fieldy = level_sp->height;
5003 for (x = 0; x < level->fieldx; x++)
5005 for (y = 0; y < level->fieldy; y++)
5007 int element_old = level_sp->playfield[x][y];
5008 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5010 if (element_new == EL_UNKNOWN)
5012 num_invalid_elements++;
5014 Debug("level:native:SP", "invalid element %d at position %d, %d",
5018 level->field[x][y] = element_new;
5022 if (num_invalid_elements > 0)
5023 Warn("found %d invalid elements%s", num_invalid_elements,
5024 (!options.debug ? " (use '--debug' for more details)" : ""));
5026 for (i = 0; i < MAX_PLAYERS; i++)
5027 level->initial_player_gravity[i] =
5028 (header->InitialGravity == 1 ? TRUE : FALSE);
5030 // skip leading spaces
5031 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5032 if (header->LevelTitle[i] != ' ')
5036 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5037 level->name[j] = header->LevelTitle[i];
5038 level->name[j] = '\0';
5040 // cut trailing spaces
5042 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5043 level->name[j - 1] = '\0';
5045 level->gems_needed = header->InfotronsNeeded;
5047 for (i = 0; i < header->SpecialPortCount; i++)
5049 SpecialPortType *port = &header->SpecialPort[i];
5050 int port_location = port->PortLocation;
5051 int gravity = port->Gravity;
5052 int port_x, port_y, port_element;
5054 port_x = (port_location / 2) % level->fieldx;
5055 port_y = (port_location / 2) / level->fieldx;
5057 if (port_x < 0 || port_x >= level->fieldx ||
5058 port_y < 0 || port_y >= level->fieldy)
5060 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5065 port_element = level->field[port_x][port_y];
5067 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5068 port_element > EL_SP_GRAVITY_PORT_UP)
5070 Warn("no special port at position (%d, %d)", port_x, port_y);
5075 // change previous (wrong) gravity inverting special port to either
5076 // gravity enabling special port or gravity disabling special port
5077 level->field[port_x][port_y] +=
5078 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5079 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5082 // change special gravity ports without database entries to normal ports
5083 for (x = 0; x < level->fieldx; x++)
5084 for (y = 0; y < level->fieldy; y++)
5085 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5086 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5087 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5089 level->time = 0; // no time limit
5090 level->amoeba_speed = 0;
5091 level->time_magic_wall = 0;
5092 level->time_wheel = 0;
5093 level->amoeba_content = EL_EMPTY;
5095 // original Supaplex does not use score values -- rate by playing time
5096 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5097 level->score[i] = 0;
5099 level->rate_time_over_score = TRUE;
5101 // there are no yamyams in supaplex levels
5102 for (i = 0; i < level->num_yamyam_contents; i++)
5103 for (x = 0; x < 3; x++)
5104 for (y = 0; y < 3; y++)
5105 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5108 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5110 struct LevelInfo_SP *level_sp = level->native_sp_level;
5111 struct DemoInfo_SP *demo = &level_sp->demo;
5114 // always start with reliable default values
5115 demo->is_available = FALSE;
5118 if (TAPE_IS_EMPTY(tape))
5121 demo->level_nr = tape.level_nr; // (currently not used)
5123 level_sp->header.DemoRandomSeed = tape.random_seed;
5127 for (i = 0; i < tape.length; i++)
5129 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5130 int demo_repeat = tape.pos[i].delay;
5131 int demo_entries = (demo_repeat + 15) / 16;
5133 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5135 Warn("tape truncated: size exceeds maximum SP demo size %d",
5141 for (j = 0; j < demo_repeat / 16; j++)
5142 demo->data[demo->length++] = 0xf0 | demo_action;
5144 if (demo_repeat % 16)
5145 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5148 demo->is_available = TRUE;
5151 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5153 struct LevelInfo_SP *level_sp = level->native_sp_level;
5154 struct DemoInfo_SP *demo = &level_sp->demo;
5155 char *filename = level->file_info.filename;
5158 // always start with reliable default values
5159 setTapeInfoToDefaults();
5161 if (!demo->is_available)
5164 tape.level_nr = demo->level_nr; // (currently not used)
5165 tape.random_seed = level_sp->header.DemoRandomSeed;
5167 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5170 tape.pos[tape.counter].delay = 0;
5172 for (i = 0; i < demo->length; i++)
5174 int demo_action = demo->data[i] & 0x0f;
5175 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5176 int tape_action = map_key_SP_to_RND(demo_action);
5177 int tape_repeat = demo_repeat + 1;
5178 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5179 boolean success = 0;
5182 for (j = 0; j < tape_repeat; j++)
5183 success = TapeAddAction(action);
5187 Warn("SP demo truncated: size exceeds maximum tape size %d",
5194 TapeHaltRecording();
5198 // ----------------------------------------------------------------------------
5199 // functions for loading MM level
5200 // ----------------------------------------------------------------------------
5202 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5204 struct LevelInfo_MM *level_mm = level->native_mm_level;
5207 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5208 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5210 level_mm->time = level->time;
5211 level_mm->kettles_needed = level->gems_needed;
5212 level_mm->auto_count_kettles = level->auto_count_gems;
5214 level_mm->mm_laser_red = level->mm_laser_red;
5215 level_mm->mm_laser_green = level->mm_laser_green;
5216 level_mm->mm_laser_blue = level->mm_laser_blue;
5218 level_mm->df_laser_red = level->df_laser_red;
5219 level_mm->df_laser_green = level->df_laser_green;
5220 level_mm->df_laser_blue = level->df_laser_blue;
5222 strcpy(level_mm->name, level->name);
5223 strcpy(level_mm->author, level->author);
5225 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5226 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5227 level_mm->score[SC_KEY] = level->score[SC_KEY];
5228 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5229 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5231 level_mm->amoeba_speed = level->amoeba_speed;
5232 level_mm->time_fuse = level->mm_time_fuse;
5233 level_mm->time_bomb = level->mm_time_bomb;
5234 level_mm->time_ball = level->mm_time_ball;
5235 level_mm->time_block = level->mm_time_block;
5237 level_mm->num_ball_contents = level->num_mm_ball_contents;
5238 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5239 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5240 level_mm->explode_ball = level->explode_mm_ball;
5242 for (i = 0; i < level->num_mm_ball_contents; i++)
5243 level_mm->ball_content[i] =
5244 map_element_RND_to_MM(level->mm_ball_content[i]);
5246 for (x = 0; x < level->fieldx; x++)
5247 for (y = 0; y < level->fieldy; y++)
5249 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5252 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5254 struct LevelInfo_MM *level_mm = level->native_mm_level;
5257 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5258 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5260 level->time = level_mm->time;
5261 level->gems_needed = level_mm->kettles_needed;
5262 level->auto_count_gems = level_mm->auto_count_kettles;
5264 level->mm_laser_red = level_mm->mm_laser_red;
5265 level->mm_laser_green = level_mm->mm_laser_green;
5266 level->mm_laser_blue = level_mm->mm_laser_blue;
5268 level->df_laser_red = level_mm->df_laser_red;
5269 level->df_laser_green = level_mm->df_laser_green;
5270 level->df_laser_blue = level_mm->df_laser_blue;
5272 strcpy(level->name, level_mm->name);
5274 // only overwrite author from 'levelinfo.conf' if author defined in level
5275 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5276 strcpy(level->author, level_mm->author);
5278 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5279 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5280 level->score[SC_KEY] = level_mm->score[SC_KEY];
5281 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5282 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5284 level->amoeba_speed = level_mm->amoeba_speed;
5285 level->mm_time_fuse = level_mm->time_fuse;
5286 level->mm_time_bomb = level_mm->time_bomb;
5287 level->mm_time_ball = level_mm->time_ball;
5288 level->mm_time_block = level_mm->time_block;
5290 level->num_mm_ball_contents = level_mm->num_ball_contents;
5291 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5292 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5293 level->explode_mm_ball = level_mm->explode_ball;
5295 for (i = 0; i < level->num_mm_ball_contents; i++)
5296 level->mm_ball_content[i] =
5297 map_element_MM_to_RND(level_mm->ball_content[i]);
5299 for (x = 0; x < level->fieldx; x++)
5300 for (y = 0; y < level->fieldy; y++)
5301 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5305 // ----------------------------------------------------------------------------
5306 // functions for loading DC level
5307 // ----------------------------------------------------------------------------
5309 #define DC_LEVEL_HEADER_SIZE 344
5311 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5314 static int last_data_encoded;
5318 int diff_hi, diff_lo;
5319 int data_hi, data_lo;
5320 unsigned short data_decoded;
5324 last_data_encoded = 0;
5331 diff = data_encoded - last_data_encoded;
5332 diff_hi = diff & ~0xff;
5333 diff_lo = diff & 0xff;
5337 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5338 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5339 data_hi = data_hi & 0xff00;
5341 data_decoded = data_hi | data_lo;
5343 last_data_encoded = data_encoded;
5345 offset1 = (offset1 + 1) % 31;
5346 offset2 = offset2 & 0xff;
5348 return data_decoded;
5351 static int getMappedElement_DC(int element)
5359 // 0x0117 - 0x036e: (?)
5362 // 0x042d - 0x0684: (?)
5378 element = EL_CRYSTAL;
5381 case 0x0e77: // quicksand (boulder)
5382 element = EL_QUICKSAND_FAST_FULL;
5385 case 0x0e99: // slow quicksand (boulder)
5386 element = EL_QUICKSAND_FULL;
5390 element = EL_EM_EXIT_OPEN;
5394 element = EL_EM_EXIT_CLOSED;
5398 element = EL_EM_STEEL_EXIT_OPEN;
5402 element = EL_EM_STEEL_EXIT_CLOSED;
5405 case 0x0f4f: // dynamite (lit 1)
5406 element = EL_EM_DYNAMITE_ACTIVE;
5409 case 0x0f57: // dynamite (lit 2)
5410 element = EL_EM_DYNAMITE_ACTIVE;
5413 case 0x0f5f: // dynamite (lit 3)
5414 element = EL_EM_DYNAMITE_ACTIVE;
5417 case 0x0f67: // dynamite (lit 4)
5418 element = EL_EM_DYNAMITE_ACTIVE;
5425 element = EL_AMOEBA_WET;
5429 element = EL_AMOEBA_DROP;
5433 element = EL_DC_MAGIC_WALL;
5437 element = EL_SPACESHIP_UP;
5441 element = EL_SPACESHIP_DOWN;
5445 element = EL_SPACESHIP_LEFT;
5449 element = EL_SPACESHIP_RIGHT;
5453 element = EL_BUG_UP;
5457 element = EL_BUG_DOWN;
5461 element = EL_BUG_LEFT;
5465 element = EL_BUG_RIGHT;
5469 element = EL_MOLE_UP;
5473 element = EL_MOLE_DOWN;
5477 element = EL_MOLE_LEFT;
5481 element = EL_MOLE_RIGHT;
5489 element = EL_YAMYAM_UP;
5493 element = EL_SWITCHGATE_OPEN;
5497 element = EL_SWITCHGATE_CLOSED;
5501 element = EL_DC_SWITCHGATE_SWITCH_UP;
5505 element = EL_TIMEGATE_CLOSED;
5508 case 0x144c: // conveyor belt switch (green)
5509 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5512 case 0x144f: // conveyor belt switch (red)
5513 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5516 case 0x1452: // conveyor belt switch (blue)
5517 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5521 element = EL_CONVEYOR_BELT_3_MIDDLE;
5525 element = EL_CONVEYOR_BELT_3_LEFT;
5529 element = EL_CONVEYOR_BELT_3_RIGHT;
5533 element = EL_CONVEYOR_BELT_1_MIDDLE;
5537 element = EL_CONVEYOR_BELT_1_LEFT;
5541 element = EL_CONVEYOR_BELT_1_RIGHT;
5545 element = EL_CONVEYOR_BELT_4_MIDDLE;
5549 element = EL_CONVEYOR_BELT_4_LEFT;
5553 element = EL_CONVEYOR_BELT_4_RIGHT;
5557 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5561 element = EL_EXPANDABLE_WALL_VERTICAL;
5565 element = EL_EXPANDABLE_WALL_ANY;
5568 case 0x14ce: // growing steel wall (left/right)
5569 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5572 case 0x14df: // growing steel wall (up/down)
5573 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5576 case 0x14e8: // growing steel wall (up/down/left/right)
5577 element = EL_EXPANDABLE_STEELWALL_ANY;
5581 element = EL_SHIELD_DEADLY;
5585 element = EL_EXTRA_TIME;
5593 element = EL_EMPTY_SPACE;
5596 case 0x1578: // quicksand (empty)
5597 element = EL_QUICKSAND_FAST_EMPTY;
5600 case 0x1579: // slow quicksand (empty)
5601 element = EL_QUICKSAND_EMPTY;
5611 element = EL_EM_DYNAMITE;
5614 case 0x15a1: // key (red)
5615 element = EL_EM_KEY_1;
5618 case 0x15a2: // key (yellow)
5619 element = EL_EM_KEY_2;
5622 case 0x15a3: // key (blue)
5623 element = EL_EM_KEY_4;
5626 case 0x15a4: // key (green)
5627 element = EL_EM_KEY_3;
5630 case 0x15a5: // key (white)
5631 element = EL_DC_KEY_WHITE;
5635 element = EL_WALL_SLIPPERY;
5642 case 0x15a8: // wall (not round)
5646 case 0x15a9: // (blue)
5647 element = EL_CHAR_A;
5650 case 0x15aa: // (blue)
5651 element = EL_CHAR_B;
5654 case 0x15ab: // (blue)
5655 element = EL_CHAR_C;
5658 case 0x15ac: // (blue)
5659 element = EL_CHAR_D;
5662 case 0x15ad: // (blue)
5663 element = EL_CHAR_E;
5666 case 0x15ae: // (blue)
5667 element = EL_CHAR_F;
5670 case 0x15af: // (blue)
5671 element = EL_CHAR_G;
5674 case 0x15b0: // (blue)
5675 element = EL_CHAR_H;
5678 case 0x15b1: // (blue)
5679 element = EL_CHAR_I;
5682 case 0x15b2: // (blue)
5683 element = EL_CHAR_J;
5686 case 0x15b3: // (blue)
5687 element = EL_CHAR_K;
5690 case 0x15b4: // (blue)
5691 element = EL_CHAR_L;
5694 case 0x15b5: // (blue)
5695 element = EL_CHAR_M;
5698 case 0x15b6: // (blue)
5699 element = EL_CHAR_N;
5702 case 0x15b7: // (blue)
5703 element = EL_CHAR_O;
5706 case 0x15b8: // (blue)
5707 element = EL_CHAR_P;
5710 case 0x15b9: // (blue)
5711 element = EL_CHAR_Q;
5714 case 0x15ba: // (blue)
5715 element = EL_CHAR_R;
5718 case 0x15bb: // (blue)
5719 element = EL_CHAR_S;
5722 case 0x15bc: // (blue)
5723 element = EL_CHAR_T;
5726 case 0x15bd: // (blue)
5727 element = EL_CHAR_U;
5730 case 0x15be: // (blue)
5731 element = EL_CHAR_V;
5734 case 0x15bf: // (blue)
5735 element = EL_CHAR_W;
5738 case 0x15c0: // (blue)
5739 element = EL_CHAR_X;
5742 case 0x15c1: // (blue)
5743 element = EL_CHAR_Y;
5746 case 0x15c2: // (blue)
5747 element = EL_CHAR_Z;
5750 case 0x15c3: // (blue)
5751 element = EL_CHAR_AUMLAUT;
5754 case 0x15c4: // (blue)
5755 element = EL_CHAR_OUMLAUT;
5758 case 0x15c5: // (blue)
5759 element = EL_CHAR_UUMLAUT;
5762 case 0x15c6: // (blue)
5763 element = EL_CHAR_0;
5766 case 0x15c7: // (blue)
5767 element = EL_CHAR_1;
5770 case 0x15c8: // (blue)
5771 element = EL_CHAR_2;
5774 case 0x15c9: // (blue)
5775 element = EL_CHAR_3;
5778 case 0x15ca: // (blue)
5779 element = EL_CHAR_4;
5782 case 0x15cb: // (blue)
5783 element = EL_CHAR_5;
5786 case 0x15cc: // (blue)
5787 element = EL_CHAR_6;
5790 case 0x15cd: // (blue)
5791 element = EL_CHAR_7;
5794 case 0x15ce: // (blue)
5795 element = EL_CHAR_8;
5798 case 0x15cf: // (blue)
5799 element = EL_CHAR_9;
5802 case 0x15d0: // (blue)
5803 element = EL_CHAR_PERIOD;
5806 case 0x15d1: // (blue)
5807 element = EL_CHAR_EXCLAM;
5810 case 0x15d2: // (blue)
5811 element = EL_CHAR_COLON;
5814 case 0x15d3: // (blue)
5815 element = EL_CHAR_LESS;
5818 case 0x15d4: // (blue)
5819 element = EL_CHAR_GREATER;
5822 case 0x15d5: // (blue)
5823 element = EL_CHAR_QUESTION;
5826 case 0x15d6: // (blue)
5827 element = EL_CHAR_COPYRIGHT;
5830 case 0x15d7: // (blue)
5831 element = EL_CHAR_UP;
5834 case 0x15d8: // (blue)
5835 element = EL_CHAR_DOWN;
5838 case 0x15d9: // (blue)
5839 element = EL_CHAR_BUTTON;
5842 case 0x15da: // (blue)
5843 element = EL_CHAR_PLUS;
5846 case 0x15db: // (blue)
5847 element = EL_CHAR_MINUS;
5850 case 0x15dc: // (blue)
5851 element = EL_CHAR_APOSTROPHE;
5854 case 0x15dd: // (blue)
5855 element = EL_CHAR_PARENLEFT;
5858 case 0x15de: // (blue)
5859 element = EL_CHAR_PARENRIGHT;
5862 case 0x15df: // (green)
5863 element = EL_CHAR_A;
5866 case 0x15e0: // (green)
5867 element = EL_CHAR_B;
5870 case 0x15e1: // (green)
5871 element = EL_CHAR_C;
5874 case 0x15e2: // (green)
5875 element = EL_CHAR_D;
5878 case 0x15e3: // (green)
5879 element = EL_CHAR_E;
5882 case 0x15e4: // (green)
5883 element = EL_CHAR_F;
5886 case 0x15e5: // (green)
5887 element = EL_CHAR_G;
5890 case 0x15e6: // (green)
5891 element = EL_CHAR_H;
5894 case 0x15e7: // (green)
5895 element = EL_CHAR_I;
5898 case 0x15e8: // (green)
5899 element = EL_CHAR_J;
5902 case 0x15e9: // (green)
5903 element = EL_CHAR_K;
5906 case 0x15ea: // (green)
5907 element = EL_CHAR_L;
5910 case 0x15eb: // (green)
5911 element = EL_CHAR_M;
5914 case 0x15ec: // (green)
5915 element = EL_CHAR_N;
5918 case 0x15ed: // (green)
5919 element = EL_CHAR_O;
5922 case 0x15ee: // (green)
5923 element = EL_CHAR_P;
5926 case 0x15ef: // (green)
5927 element = EL_CHAR_Q;
5930 case 0x15f0: // (green)
5931 element = EL_CHAR_R;
5934 case 0x15f1: // (green)
5935 element = EL_CHAR_S;
5938 case 0x15f2: // (green)
5939 element = EL_CHAR_T;
5942 case 0x15f3: // (green)
5943 element = EL_CHAR_U;
5946 case 0x15f4: // (green)
5947 element = EL_CHAR_V;
5950 case 0x15f5: // (green)
5951 element = EL_CHAR_W;
5954 case 0x15f6: // (green)
5955 element = EL_CHAR_X;
5958 case 0x15f7: // (green)
5959 element = EL_CHAR_Y;
5962 case 0x15f8: // (green)
5963 element = EL_CHAR_Z;
5966 case 0x15f9: // (green)
5967 element = EL_CHAR_AUMLAUT;
5970 case 0x15fa: // (green)
5971 element = EL_CHAR_OUMLAUT;
5974 case 0x15fb: // (green)
5975 element = EL_CHAR_UUMLAUT;
5978 case 0x15fc: // (green)
5979 element = EL_CHAR_0;
5982 case 0x15fd: // (green)
5983 element = EL_CHAR_1;
5986 case 0x15fe: // (green)
5987 element = EL_CHAR_2;
5990 case 0x15ff: // (green)
5991 element = EL_CHAR_3;
5994 case 0x1600: // (green)
5995 element = EL_CHAR_4;
5998 case 0x1601: // (green)
5999 element = EL_CHAR_5;
6002 case 0x1602: // (green)
6003 element = EL_CHAR_6;
6006 case 0x1603: // (green)
6007 element = EL_CHAR_7;
6010 case 0x1604: // (green)
6011 element = EL_CHAR_8;
6014 case 0x1605: // (green)
6015 element = EL_CHAR_9;
6018 case 0x1606: // (green)
6019 element = EL_CHAR_PERIOD;
6022 case 0x1607: // (green)
6023 element = EL_CHAR_EXCLAM;
6026 case 0x1608: // (green)
6027 element = EL_CHAR_COLON;
6030 case 0x1609: // (green)
6031 element = EL_CHAR_LESS;
6034 case 0x160a: // (green)
6035 element = EL_CHAR_GREATER;
6038 case 0x160b: // (green)
6039 element = EL_CHAR_QUESTION;
6042 case 0x160c: // (green)
6043 element = EL_CHAR_COPYRIGHT;
6046 case 0x160d: // (green)
6047 element = EL_CHAR_UP;
6050 case 0x160e: // (green)
6051 element = EL_CHAR_DOWN;
6054 case 0x160f: // (green)
6055 element = EL_CHAR_BUTTON;
6058 case 0x1610: // (green)
6059 element = EL_CHAR_PLUS;
6062 case 0x1611: // (green)
6063 element = EL_CHAR_MINUS;
6066 case 0x1612: // (green)
6067 element = EL_CHAR_APOSTROPHE;
6070 case 0x1613: // (green)
6071 element = EL_CHAR_PARENLEFT;
6074 case 0x1614: // (green)
6075 element = EL_CHAR_PARENRIGHT;
6078 case 0x1615: // (blue steel)
6079 element = EL_STEEL_CHAR_A;
6082 case 0x1616: // (blue steel)
6083 element = EL_STEEL_CHAR_B;
6086 case 0x1617: // (blue steel)
6087 element = EL_STEEL_CHAR_C;
6090 case 0x1618: // (blue steel)
6091 element = EL_STEEL_CHAR_D;
6094 case 0x1619: // (blue steel)
6095 element = EL_STEEL_CHAR_E;
6098 case 0x161a: // (blue steel)
6099 element = EL_STEEL_CHAR_F;
6102 case 0x161b: // (blue steel)
6103 element = EL_STEEL_CHAR_G;
6106 case 0x161c: // (blue steel)
6107 element = EL_STEEL_CHAR_H;
6110 case 0x161d: // (blue steel)
6111 element = EL_STEEL_CHAR_I;
6114 case 0x161e: // (blue steel)
6115 element = EL_STEEL_CHAR_J;
6118 case 0x161f: // (blue steel)
6119 element = EL_STEEL_CHAR_K;
6122 case 0x1620: // (blue steel)
6123 element = EL_STEEL_CHAR_L;
6126 case 0x1621: // (blue steel)
6127 element = EL_STEEL_CHAR_M;
6130 case 0x1622: // (blue steel)
6131 element = EL_STEEL_CHAR_N;
6134 case 0x1623: // (blue steel)
6135 element = EL_STEEL_CHAR_O;
6138 case 0x1624: // (blue steel)
6139 element = EL_STEEL_CHAR_P;
6142 case 0x1625: // (blue steel)
6143 element = EL_STEEL_CHAR_Q;
6146 case 0x1626: // (blue steel)
6147 element = EL_STEEL_CHAR_R;
6150 case 0x1627: // (blue steel)
6151 element = EL_STEEL_CHAR_S;
6154 case 0x1628: // (blue steel)
6155 element = EL_STEEL_CHAR_T;
6158 case 0x1629: // (blue steel)
6159 element = EL_STEEL_CHAR_U;
6162 case 0x162a: // (blue steel)
6163 element = EL_STEEL_CHAR_V;
6166 case 0x162b: // (blue steel)
6167 element = EL_STEEL_CHAR_W;
6170 case 0x162c: // (blue steel)
6171 element = EL_STEEL_CHAR_X;
6174 case 0x162d: // (blue steel)
6175 element = EL_STEEL_CHAR_Y;
6178 case 0x162e: // (blue steel)
6179 element = EL_STEEL_CHAR_Z;
6182 case 0x162f: // (blue steel)
6183 element = EL_STEEL_CHAR_AUMLAUT;
6186 case 0x1630: // (blue steel)
6187 element = EL_STEEL_CHAR_OUMLAUT;
6190 case 0x1631: // (blue steel)
6191 element = EL_STEEL_CHAR_UUMLAUT;
6194 case 0x1632: // (blue steel)
6195 element = EL_STEEL_CHAR_0;
6198 case 0x1633: // (blue steel)
6199 element = EL_STEEL_CHAR_1;
6202 case 0x1634: // (blue steel)
6203 element = EL_STEEL_CHAR_2;
6206 case 0x1635: // (blue steel)
6207 element = EL_STEEL_CHAR_3;
6210 case 0x1636: // (blue steel)
6211 element = EL_STEEL_CHAR_4;
6214 case 0x1637: // (blue steel)
6215 element = EL_STEEL_CHAR_5;
6218 case 0x1638: // (blue steel)
6219 element = EL_STEEL_CHAR_6;
6222 case 0x1639: // (blue steel)
6223 element = EL_STEEL_CHAR_7;
6226 case 0x163a: // (blue steel)
6227 element = EL_STEEL_CHAR_8;
6230 case 0x163b: // (blue steel)
6231 element = EL_STEEL_CHAR_9;
6234 case 0x163c: // (blue steel)
6235 element = EL_STEEL_CHAR_PERIOD;
6238 case 0x163d: // (blue steel)
6239 element = EL_STEEL_CHAR_EXCLAM;
6242 case 0x163e: // (blue steel)
6243 element = EL_STEEL_CHAR_COLON;
6246 case 0x163f: // (blue steel)
6247 element = EL_STEEL_CHAR_LESS;
6250 case 0x1640: // (blue steel)
6251 element = EL_STEEL_CHAR_GREATER;
6254 case 0x1641: // (blue steel)
6255 element = EL_STEEL_CHAR_QUESTION;
6258 case 0x1642: // (blue steel)
6259 element = EL_STEEL_CHAR_COPYRIGHT;
6262 case 0x1643: // (blue steel)
6263 element = EL_STEEL_CHAR_UP;
6266 case 0x1644: // (blue steel)
6267 element = EL_STEEL_CHAR_DOWN;
6270 case 0x1645: // (blue steel)
6271 element = EL_STEEL_CHAR_BUTTON;
6274 case 0x1646: // (blue steel)
6275 element = EL_STEEL_CHAR_PLUS;
6278 case 0x1647: // (blue steel)
6279 element = EL_STEEL_CHAR_MINUS;
6282 case 0x1648: // (blue steel)
6283 element = EL_STEEL_CHAR_APOSTROPHE;
6286 case 0x1649: // (blue steel)
6287 element = EL_STEEL_CHAR_PARENLEFT;
6290 case 0x164a: // (blue steel)
6291 element = EL_STEEL_CHAR_PARENRIGHT;
6294 case 0x164b: // (green steel)
6295 element = EL_STEEL_CHAR_A;
6298 case 0x164c: // (green steel)
6299 element = EL_STEEL_CHAR_B;
6302 case 0x164d: // (green steel)
6303 element = EL_STEEL_CHAR_C;
6306 case 0x164e: // (green steel)
6307 element = EL_STEEL_CHAR_D;
6310 case 0x164f: // (green steel)
6311 element = EL_STEEL_CHAR_E;
6314 case 0x1650: // (green steel)
6315 element = EL_STEEL_CHAR_F;
6318 case 0x1651: // (green steel)
6319 element = EL_STEEL_CHAR_G;
6322 case 0x1652: // (green steel)
6323 element = EL_STEEL_CHAR_H;
6326 case 0x1653: // (green steel)
6327 element = EL_STEEL_CHAR_I;
6330 case 0x1654: // (green steel)
6331 element = EL_STEEL_CHAR_J;
6334 case 0x1655: // (green steel)
6335 element = EL_STEEL_CHAR_K;
6338 case 0x1656: // (green steel)
6339 element = EL_STEEL_CHAR_L;
6342 case 0x1657: // (green steel)
6343 element = EL_STEEL_CHAR_M;
6346 case 0x1658: // (green steel)
6347 element = EL_STEEL_CHAR_N;
6350 case 0x1659: // (green steel)
6351 element = EL_STEEL_CHAR_O;
6354 case 0x165a: // (green steel)
6355 element = EL_STEEL_CHAR_P;
6358 case 0x165b: // (green steel)
6359 element = EL_STEEL_CHAR_Q;
6362 case 0x165c: // (green steel)
6363 element = EL_STEEL_CHAR_R;
6366 case 0x165d: // (green steel)
6367 element = EL_STEEL_CHAR_S;
6370 case 0x165e: // (green steel)
6371 element = EL_STEEL_CHAR_T;
6374 case 0x165f: // (green steel)
6375 element = EL_STEEL_CHAR_U;
6378 case 0x1660: // (green steel)
6379 element = EL_STEEL_CHAR_V;
6382 case 0x1661: // (green steel)
6383 element = EL_STEEL_CHAR_W;
6386 case 0x1662: // (green steel)
6387 element = EL_STEEL_CHAR_X;
6390 case 0x1663: // (green steel)
6391 element = EL_STEEL_CHAR_Y;
6394 case 0x1664: // (green steel)
6395 element = EL_STEEL_CHAR_Z;
6398 case 0x1665: // (green steel)
6399 element = EL_STEEL_CHAR_AUMLAUT;
6402 case 0x1666: // (green steel)
6403 element = EL_STEEL_CHAR_OUMLAUT;
6406 case 0x1667: // (green steel)
6407 element = EL_STEEL_CHAR_UUMLAUT;
6410 case 0x1668: // (green steel)
6411 element = EL_STEEL_CHAR_0;
6414 case 0x1669: // (green steel)
6415 element = EL_STEEL_CHAR_1;
6418 case 0x166a: // (green steel)
6419 element = EL_STEEL_CHAR_2;
6422 case 0x166b: // (green steel)
6423 element = EL_STEEL_CHAR_3;
6426 case 0x166c: // (green steel)
6427 element = EL_STEEL_CHAR_4;
6430 case 0x166d: // (green steel)
6431 element = EL_STEEL_CHAR_5;
6434 case 0x166e: // (green steel)
6435 element = EL_STEEL_CHAR_6;
6438 case 0x166f: // (green steel)
6439 element = EL_STEEL_CHAR_7;
6442 case 0x1670: // (green steel)
6443 element = EL_STEEL_CHAR_8;
6446 case 0x1671: // (green steel)
6447 element = EL_STEEL_CHAR_9;
6450 case 0x1672: // (green steel)
6451 element = EL_STEEL_CHAR_PERIOD;
6454 case 0x1673: // (green steel)
6455 element = EL_STEEL_CHAR_EXCLAM;
6458 case 0x1674: // (green steel)
6459 element = EL_STEEL_CHAR_COLON;
6462 case 0x1675: // (green steel)
6463 element = EL_STEEL_CHAR_LESS;
6466 case 0x1676: // (green steel)
6467 element = EL_STEEL_CHAR_GREATER;
6470 case 0x1677: // (green steel)
6471 element = EL_STEEL_CHAR_QUESTION;
6474 case 0x1678: // (green steel)
6475 element = EL_STEEL_CHAR_COPYRIGHT;
6478 case 0x1679: // (green steel)
6479 element = EL_STEEL_CHAR_UP;
6482 case 0x167a: // (green steel)
6483 element = EL_STEEL_CHAR_DOWN;
6486 case 0x167b: // (green steel)
6487 element = EL_STEEL_CHAR_BUTTON;
6490 case 0x167c: // (green steel)
6491 element = EL_STEEL_CHAR_PLUS;
6494 case 0x167d: // (green steel)
6495 element = EL_STEEL_CHAR_MINUS;
6498 case 0x167e: // (green steel)
6499 element = EL_STEEL_CHAR_APOSTROPHE;
6502 case 0x167f: // (green steel)
6503 element = EL_STEEL_CHAR_PARENLEFT;
6506 case 0x1680: // (green steel)
6507 element = EL_STEEL_CHAR_PARENRIGHT;
6510 case 0x1681: // gate (red)
6511 element = EL_EM_GATE_1;
6514 case 0x1682: // secret gate (red)
6515 element = EL_EM_GATE_1_GRAY;
6518 case 0x1683: // gate (yellow)
6519 element = EL_EM_GATE_2;
6522 case 0x1684: // secret gate (yellow)
6523 element = EL_EM_GATE_2_GRAY;
6526 case 0x1685: // gate (blue)
6527 element = EL_EM_GATE_4;
6530 case 0x1686: // secret gate (blue)
6531 element = EL_EM_GATE_4_GRAY;
6534 case 0x1687: // gate (green)
6535 element = EL_EM_GATE_3;
6538 case 0x1688: // secret gate (green)
6539 element = EL_EM_GATE_3_GRAY;
6542 case 0x1689: // gate (white)
6543 element = EL_DC_GATE_WHITE;
6546 case 0x168a: // secret gate (white)
6547 element = EL_DC_GATE_WHITE_GRAY;
6550 case 0x168b: // secret gate (no key)
6551 element = EL_DC_GATE_FAKE_GRAY;
6555 element = EL_ROBOT_WHEEL;
6559 element = EL_DC_TIMEGATE_SWITCH;
6563 element = EL_ACID_POOL_BOTTOM;
6567 element = EL_ACID_POOL_TOPLEFT;
6571 element = EL_ACID_POOL_TOPRIGHT;
6575 element = EL_ACID_POOL_BOTTOMLEFT;
6579 element = EL_ACID_POOL_BOTTOMRIGHT;
6583 element = EL_STEELWALL;
6587 element = EL_STEELWALL_SLIPPERY;
6590 case 0x1695: // steel wall (not round)
6591 element = EL_STEELWALL;
6594 case 0x1696: // steel wall (left)
6595 element = EL_DC_STEELWALL_1_LEFT;
6598 case 0x1697: // steel wall (bottom)
6599 element = EL_DC_STEELWALL_1_BOTTOM;
6602 case 0x1698: // steel wall (right)
6603 element = EL_DC_STEELWALL_1_RIGHT;
6606 case 0x1699: // steel wall (top)
6607 element = EL_DC_STEELWALL_1_TOP;
6610 case 0x169a: // steel wall (left/bottom)
6611 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6614 case 0x169b: // steel wall (right/bottom)
6615 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6618 case 0x169c: // steel wall (right/top)
6619 element = EL_DC_STEELWALL_1_TOPRIGHT;
6622 case 0x169d: // steel wall (left/top)
6623 element = EL_DC_STEELWALL_1_TOPLEFT;
6626 case 0x169e: // steel wall (right/bottom small)
6627 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6630 case 0x169f: // steel wall (left/bottom small)
6631 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6634 case 0x16a0: // steel wall (right/top small)
6635 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6638 case 0x16a1: // steel wall (left/top small)
6639 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6642 case 0x16a2: // steel wall (left/right)
6643 element = EL_DC_STEELWALL_1_VERTICAL;
6646 case 0x16a3: // steel wall (top/bottom)
6647 element = EL_DC_STEELWALL_1_HORIZONTAL;
6650 case 0x16a4: // steel wall 2 (left end)
6651 element = EL_DC_STEELWALL_2_LEFT;
6654 case 0x16a5: // steel wall 2 (right end)
6655 element = EL_DC_STEELWALL_2_RIGHT;
6658 case 0x16a6: // steel wall 2 (top end)
6659 element = EL_DC_STEELWALL_2_TOP;
6662 case 0x16a7: // steel wall 2 (bottom end)
6663 element = EL_DC_STEELWALL_2_BOTTOM;
6666 case 0x16a8: // steel wall 2 (left/right)
6667 element = EL_DC_STEELWALL_2_HORIZONTAL;
6670 case 0x16a9: // steel wall 2 (up/down)
6671 element = EL_DC_STEELWALL_2_VERTICAL;
6674 case 0x16aa: // steel wall 2 (mid)
6675 element = EL_DC_STEELWALL_2_MIDDLE;
6679 element = EL_SIGN_EXCLAMATION;
6683 element = EL_SIGN_RADIOACTIVITY;
6687 element = EL_SIGN_STOP;
6691 element = EL_SIGN_WHEELCHAIR;
6695 element = EL_SIGN_PARKING;
6699 element = EL_SIGN_NO_ENTRY;
6703 element = EL_SIGN_HEART;
6707 element = EL_SIGN_GIVE_WAY;
6711 element = EL_SIGN_ENTRY_FORBIDDEN;
6715 element = EL_SIGN_EMERGENCY_EXIT;
6719 element = EL_SIGN_YIN_YANG;
6723 element = EL_WALL_EMERALD;
6727 element = EL_WALL_DIAMOND;
6731 element = EL_WALL_PEARL;
6735 element = EL_WALL_CRYSTAL;
6739 element = EL_INVISIBLE_WALL;
6743 element = EL_INVISIBLE_STEELWALL;
6747 // EL_INVISIBLE_SAND
6750 element = EL_LIGHT_SWITCH;
6754 element = EL_ENVELOPE_1;
6758 if (element >= 0x0117 && element <= 0x036e) // (?)
6759 element = EL_DIAMOND;
6760 else if (element >= 0x042d && element <= 0x0684) // (?)
6761 element = EL_EMERALD;
6762 else if (element >= 0x157c && element <= 0x158b)
6764 else if (element >= 0x1590 && element <= 0x159f)
6765 element = EL_DC_LANDMINE;
6766 else if (element >= 0x16bc && element <= 0x16cb)
6767 element = EL_INVISIBLE_SAND;
6770 Warn("unknown Diamond Caves element 0x%04x", element);
6772 element = EL_UNKNOWN;
6777 return getMappedElement(element);
6780 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6782 byte header[DC_LEVEL_HEADER_SIZE];
6784 int envelope_header_pos = 62;
6785 int envelope_content_pos = 94;
6786 int level_name_pos = 251;
6787 int level_author_pos = 292;
6788 int envelope_header_len;
6789 int envelope_content_len;
6791 int level_author_len;
6793 int num_yamyam_contents;
6796 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6798 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6800 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6802 header[i * 2 + 0] = header_word >> 8;
6803 header[i * 2 + 1] = header_word & 0xff;
6806 // read some values from level header to check level decoding integrity
6807 fieldx = header[6] | (header[7] << 8);
6808 fieldy = header[8] | (header[9] << 8);
6809 num_yamyam_contents = header[60] | (header[61] << 8);
6811 // do some simple sanity checks to ensure that level was correctly decoded
6812 if (fieldx < 1 || fieldx > 256 ||
6813 fieldy < 1 || fieldy > 256 ||
6814 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6816 level->no_valid_file = TRUE;
6818 Warn("cannot decode level from stream -- using empty level");
6823 // maximum envelope header size is 31 bytes
6824 envelope_header_len = header[envelope_header_pos];
6825 // maximum envelope content size is 110 (156?) bytes
6826 envelope_content_len = header[envelope_content_pos];
6828 // maximum level title size is 40 bytes
6829 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6830 // maximum level author size is 30 (51?) bytes
6831 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6835 for (i = 0; i < envelope_header_len; i++)
6836 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6837 level->envelope[0].text[envelope_size++] =
6838 header[envelope_header_pos + 1 + i];
6840 if (envelope_header_len > 0 && envelope_content_len > 0)
6842 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6843 level->envelope[0].text[envelope_size++] = '\n';
6844 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6845 level->envelope[0].text[envelope_size++] = '\n';
6848 for (i = 0; i < envelope_content_len; i++)
6849 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6850 level->envelope[0].text[envelope_size++] =
6851 header[envelope_content_pos + 1 + i];
6853 level->envelope[0].text[envelope_size] = '\0';
6855 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6856 level->envelope[0].ysize = 10;
6857 level->envelope[0].autowrap = TRUE;
6858 level->envelope[0].centered = TRUE;
6860 for (i = 0; i < level_name_len; i++)
6861 level->name[i] = header[level_name_pos + 1 + i];
6862 level->name[level_name_len] = '\0';
6864 for (i = 0; i < level_author_len; i++)
6865 level->author[i] = header[level_author_pos + 1 + i];
6866 level->author[level_author_len] = '\0';
6868 num_yamyam_contents = header[60] | (header[61] << 8);
6869 level->num_yamyam_contents =
6870 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6872 for (i = 0; i < num_yamyam_contents; i++)
6874 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6876 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6877 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6879 if (i < MAX_ELEMENT_CONTENTS)
6880 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6884 fieldx = header[6] | (header[7] << 8);
6885 fieldy = header[8] | (header[9] << 8);
6886 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6887 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6889 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6891 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6892 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6894 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6895 level->field[x][y] = getMappedElement_DC(element_dc);
6898 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6899 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6900 level->field[x][y] = EL_PLAYER_1;
6902 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6903 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6904 level->field[x][y] = EL_PLAYER_2;
6906 level->gems_needed = header[18] | (header[19] << 8);
6908 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6909 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6910 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6911 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6912 level->score[SC_NUT] = header[28] | (header[29] << 8);
6913 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6914 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6915 level->score[SC_BUG] = header[34] | (header[35] << 8);
6916 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6917 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6918 level->score[SC_KEY] = header[40] | (header[41] << 8);
6919 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6921 level->time = header[44] | (header[45] << 8);
6923 level->amoeba_speed = header[46] | (header[47] << 8);
6924 level->time_light = header[48] | (header[49] << 8);
6925 level->time_timegate = header[50] | (header[51] << 8);
6926 level->time_wheel = header[52] | (header[53] << 8);
6927 level->time_magic_wall = header[54] | (header[55] << 8);
6928 level->extra_time = header[56] | (header[57] << 8);
6929 level->shield_normal_time = header[58] | (header[59] << 8);
6931 // shield and extra time elements do not have a score
6932 level->score[SC_SHIELD] = 0;
6933 level->extra_time_score = 0;
6935 // set time for normal and deadly shields to the same value
6936 level->shield_deadly_time = level->shield_normal_time;
6938 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6939 // can slip down from flat walls, like normal walls and steel walls
6940 level->em_slippery_gems = TRUE;
6942 // time score is counted for each 10 seconds left in Diamond Caves levels
6943 level->time_score_base = 10;
6946 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6947 struct LevelFileInfo *level_file_info,
6948 boolean level_info_only)
6950 char *filename = level_file_info->filename;
6952 int num_magic_bytes = 8;
6953 char magic_bytes[num_magic_bytes + 1];
6954 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6956 if (!(file = openFile(filename, MODE_READ)))
6958 level->no_valid_file = TRUE;
6960 if (!level_info_only)
6961 Warn("cannot read level '%s' -- using empty level", filename);
6966 // fseek(file, 0x0000, SEEK_SET);
6968 if (level_file_info->packed)
6970 // read "magic bytes" from start of file
6971 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6972 magic_bytes[0] = '\0';
6974 // check "magic bytes" for correct file format
6975 if (!strPrefix(magic_bytes, "DC2"))
6977 level->no_valid_file = TRUE;
6979 Warn("unknown DC level file '%s' -- using empty level", filename);
6984 if (strPrefix(magic_bytes, "DC2Win95") ||
6985 strPrefix(magic_bytes, "DC2Win98"))
6987 int position_first_level = 0x00fa;
6988 int extra_bytes = 4;
6991 // advance file stream to first level inside the level package
6992 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6994 // each block of level data is followed by block of non-level data
6995 num_levels_to_skip *= 2;
6997 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6998 while (num_levels_to_skip >= 0)
7000 // advance file stream to next level inside the level package
7001 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7003 level->no_valid_file = TRUE;
7005 Warn("cannot fseek in file '%s' -- using empty level", filename);
7010 // skip apparently unused extra bytes following each level
7011 ReadUnusedBytesFromFile(file, extra_bytes);
7013 // read size of next level in level package
7014 skip_bytes = getFile32BitLE(file);
7016 num_levels_to_skip--;
7021 level->no_valid_file = TRUE;
7023 Warn("unknown DC2 level file '%s' -- using empty level", filename);
7029 LoadLevelFromFileStream_DC(file, level);
7035 // ----------------------------------------------------------------------------
7036 // functions for loading SB level
7037 // ----------------------------------------------------------------------------
7039 int getMappedElement_SB(int element_ascii, boolean use_ces)
7047 sb_element_mapping[] =
7049 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
7050 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
7051 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
7052 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
7053 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
7054 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
7055 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
7056 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7063 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7064 if (element_ascii == sb_element_mapping[i].ascii)
7065 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7067 return EL_UNDEFINED;
7070 static void SetLevelSettings_SB(struct LevelInfo *level)
7074 level->use_step_counter = TRUE;
7077 level->score[SC_TIME_BONUS] = 0;
7078 level->time_score_base = 1;
7079 level->rate_time_over_score = TRUE;
7082 level->auto_exit_sokoban = TRUE;
7085 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7086 struct LevelFileInfo *level_file_info,
7087 boolean level_info_only)
7089 char *filename = level_file_info->filename;
7090 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7091 char last_comment[MAX_LINE_LEN];
7092 char level_name[MAX_LINE_LEN];
7095 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7096 boolean read_continued_line = FALSE;
7097 boolean reading_playfield = FALSE;
7098 boolean got_valid_playfield_line = FALSE;
7099 boolean invalid_playfield_char = FALSE;
7100 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7101 int file_level_nr = 0;
7102 int x = 0, y = 0; // initialized to make compilers happy
7104 last_comment[0] = '\0';
7105 level_name[0] = '\0';
7107 if (!(file = openFile(filename, MODE_READ)))
7109 level->no_valid_file = TRUE;
7111 if (!level_info_only)
7112 Warn("cannot read level '%s' -- using empty level", filename);
7117 while (!checkEndOfFile(file))
7119 // level successfully read, but next level may follow here
7120 if (!got_valid_playfield_line && reading_playfield)
7122 // read playfield from single level file -- skip remaining file
7123 if (!level_file_info->packed)
7126 if (file_level_nr >= num_levels_to_skip)
7131 last_comment[0] = '\0';
7132 level_name[0] = '\0';
7134 reading_playfield = FALSE;
7137 got_valid_playfield_line = FALSE;
7139 // read next line of input file
7140 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7143 // cut trailing line break (this can be newline and/or carriage return)
7144 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7145 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7148 // copy raw input line for later use (mainly debugging output)
7149 strcpy(line_raw, line);
7151 if (read_continued_line)
7153 // append new line to existing line, if there is enough space
7154 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7155 strcat(previous_line, line_ptr);
7157 strcpy(line, previous_line); // copy storage buffer to line
7159 read_continued_line = FALSE;
7162 // if the last character is '\', continue at next line
7163 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7165 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7166 strcpy(previous_line, line); // copy line to storage buffer
7168 read_continued_line = TRUE;
7174 if (line[0] == '\0')
7177 // extract comment text from comment line
7180 for (line_ptr = line; *line_ptr; line_ptr++)
7181 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7184 strcpy(last_comment, line_ptr);
7189 // extract level title text from line containing level title
7190 if (line[0] == '\'')
7192 strcpy(level_name, &line[1]);
7194 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7195 level_name[strlen(level_name) - 1] = '\0';
7200 // skip lines containing only spaces (or empty lines)
7201 for (line_ptr = line; *line_ptr; line_ptr++)
7202 if (*line_ptr != ' ')
7204 if (*line_ptr == '\0')
7207 // at this point, we have found a line containing part of a playfield
7209 got_valid_playfield_line = TRUE;
7211 if (!reading_playfield)
7213 reading_playfield = TRUE;
7214 invalid_playfield_char = FALSE;
7216 for (x = 0; x < MAX_LEV_FIELDX; x++)
7217 for (y = 0; y < MAX_LEV_FIELDY; y++)
7218 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7223 // start with topmost tile row
7227 // skip playfield line if larger row than allowed
7228 if (y >= MAX_LEV_FIELDY)
7231 // start with leftmost tile column
7234 // read playfield elements from line
7235 for (line_ptr = line; *line_ptr; line_ptr++)
7237 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7239 // stop parsing playfield line if larger column than allowed
7240 if (x >= MAX_LEV_FIELDX)
7243 if (mapped_sb_element == EL_UNDEFINED)
7245 invalid_playfield_char = TRUE;
7250 level->field[x][y] = mapped_sb_element;
7252 // continue with next tile column
7255 level->fieldx = MAX(x, level->fieldx);
7258 if (invalid_playfield_char)
7260 // if first playfield line, treat invalid lines as comment lines
7262 reading_playfield = FALSE;
7267 // continue with next tile row
7275 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7276 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7278 if (!reading_playfield)
7280 level->no_valid_file = TRUE;
7282 Warn("cannot read level '%s' -- using empty level", filename);
7287 if (*level_name != '\0')
7289 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7290 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7292 else if (*last_comment != '\0')
7294 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7295 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7299 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7302 // set all empty fields beyond the border walls to invisible steel wall
7303 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7305 if ((x == 0 || x == level->fieldx - 1 ||
7306 y == 0 || y == level->fieldy - 1) &&
7307 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7308 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7309 level->field, level->fieldx, level->fieldy);
7312 // set special level settings for Sokoban levels
7313 SetLevelSettings_SB(level);
7315 if (load_xsb_to_ces)
7317 // special global settings can now be set in level template
7318 level->use_custom_template = TRUE;
7323 // -------------------------------------------------------------------------
7324 // functions for handling native levels
7325 // -------------------------------------------------------------------------
7327 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7328 struct LevelFileInfo *level_file_info,
7329 boolean level_info_only)
7333 // determine position of requested level inside level package
7334 if (level_file_info->packed)
7335 pos = level_file_info->nr - leveldir_current->first_level;
7337 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7338 level->no_valid_file = TRUE;
7341 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7342 struct LevelFileInfo *level_file_info,
7343 boolean level_info_only)
7345 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7346 level->no_valid_file = TRUE;
7349 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7350 struct LevelFileInfo *level_file_info,
7351 boolean level_info_only)
7355 // determine position of requested level inside level package
7356 if (level_file_info->packed)
7357 pos = level_file_info->nr - leveldir_current->first_level;
7359 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7360 level->no_valid_file = TRUE;
7363 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7364 struct LevelFileInfo *level_file_info,
7365 boolean level_info_only)
7367 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7368 level->no_valid_file = TRUE;
7371 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7373 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7374 CopyNativeLevel_RND_to_BD(level);
7375 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7376 CopyNativeLevel_RND_to_EM(level);
7377 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7378 CopyNativeLevel_RND_to_SP(level);
7379 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7380 CopyNativeLevel_RND_to_MM(level);
7383 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7385 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7386 CopyNativeLevel_BD_to_RND(level);
7387 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7388 CopyNativeLevel_EM_to_RND(level);
7389 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7390 CopyNativeLevel_SP_to_RND(level);
7391 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7392 CopyNativeLevel_MM_to_RND(level);
7395 void SaveNativeLevel(struct LevelInfo *level)
7397 // saving native level files only supported for some game engines
7398 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7399 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7402 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7403 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7404 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7405 char *filename = getLevelFilenameFromBasename(basename);
7407 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7410 boolean success = FALSE;
7412 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7414 CopyNativeLevel_RND_to_BD(level);
7415 // CopyNativeTape_RND_to_BD(level);
7417 success = SaveNativeLevel_BD(filename);
7419 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7421 CopyNativeLevel_RND_to_SP(level);
7422 CopyNativeTape_RND_to_SP(level);
7424 success = SaveNativeLevel_SP(filename);
7428 Request("Native level file saved!", REQ_CONFIRM);
7430 Request("Failed to save native level file!", REQ_CONFIRM);
7434 // ----------------------------------------------------------------------------
7435 // functions for loading generic level
7436 // ----------------------------------------------------------------------------
7438 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7439 struct LevelFileInfo *level_file_info,
7440 boolean level_info_only)
7442 // always start with reliable default values
7443 setLevelInfoToDefaults(level, level_info_only, TRUE);
7445 switch (level_file_info->type)
7447 case LEVEL_FILE_TYPE_RND:
7448 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7451 case LEVEL_FILE_TYPE_BD:
7452 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7453 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7456 case LEVEL_FILE_TYPE_EM:
7457 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7458 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7461 case LEVEL_FILE_TYPE_SP:
7462 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7463 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7466 case LEVEL_FILE_TYPE_MM:
7467 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7468 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7471 case LEVEL_FILE_TYPE_DC:
7472 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7475 case LEVEL_FILE_TYPE_SB:
7476 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7480 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7484 // if level file is invalid, restore level structure to default values
7485 if (level->no_valid_file)
7486 setLevelInfoToDefaults(level, level_info_only, FALSE);
7488 if (check_special_flags("use_native_bd_game_engine"))
7489 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7491 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7492 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7494 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7495 CopyNativeLevel_Native_to_RND(level);
7498 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7500 static struct LevelFileInfo level_file_info;
7502 // always start with reliable default values
7503 setFileInfoToDefaults(&level_file_info);
7505 level_file_info.nr = 0; // unknown level number
7506 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7508 setString(&level_file_info.filename, filename);
7510 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7513 static void LoadLevel_InitVersion(struct LevelInfo *level)
7517 if (leveldir_current == NULL) // only when dumping level
7520 // all engine modifications also valid for levels which use latest engine
7521 if (level->game_version < VERSION_IDENT(3,2,0,5))
7523 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7524 level->time_score_base = 10;
7527 if (leveldir_current->latest_engine)
7529 // ---------- use latest game engine --------------------------------------
7531 /* For all levels which are forced to use the latest game engine version
7532 (normally all but user contributed, private and undefined levels), set
7533 the game engine version to the actual version; this allows for actual
7534 corrections in the game engine to take effect for existing, converted
7535 levels (from "classic" or other existing games) to make the emulation
7536 of the corresponding game more accurate, while (hopefully) not breaking
7537 existing levels created from other players. */
7539 level->game_version = GAME_VERSION_ACTUAL;
7541 /* Set special EM style gems behaviour: EM style gems slip down from
7542 normal, steel and growing wall. As this is a more fundamental change,
7543 it seems better to set the default behaviour to "off" (as it is more
7544 natural) and make it configurable in the level editor (as a property
7545 of gem style elements). Already existing converted levels (neither
7546 private nor contributed levels) are changed to the new behaviour. */
7548 if (level->file_version < FILE_VERSION_2_0)
7549 level->em_slippery_gems = TRUE;
7554 // ---------- use game engine the level was created with --------------------
7556 /* For all levels which are not forced to use the latest game engine
7557 version (normally user contributed, private and undefined levels),
7558 use the version of the game engine the levels were created for.
7560 Since 2.0.1, the game engine version is now directly stored
7561 in the level file (chunk "VERS"), so there is no need anymore
7562 to set the game version from the file version (except for old,
7563 pre-2.0 levels, where the game version is still taken from the
7564 file format version used to store the level -- see above). */
7566 // player was faster than enemies in 1.0.0 and before
7567 if (level->file_version == FILE_VERSION_1_0)
7568 for (i = 0; i < MAX_PLAYERS; i++)
7569 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7571 // default behaviour for EM style gems was "slippery" only in 2.0.1
7572 if (level->game_version == VERSION_IDENT(2,0,1,0))
7573 level->em_slippery_gems = TRUE;
7575 // springs could be pushed over pits before (pre-release version) 2.2.0
7576 if (level->game_version < VERSION_IDENT(2,2,0,0))
7577 level->use_spring_bug = TRUE;
7579 if (level->game_version < VERSION_IDENT(3,2,0,5))
7581 // time orb caused limited time in endless time levels before 3.2.0-5
7582 level->use_time_orb_bug = TRUE;
7584 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7585 level->block_snap_field = FALSE;
7587 // extra time score was same value as time left score before 3.2.0-5
7588 level->extra_time_score = level->score[SC_TIME_BONUS];
7591 if (level->game_version < VERSION_IDENT(3,2,0,7))
7593 // default behaviour for snapping was "not continuous" before 3.2.0-7
7594 level->continuous_snapping = FALSE;
7597 // only few elements were able to actively move into acid before 3.1.0
7598 // trigger settings did not exist before 3.1.0; set to default "any"
7599 if (level->game_version < VERSION_IDENT(3,1,0,0))
7601 // correct "can move into acid" settings (all zero in old levels)
7603 level->can_move_into_acid_bits = 0; // nothing can move into acid
7604 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7606 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7607 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7608 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7609 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7611 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7612 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7614 // correct trigger settings (stored as zero == "none" in old levels)
7616 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7618 int element = EL_CUSTOM_START + i;
7619 struct ElementInfo *ei = &element_info[element];
7621 for (j = 0; j < ei->num_change_pages; j++)
7623 struct ElementChangeInfo *change = &ei->change_page[j];
7625 change->trigger_player = CH_PLAYER_ANY;
7626 change->trigger_page = CH_PAGE_ANY;
7631 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7633 int element = EL_CUSTOM_256;
7634 struct ElementInfo *ei = &element_info[element];
7635 struct ElementChangeInfo *change = &ei->change_page[0];
7637 /* This is needed to fix a problem that was caused by a bugfix in function
7638 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7639 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7640 not replace walkable elements, but instead just placed the player on it,
7641 without placing the Sokoban field under the player). Unfortunately, this
7642 breaks "Snake Bite" style levels when the snake is halfway through a door
7643 that just closes (the snake head is still alive and can be moved in this
7644 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7645 player (without Sokoban element) which then gets killed as designed). */
7647 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7648 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7649 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7650 change->target_element = EL_PLAYER_1;
7653 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7654 if (level->game_version < VERSION_IDENT(3,2,5,0))
7656 /* This is needed to fix a problem that was caused by a bugfix in function
7657 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7658 corrects the behaviour when a custom element changes to another custom
7659 element with a higher element number that has change actions defined.
7660 Normally, only one change per frame is allowed for custom elements.
7661 Therefore, it is checked if a custom element already changed in the
7662 current frame; if it did, subsequent changes are suppressed.
7663 Unfortunately, this is only checked for element changes, but not for
7664 change actions, which are still executed. As the function above loops
7665 through all custom elements from lower to higher, an element change
7666 resulting in a lower CE number won't be checked again, while a target
7667 element with a higher number will also be checked, and potential change
7668 actions will get executed for this CE, too (which is wrong), while
7669 further changes are ignored (which is correct). As this bugfix breaks
7670 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7671 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7672 behaviour for existing levels and tapes that make use of this bug */
7674 level->use_action_after_change_bug = TRUE;
7677 // not centering level after relocating player was default only in 3.2.3
7678 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7679 level->shifted_relocation = TRUE;
7681 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7682 if (level->game_version < VERSION_IDENT(3,2,6,0))
7683 level->em_explodes_by_fire = TRUE;
7685 // levels were solved by the first player entering an exit up to 4.1.0.0
7686 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7687 level->solved_by_one_player = TRUE;
7689 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7690 if (level->game_version < VERSION_IDENT(4,1,1,1))
7691 level->use_life_bugs = TRUE;
7693 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7694 if (level->game_version < VERSION_IDENT(4,1,1,1))
7695 level->sb_objects_needed = FALSE;
7697 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7698 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7699 level->finish_dig_collect = FALSE;
7701 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7702 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7703 level->keep_walkable_ce = TRUE;
7706 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7708 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7711 // check if this level is (not) a Sokoban level
7712 for (y = 0; y < level->fieldy; y++)
7713 for (x = 0; x < level->fieldx; x++)
7714 if (!IS_SB_ELEMENT(Tile[x][y]))
7715 is_sokoban_level = FALSE;
7717 if (is_sokoban_level)
7719 // set special level settings for Sokoban levels
7720 SetLevelSettings_SB(level);
7724 static void LoadLevel_InitSettings(struct LevelInfo *level)
7726 // adjust level settings for (non-native) Sokoban-style levels
7727 LoadLevel_InitSettings_SB(level);
7729 // rename levels with title "nameless level" or if renaming is forced
7730 if (leveldir_current->empty_level_name != NULL &&
7731 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7732 leveldir_current->force_level_name))
7733 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7734 leveldir_current->empty_level_name, level_nr);
7737 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7741 // map elements that have changed in newer versions
7742 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7743 level->game_version);
7744 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7745 for (x = 0; x < 3; x++)
7746 for (y = 0; y < 3; y++)
7747 level->yamyam_content[i].e[x][y] =
7748 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7749 level->game_version);
7753 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7757 // map custom element change events that have changed in newer versions
7758 // (these following values were accidentally changed in version 3.0.1)
7759 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7760 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7762 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7764 int element = EL_CUSTOM_START + i;
7766 // order of checking and copying events to be mapped is important
7767 // (do not change the start and end value -- they are constant)
7768 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7770 if (HAS_CHANGE_EVENT(element, j - 2))
7772 SET_CHANGE_EVENT(element, j - 2, FALSE);
7773 SET_CHANGE_EVENT(element, j, TRUE);
7777 // order of checking and copying events to be mapped is important
7778 // (do not change the start and end value -- they are constant)
7779 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7781 if (HAS_CHANGE_EVENT(element, j - 1))
7783 SET_CHANGE_EVENT(element, j - 1, FALSE);
7784 SET_CHANGE_EVENT(element, j, TRUE);
7790 // initialize "can_change" field for old levels with only one change page
7791 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7795 int element = EL_CUSTOM_START + i;
7797 if (CAN_CHANGE(element))
7798 element_info[element].change->can_change = TRUE;
7802 // correct custom element values (for old levels without these options)
7803 if (level->game_version < VERSION_IDENT(3,1,1,0))
7805 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7807 int element = EL_CUSTOM_START + i;
7808 struct ElementInfo *ei = &element_info[element];
7810 if (ei->access_direction == MV_NO_DIRECTION)
7811 ei->access_direction = MV_ALL_DIRECTIONS;
7815 // correct custom element values (fix invalid values for all versions)
7818 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7820 int element = EL_CUSTOM_START + i;
7821 struct ElementInfo *ei = &element_info[element];
7823 for (j = 0; j < ei->num_change_pages; j++)
7825 struct ElementChangeInfo *change = &ei->change_page[j];
7827 if (change->trigger_player == CH_PLAYER_NONE)
7828 change->trigger_player = CH_PLAYER_ANY;
7830 if (change->trigger_side == CH_SIDE_NONE)
7831 change->trigger_side = CH_SIDE_ANY;
7836 // initialize "can_explode" field for old levels which did not store this
7837 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7838 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7840 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7842 int element = EL_CUSTOM_START + i;
7844 if (EXPLODES_1X1_OLD(element))
7845 element_info[element].explosion_type = EXPLODES_1X1;
7847 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7848 EXPLODES_SMASHED(element) ||
7849 EXPLODES_IMPACT(element)));
7853 // correct previously hard-coded move delay values for maze runner style
7854 if (level->game_version < VERSION_IDENT(3,1,1,0))
7856 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7858 int element = EL_CUSTOM_START + i;
7860 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7862 // previously hard-coded and therefore ignored
7863 element_info[element].move_delay_fixed = 9;
7864 element_info[element].move_delay_random = 0;
7869 // set some other uninitialized values of custom elements in older levels
7870 if (level->game_version < VERSION_IDENT(3,1,0,0))
7872 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7874 int element = EL_CUSTOM_START + i;
7876 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7878 element_info[element].explosion_delay = 17;
7879 element_info[element].ignition_delay = 8;
7883 // set mouse click change events to work for left/middle/right mouse button
7884 if (level->game_version < VERSION_IDENT(4,2,3,0))
7886 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7888 int element = EL_CUSTOM_START + i;
7889 struct ElementInfo *ei = &element_info[element];
7891 for (j = 0; j < ei->num_change_pages; j++)
7893 struct ElementChangeInfo *change = &ei->change_page[j];
7895 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7896 change->has_event[CE_PRESSED_BY_MOUSE] ||
7897 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7898 change->has_event[CE_MOUSE_PRESSED_ON_X])
7899 change->trigger_side = CH_SIDE_ANY;
7905 static void LoadLevel_InitElements(struct LevelInfo *level)
7907 LoadLevel_InitStandardElements(level);
7909 if (level->file_has_custom_elements)
7910 LoadLevel_InitCustomElements(level);
7912 // initialize element properties for level editor etc.
7913 InitElementPropertiesEngine(level->game_version);
7914 InitElementPropertiesGfxElement();
7917 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7921 // map elements that have changed in newer versions
7922 for (y = 0; y < level->fieldy; y++)
7923 for (x = 0; x < level->fieldx; x++)
7924 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7925 level->game_version);
7927 // clear unused playfield data (nicer if level gets resized in editor)
7928 for (x = 0; x < MAX_LEV_FIELDX; x++)
7929 for (y = 0; y < MAX_LEV_FIELDY; y++)
7930 if (x >= level->fieldx || y >= level->fieldy)
7931 level->field[x][y] = EL_EMPTY;
7933 // copy elements to runtime playfield array
7934 for (x = 0; x < MAX_LEV_FIELDX; x++)
7935 for (y = 0; y < MAX_LEV_FIELDY; y++)
7936 Tile[x][y] = level->field[x][y];
7938 // initialize level size variables for faster access
7939 lev_fieldx = level->fieldx;
7940 lev_fieldy = level->fieldy;
7942 // determine border element for this level
7943 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7944 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7949 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7951 struct LevelFileInfo *level_file_info = &level->file_info;
7953 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7954 CopyNativeLevel_RND_to_Native(level);
7957 static void LoadLevelTemplate_LoadAndInit(void)
7959 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7961 LoadLevel_InitVersion(&level_template);
7962 LoadLevel_InitElements(&level_template);
7963 LoadLevel_InitSettings(&level_template);
7965 ActivateLevelTemplate();
7968 void LoadLevelTemplate(int nr)
7970 if (!fileExists(getGlobalLevelTemplateFilename()))
7972 Warn("no level template found for this level");
7977 setLevelFileInfo(&level_template.file_info, nr);
7979 LoadLevelTemplate_LoadAndInit();
7982 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7984 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7986 LoadLevelTemplate_LoadAndInit();
7989 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7991 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7993 if (level.use_custom_template)
7995 if (network_level != NULL)
7996 LoadNetworkLevelTemplate(network_level);
7998 LoadLevelTemplate(-1);
8001 LoadLevel_InitVersion(&level);
8002 LoadLevel_InitElements(&level);
8003 LoadLevel_InitPlayfield(&level);
8004 LoadLevel_InitSettings(&level);
8006 LoadLevel_InitNativeEngines(&level);
8009 void LoadLevel(int nr)
8011 SetLevelSetInfo(leveldir_current->identifier, nr);
8013 setLevelFileInfo(&level.file_info, nr);
8015 LoadLevel_LoadAndInit(NULL);
8018 void LoadLevelInfoOnly(int nr)
8020 setLevelFileInfo(&level.file_info, nr);
8022 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8025 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8027 SetLevelSetInfo(network_level->leveldir_identifier,
8028 network_level->file_info.nr);
8030 copyLevelFileInfo(&network_level->file_info, &level.file_info);
8032 LoadLevel_LoadAndInit(network_level);
8035 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8039 chunk_size += putFileVersion(file, level->file_version);
8040 chunk_size += putFileVersion(file, level->game_version);
8045 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8049 chunk_size += putFile16BitBE(file, level->creation_date.year);
8050 chunk_size += putFile8Bit(file, level->creation_date.month);
8051 chunk_size += putFile8Bit(file, level->creation_date.day);
8056 #if ENABLE_HISTORIC_CHUNKS
8057 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8061 putFile8Bit(file, level->fieldx);
8062 putFile8Bit(file, level->fieldy);
8064 putFile16BitBE(file, level->time);
8065 putFile16BitBE(file, level->gems_needed);
8067 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8068 putFile8Bit(file, level->name[i]);
8070 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8071 putFile8Bit(file, level->score[i]);
8073 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8074 for (y = 0; y < 3; y++)
8075 for (x = 0; x < 3; x++)
8076 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8077 level->yamyam_content[i].e[x][y]));
8078 putFile8Bit(file, level->amoeba_speed);
8079 putFile8Bit(file, level->time_magic_wall);
8080 putFile8Bit(file, level->time_wheel);
8081 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8082 level->amoeba_content));
8083 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8084 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8085 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8086 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8088 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8090 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8091 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8092 putFile32BitBE(file, level->can_move_into_acid_bits);
8093 putFile8Bit(file, level->dont_collide_with_bits);
8095 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8096 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8098 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8099 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8100 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8102 putFile8Bit(file, level->game_engine_type);
8104 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8108 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8113 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8114 chunk_size += putFile8Bit(file, level->name[i]);
8119 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8124 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8125 chunk_size += putFile8Bit(file, level->author[i]);
8130 #if ENABLE_HISTORIC_CHUNKS
8131 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8136 for (y = 0; y < level->fieldy; y++)
8137 for (x = 0; x < level->fieldx; x++)
8138 if (level->encoding_16bit_field)
8139 chunk_size += putFile16BitBE(file, level->field[x][y]);
8141 chunk_size += putFile8Bit(file, level->field[x][y]);
8147 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8152 for (y = 0; y < level->fieldy; y++)
8153 for (x = 0; x < level->fieldx; x++)
8154 chunk_size += putFile16BitBE(file, level->field[x][y]);
8159 #if ENABLE_HISTORIC_CHUNKS
8160 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8164 putFile8Bit(file, EL_YAMYAM);
8165 putFile8Bit(file, level->num_yamyam_contents);
8166 putFile8Bit(file, 0);
8167 putFile8Bit(file, 0);
8169 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8170 for (y = 0; y < 3; y++)
8171 for (x = 0; x < 3; x++)
8172 if (level->encoding_16bit_field)
8173 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8175 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8179 #if ENABLE_HISTORIC_CHUNKS
8180 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8183 int num_contents, content_xsize, content_ysize;
8184 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8186 if (element == EL_YAMYAM)
8188 num_contents = level->num_yamyam_contents;
8192 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8193 for (y = 0; y < 3; y++)
8194 for (x = 0; x < 3; x++)
8195 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8197 else if (element == EL_BD_AMOEBA)
8203 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8204 for (y = 0; y < 3; y++)
8205 for (x = 0; x < 3; x++)
8206 content_array[i][x][y] = EL_EMPTY;
8207 content_array[0][0][0] = level->amoeba_content;
8211 // chunk header already written -- write empty chunk data
8212 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8214 Warn("cannot save content for element '%d'", element);
8219 putFile16BitBE(file, element);
8220 putFile8Bit(file, num_contents);
8221 putFile8Bit(file, content_xsize);
8222 putFile8Bit(file, content_ysize);
8224 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8226 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8227 for (y = 0; y < 3; y++)
8228 for (x = 0; x < 3; x++)
8229 putFile16BitBE(file, content_array[i][x][y]);
8233 #if ENABLE_HISTORIC_CHUNKS
8234 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8236 int envelope_nr = element - EL_ENVELOPE_1;
8237 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8241 chunk_size += putFile16BitBE(file, element);
8242 chunk_size += putFile16BitBE(file, envelope_len);
8243 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8244 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8246 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8247 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8249 for (i = 0; i < envelope_len; i++)
8250 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8256 #if ENABLE_HISTORIC_CHUNKS
8257 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8258 int num_changed_custom_elements)
8262 putFile16BitBE(file, num_changed_custom_elements);
8264 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8266 int element = EL_CUSTOM_START + i;
8268 struct ElementInfo *ei = &element_info[element];
8270 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8272 if (check < num_changed_custom_elements)
8274 putFile16BitBE(file, element);
8275 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8282 if (check != num_changed_custom_elements) // should not happen
8283 Warn("inconsistent number of custom element properties");
8287 #if ENABLE_HISTORIC_CHUNKS
8288 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8289 int num_changed_custom_elements)
8293 putFile16BitBE(file, num_changed_custom_elements);
8295 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8297 int element = EL_CUSTOM_START + i;
8299 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8301 if (check < num_changed_custom_elements)
8303 putFile16BitBE(file, element);
8304 putFile16BitBE(file, element_info[element].change->target_element);
8311 if (check != num_changed_custom_elements) // should not happen
8312 Warn("inconsistent number of custom target elements");
8316 #if ENABLE_HISTORIC_CHUNKS
8317 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8318 int num_changed_custom_elements)
8320 int i, j, x, y, check = 0;
8322 putFile16BitBE(file, num_changed_custom_elements);
8324 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8326 int element = EL_CUSTOM_START + i;
8327 struct ElementInfo *ei = &element_info[element];
8329 if (ei->modified_settings)
8331 if (check < num_changed_custom_elements)
8333 putFile16BitBE(file, element);
8335 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8336 putFile8Bit(file, ei->description[j]);
8338 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8340 // some free bytes for future properties and padding
8341 WriteUnusedBytesToFile(file, 7);
8343 putFile8Bit(file, ei->use_gfx_element);
8344 putFile16BitBE(file, ei->gfx_element_initial);
8346 putFile8Bit(file, ei->collect_score_initial);
8347 putFile8Bit(file, ei->collect_count_initial);
8349 putFile16BitBE(file, ei->push_delay_fixed);
8350 putFile16BitBE(file, ei->push_delay_random);
8351 putFile16BitBE(file, ei->move_delay_fixed);
8352 putFile16BitBE(file, ei->move_delay_random);
8354 putFile16BitBE(file, ei->move_pattern);
8355 putFile8Bit(file, ei->move_direction_initial);
8356 putFile8Bit(file, ei->move_stepsize);
8358 for (y = 0; y < 3; y++)
8359 for (x = 0; x < 3; x++)
8360 putFile16BitBE(file, ei->content.e[x][y]);
8362 putFile32BitBE(file, ei->change->events);
8364 putFile16BitBE(file, ei->change->target_element);
8366 putFile16BitBE(file, ei->change->delay_fixed);
8367 putFile16BitBE(file, ei->change->delay_random);
8368 putFile16BitBE(file, ei->change->delay_frames);
8370 putFile16BitBE(file, ei->change->initial_trigger_element);
8372 putFile8Bit(file, ei->change->explode);
8373 putFile8Bit(file, ei->change->use_target_content);
8374 putFile8Bit(file, ei->change->only_if_complete);
8375 putFile8Bit(file, ei->change->use_random_replace);
8377 putFile8Bit(file, ei->change->random_percentage);
8378 putFile8Bit(file, ei->change->replace_when);
8380 for (y = 0; y < 3; y++)
8381 for (x = 0; x < 3; x++)
8382 putFile16BitBE(file, ei->change->content.e[x][y]);
8384 putFile8Bit(file, ei->slippery_type);
8386 // some free bytes for future properties and padding
8387 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8394 if (check != num_changed_custom_elements) // should not happen
8395 Warn("inconsistent number of custom element properties");
8399 #if ENABLE_HISTORIC_CHUNKS
8400 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8402 struct ElementInfo *ei = &element_info[element];
8405 // ---------- custom element base property values (96 bytes) ----------------
8407 putFile16BitBE(file, element);
8409 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8410 putFile8Bit(file, ei->description[i]);
8412 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8414 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8416 putFile8Bit(file, ei->num_change_pages);
8418 putFile16BitBE(file, ei->ce_value_fixed_initial);
8419 putFile16BitBE(file, ei->ce_value_random_initial);
8420 putFile8Bit(file, ei->use_last_ce_value);
8422 putFile8Bit(file, ei->use_gfx_element);
8423 putFile16BitBE(file, ei->gfx_element_initial);
8425 putFile8Bit(file, ei->collect_score_initial);
8426 putFile8Bit(file, ei->collect_count_initial);
8428 putFile8Bit(file, ei->drop_delay_fixed);
8429 putFile8Bit(file, ei->push_delay_fixed);
8430 putFile8Bit(file, ei->drop_delay_random);
8431 putFile8Bit(file, ei->push_delay_random);
8432 putFile16BitBE(file, ei->move_delay_fixed);
8433 putFile16BitBE(file, ei->move_delay_random);
8435 // bits 0 - 15 of "move_pattern" ...
8436 putFile16BitBE(file, ei->move_pattern & 0xffff);
8437 putFile8Bit(file, ei->move_direction_initial);
8438 putFile8Bit(file, ei->move_stepsize);
8440 putFile8Bit(file, ei->slippery_type);
8442 for (y = 0; y < 3; y++)
8443 for (x = 0; x < 3; x++)
8444 putFile16BitBE(file, ei->content.e[x][y]);
8446 putFile16BitBE(file, ei->move_enter_element);
8447 putFile16BitBE(file, ei->move_leave_element);
8448 putFile8Bit(file, ei->move_leave_type);
8450 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8451 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8453 putFile8Bit(file, ei->access_direction);
8455 putFile8Bit(file, ei->explosion_delay);
8456 putFile8Bit(file, ei->ignition_delay);
8457 putFile8Bit(file, ei->explosion_type);
8459 // some free bytes for future custom property values and padding
8460 WriteUnusedBytesToFile(file, 1);
8462 // ---------- change page property values (48 bytes) ------------------------
8464 for (i = 0; i < ei->num_change_pages; i++)
8466 struct ElementChangeInfo *change = &ei->change_page[i];
8467 unsigned int event_bits;
8469 // bits 0 - 31 of "has_event[]" ...
8471 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8472 if (change->has_event[j])
8473 event_bits |= (1u << j);
8474 putFile32BitBE(file, event_bits);
8476 putFile16BitBE(file, change->target_element);
8478 putFile16BitBE(file, change->delay_fixed);
8479 putFile16BitBE(file, change->delay_random);
8480 putFile16BitBE(file, change->delay_frames);
8482 putFile16BitBE(file, change->initial_trigger_element);
8484 putFile8Bit(file, change->explode);
8485 putFile8Bit(file, change->use_target_content);
8486 putFile8Bit(file, change->only_if_complete);
8487 putFile8Bit(file, change->use_random_replace);
8489 putFile8Bit(file, change->random_percentage);
8490 putFile8Bit(file, change->replace_when);
8492 for (y = 0; y < 3; y++)
8493 for (x = 0; x < 3; x++)
8494 putFile16BitBE(file, change->target_content.e[x][y]);
8496 putFile8Bit(file, change->can_change);
8498 putFile8Bit(file, change->trigger_side);
8500 putFile8Bit(file, change->trigger_player);
8501 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8502 log_2(change->trigger_page)));
8504 putFile8Bit(file, change->has_action);
8505 putFile8Bit(file, change->action_type);
8506 putFile8Bit(file, change->action_mode);
8507 putFile16BitBE(file, change->action_arg);
8509 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8511 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8512 if (change->has_event[j])
8513 event_bits |= (1u << (j - 32));
8514 putFile8Bit(file, event_bits);
8519 #if ENABLE_HISTORIC_CHUNKS
8520 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8522 struct ElementInfo *ei = &element_info[element];
8523 struct ElementGroupInfo *group = ei->group;
8526 putFile16BitBE(file, element);
8528 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8529 putFile8Bit(file, ei->description[i]);
8531 putFile8Bit(file, group->num_elements);
8533 putFile8Bit(file, ei->use_gfx_element);
8534 putFile16BitBE(file, ei->gfx_element_initial);
8536 putFile8Bit(file, group->choice_mode);
8538 // some free bytes for future values and padding
8539 WriteUnusedBytesToFile(file, 3);
8541 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8542 putFile16BitBE(file, group->element[i]);
8546 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8547 boolean write_element)
8549 int save_type = entry->save_type;
8550 int data_type = entry->data_type;
8551 int conf_type = entry->conf_type;
8552 int byte_mask = conf_type & CONF_MASK_BYTES;
8553 int element = entry->element;
8554 int default_value = entry->default_value;
8556 boolean modified = FALSE;
8558 if (byte_mask != CONF_MASK_MULTI_BYTES)
8560 void *value_ptr = entry->value;
8561 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8564 // check if any settings have been modified before saving them
8565 if (value != default_value)
8568 // do not save if explicitly told or if unmodified default settings
8569 if ((save_type == SAVE_CONF_NEVER) ||
8570 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8574 num_bytes += putFile16BitBE(file, element);
8576 num_bytes += putFile8Bit(file, conf_type);
8577 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8578 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8579 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8582 else if (data_type == TYPE_STRING)
8584 char *default_string = entry->default_string;
8585 char *string = (char *)(entry->value);
8586 int string_length = strlen(string);
8589 // check if any settings have been modified before saving them
8590 if (!strEqual(string, default_string))
8593 // do not save if explicitly told or if unmodified default settings
8594 if ((save_type == SAVE_CONF_NEVER) ||
8595 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8599 num_bytes += putFile16BitBE(file, element);
8601 num_bytes += putFile8Bit(file, conf_type);
8602 num_bytes += putFile16BitBE(file, string_length);
8604 for (i = 0; i < string_length; i++)
8605 num_bytes += putFile8Bit(file, string[i]);
8607 else if (data_type == TYPE_ELEMENT_LIST)
8609 int *element_array = (int *)(entry->value);
8610 int num_elements = *(int *)(entry->num_entities);
8613 // check if any settings have been modified before saving them
8614 for (i = 0; i < num_elements; i++)
8615 if (element_array[i] != default_value)
8618 // do not save if explicitly told or if unmodified default settings
8619 if ((save_type == SAVE_CONF_NEVER) ||
8620 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8624 num_bytes += putFile16BitBE(file, element);
8626 num_bytes += putFile8Bit(file, conf_type);
8627 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8629 for (i = 0; i < num_elements; i++)
8630 num_bytes += putFile16BitBE(file, element_array[i]);
8632 else if (data_type == TYPE_CONTENT_LIST)
8634 struct Content *content = (struct Content *)(entry->value);
8635 int num_contents = *(int *)(entry->num_entities);
8638 // check if any settings have been modified before saving them
8639 for (i = 0; i < num_contents; i++)
8640 for (y = 0; y < 3; y++)
8641 for (x = 0; x < 3; x++)
8642 if (content[i].e[x][y] != default_value)
8645 // do not save if explicitly told or if unmodified default settings
8646 if ((save_type == SAVE_CONF_NEVER) ||
8647 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8651 num_bytes += putFile16BitBE(file, element);
8653 num_bytes += putFile8Bit(file, conf_type);
8654 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8656 for (i = 0; i < num_contents; i++)
8657 for (y = 0; y < 3; y++)
8658 for (x = 0; x < 3; x++)
8659 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8665 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8670 li = *level; // copy level data into temporary buffer
8672 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8673 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8678 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8683 li = *level; // copy level data into temporary buffer
8685 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8686 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8691 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8693 int envelope_nr = element - EL_ENVELOPE_1;
8697 chunk_size += putFile16BitBE(file, element);
8699 // copy envelope data into temporary buffer
8700 xx_envelope = level->envelope[envelope_nr];
8702 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8703 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8708 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8710 struct ElementInfo *ei = &element_info[element];
8714 chunk_size += putFile16BitBE(file, element);
8716 xx_ei = *ei; // copy element data into temporary buffer
8718 // set default description string for this specific element
8719 strcpy(xx_default_description, getDefaultElementDescription(ei));
8721 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8722 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8724 for (i = 0; i < ei->num_change_pages; i++)
8726 struct ElementChangeInfo *change = &ei->change_page[i];
8728 xx_current_change_page = i;
8730 xx_change = *change; // copy change data into temporary buffer
8733 setEventBitsFromEventFlags(change);
8735 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8736 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8743 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8745 struct ElementInfo *ei = &element_info[element];
8746 struct ElementGroupInfo *group = ei->group;
8750 chunk_size += putFile16BitBE(file, element);
8752 xx_ei = *ei; // copy element data into temporary buffer
8753 xx_group = *group; // copy group data into temporary buffer
8755 // set default description string for this specific element
8756 strcpy(xx_default_description, getDefaultElementDescription(ei));
8758 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8759 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8764 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8766 struct ElementInfo *ei = &element_info[element];
8770 chunk_size += putFile16BitBE(file, element);
8772 xx_ei = *ei; // copy element data into temporary buffer
8774 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8775 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8780 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8781 boolean save_as_template)
8787 if (!(file = fopen(filename, MODE_WRITE)))
8789 Warn("cannot save level file '%s'", filename);
8794 level->file_version = FILE_VERSION_ACTUAL;
8795 level->game_version = GAME_VERSION_ACTUAL;
8797 level->creation_date = getCurrentDate();
8799 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8800 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8802 chunk_size = SaveLevel_VERS(NULL, level);
8803 putFileChunkBE(file, "VERS", chunk_size);
8804 SaveLevel_VERS(file, level);
8806 chunk_size = SaveLevel_DATE(NULL, level);
8807 putFileChunkBE(file, "DATE", chunk_size);
8808 SaveLevel_DATE(file, level);
8810 chunk_size = SaveLevel_NAME(NULL, level);
8811 putFileChunkBE(file, "NAME", chunk_size);
8812 SaveLevel_NAME(file, level);
8814 chunk_size = SaveLevel_AUTH(NULL, level);
8815 putFileChunkBE(file, "AUTH", chunk_size);
8816 SaveLevel_AUTH(file, level);
8818 chunk_size = SaveLevel_INFO(NULL, level);
8819 putFileChunkBE(file, "INFO", chunk_size);
8820 SaveLevel_INFO(file, level);
8822 chunk_size = SaveLevel_BODY(NULL, level);
8823 putFileChunkBE(file, "BODY", chunk_size);
8824 SaveLevel_BODY(file, level);
8826 chunk_size = SaveLevel_ELEM(NULL, level);
8827 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8829 putFileChunkBE(file, "ELEM", chunk_size);
8830 SaveLevel_ELEM(file, level);
8833 for (i = 0; i < NUM_ENVELOPES; i++)
8835 int element = EL_ENVELOPE_1 + i;
8837 chunk_size = SaveLevel_NOTE(NULL, level, element);
8838 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8840 putFileChunkBE(file, "NOTE", chunk_size);
8841 SaveLevel_NOTE(file, level, element);
8845 // if not using template level, check for non-default custom/group elements
8846 if (!level->use_custom_template || save_as_template)
8848 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8850 int element = EL_CUSTOM_START + i;
8852 chunk_size = SaveLevel_CUSX(NULL, level, element);
8853 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8855 putFileChunkBE(file, "CUSX", chunk_size);
8856 SaveLevel_CUSX(file, level, element);
8860 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8862 int element = EL_GROUP_START + i;
8864 chunk_size = SaveLevel_GRPX(NULL, level, element);
8865 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8867 putFileChunkBE(file, "GRPX", chunk_size);
8868 SaveLevel_GRPX(file, level, element);
8872 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8874 int element = GET_EMPTY_ELEMENT(i);
8876 chunk_size = SaveLevel_EMPX(NULL, level, element);
8877 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8879 putFileChunkBE(file, "EMPX", chunk_size);
8880 SaveLevel_EMPX(file, level, element);
8887 SetFilePermissions(filename, PERMS_PRIVATE);
8890 void SaveLevel(int nr)
8892 char *filename = getDefaultLevelFilename(nr);
8894 SaveLevelFromFilename(&level, filename, FALSE);
8897 void SaveLevelTemplate(void)
8899 char *filename = getLocalLevelTemplateFilename();
8901 SaveLevelFromFilename(&level, filename, TRUE);
8904 boolean SaveLevelChecked(int nr)
8906 char *filename = getDefaultLevelFilename(nr);
8907 boolean new_level = !fileExists(filename);
8908 boolean level_saved = FALSE;
8910 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8915 Request("Level saved!", REQ_CONFIRM);
8923 void DumpLevel(struct LevelInfo *level)
8925 if (level->no_level_file || level->no_valid_file)
8927 Warn("cannot dump -- no valid level file found");
8933 Print("Level xxx (file version %08d, game version %08d)\n",
8934 level->file_version, level->game_version);
8937 Print("Level author: '%s'\n", level->author);
8938 Print("Level title: '%s'\n", level->name);
8940 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8942 Print("Level time: %d seconds\n", level->time);
8943 Print("Gems needed: %d\n", level->gems_needed);
8945 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8946 Print("Time for wheel: %d seconds\n", level->time_wheel);
8947 Print("Time for light: %d seconds\n", level->time_light);
8948 Print("Time for timegate: %d seconds\n", level->time_timegate);
8950 Print("Amoeba speed: %d\n", level->amoeba_speed);
8953 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8954 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8955 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8956 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8957 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8958 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8964 for (i = 0; i < NUM_ENVELOPES; i++)
8966 char *text = level->envelope[i].text;
8967 int text_len = strlen(text);
8968 boolean has_text = FALSE;
8970 for (j = 0; j < text_len; j++)
8971 if (text[j] != ' ' && text[j] != '\n')
8977 Print("Envelope %d:\n'%s'\n", i + 1, text);
8985 void DumpLevels(void)
8987 static LevelDirTree *dumplevel_leveldir = NULL;
8989 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8990 global.dumplevel_leveldir);
8992 if (dumplevel_leveldir == NULL)
8993 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8995 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8996 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8997 Fail("no such level number: %d", global.dumplevel_level_nr);
8999 leveldir_current = dumplevel_leveldir;
9001 LoadLevel(global.dumplevel_level_nr);
9008 // ============================================================================
9009 // tape file functions
9010 // ============================================================================
9012 static void setTapeInfoToDefaults(void)
9016 // always start with reliable default values (empty tape)
9019 // default values (also for pre-1.2 tapes) with only the first player
9020 tape.player_participates[0] = TRUE;
9021 for (i = 1; i < MAX_PLAYERS; i++)
9022 tape.player_participates[i] = FALSE;
9024 // at least one (default: the first) player participates in every tape
9025 tape.num_participating_players = 1;
9027 tape.property_bits = TAPE_PROPERTY_NONE;
9029 tape.level_nr = level_nr;
9031 tape.changed = FALSE;
9032 tape.solved = FALSE;
9034 tape.recording = FALSE;
9035 tape.playing = FALSE;
9036 tape.pausing = FALSE;
9038 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9039 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9041 tape.no_info_chunk = TRUE;
9042 tape.no_valid_file = FALSE;
9045 static int getTapePosSize(struct TapeInfo *tape)
9047 int tape_pos_size = 0;
9049 if (tape->use_key_actions)
9050 tape_pos_size += tape->num_participating_players;
9052 if (tape->use_mouse_actions)
9053 tape_pos_size += 3; // x and y position and mouse button mask
9055 tape_pos_size += 1; // tape action delay value
9057 return tape_pos_size;
9060 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9062 tape->use_key_actions = FALSE;
9063 tape->use_mouse_actions = FALSE;
9065 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9066 tape->use_key_actions = TRUE;
9068 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9069 tape->use_mouse_actions = TRUE;
9072 static int getTapeActionValue(struct TapeInfo *tape)
9074 return (tape->use_key_actions &&
9075 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9076 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9077 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9078 TAPE_ACTIONS_DEFAULT);
9081 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9083 tape->file_version = getFileVersion(file);
9084 tape->game_version = getFileVersion(file);
9089 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9093 tape->random_seed = getFile32BitBE(file);
9094 tape->date = getFile32BitBE(file);
9095 tape->length = getFile32BitBE(file);
9097 // read header fields that are new since version 1.2
9098 if (tape->file_version >= FILE_VERSION_1_2)
9100 byte store_participating_players = getFile8Bit(file);
9103 // since version 1.2, tapes store which players participate in the tape
9104 tape->num_participating_players = 0;
9105 for (i = 0; i < MAX_PLAYERS; i++)
9107 tape->player_participates[i] = FALSE;
9109 if (store_participating_players & (1 << i))
9111 tape->player_participates[i] = TRUE;
9112 tape->num_participating_players++;
9116 setTapeActionFlags(tape, getFile8Bit(file));
9118 tape->property_bits = getFile8Bit(file);
9119 tape->solved = getFile8Bit(file);
9121 engine_version = getFileVersion(file);
9122 if (engine_version > 0)
9123 tape->engine_version = engine_version;
9125 tape->engine_version = tape->game_version;
9131 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9133 tape->scr_fieldx = getFile8Bit(file);
9134 tape->scr_fieldy = getFile8Bit(file);
9139 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9141 char *level_identifier = NULL;
9142 int level_identifier_size;
9145 tape->no_info_chunk = FALSE;
9147 level_identifier_size = getFile16BitBE(file);
9149 level_identifier = checked_malloc(level_identifier_size);
9151 for (i = 0; i < level_identifier_size; i++)
9152 level_identifier[i] = getFile8Bit(file);
9154 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9155 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9157 checked_free(level_identifier);
9159 tape->level_nr = getFile16BitBE(file);
9161 chunk_size = 2 + level_identifier_size + 2;
9166 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9169 int tape_pos_size = getTapePosSize(tape);
9170 int chunk_size_expected = tape_pos_size * tape->length;
9172 if (chunk_size_expected != chunk_size)
9174 ReadUnusedBytesFromFile(file, chunk_size);
9175 return chunk_size_expected;
9178 for (i = 0; i < tape->length; i++)
9180 if (i >= MAX_TAPE_LEN)
9182 Warn("tape truncated -- size exceeds maximum tape size %d",
9185 // tape too large; read and ignore remaining tape data from this chunk
9186 for (;i < tape->length; i++)
9187 ReadUnusedBytesFromFile(file, tape_pos_size);
9192 if (tape->use_key_actions)
9194 for (j = 0; j < MAX_PLAYERS; j++)
9196 tape->pos[i].action[j] = MV_NONE;
9198 if (tape->player_participates[j])
9199 tape->pos[i].action[j] = getFile8Bit(file);
9203 if (tape->use_mouse_actions)
9205 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9206 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9207 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9210 tape->pos[i].delay = getFile8Bit(file);
9212 if (tape->file_version == FILE_VERSION_1_0)
9214 // eliminate possible diagonal moves in old tapes
9215 // this is only for backward compatibility
9217 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9218 byte action = tape->pos[i].action[0];
9219 int k, num_moves = 0;
9221 for (k = 0; k < 4; k++)
9223 if (action & joy_dir[k])
9225 tape->pos[i + num_moves].action[0] = joy_dir[k];
9227 tape->pos[i + num_moves].delay = 0;
9236 tape->length += num_moves;
9239 else if (tape->file_version < FILE_VERSION_2_0)
9241 // convert pre-2.0 tapes to new tape format
9243 if (tape->pos[i].delay > 1)
9246 tape->pos[i + 1] = tape->pos[i];
9247 tape->pos[i + 1].delay = 1;
9250 for (j = 0; j < MAX_PLAYERS; j++)
9251 tape->pos[i].action[j] = MV_NONE;
9252 tape->pos[i].delay--;
9259 if (checkEndOfFile(file))
9263 if (i != tape->length)
9264 chunk_size = tape_pos_size * i;
9269 static void LoadTape_SokobanSolution(char *filename)
9272 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9274 if (!(file = openFile(filename, MODE_READ)))
9276 tape.no_valid_file = TRUE;
9281 while (!checkEndOfFile(file))
9283 unsigned char c = getByteFromFile(file);
9285 if (checkEndOfFile(file))
9292 tape.pos[tape.length].action[0] = MV_UP;
9293 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9299 tape.pos[tape.length].action[0] = MV_DOWN;
9300 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9306 tape.pos[tape.length].action[0] = MV_LEFT;
9307 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9313 tape.pos[tape.length].action[0] = MV_RIGHT;
9314 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9322 // ignore white-space characters
9326 tape.no_valid_file = TRUE;
9328 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9336 if (tape.no_valid_file)
9339 tape.length_frames = GetTapeLengthFrames();
9340 tape.length_seconds = GetTapeLengthSeconds();
9343 void LoadTapeFromFilename(char *filename)
9345 char cookie[MAX_LINE_LEN];
9346 char chunk_name[CHUNK_ID_LEN + 1];
9350 // always start with reliable default values
9351 setTapeInfoToDefaults();
9353 if (strSuffix(filename, ".sln"))
9355 LoadTape_SokobanSolution(filename);
9360 if (!(file = openFile(filename, MODE_READ)))
9362 tape.no_valid_file = TRUE;
9367 getFileChunkBE(file, chunk_name, NULL);
9368 if (strEqual(chunk_name, "RND1"))
9370 getFile32BitBE(file); // not used
9372 getFileChunkBE(file, chunk_name, NULL);
9373 if (!strEqual(chunk_name, "TAPE"))
9375 tape.no_valid_file = TRUE;
9377 Warn("unknown format of tape file '%s'", filename);
9384 else // check for pre-2.0 file format with cookie string
9386 strcpy(cookie, chunk_name);
9387 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9389 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9390 cookie[strlen(cookie) - 1] = '\0';
9392 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9394 tape.no_valid_file = TRUE;
9396 Warn("unknown format of tape file '%s'", filename);
9403 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9405 tape.no_valid_file = TRUE;
9407 Warn("unsupported version of tape file '%s'", filename);
9414 // pre-2.0 tape files have no game version, so use file version here
9415 tape.game_version = tape.file_version;
9418 if (tape.file_version < FILE_VERSION_1_2)
9420 // tape files from versions before 1.2.0 without chunk structure
9421 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9422 LoadTape_BODY(file, 2 * tape.length, &tape);
9430 int (*loader)(File *, int, struct TapeInfo *);
9434 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9435 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9436 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9437 { "INFO", -1, LoadTape_INFO },
9438 { "BODY", -1, LoadTape_BODY },
9442 while (getFileChunkBE(file, chunk_name, &chunk_size))
9446 while (chunk_info[i].name != NULL &&
9447 !strEqual(chunk_name, chunk_info[i].name))
9450 if (chunk_info[i].name == NULL)
9452 Warn("unknown chunk '%s' in tape file '%s'",
9453 chunk_name, filename);
9455 ReadUnusedBytesFromFile(file, chunk_size);
9457 else if (chunk_info[i].size != -1 &&
9458 chunk_info[i].size != chunk_size)
9460 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9461 chunk_size, chunk_name, filename);
9463 ReadUnusedBytesFromFile(file, chunk_size);
9467 // call function to load this tape chunk
9468 int chunk_size_expected =
9469 (chunk_info[i].loader)(file, chunk_size, &tape);
9471 // the size of some chunks cannot be checked before reading other
9472 // chunks first (like "HEAD" and "BODY") that contain some header
9473 // information, so check them here
9474 if (chunk_size_expected != chunk_size)
9476 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9477 chunk_size, chunk_name, filename);
9485 tape.length_frames = GetTapeLengthFrames();
9486 tape.length_seconds = GetTapeLengthSeconds();
9489 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9491 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9493 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9494 tape.engine_version);
9498 void LoadTape(int nr)
9500 char *filename = getTapeFilename(nr);
9502 LoadTapeFromFilename(filename);
9505 void LoadSolutionTape(int nr)
9507 char *filename = getSolutionTapeFilename(nr);
9509 LoadTapeFromFilename(filename);
9511 if (TAPE_IS_EMPTY(tape))
9513 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9514 level.native_bd_level->replay != NULL)
9515 CopyNativeTape_BD_to_RND(&level);
9516 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9517 level.native_sp_level->demo.is_available)
9518 CopyNativeTape_SP_to_RND(&level);
9522 void LoadScoreTape(char *score_tape_basename, int nr)
9524 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9526 LoadTapeFromFilename(filename);
9529 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9531 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9533 LoadTapeFromFilename(filename);
9536 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9538 // chunk required for team mode tapes with non-default screen size
9539 return (tape->num_participating_players > 1 &&
9540 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9541 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9544 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9546 putFileVersion(file, tape->file_version);
9547 putFileVersion(file, tape->game_version);
9550 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9553 byte store_participating_players = 0;
9555 // set bits for participating players for compact storage
9556 for (i = 0; i < MAX_PLAYERS; i++)
9557 if (tape->player_participates[i])
9558 store_participating_players |= (1 << i);
9560 putFile32BitBE(file, tape->random_seed);
9561 putFile32BitBE(file, tape->date);
9562 putFile32BitBE(file, tape->length);
9564 putFile8Bit(file, store_participating_players);
9566 putFile8Bit(file, getTapeActionValue(tape));
9568 putFile8Bit(file, tape->property_bits);
9569 putFile8Bit(file, tape->solved);
9571 putFileVersion(file, tape->engine_version);
9574 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9576 putFile8Bit(file, tape->scr_fieldx);
9577 putFile8Bit(file, tape->scr_fieldy);
9580 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9582 int level_identifier_size = strlen(tape->level_identifier) + 1;
9585 putFile16BitBE(file, level_identifier_size);
9587 for (i = 0; i < level_identifier_size; i++)
9588 putFile8Bit(file, tape->level_identifier[i]);
9590 putFile16BitBE(file, tape->level_nr);
9593 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9597 for (i = 0; i < tape->length; i++)
9599 if (tape->use_key_actions)
9601 for (j = 0; j < MAX_PLAYERS; j++)
9602 if (tape->player_participates[j])
9603 putFile8Bit(file, tape->pos[i].action[j]);
9606 if (tape->use_mouse_actions)
9608 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9609 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9610 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9613 putFile8Bit(file, tape->pos[i].delay);
9617 void SaveTapeToFilename(char *filename)
9621 int info_chunk_size;
9622 int body_chunk_size;
9624 if (!(file = fopen(filename, MODE_WRITE)))
9626 Warn("cannot save level recording file '%s'", filename);
9631 tape_pos_size = getTapePosSize(&tape);
9633 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9634 body_chunk_size = tape_pos_size * tape.length;
9636 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9637 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9639 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9640 SaveTape_VERS(file, &tape);
9642 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9643 SaveTape_HEAD(file, &tape);
9645 if (checkSaveTape_SCRN(&tape))
9647 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9648 SaveTape_SCRN(file, &tape);
9651 putFileChunkBE(file, "INFO", info_chunk_size);
9652 SaveTape_INFO(file, &tape);
9654 putFileChunkBE(file, "BODY", body_chunk_size);
9655 SaveTape_BODY(file, &tape);
9659 SetFilePermissions(filename, PERMS_PRIVATE);
9662 static void SaveTapeExt(char *filename)
9666 tape.file_version = FILE_VERSION_ACTUAL;
9667 tape.game_version = GAME_VERSION_ACTUAL;
9669 tape.num_participating_players = 0;
9671 // count number of participating players
9672 for (i = 0; i < MAX_PLAYERS; i++)
9673 if (tape.player_participates[i])
9674 tape.num_participating_players++;
9676 SaveTapeToFilename(filename);
9678 tape.changed = FALSE;
9681 void SaveTape(int nr)
9683 char *filename = getTapeFilename(nr);
9685 InitTapeDirectory(leveldir_current->subdir);
9687 SaveTapeExt(filename);
9690 void SaveScoreTape(int nr)
9692 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9694 // used instead of "leveldir_current->subdir" (for network games)
9695 InitScoreTapeDirectory(levelset.identifier, nr);
9697 SaveTapeExt(filename);
9700 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9701 unsigned int req_state_added)
9703 char *filename = getTapeFilename(nr);
9704 boolean new_tape = !fileExists(filename);
9705 boolean tape_saved = FALSE;
9707 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9712 Request(msg_saved, REQ_CONFIRM | req_state_added);
9720 boolean SaveTapeChecked(int nr)
9722 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9725 boolean SaveTapeChecked_LevelSolved(int nr)
9727 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9728 "Level solved! Tape saved!", REQ_STAY_OPEN);
9731 void DumpTape(struct TapeInfo *tape)
9733 int tape_frame_counter;
9736 if (tape->no_valid_file)
9738 Warn("cannot dump -- no valid tape file found");
9745 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9746 tape->level_nr, tape->file_version, tape->game_version);
9747 Print(" (effective engine version %08d)\n",
9748 tape->engine_version);
9749 Print("Level series identifier: '%s'\n", tape->level_identifier);
9751 Print("Solution tape: %s\n",
9752 tape->solved ? "yes" :
9753 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9755 Print("Special tape properties: ");
9756 if (tape->property_bits == TAPE_PROPERTY_NONE)
9758 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9759 Print("[em_random_bug]");
9760 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9761 Print("[game_speed]");
9762 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9764 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9765 Print("[single_step]");
9766 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9767 Print("[snapshot]");
9768 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9769 Print("[replayed]");
9770 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9771 Print("[tas_keys]");
9772 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9773 Print("[small_graphics]");
9776 int year2 = tape->date / 10000;
9777 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9778 int month_index_raw = (tape->date / 100) % 100;
9779 int month_index = month_index_raw % 12; // prevent invalid index
9780 int month = month_index + 1;
9781 int day = tape->date % 100;
9783 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9787 tape_frame_counter = 0;
9789 for (i = 0; i < tape->length; i++)
9791 if (i >= MAX_TAPE_LEN)
9796 for (j = 0; j < MAX_PLAYERS; j++)
9798 if (tape->player_participates[j])
9800 int action = tape->pos[i].action[j];
9802 Print("%d:%02x ", j, action);
9803 Print("[%c%c%c%c|%c%c] - ",
9804 (action & JOY_LEFT ? '<' : ' '),
9805 (action & JOY_RIGHT ? '>' : ' '),
9806 (action & JOY_UP ? '^' : ' '),
9807 (action & JOY_DOWN ? 'v' : ' '),
9808 (action & JOY_BUTTON_1 ? '1' : ' '),
9809 (action & JOY_BUTTON_2 ? '2' : ' '));
9813 Print("(%03d) ", tape->pos[i].delay);
9814 Print("[%05d]\n", tape_frame_counter);
9816 tape_frame_counter += tape->pos[i].delay;
9822 void DumpTapes(void)
9824 static LevelDirTree *dumptape_leveldir = NULL;
9826 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9827 global.dumptape_leveldir);
9829 if (dumptape_leveldir == NULL)
9830 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9832 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9833 global.dumptape_level_nr > dumptape_leveldir->last_level)
9834 Fail("no such level number: %d", global.dumptape_level_nr);
9836 leveldir_current = dumptape_leveldir;
9838 if (options.mytapes)
9839 LoadTape(global.dumptape_level_nr);
9841 LoadSolutionTape(global.dumptape_level_nr);
9849 // ============================================================================
9850 // score file functions
9851 // ============================================================================
9853 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9857 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9859 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9860 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9861 scores->entry[i].score = 0;
9862 scores->entry[i].time = 0;
9864 scores->entry[i].id = -1;
9865 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9866 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9867 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9868 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9869 strcpy(scores->entry[i].country_code, "??");
9872 scores->num_entries = 0;
9873 scores->last_added = -1;
9874 scores->last_added_local = -1;
9876 scores->updated = FALSE;
9877 scores->uploaded = FALSE;
9878 scores->tape_downloaded = FALSE;
9879 scores->force_last_added = FALSE;
9881 // The following values are intentionally not reset here:
9885 // - continue_playing
9886 // - continue_on_return
9889 static void setScoreInfoToDefaults(void)
9891 setScoreInfoToDefaultsExt(&scores);
9894 static void setServerScoreInfoToDefaults(void)
9896 setScoreInfoToDefaultsExt(&server_scores);
9899 static void LoadScore_OLD(int nr)
9902 char *filename = getScoreFilename(nr);
9903 char cookie[MAX_LINE_LEN];
9904 char line[MAX_LINE_LEN];
9908 if (!(file = fopen(filename, MODE_READ)))
9911 // check file identifier
9912 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9914 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9915 cookie[strlen(cookie) - 1] = '\0';
9917 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9919 Warn("unknown format of score file '%s'", filename);
9926 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9928 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9929 Warn("fscanf() failed; %s", strerror(errno));
9931 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9934 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9935 line[strlen(line) - 1] = '\0';
9937 for (line_ptr = line; *line_ptr; line_ptr++)
9939 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9941 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9942 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9951 static void ConvertScore_OLD(void)
9953 // only convert score to time for levels that rate playing time over score
9954 if (!level.rate_time_over_score)
9957 // convert old score to playing time for score-less levels (like Supaplex)
9958 int time_final_max = 999;
9961 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9963 int score = scores.entry[i].score;
9965 if (score > 0 && score < time_final_max)
9966 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9970 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9972 scores->file_version = getFileVersion(file);
9973 scores->game_version = getFileVersion(file);
9978 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9980 char *level_identifier = NULL;
9981 int level_identifier_size;
9984 level_identifier_size = getFile16BitBE(file);
9986 level_identifier = checked_malloc(level_identifier_size);
9988 for (i = 0; i < level_identifier_size; i++)
9989 level_identifier[i] = getFile8Bit(file);
9991 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9992 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9994 checked_free(level_identifier);
9996 scores->level_nr = getFile16BitBE(file);
9997 scores->num_entries = getFile16BitBE(file);
9999 chunk_size = 2 + level_identifier_size + 2 + 2;
10004 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10008 for (i = 0; i < scores->num_entries; i++)
10010 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10011 scores->entry[i].name[j] = getFile8Bit(file);
10013 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10016 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10021 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10025 for (i = 0; i < scores->num_entries; i++)
10026 scores->entry[i].score = getFile16BitBE(file);
10028 chunk_size = scores->num_entries * 2;
10033 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10037 for (i = 0; i < scores->num_entries; i++)
10038 scores->entry[i].score = getFile32BitBE(file);
10040 chunk_size = scores->num_entries * 4;
10045 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10049 for (i = 0; i < scores->num_entries; i++)
10050 scores->entry[i].time = getFile32BitBE(file);
10052 chunk_size = scores->num_entries * 4;
10057 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10061 for (i = 0; i < scores->num_entries; i++)
10063 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10064 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10066 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10069 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10074 void LoadScore(int nr)
10076 char *filename = getScoreFilename(nr);
10077 char cookie[MAX_LINE_LEN];
10078 char chunk_name[CHUNK_ID_LEN + 1];
10080 boolean old_score_file_format = FALSE;
10083 // always start with reliable default values
10084 setScoreInfoToDefaults();
10086 if (!(file = openFile(filename, MODE_READ)))
10089 getFileChunkBE(file, chunk_name, NULL);
10090 if (strEqual(chunk_name, "RND1"))
10092 getFile32BitBE(file); // not used
10094 getFileChunkBE(file, chunk_name, NULL);
10095 if (!strEqual(chunk_name, "SCOR"))
10097 Warn("unknown format of score file '%s'", filename);
10104 else // check for old file format with cookie string
10106 strcpy(cookie, chunk_name);
10107 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10109 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10110 cookie[strlen(cookie) - 1] = '\0';
10112 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10114 Warn("unknown format of score file '%s'", filename);
10121 old_score_file_format = TRUE;
10124 if (old_score_file_format)
10126 // score files from versions before 4.2.4.0 without chunk structure
10129 // convert score to time, if possible (mainly for Supaplex levels)
10130 ConvertScore_OLD();
10138 int (*loader)(File *, int, struct ScoreInfo *);
10142 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10143 { "INFO", -1, LoadScore_INFO },
10144 { "NAME", -1, LoadScore_NAME },
10145 { "SCOR", -1, LoadScore_SCOR },
10146 { "SC4R", -1, LoadScore_SC4R },
10147 { "TIME", -1, LoadScore_TIME },
10148 { "TAPE", -1, LoadScore_TAPE },
10153 while (getFileChunkBE(file, chunk_name, &chunk_size))
10157 while (chunk_info[i].name != NULL &&
10158 !strEqual(chunk_name, chunk_info[i].name))
10161 if (chunk_info[i].name == NULL)
10163 Warn("unknown chunk '%s' in score file '%s'",
10164 chunk_name, filename);
10166 ReadUnusedBytesFromFile(file, chunk_size);
10168 else if (chunk_info[i].size != -1 &&
10169 chunk_info[i].size != chunk_size)
10171 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10172 chunk_size, chunk_name, filename);
10174 ReadUnusedBytesFromFile(file, chunk_size);
10178 // call function to load this score chunk
10179 int chunk_size_expected =
10180 (chunk_info[i].loader)(file, chunk_size, &scores);
10182 // the size of some chunks cannot be checked before reading other
10183 // chunks first (like "HEAD" and "BODY") that contain some header
10184 // information, so check them here
10185 if (chunk_size_expected != chunk_size)
10187 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10188 chunk_size, chunk_name, filename);
10197 #if ENABLE_HISTORIC_CHUNKS
10198 void SaveScore_OLD(int nr)
10201 char *filename = getScoreFilename(nr);
10204 // used instead of "leveldir_current->subdir" (for network games)
10205 InitScoreDirectory(levelset.identifier);
10207 if (!(file = fopen(filename, MODE_WRITE)))
10209 Warn("cannot save score for level %d", nr);
10214 fprintf(file, "%s\n\n", SCORE_COOKIE);
10216 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10217 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10221 SetFilePermissions(filename, PERMS_PRIVATE);
10225 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10227 putFileVersion(file, scores->file_version);
10228 putFileVersion(file, scores->game_version);
10231 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10233 int level_identifier_size = strlen(scores->level_identifier) + 1;
10236 putFile16BitBE(file, level_identifier_size);
10238 for (i = 0; i < level_identifier_size; i++)
10239 putFile8Bit(file, scores->level_identifier[i]);
10241 putFile16BitBE(file, scores->level_nr);
10242 putFile16BitBE(file, scores->num_entries);
10245 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10249 for (i = 0; i < scores->num_entries; i++)
10251 int name_size = strlen(scores->entry[i].name);
10253 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10254 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10258 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10262 for (i = 0; i < scores->num_entries; i++)
10263 putFile16BitBE(file, scores->entry[i].score);
10266 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10270 for (i = 0; i < scores->num_entries; i++)
10271 putFile32BitBE(file, scores->entry[i].score);
10274 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10278 for (i = 0; i < scores->num_entries; i++)
10279 putFile32BitBE(file, scores->entry[i].time);
10282 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10286 for (i = 0; i < scores->num_entries; i++)
10288 int size = strlen(scores->entry[i].tape_basename);
10290 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10291 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10295 static void SaveScoreToFilename(char *filename)
10298 int info_chunk_size;
10299 int name_chunk_size;
10300 int scor_chunk_size;
10301 int sc4r_chunk_size;
10302 int time_chunk_size;
10303 int tape_chunk_size;
10304 boolean has_large_score_values;
10307 if (!(file = fopen(filename, MODE_WRITE)))
10309 Warn("cannot save score file '%s'", filename);
10314 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10315 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10316 scor_chunk_size = scores.num_entries * 2;
10317 sc4r_chunk_size = scores.num_entries * 4;
10318 time_chunk_size = scores.num_entries * 4;
10319 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10321 has_large_score_values = FALSE;
10322 for (i = 0; i < scores.num_entries; i++)
10323 if (scores.entry[i].score > 0xffff)
10324 has_large_score_values = TRUE;
10326 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10327 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10329 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10330 SaveScore_VERS(file, &scores);
10332 putFileChunkBE(file, "INFO", info_chunk_size);
10333 SaveScore_INFO(file, &scores);
10335 putFileChunkBE(file, "NAME", name_chunk_size);
10336 SaveScore_NAME(file, &scores);
10338 if (has_large_score_values)
10340 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10341 SaveScore_SC4R(file, &scores);
10345 putFileChunkBE(file, "SCOR", scor_chunk_size);
10346 SaveScore_SCOR(file, &scores);
10349 putFileChunkBE(file, "TIME", time_chunk_size);
10350 SaveScore_TIME(file, &scores);
10352 putFileChunkBE(file, "TAPE", tape_chunk_size);
10353 SaveScore_TAPE(file, &scores);
10357 SetFilePermissions(filename, PERMS_PRIVATE);
10360 void SaveScore(int nr)
10362 char *filename = getScoreFilename(nr);
10365 // used instead of "leveldir_current->subdir" (for network games)
10366 InitScoreDirectory(levelset.identifier);
10368 scores.file_version = FILE_VERSION_ACTUAL;
10369 scores.game_version = GAME_VERSION_ACTUAL;
10371 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10372 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10373 scores.level_nr = level_nr;
10375 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10376 if (scores.entry[i].score == 0 &&
10377 scores.entry[i].time == 0 &&
10378 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10381 scores.num_entries = i;
10383 if (scores.num_entries == 0)
10386 SaveScoreToFilename(filename);
10389 static void LoadServerScoreFromCache(int nr)
10391 struct ScoreEntry score_entry;
10400 { &score_entry.score, FALSE, 0 },
10401 { &score_entry.time, FALSE, 0 },
10402 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10403 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10404 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10405 { &score_entry.id, FALSE, 0 },
10406 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10407 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10408 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10409 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10413 char *filename = getScoreCacheFilename(nr);
10414 SetupFileHash *score_hash = loadSetupFileHash(filename);
10417 server_scores.num_entries = 0;
10419 if (score_hash == NULL)
10422 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10424 score_entry = server_scores.entry[i];
10426 for (j = 0; score_mapping[j].value != NULL; j++)
10430 sprintf(token, "%02d.%d", i, j);
10432 char *value = getHashEntry(score_hash, token);
10437 if (score_mapping[j].is_string)
10439 char *score_value = (char *)score_mapping[j].value;
10440 int value_size = score_mapping[j].string_size;
10442 strncpy(score_value, value, value_size);
10443 score_value[value_size] = '\0';
10447 int *score_value = (int *)score_mapping[j].value;
10449 *score_value = atoi(value);
10452 server_scores.num_entries = i + 1;
10455 server_scores.entry[i] = score_entry;
10458 freeSetupFileHash(score_hash);
10461 void LoadServerScore(int nr, boolean download_score)
10463 if (!setup.use_api_server)
10466 // always start with reliable default values
10467 setServerScoreInfoToDefaults();
10469 // 1st step: load server scores from cache file (which may not exist)
10470 // (this should prevent reading it while the thread is writing to it)
10471 LoadServerScoreFromCache(nr);
10473 if (download_score && runtime.use_api_server)
10475 // 2nd step: download server scores from score server to cache file
10476 // (as thread, as it might time out if the server is not reachable)
10477 ApiGetScoreAsThread(nr);
10481 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10483 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10485 // if score tape not uploaded, ask for uploading missing tapes later
10486 if (!setup.has_remaining_tapes)
10487 setup.ask_for_remaining_tapes = TRUE;
10489 setup.provide_uploading_tapes = TRUE;
10490 setup.has_remaining_tapes = TRUE;
10492 SaveSetup_ServerSetup();
10495 void SaveServerScore(int nr, boolean tape_saved)
10497 if (!runtime.use_api_server)
10499 PrepareScoreTapesForUpload(leveldir_current->subdir);
10504 ApiAddScoreAsThread(nr, tape_saved, NULL);
10507 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10508 char *score_tape_filename)
10510 if (!runtime.use_api_server)
10513 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10516 void LoadLocalAndServerScore(int nr, boolean download_score)
10518 int last_added_local = scores.last_added_local;
10519 boolean force_last_added = scores.force_last_added;
10521 // needed if only showing server scores
10522 setScoreInfoToDefaults();
10524 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10527 // restore last added local score entry (before merging server scores)
10528 scores.last_added = scores.last_added_local = last_added_local;
10530 if (setup.use_api_server &&
10531 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10533 // load server scores from cache file and trigger update from server
10534 LoadServerScore(nr, download_score);
10536 // merge local scores with scores from server
10537 MergeServerScore();
10540 if (force_last_added)
10541 scores.force_last_added = force_last_added;
10545 // ============================================================================
10546 // setup file functions
10547 // ============================================================================
10549 #define TOKEN_STR_PLAYER_PREFIX "player_"
10552 static struct TokenInfo global_setup_tokens[] =
10556 &setup.player_name, "player_name"
10560 &setup.multiple_users, "multiple_users"
10564 &setup.sound, "sound"
10568 &setup.sound_loops, "repeating_sound_loops"
10572 &setup.sound_music, "background_music"
10576 &setup.sound_simple, "simple_sound_effects"
10580 &setup.toons, "toons"
10584 &setup.global_animations, "global_animations"
10588 &setup.scroll_delay, "scroll_delay"
10592 &setup.forced_scroll_delay, "forced_scroll_delay"
10596 &setup.scroll_delay_value, "scroll_delay_value"
10600 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10604 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10608 &setup.fade_screens, "fade_screens"
10612 &setup.autorecord, "automatic_tape_recording"
10616 &setup.autorecord_after_replay, "autorecord_after_replay"
10620 &setup.auto_pause_on_start, "auto_pause_on_start"
10624 &setup.show_titlescreen, "show_titlescreen"
10628 &setup.quick_doors, "quick_doors"
10632 &setup.team_mode, "team_mode"
10636 &setup.handicap, "handicap"
10640 &setup.skip_levels, "skip_levels"
10644 &setup.increment_levels, "increment_levels"
10648 &setup.auto_play_next_level, "auto_play_next_level"
10652 &setup.count_score_after_game, "count_score_after_game"
10656 &setup.show_scores_after_game, "show_scores_after_game"
10660 &setup.time_limit, "time_limit"
10664 &setup.fullscreen, "fullscreen"
10668 &setup.window_scaling_percent, "window_scaling_percent"
10672 &setup.window_scaling_quality, "window_scaling_quality"
10676 &setup.screen_rendering_mode, "screen_rendering_mode"
10680 &setup.vsync_mode, "vsync_mode"
10684 &setup.ask_on_escape, "ask_on_escape"
10688 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10692 &setup.ask_on_game_over, "ask_on_game_over"
10696 &setup.ask_on_quit_game, "ask_on_quit_game"
10700 &setup.ask_on_quit_program, "ask_on_quit_program"
10704 &setup.quick_switch, "quick_player_switch"
10708 &setup.input_on_focus, "input_on_focus"
10712 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10716 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10720 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10724 &setup.game_speed_extended, "game_speed_extended"
10728 &setup.game_frame_delay, "game_frame_delay"
10732 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10736 &setup.bd_skip_hatching, "bd_skip_hatching"
10740 &setup.bd_scroll_delay, "bd_scroll_delay"
10744 &setup.bd_smooth_movements, "bd_smooth_movements"
10748 &setup.bd_palette_c64, "bd_palette_c64"
10752 &setup.bd_palette_c64dtv, "bd_palette_c64dtv"
10756 &setup.bd_palette_atari, "bd_palette_atari"
10760 &setup.bd_color_type, "bd_color_type"
10764 &setup.sp_show_border_elements, "sp_show_border_elements"
10768 &setup.small_game_graphics, "small_game_graphics"
10772 &setup.show_load_save_buttons, "show_load_save_buttons"
10776 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10780 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10784 &setup.graphics_set, "graphics_set"
10788 &setup.sounds_set, "sounds_set"
10792 &setup.music_set, "music_set"
10796 &setup.override_level_graphics, "override_level_graphics"
10800 &setup.override_level_sounds, "override_level_sounds"
10804 &setup.override_level_music, "override_level_music"
10808 &setup.volume_simple, "volume_simple"
10812 &setup.volume_loops, "volume_loops"
10816 &setup.volume_music, "volume_music"
10820 &setup.network_mode, "network_mode"
10824 &setup.network_player_nr, "network_player"
10828 &setup.network_server_hostname, "network_server_hostname"
10832 &setup.touch.control_type, "touch.control_type"
10836 &setup.touch.move_distance, "touch.move_distance"
10840 &setup.touch.drop_distance, "touch.drop_distance"
10844 &setup.touch.transparency, "touch.transparency"
10848 &setup.touch.draw_outlined, "touch.draw_outlined"
10852 &setup.touch.draw_pressed, "touch.draw_pressed"
10856 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10860 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10864 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10868 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10872 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10876 static struct TokenInfo auto_setup_tokens[] =
10880 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10884 static struct TokenInfo server_setup_tokens[] =
10888 &setup.player_uuid, "player_uuid"
10892 &setup.player_version, "player_version"
10896 &setup.use_api_server, TEST_PREFIX "use_api_server"
10900 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10904 &setup.api_server_password, TEST_PREFIX "api_server_password"
10908 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10912 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10916 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10920 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10924 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10928 static struct TokenInfo editor_setup_tokens[] =
10932 &setup.editor.el_classic, "editor.el_classic"
10936 &setup.editor.el_custom, "editor.el_custom"
10940 &setup.editor.el_user_defined, "editor.el_user_defined"
10944 &setup.editor.el_dynamic, "editor.el_dynamic"
10948 &setup.editor.el_headlines, "editor.el_headlines"
10952 &setup.editor.show_element_token, "editor.show_element_token"
10956 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10960 static struct TokenInfo editor_cascade_setup_tokens[] =
10964 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10968 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10972 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10976 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10980 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10984 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10988 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10992 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10996 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
11000 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
11004 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
11008 &setup.editor_cascade.el_df, "editor.cascade.el_df"
11012 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
11016 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
11020 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
11024 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
11028 &setup.editor_cascade.el_es, "editor.cascade.el_es"
11032 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
11036 &setup.editor_cascade.el_user, "editor.cascade.el_user"
11040 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
11044 static struct TokenInfo shortcut_setup_tokens[] =
11048 &setup.shortcut.save_game, "shortcut.save_game"
11052 &setup.shortcut.load_game, "shortcut.load_game"
11056 &setup.shortcut.restart_game, "shortcut.restart_game"
11060 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
11064 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
11068 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
11072 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
11076 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11080 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11084 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11088 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11092 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11096 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11100 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11104 &setup.shortcut.tape_record, "shortcut.tape_record"
11108 &setup.shortcut.tape_play, "shortcut.tape_play"
11112 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11116 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11120 &setup.shortcut.sound_music, "shortcut.sound_music"
11124 &setup.shortcut.snap_left, "shortcut.snap_left"
11128 &setup.shortcut.snap_right, "shortcut.snap_right"
11132 &setup.shortcut.snap_up, "shortcut.snap_up"
11136 &setup.shortcut.snap_down, "shortcut.snap_down"
11140 static struct SetupInputInfo setup_input;
11141 static struct TokenInfo player_setup_tokens[] =
11145 &setup_input.use_joystick, ".use_joystick"
11149 &setup_input.joy.device_name, ".joy.device_name"
11153 &setup_input.joy.xleft, ".joy.xleft"
11157 &setup_input.joy.xmiddle, ".joy.xmiddle"
11161 &setup_input.joy.xright, ".joy.xright"
11165 &setup_input.joy.yupper, ".joy.yupper"
11169 &setup_input.joy.ymiddle, ".joy.ymiddle"
11173 &setup_input.joy.ylower, ".joy.ylower"
11177 &setup_input.joy.snap, ".joy.snap_field"
11181 &setup_input.joy.drop, ".joy.place_bomb"
11185 &setup_input.key.left, ".key.move_left"
11189 &setup_input.key.right, ".key.move_right"
11193 &setup_input.key.up, ".key.move_up"
11197 &setup_input.key.down, ".key.move_down"
11201 &setup_input.key.snap, ".key.snap_field"
11205 &setup_input.key.drop, ".key.place_bomb"
11209 static struct TokenInfo system_setup_tokens[] =
11213 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11217 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11221 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11225 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11229 static struct TokenInfo internal_setup_tokens[] =
11233 &setup.internal.program_title, "program_title"
11237 &setup.internal.program_version, "program_version"
11241 &setup.internal.program_author, "program_author"
11245 &setup.internal.program_email, "program_email"
11249 &setup.internal.program_website, "program_website"
11253 &setup.internal.program_copyright, "program_copyright"
11257 &setup.internal.program_company, "program_company"
11261 &setup.internal.program_icon_file, "program_icon_file"
11265 &setup.internal.default_graphics_set, "default_graphics_set"
11269 &setup.internal.default_sounds_set, "default_sounds_set"
11273 &setup.internal.default_music_set, "default_music_set"
11277 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11281 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11285 &setup.internal.fallback_music_file, "fallback_music_file"
11289 &setup.internal.default_level_series, "default_level_series"
11293 &setup.internal.default_window_width, "default_window_width"
11297 &setup.internal.default_window_height, "default_window_height"
11301 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11305 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11309 &setup.internal.create_user_levelset, "create_user_levelset"
11313 &setup.internal.info_screens_from_main, "info_screens_from_main"
11317 &setup.internal.menu_game, "menu_game"
11321 &setup.internal.menu_engines, "menu_engines"
11325 &setup.internal.menu_editor, "menu_editor"
11329 &setup.internal.menu_graphics, "menu_graphics"
11333 &setup.internal.menu_sound, "menu_sound"
11337 &setup.internal.menu_artwork, "menu_artwork"
11341 &setup.internal.menu_input, "menu_input"
11345 &setup.internal.menu_touch, "menu_touch"
11349 &setup.internal.menu_shortcuts, "menu_shortcuts"
11353 &setup.internal.menu_exit, "menu_exit"
11357 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11361 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11365 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11369 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11373 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11377 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11381 &setup.internal.info_title, "info_title"
11385 &setup.internal.info_elements, "info_elements"
11389 &setup.internal.info_music, "info_music"
11393 &setup.internal.info_credits, "info_credits"
11397 &setup.internal.info_program, "info_program"
11401 &setup.internal.info_version, "info_version"
11405 &setup.internal.info_levelset, "info_levelset"
11409 &setup.internal.info_exit, "info_exit"
11413 static struct TokenInfo debug_setup_tokens[] =
11417 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11421 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11425 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11429 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11433 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11437 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11441 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11445 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11449 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11453 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11457 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11461 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11465 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11469 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11473 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11477 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11481 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11485 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11489 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11493 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11497 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11500 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11504 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11508 &setup.debug.xsn_mode, "debug.xsn_mode"
11512 &setup.debug.xsn_percent, "debug.xsn_percent"
11516 static struct TokenInfo options_setup_tokens[] =
11520 &setup.options.verbose, "options.verbose"
11524 &setup.options.debug, "options.debug"
11528 &setup.options.debug_mode, "options.debug_mode"
11532 static void setSetupInfoToDefaults(struct SetupInfo *si)
11536 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11538 si->multiple_users = TRUE;
11541 si->sound_loops = TRUE;
11542 si->sound_music = TRUE;
11543 si->sound_simple = TRUE;
11545 si->global_animations = TRUE;
11546 si->scroll_delay = TRUE;
11547 si->forced_scroll_delay = FALSE;
11548 si->scroll_delay_value = STD_SCROLL_DELAY;
11549 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11550 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11551 si->fade_screens = TRUE;
11552 si->autorecord = TRUE;
11553 si->autorecord_after_replay = TRUE;
11554 si->auto_pause_on_start = FALSE;
11555 si->show_titlescreen = TRUE;
11556 si->quick_doors = FALSE;
11557 si->team_mode = FALSE;
11558 si->handicap = TRUE;
11559 si->skip_levels = TRUE;
11560 si->increment_levels = TRUE;
11561 si->auto_play_next_level = TRUE;
11562 si->count_score_after_game = TRUE;
11563 si->show_scores_after_game = TRUE;
11564 si->time_limit = TRUE;
11565 si->fullscreen = FALSE;
11566 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11567 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11568 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11569 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11570 si->ask_on_escape = TRUE;
11571 si->ask_on_escape_editor = TRUE;
11572 si->ask_on_game_over = TRUE;
11573 si->ask_on_quit_game = TRUE;
11574 si->ask_on_quit_program = TRUE;
11575 si->quick_switch = FALSE;
11576 si->input_on_focus = FALSE;
11577 si->prefer_aga_graphics = TRUE;
11578 si->prefer_lowpass_sounds = FALSE;
11579 si->prefer_extra_panel_items = TRUE;
11580 si->game_speed_extended = FALSE;
11581 si->game_frame_delay = GAME_FRAME_DELAY;
11582 si->bd_skip_uncovering = FALSE;
11583 si->bd_skip_hatching = FALSE;
11584 si->bd_scroll_delay = TRUE;
11585 si->bd_smooth_movements = AUTO;
11586 si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11587 si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11588 si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11589 si->bd_color_type = GD_DEFAULT_COLOR_TYPE;
11590 si->sp_show_border_elements = FALSE;
11591 si->small_game_graphics = FALSE;
11592 si->show_load_save_buttons = FALSE;
11593 si->show_undo_redo_buttons = FALSE;
11594 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11596 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11597 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11598 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11600 si->override_level_graphics = FALSE;
11601 si->override_level_sounds = FALSE;
11602 si->override_level_music = FALSE;
11604 si->volume_simple = 100; // percent
11605 si->volume_loops = 100; // percent
11606 si->volume_music = 100; // percent
11608 si->network_mode = FALSE;
11609 si->network_player_nr = 0; // first player
11610 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11612 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11613 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11614 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11615 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11616 si->touch.draw_outlined = TRUE;
11617 si->touch.draw_pressed = TRUE;
11619 for (i = 0; i < 2; i++)
11621 char *default_grid_button[6][2] =
11627 { "111222", " vv " },
11628 { "111222", " vv " }
11630 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11631 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11632 int min_xsize = MIN(6, grid_xsize);
11633 int min_ysize = MIN(6, grid_ysize);
11634 int startx = grid_xsize - min_xsize;
11635 int starty = grid_ysize - min_ysize;
11638 // virtual buttons grid can only be set to defaults if video is initialized
11639 // (this will be repeated if virtual buttons are not loaded from setup file)
11640 if (video.initialized)
11642 si->touch.grid_xsize[i] = grid_xsize;
11643 si->touch.grid_ysize[i] = grid_ysize;
11647 si->touch.grid_xsize[i] = -1;
11648 si->touch.grid_ysize[i] = -1;
11651 for (x = 0; x < MAX_GRID_XSIZE; x++)
11652 for (y = 0; y < MAX_GRID_YSIZE; y++)
11653 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11655 for (x = 0; x < min_xsize; x++)
11656 for (y = 0; y < min_ysize; y++)
11657 si->touch.grid_button[i][x][starty + y] =
11658 default_grid_button[y][0][x];
11660 for (x = 0; x < min_xsize; x++)
11661 for (y = 0; y < min_ysize; y++)
11662 si->touch.grid_button[i][startx + x][starty + y] =
11663 default_grid_button[y][1][x];
11666 si->touch.grid_initialized = video.initialized;
11668 si->touch.overlay_buttons = FALSE;
11670 si->editor.el_boulderdash = TRUE;
11671 si->editor.el_boulderdash_native = TRUE;
11672 si->editor.el_boulderdash_effects = TRUE;
11673 si->editor.el_emerald_mine = TRUE;
11674 si->editor.el_emerald_mine_club = TRUE;
11675 si->editor.el_more = TRUE;
11676 si->editor.el_sokoban = TRUE;
11677 si->editor.el_supaplex = TRUE;
11678 si->editor.el_diamond_caves = TRUE;
11679 si->editor.el_dx_boulderdash = TRUE;
11681 si->editor.el_mirror_magic = TRUE;
11682 si->editor.el_deflektor = TRUE;
11684 si->editor.el_chars = TRUE;
11685 si->editor.el_steel_chars = TRUE;
11687 si->editor.el_classic = TRUE;
11688 si->editor.el_custom = TRUE;
11690 si->editor.el_user_defined = FALSE;
11691 si->editor.el_dynamic = TRUE;
11693 si->editor.el_headlines = TRUE;
11695 si->editor.show_element_token = FALSE;
11697 si->editor.show_read_only_warning = TRUE;
11699 si->editor.use_template_for_new_levels = TRUE;
11701 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11702 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11703 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11704 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11705 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11707 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11708 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11709 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11710 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11711 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11713 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11714 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11715 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11716 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11717 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11718 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11720 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11721 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11722 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11724 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11725 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11726 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11727 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11729 for (i = 0; i < MAX_PLAYERS; i++)
11731 si->input[i].use_joystick = FALSE;
11732 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11733 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11734 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11735 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11736 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11737 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11738 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11739 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11740 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11741 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11742 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11743 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11744 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11745 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11746 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11749 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11750 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11751 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11752 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11754 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11755 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11756 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11757 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11758 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11759 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11760 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11762 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11764 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11765 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11766 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11768 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11769 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11770 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11772 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11773 si->internal.choose_from_top_leveldir = FALSE;
11774 si->internal.show_scaling_in_title = TRUE;
11775 si->internal.create_user_levelset = TRUE;
11776 si->internal.info_screens_from_main = FALSE;
11778 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11779 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11781 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11782 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11783 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11784 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11785 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11786 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11787 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11788 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11789 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11790 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11792 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11793 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11794 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11795 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11796 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11797 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11798 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11799 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11800 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11801 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11803 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11804 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11806 si->debug.show_frames_per_second = FALSE;
11808 si->debug.xsn_mode = AUTO;
11809 si->debug.xsn_percent = 0;
11811 si->options.verbose = FALSE;
11812 si->options.debug = FALSE;
11813 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11815 #if defined(PLATFORM_ANDROID)
11816 si->fullscreen = TRUE;
11817 si->touch.overlay_buttons = TRUE;
11820 setHideSetupEntry(&setup.debug.xsn_mode);
11823 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11825 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11828 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11830 si->player_uuid = NULL; // (will be set later)
11831 si->player_version = 1; // (will be set later)
11833 si->use_api_server = TRUE;
11834 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11835 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11836 si->ask_for_uploading_tapes = TRUE;
11837 si->ask_for_remaining_tapes = FALSE;
11838 si->provide_uploading_tapes = TRUE;
11839 si->ask_for_using_api_server = TRUE;
11840 si->has_remaining_tapes = FALSE;
11843 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11845 si->editor_cascade.el_bd = TRUE;
11846 si->editor_cascade.el_bd_native = TRUE;
11847 si->editor_cascade.el_bd_effects = FALSE;
11848 si->editor_cascade.el_em = TRUE;
11849 si->editor_cascade.el_emc = TRUE;
11850 si->editor_cascade.el_rnd = TRUE;
11851 si->editor_cascade.el_sb = TRUE;
11852 si->editor_cascade.el_sp = TRUE;
11853 si->editor_cascade.el_dc = TRUE;
11854 si->editor_cascade.el_dx = TRUE;
11856 si->editor_cascade.el_mm = TRUE;
11857 si->editor_cascade.el_df = TRUE;
11859 si->editor_cascade.el_chars = FALSE;
11860 si->editor_cascade.el_steel_chars = FALSE;
11861 si->editor_cascade.el_ce = FALSE;
11862 si->editor_cascade.el_ge = FALSE;
11863 si->editor_cascade.el_es = FALSE;
11864 si->editor_cascade.el_ref = FALSE;
11865 si->editor_cascade.el_user = FALSE;
11866 si->editor_cascade.el_dynamic = FALSE;
11869 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11871 static char *getHideSetupToken(void *setup_value)
11873 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11875 if (setup_value != NULL)
11876 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11878 return hide_setup_token;
11881 void setHideSetupEntry(void *setup_value)
11883 char *hide_setup_token = getHideSetupToken(setup_value);
11885 if (hide_setup_hash == NULL)
11886 hide_setup_hash = newSetupFileHash();
11888 if (setup_value != NULL)
11889 setHashEntry(hide_setup_hash, hide_setup_token, "");
11892 void removeHideSetupEntry(void *setup_value)
11894 char *hide_setup_token = getHideSetupToken(setup_value);
11896 if (setup_value != NULL)
11897 removeHashEntry(hide_setup_hash, hide_setup_token);
11900 boolean hideSetupEntry(void *setup_value)
11902 char *hide_setup_token = getHideSetupToken(setup_value);
11904 return (setup_value != NULL &&
11905 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11908 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11909 struct TokenInfo *token_info,
11910 int token_nr, char *token_text)
11912 char *token_hide_text = getStringCat2(token_text, ".hide");
11913 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11915 // set the value of this setup option in the setup option structure
11916 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11918 // check if this setup option should be hidden in the setup menu
11919 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11920 setHideSetupEntry(token_info[token_nr].value);
11922 free(token_hide_text);
11925 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11926 struct TokenInfo *token_info,
11929 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11930 token_info[token_nr].text);
11933 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11937 if (!setup_file_hash)
11940 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11941 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11943 setup.touch.grid_initialized = TRUE;
11944 for (i = 0; i < 2; i++)
11946 int grid_xsize = setup.touch.grid_xsize[i];
11947 int grid_ysize = setup.touch.grid_ysize[i];
11950 // if virtual buttons are not loaded from setup file, repeat initializing
11951 // virtual buttons grid with default values later when video is initialized
11952 if (grid_xsize == -1 ||
11955 setup.touch.grid_initialized = FALSE;
11960 for (y = 0; y < grid_ysize; y++)
11962 char token_string[MAX_LINE_LEN];
11964 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11966 char *value_string = getHashEntry(setup_file_hash, token_string);
11968 if (value_string == NULL)
11971 for (x = 0; x < grid_xsize; x++)
11973 char c = value_string[x];
11975 setup.touch.grid_button[i][x][y] =
11976 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11981 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11982 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11984 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11985 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11987 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11991 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11993 setup_input = setup.input[pnr];
11994 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11996 char full_token[100];
11998 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11999 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12002 setup.input[pnr] = setup_input;
12005 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12006 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12008 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12009 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12011 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12012 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12014 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12015 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12017 setHideRelatedSetupEntries();
12020 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12024 if (!setup_file_hash)
12027 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12028 setSetupInfo(auto_setup_tokens, i,
12029 getHashEntry(setup_file_hash,
12030 auto_setup_tokens[i].text));
12033 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12037 if (!setup_file_hash)
12040 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12041 setSetupInfo(server_setup_tokens, i,
12042 getHashEntry(setup_file_hash,
12043 server_setup_tokens[i].text));
12046 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12050 if (!setup_file_hash)
12053 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12054 setSetupInfo(editor_cascade_setup_tokens, i,
12055 getHashEntry(setup_file_hash,
12056 editor_cascade_setup_tokens[i].text));
12059 void LoadUserNames(void)
12061 int last_user_nr = user.nr;
12064 if (global.user_names != NULL)
12066 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12067 checked_free(global.user_names[i]);
12069 checked_free(global.user_names);
12072 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12074 for (i = 0; i < MAX_PLAYER_NAMES; i++)
12078 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12080 if (setup_file_hash)
12082 char *player_name = getHashEntry(setup_file_hash, "player_name");
12084 global.user_names[i] = getFixedUserName(player_name);
12086 freeSetupFileHash(setup_file_hash);
12089 if (global.user_names[i] == NULL)
12090 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12093 user.nr = last_user_nr;
12096 void LoadSetupFromFilename(char *filename)
12098 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12100 if (setup_file_hash)
12102 decodeSetupFileHash_Default(setup_file_hash);
12104 freeSetupFileHash(setup_file_hash);
12108 Debug("setup", "using default setup values");
12112 static void LoadSetup_SpecialPostProcessing(void)
12114 char *player_name_new;
12116 // needed to work around problems with fixed length strings
12117 player_name_new = getFixedUserName(setup.player_name);
12118 free(setup.player_name);
12119 setup.player_name = player_name_new;
12121 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12122 if (setup.scroll_delay == FALSE)
12124 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12125 setup.scroll_delay = TRUE; // now always "on"
12128 // make sure that scroll delay value stays inside valid range
12129 setup.scroll_delay_value =
12130 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12133 void LoadSetup_Default(void)
12137 // always start with reliable default values
12138 setSetupInfoToDefaults(&setup);
12140 // try to load setup values from default setup file
12141 filename = getDefaultSetupFilename();
12143 if (fileExists(filename))
12144 LoadSetupFromFilename(filename);
12146 // try to load setup values from platform setup file
12147 filename = getPlatformSetupFilename();
12149 if (fileExists(filename))
12150 LoadSetupFromFilename(filename);
12152 // try to load setup values from user setup file
12153 filename = getSetupFilename();
12155 LoadSetupFromFilename(filename);
12157 LoadSetup_SpecialPostProcessing();
12160 void LoadSetup_AutoSetup(void)
12162 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12163 SetupFileHash *setup_file_hash = NULL;
12165 // always start with reliable default values
12166 setSetupInfoToDefaults_AutoSetup(&setup);
12168 setup_file_hash = loadSetupFileHash(filename);
12170 if (setup_file_hash)
12172 decodeSetupFileHash_AutoSetup(setup_file_hash);
12174 freeSetupFileHash(setup_file_hash);
12180 void LoadSetup_ServerSetup(void)
12182 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12183 SetupFileHash *setup_file_hash = NULL;
12185 // always start with reliable default values
12186 setSetupInfoToDefaults_ServerSetup(&setup);
12188 setup_file_hash = loadSetupFileHash(filename);
12190 if (setup_file_hash)
12192 decodeSetupFileHash_ServerSetup(setup_file_hash);
12194 freeSetupFileHash(setup_file_hash);
12199 if (setup.player_uuid == NULL)
12201 // player UUID does not yet exist in setup file
12202 setup.player_uuid = getStringCopy(getUUID());
12203 setup.player_version = 2;
12205 SaveSetup_ServerSetup();
12209 void LoadSetup_EditorCascade(void)
12211 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12212 SetupFileHash *setup_file_hash = NULL;
12214 // always start with reliable default values
12215 setSetupInfoToDefaults_EditorCascade(&setup);
12217 setup_file_hash = loadSetupFileHash(filename);
12219 if (setup_file_hash)
12221 decodeSetupFileHash_EditorCascade(setup_file_hash);
12223 freeSetupFileHash(setup_file_hash);
12229 void LoadSetup(void)
12231 LoadSetup_Default();
12232 LoadSetup_AutoSetup();
12233 LoadSetup_ServerSetup();
12234 LoadSetup_EditorCascade();
12237 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12238 char *mapping_line)
12240 char mapping_guid[MAX_LINE_LEN];
12241 char *mapping_start, *mapping_end;
12243 // get GUID from game controller mapping line: copy complete line
12244 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12245 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12247 // get GUID from game controller mapping line: cut after GUID part
12248 mapping_start = strchr(mapping_guid, ',');
12249 if (mapping_start != NULL)
12250 *mapping_start = '\0';
12252 // cut newline from game controller mapping line
12253 mapping_end = strchr(mapping_line, '\n');
12254 if (mapping_end != NULL)
12255 *mapping_end = '\0';
12257 // add mapping entry to game controller mappings hash
12258 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12261 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12266 if (!(file = fopen(filename, MODE_READ)))
12268 Warn("cannot read game controller mappings file '%s'", filename);
12273 while (!feof(file))
12275 char line[MAX_LINE_LEN];
12277 if (!fgets(line, MAX_LINE_LEN, file))
12280 addGameControllerMappingToHash(mappings_hash, line);
12286 void SaveSetup_Default(void)
12288 char *filename = getSetupFilename();
12292 InitUserDataDirectory();
12294 if (!(file = fopen(filename, MODE_WRITE)))
12296 Warn("cannot write setup file '%s'", filename);
12301 fprintFileHeader(file, SETUP_FILENAME);
12303 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12305 // just to make things nicer :)
12306 if (global_setup_tokens[i].value == &setup.multiple_users ||
12307 global_setup_tokens[i].value == &setup.sound ||
12308 global_setup_tokens[i].value == &setup.graphics_set ||
12309 global_setup_tokens[i].value == &setup.volume_simple ||
12310 global_setup_tokens[i].value == &setup.network_mode ||
12311 global_setup_tokens[i].value == &setup.touch.control_type ||
12312 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12313 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12314 fprintf(file, "\n");
12316 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12319 for (i = 0; i < 2; i++)
12321 int grid_xsize = setup.touch.grid_xsize[i];
12322 int grid_ysize = setup.touch.grid_ysize[i];
12325 fprintf(file, "\n");
12327 for (y = 0; y < grid_ysize; y++)
12329 char token_string[MAX_LINE_LEN];
12330 char value_string[MAX_LINE_LEN];
12332 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12334 for (x = 0; x < grid_xsize; x++)
12336 char c = setup.touch.grid_button[i][x][y];
12338 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12341 value_string[grid_xsize] = '\0';
12343 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12347 fprintf(file, "\n");
12348 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12349 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12351 fprintf(file, "\n");
12352 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12353 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12355 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12359 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12360 fprintf(file, "\n");
12362 setup_input = setup.input[pnr];
12363 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12364 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12367 fprintf(file, "\n");
12368 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12369 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12371 // (internal setup values not saved to user setup file)
12373 fprintf(file, "\n");
12374 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12375 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12376 setup.debug.xsn_mode != AUTO)
12377 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12379 fprintf(file, "\n");
12380 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12381 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12385 SetFilePermissions(filename, PERMS_PRIVATE);
12388 void SaveSetup_AutoSetup(void)
12390 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12394 InitUserDataDirectory();
12396 if (!(file = fopen(filename, MODE_WRITE)))
12398 Warn("cannot write auto setup file '%s'", filename);
12405 fprintFileHeader(file, AUTOSETUP_FILENAME);
12407 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12408 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12412 SetFilePermissions(filename, PERMS_PRIVATE);
12417 void SaveSetup_ServerSetup(void)
12419 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12423 InitUserDataDirectory();
12425 if (!(file = fopen(filename, MODE_WRITE)))
12427 Warn("cannot write server setup file '%s'", filename);
12434 fprintFileHeader(file, SERVERSETUP_FILENAME);
12436 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12438 // just to make things nicer :)
12439 if (server_setup_tokens[i].value == &setup.use_api_server)
12440 fprintf(file, "\n");
12442 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12447 SetFilePermissions(filename, PERMS_PRIVATE);
12452 void SaveSetup_EditorCascade(void)
12454 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12458 InitUserDataDirectory();
12460 if (!(file = fopen(filename, MODE_WRITE)))
12462 Warn("cannot write editor cascade state file '%s'", filename);
12469 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12471 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12472 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12476 SetFilePermissions(filename, PERMS_PRIVATE);
12481 void SaveSetup(void)
12483 SaveSetup_Default();
12484 SaveSetup_AutoSetup();
12485 SaveSetup_ServerSetup();
12486 SaveSetup_EditorCascade();
12489 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12494 if (!(file = fopen(filename, MODE_WRITE)))
12496 Warn("cannot write game controller mappings file '%s'", filename);
12501 BEGIN_HASH_ITERATION(mappings_hash, itr)
12503 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12505 END_HASH_ITERATION(mappings_hash, itr)
12510 void SaveSetup_AddGameControllerMapping(char *mapping)
12512 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12513 SetupFileHash *mappings_hash = newSetupFileHash();
12515 InitUserDataDirectory();
12517 // load existing personal game controller mappings
12518 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12520 // add new mapping to personal game controller mappings
12521 addGameControllerMappingToHash(mappings_hash, mapping);
12523 // save updated personal game controller mappings
12524 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12526 freeSetupFileHash(mappings_hash);
12530 void LoadCustomElementDescriptions(void)
12532 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12533 SetupFileHash *setup_file_hash;
12536 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12538 if (element_info[i].custom_description != NULL)
12540 free(element_info[i].custom_description);
12541 element_info[i].custom_description = NULL;
12545 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12548 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12550 char *token = getStringCat2(element_info[i].token_name, ".name");
12551 char *value = getHashEntry(setup_file_hash, token);
12554 element_info[i].custom_description = getStringCopy(value);
12559 freeSetupFileHash(setup_file_hash);
12562 static int getElementFromToken(char *token)
12564 char *value = getHashEntry(element_token_hash, token);
12567 return atoi(value);
12569 Warn("unknown element token '%s'", token);
12571 return EL_UNDEFINED;
12574 void FreeGlobalAnimEventInfo(void)
12576 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12578 if (gaei->event_list == NULL)
12583 for (i = 0; i < gaei->num_event_lists; i++)
12585 checked_free(gaei->event_list[i]->event_value);
12586 checked_free(gaei->event_list[i]);
12589 checked_free(gaei->event_list);
12591 gaei->event_list = NULL;
12592 gaei->num_event_lists = 0;
12595 static int AddGlobalAnimEventList(void)
12597 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12598 int list_pos = gaei->num_event_lists++;
12600 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12601 sizeof(struct GlobalAnimEventListInfo *));
12603 gaei->event_list[list_pos] =
12604 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12606 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12608 gaeli->event_value = NULL;
12609 gaeli->num_event_values = 0;
12614 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12616 // do not add empty global animation events
12617 if (event_value == ANIM_EVENT_NONE)
12620 // if list position is undefined, create new list
12621 if (list_pos == ANIM_EVENT_UNDEFINED)
12622 list_pos = AddGlobalAnimEventList();
12624 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12625 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12626 int value_pos = gaeli->num_event_values++;
12628 gaeli->event_value = checked_realloc(gaeli->event_value,
12629 gaeli->num_event_values * sizeof(int *));
12631 gaeli->event_value[value_pos] = event_value;
12636 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12638 if (list_pos == ANIM_EVENT_UNDEFINED)
12639 return ANIM_EVENT_NONE;
12641 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12642 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12644 return gaeli->event_value[value_pos];
12647 int GetGlobalAnimEventValueCount(int list_pos)
12649 if (list_pos == ANIM_EVENT_UNDEFINED)
12652 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12653 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12655 return gaeli->num_event_values;
12658 // This function checks if a string <s> of the format "string1, string2, ..."
12659 // exactly contains a string <s_contained>.
12661 static boolean string_has_parameter(char *s, char *s_contained)
12665 if (s == NULL || s_contained == NULL)
12668 if (strlen(s_contained) > strlen(s))
12671 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12673 char next_char = s[strlen(s_contained)];
12675 // check if next character is delimiter or whitespace
12676 if (next_char == ',' || next_char == '\0' ||
12677 next_char == ' ' || next_char == '\t')
12681 // check if string contains another parameter string after a comma
12682 substring = strchr(s, ',');
12683 if (substring == NULL) // string does not contain a comma
12686 // advance string pointer to next character after the comma
12689 // skip potential whitespaces after the comma
12690 while (*substring == ' ' || *substring == '\t')
12693 return string_has_parameter(substring, s_contained);
12696 static int get_anim_parameter_value_ce(char *s)
12699 char *pattern_1 = "ce_change:custom_";
12700 char *pattern_2 = ".page_";
12701 int pattern_1_len = strlen(pattern_1);
12702 char *matching_char = strstr(s_ptr, pattern_1);
12703 int result = ANIM_EVENT_NONE;
12705 if (matching_char == NULL)
12706 return ANIM_EVENT_NONE;
12708 result = ANIM_EVENT_CE_CHANGE;
12710 s_ptr = matching_char + pattern_1_len;
12712 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12713 if (*s_ptr >= '0' && *s_ptr <= '9')
12715 int gic_ce_nr = (*s_ptr++ - '0');
12717 if (*s_ptr >= '0' && *s_ptr <= '9')
12719 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12721 if (*s_ptr >= '0' && *s_ptr <= '9')
12722 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12725 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12726 return ANIM_EVENT_NONE;
12728 // custom element stored as 0 to 255
12731 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12735 // invalid custom element number specified
12737 return ANIM_EVENT_NONE;
12740 // check for change page number ("page_X" or "page_XX") (optional)
12741 if (strPrefix(s_ptr, pattern_2))
12743 s_ptr += strlen(pattern_2);
12745 if (*s_ptr >= '0' && *s_ptr <= '9')
12747 int gic_page_nr = (*s_ptr++ - '0');
12749 if (*s_ptr >= '0' && *s_ptr <= '9')
12750 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12752 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12753 return ANIM_EVENT_NONE;
12755 // change page stored as 1 to 32 (0 means "all change pages")
12757 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12761 // invalid animation part number specified
12763 return ANIM_EVENT_NONE;
12767 // discard result if next character is neither delimiter nor whitespace
12768 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12769 *s_ptr == ' ' || *s_ptr == '\t'))
12770 return ANIM_EVENT_NONE;
12775 static int get_anim_parameter_value(char *s)
12777 int event_value[] =
12785 char *pattern_1[] =
12793 char *pattern_2 = ".part_";
12794 char *matching_char = NULL;
12796 int pattern_1_len = 0;
12797 int result = ANIM_EVENT_NONE;
12800 result = get_anim_parameter_value_ce(s);
12802 if (result != ANIM_EVENT_NONE)
12805 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12807 matching_char = strstr(s_ptr, pattern_1[i]);
12808 pattern_1_len = strlen(pattern_1[i]);
12809 result = event_value[i];
12811 if (matching_char != NULL)
12815 if (matching_char == NULL)
12816 return ANIM_EVENT_NONE;
12818 s_ptr = matching_char + pattern_1_len;
12820 // check for main animation number ("anim_X" or "anim_XX")
12821 if (*s_ptr >= '0' && *s_ptr <= '9')
12823 int gic_anim_nr = (*s_ptr++ - '0');
12825 if (*s_ptr >= '0' && *s_ptr <= '9')
12826 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12828 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12829 return ANIM_EVENT_NONE;
12831 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12835 // invalid main animation number specified
12837 return ANIM_EVENT_NONE;
12840 // check for animation part number ("part_X" or "part_XX") (optional)
12841 if (strPrefix(s_ptr, pattern_2))
12843 s_ptr += strlen(pattern_2);
12845 if (*s_ptr >= '0' && *s_ptr <= '9')
12847 int gic_part_nr = (*s_ptr++ - '0');
12849 if (*s_ptr >= '0' && *s_ptr <= '9')
12850 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12852 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12853 return ANIM_EVENT_NONE;
12855 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12859 // invalid animation part number specified
12861 return ANIM_EVENT_NONE;
12865 // discard result if next character is neither delimiter nor whitespace
12866 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12867 *s_ptr == ' ' || *s_ptr == '\t'))
12868 return ANIM_EVENT_NONE;
12873 static int get_anim_parameter_values(char *s)
12875 int list_pos = ANIM_EVENT_UNDEFINED;
12876 int event_value = ANIM_EVENT_DEFAULT;
12878 if (string_has_parameter(s, "any"))
12879 event_value |= ANIM_EVENT_ANY;
12881 if (string_has_parameter(s, "click:self") ||
12882 string_has_parameter(s, "click") ||
12883 string_has_parameter(s, "self"))
12884 event_value |= ANIM_EVENT_SELF;
12886 if (string_has_parameter(s, "unclick:any"))
12887 event_value |= ANIM_EVENT_UNCLICK_ANY;
12889 // if animation event found, add it to global animation event list
12890 if (event_value != ANIM_EVENT_NONE)
12891 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12895 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12896 event_value = get_anim_parameter_value(s);
12898 // if animation event found, add it to global animation event list
12899 if (event_value != ANIM_EVENT_NONE)
12900 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12902 // continue with next part of the string, starting with next comma
12903 s = strchr(s + 1, ',');
12909 static int get_anim_action_parameter_value(char *token)
12911 // check most common default case first to massively speed things up
12912 if (strEqual(token, ARG_UNDEFINED))
12913 return ANIM_EVENT_ACTION_NONE;
12915 int result = getImageIDFromToken(token);
12919 char *gfx_token = getStringCat2("gfx.", token);
12921 result = getImageIDFromToken(gfx_token);
12923 checked_free(gfx_token);
12928 Key key = getKeyFromX11KeyName(token);
12930 if (key != KSYM_UNDEFINED)
12931 result = -(int)key;
12938 result = get_hash_from_string(token); // unsigned int => int
12939 result = ABS(result); // may be negative now
12940 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12942 setHashEntry(anim_url_hash, int2str(result, 0), token);
12947 result = ANIM_EVENT_ACTION_NONE;
12952 int get_parameter_value(char *value_raw, char *suffix, int type)
12954 char *value = getStringToLower(value_raw);
12955 int result = 0; // probably a save default value
12957 if (strEqual(suffix, ".direction"))
12959 result = (strEqual(value, "left") ? MV_LEFT :
12960 strEqual(value, "right") ? MV_RIGHT :
12961 strEqual(value, "up") ? MV_UP :
12962 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12964 else if (strEqual(suffix, ".position"))
12966 result = (strEqual(value, "left") ? POS_LEFT :
12967 strEqual(value, "right") ? POS_RIGHT :
12968 strEqual(value, "top") ? POS_TOP :
12969 strEqual(value, "upper") ? POS_UPPER :
12970 strEqual(value, "middle") ? POS_MIDDLE :
12971 strEqual(value, "lower") ? POS_LOWER :
12972 strEqual(value, "bottom") ? POS_BOTTOM :
12973 strEqual(value, "any") ? POS_ANY :
12974 strEqual(value, "ce") ? POS_CE :
12975 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12976 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12978 else if (strEqual(suffix, ".align"))
12980 result = (strEqual(value, "left") ? ALIGN_LEFT :
12981 strEqual(value, "right") ? ALIGN_RIGHT :
12982 strEqual(value, "center") ? ALIGN_CENTER :
12983 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12985 else if (strEqual(suffix, ".valign"))
12987 result = (strEqual(value, "top") ? VALIGN_TOP :
12988 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12989 strEqual(value, "middle") ? VALIGN_MIDDLE :
12990 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12992 else if (strEqual(suffix, ".anim_mode"))
12994 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12995 string_has_parameter(value, "loop") ? ANIM_LOOP :
12996 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12997 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12998 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12999 string_has_parameter(value, "random") ? ANIM_RANDOM :
13000 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13001 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
13002 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
13003 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
13004 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13005 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
13006 string_has_parameter(value, "centered") ? ANIM_CENTERED :
13007 string_has_parameter(value, "all") ? ANIM_ALL :
13008 string_has_parameter(value, "tiled") ? ANIM_TILED :
13009 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
13012 if (string_has_parameter(value, "once"))
13013 result |= ANIM_ONCE;
13015 if (string_has_parameter(value, "reverse"))
13016 result |= ANIM_REVERSE;
13018 if (string_has_parameter(value, "opaque_player"))
13019 result |= ANIM_OPAQUE_PLAYER;
13021 if (string_has_parameter(value, "static_panel"))
13022 result |= ANIM_STATIC_PANEL;
13024 else if (strEqual(suffix, ".init_event") ||
13025 strEqual(suffix, ".anim_event"))
13027 result = get_anim_parameter_values(value);
13029 else if (strEqual(suffix, ".init_delay_action") ||
13030 strEqual(suffix, ".anim_delay_action") ||
13031 strEqual(suffix, ".post_delay_action") ||
13032 strEqual(suffix, ".init_event_action") ||
13033 strEqual(suffix, ".anim_event_action"))
13035 result = get_anim_action_parameter_value(value_raw);
13037 else if (strEqual(suffix, ".class"))
13039 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13040 get_hash_from_string(value));
13042 else if (strEqual(suffix, ".style"))
13044 result = STYLE_DEFAULT;
13046 if (string_has_parameter(value, "accurate_borders"))
13047 result |= STYLE_ACCURATE_BORDERS;
13049 if (string_has_parameter(value, "inner_corners"))
13050 result |= STYLE_INNER_CORNERS;
13052 if (string_has_parameter(value, "reverse"))
13053 result |= STYLE_REVERSE;
13055 if (string_has_parameter(value, "leftmost_position"))
13056 result |= STYLE_LEFTMOST_POSITION;
13058 if (string_has_parameter(value, "block_clicks"))
13059 result |= STYLE_BLOCK;
13061 if (string_has_parameter(value, "passthrough_clicks"))
13062 result |= STYLE_PASSTHROUGH;
13064 if (string_has_parameter(value, "multiple_actions"))
13065 result |= STYLE_MULTIPLE_ACTIONS;
13067 if (string_has_parameter(value, "consume_ce_event"))
13068 result |= STYLE_CONSUME_CE_EVENT;
13070 else if (strEqual(suffix, ".fade_mode"))
13072 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
13073 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
13074 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
13075 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
13076 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
13077 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
13078 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
13079 FADE_MODE_DEFAULT);
13081 else if (strEqual(suffix, ".auto_delay_unit"))
13083 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13084 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13085 AUTO_DELAY_UNIT_DEFAULT);
13087 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13089 result = gfx.get_font_from_token_function(value);
13091 else // generic parameter of type integer or boolean
13093 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13094 type == TYPE_INTEGER ? get_integer_from_string(value) :
13095 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13096 ARG_UNDEFINED_VALUE);
13104 static int get_token_parameter_value(char *token, char *value_raw)
13108 if (token == NULL || value_raw == NULL)
13109 return ARG_UNDEFINED_VALUE;
13111 suffix = strrchr(token, '.');
13112 if (suffix == NULL)
13115 if (strEqual(suffix, ".element"))
13116 return getElementFromToken(value_raw);
13118 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13119 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13122 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13123 boolean ignore_defaults)
13127 for (i = 0; image_config_vars[i].token != NULL; i++)
13129 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13131 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13132 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13136 *image_config_vars[i].value =
13137 get_token_parameter_value(image_config_vars[i].token, value);
13141 void InitMenuDesignSettings_Static(void)
13143 // always start with reliable default values from static default config
13144 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13147 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13151 // the following initializes hierarchical values from static configuration
13153 // special case: initialize "ARG_DEFAULT" values in static default config
13154 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13155 titlescreen_initial_first_default.fade_mode =
13156 title_initial_first_default.fade_mode;
13157 titlescreen_initial_first_default.fade_delay =
13158 title_initial_first_default.fade_delay;
13159 titlescreen_initial_first_default.post_delay =
13160 title_initial_first_default.post_delay;
13161 titlescreen_initial_first_default.auto_delay =
13162 title_initial_first_default.auto_delay;
13163 titlescreen_initial_first_default.auto_delay_unit =
13164 title_initial_first_default.auto_delay_unit;
13165 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13166 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13167 titlescreen_first_default.post_delay = title_first_default.post_delay;
13168 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13169 titlescreen_first_default.auto_delay_unit =
13170 title_first_default.auto_delay_unit;
13171 titlemessage_initial_first_default.fade_mode =
13172 title_initial_first_default.fade_mode;
13173 titlemessage_initial_first_default.fade_delay =
13174 title_initial_first_default.fade_delay;
13175 titlemessage_initial_first_default.post_delay =
13176 title_initial_first_default.post_delay;
13177 titlemessage_initial_first_default.auto_delay =
13178 title_initial_first_default.auto_delay;
13179 titlemessage_initial_first_default.auto_delay_unit =
13180 title_initial_first_default.auto_delay_unit;
13181 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13182 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13183 titlemessage_first_default.post_delay = title_first_default.post_delay;
13184 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13185 titlemessage_first_default.auto_delay_unit =
13186 title_first_default.auto_delay_unit;
13188 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13189 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13190 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13191 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13192 titlescreen_initial_default.auto_delay_unit =
13193 title_initial_default.auto_delay_unit;
13194 titlescreen_default.fade_mode = title_default.fade_mode;
13195 titlescreen_default.fade_delay = title_default.fade_delay;
13196 titlescreen_default.post_delay = title_default.post_delay;
13197 titlescreen_default.auto_delay = title_default.auto_delay;
13198 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13199 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13200 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13201 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13202 titlemessage_initial_default.auto_delay_unit =
13203 title_initial_default.auto_delay_unit;
13204 titlemessage_default.fade_mode = title_default.fade_mode;
13205 titlemessage_default.fade_delay = title_default.fade_delay;
13206 titlemessage_default.post_delay = title_default.post_delay;
13207 titlemessage_default.auto_delay = title_default.auto_delay;
13208 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13210 // special case: initialize "ARG_DEFAULT" values in static default config
13211 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13212 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13214 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13215 titlescreen_first[i] = titlescreen_first_default;
13216 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13217 titlemessage_first[i] = titlemessage_first_default;
13219 titlescreen_initial[i] = titlescreen_initial_default;
13220 titlescreen[i] = titlescreen_default;
13221 titlemessage_initial[i] = titlemessage_initial_default;
13222 titlemessage[i] = titlemessage_default;
13225 // special case: initialize "ARG_DEFAULT" values in static default config
13226 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13227 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13229 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13232 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13233 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13234 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13237 // special case: initialize "ARG_DEFAULT" values in static default config
13238 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13239 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13241 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13242 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13243 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13245 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13248 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13252 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13256 struct XY *dst, *src;
13258 game_buttons_xy[] =
13260 { &game.button.save, &game.button.stop },
13261 { &game.button.pause2, &game.button.pause },
13262 { &game.button.load, &game.button.play },
13263 { &game.button.undo, &game.button.stop },
13264 { &game.button.redo, &game.button.play },
13270 // special case: initialize later added SETUP list size from LEVELS value
13271 if (menu.list_size[GAME_MODE_SETUP] == -1)
13272 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13274 // set default position for snapshot buttons to stop/pause/play buttons
13275 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13276 if ((*game_buttons_xy[i].dst).x == -1 &&
13277 (*game_buttons_xy[i].dst).y == -1)
13278 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13280 // --------------------------------------------------------------------------
13281 // dynamic viewports (including playfield margins, borders and alignments)
13282 // --------------------------------------------------------------------------
13284 // dynamic viewports currently only supported for landscape mode
13285 int display_width = MAX(video.display_width, video.display_height);
13286 int display_height = MIN(video.display_width, video.display_height);
13288 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13290 struct RectWithBorder *vp_window = &viewport.window[i];
13291 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13292 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13293 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13294 boolean dynamic_window_width = (vp_window->min_width != -1);
13295 boolean dynamic_window_height = (vp_window->min_height != -1);
13296 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13297 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13299 // adjust window size if min/max width/height is specified
13301 if (vp_window->min_width != -1)
13303 int window_width = display_width;
13305 // when using static window height, use aspect ratio of display
13306 if (vp_window->min_height == -1)
13307 window_width = vp_window->height * display_width / display_height;
13309 vp_window->width = MAX(vp_window->min_width, window_width);
13312 if (vp_window->min_height != -1)
13314 int window_height = display_height;
13316 // when using static window width, use aspect ratio of display
13317 if (vp_window->min_width == -1)
13318 window_height = vp_window->width * display_height / display_width;
13320 vp_window->height = MAX(vp_window->min_height, window_height);
13323 if (vp_window->max_width != -1)
13324 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13326 if (vp_window->max_height != -1)
13327 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13329 int playfield_width = vp_window->width;
13330 int playfield_height = vp_window->height;
13332 // adjust playfield size and position according to specified margins
13334 playfield_width -= vp_playfield->margin_left;
13335 playfield_width -= vp_playfield->margin_right;
13337 playfield_height -= vp_playfield->margin_top;
13338 playfield_height -= vp_playfield->margin_bottom;
13340 // adjust playfield size if min/max width/height is specified
13342 if (vp_playfield->min_width != -1)
13343 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13345 if (vp_playfield->min_height != -1)
13346 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13348 if (vp_playfield->max_width != -1)
13349 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13351 if (vp_playfield->max_height != -1)
13352 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13354 // adjust playfield position according to specified alignment
13356 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13357 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13358 else if (vp_playfield->align == ALIGN_CENTER)
13359 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13360 else if (vp_playfield->align == ALIGN_RIGHT)
13361 vp_playfield->x += playfield_width - vp_playfield->width;
13363 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13364 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13365 else if (vp_playfield->valign == VALIGN_MIDDLE)
13366 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13367 else if (vp_playfield->valign == VALIGN_BOTTOM)
13368 vp_playfield->y += playfield_height - vp_playfield->height;
13370 vp_playfield->x += vp_playfield->margin_left;
13371 vp_playfield->y += vp_playfield->margin_top;
13373 // adjust individual playfield borders if only default border is specified
13375 if (vp_playfield->border_left == -1)
13376 vp_playfield->border_left = vp_playfield->border_size;
13377 if (vp_playfield->border_right == -1)
13378 vp_playfield->border_right = vp_playfield->border_size;
13379 if (vp_playfield->border_top == -1)
13380 vp_playfield->border_top = vp_playfield->border_size;
13381 if (vp_playfield->border_bottom == -1)
13382 vp_playfield->border_bottom = vp_playfield->border_size;
13384 // set dynamic playfield borders if borders are specified as undefined
13385 // (but only if window size was dynamic and playfield size was static)
13387 if (dynamic_window_width && !dynamic_playfield_width)
13389 if (vp_playfield->border_left == -1)
13391 vp_playfield->border_left = (vp_playfield->x -
13392 vp_playfield->margin_left);
13393 vp_playfield->x -= vp_playfield->border_left;
13394 vp_playfield->width += vp_playfield->border_left;
13397 if (vp_playfield->border_right == -1)
13399 vp_playfield->border_right = (vp_window->width -
13401 vp_playfield->width -
13402 vp_playfield->margin_right);
13403 vp_playfield->width += vp_playfield->border_right;
13407 if (dynamic_window_height && !dynamic_playfield_height)
13409 if (vp_playfield->border_top == -1)
13411 vp_playfield->border_top = (vp_playfield->y -
13412 vp_playfield->margin_top);
13413 vp_playfield->y -= vp_playfield->border_top;
13414 vp_playfield->height += vp_playfield->border_top;
13417 if (vp_playfield->border_bottom == -1)
13419 vp_playfield->border_bottom = (vp_window->height -
13421 vp_playfield->height -
13422 vp_playfield->margin_bottom);
13423 vp_playfield->height += vp_playfield->border_bottom;
13427 // adjust playfield size to be a multiple of a defined alignment tile size
13429 int align_size = vp_playfield->align_size;
13430 int playfield_xtiles = vp_playfield->width / align_size;
13431 int playfield_ytiles = vp_playfield->height / align_size;
13432 int playfield_width_corrected = playfield_xtiles * align_size;
13433 int playfield_height_corrected = playfield_ytiles * align_size;
13434 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13435 i == GFX_SPECIAL_ARG_EDITOR);
13437 if (is_playfield_mode &&
13438 dynamic_playfield_width &&
13439 vp_playfield->width != playfield_width_corrected)
13441 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13443 vp_playfield->width = playfield_width_corrected;
13445 if (vp_playfield->align == ALIGN_LEFT)
13447 vp_playfield->border_left += playfield_xdiff;
13449 else if (vp_playfield->align == ALIGN_RIGHT)
13451 vp_playfield->border_right += playfield_xdiff;
13453 else if (vp_playfield->align == ALIGN_CENTER)
13455 int border_left_diff = playfield_xdiff / 2;
13456 int border_right_diff = playfield_xdiff - border_left_diff;
13458 vp_playfield->border_left += border_left_diff;
13459 vp_playfield->border_right += border_right_diff;
13463 if (is_playfield_mode &&
13464 dynamic_playfield_height &&
13465 vp_playfield->height != playfield_height_corrected)
13467 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13469 vp_playfield->height = playfield_height_corrected;
13471 if (vp_playfield->valign == VALIGN_TOP)
13473 vp_playfield->border_top += playfield_ydiff;
13475 else if (vp_playfield->align == VALIGN_BOTTOM)
13477 vp_playfield->border_right += playfield_ydiff;
13479 else if (vp_playfield->align == VALIGN_MIDDLE)
13481 int border_top_diff = playfield_ydiff / 2;
13482 int border_bottom_diff = playfield_ydiff - border_top_diff;
13484 vp_playfield->border_top += border_top_diff;
13485 vp_playfield->border_bottom += border_bottom_diff;
13489 // adjust door positions according to specified alignment
13491 for (j = 0; j < 2; j++)
13493 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13495 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13496 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13497 else if (vp_door->align == ALIGN_CENTER)
13498 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13499 else if (vp_door->align == ALIGN_RIGHT)
13500 vp_door->x += vp_window->width - vp_door->width;
13502 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13503 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13504 else if (vp_door->valign == VALIGN_MIDDLE)
13505 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13506 else if (vp_door->valign == VALIGN_BOTTOM)
13507 vp_door->y += vp_window->height - vp_door->height;
13512 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13516 struct XYTileSize *dst, *src;
13519 editor_buttons_xy[] =
13522 &editor.button.element_left, &editor.palette.element_left,
13523 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13526 &editor.button.element_middle, &editor.palette.element_middle,
13527 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13530 &editor.button.element_right, &editor.palette.element_right,
13531 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13538 // set default position for element buttons to element graphics
13539 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13541 if ((*editor_buttons_xy[i].dst).x == -1 &&
13542 (*editor_buttons_xy[i].dst).y == -1)
13544 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13546 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13548 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13552 // adjust editor palette rows and columns if specified to be dynamic
13554 if (editor.palette.cols == -1)
13556 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13557 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13558 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13560 editor.palette.cols = (vp_width - sc_width) / bt_width;
13562 if (editor.palette.x == -1)
13564 int palette_width = editor.palette.cols * bt_width + sc_width;
13566 editor.palette.x = (vp_width - palette_width) / 2;
13570 if (editor.palette.rows == -1)
13572 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13573 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13574 int tx_height = getFontHeight(FONT_TEXT_2);
13576 editor.palette.rows = (vp_height - tx_height) / bt_height;
13578 if (editor.palette.y == -1)
13580 int palette_height = editor.palette.rows * bt_height + tx_height;
13582 editor.palette.y = (vp_height - palette_height) / 2;
13587 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13588 boolean initialize)
13590 // special case: check if network and preview player positions are redefined,
13591 // to compare this later against the main menu level preview being redefined
13592 struct TokenIntPtrInfo menu_config_players[] =
13594 { "main.network_players.x", &menu.main.network_players.redefined },
13595 { "main.network_players.y", &menu.main.network_players.redefined },
13596 { "main.preview_players.x", &menu.main.preview_players.redefined },
13597 { "main.preview_players.y", &menu.main.preview_players.redefined },
13598 { "preview.x", &preview.redefined },
13599 { "preview.y", &preview.redefined }
13605 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13606 *menu_config_players[i].value = FALSE;
13610 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13611 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13612 *menu_config_players[i].value = TRUE;
13616 static void InitMenuDesignSettings_PreviewPlayers(void)
13618 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13621 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13623 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13626 static void LoadMenuDesignSettingsFromFilename(char *filename)
13628 static struct TitleFadingInfo tfi;
13629 static struct TitleMessageInfo tmi;
13630 static struct TokenInfo title_tokens[] =
13632 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13633 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13634 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13635 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13636 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13640 static struct TokenInfo titlemessage_tokens[] =
13642 { TYPE_INTEGER, &tmi.x, ".x" },
13643 { TYPE_INTEGER, &tmi.y, ".y" },
13644 { TYPE_INTEGER, &tmi.width, ".width" },
13645 { TYPE_INTEGER, &tmi.height, ".height" },
13646 { TYPE_INTEGER, &tmi.chars, ".chars" },
13647 { TYPE_INTEGER, &tmi.lines, ".lines" },
13648 { TYPE_INTEGER, &tmi.align, ".align" },
13649 { TYPE_INTEGER, &tmi.valign, ".valign" },
13650 { TYPE_INTEGER, &tmi.font, ".font" },
13651 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13652 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13653 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13654 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13655 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13656 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13657 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13658 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13659 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13665 struct TitleFadingInfo *info;
13670 // initialize first titles from "enter screen" definitions, if defined
13671 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13672 { &title_first_default, "menu.enter_screen.TITLE" },
13674 // initialize title screens from "next screen" definitions, if defined
13675 { &title_initial_default, "menu.next_screen.TITLE" },
13676 { &title_default, "menu.next_screen.TITLE" },
13682 struct TitleMessageInfo *array;
13685 titlemessage_arrays[] =
13687 // initialize first titles from "enter screen" definitions, if defined
13688 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13689 { titlescreen_first, "menu.enter_screen.TITLE" },
13690 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13691 { titlemessage_first, "menu.enter_screen.TITLE" },
13693 // initialize titles from "next screen" definitions, if defined
13694 { titlescreen_initial, "menu.next_screen.TITLE" },
13695 { titlescreen, "menu.next_screen.TITLE" },
13696 { titlemessage_initial, "menu.next_screen.TITLE" },
13697 { titlemessage, "menu.next_screen.TITLE" },
13699 // overwrite titles with title definitions, if defined
13700 { titlescreen_initial_first, "[title_initial]" },
13701 { titlescreen_first, "[title]" },
13702 { titlemessage_initial_first, "[title_initial]" },
13703 { titlemessage_first, "[title]" },
13705 { titlescreen_initial, "[title_initial]" },
13706 { titlescreen, "[title]" },
13707 { titlemessage_initial, "[title_initial]" },
13708 { titlemessage, "[title]" },
13710 // overwrite titles with title screen/message definitions, if defined
13711 { titlescreen_initial_first, "[titlescreen_initial]" },
13712 { titlescreen_first, "[titlescreen]" },
13713 { titlemessage_initial_first, "[titlemessage_initial]" },
13714 { titlemessage_first, "[titlemessage]" },
13716 { titlescreen_initial, "[titlescreen_initial]" },
13717 { titlescreen, "[titlescreen]" },
13718 { titlemessage_initial, "[titlemessage_initial]" },
13719 { titlemessage, "[titlemessage]" },
13723 SetupFileHash *setup_file_hash;
13726 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13729 // the following initializes hierarchical values from dynamic configuration
13731 // special case: initialize with default values that may be overwritten
13732 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13733 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13735 struct TokenIntPtrInfo menu_config[] =
13737 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13738 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13739 { "menu.list_size", &menu.list_size[i] }
13742 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13744 char *token = menu_config[j].token;
13745 char *value = getHashEntry(setup_file_hash, token);
13748 *menu_config[j].value = get_integer_from_string(value);
13752 // special case: initialize with default values that may be overwritten
13753 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13754 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13756 struct TokenIntPtrInfo menu_config[] =
13758 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13759 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13760 { "menu.list_size.INFO", &menu.list_size_info[i] },
13761 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13762 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13765 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13767 char *token = menu_config[j].token;
13768 char *value = getHashEntry(setup_file_hash, token);
13771 *menu_config[j].value = get_integer_from_string(value);
13775 // special case: initialize with default values that may be overwritten
13776 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13777 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13779 struct TokenIntPtrInfo menu_config[] =
13781 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13782 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13785 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13787 char *token = menu_config[j].token;
13788 char *value = getHashEntry(setup_file_hash, token);
13791 *menu_config[j].value = get_integer_from_string(value);
13795 // special case: initialize with default values that may be overwritten
13796 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13797 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13799 struct TokenIntPtrInfo menu_config[] =
13801 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13802 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13803 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13804 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13805 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13806 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13807 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13808 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13809 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13810 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13813 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13815 char *token = menu_config[j].token;
13816 char *value = getHashEntry(setup_file_hash, token);
13819 *menu_config[j].value = get_integer_from_string(value);
13823 // special case: initialize with default values that may be overwritten
13824 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13825 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13827 struct TokenIntPtrInfo menu_config[] =
13829 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13830 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13831 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13832 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13833 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13834 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13835 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13836 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13837 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13840 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13842 char *token = menu_config[j].token;
13843 char *value = getHashEntry(setup_file_hash, token);
13846 *menu_config[j].value = get_token_parameter_value(token, value);
13850 // special case: initialize with default values that may be overwritten
13851 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13852 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13856 char *token_prefix;
13857 struct RectWithBorder *struct_ptr;
13861 { "viewport.window", &viewport.window[i] },
13862 { "viewport.playfield", &viewport.playfield[i] },
13863 { "viewport.door_1", &viewport.door_1[i] },
13864 { "viewport.door_2", &viewport.door_2[i] }
13867 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13869 struct TokenIntPtrInfo vp_config[] =
13871 { ".x", &vp_struct[j].struct_ptr->x },
13872 { ".y", &vp_struct[j].struct_ptr->y },
13873 { ".width", &vp_struct[j].struct_ptr->width },
13874 { ".height", &vp_struct[j].struct_ptr->height },
13875 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13876 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13877 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13878 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13879 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13880 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13881 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13882 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13883 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13884 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13885 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13886 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13887 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13888 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13889 { ".align", &vp_struct[j].struct_ptr->align },
13890 { ".valign", &vp_struct[j].struct_ptr->valign }
13893 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13895 char *token = getStringCat2(vp_struct[j].token_prefix,
13896 vp_config[k].token);
13897 char *value = getHashEntry(setup_file_hash, token);
13900 *vp_config[k].value = get_token_parameter_value(token, value);
13907 // special case: initialize with default values that may be overwritten
13908 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13909 for (i = 0; title_info[i].info != NULL; i++)
13911 struct TitleFadingInfo *info = title_info[i].info;
13912 char *base_token = title_info[i].text;
13914 for (j = 0; title_tokens[j].type != -1; j++)
13916 char *token = getStringCat2(base_token, title_tokens[j].text);
13917 char *value = getHashEntry(setup_file_hash, token);
13921 int parameter_value = get_token_parameter_value(token, value);
13925 *(int *)title_tokens[j].value = (int)parameter_value;
13934 // special case: initialize with default values that may be overwritten
13935 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13936 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13938 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13939 char *base_token = titlemessage_arrays[i].text;
13941 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13943 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13944 char *value = getHashEntry(setup_file_hash, token);
13948 int parameter_value = get_token_parameter_value(token, value);
13950 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13954 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13955 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13957 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13967 // read (and overwrite with) values that may be specified in config file
13968 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13970 // special case: check if network and preview player positions are redefined
13971 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13973 freeSetupFileHash(setup_file_hash);
13976 void LoadMenuDesignSettings(void)
13978 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13980 InitMenuDesignSettings_Static();
13981 InitMenuDesignSettings_SpecialPreProcessing();
13982 InitMenuDesignSettings_PreviewPlayers();
13984 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13986 // first look for special settings configured in level series config
13987 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13989 if (fileExists(filename_base))
13990 LoadMenuDesignSettingsFromFilename(filename_base);
13993 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13995 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13996 LoadMenuDesignSettingsFromFilename(filename_local);
13998 InitMenuDesignSettings_SpecialPostProcessing();
14001 void LoadMenuDesignSettings_AfterGraphics(void)
14003 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14006 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14007 boolean ignore_defaults)
14011 for (i = 0; sound_config_vars[i].token != NULL; i++)
14013 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14015 // (ignore definitions set to "[DEFAULT]" which are already initialized)
14016 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14020 *sound_config_vars[i].value =
14021 get_token_parameter_value(sound_config_vars[i].token, value);
14025 void InitSoundSettings_Static(void)
14027 // always start with reliable default values from static default config
14028 InitSoundSettings_FromHash(sound_config_hash, FALSE);
14031 static void LoadSoundSettingsFromFilename(char *filename)
14033 SetupFileHash *setup_file_hash;
14035 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14038 // read (and overwrite with) values that may be specified in config file
14039 InitSoundSettings_FromHash(setup_file_hash, TRUE);
14041 freeSetupFileHash(setup_file_hash);
14044 void LoadSoundSettings(void)
14046 char *filename_base = UNDEFINED_FILENAME, *filename_local;
14048 InitSoundSettings_Static();
14050 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14052 // first look for special settings configured in level series config
14053 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14055 if (fileExists(filename_base))
14056 LoadSoundSettingsFromFilename(filename_base);
14059 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14061 if (filename_local != NULL && !strEqual(filename_base, filename_local))
14062 LoadSoundSettingsFromFilename(filename_local);
14065 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14067 char *filename = getEditorSetupFilename();
14068 SetupFileList *setup_file_list, *list;
14069 SetupFileHash *element_hash;
14070 int num_unknown_tokens = 0;
14073 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14076 element_hash = newSetupFileHash();
14078 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14079 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14081 // determined size may be larger than needed (due to unknown elements)
14083 for (list = setup_file_list; list != NULL; list = list->next)
14086 // add space for up to 3 more elements for padding that may be needed
14087 *num_elements += 3;
14089 // free memory for old list of elements, if needed
14090 checked_free(*elements);
14092 // allocate memory for new list of elements
14093 *elements = checked_malloc(*num_elements * sizeof(int));
14096 for (list = setup_file_list; list != NULL; list = list->next)
14098 char *value = getHashEntry(element_hash, list->token);
14100 if (value == NULL) // try to find obsolete token mapping
14102 char *mapped_token = get_mapped_token(list->token);
14104 if (mapped_token != NULL)
14106 value = getHashEntry(element_hash, mapped_token);
14108 free(mapped_token);
14114 (*elements)[(*num_elements)++] = atoi(value);
14118 if (num_unknown_tokens == 0)
14121 Warn("unknown token(s) found in config file:");
14122 Warn("- config file: '%s'", filename);
14124 num_unknown_tokens++;
14127 Warn("- token: '%s'", list->token);
14131 if (num_unknown_tokens > 0)
14134 while (*num_elements % 4) // pad with empty elements, if needed
14135 (*elements)[(*num_elements)++] = EL_EMPTY;
14137 freeSetupFileList(setup_file_list);
14138 freeSetupFileHash(element_hash);
14141 for (i = 0; i < *num_elements; i++)
14142 Debug("editor", "element '%s' [%d]\n",
14143 element_info[(*elements)[i]].token_name, (*elements)[i]);
14147 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14150 SetupFileHash *setup_file_hash = NULL;
14151 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14152 char *filename_music, *filename_prefix, *filename_info;
14158 token_to_value_ptr[] =
14160 { "title_header", &tmp_music_file_info.title_header },
14161 { "artist_header", &tmp_music_file_info.artist_header },
14162 { "album_header", &tmp_music_file_info.album_header },
14163 { "year_header", &tmp_music_file_info.year_header },
14164 { "played_header", &tmp_music_file_info.played_header },
14166 { "title", &tmp_music_file_info.title },
14167 { "artist", &tmp_music_file_info.artist },
14168 { "album", &tmp_music_file_info.album },
14169 { "year", &tmp_music_file_info.year },
14170 { "played", &tmp_music_file_info.played },
14176 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14177 getCustomMusicFilename(basename));
14179 if (filename_music == NULL)
14182 // ---------- try to replace file extension ----------
14184 filename_prefix = getStringCopy(filename_music);
14185 if (strrchr(filename_prefix, '.') != NULL)
14186 *strrchr(filename_prefix, '.') = '\0';
14187 filename_info = getStringCat2(filename_prefix, ".txt");
14189 if (fileExists(filename_info))
14190 setup_file_hash = loadSetupFileHash(filename_info);
14192 free(filename_prefix);
14193 free(filename_info);
14195 if (setup_file_hash == NULL)
14197 // ---------- try to add file extension ----------
14199 filename_prefix = getStringCopy(filename_music);
14200 filename_info = getStringCat2(filename_prefix, ".txt");
14202 if (fileExists(filename_info))
14203 setup_file_hash = loadSetupFileHash(filename_info);
14205 free(filename_prefix);
14206 free(filename_info);
14209 if (setup_file_hash == NULL)
14212 // ---------- music file info found ----------
14214 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14216 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14218 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14220 *token_to_value_ptr[i].value_ptr =
14221 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14224 tmp_music_file_info.basename = getStringCopy(basename);
14225 tmp_music_file_info.music = music;
14226 tmp_music_file_info.is_sound = is_sound;
14228 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14229 *new_music_file_info = tmp_music_file_info;
14231 return new_music_file_info;
14234 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14236 return get_music_file_info_ext(basename, music, FALSE);
14239 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14241 return get_music_file_info_ext(basename, sound, TRUE);
14244 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14245 char *basename, boolean is_sound)
14247 for (; list != NULL; list = list->next)
14248 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14254 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14256 return music_info_listed_ext(list, basename, FALSE);
14259 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14261 return music_info_listed_ext(list, basename, TRUE);
14264 void LoadMusicInfo(void)
14266 int num_music_noconf = getMusicListSize_NoConf();
14267 int num_music = getMusicListSize();
14268 int num_sounds = getSoundListSize();
14269 struct FileInfo *music, *sound;
14270 struct MusicFileInfo *next, **new;
14274 while (music_file_info != NULL)
14276 next = music_file_info->next;
14278 checked_free(music_file_info->basename);
14280 checked_free(music_file_info->title_header);
14281 checked_free(music_file_info->artist_header);
14282 checked_free(music_file_info->album_header);
14283 checked_free(music_file_info->year_header);
14284 checked_free(music_file_info->played_header);
14286 checked_free(music_file_info->title);
14287 checked_free(music_file_info->artist);
14288 checked_free(music_file_info->album);
14289 checked_free(music_file_info->year);
14290 checked_free(music_file_info->played);
14292 free(music_file_info);
14294 music_file_info = next;
14297 new = &music_file_info;
14299 // get (configured or unconfigured) music file info for all levels
14300 for (i = leveldir_current->first_level;
14301 i <= leveldir_current->last_level; i++)
14305 if (levelset.music[i] != MUS_UNDEFINED)
14307 // get music file info for configured level music
14308 music_nr = levelset.music[i];
14310 else if (num_music_noconf > 0)
14312 // get music file info for unconfigured level music
14313 int level_pos = i - leveldir_current->first_level;
14315 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14322 char *basename = getMusicInfoEntryFilename(music_nr);
14324 if (basename == NULL)
14327 if (!music_info_listed(music_file_info, basename))
14329 *new = get_music_file_info(basename, music_nr);
14332 new = &(*new)->next;
14336 // get music file info for all remaining configured music files
14337 for (i = 0; i < num_music; i++)
14339 music = getMusicListEntry(i);
14341 if (music->filename == NULL)
14344 if (strEqual(music->filename, UNDEFINED_FILENAME))
14347 // a configured file may be not recognized as music
14348 if (!FileIsMusic(music->filename))
14351 if (!music_info_listed(music_file_info, music->filename))
14353 *new = get_music_file_info(music->filename, i);
14356 new = &(*new)->next;
14360 // get sound file info for all configured sound files
14361 for (i = 0; i < num_sounds; i++)
14363 sound = getSoundListEntry(i);
14365 if (sound->filename == NULL)
14368 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14371 // a configured file may be not recognized as sound
14372 if (!FileIsSound(sound->filename))
14375 if (!sound_info_listed(music_file_info, sound->filename))
14377 *new = get_sound_file_info(sound->filename, i);
14379 new = &(*new)->next;
14383 // add pointers to previous list nodes
14385 struct MusicFileInfo *node = music_file_info;
14387 while (node != NULL)
14390 node->next->prev = node;
14396 static void add_helpanim_entry(int element, int action, int direction,
14397 int delay, int *num_list_entries)
14399 struct HelpAnimInfo *new_list_entry;
14400 (*num_list_entries)++;
14403 checked_realloc(helpanim_info,
14404 *num_list_entries * sizeof(struct HelpAnimInfo));
14405 new_list_entry = &helpanim_info[*num_list_entries - 1];
14407 new_list_entry->element = element;
14408 new_list_entry->action = action;
14409 new_list_entry->direction = direction;
14410 new_list_entry->delay = delay;
14413 static void print_unknown_token(char *filename, char *token, int token_nr)
14418 Warn("unknown token(s) found in config file:");
14419 Warn("- config file: '%s'", filename);
14422 Warn("- token: '%s'", token);
14425 static void print_unknown_token_end(int token_nr)
14431 void LoadHelpAnimInfo(void)
14433 char *filename = getHelpAnimFilename();
14434 SetupFileList *setup_file_list = NULL, *list;
14435 SetupFileHash *element_hash, *action_hash, *direction_hash;
14436 int num_list_entries = 0;
14437 int num_unknown_tokens = 0;
14440 if (fileExists(filename))
14441 setup_file_list = loadSetupFileList(filename);
14443 if (setup_file_list == NULL)
14445 // use reliable default values from static configuration
14446 SetupFileList *insert_ptr;
14448 insert_ptr = setup_file_list =
14449 newSetupFileList(helpanim_config[0].token,
14450 helpanim_config[0].value);
14452 for (i = 1; helpanim_config[i].token; i++)
14453 insert_ptr = addListEntry(insert_ptr,
14454 helpanim_config[i].token,
14455 helpanim_config[i].value);
14458 element_hash = newSetupFileHash();
14459 action_hash = newSetupFileHash();
14460 direction_hash = newSetupFileHash();
14462 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14463 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14465 for (i = 0; i < NUM_ACTIONS; i++)
14466 setHashEntry(action_hash, element_action_info[i].suffix,
14467 i_to_a(element_action_info[i].value));
14469 // do not store direction index (bit) here, but direction value!
14470 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14471 setHashEntry(direction_hash, element_direction_info[i].suffix,
14472 i_to_a(1 << element_direction_info[i].value));
14474 for (list = setup_file_list; list != NULL; list = list->next)
14476 char *element_token, *action_token, *direction_token;
14477 char *element_value, *action_value, *direction_value;
14478 int delay = atoi(list->value);
14480 if (strEqual(list->token, "end"))
14482 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14487 /* first try to break element into element/action/direction parts;
14488 if this does not work, also accept combined "element[.act][.dir]"
14489 elements (like "dynamite.active"), which are unique elements */
14491 if (strchr(list->token, '.') == NULL) // token contains no '.'
14493 element_value = getHashEntry(element_hash, list->token);
14494 if (element_value != NULL) // element found
14495 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14496 &num_list_entries);
14499 // no further suffixes found -- this is not an element
14500 print_unknown_token(filename, list->token, num_unknown_tokens++);
14506 // token has format "<prefix>.<something>"
14508 action_token = strchr(list->token, '.'); // suffix may be action ...
14509 direction_token = action_token; // ... or direction
14511 element_token = getStringCopy(list->token);
14512 *strchr(element_token, '.') = '\0';
14514 element_value = getHashEntry(element_hash, element_token);
14516 if (element_value == NULL) // this is no element
14518 element_value = getHashEntry(element_hash, list->token);
14519 if (element_value != NULL) // combined element found
14520 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14521 &num_list_entries);
14523 print_unknown_token(filename, list->token, num_unknown_tokens++);
14525 free(element_token);
14530 action_value = getHashEntry(action_hash, action_token);
14532 if (action_value != NULL) // action found
14534 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14535 &num_list_entries);
14537 free(element_token);
14542 direction_value = getHashEntry(direction_hash, direction_token);
14544 if (direction_value != NULL) // direction found
14546 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14547 &num_list_entries);
14549 free(element_token);
14554 if (strchr(action_token + 1, '.') == NULL)
14556 // no further suffixes found -- this is not an action nor direction
14558 element_value = getHashEntry(element_hash, list->token);
14559 if (element_value != NULL) // combined element found
14560 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14561 &num_list_entries);
14563 print_unknown_token(filename, list->token, num_unknown_tokens++);
14565 free(element_token);
14570 // token has format "<prefix>.<suffix>.<something>"
14572 direction_token = strchr(action_token + 1, '.');
14574 action_token = getStringCopy(action_token);
14575 *strchr(action_token + 1, '.') = '\0';
14577 action_value = getHashEntry(action_hash, action_token);
14579 if (action_value == NULL) // this is no action
14581 element_value = getHashEntry(element_hash, list->token);
14582 if (element_value != NULL) // combined element found
14583 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14584 &num_list_entries);
14586 print_unknown_token(filename, list->token, num_unknown_tokens++);
14588 free(element_token);
14589 free(action_token);
14594 direction_value = getHashEntry(direction_hash, direction_token);
14596 if (direction_value != NULL) // direction found
14598 add_helpanim_entry(atoi(element_value), atoi(action_value),
14599 atoi(direction_value), delay, &num_list_entries);
14601 free(element_token);
14602 free(action_token);
14607 // this is no direction
14609 element_value = getHashEntry(element_hash, list->token);
14610 if (element_value != NULL) // combined element found
14611 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14612 &num_list_entries);
14614 print_unknown_token(filename, list->token, num_unknown_tokens++);
14616 free(element_token);
14617 free(action_token);
14620 print_unknown_token_end(num_unknown_tokens);
14622 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14623 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14625 freeSetupFileList(setup_file_list);
14626 freeSetupFileHash(element_hash);
14627 freeSetupFileHash(action_hash);
14628 freeSetupFileHash(direction_hash);
14631 for (i = 0; i < num_list_entries; i++)
14632 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14633 EL_NAME(helpanim_info[i].element),
14634 helpanim_info[i].element,
14635 helpanim_info[i].action,
14636 helpanim_info[i].direction,
14637 helpanim_info[i].delay);
14641 void LoadHelpTextInfo(void)
14643 char *filename = getHelpTextFilename();
14646 if (helptext_info != NULL)
14648 freeSetupFileHash(helptext_info);
14649 helptext_info = NULL;
14652 if (fileExists(filename))
14653 helptext_info = loadSetupFileHash(filename);
14655 if (helptext_info == NULL)
14657 // use reliable default values from static configuration
14658 helptext_info = newSetupFileHash();
14660 for (i = 0; helptext_config[i].token; i++)
14661 setHashEntry(helptext_info,
14662 helptext_config[i].token,
14663 helptext_config[i].value);
14667 BEGIN_HASH_ITERATION(helptext_info, itr)
14669 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14670 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14672 END_HASH_ITERATION(hash, itr)
14677 // ----------------------------------------------------------------------------
14679 // ----------------------------------------------------------------------------
14681 #define MAX_NUM_CONVERT_LEVELS 1000
14683 void ConvertLevels(void)
14685 static LevelDirTree *convert_leveldir = NULL;
14686 static int convert_level_nr = -1;
14687 static int num_levels_handled = 0;
14688 static int num_levels_converted = 0;
14689 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14692 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14693 global.convert_leveldir);
14695 if (convert_leveldir == NULL)
14696 Fail("no such level identifier: '%s'", global.convert_leveldir);
14698 leveldir_current = convert_leveldir;
14700 if (global.convert_level_nr != -1)
14702 convert_leveldir->first_level = global.convert_level_nr;
14703 convert_leveldir->last_level = global.convert_level_nr;
14706 convert_level_nr = convert_leveldir->first_level;
14708 PrintLine("=", 79);
14709 Print("Converting levels\n");
14710 PrintLine("-", 79);
14711 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14712 Print("Level series name: '%s'\n", convert_leveldir->name);
14713 Print("Level series author: '%s'\n", convert_leveldir->author);
14714 Print("Number of levels: %d\n", convert_leveldir->levels);
14715 PrintLine("=", 79);
14718 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14719 levels_failed[i] = FALSE;
14721 while (convert_level_nr <= convert_leveldir->last_level)
14723 char *level_filename;
14726 level_nr = convert_level_nr++;
14728 Print("Level %03d: ", level_nr);
14730 LoadLevel(level_nr);
14731 if (level.no_level_file || level.no_valid_file)
14733 Print("(no level)\n");
14737 Print("converting level ... ");
14740 // special case: conversion of some EMC levels as requested by ACME
14741 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14744 level_filename = getDefaultLevelFilename(level_nr);
14745 new_level = !fileExists(level_filename);
14749 SaveLevel(level_nr);
14751 num_levels_converted++;
14753 Print("converted.\n");
14757 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14758 levels_failed[level_nr] = TRUE;
14760 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14763 num_levels_handled++;
14767 PrintLine("=", 79);
14768 Print("Number of levels handled: %d\n", num_levels_handled);
14769 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14770 (num_levels_handled ?
14771 num_levels_converted * 100 / num_levels_handled : 0));
14772 PrintLine("-", 79);
14773 Print("Summary (for automatic parsing by scripts):\n");
14774 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14775 convert_leveldir->identifier, num_levels_converted,
14776 num_levels_handled,
14777 (num_levels_handled ?
14778 num_levels_converted * 100 / num_levels_handled : 0));
14780 if (num_levels_handled != num_levels_converted)
14782 Print(", FAILED:");
14783 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14784 if (levels_failed[i])
14789 PrintLine("=", 79);
14791 CloseAllAndExit(0);
14795 // ----------------------------------------------------------------------------
14796 // create and save images for use in level sketches (raw BMP format)
14797 // ----------------------------------------------------------------------------
14799 void CreateLevelSketchImages(void)
14805 InitElementPropertiesGfxElement();
14807 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14808 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14810 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14812 int element = getMappedElement(i);
14813 char basename1[16];
14814 char basename2[16];
14818 sprintf(basename1, "%04d.bmp", i);
14819 sprintf(basename2, "%04ds.bmp", i);
14821 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14822 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14824 DrawSizedElement(0, 0, element, TILESIZE);
14825 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14827 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14828 Fail("cannot save level sketch image file '%s'", filename1);
14830 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14831 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14833 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14834 Fail("cannot save level sketch image file '%s'", filename2);
14839 // create corresponding SQL statements (for normal and small images)
14842 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14843 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14846 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14847 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14849 // optional: create content for forum level sketch demonstration post
14851 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14854 FreeBitmap(bitmap1);
14855 FreeBitmap(bitmap2);
14858 fprintf(stderr, "\n");
14860 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14862 CloseAllAndExit(0);
14866 // ----------------------------------------------------------------------------
14867 // create and save images for element collecting animations (raw BMP format)
14868 // ----------------------------------------------------------------------------
14870 static boolean createCollectImage(int element)
14872 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14875 void CreateCollectElementImages(void)
14879 int anim_frames = num_steps - 1;
14880 int tile_size = TILESIZE;
14881 int anim_width = tile_size * anim_frames;
14882 int anim_height = tile_size;
14883 int num_collect_images = 0;
14884 int pos_collect_images = 0;
14886 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14887 if (createCollectImage(i))
14888 num_collect_images++;
14890 Info("Creating %d element collecting animation images ...",
14891 num_collect_images);
14893 int dst_width = anim_width * 2;
14894 int dst_height = anim_height * num_collect_images / 2;
14895 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14896 char *basename_bmp = "RocksCollect.bmp";
14897 char *basename_png = "RocksCollect.png";
14898 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14899 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14900 int len_filename_bmp = strlen(filename_bmp);
14901 int len_filename_png = strlen(filename_png);
14902 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14903 char cmd_convert[max_command_len];
14905 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14909 // force using RGBA surface for destination bitmap
14910 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14911 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14913 dst_bitmap->surface =
14914 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14916 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14918 if (!createCollectImage(i))
14921 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14922 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14923 int graphic = el2img(i);
14924 char *token_name = element_info[i].token_name;
14925 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14926 Bitmap *src_bitmap;
14929 Info("- creating collecting image for '%s' ...", token_name);
14931 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14933 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14934 tile_size, tile_size, 0, 0);
14936 // force using RGBA surface for temporary bitmap (using transparent black)
14937 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14938 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14940 tmp_bitmap->surface =
14941 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14943 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14945 for (j = 0; j < anim_frames; j++)
14947 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14948 int frame_size = frame_size_final * num_steps;
14949 int offset = (tile_size - frame_size_final) / 2;
14950 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14952 while (frame_size > frame_size_final)
14956 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14958 FreeBitmap(frame_bitmap);
14960 frame_bitmap = half_bitmap;
14963 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14964 frame_size_final, frame_size_final,
14965 dst_x + j * tile_size + offset, dst_y + offset);
14967 FreeBitmap(frame_bitmap);
14970 tmp_bitmap->surface_masked = NULL;
14972 FreeBitmap(tmp_bitmap);
14974 pos_collect_images++;
14977 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14978 Fail("cannot save element collecting image file '%s'", filename_bmp);
14980 FreeBitmap(dst_bitmap);
14982 Info("Converting image file from BMP to PNG ...");
14984 if (system(cmd_convert) != 0)
14985 Fail("converting image file failed");
14987 unlink(filename_bmp);
14991 CloseAllAndExit(0);
14995 // ----------------------------------------------------------------------------
14996 // create and save images for custom and group elements (raw BMP format)
14997 // ----------------------------------------------------------------------------
14999 void CreateCustomElementImages(char *directory)
15001 char *src_basename = "RocksCE-template.ilbm";
15002 char *dst_basename = "RocksCE.bmp";
15003 char *src_filename = getPath2(directory, src_basename);
15004 char *dst_filename = getPath2(directory, dst_basename);
15005 Bitmap *src_bitmap;
15007 int yoffset_ce = 0;
15008 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15011 InitVideoDefaults();
15013 ReCreateBitmap(&backbuffer, video.width, video.height);
15015 src_bitmap = LoadImage(src_filename);
15017 bitmap = CreateBitmap(TILEX * 16 * 2,
15018 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15021 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15028 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15029 TILEX * x, TILEY * y + yoffset_ce);
15031 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15033 TILEX * x + TILEX * 16,
15034 TILEY * y + yoffset_ce);
15036 for (j = 2; j >= 0; j--)
15040 BlitBitmap(src_bitmap, bitmap,
15041 TILEX + c * 7, 0, 6, 10,
15042 TILEX * x + 6 + j * 7,
15043 TILEY * y + 11 + yoffset_ce);
15045 BlitBitmap(src_bitmap, bitmap,
15046 TILEX + c * 8, TILEY, 6, 10,
15047 TILEX * 16 + TILEX * x + 6 + j * 8,
15048 TILEY * y + 10 + yoffset_ce);
15054 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15061 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15062 TILEX * x, TILEY * y + yoffset_ge);
15064 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15066 TILEX * x + TILEX * 16,
15067 TILEY * y + yoffset_ge);
15069 for (j = 1; j >= 0; j--)
15073 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15074 TILEX * x + 6 + j * 10,
15075 TILEY * y + 11 + yoffset_ge);
15077 BlitBitmap(src_bitmap, bitmap,
15078 TILEX + c * 8, TILEY + 12, 6, 10,
15079 TILEX * 16 + TILEX * x + 10 + j * 8,
15080 TILEY * y + 10 + yoffset_ge);
15086 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15087 Fail("cannot save CE graphics file '%s'", dst_filename);
15089 FreeBitmap(bitmap);
15091 CloseAllAndExit(0);