diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index ebe1aab5..1a6b415a 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -42,7 +42,7 @@ namespace auth std::string get_protected_data() { - std::string input = "X-Labs-S1x-Auth"; + std::string input = "X-Labs-H1Mod-Auth"; DATA_BLOB data_in{}, data_out{}; data_in.pbData = reinterpret_cast(input.data()); diff --git a/src/client/component/binding.cpp b/src/client/component/binding.cpp new file mode 100644 index 00000000..4013ca12 --- /dev/null +++ b/src/client/component/binding.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/client/component/exception.cpp b/src/client/component/exception.cpp new file mode 100644 index 00000000..4013ca12 --- /dev/null +++ b/src/client/component/exception.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 5543db08..93999d82 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -243,10 +243,10 @@ namespace network utils::hook::call(0x140480E62, &net_compare_address); // H1MP64(1.4) // increase cl_maxpackets - dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED); + dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED, true); // increase snaps - dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE); + dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE, true); // ignore impure client utils::hook::jump(0x140481B58, reinterpret_cast(0x140481BEE)); // H1MP64(1.4) @@ -256,10 +256,10 @@ namespace network utils::hook::set(0x14051345A, 0); // H1MP64(1.4) // don't read checksum - utils::hook::jump(0x140513389, 0x14051339F); + utils::hook::jump(0x140513389, 0x14051339F); // H1MP64(1.4) // don't try to reconnect client - utils::hook::call(0x140480DFF, reconnect_migratated_client); + utils::hook::call(0x140480DFF, reconnect_migratated_client); // H1MP64(1.4) utils::hook::nop(0x140480DDB, 4); // H1MP64(1.4) this crashes when reconnecting for some reason // allow server owner to modify net_port before the socket bind diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp new file mode 100644 index 00000000..2255b585 --- /dev/null +++ b/src/client/component/patches.cpp @@ -0,0 +1,78 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include +#include +#include + +namespace patches +{ + namespace + { + utils::hook::detour gscr_set_save_dvar_hook; + utils::hook::detour dvar_register_float_hook; + + void gscr_set_save_dvar_stub() + { + const auto string = utils::string::to_lower(utils::hook::invoke(SELECT_VALUE(0x140375210, 0x140443150), 0)); + if (string == "cg_fov" || string == "cg_fovscale") + { + return; + } + + gscr_set_save_dvar_hook.invoke(); + } + + game::dvar_t* cg_fov = nullptr; + game::dvar_t* cg_fovScale = nullptr; + + game::dvar_t* dvar_register_float_stub(int hash, const char* dvarName, float value, float min, float max, unsigned int flags) + { + static const auto cg_fov_hash = game::generateHashValue("cg_fov"); + static const auto cg_fov_scale_hash = game::generateHashValue("cg_fovscale"); + + if (hash == cg_fov_hash) + { + return cg_fov; + } + + if (hash == cg_fov_scale_hash) + { + return cg_fovScale; + } + + return dvar_register_float_hook.invoke(hash, dvarName, value, min, max, flags); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + // Unlock fps in main menu + utils::hook::set(SELECT_VALUE(0x14018D47B, 0x14025B86B), 0xEB); // H1(1.4) + + // Fix mouse lag + utils::hook::nop(SELECT_VALUE(0x1403E3C05, 0x1404DB1AF), 6); + scheduler::loop([]() + { + SetThreadExecutionState(ES_DISPLAY_REQUIRED); + }, scheduler::pipeline::main); + + // Prevent game from overriding cg_fov and cg_fovscale values + gscr_set_save_dvar_hook.create(SELECT_VALUE(0x1402AE020, 0x14036B600), &gscr_set_save_dvar_stub); + + // Make cg_fov and cg_fovscale saved dvars + cg_fov = dvars::register_float("cg_fov", 65.f, 40.f, 200.f, game::DvarFlags::DVAR_FLAG_SAVED, true); + cg_fovScale = dvars::register_float("cg_fovScale", 1.f, 0.1f, 2.f, game::DvarFlags::DVAR_FLAG_SAVED, true); + + dvar_register_float_hook.create(game::Dvar_RegisterFloat.get(), dvar_register_float_stub); + } + }; +} + +REGISTER_COMPONENT(patches::component) \ No newline at end of file diff --git a/src/client/component/slowmotion.cpp b/src/client/component/slowmotion.cpp new file mode 100644 index 00000000..232ae576 --- /dev/null +++ b/src/client/component/slowmotion.cpp @@ -0,0 +1,53 @@ +#include +#include "loader/component_loader.hpp" +#include "game/game.hpp" + +#include +#include + +namespace slowmotion +{ + namespace + { + void scr_cmd_set_slow_motion() + { + if (game::Scr_GetNumParam() < 1) + { + return; + } + + int duration = 1000; + float end = 1.0f; + const float start = game::Scr_GetFloat(0); + + if (game::Scr_GetNumParam() >= 2) + { + end = game::Scr_GetFloat(1u); + } + + if (game::Scr_GetNumParam() >= 3) + { + duration = static_cast(game::Scr_GetFloat(2u) * 1000.0f); + } + + game::SV_SetConfigstring(10, utils::string::va("%i %i %g %g", *game::mp::gameTime, duration, start, end)); + game::Com_SetSlowMotion(start, end, duration); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + if (!game::environment::is_dedi()) + { + return; + } + + utils::hook::jump(0x140365480, scr_cmd_set_slow_motion); // H1(1.4) + } + }; +} + +REGISTER_COMPONENT(slowmotion::component) \ No newline at end of file diff --git a/src/client/component/system_check.cpp b/src/client/component/system_check.cpp new file mode 100644 index 00000000..0d32e798 --- /dev/null +++ b/src/client/component/system_check.cpp @@ -0,0 +1,100 @@ +#include +#include "loader/component_loader.hpp" +#include "system_check.hpp" + +#include "game/game.hpp" + +#include +#include + +namespace system_check +{ + namespace + { + std::string read_zone(const std::string& name) + { + std::string data{}; + if (utils::io::read_file(name, &data)) + { + return data; + } + + if (utils::io::read_file("zone/" + name, &data)) + { + return data; + } + + return {}; + } + + std::string hash_zone(const std::string& name) + { + const auto data = read_zone(name); + return utils::cryptography::sha256::compute(data, true); + } + + bool verify_hashes(const std::unordered_map& zone_hashes) + { + for (const auto& zone_hash : zone_hashes) + { + const auto hash = hash_zone(zone_hash.first); + if (hash != zone_hash.second) + { + return false; + } + } + + return true; + } + + bool is_system_valid() + { + static std::unordered_map mp_zone_hashes = + { + {"patch_common_mp.ff", "3F44B0CFB0B8E0FBD9687C2942204AB7F11E66E6E15C73B8B4A5EB5920115A31"}, + }; + + static std::unordered_map sp_zone_hashes = + { + // Steam doesn't necessarily deliver this file :( + {"patch_common.ff", "BB0617DD94AF2F511571E7184BBEDE76E64D97E5D0DAFDB457F00717F035EBF0"}, + }; + + + return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes)); + } + + void verify_binary_version() + { + const auto value = *reinterpret_cast(0x140001337); + if (value != 0xFFB8006D && value != 0xFFB80080) + { + throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version(1.4)"); + } + } + } + + bool is_valid() + { + static auto valid = is_system_valid(); + return valid; + } + + class component final : public component_interface + { + public: + void post_load() override + { + verify_binary_version(); + + if (!is_valid()) + { + MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n" + "Please get the latest officially supported Call of Duty: Modern Warfare Remastered 1.4 files, or you will get random crashes and issues.", + "Invalid game files!", MB_ICONINFORMATION); + } + } + }; +} + +REGISTER_COMPONENT(system_check::component) \ No newline at end of file diff --git a/src/client/component/system_check.hpp b/src/client/component/system_check.hpp new file mode 100644 index 00000000..c4b93ec9 --- /dev/null +++ b/src/client/component/system_check.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace system_check +{ + bool is_valid(); +} \ No newline at end of file diff --git a/src/client/component/thread_names.cpp b/src/client/component/thread_names.cpp new file mode 100644 index 00000000..95a3d795 --- /dev/null +++ b/src/client/component/thread_names.cpp @@ -0,0 +1,60 @@ +#include +#include "loader/component_loader.hpp" + +#include "scheduler.hpp" + +#include "game/game.hpp" + +#include + +namespace thread_names +{ + namespace + { + void set_thread_names() + { + static std::unordered_map thread_names = + { + {game::THREAD_CONTEXT_MAIN, "Main"}, + {game::THREAD_CONTEXT_BACKEND, "Backend"}, // Renderer + {game::THREAD_CONTEXT_WORKER0, "Worker0"}, + {game::THREAD_CONTEXT_WORKER1, "Worker1"}, + {game::THREAD_CONTEXT_WORKER2, "Worker2"}, + {game::THREAD_CONTEXT_WORKER3, "Worker3"}, + {game::THREAD_CONTEXT_WORKER4, "Worker4"}, + {game::THREAD_CONTEXT_WORKER5, "Worker5"}, + {game::THREAD_CONTEXT_WORKER6, "Worker6"}, + {game::THREAD_CONTEXT_WORKER7, "Worker7"}, + {game::THREAD_CONTEXT_SERVER, "Server"}, + {game::THREAD_CONTEXT_CINEMATIC, "Cinematic"}, + {game::THREAD_CONTEXT_DATABASE, "Database"}, + {game::THREAD_CONTEXT_STREAM, "Stream"}, + {game::THREAD_CONTEXT_SNDSTREAMPACKETCALLBACK, "Snd stream packet callback"}, + {game::THREAD_CONTEXT_STATS_WRITE, "Stats write"}, + }; + + for (const auto& thread_name : thread_names) + { + const auto id = game::threadIds[thread_name.first]; + if (id) + { + utils::thread::set_name(id, thread_name.second); + } + } + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + set_thread_names(); + scheduler::once(set_thread_names, scheduler::pipeline::main); + scheduler::once(set_thread_names, scheduler::pipeline::renderer); + scheduler::once(set_thread_names, scheduler::pipeline::server); + } + }; +} + +REGISTER_COMPONENT(thread_names::component) \ No newline at end of file diff --git a/src/client/component/videos.cpp b/src/client/component/videos.cpp new file mode 100644 index 00000000..74e37e1d --- /dev/null +++ b/src/client/component/videos.cpp @@ -0,0 +1,55 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include + +namespace videos +{ + namespace + { + utils::hook::detour playvid_hook; + std::unordered_map video_replaces; + + void playvid(const char* name, const int a2, const int a3) + { + const auto vid = video_replaces.find(name); + if (vid != video_replaces.end()) + { + char path[256]; + game::Sys_BuildAbsPath(path, sizeof(path), game::SF_VIDEO, vid->second.data(), ".bik"); + + if (game::Sys_FileExists(path)) + { + name = vid->second.data(); + } + } + + return playvid_hook.invoke(name, a2, a3); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + playvid_hook.create(SELECT_VALUE(0x1404A9D00, 0x1405B0AF0), &playvid); // H1(1.4) + + if (game::environment::is_mp()) + { + video_replaces["menus_bg_comp2"] = "menus_bg_h1mod"; + video_replaces["mp_menus_bg_options"] = "menus_bg_h1mod_blur"; + } + else if (game::environment::is_sp()) + { + video_replaces["sp_menus_bg_main_menu"] = "menus_bg_h1mod_sp"; + video_replaces["sp_menus_bg_campaign"] = "menus_bg_h1mod_sp"; + video_replaces["sp_menus_bg_options"] = "menus_bg_h1mod_sp"; + } + } + }; +} + +REGISTER_COMPONENT(videos::component) \ No newline at end of file diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index 7109b80b..83f36c8a 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -21,6 +21,8 @@ namespace dvars game::dvar_t* r_fullbright; game::dvar_t* r_chams; + game::dvar_t* cg_legacyCrashHandling; + std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain) { if (domain.vector.min == -FLT_MAX) @@ -188,7 +190,7 @@ namespace dvars "cg_drawVersionX", "cg_drawVersionY", "cg_drawViewpos", - "cg_drawgun", + "cg_drawgun", "cg_fov", "cg_fov_default", "cg_fov_default_thirdperson", @@ -196,6 +198,7 @@ namespace dvars "cg_fovExtraCam", "cg_fovMin", "cg_fovScale", + "cg_legacyCrashHandling", "cl_maxpackets", "cl_maxPing", "com_introPlayed", @@ -225,7 +228,7 @@ namespace dvars "g_log", "g_logSync", "g_logTimeStampInSeconds", - "timescale", // Scale time of each frame ---> "5401" + "timescale", // Scale time of each frame ---> "5401" "g_motd", "g_scriptMainMenu", "g_smoothClients", diff --git a/src/client/game/dvars.hpp b/src/client/game/dvars.hpp index 251c3546..9eedb7a7 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -21,6 +21,8 @@ namespace dvars extern game::dvar_t* r_fullbright; extern game::dvar_t* r_chams; + extern game::dvar_t* cg_legacyCrashHandling; + extern std::vector dvar_list; std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain); diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index e8f91b5e..4634bdb4 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -17,10 +17,14 @@ namespace game WEAK symbol Com_GetCurrentCoDPlayMode{ 0, 0x1405039A0 }; // H1(1.4) + WEAK symbol Com_SetSlowMotion{ 0, 0x1400DB790 }; // H1(1.4) + WEAK symbol BG_GetWeaponNameComplete{ 0, 0x140165580 }; WEAK symbol Com_Quit_f{ 0x140352BE0, 0x1400DA830 }; // H1(1.4) + WEAK symbol Key_KeynumToString{ 0x140187CC0, 0x14024FE10 }; // H1(1.4) + WEAK symbol Cmd_TokenizeString{ 0x140344110, 0x1404046F0 }; // H1(1.4) WEAK symbol Dvar_SetCommand{ 0x1403C72B0, 0x1404FD0A0 }; // H1(1.4) @@ -46,7 +50,9 @@ namespace game WEAK symbol Dvar_RegisterVec4{ 0x1403C5220, 0x1404FAF40 }; // H1(1.4) WEAK symbol Dvar_RegisterEnum{ 0x1403C4AC0, 0x1404C0EC0 }; // H1(1.4) + WEAK symbol Scr_GetFloat{ 0x140374D20, 0x140442D10 }; // H1(1.4) + WEAK symbol Scr_GetNumParam{ 0x140374F30, 0x140442E70 }; // H1(1.4) WEAK symbol FS_ReadFile{ 0x1403B9020, 0x1404EE720 }; // H1(1.4) @@ -171,6 +177,8 @@ namespace game WEAK symbol SL_ConvertToString{ 0x14036D420, 0x1405BFBB0 }; WEAK symbol SL_GetString{ 0x14036D9A0, 0x1405C0170 }; + WEAK symbol SV_SetConfigstring{ 0, 0x140486720 }; // H1(1.4) + WEAK symbol SV_Loaded{ 0x140442F60, 0x1404864A0 }; // H1(1.4) WEAK symbol SV_MapExists{ 0, 0x14047ED60 }; // H1(1.4) @@ -192,9 +200,16 @@ namespace game WEAK symbol SV_Cmd_ArgvBuffer{ 0x1402EEFD0, 0x1403B05C0 }; - // Variables - WEAK symbol sv_cmd_args{ 0, 0x14946BA20 }; // mwr maybe + WEAK symbol + Sys_BuildAbsPath{ 0x1403CFF90, 0x140507010 }; // H1(1.4) + WEAK symbol Sys_FileExists{ 0x1403E0CE0, 0x1405115E0 }; // H1(1.4) + + // Variables + WEAK symbol sv_cmd_args{ 0, 0x14946BA20 }; // H1(1.4) + + + WEAK symbol command_whitelist{ 0x141079A60, 0x14120C6D0 }; // H1(1.4) WEAK symbol g_assetNames{ 0, 0x140BEF280 }; WEAK symbol g_poolSize{ 0, 0x140FEADF0 }; // H1(1.4) @@ -214,14 +229,17 @@ namespace game WEAK symbol scr_function_stack{ 0,0x14BAA93C0 }; WEAK symbol DB_XAssetPool{ 0x140DE8C80, 0x140FEB5D0 }; // H1(1.4) + WEAK symbol threadIds{0x14B19B880, 0x149810E00 }; // H1(1.4) + namespace mp { WEAK symbol g_entities{ 0, 0x14621E530 }; // H1(1.4) WEAK symbol svs_clients{ 0, 0x14B204A10 }; // H1(1.4) + WEAK symbol gameTime{ 0, 0x14621BDBC }; // H1(1.4) } namespace sp { - WEAK symbol g_entities{ 0x14550DD90 , 0 }; + WEAK symbol g_entities{ 0x14550DD90 , 0 }; // H1(1.4) } } diff --git a/src/client/main.cpp b/src/client/main.cpp index f4e74580..9be28653 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -110,16 +110,6 @@ void remove_crash_file() utils::io::remove_file("__h1Exe"); } -void verify_mwr_version() -{ - const auto value = *reinterpret_cast(0x140001337); - //sp && mp - if (value != 0xFFB8006D && value != 0xFFB80080) - { - throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version"s); - } -} - void enable_dpi_awareness() { const utils::nt::library user32{"user32.dll"}; @@ -203,8 +193,6 @@ int main() if (!component_loader::post_load()) return 0; - verify_mwr_version(); - premature_shutdown = false; } catch (std::exception& e)