Last active
June 19, 2022 00:56
-
-
Save altalk23/85598a67d2bb1c95bafd0d95fc7b98bd to your computer and use it in GitHub Desktop.
typeinfo cast for multiple inheritance using typeid name comparison
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <typeinfo> | |
#include <cstring> | |
#if __x86_64__ | |
using uinthalf_t = uint32_t; | |
using inthalf_t = int32_t; | |
#else | |
using uinthalf_t = uint16_t; | |
using inthalf_t = int16_t; | |
#endif | |
struct DummyClass { | |
virtual ~DummyClass() {} | |
}; | |
struct DummySingleClass : DummyClass {}; | |
struct DummyClass2 {}; | |
struct DummyMultipleClass : DummySingleClass, DummyClass2 {}; | |
struct ClassTypeinfoType { | |
void** m_typeinfoVtable; | |
char const* m_typeinfoName; | |
}; | |
struct SingleClassTypeinfoType : ClassTypeinfoType { | |
ClassTypeinfoType* m_baseClassTypeinfo; | |
}; | |
#pragma pack(push, 1) | |
struct MultipleClassSingleEntryType { | |
ClassTypeinfoType* m_baseClassTypeinfo; | |
uint8_t m_visibilityFlag; | |
inthalf_t m_offset; | |
uint8_t m_padding[sizeof(inthalf_t) - 1]; | |
}; | |
#pragma pack(pop) | |
struct MultipleClassTypeinfoType : ClassTypeinfoType { | |
uint32_t m_flags; | |
uint32_t m_numBaseClass; | |
MultipleClassSingleEntryType m_baseClasses[0x100]; | |
}; | |
struct VtableTypeinfoType { | |
inthalf_t m_offset; | |
ClassTypeinfoType* m_typeinfo; | |
}; | |
struct VtableType { | |
void* m_vtable[0x100]; | |
}; | |
struct CompleteVtableType : VtableTypeinfoType, VtableType {}; | |
void** typeinfoVtableOf(void* ptr) { | |
auto vftable = *reinterpret_cast<VtableType**>(ptr); | |
auto typeinfoPtr = static_cast<VtableTypeinfoType*>(static_cast<CompleteVtableType*>(vftable)); | |
return typeinfoPtr->m_typeinfo->m_typeinfoVtable; | |
} | |
void* traverseTypeinfoFor(void* ptr, ClassTypeinfoType* typeinfo, char const* afterIdent) { | |
DummySingleClass dummySingleClass; | |
DummyMultipleClass dummyMultipleClass; | |
{ | |
auto optionIdent = typeinfo->m_typeinfoName; | |
if (std::strcmp(optionIdent, afterIdent) == 0) { | |
return ptr; | |
} | |
} | |
if (typeinfo->m_typeinfoVtable == typeinfoVtableOf(&dummySingleClass)) { | |
auto siTypeinfo = static_cast<SingleClassTypeinfoType*>(typeinfo); | |
return traverseTypeinfoFor(ptr, siTypeinfo->m_baseClassTypeinfo, afterIdent); | |
} | |
else if (typeinfo->m_typeinfoVtable == typeinfoVtableOf(&dummyMultipleClass)) { | |
auto vmiTypeinfo = static_cast<MultipleClassTypeinfoType*>(typeinfo); | |
for (int i = 0; i < vmiTypeinfo->m_numBaseClass; ++i) { | |
auto& entry = vmiTypeinfo->m_baseClasses[i]; | |
auto optionPtr = reinterpret_cast<std::byte*>(ptr) + entry.m_offset; | |
auto ret = traverseTypeinfoFor(optionPtr, entry.m_baseClassTypeinfo, afterIdent); | |
if (ret != nullptr) return ret; | |
} | |
} | |
return nullptr; | |
} | |
template <class After, class Before> | |
After typeinfo_cast(Before ptr) { | |
static_assert( | |
std::is_polymorphic_v<std::remove_pointer_t<Before>>, | |
"something is not polymorphic" | |
); | |
auto basePtr = dynamic_cast<void*>(ptr); | |
auto vftable = *reinterpret_cast<VtableType**>(basePtr); | |
auto typeinfo = static_cast<VtableTypeinfoType*>(static_cast<CompleteVtableType*>(vftable))->m_typeinfo; | |
auto afterTypeinfo = reinterpret_cast<ClassTypeinfoType const*>(&typeid(std::remove_pointer_t<After>)); | |
auto afterIdent = afterTypeinfo->m_typeinfoName; | |
return static_cast<After>(traverseTypeinfoFor(basePtr, typeinfo, afterIdent)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <typeinfo> | |
#include <cstring> | |
struct TypeDescriptorType { | |
void* m_typeinfoTable; | |
int32_t m_spare; | |
char m_typeDescriptorName[0x100]; | |
}; | |
struct ClassDescriptorType; | |
struct BaseClassDescriptorType { | |
TypeDescriptorType* m_typeDescriptor; | |
int32_t m_numContainedBases; | |
int32_t m_memberDisplacement[3]; | |
int32_t m_attributes; | |
ClassDescriptorType* m_classDescriptor; | |
}; | |
struct ClassDescriptorType { | |
int32_t m_signature; | |
int32_t m_attributes; | |
int32_t m_numBaseClasses; | |
BaseClassDescriptorType** m_baseClassArray; | |
}; | |
struct CompleteLocatorType { | |
int32_t m_signature; | |
int32_t m_offset; | |
int32_t m_cdOffset; | |
TypeDescriptorType* m_typeDescriptor; | |
ClassDescriptorType* m_classDescriptor; | |
}; | |
struct MetaPointerType { | |
CompleteLocatorType* m_completeLocator; | |
}; | |
struct VftableType { | |
void* m_vftable[0x100]; | |
}; | |
struct CompleteVftableType : MetaPointerType, VftableType {}; | |
template <class After, class Before> | |
After typeinfo_cast(Before ptr) { | |
static_assert( | |
std::is_polymorphic_v<std::remove_pointer_t<Before>>, | |
"Input is not a polymorphic type" | |
); | |
// i dont think this is even needed but eh | |
auto basePtr = dynamic_cast<void*>(ptr); | |
auto vftable = *reinterpret_cast<VftableType**>(basePtr); | |
auto metaPtr = static_cast<MetaPointerType*>(static_cast<CompleteVftableType*>(vftable)); | |
auto afterDesc = reinterpret_cast<TypeDescriptorType const*>(&typeid(std::remove_pointer_t<After>)); | |
auto afterIdent = static_cast<char const*>(afterDesc->m_typeDescriptorName); | |
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor; | |
for (int32_t i = 0; i < classDesc->m_numBaseClasses; ++i) { | |
auto optionIdent = static_cast<char const*>(classDesc->m_baseClassArray[i]->m_typeDescriptor->m_typeDescriptorName); | |
auto optionOffset = classDesc->m_baseClassArray[i]->m_memberDisplacement[0]; | |
if (std::strcmp(afterIdent, optionIdent) == 0) { | |
auto afterPtr = reinterpret_cast<std::byte*>(basePtr) + optionOffset; | |
return reinterpret_cast<After>(afterPtr); | |
} | |
} | |
return nullptr; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment