"""Scan all static QWORD pointers in .data section of PathOfExile.exe
to find GameState / InGameState / Camera.
11/09/2026.
"""
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])

def rpm_u64(h, addr):
    b = rpm(h, addr, 8)
    return struct.unpack("<Q", b)[0] if b and len(b) == 8 else 0

def rpm_f32(h, addr):
    b = rpm(h, addr, 4)
    return struct.unpack("<f", b)[0] if b and len(b) == 4 else 0.0

out = subprocess.check_output(['powershell', '-Command', '(Get-Process -Name *PathOfExile*).Id'])
pids = [int(x.strip()) for x in out.decode().split() if x.strip().isdigit()]
pid = pids[0]
print(f"Target PID: {pid}")

h = k32.OpenProcess(0x1F0FFF, False, pid)
base = 0x7ff6afc20000 # From previous run

# .data is at VA=0x3a04000, VSize=0xd163dc
data_va = base + 0x3a04000
data_sz = 0xd163dc

print(f"Reading .data section: {data_va:#x} ({data_sz // 1024} KB)...")
data = rpm(h, data_va, data_sz)
if not data:
    print("Failed to read .data")
    exit(1)

print(f"Read {len(data)} bytes of .data. Scanning for pointers...")

# Find all QWORDs in .data that point into heap (0x10000000000 to 0x7FFFFFFFFFF)
valid_roots = []
for i in range(0, len(data) - 7, 8):
    val = struct.unpack_from("<Q", data, i)[0]
    if 0x100000000 <= val <= 0x7FFFFFFFFFF:
        valid_roots.append((data_va + i, val))

print(f"Found {len(valid_roots)} valid heap pointers in .data.")

# For each pointer, test if it's GameState or InGameState or Camera
found_cam = []
for root_addr, ptr in valid_roots:
    # 1. Test if ptr is Camera directly:
    # Camera in POE2 has Zoom at +0x528 (default 1.0f), Matrix at +0x1A0
    z = rpm_f32(h, ptr + 0x528)
    if 0.95 <= z <= 1.05:
        # Check matrix at +0x1A0
        m11 = rpm_f32(h, ptr + 0x1A0)
        m22 = rpm_f32(h, ptr + 0x1A0 + 0x14)
        m33 = rpm_f32(h, ptr + 0x1A0 + 0x28)
        m44 = rpm_f32(h, ptr + 0x1A0 + 0x3C)
        if (-5.0 <= m11 <= 5.0 and m11 != 0.0) and (-5.0 <= m22 <= 5.0 and m22 != 0.0):
            print(f"DIRECT CAMERA FOUND in .data @ {root_addr:#x} -> {ptr:#x}! Zoom={z}, M11={m11}, M22={m22}")
            found_cam.append((root_addr, ptr))

    # 2. Test if ptr is InGameState (Camera at +0x378 or +0x368)
    for cam_off in [0x378, 0x368, 0x388, 0x358]:
        c = rpm_u64(h, ptr + cam_off)
        if 0x100000000 <= c <= 0x7FFFFFFFFFF:
            cz = rpm_f32(h, c + 0x528)
            if 0.95 <= cz <= 1.05:
                cm11 = rpm_f32(h, c + 0x1A0)
                if -5.0 <= cm11 <= 5.0 and cm11 != 0.0:
                    print(f"INGAMESTATE FOUND in .data @ {root_addr:#x} (RVA {root_addr - base:#x}) -> IGS: {ptr:#x}, Cam (offset +{cam_off:#x}): {c:#x}, Zoom={cz}, M11={cm11}")
                    found_cam.append((root_addr, c))

    # 3. Test if ptr is GameState (CurrentStatePtr at +0x08 -> vector of states)
    vec_first = rpm_u64(h, ptr + 0x08)
    if 0x100000000 <= vec_first <= 0x7FFFFFFFFFF:
        igs = rpm_u64(h, vec_first)
        if 0x100000000 <= igs <= 0x7FFFFFFFFFF:
            for cam_off in [0x378, 0x368, 0x388, 0x358]:
                c = rpm_u64(h, igs + cam_off)
                if 0x100000000 <= c <= 0x7FFFFFFFFFF:
                    cz = rpm_f32(h, c + 0x528)
                    if 0.95 <= cz <= 1.05:
                        cm11 = rpm_f32(h, c + 0x1A0)
                        if -5.0 <= cm11 <= 5.0 and cm11 != 0.0:
                            print(f"GAMESTATE FOUND in .data @ {root_addr:#x} (RVA {root_addr - base:#x}) -> GameState: {ptr:#x}, IGS: {igs:#x}, Cam (offset +{cam_off:#x}): {c:#x}, Zoom={cz}")
                            found_cam.append((root_addr, c))

print(f"\nDone scan. Found {len(found_cam)} camera matches.")
