- Added the ability of using .PAK mods inside "EGameTools\UserModFiles"; just drag and drop a .PAK inside the folder, rename it to whatever you like and enjoy! CREDITS TO @12brendon34 on Discord for finding out how to implement this feature!

- Added "Allow Grapple Hook in Safezone" (Player)
This commit is contained in:
EricPlayZ
2024-05-07 05:37:29 +03:00
parent 7d048316e9
commit e3a90c6d58
9 changed files with 105 additions and 69 deletions

View File

@ -52,10 +52,12 @@ Thank you everyone for the support <3)" },
- Fetch game version using Windows' API instead of using the game's function)" },
{ "v1.2.0",
R"(- Added compatibility with v1.16.1 hotfix update
- Added the ability of using .PAK mods inside "EGameTools\UserModFiles"; just drag and drop a .PAK inside the folder, rename it to whatever you like and enjoy! CREDITS TO @12brendon34 on Discord for finding out how to implement this feature!
- Added "Player Immunity" slider (Player)
- Added "Unlimited Immunity" (Player)
- Added "Unlimited Stamina" (Player)
- Added "Invisible to Enemies" (Player)
- Added "Allow Grapple Hook in Safezone" (Player)
- Added "Increase Data PAKs Limit" (Misc; requires game restart to apply) - you can now add more than 8 data PAKs, e.g. data8.pak, data9.pak, data10.pak, etc, up to 200 PAKs in total
- Added "Disable Data PAKs CRC Check" (Misc; requires game restart to apply) - stops the game from scanning data PAKs, which allows you to use data PAK mods in multiplayer as well
- Added "Disable Savegame CRC Check" (Misc; requires game restart to apply) - stops the game from falsely saying your savegame is corrupt whenever you modify it
@ -68,6 +70,8 @@ Thank you everyone for the support <3)" },
- Fixed volatiles still being able to kill you when they jump on top of you while "God Mode" (Player) is enabled
- Fixed "Disable Out of Bounds Timer" (Player) not working in missions
- Fixed immunity drastically being lowered while rapidly changing the time forward with the "Time" slider (World) at night or while in a dark zone
- Changed the config system to only write to the config file whenever there's a change in the mod menu)" }
- Changed the config system to only write to the config file whenever there's a change in the mod menu
NOTE: Any mods that are put inside "EGameTools\UserModFiles" as a regular file (.scr or any other file that is usually present in .PAK mods) and NOT a .PAK file will make the game ignore the same files that are present in any of the .PAK mods inside "EGameTools\UserModFiles". I recommend using .PAK for most mods. If you run into issues, try extracting the files inside the PAK into the folder directly.)" }
};
}

View File

