import sys, struct, ctypes

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

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

    kernel32 = ctypes.windll.kernel32
    advapi32 = ctypes.windll.advapi32
    from ctypes import wintypes

    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)

    pids = [32572, 31164, 30076, 12768]
    bytesRead = ctypes.c_size_t()

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

        # Get name from 0x006DEC44
        nbuf = (ctypes.c_char * 32)()
        kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(0x006DEC44), nbuf, 32, ctypes.byref(bytesRead))
        name = bytes(nbuf).split(b'\x00')[0].decode('latin1', errors='ignore')

        # Read level from 0x006DEC66
        lbuf = (ctypes.c_short * 1)()
        kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(0x006DEC66), lbuf, 2, ctypes.byref(bytesRead))
        lvl = lbuf[0]

        log(f"\n=======================================================")
        log(f"PID {pid} | Character: '{name}' | Level: {lvl}")
        log(f"=======================================================")

        # Dump 0x00734920 to 0x00734990
        base = 0x00734920
        size = 0x80
        buf = (ctypes.c_char * size)()
        if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(base), buf, size, ctypes.byref(bytesRead)):
            data = bytes(buf)[:bytesRead.value]
            # Hex dump + uint16 + uint32 interpretation
            log("Address      Hex Bytes             u16 (le)     i32 (le)")
            log("---------------------------------------------------------")
            for offset in range(0, len(data), 2):
                addr = base + offset
                b2 = data[offset:offset+2]
                u16 = struct.unpack('<H', b2)[0]
                i16 = struct.unpack('<h', b2)[0]
                
                i32_str = ""
                if offset % 4 == 0 and offset + 4 <= len(data):
                    i32 = struct.unpack_from('<i', data, offset)[0]
                    i32_str = f"i32: {i32:<10}"

                hex_bytes = " ".join(f"{b:02X}" for b in b2)
                log(f"0x{addr:08X}   {hex_bytes:<10}  u16: {u16:<6} (i16: {i16:<6})  {i32_str}")

        kernel32.CloseHandle(hProc)

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

if __name__ == '__main__':
    run()
