import sys, struct, ctypes
from ctypes import wintypes

output_file = r"C:\Projects\JX\logs\dump_4_chars_struct_offsets.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)

    targets = [
        {"pid": 32572, "name": "BoCuaCacCon", "stamina_addr": 0x10B786D4},
        {"pid": 30076, "name": "MeCuaCacCon", "stamina_addr": 0x10BC65B4},
        {"pid": 31164, "name": "ConCuaBoMe",  "stamina_addr": 0x1092343C},
        {"pid": 12768, "name": "ConGailCung", "stamina_addr": 0x00A228C0},
    ]

    bytesRead = ctypes.c_size_t()

    for t in targets:
        pid = t["pid"]
        name = t["name"]
        s_addr = t["stamina_addr"]

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

        base = s_addr - 48
        size = 128
        buf = (ctypes.c_char * size)()

        log(f"\n================================================================================")
        log(f"PID {pid} | {name} | Base stamina_addr: 0x{s_addr:08X}")
        log(f"================================================================================")

        if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(base), buf, size, ctypes.byref(bytesRead)):
            data = bytes(buf)[:bytesRead.value]
            for off in range(0, len(data) - 4, 4):
                cur_addr = base + off
                rel = cur_addr - s_addr
                val_i32 = struct.unpack_from('<i', data, off)[0]
                val_u16 = struct.unpack_from('<H', data, off)[0]
                
                log(f"  0x{cur_addr:08X} [rel {rel:+4d}]: i32 = {val_i32:<10} | u16 = {val_u16:<6}")
        else:
            log(f"Failed to read at 0x{base:08X} (Error {kernel32.GetLastError()})")

        kernel32.CloseHandle(hProc)

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

if __name__ == '__main__':
    run()
