using System.Collections.Generic; namespace BSO { /// /// Klassenname eines C++-Objekts aus den MSVC-Typinfos (vtable[-1] -> CompleteObjectLocator -> TypeDescriptor). /// So erkennen wir Spielobjekte (BaseVehicle, CarVehicle ...), ohne ihren Aufbau zu kennen. /// public static unsafe class Rtti { static readonly Dictionary byVtable = new(); /// Klassenname oder null, wenn obj kein gueltiges C++-Objekt ist. 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); } /// ".?AVFoo@Bar@@" -> "Bar::Foo". 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); } } }