@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BSO
|
||||
{
|
||||
/// <summary>
|
||||
/// Klassenname eines C++-Objekts aus den MSVC-Typinfos (vtable[-1] -> CompleteObjectLocator -> TypeDescriptor).
|
||||
/// So erkennen wir Spielobjekte (BaseVehicle, CarVehicle ...), ohne ihren Aufbau zu kennen.
|
||||
/// </summary>
|
||||
public static unsafe class Rtti
|
||||
{
|
||||
static readonly Dictionary<nint, string> byVtable = new();
|
||||
|
||||
/// <summary>Klassenname oder null, wenn obj kein gueltiges C++-Objekt ist.</summary>
|
||||
public static string ClassName(nint obj)
|
||||
{
|
||||
if (!Mem.TryReadPtr(obj, out nint vtable) || !Mem.IsImage(vtable)) return null;
|
||||
if (byVtable.TryGetValue(vtable, out string name)) return name;
|
||||
name = Resolve(vtable);
|
||||
byVtable[vtable] = name;
|
||||
return name;
|
||||
}
|
||||
|
||||
static string Resolve(nint vtable)
|
||||
{
|
||||
if (!Mem.TryReadPtr(vtable - 8, out nint col) || !Mem.IsImage(col) || !Mem.IsReadable(col, 24)) return null;
|
||||
// x64: Signatur 1, Adressen als RVA relativ zum Modul; pSelf (RVA des Locators selbst) liefert die Modulbasis
|
||||
if (*(int*)col != 1) return null;
|
||||
uint typeRva = *(uint*)(col + 12);
|
||||
uint selfRva = *(uint*)(col + 20);
|
||||
nint typeDescriptor = col - (nint)selfRva + (nint)typeRva;
|
||||
string raw = Mem.ReadAscii(typeDescriptor + 16, 256); // ".?AVBaseVehicle@@"
|
||||
return Demangle(raw);
|
||||
}
|
||||
|
||||
/// <summary>".?AVFoo@Bar@@" -> "Bar::Foo".</summary>
|
||||
static string Demangle(string raw)
|
||||
{
|
||||
if (raw.Length < 6 || !raw.StartsWith(".?A")) return null;
|
||||
string body = raw.Substring(4);
|
||||
if (body.EndsWith("@@")) body = body.Substring(0, body.Length - 2);
|
||||
if (body.Contains('?')) return body; // Templates usw. roh lassen
|
||||
string[] parts = body.Split('@');
|
||||
System.Array.Reverse(parts);
|
||||
return string.Join("::", parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user