import sys, struct, ctypes
from ctypes import wintypes
import psutil

def run():
    kernel32 = ctypes.windll.kernel32
    advapi32 = ctypes.windll.advapi32

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

    procs = []
    for p in psutil.process_iter(['pid', 'name']):
        if (p.info['name'] or '').lower() in ('vggame.exe', 'game.exe'):
            procs.append(p.info['pid'])

    pat_sig = struct.pack('<iii', 43, 100, 0)
    bytesRead = ctypes.c_size_t()

    output = []
    output.append(f"=== BẢNG CHỈ SỐ NHÂN VẬT THỰC ĐỊA (LIVE RAM TELEMETRY) ===")
    output.append(f"{'PID':<7} | {'NHÂN VẬT':<14} | {'CẤP':<4} | {'SINH LỰC (HP)':<14} | {'THỂ LỰC (STAMINA)':<18} | {'NỘI LỰC (MANA)':<14} | {'ĐỊA CHỈ STRUCT':<12}")
    output.append("-" * 95)

    for pid in sorted(procs):
        hProc = kernel32.OpenProcess(0x0010 | 0x0400, False, pid)
        if not hProc:
            output.append(f"{pid:<7} | Access Denied")
            continue

        # 1. Static Name (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').strip()

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

        # 3. Dynamic Player Stat Struct via Signature Scan
        cur_addr = 0
        mbi = MEMORY_BASIC_INFORMATION()
        found = False

        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
                        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]
                                sta = struct.unpack_from('<i', data, pos + 20)[0]
                                max_sta = struct.unpack_from('<i', data, pos + 24)[0]
                                s_addr = cur_addr + pos + 20

                                output.append(f"{pid:<7} | {name:<14} | {lvl:<4} | {hp:<14} | {sta}/{max_sta:<14} | {mana:<14} | 0x{s_addr:08X}")
                                found = True
                                break
                        pos += 4
                    if found: break
            cur_addr += mbi.RegionSize
            if cur_addr >= 0x7FFFFFFF: break

        if not found:
            output.append(f"{pid:<7} | {name:<14} | {lvl:<4} | {'Chờ tải':<14} | {'Chờ tải':<18} | {'Chờ tải':<14} | N/A")

        kernel32.CloseHandle(hProc)

    res_str = "\n".join(output)
    print(res_str)
    with open(r"C:\Projects\JX\logs\live_player_stats_table.txt", "w", encoding="utf-8") as f:
        f.write(res_str)

if __name__ == '__main__':
    run()