@ -4349,6 +4349,7 @@ namespace Config {
{ "Menu:Keybinds", "DisableOutOfBoundsTimerToggleKey", std::string("VK_NONE"), &Menu::Player::disableOutOfBoundsTimer, String},
{ "Menu:Keybinds", "NightrunnerModeToggleKey", std::string("VK_F9"), &Menu::Player::nightrunnerMode, String},
{ "Menu:Keybinds", "OneHandedModeToggleKey", std::string("VK_NONE"), &Menu::Player::oneHandedMode, String},
{ "Menu:Keybinds", "AllowGrappleHookInSafezoneToggleKey", std::string("VK_NONE"), &Menu::Player::allowGrappleHookInSafezone, String},
{ "Menu:Keybinds", "FreeCamToggleKey", std::string("VK_F3"), &Menu::Camera::freeCam, String},
{ "Menu:Keybinds", "TeleportPlayerToCameraToggleKey", std::string("VK_F4"), &Menu::Camera::teleportPlayerToCamera, String},
{ "Menu:Keybinds", "ThirdPersonToggleKey", std::string("VK_F1"), &Menu::Camera::thirdPersonCamera, String},
@ -4366,6 +4367,7 @@ namespace Config {
{ "Player:Misc", "DisableOutOfBoundsTimer", true, &Menu::Player::disableOutOfBoundsTimer, OPTION },
{ "Player:Misc", "NightrunnerMode", false, &Menu::Player::nightrunnerMode, OPTION },
{ "Player:Misc", "OneHandedMode", false, &Menu::Player::oneHandedMode, OPTION },
{ "Player:Misc", "AllowGrappleHookInSafezone", false, &Menu::Player::allowGrappleHookInSafezone, OPTION },
{ "Player:PlayerVariables", "Enabled", false, &Menu::Player::playerVariables, OPTION },
{ "Player:PlayerVariables", "LastSaveSCRPath", std::string(), &Menu::Player::saveSCRPath, String },
{ "Player:PlayerVariables", "LastLoadSCRFilePath", std::string(), &Menu::Player::loadSCRFilePath, String },

View File

@ -83,11 +83,10 @@ namespace Engine {
try {
const auto rdi = std::filesystem::recursive_directory_iterator(userModFilesFullPath);
for (auto entry = std::filesystem::begin(rdi); entry != std::filesystem::end(rdi); ++entry) {
const std::filesystem::path pathToDir = entry->path();
if (!std::filesystem::is_directory(pathToDir))
if (!entry->is_directory())
continue;
cachedUserModDirs.push_back(pathToDir.string());
cachedUserModDirs.push_back(entry->path().string());
}
} catch (const std::exception& e) {
spdlog::error("Exception thrown while caching user mod directories: {}", e.what());
@ -120,15 +119,18 @@ namespace Engine {
if (!std::filesystem::exists(finalPath))
continue;
finalPath.replace(0, userModFilesFullPath.size() + 1, ""); // replace entire path until mod folder with nothing
finalPath.erase(0, userModFilesFullPath.size() + 1); // erase entire path until mod folder
const char* filePath2 = finalPath.c_str();
spdlog::warn("Loading user mod file \"{}\"", finalPath.c_str());
const DWORD64 finalAddr = firstByte != 0x0 ? (reinterpret_cast<DWORD64>(filePath2) | (firstByte << 56)) : reinterpret_cast<DWORD64>(filePath2); // restores first byte of addr if first byte was not 0
DWORD64 finalAddr = reinterpret_cast<DWORD64>(filePath2);
if (firstByte != 0x0)
finalAddr |= (firstByte << 56);
const DWORD64 result = FsOpenHook.pOriginal(finalAddr, a2, a3);
if (!result) {
spdlog::error("fs::open returned 0! Something went wrong with loading user mod file \"{}\"!\nPlease make sure the path to the file is no longer than 260 characters!", finalPath.c_str());
return FsOpenHook.pOriginal(file, a2, a3);
}
return result;
}
@ -139,65 +141,65 @@ namespace Engine {
}
#pragma endregion
/*#pragma region FsNativeOpen
static LPVOID GetFsNativeOpen() {
return Utils::Memory::GetProcAddr("filesystem_x64_rwdi.dll", "?open@native@fs@@YAPEAUSFsFile@@PEAVCFsMount@@V?$string_const@D@ttl@@W4TYPE@EFSMode@@W47FFSOpenFlags@@@Z");
}
static DWORD64 detourFsNativeOpen(DWORD64 a1, DWORD64 a2, UINT a3, UINT a4);
static Utils::Hook::MHook<LPVOID, DWORD64(*)(DWORD64, DWORD64, UINT, UINT)> FsNativeOpenHook{ "FsNativeOpen", &GetFsNativeOpen, &detourFsNativeOpen };
static DWORD64 detourFsNativeOpen(DWORD64 a1, DWORD64 a2, UINT a3, UINT a4) {
return FsNativeOpenHook.pOriginal(a1, a2, a3, a4);
}
#pragma endregion
#pragma region LoadDataPaks
struct CPath {
public:
// Thank you @12brendon34 on Discord for help with finding the function responsible for .PAK loading!
#pragma region CResourceLoadingRuntimeCreate
namespace fs {
struct mount_path {
union {
const char* gamePath;
buffer<0x8, const char*> pakPath;
buffer<0x10, const char*> fullPakPath;
};
};
static LPVOID GetLoadDataPaks() {
return reinterpret_cast<LPVOID>(reinterpret_cast<DWORD64>(GetModuleHandleA("filesystem_x64_rwdi.dll")) + 0x29580);
static DWORD64 mount(mount_path* path, USHORT flags, LPVOID* a3) {
DWORD64(*pMount)(mount_path*, USHORT, LPVOID*) = (decltype(pMount))Utils::Memory::GetProcAddr("filesystem_x64_rwdi.dll", "?mount@fs@@YA_NAEBUmount_path@1@GPEAPEAVCFsMount@@@Z");
if (!pMount)
return 0;
return pMount(path, flags, a3);
}
static bool detourLoadDataPaks(DWORD64 a1, DWORD64 a2, DWORD64 a3);
static Utils::Hook::MHook<LPVOID, bool(*)(DWORD64, DWORD64, DWORD64)> LoadDataPaksHook{ "LoadDataPaks", &GetLoadDataPaks, &detourLoadDataPaks };
static bool detourLoadDataPaks(DWORD64 a1, DWORD64 a2, DWORD64 a3) {
CPath* cPath = reinterpret_cast<CPath*>(a2);
if ((cPath->fullPakPath & 0x1FFFFFFFFFFFFFFF) != 0) {
const DWORD64 firstByte = (cPath->fullPakPath >> 56) & 0xFF; // get first byte of addr
std::string gamePath = reinterpret_cast<const char*>(reinterpret_cast<DWORD64>(cPath->gamePath) & 0x1FFFFFFFFFFFFFFF);
DWORD64 origPakPath = reinterpret_cast<DWORD64>(const_cast<char*>(cPath->pakPath.data));
std::string pakPath = reinterpret_cast<const char*>(cPath->pakPath & 0x1FFFFFFFFFFFFFFF);
DWORD64 origFullPakPath = reinterpret_cast<DWORD64>(const_cast<char*>(cPath->fullPakPath.data));
std::string fullPakPath = reinterpret_cast<const char*>(cPath->fullPakPath & 0x1FFFFFFFFFFFFFFF);
if (pakPath == "ph/source/data0.pak") {
pakPath = "ph/source/data/EGameTools/UserModFiles/data8.pak";
const char* pakPathCStr = pakPath.c_str();
fullPakPath.replace(gamePath.size() + 1, fullPakPath.size(), "ph/source/data/EGameTools/UserModFiles/data8.pak");
const char* fullPakPathCStr = fullPakPath.c_str();
const DWORD64 finalAddrPakPath = firstByte != 0x0 ? (reinterpret_cast<DWORD64>(pakPathCStr) | (firstByte << 56)) : reinterpret_cast<DWORD64>(pakPathCStr);
const DWORD64 finalAddrFullPakPath= firstByte != 0x0 ? (reinterpret_cast<DWORD64>(fullPakPathCStr) | (firstByte << 56)) : reinterpret_cast<DWORD64>(fullPakPathCStr);
cPath->pakPath = reinterpret_cast<const char*>(finalAddrPakPath);
cPath->fullPakPath = reinterpret_cast<const char*>(finalAddrFullPakPath);
bool ret = LoadDataPaksHook.pOriginal(a1, a2, a3);
cPath->pakPath = reinterpret_cast<const char*>(origPakPath);
cPath->fullPakPath = reinterpret_cast<const char*>(origFullPakPath);
}
int i = 0;
static LPVOID GetCResourceLoadingRuntimeCreate() {
return Utils::Memory::GetProcAddr("engine_x64_rwdi.dll", "?Create@CResourceLoadingRuntime@@SAPEAV1@_N@Z");
}
static LPVOID detourCResourceLoadingRuntimeCreate(bool noTexStreaming);
Utils::Hook::MHook<LPVOID, LPVOID(*)(bool)> CResourceLoadingRuntimeCreateHook{ "CResourceLoadingRuntimeCreate", &GetCResourceLoadingRuntimeCreate, &detourCResourceLoadingRuntimeCreate };
static LPVOID detourCResourceLoadingRuntimeCreate(bool noTexStreaming) {
std::string gamePath = userModFilesFullPath;
Utils::Values::str_replace(gamePath, "\\ph\\source\\data\\EGameTools\\UserModFiles", "");
std::unique_ptr<fs::mount_path> pathPtr = std::make_unique<fs::mount_path>();
pathPtr->gamePath = gamePath.c_str();
try {
const auto rdi = std::filesystem::recursive_directory_iterator(userModFilesFullPath);
for (auto entry = std::filesystem::begin(rdi); entry != std::filesystem::end(rdi); ++entry) {
if (entry->is_directory())
continue;
std::string fullPakPath = entry->path().string();
if (!Utils::Values::str_ends_with_ci(fullPakPath, ".pak"))
continue;
std::string pakPath = fullPakPath;
pakPath.erase(0, gamePath.size() + 1);
pathPtr->pakPath = pakPath.c_str();
pathPtr->fullPakPath = fullPakPath.c_str();
spdlog::warn("Loading user PAK mod file \"{}\"", pakPath.c_str());
if (!fs::mount(pathPtr.get(), 1, nullptr))
spdlog::error("fs::mount returned 0! Something went wrong with loading user PAK mod file \"{}\"!\nPlease make sure the path to the file is no longer than 260 characters, and make sure the file is valid!", pakPath.c_str());
}
} catch (const std::exception& e) {
spdlog::error("Exception thrown while iterating over user mod directories for PAK loading: {}", e.what());
}
return LoadDataPaksHook.pOriginal(a1, a2, a3);
return CResourceLoadingRuntimeCreateHook.pOriginal(noTexStreaming);
}
#pragma endregion*/
#pragma endregion
#pragma region MountDataPaks
static DWORD64 detourMountDataPaks(DWORD64 a1, UINT a2, UINT a3, DWORD64* a4, DWORD64(*a5)(DWORD64, DWORD, DWORD64, char*, int), INT16 a6, DWORD64 a7, UINT a8);

View File

@ -223,6 +223,18 @@ namespace GamePH {
}
#pragma endregion
#pragma region CanUseGrappleHook
static bool detourCanUseGrappleHook(LPVOID pInstance, bool a2);
static Utils::Hook::MHook<LPVOID, bool(*)(LPVOID, bool)> CanUseGrappleHookHook{ "CanUseGrappleHook", &Offsets::Get_CanUseGrappleHook, &detourCanUseGrappleHook };
static bool detourCanUseGrappleHook(LPVOID pInstance, bool a2) {
if (Menu::Player::allowGrappleHookInSafezone.GetValue())
return true;
return CanUseGrappleHookHook.pOriginal(pInstance, a2);
}
#pragma endregion
#pragma region ByteHooks
static unsigned char SaveGameCRCBoolCheckBytes[3] = { 0xB3, 0x01, 0x90 }; // mov bl, 01
Utils::Hook::ByteHook<LPVOID> SaveGameCRCBoolCheckHook{ "SaveGameCRCBoolCheck", &Offsets::Get_SaveGameCRCBoolCheck, SaveGameCRCBoolCheckBytes, sizeof(SaveGameCRCBoolCheckBytes), &Menu::Misc::disableSavegameCRCCheck }; // and bl, dil

View File

@ -6443,6 +6443,7 @@ namespace Menu {
KeyBindOption disableOutOfBoundsTimer{ VK_NONE };
KeyBindOption nightrunnerMode{ VK_F9 };
KeyBindOption oneHandedMode{ VK_NONE };
KeyBindOption allowGrappleHookInSafezone{ VK_NONE };
Option playerVariables{};
std::string saveSCRPath{};
@ -6904,19 +6905,20 @@ namespace Menu {
}
ImGui::CheckboxHotkey("God Mode", &godMode, "Makes the player invincible");
ImGui::SameLine();
ImGui::CheckboxHotkey("Unlimited Immunity", &unlimitedImmunity, "Stops immunity from draining");
ImGui::CheckboxHotkey("Unlimited Stamina", &unlimitedStamina, "Stops stamina from draining");
ImGui::SameLine();
ImGui::BeginDisabled(freezePlayer.GetChangesAreDisabled()); {
ImGui::CheckboxHotkey("Freeze Player", &freezePlayer, "Freezes player position");
ImGui::EndDisabled();
}
ImGui::CheckboxHotkey("Unlimited Immunity", &unlimitedImmunity, "Stops immunity from draining");
ImGui::SameLine();
ImGui::CheckboxHotkey("Unlimited Stamina", &unlimitedStamina, "Stops stamina from draining");
ImGui::CheckboxHotkey("Invisible to Enemies", &invisibleToEnemies, "Makes the player invisible to the enemies");
ImGui::SameLine();
ImGui::CheckboxHotkey("Disable Out of Bounds Timer", &disableOutOfBoundsTimer, "Disables the timer that runs when out of map bounds or mission bounds");
ImGui::CheckboxHotkey("Nightrunner Mode", &nightrunnerMode, "Makes Aiden super-human/infected");
ImGui::SameLine();
ImGui::CheckboxHotkey("One-handed Mode", &oneHandedMode, "Removes Aiden's left hand");
ImGui::CheckboxHotkey("Allow Grapple Hook in Safezone", &allowGrappleHookInSafezone, "Allows player to use grapple hook while in a safezone");
ImGui::SeparatorText("Player Jump Parameters");
if (ImGui::Button("Reload Jump Params", "Reloads jump_parameters.scr from any mod located inside EGameTools\\UserModFiles")) {

View File

@ -16,6 +16,7 @@ namespace Menu {
extern KeyBindOption disableOutOfBoundsTimer;
extern KeyBindOption nightrunnerMode;
extern KeyBindOption oneHandedMode;
extern KeyBindOption allowGrappleHookInSafezone;
extern Option playerVariables;
extern std::string saveSCRPath;

View File

@ -70,6 +70,7 @@ struct Offsets {
AddOffset(PlaySoundEvent, "gamedll_ph_x64_rwdi.dll", "4C 8B DC 49 89 5B ?? 49 89 73 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 44 24 ?? 48 8B F9 48 8B DA", Utils::SigScan::PatternType::Address, LPVOID)
AddOffset(CalculateFallHeight, "gamedll_ph_x64_rwdi.dll", "40 55 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 9C 24", Utils::SigScan::PatternType::Address, LPVOID)
AddOffset(PlayerHealthModuleKillPlayer, "gamedll_ph_x64_rwdi.dll", "40 53 48 83 EC ?? 48 8B 01 48 8B D9 FF 90 ?? ?? ?? ?? 84 C0 74 ?? 48 8B 4B ?? 48 81 C1 ?? ?? ?? ?? 48 8B 01 FF 50", Utils::SigScan::PatternType::Address, LPVOID)
AddOffset(CanUseGrappleHook, "gamedll_ph_x64_rwdi.dll", "48 89 5C 24 ?? 57 48 83 EC ?? 48 8B 01 0F B6 FA 48 8B D9 FF 90 ?? ?? ?? ?? F6 80", Utils::SigScan::PatternType::Address, LPVOID)
//AddOffset(CompareAndUpdateFloat, "gamedll_ph_x64_rwdi.dll", "0F 2F C1 73 ?? 0F 28 C1 C3", Utils::SigScan::PatternType::Address, LPVOID)
//AddOffset(HandlePlayerImmunity, "gamedll_ph_x64_rwdi.dll", "48 8B C4 53 56 57 41 56 41 57", Utils::SigScan::PatternType::Address, LPVOID)
//AddOffset(HandlePlayerImmunity2, "gamedll_ph_x64_rwdi.dll", "40 55 56 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B F1 45 0F B6 F0", Utils::SigScan::PatternType::Address, LPVOID)

View File

@ -2,13 +2,23 @@
namespace Utils {
namespace Values {
const bool are_samef(float a, float b, float precision) { return abs(a - b) < precision; }
bool str_ends_with_ci(std::string const& text, std::string const& 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::toupper(ch1) == std::toupper(ch2);
});
return it == text.rbegin();
}
bool are_samef(float a, float b, float precision) { return abs(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);
}
const bool str_replace(std::string& str, const std::string& from, const std::string& to) {
bool str_replace(std::string& str, const std::string& from, const std::string& to) {
const size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;

View File

@ -3,10 +3,12 @@
namespace Utils {
namespace Values {
extern const bool are_samef(float a, float b, float precision = 0.0001f);
extern bool str_ends_with_ci(std::string const& text, std::string const& substr);
extern bool are_samef(float a, float b, float precision = 0.0001f);
extern float round_decimal(float value, int decimal_places = 2);
extern const bool str_replace(std::string& str, const std::string& from, const std::string& to);
extern bool str_replace(std::string& str, const std::string& from, const std::string& to);
template <typename T> auto to_string(T val) {
if constexpr (std::is_same<T, std::string>::value)
return static_cast<std::string>(val);