From 17d45a02aab35f82905eef4e0bba764b7b2b38ef Mon Sep 17 00:00:00 2001 From: IcePixelx <41352111+PixieCore@users.noreply.github.com> Date: Tue, 5 Oct 2021 00:25:58 +0200 Subject: [PATCH] Read description for changes. * Completely re-wrote the DirectX creation and hooks. * Using spdlog mostly everywhere now. * Added prints for when compiled in debug. * Using LockCursor now to prevent input to the game while in the gui. * Patched the game to call CMatSystemSurface::LockCursor instead of it calling the inlined version. * Added rebuild CHostState::FrameUpdate (Not finished yet) * Added option to print to command prompt again. * Added log Auto-Clear. * Added scan for NetChan Encryption Key Pointer. * Added more error handling when loading gui config. --- r5dev/include/CGameConsole.h | 5 +- r5dev/include/enums.h | 2 +- r5dev/include/gameclasses.h | 4 +- r5dev/include/gui_utility.h | 20 +- r5dev/include/hooks.h | 14 + r5dev/include/id3dx.h | 13 +- r5dev/include/patterns.h | 12 +- r5dev/r5dev.vcxproj | 2 + r5dev/r5dev.vcxproj.filters | 12 + r5dev/src/CCompanion.cpp | 41 +- r5dev/src/CGameConsole.cpp | 64 +++- r5dev/src/console.cpp | 10 + r5dev/src/dllmain.cpp | 10 +- r5dev/src/gameclasses.cpp | 4 + r5dev/src/hooks/connectclient.cpp | 3 +- r5dev/src/hooks/hooks.cpp | 86 +++-- r5dev/src/hooks/hoststate.cpp | 179 +++++++++ r5dev/src/hooks/lockcursor.cpp | 18 + r5dev/src/hooks/sqvm.cpp | 39 +- r5dev/src/id3dx.cpp | 617 ++++++++++++------------------ r5dev/src/opcptc.cpp | 23 ++ 21 files changed, 695 insertions(+), 483 deletions(-) create mode 100644 r5dev/src/hooks/hoststate.cpp create mode 100644 r5dev/src/hooks/lockcursor.cpp diff --git a/r5dev/include/CGameConsole.h b/r5dev/include/CGameConsole.h index 674e547a..97949bb0 100644 --- a/r5dev/include/CGameConsole.h +++ b/r5dev/include/CGameConsole.h @@ -8,7 +8,7 @@ void DrawConsole(); /////////////////////////////////////////////////////////////////////////////// // Globals -inline ImVector Items; +extern ImVector Items; class CGameConsole { @@ -33,6 +33,7 @@ public: void ProcessCommand(const char* command_line); void ExecCommand(const char* command_line); int TextEditCallback(ImGuiInputTextCallbackData* data); + bool ShouldPrintToCommandPrompt() { return g_GuiConfig.CGameConsoleConfig.printCmd; }; /////////////////////////////////////////////////////////////////////////// // History @@ -46,9 +47,9 @@ public: // Utility void ClearLog() { - for (int i = 0; i < Items.Size; i++) { free(Items[i]); } Items.clear(); } + void AddLog(const char* fmt, ...) IM_FMTARGS(2) { char buf[1024]; diff --git a/r5dev/include/enums.h b/r5dev/include/enums.h index f3ff58b5..ca1d7662 100644 --- a/r5dev/include/enums.h +++ b/r5dev/include/enums.h @@ -332,7 +332,7 @@ enum ClientFrameStage_t FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE }; -enum HostStates_t +enum class HostStates_t : int { HS_NEW_GAME = 0x0, HS_LOAD_GAME = 0x1, diff --git a/r5dev/include/gameclasses.h b/r5dev/include/gameclasses.h index 05211aa2..89619123 100644 --- a/r5dev/include/gameclasses.h +++ b/r5dev/include/gameclasses.h @@ -239,8 +239,8 @@ struct QAngle // Implement the proper class of this at some point. class CHostState { public: - int m_iCurrentState; //0x0000 - int m_iNextState; //0x0004 + HostStates_t m_iCurrentState; //0x0000 + HostStates_t m_iNextState; //0x0004 Vector3 m_vecLocation; //0x0008 QAngle m_angLocation; //0x0014 char m_levelName[64]; //0x0020 diff --git a/r5dev/include/gui_utility.h b/r5dev/include/gui_utility.h index e502bdc3..2f1db191 100644 --- a/r5dev/include/gui_utility.h +++ b/r5dev/include/gui_utility.h @@ -14,6 +14,9 @@ public: { int bind1 = VK_OEM_3; int bind2 = VK_INSERT; + int autoClearLimit = 300; + bool autoClear = true; + bool printCmd = false; } CGameConsoleConfig; struct @@ -24,13 +27,13 @@ public: void Load() { + spdlog::debug("Loading the Gui Config..\n"); std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config nlohmann::json in; - std::ifstream configFile(path, std::ios::in); // Parse config file. - - if (configFile.good() && configFile) // Check if it parsed. + try { + std::ifstream configFile(path, std::ios::binary); // Parse config file. configFile >> in; configFile.close(); @@ -41,6 +44,9 @@ public: // CGameConsole CGameConsoleConfig.bind1 = in["config"]["CGameConsole"]["bind1"].get(); CGameConsoleConfig.bind2 = in["config"]["CGameConsole"]["bind2"].get(); + CGameConsoleConfig.autoClearLimit = in["config"]["CGameConsole"]["autoClearLimit"].get(); + CGameConsoleConfig.autoClear = in["config"]["CGameConsole"]["autoClear"].get(); + CGameConsoleConfig.printCmd = in["config"]["CGameConsole"]["printCmd"].get(); // CCompanion CCompanionConfig.bind1 = in["config"]["CCompanion"]["bind1"].get(); @@ -48,6 +54,11 @@ public: } } } + catch (const std::exception& ex) + { + spdlog::critical("Gui Config loading failed. Perhaps re-create it by messing with Options in CGameConsole. Reason: {}\n", ex.what()); + return; + } } void Save() @@ -57,6 +68,9 @@ public: // CGameConsole out["config"]["CGameConsole"]["bind1"] = CGameConsoleConfig.bind1; out["config"]["CGameConsole"]["bind2"] = CGameConsoleConfig.bind2; + out["config"]["CGameConsole"]["autoClearLimit"] = CGameConsoleConfig.autoClearLimit; + out["config"]["CGameConsole"]["autoClear"] = CGameConsoleConfig.autoClear; + out["config"]["CGameConsole"]["printCmd"] = CGameConsoleConfig.printCmd; // CCompanion out["config"]["CCompanion"]["bind1"] = CCompanionConfig.bind1; diff --git a/r5dev/include/hooks.h b/r5dev/include/hooks.h index 14b2e67f..f606e6bd 100644 --- a/r5dev/include/hooks.h +++ b/r5dev/include/hooks.h @@ -87,6 +87,13 @@ namespace Hooks extern ConCommand_IsFlagSetFn originalConCommand_IsFlagSet; #pragma endregion +#pragma region CMatSystemSurface + void LockCursor(void* thisptr); + + using LockCursorFn = void(*)(void*); + extern LockCursorFn originalLockCursor; +#pragma endregion + #pragma region WinAPI BOOL WINAPI GetCursorPos(LPPOINT lpPoint); BOOL WINAPI SetCursorPos(int X, int Y); @@ -113,6 +120,13 @@ namespace Hooks extern FileSystemWarningFn originalFileSystemWarning; #pragma endregion +#pragma region HostState + void FrameUpdate(void* rcx, void* rdx, float time); + + using FrameUpdateFn = void(*)(void*, void*, float); + extern FrameUpdateFn originalFrameUpdate; +#pragma endregion + #pragma region Other int MSG_EngineError(char* fmt, va_list args); bool LoadPlaylist(const char* playlist); diff --git a/r5dev/include/id3dx.h b/r5dev/include/id3dx.h index 78ed097d..cab22d1e 100644 --- a/r5dev/include/id3dx.h +++ b/r5dev/include/id3dx.h @@ -2,10 +2,7 @@ ///////////////////////////////////////////////////////////////////////////// // Initialization -void SetupImGui(); void SetupDXSwapChain(); -void DrawImGui(); -void DestroyRenderTarget(); ///////////////////////////////////////////////////////////////////////////// // Internals @@ -17,16 +14,14 @@ void RemoveDXHooks(); // Handlers extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); extern HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); +extern HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); ///////////////////////////////////////////////////////////////////////////// // Globals extern DWORD g_dThreadId; -extern BOOL g_bShowConsole; -extern BOOL g_bShowBrowser; +extern bool g_bShowConsole; +extern bool g_bShowBrowser; ///////////////////////////////////////////////////////////////////////////// - -//################################################################################# -// UTILS -//################################################################################# +// Utils bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv); \ No newline at end of file diff --git a/r5dev/include/patterns.h b/r5dev/include/patterns.h index 040b044e..7575b145 100644 --- a/r5dev/include/patterns.h +++ b/r5dev/include/patterns.h @@ -80,8 +80,8 @@ namespace FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); /*0x160686DC0*/ - MemoryAddress addr_NetChan_EncKeyPtr = MemoryAddress(0x160686DC0); - char* addr_NetChan_EncKey = addr_NetChan_EncKeyPtr.Offset(4816).RCast(); + MemoryAddress addr_NetChan_EncKeyPtr = r5_patterns.StringSearch("client:NetEncryption_NewKey").FindPatternSelf("48 8D ? ? ? ? ? 48 3B", MemoryAddress::Direction::UP, 150).ResolveRelativeAddressSelf(0x3, 0x7); + char* addr_NetChan_EncKey = addr_NetChan_EncKeyPtr.Offset(0x12D0).RCast(); /*0x140263E70*/ FUNC_AT_ADDRESS(addr_NetChan_SetEncKey, void(*)(uintptr_t, const char*), MemoryAddress(0x140263E70).GetPtr()); @@ -121,6 +121,12 @@ namespace FUNC_AT_ADDRESS(addr_CBaseFileSystem_FileSystemWarning, void(*)(void*, FileWarningLevel_t, const char*, ...), r5_patterns.PatternSearch("E8 ? ? ? ? 33 C0 80 3B 2A").FollowNearCallSelf().GetPtr()); #pragma endregion +#pragma region CMatSystemSurface + /*0x140548A00*/ + FUNC_AT_ADDRESS(addr_CMatSystemSurface_LockCursor, void(*)(void*), MemoryAddress(0x140548A00).GetPtr()); // Maybe sigscan this via RTTI. + + /*0x1405489C0*/ + FUNC_AT_ADDRESS(addr_CMatSystemSurface_UnlockCursor, void(*)(void*), MemoryAddress(0x1405489C0).GetPtr()); // Maybe sigscan this via RTTI. #pragma region Utility /*0x140295600*/ FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.StringSearch("Engine Error").FindPatternSelf("48 89 ? ? ? 48 89", MemoryAddress::Direction::UP, 500).GetPtr()); @@ -174,7 +180,7 @@ namespace PRINT_ADDRESS("CClient::Clear", addr_CClient_Clear); PRINT_ADDRESS("INetChannel::Shutdown", addr_NetChan_Shutdown); PRINT_ADDRESS("INetChannel::SetEncryptionKey", addr_NetChan_SetEncKey); - PRINT_ADDRESS("INetChannel::EncryptionKey", addr_NetChan_EncKey) + PRINT_ADDRESS("INetChannel::EncryptionKey", addr_NetChan_EncKey); PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); PRINT_ADDRESS("CServer::ConnectClient", addr_CServer_ConnectClient); diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj index c521f63a..cb9600f8 100644 --- a/r5dev/r5dev.vcxproj +++ b/r5dev/r5dev.vcxproj @@ -388,8 +388,10 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( + + diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters index e0eb1929..2b0ae6f1 100644 --- a/r5dev/r5dev.vcxproj.filters +++ b/r5dev/r5dev.vcxproj.filters @@ -106,6 +106,12 @@ {10edfee7-8c10-41de-b8f3-424826d2614a} + + {02d83321-09fe-4a60-86d9-b1e8d5e165f4} + + + {a2663195-c4f2-4d5f-8d65-cfed54976e4c} + @@ -204,6 +210,12 @@ r5-sdk\src + + hooks\src\hoststate + + + hooks\src\cmatsystemsurface + diff --git a/r5dev/src/CCompanion.cpp b/r5dev/src/CCompanion.cpp index 0ba5c913..0863af10 100644 --- a/r5dev/src/CCompanion.cpp +++ b/r5dev/src/CCompanion.cpp @@ -7,8 +7,6 @@ #include "CCompanion.h" #include "r5net.h" -//#define OVERLAY_DEBUG - CCompanion* g_ServerBrowser = nullptr; bool g_CheckCompBanDB = true; @@ -92,16 +90,17 @@ void CCompanion::UpdateHostingStatus() if (*reinterpret_cast(0x1656057E0) == NULL) // Check if script checksum is valid yet. break; - switch (ServerVisibility) { + switch (ServerVisibility) + { case EServerVisibility::Hidden: MyServer.hidden = true; - break; - + break; case EServerVisibility::Public: MyServer.hidden = false; break; - + default: + break; } SendHostingPostRequest(); @@ -122,9 +121,7 @@ void CCompanion::RefreshServerList() { std::thread t([this]() { -#ifdef OVERLAY_DEBUG - std::cout << " [+CCompanion+] Refreshing server list with string " << MatchmakingServerStringBuffer << "\n"; -#endif + spdlog::debug("[+CCompanion+] Refreshing server list with string {}\n", MatchmakingServerStringBuffer); bThreadLocked = true; ServerList = r5net->GetServersList(ServerListMessage); bThreadLocked = false; @@ -137,6 +134,7 @@ void CCompanion::RefreshServerList() void CCompanion::SendHostingPostRequest() { HostToken = std::string(); + spdlog::debug("[+CCompanion+] Sending PostServerHost request now..\n"); bool result = r5net->PostServerHost(HostRequestMessage,HostToken, ServerListing{ MyServer.name, @@ -255,10 +253,11 @@ void CCompanion::ServerBrowserSection() { ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer)); - ImGui::SameLine(); ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter the encryption key", ServerEncKeyBuffer, IM_ARRAYSIZE(ServerEncKeyBuffer)); + ImGui::SameLine(); + ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter the encryption key", ServerEncKeyBuffer, IM_ARRAYSIZE(ServerEncKeyBuffer)); + ImGui::SameLine(); - - if (ImGui::SameLine(); ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) + if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) { ConnectToServer(ServerConnStringBuffer, ServerEncKeyBuffer); } @@ -274,8 +273,6 @@ void CCompanion::ServerBrowserSection() } ImGui::PopItemWidth(); - - } void CCompanion::HiddenServersModal() @@ -465,6 +462,7 @@ void CCompanion::HostServerSection() { if (!MyServer.name.empty() && !MyServer.playlist.empty()) { + spdlog::debug("[+CCompanion+] Starting Server with name {}, map {} and playlist {}..\n", MyServer.name, ServerMap, MyServer.playlist); ServerNameErr = std::string(); UpdateHostingStatus(); @@ -482,11 +480,6 @@ void CCompanion::HostServerSection() std::stringstream cmd; cmd << "map " << ServerMap; ProcessCommand(cmd.str().c_str()); - - if (StartAsDedi) - { - Hooks::ToggleDevCommands(); - } } else { @@ -512,18 +505,21 @@ void CCompanion::HostServerSection() { if (ImGui::Button("Reload Scripts##ServerHost_ReloadServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) { + spdlog::debug("[+CCompanion+] Reloading scripts..\n"); ProcessCommand("reparse_weapons"); ProcessCommand("reload"); } if (ImGui::Button("Change Level##ServerHost_ChangeLevel", ImVec2(ImGui::GetWindowSize().x, 32))) { + spdlog::debug("[+CCompanion+] Changing level to {}..\n", ServerMap); strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. GameGlobals::HostState->m_iNextState = HostStates_t::HS_CHANGE_LEVEL_MP; // Force CHostState::FrameUpdate to change the level. } if (ImGui::Button("Stop Server##ServerHost_StopServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) { + spdlog::debug("[+CCompanion+] Stopping server..\n"); ProcessCommand("LeaveMatch"); // Use script callback instead. GameGlobals::HostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; // Force CHostState::FrameUpdate to shutdown the server for dedicated. } @@ -594,6 +590,7 @@ void CCompanion::Draw(const char* title) void CCompanion::ProcessCommand(const char* command_line) { + spdlog::debug("[+CCompanion+] Processing command {}\n", command_line); std::thread t(&CCompanion::ExecCommand, this, command_line); t.detach(); @@ -635,15 +632,16 @@ void CCompanion::ConnectToServer(const std::string connString, const std::string void CCompanion::RegenerateEncryptionKey() { + spdlog::debug("[+CCompanion+] Regenerating Encryption Key..\n"); BCRYPT_ALG_HANDLE hAlgorithm; if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0) { - std::cerr << "Failed to open rng algorithm\n"; + spdlog::critical("[+CCompanion+] Failed to open rng algorithm\n"); } unsigned char pBuffer[0x10u]; if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0) { - std::cerr << "Failed to generate random data\n"; + spdlog::critical("[+CCompanion+] Failed to generate random data\n"); } std::string fin; @@ -671,6 +669,7 @@ void DrawBrowser() static CCompanion browser; static bool AssignPtr = []() { g_ServerBrowser = &browser; + spdlog::debug("[+CCompanion+] Created CCompanion Class instance.\n"); return true; } (); diff --git a/r5dev/src/CGameConsole.cpp b/r5dev/src/CGameConsole.cpp index dc522f8b..246f93df 100644 --- a/r5dev/src/CGameConsole.cpp +++ b/r5dev/src/CGameConsole.cpp @@ -6,9 +6,8 @@ #include "gameclasses.h" #include "CGameConsole.h" -#define OVERLAY_DEBUG - CGameConsole* g_GameConsole = nullptr; +ImVector Items; /*----------------------------------------------------------------------------- * _cgameconsole.cpp @@ -35,17 +34,16 @@ CGameConsole::CGameConsole() CGameConsole::~CGameConsole() { ClearLog(); - for (int i = 0; i < History.Size; i++) - { - free(History[i]); - } + History.clear(); } /////////////////////////////////////////////////////////////////////////// // Draw void CGameConsole::Draw(const char* title) { - bool copy_to_clipboard = false; + bool CopyToClipboard = false; + static auto cgameconsoleConfig = &g_GuiConfig.CGameConsoleConfig; + static auto ccompanionConfig = &g_GuiConfig.CCompanionConfig; if (!ThemeSet) { @@ -53,6 +51,12 @@ void CGameConsole::Draw(const char* title) ThemeSet = true; } + if (cgameconsoleConfig->autoClear && Items.Size > cgameconsoleConfig->autoClearLimit) // Check if Auto-Clear is enabled and if its above our limit. If yes then clear. + { + ClearLog(); + History.clear(); + } + //ImGui::ShowStyleEditor(); ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); @@ -60,7 +64,6 @@ void CGameConsole::Draw(const char* title) ImGui::Begin(title, NULL); // ImGui::Begin should never fail, if it does we got another problem. { - // Reserve enough left-over height and width for 1 separator + 1 input text const float FooterHeightToReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); const float FooterWidthtoReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); @@ -69,24 +72,38 @@ void CGameConsole::Draw(const char* title) ImGui::Separator(); if (ImGui::BeginPopup("Options")) { - ImGui::Checkbox("Auto-scroll", &AutoScroll); + if (ImGui::Checkbox("Print to Cmd", &cgameconsoleConfig->printCmd)) + g_GuiConfig.Save(); + + ImGui::Checkbox("Auto-Scroll", &AutoScroll); + + if (ImGui::Checkbox("Auto-Clear", &cgameconsoleConfig->autoClear)) + g_GuiConfig.Save(); + + ImGui::SameLine(); + + ImGui::PushItemWidth(100); + if (ImGui::InputInt("Auto Clear Limit##AutoClearAfterCertainIndexCGameConsole", &cgameconsoleConfig->autoClearLimit)) + g_GuiConfig.Save(); + + ImGui::PopItemWidth(); + if (ImGui::SmallButton("Clear")) - { ClearLog(); - } - copy_to_clipboard = ImGui::SmallButton("Copy"); + + ImGui::SameLine(); + CopyToClipboard = ImGui::SmallButton("Copy"); ImGui::Text("CG Hotkey:"); ImGui::SameLine(); - if (ImGui::Hotkey("##OpenCGameConsoleBind1", &g_GuiConfig.CGameConsoleConfig.bind1, ImVec2(80, 80))) - { + if (ImGui::Hotkey("##OpenCGameConsoleBind1", &cgameconsoleConfig->bind1, ImVec2(80, 80))) g_GuiConfig.Save(); - } + ImGui::Text("CC Hotkey:"); ImGui::SameLine(); - if (ImGui::Hotkey("##OpenCCompanionBind1", &g_GuiConfig.CCompanionConfig.bind1, ImVec2(80, 80))) - { + + if (ImGui::Hotkey("##OpenCCompanionBind1", &ccompanionConfig->bind1, ImVec2(80, 80))) g_GuiConfig.Save(); - } + ImGui::EndPopup(); } if (ImGui::Button("Options")) @@ -139,10 +156,11 @@ void CGameConsole::Draw(const char* title) /////////////////////////////////////////////////////////////////////// ImGui::BeginChild("ScrollingRegion", ImVec2(0, -FooterHeightToReserve), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 4.f, 6.f }); - if (copy_to_clipboard) + if (CopyToClipboard) { ImGui::LogToClipboard(); } + for (int i = 0; i < Items.Size; i++) { const char* item = Items[i]; @@ -214,12 +232,15 @@ void CGameConsole::Draw(const char* title) } } - if (copy_to_clipboard) + if (CopyToClipboard) { ImGui::LogFinish(); } - if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) { ImGui::SetScrollHereY(1.0f); } + if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) + { + ImGui::SetScrollHereY(1.0f); + } ScrollToBottom = false; /////////////////////////////////////////////////////////////////////// @@ -390,6 +411,7 @@ void DrawConsole() static CGameConsole console; static bool AssignPtr = []() { g_GameConsole = &console; + spdlog::debug("[+CGameConsole+] Created CGameConsole Class instance.\n"); return true; } (); console.Draw("Console"); diff --git a/r5dev/src/console.cpp b/r5dev/src/console.cpp index ec264a85..36cc7017 100644 --- a/r5dev/src/console.cpp +++ b/r5dev/src/console.cpp @@ -52,6 +52,16 @@ void SetupConsole() printf("THREAD ID: %ld\n\n", threadId0); CloseHandle(hThread0); } + + // Initialize global spdlog. + auto console = spdlog::stdout_logger_mt("console"); + console->set_pattern("[%I:%M:%S:%e] [%L] %v"); // Set pattern. + spdlog::set_default_logger(console); // Set as default. + spdlog::flush_every(std::chrono::seconds(5)); // Flush buffers every 5 seconds for every logger. +#ifdef _DEBUG + console->set_level(spdlog::level::debug); +#endif + spdlog::debug("Console and spdlog are setup now!\n"); } //############################################################################# diff --git a/r5dev/src/dllmain.cpp b/r5dev/src/dllmain.cpp index be313cef..2e25f83c 100644 --- a/r5dev/src/dllmain.cpp +++ b/r5dev/src/dllmain.cpp @@ -17,10 +17,12 @@ void InitializeR5Dev() InstallOpcodes(); g_GuiConfig.Load(); // Load gui config. SetupDXSwapChain(); - printf("+-----------------------------------------------------------------------------+\n"); - printf("| R5 DEV -- INITIALIZED ------------------------------------------------- |\n"); - printf("+-----------------------------------------------------------------------------+\n"); - printf("\n"); + + spdlog::get("console")->set_pattern("%v"); + spdlog::info("+-----------------------------------------------------------------------------+\n"); + spdlog::info("| R5 DEV -- INITIALIZED ------------------------------------------------- |\n"); + spdlog::info("+-----------------------------------------------------------------------------+\n"); + spdlog::get("console")->set_pattern("[%I:%M:%S:%e] [%L] %v"); } void TerminateR5Dev() diff --git a/r5dev/src/gameclasses.cpp b/r5dev/src/gameclasses.cpp index 44509499..4c624819 100644 --- a/r5dev/src/gameclasses.cpp +++ b/r5dev/src/gameclasses.cpp @@ -326,6 +326,7 @@ namespace GameGlobals void NullHostNames() { + spdlog::debug("Nulling host names..\n"); const char* hostnameArray[] = { "pin_telemetry_hostname", @@ -356,6 +357,7 @@ namespace GameGlobals void InitGameGlobals() { + spdlog::debug("Initializing Game Globals..\n"); HostState = reinterpret_cast(0x141736120); // Get CHostState from memory. InputSystem = *reinterpret_cast(0x14D40B380); // Get IInputSystem from memory. Cvar = *reinterpret_cast(0x14D40B348); // Get CCVar from memory. @@ -377,6 +379,7 @@ namespace GameGlobals void InitPlaylist() { + spdlog::debug("Parsing Playlist..\n"); while (true) { if ((*PlaylistKeyValues)) @@ -400,6 +403,7 @@ namespace GameGlobals void InitAllCommandVariations() { + spdlog::debug("Initializing all Custom ConVars and Commands..\n"); void* CGameConsoleConCommand = CreateCustomConCommand("cgameconsole", "Opens the R5 Reloaded Console.", 0, CustomCommandVariations::CGameConsole_Callback, nullptr); void* CCompanionConCommand = CreateCustomConCommand("ccompanion", "Opens the R5 Reloaded Server Browser.", 0, CustomCommandVariations::CCompanion_Callback, nullptr); void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); diff --git a/r5dev/src/hooks/connectclient.cpp b/r5dev/src/hooks/connectclient.cpp index ef6f5902..210eb167 100644 --- a/r5dev/src/hooks/connectclient.cpp +++ b/r5dev/src/hooks/connectclient.cpp @@ -61,8 +61,6 @@ void* Hooks::ConnectClient(void* thisptr, void* packet) finalIPAddress = ss.str(); } - R5Net::Client* r5net = g_ServerBrowser->GetR5Net(); - const char* name = *(const char**)((std::uintptr_t)packet + 0x30); std::int64_t originID = *(std::int64_t*)((std::uintptr_t)packet + 0x28); @@ -80,6 +78,7 @@ void* Hooks::ConnectClient(void* thisptr, void* packet) if (g_CheckCompBanDB) { + R5Net::Client* r5net = g_ServerBrowser->GetR5Net(); if (r5net) { std::thread t1(IsClientBanned, r5net, finalIPAddress, originID); diff --git a/r5dev/src/hooks/hooks.cpp b/r5dev/src/hooks/hooks.cpp index cec5aa27..1787a2a5 100644 --- a/r5dev/src/hooks/hooks.cpp +++ b/r5dev/src/hooks/hooks.cpp @@ -13,6 +13,7 @@ void Hooks::InstallHooks() { /////////////////////////////////////////////////////////////////////////////// // Initialize Minhook + spdlog::debug("Hooking game functions now..\n"); MH_Initialize(); /////////////////////////////////////////////////////////////////////////////// @@ -25,7 +26,7 @@ void Hooks::InstallHooks() MH_CreateHook(addr_SQVM_RegisterCreatePlayerTasklist, &Hooks::SQVM_RegisterCreatePlayerTasklist, reinterpret_cast(&originalSQVM_RegisterCreatePlayerTasklist)); /////////////////////////////////////////////////////////////////////////////// - // Hook Game Functions + // Hook Game functions MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); MH_CreateHook(addr_CServer_ConnectClient, &Hooks::ConnectClient, reinterpret_cast(&originalConnectClient)); @@ -38,14 +39,22 @@ void Hooks::InstallHooks() MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChan_Shutdown, reinterpret_cast(&originalNetChan_ShutDown)); /////////////////////////////////////////////////////////////////////////////// - // Hook ConVar | ConCommand functions. + // Hook ConVar | ConCommand functions MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, reinterpret_cast(&originalConVar_IsFlagSet)); MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, reinterpret_cast(&originalConCommand_IsFlagSet)); /////////////////////////////////////////////////////////////////////////////// - // Hooks CBaseFileSystem functions. + // Hook CMatSystemSurface functions + MH_CreateHook(addr_CMatSystemSurface_LockCursor, &LockCursor, reinterpret_cast(&originalLockCursor)); + + /////////////////////////////////////////////////////////////////////////////// + // Hook CBaseFileSystem functions //MH_CreateHook(addr_CBaseFileSystem_FileSystemWarning, &Hooks::FileSystemWarning, reinterpret_cast(&originalFileSystemWarning); + /////////////////////////////////////////////////////////////////////////////// + // Hook HostState functions + //MH_CreateHook(MemoryAddress(0x14023EF80).RCast(), &Hooks::FrameUpdate, reinterpret_cast(&originalFrameUpdate)); + /////////////////////////////////////////////////////////////////////////////// // Hook Utility functions MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); @@ -59,12 +68,10 @@ void Hooks::InstallHooks() void* ClipCursorPtr = user32dll.GetExportedFunction("ClipCursor"); void* GetCursorPosPtr = user32dll.GetExportedFunction("GetCursorPos"); void* ShowCursorPtr = user32dll.GetExportedFunction("ShowCursor"); - MH_CreateHook(SetCursorPosPtr, &Hooks::SetCursorPos, reinterpret_cast(&originalSetCursorPos)); MH_CreateHook(ClipCursorPtr, &Hooks::ClipCursor, reinterpret_cast(&originalClipCursor)); MH_CreateHook(GetCursorPosPtr, &Hooks::GetCursorPos, reinterpret_cast(&originalGetCursorPos)); MH_CreateHook(ShowCursorPtr, &Hooks::ShowCursor, reinterpret_cast(&originalShowCursor)); - /////////////////////////////////////////////////////////////////////////// // Enable WinAPI hooks MH_EnableHook(SetCursorPosPtr); @@ -98,10 +105,18 @@ void Hooks::InstallHooks() MH_EnableHook(addr_ConVar_IsFlagSet); MH_EnableHook(addr_ConCommand_IsFlagSet); + /////////////////////////////////////////////////////////////////////////////// + // Enable CMatSystemSurface hooks + MH_EnableHook(addr_CMatSystemSurface_LockCursor); + /////////////////////////////////////////////////////////////////////////////// // Enable CBaseFileSystem hooks //MH_EnableHook(addr_CBaseFileSystem_FileSystemWarning); + /////////////////////////////////////////////////////////////////////////////// + // Enable HostState hooks +// MH_EnableHook(MemoryAddress(0x14023EF80).RCast()); + /////////////////////////////////////////////////////////////////////////////// // Enabled Utility hooks MH_EnableHook(addr_MSG_EngineError); @@ -132,10 +147,19 @@ void Hooks::RemoveHooks() MH_RemoveHook(addr_NetChan_Shutdown); /////////////////////////////////////////////////////////////////////////////// - // Unhook ConVar | ConCommand functions. + // Unhook ConVar | ConCommand functions MH_RemoveHook(addr_ConVar_IsFlagSet); MH_RemoveHook(addr_ConCommand_IsFlagSet); + /////////////////////////////////////////////////////////////////////////////// + // Unhook CMatSystemSurface functions + MH_EnableHook(MemoryAddress(0x140548A00).RCast()); + + /////////////////////////////////////////////////////////////////////////////// + // Unhook Utility functions + MH_RemoveHook(addr_MSG_EngineError); + MH_RemoveHook(addr_LoadPlaylist); + /////////////////////////////////////////////////////////////////////////////// // Unhook WinAPI if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? @@ -144,22 +168,20 @@ void Hooks::RemoveHooks() void* ClipCursorPtr = user32dll.GetExportedFunction("ClipCursor"); void* GetCursorPosPtr = user32dll.GetExportedFunction("GetCursorPos"); void* ShowCursorPtr = user32dll.GetExportedFunction("ShowCursor"); - MH_RemoveHook(SetCursorPosPtr); MH_RemoveHook(ClipCursorPtr); MH_RemoveHook(GetCursorPosPtr); MH_RemoveHook(ShowCursorPtr); } - /////////////////////////////////////////////////////////////////////////////// - // Unhook Utility functions - MH_RemoveHook(addr_MSG_EngineError); - MH_RemoveHook(addr_LoadPlaylist); - /////////////////////////////////////////////////////////////////////////////// // Unhook CBaseFileSystem functions. //MH_RemoveHook(addr_CBaseFileSystem_FileSystemWarning); + /////////////////////////////////////////////////////////////////////////////// + // Unhook HostState hooks + //MH_RemoveHook(MemoryAddress(0x14023EF80).RCast()); + /////////////////////////////////////////////////////////////////////////////// // Reset Minhook MH_Uninitialize(); @@ -171,21 +193,21 @@ void Hooks::ToggleNetTrace() { MH_EnableHook(addr_NET_ReceiveDatagram); MH_EnableHook(addr_NET_SendDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); + spdlog::info("\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("\n"); } else { MH_DisableHook(addr_NET_ReceiveDatagram); MH_DisableHook(addr_NET_SendDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); + spdlog::info("\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("\n"); } bToggledNetTrace = !bToggledNetTrace; } @@ -196,21 +218,21 @@ void Hooks::ToggleDevCommands() { MH_EnableHook(addr_ConVar_IsFlagSet); MH_EnableHook(addr_ConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); + spdlog::info("\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("\n"); } else { MH_DisableHook(addr_ConVar_IsFlagSet); MH_DisableHook(addr_ConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); + spdlog::info("\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); + spdlog::info("+--------------------------------------------------------+\n"); + spdlog::info("\n"); } bToggledDevFlags = !bToggledDevFlags; } diff --git a/r5dev/src/hooks/hoststate.cpp b/r5dev/src/hooks/hoststate.cpp new file mode 100644 index 00000000..43d5598b --- /dev/null +++ b/r5dev/src/hooks/hoststate.cpp @@ -0,0 +1,179 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + FrameUpdateFn originalFrameUpdate = nullptr; +} + +void Hooks::FrameUpdate(void* rcx, void* rdx, float time) +{ + static auto setjmpFn = MemoryAddress(0x141205460).RCast<__int64(*)(jmp_buf, void*)>(); + static auto host_abortserver = MemoryAddress(0x14B37C700).RCast(); + static auto CHostState_InitFn = MemoryAddress(0x14023E7D0).RCast(); + static auto g_ServerAbortServer = MemoryAddress(0x14B37CA22).RCast(); + static auto State_RunFn = MemoryAddress(0x14023E870).RCast(); + static auto Cbuf_ExecuteFn = MemoryAddress(0x14020D5C0).RCast(); + static auto g_ServerGameClients = MemoryAddress(0x14B383428).RCast<__int64*>(); + static auto SV_InitGameDLLFn = MemoryAddress(0x140308B90).RCast(); + static auto g_CModelLoader = MemoryAddress(0x14173B210).RCast(); + static auto CModelLoader_Map_IsValidFn = MemoryAddress(0x1402562F0).RCast(); + static auto Host_NewGameFn = MemoryAddress(0x140238DA0).RCast(); + static auto Host_Game_ShutdownFn = MemoryAddress(0x14023EDA0).RCast(); + static auto src_drawloading = MemoryAddress(0x14B37D96B).RCast(); + static auto scr_engineevent_loadingstarted = MemoryAddress(0x1666ED024).RCast(); + static auto gfExtendedError = MemoryAddress(0x14B383391).RCast(); + static auto g_CEngineVGui = MemoryAddress(0x141741310).RCast(); + static auto g_ServerDLL = MemoryAddress(0x141732048).RCast(); + static auto Host_ChangelevelFn = MemoryAddress(0x1402387B0).RCast(); + + void* placeHolder = nullptr; + if (setjmpFn(*host_abortserver, placeHolder)) + { + CHostState_InitFn(GameGlobals::HostState); + return; + } + else + { + *g_ServerAbortServer = true; + + while (true) + { + Cbuf_ExecuteFn(); + HostStates_t oldState = GameGlobals::HostState->m_iCurrentState; + switch (GameGlobals::HostState->m_iCurrentState) + { + case HostStates_t::HS_NEW_GAME: + { + // Inlined CHostState::State_NewGame + GameGlobals::HostState->m_bSplitScreenConnect = false; + if (!g_ServerGameClients) // Init Game if it ain't valid. + { + SV_InitGameDLLFn(); + } + + if (!g_ServerGameClients) // SV_InitGameDLL failed its still null. + { + std::cout << "Fatal CHostState::FrameUpdate Error. SV_InitGameDLL() failed." << std::endl; + abort(); // Placeholder. + } + + if ( !CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName) // Check if map is valid and if we can start a new game. + || !Host_NewGameFn(GameGlobals::HostState->m_levelName, nullptr, GameGlobals::HostState->m_bBackgroundLevel, GameGlobals::HostState->m_bSplitScreenConnect, nullptr) ) + { + // Inlined SCR_EndLoadingPlaque + if (*src_drawloading) + { + *scr_engineevent_loadingstarted = 0; + using HideLoadingPlaqueFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[36](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 36))(&g_CEngineVGui);// HideLoadingPlaque + } + else if (*gfExtendedError) + { + using ShowErrorMessageFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[35](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 35))(&g_CEngineVGui);// ShowErrorMessage + } + // End Inline SCR_EndLoadingPlaque + + // Inlined CHostState::GameShutdown + if (GameGlobals::HostState->m_bActiveGame) + { + using GameShutdownFn = void(*)(void*); + (*reinterpret_cast(g_ServerDLL))[9](g_ServerDLL); // (*(void(__fastcall**)(void*))(*(_QWORD*)g_ServerDLL + 72i64))(g_ServerDLL);// GameShutdown + GameGlobals::HostState->m_bActiveGame = 0; + } + // End Inline CHostState::GameShutdown + } + + // Seems useless so nope. + // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) + GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; + + // End Inline CHostState::State_NewGame + break; + } + case HostStates_t::HS_CHANGE_LEVEL_SP: + { + GameGlobals::HostState->m_flShortFrameTime = 1.5; // Set frame time. + if (CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName)) // Check if map is valid and if we can start a new game. + { + Host_ChangelevelFn(true, GameGlobals::HostState->m_levelName, GameGlobals::HostState->m_mapGroupName); // Call change level as singleplayer level. + } + else + { + std::cout << "Fatal CHostState::FrameUpdate Error. Map is not valid."; + } + + // Seems useless so nope. + // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) + GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; + + break; + } + case HostStates_t::HS_CHANGE_LEVEL_MP: + { + GameGlobals::HostState->m_flShortFrameTime = 1.5; // Set frame time. + using LevelShutdownFn = void(__thiscall*)(void*); + (*reinterpret_cast(*g_ServerDLL))[8](g_ServerDLL); // (*(void (__fastcall **)(void *))(*(_QWORD *)server_dll_var + 64i64))(server_dll_var);// LevelShutdown + + if (CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName)) // Check if map is valid and if we can start a new game. + { + using ShowErrorMessageFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[31](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGUI + 31))(&g_CEngineVGUI);// EnabledProgressBarForNextLoad + Host_ChangelevelFn(false, GameGlobals::HostState->m_levelName, GameGlobals::HostState->m_mapGroupName); // Call change level as singleplayer level. + } + + // Seems useless so nope. + // // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) + GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; + + break; + } + case HostStates_t::HS_RUN: + { + State_RunFn(&GameGlobals::HostState->m_iCurrentState, nullptr, time); + break; + } + case HostStates_t::HS_GAME_SHUTDOWN: + { + Host_Game_ShutdownFn(GameGlobals::HostState); + break; + } + case HostStates_t::HS_RESTART: + { + + break; + } + default: + { + break; + } + } + + if (oldState == HostStates_t::HS_RUN) + break; + + if (oldState == HostStates_t::HS_SHUTDOWN || oldState == HostStates_t::HS_RESTART) + break; + + if (oldState == HostStates_t::HS_GAME_SHUTDOWN) + break; + } + + } +// originalFrameUpdate(rcx, rdx, time); +} \ No newline at end of file diff --git a/r5dev/src/hooks/lockcursor.cpp b/r5dev/src/hooks/lockcursor.cpp new file mode 100644 index 00000000..676fb70d --- /dev/null +++ b/r5dev/src/hooks/lockcursor.cpp @@ -0,0 +1,18 @@ +#include "pch.h" +#include "hooks.h" +#include "id3dx.h" + +namespace Hooks +{ + LockCursorFn originalLockCursor = nullptr; +} + +void Hooks::LockCursor(void* thisptr) +{ + if (g_bShowConsole || g_bShowBrowser) + { + addr_CMatSystemSurface_UnlockCursor(thisptr); // Unlock cursor if our gui is shown. + return; + } + return originalLockCursor(thisptr); +} \ No newline at end of file diff --git a/r5dev/src/hooks/sqvm.cpp b/r5dev/src/hooks/sqvm.cpp index 154acfb0..13eab075 100644 --- a/r5dev/src/hooks/sqvm.cpp +++ b/r5dev/src/hooks/sqvm.cpp @@ -51,12 +51,18 @@ void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) vmStr.append(buf); + if (g_GameConsole && g_GameConsole->ShouldPrintToCommandPrompt()) + { + spdlog::info(vmStr); + } + logger.debug(vmStr); + std::string s = oss_print.str(); const char* c = s.c_str(); - Items.push_back(Strdup((const char*)c)); + Items.push_back(Strdup(c)); return NULL; } @@ -97,12 +103,17 @@ __int64 Hooks::SQVM_Warning(void* sqvm, int a2, int a3, int* stringSize, void** std::string stringConstructor((char*)*string, *stringSize); // Get string from memory via std::string constructor. vmStr.append(stringConstructor); + if (g_GameConsole && g_GameConsole->ShouldPrintToCommandPrompt()) + { + spdlog::info(vmStr); + } + logger.debug(vmStr); std::string s = oss_warning.str(); const char* c = s.c_str(); - Items.push_back(Strdup((const char*)c)); + Items.push_back(Strdup(c)); return result; } @@ -129,20 +140,20 @@ __int64 Hooks::SQVM_LoadRson(const char* rson_name) // Returns the new path if the rson exists on the disk if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) { - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", filepath); - printf("##################################################\n"); - printf("\n"); + spdlog::info("\n"); + spdlog::info("##################################################\n"); + spdlog::info("] '{}'\n", filepath); + spdlog::info("##################################################\n"); + spdlog::info("\n"); return originalSQVM_LoadRson(filepath); } - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", rson_name); - printf("##################################################\n"); - printf("\n"); + spdlog::info("\n"); + spdlog::info("##################################################\n"); + spdlog::info("] '{}'\n", rson_name); + spdlog::info("##################################################\n"); + spdlog::info("\n"); return originalSQVM_LoadRson(rson_name); } @@ -166,7 +177,7 @@ bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* scr } if (g_bDebugLoading) { - printf(" [+] Loading SQVM Script '%s' ...\n", filepath); + spdlog::info(" [+] Loading SQVM Script '{}' ...\n", filepath); } /////////////////////////////////////////////////////////////////////////////// // Returns true if the script exists on the disk @@ -176,7 +187,7 @@ bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* scr } if (g_bDebugLoading) { - printf(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); + spdlog::info(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); } return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); diff --git a/r5dev/src/id3dx.cpp b/r5dev/src/id3dx.cpp index 9ecddf58..98b80f7a 100644 --- a/r5dev/src/id3dx.cpp +++ b/r5dev/src/id3dx.cpp @@ -10,51 +10,29 @@ #include "CGameConsole.h" #pragma comment(lib, "d3d11.lib") - -/*--------------------------------------------------------------------------------- - * _id3dx.cpp - *---------------------------------------------------------------------------------*/ - /////////////////////////////////////////////////////////////////////////////////// +// Type definitions. typedef HRESULT(__stdcall* IDXGISwapChainPresent)(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); typedef HRESULT(__stdcall* IDXGIResizeBuffers) (IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); /////////////////////////////////////////////////////////////////////////////////// -typedef BOOL(WINAPI* IPostMessageA)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); -typedef BOOL(WINAPI* IPostMessageW)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); - -/////////////////////////////////////////////////////////////////////////////////// -extern BOOL g_bShowConsole = false; -extern BOOL g_bShowBrowser = false; -static BOOL g_bInitMenu = false; -static BOOL g_bInitialized = false; - -/////////////////////////////////////////////////////////////////////////////////// -static WNDPROC g_oWndProc = NULL; -static HWND g_hGameWindow = NULL; -extern DWORD g_dThreadId = NULL; - -/////////////////////////////////////////////////////////////////////////////////// -static IDXGISwapChainPresent g_fnIDXGISwapChainPresent = nullptr; -static IDXGISwapChain* g_pSwapChain = nullptr; -static IDXGIResizeBuffers g_oResizeBuffers = nullptr; -static ID3D11DeviceContext* g_pDeviceContext = nullptr; -static ID3D11Device* g_pDevice = nullptr; -static ID3D11RenderTargetView* g_pRenderTargetView = nullptr; -static ID3D11DepthStencilView* g_pDepthStencilView = nullptr; -static IPostMessageA g_oPostMessageA = nullptr; -static IPostMessageW g_oPostMessageW = nullptr; - -//################################################################################# -// WINDOW PROCEDURE -//################################################################################# +// Variables. +bool g_bShowConsole = false; +bool g_bShowBrowser = false; +IDXGISwapChainPresent g_fnIDXGISwapChainPresent = nullptr; +IDXGIResizeBuffers g_fnIDXGIResizeBuffers = nullptr; +ID3D11Device* g_pDevice = nullptr; +ID3D11RenderTargetView* g_pMainRenderTargetView = nullptr; +ID3D11DeviceContext* g_pDeviceContext = nullptr; +WNDPROC originalWndProc = NULL; +DWORD g_dThreadId = NULL; LRESULT CALLBACK DXGIMsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hWnd, uMsg, wParam, lParam); } -LRESULT CALLBACK HwndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) { @@ -70,313 +48,113 @@ LRESULT CALLBACK HwndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } if (g_bShowConsole || g_bShowBrowser) - {////////////////////////////////////////////////////////////////////////////// - ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam); - - g_bBlockInput = true; - - switch (uMsg) - { - case WM_LBUTTONDOWN: - return 1L; - case WM_LBUTTONUP: - return 1L; - case WM_LBUTTONDBLCLK: - return 1L; - case WM_RBUTTONDOWN: - return 1L; - case WM_RBUTTONUP: - return 1L; - case WM_RBUTTONDBLCLK: - return 1L; - case WM_MBUTTONDOWN: - return 1L; - case WM_MBUTTONUP: - return 1L; - case WM_MBUTTONDBLCLK: - return 1L; - case WM_KEYDOWN: - return 1L; - case WM_KEYUP: - return 1L; - case WM_MOUSEACTIVATE: - return 1L; - case WM_MOUSEHOVER: - return 1L; - case WM_MOUSEHWHEEL: - return 1L; - case WM_MOUSELEAVE: - return 1L; - case WM_MOUSEMOVE: - return 1L; - case WM_MOUSEWHEEL: - return 1L; - case WM_SETCURSOR: - return 1L; - default: - break; - } - }////////////////////////////////////////////////////////////////////////////// - else { - g_bBlockInput = false; - } - - /////////////////////////////////////////////////////////////////////////////// - return CallWindowProc(g_oWndProc, hWnd, uMsg, wParam, lParam); -} - -//################################################################################# -// POST MESSAGE -//################################################################################# - -BOOL WINAPI HPostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) -{ - if (g_bBlockInput && Msg == WM_MOUSEMOVE) - { - return TRUE; - } - - return g_oPostMessageA(hWnd, Msg, wParam, lParam); -} - -BOOL WINAPI HPostMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) -{ - if (g_bBlockInput && Msg == WM_MOUSEMOVE) - { - return TRUE; - } - - return g_oPostMessageW(hWnd, Msg, wParam, lParam); -} - -//################################################################################# -// IDXGI PRESENT -//################################################################################# - -void GetPresent() -{ - WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, DXGIMsgProc, 0L, 0L, GetModuleHandleA(NULL), NULL, NULL, NULL, NULL, "DX", NULL }; - RegisterClassExA(&wc); - - HWND hWnd = CreateWindowA("DX", NULL, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, wc.hInstance, NULL); - DXGI_SWAP_CHAIN_DESC sd = { 0 }; - D3D_FEATURE_LEVEL nFeatureLevelsSet = D3D_FEATURE_LEVEL_11_0; - D3D_FEATURE_LEVEL nFeatureLevelsSupported; - - ZeroMemory(&sd, sizeof(sd)); - - /////////////////////////////////////////////////////////////////////////////// - sd.BufferCount = 1; - sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - sd.BufferDesc.Height = 800; - sd.BufferDesc.Width = 600; - sd.BufferDesc.RefreshRate = { 60, 1 }; - sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; - sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - sd.Windowed = TRUE; - sd.OutputWindow = hWnd; - sd.SampleDesc.Count = 1; - sd.SampleDesc.Quality = 0; - sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - - /////////////////////////////////////////////////////////////////////////////// - g_hGameWindow = sd.OutputWindow; - UINT nFeatureLevelsRequested = 1; - HRESULT hr = 0; - IDXGISwapChain* pSwapChain = nullptr; - ID3D11Device* pDevice = nullptr; - ID3D11DeviceContext* pContext = nullptr; - - /////////////////////////////////////////////////////////////////////////////// - if (FAILED(hr = D3D11CreateDeviceAndSwapChain(NULL, - D3D_DRIVER_TYPE_HARDWARE, - NULL, - NULL, - &nFeatureLevelsSet, - nFeatureLevelsRequested, - D3D11_SDK_VERSION, - &sd, - &pSwapChain, - &pDevice, - &nFeatureLevelsSupported, - &pContext))) - { - std::cout << "+--------------------------------------------------------+" << std::endl; - std::cout << "| >>>>>>>>>| VIRTUAL METHOD TABLE HOOK FAILED |<<<<<<<<< |" << std::endl; - std::cout << "+--------------------------------------------------------+" << std::endl; - RemoveDXHooks(); - return; - } - - /////////////////////////////////////////////////////////////////////////////// - DWORD_PTR* pSwapChainVtable = nullptr; - DWORD_PTR* pContextVTable = nullptr; - DWORD_PTR* pDeviceVTable = nullptr; - - pSwapChainVtable = (DWORD_PTR*)pSwapChain; - pSwapChainVtable = (DWORD_PTR*)pSwapChainVtable[0]; - pContextVTable = (DWORD_PTR*)pContext; - pContextVTable = (DWORD_PTR*)pContextVTable[0]; - pDeviceVTable = (DWORD_PTR*)pDevice; - pDeviceVTable = (DWORD_PTR*)pDeviceVTable[0]; - - g_fnIDXGISwapChainPresent = (IDXGISwapChainPresent)(DWORD_PTR)pSwapChainVtable[(int)DXGISwapChainVTbl::Present]; - g_oResizeBuffers = (IDXGIResizeBuffers)(DWORD_PTR)pSwapChainVtable[(int)DXGISwapChainVTbl::ResizeBuffers]; - - pSwapChain->Release(); - pContext->Release(); - pDevice->Release(); -} - -//################################################################################# -// INITIALIZATION -//################################################################################# - -void SetupImGui() -{ - ImGui::CreateContext(); - IMGUI_CHECKVERSION(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplWin32_Init(g_hGameWindow); - ImGui_ImplDX11_Init(g_pDevice, g_pDeviceContext); - ImGui::GetIO().ImeWindowHandle = g_hGameWindow; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; -} - -void DrawImGui() -{ - if (!GameGlobals::IsInitialized || !GameGlobals::InputSystem) // Check if GameGlobals initialized and if InputSystem is valid. - return; - - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - - ImGui::NewFrame(); - - if (g_bShowConsole || g_bShowBrowser) - { - GameGlobals::InputSystem->EnableInput(false); // Disable input. + g_bBlockInput = true; // Prevent mouse cursor from being modified if console is open. + if (ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam) > 0) + return 1L; } else { - GameGlobals::InputSystem->EnableInput(true); // Enable input. + g_bBlockInput = false; // Allow mouse input. } - if (g_bShowConsole) - { - DrawConsole(); - } - - if (g_bShowBrowser) - { - DrawBrowser(); - } - - ImGui::EndFrame(); - ImGui::Render(); - - g_pDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + return CallWindowProc(originalWndProc, hwnd, uMsg, wParam, lParam); } -void CreateRenderTarget(IDXGISwapChain* pSwapChain) +void InitRenderer() { - /////////////////////////////////////////////////////////////////////////////// - DXGI_SWAP_CHAIN_DESC sd = { 0 }; - ID3D11Texture2D* pBackBuffer = { 0 }; - D3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc; + spdlog::debug("Registering temporary Window for DirectX..\n"); + // Register temporary window instance to get DirectX 11 relevant virtual function ptr. + WNDCLASSEX ws; + ws.cbSize = sizeof(WNDCLASSEX); + ws.style = CS_HREDRAW | CS_VREDRAW; + ws.lpfnWndProc = DXGIMsgProc; + ws.cbClsExtra = 0; + ws.cbWndExtra = 0; + ws.hInstance = GetModuleHandle(NULL); + ws.hIcon = NULL; + ws.hCursor = NULL; + ws.hbrBackground = NULL; + ws.lpszMenuName = NULL; + ws.lpszClassName = "R5 Reloaded"; + ws.hIconSm = NULL; - /////////////////////////////////////////////////////////////////////////////// - pSwapChain->GetDesc(&sd); - ZeroMemory(&render_target_view_desc, sizeof(render_target_view_desc)); + RegisterClassEx(&ws); + + // Create temporary window. + HWND window = CreateWindowA(ws.lpszClassName, "R5 Reloaded Window", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, ws.hInstance, NULL); - g_hGameWindow = sd.OutputWindow; - render_target_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - render_target_view_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + DXGI_RATIONAL refreshRate; + refreshRate.Numerator = 60; + refreshRate.Denominator = 1; - /////////////////////////////////////////////////////////////////////////////// - pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); - if (pBackBuffer != NULL) { g_pDevice->CreateRenderTargetView(pBackBuffer, &render_target_view_desc, &g_pRenderTargetView); } - g_pDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); - pBackBuffer->Release(); -} + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; -void DestroyRenderTarget() -{ - if (g_pRenderTargetView) + // Setup buffer description. + DXGI_MODE_DESC bufferDescription; + bufferDescription.Width = 100; + bufferDescription.Height = 100; + bufferDescription.RefreshRate = refreshRate; + bufferDescription.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + bufferDescription.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + bufferDescription.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + DXGI_SAMPLE_DESC sampleDescription; + sampleDescription.Count = 1; + sampleDescription.Quality = 0; + + // Setup swap chain description. + DXGI_SWAP_CHAIN_DESC swapChainDescription; + swapChainDescription.BufferDesc = bufferDescription; + swapChainDescription.SampleDesc = sampleDescription; + swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDescription.BufferCount = 1; + swapChainDescription.OutputWindow = window; + swapChainDescription.Windowed = TRUE; + swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + IDXGISwapChain* swapChain; + ID3D11Device* device; + ID3D11DeviceContext* context; + + // Create temporary fake device and swap chain. + if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &swapChainDescription, &swapChain, &device, &featureLevel, &context))) { - g_pRenderTargetView->Release(); - g_pRenderTargetView = nullptr; - g_pDeviceContext->OMSetRenderTargets(0, 0, 0); - std::cout << "+--------------------------------------------------------+" << std::endl; - std::cout << "| >>>>>>>>>>>>>>| RENDER TARGET DESTROYED |<<<<<<<<<<<<< |" << std::endl; - std::cout << "+--------------------------------------------------------+" << std::endl; - } -} - -//################################################################################# -// INTERNALS -//################################################################################# - -HRESULT GetDeviceAndCtxFromSwapchain(IDXGISwapChain* pSwapChain, ID3D11Device** ppDevice, ID3D11DeviceContext** ppContext) -{ - HRESULT ret = pSwapChain->GetDevice(__uuidof(ID3D11Device), (PVOID*)ppDevice); - if (SUCCEEDED(ret)) - { - (*ppDevice)->GetImmediateContext(ppContext); - } - return ret; -} - -IDXGIResizeBuffers originalResizeBuffers = nullptr; - -HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) -{ - g_bShowConsole = false; - g_bShowBrowser = false; - g_bInitialized = false; - - /////////////////////////////////////////////////////////////////////////////// - - DestroyRenderTarget(); - - /////////////////////////////////////////////////////////////////////////////// - return originalResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); -} - -IDXGISwapChainPresent originalPresent = nullptr; - -HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) -{ - if (!g_bInitialized) - { - if (FAILED(GetDeviceAndCtxFromSwapchain(pSwapChain, &g_pDevice, &g_pDeviceContext))) - { - return originalPresent(pSwapChain, nSyncInterval, nFlags); - std::cout << "+--------------------------------------------------------+" << std::endl; - std::cout << "| >>>>>>>>>>| GET DVS AND CTX FROM SCP FAILED |<<<<<<<<< |" << std::endl; - std::cout << "+--------------------------------------------------------+" << std::endl; - } - - CreateRenderTarget(pSwapChain); - SetupImGui(); - - if (g_oWndProc == nullptr) - { // Only initialize HwndProc pointer once to avoid stack overflow during ResizeBuffers(..) - g_oWndProc = (WNDPROC)SetWindowLongPtr(g_hGameWindow, GWLP_WNDPROC, (LONG_PTR)HwndProc); - } - - g_bInitialized = true; - g_pSwapChain = pSwapChain; + std::cout << "Creating Device and Swap Chain failed." << std::endl; + return; } - DrawImGui(); - g_bInitialized = true; - /////////////////////////////////////////////////////////////////////////////// - return originalPresent(pSwapChain, nSyncInterval, nFlags); + DWORD_PTR* swapChainVTable = nullptr; + DWORD_PTR* contextVTable = nullptr; + DWORD_PTR* deviceVTable = nullptr; + + // Get vtable by dereferencing once. + swapChainVTable = (DWORD_PTR*)swapChain; + swapChainVTable = (DWORD_PTR*)swapChainVTable[0]; + contextVTable = (DWORD_PTR*)context; + contextVTable = (DWORD_PTR*)contextVTable[0]; + deviceVTable = (DWORD_PTR*)device; + deviceVTable = (DWORD_PTR*)deviceVTable[0]; + + // Get virtual functions addresses. + g_fnIDXGISwapChainPresent = (IDXGISwapChainPresent)(DWORD_PTR)swapChainVTable[(int)DXGISwapChainVTbl::Present]; + g_fnIDXGIResizeBuffers = (IDXGIResizeBuffers)(DWORD_PTR)swapChainVTable[(int)DXGISwapChainVTbl::ResizeBuffers]; + + // Safe release all relevant ptrs. + swapChain->Release(); + swapChain = nullptr; + + device->Release(); + device = nullptr; + + context->Release(); + context = nullptr; + + // Destroy Window used for getting the virtual functions addresses and unregister its class. + DestroyWindow(swapChainDescription.OutputWindow); + UnregisterClass(ws.lpszClassName, ws.hInstance); } bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv) @@ -431,57 +209,158 @@ bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, return true; } -//################################################################################# -// MANAGEMENT -//################################################################################# +void DrawMenu() +{ + if (!GameGlobals::IsInitialized || !GameGlobals::InputSystem) // Check if GameGlobals initialized and if InputSystem is valid. + return; + + // Handle new ImGui frame. + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + // Handle game input if one of the menus is open. + if (g_bShowConsole || g_bShowBrowser) + { + GameGlobals::InputSystem->EnableInput(false); + } + else + { + GameGlobals::InputSystem->EnableInput(true); + } + + if (g_bShowConsole) + { + DrawConsole(); + } + + if (g_bShowBrowser) + { + DrawBrowser(); + } + + // Handle end of frame and prepare rendering. + ImGui::EndFrame(); + ImGui::Render(); + + // Set new render target. + // This breaks 4:3 in main menu and load screen if not applying the games DepthStencilView. Applying the games DepthStencilView makes ImGui not render tho. + g_pDeviceContext->OMSetRenderTargets(1, &g_pMainRenderTargetView, NULL); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); // Tell ImGui to render all the draw data. +} + +IDXGIResizeBuffers originalResizeBuffers = nullptr; +HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) +{ + spdlog::debug("Resizing IDXGIResizeBuffers..\n"); + // Re-create render target if our window got resized. + if (g_pMainRenderTargetView) + { + g_pDeviceContext->OMSetRenderTargets(0, 0, 0); // Set render target to null. + + // Safe release the render target. + g_pMainRenderTargetView->Release(); + g_pMainRenderTargetView = nullptr; + } + + ImGui_ImplDX11_InvalidateDeviceObjects(); // Invalidate all ImGui DirectX objects. + + HRESULT hr = originalResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); // Let DirectX resize all the buffers. + + if (!g_pDevice) // Valid device? + return hr; + + ID3D11Texture2D* pBuffer; + pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBuffer); // Grab the swapchains buffer. + + if (!pBuffer) // Valid buffer? + return hr; + + g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pMainRenderTargetView); // Create render target again with the new swapchain buffer. + + // Safe release the buffer. + pBuffer->Release(); + pBuffer = nullptr; + + if (!g_pMainRenderTargetView) // Valid render target? + return hr; + + g_pDeviceContext->OMSetRenderTargets(1, &g_pMainRenderTargetView, NULL); // Set new render target. + + // Set up the viewport. + D3D11_VIEWPORT vp; + vp.Width = nWidth; + vp.Height = nHeight; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + g_pDeviceContext->RSSetViewports(1, &vp); + + ImGui_ImplDX11_CreateDeviceObjects(); // Create new DirectX objects for ImGui. + + return hr; +} + +IDXGISwapChainPresent originalPresent = nullptr; +HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) +{ + static bool InitializedPresent = false; + if (!InitializedPresent) + { + spdlog::debug("Initializing IDXGISwapChainPresent hook..\n"); + if (SUCCEEDED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pDevice))) // Get device via swap chain. + { + g_pDevice->GetImmediateContext(&g_pDeviceContext); // Get device context via device. + DXGI_SWAP_CHAIN_DESC sd; + pSwapChain->GetDesc(&sd); // Get the swap chain description. + ID3D11Texture2D* pBuffer; + pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBuffer); // Get swap chain buffer. + + if (!pBuffer) // Valid buffer? + return originalPresent(pSwapChain, nSyncInterval, nFlags); + + g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pMainRenderTargetView); // Create new render target. + + // Safe release the buffer. + pBuffer->Release(); + pBuffer = nullptr; + + originalWndProc = (WNDPROC)SetWindowLongPtr(sd.OutputWindow, GWLP_WNDPROC, (LONG_PTR)WindowProc); // Hook current output window. + + // Initialize ImGui. + ImGui::CreateContext(); + ImGui_ImplWin32_Init(sd.OutputWindow); + ImGui_ImplDX11_Init(g_pDevice, g_pDeviceContext); + + InitializedPresent = true; + } + else + { + return originalPresent(pSwapChain, nSyncInterval, nFlags); + } + } + + DrawMenu(); + return originalPresent(pSwapChain, nSyncInterval, nFlags); +} void InstallDXHooks() { - /////////////////////////////////////////////////////////////////////////////// - // Hook SwapChain + spdlog::debug("Initializing IDXGISwapChainPresent hook..\n"); MH_CreateHook(g_fnIDXGISwapChainPresent, &Present, reinterpret_cast(&originalPresent)); - MH_CreateHook(g_oResizeBuffers, &GetResizeBuffers, reinterpret_cast(&originalResizeBuffers)); + MH_CreateHook(g_fnIDXGIResizeBuffers, &GetResizeBuffers, reinterpret_cast(&originalResizeBuffers)); - /////////////////////////////////////////////////////////////////////////////// - // Enable hooks MH_EnableHook(g_fnIDXGISwapChainPresent); - MH_EnableHook(g_oResizeBuffers); - - if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? - { - IPostMessageA PostMessageA = user32dll.GetExportedFunction("PostMessageA").RCast(); - IPostMessageW PostMessageW = user32dll.GetExportedFunction("PostMessageW").RCast(); - - /////////////////////////////////////////////////////////////////////////////// - // Hook PostMessage - MH_CreateHook(PostMessageA, &HPostMessageA, reinterpret_cast(&g_oPostMessageA)); - MH_CreateHook(PostMessageW, &HPostMessageW, reinterpret_cast(&g_oPostMessageW)); - - MH_EnableHook(PostMessageA); - MH_EnableHook(PostMessageW); - } + MH_EnableHook(g_fnIDXGIResizeBuffers); } void RemoveDXHooks() { - /////////////////////////////////////////////////////////////////////////////// - // Unhook PostMessage - if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? - { - IPostMessageA PostMessageA = user32dll.GetExportedFunction("PostMessageA").RCast(); - IPostMessageW PostMessageW = user32dll.GetExportedFunction("PostMessageW").RCast(); - - MH_RemoveHook(PostMessageA); - MH_RemoveHook(PostMessageW); - } - - /////////////////////////////////////////////////////////////////////////////// - // Unhook SwapChain + spdlog::debug("Initializing IDXGISwapChainPresent hook..\n"); MH_RemoveHook(g_fnIDXGISwapChainPresent); - MH_RemoveHook(g_oResizeBuffers); + MH_RemoveHook(g_fnIDXGIResizeBuffers); - /////////////////////////////////////////////////////////////////////////////// - // Shutdown ImGui ImGui_ImplWin32_Shutdown(); ImGui_ImplDX11_Shutdown(); } @@ -489,10 +368,9 @@ void RemoveDXHooks() void PrintDXAddress() { std::cout << "+--------------------------------------------------------+" << std::endl; - std::cout << "| ID3D11DeviceContext : " << std::hex << std::uppercase << g_pDeviceContext << std::setw(13) << " |" << std::endl; - std::cout << "| ID3D11Device : " << std::hex << std::uppercase << g_pDevice << std::setw(13) << " |" << std::endl; - std::cout << "| ID3D11RenderTargetView : " << std::hex << std::uppercase << g_pRenderTargetView << std::setw(13) << " |" << std::endl; - std::cout << "| IDXGISwapChain : " << std::hex << std::uppercase << g_pSwapChain << std::setw(13) << " |" << std::endl; + std::cout << "| ID3D11DeviceContext : " << std::hex << std::uppercase << g_pDeviceContext << std::setw(13) << " |" << std::endl; + std::cout << "| ID3D11Device : " << std::hex << std::uppercase << g_pDevice << std::setw(13) << " |" << std::endl; + std::cout << "| ID3D11RenderTargetView : " << std::hex << std::uppercase << g_pMainRenderTargetView << std::setw(13) << " |" << std::endl; std::cout << "| IDXGISwapChainPresent : " << std::hex << std::uppercase << g_fnIDXGISwapChainPresent << std::setw(13) << " |" << std::endl; std::cout << "+--------------------------------------------------------+" << std::endl; } @@ -503,13 +381,14 @@ void PrintDXAddress() DWORD __stdcall DXSwapChainWorker(LPVOID) { - GetPresent(); + InitRenderer(); InstallDXHooks(); return true; } void SetupDXSwapChain() { + spdlog::debug("Setting up DirectX thread..\n"); // Create a worker thread for the console overlay DWORD __stdcall DXSwapChainWorker(LPVOID); HANDLE hThread = CreateThread(NULL, 0, DXSwapChainWorker, NULL, 0, &g_dThreadId); diff --git a/r5dev/src/opcptc.cpp b/r5dev/src/opcptc.cpp index a6fbbbf4..3fd0b8fa 100644 --- a/r5dev/src/opcptc.cpp +++ b/r5dev/src/opcptc.cpp @@ -7,6 +7,7 @@ void InstallOpcodes() /* .TEXT */ { + spdlog::debug("Patching the game executeable..\n"); //------------------------------------------------------------------------- // JNZ --> JMP | Prevent OriginSDK from initializing //Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); @@ -32,4 +33,26 @@ void InstallOpcodes() /* .TEXT */ //------------------------------------------------------------------------- // CALL --> NOP | Prevent random netchan encryption key from being overriden by default key NetChan_EncKey_DefaultAssign.Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // INLINE CALL --> VTABLE CALL | Call LockCursor VTable class function instead of doing it inlined. + //------------------------------------------------------------------------- + // .text:0000000140548E2C 80 3D 5C 2E 1D 01 00 cmp cs:byte_14171BC8F, 0 + // .text:0000000140548E33 48 8B 0D 16 25 EC 0C mov rcx, cs:g_InputStackSystem + // .text:0000000140548E3A 48 8B 97 18 01 00 00 mov rdx, [rdi+118h] + // .text:0000000140548E41 C6 05 91 7B EC 0C 01 mov cs:byte_14D4109D9, 1 + // .text:0000000140548E48 48 8B 01 mov rax, [rcx] + // .text:0000000140548E4B 74 10 jz short loc_140548E5D + // .text:0000000140548E4D 4C 8B 05 8C 7B EC 0C mov r8, cs:qword_14D4109E0 + // .text:0000000140548E54 48 83 C4 30 add rsp, 30h + // .text:0000000140548E58 5F pop rdi + // .text:0000000140548E59 48 FF 60 60 jmp qword ptr[rax+60h] + //------------------------------------------------------------------------- + // TURNS INTO: + //------------------------------------------------------------------------- + // .text:0000000140548E2C 48 8B 07 mov rax, [rdi] + // .text:0000000140548E2F 48 89 F9 mov rcx, rdi + // .text:0000000140548E32 FF 90 90 02 00 00 call qword ptr[rax+290h] + // .text:0000000140548E38 EB 2F jmp short loc_140548E69 + //------------------------------------------------------------------------- + MemoryAddress(0x140548E2C).Patch({ 0x48, 0x8B, 0x07, 0x48, 0x89, 0xF9, 0xFF, 0x90, 0x90, 0x02, 0x00, 0x00, 0xEB, 0x2F }); }