#include #include #include #include #include #include #include namespace EGSDK::GamePH { static constexpr int STRING_SIZE_OFFSET = 3; static constexpr int FLOAT_SIZE_OFFSET = 3; static constexpr int BOOL_SIZE_OFFSET = 2; std::unordered_map PlayerVariable::playerVarNames{}; std::unordered_map PlayerVariable::playerVarTypes{}; PlayerVariable::PlayerVariable(const std::string& name) { playerVarNames[this] = name; playerVarTypes[this] = PlayerVarType::NONE; } const char* PlayerVariable::GetName() { auto it = playerVarNames.find(this); if (it != playerVarNames.end()) { return it->second.c_str(); } return nullptr; } void PlayerVariable::SetName(const std::string& newName) { playerVarNames[this] = newName; } PlayerVarType PlayerVariable::GetType() { auto it = playerVarTypes.find(this); if (it != playerVarTypes.end()) { return it->second; } return PlayerVarType::NONE; } void PlayerVariable::SetType(PlayerVarType newType) { playerVarTypes[this] = newType; } StringPlayerVariable::StringPlayerVariable(const std::string& name) : PlayerVariable(name) { SetType(PlayerVarType::String); } FloatPlayerVariable::FloatPlayerVariable(const std::string& name) : PlayerVariable(name) { SetType(PlayerVarType::Float); } void FloatPlayerVariable::SetValues(float value) { this->value = value; this->defaultValue = value; } BoolPlayerVariable::BoolPlayerVariable(const std::string& name) : PlayerVariable(name) { SetType(PlayerVarType::Bool); } void BoolPlayerVariable::SetValues(bool value) { this->value = value; this->defaultValue = value; } std::unique_ptr& PlayerVarVector::emplace_back(std::unique_ptr playerVar) { std::lock_guard lock(_mutex); _playerVars.emplace_back(std::move(playerVar)); return _playerVars.back(); } auto PlayerVarVector::begin() { std::lock_guard lock(_mutex); return _playerVars.begin(); } auto PlayerVarVector::end() { std::lock_guard lock(_mutex); return _playerVars.end(); } bool PlayerVarVector::none_of(const std::string& name) { std::lock_guard lock(_mutex); return std::none_of(_playerVars.begin(), _playerVars.end(), [&name](const auto& playerVar) { return playerVar->GetName() == name; }); } auto PlayerVarVector::FindIter(const std::string& name) { std::lock_guard lock(_mutex); auto playerVarIt = std::find_if(_playerVars.begin(), _playerVars.end(), [&name](const auto& playerVar) { return playerVar->GetName() == name; }); return playerVarIt; } std::unique_ptr* PlayerVarVector::FindPtr(const std::string& name) { std::lock_guard lock(_mutex); auto playerVarIt = FindIter(name); return playerVarIt == _playerVars.end() ? nullptr : &*playerVarIt; } PlayerVariable* PlayerVarVector::Find(const std::string& name) { std::lock_guard lock(_mutex); auto playerVarPtr = FindPtr(name); return !playerVarPtr ? nullptr : playerVarPtr->get(); } auto PlayerVarVector::Erase(const std::string& name) { std::lock_guard lock(_mutex); auto playerVarIt = FindIter(name); if (playerVarIt != _playerVars.end()) return _playerVars.erase(playerVarIt); return _playerVars.end(); } PlayerVarVector PlayerVariables::playerVars{}; PlayerVarVector PlayerVariables::customPlayerVars{}; PlayerVarVector PlayerVariables::defaultPlayerVars{}; PlayerVarVector PlayerVariables::customDefaultPlayerVars{}; bool PlayerVariables::gotPlayerVars = false; static bool sortedPlayerVars = false; std::unordered_map PlayerVariables::prevPlayerVarValueMap{}; std::unordered_map PlayerVariables::prevBoolValueMap{}; template static void updateDefaultVar(PlayerVarVector& defaultVars, const std::string& name, T value, T defaultValue) { static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Invalid type: value must be string, float or bool"); auto playerVar = defaultVars.Find(name); if (!playerVar) { if constexpr (std::is_same_v) { auto stringPlayerVar = std::make_unique(name); defaultVars.emplace_back(std::move(stringPlayerVar)); } else if constexpr (std::is_same_v) { auto floatPlayerVar = std::make_unique(name); floatPlayerVar->SetValues(value); defaultVars.emplace_back(std::move(floatPlayerVar)); } else if constexpr (std::is_same_v) { auto boolPlayerVar = std::make_unique(name); boolPlayerVar->SetValues(value); defaultVars.emplace_back(std::move(boolPlayerVar)); } } else { if constexpr (std::is_same_v) { // TO IMPLEMENT return; } else if constexpr (std::is_same_v) { auto floatPlayerVar = reinterpret_cast(playerVar); floatPlayerVar->SetValues(value); } else if constexpr (std::is_same_v) { auto boolPlayerVar = reinterpret_cast(playerVar); boolPlayerVar->SetValues(value); } } } static void processPlayerVar(DWORD64*(*playerVarsGetter)(), std::unique_ptr& playerVarPtr) { static int offset = 0; int offsetDif = 0; while (true) { std::string vTableName = Utils::RTTI::GetVTableName(playerVarsGetter() + offset); if (vTableName != "StringPlayerVariable" && vTableName != "FloatPlayerVariable" && vTableName != "BoolPlayerVariable") { if (offsetDif > 150) return; offset += 1; offsetDif += 1; continue; } std::string varName = playerVarPtr->GetName(); PlayerVarType varType = playerVarPtr->GetType(); switch (playerVarPtr->GetType()) { case PlayerVarType::String: { if (vTableName != "StringPlayerVariable") return; StringPlayerVariable* stringPlayerVar = reinterpret_cast(playerVarsGetter() + offset); playerVarPtr.reset(stringPlayerVar); playerVarPtr->SetName(varName); playerVarPtr->SetType(varType); // TO IMPLEMENT offset += STRING_SIZE_OFFSET; return; } case PlayerVarType::Float: { if (vTableName != "FloatPlayerVariable") return; FloatPlayerVariable* floatPlayerVar = reinterpret_cast(playerVarsGetter() + offset); playerVarPtr.reset(floatPlayerVar); playerVarPtr->SetName(varName); playerVarPtr->SetType(varType); updateDefaultVar(PlayerVariables::customDefaultPlayerVars, varName, floatPlayerVar->value.data, floatPlayerVar->defaultValue.data); offset += FLOAT_SIZE_OFFSET; return; } case PlayerVarType::Bool: { if (vTableName != "BoolPlayerVariable") return; BoolPlayerVariable* boolPlayerVar = reinterpret_cast(playerVarsGetter() + offset); playerVarPtr.reset(boolPlayerVar); playerVarPtr->SetName(varName); playerVarPtr->SetType(varType); updateDefaultVar(PlayerVariables::customDefaultPlayerVars, varName, boolPlayerVar->value.data, boolPlayerVar->defaultValue.data); offset += BOOL_SIZE_OFFSET; return; } default: offset += 1; return; } } } static void processPlayerVarSafe(DWORD64*(*playerVarsGetter)(), std::unique_ptr& playerVarPtr) { __try { processPlayerVar(playerVarsGetter, playerVarPtr); } __except (EXCEPTION_EXECUTE_HANDLER) { SPDLOG_ERROR("Failed to process player variable: {}", playerVarPtr->GetName()); } } void PlayerVariables::GetPlayerVars() { if (gotPlayerVars) return; if (!sortedPlayerVars) return; if (!Get()) return; for (auto& playerVarPtr : playerVars) processPlayerVarSafe(reinterpret_cast(&Get), playerVarPtr); gotPlayerVars = true; } #pragma region Player Variables Sorting struct VarTypeFieldMeta { PlayerVarType type; std::string_view className; }; const std::vector varTypeFields = { { PlayerVarType::String, "constds::FieldsCollection::TypedFieldMeta" }, { PlayerVarType::Float, "constds::FieldsCollection::TypedFieldMeta" }, { PlayerVarType::Bool, "constds::FieldsCollection::TypedFieldMeta" } }; 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; } static bool isLeaInstruction(BYTE* address, BYTE REX, BYTE ModRM) { return address[0] == REX && address[1] == 0x8D && address[2] == ModRM; } static bool isCallInstruction(BYTE* address) { return address[0] == 0xE8 && address[4] != 0xE8; } static bool isBelowFuncSizeLimit(BYTE* address, DWORD64 startOfFunc, size_t sizeLimit) { return (reinterpret_cast(address) - startOfFunc) < sizeLimit; } // to prevent infinite loops, assuming function is no longer than 500000 bytes LMAO Techland... why is your function even like 250000 bytes to begin with? bad code... static const size_t MAX_FUNC_SIZE = 500000; static const size_t MAX_LOAD_VAR_FUNC_SIZE = 2000; static const char* getPlayerVarName(BYTE*& funcAddress, DWORD64 startOfFunc) { const char* playerVarName = nullptr; while (!playerVarName && !isRetInstruction(funcAddress) && isBelowFuncSizeLimit(funcAddress, startOfFunc, MAX_FUNC_SIZE)) { // lea r8, varNameString if (!isLeaInstruction(funcAddress, 0x4C, 0x05)) { funcAddress++; continue; } playerVarName = reinterpret_cast(Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast(funcAddress), 3)); if (!playerVarName) { funcAddress++; continue; } // add the size of the instruction, so we skip this instruction because this instruction is the name funcAddress += 0x7; } return playerVarName; } static PlayerVarType getPlayerVarType(BYTE*& funcAddress, DWORD64 startOfFunc) { PlayerVarType playerVarType = PlayerVarType::NONE; while (!playerVarType && !isRetInstruction(funcAddress) && isBelowFuncSizeLimit(funcAddress, startOfFunc, MAX_FUNC_SIZE)) { // call LoadPlayerXVariable if (!isCallInstruction(funcAddress)) { funcAddress++; continue; } DWORD64 startOfLoadVarFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast(funcAddress), 1); BYTE* loadVarFuncAddress = reinterpret_cast(startOfLoadVarFunc); DWORD64 metaVTAddrFromFunc = 0; while (!metaVTAddrFromFunc && !isRetInstruction(loadVarFuncAddress) && isBelowFuncSizeLimit(loadVarFuncAddress, startOfLoadVarFunc, MAX_LOAD_VAR_FUNC_SIZE)) { // lea rax, typedFieldMetaVT if (!isLeaInstruction(loadVarFuncAddress, 0x48, 0x05)) { loadVarFuncAddress++; continue; } metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast(loadVarFuncAddress), 3); std::string vTableName = Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast(metaVTAddrFromFunc)); auto varTypeIt = std::find_if(varTypeFields.begin(), varTypeFields.end(), [&vTableName](const auto& varType) { return varType.className == vTableName; }); if (varTypeIt == varTypeFields.end()) { metaVTAddrFromFunc = 0; loadVarFuncAddress++; continue; } playerVarType = varTypeIt->type; break; } // if it's still NONE after seeing the function doesnt reference any of the variables, break so the loop stops if (playerVarType == PlayerVarType::NONE) break; } return playerVarType; } bool PlayerVariables::SortPlayerVars() { DWORD64 startOfFunc = 0; while (!startOfFunc) startOfFunc = reinterpret_cast(Offsets::Get_LoadPlayerVars()); BYTE* funcAddress = reinterpret_cast(startOfFunc); while (!isRetInstruction(funcAddress) && (reinterpret_cast(funcAddress) - startOfFunc) < MAX_FUNC_SIZE) { const char* playerVarName = getPlayerVarName(funcAddress, startOfFunc); if (!playerVarName) continue; PlayerVarType playerVarType = getPlayerVarType(funcAddress, startOfFunc); switch (playerVarType) { case PlayerVarType::String: playerVars.emplace_back(std::make_unique(playerVarName)); break; case PlayerVarType::Float: playerVars.emplace_back(std::make_unique(playerVarName)); break; case PlayerVarType::Bool: playerVars.emplace_back(std::make_unique(playerVarName)); break; default: //playerVars.emplace_back(std::make_unique(playerVarName)); break; } } sortedPlayerVars = true; return true; } #pragma endregion template static T getDefaultValue() { static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Invalid type: value must be string, float or bool"); if constexpr (std::is_same_v) return {}; else if constexpr (std::is_same_v) return -404.0f; else if constexpr (std::is_same_v) return false; else return T(); } template static T GetPlayerVarValue(const std::string& name) { static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Invalid type: value must be string, float or bool"); if (!gotPlayerVars) return getDefaultValue(); auto playerVar = playerVars.Find(name); if (!playerVar) return getDefaultValue(); if constexpr (std::is_same_v) { StringPlayerVariable* stringPlayerVar = reinterpret_cast(playerVar); return stringPlayerVar->value.data; } else if constexpr (std::is_same_v) { FloatPlayerVariable* floatPlayerVar = reinterpret_cast(playerVar); return floatPlayerVar->value.data; } else if constexpr (std::is_same_v) { BoolPlayerVariable* boolPlayerVar = reinterpret_cast(playerVar); return boolPlayerVar->value.data; } } template static void ChangePlayerVar(const std::string& name, const T value) { static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Invalid type: value must be string, float or bool"); if (!gotPlayerVars) return; auto playerVar = playerVars.Find(name); if (!playerVar) return; if constexpr (std::is_same_v) { switch (playerVar->GetType()) { case PlayerVarType::String: // TO IMPLEMENT break; case PlayerVarType::Float: { std::string valueStr = Utils::Values::to_string(value); float actualValue = std::stof(valueStr); FloatPlayerVariable* floatPlayerVar = reinterpret_cast(playerVar); floatPlayerVar->SetValues(actualValue); break; } case PlayerVarType::Bool: { std::string valueStr = Utils::Values::to_string(value); bool actualValue = valueStr == "true"; BoolPlayerVariable* boolPlayerVar = reinterpret_cast(playerVar); boolPlayerVar->SetValues(actualValue); break; } default: break; } } else if constexpr (std::is_same_v) { if (playerVar->GetType() != PlayerVarType::Float) return; FloatPlayerVariable* floatPlayerVar = reinterpret_cast(playerVar); floatPlayerVar->SetValues(value); } else if constexpr (std::is_same_v) { if (playerVar->GetType() != PlayerVarType::Bool) return; BoolPlayerVariable* boolPlayerVar = reinterpret_cast(playerVar); boolPlayerVar->SetValues(value); } } template static void ChangePlayerVarFromList(const std::string& name, const T value, PlayerVariable* playerVar = nullptr) { static_assert(std::is_same_v || std::is_same_v || std::is_same_v, "Invalid type: value must be string, float or bool"); if (!gotPlayerVars) return; if (!playerVar) { playerVar = playerVars.Find(name); if (!playerVar) return; } auto customPlayerVar = customPlayerVars.Find(name); auto defPlayerVar = defaultPlayerVars.Find(name); if constexpr (std::is_same_v) { switch (playerVar->GetType()) { case PlayerVarType::String: // TO IMPLEMENT break; case PlayerVarType::Float: { if (!customPlayerVar) customPlayerVar = customPlayerVars.emplace_back(std::make_unique(name)).get(); std::string valueStr = Utils::Values::to_string(value); float actualValue = std::stof(valueStr); FloatPlayerVariable* floatPlayerVar = reinterpret_cast(playerVar); FloatPlayerVariable* customFloatPlayerVar = reinterpret_cast(customPlayerVar); if (!defPlayerVar) { defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique(name)).get(); FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast(defPlayerVar); defFloatPlayerVar->SetValues(floatPlayerVar->value); } floatPlayerVar->SetValues(actualValue); customFloatPlayerVar->SetValues(actualValue); break; } case PlayerVarType::Bool: { if (!customPlayerVar) customPlayerVar = customPlayerVars.emplace_back(std::make_unique(name)).get(); std::string valueStr = Utils::Values::to_string(value); bool actualValue = valueStr == "true"; BoolPlayerVariable* boolPlayerVar = reinterpret_cast(playerVar); BoolPlayerVariable* customBoolPlayerVar = reinterpret_cast(customPlayerVar); if (!defPlayerVar) { defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique(name)).get(); BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast(defPlayerVar); defBoolPlayerVar->SetValues(boolPlayerVar->value); } boolPlayerVar->SetValues(actualValue); customBoolPlayerVar->SetValues(actualValue); break; } default: break; } } else if constexpr (std::is_same_v) { if (playerVar->GetType() != PlayerVarType::Float) return; if (!customPlayerVar) customPlayerVar = customPlayerVars.emplace_back(std::make_unique(name)).get(); FloatPlayerVariable* floatPlayerVar = reinterpret_cast(playerVar); FloatPlayerVariable* customFloatPlayerVar = reinterpret_cast(customPlayerVar); if (!defPlayerVar) { defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique(name)).get(); FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast(defPlayerVar); defFloatPlayerVar->SetValues(floatPlayerVar->value); } floatPlayerVar->SetValues(value); customFloatPlayerVar->SetValues(value); } else if constexpr (std::is_same_v) { if (playerVar->GetType() != PlayerVarType::Bool) return; if (!customPlayerVar) customPlayerVar = customPlayerVars.emplace_back(std::make_unique(name)).get(); BoolPlayerVariable* boolPlayerVar = reinterpret_cast(playerVar); BoolPlayerVariable* customBoolPlayerVar = reinterpret_cast(customPlayerVar); if (!defPlayerVar) { defPlayerVar = defaultPlayerVars.emplace_back(std::make_unique(name)).get(); BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast(defPlayerVar); defBoolPlayerVar->SetValues(boolPlayerVar->value); } boolPlayerVar->SetValues(value); customBoolPlayerVar->SetValues(value); } } template static void ManagePlayerVarByBool(const std::string& name, const T valueIfTrue, const T valueIfFalse, bool boolVal, bool usePreviousVal = true) { if (!gotPlayerVars) return; if (prevPlayerVarValueMap.find(name) == prevPlayerVarValueMap.end()) prevPlayerVarValueMap[name] = GetPlayerVarValue(name); if (prevBoolValueMap.find(name) == prevBoolValueMap.end()) prevBoolValueMap[name] = false; if (boolVal) { if (!prevBoolValueMap[name]) prevPlayerVarValueMap[name] = GetPlayerVarValue(name); ChangePlayerVar(name, valueIfTrue); prevBoolValueMap[name] = true; } else if (prevBoolValueMap[name]) { prevBoolValueMap[name] = false; ChangePlayerVar(name, usePreviousVal ? std::any_cast(prevPlayerVarValueMap[name]) : valueIfFalse); prevPlayerVarValueMap.erase(name); } } static bool IsPlayerVarManagedByBool(const std::string& name) { if (!gotPlayerVars) return false; return prevBoolValueMap.find(name) != prevBoolValueMap.end() && prevBoolValueMap[name]; } static PlayerVariables* GetOffset_PlayerVariables() { PlayerState* playerState = PlayerState::Get(); return playerState ? playerState->playerVariables : nullptr; } PlayerVariables* PlayerVariables::Get() { return ClassHelpers::SafeGetter(GetOffset_PlayerVariables, false, false); } }