"""Scan PathOfExile.exe .data for camera-like float clusters. 11/09/2026."""
from __future__ import annotations

import ctypes
import ctypes.wintypes as w
import struct
import sys

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


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


def main():
    pid = int(sys.argv[1]) if len(sys.argv) > 1 else 3132
    h = kernel32.OpenProcess(0x410, False, pid)
    base = 0x7FF6AFC20000
    data = base + 0x3A04000
    size = 0xD163DC
    print(f"reading .data {data:#x} {size}", flush=True)
    blob = rpm(h, data, size)
    print("got", None if blob is None else len(blob), flush=True)
    if not blob:
        return
    found = 0
    for i in range(0, len(blob) - 0x50, 8):
        cur, mn, mx, fov, zn, zf = struct.unpack_from("<ffffff", blob, i + 0x2C)
        vals = (cur, mn, mx, fov, zn, zf)
        if not all(x == x and abs(x) < 1e5 for x in vals):
            continue
        if not (3 <= mn <= 80 and 8 <= mx <= 400 and mn < mx):
            continue
        if not ((0.3 <= fov <= 1.8) or (5 <= fov <= 90)):
            continue
        if not (0.001 <= zn <= 20 and 20 <= zf <= 20000):
            continue
        if not (3 <= cur <= 800):
            continue
        vt = struct.unpack_from("<Q", blob, i)[0]
        print(
            f"{data+i:#x} vt={vt:#x} cur={cur:.2f} min={mn:.2f} max={mx:.2f} "
            f"fov={fov:.4f} zn={zn:.3f} zf={zf:.1f}",
            flush=True,
        )
        found += 1
        if found >= 25:
            break
    print("found", found, flush=True)


if __name__ == "__main__":
    main()
