예제 #1
0
def test_compute_player_motion_mount_and_active_floor():
    elevator = Movable(
        type=EntityDBEntry(id=EntityType.ACTIVEFLOOR_ELEVATOR),
        position_x=0.5,
        position_y=0.7,
        velocity_x=-0.1,
        velocity_y=-0.3,
    )
    poly_elevator = PolyPointer(101, elevator, MemContext())
    # Turkey on an elevator
    mount = Mount(
        type=EntityDBEntry(id=EntityType.MOUNT_TURKEY),
        position_x=18,
        position_y=13,
        velocity_x=-2,
        velocity_y=-7,
        overlay=poly_elevator,
    )
    poly_mount = PolyPointer(102, mount, MemContext())
    player = Player(position_x=5,
                    position_y=7,
                    velocity_x=-1,
                    velocity_y=-3,
                    overlay=poly_mount)
    expected_motion = PlayerMotion(position_x=18.5,
                                   position_y=13.7,
                                   velocity_x=-2.1,
                                   velocity_y=-7.3)
    run_state = RunState()
    assert run_state.compute_player_motion(player) == expected_motion
예제 #2
0
def test_could_tp_mount(mount_type, expected_could_tp):
    mount = Mount(type=EntityDBEntry(id=mount_type))
    poly_mount = PolyPointer(101, mount, MemContext())
    player = Player(overlay=poly_mount)

    run_state = RunState()
    assert run_state.could_tp(player, set(), set()) == expected_could_tp
예제 #3
0
def test_dataclass_struct_nested():
    meta_0 = {}
    StructFieldMeta(0x0, deferred_uint8).put_into(meta_0)
    meta_2 = {}
    StructFieldMeta(0x2, deferred_uint8).put_into(meta_2)

    @dataclass
    class Inner:
        field_a: int = field(metadata=meta_0)
        field_b: int = field(metadata=meta_2)

    def deferred_inner(path, field_type):
        return DataclassStruct(path, field_type)

    meta_3_inner = {}
    StructFieldMeta(0x3, deferred_inner).put_into(meta_3_inner)

    @dataclass
    class Outer:
        field_0: int = field(metadata=meta_0)
        field_2: int = field(metadata=meta_2)
        inner: Inner = field(metadata=meta_3_inner)

    dc_outer = DataclassStruct(FieldPath(), Outer)
    assert dc_outer.from_bytes(b"\x00\x01\x02\x03\x04\x05", MemContext()) == Outer(
        0, 2, Inner(3, 5)
    )
예제 #4
0
def test_vesctor_bad_buf():
    vec_buf = b""
    arr_buf = b"\xff\x03\x00\x09\x00"
    mem_type = Vector(FieldPath(), Optional[Tuple[int, ...]], sc_uint8)
    mem_ctx = MemContext(BytesReader(arr_buf))
    with pytest.raises(ValueError):
        mem_type.from_bytes(vec_buf, mem_ctx)
예제 #5
0
def test_poly_pointer_castless(cls, expected_val):
    pp_type = PolyPointerType(FieldPath(), PolyPointer[cls], DataclassStruct)
    pp_supreme = pp_type.from_bytes(
        SUPREME_POINTER_BYTES, MemContext(LOWEST_BYTES_READER)
    )
    assert pp_supreme.addr == SUPREME_POINTER_ADDR
    assert pp_supreme.value == expected_val
예제 #6
0
def test_has_mounted_tame(chain_status, theme, mount_type, mount_tamed,
                          expected_low):
    mount = Mount(type=EntityDBEntry(id=mount_type), is_tamed=mount_tamed)
    poly_mount = PolyPointer(101, mount, MemContext())

    run_state = RunState()
    run_state.sunken_chain_status = chain_status
    run_state.update_has_mounted_tame(theme, poly_mount)

    is_low = Label.LOW in run_state.run_label._set
    assert is_low == expected_low
