using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing;
using JXHelper.Platform;
namespace JXHelper.Core;
///
/// Bộ định danh tên nhân vật thông minh (Character Name Resolver).
/// Hỗ trợ quét bộ nhớ tiến trình (Process Memory Scan) và ánh xạ từ UserData của AutoVLBS.
///
public static class CharacterNameResolver
{
private static readonly string[] PossibleVlbsPaths = new[]
{
@"C:\JXVCTK\JxVoCongTruyenKy\_AutoVLBS\VLBS19\UserData",
@"C:\JXVCTK\JxVoCongTruyenKy\_AutoVLBS\VLBS\UserData",
@"C:\AutoVLBS\UserData",
};
private static bool _privilegeEnabled = false;
///
/// Kích hoạt SeDebugPrivilege để cho phép đọc bộ nhớ tiến trình game khi chạy quyền Administrator.
///
public static void EnableDebugPrivilege()
{
if (_privilegeEnabled) return;
try
{
if (NativeMethods.OpenProcessToken(Process.GetCurrentProcess().Handle, NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY, out var hToken))
{
if (NativeMethods.LookupPrivilegeValue(null, "SeDebugPrivilege", out var luid))
{
var tp = new NativeMethods.TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Luid = luid,
Attributes = NativeMethods.SE_PRIVILEGE_ENABLED
};
NativeMethods.AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
}
NativeMethods.CloseHandle(hToken);
}
_privilegeEnabled = true;
}
catch
{
// Bỏ qua nếu môi trường không cho phép
}
}
///
/// Lấy danh sách tất cả tên nhân vật đã biết từ SSoT accounts.json và UserData của AutoVLBS.
///
public static List GetKnownCharacterNames()
{
var names = new List();
// 1. SSoT: Đọc trực tiếp từ config/accounts.json
try
{
var accounts = Config.AccountStorage.LoadAccounts();
foreach (var a in accounts)
{
if (!string.IsNullOrWhiteSpace(a.CharacterName) && !names.Contains(a.CharacterName.Trim(), StringComparer.OrdinalIgnoreCase))
{
names.Add(a.CharacterName.Trim());
}
}
}
catch {}
// 2. Bổ sung từ UserData của AutoVLBS nếu có
foreach (var path in PossibleVlbsPaths)
{
if (Directory.Exists(path))
{
foreach (var file in Directory.GetFiles(path, "*.cfg"))
{
var name = Path.GetFileNameWithoutExtension(file).Trim();
if (!string.IsNullOrEmpty(name) && !names.Contains(name, StringComparer.OrdinalIgnoreCase))
{
names.Add(name);
}
}
}
}
return names;
}
///
/// Địa chỉ tĩnh cố định lưu tên và cấp độ của nhân vật người chơi cục bộ (Local Player) trong vggame.exe.
/// INV-STATIC-RAM-LOCAL-PLAYER: Đọc siêu tốc 0.001ms, không cần quét 2GB RAM.
///
public const long StaticPlayerNameAddress = 0x006DEC44;
public const long StaticPlayerLevelAddress = 0x006DEC66;
public const long StaticPlayerMapIdAddress = 0x006966CC;
public const long StaticPlayerSecondaryAddress = 0x006DF3B0;
///
/// Quét bộ nhớ tiến trình game vggame.exe để dò tìm chính xác tên nhân vật đang điều khiển.
///
public static string? ScanProcessForKnownName(int pid, List candidates)
{
if (candidates.Count == 0) return null;
EnableDebugPrivilege();
IntPtr hProcess = NativeMethods.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.PROCESS_VM_READ, false, pid);
if (hProcess == IntPtr.Zero) return null;
try
{
// 1. FAST PATH (INV-STATIC-RAM-LOCAL-PLAYER): Đọc trực tiếp địa chỉ tĩnh 0x006DEC44 của Local Player
byte[] nameBuf = new byte[32];
if (NativeMethods.ReadProcessMemory(hProcess, (IntPtr)StaticPlayerNameAddress, nameBuf, (IntPtr)nameBuf.Length, out var readBytes) && readBytes.ToInt64() > 0)
{
string rawName = Encoding.ASCII.GetString(nameBuf).TrimEnd('\0').Trim();
if (!string.IsNullOrEmpty(rawName))
{
var match = candidates.FirstOrDefault(c => string.Equals(c, rawName, StringComparison.OrdinalIgnoreCase));
if (match != null) return match;
if (rawName.Length >= 3 && rawName.All(c => char.IsLetterOrDigit(c) || c == '_'))
{
return rawName;
}
}
}
// 2. SECONDARY FAST PATH: Đọc địa chỉ tĩnh thứ hai 0x006DF3B0
if (NativeMethods.ReadProcessMemory(hProcess, (IntPtr)StaticPlayerSecondaryAddress, nameBuf, (IntPtr)nameBuf.Length, out readBytes) && readBytes.ToInt64() > 0)
{
string rawName = Encoding.ASCII.GetString(nameBuf).TrimEnd('\0').Trim();
if (!string.IsNullOrEmpty(rawName))
{
var match = candidates.FirstOrDefault(c => string.Equals(c, rawName, StringComparison.OrdinalIgnoreCase));
if (match != null) return match;
}
}
}
catch
{
// Bỏ qua lỗi scan
}
finally
{
NativeMethods.CloseHandle(hProcess);
}
return null;
}
///
/// Định danh trạng thái nhân vật và cửa sổ game:
/// - Quét bộ nhớ thực tế (Process Memory Scan) qua địa chỉ tĩnh 0x006DEC44.
/// - Nếu tìm thấy tên nhân vật trong RAM -> Đã đăng nhập vào game (IsInGame = true).
/// - Nếu đọc RAM bị chặn (do chưa Admin) -> Kiểm tra Pixel HUD góc máu/mana để xác nhận trạng thái in-game.
/// - Nếu không tìm thấy tên và không có HUD -> Cửa sổ rỗi / chưa đăng nhập (IsInGame = false, CharacterName = "").
///
public static (string CharacterName, bool IsInGame) ResolveCharacterState(
int pid,
int windowIndex,
List knownNames,
HashSet assignedNames,
IntPtr hWnd = default)
{
var scannedName = ScanProcessForKnownName(pid, knownNames);
if (!string.IsNullOrEmpty(scannedName) && !assignedNames.Contains(scannedName))
{
assignedNames.Add(scannedName);
return (scannedName, true);
}
// INV-DUAL-FACTOR-INGAME: Fallback kiểm tra Pixel HUD nếu không đọc được RAM
if (hWnd != IntPtr.Zero && CheckWindowIsInGameHud(hWnd))
{
// Cửa sổ đang hiển thị thế giới game thật, gán tên từ danh sách tài khoản đã biết
string candidate = windowIndex < knownNames.Count ? knownNames[windowIndex] : $"Char_{pid}";
if (!assignedNames.Contains(candidate))
{
assignedNames.Add(candidate);
return (candidate, true);
}
return (candidate, true);
}
// Không tìm thấy tên nhân vật trong RAM và không có HUD in-game -> Cửa sổ rỗi (Chờ log / Màn hình ngoài)
return (string.Empty, false);
}
///
/// Nhận diện xem cửa sổ game đã vào thế giới chưa bằng Pixel HUD (Không cần quyền Admin).
///
public static bool CheckWindowIsInGameHud(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero || !NativeMethods.IsWindow(hWnd)) return false;
if (!NativeMethods.GetClientRect(hWnd, out var rect)) return false;
int w = rect.Width;
int h = rect.Height;
if (w < 640 || h < 480) return false;
try
{
// Chụp 200x100 góc trên bên trái (nơi chứa bình máu / mana của Võ Lâm 1)
using var bmp = new Bitmap(200, 100);
using (var g = Graphics.FromImage(bmp))
{
IntPtr hdc = g.GetHdc();
try
{
if (NativeMethods.PrintWindow(hWnd, hdc, 0))
{
int redCount = 0;
int blueCount = 0;
for (int x = 30; x < 150; x += 10)
{
for (int y = 20; y < 60; y += 10)
{
var color = bmp.GetPixel(x, y);
if (color.R > 120 && color.R > color.G * 1.5 && color.R > color.B * 1.5)
redCount++;
if (color.B > 120 && color.B > color.R * 1.3 && color.B > color.G * 1.3)
blueCount++;
}
}
if (redCount >= 1 || blueCount >= 1) return true;
}
}
finally
{
g.ReleaseHdc(hdc);
}
}
}
catch
{
// Bỏ qua lỗi GDI
}
return false;
}
///
/// Đọc cấp độ nhân vật từ địa chỉ RAM tĩnh 0x006DEC66.
///
public static int ReadCharacterLevel(int pid)
{
EnableDebugPrivilege();
IntPtr hProcess = NativeMethods.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.PROCESS_VM_READ, false, pid);
if (hProcess == IntPtr.Zero) return 0;
try
{
byte[] buf = new byte[2];
if (NativeMethods.ReadProcessMemory(hProcess, (IntPtr)StaticPlayerLevelAddress, buf, (IntPtr)2, out var read) && read.ToInt64() >= 2)
{
return BitConverter.ToInt16(buf, 0);
}
}
finally
{
NativeMethods.CloseHandle(hProcess);
}
return 0;
}
///
/// Định danh tên nhân vật từ bộ nhớ RAM thực tế.
///
public static string ResolveCharacterName(int pid, int windowIndex, List knownNames, HashSet assignedNames, IntPtr hWnd = default)
{
var (charName, _) = ResolveCharacterState(pid, windowIndex, knownNames, assignedNames, hWnd);
return charName;
}
}