import ctypes
from ctypes import wintypes

PROCESS_VM_READ = 0x0010
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000

kernel32 = ctypes.windll.kernel32
pid = 49688
handle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, False, pid)
if not handle:
    err = ctypes.GetLastError()
    print(f"FAILED to open process {pid}, error={err}")
    with open("C:/Projects/AutoPOE2/bin/Release/diag_scan.txt", "w") as f:
        f.write(f"FAILED to open process {pid}, error={err}\n")
    exit(1)

class MEMORY_BASIC_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("BaseAddress", ctypes.c_void_p),
        ("AllocationBase", ctypes.c_void_p),
        ("AllocationProtect", wintypes.DWORD),
        ("RegionSize", ctypes.c_size_t),
        ("State", wintypes.DWORD),
        ("Protect", wintypes.DWORD),
        ("Type", wintypes.DWORD),
    ]

MEM_COMMIT = 0x1000
PAGE_READWRITE = 0x04

address = 0
hits = []

mbi = MEMORY_BASIC_INFORMATION()
while kernel32.VirtualQueryEx(handle, ctypes.c_void_p(address), ctypes.byref(mbi), ctypes.sizeof(mbi)):
    if mbi.State == MEM_COMMIT and (mbi.Protect & 0x04 or mbi.Protect & 0x02): # READWRITE
        base = mbi.BaseAddress
        size = mbi.RegionSize
        if size > 0 and size < 50 * 1024 * 1024:
            buf = (ctypes.c_char * size)()
            bytes_read = ctypes.c_size_t()
            if kernel32.ReadProcessMemory(handle, ctypes.c_void_p(base), buf, size, ctypes.byref(bytes_read)):
                raw = bytes(buf)[:bytes_read.value]
                # Look for {2, 4} header: bytes [0x02, 0, 0, 0, 0x04, 0, 0, 0]
                sig = b'\x02\x00\x00\x00\x04\x00\x00\x00'
                idx = 0
                while True:
                    p = raw.find(sig, idx)
                    if p == -1:
                        break
                    cand = base + p + 8
                    if p + 24 <= len(raw):
                        hp = int.from_bytes(raw[p+8:p+12], 'little')
                        max_hp = int.from_bytes(raw[p+12:p+16], 'little')
                        v8 = int.from_bytes(raw[p+16:p+20], 'little')
                        v12 = int.from_bytes(raw[p+20:p+24], 'little')
                        hits.append((cand, hp, max_hp, v8, v12))
                    idx = p + 4
    address = mbi.BaseAddress + mbi.RegionSize
    if address >= 0x7FFFFFFFFFF:
        break

kernel32.CloseHandle(handle)

with open("C:/Projects/AutoPOE2/bin/Release/diag_scan.txt", "w", encoding="utf-8") as f:
    f.write(f"Total LifeComponent hits with {{2, 4}} header: {len(hits)}\n")
    for addr, hp, max_hp, v8, v12 in hits:
        f.write(f"Addr=0x{addr:X} HP={hp}/{max_hp} v8={v8} v12={v12}\n")

print(f"Done. Wrote {len(hits)} hits to diag_scan.txt")
