import sys, struct, ctypes, json
from ctypes import wintypes

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

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

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

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

    # Let's inspect PID 32572 (BoCuaCacCon)
    # Expected from user:
    # Thể lực (Stamina): 846 / 846
    # Sinh lực (HP): 3215 / 3215
    # Nội lực (Mana): 924 / 924 (or ~853 / 924)
    pid = 32572
    hProc = kernel32.OpenProcess(0x0010 | 0x0400, False, pid)
    if not hProc:
        log(f"Cannot open PID {pid}")
        return

    bytesRead = ctypes.c_size_t()

    # Region 1: 0x10B786A0 to 0x10B787E0 (Stamina = 846 at 0x10B786D4, 0x10B786D8)
    base1 = 0x10B786A0
    size1 = 0x140
    buf1 = (ctypes.c_char * size1)()
    if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(base1), buf1, size1, ctypes.byref(bytesRead)):
        d1 = bytes(buf1)[:bytesRead.value]
        log("=== REGION 1: 0x10B786A0 (Around Stamina 0x10B786D4) ===")
        for off in range(0, len(d1) - 4, 4):
            addr = base1 + off
            v_i32 = struct.unpack_from('<i', d1, off)[0]
            v_u16 = struct.unpack_from('<H', d1, off)[0]
            tag = ""
            if v_i32 == 3215: tag = " <=== HP: 3215"
            elif v_i32 == 846: tag = " <=== STAMINA: 846"
            elif v_i32 == 924: tag = " <=== MANA: 924"
            elif 700 <= v_i32 <= 950: tag = f" <=== RANGE 700-950 ({v_i32})"
            elif v_i32 == 75: tag = " <=== LEVEL: 75"
            log(f"  0x{addr:08X} (+0x{off:03X} / rel 0x10B786BC: {addr-0x10B786BC:+4d}): i32 = {v_i32:<8} | u16 = {v_u16:<5} {tag}")

    # Region 2: 0x10B88940 to 0x10B88A70 (Contains HP 3215 at 0x10B88974 and 924 at 0x10B8897C)
    base2 = 0x10B88940
    size2 = 0x140
    buf2 = (ctypes.c_char * size2)()
    if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(base2), buf2, size2, ctypes.byref(bytesRead)):
        d2 = bytes(buf2)[:bytesRead.value]
        log("\n=== REGION 2: 0x10B88940 (Around 0x10B88974) ===")
        for off in range(0, len(d2) - 4, 4):
            addr = base2 + off
            v_i32 = struct.unpack_from('<i', d2, off)[0]
            v_u16 = struct.unpack_from('<H', d2, off)[0]
            tag = ""
            if v_i32 == 3215: tag = " <=== HP: 3215"
            elif v_i32 == 846: tag = " <=== STAMINA: 846"
            elif v_i32 == 924: tag = " <=== MANA: 924"
            elif 700 <= v_i32 <= 950: tag = f" <=== RANGE 700-950 ({v_i32})"
            elif v_i32 == 75: tag = " <=== LEVEL: 75"
            log(f"  0x{addr:08X} (+0x{off:03X} / rel 0x10B88974: {addr-0x10B88974:+4d}): i32 = {v_i32:<8} | u16 = {v_u16:<5} {tag}")

    kernel32.CloseHandle(hProc)

    # Now let's find the POINTER to Region 1 and Region 2 in STATIC memory
    log("\n=== TÌM KIẾM CON TRỎ TĨNH TRỎ ĐẾN REGION 1 VÀ REGION 2 ===")
    hProc = kernel32.OpenProcess(0x0010 | 0x0400, False, pid)
    # Search static range 0x00400000 - 0x00900000 for pointers to 0x10B7xxxx and 0x10B8xxxx
    # Let's search pointers within range [0x10B78000, 0x10B79000] and [0x10B88000, 0x10B89000]
    static_base = 0x00400000
    static_size = 0x00500000 # 5MB
    s_buf = (ctypes.c_char * static_size)()
    if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(static_base), s_buf, static_size, 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_base + off
            if 0x10B78600 <= ptr <= 0x10B78800:
                log(f"  [STATIC POINTER 1] 0x{s_addr:08X} -> trỏ tới 0x{ptr:08X} (offset to 0x10B786BC: {ptr-0x10B786BC:+d})")
            if 0x10B88900 <= ptr <= 0x10B88B00:
                log(f"  [STATIC POINTER 2] 0x{s_addr:08X} -> trỏ tới 0x{ptr:08X} (offset to 0x10B88974: {ptr-0x10B88974:+d})")
            # Also check if ptr trỏ tới exact base
            if ptr == 0x10B786BC or ptr == 0x10B786D4 or ptr == 0x10B88974:
                log(f"  *** [EXACT STATIC POINTER] 0x{s_addr:08X} -> trỏ trực tiếp 0x{ptr:08X} ***")

    kernel32.CloseHandle(hProc)

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

if __name__ == '__main__':
    run()
