Originally Posted by
axlrose
Just interested: how do you debug and test all your stuff? I made a base AbstractProcessMemory class in which all existing readBytes() member functions are abstract and from which your ProcessMemory then derives from. I dumped the entire D3 2Gb process memory with the code you already had in place (just added the IMAGE flag to dump the executable memory block for the imagebase offset pointers (the rest is in the heap pages), all protected and unallocated blocks I just initialise with zeros). I then implemented another FileProcessMemory also derived from your ProcessMemory which uses memory mapped files to work on off-line :-) From this I can make snapshots and test different scenarios without even starting the game. The engine is then created with a new constructor (not Create() but CreateFromFile(string dumpFilename)) All else stayed the same and worked like a charm...
Nice, that's actually the reason I implemented the dump method, to do exactly like that
But then I made a super clever GUI instead where I can use: (don't mind the weird IEnumerable code)
Code:
var snoGroupIds = (SnoGroupId[])Enum.GetValues(typeof(SnoGroupId));
foreach (var snoGroupId in snoGroupIds)
{
int id = (int)snoGroupId;
AddView("SNO." + snoGroupId.ToString(), new ReflectionView(() => Engine.Current.SnoGroupsByCode[id]));
foreach (var pi in typeof(SnoGroup).GetProperties().Where(a => a.PropertyType.IsMemoryObjectType()))
{
var property = pi;
AddView("SNO." + snoGroupId.ToString() + "." + property.Name, new ReflectionView(() => property.GetValue(Engine.Current.SnoGroupsByCode[id])));
if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
AddView("SNO." + snoGroupId.ToString() + "." + property.Name + ".Dump", new TextView(() =>
{
var value = property.GetValue(Engine.Current.SnoGroupsByCode[id]);
var enumerable = (IEnumerable)value;
var array = enumerable.Cast<SnoDefinition<MemoryObject>>().ToArray();
return CodeGeneratorHelper.GetDump(array);
}));
}
}
}
.. to create a lot of different views, typically initialized with a getter that is polled while that view is active and resulting object reflected and printed using special rules. I can do clever things like matching an int for all known SNO IDs and add some extra info about that during the formatting. I also have custom views for containers where I dump content with a button, would hurt to poll hard on those
Or a simple text view where I can print whatever, either updated with a button or by polling. This is the storage section for instance (a tree view is generated based on those strings btw):
Code:
AddView("ObjectManager.Storage", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage));
AddView("ObjectManager.Storage.ACDManager", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x110_ActorCommonDataManager));
AddView("ObjectManager.Storage.ACDManager.Local", new ReflectionView(() => ActorCommonDataHelper.GetLocalAcd()));
AddView("ObjectManager.Storage.ACDManager.Dump", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.Enumerate().ToArray()), true));
AddView("ObjectManager.Storage.ACDManager.Dump.Unfiltered", new ContainerDumpView<ActorCommonData>(() => Engine.Current.ObjectManager.x798_Storage.x110_ActorCommonDataManager.x00_ActorCommonData));
AddView("ObjectManager.Storage.ACDManager.Breakables", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.Enumerate(a => a.x180_GizmoType == GizmoType.BreakableChest).ToArray())));
AddView("ObjectManager.Storage.ACDManager.Items", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.EnumerateItems().ToArray())));
AddView("ObjectManager.Storage.ACDManager.GroundItems", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.EnumerateGroundItems().ToArray())));
AddView("ObjectManager.Storage.ACDManager.InventoryItems", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.EnumerateInventoryItems().ToArray())));
AddView("ObjectManager.Storage.ACDManager.EquippedItems", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.EnumerateEquippedItems().ToArray())));
AddView("ObjectManager.Storage.ACDManager.StashItems", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.EnumerateStashItems().ToArray())));
AddView("ObjectManager.Storage.ACDManager.Chests", new TextView(() => CodeGeneratorHelper.GetDump(ActorCommonDataHelper.Enumerate(a => a.x180_GizmoType == GizmoType.Chest).ToArray())));
AddView("ObjectManager.Storage.SceneAllocators", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x0E8_Ptr_116Bytes_Scenes));
AddView("ObjectManager.Storage.FastAttrib", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x104_FastAttrib));
AddView("ObjectManager.Storage.FastAttrib.Groups", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x104_FastAttrib.x54_Groups));
AddView("ObjectManager.Storage.FastAttrib.Groups.Dump", new ContainerDumpView<FastAttribGroup>(() => Engine.Current.ObjectManager.x798_Storage.x104_FastAttrib.x54_Groups));
AddView("ObjectManager.Storage.FastAttrib.Groups.Local", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x104_FastAttrib.x54_Groups[(short)ActorCommonDataHelper.GetLocalAcd().x120_FastAttribGroupId]));
AddView("ObjectManager.Storage.FastAttrib.Groups.Local.x00C_PtrMap", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x104_FastAttrib.x54_Groups[(short)ActorCommonDataHelper.GetLocalAcd().x120_FastAttribGroupId].x00C_PtrMap));
AddView("ObjectManager.Storage.FastAttrib.Groups.Local.x010_Map", new ReflectionView(() => Engine.Current.ObjectManager.x798_Storage.x104_FastAttrib.x54_Groups[(short)ActorCommonDataHelper.GetLocalAcd().x120_FastAttribGroupId].x010_Map));
Makes it super easy to try new things
For new structures I just have to find a lead using IDA first and have a known struct size to work with.
Since you'll be wondering, no, will not make this public anytime soon. I'm doing a lot of crazy things in that program that I don't feel like sharing. I might try to pull the core out into a library, and who knows, maybe that is pain free and it will be ready within a few days. Keep your fingers crossed XD