import sys, struct, ctypes
from ctypes import wintypes

output_file = r"C:\Projects\JX\logs\find_player_base_ptr.txt"

def run():
    lines = []
    def log(msg):
        print(msg)
        lines.append(str(msg))

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

    # Enable SeDebugPrivilege
    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)

    pid = 32572
    hProc = kernel32.OpenProcess(0x0010 | 0x0400, False, pid)
    if not hProc:
        log(f"Cannot open PID {pid}")
        return

    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),
        ]

    mbi1 = MEMORY_BASIC_INFORMATION()
    kernel32.VirtualQueryEx(hProc, ctypes.c_void_p(0x10B786BC), ctypes.byref(mbi1), ctypes.sizeof(mbi1))
    log(f"Region 1 (0x10B786BC): Base=0x{mbi1.BaseAddress or 0:08X}, AllocBase=0x{mbi1.AllocationBase or 0:08X}, Size=0x{mbi1.RegionSize:X}")

    mbi2 = MEMORY_BASIC_INFORMATION()
    kernel32.VirtualQueryEx(hProc, ctypes.c_void_p(0x10B88974), ctypes.byref(mbi2), ctypes.sizeof(mbi2))
    log(f"Region 2 (0x10B88974): Base=0x{mbi2.BaseAddress or 0:08X}, AllocBase=0x{mbi2.AllocationBase or 0:08X}, Size=0x{mbi2.RegionSize:X}")

    # Now let's scan all static memory 0x00400000 to 0x00A00000 for any DWORD value
    # that falls within [AllocBase, AllocBase + RegionSize] for Region 1 or Region 2
    r1_start = mbi1.AllocationBase or 0x10B70000
    r1_end = r1_start + (mbi1.RegionSize if mbi1.AllocationBase else 0x100000)

    log(f"Searching static memory for pointers in range [0x{r1_start:08X}, 0x{r1_end:08X}]...")

    static_cur = 0x00400000
    mbi_s = MEMORY_BASIC_INFORMATION()
    bytesRead = ctypes.c_size_t()

    found_ptrs = []

    while kernel32.VirtualQueryEx(hProc, ctypes.c_void_p(static_cur), ctypes.byref(mbi_s), ctypes.sizeof(mbi_s)):
        if static_cur >= 0x00A00000: break
        if mbi_s.State == 0x1000 and (mbi_s.Protect & 0xEE) != 0 and (mbi_s.Protect & 0x01) == 0:
            s_buf = (ctypes.c_char * mbi_s.RegionSize)()
            if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(static_cur), s_buf, mbi_s.RegionSize, ctypes.byref(bytesRead)):
                s_data = bytes(s_buf)[:bytesRead.value]
                for off in range(0, len(s_data) - 4, 4):
                    ptr = struct.unpack_from('<I', s_data, off)[0]
                    s_addr = static_cur + off
                    if r1_start <= ptr < r1_end:
                        rel1 = ptr - 0x10B786BC
                        log(f"  [STATIC PTR] 0x{s_addr:08X} -> 0x{ptr:08X} (rel to HP 0x10B786BC: {rel1:+d})")
                        found_ptrs.append((s_addr, ptr))
        static_cur += mbi_s.RegionSize

    log(f"Total static pointers pointing into player region: {len(found_ptrs)}")

    kernel32.CloseHandle(hProc)

    with open(output_file, 'w', encoding='utf-8') as f:
        f.write("\n".join(lines))
    print("Done finding base ptr.")

if __name__ == '__main__':
    run()