예제 #7
0
def test_dataclass_struct_scalar_c_error_passthrough():
    meta_0 = {}
    StructFieldMeta(0x0, deferred_uint8).put_into(meta_0)

    @dataclass
    class MyStruct:
        field_a: FourEnum = field(metadata=meta_0)

    dc_struct = DataclassStruct(FieldPath(), MyStruct)
    with pytest.raises(ScalarCValueConstructionError):
        dc_struct.from_bytes(b"\x00", MemContext())
예제 #8
0
def test_vector_dsl():
    vec_wrap_buf = (
        b"\xdc" * 9  # offset 1 and unused pointer
        + b"\x01\x00\x00\x00\x00\x00\x00\x00"
        + b"\xde" * 4
        + b"\x02\x00\x00\x00"
    )
    arr_buf = b"\xff\x03\x00\x09\x00"
    mem_type = DataclassStruct(FieldPath(), VecWrap)
    mem_ctx = MemContext(BytesReader(arr_buf))
    assert mem_type.from_bytes(vec_wrap_buf, mem_ctx) == VecWrap((3, 9))
예제 #9
0
def test_dataclass_struct_general_error():
    meta_0 = {}
    StructFieldMeta(0x0, deferred_uint8).put_into(meta_0)

    @dataclass
    class MyStruct:
        field_a: FourEnum = field(metadata=meta_0)

    dc_struct = DataclassStruct(FieldPath(), MyStruct)
    with pytest.raises(ValueError):
        # Too few bytes in buffer
        dc_struct.from_bytes(b"", MemContext())
예제 #10
0
def test_state():
    state_mt = DataclassStruct(FieldPath(), State)
    state_bytes = b"\x05\xff\x00\x01\x01\x63\x2a\xff\x08\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00"  # pylint: disable=line-too-long
    mem_ctx = MemContext(BytesReader(b"\x00\x00\x03\x01\x0f\x08"))
    expected = State(
        5,
        (False, True, True),
        frozenset([Player(99, 42), Player(8, 1)]),
        259,
        PolyPointer(4, Player(15, 8), mem_ctx),
    )
    assert state_mt.from_bytes(state_bytes, mem_ctx) == expected
예제 #11
0
def test_dataclass_struct_simple():
    meta_0 = {}
    StructFieldMeta(0x0, deferred_uint8).put_into(meta_0)
    meta_3 = {}
    StructFieldMeta(0x3, deferred_uint8).put_into(meta_3)

    @dataclass
    class MyStruct:
        field_a: int = field(metadata=meta_0)
        field_b: int = field(metadata=meta_3)

    dc_struct = DataclassStruct(FieldPath(), MyStruct)
    assert dc_struct.from_bytes(b"\x00\x01\x02\x03", MemContext()) == MyStruct(0, 3)
예제 #12
0
def test_poly_pointer_cast_up(cls):
    pp_type = PolyPointerType(FieldPath(), PolyPointer[Lowest], DataclassStruct)
    pp_lowest = pp_type.from_bytes(
        SUPREME_POINTER_BYTES, MemContext(LOWEST_BYTES_READER)
    )
    expected_val = Lowest(1, 2, 3)

    cast_val = pp_lowest.as_type(cls)
    assert cast_val == expected_val

    pp_poly = pp_lowest.as_poly_type(cls)

    assert pp_poly.addr == SUPREME_POINTER_ADDR
    assert pp_poly.value == expected_val
예제 #13
0
def test_compute_player_motion_mount():
    mount = Mount(
        type=EntityDBEntry(id=EntityType.MOUNT_TURKEY),
        position_x=18,
        position_y=13,
        velocity_x=-2,
        velocity_y=-7,
    )
    poly_mount = PolyPointer(101, mount, MemContext())
    player = Player(position_x=5,
                    position_y=7,
                    velocity_x=-1,
                    velocity_y=-3,
                    overlay=poly_mount)
    expected_motion = PlayerMotion(position_x=18,
                                   position_y=13,
                                   velocity_x=-2,
                                   velocity_y=-7)
    run_state = RunState()
    assert run_state.compute_player_motion(player) == expected_motion
