lots of CPU performance optimization

This commit is contained in:
EricPlayZ
2025-01-04 04:53:31 +02:00
parent 32a0f8b124
commit 5e92a4df21
27 changed files with 318 additions and 240 deletions

View File

@ -9,65 +9,63 @@
#include <EGSDK\Exports.h>
namespace EGSDK {
namespace ClassHelpers {
namespace ClassHelpers {
#pragma pack(1)
template<size_t size, typename T>
class buffer {
char buffer[size];
public:
T data;
template<size_t size, typename T>
class buffer {
char buffer[size];
public:
T data;
operator T() { return data; }
T operator->() { return data; }
operator T() { return data; }
T operator->() { return data; }
DWORD64 operator&(const DWORD64 other) const { return reinterpret_cast<DWORD64>(data) & other; }
DWORD64 operator>>(const int shift) const { return reinterpret_cast<DWORD64>(data) >> shift; }
DWORD64 operator<<(const int shift) const { return reinterpret_cast<DWORD64>(data) << shift; }
DWORD64 operator&(const DWORD64 other) const { return reinterpret_cast<DWORD64>(data) & other; }
DWORD64 operator>>(const int shift) const { return reinterpret_cast<DWORD64>(data) >> shift; }
DWORD64 operator<<(const int shift) const { return reinterpret_cast<DWORD64>(data) << shift; }
T& operator=(const T& other) { data = other; return data; }
T& operator*=(const T& other) { data *= other; return data; }
T operator*(const T& other) const { return data * other; }
T& operator/=(const T& other) { data /= other; return data; }
T operator/(const T& other) const { return data / other; }
T& operator+=(const T& other) { data += other; return data; }
T operator+(const T& other) const { return data + other; }
T& operator-=(const T& other) { data -= other; return data; }
T operator-(const T& other) const { return data - other; }
};
T& operator=(const T& other) { data = other; return data; }
T& operator*=(const T& other) { data *= other; return data; }
T operator*(const T& other) const { return data * other; }
T& operator/=(const T& other) { data /= other; return data; }
T operator/(const T& other) const { return data / other; }
T& operator+=(const T& other) { data += other; return data; }
T operator+(const T& other) const { return data + other; }
T& operator-=(const T& other) { data -= other; return data; }
T operator-(const T& other) const { return data - other; }
};
#pragma pack()
extern EGameSDK_API bool IsVftableScanningDisabled();
extern EGameSDK_API void SetIsVftableScanningDisabled(bool value);
extern EGameSDK_API bool IsVftableScanningDisabled();
extern EGameSDK_API void SetIsVftableScanningDisabled(bool value);
template <typename T, typename GetOffsetFunc, typename... Args>
T* _SafeGetImpl(GetOffsetFunc getOffset, bool isDoublePtr, std::string_view className, Args... args) {
if (!getOffset(args...))
return nullptr;
template <typename T, typename GetOffsetFunc, typename... Args>
__forceinline T* _SafeGetImpl(GetOffsetFunc getOffset, bool isDoublePtr, bool checkForVTable, Args... args) {
auto offset = getOffset(args...);
if (!offset)
return nullptr;
T* ptr = isDoublePtr ? *reinterpret_cast<T**>(getOffset(args...)) : reinterpret_cast<T*>(getOffset(args...));
if (Utils::Memory::IsBadReadPtr(ptr))
return nullptr;
T* ptr = isDoublePtr ? *reinterpret_cast<T**>(offset) : reinterpret_cast<T*>(offset);
if (Utils::Memory::IsBadReadPtr(ptr))
return nullptr;
if (IsVftableScanningDisabled())
return ptr;
if (!className.empty() && !Utils::RTTI::IsClassVTableNameEqualTo(ptr, className))
return nullptr;
else if (className.empty() && !Utils::RTTI::IsClassVTableNameEqualTo(ptr, Utils::Values::GetSimpleTypeName(typeid(T).name())))
return nullptr;
if (!checkForVTable || IsVftableScanningDisabled())
return ptr;
return ptr;
}
template <typename T, typename GetOffsetFunc, typename... Args>
T* SafeGetter(GetOffsetFunc getOffset, bool isDoublePtr = true, std::string_view className = {}, Args... args) {
return Utils::Memory::SafeExecution::Execute<T*>(
reinterpret_cast<uint64_t>(&_SafeGetImpl<T, GetOffsetFunc, Args...>),
nullptr,
reinterpret_cast<void*>(getOffset),
isDoublePtr,
className,
args...
);
}
}
}
static const std::string expectedVtableName = Utils::Values::GetSimpleTypeName(typeid(T).name());
if (Utils::RTTI::GetVTableNameFromVTPtr(*reinterpret_cast<T**>(ptr)) != expectedVtableName)
return nullptr;
return ptr;
}
template <typename T, typename GetOffsetFunc, typename... Args>
__forceinline T* SafeGetter(GetOffsetFunc getOffset, bool isDoublePtr = true, bool checkForVTable = true, Args... args) {
__try {
return _SafeGetImpl<T>(getOffset, isDoublePtr, checkForVTable, args...);
} __except (Utils::Memory::SafeExecution::fail(GetExceptionCode(), GetExceptionInformation())) {
return nullptr;
}
}
}
}

View File

