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

sys.stdout.reconfigure(encoding='utf-8')
kernel32 = ctypes.windll.kernel32
advapi32 = ctypes.windll.advapi32

def enable_debug():
    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)
enable_debug()

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

# The 4 accounts
TARGET_ACCOUNTS = {
    "BoCuaCacCon": {"sect": "Cái Bang", "expected_hp": 846, "expected_lvl": 75},
    "ConCuaBoMe":  {"sect": "Thiếu Lâm", "expected_hp": 612, "expected_lvl": 49},
    "ConGailCung": {"sect": "Ngũ Độc",  "expected_hp": 540, "expected_lvl": 46},
    "MeCuaCacCon": {"sect": "Nga My",   "expected_hp": 548, "expected_lvl": 47},
}

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

print(f"=== BẮT ĐẦU ĐỌC RAM TRỰC TIẾP CÁC CHỈ SỐ NHÂN VẬT (PIDs: {vggame_pids}) ===")

all_char_telemetry = []

for pid in vggame_pids:
    hProc = kernel32.OpenProcess(0x0010 | 0x0400, False, pid)
    if not hProc:
        print(f"PID {pid}: Không thể OpenProcess (Error {kernel32.GetLastError()})")
        continue

    # 1. Đọc tên nhân vật và Cấp từ Static Base 0x006DEC44
    buf_name = (ctypes.c_char * 64)()
    kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(0x006DEC44), buf_name, 64, None)
    raw_name = bytes(buf_name)[:32].split(b'\0')[0].decode('ascii', errors='ignore').strip()
    
    # Đọc level tại 0x006DEC66 (unsigned short)
    buf_lvl = (ctypes.c_char * 4)()
    kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(0x006DEC66), buf_lvl, 4, None)
    lvl_val = struct.unpack('<H', bytes(buf_lvl)[:2])[0]
    
    char_info = {
        "pid": pid,
        "name": raw_name,
        "level": lvl_val,
        "sect": TARGET_ACCOUNTS.get(raw_name, {}).get("sect", "Không rõ"),
        "hp_cur": 0,
        "hp_max": 0,
        "stamina": 0,
        "mana": 0,
        "mana_max": 0,
        "ram_name_addr": "0x006DEC44",
        "ram_level_addr": "0x006DEC66",
        "ram_hp_addr": "0x00000000",
        "status": "In-Game" if len(raw_name) > 0 else "Chờ log"
    }

    # 2. Quét tìm struct sinh lực (HP, MaxHP, Stamina) trong heap
    exp_hp = TARGET_ACCOUNTS.get(raw_name, {}).get("expected_hp", 0)
    if exp_hp > 0:
        pat_hp = struct.pack('<ii', exp_hp, exp_hp)
        mbi = MEMORY_BASIC_INFORMATION()
        addr = 0
        while kernel32.VirtualQueryEx(hProc, ctypes.c_void_p(addr), ctypes.byref(mbi), ctypes.sizeof(mbi)):
            if mbi.State == 0x1000 and (mbi.Protect & 0xEE) != 0 and (mbi.Protect & 0x01) == 0:
                size = min(mbi.RegionSize, 1024 * 1024 * 8)
                buf = (ctypes.c_char * size)()
                bytesRead = ctypes.c_size_t()
                if kernel32.ReadProcessMemory(hProc, ctypes.c_void_p(addr), buf, size, ctypes.byref(bytesRead)):
                    data = bytes(buf)[:bytesRead.value]
                    idx = data.find(pat_hp)
                    if idx != -1:
                        hp_addr = addr + idx
                        cur_hp = struct.unpack_from('<i', data, idx)[0]
                        max_hp = struct.unpack_from('<i', data, idx + 4)[0]
                        # Stamina is at offset -8
                        sta = struct.unpack_from('<i', data, idx - 8)[0] if idx >= 8 else 0
                        
                        char_info["hp_cur"] = cur_hp
                        char_info["hp_max"] = max_hp
                        char_info["stamina"] = sta
                        char_info["ram_hp_addr"] = f"0x{hp_addr:08X}"
                        break
            addr += mbi.RegionSize
            if addr >= 0x7FFFFFFF: break

    kernel32.CloseHandle(hProc)
    all_char_telemetry.append(char_info)
    print(f"\n[PID {pid}] NV: '{char_info['name']}' ({char_info['sect']})")
    print(f"   * Cấp: {char_info['level']} (RAM: {char_info['ram_level_addr']})")
    print(f"   * Sinh Lực: {char_info['hp_cur']}/{char_info['hp_max']} (RAM: {char_info['ram_hp_addr']})")
    print(f"   * Thể Lực: {char_info['stamina']}")

out_file = r"C:\Projects\JX\logs\live_ram_stats_summary.json"
with open(out_file, "w", encoding="utf-8") as f:
    json.dump(all_char_telemetry, f, ensure_ascii=False, indent=2)

print(f"\n=== TỔNG KẾT ĐỌC RAM THÀNH CÔNG CHO {len(all_char_telemetry)} NHÂN VẬT ===")
print(f"File kết quả: {out_file}")
