import sys, struct, ctypes
from ctypes import wintypes
import psutil

kernel32 = ctypes.windll.kernel32
advapi32 = ctypes.windll.advapi32

def enable_debug():
    hToken = wintypes.HANDLE()
    if advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0020 | 0x0008, ctypes.byref(hToken)):
        luid = wintypes.LARGE_INTEGER()
        if advapi32.LookupPrivilegeValueW(None, "SeDebugPrivilege", ctypes.byref(luid)):
            class TP(ctypes.Structure):
                _fields_ = [('Count', wintypes.DWORD), ('Luid', wintypes.LARGE_INTEGER), ('Attr', wintypes.DWORD)]
            tp = TP(1, luid, 2)
            advapi32.AdjustTokenPrivileges(hToken, False, ctypes.byref(tp), ctypes.sizeof(tp), None, None)
        kernel32.CloseHandle(hToken)
enable_debug()

log_path = r"c:\Projects\JX\logs\find_true_level.txt"
lines = []

# Target: BoCuaCacCon (PID 45384)
pid = 45384
hProc = kernel32.OpenProcess(0x0010 | 0x0400 | 0x0008, False, pid)
if not hProc:
    lines.append(f"Failed to open PID {pid}")
else:
    # 1. Inspect around 0x006DEC00 - 0x006DF000
    base = 0x006DEC00
    size = 0x1000
    buf = (ctypes.c_char * size)()
    bytesRead = ctypes.c_size_t()
    if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(base), buf, size, ctypes.byref(bytesRead)):
        data = bytes(buf)[:bytesRead.value]
        lines.append("--- DUMPING 0x006DEC00 - 0x006DF000 (Looking for level >= 80) ---")
        for off in range(0, len(data) - 4, 2):
            v_short = struct.unpack_from('<H', data, off)[0]
            v_int = struct.unpack_from('<I', data, off)[0] if off <= len(data) - 4 else 0
            if 80 <= v_short <= 150:
                lines.append(f"  [SHORT] 0x{base + off:08X} (offset +0x{off:X}): {v_short}")
            if 80 <= v_int <= 150 and v_int != v_short:
                lines.append(f"  [INT] 0x{base + off:08X} (offset +0x{off:X}): {v_int}")

    # 2. Inspect around the player struct in heap: 0x104007C0
    lines.append("\n--- INSPECTING PLAYER STRUCT AROUND 0x104007C0 ---")
    stamina_base = 0x104007C0
    struct_buf = (ctypes.c_char * 0x1000)()
    # Read from -0x200 to +0x800
    read_addr = stamina_base - 0x200
    if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(read_addr), struct_buf, 0x1000, ctypes.byref(bytesRead)):
        sdata = bytes(struct_buf)[:bytesRead.value]
        for off in range(0, len(sdata) - 4, 2):
            v_short = struct.unpack_from('<H', sdata, off)[0]
            v_int = struct.unpack_from('<I', sdata, off)[0] if off <= len(sdata) - 4 else 0
            rel_off = off - 0x200
            addr = stamina_base + rel_off
            if 80 <= v_short <= 150:
                lines.append(f"  [STRUCT SHORT] 0x{addr:08X} (Base{rel_off:+d}): {v_short}")
            if 80 <= v_int <= 150 and v_int != v_short:
                lines.append(f"  [STRUCT INT] 0x{addr:08X} (Base{rel_off:+d}): {v_int}")

    # 3. Check all 4 characters' memory for name and level
    kernel32.CloseHandle(hProc)

with open(log_path, "w", encoding="utf-8") as f:
    f.write("\n".join(lines))
