From 73262ca616f0623a9715ceac90c17b0da4b320d7 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:01:22 +0000 Subject: Port navmesh debug renderer from primedev (#626) Adds support for rendering navmeshes in-game using debug overlay Cherry-picked from primedev, originally written by F1F7Y Co-authored-by: F1F7Y Co-authored-by: Maya <11448698+RoyalBlue1@users.noreply.github.com> --- primedev/Northstar.cmake | 9 + primedev/client/cdll_client_int.h | 5 + primedev/client/debugoverlay.cpp | 69 ++--- primedev/client/debugoverlay.h | 28 +++ primedev/client/entity_client_tools.cpp | 13 + primedev/core/math/math_pfns.h | 7 + primedev/core/math/vector.h | 332 +++++++++++++++++++++++-- primedev/core/math/vplane.h | 106 ++++++++ primedev/core/tier1.cpp | 19 ++ primedev/core/tier1.h | 12 + primedev/scripts/scriptdatatables.cpp | 36 --- primedev/server/ai_helper.cpp | 159 ++++++++++++ primedev/server/ai_helper.h | 13 + primedev/server/ai_navmesh.cpp | 6 + primedev/server/ai_navmesh.h | 260 +++++++++++++++++++ primedev/shared/exploit_fixes/exploitfixes.cpp | 20 +- primedev/toolframework/itoolentity.h | 46 ++++ 17 files changed, 1026 insertions(+), 114 deletions(-) create mode 100644 primedev/client/cdll_client_int.h create mode 100644 primedev/client/debugoverlay.h create mode 100644 primedev/client/entity_client_tools.cpp create mode 100644 primedev/core/math/math_pfns.h create mode 100644 primedev/core/math/vplane.h create mode 100644 primedev/core/tier1.cpp create mode 100644 primedev/core/tier1.h create mode 100644 primedev/server/ai_helper.cpp create mode 100644 primedev/server/ai_helper.h create mode 100644 primedev/server/ai_navmesh.cpp create mode 100644 primedev/server/ai_navmesh.h create mode 100644 primedev/toolframework/itoolentity.h (limited to 'primedev') diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index 00a8dcaf..6b9c2380 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -16,6 +16,7 @@ add_library( "client/debugoverlay.cpp" "client/demofixes.cpp" "client/diskvmtfixes.cpp" + "client/entity_client_tools.cpp" "client/languagehooks.cpp" "client/latencyflex.cpp" "client/localchatwriter.cpp" @@ -41,7 +42,9 @@ add_library( "core/math/bits.h" "core/math/color.cpp" "core/math/color.h" + "core/math/math_pfns.h" "core/math/vector.h" + "core/math/vplane.h" "core/hooks.cpp" "core/hooks.h" "core/macros.h" @@ -53,6 +56,8 @@ add_library( "core/sourceinterface.h" "core/tier0.cpp" "core/tier0.h" + "core/tier1.cpp" + "core/tier1.h" "dedicated/dedicated.cpp" "dedicated/dedicated.h" "dedicated/dedicatedlogtoclient.cpp" @@ -116,6 +121,10 @@ add_library( "server/auth/serverauthentication.cpp" "server/auth/serverauthentication.h" "server/alltalk.cpp" + "server/ai_helper.cpp" + "server/ai_helper.h" + "server/ai_navmesh.cpp" + "server/ai_navmesh.h" "server/buildainfile.cpp" "server/r2server.cpp" "server/r2server.h" diff --git a/primedev/client/cdll_client_int.h b/primedev/client/cdll_client_int.h new file mode 100644 index 00000000..a32ccbeb --- /dev/null +++ b/primedev/client/cdll_client_int.h @@ -0,0 +1,5 @@ +#pragma once + +#include "toolframework/itoolentity.h" + +inline IClientTools* g_pClientTools = nullptr; diff --git a/primedev/client/debugoverlay.cpp b/primedev/client/debugoverlay.cpp index 8e860218..a67b8355 100644 --- a/primedev/client/debugoverlay.cpp +++ b/primedev/client/debugoverlay.cpp @@ -1,6 +1,9 @@ +#include "debugoverlay.h" + #include "dedicated/dedicated.h" #include "core/convar/cvar.h" #include "core/math/vector.h" +#include "server/ai_helper.h" AUTOHOOK_INIT() @@ -122,48 +125,13 @@ struct OverlaySphere_t : public OverlayBase_t bool m_bWireframe; }; -typedef bool (*OverlayBase_t__IsDeadType)(OverlayBase_t* a1); -static OverlayBase_t__IsDeadType OverlayBase_t__IsDead; -typedef void (*OverlayBase_t__DestroyOverlayType)(OverlayBase_t* a1); -static OverlayBase_t__DestroyOverlayType OverlayBase_t__DestroyOverlay; +static bool (*OverlayBase_t__IsDead)(OverlayBase_t* a1); +static void (*OverlayBase_t__DestroyOverlay)(OverlayBase_t* a1); static ConVar* Cvar_enable_debug_overlays; LPCRITICAL_SECTION s_OverlayMutex; -// Render Line -typedef void (*RenderLineType)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer); -static RenderLineType RenderLine; - -// Render box -typedef void (*RenderBoxType)( - const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); -static RenderBoxType RenderBox; - -// Render wireframe box -static RenderBoxType RenderWireframeBox; - -// Render swept box -typedef void (*RenderWireframeSweptBoxType)( - const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer); -RenderWireframeSweptBoxType RenderWireframeSweptBox; - -// Render Triangle -typedef void (*RenderTriangleType)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer); -static RenderTriangleType RenderTriangle; - -// Render Axis -typedef void (*RenderAxisType)(const Vector3& vOrigin, float flScale, bool bZBuffer); -static RenderAxisType RenderAxis; - -// I dont know -typedef void (*RenderUnknownType)(const Vector3& vUnk, float flUnk, bool bUnk); -static RenderUnknownType RenderUnknown; - -// Render Sphere -typedef void (*RenderSphereType)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer); -static RenderSphereType RenderSphere; - OverlayBase_t** s_pOverlays; int* g_nRenderTickCount; @@ -317,6 +285,11 @@ void, __fastcall, (bool bRender)) } } + if (bRender && Cvar_enable_debug_overlays->GetBool()) + { + g_pAIHelper->DrawNavmeshPolys(); + } + LeaveCriticalSection(s_OverlayMutex); } @@ -324,17 +297,17 @@ ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, (CModule module) { AUTOHOOK_DISPATCH() - OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast(); - OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast(); - - RenderLine = module.Offset(0x192A70).RCast(); - RenderBox = module.Offset(0x192520).RCast(); - RenderWireframeBox = module.Offset(0x193DA0).RCast(); - RenderWireframeSweptBox = module.Offset(0x1945A0).RCast(); - RenderTriangle = module.Offset(0x193940).RCast(); - RenderAxis = module.Offset(0x1924D0).RCast(); - RenderSphere = module.Offset(0x194170).RCast(); - RenderUnknown = module.Offset(0x1924E0).RCast(); + OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast(); + OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast(); + + RenderLine = module.Offset(0x192A70).RCast(); + RenderBox = module.Offset(0x192520).RCast(); + RenderWireframeBox = module.Offset(0x193DA0).RCast(); + RenderWireframeSweptBox = module.Offset(0x1945A0).RCast(); + RenderTriangle = module.Offset(0x193940).RCast(); + RenderAxis = module.Offset(0x1924D0).RCast(); + RenderSphere = module.Offset(0x194170).RCast(); + RenderUnknown = module.Offset(0x1924E0).RCast(); s_OverlayMutex = module.Offset(0x10DB0A38).RCast(); diff --git a/primedev/client/debugoverlay.h b/primedev/client/debugoverlay.h new file mode 100644 index 00000000..498bfafd --- /dev/null +++ b/primedev/client/debugoverlay.h @@ -0,0 +1,28 @@ +#pragma once + +// Render Line +inline void (*RenderLine)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer); + +// Render box +inline void (*RenderBox)( + const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); + +// Render wireframe box +inline void (*RenderWireframeBox)( + const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); + +// Render swept box +inline void (*RenderWireframeSweptBox)( + const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer); + +// Render Triangle +inline void (*RenderTriangle)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer); + +// Render Axis +inline void (*RenderAxis)(const Vector3& vOrigin, float flScale, bool bZBuffer); + +// I dont know +inline void (*RenderUnknown)(const Vector3& vUnk, float flUnk, bool bUnk); + +// Render Sphere +inline void (*RenderSphere)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer); diff --git a/primedev/client/entity_client_tools.cpp b/primedev/client/entity_client_tools.cpp new file mode 100644 index 00000000..d6f0be11 --- /dev/null +++ b/primedev/client/entity_client_tools.cpp @@ -0,0 +1,13 @@ +#include "toolframework/itoolentity.h" +#include "client/cdll_client_int.h" +#include "core/tier1.h" + +class CClientTools : public IClientTools +{ +public: +}; + +ON_DLL_LOAD("client.dll", ClientClientTools, (CModule module)) +{ + g_pClientTools = Sys_GetFactoryPtr("client.dll", "VCLIENTTOOLS001").RCast(); +} diff --git a/primedev/core/math/math_pfns.h b/primedev/core/math/math_pfns.h new file mode 100644 index 00000000..4dca9806 --- /dev/null +++ b/primedev/core/math/math_pfns.h @@ -0,0 +1,7 @@ +#pragma once + +inline float FastSqrt(float x) +{ + __m128 root = _mm_sqrt_ss(_mm_load_ss(&x)); + return *(reinterpret_cast(&root)); +} diff --git a/primedev/core/math/vector.h b/primedev/core/math/vector.h index 8684908f..e62f2c93 100644 --- a/primedev/core/math/vector.h +++ b/primedev/core/math/vector.h @@ -1,47 +1,331 @@ +#pragma once + +#include "bits.h" +#include "math_pfns.h" + #include -#pragma once +#define DEG2RAD(a) (a) * (3.14159265358979323846f / 180.0f) +#define RAD2DEG(a) (a) * (180.0f / 3.14159265358979323846f) -union Vector3 +class Vector3 { - struct +public: + float x, y, z; + + Vector3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + Vector3(float _f) : x(_f), y(_f), z(_f) {} + Vector3() : x(0.0f), y(0.0f), z(0.0f) {} + + inline bool IsValid() { - float x; - float y; - float z; - }; + return IsFinite(x) && IsFinite(y) && IsFinite(z); + } - float raw[3]; + inline void Init(float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f) + { + x = fX; + y = fY; + z = fZ; + } - void MakeValid() + inline float Length() { - for (auto& fl : raw) - if (std::isnan(fl)) - fl = 0; + return FastSqrt(x * x + y * y + z * z); } - // todo: more operators maybe - bool operator==(const Vector3& other) + inline float DistTo(const Vector3& vOther) { - return x == other.x && y == other.y && z == other.z; + Vector3 vDelta; + vDelta.x = vOther.x - x; + vDelta.y = vOther.y - y; + vDelta.z = vOther.z - z; + + return vDelta.Length(); + } + + float Dot(const Vector3& vOther) const + { + return x * vOther.x + y * vOther.y + z * vOther.z; } + + // Cross product between two vectors. + Vector3 Cross(const Vector3& vOther) const; + + void Normalize(); + + bool operator==(const Vector3& v) const; + bool operator!=(const Vector3& v) const; + + FORCEINLINE Vector3& operator+=(const Vector3& v); + FORCEINLINE Vector3& operator-=(const Vector3& v); + FORCEINLINE Vector3& operator*=(const Vector3& v); + FORCEINLINE Vector3& operator*=(float s); + FORCEINLINE Vector3& operator/=(const Vector3& v); + FORCEINLINE Vector3& operator/=(float s); + FORCEINLINE Vector3& operator+=(float fl); ///< broadcast add + FORCEINLINE Vector3& operator-=(float fl); + + // arithmetic operations + Vector3 operator-(void) const; + + Vector3 operator+(const Vector3& v) const; + Vector3 operator-(const Vector3& v) const; + Vector3 operator*(const Vector3& v) const; + Vector3 operator/(const Vector3& v) const; + Vector3 operator*(float fl) const; + Vector3 operator/(float fl) const; }; -union QAngle +FORCEINLINE void VectorAdd(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorSubtract(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorMultiply(const Vector3& a, float b, Vector3& result); +FORCEINLINE void VectorMultiply(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorDivide(const Vector3& a, float b, Vector3& result); +FORCEINLINE void VectorDivide(const Vector3& a, const Vector3& b, Vector3& result); + +inline bool Vector3::operator==(const Vector3& src) const +{ + return (src.x == x) && (src.y == y) && (src.z == z); +} + +inline bool Vector3::operator!=(const Vector3& src) const +{ + return (src.x != x) || (src.y != y) || (src.z != z); +} + +FORCEINLINE Vector3& Vector3::operator+=(const Vector3& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator-=(const Vector3& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator*=(float fl) +{ + x *= fl; + y *= fl; + z *= fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator*=(const Vector3& v) +{ + x *= v.x; + y *= v.y; + z *= v.z; + return *this; +} + +// this ought to be an opcode. +FORCEINLINE Vector3& Vector3::operator+=(float fl) +{ + x += fl; + y += fl; + z += fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator-=(float fl) +{ + x -= fl; + y -= fl; + z -= fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator/=(float fl) +{ + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + z *= oofl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator/=(const Vector3& v) +{ + x /= v.x; + y /= v.y; + z /= v.z; + return *this; +} + +inline Vector3 Vector3::operator-(void) const +{ + return Vector3(-x, -y, -z); +} + +inline Vector3 Vector3::operator+(const Vector3& v) const { - struct + Vector3 res; + VectorAdd(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator-(const Vector3& v) const +{ + Vector3 res; + VectorSubtract(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator*(float fl) const +{ + Vector3 res; + VectorMultiply(*this, fl, res); + return res; +} + +inline Vector3 Vector3::operator*(const Vector3& v) const +{ + Vector3 res; + VectorMultiply(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator/(float fl) const +{ + Vector3 res; + VectorDivide(*this, fl, res); + return res; +} + +inline Vector3 Vector3::operator/(const Vector3& v) const +{ + Vector3 res; + VectorDivide(*this, v, res); + return res; +} + +inline Vector3 operator*(float fl, const Vector3& v) +{ + return v * fl; +} + +inline Vector3 Vector3::Cross(const Vector3& vOther) const +{ + return Vector3(y * vOther.z - z * vOther.y, z * vOther.x - x * vOther.z, x * vOther.y - y * vOther.x); +} + +inline void Vector3::Normalize() +{ + float fLen = Length(); + x /= fLen; + y /= fLen; + z /= fLen; +} + +inline Vector3 StringToVector(char* pString) +{ + Vector3 vRet; + + int length = 0; + while (pString[length]) { - float x; - float y; - float z; - float w; - }; + if ((pString[length] == '<') || (pString[length] == '>')) + pString[length] = '\0'; + length++; + } + + int startOfFloat = 1; + int currentIndex = 1; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.x = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; - float raw[4]; + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.y = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.z = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + return vRet; +} + +FORCEINLINE void VectorAdd(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x + b.x; + c.y = a.y + b.y; + c.z = a.z + b.z; +} + +FORCEINLINE void VectorSubtract(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x - b.x; + c.y = a.y - b.y; + c.z = a.z - b.z; +} + +FORCEINLINE void VectorMultiply(const Vector3& a, float b, Vector3& c) +{ + c.x = a.x * b; + c.y = a.y * b; + c.z = a.z * b; +} + +FORCEINLINE void VectorMultiply(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x * b.x; + c.y = a.y * b.y; + c.z = a.z * b.z; +} + +FORCEINLINE void VectorDivide(const Vector3& a, float b, Vector3& c) +{ + float oob = 1.0f / b; + c.x = a.x * oob; + c.y = a.y * oob; + c.z = a.z * oob; +} + +FORCEINLINE void VectorDivide(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x / b.x; + c.y = a.y / b.y; + c.z = a.z / b.z; +} + +class QAngle +{ +public: + float x; + float y; + float z; + + QAngle(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + QAngle(float _f) : x(_f), y(_f), z(_f) {} + QAngle() : x(0.0f), y(0.0f), z(0.0f) {} + + Vector3 GetNormal() const; // todo: more operators maybe bool operator==(const QAngle& other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return x == other.x && y == other.y && z == other.z; } }; + +inline Vector3 QAngle::GetNormal() const +{ + Vector3 ret(cos(DEG2RAD(y)), sin(DEG2RAD(y)), -sin(DEG2RAD(x))); + return ret; +} diff --git a/primedev/core/math/vplane.h b/primedev/core/math/vplane.h new file mode 100644 index 00000000..8b8de423 --- /dev/null +++ b/primedev/core/math/vplane.h @@ -0,0 +1,106 @@ +#pragma once + +typedef int SideType; + +// Used to represent sides of things like planes. +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + +#define VP_EPSILON 0.01f + +class VPlane +{ +public: + VPlane(); + VPlane(const Vector3& vNormal, float dist); + VPlane(const Vector3& vPoint, const QAngle& ang); + + void Init(const Vector3& vNormal, float dist); + + // Return the distance from the point to the plane. + float DistTo(const Vector3& vVec) const; + + // Copy. + VPlane& operator=(const VPlane& thePlane); + + // Returns SIDE_ON, SIDE_FRONT, or SIDE_BACK. + // The epsilon for SIDE_ON can be passed in. + SideType GetPointSide(const Vector3& vPoint, float sideEpsilon = VP_EPSILON) const; + + // Returns SIDE_FRONT or SIDE_BACK. + SideType GetPointSideExact(const Vector3& vPoint) const; + + // Get a point on the plane (normal*dist). + Vector3 GetPointOnPlane() const; + + // Snap the specified point to the plane (along the plane's normal). + Vector3 SnapPointToPlane(const Vector3& vPoint) const; + +public: + Vector3 m_Normal; + float m_Dist; +}; + +//----------------------------------------------------------------------------- +// Inlines. +//----------------------------------------------------------------------------- +inline VPlane::VPlane() {} + +inline VPlane::VPlane(const Vector3& vNormal, float dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline VPlane::VPlane(const Vector3& vPoint, const QAngle& ang) +{ + m_Normal = ang.GetNormal(); + m_Dist = vPoint.x * m_Normal.x + vPoint.y * m_Normal.y + vPoint.z * m_Normal.z; +} + +inline void VPlane::Init(const Vector3& vNormal, float dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline float VPlane::DistTo(const Vector3& vVec) const +{ + return vVec.Dot(m_Normal) - m_Dist; +} + +inline VPlane& VPlane::operator=(const VPlane& thePlane) +{ + m_Normal = thePlane.m_Normal; + m_Dist = thePlane.m_Dist; + return *this; +} + +inline Vector3 VPlane::GetPointOnPlane() const +{ + return m_Normal * m_Dist; +} + +inline Vector3 VPlane::SnapPointToPlane(const Vector3& vPoint) const +{ + return vPoint - m_Normal * DistTo(vPoint); +} + +inline SideType VPlane::GetPointSide(const Vector3& vPoint, float sideEpsilon) const +{ + float fDist; + + fDist = DistTo(vPoint); + if (fDist >= sideEpsilon) + return SIDE_FRONT; + else if (fDist <= -sideEpsilon) + return SIDE_BACK; + else + return SIDE_ON; +} + +inline SideType VPlane::GetPointSideExact(const Vector3& vPoint) const +{ + return DistTo(vPoint) > 0.0f ? SIDE_FRONT : SIDE_BACK; +} diff --git a/primedev/core/tier1.cpp b/primedev/core/tier1.cpp new file mode 100644 index 00000000..a2995496 --- /dev/null +++ b/primedev/core/tier1.cpp @@ -0,0 +1,19 @@ +#include "tier1.h" + +// Note: this file is tier1/interface.cpp in primedev, but given that tier0 is yet to be split +// I am following the existing "pattern" and putting this here + +CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) +{ + HMODULE hModule = GetModuleHandleA(svModuleName.c_str()); + + if (!hModule) + { + spdlog::error("Failed to get module handle of '{}'!", svModuleName.c_str()); + exit(EXIT_FAILURE); + } + + CreateInterfaceFn fnCreateInterface = reinterpret_cast(GetProcAddress(hModule, CREATEINTERFACE_PROCNAME)); + + return fnCreateInterface(svFactoryName.c_str(), NULL); +} diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h new file mode 100644 index 00000000..5be58274 --- /dev/null +++ b/primedev/core/tier1.h @@ -0,0 +1,12 @@ +#pragma once + +// Note: this file is tier1/interface.h in primedev, but given that tier0 is yet to be split +// I am following the existing "pattern" and putting this here + +#include "memory.h" + +#define CREATEINTERFACE_PROCNAME "CreateInterface" + +typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); + +CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); diff --git a/primedev/scripts/scriptdatatables.cpp b/primedev/scripts/scriptdatatables.cpp index 865b6243..47c3a0f1 100644 --- a/primedev/scripts/scriptdatatables.cpp +++ b/primedev/scripts/scriptdatatables.cpp @@ -59,42 +59,6 @@ struct CSVData std::unordered_map CSVCache; -Vector3 StringToVector(char* pString) -{ - Vector3 vRet; - - int length = 0; - while (pString[length]) - { - if ((pString[length] == '<') || (pString[length] == '>')) - pString[length] = '\0'; - length++; - } - - int startOfFloat = 1; - int currentIndex = 1; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.x = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.y = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.z = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - return vRet; -} - // var function GetDataTable( asset path ) REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) { diff --git a/primedev/server/ai_helper.cpp b/primedev/server/ai_helper.cpp new file mode 100644 index 00000000..ebb56af2 --- /dev/null +++ b/primedev/server/ai_helper.cpp @@ -0,0 +1,159 @@ +#include "ai_helper.h" + +#include "client/debugoverlay.h" +#include "client/cdll_client_int.h" +#include "engine/hoststate.h" + +#include "core/math/vplane.h" + +#include + +const int AINET_VERSION_NUMBER = 57; +const int AINET_SCRIPT_VERSION_NUMBER = 21; +const int PLACEHOLDER_CRC = 0; + +static ConVar* Cvar_navmesh_debug_hull; +static ConVar* Cvar_navmesh_debug_camera_radius; +static ConVar* Cvar_navmesh_debug_lossy_optimization; + +//----------------------------------------------------------------------------- +// Purpose: Get navmesh pointer for hull +// Output : navmesh*, nullptr if out of range +//----------------------------------------------------------------------------- +dtNavMesh* GetNavMeshForHull(int nHull) +{ + if (nHull < 1 || nHull > 4) + return nullptr; + + return g_pNavMesh[nHull - 1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Packs two vectors into a __m128i +// Input : &v1 - +// &v2 - +// Output : +//----------------------------------------------------------------------------- +__m128i PackVerticesSIMD16(const Vector3& v1, const Vector3& v2) +{ + short x1, x2, y1, y2, z1, z2; + x1 = static_cast(v1.x); + x2 = static_cast(v2.x); + y1 = static_cast(v1.y); + y2 = static_cast(v2.y); + z1 = static_cast(v1.z); + z2 = static_cast(v2.z); + + __m128i xRes = _mm_set_epi16(x1, x2, y1, y2, z1, z2, 0, 0); + + if (x1 < x2) + xRes = _mm_shufflehi_epi16(xRes, _MM_SHUFFLE(2, 3, 1, 0)); + + if (y1 < y2) + xRes = _mm_shufflehi_epi16(xRes, _MM_SHUFFLE(3, 2, 0, 1)); + + if (z1 < z2) + xRes = _mm_shufflelo_epi16(xRes, _MM_SHUFFLE(2, 3, 1, 0)); + + return xRes; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw navmesh polys using debug overlay +// Input : *pNavMesh +//----------------------------------------------------------------------------- +void CAI_Helper::DrawNavmeshPolys(dtNavMesh* pNavMesh) +{ + if (!pNavMesh) + pNavMesh = GetNavMeshForHull(Cvar_navmesh_debug_hull->GetInt()); + if (!pNavMesh) + return; + + Vector3 vCamera; + QAngle aCamera; + float fFov; + g_pClientTools->GetLocalPlayerEyePosition(vCamera, aCamera, fFov); + + const VPlane CullPlane(vCamera - aCamera.GetNormal() * 256.0f, aCamera); + + const float fCamRadius = Cvar_navmesh_debug_camera_radius->GetFloat(); + const bool bOptimize = Cvar_navmesh_debug_lossy_optimization->GetBool(); + + // Used for lossy optimization ( z is ignored when checking for duplicates ) + // [Fifty]: On a release build i gained around 12 fps on a 1050 ti + std::unordered_set sOutlines; + + for (int i = 0; i < pNavMesh->m_maxTiles; ++i) + { + const dtMeshTile* pTile = &pNavMesh->m_tiles[i]; + + if (!pTile->header) + continue; + + for (int j = 0; j < pTile->header->polyCount; j++) + { + const dtPoly* pPoly = &pTile->polys[j]; + + if (vCamera.DistTo(pPoly->org) > fCamRadius) + continue; + + if (CullPlane.GetPointSide(pPoly->org) != SIDE_FRONT) + continue; + + const unsigned int ip = (unsigned int)(pPoly - pTile->polys); + + if (pPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + const dtOffMeshConnection* pCon = &pTile->offMeshConnections[ip - pTile->header->offMeshBase]; + RenderLine(pCon->origin, pCon->dest, Color(255, 250, 50, 255), true); + } + else + { + const dtPolyDetail* pDetail = &pTile->detailMeshes[ip]; + + Vector3 v[3]; + + for (int k = 0; k < pDetail->triCount; ++k) + { + const unsigned char* t = &pTile->detailTris[(pDetail->triBase + k) * 4]; + for (int l = 0; l < 3; ++l) + { + if (t[l] < pPoly->vertCount) + { + float* pfVerts = &pTile->verts[pPoly->verts[t[l]] * 3]; + v[l] = Vector3(pfVerts[0], pfVerts[1], pfVerts[2]); + } + else + { + float* pfVerts = &pTile->detailVerts[(pDetail->vertBase + t[l] - pPoly->vertCount) * 3]; + v[l] = Vector3(pfVerts[0], pfVerts[1], pfVerts[2]); + } + } + + RenderTriangle(v[0], v[1], v[2], Color(110, 200, 220, 160), true); + + auto r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[0], v[1]), 1)); + if (r.second || !bOptimize) + RenderLine(v[0], v[1], Color(0, 0, 150), true); + + r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[1], v[2]), 1)); + if (r.second || !bOptimize) + RenderLine(v[1], v[2], Color(0, 0, 150), true); + + r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[2], v[0]), 1)); + if (r.second || !bOptimize) + RenderLine(v[2], v[0], Color(0, 0, 150), true); + } + } + } + } +} + +ON_DLL_LOAD("server.dll", ServerAIHelper, (CModule module)) +{ + Cvar_navmesh_debug_hull = new ConVar("navmesh_debug_hull", "0", FCVAR_RELEASE, "0 = NONE"); + Cvar_navmesh_debug_camera_radius = + new ConVar("navmesh_debug_camera_radius", "1000", FCVAR_RELEASE, "Radius in which to draw navmeshes"); + Cvar_navmesh_debug_lossy_optimization = + new ConVar("navmesh_debug_lossy_optimization", "1", FCVAR_RELEASE, "Whether to enable lossy navmesh debug draw optimizations"); +} diff --git a/primedev/server/ai_helper.h b/primedev/server/ai_helper.h new file mode 100644 index 00000000..0b3c3a53 --- /dev/null +++ b/primedev/server/ai_helper.h @@ -0,0 +1,13 @@ +#pragma once + +#include "server/ai_navmesh.h" + +dtNavMesh* GetNavMeshForHull(int nHull); + +class CAI_Helper +{ +public: + void DrawNavmeshPolys(dtNavMesh* pNavMesh = nullptr); +}; + +inline CAI_Helper* g_pAIHelper = nullptr; diff --git a/primedev/server/ai_navmesh.cpp b/primedev/server/ai_navmesh.cpp new file mode 100644 index 00000000..966726b2 --- /dev/null +++ b/primedev/server/ai_navmesh.cpp @@ -0,0 +1,6 @@ +#include "ai_navmesh.h" + +ON_DLL_LOAD("server.dll", ServerAiNavMesh, (CModule module)) +{ + g_pNavMesh = module.Offset(0x105F5D0).RCast(); +} diff --git a/primedev/server/ai_navmesh.h b/primedev/server/ai_navmesh.h new file mode 100644 index 00000000..65529f7a --- /dev/null +++ b/primedev/server/ai_navmesh.h @@ -0,0 +1,260 @@ +#pragma once + +// [Fifty]: Taken from https://github.com/ASpoonPlaysGames/r2recast + +#include "core/math/vector.h" + +struct dtMeshHeader; +struct dtMeshTile; +struct dtPoly; +struct dtBVNode; +struct dtLink; + +typedef unsigned int dtPolyRef; + +/// The maximum number of vertices per navigation polygon. +/// @ingroup detour +static const int DT_VERTS_PER_POLYGON = 6; + +/// Flags representing the type of a navigation mesh polygon. +enum dtPolyTypes +{ + /// The polygon is a standard convex polygon that is part of the surface of the mesh. + DT_POLYTYPE_GROUND = 0, + /// The polygon is an off-mesh connection consisting of two vertices. + DT_POLYTYPE_OFFMESH_CONNECTION = 1, +}; + +/// Configuration parameters used to define multi-tile navigation meshes. +/// The values are used to allocate space during the initialization of a navigation mesh. +/// @see dtNavMesh::init() +/// @ingroup detour +struct dtNavMeshParams +{ + float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)] + float tileWidth; ///< The width of each tile. (Along the x-axis.) + float tileHeight; ///< The height of each tile. (Along the z-axis.) + int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits + ///< are needed to identify tiles and polygons uniquely. + int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are + ///< needed to identify tiles and polygons uniquely. + // + //// i hate this + int disjointPolyGroupCount = 0; + int reachabilityTableSize = 0; + int reachabilityTableCount = 0; +}; + +/// Defines an navigation mesh off-mesh connection within a dtMeshTile object. +/// An off-mesh connection is a user defined traversable connection made up to two vertices. +struct dtOffMeshConnection +{ + /// The endpoints of the connection. + Vector3 origin; + Vector3 dest; + + /// The radius of the endpoints. [Limit: >= 0] + float rad; + + /// The polygon reference of the connection within the tile. + unsigned short poly; + + /// Link flags. + /// @note These are not the connection's user defined flags. Those are assigned via the + /// connection's dtPoly definition. These are link flags used for internal purposes. + unsigned char flags; + + /// End point side. + unsigned char side; + + /// The id of the offmesh connection. (User assigned when the navigation mesh is built.) + unsigned int userId; + + float unk[3]; + float another_unk; +}; + +/// A navigation mesh based on tiles of convex polygons. +/// @ingroup detour +class dtNavMesh +{ +public: + dtMeshTile** m_posLookup; ///< Tile hash lookup. + dtMeshTile* m_nextFree; ///< Freelist of tiles. + dtMeshTile* m_tiles; ///< List of tiles. + + void* disjointPolyGroup; + int** reachabilityTable; + + __int64 unk; + dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice. + float m_orig[3]; ///< Origin of the tile (0,0) + float m_tileWidth, m_tileHight; ///< Dimensions of each tile. + int m_pad; + int m_maxTiles; ///< Max number of tiles. + + int m_tileLutSize; ///< Tile hash lookup size (must be pot). + int m_tileLutMask; ///< Tile hash lookup mask. + + int m_saltBits; ///< Number of salt bits in the tile ID. + int m_tileBits; ///< Number of tile bits in the tile ID. + int m_polyBits; ///< Number of poly bits in the tile ID. +}; + +/// Defines the location of detail sub-mesh data within a dtMeshTile. +struct dtPolyDetail +{ + unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array. + unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array. + unsigned char vertCount; ///< The number of vertices in the sub-mesh. + unsigned char triCount; ///< The number of triangles in the sub-mesh. +}; + +/// Defines a navigation mesh tile. +/// @ingroup detour +struct dtMeshTile +{ + int salt; ///< Counter describing modifications to the tile. + unsigned int linksFreeList; ///< Index to the next free link. + dtMeshHeader* header; ///< The tile header. + dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount] + void* unkPolyThing; + float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount] + dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount] + dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount] + + /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount] + float* detailVerts; + + /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount]. + /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags. + unsigned char* detailTris; + + /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount] + /// (Will be null if bounding volumes are disabled.) + dtBVNode* bvTree; + + dtOffMeshConnection* offMeshConnections; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount] + void* data; ///< The tile data. (Not directly accessed under normal situations.) + int dataSize; ///< Size of the tile data. + int flags; ///< Tile flags. (See: #dtTileFlags) + dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid. + __int64 unk; +}; + +/// Provides high level information related to a dtMeshTile object. +/// @ingroup detour +struct dtMeshHeader +{ + int magic; ///< Tile magic number. (Used to identify the data format.) + int version; ///< Tile data format version number. + int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer) + int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer) + int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer) + unsigned int userId; ///< The user defined id of the tile. + int polyCount; ///< The number of polygons in the tile. + int sth_per_poly; + int vertCount; ///< The number of vertices in the tile. + int maxLinkCount; ///< The number of allocated links. + + int detailMeshCount; + + /// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.) + int detailVertCount; + + int detailTriCount; ///< The number of triangles in the detail mesh. + int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.) + int offMeshConCount; ///< The number of off-mesh connections. + // int unk1; + int offMeshBase; ///< The index of the first polygon which is an off-mesh connection. + + float walkableHeight; ///< The height of the agents using the tile. + float walkableRadius; ///< The radius of the agents using the tile. + float walkableClimb; ///< The maximum climb height of the agents using the tile. + float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)] + float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)] + + /// The bounding volume quantization factor. + float bvQuantFactor; +}; + +/// Defines a polygon within a dtMeshTile object. +/// @ingroup detour +struct dtPoly +{ + /// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.) + unsigned int firstLink; + + /// The indices of the polygon's vertices. + /// The actual vertices are located in dtMeshTile::verts. + unsigned short verts[DT_VERTS_PER_POLYGON]; + + /// Packed data representing neighbor polygons references and flags for each edge. + unsigned short neis[DT_VERTS_PER_POLYGON]; + + /// The user defined polygon flags. + unsigned short flags; + + /// The number of vertices in the polygon. + unsigned char vertCount; + + /// The bit packed area id and polygon type. + /// @note Use the structure's set and get methods to acess this value. + unsigned char areaAndtype; + + unsigned short disjointSetId; + unsigned short unk; // IDK but looks filled + Vector3 org; // + + /// Sets the user defined area id. [Limit: < #DT_MAX_AREAS] + inline void setArea(unsigned char a) + { + areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); + } + + /// Sets the polygon type. (See: #dtPolyTypes.) + inline void setType(unsigned char t) + { + areaAndtype = (areaAndtype & 0x3f) | (t << 6); + } + + /// Gets the user defined area id. + inline unsigned char getArea() const + { + return areaAndtype & 0x3f; + } + + /// Gets the polygon type. (See: #dtPolyTypes) + inline unsigned char getType() const + { + return areaAndtype >> 6; + } +}; + +/// Defines a link between polygons. +/// @note This structure is rarely if ever used by the end user. +/// @see dtMeshTile +struct dtLink +{ + dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.) + unsigned int next; ///< Index of the next link. + unsigned char edge; ///< Index of the polygon edge that owns this link. + unsigned char side; ///< If a boundary link, defines on which side the link is. + unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area. + unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area. + unsigned char jumpType; + unsigned char otherUnk; + unsigned short reverseLinkIndex; +}; + +/// Bounding volume node. +/// @note This structure is rarely if ever used by the end user. +/// @see dtMeshTile +struct dtBVNode +{ + unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)] + unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)] + int i; ///< The node's index. (Negative for escape sequence.) +}; + +inline dtNavMesh** g_pNavMesh = nullptr; diff --git a/primedev/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp index 44651b3c..cade4084 100644 --- a/primedev/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -222,16 +222,24 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; // fix invalid player angles - cmd->worldViewAngles.MakeValid(); - cmd->attackangles.MakeValid(); - cmd->localViewAngles.MakeValid(); + if (!cmd->worldViewAngles.IsValid()) + cmd->worldViewAngles.Init(); + + if (!cmd->attackangles.IsValid()) + cmd->attackangles.Init(); + + if (!cmd->localViewAngles.IsValid()) + cmd->localViewAngles.Init(); // Fix invalid camera angles - cmd->cameraPos.MakeValid(); - cmd->cameraAngles.MakeValid(); + if (!cmd->cameraPos.IsValid()) + cmd->cameraPos.Init(); + if (!cmd->cameraAngles.IsValid()) + cmd->cameraAngles.Init(); // Fix invaid movement vector - cmd->move.MakeValid(); + if (!cmd->move.IsValid()) + cmd->move.Init(); if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) { diff --git a/primedev/toolframework/itoolentity.h b/primedev/toolframework/itoolentity.h new file mode 100644 index 00000000..332004f2 --- /dev/null +++ b/primedev/toolframework/itoolentity.h @@ -0,0 +1,46 @@ +#pragma once + +#include "core/math/vector.h" + +class IClientTools // : public IBaseInterface +{ +public: + virtual void sub_1805E4960() = 0; + virtual void sub_1805E4B10() = 0; + virtual void sub_1805E4C50() = 0; + virtual void sub_1805E5670() = 0; + virtual void sub_1805E66C0() = 0; + virtual void sub_1805E5910() = 0; + virtual void sub_1805E59A0() = 0; + virtual void sub_1805E6B10() = 0; + virtual void sub_1805E8580() = 0; + virtual void sub_1805E59D0() = 0; + virtual void sub_1805E57B0() = 0; + virtual void sub_1805E5860() = 0; + virtual void sub_1805E55A0() = 0; + virtual void sub_1805E49C0() = 0; + virtual void sub_1805E7580() = 0; + virtual void sub_1805E86A0() = 0; + virtual void sub_1805E69B0() = 0; + virtual void sub_1805E4D70() = 0; // IClientTools::DrawSprite + virtual void* GetLocalPlayer() = 0; // return type unknown probably CBasePlayer* + virtual bool GetLocalPlayerEyePosition(Vector3& org, QAngle& ang, float& fov) = 0; + virtual void sub_1805E5960() = 0; + virtual void sub_1805E5650() = 0; + virtual void sub_1805E5920() = 0; + virtual void sub_1805E64E0() = 0; + virtual void sub_1805E63C0() = 0; + virtual void sub_1805E64C0() = 0; + virtual void sub_1805E6520() = 0; + virtual void sub_1805E6770() = 0; + virtual void sub_1805E67B0() = 0; + virtual void sub_1805E67F0() = 0; + virtual void sub_1805E66A0() = 0; + virtual void sub_1805E6500() = 0; + virtual void sub_1805E63B0() = 0; + virtual void sub_1805E54C0() = 0; + virtual void sub_1805E53E0() = 0; + virtual void sub_1805E6D70() = 0; + virtual void sub_1805E50D0() = 0; + virtual void sub_1805E65C0() = 0; +}; -- cgit v1.2.3 From fc63948e092b3495461e7aab4748af27c6dfa5ee Mon Sep 17 00:00:00 2001 From: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Date: Tue, 6 Feb 2024 23:03:10 +0100 Subject: Update default mods list URL (#662) The default branch for the VerifiedMods repo was renamed from `master` to `main`. --- primedev/mods/autodownload/moddownloader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'primedev') diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h index 5302c21e..10df39ce 100644 --- a/primedev/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -4,7 +4,7 @@ private: const char* VERIFICATION_FLAG = "-disablemodverification"; const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl="; const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/"; - const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/verified-mods.json"; + const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json"; char* modsListUrl; struct VerifiedModVersion @@ -79,7 +79,7 @@ public: * The Northstar auto-downloading feature does NOT allow automatically installing * all mods for various (notably security) reasons; mods that are candidate to * auto-downloading are rather listed on a GitHub repository - * (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/verified-mods.json), + * (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json), * which this method gets via a HTTP call to load into local state. * * If list fetching fails, local mods list will be initialized as empty, thus -- cgit v1.2.3 From 30e58ac08b6ee122de3130f3f02d6a855130ae51 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 18 Feb 2024 22:11:09 +0100 Subject: Clean up wsock proxy code and move wsock build system logic (#671) - moves `WSockProxy` to `wsockproxy/CmakeLists` - remove exepath stuff from dllmain + its still done in loader.cpp because its used when reporting failure - Disabled any Thread Library calls + we don't need to know about threads at all in the proxy - yoink `wsock32.asm` into outer space + turns out, we can just call the function in a void shim since that wont touch the registers - stop copying `wsock32.dll` to the game directory + this should improve the state of things when using the EA App --- primedev/CMakeLists.txt | 2 +- primedev/WSockProxy.cmake | 49 ------------ primedev/wsockproxy/CMakeLists.txt | 48 ++++++++++++ primedev/wsockproxy/dllmain.cpp | 153 ++++++------------------------------- primedev/wsockproxy/loader.cpp | 38 +++++++-- primedev/wsockproxy/wsock32.asm | 7 -- 6 files changed, 102 insertions(+), 195 deletions(-) delete mode 100644 primedev/WSockProxy.cmake create mode 100644 primedev/wsockproxy/CMakeLists.txt delete mode 100644 primedev/wsockproxy/wsock32.asm (limited to 'primedev') diff --git a/primedev/CMakeLists.txt b/primedev/CMakeLists.txt index 03f2628e..31dda4b2 100644 --- a/primedev/CMakeLists.txt +++ b/primedev/CMakeLists.txt @@ -1,3 +1,3 @@ include(Northstar.cmake) include(Launcher.cmake) -include(WSockProxy.cmake) +add_subdirectory(wsockproxy) diff --git a/primedev/WSockProxy.cmake b/primedev/WSockProxy.cmake deleted file mode 100644 index 017e358a..00000000 --- a/primedev/WSockProxy.cmake +++ /dev/null @@ -1,49 +0,0 @@ -# loader_wsock32_proxy - -find_package(minhook REQUIRED) - -add_library( - loader_wsock32_proxy SHARED - "wsockproxy/dllmain.cpp" - "wsockproxy/loader.cpp" - "wsockproxy/loader.h" - "wsockproxy/wsock32.asm" - "wsockproxy/wsock32.def" - ) - -target_link_libraries( - loader_wsock32_proxy - PRIVATE minhook - mswsock.lib - ws2_32.lib - ShLwApi.lib - imagehlp.lib - dbghelp.lib - kernel32.lib - user32.lib - gdi32.lib - winspool.lib - comdlg32.lib - advapi32.lib - shell32.lib - ole32.lib - oleaut32.lib - uuid.lib - odbc32.lib - odbccp32.lib - ) - -target_precompile_headers( - loader_wsock32_proxy - PRIVATE - wsockproxy/pch.h - ) - -target_compile_definitions(loader_wsock32_proxy PRIVATE UNICODE _UNICODE) - -set_target_properties( - loader_wsock32_proxy - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR}/bin/x64_retail - OUTPUT_NAME wsock32 - LINK_FLAGS "/MANIFEST:NO /DEBUG" - ) diff --git a/primedev/wsockproxy/CMakeLists.txt b/primedev/wsockproxy/CMakeLists.txt new file mode 100644 index 00000000..0dbac745 --- /dev/null +++ b/primedev/wsockproxy/CMakeLists.txt @@ -0,0 +1,48 @@ +# loader_wsock32_proxy + +find_package(minhook REQUIRED) + +add_library( + loader_wsock32_proxy SHARED + "dllmain.cpp" + "loader.cpp" + "loader.h" + "wsock32.def" + ) + +target_link_libraries( + loader_wsock32_proxy + PRIVATE minhook + mswsock.lib + ws2_32.lib + ShLwApi.lib + imagehlp.lib + dbghelp.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + comdlg32.lib + advapi32.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + odbc32.lib + odbccp32.lib + ) + +target_precompile_headers( + loader_wsock32_proxy + PRIVATE + pch.h + ) + +target_compile_definitions(loader_wsock32_proxy PRIVATE UNICODE _UNICODE) + +set_target_properties( + loader_wsock32_proxy + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR}/bin/x64_retail + OUTPUT_NAME wsock32 + LINK_FLAGS "/MANIFEST:NO /DEBUG" + ) diff --git a/primedev/wsockproxy/dllmain.cpp b/primedev/wsockproxy/dllmain.cpp index 4cc4f26e..5a606e45 100644 --- a/primedev/wsockproxy/dllmain.cpp +++ b/primedev/wsockproxy/dllmain.cpp @@ -1,90 +1,29 @@ #include "loader.h" -#include #include -HINSTANCE hLThis = 0; -FARPROC p[857]; -HINSTANCE hL = 0; +FARPROC p[73]; +HMODULE hL = 0; -bool GetExePathWide(wchar_t* dest, DWORD destSize) -{ - if (!dest) - return NULL; - if (destSize < MAX_PATH) - return NULL; - - DWORD length = GetModuleFileNameW(NULL, dest, destSize); - return length && PathRemoveFileSpecW(dest); -} - -wchar_t exePath[4096]; -wchar_t buffer1[8192]; -wchar_t buffer2[12288]; +static wchar_t wsockPath[4096]; BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { - hLThis = hInst; - - if (!GetExePathWide(exePath, 4096)) - { - MessageBoxA( - GetForegroundWindow(), - "Failed getting game directory.\nThe game cannot continue and has to exit.", - "Northstar Wsock32 Proxy Error", - 0); - return true; - } - - SetCurrentDirectoryW(exePath); + // Tell the OS we don't need to know about threads + DisableThreadLibraryCalls(hInst); if (!ProvisionNorthstar()) // does not call InitialiseNorthstar yet, will do it on LauncherMain hook return true; - // copy the original library for system to our local directory, with changed name so that we can load it - swprintf_s(buffer1, L"%s\\bin\\x64_retail\\wsock32.org.dll", exePath); - GetSystemDirectoryW(buffer2, 4096); - swprintf_s(buffer2, L"%s\\wsock32.dll", buffer2); - try - { - std::filesystem::copy_file(buffer2, buffer1); - } - catch (const std::exception& e1) - { - if (!std::filesystem::exists(buffer1)) - { - // fallback by copying to temp dir... - // because apparently games installed by EA Desktop app don't have write permissions in their directories - auto temp_dir = std::filesystem::temp_directory_path() / L"wsock32.org.dll"; - try - { - std::filesystem::copy_file(buffer2, temp_dir); - } - catch (const std::exception& e2) - { - if (!std::filesystem::exists(temp_dir)) - { - swprintf_s( - buffer2, - L"Failed copying wsock32.dll from system32 to \"%s\"\n\n%S\n\nFurthermore, we failed copying wsock32.dll into " - L"temporary directory at \"%s\"\n\n%S", - buffer1, - e1.what(), - temp_dir.c_str(), - e2.what()); - MessageBoxW(GetForegroundWindow(), buffer2, L"Northstar Wsock32 Proxy Error", 0); - return false; - } - } - swprintf_s(buffer1, L"%s", temp_dir.c_str()); - } - } - hL = LoadLibraryExW(buffer1, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + GetSystemDirectoryW(wsockPath, 4096); + swprintf_s(wsockPath, 4096, L"%s\\wsock32.dll", wsockPath); + + hL = LoadLibraryExW(wsockPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); if (!hL) { - LibraryLoadError(GetLastError(), L"wsock32.org.dll", buffer1); + LibraryLoadError(GetLastError(), L"wsock32.dll", wsockPath); return false; } @@ -119,64 +58,16 @@ extern "C" FARPROC PA = NULL; int RunASM(); - void PROXY_EnumProtocolsA() - { - PA = p[1]; - RunASM(); - } - void PROXY_EnumProtocolsW() - { - PA = p[2]; - RunASM(); - } - void PROXY_GetAddressByNameA() - { - PA = p[4]; - RunASM(); - } - void PROXY_GetAddressByNameW() - { - PA = p[5]; - RunASM(); - } - void PROXY_WEP() - { - PA = p[17]; - RunASM(); - } - void PROXY_WSARecvEx() - { - PA = p[30]; - RunASM(); - } - void PROXY___WSAFDIsSet() - { - PA = p[36]; - RunASM(); - } - void PROXY_getnetbyname() - { - PA = p[45]; - RunASM(); - } - void PROXY_getsockopt() - { - PA = p[52]; - RunASM(); - } - void PROXY_inet_network() - { - PA = p[56]; - RunASM(); - } - void PROXY_s_perror() - { - PA = p[67]; - RunASM(); - } - void PROXY_setsockopt() - { - PA = p[72]; - RunASM(); - } + void PROXY_EnumProtocolsA() { p[1](); } + void PROXY_EnumProtocolsW() { p[2](); } + void PROXY_GetAddressByNameA() { p[4](); } + void PROXY_GetAddressByNameW() { p[5](); } + void PROXY_WEP() { p[17](); } + void PROXY_WSARecvEx() { p[30](); } + void PROXY___WSAFDIsSet() { p[36](); } + void PROXY_getnetbyname() { p[45](); } + void PROXY_getsockopt() { p[52](); } + void PROXY_inet_network() { p[56](); } + void PROXY_s_perror() { p[67](); } + void PROXY_setsockopt() { p[72](); } } diff --git a/primedev/wsockproxy/loader.cpp b/primedev/wsockproxy/loader.cpp index 0a299ba8..a3abf11c 100644 --- a/primedev/wsockproxy/loader.cpp +++ b/primedev/wsockproxy/loader.cpp @@ -1,4 +1,5 @@ #include "loader.h" +#include #include #include #include @@ -8,6 +9,21 @@ namespace fs = std::filesystem; +static wchar_t northstarPath[8192]; +static wchar_t exePath[4096]; + +bool GetExePathWide(wchar_t* dest, DWORD destSize) +{ + if (!dest) + return NULL; + if (destSize < MAX_PATH) + return NULL; + + DWORD length = GetModuleFileNameW(NULL, dest, destSize); + return length && PathRemoveFileSpecW(dest); +} + + void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location) { char text[4096]; @@ -75,22 +91,30 @@ bool LoadNorthstar() strProfile = "R2Northstar"; } - wchar_t buffer[8192]; + if (!GetExePathWide(exePath, 4096)) + { + MessageBoxA( + GetForegroundWindow(), + "Failed getting game directory.\nThe game cannot continue and has to exit.", + "Northstar Wsock32 Proxy Error", + 0); + return true; + } // Check if "Northstar.dll" exists in profile directory, if it doesnt fall back to root - swprintf_s(buffer, L"%s\\%s\\Northstar.dll", exePath, std::wstring(strProfile.begin(), strProfile.end()).c_str()); + swprintf_s(northstarPath, L"%s\\%s\\Northstar.dll", exePath, std::wstring(strProfile.begin(), strProfile.end()).c_str()); - if (!fs::exists(fs::path(buffer))) - swprintf_s(buffer, L"%s\\Northstar.dll", exePath); + if (!fs::exists(fs::path(northstarPath))) + swprintf_s(northstarPath, L"%s\\Northstar.dll", exePath); - std::wcout << L"[*] Using: " << buffer << std::endl; + std::wcout << L"[*] Using: " << northstarPath << std::endl; - HMODULE hHookModule = LoadLibraryExW(buffer, 0, 8u); + HMODULE hHookModule = LoadLibraryExW(northstarPath, 0, 8u); if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); if (!hHookModule || Hook_Init == nullptr) { - LibraryLoadError(GetLastError(), L"Northstar.dll", buffer); + LibraryLoadError(GetLastError(), L"Northstar.dll", northstarPath); return false; } } diff --git a/primedev/wsockproxy/wsock32.asm b/primedev/wsockproxy/wsock32.asm deleted file mode 100644 index 22a9c384..00000000 --- a/primedev/wsockproxy/wsock32.asm +++ /dev/null @@ -1,7 +0,0 @@ -.data -extern PA : qword -.code -RunASM proc -jmp qword ptr [PA] -RunASM endp -end -- cgit v1.2.3 From ec5dc504bf91b494069b03e7903e89eff951b46c Mon Sep 17 00:00:00 2001 From: cat_or_not <41955154+catornot@users.noreply.github.com> Date: Sun, 25 Feb 2024 13:29:38 -0500 Subject: Fix bad offsets of `sq_getfunction` (#672) and remove duplicate assignment --- primedev/squirrel/squirrel.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'primedev') diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp index 70dedcb8..259d0f43 100644 --- a/primedev/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -715,7 +715,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) // Message buffer stuff g_pSquirrel->messageBuffer = g_pSquirrel->messageBuffer; - g_pSquirrel->__sq_getfunction = module.Offset(0x572FB0).RCast(); + g_pSquirrel->__sq_getfunction = module.Offset(0x6CB0).RCast(); g_pSquirrel->__sq_getfunction = g_pSquirrel->__sq_getfunction; g_pSquirrel->__sq_stackinfos = module.Offset(0x35970).RCast(); g_pSquirrel->__sq_stackinfos = g_pSquirrel->__sq_stackinfos; @@ -756,9 +756,6 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) StubUnsafeSQFuncs(); StubUnsafeSQFuncs(); - - g_pSquirrel->__sq_getfunction = module.Offset(0x6CB0).RCast(); - g_pSquirrel->__sq_getfunction = g_pSquirrel->__sq_getfunction; } ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) @@ -808,7 +805,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) g_pSquirrel->logger = NS::log::SCRIPT_SV; // Message buffer stuff - g_pSquirrel->__sq_getfunction = module.Offset(0x6C85).RCast(); + g_pSquirrel->__sq_getfunction = module.Offset(0x6C80).RCast(); g_pSquirrel->__sq_stackinfos = module.Offset(0x35920).RCast(); // Structs -- cgit v1.2.3 From 026adbebe59a0a9ea1f39a8da135534a2c2ae393 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sat, 2 Mar 2024 14:29:02 +0000 Subject: Check OpenExternalWebBrowser custom flag better (#674) Properly check string prefix to see if we should open it in browser --- primedev/scripts/client/scriptbrowserhooks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'primedev') diff --git a/primedev/scripts/client/scriptbrowserhooks.cpp b/primedev/scripts/client/scriptbrowserhooks.cpp index 86b4a356..dcf051d2 100644 --- a/primedev/scripts/client/scriptbrowserhooks.cpp +++ b/primedev/scripts/client/scriptbrowserhooks.cpp @@ -9,7 +9,8 @@ void, __fastcall, (char* pUrl, char flags)) // clang-format on { bool bIsOriginOverlayEnabledOriginal = *bIsOriginOverlayEnabled; - if (flags & 2 && !strncmp(pUrl, "http", 4)) // custom force external browser flag + bool isHttp = !strncmp(pUrl, "http://", 7) || !strncmp(pUrl, "https://", 8); + if (flags & 2 && isHttp) // custom force external browser flag *bIsOriginOverlayEnabled = false; // if this bool is false, game will use an external browser rather than the origin overlay one OpenExternalWebBrowser(pUrl, flags); -- cgit v1.2.3 From e1eb2a6f4b858e903603226098861c3b62d5d1a4 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sat, 2 Mar 2024 14:29:58 +0000 Subject: Don't allow too many arguments to be sent to the map command (#673) `map` should only take a single arg that specifies the map to load. --- primedev/util/printmaps.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'primedev') diff --git a/primedev/util/printmaps.cpp b/primedev/util/printmaps.cpp index 906bed06..28325db9 100644 --- a/primedev/util/printmaps.cpp +++ b/primedev/util/printmaps.cpp @@ -200,7 +200,13 @@ AUTOHOOK(Host_Map_f, engine.dll + 0x15B340, void, __fastcall, (const CCommand& a { RefreshMapList(); - if (args.ArgC() > 1 && + if (args.ArgC() > 2) + { + spdlog::warn("Map load failed: too many arguments provided"); + return; + } + else if ( + args.ArgC() == 2 && std::find_if(vMapList.begin(), vMapList.end(), [&](MapVPKInfo map) -> bool { return map.name == args.Arg(1); }) == vMapList.end()) { spdlog::warn("Map load failed: {} not found or invalid", args.Arg(1)); -- cgit v1.2.3 From 4b0726d97788edff5d83476cb52057f409d623af Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:01:32 +0000 Subject: Update silver-bun to `72c74b4` (#664) Bumps the vendored silver-bun library to the newest commit in upstream Co-authored-by: F1F7Y Co-authored-by: IcePixelx <41352111+IcePixelx@users.noreply.github.com> --- primedev/Northstar.cmake | 4 +- primedev/cmake/Findsilver-bun.cmake | 9 + primedev/core/hooks.h | 1 - primedev/core/memory.cpp | 347 --------------- primedev/core/memory.h | 90 ---- primedev/core/tier0.cpp | 6 +- primedev/core/tier1.cpp | 2 +- primedev/core/tier1.h | 2 +- primedev/dedicated/dedicated.cpp | 4 +- primedev/pch.h | 3 +- primedev/server/alltalk.cpp | 2 +- primedev/shared/exploit_fixes/exploitfixes.cpp | 8 +- .../exploit_fixes/exploitfixes_utf8parser.cpp | 6 +- primedev/shared/keyvalues.cpp | 6 +- primedev/shared/maxplayers.cpp | 4 +- primedev/thirdparty/silver-bun/CMakeLists.txt | 9 + primedev/thirdparty/silver-bun/memaddr.cpp | 368 ++++++++++++++++ primedev/thirdparty/silver-bun/memaddr.h | 154 +++++++ primedev/thirdparty/silver-bun/module.cpp | 474 +++++++++++++++++++++ primedev/thirdparty/silver-bun/module.h | 76 ++++ primedev/thirdparty/silver-bun/utils.cpp | 127 ++++++ primedev/thirdparty/silver-bun/utils.h | 18 + 22 files changed, 1259 insertions(+), 461 deletions(-) create mode 100644 primedev/cmake/Findsilver-bun.cmake delete mode 100644 primedev/core/memory.cpp delete mode 100644 primedev/core/memory.h create mode 100644 primedev/thirdparty/silver-bun/CMakeLists.txt create mode 100644 primedev/thirdparty/silver-bun/memaddr.cpp create mode 100644 primedev/thirdparty/silver-bun/memaddr.h create mode 100644 primedev/thirdparty/silver-bun/module.cpp create mode 100644 primedev/thirdparty/silver-bun/module.h create mode 100644 primedev/thirdparty/silver-bun/utils.cpp create mode 100644 primedev/thirdparty/silver-bun/utils.h (limited to 'primedev') diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index 6b9c2380..aef630c8 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -3,6 +3,7 @@ find_package(minhook REQUIRED) find_package(libcurl REQUIRED) find_package(minizip REQUIRED) +find_package(silver-bun REQUIRED) add_library( NorthstarDLL SHARED @@ -50,8 +51,6 @@ add_library( "core/macros.h" "core/memalloc.cpp" "core/memalloc.h" - "core/memory.cpp" - "core/memory.h" "core/sourceinterface.cpp" "core/sourceinterface.h" "core/tier0.cpp" @@ -172,6 +171,7 @@ target_link_libraries( PRIVATE minhook libcurl minizip + silver-bun WS2_32.lib Crypt32.lib Cryptui.lib diff --git a/primedev/cmake/Findsilver-bun.cmake b/primedev/cmake/Findsilver-bun.cmake new file mode 100644 index 00000000..fa445aba --- /dev/null +++ b/primedev/cmake/Findsilver-bun.cmake @@ -0,0 +1,9 @@ +if(NOT silver-bun_FOUND) + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/silver-bun) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/silver-bun silver-bun) + set(silver-bun_FOUND + 1 + PARENT_SCOPE + ) +endif() diff --git a/primedev/core/hooks.h b/primedev/core/hooks.h index 7c1b001c..f842afbb 100644 --- a/primedev/core/hooks.h +++ b/primedev/core/hooks.h @@ -1,5 +1,4 @@ #pragma once -#include "memory.h" #include #include diff --git a/primedev/core/memory.cpp b/primedev/core/memory.cpp deleted file mode 100644 index 41110aee..00000000 --- a/primedev/core/memory.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "memory.h" - -CMemoryAddress::CMemoryAddress() : m_nAddress(0) {} -CMemoryAddress::CMemoryAddress(const uintptr_t nAddress) : m_nAddress(nAddress) {} -CMemoryAddress::CMemoryAddress(const void* pAddress) : m_nAddress(reinterpret_cast(pAddress)) {} - -// operators -CMemoryAddress::operator uintptr_t() const -{ - return m_nAddress; -} - -CMemoryAddress::operator void*() const -{ - return reinterpret_cast(m_nAddress); -} - -CMemoryAddress::operator bool() const -{ - return m_nAddress != 0; -} - -bool CMemoryAddress::operator==(const CMemoryAddress& other) const -{ - return m_nAddress == other.m_nAddress; -} - -bool CMemoryAddress::operator!=(const CMemoryAddress& other) const -{ - return m_nAddress != other.m_nAddress; -} - -bool CMemoryAddress::operator==(const uintptr_t& addr) const -{ - return m_nAddress == addr; -} - -bool CMemoryAddress::operator!=(const uintptr_t& addr) const -{ - return m_nAddress != addr; -} - -CMemoryAddress CMemoryAddress::operator+(const CMemoryAddress& other) const -{ - return Offset(other.m_nAddress); -} - -CMemoryAddress CMemoryAddress::operator-(const CMemoryAddress& other) const -{ - return CMemoryAddress(m_nAddress - other.m_nAddress); -} - -CMemoryAddress CMemoryAddress::operator+(const uintptr_t& addr) const -{ - return Offset(addr); -} - -CMemoryAddress CMemoryAddress::operator-(const uintptr_t& addr) const -{ - return CMemoryAddress(m_nAddress - addr); -} - -CMemoryAddress CMemoryAddress::operator*() const -{ - return Deref(); -} - -// traversal -CMemoryAddress CMemoryAddress::Offset(const uintptr_t nOffset) const -{ - return CMemoryAddress(m_nAddress + nOffset); -} - -CMemoryAddress CMemoryAddress::Deref(const int nNumDerefs) const -{ - uintptr_t ret = m_nAddress; - for (int i = 0; i < nNumDerefs; i++) - ret = *reinterpret_cast(ret); - - return CMemoryAddress(ret); -} - -// patching -void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize) -{ - if (nSize) - WriteProcessMemory(GetCurrentProcess(), reinterpret_cast(m_nAddress), pBytes, nSize, NULL); -} - -void CMemoryAddress::Patch(const std::initializer_list bytes) -{ - uint8_t* pBytes = new uint8_t[bytes.size()]; - - int i = 0; - for (const uint8_t& byte : bytes) - pBytes[i++] = byte; - - Patch(pBytes, bytes.size()); - delete[] pBytes; -} - -inline std::vector HexBytesToString(const char* pHexString) -{ - std::vector ret; - - size_t size = strlen(pHexString); - for (int i = 0; i < size; i++) - { - // If this is a space character, ignore it - if (isspace(pHexString[i])) - continue; - - if (i < size - 1) - { - BYTE result = 0; - for (int j = 0; j < 2; j++) - { - int val = 0; - char c = *(pHexString + i + j); - if (c >= 'a') - { - val = c - 'a' + 0xA; - } - else if (c >= 'A') - { - val = c - 'A' + 0xA; - } - else if (isdigit(c)) - { - val = c - '0'; - } - else - { - assert_msg(false, "Failed to parse invalid hex string."); - val = -1; - } - - result += (j == 0) ? val * 16 : val; - } - ret.push_back(result); - } - - i++; - } - - return ret; -} - -void CMemoryAddress::Patch(const char* pBytes) -{ - std::vector vBytes = HexBytesToString(pBytes); - Patch(vBytes.data(), vBytes.size()); -} - -void CMemoryAddress::NOP(const size_t nSize) -{ - uint8_t* pBytes = new uint8_t[nSize]; - - memset(pBytes, 0x90, nSize); - Patch(pBytes, nSize); - - delete[] pBytes; -} - -bool CMemoryAddress::IsMemoryReadable(const size_t nSize) -{ - static SYSTEM_INFO sysInfo; - if (!sysInfo.dwPageSize) - GetSystemInfo(&sysInfo); - - MEMORY_BASIC_INFORMATION memInfo; - if (!VirtualQuery(reinterpret_cast(m_nAddress), &memInfo, sizeof(memInfo))) - return false; - - return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS); -} - -CModule::CModule(const HMODULE pModule) -{ - MODULEINFO mInfo {0}; - - if (pModule && pModule != INVALID_HANDLE_VALUE) - GetModuleInformation(GetCurrentProcess(), pModule, &mInfo, sizeof(MODULEINFO)); - - m_nModuleSize = static_cast(mInfo.SizeOfImage); - m_pModuleBase = reinterpret_cast(mInfo.lpBaseOfDll); - m_nAddress = m_pModuleBase; - - if (!m_nModuleSize || !m_pModuleBase) - return; - - m_pDOSHeader = reinterpret_cast(m_pModuleBase); - m_pNTHeaders = reinterpret_cast(m_pModuleBase + m_pDOSHeader->e_lfanew); - - const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(m_pNTHeaders); // Get first image section. - - for (WORD i = 0; i < m_pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections. - { - const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section. - - ModuleSections_t moduleSection = ModuleSections_t( - std::string(reinterpret_cast(hCurrentSection.Name)), - static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), - hCurrentSection.SizeOfRawData); - - if (!strcmp((const char*)hCurrentSection.Name, ".text")) - m_ExecutableCode = moduleSection; - else if (!strcmp((const char*)hCurrentSection.Name, ".pdata")) - m_ExceptionTable = moduleSection; - else if (!strcmp((const char*)hCurrentSection.Name, ".data")) - m_RunTimeData = moduleSection; - else if (!strcmp((const char*)hCurrentSection.Name, ".rdata")) - m_ReadOnlyData = moduleSection; - - m_vModuleSections.push_back(moduleSection); // Push back a struct with the section data. - } -} - -CModule::CModule(const char* pModuleName) : CModule(GetModuleHandleA(pModuleName)) {} - -CMemoryAddress CModule::GetExport(const char* pExportName) -{ - return CMemoryAddress(reinterpret_cast(GetProcAddress(reinterpret_cast(m_nAddress), pExportName))); -} - -CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask) -{ - if (!m_ExecutableCode.IsSectionValid()) - return CMemoryAddress(); - - uint64_t nBase = static_cast(m_ExecutableCode.m_pSectionBase); - uint64_t nSize = static_cast(m_ExecutableCode.m_nSectionSize); - - const uint8_t* pData = reinterpret_cast(nBase); - const uint8_t* pEnd = pData + static_cast(nSize) - strlen(pMask); - - int nMasks[64]; // 64*16 = enough masks for 1024 bytes. - int iNumMasks = static_cast(ceil(static_cast(strlen(pMask)) / 16.f)); - - memset(nMasks, '\0', iNumMasks * sizeof(int)); - for (intptr_t i = 0; i < iNumMasks; ++i) - { - for (intptr_t j = strnlen(pMask + i * 16, 16) - 1; j >= 0; --j) - { - if (pMask[i * 16 + j] == 'x') - { - _bittestandset(reinterpret_cast(&nMasks[i]), j); - } - } - } - __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(pPattern)); - __m128i xmm2, xmm3, msks; - for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) - { - if (pPattern[0] == pData[0]) - { - xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); - msks = _mm_cmpeq_epi8(xmm1, xmm2); - if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) - { - for (uintptr_t i = 1; i < static_cast(iNumMasks); ++i) - { - xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); - xmm3 = _mm_loadu_si128(reinterpret_cast((pPattern + i * 16))); - msks = _mm_cmpeq_epi8(xmm2, xmm3); - if ((_mm_movemask_epi8(msks) & nMasks[i]) == nMasks[i]) - { - if ((i + 1) == iNumMasks) - { - return CMemoryAddress(const_cast(pData)); - } - } - else - goto CONTINUE; - } - - return CMemoryAddress((&*(const_cast(pData)))); - } - } - - CONTINUE:; - } - - return CMemoryAddress(); -} - -inline std::pair, std::string> MaskedBytesFromPattern(const char* pPatternString) -{ - std::vector vRet; - std::string sMask; - - size_t size = strlen(pPatternString); - for (int i = 0; i < size; i++) - { - // If this is a space character, ignore it - if (isspace(pPatternString[i])) - continue; - - if (pPatternString[i] == '?') - { - // Add a wildcard - vRet.push_back(0); - sMask.append("?"); - } - else if (i < size - 1) - { - BYTE result = 0; - for (int j = 0; j < 2; j++) - { - int val = 0; - char c = *(pPatternString + i + j); - if (c >= 'a') - { - val = c - 'a' + 0xA; - } - else if (c >= 'A') - { - val = c - 'A' + 0xA; - } - else if (isdigit(c)) - { - val = c - '0'; - } - else - { - assert_msg(false, "Failed to parse invalid pattern string."); - val = -1; - } - - result += (j == 0) ? val * 16 : val; - } - - vRet.push_back(result); - sMask.append("x"); - } - - i++; - } - - return std::make_pair(vRet, sMask); -} - -CMemoryAddress CModule::FindPattern(const char* pPattern) -{ - const auto pattern = MaskedBytesFromPattern(pPattern); - return FindPattern(pattern.first.data(), pattern.second.c_str()); -} diff --git a/primedev/core/memory.h b/primedev/core/memory.h deleted file mode 100644 index a978963e..00000000 --- a/primedev/core/memory.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -class CMemoryAddress -{ -public: - uintptr_t m_nAddress; - -public: - CMemoryAddress(); - CMemoryAddress(const uintptr_t nAddress); - CMemoryAddress(const void* pAddress); - - // operators - operator uintptr_t() const; - operator void*() const; - operator bool() const; - - bool operator==(const CMemoryAddress& other) const; - bool operator!=(const CMemoryAddress& other) const; - bool operator==(const uintptr_t& addr) const; - bool operator!=(const uintptr_t& addr) const; - - CMemoryAddress operator+(const CMemoryAddress& other) const; - CMemoryAddress operator-(const CMemoryAddress& other) const; - CMemoryAddress operator+(const uintptr_t& other) const; - CMemoryAddress operator-(const uintptr_t& other) const; - CMemoryAddress operator*() const; - - template T RCast() - { - return reinterpret_cast(m_nAddress); - } - - // traversal - CMemoryAddress Offset(const uintptr_t nOffset) const; - CMemoryAddress Deref(const int nNumDerefs = 1) const; - - // patching - void Patch(const uint8_t* pBytes, const size_t nSize); - void Patch(const std::initializer_list bytes); - void Patch(const char* pBytes); - void NOP(const size_t nSize); - - bool IsMemoryReadable(const size_t nSize); -}; - -// based on https://github.com/Mauler125/r5sdk/blob/master/r5dev/public/include/module.h -class CModule : public CMemoryAddress -{ -public: - struct ModuleSections_t - { - ModuleSections_t(void) = default; - ModuleSections_t(const std::string& svSectionName, uintptr_t pSectionBase, size_t nSectionSize) - : m_svSectionName(svSectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) - { - } - - bool IsSectionValid(void) const - { - return m_nSectionSize != 0; - } - - std::string m_svSectionName; // Name of section. - uintptr_t m_pSectionBase {}; // Start address of section. - size_t m_nSectionSize {}; // Size of section. - }; - - ModuleSections_t m_ExecutableCode; - ModuleSections_t m_ExceptionTable; - ModuleSections_t m_RunTimeData; - ModuleSections_t m_ReadOnlyData; - -private: - std::string m_svModuleName; - uintptr_t m_pModuleBase {}; - DWORD m_nModuleSize {}; - IMAGE_NT_HEADERS64* m_pNTHeaders = nullptr; - IMAGE_DOS_HEADER* m_pDOSHeader = nullptr; - std::vector m_vModuleSections; - -public: - CModule() = delete; // no default, we need a module name - CModule(const HMODULE pModule); - CModule(const char* pModuleName); - - CMemoryAddress GetExport(const char* pExportName); - CMemoryAddress FindPattern(const uint8_t* pPattern, const char* pMask); - CMemoryAddress FindPattern(const char* pPattern); -}; diff --git a/primedev/core/tier0.cpp b/primedev/core/tier0.cpp index 1f59722c..dd5ac245 100644 --- a/primedev/core/tier0.cpp +++ b/primedev/core/tier0.cpp @@ -24,7 +24,7 @@ ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module)) TryCreateGlobalMemAlloc(); // setup tier0 funcs - CommandLine = module.GetExport("CommandLine").RCast(); - Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast(); - ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast(); + CommandLine = module.GetExportedFunction("CommandLine").RCast(); + Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast(); + ThreadInServerFrameThread = module.GetExportedFunction("ThreadInServerFrameThread").RCast(); } diff --git a/primedev/core/tier1.cpp b/primedev/core/tier1.cpp index a2995496..f857fdba 100644 --- a/primedev/core/tier1.cpp +++ b/primedev/core/tier1.cpp @@ -3,7 +3,7 @@ // Note: this file is tier1/interface.cpp in primedev, but given that tier0 is yet to be split // I am following the existing "pattern" and putting this here -CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) +CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) { HMODULE hModule = GetModuleHandleA(svModuleName.c_str()); diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h index 5be58274..d162e7c8 100644 --- a/primedev/core/tier1.h +++ b/primedev/core/tier1.h @@ -9,4 +9,4 @@ typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); -CMemoryAddress Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); +CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); diff --git a/primedev/dedicated/dedicated.cpp b/primedev/dedicated/dedicated.cpp index df6e5787..5c679b76 100644 --- a/primedev/dedicated/dedicated.cpp +++ b/primedev/dedicated/dedicated.cpp @@ -135,7 +135,7 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul { // CModAppSystemGroup::Create // force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment - CMemoryAddress base = module.Offset(0x1C4EBD); + CMemory base = module.Offset(0x1C4EBD); // cmp => mov base.Offset(1).Patch("C6 87"); @@ -262,7 +262,7 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module)) // disable origin on dedicated // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without // an origin id as a client for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server - module.GetExport("Tier0_InitOrigin").Patch("C3"); + module.GetExportedFunction("Tier0_InitOrigin").Patch("C3"); } // clang-format off diff --git a/primedev/pch.h b/primedev/pch.h index 8a03b857..141995c7 100644 --- a/primedev/pch.h +++ b/primedev/pch.h @@ -37,7 +37,8 @@ typedef void (*callable_v)(void* v); #include "logging/logging.h" #include "MinHook.h" #include "curl/curl.h" +#include "silver-bun/module.h" +#include "silver-bun/memaddr.h" #include "core/hooks.h" -#include "core/memory.h" #endif diff --git a/primedev/server/alltalk.cpp b/primedev/server/alltalk.cpp index 74119309..4eb5aef7 100644 --- a/primedev/server/alltalk.cpp +++ b/primedev/server/alltalk.cpp @@ -15,7 +15,7 @@ size_t __fastcall ShouldAllowAlltalk() ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module)) { // replace strcmp function called in CClient::ProcessVoiceData with our own code that calls ShouldAllowAllTalk - CMemoryAddress base = module.Offset(0x1085FA); + CMemory base = module.Offset(0x1085FA); base.Patch("48 B8"); // mov rax, 64 bit int // (uint8_t*)&ShouldAllowAlltalk doesn't work for some reason? need to make it a uint64 first diff --git a/primedev/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp index cade4084..7850f7b0 100644 --- a/primedev/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -103,7 +103,7 @@ bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 auto entry = msg->m_ConVars + i; // Safety check for memory access - if (CMemoryAddress(entry).IsMemoryReadable(sizeof(*entry))) + if (CMemory(entry).IsMemoryReadable(sizeof(*entry))) { // Find null terminators bool nameValid = false, valValid = false; @@ -421,9 +421,9 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module)) // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails { - CMemoryAddress writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).m_nAddress); + CMemory writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).GetPtr()); - CMemoryAddress addr = module.Offset(0x234ED2); + CMemory addr = module.Offset(0x234ED2); addr.Patch("C7 05"); addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress)); @@ -451,7 +451,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerExploitFixes, ConVar, (CModule module)) // Prevent these from actually doing anything for (auto exportName : ANTITAMPER_EXPORTS) { - CMemoryAddress exportAddr = module.GetExport(exportName); + CMemory exportAddr = module.GetExportedFunction(exportName); if (exportAddr) { // Just return, none of them have any args or are userpurge diff --git a/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp b/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp index 3d97f750..d63ba38a 100644 --- a/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp @@ -67,7 +67,7 @@ bool __fastcall CheckUTF8Valid(INT64* a1, DWORD* a2, char* strData) { while (1) { - if (!CMemoryAddress(v4).IsMemoryReadable(1)) + if (!CMemory(v4).IsMemoryReadable(1)) return false; // INVALID v11 = *v4++; // crash potential @@ -174,7 +174,7 @@ AUTOHOOK(Rson_ParseUTF8, engine.dll + 0xEF670, bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A // clang-format on { - static void* targetRetAddr = CModule("engine.dll").FindPattern("84 C0 75 2C 49 8B 16"); + static void* targetRetAddr = CModule("engine.dll").FindPatternSIMD("84 C0 75 2C 49 8B 16"); // only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues void* pReturnAddress = @@ -195,5 +195,5 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes_UTF8Parser, (CModule module)) { AUTOHOOK_DISPATCH() - sub_F1320 = module.FindPattern("83 F9 7F 77 08 88 0A").RCast(); + sub_F1320 = module.FindPatternSIMD("83 F9 7F 77 08 88 0A").RCast(); } diff --git a/primedev/shared/keyvalues.cpp b/primedev/shared/keyvalues.cpp index 88753723..36f891eb 100644 --- a/primedev/shared/keyvalues.cpp +++ b/primedev/shared/keyvalues.cpp @@ -1289,9 +1289,9 @@ KeyValues* KeyValues::MakeCopy(void) const ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module)) { - V_UTF8ToUnicode = module.GetExport("V_UTF8ToUnicode").RCast(); - V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast(); - KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast(); + V_UTF8ToUnicode = module.GetExportedFunction("V_UTF8ToUnicode").RCast(); + V_UnicodeToUTF8 = module.GetExportedFunction("V_UnicodeToUTF8").RCast(); + KeyValuesSystem = module.GetExportedFunction("KeyValuesSystem").RCast(); } AUTOHOOK_INIT() diff --git a/primedev/shared/maxplayers.cpp b/primedev/shared/maxplayers.cpp index 711193d4..69d625f2 100644 --- a/primedev/shared/maxplayers.cpp +++ b/primedev/shared/maxplayers.cpp @@ -60,7 +60,7 @@ int GetMaxPlayers() return 32; } -template void ChangeOffset(CMemoryAddress addr, unsigned int offset) +template void ChangeOffset(CMemory addr, unsigned int offset) { addr.Patch((BYTE*)&offset, sizeof(T)); } @@ -296,7 +296,7 @@ ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module)) AUTOHOOK_DISPATCH_MODULE(server.dll) // get required data - serverBase = (HMODULE)module.m_nAddress; + serverBase = (HMODULE)module.GetModuleBase(); RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax")); // patch max players amount diff --git a/primedev/thirdparty/silver-bun/CMakeLists.txt b/primedev/thirdparty/silver-bun/CMakeLists.txt new file mode 100644 index 00000000..c39dd8bc --- /dev/null +++ b/primedev/thirdparty/silver-bun/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library( + silver-bun STATIC + "memaddr.cpp" + "memaddr.h" + "module.cpp" + "module.h" + "utils.cpp" + "utils.h" + ) diff --git a/primedev/thirdparty/silver-bun/memaddr.cpp b/primedev/thirdparty/silver-bun/memaddr.cpp new file mode 100644 index 00000000..929d1682 --- /dev/null +++ b/primedev/thirdparty/silver-bun/memaddr.cpp @@ -0,0 +1,368 @@ +//===========================================================================// +// +// Purpose: Implementation of the CMemory class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// + +#include "memaddr.h" +#include "utils.h" + +//----------------------------------------------------------------------------- +// Purpose: check array of opcodes starting from current address +// Input : &vOpcodeArray - +// Output : true if equal, false otherwise +//----------------------------------------------------------------------------- +bool CMemory::CheckOpCodes(const std::vector& vOpcodeArray) const +{ + uintptr_t ref = ptr; + + // Loop forward in the ptr class member. + for (auto [byteAtCurrentAddress, i] = std::tuple{ uint8_t(), (size_t)0 }; i < vOpcodeArray.size(); i++, ref++) + { + byteAtCurrentAddress = *reinterpret_cast(ref); + + // If byte at ptr doesn't equal in the byte array return false. + if (byteAtCurrentAddress != vOpcodeArray[i]) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Checx if memory is readable +// Input : nSize - +//----------------------------------------------------------------------------- +bool CMemory::IsMemoryReadable(const size_t nSize) const +{ + static SYSTEM_INFO sysInfo; + if (!sysInfo.dwPageSize) + GetSystemInfo(&sysInfo); + + MEMORY_BASIC_INFORMATION memInfo; + if (!VirtualQuery(reinterpret_cast(GetPtr()), &memInfo, sizeof(memInfo))) + return false; + + return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS); +} + +//----------------------------------------------------------------------------- +// Purpose: patch size with nop opcodes +// Input : nSize - +//----------------------------------------------------------------------------- +void CMemory::NOP(const size_t nSize) const +{ + std::vector vOpcodeArray; + vOpcodeArray.resize(nSize); + memset(vOpcodeArray.data(), 0x90, nSize); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes +// Input : *pszOpcodes - +//----------------------------------------------------------------------------- +void CMemory::Patch(const char* pszOpcodes) const +{ + const std::vector vOpcodeArray = Utils::StringPatternToBytes(pszOpcodes); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes starting from current address +// Input : *pOpcodeArray - +// nSize - +//----------------------------------------------------------------------------- +void CMemory::Patch(const uint8_t* pOpcodeArray, const size_t nSize) const +{ + const std::vector vOpcodeArray(pOpcodeArray, pOpcodeArray + nSize * sizeof(uint8_t)); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes starting from current address +// Input : &vOpcodeArray - +//----------------------------------------------------------------------------- +void CMemory::Patch(const std::vector& vOpcodeArray) const +{ + DWORD oldProt = NULL; + + SIZE_T dwSize = vOpcodeArray.size(); + VirtualProtect(reinterpret_cast(ptr), dwSize, PAGE_EXECUTE_READWRITE, &oldProt); // Patch page to be able to read and write to it. + + for (size_t i = 0; i < vOpcodeArray.size(); i++) + { + *reinterpret_cast(ptr + i) = vOpcodeArray[i]; // Write opcodes to Address. + } + + dwSize = vOpcodeArray.size(); + VirtualProtect(reinterpret_cast(ptr), dwSize, oldProt, &oldProt); // Restore protection. +} + +//----------------------------------------------------------------------------- +// Purpose: patch string constant at current address +// Input : *szString - +//----------------------------------------------------------------------------- +void CMemory::PatchString(const char* szString) const +{ + DWORD oldProt = NULL; + SIZE_T dwSize = strlen(szString); + + VirtualProtect(reinterpret_cast(ptr), dwSize, PAGE_EXECUTE_READWRITE, &oldProt); // Patch page to be able to read and write to it. + + for (size_t i = 0; i < dwSize; i++) + { + *reinterpret_cast(ptr + i) = szString[i]; // Write string to Address. + } + + VirtualProtect(reinterpret_cast(ptr), dwSize, oldProt, &oldProt); // Restore protection. +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory +// Input : *szPattern - +// searchDirect - +// opCodesToScan - +// occurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FindPattern(const char* szPattern, const Direction searchDirect, const int opCodesToScan, const ptrdiff_t occurrence) const +{ + uint8_t* pScanBytes = reinterpret_cast(ptr); // Get the base of the module. + + const std::vector PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array. + const std::pair bytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. + ptrdiff_t occurrences = 0; + + for (long i = 01; i < opCodesToScan + bytesInfo.first; i++) + { + bool bFound = true; + int nMemOffset = searchDirect == Direction::DOWN ? i : -i; + + for (DWORD j = 0ul; j < bytesInfo.first; j++) + { + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + uint8_t currentByte = *(pScanBytes + nMemOffset + j); + _mm_prefetch(reinterpret_cast(static_cast(currentByte + nMemOffset + 64)), _MM_HINT_T0); // precache some data in L1. + if (currentByte != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + occurrences++; + if (occurrence == occurrences) + { + return CMemory(&*(pScanBytes + nMemOffset)); + } + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory starting from current address +// Input : *szPattern - +// searchDirect - +// opCodesToScan - +// occurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FindPatternSelf(const char* szPattern, const Direction searchDirect, const int opCodesToScan, const ptrdiff_t occurrence) +{ + uint8_t* pScanBytes = reinterpret_cast(ptr); // Get the base of the module. + + const std::vector PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array. + const std::pair bytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. + ptrdiff_t occurrences = 0; + + for (long i = 01; i < opCodesToScan + bytesInfo.first; i++) + { + bool bFound = true; + int nMemOffset = searchDirect == Direction::DOWN ? i : -i; + + for (DWORD j = 0ul; j < bytesInfo.first; j++) + { + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + uint8_t currentByte = *(pScanBytes + nMemOffset + j); + _mm_prefetch(reinterpret_cast(static_cast(currentByte + nMemOffset + 64)), _MM_HINT_T0); // precache some data in L1. + if (currentByte != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + occurrences++; + if (occurrence == occurrences) + { + ptr = uintptr_t(&*(pScanBytes + nMemOffset)); + return *this; + } + } + } + + ptr = uintptr_t(); + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddress wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCall(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) const +{ + return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddressSelf wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCallSelf(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) +{ + return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddress(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) const +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + return CMemory(nextInstruction + relativeAddress); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset from current address +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddressSelf(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + ptr = nextInstruction + relativeAddress; + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: resolve all 'call' references to ptr +// (This is very slow only use for mass patching.) +// Input : sectionBase - +// sectionSize - +// Output : std::vector +//----------------------------------------------------------------------------- +std::vector CMemory::FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize) +{ + std::vector referencesInfo = {}; + + uint8_t* pTextStart = reinterpret_cast(sectionBase); + for (size_t i = 0ull; i < sectionSize - 0x5; i++, _mm_prefetch(reinterpret_cast(pTextStart + 64), _MM_HINT_NTA)) + { + if (pTextStart[i] == 0xE8) + { + CMemory memAddr = CMemory(&pTextStart[i]); + if (!memAddr.Offset(0x1).CheckOpCodes({ 0x00, 0x00, 0x00, 0x00 })) // Check if its not a dynamic resolved call. + { + if (memAddr.FollowNearCall() == *this) + referencesInfo.push_back(memAddr); + } + } + } + + return referencesInfo; +} + +//----------------------------------------------------------------------------- +// Purpose: patch virtual method to point to a user set function +// Input : virtualTable - +// pHookMethod - +// methodIndex - +// ppOriginalMethod - +// Output : void** via ppOriginalMethod +//----------------------------------------------------------------------------- +void CMemory::HookVirtualMethod(const uintptr_t virtualTable, const void* pHookMethod, const ptrdiff_t methodIndex, void** ppOriginalMethod) +{ + DWORD oldProt = NULL; + + // Calculate delta to next virtual method. + const uintptr_t virtualMethod = virtualTable + (methodIndex * sizeof(ptrdiff_t)); + + // Preserve original function. + const uintptr_t originalFunction = *reinterpret_cast(virtualMethod); + + // Set page for current virtual method to execute n read n write. + VirtualProtect(reinterpret_cast(virtualMethod), sizeof(virtualMethod), PAGE_EXECUTE_READWRITE, &oldProt); + + // Set virtual method to our hook. + *reinterpret_cast(virtualMethod) = reinterpret_cast(pHookMethod); + + // Restore original page. + VirtualProtect(reinterpret_cast(virtualMethod), sizeof(virtualMethod), oldProt, &oldProt); + + // Move original function into argument. + *ppOriginalMethod = reinterpret_cast(originalFunction); +} + +//----------------------------------------------------------------------------- +// Purpose: patch iat entry to point to a user set function +// Input : pImportedMethod - +// pHookMethod - +// ppOriginalMethod - +// Output : void** via ppOriginalMethod +//----------------------------------------------------------------------------- +void CMemory::HookImportedFunction(const uintptr_t pImportedMethod, const void* pHookMethod, void** ppOriginalMethod) +{ + DWORD oldProt = NULL; + + // Preserve original function. + const uintptr_t originalFunction = *reinterpret_cast(pImportedMethod); + + // Set page for current iat entry to execute n read n write. + VirtualProtect(reinterpret_cast(pImportedMethod), sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt); + + // Set method to our hook. + *reinterpret_cast(pImportedMethod) = reinterpret_cast(pHookMethod); + + // Restore original page. + VirtualProtect(reinterpret_cast(pImportedMethod), sizeof(void*), oldProt, &oldProt); + + // Move original function into argument. + *ppOriginalMethod = reinterpret_cast(originalFunction); +} diff --git a/primedev/thirdparty/silver-bun/memaddr.h b/primedev/thirdparty/silver-bun/memaddr.h new file mode 100644 index 00000000..3060c0cd --- /dev/null +++ b/primedev/thirdparty/silver-bun/memaddr.h @@ -0,0 +1,154 @@ +//===========================================================================// +// +// Purpose: Implementation of the CMemory class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// +#pragma once + +#include +#include +#include +#include +#include + +class CMemory +{ +public: + enum class Direction : int + { + DOWN = 0, + UP, + }; + + CMemory(void) = default; + CMemory(const uintptr_t ptr) : ptr(ptr) {} + CMemory(const void* ptr) : ptr(uintptr_t(ptr)) {} + + inline operator uintptr_t(void) const + { + return ptr; + } + + inline operator void*(void) const + { + return reinterpret_cast(ptr); + } + + inline operator bool(void) const + { + return ptr != NULL; + } + + inline bool operator!= (const CMemory& addr) const + { + return ptr != addr.ptr; + } + + inline bool operator== (const CMemory& addr) const + { + return ptr == addr.ptr; + } + + inline bool operator== (const uintptr_t& addr) const + { + return ptr == addr; + } + + inline uintptr_t GetPtr(void) const + { + return ptr; + } + + template inline T GetValue(void) const + { + return *reinterpret_cast(ptr); + } + + template inline T GetVirtualFunctionIndex(void) const + { + return *reinterpret_cast(ptr) / 8; + } + + template inline T CCast(void) const + { + return (T)ptr; + } + + template inline T RCast(void) const + { + return reinterpret_cast(ptr); + } + + inline CMemory Offset(ptrdiff_t offset) const + { + return CMemory(ptr + offset); + } + + inline CMemory OffsetSelf(ptrdiff_t offset) + { + ptr += offset; + return *this; + } + + inline CMemory Deref(int deref = 1) const + { + uintptr_t reference = ptr; + + while (deref--) + { + if (reference) + reference = *reinterpret_cast(reference); + } + + return CMemory(reference); + } + + inline CMemory DerefSelf(int deref = 1) + { + while (deref--) + { + if (ptr) + ptr = *reinterpret_cast(ptr); + } + + return *this; + } + + inline CMemory WalkVTable(ptrdiff_t vfuncIndex) + { + uintptr_t reference = ptr + (sizeof(uintptr_t) * vfuncIndex); + return CMemory(reference); + } + + inline CMemory WalkVTableSelf(ptrdiff_t vfuncIndex) + { + ptr += (sizeof(uintptr_t) * vfuncIndex); + return *this; + } + + bool CheckOpCodes(const std::vector& vOpcodeArray) const; + bool IsMemoryReadable(const size_t nSize) const; + + void NOP(const size_t nSize) const; + void Patch(const char* pszOpcodes) const; + void Patch(const uint8_t* pOpcodeArray, const size_t nSize) const; + void Patch(const std::vector& vOpcodeArray) const; + void PatchString(const char* szString) const; + + CMemory FindPattern(const char* szPattern, const Direction searchDirect = Direction::DOWN, const int opCodesToScan = 512, const ptrdiff_t occurrence = 1) const; + CMemory FindPatternSelf(const char* szPattern, const Direction searchDirect = Direction::DOWN, const int opCodesToScan = 512, const ptrdiff_t occurrence = 1); + std::vector FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize); + + CMemory FollowNearCall(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5) const; + CMemory FollowNearCallSelf(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5); + CMemory ResolveRelativeAddress(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4) const; + CMemory ResolveRelativeAddressSelf(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4); + + static void HookVirtualMethod(const uintptr_t virtualTable, const void* pHookMethod, const ptrdiff_t methodIndex, void** ppOriginalMethod); + static void HookImportedFunction(const uintptr_t pImportedMethod, const void* pHookMethod, void** ppOriginalMethod); + +private: + uintptr_t ptr = 0; +}; diff --git a/primedev/thirdparty/silver-bun/module.cpp b/primedev/thirdparty/silver-bun/module.cpp new file mode 100644 index 00000000..4b9330e0 --- /dev/null +++ b/primedev/thirdparty/silver-bun/module.cpp @@ -0,0 +1,474 @@ +//===========================================================================// +// +// Purpose: Implementation of the CModule class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// + +#include "module.h" +#include "utils.h" + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : *szModuleName - +//----------------------------------------------------------------------------- +CModule::CModule(HMODULE hModule) +{ + m_pModuleBase = reinterpret_cast(hModule); + + CHAR szModuleName[MAX_PATH]; + DWORD dwSize = GetModuleFileNameA(hModule, szModuleName, sizeof(szModuleName)); + m_ModuleName = strrchr(szModuleName, '\\') + 1; + + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : *szModuleName - +//----------------------------------------------------------------------------- +CModule::CModule(const char* szModuleName) +{ + m_pModuleBase = reinterpret_cast(GetModuleHandleA(szModuleName)); + m_ModuleName = szModuleName; + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: initializes module descriptors +//----------------------------------------------------------------------------- +void CModule::Init() +{ + m_pDOSHeader = reinterpret_cast(m_pModuleBase); + m_pNTHeaders = reinterpret_cast(m_pModuleBase + m_pDOSHeader->e_lfanew); + m_nModuleSize = static_cast(m_pNTHeaders->OptionalHeader.SizeOfImage); + + const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(m_pNTHeaders); // Get first image section. + + for (WORD i = 0; i < m_pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections. + { + const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section. + m_ModuleSections.push_back(ModuleSections_t(reinterpret_cast(hCurrentSection.Name), + static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData)); // Push back a struct with the section data. + } +} + +//----------------------------------------------------------------------------- +// Purpose: initializes the default executable segments +//----------------------------------------------------------------------------- +void CModule::LoadSections() +{ + m_ExecutableCode = GetSectionByName(".text"); + m_ExceptionTable = GetSectionByName(".pdata"); + m_RunTimeData = GetSectionByName(".data"); + m_ReadOnlyData = GetSectionByName(".rdata"); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets memory at relative offset +// Input : nOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::Offset(const uintptr_t nOffset) const +{ + return CMemory(m_pModuleBase + nOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory using SIMD instructions +// Input : *pPattern - +// *szMask - +// *moduleSection - +// nOccurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const uint8_t* pPattern, const char* szMask, + const ModuleSections_t* moduleSection, const size_t nOccurrence) const +{ + if (!m_ExecutableCode.IsSectionValid()) + return CMemory(); + + const bool bSectionValid = moduleSection ? moduleSection->IsSectionValid() : false; + + const uintptr_t nBase = bSectionValid ? moduleSection->m_pSectionBase : m_ExecutableCode.m_pSectionBase; + const uintptr_t nSize = bSectionValid ? moduleSection->m_nSectionSize : m_ExecutableCode.m_nSectionSize; + + const size_t nMaskLen = strlen(szMask); + const uint8_t* pData = reinterpret_cast(nBase); + const uint8_t* pEnd = pData + nSize - nMaskLen; + + size_t nOccurrenceCount = 0; + int nMasks[64]; // 64*16 = enough masks for 1024 bytes. + const int iNumMasks = static_cast(ceil(static_cast(nMaskLen) / 16.f)); + + memset(nMasks, '\0', iNumMasks * sizeof(int)); + for (intptr_t i = 0; i < iNumMasks; ++i) + { + for (intptr_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + _bittestandset(reinterpret_cast(&nMasks[i]), static_cast(j)); + } + } + } + const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(pPattern)); + __m128i xmm2, xmm3, msks; + for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) + { + if (pPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) + { + for (uintptr_t i = 1; i < static_cast(iNumMasks); ++i) + { + xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); + xmm3 = _mm_loadu_si128(reinterpret_cast((pPattern + i * 16))); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & nMasks[i]) == nMasks[i]) + { + if ((i + 1) == iNumMasks) + { + if (nOccurrenceCount == nOccurrence) + { + return static_cast(const_cast(pData)); + } + nOccurrenceCount++; + } + } + else + { + goto cont; + } + } + if (nOccurrenceCount == nOccurrence) + { + return static_cast((&*(const_cast(pData)))); + } + nOccurrenceCount++; + } + }cont:; + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find a string pattern in process memory using SIMD instructions +// Input : *szPattern - +// *moduleSection - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const char* szPattern, const ModuleSections_t* moduleSection) const +{ + const std::pair, std::string> patternInfo = Utils::PatternToMaskedBytes(szPattern); + return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str(), moduleSection); +} + +//----------------------------------------------------------------------------- +// Purpose: find address of reference to string constant in executable memory +// Input : *szString - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindString(const char* szString, const ptrdiff_t nOccurrence, bool bNullTerminator) const +{ + if (!m_ExecutableCode.IsSectionValid()) + return CMemory(); + + const CMemory stringAddress = FindStringReadOnly(szString, bNullTerminator); // Get Address for the string in the .rdata section. + + if (!stringAddress) + return CMemory(); + + uint8_t* pLatestOccurrence = nullptr; + uint8_t* pTextStart = reinterpret_cast(m_ExecutableCode.m_pSectionBase); // Get the start of the .text section. + ptrdiff_t dOccurrencesFound = 0; + CMemory resultAddress; + + for (size_t i = 0ull; i < m_ExecutableCode.m_nSectionSize - 0x5; i++) + { + byte byte = pTextStart[i]; + if (byte == 0x8D) // 0x8D = LEA + { + const CMemory skipOpCode = CMemory(reinterpret_cast(&pTextStart[i])).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and the register. + const int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative Address + const uintptr_t nextInstruction = skipOpCode.Offset(0x4).GetPtr(); // Get location of next instruction. + const CMemory potentialLocation = CMemory(nextInstruction + relativeAddress); // Get potential string location. + + if (potentialLocation == stringAddress) + { + dOccurrencesFound++; + if (nOccurrence == dOccurrencesFound) + { + return CMemory(&pTextStart[i]); + } + + pLatestOccurrence = &pTextStart[i]; // Stash latest occurrence. + } + } + } + + return CMemory(pLatestOccurrence); +} + +//----------------------------------------------------------------------------- +// Purpose: find address of input string constant in read only memory +// Input : *szString - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindStringReadOnly(const char* szString, bool bNullTerminator) const +{ + if (!m_ReadOnlyData.IsSectionValid()) + return CMemory(); + + const std::vector vBytes = Utils::StringToBytes(szString, bNullTerminator); // Convert our string to a byte array. + const std::pair bytesInfo = std::make_pair(vBytes.size(), vBytes.data()); // Get the size and data of our bytes. + + const uint8_t* pBase = reinterpret_cast(m_ReadOnlyData.m_pSectionBase); // Get start of .rdata section. + + for (size_t i = 0ull; i < m_ReadOnlyData.m_nSectionSize - bytesInfo.first; i++) + { + bool bFound = true; + + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + for (size_t j = 0ull; j < bytesInfo.first; j++) + { + if (pBase[i + j] != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + return CMemory(&pBase[i]); + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find 'free' page in r/w/x sections +// Input : nSize - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindFreeDataPage(const size_t nSize) const +{ + auto checkDataSection = [](const void* address, const std::size_t size) + { + MEMORY_BASIC_INFORMATION membInfo = { 0 }; + + VirtualQuery(address, &membInfo, sizeof(membInfo)); + + if (membInfo.AllocationBase && membInfo.BaseAddress && membInfo.State == MEM_COMMIT && !(membInfo.Protect & PAGE_GUARD) && membInfo.Protect != PAGE_NOACCESS) + { + if ((membInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)) && membInfo.RegionSize >= size) + { + return ((membInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)) && membInfo.RegionSize >= size) ? true : false; + } + } + return false; + }; + + // This is very unstable, this doesn't check for the actual 'page' sizes. + // Also can be optimized to search per 'section'. + const uintptr_t endOfModule = m_pModuleBase + m_pNTHeaders->OptionalHeader.SizeOfImage - sizeof(uintptr_t); + for (uintptr_t currAddr = endOfModule; m_pModuleBase < currAddr; currAddr -= sizeof(uintptr_t)) + { + if (*reinterpret_cast(currAddr) == 0 && checkDataSection(reinterpret_cast(currAddr), nSize)) + { + bool bIsGoodPage = true; + uint32_t nPageCount = 0; + + for (; nPageCount < nSize && bIsGoodPage; nPageCount += sizeof(uintptr_t)) + { + const uintptr_t pageData = *reinterpret_cast(currAddr + nPageCount); + if (pageData != 0) + bIsGoodPage = false; + } + + if (bIsGoodPage && nPageCount >= nSize) + return currAddr; + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of a virtual method table by rtti type descriptor name +// Input : *szTableName - +// nRefIndex - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetVirtualMethodTable(const char* szTableName, const size_t nRefIndex) +{ + if (!m_ReadOnlyData.IsSectionValid()) // Process decided to rename the readonlydata section if this fails. + return CMemory(); + + ModuleSections_t moduleSection(".data", m_RunTimeData.m_pSectionBase, m_RunTimeData.m_nSectionSize); + + const auto tableNameInfo = Utils::StringToMaskedBytes(szTableName, false); + CMemory rttiTypeDescriptor = FindPatternSIMD(tableNameInfo.first.data(), tableNameInfo.second.c_str(), &moduleSection).OffsetSelf(-0x10); + if (!rttiTypeDescriptor) + return CMemory(); + + uintptr_t scanStart = m_ReadOnlyData.m_pSectionBase; // Get the start address of our scan. + + const uintptr_t scanEnd = (m_ReadOnlyData.m_pSectionBase + m_ReadOnlyData.m_nSectionSize) - 0x4; // Calculate the end of our scan. + const uintptr_t rttiTDRva = rttiTypeDescriptor.GetPtr() - m_pModuleBase; // The RTTI gets referenced by a 4-Byte RVA address. We need to scan for that address. + while (scanStart < scanEnd) + { + moduleSection = { ".rdata", scanStart, m_ReadOnlyData.m_nSectionSize }; + CMemory reference = FindPatternSIMD(reinterpret_cast(&rttiTDRva), "xxxx", &moduleSection, nRefIndex); + if (!reference) + break; + + CMemory referenceOffset = reference.Offset(-0xC); + if (referenceOffset.GetValue() != 1) // Check if we got a RTTI Object Locator for this reference by checking if -0xC is 1, which is the 'signature' field which is always 1 on x64. + { + scanStart = reference.Offset(0x4).GetPtr(); // Set location to current reference + 0x4 so we avoid pushing it back again into the vector. + continue; + } + + moduleSection = { ".rdata", m_ReadOnlyData.m_pSectionBase, m_ReadOnlyData.m_nSectionSize }; + return FindPatternSIMD(reinterpret_cast(&referenceOffset), "xxxxxxxx", &moduleSection).OffsetSelf(0x8); + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of imported function in this module +// Input : *szModuleName - +// *szFunctionName - +// bGetFunctionReference - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetImportedFunction(const char* szModuleName, const char* szFunctionName, const bool bGetFunctionReference) const +{ + if (!m_pDOSHeader || m_pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? + return CMemory(); + + if (!m_pNTHeaders || m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? + return CMemory(); + + // Get the location of IMAGE_IMPORT_DESCRIPTOR for this module by adding the IMAGE_DIRECTORY_ENTRY_IMPORT relative virtual address onto our module base address. + IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptors = reinterpret_cast(m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + if (!pImageImportDescriptors) + return CMemory(); + + for (IMAGE_IMPORT_DESCRIPTOR* pIID = pImageImportDescriptors; pIID->Name != 0; pIID++) + { + // Get virtual relative Address of the imported module name. Then add module base Address to get the actual location. + const char* szImportedModuleName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pIID->Name)); + + if (_stricmp(szImportedModuleName, szModuleName) == 0) // Is this our wanted imported module?. + { + // Original First Thunk to get function name. + IMAGE_THUNK_DATA* pOgFirstThunk = reinterpret_cast(m_pModuleBase + pIID->OriginalFirstThunk); + + // To get actual function address. + IMAGE_THUNK_DATA* pFirstThunk = reinterpret_cast(m_pModuleBase + pIID->FirstThunk); + for (; pOgFirstThunk->u1.AddressOfData; ++pOgFirstThunk, ++pFirstThunk) + { + // Get image import by name. + const IMAGE_IMPORT_BY_NAME* pImageImportByName = reinterpret_cast(m_pModuleBase + pOgFirstThunk->u1.AddressOfData); + + if (strcmp(pImageImportByName->Name, szFunctionName) == 0) // Is this our wanted imported function? + { + // Grab function address from firstThunk. +#if _WIN64 + uintptr_t* pFunctionAddress = &pFirstThunk->u1.Function; +#else + uintptr_t* pFunctionAddress = reinterpret_cast(&pFirstThunk->u1.Function); +#endif // #if _WIN64 + + // Reference or address? + return bGetFunctionReference ? CMemory(pFunctionAddress) : CMemory(*pFunctionAddress); // Return as CMemory class. + } + } + + } + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of exported function in this module +// Input : *szFunctionName - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetExportedFunction(const char* szFunctionName) const +{ + if (!m_pDOSHeader || m_pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? + return CMemory(); + + if (!m_pNTHeaders || m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? + return CMemory(); + + // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual address onto our module base address. + const IMAGE_EXPORT_DIRECTORY* pImageExportDirectory = reinterpret_cast(m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + if (!pImageExportDirectory) + return CMemory(); + + // Are there any exported functions? + if (!pImageExportDirectory->NumberOfFunctions) + return CMemory(); + + // Get the location of the functions via adding the relative virtual address from the struct into our module base address. + const DWORD* pAddressOfFunctions = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfFunctions); + if (!pAddressOfFunctions) + return CMemory(); + + // Get the names of the functions via adding the relative virtual address from the struct into our module base Address. + const DWORD* pAddressOfName = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfNames); + if (!pAddressOfName) + return CMemory(); + + // Get the ordinals of the functions via adding the relative virtual Address from the struct into our module base address. + DWORD* pAddressOfOrdinals = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfNameOrdinals); + if (!pAddressOfOrdinals) + return CMemory(); + + for (DWORD i = 0; i < pImageExportDirectory->NumberOfNames; i++) // Iterate through all the functions. + { + // Get virtual relative Address of the function name. Then add module base Address to get the actual location. + const char* ExportFunctionName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pAddressOfName[i])); + + if (strcmp(ExportFunctionName, szFunctionName) == 0) // Is this our wanted exported function? + { + // Get the function ordinal. Then grab the relative virtual address of our wanted function. Then add module base address so we get the actual location. + return CMemory(m_pModuleBase + pAddressOfFunctions[reinterpret_cast(pAddressOfOrdinals)[i]]); // Return as CMemory class. + } + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get the module section by name (example: '.rdata', '.text') +// Input : *szSectionName - +// Output : ModuleSections_t +//----------------------------------------------------------------------------- +CModule::ModuleSections_t CModule::GetSectionByName(const char* szSectionName) const +{ + for (const ModuleSections_t& section : m_ModuleSections) + { + if (section.m_SectionName.compare(szSectionName) == 0) + return section; + } + + return ModuleSections_t(); +} diff --git a/primedev/thirdparty/silver-bun/module.h b/primedev/thirdparty/silver-bun/module.h new file mode 100644 index 00000000..5683ee14 --- /dev/null +++ b/primedev/thirdparty/silver-bun/module.h @@ -0,0 +1,76 @@ +//===========================================================================// +// +// Purpose: Implementation of the CModule class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// +#pragma once + +#include "memaddr.h" +#include +#include +#include +#include +#include +#include +#include + +class CModule +{ +public: + struct ModuleSections_t + { + ModuleSections_t(void) = default; + ModuleSections_t(const char* sectionName, uintptr_t pSectionBase, size_t nSectionSize) : + m_SectionName(sectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) {} + + inline bool IsSectionValid(void) const { return m_nSectionSize != 0; } + + std::string m_SectionName; // Name of section. + uintptr_t m_pSectionBase; // Start address of section. + size_t m_nSectionSize; // Size of section. + }; + + CModule(void) = default; + CModule(HMODULE hModule); + CModule(const char* szModuleName); + + void Init(); + void LoadSections(); + + CMemory Offset(const uintptr_t nOffset) const; + + CMemory FindPatternSIMD(const char* szPattern, const ModuleSections_t* moduleSection = nullptr) const; + CMemory FindString(const char* szString, const ptrdiff_t occurrence = 1, bool nullTerminator = false) const; + CMemory FindStringReadOnly(const char* szString, bool nullTerminator) const; + CMemory FindFreeDataPage(const size_t nSize) const; + + CMemory GetVirtualMethodTable(const char* szTableName, const size_t nRefIndex = 0); + CMemory GetImportedFunction(const char* szModuleName, const char* szFunctionName, const bool bGetFunctionReference) const; + CMemory GetExportedFunction(const char* szFunctionName) const; + ModuleSections_t GetSectionByName(const char* szSectionName) const; + + inline const std::vector& GetSections() const { return m_ModuleSections; } + inline uintptr_t GetModuleBase(void) const { return m_pModuleBase; } + inline DWORD GetModuleSize(void) const { return m_nModuleSize; } + inline const std::string& GetModuleName(void) const { return m_ModuleName; } + inline uintptr_t GetRVA(const uintptr_t nAddress) const { return (nAddress - GetModuleBase()); } + + IMAGE_NT_HEADERS64* m_pNTHeaders; + IMAGE_DOS_HEADER* m_pDOSHeader; + + ModuleSections_t m_ExecutableCode; + ModuleSections_t m_ExceptionTable; + ModuleSections_t m_RunTimeData; + ModuleSections_t m_ReadOnlyData; + +private: + CMemory FindPatternSIMD(const uint8_t* pPattern, const char* szMask, + const ModuleSections_t* moduleSection = nullptr, const size_t nOccurrence = 0) const; + + std::string m_ModuleName; + uintptr_t m_pModuleBase; + DWORD m_nModuleSize; + std::vector m_ModuleSections; +}; diff --git a/primedev/thirdparty/silver-bun/utils.cpp b/primedev/thirdparty/silver-bun/utils.cpp new file mode 100644 index 00000000..6f7b397e --- /dev/null +++ b/primedev/thirdparty/silver-bun/utils.cpp @@ -0,0 +1,127 @@ +#include "utils.h" + +namespace Utils +{ + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern to an array of bytes. Doesnt support wildcards + //---------------------------------------------------------------------------------------- + std::vector StringPatternToBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + vBytes.push_back(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16)); + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern with wildcards to an array of bytes. + //---------------------------------------------------------------------------------------- + std::vector PatternToBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + vBytes.push_back(-1); // Push the byte back as invalid. + } + else + { + vBytes.push_back(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16)); + } + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern with wildcards to an array of bytes and mask. + //---------------------------------------------------------------------------------------- + std::pair, std::string> PatternToMaskedBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + + std::vector vBytes; + std::string svMask; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + vBytes.push_back(0); // Push the byte back as invalid. + svMask += '?'; + } + else + { + vBytes.push_back(uint8_t(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16))); + svMask += 'x'; + } + } + return make_pair(vBytes, svMask); + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string to an array of bytes. + //---------------------------------------------------------------------------------------- + std::vector StringToBytes(const char* szInput, bool bNullTerminator) + { + const char* pszStringStart = const_cast(szInput); + const char* pszStringEnd = pszStringStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + } + + if (bNullTerminator) + { + vBytes.push_back('\0'); + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string to an array of masked bytes. + //---------------------------------------------------------------------------------------- + std::pair, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator) + { + const char* pszStringStart = const_cast(szInput); + const char* pszStringEnd = pszStringStart + strlen(szInput); + std::vector vBytes; + std::string svMask; + + for (const char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + svMask += 'x'; + } + + if (bNullTerminator) + { + vBytes.push_back(0x0); + svMask += 'x'; + } + return make_pair(vBytes, svMask); + }; +} diff --git a/primedev/thirdparty/silver-bun/utils.h b/primedev/thirdparty/silver-bun/utils.h new file mode 100644 index 00000000..15e43a92 --- /dev/null +++ b/primedev/thirdparty/silver-bun/utils.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Utils +{ + std::vector StringPatternToBytes(const char* szInput); + std::vector PatternToBytes(const char* szInput); + std::pair, std::string> PatternToMaskedBytes(const char* szInput); + std::vector StringToBytes(const char* szInput, bool bNullTerminator); + std::pair, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator); +} + +typedef const unsigned char* rsig_t; -- cgit v1.2.3 From 85a2fb9c56c1942958c09c8aeafd14ddefb6e0c3 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:12:05 +0000 Subject: Address C4100 compiler warnings (unused var) (#648) Adds and uses a macro to avoid the warning --- primedev/core/convar/convar.cpp | 1 + primedev/dedicated/dedicated.cpp | 5 +++++ primedev/dedicated/dedicatedlogtoclient.cpp | 1 + primedev/dllmain.cpp | 2 ++ primedev/logging/logging.cpp | 1 + primedev/logging/logging.h | 1 + primedev/logging/loghooks.cpp | 5 +++++ primedev/logging/sourceconsole.cpp | 4 ++++ primedev/masterserver/masterserver.cpp | 5 +++++ primedev/mods/autodownload/moddownloader.cpp | 2 ++ primedev/mods/modmanager.cpp | 1 + primedev/pch.h | 2 ++ primedev/scripts/client/scriptmainmenupromos.cpp | 1 + primedev/scripts/client/scriptmodmenu.cpp | 1 + primedev/scripts/client/scriptserverbrowser.cpp | 4 ++++ primedev/scripts/scriptdatatables.cpp | 1 + primedev/server/auth/bansystem.cpp | 1 + primedev/server/auth/serverauthentication.cpp | 1 + primedev/server/serverpresence.cpp | 9 +++++++++ primedev/server/serverpresence.h | 8 ++++---- primedev/shared/exploit_fixes/exploitfixes.cpp | 2 ++ primedev/shared/exploit_fixes/exploitfixes_lzss.cpp | 1 + primedev/shared/exploit_fixes/ns_limits.cpp | 1 + primedev/shared/misccommands.cpp | 2 ++ primedev/squirrel/squirrel.cpp | 3 +++ primedev/util/printcommands.cpp | 2 ++ 26 files changed, 63 insertions(+), 4 deletions(-) (limited to 'primedev') diff --git a/primedev/core/convar/convar.cpp b/primedev/core/convar/convar.cpp index 767961ed..87a1e159 100644 --- a/primedev/core/convar/convar.cpp +++ b/primedev/core/convar/convar.cpp @@ -363,6 +363,7 @@ void ConVar::SetValue(Color clValue) //----------------------------------------------------------------------------- void ConVar::ChangeStringValue(const char* pszTempVal, float flOldValue) { + NOTE_UNUSED(flOldValue); assert(!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING)); char* pszOldValue = (char*)_malloca(m_Value.m_iStringLength); diff --git a/primedev/dedicated/dedicated.cpp b/primedev/dedicated/dedicated.cpp index 5c679b76..eca9b9f1 100644 --- a/primedev/dedicated/dedicated.cpp +++ b/primedev/dedicated/dedicated.cpp @@ -35,11 +35,14 @@ struct CDedicatedExports void Sys_Printf(CDedicatedExports* dedicated, const char* msg) { + NOTE_UNUSED(dedicated); spdlog::info("[DEDICATED SERVER] {}", msg); } void RunServer(CDedicatedExports* dedicated) { + NOTE_UNUSED(dedicated); + spdlog::info("CDedicatedExports::RunServer(): starting"); spdlog::info(CommandLine()->GetCmdLine()); @@ -86,6 +89,8 @@ class DedicatedConsoleServerPresence : public ServerPresenceReporter HANDLE consoleInputThreadHandle = NULL; DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter) { + NOTE_UNUSED(pThreadParameter); + while (!g_pEngine || !g_pHostState || g_pHostState->m_iCurrentState != HostState_t::HS_RUN) Sleep(1000); diff --git a/primedev/dedicated/dedicatedlogtoclient.cpp b/primedev/dedicated/dedicatedlogtoclient.cpp index bf2cf77a..48954d34 100644 --- a/primedev/dedicated/dedicatedlogtoclient.cpp +++ b/primedev/dedicated/dedicatedlogtoclient.cpp @@ -37,6 +37,7 @@ void DedicatedServerLogToClientSink::custom_sink_it_(const custom_log_msg& msg) void DedicatedServerLogToClientSink::sink_it_(const spdlog::details::log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("sink_it_ called on DedicatedServerLogToClientSink with pure log_msg. This is an error!"); } diff --git a/primedev/dllmain.cpp b/primedev/dllmain.cpp index c87c8bae..1191307f 100644 --- a/primedev/dllmain.cpp +++ b/primedev/dllmain.cpp @@ -20,6 +20,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + NOTE_UNUSED(hModule); + NOTE_UNUSED(lpReserved); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: diff --git a/primedev/logging/logging.cpp b/primedev/logging/logging.cpp index ef9a6737..72171e25 100644 --- a/primedev/logging/logging.cpp +++ b/primedev/logging/logging.cpp @@ -70,6 +70,7 @@ void CreateLogFiles() void ExternalConsoleSink::sink_it_(const spdlog::details::log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("sink_it_ called on SourceConsoleSink with pure log_msg. This is an error!"); } diff --git a/primedev/logging/logging.h b/primedev/logging/logging.h index 5056af27..be41cb39 100644 --- a/primedev/logging/logging.h +++ b/primedev/logging/logging.h @@ -25,6 +25,7 @@ public: void custom_log(const custom_log_msg& msg); virtual void custom_sink_it_(const custom_log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("Pure virtual call to CustomSink::custom_sink_it_"); } }; diff --git a/primedev/logging/loghooks.cpp b/primedev/logging/loghooks.cpp index 7efb5b99..dcd9b85a 100644 --- a/primedev/logging/loghooks.cpp +++ b/primedev/logging/loghooks.cpp @@ -108,6 +108,8 @@ AUTOHOOK(Hook_fprintf, engine.dll + 0x51B1F0, int,, (void* const stream, const char* const format, ...)) // clang-format on { + NOTE_UNUSED(stream); + va_list va; va_start(va, format); @@ -139,6 +141,7 @@ AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80, void, __fastcall, (void* pEngineServer, SpewType_t type, const char* format, va_list args)) // clang-format on { + NOTE_UNUSED(pEngineServer); if (!Cvar_spewlog_enable->GetBool()) return; @@ -235,6 +238,8 @@ AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530, bool,, (void* thisptr, uintptr_t msg)) // clang-format on { + NOTE_UNUSED(thisptr); + char* text = *(char**)(msg + 0x20); auto endpos = strlen(text); diff --git a/primedev/logging/sourceconsole.cpp b/primedev/logging/sourceconsole.cpp index e436d1d4..55be4723 100644 --- a/primedev/logging/sourceconsole.cpp +++ b/primedev/logging/sourceconsole.cpp @@ -8,6 +8,7 @@ SourceInterface* g_pSourceGameConsole; void ConCommand_toggleconsole(const CCommand& arg) { + NOTE_UNUSED(arg); if ((*g_pSourceGameConsole)->IsConsoleVisible()) (*g_pSourceGameConsole)->Hide(); else @@ -16,11 +17,13 @@ void ConCommand_toggleconsole(const CCommand& arg) void ConCommand_showconsole(const CCommand& arg) { + NOTE_UNUSED(arg); (*g_pSourceGameConsole)->Activate(); } void ConCommand_hideconsole(const CCommand& arg) { + NOTE_UNUSED(arg); (*g_pSourceGameConsole)->Hide(); } @@ -47,6 +50,7 @@ void SourceConsoleSink::custom_sink_it_(const custom_log_msg& msg) void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("sink_it_ called on SourceConsoleSink with pure log_msg. This is an error!"); } diff --git a/primedev/masterserver/masterserver.cpp b/primedev/masterserver/masterserver.cpp index a2d9f00e..e7bb1132 100644 --- a/primedev/masterserver/masterserver.cpp +++ b/primedev/masterserver/masterserver.cpp @@ -988,6 +988,7 @@ void MasterServerManager::ProcessConnectionlessPacketSigreq1(std::string data) void ConCommand_ns_fetchservers(const CCommand& args) { + NOTE_UNUSED(args); g_pMasterServerManager->RequestServerList(); } @@ -1008,6 +1009,7 @@ ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), ( void MasterServerPresenceReporter::CreatePresence(const ServerPresence* pServerPresence) { + NOTE_UNUSED(pServerPresence); m_nNumRegistrationAttempts = 0; } @@ -1051,6 +1053,7 @@ void MasterServerPresenceReporter::ReportPresence(const ServerPresence* pServerP void MasterServerPresenceReporter::DestroyPresence(const ServerPresence* pServerPresence) { + NOTE_UNUSED(pServerPresence); // Don't call this if we don't have a server id. if (!*g_pMasterServerManager->m_sOwnServerId) { @@ -1086,6 +1089,8 @@ void MasterServerPresenceReporter::DestroyPresence(const ServerPresence* pServer void MasterServerPresenceReporter::RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) { + NOTE_UNUSED(flCurrentTime); + NOTE_UNUSED(pServerPresence); // Check if we're already running an InternalAddServer() call in the background. // If so, grab the result if it's ready. if (addServerFuture.valid()) diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index 960f435a..3a946263 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -126,6 +126,8 @@ size_t WriteData(void* ptr, size_t size, size_t nmemb, FILE* stream) int ModDownloader::ModFetchingProgressCallback( void* ptr, curl_off_t totalDownloadSize, curl_off_t finishedDownloadSize, curl_off_t totalToUpload, curl_off_t nowUploaded) { + NOTE_UNUSED(totalToUpload); + NOTE_UNUSED(nowUploaded); if (totalDownloadSize != 0 && finishedDownloadSize != 0) { ModDownloader* instance = static_cast(ptr); diff --git a/primedev/mods/modmanager.cpp b/primedev/mods/modmanager.cpp index e1077922..268a65a5 100644 --- a/primedev/mods/modmanager.cpp +++ b/primedev/mods/modmanager.cpp @@ -1121,6 +1121,7 @@ void ModManager::CompileAssetsForFile(const char* filename) void ConCommand_reload_mods(const CCommand& args) { + NOTE_UNUSED(args); g_pModManager->LoadMods(); } diff --git a/primedev/pch.h b/primedev/pch.h index 141995c7..577f803c 100644 --- a/primedev/pch.h +++ b/primedev/pch.h @@ -29,6 +29,8 @@ typedef void (*callable_v)(void* v); #define assert_msg(exp, msg) assert((exp, msg)) //clang-format on +#define NOTE_UNUSED(var) do { (void)var; } while(false) + #include "core/macros.h" #include "core/math/color.h" diff --git a/primedev/scripts/client/scriptmainmenupromos.cpp b/primedev/scripts/client/scriptmainmenupromos.cpp index ecb47af7..91bc3002 100644 --- a/primedev/scripts/client/scriptmainmenupromos.cpp +++ b/primedev/scripts/client/scriptmainmenupromos.cpp @@ -23,6 +23,7 @@ enum eMainMenuPromoDataProperty }; ADD_SQFUNC("void", NSRequestCustomMainMenuPromos, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pMasterServerManager->RequestMainMenuPromos(); return SQRESULT_NULL; } diff --git a/primedev/scripts/client/scriptmodmenu.cpp b/primedev/scripts/client/scriptmodmenu.cpp index a88478fb..2e877db4 100644 --- a/primedev/scripts/client/scriptmodmenu.cpp +++ b/primedev/scripts/client/scriptmodmenu.cpp @@ -160,6 +160,7 @@ ADD_SQFUNC( ADD_SQFUNC("void", NSReloadMods, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pModManager->LoadMods(); return SQRESULT_NULL; } diff --git a/primedev/scripts/client/scriptserverbrowser.cpp b/primedev/scripts/client/scriptserverbrowser.cpp index 21324535..b946f7a9 100644 --- a/primedev/scripts/client/scriptserverbrowser.cpp +++ b/primedev/scripts/client/scriptserverbrowser.cpp @@ -8,6 +8,7 @@ ADD_SQFUNC("void", NSRequestServerList, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pMasterServerManager->RequestServerList(); return SQRESULT_NULL; } @@ -32,6 +33,7 @@ ADD_SQFUNC("int", NSGetServerCount, "", "", ScriptContext::UI) ADD_SQFUNC("void", NSClearRecievedServerList, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pMasterServerManager->ClearServerList(); return SQRESULT_NULL; } @@ -114,6 +116,7 @@ ADD_SQFUNC("void", NSConnectToAuthedServer, "", "", ScriptContext::UI) ADD_SQFUNC("void", NSTryAuthWithLocalServer, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); // do auth request g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); @@ -122,6 +125,7 @@ ADD_SQFUNC("void", NSTryAuthWithLocalServer, "", "", ScriptContext::UI) ADD_SQFUNC("void", NSCompleteAuthWithLocalServer, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); // literally just set serverfilter // note: this assumes we have no authdata other than our own if (g_pServerAuthentication->m_RemoteAuthenticationData.size()) diff --git a/primedev/scripts/scriptdatatables.cpp b/primedev/scripts/scriptdatatables.cpp index 47c3a0f1..5e685b48 100644 --- a/primedev/scripts/scriptdatatables.cpp +++ b/primedev/scripts/scriptdatatables.cpp @@ -781,6 +781,7 @@ void ConCommand_dump_datatable(const CCommand& args) void ConCommand_dump_datatables(const CCommand& args) { + NOTE_UNUSED(args); // likely not a comprehensive list, might be missing a couple? static const std::vector VANILLA_DATATABLE_PATHS = { "datatable/burn_meter_rewards.rpak", diff --git a/primedev/server/auth/bansystem.cpp b/primedev/server/auth/bansystem.cpp index 59bb6067..20c07844 100644 --- a/primedev/server/auth/bansystem.cpp +++ b/primedev/server/auth/bansystem.cpp @@ -213,6 +213,7 @@ void ConCommand_unban(const CCommand& args) void ConCommand_clearbanlist(const CCommand& args) { + NOTE_UNUSED(args); g_pBanSystem->ClearBanlist(); } diff --git a/primedev/server/auth/serverauthentication.cpp b/primedev/server/auth/serverauthentication.cpp index 7d656820..d0d4c698 100644 --- a/primedev/server/auth/serverauthentication.cpp +++ b/primedev/server/auth/serverauthentication.cpp @@ -333,6 +333,7 @@ void,, (CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...) void ConCommand_ns_resetpersistence(const CCommand& args) { + NOTE_UNUSED(args); if (*g_pServerState == server_state_t::ss_active) { spdlog::error("ns_resetpersistence must be entered from the main menu"); diff --git a/primedev/server/serverpresence.cpp b/primedev/server/serverpresence.cpp index 159b9f30..509243f0 100644 --- a/primedev/server/serverpresence.cpp +++ b/primedev/server/serverpresence.cpp @@ -79,6 +79,9 @@ void ServerPresenceManager::CreateConVars() "ns_server_presence_update_rate", "5000", FCVAR_GAMEDLL, "How often we update our server's presence on server lists in ms"); Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + NOTE_UNUSED(cvar); + NOTE_UNUSED(pOldValue); + NOTE_UNUSED(flOldValue); g_pServerPresence->SetName(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_name->GetString())); // update engine hostname cvar @@ -86,10 +89,16 @@ void ServerPresenceManager::CreateConVars() }); Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "This server's description", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + NOTE_UNUSED(cvar); + NOTE_UNUSED(pOldValue); + NOTE_UNUSED(flOldValue); g_pServerPresence->SetDescription(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_desc->GetString())); }); Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "This server's password", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + NOTE_UNUSED(cvar); + NOTE_UNUSED(pOldValue); + NOTE_UNUSED(flOldValue); g_pServerPresence->SetPassword(g_pServerPresence->Cvar_ns_server_password->GetString()); }); diff --git a/primedev/server/serverpresence.h b/primedev/server/serverpresence.h index c644cc37..94ecfe6a 100644 --- a/primedev/server/serverpresence.h +++ b/primedev/server/serverpresence.h @@ -45,10 +45,10 @@ public: class ServerPresenceReporter { public: - virtual void CreatePresence(const ServerPresence* pServerPresence) {} - virtual void ReportPresence(const ServerPresence* pServerPresence) {} - virtual void DestroyPresence(const ServerPresence* pServerPresence) {} - virtual void RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) {} + virtual void CreatePresence(const ServerPresence* /*pServerPresence*/) {} + virtual void ReportPresence(const ServerPresence* /*pServerPresence*/) {} + virtual void DestroyPresence(const ServerPresence* /*pServerPresence*/) {} + virtual void RunFrame(double /*flCurrentTime*/, const ServerPresence* /*pServerPresence*/) {} }; class ServerPresenceManager diff --git a/primedev/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp index 7850f7b0..d96bc41e 100644 --- a/primedev/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -55,6 +55,8 @@ AUTOHOOK(Base_CmdKeyValues_ReadFromBuffer, engine.dll + 0x220040, bool, __fastcall, (void* thisptr, void* buffer)) // 40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70 // clang-format on { + NOTE_UNUSED(thisptr); + NOTE_UNUSED(buffer); return false; } diff --git a/primedev/shared/exploit_fixes/exploitfixes_lzss.cpp b/primedev/shared/exploit_fixes/exploitfixes_lzss.cpp index ccb6ac18..9a9a5691 100644 --- a/primedev/shared/exploit_fixes/exploitfixes_lzss.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes_lzss.cpp @@ -16,6 +16,7 @@ AUTOHOOK(CLZSS__SafeDecompress, engine.dll + 0x432A10, unsigned int, __fastcall, (void* self, const unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize)) // clang-format on { + NOTE_UNUSED(self); unsigned int totalBytes = 0; int getCmdByte = 0; int cmdByte = 0; diff --git a/primedev/shared/exploit_fixes/ns_limits.cpp b/primedev/shared/exploit_fixes/ns_limits.cpp index bd855ee4..63abbf69 100644 --- a/primedev/shared/exploit_fixes/ns_limits.cpp +++ b/primedev/shared/exploit_fixes/ns_limits.cpp @@ -16,6 +16,7 @@ float (*CEngineServer__GetTimescale)(); // todo: make this work on higher timescales, also possibly disable when sv_cheats is set void ServerLimitsManager::RunFrame(double flCurrentTime, float flFrameTime) { + NOTE_UNUSED(flCurrentTime); if (Cvar_sv_antispeedhack_enable->GetBool()) { // for each player, set their usercmd processing budget for the frame to the last frametime for the server diff --git a/primedev/shared/misccommands.cpp b/primedev/shared/misccommands.cpp index 648525b9..29d92d2d 100644 --- a/primedev/shared/misccommands.cpp +++ b/primedev/shared/misccommands.cpp @@ -21,6 +21,7 @@ void ConCommand_force_newgame(const CCommand& arg) void ConCommand_ns_start_reauth_and_leave_to_lobby(const CCommand& arg) { + NOTE_UNUSED(arg); // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect g_pMasterServerManager->m_bNewgameAfterSelfAuth = true; g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); @@ -28,6 +29,7 @@ void ConCommand_ns_start_reauth_and_leave_to_lobby(const CCommand& arg) void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) { + NOTE_UNUSED(arg); if (g_pServerAuthentication->m_RemoteAuthenticationData.size()) g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str()); diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp index 259d0f43..41a6a782 100644 --- a/primedev/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -316,6 +316,7 @@ template void SquirrelManager::AddFuncOverride( // hooks bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm) { + NOTE_UNUSED(context); return ScriptContext(pSqvm->sharedState->cSquirrelVM->vmContext) == ScriptContext::UI; } @@ -334,6 +335,8 @@ template void* __fastcall sq_compiler_createHook(HSquirr template SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt); template SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...) { + NOTE_UNUSED(sqvm); + va_list va; va_start(va, fmt); diff --git a/primedev/util/printcommands.cpp b/primedev/util/printcommands.cpp index 20ebfffc..03ccce5e 100644 --- a/primedev/util/printcommands.cpp +++ b/primedev/util/printcommands.cpp @@ -188,6 +188,7 @@ void ConCommand_findflags(const CCommand& arg) void ConCommand_list(const CCommand& arg) { + NOTE_UNUSED(arg); ConCommandBase* var; CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); std::map sorted; @@ -210,6 +211,7 @@ void ConCommand_list(const CCommand& arg) void ConCommand_differences(const CCommand& arg) { + NOTE_UNUSED(arg); CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); std::map sorted; -- cgit v1.2.3 From d3ee91c1f5cf7eeacbb091c28e248bc3b88d5a6e Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Fri, 8 Mar 2024 23:20:49 +0000 Subject: Fix crash in silver-bun (#679) --- primedev/thirdparty/silver-bun/module.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'primedev') diff --git a/primedev/thirdparty/silver-bun/module.cpp b/primedev/thirdparty/silver-bun/module.cpp index 4b9330e0..84f4da9e 100644 --- a/primedev/thirdparty/silver-bun/module.cpp +++ b/primedev/thirdparty/silver-bun/module.cpp @@ -15,11 +15,21 @@ //----------------------------------------------------------------------------- CModule::CModule(HMODULE hModule) { - m_pModuleBase = reinterpret_cast(hModule); + MODULEINFO mInfo {0}; + + if (hModule && hModule != INVALID_HANDLE_VALUE) + GetModuleInformation(GetCurrentProcess(), hModule, &mInfo, sizeof(MODULEINFO)); + + m_nModuleSize = static_cast(mInfo.SizeOfImage); + m_pModuleBase = reinterpret_cast(mInfo.lpBaseOfDll); + + if (!m_nModuleSize || !m_pModuleBase) + return; CHAR szModuleName[MAX_PATH]; DWORD dwSize = GetModuleFileNameA(hModule, szModuleName, sizeof(szModuleName)); - m_ModuleName = strrchr(szModuleName, '\\') + 1; + char* chLast = strrchr(szModuleName, '\\'); + m_ModuleName = chLast == nullptr ? szModuleName : chLast + 1; Init(); -- cgit v1.2.3 From ab653555f6aa0ec57e1b23a590fb3061929fdc30 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 5 May 2024 18:00:04 +0200 Subject: Fix plugin with NULL strings causing UB (#695) Instance string members only after checking for null pointers to prevent UB --- primedev/plugins/plugins.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'primedev') diff --git a/primedev/plugins/plugins.cpp b/primedev/plugins/plugins.cpp index eddaa8ac..ae6fd0cb 100644 --- a/primedev/plugins/plugins.cpp +++ b/primedev/plugins/plugins.cpp @@ -69,10 +69,6 @@ Plugin::Plugin(std::string path) : m_location(path) m_runOnServer = context & PluginContext::DEDICATED; m_runOnClient = context & PluginContext::CLIENT; - m_name = std::string(name); - m_logName = std::string(logName); - m_dependencyName = std::string(dependencyName); - if (!name) { NS::log::PLUGINSYS->error("Could not load name of plugin at '{}'", path); @@ -91,6 +87,10 @@ Plugin::Plugin(std::string path) : m_location(path) return; } + m_name = std::string(name); + m_logName = std::string(logName); + m_dependencyName = std::string(dependencyName); + if (!isValidSquirrelIdentifier(m_dependencyName)) { NS::log::PLUGINSYS->error("Dependency name \"{}\" of plugin {} is not valid", dependencyName, name); -- cgit v1.2.3 From 220b7a1bf7915a336b9c2a08806e60c1c32bde8e Mon Sep 17 00:00:00 2001 From: wolf109909 <84360921+wolf109909@users.noreply.github.com> Date: Tue, 21 May 2024 21:50:22 +0800 Subject: Fix engine error crash caused by language detection hooks (#699) Previous logic would always return `true` --- primedev/client/languagehooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'primedev') diff --git a/primedev/client/languagehooks.cpp b/primedev/client/languagehooks.cpp index 35ca5659..0146d1d4 100644 --- a/primedev/client/languagehooks.cpp +++ b/primedev/client/languagehooks.cpp @@ -41,7 +41,7 @@ std::vector file_list(fs::path dir, std::regex ext_pattern) std::string GetAnyInstalledAudioLanguage() { for (const auto& lang : file_list("r2\\sound\\", std::regex(".*?general_([a-z]+)_patch_1\\.mstr"))) - if (lang != "general" || lang != "") + if (lang != "general" && lang != "" && lang != "stream") return lang; return "NO LANGUAGE DETECTED"; } -- cgit v1.2.3