Files
MrMahoandClaude Opus 5.5 35f2d1330d Erste Version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:26:23 +01:00

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);
}
}
}