From f4f5960dcf5588debf6f45060a3bff3aab7a351b Mon Sep 17 00:00:00 2001 From: FutureRave Date: Thu, 12 May 2022 19:45:29 +0100 Subject: [PATCH] Add bullet changes from iw4x --- .gitmodules | 3 ++ deps/minhook | 1 + deps/premake/minhook.lua | 31 ++++++++++++++++ src/game/game.cpp | 63 +++++++++++++++++++++++++++---- src/game/game.hpp | 10 ++--- src/game/structs.hpp | 17 +++++---- src/module/bullet.cpp | 56 ++++++++++++++++++++++++++++ src/module/bullet.hpp | 17 +++++++++ src/utils/hook.cpp | 80 ++++++++++++++++++++++++++++++++++++++++ src/utils/hook.hpp | 58 +++++++++++++++++++++++++++++ 10 files changed, 316 insertions(+), 20 deletions(-) create mode 160000 deps/minhook create mode 100644 deps/premake/minhook.lua create mode 100644 src/module/bullet.cpp create mode 100644 src/module/bullet.hpp diff --git a/.gitmodules b/.gitmodules index e4abb5e..07229b4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ path = deps/zstd url = https://github.com/facebook/zstd.git branch = dev +[submodule "deps/minhook"] + path = deps/minhook + url = https://github.com/TsudaKageyu/minhook.git diff --git a/deps/minhook b/deps/minhook new file mode 160000 index 0000000..4a45552 --- /dev/null +++ b/deps/minhook @@ -0,0 +1 @@ +Subproject commit 4a455528f61b5a375b1f9d44e7d296d47f18bb18 diff --git a/deps/premake/minhook.lua b/deps/premake/minhook.lua new file mode 100644 index 0000000..396d4d3 --- /dev/null +++ b/deps/premake/minhook.lua @@ -0,0 +1,31 @@ +minhook = { + source = path.join(dependencies.basePath, "minhook"), +} + +function minhook.import() + links { "minhook" } + minhook.includes() +end + +function minhook.includes() + includedirs { + path.join(minhook.source, "include") + } +end + +function minhook.project() + project "minhook" + language "C" + + minhook.includes() + + files { + path.join(minhook.source, "src/**.h"), + path.join(minhook.source, "src/**.c"), + } + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, minhook) diff --git a/src/game/game.cpp b/src/game/game.cpp index 9356f5a..e3eeed1 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -17,8 +17,6 @@ namespace game Dvar_RegisterInt_t Dvar_RegisterInt; - Dvar_RegisterFloat_t Dvar_RegisterFloat; - Dvar_SetIntByName_t Dvar_SetIntByName; Dvar_SetFromStringByName_t Dvar_SetFromStringByName; @@ -246,13 +244,66 @@ namespace game { return dvar_find_malleable_var(dvarName); } - else + + return reinterpret_cast + (SELECT_VALUE(0x539550, 0x5BDCC0, 0x0))(dvarName); + } + + constexpr auto Dvar_RegisterVariant_Addr = 0x531F70; + __declspec(naked) const dvar_t* Dvar_RegisterVariant(const char* dvarName, unsigned char type, + unsigned __int16 flags, DvarValue value, DvarLimits domain, const char* description) + { + __asm { - return reinterpret_cast - (SELECT_VALUE(0x539550, 0x5BDCC0, 0x0))(dvarName); + push eax + pushad + + mov edi, [esp + 0x24 + 0x28] // description + mov eax, [esp + 0x24 + 0x4] // dvarName + + push [esp + 0x24 + 0x24] // domain + push [esp + 0x24 + 0x24] // domain + + push [esp + 0x24 + 0x24] // value + push [esp + 0x24 + 0x24] // value + push [esp + 0x24 + 0x24] // value + push [esp + 0x24 + 0x24] // value + + push [esp + 0x24 + 0x24] // flags + push [esp + 0x24 + 0x24] // type + + call Dvar_RegisterVariant_Addr + add esp, 0x20 + + mov [esp + 0x20], eax // result + popad + pop eax + + retn } } + const dvar_t* Dvar_RegisterFloat(const char* dvarName, float value, + float min, float max, unsigned __int16 flags, const char* description) + { + if (!is_dedi()) + { + return reinterpret_cast + (SELECT_VALUE(0x4F9CC0, 0x5BEA80, 0x0))(dvarName, value, min, max, flags, description); + } + + DvarLimits domain; + DvarValue dvar_value; + + domain.value.min = min; + domain.value.max = max; + + dvar_value.value = value; + + return Dvar_RegisterVariant(dvarName, dvar_type::DVAR_TYPE_FLOAT, + flags, dvar_value, domain, description); + } + const float* Scr_AllocVector(const float* v) { const auto mem = static_cast(MT_Alloc(16, 2)); @@ -632,8 +683,6 @@ namespace game native::Dvar_RegisterInt = native::Dvar_RegisterInt_t(SELECT_VALUE(0x48CD40, 0x5BEA40, 0x0)); - native::Dvar_RegisterFloat = native::Dvar_RegisterFloat_t(SELECT_VALUE(0x4F9CC0, 0x5BEA80, 0x0)); - native::Dvar_SetIntByName = native::Dvar_SetIntByName_t(SELECT_VALUE(0x5396B0, 0x5BF560, 0x0)); native::Dvar_SetFromStringByName = native::Dvar_SetFromStringByName_t( diff --git a/src/game/game.hpp b/src/game/game.hpp index 1d80191..47fc90e 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -31,10 +31,6 @@ namespace game int min, int max, unsigned __int16 flags, const char* description); extern Dvar_RegisterInt_t Dvar_RegisterInt; - typedef const dvar_t* (*Dvar_RegisterFloat_t)(const char* dvarName, float value, - float min, float max, unsigned __int16 flags, const char* description); - extern Dvar_RegisterFloat_t Dvar_RegisterFloat; - typedef void (*Dvar_SetIntByName_t)(const char* dvarName, int value); extern Dvar_SetIntByName_t Dvar_SetIntByName; @@ -104,8 +100,8 @@ namespace game typedef void (*PM_WeaponUseAmmo_t)(playerState_s* ps, const Weapon weapon, bool isAlternate, int amount, PlayerHandIndex hand); extern PM_WeaponUseAmmo_t PM_WeaponUseAmmo; - typedef void (*CM_TransformedCapsuleTrace_t)(game::native::trace_t* results, const float* start, const float* end, - const game::native::Bounds* bounds, const game::native::Bounds* capsule, int contents, + typedef void (*CM_TransformedCapsuleTrace_t)(trace_t* results, const float* start, const float* end, + const Bounds* bounds, const Bounds* capsule, int contents, const float* origin, const float* angles); extern CM_TransformedCapsuleTrace_t CM_TransformedCapsuleTrace; @@ -197,6 +193,8 @@ namespace game void* MT_Alloc(int numBytes, int type); dvar_t* Dvar_FindVar(const char* dvarName); + const dvar_t* Dvar_RegisterVariant(const char* dvarName, unsigned char type, unsigned __int16 flags, DvarValue value, DvarLimits domain, const char* description); + const dvar_t* Dvar_RegisterFloat(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description); const float* Scr_AllocVector(const float* v); void Scr_ClearOutParams(); diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 0800859..831615a 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -547,13 +547,14 @@ namespace game enum dvar_flags : std::uint16_t { - DVAR_ARCHIVE = 0x1, - DVAR_CHEAT = 0x4, - DVAR_CODINFO = 0x8, - DVAR_SCRIPTINFO = 0x10, - DVAR_SERVERINFO = 0x400, - DVAR_WRITEPROTECTED = 0x800, - DVAR_READONLY = 0x2000, + DVAR_ARCHIVE = 1 << 0, + DVAR_CHEAT = 1 << 2, + DVAR_CODINFO = 1 << 3, + DVAR_SCRIPTINFO = 1 << 4, + DVAR_SERVERINFO = 1 << 10, + DVAR_WRITEPROTECTED = 1 << 11, + DVAR_READONLY = 1 << 13, + DVAR_AUTOEXEC = 1 << 15, }; // Incomplete enum dvar_type : std::int8_t @@ -581,6 +582,8 @@ namespace game char color[4]; }; + static_assert(sizeof(DvarValue) == 0x10); + struct enum_limit { int stringCount; diff --git a/src/module/bullet.cpp b/src/module/bullet.cpp new file mode 100644 index 0000000..3d27a76 --- /dev/null +++ b/src/module/bullet.cpp @@ -0,0 +1,56 @@ +#include +#include +#include "game/game.hpp" + +#include + +#include "bullet.hpp" + +const game::native::dvar_t* bullet::bg_bulletRange; +const game::native::dvar_t* bullet::bg_surfacePenetration; + +DWORD bullet::bullet_fire_addr; + +utils::hook::detour bullet::bg_get_surface_penetration_depth_hook; + +__declspec(naked) void bullet::bullet_fire_stub() +{ + __asm + { + push eax + mov eax, bg_bulletRange + fld dword ptr [eax + 0xC] //dvar_t.current + pop eax + + jmp bullet_fire_addr + } +} + +float bullet::bg_get_surface_penetration_depth_stub(const game::native::Weapon weapon, bool is_alternate, int surface_type) +{ + const auto value = bg_surfacePenetration->current.value; + if (value > 0.0f) + { + return value; + } + + return bg_get_surface_penetration_depth_hook.invoke(weapon, is_alternate, surface_type); +} + +void bullet::post_load() +{ + bg_bulletRange = game::native::Dvar_RegisterFloat("bg_bulletRange", 8192.0f, 0.0f, + std::numeric_limits::max(), game::native::DVAR_CODINFO, + "Max range used when calculating the bullet end position"); + + bg_surfacePenetration = game::native::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f, + 0.0f, std::numeric_limits::max(), game::native::DVAR_CODINFO, + "Set to a value greater than 0 to override the surface penetration depth"); + + bullet_fire_addr = SELECT_VALUE(0x5B6442, 0x4F6C5C, 0x46CFFA); + utils::hook(SELECT_VALUE(0x5B643C, 0x4F6C56, 0x46CFF4), &bullet_fire_stub, HOOK_JUMP).install()->quick(); + + bg_get_surface_penetration_depth_hook.create(SELECT_VALUE(0x43BDE0, 0x42F4D0, 0x421610), &bg_get_surface_penetration_depth_stub); +} + +REGISTER_MODULE(bullet) diff --git a/src/module/bullet.hpp b/src/module/bullet.hpp new file mode 100644 index 0000000..e3fe7d5 --- /dev/null +++ b/src/module/bullet.hpp @@ -0,0 +1,17 @@ +#pragma once + +class bullet final : public module +{ +public: + void post_load() override; + +private: + static const game::native::dvar_t* bg_bulletRange; + static const game::native::dvar_t* bg_surfacePenetration; + + static DWORD bullet_fire_addr; + static void bullet_fire_stub(); + + static utils::hook::detour bg_get_surface_penetration_depth_hook; + static float bg_get_surface_penetration_depth_stub(const game::native::Weapon weapon, bool isAlternate, int surfaceType); +}; diff --git a/src/utils/hook.cpp b/src/utils/hook.cpp index 7902ecd..5705cc4 100644 --- a/src/utils/hook.cpp +++ b/src/utils/hook.cpp @@ -1,8 +1,30 @@ #include #include "hook.hpp" +#include + namespace utils { + namespace + { + [[maybe_unused]] class _ + { + public: + _() + { + if (MH_Initialize() != MH_OK) + { + throw std::runtime_error("Failed to initialize MinHook"); + } + } + + ~_() + { + MH_Uninitialize(); + } + } __; + } + void hook::signature::process() { if (this->signatures_.empty()) return; @@ -42,6 +64,64 @@ namespace utils signatures_.push_back(container); } + hook::detour::detour(const size_t place, void* target) : detour(reinterpret_cast(place), target) + { + } + + hook::detour::detour(void* place, void* target) + { + this->create(place, target); + } + + hook::detour::~detour() + { + this->clear(); + } + + void hook::detour::enable() const + { + MH_EnableHook(this->place_); + } + + void hook::detour::disable() const + { + MH_DisableHook(this->place_); + } + + void hook::detour::create(void* place, void* target) + { + this->clear(); + this->place_ = place; + + if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK) + { + throw std::runtime_error("Unable to create hook"); + } + + this->enable(); + } + + void hook::detour::create(const size_t place, void* target) + { + this->create(reinterpret_cast(place), target); + } + + void hook::detour::clear() + { + if (this->place_) + { + MH_RemoveHook(this->place_); + } + + this->place_ = nullptr; + this->original_ = nullptr; + } + + void* hook::detour::get_original() const + { + return this->original_; + } + hook::~hook() { if (this->initialized_) diff --git a/src/utils/hook.hpp b/src/utils/hook.hpp index f35d25b..91f89c0 100644 --- a/src/utils/hook.hpp +++ b/src/utils/hook.hpp @@ -40,6 +40,64 @@ namespace utils std::vector signatures_; }; + class detour + { + public: + detour() = default; + detour(void* place, void* target); + detour(size_t place, void* target); + ~detour(); + + detour(detour&& other) noexcept + { + this->operator=(std::move(other)); + } + + detour& operator=(detour&& other) noexcept + { + if (this != &other) + { + this->~detour(); + + this->place_ = other.place_; + this->original_ = other.original_; + + other.place_ = nullptr; + other.original_ = nullptr; + } + + return *this; + } + + detour(const detour&) = delete; + detour& operator=(const detour&) = delete; + + void enable() const; + void disable() const; + + void create(void* place, void* target); + void create(size_t place, void* target); + void clear(); + + template + T* get() const + { + return static_cast(this->get_original()); + } + + template + T invoke(Args ... args) + { + return static_cast(this->get_original())(args...); + } + + [[nodiscard]] void* get_original() const; + + private: + void* place_{}; + void* original_{}; + }; + hook() : initialized_(false), installed_(false), place_(nullptr), stub_(nullptr), original_(nullptr), use_jump_(false), protection_(0) {