import time
import ctypes
from ctypes import wintypes
import struct

kernel32 = ctypes.windll.kernel32

pid = 43084
h_proc = kernel32.OpenProcess(0x0410, False, pid) # PROCESS_QUERY_INFORMATION | PROCESS_VM_READ


def read_u64(addr):
    buf = ctypes.create_string_buffer(8)
    br = ctypes.c_size_t(0)
    kernel32.ReadProcessMemory(h_proc, ctypes.c_void_p(addr), buf, 8, ctypes.byref(br))
    return struct.unpack('Q', buf.raw)[0] if br.value == 8 else 0

def read_u32(addr):
    buf = ctypes.create_string_buffer(4)
    br = ctypes.c_size_t(0)
    kernel32.ReadProcessMemory(h_proc, ctypes.c_void_p(addr), buf, 4, ctypes.byref(br))
    return struct.unpack('I', buf.raw)[0] if br.value == 4 else 0

def read_f32_3(addr):
    buf = ctypes.create_string_buffer(12)
    br = ctypes.c_size_t(0)
    kernel32.ReadProcessMemory(h_proc, ctypes.c_void_p(addr), buf, 12, ctypes.byref(br))
    return struct.unpack('3f', buf.raw) if br.value == 12 else (0.0, 0.0, 0.0)

# Get base address of PathOfExile.exe
# Let's inspect known static roots
# From offsets.toml:
# static_root_1 = 0x45ccfe0
# static_root_2 = 0x45ccfd8
# static_root_3 = 0x45d4100
# static_root_4 = 0x45d3dd8
# static_root_5 = 0x45d40f8
# static_root_6 = 0x4716880
# static_root_7 = 0x4435f90
# static_root_8 = 0x4435f98

# Let's find base address via EnumProcessModules
import ctypes.wintypes
psapi = ctypes.windll.psapi
hMods = (wintypes.HMODULE * 1024)()
cbNeeded = wintypes.DWORD()
psapi.EnumProcessModules(h_proc, hMods, ctypes.sizeof(hMods), ctypes.byref(cbNeeded))
base_addr = hMods[0]
print(f"Base Address of PathOfExile.exe: 0x{base_addr:X}")

roots = [0x45ccfe0, 0x45ccfd8, 0x45d4100, 0x45d3dd8, 0x45d40f8, 0x4716880, 0x4435f90, 0x4435f98]

for idx, rva in enumerate(roots, 1):
    root_val = read_u64(base_addr + rva)
    if root_val > 0x10000:
        player_ptr = read_u64(root_val + 0xBE0)
        if player_ptr > 0x10000:
            life_addr = player_ptr + 0x3D8
            hp = read_u32(life_addr)
            max_hp = read_u32(life_addr + 4)
            es = read_u32(life_addr + 0x0C)
            max_es = read_u32(life_addr + 0x10)
            xyz_addr = player_ptr + 0x540
            xyz = read_f32_3(xyz_addr)
            print(f"Root #{idx} (0x{rva:X}) -> InGame: 0x{root_val:X} -> Player: 0x{player_ptr:X}")
            print(f"  Life: 0x{life_addr:X} (HP={hp}/{max_hp}, ES={es}/{max_es})")
            print(f"  XYZ:  0x{xyz_addr:X} {xyz}")

print(f"\nOld Cached Addr check:")
old_xyz_addr = 0x2AF791A0550
old_life_addr = 0x2AF791A03E8
print(f"Old Life (0x{old_life_addr:X}): HP={read_u32(old_life_addr)}/{read_u32(old_life_addr+4)}, ES={read_u32(old_life_addr+0x0C)}/{read_u32(old_life_addr+0x10)}")
print(f"Old XYZ  (0x{old_xyz_addr:X}): {read_f32_3(old_xyz_addr)}")

kernel32.CloseHandle(h_proc)
