diff --git a/UEDumper/Engine/Core/Core.cpp b/UEDumper/Engine/Core/Core.cpp index dd195c56..94bb0992 100644 --- a/UEDumper/Engine/Core/Core.cpp +++ b/UEDumper/Engine/Core/Core.cpp @@ -159,7 +159,7 @@ uint64_t EngineCore::getOffsetAddress(const Offset& offset) Offset EngineCore::getOffsetForName(const std::string& name) { - for(auto& offset : offsets) + for (auto& offset : offsets) { if (offset.name == name) return offset; @@ -181,7 +181,7 @@ bool EngineCore::generateFNameFile(int& progressDone, int& totalProgress) std::vector> sortedNames; - for (const auto& pair : FNameCache) + for (const auto& pair : FNameCache) { sortedNames.emplace_back(pair); } @@ -191,17 +191,16 @@ bool EngineCore::generateFNameFile(int& progressDone, int& totalProgress) FNameFile << "FName dump generated by UEDumper by Spuckwaffel.\n\n\n"; - for (const auto& pair : sortedNames) + for (const auto& pair : sortedNames) { - progressDone++; - char buff[2000] = {0}; + char buff[2000] = { 0 }; if (pair.second.length() > 1900) { FNameFile << "Name for id " << pair.first << "too long!\n"; continue; } - + sprintf_s(buff, sizeof(buff), "[%05d] %s", pair.first, pair.second.c_str()); FNameFile << buff << std::endl; } @@ -236,7 +235,7 @@ bool EngineCore::generateStructOrClass(UStruct* object, std::vectorPropertiesSize; } - + } @@ -256,9 +255,9 @@ bool EngineCore::generateStructOrClass(UStruct* object, std::vectorChildren) + if (object->Children) { - + for (auto child = object->getChildren(); child; child = child->getNext()) { if (!ObjectsManager::operationSuccess()) @@ -349,6 +348,23 @@ bool EngineCore::generateStructOrClass(UStruct* object, std::vector +constexpr uint64_t GetMaxOfType() +{ + return (1ull << (sizeof(T) * 0x8ull)) - 1; +} + +std::string setEnumSizeForValue(uint64_t EnumValue) +{ + if (EnumValue > GetMaxOfType()) + return TYPE_UI64; + if (EnumValue > GetMaxOfType()) + return TYPE_UI32; + if (EnumValue > GetMaxOfType()) + return TYPE_UI16; + return TYPE_UI8; +} + bool EngineCore::generateEnum(const UEnum* object, std::vector& data) { EngineStructs::Enum eEnum; @@ -362,16 +378,16 @@ bool EngineCore::generateEnum(const UEnum* object, std::vector maxNum) maxNum = name.Value(); + auto& name = names[i]; + if (name.Value() > maxNum && i != names.size() - 1) maxNum = name.Value(); auto fname = FNameToString(name.Key()); std::ranges::replace(fname, ':', '_'); eEnum.members.push_back(std::pair(fname, name.Value())); } - - eEnum.type = maxNum > 256 ? TYPE_UI32 : TYPE_UI8; + eEnum.type = setEnumSizeForValue(maxNum); eEnum.cppName = object->getName(); @@ -388,78 +404,79 @@ bool EngineCore::generateFunctions(const UStruct* object, std::vectorChildren) return false; #else - if (!object->Children || !object->ChildProperties) + if (!object->Children) return false; #endif -//i am so sorry for the indent here but fucking reSharper from intellij is so bad and fucks up the -//indenting for the entire rest of the core.cpp file just because some #define shit -//this made me so mad i couldnt care less the code misses now a indent + //i am so sorry for the indent here but fucking reSharper from intellij is so bad and fucks up the + //indenting for the entire rest of the core.cpp file just because some #define shit + //this made me so mad i couldnt care less the code misses now a indent -//in every version we have to go through the children to -for (auto fieldChild = object->getChildren(); fieldChild; fieldChild = fieldChild->getNext()) -{ - if (!fieldChild || !fieldChild->IsA()) - continue; + //in every version we have to go through the children to + for (auto fieldChild = object->getChildren(); fieldChild; fieldChild = fieldChild->getNext()) + { + if (!fieldChild || !fieldChild->IsA()) + continue; - const auto fn = fieldChild->castTo(); - EngineStructs::Function eFunction; - eFunction.fullName = fn->getFullName(); - eFunction.cppName = fn->getName(); - eFunction.memoryAddress = fn->objectptr; - eFunction.functionFlags = fn->getFunctionFlagsString(); - eFunction.binaryOffset = fn->Func - Memory::getBaseAddress(); + const auto fn = fieldChild->castTo(); + + EngineStructs::Function eFunction; + eFunction.fullName = fn->getFullName(); + eFunction.cppName = fn->getName(); + eFunction.memoryAddress = fn->objectptr; + eFunction.functionFlags = fn->getFunctionFlagsString(); + eFunction.binaryOffset = fn->Func - Memory::getBaseAddress(); #if UE_VERSION < UE_4_25 - //ue < 4.25 uses the children but we have to cast them to a UProperty to use the flags - for (auto child = fn->getChildren(); child; child = child->getNext()) - { - const auto propChild = child->castTo(); + //ue < 4.25 uses the children but we have to cast them to a UProperty to use the flags + for (auto child = fn->getChildren(); child; child = child->getNext()) + { + const auto propChild = child->castTo(); #else - //ue >= 4.25 we go through the childproperties and we dont have to cast as they are already FProperties - for (auto child = fn->getChildProperties(); child; child = child->getNext()) - { - const auto propChild = child; + //ue >= 4.25 we go through the childproperties and we dont have to cast as they are already FProperties + for (auto child = fn->getChildProperties(); child; child = child->getNext()) + { + const auto propChild = child; #endif - //rest of the code is identical, nothing changed here - const auto propertyFlags = propChild->PropertyFlags; + //rest of the code is identical, nothing changed here + const auto propertyFlags = propChild->PropertyFlags; - if (propertyFlags & EPropertyFlags::CPF_ReturnParm && !eFunction.returnType) - eFunction.returnType = propChild->getType(); - else if (propertyFlags & EPropertyFlags::CPF_Parm) - { - eFunction.params.push_back(std::tuple(propChild->getType(), propChild->getName(), propertyFlags, propChild->ArrayDim)); + if (propertyFlags & EPropertyFlags::CPF_ReturnParm && !eFunction.returnType) + eFunction.returnType = propChild->getType(); + else if (propertyFlags & EPropertyFlags::CPF_Parm) + { + eFunction.params.push_back(std::tuple(propChild->getType(), propChild->getName(), propertyFlags, propChild->ArrayDim)); + } } - } - // no defined return type => void - if (!eFunction.returnType) - eFunction.returnType = { false, PropertyType::StructProperty, "void" }; + // no defined return type => void + if (!eFunction.returnType) + eFunction.returnType = { false, PropertyType::StructProperty, "void" }; - data.push_back(eFunction); -} -return true; + data.push_back(eFunction); + } + return true; -} + } -bool EngineCore::RUNAddMemberToMemberArray(EngineStructs::Struct& eStruct, const EngineStructs::Member& newMember) +bool EngineCore::RUNAddMemberToMemberArray(EngineStructs::Struct & eStruct, const EngineStructs::Member & newMember) { //basic 0(1) checks before iterating //below class base offset? - if (newMember.offset < eStruct.inheretedSize) - { + if (newMember.offset < eStruct.inheretedSize) + { windows::LogWindow::Log(windows::LogWindow::log_0, "CORE", "Add member failed: offset 0x%X is below base class offset 0x%X!", newMember.offset, eStruct.inheretedSize); return false; } //above class? - if (newMember.offset > eStruct.size) + if (newMember.offset > eStruct.size) { windows::LogWindow::Log(windows::LogWindow::log_0, "CORE", "Add member failed: offset 0x%X is greater than class size 0x%X!", newMember.offset, eStruct.size); return false; @@ -479,7 +496,7 @@ bool EngineCore::RUNAddMemberToMemberArray(EngineStructs::Struct& eStruct, const } //empty? Mostly the case if the struct has just a unknownmember and nothing defined - if (eStruct.definedMembers.size() == 0) + if (eStruct.definedMembers.size() == 0) { //nothing really needed to check eStruct.definedMembers.push_back(newMember); @@ -538,7 +555,7 @@ bool EngineCore::RUNAddMemberToMemberArray(EngineStructs::Struct& eStruct, const return false; } -void EngineCore::cookMemberArray(EngineStructs::Struct& eStruct) +void EngineCore::cookMemberArray(EngineStructs::Struct & eStruct) { //clear the existing array if (!eStruct.cookedMembers.empty()) @@ -582,7 +599,7 @@ void EngineCore::cookMemberArray(EngineStructs::Struct& eStruct) //fill that with a unknownmember instead of bits genUnknownMember(startOffset + 1, endOffset, 3); //check if the end is < 0, then we can just stop - if(endBit == 0) + if (endBit == 0) return; //adjust, now we just gotta fill the bits until endbit startOffset = endOffset; @@ -605,7 +622,7 @@ void EngineCore::cookMemberArray(EngineStructs::Struct& eStruct) unknown.type = { false, PropertyType::BoolProperty, TYPE_UCHAR }; unknown.isBit = true; unknown.bitOffset = startBit++; - if(startBit >= 8) //should actually just be == 8 otherwise its super weird + if (startBit >= 8) //should actually just be == 8 otherwise its super weird { startBit = startBit % 8; startOffset++; @@ -629,7 +646,7 @@ void EngineCore::cookMemberArray(EngineStructs::Struct& eStruct) } //we are hoping (very hard) that definedmembers array is 1. sorted and 2. checked for collisions - for( int i = 0; i < eStruct.definedMembers.size() - 1; i++) + for (int i = 0; i < eStruct.definedMembers.size() - 1; i++) { const auto& currentMember = eStruct.definedMembers[i]; const auto& nextMember = eStruct.definedMembers[i + 1]; @@ -699,24 +716,25 @@ void EngineCore::cookMemberArray(EngineStructs::Struct& eStruct) //add the last member eStruct.cookedMembers.push_back(eStruct.definedMembers[eStruct.definedMembers.size() - 1]); const auto& last = eStruct.cookedMembers[eStruct.cookedMembers.size() - 1]; - if(last.offset + last.size < eStruct.size) + if (last.offset + last.size < eStruct.size) genUnknownMember(last.offset + last.size, eStruct.size, 6); } + EngineCore::EngineCore() { static bool loaded = false; bSuccess = false; - if(!loaded) + if (!loaded) { windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINECORE", "Loading core..."); offsets = setOffsets(); - + gNames = getOffsetAddress(getOffsetForName("OFFSET_GNAMES")); - if(!gNames) + if (!gNames) { windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINECORE", "GNames offset not found!"); return; @@ -734,13 +752,13 @@ EngineCore::EngineCore() } #endif - + loaded = true; } - + bSuccess = true; - + } bool EngineCore::initSuccess() @@ -748,7 +766,7 @@ bool EngineCore::initSuccess() return bSuccess; } -void EngineCore::cacheFNames(int64_t& finishedNames, int64_t& totalNames, CopyStatus& status) +void EngineCore::cacheFNames(int64_t & finishedNames, int64_t & totalNames, CopyStatus & status) { windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "Caching FNames..."); status = CS_busy; @@ -766,7 +784,7 @@ void EngineCore::cacheFNames(int64_t& finishedNames, int64_t& totalNames, CopySt #if BREAK_IF_INVALID_NAME if (finishedNames == 0 && res != "/Script/CoreUObject") { - windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", + windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "ERROR: The first object name should be always /Script/CoreUObject! This is most likely the result of a invalid FName offset or no decryption!"); status = CS_error; return; @@ -778,7 +796,7 @@ void EngineCore::cacheFNames(int64_t& finishedNames, int64_t& totalNames, CopySt windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "Cached all FNames!"); } -void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPackages, CopyStatus& status) +void EngineCore::generatePackages(int64_t & finishedPackages, int64_t & totalPackages, CopyStatus & status) { windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "Caching all Packets..."); status = CS_busy; @@ -803,7 +821,7 @@ void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPacka addStructs(); windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "adding overrigind unknown members...."); overrideUnknownMembers(); - + for (; finishedPackages < ObjectsManager::gUObjectManager.UObjectArray.NumElements; finishedPackages++) { auto object = ObjectsManager::getUObjectByIndex(finishedPackages); @@ -813,9 +831,9 @@ void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPacka return; //is it even valid? Some indexes arent - if (!object) + if (!object) continue; - + if (!object->IsA() && !object->IsA()) continue; @@ -827,6 +845,8 @@ void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPacka totalPackages = upackages.size(); windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "Total packages: %d", totalPackages); + + EngineStructs::Package basicType; basicType.index = 0; basicType.packageName = "BasicType"; //dont rename!! @@ -839,22 +859,24 @@ void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPacka packages.push_back(basicType); //package 0 is reserved for our special defined structs - for (auto& package: upackages) + for (auto& package : upackages) { EngineStructs::Package ePackage; ePackage.packageName = package.first; - + + for (const auto& object : package.second) { const bool isClass = object->IsA(); if (!ObjectsManager::operationSuccess()) return; - if (isClass || object->IsA()) { auto& dataVector = isClass ? ePackage.classes : ePackage.structs; const auto naming = isClass ? "Class" : "Struct"; + + //is the struct predefined? if (overridingStructs.contains(object->getFullName())) { @@ -885,7 +907,8 @@ void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPacka windows::LogWindow::Log(windows::LogWindow::log_0, "CORE", "Generating %s %s::%s", naming, ePackage.packageName.c_str(), object->getCName().c_str()); - + + const auto sObject = object->castTo(); if (!generateStructOrClass(sObject, dataVector)) @@ -909,11 +932,13 @@ void EngineCore::generatePackages(int64_t& finishedPackages, int64_t& totalPacka finishedPackages++; } + + std::ranges::sort(packages, EngineStructs::Package::packageCompare); //were done, now we do packageObjectInfos caching, we couldnt do before because pointers are all on stack data and not in the static package vec finishPackages(); - + status = CS_success; windows::LogWindow::Log(windows::LogWindow::log_0, "ENGINECORE", "Done generating packets!"); } @@ -924,15 +949,16 @@ std::vector& EngineCore::getPackages() } -EngineCore::ObjectInfo EngineCore::getInfoOfObject(const std::string& CName) +const ObjectInfo* EngineCore::getInfoOfObject(const std::string & CName) { //in functions we compare packageIndex and objectIndex anyways so the type doesnt matter - if(!packageObjectInfos.contains(CName)) - return { false, ObjectInfo::OI_MAX, nullptr }; + if (!packageObjectInfos.contains(CName)) + return nullptr; - return packageObjectInfos[CName]; + return &packageObjectInfos[CName]; } + const std::vector& EngineCore::getAllUnknownTypes() { //already checked? Well then dont do it again @@ -954,7 +980,7 @@ const std::vector& EngineCore::getAllUnknownTypes() } }; - for(auto& struc : pack.structs) + for (auto& struc : pack.structs) checkMembers(struc); for (auto& struc : pack.classes) checkMembers(struc); @@ -962,7 +988,7 @@ const std::vector& EngineCore::getAllUnknownTypes() return unknownProperties; } -void EngineCore::overrideStruct(EngineStructs::Struct& eStruct) +void EngineCore::overrideStruct(EngineStructs::Struct & eStruct) { if (overridingStructs.contains(eStruct.fullName)) return; @@ -970,7 +996,7 @@ void EngineCore::overrideStruct(EngineStructs::Struct& eStruct) overridingStructs.insert(std::pair(eStruct.fullName, eStruct)); } -void EngineCore::createStruct(const EngineStructs::Struct& eStruct) +void EngineCore::createStruct(const EngineStructs::Struct & eStruct) { if (std::ranges::find(customStructs, eStruct) != customStructs.end()) return; @@ -978,7 +1004,7 @@ void EngineCore::createStruct(const EngineStructs::Struct& eStruct) customStructs.push_back(eStruct); } -void EngineCore::overrideStructMembers(const EngineStructs::Struct& eStruct) +void EngineCore::overrideStructMembers(const EngineStructs::Struct & eStruct) { if (overridingStructMembers.contains(eStruct.fullName)) return; @@ -988,6 +1014,7 @@ void EngineCore::overrideStructMembers(const EngineStructs::Struct& eStruct) void EngineCore::finishPackages() { + std::unordered_map enumMap = {}; //were done, now we do packageObjectInfos caching, we couldnt do before because pointers are all on stack data and not in the static package vec for (int i = 0; i < packages.size(); i++) { @@ -1016,6 +1043,15 @@ void EngineCore::finishPackages() packageObjectInfos.insert(std::pair(func.cppName, ObjectInfo(true, ObjectInfo::OI_Function, &func))); } + + for (const auto& var : struc.definedMembers) + { + if (var.type.propertyType == PropertyType::EnumProperty) + { + if (!enumMap.contains(var.type.name)) + enumMap[var.type.name] = var.size; + } + } } }; @@ -1029,6 +1065,9 @@ void EngineCore::finishPackages() enu.owningVectorIndex = j; packageObjectInfos.insert(std::pair(enu.cppName, ObjectInfo(true, ObjectInfo::OI_Enum, &enu))); } + + + } //we have to loop again for dependency tracking and supers @@ -1041,23 +1080,99 @@ void EngineCore::finishPackages() for (auto& name : struc->superNames) { const auto info = getInfoOfObject(name); - auto superStruc = static_cast(info.target); + if (!info || !info->valid) + continue; + auto superStruc = static_cast(info->target); struc->supers.push_back(superStruc); if (superStruc->owningPackage->index != package.index) package.dependencyPackages.insert(superStruc->owningPackage); } + + for (auto& var : struc->cookedMembers) + { + if (!var.type.clickable) + continue; + const auto info = getInfoOfObject(var.type.name); + if (!info || !info->valid) + continue; + + var.type.info = info; + + for (auto& subtype : var.type.subTypes) + { + const auto subInfo = getInfoOfObject(subtype.name); + if (!subInfo || !subInfo->valid) + continue; + + subtype.info = subInfo; + + if (subtype.propertyType != PropertyType::ObjectProperty && subtype.propertyType != PropertyType::ClassProperty) + { + //casting is fine even if its a enum as owningpackage is the first package + const auto targetStruc = static_cast(subInfo->target); + if (targetStruc->owningPackage->index != package.index) + package.dependencyPackages.insert(targetStruc->owningPackage); + } + } + + + //casting is fine even if its a enum as owningpackage is the first package + const auto targetStruc = static_cast(info->target); + if (targetStruc->owningPackage->index != package.index) + package.dependencyPackages.insert(targetStruc->owningPackage); + } + } + + for (const auto& func : package.functions) + { + auto& ret = func->returnType; + auto addInfoPtr = [&](fieldType& type) + { + if (type.clickable) + { + const auto info = getInfoOfObject(type.name); + if (info && info->valid) + type.info = info; + } + }; + addInfoPtr(ret); + + for (auto& param : func->params) + { + auto& type = std::get<0>(param); + addInfoPtr(type); + } + } + + for (int j = 0; j < package.enums.size(); j++) + { + auto& enu = package.enums[j]; + if (enumMap.contains(enu.cppName)) + { + const auto eSize = enumMap[enu.cppName]; + + std::string nam = TYPE_UI8; + if (eSize == 2) + nam = TYPE_UI16; + else if (eSize == 4) + nam = TYPE_UI32; + else if (eSize == 8) + nam = TYPE_UI64; + + enu.type = nam; + } } } } -void EngineCore::runtimeOverrideStructMembers(EngineStructs::Struct* eStruct, const std::vector& members) +void EngineCore::runtimeOverrideStructMembers(EngineStructs::Struct * eStruct, const std::vector&members) { if (eStruct == nullptr) return; - for(const auto& member : members) + for (const auto& member : members) { RUNAddMemberToMemberArray(*eStruct, member); } @@ -1066,12 +1181,13 @@ void EngineCore::runtimeOverrideStructMembers(EngineStructs::Struct* eStruct, co void EngineCore::saveToDisk(int& progressDone, int& totalProgress) { - totalProgress = 1 + FNameCache.size() + packageObjectInfos.size() + + totalProgress = 1 + FNameCache.size() + packageObjectInfos.size() + overridingStructs.size() + packages.size() + unknownProperties.size() + customStructs.size() + offsets.size() + 5000; progressDone = 0; windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINECORE", "Saving to disk..."); nlohmann::json UEDProject; + UEDProject["EngineSettings"] = EngineSettings::toJson(); progressDone++; @@ -1083,6 +1199,7 @@ void EngineCore::saveToDisk(int& progressDone, int& totalProgress) unordered_maps["FNameCache"] = jFNameCache; progressDone += FNameCache.size(); + nlohmann::json jOverridingStructs; for (const auto& entry : overridingStructs) jOverridingStructs[entry.first] = entry.second.toJson(); @@ -1128,13 +1245,14 @@ void EngineCore::saveToDisk(int& progressDone, int& totalProgress) unsigned char* strBytes = static_cast(calloc(1, totalBytes)); std::memcpy(strBytes, dump.data(), dump.length()); - + //super secret const char* key = "UEDumper secret!"; AES aes(AESKeyLength::AES_128); auto c = aes.EncryptECB(strBytes, totalBytes, reinterpret_cast(key)); - + + std::ofstream file(EngineSettings::getWorkingDirectory() / "SaveState.uedproj", std::ios::binary); file.write(reinterpret_cast(c), totalBytes); @@ -1142,17 +1260,17 @@ void EngineCore::saveToDisk(int& progressDone, int& totalProgress) free(strBytes); delete[] c; - + windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINECORE", "Saved!"); progressDone = totalProgress; } -bool EngineCore::loadProject(const std::string& filepath, int& progressDone, int& totalProgress) +bool EngineCore::loadProject(const std::string & filepath, int& progressDone, int& totalProgress) { progressDone = 0; totalProgress = 11; std::ifstream file(filepath, std::ios::binary | std::ios::ate); - if (!file) + if (!file) { windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINECORE", "Error opening file!"); return false; @@ -1181,7 +1299,7 @@ bool EngineCore::loadProject(const std::string& filepath, int& progressDone, int AES aes(AESKeyLength::AES_128); auto c = aes.DecryptECB(buffer, fileSize, reinterpret_cast(key)); - + free(buffer); std::string cmp = "{\"EngineSettings\""; @@ -1195,11 +1313,15 @@ bool EngineCore::loadProject(const std::string& filepath, int& progressDone, int const nlohmann::json UEDProject = nlohmann::json::parse(c); + + delete[] c; + + //now set all enginesettings settings const nlohmann::json engineSettings = UEDProject["EngineSettings"]; - + if (!EngineSettings::loadJson(engineSettings)) return false; @@ -1213,10 +1335,10 @@ bool EngineCore::loadProject(const std::string& filepath, int& progressDone, int windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINECORE", "Project corrupted! (-4)"); return false; } - + nlohmann::json jFNameCache = unordered_maps["FNameCache"]; - for (auto it = jFNameCache.begin(); it != jFNameCache.end(); ++it) + for (auto it = jFNameCache.begin(); it != jFNameCache.end(); ++it) { FNameCache.insert(std::pair(std::stoi(it.key()), it.value())); } @@ -1225,7 +1347,7 @@ bool EngineCore::loadProject(const std::string& filepath, int& progressDone, int progressDone++; nlohmann::json jOverridingStructs = unordered_maps["OverridingStructs"]; - for (auto it = jOverridingStructs.begin(); it != jOverridingStructs.end(); ++it) + for (auto it = jOverridingStructs.begin(); it != jOverridingStructs.end(); ++it) { overridingStructs.insert(std::pair(it.key(), EngineStructs::Struct::fromJson(it.value()))); } @@ -1282,14 +1404,16 @@ bool EngineCore::loadProject(const std::string& filepath, int& progressDone, int progressDone = totalProgress; + return true; } + void EngineCore::generateStructDefinitionsFile() { std::ofstream file(EngineSettings::getWorkingDirectory() / "StructDefinitions.txt"); file << "/// All changes made to structs are dumped here.\n\n" << std::endl; - + auto printToFile = [&](const std::unordered_map& map) mutable { @@ -1309,7 +1433,7 @@ void EngineCore::generateStructDefinitionsFile() file << spacing << objectName << ".inherited = " << boolToSt(val.inherited) << ";" << std::endl; file << spacing << objectName << ".isClass = " << boolToSt(val.isClass) << ";" << std::endl; file << spacing << objectName << ".members = std::vector {" << std::endl; - for(const auto member : val.cookedMembers) + for (const auto member : val.cookedMembers) { auto printFieldType = [&](const fieldType& type) mutable { @@ -1320,10 +1444,10 @@ void EngineCore::generateStructDefinitionsFile() file << spacing << spacing << "{"; printFieldType(member.type); - if(member.type.subTypes.size() > 0) + if (member.type.subTypes.size() > 0) { file << ", std::vector{"; - for(int i = 0; i < member.type.subTypes.size(); i++) + for (int i = 0; i < member.type.subTypes.size(); i++) { printFieldType(member.type.subTypes[i]); file << "}"; @@ -1333,16 +1457,16 @@ void EngineCore::generateStructDefinitionsFile() file << "}"; } file << "}, \"" << member.name << "\", " << member.offset << ", " << member.size << ", " << boolToSt(member.missed); - if(member.isBit) + if (member.isBit) { - + file << ", " << boolToSt(member.isBit) << ", " << (member.bitOffset >= 99 ? member.bitOffset - 99 : member.bitOffset) << ", " << boolToSt(member.userEdited); } file << "}," << std::endl; } file << spacing << "};" << std::endl; file << spacing << "EngineCore::overrideStructMembers(" << objectName << ");\n\n" << std::endl; - + } }; @@ -1351,12 +1475,12 @@ void EngineCore::generateStructDefinitionsFile() //customstructs is not a unordered map, its a vector. so we create a fake map to use the function above std::unordered_map customStructsMap; - for(const auto& struc : customStructs) + for (const auto& struc : customStructs) customStructsMap.insert(std::pair(struc.fullName, struc)); file << "/// customStructs\n" << std::endl; printToFile(customStructsMap); file << "/// overridingStructMembers\n" << std::endl; printToFile(overridingStructMembers); file.close(); - + } \ No newline at end of file diff --git a/UEDumper/Engine/Core/Core.h b/UEDumper/Engine/Core/Core.h index dac7282a..96b8f42c 100644 --- a/UEDumper/Engine/Core/Core.h +++ b/UEDumper/Engine/Core/Core.h @@ -35,55 +35,6 @@ namespace EngineStructs ENGINE_CORE EngineCore { -public: - - - - //objectinfo struct that holds the info of a defined struct/class/enum/function - struct ObjectInfo - { - - enum ObjectType - { - OI_Struct, - OI_Class, - OI_Enum, - OI_Function, - OI_MAX - }; - - bool valid = false; - - ObjectType type; - - void* target = nullptr;; - - operator bool() const { return valid; } - - //converts the struct to a JSON object - nlohmann::json toJson() const - { - nlohmann::json j; - j["type"] = type; - j["valid"] = valid; - //j["packageIndex"] = packageIndex; - //j["objectIndex"] = objectIndex; - return j; - } - - //returns a valid object from a JSON object - static ObjectInfo fromJson(nlohmann::json& json) - { - ObjectInfo j; - j.type = json["type"]; - j.valid = json["valid"]; - //j.packageIndex = json["packageIndex"]; - //j.objectIndex = json["objectIndex"]; - return j; - } - }; - - private: #if UE_VERSION < UE_4_25 //pointer to GNames on heap @@ -131,7 +82,7 @@ ENGINE_CORE EngineCore //vector of all offsets that got defined by the user inline static std::vector offsets{}; - + /** * \brief generates all the members for the specific struct or class @@ -166,7 +117,7 @@ ENGINE_CORE EngineCore * \param eStruct the struct where the cookedmembers array should be (re)generated */ static void cookMemberArray(EngineStructs::Struct& eStruct); - + public: /// constructors @@ -205,13 +156,14 @@ ENGINE_CORE EngineCore * \param CName CName of the UObject * \return ObjectInfo of the UObject */ - static ObjectInfo getInfoOfObject(const std::string& CName); + static const ObjectInfo* getInfoOfObject(const std::string& CName); /** * \brief USE ONLY AFTER PACKAGE GENERATION! - * This function lists all types that were used in structs but were never defined. (e.g TArray or TMap) + * This function lists all types that were used in structs but were never defined. (e.g TArray or TMap). Calling getInfoOfObject + * is fine too, this does the same but actually return a entire list * \return vector of all unknown types */ static const std::vector& getAllUnknownTypes(); @@ -227,7 +179,7 @@ ENGINE_CORE EngineCore /** * \brief USE ONLY BEFORE PACKAGE GENERATION! Creates a new struct that gets added to the BasicTypes package. - * Use this to create structs the game has but arent present in the SDK. + * Use this to create structs the game has but arent present in the SDK. * \param eStruct the struct that gets created */ static void createStruct(const EngineStructs::Struct& eStruct); @@ -253,7 +205,7 @@ ENGINE_CORE EngineCore /** * \brief ONLY AFTER FULL PACKAGE GENERATION! - * Saves all the states and data to disk into a .uedproj file that can get loaded + * Saves all the states and data to disk into a .uedproj file that can get loaded */ static void saveToDisk(int& progressDone, int& totalProgress); @@ -300,4 +252,3 @@ ENGINE_CORE EngineCore static bool generateFNameFile(int& progressDone, int& totalProgress); }; - diff --git a/UEDumper/Engine/Core/EngineStructs.h b/UEDumper/Engine/Core/EngineStructs.h index 19d15251..0bc1cdeb 100644 --- a/UEDumper/Engine/Core/EngineStructs.h +++ b/UEDumper/Engine/Core/EngineStructs.h @@ -5,10 +5,57 @@ #include #include "../structs.h" +#include "Engine/Userdefined/Datatypes.h" + +//objectinfo struct that holds the info of a defined struct/class/enum/function +struct ObjectInfo +{ + + enum ObjectType + { + OI_Struct, + OI_Class, + OI_Enum, + OI_Function, + OI_MAX + }; + + bool valid = false; + + ObjectType type; + + void* target = nullptr;; + + operator bool() const { return valid; } + + //converts the struct to a JSON object + nlohmann::json toJson() const + { + nlohmann::json j; + j["type"] = type; + j["valid"] = valid; + //j["packageIndex"] = packageIndex; + //j["objectIndex"] = objectIndex; + return j; + } + + //returns a valid object from a JSON object + static ObjectInfo fromJson(nlohmann::json& json) + { + ObjectInfo j; + j.type = json["type"]; + j.valid = json["valid"]; + //j.packageIndex = json["packageIndex"]; + //j.objectIndex = json["objectIndex"]; + return j; + } +}; //type struct that is used for a field in the package struct fieldType { + //linked info if valid + const ObjectInfo* info = nullptr; //clickable if redirection is supported bool clickable = false; //typedef of the type @@ -22,6 +69,23 @@ struct fieldType //it makes it possible to click objects e.g in a TArray for redirection std::vector subTypes = {}; + fieldType() {} + + fieldType(bool clickable, PropertyType propertyType, const std::string& name) + { + this->clickable = clickable; + this->propertyType = propertyType; + this->name = name; + } + + fieldType(bool clickable, PropertyType propertyType, const std::string& name, const std::vector& subTypes) + { + this->clickable = clickable; + this->propertyType = propertyType; + this->name = name; + this->subTypes = subTypes; + } + /** * \brief essentially for dumpspace, gets the short type * \return returns the short type of the fieldType (S, C, E, D) @@ -63,10 +127,36 @@ struct fieldType return arr; } - //essentially for dumps.host std::string stringify() const { - std::string typeStr = name; + + auto generateValidVarName = [](const std::string& str) + { + //hacky way to ignore shit like unsigned char get to unsigned_char + if (getSize(str) != -1) + return str; + std::string result = ""; + + for (const char c : str) + { + if (static_cast(c) < 0 || !std::isalnum(c)) + result += '_'; + else + result += c; + + } + //guaranteed 0 termination + return result; + }; + + + std::string typeStr = generateValidVarName(name); + + if ((propertyType == PropertyType::ObjectProperty || propertyType == PropertyType::ClassProperty) && (info && info->valid)) + { + const std::string prefix = info->type == ObjectInfo::OI_Class ? "class " : "struct "; + typeStr = prefix + typeStr; + } if (subTypes.size() > 0) { @@ -75,10 +165,7 @@ struct fieldType for (int i = 0; i < subTypes.size(); i++) { - typeStr += subTypes[i].name; - - if (subTypes[i].propertyType == PropertyType::ObjectProperty || subTypes[i].propertyType == PropertyType::ClassProperty) - typeStr += "*"; + typeStr += subTypes[i].stringify(); if (i < subTypes.size() - 1) typeStr += ", "; diff --git a/UEDumper/Engine/Core/ObjectsManager.cpp b/UEDumper/Engine/Core/ObjectsManager.cpp index 185db493..8be87c48 100644 --- a/UEDumper/Engine/Core/ObjectsManager.cpp +++ b/UEDumper/Engine/Core/ObjectsManager.cpp @@ -228,12 +228,10 @@ void ObjectsManager::copyUBigObjects(int64_t& finishedBytes, int64_t& totalBytes uint64_t UObjectAddress = *reinterpret_cast(gUObjectManager.pGObjectPtrArray + i * 24); //this happens quite often, those objects just got deleted //the array is like a block of cheese with holes - if (!UObjectAddress) - { - windows::LogWindow::Log(windows::LogWindow::log_1, "ENGINECORE", "Could not resolve address for object %d!", i); + if (!UObjectAddress) { + windows::LogWindow::Log(windows::LogWindow::log_1, "ENGINECORE", "Could not resolve address for obect %d!", i); } - else - { + else { //gets the memory address where the objects gonna be UObjectManager::UBigObject* newBigObject = reinterpret_cast(gUObjectManager.pUBigObjectArray + i * sizeof(UObjectManager::UBigObject)); newBigObject->readSize = sizeof(UObject); diff --git a/UEDumper/Engine/Generation/BasicType.h b/UEDumper/Engine/Generation/BasicType.h index 5d56dff7..3e6531fc 100644 --- a/UEDumper/Engine/Generation/BasicType.h +++ b/UEDumper/Engine/Generation/BasicType.h @@ -26,22 +26,22 @@ struct DefinedStruct { - //the name of the struct. This name should match the name in the engine, this is case sensitive!!!!! - std::string name; + //the name of the struct. This name should match the name in the engine, this is case sensitive!!!!! + std::string name; - //your multiline definition of the struct. Dont forget the brackets and ";" - std::string definition; + //your multiline definition of the struct. Dont forget the brackets and ";" + std::string definition; }; inline std::vector basicDefinitions() { std::vector definedStructs; - DefinedStruct dStruct; + DefinedStruct dStruct; dStruct.name = "TArray"; dStruct.definition = -R"( -template + R"( +template struct TArray { friend struct FString; @@ -79,7 +79,7 @@ struct TArray return Max - Count; } - FORCEINLINE bool RemoveSingle(const int Index) + __forceinline bool RemoveSingle(const int Index) { if (Index < Count) { @@ -93,7 +93,7 @@ struct TArray return false; } - FORCEINLINE void RemoveAt(int Index, int Length = 1) + __forceinline void RemoveAt(int Index, int Length = 1) { for (; Length != 0; --Length) { @@ -120,10 +120,10 @@ struct FName /** Index into the Names array (used to find String portion of the string/number pair used for comparison) */ FNameEntryId ComparisonIndex = 0; )"; - dStruct.definition += + dStruct.definition += #if UE_VERSION >= UE_5_01 #if !UE_FNAME_OUTLINE_NUMBER -R"( + R"( /** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */ FNameEntryId Number = 0; )"; @@ -132,7 +132,7 @@ R"( #endif #if WITH_CASE_PRESERVING_NAME -R"( + R"( /** Index into the Names array (used to find String portion of the string/number pair used for display) */ FNameEntryId DisplayIndex = 0; )"; @@ -140,13 +140,13 @@ R"( #endif #if UE_VERSION < UE_5_01 -R"( + R"( /** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */ int32_t Number = 0; )"; dStruct.definition += #endif -R"( + R"( }; )"; @@ -154,7 +154,7 @@ R"( dStruct.name = "FName"; dStruct.definition = -R"( + R"( struct FString : public TArray { inline FString() {}; @@ -322,19 +322,19 @@ class TPair TPair(){}; public: - FORCEINLINE KeyType& Key() + __forceinline KeyType& Key() { return First; } - FORCEINLINE const KeyType& Key() const + __forceinline const KeyType& Key() const { return First; } - FORCEINLINE ValueType& Value() + __forceinline ValueType& Value() { return Second; } - FORCEINLINE const ValueType& Value() const + __forceinline const ValueType& Value() const { return Second; } @@ -358,32 +358,32 @@ class TUniquePtr return Ptr != nullptr; } - FORCEINLINE explicit operator bool() const + __forceinline explicit operator bool() const { return IsValid(); } - FORCEINLINE bool operator!() const + __forceinline bool operator!() const { return !IsValid(); } - FORCEINLINE PtrType* operator->() const + __forceinline PtrType* operator->() const { return Ptr; } - FORCEINLINE PtrType& operator*() const + __forceinline PtrType& operator*() const { return *Ptr; } - FORCEINLINE const PtrType*& Get() const + __forceinline const PtrType*& Get() const { return Ptr; } - FORCEINLINE PtrType*& Get() + __forceinline PtrType*& Get() { return Ptr; } diff --git a/UEDumper/Engine/Generation/MDK.cpp b/UEDumper/Engine/Generation/MDK.cpp index 3c7e20bb..2f06889f 100644 --- a/UEDumper/Engine/Generation/MDK.cpp +++ b/UEDumper/Engine/Generation/MDK.cpp @@ -232,14 +232,21 @@ void MDKGeneration::generatePackage(std::ofstream& stream, const EngineStructs:: stream << "/// " << buf << std::endl; stream << "enum " << enu.cppName << " : " << enu.type << std::endl; stream << "{" << std::endl; + int j = 0; - int i = 0; + std::vector usedNames{ "float", "int", "bool", "double", "long", "char"}; + for (const auto& member : enu.members) { j++; char memberBuf[300]; - std::string fir = member.first + std::to_string(i++); + std::string fir = member.first; + + if (std::ranges::find(usedNames, fir) != usedNames.end()) + fir += std::to_string(j); + + usedNames.push_back(fir); sprintf_s(memberBuf, " %-80s = %d%s", fir.c_str(), member.second, j == enu.members.size() ? "" : ","); stream << memberBuf << std::endl; @@ -483,6 +490,7 @@ void MDKGeneration::generate(int& progressDone, int& totalProgress) continue; } //move up + didReordering = true; auto it = *neededIt; orderedStructsAndClasses.erase(neededIt); orderedStructsAndClasses.insert(currentIt, it); diff --git a/UEDumper/Engine/Generation/SDK.cpp b/UEDumper/Engine/Generation/SDK.cpp index 4be2ba22..87b35b97 100644 --- a/UEDumper/Engine/Generation/SDK.cpp +++ b/UEDumper/Engine/Generation/SDK.cpp @@ -8,6 +8,7 @@ #include "Frontend/Windows/LogWindow.h" #include "Settings/EngineSettings.h" #include "BasicType.h" +#include "packageSorter.h" void SDKGeneration::printCredits(std::ofstream& stream) { @@ -59,7 +60,7 @@ void SDKGeneration::generateBasicType() BasicType << "\n\n"; if (noDefs.size() > 0) - BasicType << "///\n/// THERE ARE MISSING STRUCTS!! This will result in errors!!!\n///\n\n"; + BasicType << "///\n/// THERE ARE MISSING STRUCTS!! This will result in members having the SDK_UNDEFINED pragma!!!\n///\n\n"; for (const auto& missingDef : noDefs) { @@ -75,8 +76,10 @@ void SDKGeneration::generateBasicType() BasicType.close(); } -void SDKGeneration::generatePackage(std::ofstream& file, const EngineStructs::Package& package) + +void SDKGeneration::generatePackage(std::ofstream& stream, const EngineStructs::Package& package) { + stream << "#pragma once\n"; //flag some invalid characters in a name auto generateValidVarName = [](const std::string& str) { @@ -92,64 +95,132 @@ void SDKGeneration::generatePackage(std::ofstream& file, const EngineStructs::Pa //guaranteed 0 termination return result; }; - auto generateStruct = [&](const std::vector& DataStruc) + + for (auto& dependencies : package.dependencyPackages) + { + stream << "/// dependency: " << dependencies->packageName << std::endl; + } + + stream << "\n"; + + auto generateStruct = [&](const std::vector& DataStruc) { for (const auto& struc : DataStruc) { - if (struc.isClass) - file << "/// Class " << struc.fullName << std::endl; + if (std::ranges::find(allnames, generateValidVarName(struc->cppName)) != allnames.end()) + continue; + allnames.push_back(generateValidVarName(struc->cppName)); + + if (struc->isClass) + stream << "/// Class " << struc->fullName << std::endl; else - file << "/// Struct " << struc.fullName << std::endl; + stream << "/// Struct " << struc->fullName << std::endl; char buf[100] = { 0 }; - sprintf_s(buf, "Size: 0x%04X (0x%06X - 0x%06X)", struc.size - struc.inheretedSize, struc.inheretedSize, struc.size); - file << "/// " << buf << std::endl; - if (struc.isClass) - file << "class " << generateValidVarName(struc.cppName); + sprintf_s(buf, "Size: 0x%04X (0x%06X - 0x%06X)", struc->size - struc->inheretedSize, struc->inheretedSize, struc->size); + stream << "/// " << buf << std::endl; + if (struc->isClass) + stream << "class " << generateValidVarName(struc->cppName); else - file << "struct " << generateValidVarName(struc.cppName); + stream << "struct " << generateValidVarName(struc->cppName); + + if (struc->inherited && struc->isClass) + stream << " : public " << generateValidVarName(struc->supers[0]->cppName); + else if (struc->inherited) + stream << " : " << generateValidVarName(struc->supers[0]->cppName); + + + stream << "\n{ " << std::endl; - if (struc.inherited && struc.isClass) - file << " : public " << generateValidVarName(struc.supers[0]->cppName); - else if (struc.inherited) - file << " : " << generateValidVarName(struc.supers[0]->cppName); + if (struc->isClass) + stream << "public:" << std::endl; + std::vector usedNames{ "float", "int", "bool", "double", "long", "char", "TRUE", "FALSE"}; - file << "\n{ " << std::endl; - if (struc.isClass) - file << "public:" << std::endl; + int j = 0; - for (const auto& member : struc.cookedMembers) + for (const auto& member : struc->cookedMembers) { char finalBuf[600]; char nameBuf[500]; std::string name = member.name; + + if (std::isdigit(name[0])) + name = "_" + name; + + if (name.empty()) + name = "noname"; + + if (std::ranges::find(usedNames, name) != usedNames.end()) + name += std::to_string(j++); + + + + usedNames.push_back(name); + + std::string memberType = member.type.stringify().c_str(); + + if (member.type.clickable) + { + bool anyUndef = !member.type.info || !member.type.info->valid; + if (!anyUndef && member.type.subTypes.size() > 0) + { + auto checkAllSubs = [&](const fieldType& type, auto& self) -> void + { + for (auto& sub : type.subTypes) + { + if (sub.clickable && (!sub.info || !sub.info->valid)) + { + anyUndef = true; + break; + } + if (sub.propertyType == PropertyType::Unknown) + { + anyUndef = true; + break; + } + if (sub.subTypes.size() > 0) + self(sub, self); + } + }; + checkAllSubs(member.type, checkAllSubs); + } + + if (anyUndef) + { + memberType = "SDK_UNDEFINED(" + std::to_string(member.size) + "," + std::to_string(undefinedCnt++) + ") /* " + memberType + " */"; + name = "__um(" + member.name + ")"; + } + } + + + if (member.isBit) name += " : 1"; - sprintf_s(nameBuf, "%-50s %s;", member.type.stringify().c_str(), name.c_str()); + sprintf_s(nameBuf, "%-50s %s;", memberType.c_str(), name.c_str()); if (member.isBit) sprintf_s(finalBuf, " %-110s // 0x%04X:%d (0x%04X) ", nameBuf, member.offset, member.bitOffset, member.size); else sprintf_s(finalBuf, " %-110s // 0x%04X (0x%04X) ", nameBuf, member.offset, member.size); - file << finalBuf << " "; // << static_cast(member.type.propertyType); + stream << finalBuf << " "; // << static_cast(member.type.propertyType); if (member.userEdited) - file << "USER-MODIFIED"; + stream << "USER-MODIFIED"; else if (member.missed) - file << "MISSED"; - file << std::endl; + stream << "MISSED"; + stream << std::endl; } // Add function section header - if (!struc.functions.empty()) + if (!struc->functions.empty()) { - file << "\n\n\t/// Functions" << std::endl; + stream << "\n\n\t/// Functions" << std::endl; } - for (const auto& func : struc.functions) + for (const auto& func : struc->functions) { - file << "\t// Function " << func.fullName << std::endl; + stream << "\t// Function " << func.fullName << std::endl; char funcBuf[1200]; - std::string params = func.returnType.stringify() + " " + func.cppName.c_str() + "("; + std::string params = "// " + func.returnType.stringify() + " " + func.cppName.c_str() + "("; for (auto param : func.params) { params += std::get<0>(param).stringify(); @@ -166,40 +237,48 @@ void SDKGeneration::generatePackage(std::ofstream& file, const EngineStructs::Pa sprintf_s(funcBuf, " %-120s // [0x%llx] %-20s ", params.c_str(), func.binaryOffset, func.functionFlags.c_str()); - file << funcBuf << std::endl; + stream << funcBuf << std::endl; } - file << "};\n\n"; + stream << "};\n\n"; } }; - generateStruct(package.structs); - - generateStruct(package.classes); - + //first we generate enums for (const auto& enu : package.enums) { - file << "/// Enum " << enu.fullName << std::endl; + stream << "/// Enum " << enu.fullName << std::endl; char buf[100] = { 0 }; sprintf_s(buf, "Size: 0x%02d", enu.members.size()); - file << "/// " << buf << std::endl; - file << "enum " << enu.cppName << " : " << enu.type << std::endl; - file << "{" << std::endl; + stream << "/// " << buf << std::endl; + stream << "enum class " << enu.cppName << " : " << enu.type << std::endl; + stream << "{" << std::endl; + int j = 0; - int i = 0; + std::vector usedNames{ "float", "int", "bool", "double", "long", "char"}; + for (const auto& member : enu.members) { j++; char memberBuf[300]; - std::string fir = member.first + std::to_string(i++); + std::string fir = member.first; + + if (std::ranges::find(usedNames, fir) != usedNames.end()) + fir += std::to_string(j); + + usedNames.push_back(fir); sprintf_s(memberBuf, " %-80s = %d%s", fir.c_str(), member.second, j == enu.members.size() ? "" : ","); - file << memberBuf << std::endl; + stream << memberBuf << std::endl; } - file << "};\n\n"; + stream << "};\n\n"; } + + + generateStruct(package.combinedStructsAndClasses); + } SDKGeneration::SDKGeneration() @@ -249,28 +328,44 @@ void SDKGeneration::Generate(int& progressDone, int& totalProgress) #include -#include "SDK/BasicType.h" +#define SDK_UNDEFINED(__ssize__, __cnt__, ...) char undefined##__cnt__[__ssize__]; // +#define __um(...) // x + )"; - generateBasicType(); + std::vector newPackages; - totalProgress = 10; - progressDone = totalProgress; - totalProgress = EngineCore::getPackages().size(); - for (const auto& package : EngineCore::getPackages()) + const auto soredPackages = sortPackages(progressDone, totalProgress, newPackages); + + + puts("------sorted packages------"); + for (auto& pack : soredPackages) { - masterHeader << "#include \"" + package.packageName + ".h" + "\"" << std::endl; + printf("%s\n", pack->package.packageName.c_str()); + } + puts("---------------------------"); - if (package.packageName == "BasicType") - continue; + //generateBasicType(); - std::ofstream file(SDKPath / (package.packageName + ".h")); - printCredits(file); - file << "/// Package " + package.packageName << ".\n\n"; + totalProgress = soredPackages.size(); + progressDone = 0; + for (auto& pack : soredPackages) + { + windows::LogWindow::Log(windows::LogWindow::log_2, "SDK GEN", "Baking package %s", pack->package.packageName.c_str()); + masterHeader << "#include \"SDK/" + pack->package.packageName + ".h\"" << std::endl; + if (pack->package.packageName == "BasicType") + generateBasicType(); + else + { + std::string packageName = pack->package.packageName + ".h"; + std::ofstream package(SDKPath / packageName); + printCredits(package); + generatePackage(package, pack->package); + package.close(); + } - generatePackage(file, package); - file.close(); progressDone++; } + masterHeader.close(); progressDone = totalProgress; } \ No newline at end of file diff --git a/UEDumper/Engine/Generation/SDK.h b/UEDumper/Engine/Generation/SDK.h index 811b01fd..14d93cb0 100644 --- a/UEDumper/Engine/Generation/SDK.h +++ b/UEDumper/Engine/Generation/SDK.h @@ -9,18 +9,20 @@ class SDKGeneration static inline std::filesystem::path SDKPath{}; - + static inline std::vector allnames{}; + + static inline int undefinedCnt = 0; static void generateBasicType(); - + public: SDKGeneration(); static void printCredits(std::ofstream& stream); - static void generatePackage(std::ofstream& file, const EngineStructs::Package& package); + static void generatePackage(std::ofstream& stream, const EngineStructs::Package& package); static void Generate(int& progressDone, int& totalProgress); -}; +}; \ No newline at end of file diff --git a/UEDumper/Engine/Generation/packageSorter.h b/UEDumper/Engine/Generation/packageSorter.h new file mode 100644 index 00000000..59b8e2db --- /dev/null +++ b/UEDumper/Engine/Generation/packageSorter.h @@ -0,0 +1,407 @@ +#pragma once +#include "stdafx.h" +#include "Engine/Core/EngineStructs.h" +#include "Frontend/Windows/LogWindow.h" +#include "Engine/Core/Core.h" + +struct MergedPackage +{ + std::vector mergedPackages; //all packages this package contains + EngineStructs::Package package; //the new package +}; + +inline std::vector sortPackages(int& progressDone, int& totalProgress, std::vector& newPackages) +{ + + //first we cast all packages to merged packages + for (auto& pack : EngineCore::getPackages()) + { + MergedPackage p; + p.mergedPackages = { &pack }; + p.package = pack; + newPackages.push_back(p); + } + + totalProgress = newPackages.size(); + + bool anyMergeFound = false; + + windows::LogWindow::Log(windows::LogWindow::log_2, "SORTER", "Merging packages..."); + + do + { + progressDone = 0; + anyMergeFound = false; + //a new vector we fill up this round and then replace the newPackages vector + std::vector _newPackages{}; + //this just acts as a temporary identifier to make sure when we queue a package to be merged we dont chack that package + //otherwise when a abd b should merge obviously b and a have to merge too resulting in a duplicate merge + std::vector skippedPackageDueToMerge{}; + + //go over all packages + for (auto& pack : newPackages) + { + //skip this if package is listed as a merge with a package processed before + if (std::ranges::find(skippedPackageDueToMerge, pack.package.packageName) != skippedPackageDueToMerge.end()) + continue; + + //reset the boolean + bool mergefound = false; + //go over all dependencies of the root package + for (auto& dependencyPackage : pack.package.dependencyPackages) + { + //go over all dependencies of the dependency package + for (auto& dependencyOfDependencyPackage : dependencyPackage->dependencyPackages) + { + //is the dependency of the depend package in the merge pack? + //if not, continue + if (std::ranges::find(pack.mergedPackages, dependencyOfDependencyPackage) == pack.mergedPackages.end()) + continue; + + //if it is in, we have a cyclic dependency + + windows::LogWindow::Log(windows::LogWindow::log_2, "MDK GEN", + "merge found with %s and %s origin %s", dependencyPackage->packageName.c_str(), dependencyOfDependencyPackage->packageName.c_str(), pack.package.packageName.c_str()); + + anyMergeFound = true; + mergefound = true; + //add him to the merges + pack.mergedPackages.push_back(dependencyPackage); + skippedPackageDueToMerge.push_back(dependencyPackage->packageName); + + //break operation, once we found once cycle in a dependency, we dont have to look for more in the same dependency + break; + } + + //obviously dont break here, we have to look at the other dependencies if they are cyclic too + + } + + //if a merge has been found, recreate the entire package + if (mergefound) + { + MergedPackage p; + //first sort out own references and remove dups + std::set totalDependencyPackage; + std::string name = "merged"; + //merge all the mininal needed stuff together + for (auto& mergedpack : pack.mergedPackages) + { + name += "_" + mergedpack->packageName; + //add da items to the new mergedpackage + p.package.enums.insert(p.package.enums.end(), mergedpack->enums.begin(), mergedpack->enums.end()); + p.package.combinedStructsAndClasses.insert(p.package.combinedStructsAndClasses.end(), mergedpack->combinedStructsAndClasses.begin(), mergedpack->combinedStructsAndClasses.end()); + for (auto& dependencyPackage : mergedpack->dependencyPackages) + { + //skip these packages that get merged, so if 1 and 2 merge, 2 needs 1 and 1 needs 2 get skipped + if (std::ranges::find(pack.mergedPackages, dependencyPackage) == pack.mergedPackages.end()) + { + totalDependencyPackage.insert(dependencyPackage); + } + } + } + //merge mergedPackages, this is in general just 1 or 2 + p.mergedPackages.insert(p.mergedPackages.end(), pack.mergedPackages.begin(), pack.mergedPackages.end()); + p.package.packageName = name; + //add the depenencies + for (auto& dependencyPackage : totalDependencyPackage) + { + p.package.dependencyPackages.insert(dependencyPackage); + } + _newPackages.push_back(p); + + } + //no merge? just add + else + _newPackages.push_back(pack); + + progressDone++; + } + //clear the vector and overwrite + newPackages.clear(); + newPackages.insert(newPackages.end(), _newPackages.begin(), _newPackages.end()); + } while (anyMergeFound); + + progressDone = 0; + totalProgress = newPackages.size(); + + //we cant get rid of all duplicates so we do it here just to check + windows::LogWindow::Log(windows::LogWindow::log_2, "MDK GEN", "Eliminating double merges...."); + bool eraseDone = false; + do + { + //reset flag + eraseDone = false; + + //iterate over all packages + for (auto& [mergedPackages, package] : newPackages) + { + //keep a tracker, a for int i loop would do the same + int tracker = -1; + for (auto& p1 : newPackages) + { + tracker++; + //own package? skip + if (package.packageName == p1.package.packageName) + continue; + //are the merged packages a different size? then its guaranteed not a dup + if (mergedPackages.size() != p1.mergedPackages.size()) + continue; + + + //sort the merged packages + std::ranges::sort(mergedPackages); + std::ranges::sort(p1.mergedPackages); + + //not the same? skip + if (mergedPackages != p1.mergedPackages) + continue; + + //always a duplicate then + + windows::LogWindow::Log(windows::LogWindow::log_2, "MDK GEN", + "deleted %s because its same to %s", p1.package.packageName.c_str(), package.packageName.c_str()); + + //delete p1 package + newPackages.erase(newPackages.begin() + tracker); + //set this flag to recheck + eraseDone = true; + //break as we are done with this package + break; + + } + //completely break through and redo + if (eraseDone) + break; + } + } while (eraseDone); + + progressDone = 0; + totalProgress = newPackages.size(); + + //now we reorder the structs inside the package so the compiler doesnt throw errors + windows::LogWindow::Log(windows::LogWindow::log_2, "SORTER", "Reordering structs"); + + bool didReordering = false; + int kk = 0; + do + { + windows::LogWindow::Log(windows::LogWindow::log_2, "SORTER", "%d", kk++); + didReordering = false; + + //iterate through all packages + for (auto& [mergedPackages, package] : newPackages) + { + //if (package.packageName == "SlateCore") + // DebugBreak(); + //create a vector that holds all structs and classes ordered + std::vector orderedStructsAndClasses; + //package.combinedStructsAndClasses contains ALL structs and classes from ALL merged packages at this point + for (auto& item : package.combinedStructsAndClasses) + { + auto currentIt = std::ranges::find( + orderedStructsAndClasses, item); + //is the current struct not found in the ordered list? then add it + if (currentIt == orderedStructsAndClasses.end()) + { + orderedStructsAndClasses.push_back(item); + } + + currentIt = std::ranges::find( + orderedStructsAndClasses, item); + + //check if the needed struct is in the package + //if not, we are done here, if yes, we have to check if the needed struct is defined before the current struct + //otherwise compiler will throw an error + //tldr order: + // struct a + // struct b : a + // or + // struct c { a member1, a member2} + // works both for inheritance and any struct + auto fixOrder = [&](EngineStructs::Struct* neededStruct) mutable + { + //is it not in our package? then theres nothing we have to do + if (std::ranges::find(mergedPackages, neededStruct->owningPackage) == mergedPackages.end()) + return; + + //now we get the position of the item + const auto neededIt = std::ranges::find(orderedStructsAndClasses, neededStruct); + + //is it before defined? then nothing we have to do + if (neededIt <= currentIt) + return; + + //set the rorder flag + didReordering = true; + + //not in the list? + if (neededIt == orderedStructsAndClasses.end()) + { + //add it before, this ensures it will be defined before + //fix the iterator + + printf("%d - %s: agded %s before %s\n", kk, package.packageName.c_str(), neededStruct->cppName.c_str(), (*currentIt)->cppName.c_str()); + + orderedStructsAndClasses.insert(currentIt, neededStruct); + currentIt = std::ranges::find( + orderedStructsAndClasses, item); + + return; + } + + //its defined but too high up + const auto it = *neededIt; + + printf("%d - %s: added %s before %s\n", kk, package.packageName.c_str(), it->cppName.c_str(), (*currentIt)->cppName.c_str()); + orderedStructsAndClasses.erase(neededIt); + //fix the iterator + orderedStructsAndClasses.insert(currentIt, it); + currentIt = std::ranges::find( + orderedStructsAndClasses, item); + }; + + //if the class or struct has no inheritance we skip this + if (item->inherited) + { + //if the item is inherited check the super + fixOrder(item->supers[0]); + } + + + //now we do the same check for every member + for (auto& member : item->cookedMembers) + { + //if the member is not clickable its prob some bool or int + if (!member.type.clickable) + continue; + + + //these types are always pointers to classes or structs. They arent really a dependency as the compiler will know + //it will be 8 bytes large + if (member.type.propertyType == PropertyType::ObjectProperty || member.type.propertyType == PropertyType::ClassProperty) + continue; + + //is the type a unknown type? Nothing we can do about it, SDK will handle it via macro (see SDK_UNDEFINED in SDK.cpp) + //unknown type means info.valid is false + auto info = member.type.info; + if (!info || !info->valid) + continue; + + + + //if the type is function (never happens) or enum, we can ignore. + //enums are always at the top of the file + if (info->type == ObjectInfo::OI_Class || info->type == ObjectInfo::OI_Struct) + { + auto typeItem = static_cast(info->target); + fixOrder(typeItem); + } + + for(auto& sub : member.type.subTypes) + { + auto subInfo = sub.info; + if (!subInfo || !subInfo->valid) + continue; + + if (sub.propertyType == PropertyType::ObjectProperty || sub.propertyType == PropertyType::ClassProperty) + continue; + + if (subInfo->type == ObjectInfo::OI_Class || subInfo->type == ObjectInfo::OI_Struct) + { + auto typeItem = static_cast(subInfo->target); + fixOrder(typeItem); + } + } + + } + } + //we have to create new vecotr, if we erase item vector now, pointers are invalid + std::vector newitems; + for (auto& pair : orderedStructsAndClasses) + { + newitems.push_back(pair); + } + package.combinedStructsAndClasses.clear(); + package.combinedStructsAndClasses.insert(package.combinedStructsAndClasses.begin(), newitems.begin(), newitems.end()); + + progressDone++; + } + + } while (didReordering); + + + windows::LogWindow::Log(windows::LogWindow::log_2, "MDK GEN", "Reordering packages"); + std::vector orderedPackages; + + do + { + //reset flags + progressDone = 0; + didReordering = false; + + for (auto& p : newPackages) + { + progressDone++; + windows::LogWindow::Log(windows::LogWindow::log_2, "MDK GEN", "fixing package imports of %s", p.package.packageName.c_str()); + auto currentPackageIt = std::ranges::find( + orderedPackages, &p); + + //is it not in? then add + if (currentPackageIt == orderedPackages.end()) + { + orderedPackages.push_back(&p); + currentPackageIt = orderedPackages.end() - 1; + } + for (auto& neighbour : p.package.dependencyPackages) + { + //iterate through all merged package until we find the neighbour this class needs, so + //we can ensure the dependeny is before our current packafwe + for (auto& p_ : newPackages) + { + //check the mergedpackages if this is the package were actually looking for + //we have to look through the merged because maybe the package we need as dependency is merged with another package + if (std::ranges::find(p_.mergedPackages, neighbour) != p_.mergedPackages.end()) + { + auto neededPackageIt = std::ranges::find(orderedPackages, &p_); + + //is the needed package too high up? + if (neededPackageIt > currentPackageIt) //also if that merge is not in the list before + { + //set reordering flag to recheck the loop again + didReordering = true; + //not in? + if (neededPackageIt == orderedPackages.end()) + { + //printf("inserting %s before %s because it needs it and isnt in it or above\n", p_.package.packageName.c_str(), p.package.packageName.c_str()); + orderedPackages.insert(currentPackageIt, &p_); + currentPackageIt = std::ranges::find( + orderedPackages, &p); + break; + //currentPackageIt = orderedPackages.end() - 1; + } + + //looks like its in but not low enough + auto it = *neededPackageIt; + //erase the entry + orderedPackages.erase(neededPackageIt); + //get the current it + currentPackageIt = std::ranges::find( + orderedPackages, &p); + //and insert it before + orderedPackages.insert(currentPackageIt, it); + currentPackageIt = std::ranges::find( + orderedPackages, &p); + break; + //std::rotate(currentPackageIt, neededPackageIt, neededPackageIt + 1); + //printf("inserted %s before %s because it was too high up\n", newNeededPackageIt.operator*()->package.packageName.c_str(), currentPackageIt.operator*()->package.packageName.c_str()); + } + } + + } + } + } + } while (didReordering); + + return orderedPackages; +} \ No newline at end of file diff --git a/UEDumper/Engine/UEClasses/UnrealClasses.h b/UEDumper/Engine/UEClasses/UnrealClasses.h index dc54cd16..b9a94e46 100644 --- a/UEDumper/Engine/UEClasses/UnrealClasses.h +++ b/UEDumper/Engine/UEClasses/UnrealClasses.h @@ -36,13 +36,13 @@ class UObject int32_t InternalIndex = 0; /** Class the object belongs to. */ - class UClass* ClassPrivate = nullptr; + class UClass* ClassPrivate = nullptr; /** Name of this object */ FName NamePrivate = FName(); /** Object this object resides in. */ - class UObject* OuterPrivate = nullptr; + class UObject* OuterPrivate = nullptr; static std::string typeName() { return "UObject"; } @@ -197,10 +197,10 @@ class UStruct : public UField #if UE_VERSION < UE_5_03 /** Struct this inherits from, may be null */ - UStruct* SuperStruct; + UStruct* SuperStruct; /** Pointer to start of linked list of child fields */ - UField* Children; + UField* Children; #else //commented out because its the same (most of the cases) @@ -269,7 +269,7 @@ class UStruct : public UField /** Array of object references embedded in script code and referenced by FProperties. Mirrored for easy access by realtime garbage collection code */ TArray> ScriptAndPropertyObjectReferences; - + #endif //things are defined easier because theres no reason implementing all classes @@ -379,11 +379,11 @@ class UFunction : public UStruct /** pointer to first local struct property in this UFunction that contains defaults */ - UProperty* FirstPropertyToInit; + UProperty* FirstPropertyToInit; #if UE_BLUEPRINT_EVENTGRAPH_FASTCALLS /** The event graph this function calls in to (persistent) */ - UFunction* EventGraphFunction; + UFunction* EventGraphFunction; /** The state offset inside of the event graph (persistent) */ int32_t EventGraphCallOffset; @@ -619,7 +619,7 @@ class UObjectPropertyBase : public UProperty public: using UProperty::UProperty; - UClass* PropertyClass; + UClass* PropertyClass; UClass* getPropertyClass() const; @@ -846,7 +846,7 @@ class UEnumProperty : public UProperty using UProperty::UProperty; uintptr_t UnderlyingProp; - UEnum* Enum; + UEnum* Enum; UEnum* getEnum() const; @@ -982,16 +982,16 @@ class FProperty : public FField FName RepNotifyFunc; /** In memory only: Linked list of properties from most-derived to base **/ - FProperty* PropertyLinkNext; + FProperty* PropertyLinkNext; /** In memory only: Linked list of object reference properties from most-derived to base **/ - FProperty* NextRef; + FProperty* NextRef; /** In memory only: Linked list of properties requiring destruction. Note this does not include things that will be destroyed byt he native destructor **/ - FProperty* DestructorLinkNext; + FProperty* DestructorLinkNext; /** In memory only: Linked list of properties requiring post constructor initialization.**/ - FProperty* PostConstructLinkNext; + FProperty* PostConstructLinkNext; //static std::string typeName() { return "UProperty"; } //FIXMEEEE @@ -999,6 +999,7 @@ class FProperty : public FField int32_t getOffset() const; + // this generates the field type for the given type. however, this will not add the Objectinfo as this has to be done manually at the very end of generation! fieldType getType(); }; diff --git a/UEDumper/Engine/Userdefined/StructDefinitions.h b/UEDumper/Engine/Userdefined/StructDefinitions.h index a5a08c4e..b18223bf 100644 --- a/UEDumper/Engine/Userdefined/StructDefinitions.h +++ b/UEDumper/Engine/Userdefined/StructDefinitions.h @@ -46,7 +46,7 @@ inline void overrideStructs() int uObjectOffset = 0; //just creating a variable for the offset that increases with the members (makes it a lot easier) uObject.definedMembers = std::vector{ // a void** is not clickable! The property is a ObjectProperty indicates that its a object (which means basically anything) - {{false, PropertyType::ObjectProperty, "void**"}, "vtable", uObjectOffset, 8}, + {{false, PropertyType::ObjectProperty, "uint64_t"}, "vtable", uObjectOffset, 8}, // a enum is clickable! The property is a EnumProperty because, well, its a enum. We can freely use the typename EObjectFlags even if // it doesnt exist in the engine. This is no problem. If we click it, nothing will happen, a console message will just appear. {{true, PropertyType::EnumProperty, "EObjectFlags"},"ObjectFlags", uObjectOffset += 8, sizeof(EObjectFlags)}, diff --git a/UEDumper/Engine/Userdefined/UEdefinitions.h b/UEDumper/Engine/Userdefined/UEdefinitions.h index 9e735e5b..60274c0b 100644 --- a/UEDumper/Engine/Userdefined/UEdefinitions.h +++ b/UEDumper/Engine/Userdefined/UEdefinitions.h @@ -29,8 +29,10 @@ #define RELEASE_1_6_FINAL 11 #define RELEASE_1_7_BETA 12 #define RELEASE_1_7_FINAL 13 +#define RELEASE_1_8_BETA 14 +#define RELEASE_1_8_FINAL 15 -#define DUMPER_VERSION RELEASE_1_7_BETA +#define DUMPER_VERSION RELEASE_1_8_BETA /// This file contains engine definitions that you have to edit depending on the game! @@ -57,13 +59,13 @@ /* UE version settings */ //set your games ue version -#define UE_VERSION UE_5_03 +#define UE_VERSION UE_5_02 /* FName settings */ - //in case the FNames are encrypted, it will use your decryption function in FName_decryption.h +//in case the FNames are encrypted, it will use your decryption function in FName_decryption.h #define USE_FNAME_ENCRYPTION FALSE //set this to TRUE if your game uses WITH_CASE_PRESERVING_NAME (WITH_EDITORONLY_DATA) diff --git a/UEDumper/Engine/structs.h b/UEDumper/Engine/structs.h index 7c8c24e0..c29e5fdd 100644 --- a/UEDumper/Engine/structs.h +++ b/UEDumper/Engine/structs.h @@ -65,23 +65,23 @@ struct FName /** Index into the Names array (used to find String portion of the string/number pair used for comparison) */ FNameEntryId ComparisonIndex = 0; - #if UE_VERSION >= UE_5_01 - #if !UE_FNAME_OUTLINE_NUMBER +#if UE_VERSION >= UE_5_01 +#if !UE_FNAME_OUTLINE_NUMBER /** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */ FNameEntryId Number = 0; - #endif - #endif +#endif +#endif - #if WITH_CASE_PRESERVING_NAME +#if WITH_CASE_PRESERVING_NAME /** Index into the Names array (used to find String portion of the string/number pair used for display) */ FNameEntryId DisplayIndex = 0; - #endif +#endif - #if UE_VERSION < UE_5_01 +#if UE_VERSION < UE_5_01 /** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */ - int32_t Number = 0; - #endif + int32_t Number = 0; +#endif }; struct FUObjectItem @@ -300,7 +300,7 @@ class TPair , Second(Value) { } - TPair(){}; + TPair() {}; public: FORCEINLINE KeyType& Key() @@ -402,7 +402,7 @@ struct FObjectPtr union { - uint64_t Handle; + uint64_t Handle; // DebugPtr allows for easier dereferencing of a resolved FObjectPtr in watch windows of debuggers. If the address in the pointer // is an odd/uneven number, that means the object reference is unresolved and you will not be able to dereference it successfully. uint64_t DebugPtr; @@ -414,17 +414,17 @@ template struct TObjectPtr { public: - TObjectPtr() - : ObjectPtr() - { - } - union - { - FObjectPtr ObjectPtr; - // DebugPtr allows for easier dereferencing of a resolved TObjectPtr in watch windows of debuggers. If the address in the pointer - // is an odd/uneven number, that means the object reference is unresolved and you will not be able to dereference it successfully. - T* DebugPtr; - }; + TObjectPtr() + : ObjectPtr() + { + } + union + { + FObjectPtr ObjectPtr; + // DebugPtr allows for easier dereferencing of a resolved TObjectPtr in watch windows of debuggers. If the address in the pointer + // is an odd/uneven number, that means the object reference is unresolved and you will not be able to dereference it successfully. + T* DebugPtr; + }; }; // https://github.com/EpicGames/UnrealEngine/blob/5.3/Engine/Source/Runtime/CoreUObject/Public/UObject/ObjectPtr.h#L643 diff --git a/UEDumper/Frontend/Windows/HelloWindow.cpp b/UEDumper/Frontend/Windows/HelloWindow.cpp index 8b309e9a..a5b984f0 100644 --- a/UEDumper/Frontend/Windows/HelloWindow.cpp +++ b/UEDumper/Frontend/Windows/HelloWindow.cpp @@ -13,7 +13,7 @@ windows::HelloWindow::HelloWindow() bool windows::HelloWindow::render() { - if(alreadyCompleted) return true; + if (alreadyCompleted) return true; static char processName[100] = { 0 }; static char projectName[50] = { 0 }; @@ -25,9 +25,9 @@ bool windows::HelloWindow::render() const ImVec2 bigWindow = IGHelper::getWindowSize(); - + //if we dont show engine infos render the new project child - if(!showEngineInfos) + if (!showEngineInfos) { constexpr auto childSize = ImVec2(750, 300); ImGui::SetCursorPos(ImVec2(bigWindow.x / 2 - childSize.x / 2, bigWindow.y / 2 - childSize.y / 2)); @@ -50,7 +50,7 @@ bool windows::HelloWindow::render() ImGui::SameLine(); //only allow any interaction if projectname > 4 and there is no created dir - if(!createdDir && (ImGui::Button(merge(ICON_FA_FOLDER, " Create##createProject")) && strlen(projectName) > 4)) + if (!createdDir && (ImGui::Button(merge(ICON_FA_FOLDER, " Create##createProject")) && strlen(projectName) > 4)) { createdDir = EngineSettings::setProjectName(projectName); if (!createdDir) @@ -58,7 +58,7 @@ bool windows::HelloWindow::render() else memset(errorText, 0, sizeof(errorText)); } - if(createdDir) + if (createdDir) { ImGui::Text("Created!"); } @@ -122,7 +122,7 @@ bool windows::HelloWindow::render() else if (ImGui::Button(merge(ICON_FA_SEARCH, " Find##FindProcess"))) Memory::load(std::string(processName)); - + } @@ -130,20 +130,20 @@ bool windows::HelloWindow::render() if (ImGui::Button("Engine settings")) showEngineInfos = true; - if(strlen(errorText) > 0) + if (strlen(errorText) > 0) { ImGui::PushStyleColor(ImGuiCol_Text, IGHelper::Colors::classOrange); ImGui::TextWrapped(errorText); ImGui::PopStyleColor(); } - + if (Memory::getStatus() == Memory::MemoryStatus::loaded) { ImGui::Text("Process ID: 0x%05X", Memory::getProcessID()); ImGui::Text("Base Address: 0x%p", Memory::getBaseAddress()); } - if(Memory::getStatus() == Memory::MemoryStatus::loaded && createdDir) + if (Memory::getStatus() == Memory::MemoryStatus::loaded && createdDir) { ImGui::SetCursorPosX(40); ImGui::SetCursorPosY(240); @@ -166,7 +166,7 @@ bool windows::HelloWindow::render() ImGui::SetCursorPos(ImVec2(bigWindow.x / 2 - childSize.x / 2, bigWindow.y / 2 - childSize.y / 2 - 80)); EngineSettings::drawEngineSettings(childSize, &showEngineInfos); } - + return false; } @@ -183,4 +183,4 @@ void windows::HelloWindow::setCompleted() void windows::HelloWindow::renderProjectPopup() { -} +} \ No newline at end of file diff --git a/UEDumper/Frontend/Windows/LiveEditor.cpp b/UEDumper/Frontend/Windows/LiveEditor.cpp index 4d378901..b310c37c 100644 --- a/UEDumper/Frontend/Windows/LiveEditor.cpp +++ b/UEDumper/Frontend/Windows/LiveEditor.cpp @@ -73,18 +73,18 @@ void windows::LiveEditor::renderAddAddress() ImGui::EndDisabled(); return; } - + LogWindow::Log(LogWindow::log_0, "LIVE", "Looking for %s...", structName.c_str()); const auto info = EngineCore::getInfoOfObject(structName); - if(!info) + if (!info || !info->valid) { sprintf_s(errorText, "Object not found in the packages!"); ImGui::EndDisabled(); return; } - tab.struc = static_cast(info.target); - tab.isClass = info.type == EngineCore::ObjectInfo::OI_Class; + tab.struc = static_cast(info->target); + tab.isClass = info->type == ObjectInfo::OI_Class; tab.found = true; memset(errorText, 0, sizeof(errorText)); @@ -96,10 +96,10 @@ void windows::LiveEditor::renderAddAddress() memset(name, 0, sizeof(name)); memset(caddress, 0, sizeof(caddress)); return; - + } sprintf_s(errorText, "Only hexadecimal characters supported!"); - + } ImGui::EndDisabled(); } @@ -112,25 +112,25 @@ void windows::LiveEditor::renderAddOffset() static uint64_t followAddress = 0; static bool checked = false; static bool once = false; - + ImGui::TextWrapped("Add a offset you added in Offset.h. Any Offset or sig requires to lead to a pointer that points " - "to the UObject. e.g offset leads to 0x7FFF8001ABC where a pointer is pointing to 0x12333445C. Any time the " - "pointer points to a different address, the live editor will update too."); + "to the UObject. e.g offset leads to 0x7FFF8001ABC where a pointer is pointing to 0x12333445C. Any time the " + "pointer points to a different address, the live editor will update too."); + + - - std::vector offsetsVec{}; - for(const auto& offset : EngineCore::getOffsets()) + for (const auto& offset : EngineCore::getOffsets()) { if (offset.flag & OFFSET_LIVE_EDITOR) offsetsVec.push_back(offset); } - if(offsetsVec.size() == 0) + if (offsetsVec.size() == 0) { ImGui::TextColored(IGHelper::Colors::red, "You have no offsets deined that have the OFFSET_LIVE_EDITOR flag."); return; } - + static int selector = 0; auto check = [&]() mutable { @@ -156,8 +156,8 @@ void windows::LiveEditor::renderAddOffset() once = true; check(); } - - + + if (ImGui::BeginCombo(std::string("OffsetCombo##renderAddOffset").c_str(), selector >= 0 ? offsetsVec[selector].name.c_str() : "")) { @@ -183,12 +183,12 @@ void windows::LiveEditor::renderAddOffset() if (address > 0) ImGui::Text("Offset leads to : 0x%llX", address); - if(followAddress > 0) + if (followAddress > 0) ImGui::Text("Pointer at offset pointing to: 0x%llX", followAddress); else ImGui::TextColored(IGHelper::Colors::classOrange, errorText); - + ImGui::Dummy(ImVec2(100, 0)); ImGui::SameLine(); @@ -206,7 +206,7 @@ void windows::LiveEditor::renderAddOffset() ImGui::BeginDisabled(address == 0 || followAddress == 0 || !checked); if (ImGui::Button("Add##renderAddOffset", ImVec2(120, 30))) { - + EditorTab tab; tab.type = TabTypeOffset; tab.name = std::string(displayName); @@ -218,15 +218,15 @@ void windows::LiveEditor::renderAddOffset() if (structName != "nil" && !structName.empty()) { const auto info = EngineCore::getInfoOfObject(structName); - if (!info) + if (!info || !info->valid) { sprintf_s(errorText, "Object not found in the packages!"); LogWindow::Log(LogWindow::log_2, "LIVE EDITOR", "Object not found in the packages!"); goto failed; } - tab.struc = static_cast(info.target); + tab.struc = static_cast(info->target); - tab.isClass = info.type == EngineCore::ObjectInfo::OI_Class; + tab.isClass = info->type == ObjectInfo::OI_Class; tab.origin = std::string(offsetsVec[selector].name) + "->"; tab.found = true; @@ -250,7 +250,7 @@ void windows::LiveEditor::renderAddOffset() failed: checked = false; - + } ImGui::EndDisabled(); } @@ -258,9 +258,9 @@ void windows::LiveEditor::renderAddOffset() void windows::LiveEditor::renderAddInspector() { - if(!bRenderAddInspector) + if (!bRenderAddInspector) return; - + static ImVec2 smallWindow = ImVec2(500, 270); const ImVec2 bigWindow = IGHelper::getWindowSize(); @@ -313,148 +313,148 @@ void windows::LiveEditor::drawReadWriteableField(LiveMemory::MemoryBlock* block, switch (type.propertyType) { case PropertyType::Int8Property: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S8, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S8, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::Int16Property: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S16, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S16, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::IntProperty: + { + auto val = block->read(offset); + if (ImGui::DragInt(varSecret.c_str(), &val)) { - auto val = block->read(offset); - if (ImGui::DragInt(varSecret.c_str(), &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::Int64Property: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S64, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S64, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::UInt16Property: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U16, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U16, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::UInt32Property: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U32, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U32, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::UInt64Property: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U64, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U64, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); } + break; + } case PropertyType::DoubleProperty: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_Double, &val)) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_Double, &val)) + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); + } + break; + } + case PropertyType::FloatProperty: + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_Float, &val)) + { + block->write(offset, val); + Memory::write(block->gameAddress + offset, val); + } + break; + } + case PropertyType::ByteProperty: + case PropertyType::BoolProperty: + { + //these types should have a scalar + if (type.name == TYPE_CHAR) + { + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S8, &val)) { block->write(offset, val); Memory::write(block->gameAddress + offset, val); } break; } - case PropertyType::FloatProperty: + if (type.name == TYPE_UCHAR) { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_Float, &val)) + auto val = block->read(offset); + if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U8, &val)) { block->write(offset, val); Memory::write(block->gameAddress + offset, val); } break; } - case PropertyType::ByteProperty: - case PropertyType::BoolProperty: + //or the basic boolean support + bool value; + if (isBit) { - //these types should have a scalar - if(type.name == TYPE_CHAR) - { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_S8, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; - } - if (type.name == TYPE_UCHAR) - { - auto val = block->read(offset); - if (ImGui::DragScalar(varSecret.c_str(), ImGuiDataType_U8, &val)) - { - block->write(offset, val); - Memory::write(block->gameAddress + offset, val); - } - break; - } - //or the basic boolean support - bool value; - if (isBit) - { - value = (block->read(offset) >> bitOffset & 1) == 1; - } + value = (block->read(offset) >> bitOffset & 1) == 1; + } + else + { + value = block->read(offset); + } + if (ImGui::Checkbox(std::string("##" + secret + std::to_string(offset) + ":" + std::to_string(bitOffset)).c_str(), &value)) + { + char c = Memory::read(block->gameAddress + offset); + if (value) //upon clicking we obviously wanna change the value, so we take the inverted + c |= (1 << bitOffset); // Set nth bit to 1 else - { - value = block->read(offset); - } - if (ImGui::Checkbox(std::string("##" + secret + std::to_string(offset) + ":" + std::to_string(bitOffset)).c_str(), &value)) - { - char c = Memory::read(block->gameAddress + offset); - if (value) //upon clicking we obviously wanna change the value, so we take the inverted - c |= (1 << bitOffset); // Set nth bit to 1 - else - c &= ~(1 << bitOffset); // Set nth bit to 0 - Memory::write(block->gameAddress + offset, c); - block->write(offset, c); - } - break; + c &= ~(1 << bitOffset); // Set nth bit to 0 + Memory::write(block->gameAddress + offset, c); + block->write(offset, c); } + break; + } default: ImGui::PopItemWidth(); @@ -477,7 +477,7 @@ void windows::LiveEditor::drawMemberArrayProperty(const EngineStructs::Member& m { ImGui::PopStyleColor(); //if the TArray does not have 1 subtype its broken - if(member.type.subTypes.size() != 1) + if (member.type.subTypes.size() != 1) { ImGui::Text("Broken TArray!"); ImGui::TreePop(); @@ -501,9 +501,9 @@ void windows::LiveEditor::drawMemberArrayProperty(const EngineStructs::Member& m const auto arr = block->read>(member.offset + innerOffset); ImGui::SameLine(); ImGui::TextColored(IGHelper::Colors::grayedOut, "(Count: %d) 0x%llX", arr.Count, arr.Data); - + //nothing to show if data is invalid - if(arr.Data == nullptr || arr.Count <= 0) + if (arr.Data == nullptr || arr.Count <= 0) { ImGui::TreePop(); return; @@ -526,9 +526,9 @@ void windows::LiveEditor::drawMemberArrayProperty(const EngineStructs::Member& m ImGui::TreePop(); return; } - + EngineStructs::Struct* st; - if(!isValidStructName(reinterpret_cast(arr.Data), member.type.subTypes[0].name, st)) + if (!isValidStructName(reinterpret_cast(arr.Data), member.type.subTypes[0].name, st)) { ImGui::TextColored(IGHelper::Colors::red, "Struct or Class doesnt exist in the SDK!"); ImGui::TreePop(); @@ -549,7 +549,7 @@ void windows::LiveEditor::drawMemberArrayProperty(const EngineStructs::Member& m //we get the first pointer so we can check what class the array indexes really are (tarray lies just like pointers) const uint64_t firstIndexPtr = arrBlock->read(0); //look for the struct - if(!isValidStructName(firstIndexPtr, member.type.subTypes[0].name, st, true)) + if (!isValidStructName(firstIndexPtr, member.type.subTypes[0].name, st, true)) { //this should never fail! ImGui::TextColored(IGHelper::Colors::red, "Struct or Class doesnt exist in the SDK!"); @@ -581,7 +581,7 @@ void windows::LiveEditor::drawMemberArrayProperty(const EngineStructs::Member& m //add the * ImGui::Text("*"); ImGui::SameLine(); - + ImGui::TextColored(IGHelper::Colors::varBlue, std::string(addressBuf).c_str()); //add a memory block for the index struct LiveMemory::addNewBlock(objPtr, st->size); @@ -613,7 +613,7 @@ void windows::LiveEditor::drawMemberArrayProperty(const EngineStructs::Member& m } //then draw every index for (int i = 0; i < arr.Count; i++) - drawStructProperty(st, member.name + "_" + std::to_string(i), subBlock, + drawStructProperty(st, member.name + "_" + std::to_string(i), subBlock, appendSecret(secret, member.type.name + std::to_string(reinterpret_cast(arr.Data)), i), st->size * i + innerOffset); } ImGui::TreePop(); @@ -673,7 +673,7 @@ void windows::LiveEditor::drawStructProperty(const EngineStructs::Struct* struc, return; } //show a cool color pallette - if(struc->cppName == "FLinearColor") + if (struc->cppName == "FLinearColor") { ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10); ImGui::TextColored(IGHelper::Colors::classGreen, struc->cppName.c_str()); @@ -749,7 +749,7 @@ void windows::LiveEditor::drawNonclickableMember(const EngineStructs::Member& me const int combinedOffset = member.offset + innerOffset; //sometimes we dont wanna display the typename - if(simple) + if (simple) ImGui::TextColored(IGHelper::Colors::varPink, "%s:", member.name.c_str()); else { @@ -765,7 +765,7 @@ void windows::LiveEditor::drawNonclickableMember(const EngineStructs::Member& me ImGui::Spinner(); else drawReadWriteableField(block, combinedOffset, member.bitOffset, member.isBit, member.type, secret); - + } void windows::LiveEditor::drawMemberObjectProperty(const EngineStructs::Member& member, LiveMemory::MemoryBlock* block, const std::string& secret, int innerOffset) @@ -783,7 +783,7 @@ void windows::LiveEditor::drawMemberObjectProperty(const EngineStructs::Member& ImGui::SameLine(); ImGui::TextColored(IGHelper::Colors::varPink, member.name.c_str()); - + ImGui::SameLine(); ImGui::TextColored(IGHelper::colToVec(255, 255, 255, 150), "0x%llX", memberPtr); //check whether its a valid struct and look for the best because SDKS dont point to the right one @@ -792,7 +792,7 @@ void windows::LiveEditor::drawMemberObjectProperty(const EngineStructs::Member& if (memberPtr != 0 && isValidStructName(memberPtr, member.type.name, newStruct, true)) { //add the new block so we can track the data for it - if(!LiveMemory::addNewBlock(memberPtr, newStruct->size)) + if (!LiveMemory::addNewBlock(memberPtr, newStruct->size)) { LogWindow::Log(LogWindow::log_2, "LIVEEDITOR", "Could not add new memory block for %p (%s) with size %d!", memberPtr, member.name, newStruct->size); } @@ -825,9 +825,9 @@ void windows::LiveEditor::drawTEnumAsByteProperty(const EngineStructs::Member& m // Use std::transform to extract the first elements of each pair std::ranges::transform(subEnum->members, std::back_inserter(items), - [](const std::pair& pair) { - return pair.first; - }); + [](const std::pair& pair) { + return pair.first; + }); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10); @@ -895,7 +895,7 @@ void windows::LiveEditor::drawMembers(const EngineStructs::Struct* struc, uint64 ImGui::TextColored(IGHelper::Colors::red, "Invalid memory block??"); return; } - + //draw the secret for debug purposes. //ImGui::TextColored(IGHelper::Colors::grayedOut, secret.c_str()); @@ -910,7 +910,7 @@ void windows::LiveEditor::drawMembers(const EngineStructs::Struct* struc, uint64 //get the current cursor pos so we can use it below const auto posX = ImGui::GetCursorPosX(); //also draw the bit if it is one - if(member.isBit) + if (member.isBit) ImGui::TextColored(IGHelper::Colors::red, "+%04X:%d", member.offset + innerOffset, member.bitOffset); else ImGui::TextColored(IGHelper::Colors::red, "+%04X", member.offset + innerOffset); @@ -937,19 +937,19 @@ void windows::LiveEditor::drawMembers(const EngineStructs::Struct* struc, uint64 //for structs inside a struct that are directly in it //applies to FVector etc too case PropertyType::StructProperty: - { - //we have to resolve the struct here + { + //we have to resolve the struct here EngineStructs::Struct* subStruct; - if (isValidStructName(address, member.type.name, subStruct)) - { - //draw the substruct - //also inneroffset (thats always needed) plus member offset because the struct is within the current struct - drawStructProperty(subStruct, member.name, block, secret, innerOffset + member.offset); - } - break; + if (isValidStructName(address, member.type.name, subStruct)) + { + //draw the substruct + //also inneroffset (thats always needed) plus member offset because the struct is within the current struct + drawStructProperty(subStruct, member.name, block, secret, innerOffset + member.offset); } - + break; + } + case PropertyType::NameProperty: //support for FNames //all props that are directly displayed need a 10 indent @@ -964,15 +964,15 @@ void windows::LiveEditor::drawMembers(const EngineStructs::Struct* struc, uint64 //TEnumAsByte support case PropertyType::ByteProperty: - { + { EngineStructs::Enum* subEnum; - //we have to use the first subtype which is the enum name - if(member.type.subTypes.size() == 1 &&isValidEnumName(member.type.subTypes[0].name, subEnum)) - { - drawTEnumAsByteProperty(member, subEnum, block, secret, innerOffset); - } - break; + //we have to use the first subtype which is the enum name + if (member.type.subTypes.size() == 1 && isValidEnumName(member.type.subTypes[0].name, subEnum)) + { + drawTEnumAsByteProperty(member, subEnum, block, secret, innerOffset); } + break; + } default: //no support? Just draw the typename and name //all props that are directly displayed need a 10 indent @@ -993,10 +993,10 @@ void windows::LiveEditor::drawMembers(const EngineStructs::Struct* struc, uint64 //non clickable types can be (mostly) directly modified like ints floats etc //all props that are directly displayed need a 10 indent - ImGui::SetCursorPosX(ImGui::GetCursorPosX()+ 10); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10); drawNonclickableMember(member, block, innerOffset, secret); } - + } @@ -1012,11 +1012,11 @@ void windows::LiveEditor::renderStruct(const EngineStructs::Struct* struc, uint6 } //print the origin or the address ImGui::SameLine(); - if(origin.length() > 0) + if (origin.length() > 0) ImGui::TextColored(IGHelper::Colors::red, origin.c_str()); else ImGui::TextColored(IGHelper::Colors::red, "0x%llX", address); - + ImGui::SameLine(); //copy the struct name if (ImGui::Button(std::string(std::string(ICON_FA_CLIPBOARD) + "##" + struc->cppName + secret).c_str())) @@ -1036,10 +1036,10 @@ void windows::LiveEditor::renderStruct(const EngineStructs::Struct* struc, uint6 //ustruct //.. //finalClass - for(int i = struc->supers.size(); i > 0; i--) + for (int i = struc->supers.size(); i > 0; i--) { - auto& super = struc->supers[i-1]; - + auto& super = struc->supers[i - 1]; + EngineStructs::Struct* superStruct; //find the strucr for the given name //dont look for best because we specifically want that struct @@ -1068,7 +1068,7 @@ void windows::LiveEditor::renderStruct(const EngineStructs::Struct* struc, uint6 } //two code pieces here, one we have to indicate via a treenode our real (in this case) uworld class //but if there are no other supers (like uobject) we dont need a treenode and can draw it directly (just like below) - if(struc->supers.size() > 0) + if (struc->supers.size() > 0) { ImGui::PushStyleColor(ImGuiCol_Text, IGHelper::Colors::classGreen); //create a non indent treenode @@ -1077,7 +1077,7 @@ void windows::LiveEditor::renderStruct(const EngineStructs::Struct* struc, uint6 ImGui::PopStyleColor(); //draw our struct drawMembers(struc, address, secret); - + } else ImGui::PopStyleColor(); @@ -1093,14 +1093,14 @@ void windows::LiveEditor::renderStruct(const EngineStructs::Struct* struc, uint6 bool windows::LiveEditor::isValidStructName(uint64_t classPointer, const std::string& CName, EngineStructs::Struct*& outStruct, bool lookForBest) { - if(classPointer != 0 && guessSuperClass && lookForBest) + if (classPointer != 0 && guessSuperClass && lookForBest) { //get the super class by getting the uobjects class name std::string superName = ""; bool found = false; //already searched this address? - if(realSuperClassCache.contains(classPointer)) + if (realSuperClassCache.contains(classPointer)) { superName = realSuperClassCache[classPointer]; found = true; @@ -1110,41 +1110,41 @@ bool windows::LiveEditor::isValidStructName(uint64_t classPointer, const std::st //we cannot use enginecores functions because some objets arent in the object list generated //for the SDK! So we have to do it all here and (most likely) read data multiple times const auto obj = ObjectsManager::getUObject(classPointer); - if(obj->getClass()) + if (obj->getClass()) superName = obj->getClass()->getCName(); } - if(superName == "nil" || superName.empty()) + if (superName == "nil" || superName.empty()) goto tryAgain; const auto info = EngineCore::getInfoOfObject(superName); - if(!info || !(info.type == EngineCore::ObjectInfo::OI_Class || info.type == EngineCore::ObjectInfo::OI_Struct)) //invalid! + if (!info || !info->valid || !(info->type == ObjectInfo::OI_Class || info->type == ObjectInfo::OI_Struct)) //invalid! goto tryAgain; - outStruct = static_cast(info.target); - + outStruct = static_cast(info->target); + //found it! Adding... - if(!found) + if (!found) realSuperClassCache.insert(std::pair(classPointer, superName)); return true; } - tryAgain: +tryAgain: const auto info = EngineCore::getInfoOfObject(CName); - if(!info) + if (!info || !info->valid) return false; - outStruct = static_cast(info.target); - + outStruct = static_cast(info->target); + return true; } bool windows::LiveEditor::isValidEnumName(const std::string& CName, EngineStructs::Enum*& enu) { const auto info = EngineCore::getInfoOfObject(CName); - if (!info || info.type != EngineCore::ObjectInfo::OI_Enum) //invalid! + if (!info || !info->valid || info->type != ObjectInfo::OI_Enum) //invalid! return false; - enu = static_cast(info.target); + enu = static_cast(info->target); return true; @@ -1159,7 +1159,7 @@ windows::LiveEditor::LiveEditor() bool windows::LiveEditor::renderLaunchPopup() { static bool accepted = false; - if(accepted) + if (accepted) { liveEditorStarted = true; return true; @@ -1177,17 +1177,17 @@ bool windows::LiveEditor::renderLaunchPopup() ImGui::PopStyleVar(); ImGui::TextWrapped("You are about to launch the live editor. Make sure you saved all your changes before starting " - "the the live editor, because crashes can happen very easily. This is still a beta feature!"); + "the the live editor, because crashes can happen very easily. This is still a beta feature!"); ImGui::Dummy(ImVec2(60, 0)); ImGui::SameLine(); - if(ImGui::Button("Cancel", ImVec2(120, 30))) + if (ImGui::Button("Cancel", ImVec2(120, 30))) { ImGui::End(); return true; } - + ImGui::SameLine(); - if(ImGui::Button("Continue", ImVec2(120, 30))) + if (ImGui::Button("Continue", ImVec2(120, 30))) { LiveMemory::cacheBlocks(); liveEditorStarted = true; @@ -1195,7 +1195,7 @@ bool windows::LiveEditor::renderLaunchPopup() ImGui::End(); return true; } - + ImGui::End(); return false; } @@ -1221,7 +1221,7 @@ bool windows::LiveEditor::renderQuitPopup() ImGui::PopStyleVar(); ImGui::TextWrapped("You are about to quit the live editor. All your progress is saved and all structs automatically update " - "if you make any changes in the editor."); + "if you make any changes in the editor."); ImGui::Dummy(ImVec2(60, 0)); ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120, 30))) @@ -1229,7 +1229,7 @@ bool windows::LiveEditor::renderQuitPopup() ImGui::End(); return true; } - + ImGui::SameLine(); if (ImGui::Button("Continue", ImVec2(120, 30))) { @@ -1247,12 +1247,12 @@ void windows::LiveEditor::renderLiveEditor() { ImGui::BeginChild("LiveTabChild", ImVec2(330, ImGui::GetWindowSize().y - LogWindow::getLogWindowYSize() - 40), true, ImGuiWindowFlags_NoScrollbar); - if(ImGui::Button("Add Inspector")) + if (ImGui::Button("Add Inspector")) bRenderAddInspector = true; if (ImGui::BeginListBox("##liveInspectorList", ImVec2(ImGui::GetWindowSize().x - 15, ImGui::GetWindowSize().y - 50))) { - - for(int i = 0; i < tabs.size(); i++) + + for (int i = 0; i < tabs.size(); i++) { const bool is_selected = (tabPicked == i); if (ImGui::Selectable(tabs[i].name.c_str(), is_selected)) @@ -1268,12 +1268,12 @@ void windows::LiveEditor::renderLiveEditor() ImGui::BeginChild("LiveViewerChild", ImVec2(ImGui::GetWindowSize().x - 350, ImGui::GetWindowSize().y - LogWindow::getLogWindowYSize() - 40), true, ImGuiWindowFlags_HorizontalScrollbar); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0,0 }); //looks like we have to render a struct - if(tabs.size() > 0 && tabPicked < tabs.size() && !bRenderAddInspector) + if (tabs.size() > 0 && tabPicked < tabs.size() && !bRenderAddInspector) { const auto& tab = tabs[tabPicked]; //whether the given type is a direct address, then render the struct directly for the address - if(tab.type == TabTypeAddress) + if (tab.type == TabTypeAddress) { renderStruct(tab.struc, tab.address, tab.name, tab.name + std::to_string(tab.address)); //secret is a Address, so 0x3cFFFF } @@ -1285,12 +1285,12 @@ void windows::LiveEditor::renderLiveEditor() const uint64_t address = block->read(0); //read the address //if the address is not 0 the pointer (should be) valid - if(address != 0) + if (address != 0) { //add a new block for the struct for the specific address and its size to fetch the data LiveMemory::addNewBlock(address, tab.struc->size); - char addressBuf[30] = {0}; + char addressBuf[30] = { 0 }; sprintf_s(addressBuf, "0x%llX", address); //render it! @@ -1304,7 +1304,7 @@ void windows::LiveEditor::renderLiveEditor() ImGui::Spinner(); } } - + } ImGui::PopStyleVar(); @@ -1319,7 +1319,7 @@ bool windows::LiveEditor::LiveEditorStarted() void windows::LiveEditor::renderEditPopUp() { - if(!liveEditorStarted) + if (!liveEditorStarted) return; ImGui::Spacing(); @@ -1331,15 +1331,15 @@ void windows::LiveEditor::renderEditPopUp() ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted("Normally when opening a node (e.g AActor* actor), it will display the AActor class and its inherited classes. " - "Though most of the time the UObject actually has even more classes than defined from the Engine. By enabling this, the " - "Engine will search the UObject->ClassPrivate->SuperStruct.Name to get the real class and inherited classes. Careful, this " - "might give wong results! If no SuperStruct exists, it will use the Engine definition."); + "Though most of the time the UObject actually has even more classes than defined from the Engine. By enabling this, the " + "Engine will search the UObject->ClassPrivate->SuperStruct.Name to get the real class and inherited classes. Careful, this " + "might give wong results! If no SuperStruct exists, it will use the Engine definition."); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } - if(guessSuperClass) + if (guessSuperClass) { - if(ImGui::Button("Clear superclass cache")) + if (ImGui::Button("Clear superclass cache")) { LogWindow::Log(LogWindow::log_2, "LIVE", "Cleared superclass cache!"); realSuperClassCache.clear(); @@ -1351,7 +1351,7 @@ void windows::LiveEditor::renderEditPopUp() ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted("In case you enabled the \"Guess Object by class\" you can clear the cache that holds information of UObjects " - "real classes in case you get wrong results."); + "real classes in case you get wrong results."); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } @@ -1361,4 +1361,4 @@ void windows::LiveEditor::renderEditPopUp() void windows::LiveEditor::topmostCallback() { -} +} \ No newline at end of file diff --git a/UEDumper/Frontend/Windows/LogWindow.cpp b/UEDumper/Frontend/Windows/LogWindow.cpp index 9df2cff8..ddc19feb 100644 --- a/UEDumper/Frontend/Windows/LogWindow.cpp +++ b/UEDumper/Frontend/Windows/LogWindow.cpp @@ -15,7 +15,7 @@ void windows::LogWindow::Log(logLevels level, const std::string& origin, const c log l; - char logBuffer[2001] = {0}; + char logBuffer[2001] = { 0 }; const auto now_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::tm time_info; localtime_s(&time_info, &now_time_t); @@ -25,9 +25,10 @@ void windows::LogWindow::Log(logLevels level, const std::string& origin, const c va_list args; va_start(args, fmt); vsprintf_s(logBuffer, 2000, fmt, args); - l.message = std::string(logBuffer); + memset(l.message, 0, sizeof(l.message)); + memcpy(l.message, logBuffer, 2000); l.originandTime = "[" + oss.str() + " - " + origin + "]:"; - + logs.push_back(l); } @@ -55,23 +56,23 @@ void windows::LogWindow::render() ImGui::BeginChild("LogChild", ImVec2(ImGui::GetWindowSize().x - 15, logWindowYSize - 10), true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); //ImGui::SetWindowSize(ImVec2(1000, 300), ImGuiCond_Once); - if(autoScroll) + if (autoScroll) { selectedLogRange = logSize - logSize % logRange; } - + if (ImGui::ArrowButton("logselect_btn_fastleft", ImGuiDir_LLeft)) selectedLogRange = 0; ImGui::SameLine(); - if(ImGui::ArrowButton("logselect_btn_left", ImGuiDir_Left) && selectedLogRange >= logRange) + if (ImGui::ArrowButton("logselect_btn_left", ImGuiDir_Left) && selectedLogRange >= logRange) selectedLogRange -= logRange; ImGui::SameLine(); - if(ImGui::ArrowButton("logselect_btn_right", ImGuiDir_Right) && selectedLogRange + logRange < logSize) + if (ImGui::ArrowButton("logselect_btn_right", ImGuiDir_Right) && selectedLogRange + logRange < logSize) selectedLogRange += logRange; ImGui::SameLine(); if (ImGui::ArrowButton("logselect_btn_fastright", ImGuiDir_RRight)) { - while(selectedLogRange + logRange < logSize) + while (selectedLogRange + logRange < logSize) { selectedLogRange += logRange; } @@ -95,7 +96,7 @@ void windows::LogWindow::render() logWindowYSize = 600; } ImGui::SameLine(); - + ImGui::SetCursorPosX(ImGui::GetWindowSize().x - ImGui::CalcTextSize("Showing 999 logs").x - 80); if (ImGui::ArrowButton("logrange_btn_left", ImGuiDir_Left) && logRange >= 40) @@ -105,30 +106,30 @@ void windows::LogWindow::render() logRange += 20; ImGui::SameLine(); ImGui::Text("Showing %d logs", logRange); - + char buf[2501] = { 0 }; - if(ImGui::BeginListBox("##loglistbox", ImVec2(ImGui::GetWindowSize().x - 15, ImGui::GetWindowSize().y - 50))) + if (ImGui::BeginListBox("##loglistbox", ImVec2(ImGui::GetWindowSize().x - 15, ImGui::GetWindowSize().y - 50))) { for (int i = selectedLogRange; i < logSize && i < selectedLogRange + logRange; i++) { const bool is_selected = (selectedLog == i); memset(buf, 0, 2500); - sprintf_s(buf, 2500, "%d %s %s", i, logs[i].originandTime.c_str(), logs[i].message.c_str()); + sprintf_s(buf, 2500, "%d %s %s", i, logs[i].originandTime.c_str(), logs[i].message); if (ImGui::Selectable(buf, is_selected)) { selectedLog = i; } if (is_selected && ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("%s", logs[i].message.c_str()); + ImGui::Text("%s", logs[i].message); ImGui::EndTooltip(); } } - if(oldSize != logs.size()) + if (oldSize != logs.size()) { ImGui::SetScrollHereY(1.0f); oldSize = logs.size(); } - + ImGui::EndListBox(); } @@ -137,10 +138,10 @@ void windows::LogWindow::render() void windows::LogWindow::renderEditPopup() { - if(ImGui::Button("Export Log")) + if (ImGui::Button("Export Log")) { std::ofstream file(EngineSettings::getWorkingDirectory() / "log.txt"); - for(size_t i = 0; i < logs.size(); i++) + for (size_t i = 0; i < logs.size(); i++) { file << i << " " << logs[i].originandTime << " " << logs[i].message << std::endl; } @@ -163,4 +164,4 @@ float windows::LogWindow::getLogWindowYSize() void windows::LogWindow::topmostCallback() { -} +} \ No newline at end of file diff --git a/UEDumper/Frontend/Windows/LogWindow.h b/UEDumper/Frontend/Windows/LogWindow.h index 7712bd79..aa54c23b 100644 --- a/UEDumper/Frontend/Windows/LogWindow.h +++ b/UEDumper/Frontend/Windows/LogWindow.h @@ -32,7 +32,7 @@ namespace windows struct log { std::string originandTime; - std::string message; + char message[2500]; }; static inline std::vector logs{}; static inline int oldSize = 0; @@ -42,13 +42,13 @@ namespace windows static inline int logRange = 100; static inline bool autoScroll = true; - static inline float logWindowYSize = 310; + static inline float logWindowYSize = 250; static inline int logLevel = LOGLEVEL_ALL_DEBUG_MESSAGES; public: - + LogWindow(); diff --git a/UEDumper/Frontend/Windows/PackageViewerWindow.cpp b/UEDumper/Frontend/Windows/PackageViewerWindow.cpp index 9ecc46ad..c071cdab 100644 --- a/UEDumper/Frontend/Windows/PackageViewerWindow.cpp +++ b/UEDumper/Frontend/Windows/PackageViewerWindow.cpp @@ -451,7 +451,7 @@ void windows::PackageViewerWindow::setOpenTabsClosed() } -void windows::PackageViewerWindow::updateNavBar(NavigationTab& navtab, void* itemSelected, EngineCore::ObjectInfo::ObjectType typeSelected) +void windows::PackageViewerWindow::updateNavBar(NavigationTab& navtab, void* itemSelected, ObjectInfo::ObjectType typeSelected) { //dont do anything if the user tries to go into the tab that is currently the tab const auto currentTab = navtab.tabIndex[navtab.currentVecIndex]; @@ -471,10 +471,10 @@ void windows::PackageViewerWindow::updateNavBar(NavigationTab& navtab, void* ite bool windows::PackageViewerWindow::openTabFromCName(const std::string& name) { const auto info = EngineCore::getInfoOfObject(name); - if (!info) + if (!info || !info->valid) return false; - createTab(info.target, info.type); + createTab(info->target, info->type); return true; } @@ -529,7 +529,7 @@ void windows::PackageViewerWindow::renderTabs() ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 730); ImGui::PushItemWidth(278); //disabled if it snot a struct or class. I mean who needs enum search? And functions is useless, use the search top left.... - ImGui::BeginDisabled(Tabs[currentTab].typeSelected != EngineCore::ObjectInfo::OI_Class && Tabs[currentTab].typeSelected != EngineCore::ObjectInfo::OI_Struct); + ImGui::BeginDisabled(Tabs[currentTab].typeSelected != ObjectInfo::OI_Class && Tabs[currentTab].typeSelected != ObjectInfo::OI_Struct); std::string beforeTest = std::string(Tabs[currentTab].objectBuf); //we cant scroll here as we arent in any scroll window if (ImGui::InputTextWithHint("##CNameSearchBox", "Search for Object...", Tabs[currentTab].objectBuf, sizeof(Tabs[currentTab].objectBuf) - 1, ImGuiInputTextFlags_EnterReturnsTrue)) @@ -562,7 +562,7 @@ void windows::PackageViewerWindow::renderTabs() ImGui::Separator(); }; - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Struct) + if (Tabs[currentTab].typeSelected == ObjectInfo::OI_Struct) { for (auto& struc : package->structs) { @@ -571,7 +571,7 @@ void windows::PackageViewerWindow::renderTabs() } } - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Class) + if (Tabs[currentTab].typeSelected == ObjectInfo::OI_Class) { for (auto& struc : package->classes) { @@ -580,7 +580,7 @@ void windows::PackageViewerWindow::renderTabs() } } - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Enum) + if (Tabs[currentTab].typeSelected == ObjectInfo::OI_Enum) { for (const auto& enu : package->enums) { @@ -588,7 +588,7 @@ void windows::PackageViewerWindow::renderTabs() addSpacing(); } } - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Function) + if (Tabs[currentTab].typeSelected == ObjectInfo::OI_Function) { for (const auto& func : package->functions) { @@ -599,16 +599,16 @@ void windows::PackageViewerWindow::renderTabs() } else { - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Class) + if (Tabs[currentTab].typeSelected == ObjectInfo::ObjectType::OI_Class) renderClassOrStruct(&Tabs[currentTab], *static_cast(Tabs[currentTab].itemSelected)); - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Struct) + if (Tabs[currentTab].typeSelected == ObjectInfo::ObjectType::OI_Struct) renderClassOrStruct(&Tabs[currentTab], *static_cast(Tabs[currentTab].itemSelected)); - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Function) + if (Tabs[currentTab].typeSelected == ObjectInfo::ObjectType::OI_Function) renderFunction(*static_cast(Tabs[currentTab].itemSelected)); - if (Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Enum) + if (Tabs[currentTab].typeSelected == ObjectInfo::ObjectType::OI_Enum) renderEnum(*static_cast(Tabs[currentTab].itemSelected)); } @@ -633,7 +633,7 @@ void windows::PackageViewerWindow::renderTabs() auto renderStructorClass = [&](bool isClass) { auto& dataVector = isClass ? package->classes : package->structs; - const EngineCore::ObjectInfo::ObjectType type = isClass ? EngineCore::ObjectInfo::ObjectType::OI_Class : EngineCore::ObjectInfo::ObjectType::OI_Struct; + const ObjectInfo::ObjectType type = isClass ? ObjectInfo::ObjectType::OI_Class : ObjectInfo::ObjectType::OI_Struct; auto& itemRange = isClass ? Tabs[currentTab].itemRange_C : Tabs[currentTab].itemRange_S; renderItemRange(itemRange, dataVector.size()); @@ -677,7 +677,7 @@ void windows::PackageViewerWindow::renderTabs() ImGui::PushStyleColor(ImGuiCol_Text, IGHelper::Colors::varPink); if (ImGui::Selectable("Show all Functions")) { Tabs[currentTab].itemSelected = nullptr; - Tabs[currentTab].typeSelected = EngineCore::ObjectInfo::ObjectType::OI_Function; + Tabs[currentTab].typeSelected = ObjectInfo::ObjectType::OI_Function; } ImGui::PopStyleColor(); @@ -693,12 +693,12 @@ void windows::PackageViewerWindow::renderTabs() for (auto& func : struc->functions) { - const bool is_selected = (Tabs[currentTab].itemSelected == &func && Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Function); + const bool is_selected = (Tabs[currentTab].itemSelected == &func && Tabs[currentTab].typeSelected == ObjectInfo::ObjectType::OI_Function); ImGui::PushStyleColor(ImGuiCol_Text, IGHelper::Colors::green); if (ImGui::Selectable(std::string(std::string(ICON_FA_ANGLE_DOUBLE_RIGHT) + "##" + func.cppName).c_str(), is_selected)) { Tabs[currentTab].itemSelected = &func; - Tabs[currentTab].typeSelected = EngineCore::ObjectInfo::ObjectType::OI_Function; + Tabs[currentTab].typeSelected = ObjectInfo::ObjectType::OI_Function; updateNavBar(Tabs[currentTab].navTab, &func, Tabs[currentTab].typeSelected); } ImGui::PopStyleColor(); @@ -725,17 +725,17 @@ void windows::PackageViewerWindow::renderTabs() ImGui::PushStyleColor(ImGuiCol_Text, IGHelper::Colors::varPink); if (ImGui::Selectable("Show all Enums")) { Tabs[currentTab].itemSelected = nullptr; - Tabs[currentTab].typeSelected = EngineCore::ObjectInfo::ObjectType::OI_Enum; + Tabs[currentTab].typeSelected = ObjectInfo::ObjectType::OI_Enum; } ImGui::PopStyleColor(); for (int j = Tabs[currentTab].itemRange_E; j < package->enums.size() && j < Tabs[currentTab].itemRange_E + 99; j++) { auto& enu = package->enums[j]; - const bool is_selected = (Tabs[currentTab].itemSelected == &enu && Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Enum); + const bool is_selected = (Tabs[currentTab].itemSelected == &enu && Tabs[currentTab].typeSelected == ObjectInfo::ObjectType::OI_Enum); if (ImGui::Selectable(enu.cppName.c_str(), is_selected)) { Tabs[currentTab].itemSelected = &enu; - Tabs[currentTab].typeSelected = EngineCore::ObjectInfo::ObjectType::OI_Enum; + Tabs[currentTab].typeSelected = ObjectInfo::ObjectType::OI_Enum; updateNavBar(Tabs[currentTab].navTab, &enu, Tabs[currentTab].typeSelected); } if (is_selected && ImGui::IsItemHovered()) { @@ -767,10 +767,10 @@ void windows::PackageViewerWindow::renderTabs() //ugly code any % //i hate pushstylecol and popstylecol - beginTab("Structs##PackageContentsTabBar", IGHelper::Colors::red, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Struct, renderStructorClass, false); - beginTab("Classes##PackageContentsTabBar", IGHelper::Colors::classOrange, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Class, renderStructorClass, true); - beginTab("Functions##PackageContentsTabBar", IGHelper::Colors::yellow, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Function, renderFunctions); - beginTab("Enums##PackageContentsTabBar", IGHelper::Colors::enumBlue, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == EngineCore::ObjectInfo::OI_Enum, renderEnums); + beginTab("Structs##PackageContentsTabBar", IGHelper::Colors::red, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == ObjectInfo::OI_Struct, renderStructorClass, false); + beginTab("Classes##PackageContentsTabBar", IGHelper::Colors::classOrange, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == ObjectInfo::OI_Class, renderStructorClass, true); + beginTab("Functions##PackageContentsTabBar", IGHelper::Colors::yellow, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == ObjectInfo::OI_Function, renderFunctions); + beginTab("Enums##PackageContentsTabBar", IGHelper::Colors::enumBlue, Tabs[currentTab].focus && Tabs[currentTab].typeSelected == ObjectInfo::OI_Enum, renderEnums); ImGui::EndTabBar(); } @@ -817,20 +817,20 @@ bool windows::PackageViewerWindow::render() return false; } -void windows::PackageViewerWindow::createTab(void* typeSt, const EngineCore::ObjectInfo::ObjectType type) +void windows::PackageViewerWindow::createTab(void* typeSt, const ObjectInfo::ObjectType type) { //dont create double packages EngineStructs::Package* package = nullptr; - if (type == EngineCore::ObjectInfo::ObjectType::OI_Struct || type == EngineCore::ObjectInfo::ObjectType::OI_Class) + if (type == ObjectInfo::ObjectType::OI_Struct || type == ObjectInfo::ObjectType::OI_Class) { const auto st = static_cast(typeSt); package = st->owningPackage; } - else if (type == EngineCore::ObjectInfo::ObjectType::OI_Function) + else if (type == ObjectInfo::ObjectType::OI_Function) package = static_cast(typeSt)->owningStruct->owningPackage; - else if (type == EngineCore::ObjectInfo::ObjectType::OI_Enum) + else if (type == ObjectInfo::ObjectType::OI_Enum) package = static_cast(typeSt)->owningPackage; if (!package) diff --git a/UEDumper/Frontend/Windows/PackageViewerWindow.h b/UEDumper/Frontend/Windows/PackageViewerWindow.h index 6aea0de4..356e831d 100644 --- a/UEDumper/Frontend/Windows/PackageViewerWindow.h +++ b/UEDumper/Frontend/Windows/PackageViewerWindow.h @@ -9,34 +9,34 @@ namespace windows { class PackageViewerWindow { - + public: struct NavigationTab { //vector of all tab indexes we went through - std::vector> tabIndex{}; + std::vector> tabIndex{}; //at what index are we in the vector? AKA tabindex[currentVecIndex] should be the package index displayed int currentVecIndex = 0; - EngineCore::ObjectInfo::ObjectType currentType = EngineCore::ObjectInfo::ObjectType::OI_MAX; + ObjectInfo::ObjectType currentType = ObjectInfo::ObjectType::OI_MAX; nlohmann::json toJson() const { nlohmann::json j; nlohmann::json tabindexes; - for(int i = 0; i < tabIndex.size(); i++) + for (int i = 0; i < tabIndex.size(); i++) { auto& idx = tabIndex[i]; - if (idx.second == EngineCore::ObjectInfo::ObjectType::OI_Struct || idx.second == EngineCore::ObjectInfo::ObjectType::OI_Class) + if (idx.second == ObjectInfo::ObjectType::OI_Struct || idx.second == ObjectInfo::ObjectType::OI_Class) tabindexes.push_back(static_cast(idx.first)->cppName); - else if (idx.second == EngineCore::ObjectInfo::ObjectType::OI_Function) + else if (idx.second == ObjectInfo::ObjectType::OI_Function) tabindexes.push_back(static_cast(idx.first)->cppName); - else if (idx.second == EngineCore::ObjectInfo::ObjectType::OI_Enum) - tabindexes.push_back(static_cast(idx.first)->cppName); + else if (idx.second == ObjectInfo::ObjectType::OI_Enum) + tabindexes.push_back(static_cast(idx.first)->cppName); } j["ti"] = tabindexes; j["cv"] = currentVecIndex; @@ -48,11 +48,11 @@ namespace windows static NavigationTab fromJson(const nlohmann::json& j) { NavigationTab n; - for(const nlohmann::json& js : j["ti"]) + for (const nlohmann::json& js : j["ti"]) { - if(auto info = EngineCore::getInfoOfObject(js)) + if (const auto info = EngineCore::getInfoOfObject(js)) { - n.tabIndex.push_back(std::pair(info.target, info.type)); + n.tabIndex.push_back(std::pair(info->target, info->type)); } } @@ -65,7 +65,7 @@ namespace windows struct PackageTab { EngineStructs::Package* packageSelected = 0; //selected package - EngineCore::ObjectInfo::ObjectType typeSelected = EngineCore::ObjectInfo::ObjectType::OI_MAX; //selected type + ObjectInfo::ObjectType typeSelected = ObjectInfo::ObjectType::OI_MAX; //selected type void* itemSelected = 0; //selected item of the type int itemRange_S = 0; //current range from n - n+100 of structs int itemRange_C = 0; //current range from n - n+100 of classes @@ -73,7 +73,7 @@ namespace windows int itemRange_F = 0; //current range from n - n+100 of functions bool focus = false; bool open = false; - char objectBuf[250] = {0}; + char objectBuf[250] = { 0 }; enum class findState { FS_none, //no operation FS_highlight,//only highlight @@ -89,12 +89,12 @@ namespace windows j["ps"] = packageSelected->packageName; j["ts"] = typeSelected; - if (typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Struct || typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Class) + if (typeSelected == ObjectInfo::ObjectType::OI_Struct || typeSelected == ObjectInfo::ObjectType::OI_Class) j["is"] = static_cast(itemSelected)->cppName; - else if (typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Function) + else if (typeSelected == ObjectInfo::ObjectType::OI_Function) j["is"] = static_cast(itemSelected)->cppName; - else if (typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Enum) + else if (typeSelected == ObjectInfo::ObjectType::OI_Enum) j["is"] = static_cast(itemSelected)->cppName; j["irs"] = itemRange_S; j["irc"] = itemRange_C; @@ -115,37 +115,37 @@ namespace windows p.typeSelected = j["ts"]; const std::string packagename = j["ps"]; const std::string structname = j["is"]; - for(auto& pack : EngineCore::getPackages()) + for (auto& pack : EngineCore::getPackages()) { - if(pack.packageName == packagename) + if (pack.packageName == packagename) { p.packageSelected = &pack; - if(p.typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Struct || p.typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Class) + if (p.typeSelected == ObjectInfo::ObjectType::OI_Struct || p.typeSelected == ObjectInfo::ObjectType::OI_Class) { - for(const auto& st : pack.combinedStructsAndClasses) + for (const auto& st : pack.combinedStructsAndClasses) { if (st->cppName == structname) { p.itemSelected = st; break; } - + } } - else if (p.typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Function) + else if (p.typeSelected == ObjectInfo::ObjectType::OI_Function) { - for(const auto& fn : pack.functions) + for (const auto& fn : pack.functions) { - if(fn->cppName == structname) + if (fn->cppName == structname) { p.itemSelected = fn; break; } } } - else if (p.typeSelected == EngineCore::ObjectInfo::ObjectType::OI_Enum) + else if (p.typeSelected == ObjectInfo::ObjectType::OI_Enum) { - for(auto& en : pack.enums) + for (auto& en : pack.enums) { if (en.cppName == structname) { @@ -172,7 +172,7 @@ namespace windows return p; } }; - + static inline int packagePicked = 0; static inline bool alreadyCompleted = false; @@ -183,7 +183,7 @@ namespace windows static nlohmann::json getTabsToJson() { nlohmann::json j; - for(const auto& tab: Tabs) + for (const auto& tab : Tabs) j.push_back(tab.toJson()); return j; } @@ -204,14 +204,14 @@ namespace windows static void renderSubTypes(const fieldType& type, bool inChild); /** - * \brief + * \brief * \param tab the current tab this functions gets called in * \param struc struct */ static void renderClassOrStruct(PackageTab* tab, EngineStructs::Struct& struc); /** - * \brief + * \brief * \param enu enum */ static void renderEnum(const EngineStructs::Enum& enu); @@ -221,9 +221,9 @@ namespace windows static void setOpenTabsClosed(); - static void updateNavBar(NavigationTab& navtab, void* itemSelected, EngineCore::ObjectInfo::ObjectType typeSelected); + static void updateNavBar(NavigationTab& navtab, void* itemSelected, ObjectInfo::ObjectType typeSelected); + - public: PackageViewerWindow(); @@ -232,7 +232,7 @@ namespace windows static bool render(); - static void createTab(void* typeSt, EngineCore::ObjectInfo::ObjectType type = EngineCore::ObjectInfo::ObjectType::OI_Struct); + static void createTab(void* typeSt, ObjectInfo::ObjectType type = ObjectInfo::ObjectType::OI_Struct); static bool openTabFromCName(const std::string& name); @@ -242,4 +242,4 @@ namespace windows */ static void topmostCallback(); }; -} +} \ No newline at end of file diff --git a/UEDumper/Frontend/Windows/PackageWindow.cpp b/UEDumper/Frontend/Windows/PackageWindow.cpp index 9183cfc4..e002894a 100644 --- a/UEDumper/Frontend/Windows/PackageWindow.cpp +++ b/UEDumper/Frontend/Windows/PackageWindow.cpp @@ -86,13 +86,13 @@ bool windows::PackageWindow::render() LogWindow::Log(windows::LogWindow::log_2, "PACKAGE", "opening package %d", packagePicked); packagePicked = i; if (packages[i].structs.size() > 0) - PackageViewerWindow::createTab(&packages[i].structs[0], EngineCore::ObjectInfo::OI_Struct); + PackageViewerWindow::createTab(&packages[i].structs[0], ObjectInfo::OI_Struct); else if (packages[i].classes.size() > 0) - PackageViewerWindow::createTab(&packages[i].classes[0], EngineCore::ObjectInfo::OI_Class); + PackageViewerWindow::createTab(&packages[i].classes[0], ObjectInfo::OI_Class); else if (packages[i].functions.size() > 0) - PackageViewerWindow::createTab(&packages[i].functions[0], EngineCore::ObjectInfo::OI_Function); + PackageViewerWindow::createTab(&packages[i].functions[0], ObjectInfo::OI_Function); else if (packages[i].enums.size() > 0) - PackageViewerWindow::createTab(&packages[i].enums[0], EngineCore::ObjectInfo::OI_Enum); + PackageViewerWindow::createTab(&packages[i].enums[0], ObjectInfo::OI_Enum); else LogWindow::Log(windows::LogWindow::log_2, "PACKAGE", "failed to open package: package is empty!"); @@ -289,7 +289,7 @@ void windows::PackageWindow::renderProjectPopup() ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted("The MDK files are a new SDK-type resolving all dependency errors and missing structs, so you never have a incomplete SDK. " - "You can straight up put it into your project with the MDK library (found on my github). Try it out!"); + "You can straight up put it into your project with the MDK library (found on my github). Try it out!"); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } @@ -324,7 +324,7 @@ void windows::PackageWindow::renderProjectPopup() ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted("This will generate the FNames.txt file with all the names found that are paired to objects. " - "This aso means that if a UObject didnt use for example fname index 1, there wont be that name in the dump."); + "This aso means that if a UObject didnt use for example fname index 1, there wont be that name in the dump."); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); } diff --git a/UEDumper/Memory/Memory.cpp b/UEDumper/Memory/Memory.cpp index 5e2df471..649436ef 100644 --- a/UEDumper/Memory/Memory.cpp +++ b/UEDumper/Memory/Memory.cpp @@ -9,7 +9,7 @@ Memory::Memory() { windows::LogWindow::Log(windows::LogWindow::log_0, "MEMORY", "Initializing memory class..."); //only call the init function if status is bad - if(status == bad) + if (status == bad) { //call the init function init(); @@ -18,16 +18,16 @@ Memory::Memory() status = inizilaized; windows::LogWindow::Log(windows::LogWindow::log_0, "MEMORY", "Initialized Memory class!"); } - + } Memory::LoadError Memory::load(std::string processName) { //should not happen! - if(status == bad) DebugBreak(); + if (status == bad) DebugBreak(); //only call the load function if the status is initialized - if(status == inizilaized) + if (status == inizilaized) { loadData(processName, baseAddress, processID); @@ -80,7 +80,7 @@ Memory::LoadError Memory::load(int processPID) void Memory::checkStatus() { - if(status != loaded) DebugBreak(); + if (status != loaded) DebugBreak(); } Memory::MemoryStatus Memory::getStatus() @@ -148,13 +148,13 @@ uint64_t Memory::patternScan(int flag, const char* pattern, const std::string& m if (patternMap.contains(pattern)) return patternMap[pattern]; - if(!init) + if (!init) { init = true; static IMAGE_DOS_HEADER dosHeader; static IMAGE_NT_HEADERS ntHeaders; - + dosHeader = read(baseAddress); @@ -189,7 +189,7 @@ uint64_t Memory::patternScan(int flag, const char* pattern, const std::string& m const int length = virtualSize - mask.length(); - for(int i = 0; i <= length; ++i) + for (int i = 0; i <= length; ++i) { char* addr = &textBuff[i]; @@ -197,7 +197,7 @@ uint64_t Memory::patternScan(int flag, const char* pattern, const std::string& m continue; const uint64_t uAddr = reinterpret_cast(addr); - if(flag & OFFSET_SIG_RVA) + if (flag & OFFSET_SIG_RVA) { const auto res = vaStart + i + *reinterpret_cast(uAddr + 3) + 7; patternMap.insert(std::pair(pattern, res)); @@ -211,4 +211,4 @@ uint64_t Memory::patternScan(int flag, const char* pattern, const std::string& m return 0; //ReadProcessMemory(hProcess, (LPBYTE)baseAddress + dosHeader.e_lfanew + sizeof(DWORD), &fileHeader, sizeof(IMAGE_FILE_HEADER), nullptr) -} +} \ No newline at end of file diff --git a/UEDumper/Memory/Memory.h b/UEDumper/Memory/Memory.h index 6aeef895..790b1fa1 100644 --- a/UEDumper/Memory/Memory.h +++ b/UEDumper/Memory/Memory.h @@ -56,8 +56,8 @@ class Memory //counter for all writes done inline static int totalWrites = 0; - - + + public: //add your initializers here @@ -89,9 +89,9 @@ class Memory static int getProcessID(); static int getTotalReads(); - + static int getTotalWrites(); - + /* * Memory operations here. If you change any params on the templates, @@ -99,7 +99,7 @@ class Memory * In general you dont have to change them. */ - //read function that gets called from the templates + //read function that gets called from the templates static void read(const void* address, void* buffer, DWORD64 size); @@ -148,4 +148,4 @@ class Memory * \return the address */ static uint64_t patternScan(int flag, const char* pattern, const std::string& mask); -}; +}; \ No newline at end of file diff --git a/UEDumper/Memory/driver.h b/UEDumper/Memory/driver.h index 45a75b09..cff9d35f 100644 --- a/UEDumper/Memory/driver.h +++ b/UEDumper/Memory/driver.h @@ -29,7 +29,7 @@ HANDLE procHandle = nullptr; //use the function "load" below which will contain data about the process name inline void init() { - //... + //... } uint64_t _getBaseAddress(const wchar_t* processName, int& pid); @@ -60,15 +60,15 @@ inline void loadData(std::string& processName, uint64_t& baseAddress, int& proce inline void _read(const void* address, void* buffer, const DWORD64 size) { size_t bytes_read = 0; - BOOL b = ReadProcessMemory(procHandle, address, buffer, size, &bytes_read); + BOOL b = ReadProcessMemory(procHandle, address, buffer, size, &bytes_read); //if failed, try with lower byte amount if (!b) { //always read 10 bytes lower - for(int i = 1; i < size && !b; i+= 10) - { + for (int i = 1; i < size && !b; i += 10) + { b = ReadProcessMemory(procHandle, address, buffer, size - i, nullptr); - } + } } } @@ -81,7 +81,7 @@ inline void _read(const void* address, void* buffer, const DWORD64 size) */ inline void _write(void* address, const void* buffer, const DWORD64 size) { - WriteProcessMemory(procHandle, address, buffer, size, nullptr); + WriteProcessMemory(procHandle, address, buffer, size, nullptr); } @@ -95,7 +95,7 @@ uint64_t _getBaseAddress(const wchar_t* processName, int& pid) { uint64_t baseAddress = 0; - if(!pid) + if (!pid) { // Get a handle to the process const HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); @@ -130,9 +130,9 @@ uint64_t _getBaseAddress(const wchar_t* processName, int& pid) CloseHandle(hModule); } } - + // Clean up and return - + return baseAddress; } @@ -142,5 +142,5 @@ uint64_t _getBaseAddress(const wchar_t* processName, int& pid) */ void attachToProcess(const int& pid) { - procHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + procHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); } \ No newline at end of file diff --git a/UEDumper/Settings/EngineSettings.cpp b/UEDumper/Settings/EngineSettings.cpp index e0a6591a..57cfbded 100644 --- a/UEDumper/Settings/EngineSettings.cpp +++ b/UEDumper/Settings/EngineSettings.cpp @@ -23,6 +23,7 @@ bool EngineSettings::setProjectName(const std::string& name) { const auto path = std::filesystem::current_path() / name; + workingDir = path; projectName = name; @@ -92,7 +93,7 @@ nlohmann::json EngineSettings::toJson() if (_UE_VERSION == UE_4_25) EngineSettings["USE_LOWERCASE_STRUCT"] = _USE_LOWERCASE_STRUCT; - + EngineSettings["UE_BLUEPRINT_EVENTGRAPH_FASTCALLS"] = _UE_BLUEPRINT_EVENTGRAPH_FASTCALLS; if (_UE_VERSION >= UE_5_00) @@ -117,7 +118,7 @@ bool EngineSettings::loadJson(const nlohmann::json& json) if (ver != DUMPER_VERSION) { windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINESETTINGS", "The save file was generated with an older " - "version of the dumper."); + "version of the dumper."); windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINESETTINGS", "File has version %d but dumper has version %d.", ver, DUMPER_VERSION); return false; } @@ -143,7 +144,7 @@ bool EngineSettings::loadJson(const nlohmann::json& json) _UE_BLUEPRINT_EVENTGRAPH_FASTCALLS = json["UE_BLUEPRINT_EVENTGRAPH_FASTCALLS"]; #define checkMacro(condition, name, save) \ - if (condition) { \ + if(condition) { \ if (!json.contains(name)) { \ windows::LogWindow::Log(windows::LogWindow::log_2, "ENGINESETTINGS", "Project is missing the %s macro!", name); \ return false; \ @@ -163,6 +164,7 @@ bool EngineSettings::loadJson(const nlohmann::json& json) checkMacro(_UE_VERSION >= UE_4_25, "WITH_EDITORONLY_DATA", _WITH_EDITORONLY_DATA); + projectName = json["projectName"]; workingDir = json.value("workingDir", std::filesystem::path()); @@ -174,15 +176,17 @@ bool EngineSettings::loadJson(const nlohmann::json& json) targetApplicationName = json["targetApplicationName"]; + return true; } void EngineSettings::drawEngineSettings(ImVec2 window, bool* show) { + ImGui::BeginChild(merge(ICON_FA_INFO, " Engine Settings"), window, true, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove); IGHelper::placeInCenter(merge(ICON_FA_INFO, " Engine Settings")); - + ImGui::Text("Dumper version: %s", getDumperVersion().c_str()); ImGui::Text("UE version: %s", getEngineVersion().name.c_str()); if (ImGui::ArrowButton("loglevel_btn_left", ImGuiDir_Left) && windows::LogWindow::getLogLevel() > 0) @@ -198,7 +202,7 @@ void EngineSettings::drawEngineSettings(ImVec2 window, bool* show) ImGui::Checkbox("WITH_CASE_PRESERVING_NAME", reinterpret_cast(&_WITH_CASE_PRESERVING_NAME)); ImGui::Checkbox("BREAK_IF_INVALID_NAME", reinterpret_cast(&_BREAK_IF_INVALID_NAME)); - if(_UE_VERSION < UE_4_23) + if (_UE_VERSION < UE_4_23) ImGui::Text("GNAMES_POOL_OFFSET %d", _GNAMES_POOL_OFFSET); if (_UE_VERSION > UE_5_00) ImGui::Checkbox("UE_FNAME_OUTLINE_NUMBER", reinterpret_cast(&_UE_FNAME_OUTLINE_NUMBER)); @@ -208,7 +212,7 @@ void EngineSettings::drawEngineSettings(ImVec2 window, bool* show) ImGui::Text("UFunction settings"); ImGui::Checkbox("UE_BLUEPRINT_EVENTGRAPH_FASTCALLS", reinterpret_cast(&_UE_BLUEPRINT_EVENTGRAPH_FASTCALLS)); - + if (_UE_VERSION >= UE_5_00) ImGui::Checkbox("WITH_LIVE_CODING", reinterpret_cast(&_WITH_LIVE_CODING)); @@ -224,7 +228,7 @@ void EngineSettings::drawEngineSettings(ImVec2 window, bool* show) } ImGui::EndDisabled(); - if(show) + if (show) { ImGui::Dummy(ImVec2(ImGui::GetWindowSize().x / 2 - 65, 0)); ImGui::SameLine(); diff --git a/UEDumper/Settings/EngineSettings.h b/UEDumper/Settings/EngineSettings.h index fb67146a..3cc5971a 100644 --- a/UEDumper/Settings/EngineSettings.h +++ b/UEDumper/Settings/EngineSettings.h @@ -45,6 +45,8 @@ class EngineSettings "Version 1.6 Release", "Version 1.7 BETA", "Version 1.7 Release", + "Version 1.8 BETA", + "Version 1.8 Release", }; private: @@ -119,7 +121,7 @@ class EngineSettings static void setLiveEditor(bool enabled); /** - * \brief returns the current live editor status + * \brief returns the current live editor status * \return whether the live editor is enabled */ static bool liveEditorEnabled(); @@ -145,7 +147,7 @@ class EngineSettings static void drawEngineSettings(ImVec2 window, bool* show); //all macros defined here - + static inline int _UE_VERSION = 0; static inline int _USE_FNAME_ENCRYPTION = 0; static inline int _WITH_CASE_PRESERVING_NAME = 0; @@ -182,7 +184,7 @@ class EngineSettings _UE_BLUEPRINT_EVENTGRAPH_FASTCALLS = UE_BLUEPRINT_EVENTGRAPH_FASTCALLS; #if UE_VERSION >= UE_5_00 _WITH_LIVE_CODING = WITH_LIVE_CODING; - + #endif #if UE_VERSION >= UE_4_22 _USTRUCT_FAST_ISCHILDOF_IMPL = USTRUCT_FAST_ISCHILDOF_IMPL; @@ -193,5 +195,4 @@ class EngineSettings _WITH_EDITORONLY_DATA = WITH_EDITORONLY_DATA; #endif } -}; - +}; \ No newline at end of file diff --git a/UEDumper/UEDumper.vcxproj b/UEDumper/UEDumper.vcxproj index fe6ce630..7f5e1127 100644 --- a/UEDumper/UEDumper.vcxproj +++ b/UEDumper/UEDumper.vcxproj @@ -189,6 +189,7 @@ +