import ctypes
from ctypes import wintypes
import struct
import psutil

pids = [p.pid for p in psutil.process_iter() if 'PathOfExile' in p.name()]
if not pids:
    print('No PathOfExile.exe found!')
    exit(1)
pid = pids[0]
print(f'Scanning PID: {pid}')

kernel32 = ctypes.windll.kernel32
hProcess = kernel32.OpenProcess(0x0010 | 0x0400, False, pid)

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

mbi = MEMORY_BASIC_INFORMATION()
mbi_size = ctypes.sizeof(mbi)
addr = 0x100000000
max_addr = 0x30000000000

hits = []

while addr < max_addr:
    if not kernel32.VirtualQueryEx(hProcess, ctypes.c_void_p(addr), ctypes.byref(mbi), mbi_size):
        break
    base = mbi.BaseAddress or 0
    size = mbi.RegionSize
    protect = mbi.Protect
    state = mbi.State
    addr = base + size
    if state != 0x1000 or protect != 0x04 or size > 64 * 1024 * 1024:
        continue

    buf = (ctypes.c_char * size)()
    bytesRead = ctypes.c_size_t()
    if not kernel32.ReadProcessMemory(hProcess, ctypes.c_void_p(base), buf, size, ctypes.byref(bytesRead)):
        continue
    raw = bytes(buf[:bytesRead.value])

    for i in range(8, len(raw) - 48, 4):
        h1, h2, h3, es_cur, es_max = struct.unpack_from('<IIIII', raw, i)
        if h1 == 1 and h2 == 1 and h3 == 1:
            if 500 <= es_max <= 30000 and 0 < es_cur <= es_max:
                prev8, prev4 = struct.unpack_from('<II', raw, i - 8)
                hits.append({
                    'addr': base + i,
                    'hp': (h1, h2),
                    'es': (es_cur, es_max),
                    'prev': (prev8, prev4),
                    'raw_extra': [struct.unpack_from('<I', raw, i + off)[0] for off in range(20, 48, 4)]
                })

kernel32.CloseHandle(hProcess)

print(f'Found {len(hits)} CI LifeComponent candidates in heap:')
for h in hits:
    print(f"  -> Address: 0x{h['addr']:X}, HP={h['hp']}, ES={h['es']}, prev={h['prev']}, extra={h['raw_extra']}")