@ -25,7 +25,8 @@ namespace EGSDK::GamePH {
static bool SortPlayerVars();
#endif
template <typename T> static T getDefaultValue() {
template <typename T>
static T getDefaultValue() {
if constexpr (std::is_same<T, std::string>::value)
return {};
else if constexpr (std::is_same<T, bool>::value)
@ -36,7 +37,8 @@ namespace EGSDK::GamePH {
return T();
}
template <typename T> static T GetPlayerVar(const std::string& playerVar) {
template <typename T>
static T GetPlayerVar(const std::string& playerVar) {
static_assert(std::is_same<T, bool>::value || std::is_same<T, float>::value || std::is_same<T, std::string>::value, "Invalid type: value must be bool, float or string");
if (!gotPlayerVars)
@ -51,7 +53,8 @@ namespace EGSDK::GamePH {
return *reinterpret_cast<T*>(it->second.first);
}
template <typename T> static void ChangePlayerVar(const std::string& playerVar, const T value) {
template <typename T>
static void ChangePlayerVar(const std::string& playerVar, const T value) {
static_assert(std::is_same<T, bool>::value || std::is_same<T, float>::value || std::is_same<T, std::string>::value, "Invalid type: value must be bool, float or string");
if (!gotPlayerVars)
@ -89,7 +92,8 @@ namespace EGSDK::GamePH {
static std::unordered_map<std::string, std::any> prevPlayerVarValueMap;
static std::unordered_map<std::string, bool> prevBoolValueMap;
template <typename T> static void ManagePlayerVarByBool(const std::string& playerVar, const T valueIfTrue, const T valueIfFalse, bool boolVal, bool usePreviousVal = true) {
template <typename T>
static void ManagePlayerVarByBool(const std::string& playerVar, const T valueIfTrue, const T valueIfFalse, bool boolVal, bool usePreviousVal = true) {
if (!gotPlayerVars)
return;

View File

@ -31,8 +31,6 @@ namespace EGSDK {
AddOffset(CInput, "engine_x64_rwdi.dll", "48 8B 0D [?? ?? ?? ?? 48 85 C9 74 ?? 48 8B 01 84 D2", Utils::SigScan::PatternType::RelativePointer, DWORD64**) // g_CInput
// Player vars related
//AddVTOffset(TypedFieldMetaFloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VFloatPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", void*)
//AddVTOffset(TypedFieldMetaBoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VBoolPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", void*)
AddOffset(LoadPlayerVars, "gamedll_ph_x64_rwdi.dll", "48 89 4C 24 ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 8C 24", Utils::SigScan::PatternType::Address, void*)
AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "48 8B 35 [?? ?? ?? ?? 4C 8B F2 48 8B F9", Utils::SigScan::PatternType::RelativePointer, void*)

View File

@ -6,21 +6,25 @@
#include <string>
#include <string_view>
#include <vector>
#include <unordered_map>
#include <shared_mutex>
#include <EGSDK\Exports.h>
namespace EGSDK::Utils {
namespace Memory {
static const BYTE SigScanWildCard = 0xAA;
static const std::string_view SigScanWildCardStr = "AA";
static constexpr BYTE SigScanWildCard = 0xAA;
static constexpr std::string_view SigScanWildCardStr = "AA";
class EGameSDK_API SafeExecution {
public:
static int fail(unsigned int code, struct _EXCEPTION_POINTERS* ep);
template<typename T = void*, typename R = T, typename... Args>
static T Execute(uint64_t ptr, R ret, Args... args) {
template<typename T = void*, typename... Args>
static T Execute(uint64_t ptr, T ret, Args... args) {
__try {
return reinterpret_cast<T(__stdcall*)(Args...)>(ptr)(args...);
using FunctionType = T(__stdcall*)(Args...);
auto func = reinterpret_cast<FunctionType>(ptr);
return func(args...);
} __except (fail(GetExceptionCode(), GetExceptionInformation())) {
return ret;
}
@ -29,7 +33,9 @@ namespace EGSDK::Utils {
template<typename... Args>
static void ExecuteVoid(uint64_t ptr, Args... args) {
__try {
return reinterpret_cast<void(__stdcall*)(Args...)>(ptr)(args...);
using FunctionType = void(__stdcall*)(Args...);
auto func = reinterpret_cast<FunctionType>(ptr);
func(args...);
} __except (fail(GetExceptionCode(), GetExceptionInformation())) {
}
@ -70,71 +76,82 @@ namespace EGSDK::Utils {
*/
// COPYRIGHT NOTICE - START
template <typename T>
bool IsBadMemPtr(const bool write, T* ptr, const std::size_t size) {
const auto min_ptr = 0x10000;
const auto max_ptr = 0x000F000000000000;
bool IsBadMemPtr(bool write, T* ptr, std::size_t size) {
struct PointerHash {
std::size_t operator()(const void* ptr) const {
return reinterpret_cast<std::size_t>(ptr) >> 12; // Reduce collisions by using shifted addresses.
}
};
static std::unordered_map<void*, MEMORY_BASIC_INFORMATION, PointerHash> memoryCache{};
static std::shared_mutex cacheMutex{};
constexpr DWORD64 min_ptr = 0x10000;
constexpr DWORD64 max_ptr = 0x000F000000000000;
if (ptr == nullptr || reinterpret_cast<DWORD64>(ptr) < min_ptr || reinterpret_cast<DWORD64>(ptr) >= max_ptr)
return true;
DWORD mask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
DWORD mask = write
? (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)
: (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
if (write)
mask = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
auto current = reinterpret_cast<BYTE*>(ptr);
const auto last = current + size;
// So we are considering the region:
// [ptr, ptr+size)
BYTE* current = reinterpret_cast<BYTE*>(ptr);
const BYTE* last = current + size;
while (current < last) {
MEMORY_BASIC_INFORMATION mbi;
MEMORY_BASIC_INFORMATION mbi{};
{
std::shared_lock lock(cacheMutex);
auto pageAlignedAddr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(current) & ~0xFFF);
auto it = memoryCache.find(pageAlignedAddr);
if (it != memoryCache.end())
mbi = it->second;
else {
lock.unlock();
if (VirtualQuery(reinterpret_cast<LPCVOID>(pageAlignedAddr), &mbi, sizeof mbi) == 0)
return true;
// We couldn't get any information on this region.
// Let's not risk any read/write operation.
if (VirtualQuery(reinterpret_cast<LPCVOID>(current), &mbi, sizeof mbi) == 0)
std::unique_lock writeLock(cacheMutex);
memoryCache[pageAlignedAddr] = mbi;
}
}
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS) || (mbi.Protect & mask) == 0)
return true;
// We can't perform our desired read/write operations in this region.
if ((mbi.Protect & mask) == 0)
return true;
// We can't access this region.
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
return true;
// Let's consider the next region.
current = reinterpret_cast<BYTE*>(mbi.BaseAddress) + mbi.RegionSize;
}
return false;
}
template <typename T>
bool IsBadReadPtr(T* ptr, const std::size_t size) {
__forceinline bool IsBadReadPtr(T* ptr, const std::size_t size) {
return IsBadMemPtr(false, ptr, size);
}
template <typename T>
bool IsBadReadPtr(T* ptr) {
return IsBadReadPtr(ptr, sizeof ptr);
__forceinline bool IsBadReadPtr(T* ptr) {
return IsBadReadPtr(ptr, sizeof(ptr));
}
template <typename T>
bool IsBadWritePtr(T* ptr, const std::size_t size) {
__forceinline bool IsBadWritePtr(T* ptr, const std::size_t size) {
return IsBadMemPtr(true, ptr, size);
}
template <typename T>
bool IsBadWritePtr(T* ptr) {
return IsBadWritePtr(ptr, sizeof ptr);
__forceinline bool IsBadWritePtr(T* ptr) {
return IsBadWritePtr(ptr, sizeof(ptr));
}
// COPYRIGHT NOTICE - END
template <std::size_t Index, typename ReturnType = void, typename... Args> __forceinline ReturnType CallVT(void* instance, Args... args) {
template <std::size_t Index, typename ReturnType = void, typename... Args>
__forceinline ReturnType CallVT(void* instance, Args... args) {
using Fn = ReturnType(__thiscall*)(void*, Args...);
auto function = (*reinterpret_cast<Fn**>(instance))[Index];
return function(instance, args...);
}
template <std::size_t Index, typename ReturnType = void, typename... Args> __forceinline ReturnType CallFromVT(void* instance, void* vtable, Args... args) {
template <std::size_t Index, typename ReturnType = void, typename... Args>
__forceinline ReturnType CallFromVT(void* instance, void* vtable, Args... args) {
using Fn = ReturnType(__thiscall*)(void*, Args...);
auto function = reinterpret_cast<Fn*>(vtable)[Index];
@ -142,41 +159,57 @@ namespace EGSDK::Utils {
}
template <typename ReturnType, typename... Args>
static ReturnType _SafeCallFunction(const char* moduleName, const char* functionName, ReturnType ret, Args... args) {
__forceinline ReturnType SafeCallFunction(const char* moduleName, const char* functionName, ReturnType ret, Args... args) {
using FunctionType = ReturnType(__stdcall*)(Args...);
FunctionType function = reinterpret_cast<FunctionType>(Utils::Memory::GetProcAddr(moduleName, functionName));
if (!function)
return ret;
return Utils::Memory::SafeExecution::Execute<ReturnType>(reinterpret_cast<uint64_t>(function), ReturnType(), args...);
__try {
return function(args...);
} __except (SafeExecution::fail(GetExceptionCode(), GetExceptionInformation())) {
return ret;
}
}
template <typename... Args>
static void _SafeCallFunctionVoid(const char* moduleName, const char* functionName, Args... args) {
__forceinline void SafeCallFunctionVoid(const char* moduleName, const char* functionName, Args... args) {
using FunctionType = void(__stdcall*)(Args...);
FunctionType function = reinterpret_cast<FunctionType>(Utils::Memory::GetProcAddr(moduleName, functionName));
if (!function)
return;
return Utils::Memory::SafeExecution::ExecuteVoid(reinterpret_cast<uint64_t>(function), args...);
__try {
function(args...);
} __except (SafeExecution::fail(GetExceptionCode(), GetExceptionInformation())) {
return;
}
}
template <typename ReturnType, typename GetOffsetFunc, typename... Args>
static ReturnType _SafeCallFunctionOffset(GetOffsetFunc getOffset, ReturnType ret, Args... args) {
__forceinline ReturnType SafeCallFunctionOffset(GetOffsetFunc getOffset, ReturnType ret, Args... args) {
using FunctionType = ReturnType(__stdcall*)(Args...);
FunctionType function = reinterpret_cast<FunctionType>(getOffset());
if (!function)
return ret;
return Utils::Memory::SafeExecution::Execute<ReturnType>(reinterpret_cast<uint64_t>(function), ReturnType(), args...);
__try {
return function(args...);
} __except (SafeExecution::fail(GetExceptionCode(), GetExceptionInformation())) {
return ret;
}
}
template <typename GetOffsetFunc, typename... Args>
static void _SafeCallFunctionOffsetVoid(GetOffsetFunc getOffset, Args... args) {
__forceinline void SafeCallFunctionOffsetVoid(GetOffsetFunc getOffset, Args... args) {
using FunctionType = void(__stdcall*)(Args...);
FunctionType function = reinterpret_cast<FunctionType>(getOffset());
if (!function)
return;
return Utils::Memory::SafeExecution::ExecuteVoid(reinterpret_cast<uint64_t>(function), args...);
__try {
function(args...);
} __except (SafeExecution::fail(GetExceptionCode(), GetExceptionInformation())) {
return;
}
}
#pragma endregion
}

View File

@ -8,7 +8,5 @@ namespace EGSDK::Utils {
namespace RTTI {
extern EGameSDK_API std::string GetVTableNameFromVTPtr(void* vtPtr);
extern EGameSDK_API std::string GetVTableName(void* classPtr);
extern EGameSDK_API bool IsClassVTableNameEqualTo(void* classPtr, std::string_view tableName);
extern EGameSDK_API bool IsVTableNameEqualTo(void* vtPtr, std::string_view tableName);
}
}

View File

@ -6,7 +6,7 @@ namespace EGSDK::Utils {
namespace Values {
extern EGameSDK_API bool str_ends_with_ci(std::string const& text, std::string const& substr);
extern EGameSDK_API bool are_samef(float a, float b, float precision = 0.0001f);
extern EGameSDK_API __forceinline bool are_samef(float a, float b, float precision = 0.0001f);
extern EGameSDK_API float round_decimal(float value, int decimal_places = 2);
extern EGameSDK_API bool str_replace(std::string& str, const std::string& from, const std::string& to);

View File

@ -6,22 +6,22 @@
namespace EGSDK::Engine {
Vector3* CBaseCamera::GetForwardVector(Vector3* outForwardVec) {
return Utils::Memory::_SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetForwardVector@IBaseCamera@@QEBA?BVvec3@@XZ", nullptr, this, outForwardVec);
return Utils::Memory::SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetForwardVector@IBaseCamera@@QEBA?BVvec3@@XZ", nullptr, this, outForwardVec);
}
Vector3* CBaseCamera::GetUpVector(Vector3* outUpVec) {
return Utils::Memory::_SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetUpVector@IBaseCamera@@QEBA?BVvec3@@XZ", nullptr, this, outUpVec);
return Utils::Memory::SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetUpVector@IBaseCamera@@QEBA?BVvec3@@XZ", nullptr, this, outUpVec);
}
Vector3* CBaseCamera::GetLeftVector(Vector3* outLeftVec) {
return Utils::Memory::_SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetLeftVector@IBaseCamera@@QEBA?BVvec3@@XZ", nullptr, this, outLeftVec);
return Utils::Memory::SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetLeftVector@IBaseCamera@@QEBA?BVvec3@@XZ", nullptr, this, outLeftVec);
}
Vector3* CBaseCamera::GetPosition(Vector3* outPos) {
return Utils::Memory::_SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetPosition@IBaseCamera@@UEBA?BVvec3@@XZ", nullptr, this, outPos);
return Utils::Memory::SafeCallFunction<Vector3*>("engine_x64_rwdi.dll", "?GetPosition@IBaseCamera@@UEBA?BVvec3@@XZ", nullptr, this, outPos);
}
void CBaseCamera::SetPosition(const Vector3* pos) {
Utils::Memory::_SafeCallFunctionVoid("engine_x64_rwdi.dll", "?SetPosition@IBaseCamera@@QEAAXAEBVvec3@@@Z", this, pos);
Utils::Memory::SafeCallFunctionVoid("engine_x64_rwdi.dll", "?SetPosition@IBaseCamera@@QEAAXAEBVvec3@@@Z", this, pos);
}
void CBaseCamera::SetFOV(float fov) {
Utils::Memory::_SafeCallFunctionVoid("engine_x64_rwdi.dll", "?SetFOV@IBaseCamera@@QEAAXM@Z", this, fov);
Utils::Memory::SafeCallFunctionVoid("engine_x64_rwdi.dll", "?SetFOV@IBaseCamera@@QEAAXM@Z", this, fov);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCLevel2 ? pCLevel2->pCGSObject2 : nullptr;
}
CGSObject2* CGSObject2::Get() {
return ClassHelpers::SafeGetter<CGSObject2>(GetOffset_CGSObject2, false);
return ClassHelpers::SafeGetter<CGSObject2>(GetOffset_CGSObject2, false, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCLobbySteam ? pCLobbySteam->pCGame : nullptr;
}
CGame* CGame::Get() {
return ClassHelpers::SafeGetter<CGame>(GetOffset_CGame, false);
return ClassHelpers::SafeGetter<CGame>(GetOffset_CGame, false, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pCGSObject ? pCGSObject->pCLevel2 : nullptr;
}
CLevel2* CLevel2::Get() {
return ClassHelpers::SafeGetter<CLevel2>(GetOffset_CLevel2, false);
return ClassHelpers::SafeGetter<CLevel2>(GetOffset_CLevel2, false, false);
}
}

View File

@ -9,6 +9,6 @@ namespace EGSDK::Engine {
return pPlayerDI_PH ? pPlayerDI_PH->pCoPhysicsProperty : nullptr;
}
CoPhysicsProperty* CoPhysicsProperty::Get() {
return ClassHelpers::SafeGetter<CoPhysicsProperty>(GetOffset_CoPhysicsProperty, false);
return ClassHelpers::SafeGetter<CoPhysicsProperty>(GetOffset_CoPhysicsProperty, false, false);
}
}

View File

@ -3,6 +3,6 @@
namespace EGSDK::Engine {
void AuthenticateDataResultsClear(void* instance) {
Utils::Memory::_SafeCallFunctionVoid("engine_x64_rwdi.dll", "?Clear@Results@AuthenticateData@@QEAAXXZ", instance);
Utils::Memory::SafeCallFunctionVoid("engine_x64_rwdi.dll", "?Clear@Results@AuthenticateData@@QEAAXXZ", instance);
}
}

View File

@ -4,7 +4,7 @@
namespace EGSDK::GamePH {
void FreeCamera::AllowCameraMovement(int mode) {
Utils::Memory::_SafeCallFunctionOffsetVoid(Offsets::Get_AllowCameraMovement, this, mode);
Utils::Memory::SafeCallFunctionOffsetVoid(Offsets::Get_AllowCameraMovement, this, mode);
}
FreeCamera* FreeCamera::Get() {

View File

@ -6,10 +6,10 @@
namespace EGSDK::GamePH {
float GameDI_PH::GetGameTimeDelta() {
return Utils::Memory::_SafeCallFunction<float>("engine_x64_rwdi.dll", "?GetGameTimeDelta@IGame@@QEBAMXZ", -1.0f, this);
return Utils::Memory::SafeCallFunction<float>("engine_x64_rwdi.dll", "?GetGameTimeDelta@IGame@@QEBAMXZ", -1.0f, this);
}
void GameDI_PH::TogglePhotoMode(bool doNothing, bool setAsOptionalCamera) {
Utils::Memory::_SafeCallFunctionOffsetVoid(Offsets::Get_TogglePhotoMode2, this, doNothing, setAsOptionalCamera);
Utils::Memory::SafeCallFunctionOffsetVoid(Offsets::Get_TogglePhotoMode2, this, doNothing, setAsOptionalCamera);
}
static GameDI_PH* GetOffset_GameDI_PH() {

View File

@ -9,6 +9,6 @@ namespace EGSDK::GamePH {
return pGameDI_PH ? reinterpret_cast<GameDI_PH2*>(reinterpret_cast<DWORD64>(pGameDI_PH) + Offsets::Get_gameDI_PH2_offset()) : nullptr;
}
GameDI_PH2* GameDI_PH2::Get() {
return ClassHelpers::SafeGetter<GameDI_PH2>(GetOffset_GameDI_PH2, false);
return ClassHelpers::SafeGetter<GameDI_PH2>(GetOffset_GameDI_PH2, false, false);
}
}

View File

@ -54,9 +54,9 @@ namespace EGSDK::GamePH {
if (!pPlayerDI_PH)
return;
Utils::Memory::_SafeCallFunctionOffsetVoid(Offsets::Get_ShowTPPModelFunc3, pPlayerDI_PH, showTPPModel);
Utils::Memory::SafeCallFunctionOffsetVoid(Offsets::Get_ShowTPPModelFunc3, pPlayerDI_PH, showTPPModel);
}
bool ReloadJumps() {
return Utils::Memory::_SafeCallFunctionOffset<bool>(Offsets::Get_ReloadJumps, false);
return Utils::Memory::SafeCallFunctionOffset<bool>(Offsets::Get_ReloadJumps, false);
}
}

View File

@ -5,9 +5,9 @@
namespace EGSDK::GamePH {
static InventoryMoney* GetOffset_InventoryMoney(InventoryContainerDI* pInventoryContainerDI, UINT indexMaybe) {
return Utils::Memory::_SafeCallFunctionOffset<InventoryMoney*>(Offsets::Get_PlayerGetInventoryMoney, nullptr, pInventoryContainerDI, indexMaybe);
return Utils::Memory::SafeCallFunctionOffset<InventoryMoney*>(Offsets::Get_PlayerGetInventoryMoney, nullptr, pInventoryContainerDI, indexMaybe);
}
InventoryMoney* InventoryContainerDI::GetInventoryMoney(UINT indexMaybe) {
return ClassHelpers::SafeGetter<InventoryMoney>(GetOffset_InventoryMoney, false, {}, this, indexMaybe);
return ClassHelpers::SafeGetter<InventoryMoney>(GetOffset_InventoryMoney, false, true, this, indexMaybe);
}
}

View File

@ -8,6 +8,6 @@ namespace EGSDK::GamePH {
return reinterpret_cast<ItemDescWithContext*>(reinterpret_cast<DWORD64>(pInventoryItem) + 0x40);
}
ItemDescWithContext* InventoryItem::GetItemDescCtx() {
return ClassHelpers::SafeGetter<ItemDescWithContext>(GetOffset_ItemDescWithContext, false, {}, this);
return ClassHelpers::SafeGetter<ItemDescWithContext>(GetOffset_ItemDescWithContext, false, true, this);
}
}

View File

@ -11,7 +11,7 @@ namespace EGSDK::GamePH {
bool LevelDI::hasLoaded = false;
bool LevelDI::IsLoading() {
return Utils::Memory::_SafeCallFunction<bool>("engine_x64_rwdi.dll", "?IsLoading@ILevel@@QEBA_NXZ", true, this);
return Utils::Memory::SafeCallFunction<bool>("engine_x64_rwdi.dll", "?IsLoading@ILevel@@QEBA_NXZ", true, this);
}
bool LevelDI::IsLoaded() {
if (IsLoading() || !GamePH::PlayerDI_PH::Get()) {
@ -38,31 +38,31 @@ namespace EGSDK::GamePH {
hasLoaded = false;
}
void* LevelDI::GetViewCamera() {
return Utils::Memory::_SafeCallFunction<void*>("engine_x64_rwdi.dll", "?GetViewCamera@ILevel@@QEBAPEAVIBaseCamera@@XZ", nullptr, this);
return Utils::Memory::SafeCallFunction<void*>("engine_x64_rwdi.dll", "?GetViewCamera@ILevel@@QEBAPEAVIBaseCamera@@XZ", nullptr, this);
}
float LevelDI::GetTimeDelta() {
return Utils::Memory::_SafeCallFunction<float>("gamedll_ph_x64_rwdi.dll", "?GetTimeDelta@IGSObject@@UEBAMXZ", 0.0f, this);
return Utils::Memory::SafeCallFunction<float>("gamedll_ph_x64_rwdi.dll", "?GetTimeDelta@IGSObject@@UEBAMXZ", 0.0f, this);
}
void LevelDI::SetViewCamera(void* viewCam) {
Utils::Memory::_SafeCallFunctionVoid("gamedll_ph_x64_rwdi.dll", "?SetViewCamera@ILevel@@UEAAXPEAVIBaseCamera@@@Z_0", this, viewCam);
Utils::Memory::SafeCallFunctionVoid("gamedll_ph_x64_rwdi.dll", "?SetViewCamera@ILevel@@UEAAXPEAVIBaseCamera@@@Z_0", this, viewCam);
}
float LevelDI::GetTimePlayed() {
return Utils::Memory::_SafeCallFunction<float>("gamedll_ph_x64_rwdi.dll", "?GetTimePlayed@ILevel@@UEBAMXZ", 0.0f, this);
return Utils::Memory::SafeCallFunction<float>("gamedll_ph_x64_rwdi.dll", "?GetTimePlayed@ILevel@@UEBAMXZ", 0.0f, this);
}
void LevelDI::ShowUIManager(bool enabled) {
Utils::Memory::_SafeCallFunctionVoid("engine_x64_rwdi.dll", "?ShowUIManager@ILevel@@QEAAX_N@Z", this, enabled);
Utils::Memory::SafeCallFunctionVoid("engine_x64_rwdi.dll", "?ShowUIManager@ILevel@@QEAAX_N@Z", this, enabled);
}
bool LevelDI::IsTimerFrozen() {
return Utils::Memory::_SafeCallFunction<bool>("engine_x64_rwdi.dll", "?IsTimerFrozen@ILevel@@QEBA_NXZ", false, this);
return Utils::Memory::SafeCallFunction<bool>("engine_x64_rwdi.dll", "?IsTimerFrozen@ILevel@@QEBA_NXZ", false, this);
}
float LevelDI::TimerGetSpeedUp() {
return Utils::Memory::_SafeCallFunction<float>("engine_x64_rwdi.dll", "?TimerGetSpeedUp@ILevel@@QEBAMXZ", -1.0f, this);
return Utils::Memory::SafeCallFunction<float>("engine_x64_rwdi.dll", "?TimerGetSpeedUp@ILevel@@QEBAMXZ", -1.0f, this);
}
void LevelDI::TimerSetSpeedUp(float timeScale) {
Utils::Memory::_SafeCallFunctionVoid("engine_x64_rwdi.dll", "?TimerSetSpeedUp@ILevel@@QEAAXM@Z", this, timeScale);
Utils::Memory::SafeCallFunctionVoid("engine_x64_rwdi.dll", "?TimerSetSpeedUp@ILevel@@QEAAXM@Z", this, timeScale);
}
TimeWeather::CSystem* LevelDI::GetTimeWeatherSystem() {
return Utils::Memory::_SafeCallFunction<TimeWeather::CSystem*>("engine_x64_rwdi.dll", "?GetTimeWeatherSystem@ILevel@@QEBAPEAVCSystem@TimeWeather@@XZ", nullptr, this);
return Utils::Memory::SafeCallFunction<TimeWeather::CSystem*>("engine_x64_rwdi.dll", "?GetTimeWeatherSystem@ILevel@@QEBAPEAVCSystem@TimeWeather@@XZ", nullptr, this);
}
static LevelDI* GetOffset_LevelDI() {

View File

@ -7,17 +7,17 @@
namespace EGSDK::GamePH {
static InventoryItem* GetOffset_CurrentWeapon(PlayerDI_PH* pPlayerDI_PH, UINT indexMaybe) {
return Utils::Memory::_SafeCallFunctionOffset<InventoryItem*>(Offsets::Get_PlayerGetCurrentWeapon, nullptr, pPlayerDI_PH, indexMaybe);
return Utils::Memory::SafeCallFunctionOffset<InventoryItem*>(Offsets::Get_PlayerGetCurrentWeapon, nullptr, pPlayerDI_PH, indexMaybe);
}
InventoryItem* PlayerDI_PH::GetCurrentWeapon(UINT indexMaybe) {
return ClassHelpers::SafeGetter<InventoryItem>(GetOffset_CurrentWeapon, false, {}, this, indexMaybe);
}
static InventoryContainerDI* GetOffset_InventoryContainer(PlayerDI_PH* pPlayerDI_PH) {
static InventoryContainerDI* GetOffset_InventoryContainerDI(PlayerDI_PH* pPlayerDI_PH) {
return reinterpret_cast<InventoryContainerDI*>(*reinterpret_cast<DWORD64*>(reinterpret_cast<DWORD64>(pPlayerDI_PH) + 0x470));
}
InventoryContainerDI* PlayerDI_PH::GetInventoryContainer() {
return ClassHelpers::SafeGetter<InventoryContainerDI>(GetOffset_InventoryContainer, false, {}, this);
return ClassHelpers::SafeGetter<InventoryContainerDI>(GetOffset_InventoryContainerDI, false, true, this);
}
static PlayerDI_PH* GetOffset_PlayerDI_PH() {
@ -25,6 +25,6 @@ namespace EGSDK::GamePH {
return pLocalClientDI ? pLocalClientDI->pPlayerDI_PH : nullptr;
}
PlayerDI_PH* PlayerDI_PH::Get() {
return ClassHelpers::SafeGetter<PlayerDI_PH>(GetOffset_PlayerDI_PH, false);
return ClassHelpers::SafeGetter<PlayerDI_PH>(GetOffset_PlayerDI_PH, false, false);
}
}

View File

@ -20,7 +20,7 @@ namespace EGSDK::GamePH {
return pPlayerHealthModule;
}
PlayerHealthModule* PlayerHealthModule::Get() {
return ClassHelpers::SafeGetter<PlayerHealthModule>(GetOffset_PlayerHealthModule, false);
return ClassHelpers::SafeGetter<PlayerHealthModule>(GetOffset_PlayerHealthModule, false, false);
}
void PlayerHealthModule::EmplaceBack(PlayerHealthModule* ptr) {

View File

@ -20,7 +20,7 @@ namespace EGSDK::GamePH {
return pPlayerInfectionModule;
}
PlayerInfectionModule* PlayerInfectionModule::Get() {
return ClassHelpers::SafeGetter<PlayerInfectionModule>(GetOffset_PlayerInfectionModule, false);
return ClassHelpers::SafeGetter<PlayerInfectionModule>(GetOffset_PlayerInfectionModule, false, false);
}
void PlayerInfectionModule::EmplaceBack(PlayerInfectionModule* ptr) {

View File

@ -30,37 +30,37 @@ namespace EGSDK::GamePH {
it->second.first.template emplace<T>(varValue);
}
static void processPlayerVar(DWORD64** (*playerVarsGetter)(), std::pair<std::string, std::pair<void*, std::string>>& var) {
static void processPlayerVar(DWORD64*(*playerVarsGetter)(), std::pair<std::string, std::pair<void*, std::string>>& var) {
static int offset = 0;
__try {
while (true) {
const bool isFloatPlayerVar = Utils::RTTI::IsClassVTableNameEqualTo(*(playerVarsGetter() + offset), "FloatPlayerVariable");
const bool isBoolPlayerVar = Utils::RTTI::IsClassVTableNameEqualTo(*(playerVarsGetter() + offset), "BoolPlayerVariable");
static const std::string floatPlayerVarClassName = "FloatPlayerVariable";
static const std::string boolPlayerVarClassName = "BoolPlayerVariable";
if (isFloatPlayerVar || isBoolPlayerVar) {
var.second.first = playerVarsGetter() + offset + VAR_LOC_OFFSET;
const std::string& varName = var.first;
while (true) {
const std::string vTableName = Utils::RTTI::GetVTableName(playerVarsGetter() + offset);
const bool isFloatPlayerVar = vTableName == floatPlayerVarClassName;
const bool isBoolPlayerVar = vTableName == boolPlayerVarClassName;
if (isFloatPlayerVar) {
float* varValue = reinterpret_cast<float*>(var.second.first);
updateDefaultVar(GamePH::PlayerVariables::playerVarsDefault, varName, *varValue);
updateDefaultVar(GamePH::PlayerVariables::playerCustomVarsDefault, varName, *varValue);
if (isFloatPlayerVar || isBoolPlayerVar) {
var.second.first = playerVarsGetter() + offset + VAR_LOC_OFFSET;
const std::string& varName = var.first;
offset += FLOAT_VAR_OFFSET;
} else {
bool* varValue = reinterpret_cast<bool*>(var.second.first);
updateDefaultVar(GamePH::PlayerVariables::playerVarsDefault, varName, *varValue);
updateDefaultVar(GamePH::PlayerVariables::playerCustomVarsDefault, varName, *varValue);
if (isFloatPlayerVar) {
float* varValue = reinterpret_cast<float*>(var.second.first);
updateDefaultVar(GamePH::PlayerVariables::playerVarsDefault, varName, *varValue);
updateDefaultVar(GamePH::PlayerVariables::playerCustomVarsDefault, varName, *varValue);
offset += BOOL_VAR_OFFSET;
}
offset += FLOAT_VAR_OFFSET;
} else {
bool* varValue = reinterpret_cast<bool*>(var.second.first);
updateDefaultVar(GamePH::PlayerVariables::playerVarsDefault, varName, *varValue);
updateDefaultVar(GamePH::PlayerVariables::playerCustomVarsDefault, varName, *varValue);
break;
} else
offset += 1;
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
SPDLOG_ERROR("Failed to process player variable: {}", var.first);
offset += BOOL_VAR_OFFSET;
}
break;
} else
offset += 1;
}
}
@ -72,8 +72,13 @@ namespace EGSDK::GamePH {
if (!Get())
return;
for (auto& var : playerVars)
processPlayerVar(reinterpret_cast<DWORD64**(*)()>(&Get), var);
for (auto& var : playerVars) {
__try {
processPlayerVar(reinterpret_cast<DWORD64*(*)()>(&Get), var);
} __except (EXCEPTION_EXECUTE_HANDLER) {
SPDLOG_ERROR("Failed to process player variable: {}", var.first);
}
}
gotPlayerVars = true;
}
@ -88,17 +93,17 @@ namespace EGSDK::GamePH {
{ PlayerVariables::PlayerVarType::Bool, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<BoolPlayerVariable>" }
};
bool isRetInstruction(BYTE* address) {
static bool isRetInstruction(BYTE* address) {
//return address[0] == 0xC3 && address[1] == 0xCC;
return address[0] == 0x00 && address[1] == 0x00 && address[2] == 0xC3 && address[3] == 0xCC;
}
bool isLeaInstruction(BYTE* address, BYTE REX, BYTE ModRM) {
static bool isLeaInstruction(BYTE* address, BYTE REX, BYTE ModRM) {
return address[0] == REX && address[1] == 0x8D && address[2] == ModRM;
}
bool isCallInstruction(BYTE* address) {
static bool isCallInstruction(BYTE* address) {
return address[0] == 0xE8 && address[4] != 0xE8;
}
bool isBelowFuncSizeLimit(BYTE* address, DWORD64 startOfFunc, size_t sizeLimit) {
static bool isBelowFuncSizeLimit(BYTE* address, DWORD64 startOfFunc, size_t sizeLimit) {
return (reinterpret_cast<DWORD64>(address) - startOfFunc) < sizeLimit;
}
@ -127,7 +132,7 @@ namespace EGSDK::GamePH {
return playerVarName;
}
PlayerVariables::PlayerVarType getPlayerVarType(BYTE*& funcAddress, DWORD64 startOfFunc) {
static PlayerVariables::PlayerVarType getPlayerVarType(BYTE*& funcAddress, DWORD64 startOfFunc) {
PlayerVariables::PlayerVarType playerVarType = PlayerVariables::PlayerVarType::NONE;
while (!playerVarType && !isRetInstruction(funcAddress) && isBelowFuncSizeLimit(funcAddress, startOfFunc, MAX_FUNC_SIZE)) {
@ -150,14 +155,14 @@ namespace EGSDK::GamePH {
}
metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(loadVarFuncAddress), 3);
if (!Utils::RTTI::IsVTableNameEqualTo(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc), varType.className)) {
if (Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc)) != varType.className) {
metaVTAddrFromFunc = 0;
loadVarFuncAddress++;
continue;
}
}
if (Utils::RTTI::IsVTableNameEqualTo(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc), varType.className)) {
if (Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc)) == varType.className) {
playerVarType = varType.type;
break;
}
@ -216,6 +221,6 @@ namespace EGSDK::GamePH {
return pPlayerState ? pPlayerState->pPlayerVariables : nullptr;
}
PlayerVariables* PlayerVariables::Get() {
return ClassHelpers::SafeGetter<PlayerVariables>(GetOffset_PlayerVariables, false);
return ClassHelpers::SafeGetter<PlayerVariables>(GetOffset_PlayerVariables, false, false);
}
}

View File

@ -7,10 +7,10 @@
namespace EGSDK::GamePH {
namespace TimeWeather {
void CSystem::SetForcedWeather(int weather) {
Utils::Memory::_SafeCallFunctionVoid("engine_x64_rwdi.dll", "?SetForcedWeather@CSystem@TimeWeather@@QEAAXW4TYPE@EWeather@@VApiDebugAccess@2@@Z", this, weather);
Utils::Memory::SafeCallFunctionVoid("engine_x64_rwdi.dll", "?SetForcedWeather@CSystem@TimeWeather@@QEAAXW4TYPE@EWeather@@VApiDebugAccess@2@@Z", this, weather);
}
int CSystem::GetCurrentWeather() {
return Utils::Memory::_SafeCallFunction<int>("engine_x64_rwdi.dll", "?GetCurrentWeather@CSystem@TimeWeather@@QEBA?AW4TYPE@EWeather@@XZ", EWeather::TYPE::Default, this);
return Utils::Memory::SafeCallFunction<int>("engine_x64_rwdi.dll", "?GetCurrentWeather@CSystem@TimeWeather@@QEBA?AW4TYPE@EWeather@@XZ", EWeather::TYPE::Default, this);
}
static CSystem* GetOffset_CSystem() {
@ -18,7 +18,7 @@ namespace EGSDK::GamePH {
return pLevelDI ? pLevelDI->GetTimeWeatherSystem() : nullptr;
}
CSystem* CSystem::Get() {
return ClassHelpers::SafeGetter<CSystem>(GetOffset_CSystem, false);
return ClassHelpers::SafeGetter<CSystem>(GetOffset_CSystem, false, false);
}
}
}

View File

@ -4,6 +4,7 @@
#include <sstream>
#include <iomanip>
#include <unordered_map>
#include <shared_mutex>
#include <DbgHelp.h>
#include <memscan\memscan.h>
#include <EGSDK\Utils\RTTI.h>
@ -13,64 +14,93 @@
namespace EGSDK::Utils {
namespace RTTI {
static std::unordered_map<DWORD64, std::string> vtableAddressCache{};
static std::shared_mutex vtableCacheMutex;
static std::string _GetVTableNameFromVTPtr(void* vtPtr) {
if (Utils::Memory::IsBadReadPtr(vtPtr))
return "bad_read_vtable";
DWORD64 vtableStructAddr = reinterpret_cast<DWORD64>(vtPtr);
if (auto it = vtableAddressCache.find(vtableStructAddr); it != vtableAddressCache.end())
return it->second;
static __forceinline std::string CacheEmptyResult(DWORD64 vtableStructAddr) {
std::unique_lock lock(vtableCacheMutex);
vtableAddressCache.try_emplace(vtableStructAddr, "");
return {};
}
DWORD64 objectLocatorAddr = *reinterpret_cast<DWORD64*>(vtableStructAddr - sizeof(DWORD64));
std::string GetVTableNameFromVTPtr(void* vtPtr) {
if (Utils::Memory::IsBadReadPtr(vtPtr))
return "bad_read_vtable";
DWORD64 baseOffset = *reinterpret_cast<DWORD64*>(objectLocatorAddr + 0x14);
DWORD64 baseAddr = objectLocatorAddr - baseOffset;
DWORD64 vtableStructAddr = reinterpret_cast<DWORD64>(vtPtr);
DWORD classHierarchyDescriptorOffset = *reinterpret_cast<DWORD*>(objectLocatorAddr + 0x10);
DWORD64 classHierarchyDescriptorAddr = baseAddr + classHierarchyDescriptorOffset;
{
std::shared_lock lock(vtableCacheMutex);
if (auto it = vtableAddressCache.find(vtableStructAddr); it != vtableAddressCache.end())
return it->second;
}
int baseClassCount = *reinterpret_cast<int*>(classHierarchyDescriptorAddr + 0x8);
if (baseClassCount == 0 || baseClassCount > 24)
return {};
DWORD64* objectLocatorPtr = reinterpret_cast<DWORD64*>(vtableStructAddr - sizeof(DWORD64));
if (Utils::Memory::IsBadReadPtr(objectLocatorPtr))
return CacheEmptyResult(vtableStructAddr);
DWORD baseClassArrayOffset = *reinterpret_cast<DWORD*>(classHierarchyDescriptorAddr + 0xC);
DWORD64 baseClassArrayAddr = baseAddr + baseClassArrayOffset;
DWORD64 objectLocatorAddr = *objectLocatorPtr;
DWORD64* baseOffsetPtr = reinterpret_cast<DWORD64*>(objectLocatorAddr + 0x14);
if (Utils::Memory::IsBadReadPtr(baseOffsetPtr))
return CacheEmptyResult(vtableStructAddr);
DWORD baseClassDescriptorOffset = *reinterpret_cast<DWORD*>(baseClassArrayAddr);
DWORD64 baseClassDescriptorAddr = baseAddr + baseClassDescriptorOffset;
DWORD64 baseOffset = *baseOffsetPtr;
DWORD64 baseAddr = objectLocatorAddr - baseOffset;
DWORD typeDescriptorOffset = *reinterpret_cast<DWORD*>(baseClassDescriptorAddr);
DWORD64 typeDescriptorAddr = baseAddr + typeDescriptorOffset;
DWORD* classHierarchyDescriptorOffsetPtr = reinterpret_cast<DWORD*>(objectLocatorAddr + 0x10);
if (Utils::Memory::IsBadReadPtr(classHierarchyDescriptorOffsetPtr))
return CacheEmptyResult(vtableStructAddr);
std::string decoratedClassName = "?" + std::string(reinterpret_cast<const char*>(typeDescriptorAddr + 0x14));
char outUndecoratedClassName[255]{};
UnDecorateSymbolName(decoratedClassName.c_str(), outUndecoratedClassName, sizeof(outUndecoratedClassName), UNDNAME_NAME_ONLY);
DWORD classHierarchyDescriptorOffset = *classHierarchyDescriptorOffsetPtr;
DWORD64 classHierarchyDescriptorAddr = baseAddr + classHierarchyDescriptorOffset;
vtableAddressCache[vtableStructAddr] = outUndecoratedClassName;
int* baseClassCountPtr = reinterpret_cast<int*>(classHierarchyDescriptorAddr + 0x8);
if (Utils::Memory::IsBadReadPtr(baseClassCountPtr) || *baseClassCountPtr == 0 || *baseClassCountPtr > 24)
return CacheEmptyResult(vtableStructAddr);
return outUndecoratedClassName;
}
static std::string _GetVTableName(void* classPtr) {
if (Utils::Memory::IsBadReadPtr(classPtr))
return "bad_read_class";
return _GetVTableNameFromVTPtr(*reinterpret_cast<DWORD64**>(classPtr));
}
DWORD* baseClassArrayOffsetPtr = reinterpret_cast<DWORD*>(classHierarchyDescriptorAddr + 0xC);
if (Utils::Memory::IsBadReadPtr(baseClassArrayOffsetPtr))
return CacheEmptyResult(vtableStructAddr);
std::string GetVTableNameFromVTPtr(void* vtPtr) {
std::string result = _GetVTableNameFromVTPtr(vtPtr);
if (result.empty())
vtableAddressCache.try_emplace(*reinterpret_cast<DWORD64*>(vtPtr), "");
DWORD baseClassArrayOffset = *baseClassArrayOffsetPtr;
DWORD64 baseClassArrayAddr = baseAddr + baseClassArrayOffset;
return result;
}
std::string GetVTableName(void* classPtr) {
std::string result = _GetVTableName(classPtr);
if (result.empty())
vtableAddressCache.try_emplace(*reinterpret_cast<DWORD64*>(classPtr), "");
DWORD* baseClassDescriptorOffsetPtr = reinterpret_cast<DWORD*>(baseClassArrayAddr);
if (Utils::Memory::IsBadReadPtr(baseClassDescriptorOffsetPtr))
return CacheEmptyResult(vtableStructAddr);
return result;
}
bool IsClassVTableNameEqualTo(void* classPtr, std::string_view tableName) { return GetVTableName(classPtr) == tableName; }
bool IsVTableNameEqualTo(void* vtPtr, std::string_view tableName) { return GetVTableNameFromVTPtr(vtPtr) == tableName; }
DWORD baseClassDescriptorOffset = *baseClassDescriptorOffsetPtr;
DWORD64 baseClassDescriptorAddr = baseAddr + baseClassDescriptorOffset;
DWORD* typeDescriptorOffsetPtr = reinterpret_cast<DWORD*>(baseClassDescriptorAddr);
if (Utils::Memory::IsBadReadPtr(typeDescriptorOffsetPtr))
return CacheEmptyResult(vtableStructAddr);
DWORD typeDescriptorOffset = *typeDescriptorOffsetPtr;
DWORD64 typeDescriptorAddr = baseAddr + typeDescriptorOffset;
const char* decoratedClassNameCStr = reinterpret_cast<const char*>(typeDescriptorAddr + 0x14);
std::string decoratedClassName = "?" + std::string(decoratedClassNameCStr);
char outUndecoratedClassNameCStr[255]{};
if (UnDecorateSymbolName(decoratedClassName.c_str(), outUndecoratedClassNameCStr, sizeof(outUndecoratedClassNameCStr), UNDNAME_NAME_ONLY) == 0)
return CacheEmptyResult(vtableStructAddr);
std::string result(outUndecoratedClassNameCStr);
// Cache the valid result
{
std::unique_lock lock(vtableCacheMutex);
vtableAddressCache[vtableStructAddr] = result;
}
return result;
}
std::string GetVTableName(void* classPtr) {
if (Utils::Memory::IsBadReadPtr(classPtr))
return "bad_read_class";
void* vtablePtr = *reinterpret_cast<void**>(classPtr);
return GetVTableNameFromVTPtr(vtablePtr);
}
}
}

View File

@ -1,29 +1,35 @@
#include <string>
#include <algorithm>
#include <array>
#define _USE_MATH_DEFINES
#include <cmath>
#include <EGSDK\Utils\Values.h>
namespace EGSDK::Utils {
namespace Values {
bool str_ends_with_ci(std::string const& text, std::string const& substr) {
bool str_ends_with_ci(const std::string& text, const std::string& substr) {
if (substr.length() > text.length())
return false;
auto it = std::search(text.rbegin(), text.rbegin() + substr.length(), substr.rbegin(), substr.rend(), [](char ch1, char ch2) {
return std::equal(substr.rbegin(), substr.rend(), text.rbegin(), [](char ch1, char ch2) {
return std::toupper(ch1) == std::toupper(ch2);
});
return it == text.rbegin();
}
bool are_samef(float a, float b, float precision) { return abs(a - b) < precision; }
__forceinline bool are_samef(float a, float b, float precision) {
return std::fabs(a - b) < precision;
}
float round_decimal(float value, int decimal_places) {
const double multiplier = std::pow(10.0f, decimal_places);
return std::roundf(value * static_cast<float>(multiplier)) / static_cast<float>(multiplier);
float multiplier = 1.0f;
for (int i = 0; i < decimal_places; ++i)
multiplier *= 10.0f;
return std::round(value * multiplier) / multiplier;
}
bool str_replace(std::string& str, const std::string& from, const std::string& to) {
const size_t start_pos = str.find(from);
size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;
@ -32,12 +38,16 @@ namespace EGSDK::Utils {
}
std::string GetSimpleTypeName(std::string fullName) {
if (fullName.compare(0, 6, "class ") == 0)
fullName.erase(0, 6);
else if (fullName.compare(0, 7, "struct ") == 0)
fullName.erase(0, 7);
else if (fullName.compare(0, 5, "enum ") == 0)
fullName.erase(0, 5);
static const std::string class_prefix = "class ";
static const std::string struct_prefix = "struct ";
static const std::string enum_prefix = "enum ";
if (fullName.compare(0, class_prefix.size(), class_prefix) == 0)
fullName.erase(0, class_prefix.size());
else if (fullName.compare(0, struct_prefix.size(), struct_prefix) == 0)
fullName.erase(0, struct_prefix.size());
else if (fullName.compare(0, enum_prefix.size(), enum_prefix) == 0)
fullName.erase(0, enum_prefix.size());
size_t pos = fullName.find_last_of("::");
if (pos != std::string::npos)

View File

@ -4,10 +4,12 @@
<LocalDebuggerCommand>D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64\DyingLightGame_x64_rwdi.exe</LocalDebuggerCommand>
<LocalDebuggerWorkingDirectory>D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerAttach>false</LocalDebuggerAttach>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommand>D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64\DyingLightGame_x64_rwdi.exe</LocalDebuggerCommand>
<LocalDebuggerWorkingDirectory>D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64</LocalDebuggerWorkingDirectory>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
<LocalDebuggerAttach>false</LocalDebuggerAttach>
</PropertyGroup>
</Project>