예제 #1
0
    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]
예제 #2
0
    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)
예제 #3
0
def test_little_endian():
    rom_bytes = bytearray(b"\x00\x01\x02\x03\x04\x05\x06\x00")

    rom = Rom(rom_bytes)

    assert rom.little_endian(0) == (0x01 << 8) + 0x00
    assert rom.little_endian(6) == (0x00 << 8) + 0x06
예제 #4
0
def test_find():
    rom_bytes = bytearray(b"\x00\x01\x02\x03\x04\x05\x06\x00")

    rom = Rom(rom_bytes)

    assert rom.find(b"\x00") == 0
    assert rom.find(b"\x00", 1) == 7
    assert rom.find(b"\x07") == -1
예제 #5
0
def test_int():
    rom_bytes = bytearray(b"\x00\x01\x02\x03\x04\x1f")
    numbers = [0, 1, 2, 3, 4, 0x1F]

    rom = Rom(rom_bytes)

    for offset, number in enumerate(numbers):
        assert rom.int(offset) == number
예제 #6
0
def rom():
    if not test_rom_path.exists():
        raise ValueError(
            f"To run the test suite, place a US SMB3 Rom named '{test_rom_path}' in the root of the repository."
        )

    with open(test_rom_path, "rb") as rom_file:
        yield Rom(bytearray(rom_file.read()))
예제 #7
0
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
예제 #8
0
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)
예제 #9
0
def _get_special_enterable_tiles(rom: Rom) -> bytearray:
    return rom.read(SPECIAL_ENTERABLE_TILES_LIST, SPECIAL_ENTERABLE_TILE_AMOUNT)
예제 #10
0
def _get_normal_enterable_tiles(rom: Rom) -> bytearray:
    return rom.read(TILE_ATTRIBUTES_TS0_OFFSET, 4)
예제 #11
0
def rom():
    with open("SMB3.nes", "rb") as rom_file:
        yield Rom(bytearray(rom_file.read()))