diff --git a/DL2GameOverhaulScript/source/ImGui/impl/d3d11_impl.cpp b/DL2GameOverhaulScript/source/ImGui/impl/d3d11_impl.cpp index 0f15d37..79619af 100644 --- a/DL2GameOverhaulScript/source/ImGui/impl/d3d11_impl.cpp +++ b/DL2GameOverhaulScript/source/ImGui/impl/d3d11_impl.cpp @@ -43,6 +43,7 @@ HRESULT __stdcall hkPresent11(IDXGISwapChain* pSwapChain, UINT SyncInterval, UIN ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); + Menu::FirstTimeRunning(); if (Menu::menuToggle.GetValue()) Menu::Render(); diff --git a/DL2GameOverhaulScript/source/ImGui/impl/d3d12_impl.cpp b/DL2GameOverhaulScript/source/ImGui/impl/d3d12_impl.cpp index 90c8210..eeeff96 100644 --- a/DL2GameOverhaulScript/source/ImGui/impl/d3d12_impl.cpp +++ b/DL2GameOverhaulScript/source/ImGui/impl/d3d12_impl.cpp @@ -109,6 +109,7 @@ static void RenderImGui_DX12(IDXGISwapChain3* pSwapChain) { ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); + Menu::FirstTimeRunning(); if (Menu::menuToggle.GetValue()) Menu::Render(); diff --git a/DL2GameOverhaulScript/source/ImGui/impl/win32_impl.cpp b/DL2GameOverhaulScript/source/ImGui/impl/win32_impl.cpp index faea921..f0d4564 100644 --- a/DL2GameOverhaulScript/source/ImGui/impl/win32_impl.cpp +++ b/DL2GameOverhaulScript/source/ImGui/impl/win32_impl.cpp @@ -21,7 +21,7 @@ static LRESULT __stdcall hkWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARA switch (uMsg) { case WM_KEYDOWN: case WM_SYSKEYDOWN: - if (ImGui::isAnyHotkeyBtnPressed || !ImGui::timeSinceHotkeyBtnPressed.GetTimePassed() || KeyBindOption::wasAnyKeyPressed) + if (Menu::firstTimeRunning.GetValue() || ImGui::isAnyHotkeyBtnPressed || !ImGui::timeSinceHotkeyBtnPressed.DidTimePass() || KeyBindOption::wasAnyKeyPressed) break; for (auto& option : *KeyBindOption::GetInstances()) { @@ -50,15 +50,19 @@ static LRESULT __stdcall hkWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARA if (!pCInput) return CallWindowProc(oWndProc, hwnd, uMsg, wParam, lParam); - ImGui::GetIO().MouseDrawCursor = Menu::menuToggle.GetValue(); + ImGui::GetIO().MouseDrawCursor = Menu::firstTimeRunning.GetValue() || Menu::menuToggle.GetValue(); ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam); - if (Menu::menuToggle.GetValue()) { + if (Menu::firstTimeRunning.GetValue() || Menu::menuToggle.GetValue()) { pCInput->BlockGameInput(); - Menu::menuToggle.SetPrevValue(true); - } else if (Menu::menuToggle.GetPrevValue()) { - Menu::menuToggle.SetPrevValue(false); + if (Menu::menuToggle.GetValue()) + Menu::menuToggle.SetPrevValue(true); + } else if (Menu::firstTimeRunning.GetPrevValue() || Menu::menuToggle.GetPrevValue()) { + if (Menu::firstTimeRunning.GetPrevValue()) + Menu::firstTimeRunning.SetPrevValue(false); + else if (Menu::menuToggle.GetPrevValue()) + Menu::menuToggle.SetPrevValue(false); pCInput->UnlockGameInput(); } @@ -77,9 +81,11 @@ static LRESULT CALLBACK hkMouseProc(int nCode, WPARAM wParam, LPARAM lParam) { case WM_MOUSEHWHEEL: { MSLLHOOKSTRUCT* pMouseStruct = (MSLLHOOKSTRUCT*)lParam; if (pMouseStruct == nullptr) - return CallNextHookEx(oMouseProc, nCode, wParam, lParam); + break; if (GET_WHEEL_DELTA_WPARAM(pMouseStruct->mouseData)) { + if (Menu::firstTimeRunning.GetValue()) + break; for (auto& option : *KeyBindOption::GetInstances()) { if (option->GetChangesAreDisabled()) continue; diff --git a/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.cpp b/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.cpp index 2fdfbb8..4320e97 100644 --- a/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.cpp +++ b/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.cpp @@ -30,7 +30,6 @@ namespace ImGui { style->DisplaySafeAreaPadding = ImFloor(defStyle.DisplaySafeAreaPadding * scale_factor); style->MouseCursorScale = ImFloor(defStyle.MouseCursorScale * scale_factor); } - bool Checkbox(const char* label, Option* v) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -81,4 +80,105 @@ namespace ImGui { IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (v->GetValue() ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } + static const float CalculateIndentation(const float& window_width, const float& text_width, const float& min_indentation) { + const float indentation = (window_width - text_width) * 0.5f; + return indentation > min_indentation ? indentation : min_indentation; + } + void TextCentered(const char* text) { + const float min_indentation = 20.0f; + const float window_width = ImGui::GetWindowSize().x - ImGui::GetStyle().ScrollbarSize - min_indentation; + const float wrap_pos = window_width - min_indentation; + + std::istringstream iss(text); + std::vector words((std::istream_iterator(iss)), std::istream_iterator()); + + std::string line{}; + for (const auto& word : words) { + std::string new_line = line.empty() ? word : line + " " + word; + if (ImGui::CalcTextSize(new_line.c_str()).x <= wrap_pos) { + line = new_line; + continue; + } + + ImGui::SameLine(CalculateIndentation(window_width, ImGui::CalcTextSize(line.c_str()).x, min_indentation)); + ImGui::TextUnformatted(line.c_str()); + ImGui::NewLine(); + line = word; + } + + if (!line.empty()) { + ImGui::SameLine(CalculateIndentation(window_width, ImGui::CalcTextSize(line.c_str()).x, min_indentation)); + ImGui::TextUnformatted(line.c_str()); + ImGui::NewLine(); + } + } + void TextCenteredColored(const char* text, const ImU32& col) { + const float min_indentation = 20.0f; + const float window_width = ImGui::GetWindowSize().x - ImGui::GetStyle().ScrollbarSize - min_indentation; + const float wrap_pos = window_width - min_indentation; + + std::istringstream iss(text); + std::vector words((std::istream_iterator(iss)), std::istream_iterator()); + + std::string line{}; + for (const auto& word : words) { + std::string new_line = line.empty() ? word : line + " " + word; + if (ImGui::CalcTextSize(new_line.c_str()).x <= wrap_pos) { + line = new_line; + continue; + } + + ImGui::SameLine(CalculateIndentation(window_width, ImGui::CalcTextSize(line.c_str()).x, min_indentation)); + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::TextUnformatted(line.c_str()); + ImGui::PopStyleColor(); + ImGui::NewLine(); + line = word; + } + + if (!line.empty()) { + ImGui::SameLine(CalculateIndentation(window_width, ImGui::CalcTextSize(line.c_str()).x, min_indentation)); + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::TextUnformatted(line.c_str()); + ImGui::PopStyleColor(); + ImGui::NewLine(); + } + } + bool ButtonCentered(const char* label, const ImVec2& size) { + ImGuiStyle& style = ImGui::GetStyle(); + const float window_width = ImGui::GetContentRegionAvail().x; + const float button_width = ImGui::CalcTextSize(label).x + style.FramePadding.x * 2.0f; + float button_indentation = (window_width - button_width) * 0.5f; + + const float min_indentation = 20.0f; + if (button_indentation <= min_indentation) + button_indentation = min_indentation; + + ImGui::SameLine(button_indentation); + return ImGui::Button(label, size); + ImGui::NewLine(); + } + void SeparatorTextColored(const char* label, const ImU32& col) { + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::SeparatorText(label); + ImGui::PopStyleColor(); + } + void Spacing(const ImVec2& size, const bool& customPosOffset) { + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return; + + if (!customPosOffset) { + ImGui::ItemSize(size); + return; + } + + const ImVec2 currentCursorPos = ImGui::GetCursorPos(); + if (size.y == 0.0f) + ImGui::SetCursorPosX(currentCursorPos.x + size.x); + else if (size.x == 0.0f) + ImGui::SetCursorPosY(currentCursorPos.y + size.y); + else + ImGui::SetCursorPos(currentCursorPos + size); + } } \ No newline at end of file diff --git a/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.h b/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.h index d095e81..6c07cc8 100644 --- a/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.h +++ b/DL2GameOverhaulScript/source/ImGuiEx/ImGuiEx.h @@ -4,4 +4,9 @@ namespace ImGui { extern void StyleScaleAllSizes(ImGuiStyle* style, float scale_factor); extern bool Checkbox(const char* label, Option* v); + extern void TextCentered(const char* text); + extern void TextCenteredColored(const char* text, const ImU32& col); + extern bool ButtonCentered(const char* label, const ImVec2& size = ImVec2(0.0f, 0.0f)); + extern void SeparatorTextColored(const char* text, const ImU32& col); + extern void Spacing(const ImVec2& size, const bool& customPosOffset = false); } \ No newline at end of file diff --git a/DL2GameOverhaulScript/source/config/config.cpp b/DL2GameOverhaulScript/source/config/config.cpp index 820cdb4..53aec6d 100644 --- a/DL2GameOverhaulScript/source/config/config.cpp +++ b/DL2GameOverhaulScript/source/config/config.cpp @@ -4022,6 +4022,7 @@ namespace Config { static const auto configVariablesDefault = std::to_array({ { "Menu", "Transparency", 99.0f, &Menu::transparency, Float }, { "Menu", "Scale", 1.0f, &Menu::scale, Float }, + { "Menu", "FirstTimeRunning", true, &Menu::firstTimeRunning, OPTION }, { "Menu:Keybinds", "MenuToggleKey", std::string("VK_F5"), &Menu::menuToggle, String}, { "Menu:Keybinds", "GodModeToggleKey", std::string("VK_F6"), &Menu::Player::godMode, String}, { "Menu:Keybinds", "FreezePlayerToggleKey", std::string("VK_F7"), &Menu::Player::freezePlayer, String}, diff --git a/DL2GameOverhaulScript/source/core.cpp b/DL2GameOverhaulScript/source/core.cpp index 54a61dc..35f4bda 100644 --- a/DL2GameOverhaulScript/source/core.cpp +++ b/DL2GameOverhaulScript/source/core.cpp @@ -10,6 +10,7 @@ #include "hook.h" #include "kiero.h" #include "menu\menu.h" +#include "print.h" #include "sigscan\offsets.h" #pragma region KeyBindOption @@ -19,7 +20,7 @@ bool KeyBindOption::scrolledMouseWheelDown = false; #pragma endregion namespace Core { - // Console stuff +#pragma region Console static void DisableConsoleQuickEdit() { DWORD prev_mode = 0; const HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); @@ -39,15 +40,12 @@ namespace Core { fclose(f); FreeConsole(); } +#pragma endregion // Core bool exiting = false; static bool createdConfigThread = false; - std::thread hookRendererThread{}; - std::thread configLoopThread{}; - std::thread configSaveLoopThread{}; - static std::string_view rendererAPI{}; static void LoopHookRenderer() { while (true) { @@ -58,7 +56,6 @@ namespace Core { if (rendererAPI.empty()) continue; - if (kiero::init(rendererAPI == "d3d11" ? kiero::RenderType::D3D11 : kiero::RenderType::D3D12) != kiero::Status::Success) continue; @@ -77,38 +74,26 @@ namespace Core { } } - #pragma region GetRendererAPI - static bool(*pReadVideoSettings)(LPVOID instance, LPVOID file, bool flag1) = nullptr; - static bool(*oReadVideoSettings)(LPVOID instance, LPVOID file, bool flag1) = nullptr; - bool detourReadVideoSettings(LPVOID instance, LPVOID file, bool flag1) { + #pragma region ReadVideoSettings + // forward decl + static bool detourReadVideoSettings(LPVOID instance, LPVOID file, bool flag1); + static Hook::MHook ReadVideoSettingsHook{ "ReadVideoSettings", &Offsets::Get_ReadVideoSettings, &detourReadVideoSettings}; + + static bool detourReadVideoSettings(LPVOID instance, LPVOID file, bool flag1) { if (!rendererAPI.empty()) - return oReadVideoSettings(instance, file, flag1); + return ReadVideoSettingsHook.pOriginal(instance, file, flag1); DWORD renderer = *reinterpret_cast(reinterpret_cast(instance) + 0x7C); rendererAPI = !renderer ? "d3d11" : "d3d12"; - - return oReadVideoSettings(instance, file, flag1); - } - void LoopHookReadVideoSettings() { - while (true) { - Sleep(250); - if (!pReadVideoSettings) - pReadVideoSettings = (decltype(pReadVideoSettings))Offsets::Get_ReadVideoSettings(); - else if (!oReadVideoSettings && MH_CreateHook(pReadVideoSettings, &detourReadVideoSettings, reinterpret_cast(&oReadVideoSettings)) == MH_OK) { - MH_EnableHook(pReadVideoSettings); - break; - } - } + return ReadVideoSettingsHook.pOriginal(instance, file, flag1); } #pragma endregion void OnPostUpdate() { if (!createdConfigThread) { - configLoopThread = std::thread(Config::ConfigLoop); - configLoopThread.detach(); - configSaveLoopThread = std::thread(Config::ConfigSaveLoop); - configSaveLoopThread.detach(); + std::thread(Config::ConfigLoop).detach(); + std::thread(Config::ConfigSaveLoop).detach(); createdConfigThread = true; } @@ -130,10 +115,13 @@ namespace Core { } static void CreateSymlinkForLoadingFiles() { + std::filesystem::create_directories("EGameTools\\FilesToLoad"); + for (const auto& entry : std::filesystem::directory_iterator("..\\..\\data")) { if (entry.path().filename().string() == "EGameTools" && is_symlink(entry.symlink_status())) { - assert(std::filesystem::equivalent("..\\..\\data\\EGameTools", "EGameTools")); - return; + if (std::filesystem::equivalent("..\\..\\data\\EGameTools", "EGameTools")) + return; + std::filesystem::remove(entry.path()); } } std::filesystem::create_directory_symlink(Utils::GetCurrentProcDirectory() + "\\EGameTools", "..\\..\\data\\EGameTools"); @@ -142,18 +130,33 @@ namespace Core { DWORD64 WINAPI MainThread(HMODULE hModule) { EnableConsole(); + PrintInfo("Initializing config"); Config::InitConfig(); + PrintInfo("Creating \"EGameTools\\FilesToLoad\""); CreateSymlinkForLoadingFiles(); + PrintInfo("Sorting Player Variables"); GamePH::PlayerVariables::SortPlayerVars(); + PrintInfo("Initializing MinHook"); MH_Initialize(); - LoopHookReadVideoSettings(); - hookRendererThread = std::thread(LoopHookRenderer); - hookRendererThread.detach(); + PrintInfo("Hooking ReadVideoSettings"); + const auto readVidSettingsHook = std::find_if(Hook::HookBase::GetInstances()->begin(), Hook::HookBase::GetInstances()->end(), [](const auto& item) { return item->name == "ReadVideoSettings"; }); + if (readVidSettingsHook != Hook::HookBase::GetInstances()->end()) { + (*readVidSettingsHook)->HookLoop(); + Hook::HookBase::GetInstances()->erase(readVidSettingsHook); + PrintSuccess("Hooked ReadVideoSettings!"); + } - for (auto& hook : *Hook::HookBase::GetInstances()) + PrintInfo("Hooking DX11/DX12 renderer"); + LoopHookRenderer(); + PrintSuccess("Hooked DX11/DX12 renderer!"); + + for (auto& hook : *Hook::HookBase::GetInstances()) { + PrintInfo("Hooking %s", hook->name.data()); hook->HookLoop(); + PrintSuccess("Hooked %s!", hook->name.data()); + } const HANDLE proc = GetCurrentProcess(); WaitForSingleObject(proc, INFINITE); diff --git a/DL2GameOverhaulScript/source/core.h b/DL2GameOverhaulScript/source/core.h index d23ade7..c4d372a 100644 --- a/DL2GameOverhaulScript/source/core.h +++ b/DL2GameOverhaulScript/source/core.h @@ -6,7 +6,12 @@ #include #include "utils.h" #ifndef VK_NONE -// #define LLMH_IMPL_DISABLE_DEBUG // this is for disabling low-level mouse hook in case ure trying to debug and u dont want ur pc to die lol +#define LLMH_IMPL_DISABLE_DEBUG // this is for disabling low-level mouse hook in case ure trying to debug and u dont want ur pc to die lol + +#define MOD_VERSION_STR "v1.1.0" +#define GAME_VER_COMPAT_STR ">= v1.14.0" +#define GAME_VER_COMPAT 11401 + #define VK_NONE -1 #define VK_MWHEELDOWN 0x100 #define VK_MWHEELUP 0x101 diff --git a/DL2GameOverhaulScript/source/game_classes.cpp b/DL2GameOverhaulScript/source/game_classes.cpp index d90cf75..8277954 100644 --- a/DL2GameOverhaulScript/source/game_classes.cpp +++ b/DL2GameOverhaulScript/source/game_classes.cpp @@ -20,7 +20,6 @@ namespace Core { namespace GamePH { #pragma region Hooks // Forward decl - static LRESULT __fastcall detourWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static DWORD64 detourCreatePlayerHealthModule(DWORD64 playerHealthModule); static void detourOnPostUpdate(LPVOID pGameDI_PH2); static DWORD64 detourCalculateFreeCamCollision(LPVOID pFreeCamera, float* finalPos); @@ -32,16 +31,8 @@ namespace GamePH { static void detourShowUIManager(LPVOID pLevelDI, bool enabled); static DWORD64 detourFsOpen(DWORD64 file, DWORD a2, DWORD a3); -#pragma region WndProc - static Hook::MHook CreateWndProcHook{ &Offsets::Get_WndProc, &detourWndProc }; - - static LRESULT __fastcall detourWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - return CreateWndProcHook.pOriginal(hwnd, uMsg, wParam, lParam); - } -#pragma endregion - #pragma region CreatePlayerHealthModule - static Hook::MHook CreatePlayerHealthModuleHook{ &Offsets::Get_CreatePlayerHealthModule, &detourCreatePlayerHealthModule }; + static Hook::MHook CreatePlayerHealthModuleHook{ "CreatePlayerHealthModule", &Offsets::Get_CreatePlayerHealthModule, &detourCreatePlayerHealthModule}; static DWORD64 detourCreatePlayerHealthModule(DWORD64 playerHealthModule) { PlayerHealthModule::pPlayerHealthModule = reinterpret_cast(playerHealthModule); @@ -50,7 +41,7 @@ namespace GamePH { #pragma endregion #pragma region OnPostUpdate - static Hook::VTHook OnPostUpdateHook{ &GamePH::GameDI_PH2::Get, &detourOnPostUpdate, 0x3A8 }; + static Hook::VTHook OnPostUpdateHook{ "OnPostUpdate", &GamePH::GameDI_PH2::Get, &detourOnPostUpdate, 0x3A8 }; static void detourOnPostUpdate(LPVOID pGameDI_PH2) { OnPostUpdateHook.pOriginal(pGameDI_PH2); @@ -59,7 +50,7 @@ namespace GamePH { #pragma endregion #pragma region CalculateFreeCamCollision - static Hook::MHook CalculateFreeCamCollisionHook{ &Offsets::Get_CalculateFreeCamCollision, &detourCalculateFreeCamCollision }; + static Hook::MHook CalculateFreeCamCollisionHook{ "CalculateFreeCamCollision", &Offsets::Get_CalculateFreeCamCollision, &detourCalculateFreeCamCollision }; static DWORD64 detourCalculateFreeCamCollision(LPVOID pFreeCamera, float* finalPos) { if (Menu::Camera::disablePhotoModeLimits.GetValue() || Menu::Camera::freeCam.GetValue()) @@ -70,7 +61,7 @@ namespace GamePH { #pragma endregion #pragma region LifeSetHealth - static Hook::MHook LifeSetHealthHook{ &Offsets::Get_LifeSetHealth, &detourLifeSetHealth }; + static Hook::MHook LifeSetHealthHook{ "LifeSetHealth", &Offsets::Get_LifeSetHealth, &detourLifeSetHealth }; static void detourLifeSetHealth(float* pLifeHealth, float health) { if (!Menu::Player::godMode.GetValue() && !Menu::Camera::freeCam.GetValue()) @@ -91,7 +82,7 @@ namespace GamePH { #pragma endregion #pragma region TogglePhotoMode - static Hook::MHook TogglePhotoModeHook{ &Offsets::Get_TogglePhotoMode, &detourTogglePhotoMode }; + static Hook::MHook TogglePhotoModeHook{ "TogglePhotoMode", &Offsets::Get_TogglePhotoMode, &detourTogglePhotoMode }; static void detourTogglePhotoMode(LPVOID guiPhotoModeData, bool enabled) { Menu::Camera::photoMode.Set(enabled); @@ -116,7 +107,7 @@ namespace GamePH { #pragma region ShowTPPModelFunc3 static Option wannaUseTPPModel{}; - static Hook::MHook ShowTPPModelFunc3Hook{ &Offsets::Get_ShowTPPModelFunc3, &detourShowTPPModelFunc3 }; + static Hook::MHook ShowTPPModelFunc3Hook{ "ShowTPPModelFunc3", &Offsets::Get_ShowTPPModelFunc3, &detourShowTPPModelFunc3 }; static void detourShowTPPModelFunc3(DWORD64 a1, bool showTPPModel) { wannaUseTPPModel.Set(showTPPModel); @@ -136,7 +127,7 @@ namespace GamePH { #pragma endregion #pragma region MoveCameraFromForwardUpPos - static Hook::MHook MoveCameraFromForwardUpPosHook{ &Offsets::Get_MoveCameraFromForwardUpPos, &detourMoveCameraFromForwardUpPos }; + static Hook::MHook MoveCameraFromForwardUpPosHook{ "MoveCameraFromForwardUpPos", &Offsets::Get_MoveCameraFromForwardUpPos, &detourMoveCameraFromForwardUpPos }; static void detourMoveCameraFromForwardUpPos(LPVOID pCBaseCamera, float* a3, float* a4, Vector3* pos) { GamePH::LevelDI* iLevel = GamePH::LevelDI::Get(); @@ -225,7 +216,7 @@ namespace GamePH { #pragma endregion #pragma region IsNotOutOfBounds - static Hook::MHook IsNotOutOfBoundsHook{ &Offsets::Get_IsNotOutOfBounds, &detourIsNotOutOfBounds }; + static Hook::MHook IsNotOutOfBoundsHook{ "IsNotOutOfBounds", &Offsets::Get_IsNotOutOfBounds, &detourIsNotOutOfBounds }; static bool detourIsNotOutOfBounds(LPVOID pInstance, DWORD64 a2) { if (Menu::Player::disableOutOfBoundsTimer.GetValue()) @@ -239,7 +230,7 @@ namespace GamePH { static LPVOID GetShowUIManager() { return Utils::GetProcAddr("engine_x64_rwdi.dll", "?ShowUIManager@ILevel@@QEAAX_N@Z"); } - static Hook::MHook ShowUIManagerHook{ &GetShowUIManager, &detourShowUIManager }; + static Hook::MHook ShowUIManagerHook{ "ShowUIManager", &GetShowUIManager, &detourShowUIManager }; static void detourShowUIManager(LPVOID pLevelDI, bool enabled) { if (Menu::Misc::disableHUD.GetValue()) @@ -250,39 +241,66 @@ namespace GamePH { #pragma endregion #pragma region fs::open -static LPVOID GetFsOpen() { - return Utils::GetProcAddr("filesystem_x64_rwdi.dll", "?open@fs@@YAPEAUSFsFile@@V?$string_const@D@ttl@@W4TYPE@EFSMode@@W45FFSOpenFlags@@@Z"); -} -static Hook::MHook FsOpenHook{ &GetFsOpen, &detourFsOpen }; - -static DWORD64 detourFsOpen(DWORD64 file, DWORD a2, DWORD a3) { - DWORD64 firstByte = (file >> 56) & 0xFF; // get first byte of addr - - const char* filePath = reinterpret_cast(file & 0x1FFFFFFFFFFFFFFF); // remove first byte of addr in case it exists - std::string fileName = std::filesystem::path(filePath).filename().string(); - if (fileName.empty()) - return FsOpenHook.pOriginal(file, a2, a3); - - for (const auto& entry : std::filesystem::directory_iterator("EGameTools\\FilesToLoad")) { - if (fileName.contains(".rpack")) - return FsOpenHook.pOriginal(file, a2, a3); - if (fileName.contains("player_anims_pc")) - return FsOpenHook.pOriginal(file, a2, a3); - if (fileName.contains("sfx")) - return FsOpenHook.pOriginal(file, a2, a3); - if (entry.path().filename().string().contains(fileName)) { - std::string finalPath = std::string("EGameTools\\FilesToLoad\\") + fileName; - const char* filePath2 = finalPath.c_str(); - - return FsOpenHook.pOriginal(firstByte != 0x0 ? (reinterpret_cast(filePath2) | (firstByte << 56)) : reinterpret_cast(filePath2), a2, a3); // restores first byte of addr if first byte was not 0 - } + static LPVOID GetFsOpen() { + return Utils::GetProcAddr("filesystem_x64_rwdi.dll", "?open@fs@@YAPEAUSFsFile@@V?$string_const@D@ttl@@W4TYPE@EFSMode@@W45FFSOpenFlags@@@Z"); + } + static Hook::MHook FsOpenHook{ "fs::open", &GetFsOpen, &detourFsOpen }; + + static DWORD64 detourFsOpen(DWORD64 file, DWORD a2, DWORD a3) { + DWORD64 firstByte = (file >> 56) & 0xFF; // get first byte of addr + + const char* filePath = reinterpret_cast(file & 0x1FFFFFFFFFFFFFFF); // remove first byte of addr in case it exists + std::string fileName = std::filesystem::path(filePath).filename().string(); + if (fileName.empty()) + return FsOpenHook.pOriginal(file, a2, a3); + + for (const auto& entry : std::filesystem::directory_iterator("EGameTools\\FilesToLoad")) { + if (fileName.contains(".rpack")) + return FsOpenHook.pOriginal(file, a2, a3); + if (fileName.contains("player_anims_pc")) + return FsOpenHook.pOriginal(file, a2, a3); + if (fileName.contains("sfx")) + return FsOpenHook.pOriginal(file, a2, a3); + if (entry.path().filename().string().contains(fileName)) { + std::string finalPath = std::string("EGameTools\\FilesToLoad\\") + fileName; + const char* filePath2 = finalPath.c_str(); + + return FsOpenHook.pOriginal(firstByte != 0x0 ? (reinterpret_cast(filePath2) | (firstByte << 56)) : reinterpret_cast(filePath2), a2, a3); // restores first byte of addr if first byte was not 0 + } + } + return FsOpenHook.pOriginal(file, a2, a3); } - return FsOpenHook.pOriginal(file, a2, a3); -} #pragma endregion #pragma endregion #pragma region OtherFuncs + const std::string GameVerToStr(DWORD64 version) { + DWORD64 major = version / 10000; + DWORD64 minor = (version / 100) % 100; + DWORD64 patch = version % 100; + + return std::string(std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch)); + } + const std::string GetCurrentGameVersionStr() { + if (!GetCurrentGameVersion()) + return {}; + + DWORD64 version = GetCurrentGameVersion(); + + DWORD64 major = version / 10000; + DWORD64 minor = (version / 100) % 100; + DWORD64 patch = version % 100; + + return std::string(std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch)); + } + const DWORD64 GetCurrentGameVersion() { + DWORD64(*pGetCurrentGameVersion)() = (decltype(pGetCurrentGameVersion))Offsets::Get_GetCurrentGameVersion(); + if (!pGetCurrentGameVersion) + return 0; + + return pGetCurrentGameVersion(); + } + static DWORD64 ShowTPPModelFunc2(LPVOID a1) { DWORD64(*pShowTPPModelFunc2)(LPVOID a1) = (decltype(pShowTPPModelFunc2))Offsets::Get_ShowTPPModelFunc2(); if (!pShowTPPModelFunc2) diff --git a/DL2GameOverhaulScript/source/game_classes.h b/DL2GameOverhaulScript/source/game_classes.h index 0c8ed6f..b31b9f4 100644 --- a/DL2GameOverhaulScript/source/game_classes.h +++ b/DL2GameOverhaulScript/source/game_classes.h @@ -106,6 +106,9 @@ namespace Engine { } namespace GamePH { + extern const std::string GameVerToStr(DWORD64 version); + extern const std::string GetCurrentGameVersionStr(); + extern const DWORD64 GetCurrentGameVersion(); extern void ShowTPPModel(bool showTPPModel); class PlayerVariables { diff --git a/DL2GameOverhaulScript/source/hook.h b/DL2GameOverhaulScript/source/hook.h index a3f352f..2a8397f 100644 --- a/DL2GameOverhaulScript/source/hook.h +++ b/DL2GameOverhaulScript/source/hook.h @@ -41,16 +41,18 @@ namespace Hook { class HookBase { public: - HookBase() { GetInstances()->insert(this); } + HookBase(const std::string_view& name) : name(name) { GetInstances()->insert(this); } ~HookBase() { GetInstances()->erase(this); } static std::set* GetInstances() { static std::set instances{}; return &instances; }; virtual void HookLoop() {}; + + const std::string_view name; }; template class MHook : HookBase { public: - MHook(GetTargetOffsetRetType(*pGetOffsetFunc)(), OrigType pDetour) : pGetOffsetFunc(pGetOffsetFunc), pDetour(pDetour) {} + MHook(const std::string_view& name, GetTargetOffsetRetType(*pGetOffsetFunc)(), OrigType pDetour) : HookBase(name), pGetOffsetFunc(pGetOffsetFunc), pDetour(pDetour) {} void HookLoop() override { while (true) { @@ -77,7 +79,7 @@ namespace Hook { template class VTHook : HookBase { public: - VTHook(GetTargetOffsetRetType(*pGetOffsetFunc)(), OrigType pDetour, DWORD offset) : pGetOffsetFunc(pGetOffsetFunc), pDetour(pDetour), offset(offset) {} + VTHook(const std::string_view& name, GetTargetOffsetRetType(*pGetOffsetFunc)(), OrigType pDetour, DWORD offset) : HookBase(name), pGetOffsetFunc(pGetOffsetFunc), pDetour(pDetour), offset(offset) {} void HookLoop() override { while (true) { diff --git a/DL2GameOverhaulScript/source/menu/camera.cpp b/DL2GameOverhaulScript/source/menu/camera.cpp index 6a2ac16..439e7a7 100644 --- a/DL2GameOverhaulScript/source/menu/camera.cpp +++ b/DL2GameOverhaulScript/source/menu/camera.cpp @@ -67,11 +67,10 @@ namespace Menu { freeCamSpeed -= 0.5f; } - /*if (ImGui::GetIO().MouseWheel > 0.0f) { - freeCamSpeed += 0.5f; - } else if (ImGui::GetIO().MouseWheel < 0.0f) { - freeCamSpeed -= 0.5f; - }*/ + if (freeCamSpeed < 0.1f) + freeCamSpeed = 0.1f; + else if (freeCamSpeed > 200.0f) + freeCamSpeed = 200.0f; pFreeCam->speedMultiplier = freeCamSpeed; @@ -135,7 +134,7 @@ namespace Menu { ImGui::EndDisabled(); } ImGui::Hotkey("##FreeCamToggleKey", freeCam); - ImGui::SliderFloat("Speed##FreeCam", &freeCamSpeed, 0.0f, 100.0f); + ImGui::SliderFloat("Speed##FreeCam", &freeCamSpeed, 0.1f, 200.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp); ImGui::BeginDisabled(teleportPlayerToCamera.GetChangesAreDisabled()); { ImGui::Checkbox("Teleport Player to Camera", &teleportPlayerToCamera); ImGui::EndDisabled(); diff --git a/DL2GameOverhaulScript/source/menu/menu.cpp b/DL2GameOverhaulScript/source/menu/menu.cpp index 209ad2c..5bcbde6 100644 --- a/DL2GameOverhaulScript/source/menu/menu.cpp +++ b/DL2GameOverhaulScript/source/menu/menu.cpp @@ -1,18 +1,121 @@ #define IMGUI_DEFINE_MATH_OPERATORS #include #include +#include "..\game_classes.h" #include "menu.h" namespace Menu { - static const ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_HorizontalScrollbar; - static const ImVec2 minWndSize = ImVec2(0.0f, 0.0f); - static const ImVec2 defMaxWndSize = ImVec2(900.0f, 675.0f); + static constexpr ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_HorizontalScrollbar; + static constexpr ImGuiWindowFlags welcomeWindowFlags = (windowFlags | ImGuiWindowFlags_NoMove) & ~ImGuiWindowFlags_HorizontalScrollbar; + static constexpr ImVec2 minWelcomeWndSize = ImVec2(700.0f, 0.0f); + static constexpr ImVec2 defMaxWelcomeWndSize = ImVec2(700.0f, 700.0f); + + static constexpr ImVec2 minWndSize = ImVec2(0.0f, 0.0f); + static constexpr ImVec2 defMaxWndSize = ImVec2(900.0f, 675.0f); static ImVec2 maxWndSize = defMaxWndSize; KeyBindOption menuToggle = KeyBindOption(VK_F5); float transparency = 99.0f; float scale = 1.0f; + static Utils::Timer timePassedFromWelcomeScreen{}; + static bool firstTimeRunningFunc = true; + Option firstTimeRunning{}; + + void FirstTimeRunning() { + if (!firstTimeRunning.GetValue()) + return; + if (firstTimeRunningFunc) { + firstTimeRunningFunc = false; + timePassedFromWelcomeScreen = Utils::Timer(10000); + menuToggle.SetChangesAreDisabled(true); + ImGui::OpenPopup("EGameTools - Welcome!"); + } + + ImGui::SetNextWindowSizeConstraints(minWelcomeWndSize, defMaxWelcomeWndSize); + if (ImGui::BeginPopupModal("EGameTools - Welcome!", nullptr, welcomeWindowFlags)) { + ImGui::TextCenteredColored("PLEASE read the following text!", IM_COL32(230, 0, 0, 255)); + ImGui::Spacing(ImVec2(0.0f, 5.0f)); + + const std::string thankYou = "Thank you for downloading EGameTools (" + std::string(MOD_VERSION_STR) + ")!"; + ImGui::TextCentered(thankYou.c_str()); + ImGui::Spacing(ImVec2(0.0f, 5.0f)); + + const std::string gameCompat = "This version of the mod is compatible with game version " + std::string(GAME_VER_COMPAT_STR) + "."; + ImGui::TextCentered(gameCompat.c_str()); + const std::string gameVer = "The game version you are currently running is v" + GamePH::GetCurrentGameVersionStr() + "."; + ImGui::TextCentered(gameVer.c_str()); + const std::string gameTestedVer = "This mod has been tested with version v" + GamePH::GameVerToStr(GAME_VER_COMPAT) + "."; + ImGui::TextCentered(gameTestedVer.c_str()); + if (GamePH::GetCurrentGameVersion() < 11400 || GamePH::GetCurrentGameVersion() > GAME_VER_COMPAT) { + const std::string gameNotCompat = "Please note that your game version has not been officially tested with this mod, therefore expect bugs, glitches or the mod to completely stop working. If so, please " + std::string(GamePH::GetCurrentGameVersion() > GAME_VER_COMPAT ? "wait for a new patch." : "upgrade your game version to one that the mod supports."); + ImGui::TextCenteredColored(gameNotCompat.c_str(), IM_COL32(200, 0, 0, 255)); + } + ImGui::Spacing(ImVec2(0.0f, 5.0f)); + ImGui::TextCentered("I will not bore you with what this mod is about, so let's get right to teaching you how to use it!"); + + ImGui::SeparatorTextColored("Menu Toggle", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("The default key for opening/closing the menu is F5. You can use your mouse to navigate the menu."); + ImGui::TextCentered("To change it, you can open up the menu and change the hotkey by clicking the hotkey button for \"Menu Toggle Key\" and then pressing a key on your keyboard."); + + ImGui::SeparatorTextColored("FreeCam", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("While using FreeCam, you can press Shift or Alt to boost your speed or slow you down respectively."); + ImGui::TextCentered("You can also use the scroll wheel to change FreeCam speed while FreeCam is enabled."); + + ImGui::SeparatorTextColored("Menu Sliders", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("To manually change the value of a slider option, hold \"CTRL\" while clicking the slider."); + ImGui::TextCentered("This will let you input a value manually into the slider, which can even surpass the option's slider limit, given I allowed the option to do so."); + + ImGui::SeparatorTextColored("Custom File Loading", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("The mod always creates a folder \"EGameTools\\FilesToLoad\" inside the same folder as the game executable (exe) or in the same folder as the mod file."); + ImGui::TextCentered("This folder is used for custom file loading. This has only been tested with a few mods that change some .scr files, GPUfx files, and other files included inside .pak game archives, or files like .rpack files."); + ImGui::TextCentered("Files in this folder must have the same names as the ones from the game files, otherwise the game won't know it should load those files. Files in subfolders of the \"EGameTools\\FilesToLoad\" folder will automatically be detected, so you can sort all your mods in different folders!"); + ImGui::TextCentered("The game will reload a lot of the files upon a load of your savegame, so if you want to edit those files and reload them without having to restart the game, just reload your savegame and the game should automatically reload most of those files!"); + ImGui::Spacing(ImVec2(0.0f, 5.0f)); + ImGui::TextCentered("The gist of it is, you now don't have to use dataX.pak mods anymore! You can open the pak files, extract their files in the \"EGameTools\\FilesToLoad\" folder and start the game!"); + + ImGui::SeparatorTextColored("Game Variables Reloading", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("You can also reload Player Variables from a file specified by you, or reload Jump Parameters from \"EGameTools\\FilesToLoad\"."); + + ImGui::SeparatorTextColored("Hotkeys", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("Most mod menu options are toggleable by a hotkey that you can change by clicking the hotkey button for the respective option and then pressing a key on your keyboard."); + ImGui::TextCentered("To change those hotkeys through the config file, visit the \"Virtual-Key Codes\" page from Microsoft which contains a list of all virtual key codes. Simply write the name of the keycode you want to use for each hotkey and save the config file."); + + ImGui::SeparatorTextColored("Config", IM_COL32(200, 0, 0, 255)); + ImGui::NewLine(); + ImGui::TextCentered("A config file \"EGameTools.ini\" is stored in the same folder as the game executable (exe) or in the same folder as the mod file."); + ImGui::TextCentered("The config file stores the mod menu's options and hotkeys."); + + ImGui::Spacing(ImVec2(0.0f, 5.0f)); + + ImGui::TextCentered("Changes to the mod menu or to the config file are always automatically saved or refreshed respectively."); + ImGui::TextCentered("You DO NOT NEED to restart the game for the changes in the config to be applied!"); + ImGui::TextCentered("If you want to regenerate the config file, delete it and it will automatically be regenerated."); + + ImGui::Separator(); + ImGui::NewLine(); + ImGui::TextCentered("Finally, if you've got any issue, no matter how small, please make sure to report it! I will try my best to fix it. I want this mod to be polished and enjoyable to use!"); + ImGui::TextCentered("If you've got any suggestions for how I could improve the mod, in terms of UI, features, among other things, please let me know!"); + ImGui::Spacing(ImVec2(0.0f, 5.0f)); + + ImGui::BeginDisabled(!timePassedFromWelcomeScreen.DidTimePass()); { + const std::string btnText = "Let me play!" + (!timePassedFromWelcomeScreen.DidTimePass() ? (std::to_string(10 - (timePassedFromWelcomeScreen.GetTimePassed() / 1000)) + ")") : ""); + if (ImGui::ButtonCentered(btnText.c_str(), ImVec2(0.0f, 30.0f))) { + ImGui::CloseCurrentPopup(); + menuToggle.SetChangesAreDisabled(false); + firstTimeRunning.Set(false); + } + ImGui::EndDisabled(); + } + ImGui::EndPopup(); + } + } void Render() { ImGuiStyle* style = &ImGui::GetStyle(); style->Colors[ImGuiCol_WindowBg] = ImVec4(style->Colors[ImGuiCol_WindowBg].x, style->Colors[ImGuiCol_WindowBg].y, style->Colors[ImGuiCol_WindowBg].z, static_cast(transparency) / 100.0f); diff --git a/DL2GameOverhaulScript/source/menu/menu.h b/DL2GameOverhaulScript/source/menu/menu.h index 781cf3c..17586ef 100644 --- a/DL2GameOverhaulScript/source/menu/menu.h +++ b/DL2GameOverhaulScript/source/menu/menu.h @@ -20,5 +20,8 @@ namespace Menu { extern float transparency; extern float scale; + extern Option firstTimeRunning; + + extern void FirstTimeRunning(); extern void Render(); } \ No newline at end of file diff --git a/DL2GameOverhaulScript/source/sigscan/offsets.h b/DL2GameOverhaulScript/source/sigscan/offsets.h index c8308a5..000909a 100644 --- a/DL2GameOverhaulScript/source/sigscan/offsets.h +++ b/DL2GameOverhaulScript/source/sigscan/offsets.h @@ -28,7 +28,7 @@ struct Offsets { // Input related AddOffset(g_CInput, "engine_x64_rwdi.dll", "48 8B 0D [?? ?? ?? ?? 48 85 C9 74 0F 48 8B 01 84 D2", PatternType::RelativePointer, PDWORD64*) - AddOffset(WndProc, "engine_x64_rwdi.dll", "40 55 56 57 41 54 41 56 48 83 EC ?? 49 8B E9", PatternType::Address, LPVOID) + //AddOffset(WndProc, "engine_x64_rwdi.dll", "40 55 56 57 41 54 41 56 48 83 EC ?? 49 8B E9", PatternType::Address, LPVOID) // Player vars related AddStaticOffset(LoadPlayerVariableFunc_size, 0x14E) @@ -49,6 +49,7 @@ struct Offsets { //AddOffset(CameraFPPDI_VT, "gamedll_ph_x64_rwdi.dll", "48 8D 05 [?? ?? ?? ?? 48 89 07 48 8D 4F 60", PatternType::RelativePointer, DWORD64) // Functions + AddOffset(GetCurrentGameVersion, "gamedll_ph_x64_rwdi.dll", "B8 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC CC 48 83 79", PatternType::Address, LPVOID) AddOffset(ReadVideoSettings, "engine_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 8D 4C 24 ?? FF 15 ?? ?? ?? ?? 48 C7 C2 ?? ?? ?? ??", PatternType::RelativePointer, LPVOID) AddOffset(CalculateFreeCamCollision, "gamedll_ph_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 8B 06 4C 8D 4C 24 ??", PatternType::RelativePointer, LPVOID) //AddOffset(GetViewCamera, "engine_x64_rwdi.dll", "E8 [?? ?? ?? ?? 48 85 C0 74 28 48 8B C8", PatternType::RelativePointer, LPVOID) diff --git a/DL2GameOverhaulScript/source/utils.cpp b/DL2GameOverhaulScript/source/utils.cpp index fbbdd8f..e142905 100644 --- a/DL2GameOverhaulScript/source/utils.cpp +++ b/DL2GameOverhaulScript/source/utils.cpp @@ -4,22 +4,38 @@ #include "utils.h" namespace Utils { - Timer::Timer(long timeMs) : timeToPass(timeMs), timePassed(false) { + Timer::Timer(long timeMs) : timeToPass(std::chrono::milliseconds(timeMs)), timePassed(false) { start = std::chrono::time_point_cast(clock::now()); } - bool Timer::GetTimePassed() { + + long Timer::GetTimePassed() { + if (timePassed) + return -1; + + auto end = clock::now(); + auto duration = std::chrono::duration_cast(end - start); + long timePassedMs = duration.count(); + + if (timePassedMs < 0) { + start = std::chrono::time_point_cast(clock::now()); + return -1; + } + return timePassedMs; + } + bool Timer::DidTimePass() { if (timePassed) return timePassed; auto end = clock::now(); - long timePassedMs = static_cast((end - start).count()); + auto duration = std::chrono::duration_cast(end - start); + long timePassedMs = duration.count(); if (timePassedMs < 0) { start = std::chrono::time_point_cast(clock::now()); return false; } - - if (timePassedMs >= timeToPass) + + if (duration >= timeToPass) timePassed = true; return timePassed; } diff --git a/DL2GameOverhaulScript/source/utils.h b/DL2GameOverhaulScript/source/utils.h index 563fd85..005d3b7 100644 --- a/DL2GameOverhaulScript/source/utils.h +++ b/DL2GameOverhaulScript/source/utils.h @@ -14,10 +14,12 @@ namespace Utils { using clock = std::chrono::system_clock; using time_point_type = std::chrono::time_point; public: - long timeToPass; + std::chrono::milliseconds timeToPass; + Timer() : timeToPass(0), timePassed(false) {} Timer(long timeMs); - bool GetTimePassed(); + long GetTimePassed(); + bool DidTimePass(); private: time_point_type start; bool timePassed;