diff --git a/data/ui_scripts/patches/__init__.lua b/data/ui_scripts/patches/__init__.lua index 693a249b..291f5367 100644 --- a/data/ui_scripts/patches/__init__.lua +++ b/data/ui_scripts/patches/__init__.lua @@ -2,6 +2,10 @@ if (game:issingleplayer()) then return end +if (Engine.InFrontend()) then + require("shaderdialog") +end + -- defined in mp_hud/hudutils.lua function GetGameModeName() return Engine.Localize(Engine.TableLookup(GameTypesTable.File, diff --git a/data/ui_scripts/patches/shaderdialog.lua b/data/ui_scripts/patches/shaderdialog.lua new file mode 100644 index 00000000..9d658d27 --- /dev/null +++ b/data/ui_scripts/patches/shaderdialog.lua @@ -0,0 +1,28 @@ +LUI.MenuBuilder.registerPopupType("ShaderCacheDialog_original", LUI.ShaderCacheDialog.new) + +game:addlocalizedstring("PLATFORM_SHADER_PRECACHE_ASK", "Would you like to populate the shader cache? It may cause crashes with certain GPUs (e.g. RTX cards) but will improve performance if successful.") +game:addlocalizedstring("MENU_NO_DONT_ASK", "No, don't ask me again") + +local function dialog(...) + if (game:sharedget("has_accepted_shader_caching") == "1") then + return LUI.ShaderCacheDialog.new(...) + end + + return LUI.MenuBuilder.BuildRegisteredType("generic_yesno_popup", { + popup_title = Engine.Localize("@MENU_WARNING"), + message_text = Engine.Localize("@PLATFORM_SHADER_PRECACHE_ASK"), + yes_action = function() + game:sharedset("has_accepted_shader_caching", "1") + LUI.FlowManager.RequestAddMenu(nil, "ShaderCacheDialog_original") + end, + yes_text = Engine.Localize("@MENU_YES"), + no_text = Engine.Localize("@MENU_NO_DONT_ASK"), + no_action = function() + Engine.SetDvarInt("r_preloadShadersFrontendAllow", 0) + end, + default_focus_index = 2, + cancel_will_close = false + }) +end + +LUI.MenuBuilder.m_types_build["ShaderCacheDialog"] = dialog diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 343254e5..3d9b7a03 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -2,6 +2,7 @@ #include "loader/component_loader.hpp" #include "dvars.hpp" +#include "fastfiles.hpp" #include "version.h" #include "command.hpp" #include "console.hpp" @@ -155,6 +156,12 @@ namespace patches game::AimAssist_AddToTargetList(aaGlob, screenTarget); } + void missing_content_error_stub(int, const char*) + { + game::Com_Error(game::ERR_DROP, utils::string::va("MISSING FILE\n%s.ff", + fastfiles::get_current_fastfile().data())); + } + utils::hook::detour init_network_dvars_hook; void init_network_dvars_stub(game::dvar_t* dvar) { @@ -169,6 +176,33 @@ namespace patches { return 1; } + + utils::hook::detour cl_gamepad_scrolling_buttons_hook; + void cl_gamepad_scrolling_buttons_stub(int local_client_num, int a2) + { + if (local_client_num <= 3) + { + cl_gamepad_scrolling_buttons_hook.invoke(local_client_num, a2); + } + } + + int out_of_memory_text_stub(char* dest, int size, const char* fmt, ...) + { + fmt = "%s (%d)\n\n" + "Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers.\n\n" + "If this still occurs, try using the '-memoryfix' parameter to generate the 'players2' folder."; + + char buffer[2048]; + + va_list ap; + va_start(ap, fmt); + + vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); + + va_end(ap); + + return utils::hook::invoke(SELECT_VALUE(0x429200_b, 0x5AF0F0_b), dest, size, "%s", buffer); + } } class component final : public component_interface @@ -201,9 +235,21 @@ namespace patches utils::hook::nop(SELECT_VALUE(0x1AC0CE_b, 0x135EFB_b), 2); utils::hook::nop(SELECT_VALUE(0x1A9DDC_b, 0x13388F_b), 6); + // Show missing fastfiles + utils::hook::call(SELECT_VALUE(0x0_b, 0x39A78E_b), missing_content_error_stub); + // Allow executing custom cfg files with the "exec" command utils::hook::call(SELECT_VALUE(0x376EB5_b, 0x156D41_b), db_read_raw_file_stub); + // Remove useless information from errors + add additional help to common errors + utils::hook::set(SELECT_VALUE(0x7E3DF0_b, 0x937B80_b), + "Create2DTexture( %s, %i, %i, %i, %i ) failed\n\n" + "Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers."); + utils::hook::set(SELECT_VALUE(0x800EA8_b, 0x954FF0_b), + "IDXGISwapChain::Present failed: %s\n\n" + "Disable shader caching, lower graphic settings, free up RAM, or update your GPU drivers."); + utils::hook::call(SELECT_VALUE(0x457BC9_b, 0x1D8E09_b), out_of_memory_text_stub); // "Out of memory. You are probably low on disk space." + if (!game::environment::is_sp()) { patch_mp(); @@ -302,6 +348,9 @@ namespace patches // Dont free server/client memory on asset loading (fixes crashing on map rotation) utils::hook::nop(0x132474_b, 5); + + // Fix gamepad related crash + cl_gamepad_scrolling_buttons_hook.create(0x133210_b, cl_gamepad_scrolling_buttons_stub); } }; } diff --git a/src/client/component/security.cpp b/src/client/component/security.cpp index 12285f85..f1300cc6 100644 --- a/src/client/component/security.cpp +++ b/src/client/component/security.cpp @@ -16,6 +16,29 @@ namespace security utils::hook::invoke(0x61A9D0_b, localclient, index1, index2); } } + + void remap_cached_entities(game::mp::cachedSnapshot_t& snapshot) + { + static bool printed = false; + if (snapshot.num_clients > 1200 && !printed) + { + printed = true; + printf("Too many entities (%d)... remapping!\n", snapshot.num_clients); + } + + snapshot.num_clients = std::min(snapshot.num_clients, 1200); + } + + void remap_cached_entities_stub(utils::hook::assembler& a) + { + a.pushad64(); + + a.mov(rcx, rbx); + a.call_aligned(remap_cached_entities); + + a.popad64(); + a.jmp(0x55E4D8_b); + } } class component final : public component_interface @@ -30,6 +53,9 @@ namespace security // Patch vulnerability in PlayerCards_SetCachedPlayerData utils::hook::call(0xF4632_b, set_cached_playerdata_stub); + + // Patch entity overflow + utils::hook::jump(0x55E4C7_b, assemble(remap_cached_entities_stub), true); } }; } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 7e70294a..cbe7db08 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -7,6 +7,19 @@ namespace game /*************************************************************** * Functions **************************************************************/ + + namespace mp + { + WEAK symbol Cbuf_AddText{0x0, 0x1CF480}; + WEAK symbol Cmd_TokenizeStringWithLimit{0x0, 0x157A40}; + } + + namespace sp + { + WEAK symbol Cbuf_AddText{0x3764A0, 0x0}; + WEAK symbol Cmd_TokenizeString{0x377790, 0x0}; + WEAK symbol Cmd_EndTokenizeString{0x376C90, 0x0}; + } WEAK symbol AddRefToValue{0x3C1F50, 0x5090E0}; WEAK symbol RemoveRefToValue{0x3C3A60, 0x50ABF0}; @@ -15,16 +28,6 @@ namespace game WEAK symbol AllocThread{0x3C22B0, 0x509440}; WEAK symbol AllocVariable{0x3C2310, 0x5094A0}; - namespace sp - { - WEAK symbol Cbuf_AddText{0x3764A0, 0x0}; - } - - namespace mp - { - WEAK symbol Cbuf_AddText{0x0, 0x1CF480}; - } - WEAK symbol Cbuf_ExecuteBufferInternal{0x3765B0, 0x155BC0}; WEAK symbol Conbuf_AppendText{0x0, 0x0}; @@ -33,17 +36,6 @@ namespace game WEAK symbol Cmd_AddCommandInternal{0x376A40, 0x156880}; WEAK symbol Cmd_RemoveCommand{0x377670, 0x157690}; - namespace sp - { - WEAK symbol Cmd_TokenizeString{0x377790, 0x0}; - WEAK symbol Cmd_EndTokenizeString{0x376C90, 0x0}; - } - - namespace mp - { - WEAK symbol Cmd_TokenizeStringWithLimit{0x0, 0x157A40}; - } - WEAK symbol AimAssist_AddToTargetList{0x0, 0xE66C0}; WEAK symbolsize() != 0x1B97788 && data->size() != 0x1346D88) { - return false; + throw std::runtime_error("File size mismatch, bad game files"); } auto* dos_header = reinterpret_cast(&data->at(0)); @@ -56,22 +56,26 @@ bool apply_aslr_patch(std::string* data) { optional_header->DllCharacteristics &= ~(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE); } - - return true; } void get_aslr_patched_binary(std::string* binary, std::string* data) { - std::string patched_binary = "h1-mod\\" + *binary; + const auto patched_binary = "h1-mod\\"s + *binary; - if (!apply_aslr_patch(data) || - (!utils::io::file_exists(patched_binary) && - !utils::io::write_file(patched_binary, *data, false))) + try { - throw std::runtime_error(utils::string::va( - "Could not create aslr patched binary!\n(%s)", - binary->data() - )); + apply_aslr_patch(data); + if (!utils::io::file_exists(patched_binary) && !utils::io::write_file(patched_binary, *data, false)) + { + throw std::runtime_error("Could not write file"); + } + } + catch (const std::exception& e) + { + throw std::runtime_error( + utils::string::va("Could not create aslr patched binary for %s! %s", + binary->data(), e.what()) + ); } *binary = patched_binary;