"""Scan AreaInstance 0x485c4c75000 for Terrain and Exploration.
"""
import ctypes
import ctypes.wintypes as w
import struct
import subprocess

k32 = ctypes.WinDLL("kernel32", use_last_error=True)
k32.OpenProcess.restype = w.HANDLE
k32.ReadProcessMemory.argtypes = [
    w.HANDLE, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)
]
k32.ReadProcessMemory.restype = w.BOOL

def rpm(h, addr, size):
    buf = (ctypes.c_ubyte * size)()
    got = ctypes.c_size_t(0)
    if not k32.ReadProcessMemory(h, ctypes.c_void_p(addr), buf, size, ctypes.byref(got)):
        return None
    return bytes(buf[: got.value])

out = subprocess.check_output(['powershell', '-Command', '(Get-Process -Name *PathOfExile*).Id'])
pid = int(out.decode().split()[0])
h = k32.OpenProcess(0x1F0FFF, False, pid)

ai = 0x485c4c75000
b = rpm(h, ai, 0x1200)

print("Scanning for grid/terrain dimensions (cols, rows in 50..3000)...")
for off in range(0, len(b) - 16, 4):
    c, r = struct.unpack_from("<II", b, off)
    if 50 <= c <= 2500 and 50 <= r <= 2500:
        # Check if bytesPerRow is at off+8 or nearby
        bpr = struct.unpack_from("<I", b, off + 8)[0]
        print(f"Candidate dimension at ai+{off:#x}: cols={c}, rows={r}, bpr={bpr}")

print("\nScanning for pointers to nested structs with cols, rows...")
for off in range(0, len(b) - 8, 8):
    ptr = struct.unpack_from("<Q", b, off)[0]
    if 0x100000000 <= ptr <= 0x7FFFFFFFFFF:
        sub = rpm(h, ptr, 0x80)
        if sub:
            c, r = struct.unpack_from("<II", sub, 0)
            if 50 <= c <= 2500 and 50 <= r <= 2500:
                print(f"Pointer at ai+{off:#x} -> {ptr:#x}: cols={c}, rows={r}")
            # check offset 0x10, 0x20 in sub
            for soff in [8, 0x10, 0x18, 0x20, 0x28]:
                c2, r2 = struct.unpack_from("<II", sub, soff)
                if 50 <= c2 <= 2500 and 50 <= r2 <= 2500:
                    print(f"Pointer at ai+{off:#x} -> {ptr:#x}+{soff:#x}: cols={c2}, rows={r2}")

