def __init__(self, layout_address: int, rom: Rom): super(WorldMap, self).__init__(WORLD_MAP_OBJECT_SET, layout_address) self._rom = rom self._minimal_enterable_tiles = _get_normal_enterable_tiles(self._rom) self._special_enterable_tiles = _get_special_enterable_tiles(self._rom) self._completable_tiles = _get_completable_tiles(self._rom) memory_addresses = list_world_map_addresses(rom) try: self.number = memory_addresses.index(layout_address) + 1 except ValueError: raise ValueError(f"World map was not found at given memory address {hex(layout_address)}.") self.height = WORLD_MAP_HEIGHT layout_end_index = rom.find(b"\xFF", layout_address) self.layout_bytes = rom.read(layout_address, layout_end_index - layout_address) if len(self.layout_bytes) % WORLD_MAP_SCREEN_SIZE != 0: raise ValueError( f"Invalid length of layout bytes for world map ({self.layout_bytes}). " f"Should be divisible by {WORLD_MAP_SCREEN_SIZE}." ) self.screen_count = len(self.layout_bytes) // WORLD_MAP_SCREEN_SIZE self.width = int(self.screen_count * WORLD_MAP_SCREEN_WIDTH) self._parse_structure_data_block(rom)
def _parse_structure_data_block(self, rom: Rom): structure_block_offset = rom.little_endian(STRUCTURE_DATA_OFFSETS + OFFSET_SIZE * self.world_index) self.structure_block_start = WORLD_MAP_BASE_OFFSET + structure_block_offset # the indexes into the y_pos list, where the levels for the n-th screen start y_pos_start_by_screen = rom.read(self.structure_block_start, 4) level_y_pos_list_start = WORLD_MAP_BASE_OFFSET + rom.little_endian( LEVEL_Y_POS_LISTS + OFFSET_SIZE * self.world_index) level_x_pos_list_start = WORLD_MAP_BASE_OFFSET + rom.little_endian( LEVEL_X_POS_LISTS + OFFSET_SIZE * self.world_index) level_y_pos_list_end = level_x_pos_list_start - level_y_pos_list_start self.level_count_s1 = y_pos_start_by_screen[1] - y_pos_start_by_screen[ 0] self.level_count_s2 = y_pos_start_by_screen[2] - y_pos_start_by_screen[ 1] self.level_count_s3 = y_pos_start_by_screen[3] - y_pos_start_by_screen[ 2] self.level_count_s4 = level_y_pos_list_end - y_pos_start_by_screen[3]
def list_world_map_addresses(rom: Rom) -> List[int]: offsets = rom.read(LAYOUT_LIST_OFFSET, WORLD_COUNT * OFFSET_SIZE) addresses = [] for world in range(WORLD_COUNT): index = world * 2 world_map_offset = (offsets[index + 1] << 8) + offsets[index] addresses.append(WORLD_MAP_BASE_OFFSET + world_map_offset) return addresses
def _get_completable_tiles(rom: Rom) -> bytearray: completable_tile_amount = rom.find(COMPLETABLE_LIST_END_MARKER, COMPLETABLE_TILES_LIST) - COMPLETABLE_TILES_LIST - 1 return rom.read(COMPLETABLE_TILES_LIST, completable_tile_amount)
def _get_special_enterable_tiles(rom: Rom) -> bytearray: return rom.read(SPECIAL_ENTERABLE_TILES_LIST, SPECIAL_ENTERABLE_TILE_AMOUNT)
def _get_normal_enterable_tiles(rom: Rom) -> bytearray: return rom.read(TILE_ATTRIBUTES_TS0_OFFSET, 4)