예제 #14
0
def test_compute_player_motion_active_floor():
    elevator = Movable(
        type=EntityDBEntry(id=EntityType.ACTIVEFLOOR_ELEVATOR),
        position_x=0.5,
        position_y=0.7,
        velocity_x=-0.1,
        velocity_y=-0.3,
    )
    poly_elevator = PolyPointer(101, elevator, MemContext())
    player = Player(position_x=5,
                    position_y=7,
                    velocity_x=-1,
                    velocity_y=-3,
                    overlay=poly_elevator)
    expected_motion = PlayerMotion(position_x=5.5,
                                   position_y=7.7,
                                   velocity_x=-1.1,
                                   velocity_y=-3.3)
    run_state = RunState()
    assert run_state.compute_player_motion(player) == expected_motion
예제 #15
0
def test_unordered_map_type():
    def uint32_deferred(field, py_type):
        assert field == FieldPath()
        assert py_type is int
        return ScalarCType(FieldPath(), int, ctypes.c_uint32)

    def uint16_deferred(field, py_type):
        assert field == FieldPath()
        assert py_type is int
        return ScalarCType(FieldPath(), int, ctypes.c_uint16)

    mem_type = UnorderedMapType(
        FieldPath(), UnorderedMap[int, int], uint32_deferred, uint16_deferred
    )
    mem_ctx = MemContext(BytesReader(UNORDERED_MAP_MEM))
    uo_map: UnorderedMap = mem_type.from_bytes(UNORDERED_MAP_BYTES, mem_ctx)
    # Sanity check key fields before we try to call get()
    assert uo_map.meta.buckets_ptr == 1
    assert uo_map.meta.mask == 1

    value = uo_map.get(2)
    assert value == 3
예제 #16
0
 def read():
     sc_type = ScalarCType(FieldPath(), FourEnum, ctypes.c_uint8)
     sc_type.from_bytes(b"\x00", MemContext())
예제 #17
0
 def read():
     sc_type = ScalarCType(FieldPath(), int, ctypes.c_uint8)
     sc_type.from_bytes(b"", MemContext())
예제 #18
0
def test_scalar_c_type_pointer(addr_bytes, expected):
    sc_type = ScalarCType(FieldPath(), int, ctypes.c_void_p)
    assert sc_type.from_bytes(addr_bytes, MemContext()) == expected
예제 #19
0
def test_scalar_c_type_byte(py_type, expected):
    sc_type = ScalarCType(FieldPath(), py_type, ctypes.c_uint8)
    assert sc_type.from_bytes(b"\x04", MemContext()) == expected
