import ctypes, struct
from ctypes import wintypes

k32 = ctypes.WinDLL('kernel32', use_last_error=True)
pid = 49116
h = k32.OpenProcess(0x410, False, pid)
base = 0x7ff6afc20000

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

def rpm_ptr(addr):
    b = rpm(addr, 8)
    if b and len(b) == 8:
        return struct.unpack('<Q', b)[0]
    return 0

def rpm_float(addr):
    b = rpm(addr, 4)
    if b and len(b) == 4:
        return struct.unpack('<f', b)[0]
    return 0.0

def rpm_chunked(addr, total_size, chunk_size=65536):
    res = bytearray()
    for off in range(0, total_size, chunk_size):
        curr_addr = addr + off
        to_read = min(chunk_size, total_size - off)
        buf = (ctypes.c_ubyte * to_read)()
        got = ctypes.c_size_t(0)
        if k32.ReadProcessMemory(h, ctypes.c_void_p(curr_addr), buf, to_read, ctypes.byref(got)):
            res.extend(buf[:got.value])
        else:
            res.extend(b"\x00" * to_read)
    return bytes(res)

print("Reading .text...")
blob = rpm_chunked(base + 0x1000, 0x2E86000)
print(f"Read {len(blob)} bytes")

# 1. Try InGameState pattern 1 from POE2Radar:
# 8B C7 48 83 C4 40 5F C3 48 8B 05 ? ? ? ? 48 89 07 48
# 8B C7 48 83 C4 40 5F C3
prefix = bytes.fromhex("8BC74883C4405FC3488B05")
idx = blob.find(prefix)
if idx != -1:
    va = base + 0x1000 + idx
    disp = struct.unpack('<i', blob[idx+11:idx+15])[0]
    slot = va + 15 + disp
    print(f"Found Pattern 1 @ {va:#x} -> Slot: {slot:#x}")
    in_game = rpm_ptr(slot)
    print(f"   InGameState: {in_game:#x}")
else:
    print("Pattern 1 not found")

# 2. Try InGameState pattern from AutoPOE2:
# 48 8B 05 ? ? ? ? 48 8B 40 38 48 8B 88
# 48 8B 05 ... 48 8B 40 38
# Look for 48 8B 40 38
print("\nScanning for '48 8B 40 38'...")
hits = []
start = 0
while len(hits) < 10:
    idx = blob.find(b"\x48\x8B\x40\x38", start)
    if idx == -1:
        break
    va = base + 0x1000 + idx
    # Check if 7 bytes before is 48 8B 05
    if idx >= 7 and blob[idx-7:idx-4] == b"\x48\x8B\x05":
        disp = struct.unpack('<i', blob[idx-4:idx])[0]
        slot = (va - 7) + 7 + disp
        in_game = rpm_ptr(slot)
        print(f"Hit @ {va-7:#x} -> Slot: {slot:#x}, InGame: {in_game:#x}")
        hits.append((slot, in_game))
    start = idx + 1

# 3. Try GameState pattern from POE2Radar:
# 48 39 2D ? ? ? ? 0F 85 16 01 00 00
print("\nScanning for GameState pattern '48 39 2D ... 0F 85 16 01 00 00'...")
pat = bytes.fromhex("48392D")
suffix = bytes.fromhex("0F8516010000")
start = 0
while True:
    idx = blob.find(pat, start)
    if idx == -1:
        break
    if idx + 13 <= len(blob) and blob[idx+7:idx+13] == suffix:
        va = base + 0x1000 + idx
        disp = struct.unpack('<i', blob[idx+3:idx+7])[0]
        slot = va + 7 + disp
        print(f"Found GameState Ref @ {va:#x} -> Slot: {slot:#x}")
        gs = rpm_ptr(slot)
        print(f"   GameState: {gs:#x}")
    start = idx + 1

# If we have any candidates for InGameState, inspect camera offsets!
for slot, in_game in hits:
    if in_game > 0x10000:
        for cam_off in [0x378, 0x368, 0x358, 0x388, 0x310, 0x340]:
            cam_ptr = rpm_ptr(in_game + cam_off)
            if cam_ptr > 0x10000:
                zoom_val = rpm_float(cam_ptr + 0x528)
                print(f"InGame {in_game:#x} + {cam_off:#x} -> Cam: {cam_ptr:#x}, Zoom@+0x528: {zoom_val}")
                # Also check surrounding floats at cam_ptr
                floats = [rpm_float(cam_ptr + off) for off in range(0x500, 0x550, 4)]
                print(f"   Floats around 0x500..0x550: {[round(x, 3) for x in floats]}")
