import sys, struct, ctypes
from ctypes import wintypes

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

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

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

    # Pattern: 43, 100, 0
    # In bytes:
    pat_sig = struct.pack('<iii', 43, 100, 0)

    cur_addr = 0
    mbi = MEMORY_BASIC_INFORMATION()
    bytesRead = ctypes.c_size_t()

    log(f"Scanning PID {pid} for Player Stat Struct with signature [43, 100, 0]...")

    while kernel32.VirtualQueryEx(hProc, ctypes.c_void_p(cur_addr), ctypes.byref(mbi), ctypes.sizeof(mbi)):
        if mbi.State == 0x1000 and (mbi.Protect & 0xEE) != 0 and (mbi.Protect & 0x01) == 0:
            rsize = min(mbi.RegionSize, 1024 * 1024 * 8)
            buf = (ctypes.c_char * rsize)()
            if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(cur_addr), buf, rsize, ctypes.byref(bytesRead)):
                data = bytes(buf)[:bytesRead.value]
                pos = 0
                while True:
                    pos = data.find(pat_sig, pos)
                    if pos == -1: break
                    
                    # pos corresponds to rel -20
                    # hp is at pos - 4
                    # mana is at pos + 12
                    # 39 is at pos + 16
                    # stamina is at pos + 20
                    # max_stamina is at pos + 24
                    if pos >= 4 and pos + 28 <= len(data):
                        val_39 = struct.unpack_from('<i', data, pos + 16)[0]
                        if val_39 == 39:
                            hp = struct.unpack_from('<i', data, pos - 4)[0]
                            mana = struct.unpack_from('<i', data, pos + 12)[0]
                            stamina = struct.unpack_from('<i', data, pos + 20)[0]
                            max_sta = struct.unpack_from('<i', data, pos + 24)[0]
                            sta_addr = cur_addr + pos + 20
                            log(f"  [KHỚP HOÀN HẢO CHO CONGAILCUNG]!")
                            log(f"    * Địa chỉ Stamina : 0x{sta_addr:08X}")
                            log(f"    * Sinh lực (HP)   : {hp}")
                            log(f"    * Thể lực (Stamina): {stamina} / {max_sta}")
                            log(f"    * Nội lực (Mana)  : {mana}")
                    pos += 4
        cur_addr += mbi.RegionSize
        if cur_addr >= 0x7FFFFFFF: break

    kernel32.CloseHandle(hProc)

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

if __name__ == '__main__':
    run()
