48 lines
2.0 KiB
C#
48 lines
2.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|