aboutsummaryrefslogtreecommitdiff
path: root/primedev
diff options
context:
space:
mode:
authorGeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com>2024-05-21 22:55:26 +0200
committerGitHub <noreply@github.com>2024-05-21 22:55:26 +0200
commitd1d4f2a086209f041fa357e03fdf93614a7a9fd7 (patch)
tree290b09ba4b9eec05deaf6dd44d7bd8afad86c31b /primedev
parent60d872d3756854d29b65b63fb146aa4f638dd29e (diff)
parent220b7a1bf7915a336b9c2a08806e60c1c32bde8e (diff)
downloadNorthstarLauncher-d1d4f2a086209f041fa357e03fdf93614a7a9fd7.tar.gz
NorthstarLauncher-d1d4f2a086209f041fa357e03fdf93614a7a9fd7.zip
Merge branch 'main' into chore/revert-zlib-cmake-fixchore/revert-zlib-cmake-fix
Diffstat (limited to 'primedev')
-rw-r--r--primedev/CMakeLists.txt2
-rw-r--r--primedev/Northstar.cmake13
-rw-r--r--primedev/client/cdll_client_int.h5
-rw-r--r--primedev/client/debugoverlay.cpp69
-rw-r--r--primedev/client/debugoverlay.h28
-rw-r--r--primedev/client/entity_client_tools.cpp13
-rw-r--r--primedev/client/languagehooks.cpp2
-rw-r--r--primedev/cmake/Findsilver-bun.cmake9
-rw-r--r--primedev/core/convar/convar.cpp1
-rw-r--r--primedev/core/hooks.h1
-rw-r--r--primedev/core/math/math_pfns.h7
-rw-r--r--primedev/core/math/vector.h332
-rw-r--r--primedev/core/math/vplane.h106
-rw-r--r--primedev/core/memory.cpp347
-rw-r--r--primedev/core/memory.h90
-rw-r--r--primedev/core/tier0.cpp6
-rw-r--r--primedev/core/tier1.cpp19
-rw-r--r--primedev/core/tier1.h12
-rw-r--r--primedev/dedicated/dedicated.cpp9
-rw-r--r--primedev/dedicated/dedicatedlogtoclient.cpp1
-rw-r--r--primedev/dllmain.cpp2
-rw-r--r--primedev/logging/logging.cpp1
-rw-r--r--primedev/logging/logging.h1
-rw-r--r--primedev/logging/loghooks.cpp5
-rw-r--r--primedev/logging/sourceconsole.cpp4
-rw-r--r--primedev/masterserver/masterserver.cpp5
-rw-r--r--primedev/mods/autodownload/moddownloader.cpp2
-rw-r--r--primedev/mods/autodownload/moddownloader.h4
-rw-r--r--primedev/mods/modmanager.cpp1
-rw-r--r--primedev/pch.h5
-rw-r--r--primedev/plugins/plugins.cpp8
-rw-r--r--primedev/scripts/client/scriptbrowserhooks.cpp3
-rw-r--r--primedev/scripts/client/scriptmainmenupromos.cpp1
-rw-r--r--primedev/scripts/client/scriptmodmenu.cpp1
-rw-r--r--primedev/scripts/client/scriptserverbrowser.cpp4
-rw-r--r--primedev/scripts/scriptdatatables.cpp37
-rw-r--r--primedev/server/ai_helper.cpp159
-rw-r--r--primedev/server/ai_helper.h13
-rw-r--r--primedev/server/ai_navmesh.cpp6
-rw-r--r--primedev/server/ai_navmesh.h260
-rw-r--r--primedev/server/alltalk.cpp2
-rw-r--r--primedev/server/auth/bansystem.cpp1
-rw-r--r--primedev/server/auth/serverauthentication.cpp1
-rw-r--r--primedev/server/serverpresence.cpp9
-rw-r--r--primedev/server/serverpresence.h8
-rw-r--r--primedev/shared/exploit_fixes/exploitfixes.cpp30
-rw-r--r--primedev/shared/exploit_fixes/exploitfixes_lzss.cpp1
-rw-r--r--primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp6
-rw-r--r--primedev/shared/exploit_fixes/ns_limits.cpp1
-rw-r--r--primedev/shared/keyvalues.cpp6
-rw-r--r--primedev/shared/maxplayers.cpp4
-rw-r--r--primedev/shared/misccommands.cpp2
-rw-r--r--primedev/squirrel/squirrel.cpp10
-rw-r--r--primedev/thirdparty/silver-bun/CMakeLists.txt9
-rw-r--r--primedev/thirdparty/silver-bun/memaddr.cpp368
-rw-r--r--primedev/thirdparty/silver-bun/memaddr.h154
-rw-r--r--primedev/thirdparty/silver-bun/module.cpp484
-rw-r--r--primedev/thirdparty/silver-bun/module.h76
-rw-r--r--primedev/thirdparty/silver-bun/utils.cpp127
-rw-r--r--primedev/thirdparty/silver-bun/utils.h18
-rw-r--r--primedev/toolframework/itoolentity.h46
-rw-r--r--primedev/util/printcommands.cpp2
-rw-r--r--primedev/util/printmaps.cpp8
-rw-r--r--primedev/wsockproxy/CMakeLists.txt (renamed from primedev/WSockProxy.cmake)11
-rw-r--r--primedev/wsockproxy/dllmain.cpp153
-rw-r--r--primedev/wsockproxy/loader.cpp38
-rw-r--r--primedev/wsockproxy/wsock32.asm7
67 files changed, 2433 insertions, 743 deletions
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/Northstar.cmake b/primedev/Northstar.cmake
index 00a8dcaf..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
@@ -16,6 +17,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,18 +43,20 @@ 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"
"core/memalloc.cpp"
"core/memalloc.h"
- "core/memory.cpp"
- "core/memory.h"
"core/sourceinterface.cpp"
"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 +120,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"
@@ -163,6 +171,7 @@ target_link_libraries(
PRIVATE minhook
libcurl
minizip
+ silver-bun
WS2_32.lib
Crypt32.lib
Cryptui.lib
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__IsDeadType>();
- OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<OverlayBase_t__DestroyOverlayType>();
-
- RenderLine = module.Offset(0x192A70).RCast<RenderLineType>();
- RenderBox = module.Offset(0x192520).RCast<RenderBoxType>();
- RenderWireframeBox = module.Offset(0x193DA0).RCast<RenderBoxType>();
- RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<RenderWireframeSweptBoxType>();
- RenderTriangle = module.Offset(0x193940).RCast<RenderTriangleType>();
- RenderAxis = module.Offset(0x1924D0).RCast<RenderAxisType>();
- RenderSphere = module.Offset(0x194170).RCast<RenderSphereType>();
- RenderUnknown = module.Offset(0x1924E0).RCast<RenderUnknownType>();
+ OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast<decltype(OverlayBase_t__IsDead)>();
+ OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<decltype(OverlayBase_t__DestroyOverlay)>();
+
+ RenderLine = module.Offset(0x192A70).RCast<decltype(RenderLine)>();
+ RenderBox = module.Offset(0x192520).RCast<decltype(RenderBox)>();
+ RenderWireframeBox = module.Offset(0x193DA0).RCast<decltype(RenderWireframeBox)>();
+ RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<decltype(RenderWireframeSweptBox)>();
+ RenderTriangle = module.Offset(0x193940).RCast<decltype(RenderTriangle)>();
+ RenderAxis = module.Offset(0x1924D0).RCast<decltype(RenderAxis)>();
+ RenderSphere = module.Offset(0x194170).RCast<decltype(RenderSphere)>();
+ RenderUnknown = module.Offset(0x1924E0).RCast<decltype(RenderUnknown)>();
s_OverlayMutex = module.Offset(0x10DB0A38).RCast<LPCRITICAL_SECTION>();
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<IClientTools*>();
+}
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<std::string> 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";
}
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/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/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 <string>
#include <iostream>
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<float*>(&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 <cmath>
-#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/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<uintptr_t>(pAddress)) {}
-
-// operators
-CMemoryAddress::operator uintptr_t() const
-{
- return m_nAddress;
-}
-
-CMemoryAddress::operator void*() const
-{
- return reinterpret_cast<void*>(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<uintptr_t*>(ret);
-
- return CMemoryAddress(ret);
-}
-
-// patching
-void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize)
-{
- if (nSize)
- WriteProcessMemory(GetCurrentProcess(), reinterpret_cast<LPVOID>(m_nAddress), pBytes, nSize, NULL);
-}
-
-void CMemoryAddress::Patch(const std::initializer_list<uint8_t> 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<uint8_t> HexBytesToString(const char* pHexString)
-{
- std::vector<uint8_t> 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<uint8_t> 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<LPCVOID>(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<size_t>(mInfo.SizeOfImage);
- m_pModuleBase = reinterpret_cast<uintptr_t>(mInfo.lpBaseOfDll);
- m_nAddress = m_pModuleBase;
-
- if (!m_nModuleSize || !m_pModuleBase)
- return;
-
- m_pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
- m_pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS64*>(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<const char*>(hCurrentSection.Name)),
- static_cast<uintptr_t>(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<uintptr_t>(GetProcAddress(reinterpret_cast<HMODULE>(m_nAddress), pExportName)));
-}
-
-CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask)
-{
- if (!m_ExecutableCode.IsSectionValid())
- return CMemoryAddress();
-
- uint64_t nBase = static_cast<uint64_t>(m_ExecutableCode.m_pSectionBase);
- uint64_t nSize = static_cast<uint64_t>(m_ExecutableCode.m_nSectionSize);
-
- const uint8_t* pData = reinterpret_cast<uint8_t*>(nBase);
- const uint8_t* pEnd = pData + static_cast<uint32_t>(nSize) - strlen(pMask);
-
- int nMasks[64]; // 64*16 = enough masks for 1024 bytes.
- int iNumMasks = static_cast<int>(ceil(static_cast<float>(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<LONG*>(&nMasks[i]), j);
- }
- }
- }
- __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pPattern));
- __m128i xmm2, xmm3, msks;
- for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
- {
- if (pPattern[0] == pData[0])
- {
- xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pData));
- msks = _mm_cmpeq_epi8(xmm1, xmm2);
- if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0])
- {
- for (uintptr_t i = 1; i < static_cast<uintptr_t>(iNumMasks); ++i)
- {
- xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
- xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((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<uint8_t*>(pData));
- }
- }
- else
- goto CONTINUE;
- }
-
- return CMemoryAddress((&*(const_cast<uint8_t*>(pData))));
- }
- }
-
- CONTINUE:;
- }
-
- return CMemoryAddress();
-}
-
-inline std::pair<std::vector<uint8_t>, std::string> MaskedBytesFromPattern(const char* pPatternString)
-{
- std::vector<uint8_t> 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 <typename T> T RCast()
- {
- return reinterpret_cast<T>(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<uint8_t> 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<ModuleSections_t> 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<CommandLineType>();
- Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast<Plat_FloatTimeType>();
- ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast<ThreadInServerFrameThreadType>();
+ CommandLine = module.GetExportedFunction("CommandLine").RCast<CommandLineType>();
+ Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast<Plat_FloatTimeType>();
+ ThreadInServerFrameThread = module.GetExportedFunction("ThreadInServerFrameThread").RCast<ThreadInServerFrameThreadType>();
}
diff --git a/primedev/core/tier1.cpp b/primedev/core/tier1.cpp
new file mode 100644
index 00000000..f857fdba
--- /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
+
+CMemory 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<CreateInterfaceFn>(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..d162e7c8
--- /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);
+
+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..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);
@@ -135,7 +140,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 +267,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/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<CGameConsole>* 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<ModDownloader*>(ptr);
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
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 8a03b857..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"
@@ -37,7 +39,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/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);
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);
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 865b6243..5e685b48 100644
--- a/primedev/scripts/scriptdatatables.cpp
+++ b/primedev/scripts/scriptdatatables.cpp
@@ -59,42 +59,6 @@ struct CSVData
std::unordered_map<std::string, CSVData> 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))
{
@@ -817,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<const char*> VANILLA_DATATABLE_PATHS = {
"datatable/burn_meter_rewards.rpak",
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 <fstream>
+
+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<short>(v1.x);
+ x2 = static_cast<short>(v2.x);
+ y1 = static_cast<short>(v1.y);
+ y2 = static_cast<short>(v2.y);
+ z1 = static_cast<short>(v1.z);
+ z2 = static_cast<short>(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<int64_t> 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<dtNavMesh**>();
+}
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/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/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 44651b3c..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;
}
@@ -103,7 +105,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;
@@ -222,16 +224,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)
{
@@ -413,9 +423,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));
@@ -443,7 +453,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_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/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<INT64(__fastcall*)(DWORD, char*)>();
+ sub_F1320 = module.FindPatternSIMD("83 F9 7F 77 08 88 0A").RCast<INT64(__fastcall*)(DWORD, char*)>();
}
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/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<int (*)(const char*, wchar_t*, int)>();
- V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast<int (*)(const wchar_t*, char*, int)>();
- KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast<CKeyValuesSystem* (*)()>();
+ V_UTF8ToUnicode = module.GetExportedFunction("V_UTF8ToUnicode").RCast<int (*)(const char*, wchar_t*, int)>();
+ V_UnicodeToUTF8 = module.GetExportedFunction("V_UnicodeToUTF8").RCast<int (*)(const wchar_t*, char*, int)>();
+ KeyValuesSystem = module.GetExportedFunction("KeyValuesSystem").RCast<CKeyValuesSystem* (*)()>();
}
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 <class T> void ChangeOffset(CMemoryAddress addr, unsigned int offset)
+template <class T> 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/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 70dedcb8..41a6a782 100644
--- a/primedev/squirrel/squirrel.cpp
+++ b/primedev/squirrel/squirrel.cpp
@@ -316,6 +316,7 @@ template <ScriptContext context> void SquirrelManager<context>::AddFuncOverride(
// hooks
bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm)
{
+ NOTE_UNUSED(context);
return ScriptContext(pSqvm->sharedState->cSquirrelVM->vmContext) == ScriptContext::UI;
}
@@ -334,6 +335,8 @@ template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSquirr
template <ScriptContext context> SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt);
template <ScriptContext context> SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...)
{
+ NOTE_UNUSED(sqvm);
+
va_list va;
va_start(va, fmt);
@@ -715,7 +718,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
// Message buffer stuff
g_pSquirrel<ScriptContext::UI>->messageBuffer = g_pSquirrel<ScriptContext::CLIENT>->messageBuffer;
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x572FB0).RCast<sq_getfunctionType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x6CB0).RCast<sq_getfunctionType>();
g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
g_pSquirrel<ScriptContext::CLIENT>->__sq_stackinfos = module.Offset(0x35970).RCast<sq_stackinfosType>();
g_pSquirrel<ScriptContext::UI>->__sq_stackinfos = g_pSquirrel<ScriptContext::CLIENT>->__sq_stackinfos;
@@ -756,9 +759,6 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
StubUnsafeSQFuncs<ScriptContext::CLIENT>();
StubUnsafeSQFuncs<ScriptContext::UI>();
-
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x6CB0).RCast<sq_getfunctionType>();
- g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
}
ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
@@ -808,7 +808,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
g_pSquirrel<ScriptContext::SERVER>->logger = NS::log::SCRIPT_SV;
// Message buffer stuff
- g_pSquirrel<ScriptContext::SERVER>->__sq_getfunction = module.Offset(0x6C85).RCast<sq_getfunctionType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getfunction = module.Offset(0x6C80).RCast<sq_getfunctionType>();
g_pSquirrel<ScriptContext::SERVER>->__sq_stackinfos = module.Offset(0x35920).RCast<sq_stackinfosType>();
// Structs
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<uint8_t>& vOpcodeArray) const
+{
+ uintptr_t ref = ptr;
+
+ // Loop forward in the ptr class member.
+ for (auto [byteAtCurrentAddress, i] = std::tuple<uint8_t, size_t>{ uint8_t(), (size_t)0 }; i < vOpcodeArray.size(); i++, ref++)
+ {
+ byteAtCurrentAddress = *reinterpret_cast<uint8_t*>(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<LPCVOID>(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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t>& vOpcodeArray) const
+{
+ DWORD oldProt = NULL;
+
+ SIZE_T dwSize = vOpcodeArray.size();
+ VirtualProtect(reinterpret_cast<void*>(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<uint8_t*>(ptr + i) = vOpcodeArray[i]; // Write opcodes to Address.
+ }
+
+ dwSize = vOpcodeArray.size();
+ VirtualProtect(reinterpret_cast<void*>(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<void*>(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<uint8_t*>(ptr + i) = szString[i]; // Write string to Address.
+ }
+
+ VirtualProtect(reinterpret_cast<void*>(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<uint8_t*>(ptr); // Get the base of the module.
+
+ const std::vector<int> PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array.
+ const std::pair<size_t, const int*> bytesInfo = std::make_pair<size_t, const int*>(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<const CHAR*>(static_cast<int64_t>(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<uint8_t*>(ptr); // Get the base of the module.
+
+ const std::vector<int> PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array.
+ const std::pair<size_t, const int*> bytesInfo = std::make_pair<size_t, const int*>(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<const CHAR*>(static_cast<int64_t>(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<int32_t*>(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<int32_t*>(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<CMemory>
+//-----------------------------------------------------------------------------
+std::vector<CMemory> CMemory::FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize)
+{
+ std::vector <CMemory> referencesInfo = {};
+
+ uint8_t* pTextStart = reinterpret_cast<uint8_t*>(sectionBase);
+ for (size_t i = 0ull; i < sectionSize - 0x5; i++, _mm_prefetch(reinterpret_cast<const char*>(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<uintptr_t*>(virtualMethod);
+
+ // Set page for current virtual method to execute n read n write.
+ VirtualProtect(reinterpret_cast<void*>(virtualMethod), sizeof(virtualMethod), PAGE_EXECUTE_READWRITE, &oldProt);
+
+ // Set virtual method to our hook.
+ *reinterpret_cast<uintptr_t*>(virtualMethod) = reinterpret_cast<uintptr_t>(pHookMethod);
+
+ // Restore original page.
+ VirtualProtect(reinterpret_cast<void*>(virtualMethod), sizeof(virtualMethod), oldProt, &oldProt);
+
+ // Move original function into argument.
+ *ppOriginalMethod = reinterpret_cast<void*>(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<uintptr_t*>(pImportedMethod);
+
+ // Set page for current iat entry to execute n read n write.
+ VirtualProtect(reinterpret_cast<void*>(pImportedMethod), sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt);
+
+ // Set method to our hook.
+ *reinterpret_cast<uintptr_t*>(pImportedMethod) = reinterpret_cast<uintptr_t>(pHookMethod);
+
+ // Restore original page.
+ VirtualProtect(reinterpret_cast<void*>(pImportedMethod), sizeof(void*), oldProt, &oldProt);
+
+ // Move original function into argument.
+ *ppOriginalMethod = reinterpret_cast<void*>(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 <iostream>
+#include <string>
+#include <vector>
+#include <windows.h>
+#include <psapi.h>
+
+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<void*>(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<class T> inline T GetValue(void) const
+ {
+ return *reinterpret_cast<T*>(ptr);
+ }
+
+ template<class T> inline T GetVirtualFunctionIndex(void) const
+ {
+ return *reinterpret_cast<T*>(ptr) / 8;
+ }
+
+ template<typename T> inline T CCast(void) const
+ {
+ return (T)ptr;
+ }
+
+ template<typename T> inline T RCast(void) const
+ {
+ return reinterpret_cast<T>(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<uintptr_t*>(reference);
+ }
+
+ return CMemory(reference);
+ }
+
+ inline CMemory DerefSelf(int deref = 1)
+ {
+ while (deref--)
+ {
+ if (ptr)
+ ptr = *reinterpret_cast<uintptr_t*>(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<uint8_t>& 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<uint8_t>& 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<CMemory> 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..84f4da9e
--- /dev/null
+++ b/primedev/thirdparty/silver-bun/module.cpp
@@ -0,0 +1,484 @@
+//===========================================================================//
+//
+// 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)
+{
+ MODULEINFO mInfo {0};
+
+ if (hModule && hModule != INVALID_HANDLE_VALUE)
+ GetModuleInformation(GetCurrentProcess(), hModule, &mInfo, sizeof(MODULEINFO));
+
+ m_nModuleSize = static_cast<size_t>(mInfo.SizeOfImage);
+ m_pModuleBase = reinterpret_cast<uintptr_t>(mInfo.lpBaseOfDll);
+
+ if (!m_nModuleSize || !m_pModuleBase)
+ return;
+
+ CHAR szModuleName[MAX_PATH];
+ DWORD dwSize = GetModuleFileNameA(hModule, szModuleName, sizeof(szModuleName));
+ char* chLast = strrchr(szModuleName, '\\');
+ m_ModuleName = chLast == nullptr ? szModuleName : chLast + 1;
+
+
+ Init();
+ LoadSections();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+// Input : *szModuleName -
+//-----------------------------------------------------------------------------
+CModule::CModule(const char* szModuleName)
+{
+ m_pModuleBase = reinterpret_cast<uintptr_t>(GetModuleHandleA(szModuleName));
+ m_ModuleName = szModuleName;
+
+ Init();
+ LoadSections();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: initializes module descriptors
+//-----------------------------------------------------------------------------
+void CModule::Init()
+{
+ m_pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
+ m_pNTHeaders = reinterpret_cast<decltype(m_pNTHeaders)>(m_pModuleBase + m_pDOSHeader->e_lfanew);
+ m_nModuleSize = static_cast<size_t>(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<const char*>(hCurrentSection.Name),
+ static_cast<uintptr_t>(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<uint8_t*>(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<int>(ceil(static_cast<float>(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<LONG*>(&nMasks[i]), static_cast<LONG>(j));
+ }
+ }
+ }
+ const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pPattern));
+ __m128i xmm2, xmm3, msks;
+ for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
+ {
+ if (pPattern[0] == pData[0])
+ {
+ xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pData));
+ msks = _mm_cmpeq_epi8(xmm1, xmm2);
+ if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0])
+ {
+ for (uintptr_t i = 1; i < static_cast<uintptr_t>(iNumMasks); ++i)
+ {
+ xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
+ xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((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<CMemory>(const_cast<uint8_t*>(pData));
+ }
+ nOccurrenceCount++;
+ }
+ }
+ else
+ {
+ goto cont;
+ }
+ }
+ if (nOccurrenceCount == nOccurrence)
+ {
+ return static_cast<CMemory>((&*(const_cast<uint8_t*>(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::vector<uint8_t>, 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<uint8_t*>(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<uintptr_t>(&pTextStart[i])).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and the register.
+ const int32_t relativeAddress = skipOpCode.GetValue<int32_t>(); // 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<int> vBytes = Utils::StringToBytes(szString, bNullTerminator); // Convert our string to a byte array.
+ const std::pair<size_t, const int*> bytesInfo = std::make_pair<size_t, const int*>(vBytes.size(), vBytes.data()); // Get the size and data of our bytes.
+
+ const uint8_t* pBase = reinterpret_cast<uint8_t*>(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<uintptr_t*>(currAddr) == 0 && checkDataSection(reinterpret_cast<void*>(currAddr), nSize))
+ {
+ bool bIsGoodPage = true;
+ uint32_t nPageCount = 0;
+
+ for (; nPageCount < nSize && bIsGoodPage; nPageCount += sizeof(uintptr_t))
+ {
+ const uintptr_t pageData = *reinterpret_cast<std::uintptr_t*>(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<rsig_t>(&rttiTDRva), "xxxx", &moduleSection, nRefIndex);
+ if (!reference)
+ break;
+
+ CMemory referenceOffset = reference.Offset(-0xC);
+ if (referenceOffset.GetValue<int32_t>() != 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<rsig_t>(&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<IMAGE_IMPORT_DESCRIPTOR*>(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<char*>(reinterpret_cast<DWORD*>(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<IMAGE_THUNK_DATA*>(m_pModuleBase + pIID->OriginalFirstThunk);
+
+ // To get actual function address.
+ IMAGE_THUNK_DATA* pFirstThunk = reinterpret_cast<IMAGE_THUNK_DATA*>(m_pModuleBase + pIID->FirstThunk);
+ for (; pOgFirstThunk->u1.AddressOfData; ++pOgFirstThunk, ++pFirstThunk)
+ {
+ // Get image import by name.
+ const IMAGE_IMPORT_BY_NAME* pImageImportByName = reinterpret_cast<IMAGE_IMPORT_BY_NAME*>(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<uintptr_t*>(&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<IMAGE_EXPORT_DIRECTORY*>(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<DWORD*>(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<DWORD*>(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<DWORD*>(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<char*>(reinterpret_cast<DWORD*>(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<WORD*>(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 <iostream>
+#include <string>
+#include <vector>
+#include <windows.h>
+#include <psapi.h>
+#include <intrin.h>
+#include <algorithm>
+
+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<CModule::ModuleSections_t>& 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<ModuleSections_t> 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<uint8_t> StringPatternToBytes(const char* szInput)
+ {
+ const char* pszPatternStart = const_cast<char*>(szInput);
+ const char* pszPatternEnd = pszPatternStart + strlen(szInput);
+ std::vector<uint8_t> vBytes;
+
+ for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte)
+ {
+ vBytes.push_back(strtoul(pszCurrentByte, const_cast<char**>(&pszCurrentByte), 16));
+ }
+ return vBytes;
+ };
+
+ //----------------------------------------------------------------------------------------
+ // Purpose: For converting a string pattern with wildcards to an array of bytes.
+ //----------------------------------------------------------------------------------------
+ std::vector<int> PatternToBytes(const char* szInput)
+ {
+ const char* pszPatternStart = const_cast<char*>(szInput);
+ const char* pszPatternEnd = pszPatternStart + strlen(szInput);
+ std::vector<int> 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<char**>(&pszCurrentByte), 16));
+ }
+ }
+ return vBytes;
+ };
+
+ //----------------------------------------------------------------------------------------
+ // Purpose: For converting a string pattern with wildcards to an array of bytes and mask.
+ //----------------------------------------------------------------------------------------
+ std::pair<std::vector<uint8_t>, std::string> PatternToMaskedBytes(const char* szInput)
+ {
+ const char* pszPatternStart = const_cast<char*>(szInput);
+ const char* pszPatternEnd = pszPatternStart + strlen(szInput);
+
+ std::vector<uint8_t> 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<char**>(&pszCurrentByte), 16)));
+ svMask += 'x';
+ }
+ }
+ return make_pair(vBytes, svMask);
+ };
+
+ //----------------------------------------------------------------------------------------
+ // Purpose: For converting a string to an array of bytes.
+ //----------------------------------------------------------------------------------------
+ std::vector<int> StringToBytes(const char* szInput, bool bNullTerminator)
+ {
+ const char* pszStringStart = const_cast<char*>(szInput);
+ const char* pszStringEnd = pszStringStart + strlen(szInput);
+ std::vector<int> 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::vector<uint8_t>, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator)
+ {
+ const char* pszStringStart = const_cast<char*>(szInput);
+ const char* pszStringEnd = pszStringStart + strlen(szInput);
+ std::vector<uint8_t> 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 <iostream>
+#include <string>
+#include <vector>
+#include <windows.h>
+#include <psapi.h>
+
+namespace Utils
+{
+ std::vector<uint8_t> StringPatternToBytes(const char* szInput);
+ std::vector<int> PatternToBytes(const char* szInput);
+ std::pair<std::vector<uint8_t>, std::string> PatternToMaskedBytes(const char* szInput);
+ std::vector<int> StringToBytes(const char* szInput, bool bNullTerminator);
+ std::pair<std::vector<uint8_t>, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator);
+}
+
+typedef const unsigned char* rsig_t;
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;
+};
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<std::string, ConCommandBase*> 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<std::string, ConCommandBase*> sorted;
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));
diff --git a/primedev/WSockProxy.cmake b/primedev/wsockproxy/CMakeLists.txt
index 017e358a..0dbac745 100644
--- a/primedev/WSockProxy.cmake
+++ b/primedev/wsockproxy/CMakeLists.txt
@@ -4,11 +4,10 @@ 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"
+ "dllmain.cpp"
+ "loader.cpp"
+ "loader.h"
+ "wsock32.def"
)
target_link_libraries(
@@ -36,7 +35,7 @@ target_link_libraries(
target_precompile_headers(
loader_wsock32_proxy
PRIVATE
- wsockproxy/pch.h
+ pch.h
)
target_compile_definitions(loader_wsock32_proxy PRIVATE UNICODE _UNICODE)
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 <shlwapi.h>
#include <filesystem>
-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 <shlwapi.h>
#include <string>
#include <system_error>
#include <sstream>
@@ -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