예제 #20
0
class Spel2Process:
    def __init__(self, proc_handle):
        self.proc_handle = proc_handle
        self._feedcode = None
        self.mem_ctx = MemContext(Spel2Reader(self))

    @classmethod
    def from_pid(cls, pid):
        handle = win32api.OpenProcess(
            win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ,
            False, pid)
        if not handle:
            return None

        return cls(handle)

    def running(self):
        return win32con.STILL_ACTIVE == win32process.GetExitCodeProcess(
            self.proc_handle)

    def read_memory(self, offset, size):
        try:
            return win32process.ReadProcessMemory(self.proc_handle, offset,
                                                  size)
        except pywintypes.error:
            return None

    def find(self, offset, needle, bsize=4096):
        if bsize < len(needle):
            raise ValueError(
                "The buffer size must be larger than the string being searched for."
            )

        cursor = offset
        overlap = len(needle) - 1
        while True:
            buffer = self.read_memory(cursor, bsize)
            if not buffer:
                return None
            cursor += len(buffer)

            pos = buffer.find(needle)
            if pos >= 0:
                return cursor - len(buffer) + pos

            if len(buffer) <= overlap:
                return None

            cursor -= overlap

    def find_one(self, start, needle, size):
        buffer = self.read_memory(start, size)
        if buffer is None:
            return None

        pos = buffer.find(needle)
        if pos >= 0:
            return start + pos

        return None

    def find_in_page(self, mbi: MemoryBasicInformation, needle):
        return self.find(mbi.base_address, needle, mbi.region_size)

    def memory_pages(self, min_addr=0x10000, max_addr=0x00007FFFFFFEFFFF):
        addr = min_addr
        while True:
            mbi = MemoryBasicInformation.from_virtual_query(
                self.proc_handle, addr)
            if not mbi:
                break

            addr += mbi.region_size

            if mbi.state != win32con.MEM_COMMIT:
                continue

            if mbi.type != win32con.MEM_PRIVATE:
                continue

            if mbi.protect & win32con.PAGE_NOACCESS:
                continue

            yield mbi

            if addr >= max_addr:
                break

    def get_spel2_module(self):
        module_handles = win32process.EnumProcessModules(self.proc_handle)
        for module_handle in module_handles:

            module_filename = Path(
                win32process.GetModuleFileNameEx(self.proc_handle,
                                                 module_handle))

            if module_filename.name == "Spel2.exe":
                return module_handle
        return None

    def get_offset_past_bundle(self):
        exe = self.get_spel2_module()
        offset = 0x1000

        while True:
            header = win32process.ReadProcessMemory(self.proc_handle,
                                                    exe + offset, 8)
            data_len, filepath_len = unpack(b"<II", header)
            if (data_len, filepath_len) == (0, 0):
                break
            offset += 8 + data_len + filepath_len

        return exe + offset

    def try_get_feedcode(self) -> Optional[int]:
        if self._feedcode is not None:
            return self._feedcode
        for page in self.memory_pages(min_addr=0x40000000000):
            result = self.find_in_page(page, b"\x00\xde\xc0\xed\xfe")
            if result:
                self._feedcode = result
                return result
        return None

    def get_feedcode(self) -> int:
        feedcode = self.try_get_feedcode()
        if feedcode is None:
            raise FeedcodeNotFound()
        return feedcode

    def get_state(self) -> Optional[State]:
        addr = self.get_feedcode() - 0x5F
        return self.mem_ctx.type_at_addr(State, addr)
예제 #21
0
def test_pointer_uint8(addr_bytes, expected):
    mem_ctx = MemContext(BytesReader(b"\x0c\x03\x10"))
    arr = Pointer(FieldPath, Optional[int], deferred_uint8)
    assert arr.from_bytes(addr_bytes, mem_ctx) == expected
예제 #22
0
 def __init__(self, proc_handle):
     self.proc_handle = proc_handle
     self._feedcode = None
     self.mem_ctx = MemContext(Spel2Reader(self))
예제 #23
0
def poly_pointer_no_mem(value):
    return PolyPointer(addr=0xBAD, mem_ctx=MemContext(), value=value)
예제 #24
0
def test_unordered_map_node_deserialize(key_type, val_type, node_bytes, expected):
    node_type = _UnorderedMapNodeType(key_type, val_type)
    node = node_type.from_bytes(node_bytes, MemContext())
    assert node == expected
예제 #25
0
def test_array_uint8(py_type, expected):
    arr = Array(FieldPath, py_type, deferred_uint8, count=2)
    assert arr.from_bytes(b"\x0a\x02", MemContext()) == expected
예제 #26
0
def test_vector_model(vec_buf, expected):
    mem_type = Vector(FieldPath(), Optional[Tuple[int, ...]], sc_uint8)
    arr_buf = b"\x0a\x0b\x0c\x0d"
    mem_ctx = MemContext(BytesReader(arr_buf))
    assert mem_type.from_bytes(vec_buf, mem_ctx) == expected
예제 #27
0
 def read():
     sc_type = Array(FieldPath, Tuple[int, ...], deferred_uint8, count=2)
     sc_type.from_bytes(b"", MemContext())
예제 #28
0
 def read():
     sc_type = Array(FieldPath, Tuple[FourEnum, ...], deferred_uint8, count=2)
     sc_type.from_bytes(b"\x04\x00", MemContext())
예제 #29
0
def test_player():
    player_mt = DataclassStruct(FieldPath(), Player)
    state_bytes = b"\x10\x20"
    assert player_mt.from_bytes(state_bytes, MemContext()) == Player(16, 32)