aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format6
-rw-r--r--.git-blame-ignore-revs6
-rw-r--r--.github/dependabot.yml9
-rw-r--r--.github/labeler.yml9
-rw-r--r--.github/pull_request_template.md8
-rw-r--r--.github/workflows/auto-label-pr.yml14
-rw-r--r--.github/workflows/ci.yml8
-rw-r--r--.github/workflows/release.yml14
-rw-r--r--.gitignore2
-rw-r--r--BUILD.md12
-rw-r--r--CMakeLists.txt4
-rw-r--r--Dockerfile24
-rw-r--r--README.md2
-rw-r--r--STANDARDS.md53
-rw-r--r--primedev/Northstar.cmake21
-rw-r--r--primedev/client/audio.cpp84
-rw-r--r--primedev/client/clientauthhooks.cpp26
-rw-r--r--primedev/client/clientruihooks.cpp13
-rw-r--r--primedev/client/clientvideooverrides.cpp20
-rw-r--r--primedev/client/debugoverlay.cpp50
-rw-r--r--primedev/client/languagehooks.cpp15
-rw-r--r--primedev/client/latencyflex.cpp13
-rw-r--r--primedev/client/localchatwriter.cpp10
-rw-r--r--primedev/client/modlocalisation.cpp43
-rw-r--r--primedev/client/rejectconnectionfixes.cpp13
-rw-r--r--primedev/core/convar/convar.cpp5
-rw-r--r--primedev/core/convar/convar.h1
-rw-r--r--primedev/core/convar/cvar.cpp1
-rw-r--r--primedev/core/convar/cvar.h1
-rw-r--r--primedev/core/filesystem/filesystem.cpp83
-rw-r--r--primedev/core/filesystem/filesystem.h3
-rw-r--r--primedev/core/filesystem/rpakfilesystem.cpp543
-rw-r--r--primedev/core/filesystem/rpakfilesystem.h86
-rw-r--r--primedev/core/hooks.cpp38
-rw-r--r--primedev/core/hooks.h17
-rw-r--r--primedev/core/math/bitbuf.h115
-rw-r--r--primedev/core/math/color.h80
-rw-r--r--primedev/core/math/vector.h60
-rw-r--r--primedev/core/memalloc.h5
-rw-r--r--primedev/core/sourceinterface.cpp49
-rw-r--r--primedev/core/sourceinterface.h38
-rw-r--r--primedev/core/tier0.cpp29
-rw-r--r--primedev/core/tier1.h6
-rw-r--r--primedev/core/vanilla.h5
-rw-r--r--primedev/dedicated/dedicated.cpp22
-rw-r--r--primedev/dedicated/dedicatedmaterialsystem.cpp25
-rw-r--r--primedev/engine/gl_matsysiface.cpp50
-rw-r--r--primedev/engine/host.cpp13
-rw-r--r--primedev/engine/hoststate.cpp64
-rw-r--r--primedev/engine/runframe.cpp13
-rw-r--r--primedev/game/client/clientmode_shared.cpp66
-rw-r--r--primedev/logging/crashhandler.h40
-rw-r--r--primedev/logging/logging.cpp2
-rw-r--r--primedev/logging/logging.h9
-rw-r--r--primedev/logging/loghooks.cpp56
-rw-r--r--primedev/logging/sourceconsole.cpp32
-rw-r--r--primedev/logging/sourceconsole.h3
-rw-r--r--primedev/masterserver/masterserver.cpp7
-rw-r--r--primedev/materialsystem/cmaterialglue.h47
-rw-r--r--primedev/materialsystem/cshaderglue.h7
-rw-r--r--primedev/mods/autodownload/moddownloader.cpp42
-rw-r--r--primedev/mods/autodownload/moddownloader.h19
-rw-r--r--primedev/mods/compiled/modkeyvalues.cpp2
-rw-r--r--primedev/mods/modmanager.cpp51
-rw-r--r--primedev/mods/modmanager.h29
-rw-r--r--primedev/mods/modsavefiles.cpp2
-rw-r--r--primedev/plugins/interfaces/IPluginId.h1
-rw-r--r--primedev/plugins/interfaces/interface.cpp4
-rw-r--r--primedev/plugins/interfaces/sys/ISys.cpp1
-rw-r--r--primedev/plugins/plugins.cpp15
-rw-r--r--primedev/plugins/plugins.h3
-rw-r--r--primedev/scripts/client/clientchathooks.cpp13
-rw-r--r--primedev/scripts/scriptdatatables.cpp19
-rw-r--r--primedev/scripts/scripthttprequesthandler.cpp10
-rw-r--r--primedev/scripts/scripthttprequesthandler.h5
-rw-r--r--primedev/scripts/scriptjson.cpp12
-rw-r--r--primedev/scripts/scriptjson.h2
-rw-r--r--primedev/server/ai_navmesh.h20
-rw-r--r--primedev/server/auth/serverauthentication.cpp67
-rw-r--r--primedev/server/buildainfile.cpp24
-rw-r--r--primedev/squirrel/squirrel.cpp22
-rw-r--r--primedev/squirrel/squirrel.h143
-rw-r--r--primedev/squirrel/squirrelautobind.h8
-rw-r--r--primedev/squirrel/squirrelclasstypes.h140
-rw-r--r--primedev/squirrel/squirreldatatypes.h501
-rw-r--r--primedev/thirdparty/silver-bun/module.cpp19
-rw-r--r--primedev/thirdparty/silver-bun/module.h2
-rw-r--r--primedev/util/utils.h10
-rw-r--r--primedev/vscript/languages/squirrel_re/include/squirrel.h95
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqarray.h12
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqclosure.h29
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqcompiler.h26
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqfunctionproto.h24
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqlexer.h9
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqobject.h93
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqopcodes.h13
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqstate.h120
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqstring.h15
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqstruct.h24
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqtable.h21
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/squserdata.h15
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqvector.h12
-rw-r--r--primedev/vscript/languages/squirrel_re/squirrel/sqvm.h69
-rw-r--r--primedev/vscript/languages/squirrel_re/vsquirrel.h16
-rw-r--r--primedev/vscript/vscript.h20
-rw-r--r--primedev/windows/libsys.cpp16
106 files changed, 2233 insertions, 1682 deletions
diff --git a/.clang-format b/.clang-format
index 2ad2ec20..fb7ca9cf 100644
--- a/.clang-format
+++ b/.clang-format
@@ -7,7 +7,7 @@ AccessModifierOffset: -4
AlignTrailingComments: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
-AllowShortFunctionsOnASingleLine: Empty
+AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: No
@@ -35,5 +35,5 @@ IndentExternBlock: Indent
PointerAlignment: Left
SortIncludes: false
NamespaceIndentation: All
-PackConstructorInitializers: NextLine
-BreakConstructorInitializersBeforeComma: true
+PackConstructorInitializers: Never
+BreakConstructorInitializers: BeforeComma
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 10640d70..c104bb38 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -5,3 +5,9 @@
# Folder rename
f9bc3c9d1834cb8bd5f872b749b057c33e8b0102
+
+# Clang format change: one-line inline functions
+5b2c608b22ba272e4ab1a45adc1f43b60b1aea79
+
+# Clang format change: see PR #775
+6e9792f3651d1e0c7045c5d67312c10f91ce6962
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..786ba213
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,9 @@
+# Configures dependabot
+
+version: 2
+updates:
+ # GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "monthly"
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 00000000..cc83d7cd
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,9 @@
+# Add 'needs code review' label to any changes within the entire repository
+needs code review:
+- changed-files:
+ - any-glob-to-any-file: '**'
+
+# Add 'needs testing' label to any changes within the entire repository
+needs testing:
+- changed-files:
+ - any-glob-to-any-file: '**'
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 727a3060..ba2b0cfb 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -16,3 +16,11 @@ Note that commit messages in PRs will generally be squashed to keep commit histo
-->
Replace this line with a description of your change (and screenshots/screenrecordings if applicable).
+
+### Code review:
+
+Replace this line with anything specific to look out for during code reviews.
+
+### Testing:
+
+Replace this line with instructions on how to test your pull request. The more detailed, the easier it is for reviewers to test, the faster your PR gets merged.
diff --git a/.github/workflows/auto-label-pr.yml b/.github/workflows/auto-label-pr.yml
new file mode 100644
index 00000000..659ff351
--- /dev/null
+++ b/.github/workflows/auto-label-pr.yml
@@ -0,0 +1,14 @@
+name: Auto-Labeler
+on:
+ pull_request_target:
+ types:
+ - opened
+
+jobs:
+ labeler:
+ permissions:
+ contents: read
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v5
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e562dc3b..5f647faa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
- name: Setup msvc
uses: ilammy/msvc-dev-cmd@v1
- name: Configure cmake
- run: cmake -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="${{ env.BUILD_PROFILE }}"
+ run: cmake -G "Ninja" -B build -DCMAKE_BUILD_TYPE:STRING="${{ env.BUILD_PROFILE }}"
- name: Setup resource file version
shell: bash
run: |
@@ -33,7 +33,7 @@ jobs:
FILEVERSION=$(echo ${{ env.NORTHSTAR_VERSION }} | tr '.' ',' | sed -E 's/-rc[0-9]+//' | tr -d '[:alpha:]')
sed -i "s/0,0,0,1/${FILEVERSION}/g" primedev/ns_version.h
- name: Build
- run: cmake --build .
+ run: cmake --build build/
- name: Extract Short Commit Hash
id: extract
shell: bash
@@ -43,13 +43,13 @@ jobs:
with:
name: NorthstarLauncher-${{ matrix.config.name }}-${{ steps.extract.outputs.commit }}
path: |
- game/
+ build/game/
format-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: DoozyX/clang-format-lint-action@v0.16.2
+ - uses: DoozyX/clang-format-lint-action@v0.18.2
with:
source: 'primedev'
exclude: 'primedev/include primedev/thirdparty primedev/wsockproxy'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e6dd8cc3..a440aea3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,7 +21,7 @@ jobs:
- name: Setup msvc
uses: ilammy/msvc-dev-cmd@v1
- name: Configure cmake
- run: cmake -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="Release"
+ run: cmake -G "Ninja" -B build -DCMAKE_BUILD_TYPE:STRING="Release"
- name: Setup resource file version
shell: bash
run: |
@@ -30,22 +30,22 @@ jobs:
FILEVERSION=$(echo ${{ env.NORTHSTAR_VERSION }} | tr '.' ',' | sed -E 's/-rc[0-9]+//' | tr -d '[:alpha:]')
sed -i "s/0,0,0,1/${FILEVERSION}/g" primedev/ns_version.h
- name: Build
- run: cmake --build .
+ run: cmake --build build/
- name: Upload launcher build as artifact
uses: actions/upload-artifact@v3
with:
name: northstar-launcher
path: |
- game/*.exe
- game/*.dll
- game/bin/x64_retail/*.dll
+ build/game/*.exe
+ build/game/*.dll
+ build/game/bin/x64_retail/*.dll
- name: Upload debug build artifact
uses: actions/upload-artifact@v3
with:
name: launcher-debug-files
path: |
- game/*.pdb
- game/bin/x64_retail/*.pdb
+ build/game/*.pdb
+ build/game/bin/x64_retail/*.pdb
upload-launcher-to-release:
if: startsWith(github.ref, 'refs/tags/v')
diff --git a/.gitignore b/.gitignore
index c3c50a40..5e881b2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,7 @@ mono_crash.*
# CMake output
out/
-game/
+build/game/
build/
CMakeFiles/
cmake_install.cmake
diff --git a/BUILD.md b/BUILD.md
index f0ed5e72..76f20eb6 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -37,9 +37,9 @@ Developers who can work a command line may be interested in using [Visual Studio
- Follow the same steps as above for Visual Studio Build Tools, but instead of opening in Visual Studio, run the Command Prompt for VS 2022 and navigate to the NorthstarLauncher.
-- Run `cmake . -G "Ninja"` to generate build files.
+- Run `cmake . -G "Ninja" -B build` to generate build files.
-- Run `cmake --build .` to build the project.
+- Run `cmake --build build/` to build the project.
## Linux
### Steps
@@ -47,8 +47,8 @@ Developers who can work a command line may be interested in using [Visual Studio
2. Use `cd` to navigate to the cloned repo's directory
3. Then, run the following commands in order:
* `docker build --rm -t northstar-build-fedora .`
-* `docker run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build northstar-build-fedora cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -G "Ninja"`
-* `docker run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build northstar-build-fedora cmake --build .`
+* `docker run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build northstar-build-fedora cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -G "Ninja" -B build`
+* `docker run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build northstar-build-fedora cmake --build build/`
#### Podman
@@ -57,5 +57,5 @@ When using [`podman`](https://podman.io/) instead of Docker on an SELinux enable
As such the corresponding commands are
* `podman build --rm -t northstar-build-fedora .`
-* `podman run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build,z northstar-build-fedora cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -G "Ninja"`
-* `podman run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build,z northstar-build-fedora cmake --build .`
+* `podman run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build,z northstar-build-fedora cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_NAME=Windows -G "Ninja" -B build`
+* `podman run --rm -it -e CC=cl -e CXX=cl --mount type=bind,source="$(pwd)",destination=/build,z northstar-build-fedora cmake --build build/`
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a9646ae1..c9516e52 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -50,3 +50,7 @@ include_directories(primedev/thirdparty)
# Targets
add_subdirectory(primedev)
+# Forces Minizip to not use functions that are not available in Win 7 libraries.
+if(WIN32)
+ target_compile_definitions(minizip PUBLIC -D_WIN32_WINNT=0x0601)
+endif()
diff --git a/Dockerfile b/Dockerfile
index 261d649f..af1bf341 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,18 +1,18 @@
FROM registry.fedoraproject.org/fedora-toolbox:38
RUN dnf update -y && \
dnf install -y \
- git \
- wine \
- wine-mono \
- python3 \
- msitools \
- python3-simplejson \
- python3-six \
- cmake \
- ninja-build \
- make \
- samba \
- libunwind && \
+ git \
+ wine \
+ wine-mono \
+ python3 \
+ msitools \
+ python3-simplejson \
+ python3-six \
+ cmake \
+ ninja-build \
+ make \
+ samba \
+ libunwind && \
dnf clean all && \
mkdir /opt/msvc/ /build
diff --git a/README.md b/README.md
index 80be8fd0..64812201 100644
--- a/README.md
+++ b/README.md
@@ -9,4 +9,6 @@ Check [BUILD.md](BUILD.md) for instructions on how to compile, you can also down
## Format
+For project coding standards check out [STANDARDS.md](STANDARDS.md).
+
This project uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html), make sure you run `clang-format -i --style=file --exclude=primedev/include primedev/*.cpp primedev/*.h` when opening a Pull Request. Check the tool's website for instructions on how to integrate it with your IDE.
diff --git a/STANDARDS.md b/STANDARDS.md
new file mode 100644
index 00000000..89d36523
--- /dev/null
+++ b/STANDARDS.md
@@ -0,0 +1,53 @@
+# Code standards
+
+There are exceptions, ask for them!
+
+### Preamble
+
+You are more than welcome to reformat any existing files using these rules should they fail to match them but please SPLIT your formatting changes from the rest by making a separate PR!
+
+### General rules
+
+> Basic rules that apply all the time.
+
+Always assert your assumptions!
+
+Use PascalCase for all names.
+
+Suffix structs with `_t`.
+
+Prefix classes with `C` (class) and `I` (abstract class).
+
+Prefix all class member variables with `m_`.
+
+Prefixes `g_` for global variables and `s_` for static variables are welcome.
+
+For hooking we use `o_<function name>` for function pointers pointing to the original implementation and `h_<function name>` for functions we replace them with.
+
+Document all function implementations and their arguments (if the argument is self explanatory you don't need to document it) valve style:
+```
+//-----------------------------------------------------------------------------
+// Purpose: MH_MakeHook wrapper
+// Input : *ppOriginal - Original function being detoured
+// pDetour - Detour function
+// Output : true on success, false otherwise
+//-----------------------------------------------------------------------------
+```
+
+Don't overcomment your code unless nescessary, expect the reader to have limited knowledge.
+
+Use `FIXME` comments for possible improvements/issues, `NOTE` for important information one might want to look into.
+
+### Valve source files
+
+> Rules that apply to all files from original valve code base.
+
+When adding or just modifying a file that's present in valve source place it where valve put it.
+
+Always use hungarian notation in these files.
+
+### New files
+
+> Rules that apply to Respawn or our own files.
+
+When adding new files follow the general rules, you don't have to use hungarian notation. Put the file where you think it makes the most sense.
diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake
index 9e9d1ed6..35383e69 100644
--- a/primedev/Northstar.cmake
+++ b/primedev/Northstar.cmake
@@ -52,7 +52,6 @@ add_library(
"core/memalloc.cpp"
"core/memalloc.h"
"core/sourceinterface.cpp"
- "core/sourceinterface.h"
"core/tier0.cpp"
"core/tier0.h"
"core/tier1.cpp"
@@ -62,12 +61,14 @@ add_library(
"dedicated/dedicatedlogtoclient.cpp"
"dedicated/dedicatedlogtoclient.h"
"dedicated/dedicatedmaterialsystem.cpp"
+ "engine/gl_matsysiface.cpp"
"engine/host.cpp"
"engine/hoststate.cpp"
"engine/hoststate.h"
"engine/r2engine.cpp"
"engine/r2engine.h"
"engine/runframe.cpp"
+ "game/client/clientmode_shared.cpp"
"logging/crashhandler.cpp"
"logging/crashhandler.h"
"logging/logging.cpp"
@@ -150,7 +151,6 @@ add_library(
"squirrel/squirrelautobind.cpp"
"squirrel/squirrelautobind.h"
"squirrel/squirrelclasstypes.h"
- "squirrel/squirreldatatypes.h"
"util/printcommands.cpp"
"util/printcommands.h"
"util/printmaps.cpp"
@@ -161,6 +161,23 @@ add_library(
"util/version.h"
"util/wininfo.cpp"
"util/wininfo.h"
+ "vscript/languages/squirrel_re/include/squirrel.h"
+ "vscript/languages/squirrel_re/squirrel/sqarray.h"
+ "vscript/languages/squirrel_re/squirrel/sqclosure.h"
+ "vscript/languages/squirrel_re/squirrel/sqcompiler.h"
+ "vscript/languages/squirrel_re/squirrel/sqfunctionproto.h"
+ "vscript/languages/squirrel_re/squirrel/sqlexer.h"
+ "vscript/languages/squirrel_re/squirrel/sqobject.h"
+ "vscript/languages/squirrel_re/squirrel/sqopcodes.h"
+ "vscript/languages/squirrel_re/squirrel/sqstate.h"
+ "vscript/languages/squirrel_re/squirrel/sqstring.h"
+ "vscript/languages/squirrel_re/squirrel/sqstruct.h"
+ "vscript/languages/squirrel_re/squirrel/sqtable.h"
+ "vscript/languages/squirrel_re/squirrel/squserdata.h"
+ "vscript/languages/squirrel_re/squirrel/sqvector.h"
+ "vscript/languages/squirrel_re/squirrel/sqvm.h"
+ "vscript/languages/squirrel_re/vsquirrel.h"
+ "vscript/vscript.h"
"windows/libsys.cpp"
"windows/libsys.h"
"dllmain.cpp"
diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp
index 63501414..4a759cda 100644
--- a/primedev/client/audio.cpp
+++ b/primedev/client/audio.cpp
@@ -11,8 +11,6 @@
namespace fs = std::filesystem;
-AUTOHOOK_INIT()
-
static const char* pszAudioEventName;
ConVar* Cvar_mileslog_enable;
@@ -389,14 +387,12 @@ bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptr<EventOver
return true; // good to go
}
-// clang-format off
-AUTOHOOK(LoadSampleMetadata, mileswin64.dll + 0xF110,
-bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType))
-// clang-format on
+static bool(__fastcall* o_pLoadSampleMetadata)(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType) = nullptr;
+static bool __fastcall h_LoadSampleMetadata(void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType)
{
// Raw source, used for voice data only
if (audioType == 0)
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
+ return o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
const char* eventName = pszAudioEventName;
@@ -423,7 +419,7 @@ bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLeng
if (!overrideData)
// not found either
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
+ return o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
else
{
// cache found pattern to improve performance
@@ -437,7 +433,7 @@ bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLeng
overrideData = iter->second;
if (!ShouldPlayAudioEvent(eventName, overrideData))
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
+ return o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
void* data = 0;
unsigned int dataLength = 0;
@@ -479,7 +475,7 @@ bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLeng
if (!data)
{
spdlog::warn("Could not fetch override sample data for event {}! Using original data instead.", eventName);
- return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
+ return o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
}
audioBuffer = data;
@@ -490,26 +486,22 @@ bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLeng
*(unsigned int*)((uintptr_t)sample + 0xF0) = audioBufferLength;
// 64 - Auto-detect sample type
- bool res = LoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64);
+ bool res = o_pLoadSampleMetadata(sample, audioBuffer, audioBufferLength, 64);
if (!res)
spdlog::error("LoadSampleMetadata failed! The game will crash :(");
return res;
}
-// clang-format off
-AUTOHOOK(sub_1800294C0, mileswin64.dll + 0x294C0,
-void*, __fastcall, (void* a1, void* a2))
-// clang-format on
+static void*(__fastcall* o_pSub_1800294C0)(void* a1, void* a2) = nullptr;
+static void* __fastcall h_Sub_1800294C0(void* a1, void* a2)
{
pszAudioEventName = reinterpret_cast<const char*>((*((__int64*)a2 + 6)));
- return sub_1800294C0(a1, a2);
+ return o_pSub_1800294C0(a1, a2);
}
-// clang-format off
-AUTOHOOK(MilesLog, client.dll + 0x57DAD0,
-void, __fastcall, (int level, const char* string))
-// clang-format on
+static void(__fastcall* o_pMilesLog)(int level, const char* string) = nullptr;
+static void __fastcall h_MilesLog(int level, const char* string)
{
if (!Cvar_mileslog_enable->GetBool())
return;
@@ -517,6 +509,55 @@ void, __fastcall, (int level, const char* string))
spdlog::info("[MSS] {} - {}", level, string);
}
+static void(__fastcall* o_pSub_18003EBD0)(DWORD dwThreadID, const char* threadName) = nullptr;
+static void __fastcall h_Sub_18003EBD0(DWORD dwThreadID, const char* threadName)
+{
+ HANDLE hThread = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, dwThreadID);
+
+ if (hThread != NULL)
+ {
+ // TODO: This "method" of "charset conversion" from string to wstring is abhorrent. Change it to a proper one
+ // as soon as Northstar has some helper function to do proper charset conversions.
+ auto tmp = std::string(threadName);
+ HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription);
+ _SetThreadDescription(hThread, std::wstring(tmp.begin(), tmp.end()).c_str());
+
+ CloseHandle(hThread);
+ }
+
+ o_pSub_18003EBD0(dwThreadID, threadName);
+}
+
+static char*(__fastcall* o_pSub_18003BC10)(void* a1, void* a2, void* a3, void* a4, void* a5, int a6) = nullptr;
+static char* __fastcall h_Sub_18003BC10(void* a1, void* a2, void* a3, void* a4, void* a5, int a6)
+{
+ HANDLE hThread;
+ char* ret = o_pSub_18003BC10(a1, a2, a3, a4, a5, a6);
+
+ if (ret != NULL && (hThread = reinterpret_cast<HANDLE>(*((uint64_t*)ret + 55))) != NULL)
+ {
+ HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription);
+ _SetThreadDescription(hThread, L"[Miles] WASAPI Service Thread");
+ }
+
+ return ret;
+}
+
+ON_DLL_LOAD("mileswin64.dll", MilesWin64_Audio, (CModule module))
+{
+ o_pLoadSampleMetadata = module.Offset(0xF110).RCast<decltype(o_pLoadSampleMetadata)>();
+ HookAttach(&(PVOID&)o_pLoadSampleMetadata, (PVOID)h_LoadSampleMetadata);
+
+ o_pSub_1800294C0 = module.Offset(0x294C0).RCast<decltype(o_pSub_1800294C0)>();
+ HookAttach(&(PVOID&)o_pSub_1800294C0, (PVOID)h_Sub_1800294C0);
+
+ o_pSub_18003EBD0 = module.Offset(0x3EBD0).RCast<decltype(o_pSub_18003EBD0)>();
+ HookAttach(&(PVOID&)o_pSub_18003EBD0, (PVOID)h_Sub_18003EBD0);
+
+ o_pSub_18003BC10 = module.Offset(0x3BC10).RCast<decltype(o_pSub_18003BC10)>();
+ HookAttach(&(PVOID&)o_pSub_18003BC10, (PVOID)h_Sub_18003BC10);
+}
+
ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module))
{
Cvar_mileslog_enable = new ConVar("mileslog_enable", "0", FCVAR_NONE, "Enables/disables whether the mileslog func should be logged");
@@ -524,7 +565,8 @@ ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module))
ON_DLL_LOAD_CLIENT_RELIESON("client.dll", AudioHooks, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pMilesLog = module.Offset(0x57DAD0).RCast<decltype(o_pMilesLog)>();
+ HookAttach(&(PVOID&)o_pMilesLog, (PVOID)h_MilesLog);
Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, "");
MilesStopAll = module.Offset(0x580850).RCast<MilesStopAll_Type>();
diff --git a/primedev/client/clientauthhooks.cpp b/primedev/client/clientauthhooks.cpp
index 35ae3aa7..ceb648a5 100644
--- a/primedev/client/clientauthhooks.cpp
+++ b/primedev/client/clientauthhooks.cpp
@@ -3,8 +3,6 @@
#include "client/r2client.h"
#include "core/vanilla.h"
-AUTOHOOK_INIT()
-
ConVar* Cvar_ns_has_agreed_to_send_token;
// mirrored in script
@@ -12,16 +10,14 @@ const int NOT_DECIDED_TO_SEND_TOKEN = 0;
const int AGREED_TO_SEND_TOKEN = 1;
const int DISAGREED_TO_SEND_TOKEN = 2;
-// clang-format off
-AUTOHOOK(AuthWithStryder, engine.dll + 0x1843A0,
-void, __fastcall, (void* a1))
-// clang-format on
+static void (*__fastcall o_pAuthWithStryder)(void* a1) = nullptr;
+static void __fastcall h_AuthWithStryder(void* a1)
{
// don't attempt to do Atlas auth if we are in vanilla compatibility mode
// this prevents users from joining untrustworthy servers (unless they use a concommand or something)
if (g_pVanillaCompatibility->GetVanillaCompatibility())
{
- AuthWithStryder(a1);
+ o_pAuthWithStryder(a1);
return;
}
@@ -38,15 +34,13 @@ void, __fastcall, (void* a1))
*g_pLocalPlayerOriginToken = 0;
}
- AuthWithStryder(a1);
+ o_pAuthWithStryder(a1);
}
char* p3PToken;
-// clang-format off
-AUTOHOOK(Auth3PToken, engine.dll + 0x183760,
-char*, __fastcall, ())
-// clang-format on
+static char* (*__fastcall o_pAuth3PToken)() = nullptr;
+static char* __fastcall h_Auth3PToken()
{
if (!g_pVanillaCompatibility->GetVanillaCompatibility() && g_pMasterServerManager->m_sOwnClientAuthToken[0])
{
@@ -54,12 +48,16 @@ char*, __fastcall, ())
strcpy(p3PToken, "Protocol 3: Protect the Pilot");
}
- return Auth3PToken();
+ return o_pAuth3PToken();
}
ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", ClientAuthHooks, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pAuthWithStryder = module.Offset(0x1843A0).RCast<decltype(o_pAuthWithStryder)>();
+ HookAttach(&(PVOID&)o_pAuthWithStryder, (PVOID)h_AuthWithStryder);
+
+ o_pAuth3PToken = module.Offset(0x183760).RCast<decltype(o_pAuth3PToken)>();
+ HookAttach(&(PVOID&)o_pAuth3PToken, (PVOID)h_Auth3PToken);
p3PToken = module.Offset(0x13979D80).RCast<char*>();
diff --git a/primedev/client/clientruihooks.cpp b/primedev/client/clientruihooks.cpp
index ad50d11a..e49e6f63 100644
--- a/primedev/client/clientruihooks.cpp
+++ b/primedev/client/clientruihooks.cpp
@@ -1,23 +1,20 @@
#include "core/convar/convar.h"
-AUTOHOOK_INIT()
-
ConVar* Cvar_rui_drawEnable;
-// clang-format off
-AUTOHOOK(DrawRUIFunc, engine.dll + 0xFC500,
-bool, __fastcall, (void* a1, float* a2))
-// clang-format on
+static bool (*__fastcall o_pDrawRUIFunc)(void* a1, float* a2) = nullptr;
+static bool __fastcall h_DrawRUIFunc(void* a1, float* a2)
{
if (!Cvar_rui_drawEnable->GetBool())
return 0;
- return DrawRUIFunc(a1, a2);
+ return o_pDrawRUIFunc(a1, a2);
}
ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", RUI, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pDrawRUIFunc = module.Offset(0xFC500).RCast<decltype(o_pDrawRUIFunc)>();
+ HookAttach(&(PVOID&)o_pDrawRUIFunc, (PVOID)h_DrawRUIFunc);
Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn");
}
diff --git a/primedev/client/clientvideooverrides.cpp b/primedev/client/clientvideooverrides.cpp
index d8aa2754..54bb6469 100644
--- a/primedev/client/clientvideooverrides.cpp
+++ b/primedev/client/clientvideooverrides.cpp
@@ -1,11 +1,7 @@
#include "mods/modmanager.h"
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK_PROCADDRESS(BinkOpen, bink2w64.dll, BinkOpen,
-void*, __fastcall, (const char* path, uint32_t flags))
-// clang-format on
+static void* (*__fastcall o_pBinkOpen)(const char* path, uint32_t flags) = nullptr;
+static void* __fastcall h_BinkOpen(const char* path, uint32_t flags)
{
std::string filename(fs::path(path).filename().string());
spdlog::info("BinkOpen {}", filename);
@@ -25,16 +21,20 @@ void*, __fastcall, (const char* path, uint32_t flags))
{
// create new path
fs::path binkPath(fileOwner->m_ModDirectory / "media" / filename);
- return BinkOpen(binkPath.string().c_str(), flags);
+ return o_pBinkOpen(binkPath.string().c_str(), flags);
}
else
- return BinkOpen(path, flags);
+ return o_pBinkOpen(path, flags);
}
-ON_DLL_LOAD_CLIENT("engine.dll", BinkVideo, (CModule module))
+ON_DLL_LOAD_CLIENT("bink2w64.dll", BinkRead, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pBinkOpen = module.GetExportedFunction("BinkOpen").RCast<decltype(o_pBinkOpen)>();
+ HookAttach(&(PVOID&)o_pBinkOpen, (PVOID)h_BinkOpen);
+}
+ON_DLL_LOAD_CLIENT("engine.dll", BinkVideo, (CModule module))
+{
// remove engine check for whether the bik we're trying to load exists in r2/media, as this will fail for biks in mods
// note: the check in engine is actually unnecessary, so it's just useless in practice and we lose nothing by removing it
module.Offset(0x459AD).NOP(6);
diff --git a/primedev/client/debugoverlay.cpp b/primedev/client/debugoverlay.cpp
index a67b8355..9cfc1612 100644
--- a/primedev/client/debugoverlay.cpp
+++ b/primedev/client/debugoverlay.cpp
@@ -5,8 +5,6 @@
#include "core/math/vector.h"
#include "server/ai_helper.h"
-AUTOHOOK_INIT()
-
enum OverlayType_t
{
OVERLAY_BOX = 0,
@@ -41,10 +39,7 @@ struct OverlayBase_t
struct OverlayLine_t : public OverlayBase_t
{
- OverlayLine_t()
- {
- m_Type = OVERLAY_LINE;
- }
+ OverlayLine_t() { m_Type = OVERLAY_LINE; }
Vector3 origin;
Vector3 dest;
@@ -57,10 +52,7 @@ struct OverlayLine_t : public OverlayBase_t
struct OverlayBox_t : public OverlayBase_t
{
- OverlayBox_t()
- {
- m_Type = OVERLAY_BOX;
- }
+ OverlayBox_t() { m_Type = OVERLAY_BOX; }
Vector3 origin;
Vector3 mins;
@@ -74,10 +66,7 @@ struct OverlayBox_t : public OverlayBase_t
struct OverlayTriangle_t : public OverlayBase_t
{
- OverlayTriangle_t()
- {
- m_Type = OVERLAY_TRIANGLE;
- }
+ OverlayTriangle_t() { m_Type = OVERLAY_TRIANGLE; }
Vector3 p1;
Vector3 p2;
@@ -91,10 +80,7 @@ struct OverlayTriangle_t : public OverlayBase_t
struct OverlaySweptBox_t : public OverlayBase_t
{
- OverlaySweptBox_t()
- {
- m_Type = OVERLAY_SWEPT_BOX;
- }
+ OverlaySweptBox_t() { m_Type = OVERLAY_SWEPT_BOX; }
Vector3 start;
Vector3 end;
@@ -109,10 +95,7 @@ struct OverlaySweptBox_t : public OverlayBase_t
struct OverlaySphere_t : public OverlayBase_t
{
- OverlaySphere_t()
- {
- m_Type = OVERLAY_SPHERE;
- }
+ OverlaySphere_t() { m_Type = OVERLAY_SPHERE; }
Vector3 vOrigin;
float flRadius;
@@ -137,10 +120,8 @@ OverlayBase_t** s_pOverlays;
int* g_nRenderTickCount;
int* g_nOverlayTickCount;
-// clang-format off
-AUTOHOOK(DrawOverlay, engine.dll + 0xABCB0,
-void, __fastcall, (OverlayBase_t * pOverlay))
-// clang-format on
+static void(__fastcall* o_pDrawOverlay)(OverlayBase_t* pOverlay) = nullptr;
+static void __fastcall h_DrawOverlay(OverlayBase_t* pOverlay)
{
EnterCriticalSection(s_OverlayMutex);
@@ -220,10 +201,8 @@ void, __fastcall, (OverlayBase_t * pOverlay))
LeaveCriticalSection(s_OverlayMutex);
}
-// clang-format off
-AUTOHOOK(DrawAllOverlays, engine.dll + 0xAB780,
-void, __fastcall, (bool bRender))
-// clang-format on
+static void(__fastcall* o_pDrawAllOverlays)(bool bRender) = nullptr;
+static void __fastcall h_DrawAllOverlays(bool bRender)
{
EnterCriticalSection(s_OverlayMutex);
@@ -274,10 +253,7 @@ void, __fastcall, (bool bRender))
if (bShouldDraw && bRender && (Cvar_enable_debug_overlays->GetBool() || pCurrOverlay->m_Type == OVERLAY_SMARTAMMO))
{
- // call the new function, not the original
- // note: if there is a beter way to call the hooked version of an
- // autohook func then that would be better than this
- __autohookfuncDrawOverlay(pCurrOverlay);
+ h_DrawOverlay(pCurrOverlay);
}
pPrevOverlay = pCurrOverlay;
@@ -295,7 +271,11 @@ void, __fastcall, (bool bRender))
ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pDrawOverlay = module.Offset(0xABCB0).RCast<decltype(o_pDrawOverlay)>();
+ HookAttach(&(PVOID&)o_pDrawOverlay, (PVOID)h_DrawOverlay);
+
+ o_pDrawAllOverlays = module.Offset(0xAB780).RCast<decltype(o_pDrawAllOverlays)>();
+ HookAttach(&(PVOID&)o_pDrawAllOverlays, (PVOID)h_DrawAllOverlays);
OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast<decltype(OverlayBase_t__IsDead)>();
OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<decltype(OverlayBase_t__DestroyOverlay)>();
diff --git a/primedev/client/languagehooks.cpp b/primedev/client/languagehooks.cpp
index 36b5d5ae..80d3c31d 100644
--- a/primedev/client/languagehooks.cpp
+++ b/primedev/client/languagehooks.cpp
@@ -5,8 +5,6 @@
namespace fs = std::filesystem;
-AUTOHOOK_INIT()
-
typedef LANGID (*Tier0_DetectDefaultLanguageType)();
bool CheckLangAudioExists(char* lang)
@@ -48,10 +46,8 @@ std::string GetAnyInstalledAudioLanguage()
return "NO LANGUAGE DETECTED";
}
-// clang-format off
-AUTOHOOK(GetGameLanguage, tier0.dll + 0xF560,
-char*, __fastcall, ())
-// clang-format on
+static char*(__fastcall* o_pGetGameLanguage)() = nullptr;
+static char* __fastcall h_GetGameLanguage()
{
auto tier0Handle = GetModuleHandleA("tier0.dll");
auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage");
@@ -78,7 +74,7 @@ char*, __fastcall, ())
canOriginDictateLang = true; // let it try
{
- auto lang = GetGameLanguage();
+ auto lang = o_pGetGameLanguage();
if (!CheckLangAudioExists(lang))
{
if (strcmp(lang, "russian") !=
@@ -96,7 +92,7 @@ char*, __fastcall, ())
Tier0_DetectDefaultLanguageType(); // force the global in tier0 to be populated with language inferred from user's system rather than
// defaulting to Russian
canOriginDictateLang = false; // Origin has no say anymore, we will fallback to user's system setup language
- auto lang = GetGameLanguage();
+ auto lang = o_pGetGameLanguage();
spdlog::info("Detected system language: {}", lang);
if (!CheckLangAudioExists(lang))
{
@@ -113,5 +109,6 @@ char*, __fastcall, ())
ON_DLL_LOAD_CLIENT("tier0.dll", LanguageHooks, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pGetGameLanguage = module.Offset(0xF560).RCast<decltype(o_pGetGameLanguage)>();
+ HookAttach(&(PVOID&)o_pGetGameLanguage, (PVOID)h_GetGameLanguage);
}
diff --git a/primedev/client/latencyflex.cpp b/primedev/client/latencyflex.cpp
index 39557870..be93fabd 100644
--- a/primedev/client/latencyflex.cpp
+++ b/primedev/client/latencyflex.cpp
@@ -1,20 +1,16 @@
#include "core/convar/convar.h"
-AUTOHOOK_INIT()
-
ConVar* Cvar_r_latencyflex;
void (*m_winelfx_WaitAndBeginFrame)();
-// clang-format off
-AUTOHOOK(OnRenderStart, client.dll + 0x1952C0,
-void, __fastcall, ())
-// clang-format on
+void(__fastcall* o_pOnRenderStart)() = nullptr;
+void __fastcall h_OnRenderStart()
{
if (Cvar_r_latencyflex->GetBool() && m_winelfx_WaitAndBeginFrame)
m_winelfx_WaitAndBeginFrame();
- OnRenderStart();
+ o_pOnRenderStart();
}
ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, (CModule module))
@@ -36,7 +32,8 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, (CModule module))
return;
}
- AUTOHOOK_DISPATCH()
+ o_pOnRenderStart = module.Offset(0x1952C0).RCast<decltype(o_pOnRenderStart)>();
+ HookAttach(&(PVOID&)o_pOnRenderStart, (PVOID)h_OnRenderStart);
spdlog::info("LatencyFleX initialized.");
Cvar_r_latencyflex = new ConVar("r_latencyflex", "1", FCVAR_ARCHIVE, "Whether or not to use LatencyFleX input latency reduction.");
diff --git a/primedev/client/localchatwriter.cpp b/primedev/client/localchatwriter.cpp
index 35cc065f..042e77d9 100644
--- a/primedev/client/localchatwriter.cpp
+++ b/primedev/client/localchatwriter.cpp
@@ -102,7 +102,10 @@ Color lightColors[8] = {
class AnsiEscapeParser
{
public:
- explicit AnsiEscapeParser(LocalChatWriter* writer) : m_writer(writer) {}
+ explicit AnsiEscapeParser(LocalChatWriter* writer)
+ : m_writer(writer)
+ {
+ }
void HandleVal(unsigned long val)
{
@@ -257,7 +260,10 @@ private:
}
};
-LocalChatWriter::LocalChatWriter(Context context) : m_context(context) {}
+LocalChatWriter::LocalChatWriter(Context context)
+ : m_context(context)
+{
+}
void LocalChatWriter::Write(const char* str)
{
diff --git a/primedev/client/modlocalisation.cpp b/primedev/client/modlocalisation.cpp
index 2b73876b..e5efa074 100644
--- a/primedev/client/modlocalisation.cpp
+++ b/primedev/client/modlocalisation.cpp
@@ -1,55 +1,58 @@
#include "mods/modmanager.h"
-AUTOHOOK_INIT()
-
void* g_pVguiLocalize;
-// clang-format off
-AUTOHOOK(CLocalize__AddFile, localize.dll + 0x6D80,
-bool, __fastcall, (void* pVguiLocalize, const char* path, const char* pathId, bool bIncludeFallbackSearchPaths))
-// clang-format on
+static bool(__fastcall* o_pCLocalise__AddFile)(
+ void* pVguiLocalize, const char* path, const char* pathId, bool bIncludeFallbackSearchPaths) = nullptr;
+static bool __fastcall h_CLocalise__AddFile(void* pVguiLocalize, const char* path, const char* pathId, bool bIncludeFallbackSearchPaths)
{
// save this for later
g_pVguiLocalize = pVguiLocalize;
- bool ret = CLocalize__AddFile(pVguiLocalize, path, pathId, bIncludeFallbackSearchPaths);
+ bool ret = o_pCLocalise__AddFile(pVguiLocalize, path, pathId, bIncludeFallbackSearchPaths);
if (ret)
spdlog::info("Loaded localisation file {} successfully", path);
return true;
}
-// clang-format off
-AUTOHOOK(CLocalize__ReloadLocalizationFiles, localize.dll + 0xB830,
-void, __fastcall, (void* pVguiLocalize))
-// clang-format on
+static void(__fastcall* o_pCLocalize__ReloadLocalizationFiles)(void* pVguiLocalize) = nullptr;
+static void __fastcall h_CLocalize__ReloadLocalizationFiles(void* pVguiLocalize)
{
// load all mod localization manually, so we keep track of all files, not just previously loaded ones
for (Mod mod : g_pModManager->m_LoadedMods)
if (mod.m_bEnabled)
for (std::string& localisationFile : mod.LocalisationFiles)
- CLocalize__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false);
+ o_pCLocalise__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false);
spdlog::info("reloading localization...");
- CLocalize__ReloadLocalizationFiles(pVguiLocalize);
+ o_pCLocalize__ReloadLocalizationFiles(pVguiLocalize);
}
-// clang-format off
-AUTOHOOK(CEngineVGui__Init, engine.dll + 0x247E10,
-void, __fastcall, (void* self))
-// clang-format on
+static void(__fastcall* o_pCEngineVGui__Init)(void* self) = nullptr;
+static void __fastcall h_CEngineVGui__Init(void* self)
{
- CEngineVGui__Init(self); // this loads r1_english, valve_english, dev_english
+ o_pCEngineVGui__Init(self); // this loads r1_english, valve_english, dev_english
// previously we did this in CLocalize::AddFile, but for some reason it won't properly overwrite localization from
// files loaded previously if done there, very weird but this works so whatever
for (Mod mod : g_pModManager->m_LoadedMods)
if (mod.m_bEnabled)
for (std::string& localisationFile : mod.LocalisationFiles)
- CLocalize__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false);
+ o_pCLocalise__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false);
+}
+
+ON_DLL_LOAD_CLIENT("engine.dll", VGuiInit, (CModule module))
+{
+ o_pCEngineVGui__Init = module.Offset(0x247E10).RCast<decltype(o_pCEngineVGui__Init)>();
+ HookAttach(&(PVOID&)o_pCEngineVGui__Init, (PVOID)h_CEngineVGui__Init);
}
ON_DLL_LOAD_CLIENT("localize.dll", Localize, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCLocalise__AddFile = module.Offset(0x6D80).RCast<decltype(o_pCLocalise__AddFile)>();
+ HookAttach(&(PVOID&)o_pCLocalise__AddFile, (PVOID)h_CLocalise__AddFile);
+
+ o_pCLocalize__ReloadLocalizationFiles = module.Offset(0xB830).RCast<decltype(o_pCLocalize__ReloadLocalizationFiles)>();
+ HookAttach(&(PVOID&)o_pCLocalize__ReloadLocalizationFiles, (PVOID)h_CLocalize__ReloadLocalizationFiles);
}
diff --git a/primedev/client/rejectconnectionfixes.cpp b/primedev/client/rejectconnectionfixes.cpp
index 1b326a3c..adfd772c 100644
--- a/primedev/client/rejectconnectionfixes.cpp
+++ b/primedev/client/rejectconnectionfixes.cpp
@@ -1,12 +1,8 @@
#include "engine/r2engine.h"
-AUTOHOOK_INIT()
-
// this is called from when our connection is rejected, this is the only case we're hooking this for
-// clang-format off
-AUTOHOOK(COM_ExplainDisconnection, engine.dll + 0x1342F0,
-void,, (bool a1, const char* fmt, ...))
-// clang-format on
+static void (*o_pCOM_ExplainDisconnection)(bool a1, const char* fmt, ...) = nullptr;
+static void h_COM_ExplainDisconnection(bool a1, const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
@@ -25,10 +21,11 @@ void,, (bool a1, const char* fmt, ...))
Cbuf_AddText(Cbuf_GetCurrentPlayer(), "disconnect", cmd_source_t::kCommandSrcCode);
}
- return COM_ExplainDisconnection(a1, "%s", buf);
+ return o_pCOM_ExplainDisconnection(a1, "%s", buf);
}
ON_DLL_LOAD_CLIENT("engine.dll", RejectConnectionFixes, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCOM_ExplainDisconnection = module.Offset(0x1342F0).RCast<decltype(o_pCOM_ExplainDisconnection)>();
+ HookAttach(&(PVOID&)o_pCOM_ExplainDisconnection, (PVOID)h_COM_ExplainDisconnection);
}
diff --git a/primedev/core/convar/convar.cpp b/primedev/core/convar/convar.cpp
index 87a1e159..802c088d 100644
--- a/primedev/core/convar/convar.cpp
+++ b/primedev/core/convar/convar.cpp
@@ -1,7 +1,7 @@
#include "bits.h"
#include "cvar.h"
#include "convar.h"
-#include "core/sourceinterface.h"
+#include "core/tier1.h"
#include <float.h>
@@ -35,8 +35,7 @@ ON_DLL_LOAD("engine.dll", ConVar, (CModule module))
g_pConVar_Vtable = module.Offset(0x67FD28);
g_pIConVar_Vtable = module.Offset(0x67FDC8);
- g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
- g_pCVar = *g_pCVarInterface;
+ g_pCVar = Sys_GetFactoryPtr("vstdlib.dll", "VEngineCvar007").RCast<CCvar*>();
}
//-----------------------------------------------------------------------------
diff --git a/primedev/core/convar/convar.h b/primedev/core/convar/convar.h
index f0366b46..33a50c1c 100644
--- a/primedev/core/convar/convar.h
+++ b/primedev/core/convar/convar.h
@@ -1,5 +1,4 @@
#pragma once
-#include "core/sourceinterface.h"
#include "core/math/color.h"
#include "cvar.h"
#include "concommand.h"
diff --git a/primedev/core/convar/cvar.cpp b/primedev/core/convar/cvar.cpp
index aa5f0365..78da1ad2 100644
--- a/primedev/core/convar/cvar.cpp
+++ b/primedev/core/convar/cvar.cpp
@@ -22,5 +22,4 @@ std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
return allConVars;
}
-SourceInterface<CCvar>* g_pCVarInterface;
CCvar* g_pCVar;
diff --git a/primedev/core/convar/cvar.h b/primedev/core/convar/cvar.h
index beaa84f4..024f2b87 100644
--- a/primedev/core/convar/cvar.h
+++ b/primedev/core/convar/cvar.h
@@ -34,5 +34,4 @@ public:
std::unordered_map<std::string, ConCommandBase*> DumpToMap();
};
-extern SourceInterface<CCvar>* g_pCVarInterface;
extern CCvar* g_pCVar;
diff --git a/primedev/core/filesystem/filesystem.cpp b/primedev/core/filesystem/filesystem.cpp
index b39939e4..3c711e8e 100644
--- a/primedev/core/filesystem/filesystem.cpp
+++ b/primedev/core/filesystem/filesystem.cpp
@@ -1,34 +1,32 @@
#include "filesystem.h"
-#include "core/sourceinterface.h"
+#include "core/tier1.h"
#include "mods/modmanager.h"
#include <iostream>
#include <sstream>
-AUTOHOOK_INIT()
-
bool bReadingOriginalFile = false;
std::string sCurrentModPath;
ConVar* Cvar_ns_fs_log_reads;
-SourceInterface<IFileSystem>* g_pFilesystem;
+IFileSystem* g_pFilesystem;
std::string ReadVPKFile(const char* path)
{
// read scripts.rson file, todo: check if this can be overwritten
- FileHandle_t fileHandle = (*g_pFilesystem)->m_vtable2->Open(&(*g_pFilesystem)->m_vtable2, path, "rb", "GAME", 0);
+ FileHandle_t fileHandle = g_pFilesystem->m_vtable2->Open(&g_pFilesystem->m_vtable2, path, "rb", "GAME", 0);
std::stringstream fileStream;
int bytesRead = 0;
char data[4096];
do
{
- bytesRead = (*g_pFilesystem)->m_vtable2->Read(&(*g_pFilesystem)->m_vtable2, data, (int)std::size(data), fileHandle);
+ bytesRead = g_pFilesystem->m_vtable2->Read(&g_pFilesystem->m_vtable2, data, (int)std::size(data), fileHandle);
fileStream.write(data, bytesRead);
} while (bytesRead == std::size(data));
- (*g_pFilesystem)->m_vtable2->Close(*g_pFilesystem, fileHandle);
+ g_pFilesystem->m_vtable2->Close(g_pFilesystem, fileHandle);
return fileStream.str();
}
@@ -44,18 +42,17 @@ std::string ReadVPKOriginalFile(const char* path)
return ret;
}
-// clang-format off
-HOOK(AddSearchPathHook, AddSearchPath,
-void, __fastcall, (IFileSystem * fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType))
-// clang-format on
+static void(__fastcall* o_pAddSearchPath)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType) =
+ nullptr;
+static void __fastcall h_AddSearchPath(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType)
{
- AddSearchPath(fileSystem, pPath, pathID, addType);
+ o_pAddSearchPath(fileSystem, pPath, pathID, addType);
// make sure current mod paths are at head
if (!strcmp(pathID, "GAME") && sCurrentModPath.compare(pPath) && addType == PATH_ADD_TO_HEAD)
{
- AddSearchPath(fileSystem, sCurrentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD);
- AddSearchPath(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(fileSystem, sCurrentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD);
}
}
@@ -67,13 +64,13 @@ void SetNewModSearchPaths(Mod* mod)
{
if ((fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().compare(sCurrentModPath))
{
- AddSearchPath(
- &*(*g_pFilesystem), (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(
+ g_pFilesystem, (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
sCurrentModPath = (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string();
}
}
else // push compiled to head
- AddSearchPath(&*(*g_pFilesystem), fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(g_pFilesystem, fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
}
bool TryReplaceFile(const char* pPath, bool shouldCompile)
@@ -97,22 +94,17 @@ bool TryReplaceFile(const char* pPath, bool shouldCompile)
}
// force modded files to be read from mods, not cache
-// clang-format off
-HOOK(ReadFromCacheHook, ReadFromCache,
-bool, __fastcall, (IFileSystem * filesystem, char* pPath, void* result))
-// clang-format off
+static bool(__fastcall* o_pReadFromCache)(IFileSystem* filesystem, char* pPath, void* result) = nullptr;
+static bool __fastcall h_ReadFromCache(IFileSystem* filesystem, char* pPath, void* result)
{
if (TryReplaceFile(pPath, true))
return false;
- return ReadFromCache(filesystem, pPath, result);
+ return o_pReadFromCache(filesystem, pPath, result);
}
-// force modded files to be read from mods, not vpk
-// clang-format off
-AUTOHOOK(ReadFileFromVPK, filesystem_stdio.dll + 0x5CBA0,
-FileHandle_t, __fastcall, (VPKData* vpkInfo, uint64_t* b, char* filename))
-// clang-format on
+static FileHandle_t(__fastcall* o_pReadFileFromVPK)(VPKData* vpkInfo, uint64_t* b, char* filename) = nullptr;
+static FileHandle_t __fastcall h_ReadFileFromVPK(VPKData* vpkInfo, uint64_t* b, char* filename)
{
// don't compile here because this is only ever called from OpenEx, which already compiles
if (TryReplaceFile(filename, false))
@@ -121,22 +113,24 @@ FileHandle_t, __fastcall, (VPKData* vpkInfo, uint64_t* b, char* filename))
return b;
}
- return ReadFileFromVPK(vpkInfo, b, filename);
+ return o_pReadFileFromVPK(vpkInfo, b, filename);
}
-// clang-format off
-AUTOHOOK(CBaseFileSystem__OpenEx, filesystem_stdio.dll + 0x15F50,
-FileHandle_t, __fastcall, (IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char **ppszResolvedFilename))
-// clang-format on
+static FileHandle_t(__fastcall* o_pCBaseFileSystem__OpenEx)(
+ IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char** ppszResolvedFilename) =
+ nullptr;
+static FileHandle_t __fastcall h_CBaseFileSystem__OpenEx(
+ IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char** ppszResolvedFilename)
{
TryReplaceFile(pPath, true);
- return CBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename);
+ return o_pCBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename);
}
-HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char* pVpkPath))
+static VPKData* (*o_pMountVPK)(IFileSystem* fileSystem, const char* pVpkPath) = nullptr;
+static VPKData* h_MountVPK(IFileSystem* fileSystem, const char* pVpkPath)
{
NS::log::fs->info("MountVPK {}", pVpkPath);
- VPKData* ret = MountVPK(fileSystem, pVpkPath);
+ VPKData* ret = o_pMountVPK(fileSystem, pVpkPath);
for (Mod mod : g_pModManager->m_LoadedMods)
{
@@ -156,7 +150,7 @@ HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char*
continue;
}
- VPKData* loaded = MountVPK(fileSystem, vpkEntry.m_sVpkPath.c_str());
+ VPKData* loaded = o_pMountVPK(fileSystem, vpkEntry.m_sVpkPath.c_str());
if (!ret) // this is primarily for map vpks and stuff, so the map's vpk is what gets returned from here
ret = loaded;
}
@@ -167,11 +161,18 @@ HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char*
ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pReadFileFromVPK = module.Offset(0x5CBA0).RCast<decltype(o_pReadFileFromVPK)>();
+ HookAttach(&(PVOID&)o_pReadFileFromVPK, (PVOID)h_ReadFileFromVPK);
+
+ o_pCBaseFileSystem__OpenEx = module.Offset(0x15F50).RCast<decltype(o_pCBaseFileSystem__OpenEx)>();
+ HookAttach(&(PVOID&)o_pCBaseFileSystem__OpenEx, (PVOID)h_CBaseFileSystem__OpenEx);
- g_pFilesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017");
+ g_pFilesystem = Sys_GetFactoryPtr("filesystem_stdio.dll", "VFileSystem017").RCast<IFileSystem*>();
- AddSearchPathHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->AddSearchPath);
- ReadFromCacheHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->ReadFromCache);
- MountVPKHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->MountVPK);
+ o_pAddSearchPath = reinterpret_cast<decltype(o_pAddSearchPath)>(g_pFilesystem->m_vtable->AddSearchPath);
+ HookAttach(&(PVOID&)o_pAddSearchPath, (PVOID)h_AddSearchPath);
+ o_pReadFromCache = reinterpret_cast<decltype(o_pReadFromCache)>(g_pFilesystem->m_vtable->ReadFromCache);
+ HookAttach(&(PVOID&)o_pReadFromCache, (PVOID)h_ReadFromCache);
+ o_pMountVPK = reinterpret_cast<decltype(o_pMountVPK)>(g_pFilesystem->m_vtable->MountVPK);
+ HookAttach(&(PVOID&)o_pMountVPK, (PVOID)h_MountVPK);
}
diff --git a/primedev/core/filesystem/filesystem.h b/primedev/core/filesystem/filesystem.h
index 4e2c18d9..2c592b3f 100644
--- a/primedev/core/filesystem/filesystem.h
+++ b/primedev/core/filesystem/filesystem.h
@@ -1,5 +1,4 @@
#pragma once
-#include "core/sourceinterface.h"
// taken from ttf2sdk
typedef void* FileHandle_t;
@@ -48,7 +47,7 @@ public:
VTable2* m_vtable2;
};
-extern SourceInterface<IFileSystem>* g_pFilesystem;
+extern IFileSystem* g_pFilesystem;
std::string ReadVPKFile(const char* path);
std::string ReadVPKOriginalFile(const char* path);
diff --git a/primedev/core/filesystem/rpakfilesystem.cpp b/primedev/core/filesystem/rpakfilesystem.cpp
index da72646b..c3e5e74e 100644
--- a/primedev/core/filesystem/rpakfilesystem.cpp
+++ b/primedev/core/filesystem/rpakfilesystem.cpp
@@ -2,206 +2,447 @@
#include "mods/modmanager.h"
#include "dedicated/dedicated.h"
#include "core/tier0.h"
+#include "util/utils.h"
-AUTOHOOK_INIT()
-
-// there are more i'm just too lazy to add
+#pragma pack(push, 1)
struct PakLoadFuncs
{
- void* unk0[3];
- int (*LoadPakAsync)(const char* pPath, void* unknownSingleton, int flags, void* callback0, void* callback1);
- void* unk1[2];
- void* (*UnloadPak)(int iPakHandle, void* callback);
- void* unk2[6];
- void* (*LoadFile)(const char* path); // unsure
- void* unk3[10];
- void* (*ReadFileAsync)(const char* pPath, void* a2);
+ void (*InitRpakSystem)();
+ void (*AddAssetLoaderWithJobDetails)(/*assetTypeHeader*/ void*, uint32_t, int);
+ void (*AddAssetLoader)(/*assetTypeHeader*/ void*);
+ PakHandle (*LoadRpakFileAsync)(const char* pPath, void* allocator, int flags);
+ void (*LoadRpakFile)(const char*, __int64(__fastcall*)(), __int64, void(__cdecl*)());
+ __int64 qword28;
+ void (*UnloadPak)(PakHandle iPakHandle, void* callback);
+ __int64 qword38;
+ __int64 qword40;
+ __int64 qword48;
+ __int64 qword50;
+ FARPROC (*GetDllCallback)(__int16 a1, const CHAR* a2);
+ __int64 (*GetAssetByHash)(__int64 hash);
+ __int64 (*GetAssetByName)(const char* a1);
+ __int64 qword70;
+ __int64 qword78;
+ __int64 qword80;
+ __int64 qword88;
+ __int64 qword90;
+ __int64 qword98;
+ __int64 qwordA0;
+ __int64 qwordA8;
+ __int64 qwordB0;
+ __int64 qwordBB;
+ void* (*OpenFile)(const char* pPath);
+ __int64 CloseFile;
+ __int64 qwordD0;
+ __int64 FileReadAsync;
+ __int64 ComplexFileReadAsync;
+ __int64 GetReadJobState;
+ __int64 WaitForFileReadJobComplete;
+ __int64 CancelFileReadJob;
+ __int64 CancelFileReadJobAsync;
+ __int64 qword108;
};
+static_assert(sizeof(PakLoadFuncs) == 0x110);
+#pragma pack(pop)
PakLoadFuncs* g_pakLoadApi;
-
PakLoadManager* g_pPakLoadManager;
-void** pUnknownPakLoadSingleton;
-int PakLoadManager::LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource)
+static char* pszCurrentMapRpakPath = nullptr;
+static PakHandle* piCurrentMapRpakHandle = nullptr;
+static PakHandle* piCurrentMapPatchRpakHandle = nullptr;
+static /*CModelLoader*/ void** ppModelLoader = nullptr;
+static void** rpakMemoryAllocator = nullptr;
+
+static __int64 (*o_pLoadGametypeSpecificRpaks)(const char* levelName) = nullptr;
+static __int64 (**o_pCleanMaterialSystemStuff)() = nullptr;
+static __int64 (**o_pCModelLoader_UnreferenceAllModels)(/*CModelLoader*/ void* a1) = nullptr;
+static char* (*o_pLoadlevelLoadscreen)(const char* levelName) = nullptr;
+static unsigned int (*o_pGetPakPatchNumber)(const char* pPakPath) = nullptr;
+
+// Marks all mod Paks to be unloaded on next map load.
+// Also cleans up any mod Paks that are already unloaded.
+void PakLoadManager::UnloadAllModPaks()
{
- int nHandle = g_pakLoadApi->LoadPakAsync(pPath, *pUnknownPakLoadSingleton, 2, nullptr, nullptr);
-
- // set the load source of the pak we just loaded
- if (nHandle != -1)
- GetPakInfo(nHandle)->m_nLoadSource = nLoadSource;
-
- return nHandle;
+ NS::log::rpak->info("Reloading RPaks on next map load...");
+ for (auto& modPak : m_modPaks)
+ {
+ modPak.m_markedForDelete = true;
+ }
+ // clean up any paks that are both marked for unload and already unloaded
+ CleanUpUnloadedPaks();
+ SetForceReloadOnMapLoad(true);
}
-void PakLoadManager::UnloadPak(const int nPakHandle)
+// Tracks all Paks related to a mod.
+void PakLoadManager::TrackModPaks(Mod& mod)
{
- g_pakLoadApi->UnloadPak(nPakHandle, nullptr);
+ const fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
+
+ for (auto& modRpakEntry : mod.Rpaks)
+ {
+ ModPak_t pak;
+ pak.m_modName = mod.Name;
+ pak.m_path = (modPakPath / modRpakEntry.m_pakName).string();
+ pak.m_pathHash = STR_HASH(pak.m_path);
+
+ pak.m_preload = modRpakEntry.m_preload;
+ pak.m_dependentPakHash = modRpakEntry.m_dependentPakHash;
+ pak.m_mapRegex = modRpakEntry.m_loadRegex;
+
+ m_modPaks.push_back(pak);
+ }
}
-void PakLoadManager::UnloadMapPaks()
+// Untracks all paks that aren't currently loaded and are marked for unload.
+void PakLoadManager::CleanUpUnloadedPaks()
{
- for (auto& pair : m_vLoadedPaks)
- if (pair.second.m_nLoadSource == ePakLoadSource::MAP)
- UnloadPak(pair.first);
+ auto fnRemovePredicate = [](ModPak_t& pak) -> bool { return pak.m_markedForDelete && pak.m_handle == PakHandle::INVALID; };
+
+ m_modPaks.erase(std::remove_if(m_modPaks.begin(), m_modPaks.end(), fnRemovePredicate), m_modPaks.end());
}
-LoadedPak* PakLoadManager::TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash)
+// Unloads all paks that are marked for unload.
+void PakLoadManager::UnloadMarkedPaks()
{
- LoadedPak pak;
- pak.m_nLoadSource = nLoadSource;
- pak.m_nPakHandle = nPakHandle;
- pak.m_nPakNameHash = nPakNameHash;
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_handle == PakHandle::INVALID || !modPak.m_markedForDelete)
+ continue;
- m_vLoadedPaks.insert(std::make_pair(nPakHandle, pak));
- return &m_vLoadedPaks.at(nPakHandle);
+ g_pakLoadApi->UnloadPak(modPak.m_handle, *o_pCleanMaterialSystemStuff);
+ modPak.m_handle = PakHandle::INVALID;
+ }
}
-void PakLoadManager::RemoveLoadedPak(int nPakHandle)
+// Loads all modded paks for the given map.
+void PakLoadManager::LoadModPaksForMap(const char* mapName)
{
- m_vLoadedPaks.erase(nPakHandle);
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ for (auto& modPak : m_modPaks)
+ {
+ // don't load paks that are already loaded
+ if (modPak.m_handle != PakHandle::INVALID)
+ continue;
+ std::cmatch matches;
+ if (!std::regex_match(mapName, matches, modPak.m_mapRegex))
+ continue;
+
+ modPak.m_handle = g_pakLoadApi->LoadRpakFileAsync(modPak.m_path.c_str(), *rpakMemoryAllocator, 7);
+ m_mapPaks.push_back(modPak.m_pathHash);
+ }
}
-LoadedPak* PakLoadManager::GetPakInfo(const int nPakHandle)
+// Unloads all modded map paks.
+void PakLoadManager::UnloadModPaks()
{
- return &m_vLoadedPaks.at(nPakHandle);
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+
+ for (auto& modPak : m_modPaks)
+ {
+ for (auto it = m_mapPaks.begin(); it != m_mapPaks.end(); ++it)
+ {
+ if (*it != modPak.m_pathHash)
+ continue;
+
+ m_mapPaks.erase(it, it + 1);
+ g_pakLoadApi->UnloadPak(modPak.m_handle, *o_pCleanMaterialSystemStuff);
+ modPak.m_handle = PakHandle::INVALID;
+ break;
+ }
+ }
+
+ // If this has happened, we may have leaked a pak?
+ // It basically means that none of the entries in m_modPaks matched the hash in m_mapPaks so we didn't end up unloading it
+ assert_msg(m_mapPaks.size() == 0, "Not all map paks were unloaded?");
}
-int PakLoadManager::GetPakHandle(const size_t nPakNameHash)
+// Called after a Pak was loaded.
+void PakLoadManager::OnPakLoaded(std::string& originalPath, std::string& resultingPath, PakHandle resultingHandle)
{
- for (auto& pair : m_vLoadedPaks)
- if (pair.second.m_nPakNameHash == nPakNameHash)
- return pair.first;
+ if (IsVanillaCall())
+ {
+ // add entry to loaded vanilla rpaks
+ m_vanillaPaks.emplace_back(originalPath, resultingHandle);
+ }
- return -1;
+ LoadDependentPaks(resultingPath, resultingHandle);
}
-int PakLoadManager::GetPakHandle(const char* pPath)
+// Called before a Pak was unloaded.
+void PakLoadManager::OnPakUnloading(PakHandle handle)
{
- return GetPakHandle(STR_HASH(pPath));
+ UnloadDependentPaks(handle);
+
+ if (IsVanillaCall())
+ {
+ // remove entry from loaded vanilla rpaks
+ auto fnRemovePredicate = [handle](std::pair<std::string, PakHandle>& pair) -> bool { return pair.second == handle; };
+
+ m_vanillaPaks.erase(std::remove_if(m_vanillaPaks.begin(), m_vanillaPaks.end(), fnRemovePredicate), m_vanillaPaks.end());
+
+ // no need to handle aliasing here, if vanilla wants it gone, it's gone
+ }
+ else
+ {
+ // note: aliasing is handled the old way, long term todo: move it over to the PakLoadManager
+ // handle the potential unloading of an aliased vanilla rpak (we aliased it, and we are now unloading the alias, so we should load
+ // the vanilla one again)
+ // for (auto& [path, resultingHandle] : m_vanillaPaks)
+ //{
+ // if (resultingHandle != handle)
+ // continue;
+
+ // // load vanilla rpak
+ //}
+ }
+
+ // set handle of the mod pak (if any) that has this handle for proper tracking
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_handle == handle)
+ modPak.m_handle = PakHandle::INVALID;
+ }
}
-void* PakLoadManager::LoadFile(const char* path)
+// Whether the vanilla game has this rpak
+static bool VanillaHasPak(const char* pakName)
{
- return g_pakLoadApi->LoadFile(path);
+ fs::path originalPath = fs::path("./r2/paks/Win64") / pakName;
+ unsigned int highestPatch = o_pGetPakPatchNumber(pakName);
+ if (highestPatch)
+ {
+ // add the patch path to the extension
+ char buf[16];
+ snprintf(buf, sizeof(buf), "(%02u).rpak", highestPatch);
+ // remove the .rpak and add the new suffix
+ originalPath = originalPath.replace_extension().string() + buf;
+ }
+ else
+ {
+ originalPath /= pakName;
+ }
+
+ return fs::exists(originalPath);
}
-void HandlePakAliases(char** map)
+// If vanilla doesn't have an rpak for this path, tries to map it to a modded rpak of the same name.
+void PakLoadManager::FixupPakPath(std::string& pakPath)
{
- // convert the pak being loaded to it's aliased one, e.g. aliasing mp_hub_timeshift => sp_hub_timeshift
- for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--)
+ if (VanillaHasPak(pakPath.c_str()))
+ return;
+
+ for (ModPak_t& modPak : m_modPaks)
{
- Mod* mod = &g_pModManager->m_LoadedMods[i];
- if (!mod->m_bEnabled)
+ if (modPak.m_markedForDelete)
continue;
- if (mod->RpakAliases.find(*map) != mod->RpakAliases.end())
+ fs::path modPakFilename = fs::path(modPak.m_path).filename();
+ if (pakPath == modPakFilename.string())
{
- *map = &mod->RpakAliases[*map][0];
+ pakPath = modPak.m_path;
return;
}
}
}
-void LoadPreloadPaks()
+// Loads all "Preload" Paks. todo: deprecate Preload.
+void PakLoadManager::LoadPreloadPaks()
{
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ for (auto& modPak : m_modPaks)
{
- if (!mod.m_bEnabled)
+ if (modPak.m_markedForDelete || modPak.m_handle != PakHandle::INVALID || !modPak.m_preload)
continue;
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
+ modPak.m_handle = g_pakLoadApi->LoadRpakFileAsync(modPak.m_path.c_str(), *rpakMemoryAllocator, 7);
+ }
+}
- for (ModRpakEntry& pak : mod.Rpaks)
- if (pak.m_bAutoLoad)
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT);
+// Causes all "Postload" paks to be loaded again.
+void PakLoadManager::ReloadPostloadPaks()
+{
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ // pretend that we just loaded all of these vanilla paks
+ for (auto& [path, handle] : m_vanillaPaks)
+ {
+ LoadDependentPaks(path, handle);
}
}
-void LoadPostloadPaks(const char* pPath)
+// Wrapper for Pak load API.
+void* PakLoadManager::OpenFile(const char* path)
{
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
+ return g_pakLoadApi->OpenFile(path);
+}
+
+// Loads Paks that depend on this Pak.
+void PakLoadManager::LoadDependentPaks(std::string& path, PakHandle handle)
+{
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ const size_t hash = STR_HASH(path);
+ for (auto& modPak : m_modPaks)
{
- if (!mod.m_bEnabled)
+ if (modPak.m_handle != PakHandle::INVALID)
+ continue;
+ if (modPak.m_dependentPakHash != hash)
continue;
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
-
- for (ModRpakEntry& pak : mod.Rpaks)
- if (pak.m_sLoadAfterPak == pPath)
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT);
+ // load pak
+ modPak.m_handle = g_pakLoadApi->LoadRpakFileAsync(modPak.m_path.c_str(), *rpakMemoryAllocator, 7);
+ m_dependentPaks.emplace_back(handle, hash);
}
}
-void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName)
+// Unloads Paks that depend on this Pak.
+void PakLoadManager::UnloadDependentPaks(PakHandle handle)
{
- // whether the vanilla game has this rpak
- bool bHasOriginalPak = fs::exists(fs::path("r2/paks/Win64/") / *pakName);
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
+ auto fnRemovePredicate = [&](std::pair<PakHandle, size_t>& pair) -> bool
{
- if (!mod.m_bEnabled)
- continue;
+ if (pair.first != handle)
+ return false;
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_pathHash != pair.second || modPak.m_handle == PakHandle::INVALID)
+ continue;
- for (ModRpakEntry& pak : mod.Rpaks)
+ // unload pak
+ g_pakLoadApi->UnloadPak(modPak.m_handle, *o_pCleanMaterialSystemStuff);
+ modPak.m_handle = PakHandle::INVALID;
+ }
+
+ return true;
+ };
+ m_dependentPaks.erase(std::remove_if(m_dependentPaks.begin(), m_dependentPaks.end(), fnRemovePredicate), m_dependentPaks.end());
+}
+
+// Handles aliases for rpaks defined in rpak.json, effectively redirecting an rpak load to a different path.
+static void HandlePakAliases(std::string& originalPath)
+{
+ // convert the pak being loaded to its aliased one, e.g. aliasing mp_hub_timeshift => sp_hub_timeshift
+ for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--)
+ {
+ Mod* mod = &g_pModManager->m_LoadedMods[i];
+ if (!mod->m_bEnabled)
+ continue;
+
+ if (mod->RpakAliases.find(originalPath) != mod->RpakAliases.end())
{
- if (!pak.m_bAutoLoad && !pak.m_sPakName.compare(*pakName))
- {
- // if the game doesn't have the original pak, let it handle loading this one as if it was the one it was loading originally
- if (!bHasOriginalPak)
- {
- std::string path = (modPakPath / pak.m_sPakName).string();
- *pakName = new char[path.size() + 1];
- strcpy(*pakName, &path[0]);
- (*pakName)[path.size()] = '\0';
-
- bHasOriginalPak = true;
- *bNeedToFreePakName =
- true; // we can't free this memory until we're done with the pak, so let whatever's calling this deal with it
- }
- else
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::MAP);
- }
+ originalPath = mod->RpakAliases[originalPath];
+ return;
}
}
}
-// clang-format off
-HOOK(LoadPakAsyncHook, LoadPakAsync,
-int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallback0, void* pCallback1))
-// clang-format on
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool (*o_pLoadMapRpaks)(char* mapPath) = nullptr;
+static bool h_LoadMapRpaks(char* mapPath)
{
- HandlePakAliases(&pPath);
+ // unload all mod rpaks that are marked for unload
+ g_pPakLoadManager->UnloadMarkedPaks();
+ g_pPakLoadManager->CleanUpUnloadedPaks();
+
+ // strip file extension
+ const std::string mapName = fs::path(mapPath).replace_extension().string();
+
+ // load mp_common, sp_common etc.
+ o_pLoadGametypeSpecificRpaks(mapName.c_str());
- // dont load the pak if it's currently loaded already
- size_t nPathHash = STR_HASH(pPath);
- if (g_pPakLoadManager->GetPakHandle(nPathHash) != -1)
- return -1;
+ // unload old modded map paks
+ g_pPakLoadManager->UnloadModPaks();
+ // load modded map paks
+ g_pPakLoadManager->LoadModPaksForMap(mapName.c_str());
- bool bNeedToFreePakName = false;
+ // don't load/unload anything when going to the lobby, presumably to save load times when going back to the same map
+ if (!g_pPakLoadManager->GetForceReloadOnMapLoad() && !strcmp("mp_lobby", mapName.c_str()))
+ return false;
- static bool bShouldLoadPaks = true;
- if (bShouldLoadPaks)
+ if (g_pPakLoadManager->GetForceReloadOnMapLoad())
{
- // make a copy of the path for comparing to determine whether we should load this pak on dedi, before it could get overwritten by
- // LoadCustomMapPaks
- std::string originalPath(pPath);
+ g_pPakLoadManager->LoadPreloadPaks();
+ g_pPakLoadManager->ReloadPostloadPaks();
+ }
+
+ char mapRpakStr[272];
+ snprintf(mapRpakStr, 272, "%s.rpak", mapName.c_str());
+
+ // if level being loaded is the same as current level, do nothing
+ if (!g_pPakLoadManager->GetForceReloadOnMapLoad() && !strcmp(mapRpakStr, pszCurrentMapRpakPath))
+ return true;
+
+ strcpy(pszCurrentMapRpakPath, mapRpakStr);
- // disable preloading while we're doing this
- bShouldLoadPaks = false;
+ (*o_pCleanMaterialSystemStuff)();
+ o_pLoadlevelLoadscreen(mapName.c_str());
- LoadPreloadPaks();
- LoadCustomMapPaks(&pPath, &bNeedToFreePakName);
+ // unload old map rpaks
+ PakHandle curHandle = *piCurrentMapRpakHandle;
+ PakHandle curPatchHandle = *piCurrentMapPatchRpakHandle;
+ if (curHandle != PakHandle::INVALID)
+ {
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+ g_pakLoadApi->UnloadPak(curHandle, *o_pCleanMaterialSystemStuff);
+ *piCurrentMapRpakHandle = PakHandle::INVALID;
+ }
+ if (curPatchHandle != PakHandle::INVALID)
+ {
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+ g_pakLoadApi->UnloadPak(curPatchHandle, *o_pCleanMaterialSystemStuff);
+ *piCurrentMapPatchRpakHandle = PakHandle::INVALID;
+ }
- bShouldLoadPaks = true;
+ *piCurrentMapRpakHandle = g_pakLoadApi->LoadRpakFileAsync(mapRpakStr, *rpakMemoryAllocator, 7);
+
+ // load special _patch rpak (seemingly used for dev things?)
+ char levelPatchRpakStr[272];
+ snprintf(levelPatchRpakStr, 272, "%s_patch.rpak", mapName.c_str());
+ *piCurrentMapPatchRpakHandle = g_pakLoadApi->LoadRpakFileAsync(levelPatchRpakStr, *rpakMemoryAllocator, 7);
+
+ // we just reloaded the paks, so we don't need to force it again
+ g_pPakLoadManager->SetForceReloadOnMapLoad(false);
+ return true;
+}
+
+// clang-format off
+HOOK(LoadPakAsyncHook, LoadPakAsync,
+PakHandle, __fastcall, (const char* pPath, void* memoryAllocator, int flags))
+// clang-format on
+{
+ // make a copy of the path for comparing to determine whether we should load this pak on dedi, before it could get overwritten
+ std::string svOriginalPath(pPath);
+
+ std::string resultingPath(pPath);
+ HandlePakAliases(resultingPath);
+
+ if (g_pPakLoadManager->IsVanillaCall())
+ {
+ g_pPakLoadManager->LoadPreloadPaks();
+ g_pPakLoadManager->FixupPakPath(resultingPath);
// do this after custom paks load and in bShouldLoadPaks so we only ever call this on the root pakload call
// todo: could probably add some way to flag custom paks to not be loaded on dedicated servers in rpak.json
@@ -210,44 +451,27 @@ int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallbac
// sp_<map> rpaks contain tutorial ghost data
// sucks to have to load the entire rpak for that but sp was never meant to be done on dedi
if (IsDedicatedServer() &&
- (CommandLine()->CheckParm("-nopakdedi") || strncmp(&originalPath[0], "common", 6) && strncmp(&originalPath[0], "sp_", 3)))
+ (CommandLine()->CheckParm("-nopakdedi") || strncmp(&svOriginalPath[0], "common", 6) && strncmp(&svOriginalPath[0], "sp_", 3)))
{
- if (bNeedToFreePakName)
- delete[] pPath;
-
- NS::log::rpak->info("Not loading pak {} for dedicated server", originalPath);
- return -1;
+ NS::log::rpak->info("Not loading pak {} for dedicated server", svOriginalPath);
+ return PakHandle::INVALID;
}
}
- int iPakHandle = LoadPakAsync(pPath, unknownSingleton, flags, pCallback0, pCallback1);
- NS::log::rpak->info("LoadPakAsync {} {}", pPath, iPakHandle);
-
- // trak the pak
- g_pPakLoadManager->TrackLoadedPak(ePakLoadSource::UNTRACKED, iPakHandle, nPathHash);
- LoadPostloadPaks(pPath);
+ PakHandle iPakHandle = LoadPakAsync(resultingPath.c_str(), memoryAllocator, flags);
+ NS::log::rpak->info("LoadPakAsync {} {}", resultingPath, iPakHandle);
- if (bNeedToFreePakName)
- delete[] pPath;
+ g_pPakLoadManager->OnPakLoaded(svOriginalPath, resultingPath, iPakHandle);
return iPakHandle;
}
// clang-format off
HOOK(UnloadPakHook, UnloadPak,
-void*, __fastcall, (int nPakHandle, void* pCallback))
+void*, __fastcall, (PakHandle nPakHandle, void* pCallback))
// clang-format on
{
- // stop tracking the pak
- g_pPakLoadManager->RemoveLoadedPak(nPakHandle);
-
- static bool bShouldUnloadPaks = true;
- if (bShouldUnloadPaks)
- {
- bShouldUnloadPaks = false;
- g_pPakLoadManager->UnloadMapPaks();
- bShouldUnloadPaks = true;
- }
+ g_pPakLoadManager->OnPakUnloading(nPakHandle);
NS::log::rpak->info("UnloadPak {}", nPakHandle);
return UnloadPak(nPakHandle, pCallback);
@@ -256,7 +480,7 @@ void*, __fastcall, (int nPakHandle, void* pCallback))
// we hook this exclusively for resolving stbsp paths, but seemingly it's also used for other stuff like vpk, rpak, mprj and starpak loads
// tbh this actually might be for memory mapped files or something, would make sense i think
// clang-format off
-HOOK(ReadFileAsyncHook, ReadFileAsync,
+HOOK(OpenFileHook, o_pOpenFile,
void*, __fastcall, (const char* pPath, void* pCallback))
// clang-format on
{
@@ -329,19 +553,34 @@ void*, __fastcall, (const char* pPath, void* pCallback))
NS::log::rpak->info("LoadStreamPak: {}", filename.string());
}
- return ReadFileAsync(pPath, pCallback);
+ return o_pOpenFile(pPath, pCallback);
}
ON_DLL_LOAD("engine.dll", RpakFilesystem, (CModule module))
{
- AUTOHOOK_DISPATCH();
-
g_pPakLoadManager = new PakLoadManager;
g_pakLoadApi = module.Offset(0x5BED78).Deref().RCast<PakLoadFuncs*>();
- pUnknownPakLoadSingleton = module.Offset(0x7C5E20).RCast<void**>();
- LoadPakAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->LoadPakAsync);
+ LoadPakAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->LoadRpakFileAsync);
UnloadPakHook.Dispatch((LPVOID*)g_pakLoadApi->UnloadPak);
- ReadFileAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->ReadFileAsync);
+ OpenFileHook.Dispatch((LPVOID*)g_pakLoadApi->OpenFile);
+
+ pszCurrentMapRpakPath = module.Offset(0x1315C3E0).RCast<decltype(pszCurrentMapRpakPath)>();
+ piCurrentMapRpakHandle = module.Offset(0x7CB5A0).RCast<decltype(piCurrentMapRpakHandle)>();
+ piCurrentMapPatchRpakHandle = module.Offset(0x7CB5A4).RCast<decltype(piCurrentMapPatchRpakHandle)>();
+ ppModelLoader = module.Offset(0x7C4AC0).RCast<decltype(ppModelLoader)>();
+ rpakMemoryAllocator = module.Offset(0x7C5E20).RCast<decltype(rpakMemoryAllocator)>();
+
+ o_pLoadGametypeSpecificRpaks = module.Offset(0x15AD20).RCast<decltype(o_pLoadGametypeSpecificRpaks)>();
+ o_pCleanMaterialSystemStuff = module.Offset(0x12A11F00).RCast<decltype(o_pCleanMaterialSystemStuff)>();
+ o_pCModelLoader_UnreferenceAllModels = module.Offset(0x5ED580).RCast<decltype(o_pCModelLoader_UnreferenceAllModels)>();
+ o_pLoadlevelLoadscreen = module.Offset(0x15A810).RCast<decltype(o_pLoadlevelLoadscreen)>();
+
+ o_pLoadMapRpaks = module.Offset(0x15A8C0).RCast<decltype(o_pLoadMapRpaks)>();
+ HookAttach(&(PVOID&)o_pLoadMapRpaks, (PVOID)h_LoadMapRpaks);
+
+ // kinda bad, doing things in rtech in an engine callback but it seems fine for now
+ CModule rtechModule(GetModuleHandleA("rtech_game.dll"));
+ o_pGetPakPatchNumber = rtechModule.Offset(0x9A00).RCast<decltype(o_pGetPakPatchNumber)>();
}
diff --git a/primedev/core/filesystem/rpakfilesystem.h b/primedev/core/filesystem/rpakfilesystem.h
index bcd57a73..87a41e7b 100644
--- a/primedev/core/filesystem/rpakfilesystem.h
+++ b/primedev/core/filesystem/rpakfilesystem.h
@@ -1,39 +1,81 @@
#pragma once
-enum class ePakLoadSource
-{
- UNTRACKED = -1, // not a pak we loaded, we shouldn't touch this one
+#include <regex>
- CONSTANT, // should be loaded at all times
- MAP // loaded from a map, should be unloaded when the map is unloaded
+enum PakHandle : int
+{
+ INVALID = -1,
};
-struct LoadedPak
+struct ModPak_t
{
- ePakLoadSource m_nLoadSource;
- int m_nPakHandle;
- size_t m_nPakNameHash;
+ std::string m_modName;
+
+ std::string m_path;
+ size_t m_pathHash = 0;
+
+ // If the map being loaded matches this regex, this pak will be loaded.
+ std::regex m_mapRegex;
+ // If a pak with a hash matching this is loaded, this pak will be loaded.
+ size_t m_dependentPakHash = 0;
+ // If this is set, this pak will be loaded whenever any other pak is loaded.
+ bool m_preload = false;
+
+ // If this is set, the Pak will be unloaded on next map load
+ bool m_markedForDelete = false;
+ // The current rpak handle associated with this Pak
+ PakHandle m_handle = PakHandle::INVALID;
};
class PakLoadManager
{
-private:
- std::map<int, LoadedPak> m_vLoadedPaks {};
- std::unordered_map<size_t, int> m_HashToPakHandle {};
-
public:
- int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource);
- void UnloadPak(const int nPakHandle);
- void UnloadMapPaks();
- void* LoadFile(const char* path); // this is a guess
+ void UnloadAllModPaks();
+ void TrackModPaks(Mod& mod);
+
+ void CleanUpUnloadedPaks();
+ void UnloadMarkedPaks();
+
+ void LoadModPaksForMap(const char* mapName);
+ void UnloadModPaks();
+
+ // Whether the current context is a vanilla call to a function, or a modded one
+ bool IsVanillaCall() const { return m_reentranceCounter == 0; }
+ // Whether paks will be forced to reload on the next map load
+ bool GetForceReloadOnMapLoad() const { return m_forceReloadOnMapLoad; }
+ void SetForceReloadOnMapLoad(bool value) { m_forceReloadOnMapLoad = value; }
+
+ void OnPakLoaded(std::string& originalPath, std::string& resultingPath, PakHandle resultingHandle);
+ void OnPakUnloading(PakHandle handle);
+
+ void FixupPakPath(std::string& path);
+
+ void LoadPreloadPaks();
+ void ReloadPostloadPaks();
+
+ void* OpenFile(const char* path);
+
+private:
+ void LoadDependentPaks(std::string& path, PakHandle handle);
+ void UnloadDependentPaks(PakHandle handle);
- LoadedPak* TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash);
- void RemoveLoadedPak(int nPakHandle);
+ // All paks that vanilla has attempted to load. (they may have been aliased away)
+ // Also known as a list of rpaks that the vanilla game would have loaded at this point in time.
+ std::vector<std::pair<std::string, PakHandle>> m_vanillaPaks;
- LoadedPak* GetPakInfo(const int nPakHandle);
+ // All mod Paks that are currently tracked
+ std::vector<ModPak_t> m_modPaks;
+ // Hashes of the currently loaded map mod paks
+ std::vector<size_t> m_mapPaks;
+ // Currently loaded Pak path hashes that depend on a handle to remain loaded (Postload)
+ std::vector<std::pair<PakHandle, size_t>> m_dependentPaks;
- int GetPakHandle(const size_t nPakNameHash);
- int GetPakHandle(const char* pPath);
+ // Used to force rpaks to be unloaded and reloaded on the next map load.
+ // Vanilla behaviour is to not do this when loading into mp_lobby, or loading into the same map you were last in.
+ bool m_forceReloadOnMapLoad = false;
+ // Used to track if the current hook call is a vanilla call or not.
+ // When loading/unloading a mod Pak, increment this before doing so, and decrement afterwards.
+ int m_reentranceCounter = 0;
};
extern PakLoadManager* g_pPakLoadManager;
diff --git a/primedev/core/hooks.cpp b/primedev/core/hooks.cpp
index 5026f837..6146c93c 100644
--- a/primedev/core/hooks.cpp
+++ b/primedev/core/hooks.cpp
@@ -12,8 +12,6 @@
namespace fs = std::filesystem;
-AUTOHOOK_INIT()
-
// called from the ON_DLL_LOAD macros
__dllLoadCallback::__dllLoadCallback(
eDllLoadCallbackSide side, const std::string dllName, DllLoadCallbackFuncType callback, std::string uniqueStr, std::string reliesOn)
@@ -85,18 +83,18 @@ void __fileAutohook::DispatchForModule(const char* pModuleName)
hook->Dispatch();
}
-ManualHook::ManualHook(const char* funcName, LPVOID func) : pHookFunc(func), ppOrigFunc(nullptr)
+ManualHook::ManualHook(const char* funcName, LPVOID func)
+ : svFuncName(funcName)
+ , pHookFunc(func)
+ , ppOrigFunc(nullptr)
{
- const size_t iFuncNameStrlen = strlen(funcName);
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
}
-ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : pHookFunc(func), ppOrigFunc(orig)
+ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func)
+ : svFuncName(funcName)
+ , pHookFunc(func)
+ , ppOrigFunc(orig)
{
- const size_t iFuncNameStrlen = strlen(funcName);
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
}
bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig)
@@ -105,19 +103,19 @@ bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig)
ppOrigFunc = orig;
if (!addr)
- spdlog::error("Address for hook {} is invalid", pFuncName);
+ spdlog::error("Address for hook {} is invalid", svFuncName);
else if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK)
{
if (MH_EnableHook(addr) == MH_OK)
{
- spdlog::info("Enabling hook {}", pFuncName);
+ spdlog::info("Enabling hook {}", svFuncName);
return true;
}
else
- spdlog::error("MH_EnableHook failed for function {}", pFuncName);
+ spdlog::error("MH_EnableHook failed for function {}", svFuncName);
}
else
- spdlog::error("MH_CreateHook failed for function {}", pFuncName);
+ spdlog::error("MH_CreateHook failed for function {}", svFuncName);
return false;
}
@@ -223,14 +221,15 @@ void MakeHook(LPVOID pTarget, LPVOID pDetour, void* ppOriginal, const char* pFun
spdlog::error("MH_CreateHook failed for function {}", pStrippedFuncName);
}
-AUTOHOOK_ABSOLUTEADDR(_GetCommandLineA, (LPVOID)GetCommandLineA, LPSTR, WINAPI, ())
+static LPSTR(WINAPI* o_pGetCommandLineA)() = nullptr;
+static LPSTR WINAPI h_GetCommandLineA()
{
static char* cmdlineModified;
static char* cmdlineOrg;
if (cmdlineOrg == nullptr || cmdlineModified == nullptr)
{
- cmdlineOrg = _GetCommandLineA();
+ cmdlineOrg = o_pGetCommandLineA();
bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument
bool ignoreStartupArgs = strstr(cmdlineOrg, "-nostartupargs");
@@ -328,8 +327,6 @@ void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress)
void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress)
{
- CModule cModule(moduleAddress);
-
while (true)
{
bool bDoneCalling = true;
@@ -396,6 +393,7 @@ void HookSys_Init()
{
spdlog::error("MH_Initialize (minhook initialization) failed");
}
- // todo: remove remaining instances of autohook in this file
- AUTOHOOK_DISPATCH()
+
+ o_pGetCommandLineA = GetCommandLineA;
+ HookAttach(&(PVOID&)o_pGetCommandLineA, (PVOID)h_GetCommandLineA);
}
diff --git a/primedev/core/hooks.h b/primedev/core/hooks.h
index facf51bf..26d9483c 100644
--- a/primedev/core/hooks.h
+++ b/primedev/core/hooks.h
@@ -140,7 +140,9 @@ public:
__autohook() = delete;
__autohook(__fileAutohook* autohook, const char* funcName, LPVOID absoluteAddress, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig), iAbsoluteAddress(absoluteAddress)
+ : pHookFunc(func)
+ , ppOrigFunc(orig)
+ , iAbsoluteAddress(absoluteAddress)
{
iAddressResolutionMode = ABSOLUTE_ADDR;
@@ -152,7 +154,8 @@ public:
}
__autohook(__fileAutohook* autohook, const char* funcName, const char* addrString, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig)
+ : pHookFunc(func)
+ , ppOrigFunc(orig)
{
iAddressResolutionMode = OFFSET_STRING;
@@ -168,7 +171,8 @@ public:
}
__autohook(__fileAutohook* autohook, const char* funcName, const char* moduleName, const char* procName, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig)
+ : pHookFunc(func)
+ , ppOrigFunc(orig)
{
iAddressResolutionMode = PROCADDRESS;
@@ -278,7 +282,7 @@ public:
class ManualHook
{
public:
- char* pFuncName;
+ std::string svFuncName;
LPVOID pHookFunc;
LPVOID* ppOrigFunc;
@@ -327,10 +331,7 @@ public:
pAutohook->vars.push_back(this);
}
- void Dispatch()
- {
- *m_pTarget = (void*)ParseDLLOffsetString(m_pAddrString);
- }
+ void Dispatch() { *m_pTarget = (void*)ParseDLLOffsetString(m_pAddrString); }
};
// VAR_AT(engine.dll+0x404, ConVar*, Cvar_host_timescale)
diff --git a/primedev/core/math/bitbuf.h b/primedev/core/math/bitbuf.h
index a06dab17..33056e63 100644
--- a/primedev/core/math/bitbuf.h
+++ b/primedev/core/math/bitbuf.h
@@ -90,25 +90,13 @@ enum EBitCoordType
class BitBufferBase
{
protected:
- INLINE void SetName(const char* name)
- {
- m_BufferName = name;
- }
+ INLINE void SetName(const char* name) { m_BufferName = name; }
public:
- INLINE bool IsOverflowed()
- {
- return m_Overflow;
- }
- INLINE void SetOverflowed()
- {
- m_Overflow = true;
- }
+ INLINE bool IsOverflowed() { return m_Overflow; }
+ INLINE void SetOverflowed() { m_Overflow = true; }
- INLINE const char* GetName()
- {
- return m_BufferName;
- }
+ INLINE const char* GetName() { return m_BufferName; }
private:
const char* m_BufferName = "";
@@ -437,28 +425,13 @@ public:
return fReturn;
}
- INLINE i32 ReadChar()
- {
- return ReadSBitLong(sizeof(char) << 3);
- }
- INLINE u32 ReadByte()
- {
- return ReadUBitLong(sizeof(unsigned char) << 3);
- }
+ INLINE i32 ReadChar() { return ReadSBitLong(sizeof(char) << 3); }
+ INLINE u32 ReadByte() { return ReadUBitLong(sizeof(unsigned char) << 3); }
- INLINE i32 ReadShort()
- {
- return ReadSBitLong(sizeof(short) << 3);
- }
- INLINE u32 ReadWord()
- {
- return ReadUBitLong(sizeof(unsigned short) << 3);
- }
+ INLINE i32 ReadShort() { return ReadSBitLong(sizeof(short) << 3); }
+ INLINE u32 ReadWord() { return ReadUBitLong(sizeof(unsigned short) << 3); }
- INLINE i32 ReadLong()
- {
- return (i32)(ReadUBitLong(sizeof(i32) << 3));
- }
+ INLINE i32 ReadLong() { return (i32)(ReadUBitLong(sizeof(i32) << 3)); }
INLINE float ReadFloat()
{
u32 temp = ReadUBitLong(sizeof(float) << 3);
@@ -687,24 +660,12 @@ public:
return std::min(nCurOfs + nAdjust, m_DataBits);
}
- INLINE bool SeekRelative(size_t offset)
- {
- return Seek(GetNumBitsRead() + offset);
- }
+ INLINE bool SeekRelative(size_t offset) { return Seek(GetNumBitsRead() + offset); }
- INLINE size_t TotalBytesAvailable()
- {
- return m_DataBytes;
- }
+ INLINE size_t TotalBytesAvailable() { return m_DataBytes; }
- INLINE size_t GetNumBitsLeft()
- {
- return m_DataBits - GetNumBitsRead();
- }
- INLINE size_t GetNumBytesLeft()
- {
- return GetNumBitsLeft() >> 3;
- }
+ INLINE size_t GetNumBitsLeft() { return m_DataBits - GetNumBitsRead(); }
+ INLINE size_t GetNumBytesLeft() { return GetNumBitsLeft() >> 3; }
private:
size_t m_DataBits; // 0x0010
@@ -743,10 +704,7 @@ public:
m_DataEnd = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_Data) + m_DataBytes);
}
- INLINE int GetNumBitsLeft()
- {
- return m_OutBitsLeft + (32 * (m_DataEnd - m_DataOut - 1));
- }
+ INLINE int GetNumBitsLeft() { return m_OutBitsLeft + (32 * (m_DataEnd - m_DataOut - 1)); }
INLINE void Reset()
{
@@ -775,10 +733,7 @@ public:
return reinterpret_cast<u8*>(m_Data);
}
- INLINE u8* GetData()
- {
- return GetBasePointer();
- }
+ INLINE u8* GetData() { return GetBasePointer(); }
INLINE void Finish()
{
@@ -851,10 +806,7 @@ public:
}
}
- INLINE void WriteSBitLong(i32 data, i32 numBits)
- {
- WriteUBitLong((u32)data, numBits, false);
- }
+ INLINE void WriteSBitLong(i32 data, i32 numBits) { WriteUBitLong((u32)data, numBits, false); }
INLINE void WriteUBitVar(u32 n)
{
@@ -911,40 +863,19 @@ public:
return !IsOverflowed();
}
- INLINE bool WriteBytes(const uptr data, i32 numBytes)
- {
- return WriteBits(data, numBytes << 3);
- }
+ INLINE bool WriteBytes(const uptr data, i32 numBytes) { return WriteBits(data, numBytes << 3); }
- INLINE i32 GetNumBitsWritten()
- {
- return (32 - m_OutBitsLeft) + (32 * (m_DataOut - m_Data));
- }
+ INLINE i32 GetNumBitsWritten() { return (32 - m_OutBitsLeft) + (32 * (m_DataOut - m_Data)); }
- INLINE i32 GetNumBytesWritten()
- {
- return (GetNumBitsWritten() + 7) >> 3;
- }
+ INLINE i32 GetNumBytesWritten() { return (GetNumBitsWritten() + 7) >> 3; }
- INLINE void WriteChar(i32 val)
- {
- WriteSBitLong(val, sizeof(char) << 3);
- }
+ INLINE void WriteChar(i32 val) { WriteSBitLong(val, sizeof(char) << 3); }
- INLINE void WriteByte(i32 val)
- {
- WriteUBitLong(val, sizeof(unsigned char) << 3, false);
- }
+ INLINE void WriteByte(i32 val) { WriteUBitLong(val, sizeof(unsigned char) << 3, false); }
- INLINE void WriteShort(i32 val)
- {
- WriteSBitLong(val, sizeof(short) << 3);
- }
+ INLINE void WriteShort(i32 val) { WriteSBitLong(val, sizeof(short) << 3); }
- INLINE void WriteWord(i32 val)
- {
- WriteUBitLong(val, sizeof(unsigned short) << 3);
- }
+ INLINE void WriteWord(i32 val) { WriteUBitLong(val, sizeof(unsigned short) << 3); }
INLINE bool WriteString(const char* str)
{
diff --git a/primedev/core/math/color.h b/primedev/core/math/color.h
index 013c4e4c..a46bc089 100644
--- a/primedev/core/math/color.h
+++ b/primedev/core/math/color.h
@@ -7,22 +7,10 @@ struct color24
typedef struct color32_s
{
- bool operator!=(const struct color32_s& other) const
- {
- return r != other.r || g != other.g || b != other.b || a != other.a;
- }
- inline unsigned* asInt(void)
- {
- return reinterpret_cast<unsigned*>(this);
- }
- inline const unsigned* asInt(void) const
- {
- return reinterpret_cast<const unsigned*>(this);
- }
- inline void Copy(const color32_s& rhs)
- {
- *asInt() = *rhs.asInt();
- }
+ bool operator!=(const struct color32_s& other) const { return r != other.r || g != other.g || b != other.b || a != other.a; }
+ inline unsigned* asInt(void) { return reinterpret_cast<unsigned*>(this); }
+ inline const unsigned* asInt(void) const { return reinterpret_cast<const unsigned*>(this); }
+ inline void Copy(const color32_s& rhs) { *asInt() = *rhs.asInt(); }
uint8_t r, g, b, a;
} color32;
@@ -79,55 +67,22 @@ public:
_b = _color[2];
_a = _color[3];
}
- int GetValue(int index) const
- {
- return _color[index];
- }
- void SetRawColor(int color32)
- {
- *((int*)this) = color32;
- }
- int GetRawColor(void) const
- {
- return *((int*)this);
- }
+ int GetValue(int index) const { return _color[index]; }
+ void SetRawColor(int color32) { *((int*)this) = color32; }
+ int GetRawColor(void) const { return *((int*)this); }
- inline int r() const
- {
- return _color[0];
- }
- inline int g() const
- {
- return _color[1];
- }
- inline int b() const
- {
- return _color[2];
- }
- inline int a() const
- {
- return _color[3];
- }
+ inline int r() const { return _color[0]; }
+ inline int g() const { return _color[1]; }
+ inline int b() const { return _color[2]; }
+ inline int a() const { return _color[3]; }
- unsigned char& operator[](int index)
- {
- return _color[index];
- }
+ unsigned char& operator[](int index) { return _color[index]; }
- const unsigned char& operator[](int index) const
- {
- return _color[index];
- }
+ const unsigned char& operator[](int index) const { return _color[index]; }
- bool operator==(const Color& rhs) const
- {
- return (*((int*)this) == *((int*)&rhs));
- }
+ bool operator==(const Color& rhs) const { return (*((int*)this) == *((int*)&rhs)); }
- bool operator!=(const Color& rhs) const
- {
- return !(operator==(rhs));
- }
+ bool operator!=(const Color& rhs) const { return !(operator==(rhs)); }
Color& operator=(const Color& rhs)
{
@@ -164,10 +119,7 @@ public:
return out;
}
- SourceColor ToSourceColor()
- {
- return SourceColor(_color[0], _color[1], _color[2], _color[3]);
- }
+ SourceColor ToSourceColor() { return SourceColor(_color[0], _color[1], _color[2], _color[3]); }
private:
unsigned char _color[4];
diff --git a/primedev/core/math/vector.h b/primedev/core/math/vector.h
index e62f2c93..ea1de65e 100644
--- a/primedev/core/math/vector.h
+++ b/primedev/core/math/vector.h
@@ -13,15 +13,27 @@ class Vector3
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()
+ 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)
{
- return IsFinite(x) && IsFinite(y) && IsFinite(z);
}
+ inline bool IsValid() { return IsFinite(x) && IsFinite(y) && IsFinite(z); }
+
inline void Init(float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f)
{
x = fX;
@@ -29,10 +41,7 @@ public:
z = fZ;
}
- inline float Length()
- {
- return FastSqrt(x * x + y * y + z * z);
- }
+ inline float Length() { return FastSqrt(x * x + y * y + z * z); }
inline float DistTo(const Vector3& vOther)
{
@@ -44,10 +53,7 @@ public:
return vDelta.Length();
}
- float Dot(const Vector3& vOther) const
- {
- return x * vOther.x + y * vOther.y + z * vOther.z;
- }
+ 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;
@@ -311,17 +317,29 @@ public:
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) {}
+ 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;
- }
+ bool operator==(const QAngle& other) { return x == other.x && y == other.y && z == other.z; }
};
inline Vector3 QAngle::GetNormal() const
diff --git a/primedev/core/memalloc.h b/primedev/core/memalloc.h
index 73e078f5..a55ce361 100644
--- a/primedev/core/memalloc.h
+++ b/primedev/core/memalloc.h
@@ -40,10 +40,7 @@ public:
}
return _realloc_base(originalPtr, newSize);
}
- static void Free(void* ptr)
- {
- _free_base(ptr);
- }
+ static void Free(void* ptr) { _free_base(ptr); }
};
typedef rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<SourceAllocator>, SourceAllocator> rapidjson_document;
diff --git a/primedev/core/sourceinterface.cpp b/primedev/core/sourceinterface.cpp
index 5a72beb0..7ce33925 100644
--- a/primedev/core/sourceinterface.cpp
+++ b/primedev/core/sourceinterface.cpp
@@ -1,16 +1,11 @@
-#include "sourceinterface.h"
#include "logging/sourceconsole.h"
-AUTOHOOK_INIT()
-
// really wanted to do a modular callback system here but honestly couldn't be bothered so hardcoding stuff for now: todo later
-// clang-format off
-AUTOHOOK_PROCADDRESS(ClientCreateInterface, client.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
+static void*(__fastcall* o_pClientCreateInterface)(const char* pName, const int* pReturnCode) = nullptr;
+static void* __fastcall h_ClientCreateInterface(const char* pName, const int* pReturnCode)
{
- void* ret = ClientCreateInterface(pName, pReturnCode);
+ void* ret = o_pClientCreateInterface(pName, pReturnCode);
spdlog::info("CreateInterface CLIENT {}", pName);
if (!strcmp(pName, "GameClientExports001"))
@@ -19,30 +14,38 @@ void*, __fastcall, (const char* pName, const int* pReturnCode))
return ret;
}
-// clang-format off
-AUTOHOOK_PROCADDRESS(ServerCreateInterface, server.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
+static void*(__fastcall* o_pServerCreateInterface)(const char* pName, const int* pReturnCode) = nullptr;
+static void* __fastcall h_ServerCreateInterface(const char* pName, const int* pReturnCode)
{
- void* ret = ServerCreateInterface(pName, pReturnCode);
+ void* ret = o_pServerCreateInterface(pName, pReturnCode);
spdlog::info("CreateInterface SERVER {}", pName);
return ret;
}
-// clang-format off
-AUTOHOOK_PROCADDRESS(EngineCreateInterface, engine.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
+static void*(__fastcall* o_pEngineCreateInterface)(const char* pName, const int* pReturnCode) = nullptr;
+static void* __fastcall h_EngineCreateInterface(const char* pName, const int* pReturnCode)
{
- void* ret = EngineCreateInterface(pName, pReturnCode);
+ void* ret = o_pEngineCreateInterface(pName, pReturnCode);
spdlog::info("CreateInterface ENGINE {}", pName);
return ret;
}
-// clang-format off
-ON_DLL_LOAD("client.dll", ClientInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(client.dll)}
-ON_DLL_LOAD("server.dll", ServerInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(server.dll)}
-ON_DLL_LOAD("engine.dll", EngineInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(engine.dll)}
-// clang-format on
+ON_DLL_LOAD("client.dll", ClientInterface, (CModule module))
+{
+ o_pClientCreateInterface = module.GetExportedFunction("CreateInterface").RCast<decltype(o_pClientCreateInterface)>();
+ HookAttach(&(PVOID&)o_pClientCreateInterface, (PVOID)h_ClientCreateInterface);
+}
+
+ON_DLL_LOAD("server.dll", ServerInterface, (CModule module))
+{
+ o_pServerCreateInterface = module.GetExportedFunction("CreateInterface").RCast<decltype(o_pServerCreateInterface)>();
+ HookAttach(&(PVOID&)o_pServerCreateInterface, (PVOID)h_ServerCreateInterface);
+}
+
+ON_DLL_LOAD("engine.dll", EngineInterface, (CModule module))
+{
+ o_pEngineCreateInterface = module.GetExportedFunction("CreateInterface").RCast<decltype(o_pEngineCreateInterface)>();
+ HookAttach(&(PVOID&)o_pEngineCreateInterface, (PVOID)h_EngineCreateInterface);
+}
diff --git a/primedev/core/sourceinterface.h b/primedev/core/sourceinterface.h
deleted file mode 100644
index bbbbd3bc..00000000
--- a/primedev/core/sourceinterface.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-#include <string>
-
-// interface return status
-enum class InterfaceStatus : int
-{
- IFACE_OK = 0,
- IFACE_FAILED,
-};
-
-// literally just copied from ttf2sdk definition
-typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
-
-template <typename T> class SourceInterface
-{
-private:
- T* m_interface;
-
-public:
- SourceInterface(const std::string& moduleName, const std::string& interfaceName)
- {
- HMODULE handle = GetModuleHandleA(moduleName.c_str());
- CreateInterfaceFn createInterface = (CreateInterfaceFn)GetProcAddress(handle, "CreateInterface");
- m_interface = (T*)createInterface(interfaceName.c_str(), NULL);
- if (m_interface == nullptr)
- spdlog::error("Failed to call CreateInterface for %s in %s", interfaceName, moduleName);
- }
-
- T* operator->() const
- {
- return m_interface;
- }
-
- operator T*() const
- {
- return m_interface;
- }
-};
diff --git a/primedev/core/tier0.cpp b/primedev/core/tier0.cpp
index dd5ac245..639b3bf8 100644
--- a/primedev/core/tier0.cpp
+++ b/primedev/core/tier0.cpp
@@ -18,11 +18,40 @@ void TryCreateGlobalMemAlloc()
g_pMemAllocSingleton = CreateGlobalMemAlloc(); // if it already exists, this returns the preexisting IMemAlloc instance
}
+HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ // need to grab it dynamically as this function was only introduced at some point in Windows 10
+ static decltype(&SetThreadDescription) _SetThreadDescription =
+ CModule("KernelBase.dll").GetExportedFunction("SetThreadDescription").RCast<decltype(&SetThreadDescription)>();
+
+ if (_SetThreadDescription)
+ return _SetThreadDescription(hThread, lpThreadDescription);
+
+ return ERROR_OLD_WIN_VERSION;
+}
+
+static void(__fastcall* o_pThreadSetDebugName)(HANDLE threadHandle, const char* name) = nullptr;
+static void __fastcall h_ThreadSetDebugName(HANDLE threadHandle, const char* name)
+{
+ if (threadHandle == 0)
+ threadHandle = GetCurrentThread();
+
+ // TODO: This "method" of "charset conversion" from string to wstring is abhorrent. Change it to a proper one
+ // as soon as Northstar has some helper function to do proper charset conversions.
+ auto tmp = std::string(name);
+ _SetThreadDescription(threadHandle, std::wstring(tmp.begin(), tmp.end()).c_str());
+
+ o_pThreadSetDebugName(threadHandle, name);
+}
+
ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module))
{
// shouldn't be necessary, but do this just in case
TryCreateGlobalMemAlloc();
+ o_pThreadSetDebugName = module.GetExportedFunction("ThreadSetDebugName").RCast<decltype(o_pThreadSetDebugName)>();
+ HookAttach(&(PVOID&)o_pThreadSetDebugName, (PVOID)h_ThreadSetDebugName);
+
// setup tier0 funcs
CommandLine = module.GetExportedFunction("CommandLine").RCast<CommandLineType>();
Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast<Plat_FloatTimeType>();
diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h
index d162e7c8..36f577cc 100644
--- a/primedev/core/tier1.h
+++ b/primedev/core/tier1.h
@@ -7,6 +7,12 @@
#define CREATEINTERFACE_PROCNAME "CreateInterface"
+enum class InterfaceStatus : int
+{
+ IFACE_OK = 0,
+ IFACE_FAILED,
+};
+
typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact);
diff --git a/primedev/core/vanilla.h b/primedev/core/vanilla.h
index b0797803..356495c8 100644
--- a/primedev/core/vanilla.h
+++ b/primedev/core/vanilla.h
@@ -17,10 +17,7 @@ public:
m_bIsVanillaCompatible = isVanilla;
}
- bool GetVanillaCompatibility()
- {
- return m_bIsVanillaCompatible;
- }
+ bool GetVanillaCompatibility() { return m_bIsVanillaCompatible; }
private:
bool m_bIsVanillaCompatible = false;
diff --git a/primedev/dedicated/dedicated.cpp b/primedev/dedicated/dedicated.cpp
index eca9b9f1..8b0604fc 100644
--- a/primedev/dedicated/dedicated.cpp
+++ b/primedev/dedicated/dedicated.cpp
@@ -8,8 +8,6 @@
#include "masterserver/masterserver.h"
#include "util/printcommands.h"
-AUTOHOOK_INIT()
-
bool IsDedicatedServer()
{
static bool result = strstr(GetCommandLineA(), "-dedicated");
@@ -114,10 +112,8 @@ DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter)
return 0;
}
-// clang-format off
-AUTOHOOK(IsGameActiveWindow, engine.dll + 0x1CDC80,
-bool,, ())
-// clang-format on
+static bool (*o_pIsGameActiveWindow)() = nullptr;
+static bool h_IsGameActiveWindow()
{
return true;
}
@@ -126,7 +122,8 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul
{
spdlog::info("InitialiseDedicated");
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
+ o_pIsGameActiveWindow = module.Offset(0x1CDC80).RCast<decltype(o_pIsGameActiveWindow)>();
+ HookAttach(&(PVOID&)o_pIsGameActiveWindow, (PVOID)h_IsGameActiveWindow);
// Host_Init
// prevent a particle init that relies on client dll
@@ -270,12 +267,10 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module))
module.GetExportedFunction("Tier0_InitOrigin").Patch("C3");
}
-// clang-format off
-AUTOHOOK(PrintSquirrelError, server.dll + 0x794D0,
-void, __fastcall, (void* sqvm))
-// clang-format on
+static void(__fastcall* o_pPrintSquirrelError)(void* sqvm) = nullptr;
+static void __fastcall h_PrintSquirrelError(void* sqvm)
{
- PrintSquirrelError(sqvm);
+ o_pPrintSquirrelError(sqvm);
// close dedicated server if a fatal error is hit
// atm, this will crash if not aborted, so this just closes more gracefully
@@ -289,7 +284,8 @@ void, __fastcall, (void* sqvm))
ON_DLL_LOAD_DEDI("server.dll", DedicatedServerGameDLL, (CModule module))
{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
+ o_pPrintSquirrelError = module.Offset(0x794D0).RCast<decltype(o_pPrintSquirrelError)>();
+ HookAttach(&(PVOID&)o_pPrintSquirrelError, (PVOID)h_PrintSquirrelError);
if (CommandLine()->CheckParm("-nopakdedi"))
{
diff --git a/primedev/dedicated/dedicatedmaterialsystem.cpp b/primedev/dedicated/dedicatedmaterialsystem.cpp
index 01078086..f74cbfe3 100644
--- a/primedev/dedicated/dedicatedmaterialsystem.cpp
+++ b/primedev/dedicated/dedicatedmaterialsystem.cpp
@@ -1,11 +1,18 @@
#include "dedicated.h"
#include "core/tier0.h"
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(D3D11CreateDevice, materialsystem_dx11.dll + 0xD9A0E,
-HRESULT, __stdcall, (
+static HRESULT(__stdcall* o_pD3D11CreateDevice)(
+ void* pAdapter,
+ int DriverType,
+ HMODULE Software,
+ UINT Flags,
+ int* pFeatureLevels,
+ UINT FeatureLevels,
+ UINT SDKVersion,
+ void** ppDevice,
+ int* pFeatureLevel,
+ void** ppImmediateContext) = nullptr;
+static HRESULT __stdcall h_D3D11CreateDevice(
void* pAdapter,
int DriverType,
HMODULE Software,
@@ -15,8 +22,7 @@ HRESULT, __stdcall, (
UINT SDKVersion,
void** ppDevice,
int* pFeatureLevel,
- void** ppImmediateContext))
-// clang-format on
+ void** ppImmediateContext)
{
// note: this is super duper temp pretty much just messing around with it
// does run surprisingly well on dedi for a software driver tho if you ignore the +1gb ram usage at times, seems like dedi doesn't
@@ -26,13 +32,14 @@ HRESULT, __stdcall, (
if (CommandLine()->CheckParm("-softwared3d11"))
DriverType = 5; // D3D_DRIVER_TYPE_WARP
- return D3D11CreateDevice(
+ return o_pD3D11CreateDevice(
pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext);
}
ON_DLL_LOAD_DEDI("materialsystem_dx11.dll", DedicatedServerMaterialSystem, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pD3D11CreateDevice = module.Offset(0xD9A0E).RCast<decltype(o_pD3D11CreateDevice)>();
+ HookAttach(&(PVOID&)o_pD3D11CreateDevice, (PVOID)h_D3D11CreateDevice);
// CMaterialSystem::FindMaterial
// make the game always use the error material
diff --git a/primedev/engine/gl_matsysiface.cpp b/primedev/engine/gl_matsysiface.cpp
new file mode 100644
index 00000000..075a56ac
--- /dev/null
+++ b/primedev/engine/gl_matsysiface.cpp
@@ -0,0 +1,50 @@
+#include "materialsystem/cmaterialglue.h"
+
+CMaterialGlue* (*GetMaterialAtCrossHair)();
+
+static void(__fastcall* o_pCC_mat_crosshair_printmaterial_f)(const CCommand& args) = nullptr;
+static void __fastcall h_CC_mat_crosshair_printmaterial_f(const CCommand& args)
+{
+ CMaterialGlue* pMat = GetMaterialAtCrossHair();
+
+ if (!pMat)
+ {
+ spdlog::error("Not looking at a material!");
+ return;
+ }
+
+ std::function<void(CMaterialGlue * pGlue, const char* szName)> fnPrintGlue = [](CMaterialGlue* pGlue, const char* szName)
+ {
+ spdlog::info("|-----------------------------------------------");
+
+ if (!pGlue)
+ {
+ spdlog::info("|-- {} is NULL", szName);
+ return;
+ }
+
+ spdlog::info("|-- Name: {}", szName);
+ spdlog::info("|-- GUID: {:#x}", pGlue->m_GUID);
+ spdlog::info("|-- Name: {}", pGlue->m_pszName);
+ spdlog::info("|-- Width : {}", pGlue->m_iWidth);
+ spdlog::info("|-- Height: {}", pGlue->m_iHeight);
+ };
+
+ spdlog::info("|- GUID: {:#x}", pMat->m_GUID);
+ spdlog::info("|- Name: {}", pMat->m_pszName);
+ spdlog::info("|- Width : {}", pMat->m_iWidth);
+ spdlog::info("|- Height: {}", pMat->m_iHeight);
+
+ fnPrintGlue(pMat->m_pDepthShadow, "DepthShadow");
+ fnPrintGlue(pMat->m_pDepthPrepass, "DepthPrepass");
+ fnPrintGlue(pMat->m_pDepthVSM, "DepthVSM");
+ fnPrintGlue(pMat->m_pColPass, "ColPass");
+}
+
+ON_DLL_LOAD("engine.dll", GlMatSysIFace, (CModule module))
+{
+ o_pCC_mat_crosshair_printmaterial_f = module.Offset(0xB3C40).RCast<decltype(o_pCC_mat_crosshair_printmaterial_f)>();
+ HookAttach(&(PVOID&)o_pCC_mat_crosshair_printmaterial_f, (PVOID)h_CC_mat_crosshair_printmaterial_f);
+
+ GetMaterialAtCrossHair = module.Offset(0xB37D0).RCast<CMaterialGlue* (*)()>();
+}
diff --git a/primedev/engine/host.cpp b/primedev/engine/host.cpp
index dacb8fc1..e414908b 100644
--- a/primedev/engine/host.cpp
+++ b/primedev/engine/host.cpp
@@ -6,15 +6,11 @@
#include "r2engine.h"
#include "core/tier0.h"
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(Host_Init, engine.dll + 0x155EA0,
-void, __fastcall, (bool bDedicated))
-// clang-format on
+static void(__fastcall* o_pHost_Init)(bool bDedicated) = nullptr;
+static void __fastcall h_Host_Init(bool bDedicated)
{
spdlog::info("Host_Init()");
- Host_Init(bDedicated);
+ o_pHost_Init(bDedicated);
FixupCvarFlags();
// need to initialise these after host_init since they do stuff to preexisting concommands/convars without being client/server specific
InitialiseCommandPrint();
@@ -29,5 +25,6 @@ void, __fastcall, (bool bDedicated))
ON_DLL_LOAD("engine.dll", Host_Init, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pHost_Init = module.Offset(0x155EA0).RCast<decltype(o_pHost_Init)>();
+ HookAttach(&(PVOID&)o_pHost_Init, (PVOID)h_Host_Init);
}
diff --git a/primedev/engine/hoststate.cpp b/primedev/engine/hoststate.cpp
index d5942551..4a4d909d 100644
--- a/primedev/engine/hoststate.cpp
+++ b/primedev/engine/hoststate.cpp
@@ -9,14 +9,12 @@
#include "squirrel/squirrel.h"
#include "plugins/pluginmanager.h"
-AUTOHOOK_INIT()
-
CHostState* g_pHostState;
std::string sLastMode;
-VAR_AT(engine.dll + 0x13FA6070, ConVar*, Cvar_hostport);
-FUNCTION_AT(engine.dll + 0x1232C0, void, __fastcall, _Cmd_Exec_f, (const CCommand& arg, bool bOnlyIfExists, bool bUseWhitelists));
+static ConVar* Cvar_hostport = nullptr;
+static void(__fastcall* _Cmd_Exec_f)(const CCommand& arg, bool bOnlyIfExists, bool bUseWhitelists) = nullptr;
void ServerStartingOrChangingMap()
{
@@ -53,10 +51,8 @@ void ServerStartingOrChangingMap()
g_pServerAuthentication->m_bStartingLocalSPGame = false;
}
-// clang-format off
-AUTOHOOK(CHostState__State_NewGame, engine.dll + 0x16E7D0,
-void, __fastcall, (CHostState* self))
-// clang-format on
+static void(__fastcall* o_pCHostState__State_NewGame)(CHostState* self) = nullptr;
+static void __fastcall h_CHostState__State_NewGame(CHostState* self)
{
spdlog::info("HostState: NewGame");
@@ -70,7 +66,7 @@ void, __fastcall, (CHostState* self))
ServerStartingOrChangingMap();
double dStartTime = Plat_FloatTime();
- CHostState__State_NewGame(self);
+ o_pCHostState__State_NewGame(self);
spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime);
// setup server presence
@@ -82,10 +78,8 @@ void, __fastcall, (CHostState* self))
g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false;
}
-// clang-format off
-AUTOHOOK(CHostState__State_LoadGame, engine.dll + 0x16E730,
-void, __fastcall, (CHostState* self))
-// clang-format on
+static void(__fastcall* o_pCHostState__State_LoadGame)(CHostState* self) = nullptr;
+static void __fastcall h_CHostState__State_LoadGame(CHostState* self)
{
// singleplayer server starting
// useless in 99% of cases but without it things could potentially break very much
@@ -100,7 +94,7 @@ void, __fastcall, (CHostState* self))
g_pServerAuthentication->m_bStartingLocalSPGame = true;
double dStartTime = Plat_FloatTime();
- CHostState__State_LoadGame(self);
+ o_pCHostState__State_LoadGame(self);
spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime);
// no server presence, can't do it because no map name in hoststate
@@ -109,32 +103,28 @@ void, __fastcall, (CHostState* self))
g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false;
}
-// clang-format off
-AUTOHOOK(CHostState__State_ChangeLevelMP, engine.dll + 0x16E520,
-void, __fastcall, (CHostState* self))
-// clang-format on
+static void(__fastcall* o_pCHostState__State_ChangeLevelMP)(CHostState* self) = nullptr;
+static void __fastcall h_CHostState__State_ChangeLevelMP(CHostState* self)
{
spdlog::info("HostState: ChangeLevelMP");
ServerStartingOrChangingMap();
double dStartTime = Plat_FloatTime();
- CHostState__State_ChangeLevelMP(self);
+ o_pCHostState__State_ChangeLevelMP(self);
spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime);
g_pServerPresence->SetMap(g_pHostState->m_levelName);
}
-// clang-format off
-AUTOHOOK(CHostState__State_GameShutdown, engine.dll + 0x16E640,
-void, __fastcall, (CHostState* self))
-// clang-format on
+static void(__fastcall* o_pCHostState__State_GameShutdown)(CHostState* self) = nullptr;
+static void __fastcall h_CHostState__State_GameShutdown(CHostState* self)
{
spdlog::info("HostState: GameShutdown");
g_pServerPresence->DestroyPresence();
- CHostState__State_GameShutdown(self);
+ o_pCHostState__State_GameShutdown(self);
// run gamemode cleanup cfg now instead of when we start next map
if (sLastMode.length())
@@ -153,12 +143,10 @@ void, __fastcall, (CHostState* self))
}
}
-// clang-format off
-AUTOHOOK(CHostState__FrameUpdate, engine.dll + 0x16DB00,
-void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime))
-// clang-format on
+static void(__fastcall* o_pCHostState__FrameUpdate)(CHostState* self, double flCurrentTime, float flFrameTime) = nullptr;
+static void __fastcall h_CHostState__FrameUpdate(CHostState* self, double flCurrentTime, float flFrameTime)
{
- CHostState__FrameUpdate(self, flCurrentTime, flFrameTime);
+ o_pCHostState__FrameUpdate(self, flCurrentTime, flFrameTime);
if (*g_pServerState == server_state_t::ss_active)
{
@@ -184,7 +172,23 @@ void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime))
ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCHostState__State_NewGame = module.Offset(0x16E7D0).RCast<decltype(o_pCHostState__State_NewGame)>();
+ HookAttach(&(PVOID&)o_pCHostState__State_NewGame, (PVOID)h_CHostState__State_NewGame);
+
+ o_pCHostState__State_LoadGame = module.Offset(0x16E730).RCast<decltype(o_pCHostState__State_LoadGame)>();
+ HookAttach(&(PVOID&)o_pCHostState__State_LoadGame, (PVOID)h_CHostState__State_LoadGame);
+
+ o_pCHostState__State_ChangeLevelMP = module.Offset(0x16E520).RCast<decltype(o_pCHostState__State_ChangeLevelMP)>();
+ HookAttach(&(PVOID&)o_pCHostState__State_ChangeLevelMP, (PVOID)h_CHostState__State_ChangeLevelMP);
+
+ o_pCHostState__State_GameShutdown = module.Offset(0x16E640).RCast<decltype(o_pCHostState__State_GameShutdown)>();
+ HookAttach(&(PVOID&)o_pCHostState__State_GameShutdown, (PVOID)h_CHostState__State_GameShutdown);
+
+ o_pCHostState__FrameUpdate = module.Offset(0x16DB00).RCast<decltype(o_pCHostState__FrameUpdate)>();
+ HookAttach(&(PVOID&)o_pCHostState__FrameUpdate, (PVOID)h_CHostState__FrameUpdate);
+
+ Cvar_hostport = module.Offset(0x13FA6070).RCast<decltype(Cvar_hostport)>();
+ _Cmd_Exec_f = module.Offset(0x1232C0).RCast<decltype(_Cmd_Exec_f)>();
g_pHostState = module.Offset(0x7CF180).RCast<CHostState*>();
}
diff --git a/primedev/engine/runframe.cpp b/primedev/engine/runframe.cpp
index ddfd9253..bfec9b8f 100644
--- a/primedev/engine/runframe.cpp
+++ b/primedev/engine/runframe.cpp
@@ -3,17 +3,14 @@
#include "hoststate.h"
#include "server/serverpresence.h"
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(CEngine__Frame, engine.dll + 0x1C8650,
-void, __fastcall, (CEngine* self))
-// clang-format on
+static void(__fastcall* o_pCEngine__Frame)(CEngine* self) = nullptr;
+static void __fastcall h_CEngine__Frame(CEngine* self)
{
- CEngine__Frame(self);
+ o_pCEngine__Frame(self);
}
ON_DLL_LOAD("engine.dll", RunFrame, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCEngine__Frame = module.Offset(0x1C8650).RCast<decltype(o_pCEngine__Frame)>();
+ HookAttach(&(PVOID&)o_pCEngine__Frame, (PVOID)h_CEngine__Frame);
}
diff --git a/primedev/game/client/clientmode_shared.cpp b/primedev/game/client/clientmode_shared.cpp
new file mode 100644
index 00000000..e5793261
--- /dev/null
+++ b/primedev/game/client/clientmode_shared.cpp
@@ -0,0 +1,66 @@
+
+//-----------------------------------------------------------------------------
+// Some explanation might be needed for this. The crash is caused by
+// us calling a pure virtual function in the constructor.
+// The order goes like this:
+// ctor
+// -> vftable = IPureCall::vftable
+// -> IPureCall::Ok()
+// -> IPureCall::CallMeIDareYou()
+// -> purecall_handler
+// -> crash :(
+class IPureCall
+{
+public:
+ IPureCall() { Ok(); }
+
+ virtual void CallMeIDareYou() = 0;
+
+ void Ok() { CallMeIDareYou(); }
+};
+
+class CPureCall : IPureCall
+{
+ virtual void CallMeIDareYou() {}
+};
+
+static void (*o_pCC_crash_test_f)(const CCommand& args);
+static void h_CC_crash_test_f(const CCommand& args)
+{
+ int crashtype = 0;
+ int dummy;
+ if (args.ArgC() > 1)
+ {
+ crashtype = atoi(args.Arg(1));
+ }
+ switch (crashtype)
+ {
+ case 0:
+ dummy = *((int*)NULL);
+ spdlog::info("Crashed! {}", dummy);
+ break;
+ case 1:
+ *((int*)NULL) = 24122021;
+ break;
+ case 2:
+ throw std::exception("Crashed!");
+ break;
+ case 3:
+ RaiseException(7, 0, 0, NULL);
+ break;
+ case 4:
+ {
+ CPureCall PureCall;
+ break;
+ }
+ default:
+ spdlog::info("Unknown variety of crash. You have now failed to crash. I hope you're happy.");
+ break;
+ }
+}
+
+ON_DLL_LOAD("engine.dll", ClientModeShared, (CModule module))
+{
+ o_pCC_crash_test_f = module.Offset(0x15BEE0).RCast<decltype(o_pCC_crash_test_f)>();
+ HookAttach(&(PVOID&)o_pCC_crash_test_f, (PVOID)h_CC_crash_test_f);
+}
diff --git a/primedev/logging/crashhandler.h b/primedev/logging/crashhandler.h
index c059a8ca..992dd7d2 100644
--- a/primedev/logging/crashhandler.h
+++ b/primedev/logging/crashhandler.h
@@ -14,35 +14,17 @@ public:
void Init();
void Shutdown();
- void Lock()
- {
- m_Mutex.lock();
- }
-
- void Unlock()
- {
- m_Mutex.unlock();
- }
-
- void SetState(bool bState)
- {
- m_bState = bState;
- }
-
- bool GetState() const
- {
- return m_bState;
- }
-
- void SetAllFatal(bool bState)
- {
- m_bAllExceptionsFatal = bState;
- }
-
- bool GetAllFatal() const
- {
- return m_bAllExceptionsFatal;
- }
+ void Lock() { m_Mutex.lock(); }
+
+ void Unlock() { m_Mutex.unlock(); }
+
+ void SetState(bool bState) { m_bState = bState; }
+
+ bool GetState() const { return m_bState; }
+
+ void SetAllFatal(bool bState) { m_bAllExceptionsFatal = bState; }
+
+ bool GetAllFatal() const { return m_bAllExceptionsFatal; }
//-----------------------------------------------------------------------------
// Exception helpers
diff --git a/primedev/logging/logging.cpp b/primedev/logging/logging.cpp
index 6d71eea0..e1298553 100644
--- a/primedev/logging/logging.cpp
+++ b/primedev/logging/logging.cpp
@@ -11,8 +11,6 @@
#include <iomanip>
#include <sstream>
-AUTOHOOK_INIT()
-
std::vector<std::shared_ptr<ColoredLogger>> loggers {};
namespace NS::log
diff --git a/primedev/logging/logging.h b/primedev/logging/logging.h
index be41cb39..edfa20dc 100644
--- a/primedev/logging/logging.h
+++ b/primedev/logging/logging.h
@@ -14,7 +14,11 @@ class ColoredLogger;
struct custom_log_msg : spdlog::details::log_msg
{
public:
- custom_log_msg(ColoredLogger* origin, spdlog::details::log_msg msg) : origin(origin), spdlog::details::log_msg(msg) {}
+ custom_log_msg(ColoredLogger* origin, spdlog::details::log_msg msg)
+ : origin(origin)
+ , spdlog::details::log_msg(msg)
+ {
+ }
ColoredLogger* origin;
};
@@ -38,7 +42,8 @@ public:
std::vector<std::shared_ptr<CustomSink>> custom_sinks_;
- ColoredLogger(std::string name, Color color, bool first = false) : spdlog::logger(*spdlog::default_logger())
+ ColoredLogger(std::string name, Color color, bool first = false)
+ : spdlog::logger(*spdlog::default_logger())
{
name_ = std::move(name);
if (!first)
diff --git a/primedev/logging/loghooks.cpp b/primedev/logging/loghooks.cpp
index dcd9b85a..51b4c241 100644
--- a/primedev/logging/loghooks.cpp
+++ b/primedev/logging/loghooks.cpp
@@ -9,8 +9,6 @@
#include <iomanip>
#include <sstream>
-AUTOHOOK_INIT()
-
ConVar* Cvar_spewlog_enable;
ConVar* Cvar_cl_showtextmsg;
@@ -70,10 +68,8 @@ const std::unordered_map<SpewType_t, const char> PrintSpewTypes_Short = {
ICenterPrint* pInternalCenterPrint = NULL;
-// clang-format off
-AUTOHOOK(TextMsg, client.dll + 0x198710,
-void,, (BFRead* msg))
-// clang-format on
+static void (*o_pTextMsg)(BFRead* msg) = nullptr;
+static void h_TextMsg(BFRead* msg)
{
TextMsgPrintType_t msg_dest = (TextMsgPrintType_t)msg->ReadByte();
@@ -103,10 +99,8 @@ void,, (BFRead* msg))
}
}
-// clang-format off
-AUTOHOOK(Hook_fprintf, engine.dll + 0x51B1F0,
-int,, (void* const stream, const char* const format, ...))
-// clang-format on
+static int (*o_pfprintf)(void* const stream, const char* const format, ...) = nullptr;
+static int h_fprintf(void* const stream, const char* const format, ...)
{
NOTE_UNUSED(stream);
@@ -127,19 +121,15 @@ int,, (void* const stream, const char* const format, ...))
return 0;
}
-// clang-format off
-AUTOHOOK(ConCommand_echo, engine.dll + 0x123680,
-void,, (const CCommand& arg))
-// clang-format on
+static void (*o_pConCommand_echo)(const CCommand& arg) = nullptr;
+static void h_ConCommand_echo(const CCommand& arg)
{
if (arg.ArgC() >= 2)
NS::log::echo->info("{}", arg.ArgS());
}
-// clang-format off
-AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80,
-void, __fastcall, (void* pEngineServer, SpewType_t type, const char* format, va_list args))
-// clang-format on
+static void(__fastcall* o_pEngineSpewFunc)(void* pEngineServer, SpewType_t type, const char* format, va_list args) = nullptr;
+static void __fastcall h_EngineSpewFunc(void* pEngineServer, SpewType_t type, const char* format, va_list args)
{
NOTE_UNUSED(pEngineServer);
if (!Cvar_spewlog_enable->GetBool())
@@ -214,10 +204,8 @@ void, __fastcall, (void* pEngineServer, SpewType_t type, const char* format, va_
}
// used for printing the output of status
-// clang-format off
-AUTOHOOK(Status_ConMsg, engine.dll + 0x15ABD0,
-void,, (const char* text, ...))
-// clang-format on
+static void (*o_pStatus_ConMsg)(const char* text, ...) = nullptr;
+static void h_Status_ConMsg(const char* text, ...)
{
char formatted[2048];
va_list list;
@@ -233,10 +221,8 @@ void,, (const char* text, ...))
spdlog::info(formatted);
}
-// clang-format off
-AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530,
-bool,, (void* thisptr, uintptr_t msg))
-// clang-format on
+static bool (*o_pCClientState_ProcessPrint)(void* thisptr, uintptr_t msg) = nullptr;
+static bool h_CClientState_ProcessPrint(void* thisptr, uintptr_t msg)
{
NOTE_UNUSED(thisptr);
@@ -252,14 +238,28 @@ bool,, (void* thisptr, uintptr_t msg))
ON_DLL_LOAD_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
+ o_pfprintf = module.Offset(0x51B1F0).RCast<decltype(o_pfprintf)>();
+ HookAttach(&(PVOID&)o_pfprintf, (PVOID)h_fprintf);
+
+ o_pConCommand_echo = module.Offset(0x123680).RCast<decltype(o_pConCommand_echo)>();
+ HookAttach(&(PVOID&)o_pConCommand_echo, (PVOID)h_ConCommand_echo);
+
+ o_pEngineSpewFunc = module.Offset(0x11CA80).RCast<decltype(o_pEngineSpewFunc)>();
+ HookAttach(&(PVOID&)o_pEngineSpewFunc, (PVOID)h_EngineSpewFunc);
+
+ o_pStatus_ConMsg = module.Offset(0x15ABD0).RCast<decltype(o_pStatus_ConMsg)>();
+ HookAttach(&(PVOID&)o_pStatus_ConMsg, (PVOID)h_Status_ConMsg);
+
+ o_pCClientState_ProcessPrint = module.Offset(0x1A1530).RCast<decltype(o_pCClientState_ProcessPrint)>();
+ HookAttach(&(PVOID&)o_pCClientState_ProcessPrint, (PVOID)h_CClientState_ProcessPrint);
Cvar_spewlog_enable = new ConVar("spewlog_enable", "0", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged");
}
ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientPrintHooks, ConVar, (CModule module))
{
- AUTOHOOK_DISPATCH_MODULE(client.dll)
+ o_pTextMsg = module.Offset(0x198710).RCast<decltype(o_pTextMsg)>();
+ HookAttach(&(PVOID&)o_pTextMsg, (PVOID)h_TextMsg);
Cvar_cl_showtextmsg = new ConVar("cl_showtextmsg", "1", FCVAR_NONE, "Enable/disable text messages printing on the screen.");
pInternalCenterPrint = module.Offset(0x216E940).RCast<ICenterPrint*>();
diff --git a/primedev/logging/sourceconsole.cpp b/primedev/logging/sourceconsole.cpp
index 55be4723..c9063d19 100644
--- a/primedev/logging/sourceconsole.cpp
+++ b/primedev/logging/sourceconsole.cpp
@@ -1,35 +1,35 @@
#include "core/convar/convar.h"
#include "sourceconsole.h"
-#include "core/sourceinterface.h"
+#include "core/tier1.h"
#include "core/convar/concommand.h"
#include "util/printcommands.h"
-SourceInterface<CGameConsole>* g_pSourceGameConsole;
+CGameConsole* g_pGameConsole;
void ConCommand_toggleconsole(const CCommand& arg)
{
NOTE_UNUSED(arg);
- if ((*g_pSourceGameConsole)->IsConsoleVisible())
- (*g_pSourceGameConsole)->Hide();
+ if (g_pGameConsole->IsConsoleVisible())
+ g_pGameConsole->Hide();
else
- (*g_pSourceGameConsole)->Activate();
+ g_pGameConsole->Activate();
}
void ConCommand_showconsole(const CCommand& arg)
{
NOTE_UNUSED(arg);
- (*g_pSourceGameConsole)->Activate();
+ g_pGameConsole->Activate();
}
void ConCommand_hideconsole(const CCommand& arg)
{
NOTE_UNUSED(arg);
- (*g_pSourceGameConsole)->Hide();
+ g_pGameConsole->Hide();
}
void SourceConsoleSink::custom_sink_it_(const custom_log_msg& msg)
{
- if (!(*g_pSourceGameConsole)->m_bInitialized)
+ if (!g_pGameConsole->m_bInitialized)
return;
spdlog::memory_buf_t formatted;
@@ -41,11 +41,11 @@ void SourceConsoleSink::custom_sink_it_(const custom_log_msg& msg)
SourceColor levelColor = m_LogColours[msg.level];
std::string name {msg.logger_name.begin(), msg.logger_name.end()};
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->ColorPrint(msg.origin->SRCColor, ("[" + name + "]").c_str());
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->Print(" ");
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->ColorPrint(levelColor, ("[" + std::string(level_names[msg.level]) + "]").c_str());
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->Print(" ");
- (*g_pSourceGameConsole)->m_pConsole->m_pConsolePanel->Print(fmt::to_string(formatted).c_str());
+ g_pGameConsole->m_pConsole->m_pConsolePanel->ColorPrint(msg.origin->SRCColor, ("[" + name + "]").c_str());
+ g_pGameConsole->m_pConsole->m_pConsolePanel->Print(" ");
+ g_pGameConsole->m_pConsole->m_pConsolePanel->ColorPrint(levelColor, ("[" + std::string(level_names[msg.level]) + "]").c_str());
+ g_pGameConsole->m_pConsole->m_pConsolePanel->Print(" ");
+ g_pGameConsole->m_pConsole->m_pConsolePanel->Print(fmt::to_string(formatted).c_str());
}
void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg)
@@ -73,9 +73,9 @@ void, __fastcall, (CConsoleDialog* consoleDialog, const char* pCommand))
// called from sourceinterface.cpp in client createinterface hooks, on GameClientExports001
void InitialiseConsoleOnInterfaceCreation()
{
- (*g_pSourceGameConsole)->Initialize();
+ g_pGameConsole->Initialize();
// hook OnCommandSubmitted so we print inputted commands
- OnCommandSubmittedHook.Dispatch((LPVOID)(*g_pSourceGameConsole)->m_pConsole->m_vtable->OnCommandSubmitted);
+ OnCommandSubmittedHook.Dispatch((LPVOID)g_pGameConsole->m_pConsole->m_vtable->OnCommandSubmitted);
auto consoleSink = std::make_shared<SourceConsoleSink>();
if (g_bSpdLog_UseAnsiColor)
@@ -87,7 +87,7 @@ void InitialiseConsoleOnInterfaceCreation()
ON_DLL_LOAD_CLIENT_RELIESON("client.dll", SourceConsole, ConCommand, (CModule module))
{
- g_pSourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004");
+ g_pGameConsole = Sys_GetFactoryPtr("client.dll", "GameConsole004").RCast<CGameConsole*>();
RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "Show/hide the console.", FCVAR_DONTRECORD);
RegisterConCommand("showconsole", ConCommand_showconsole, "Show the console.", FCVAR_DONTRECORD);
diff --git a/primedev/logging/sourceconsole.h b/primedev/logging/sourceconsole.h
index 215dae1a..8c647fb4 100644
--- a/primedev/logging/sourceconsole.h
+++ b/primedev/logging/sourceconsole.h
@@ -1,5 +1,4 @@
#pragma once
-#include "core/sourceinterface.h"
#include "spdlog/sinks/base_sink.h"
#include <map>
@@ -61,8 +60,6 @@ public:
CConsoleDialog* m_pConsole;
};
-extern SourceInterface<CGameConsole>* g_pSourceGameConsole;
-
// spdlog logger
class SourceConsoleSink : public CustomSink
{
diff --git a/primedev/masterserver/masterserver.cpp b/primedev/masterserver/masterserver.cpp
index e7bb1132..56ab608d 100644
--- a/primedev/masterserver/masterserver.cpp
+++ b/primedev/masterserver/masterserver.cpp
@@ -992,7 +992,12 @@ void ConCommand_ns_fetchservers(const CCommand& args)
g_pMasterServerManager->RequestServerList();
}
-MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_sOwnServerId {""}, m_sOwnClientAuthToken {""} {}
+MasterServerManager::MasterServerManager()
+ : m_pendingConnectionInfo {}
+ , m_sOwnServerId {""}
+ , m_sOwnClientAuthToken {""}
+{
+}
ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), (CModule module))
{
diff --git a/primedev/materialsystem/cmaterialglue.h b/primedev/materialsystem/cmaterialglue.h
new file mode 100644
index 00000000..1738a91a
--- /dev/null
+++ b/primedev/materialsystem/cmaterialglue.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "materialsystem/cshaderglue.h"
+
+class CMaterialGlue
+{
+public:
+ void* m_pVFTable;
+ char m_unk[8];
+
+ uint64_t m_GUID;
+
+ const char* m_pszName;
+ const char* m_pszSurfaceProp;
+ const char* m_pszSurfaceProp2;
+
+ CMaterialGlue* m_pDepthShadow;
+ CMaterialGlue* m_pDepthPrepass;
+ CMaterialGlue* m_pDepthVSM;
+ CMaterialGlue* m_pColPass;
+
+ char gap_50[64];
+
+ CShaderGlue* m_pShaderGlue;
+ void** m_pTextureHandles;
+ void** m_pStreamingTextures;
+ int16_t m_iStreamingTextureCount;
+ uint8_t m_iSamplersIndices[4];
+ int16_t m_iUnknown0;
+ char gap_B0[12];
+
+ int16_t aword_BC[2];
+ int32_t flags2;
+ int32_t flags3;
+ int16_t m_iWidth;
+ int16_t m_iHeight;
+ int16_t m_iUnknown1;
+ int16_t m_iUnknown2;
+
+ void** m_pUnkD3D11Ptr;
+ void* m_pD3D11Buffer;
+ void* qword_E0;
+ void* pointer_E8;
+
+ int32_t dword_F0;
+ char gap_F4[12];
+};
diff --git a/primedev/materialsystem/cshaderglue.h b/primedev/materialsystem/cshaderglue.h
new file mode 100644
index 00000000..8194f1fb
--- /dev/null
+++ b/primedev/materialsystem/cshaderglue.h
@@ -0,0 +1,7 @@
+#pragma once
+
+class CShaderGlue
+{
+public:
+ void* vftable;
+};
diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp
index 8e533dec..c20a3adb 100644
--- a/primedev/mods/autodownload/moddownloader.cpp
+++ b/primedev/mods/autodownload/moddownloader.cpp
@@ -103,23 +103,23 @@ void ModDownloader::FetchModsListFromAPI()
for (auto i = verifiedModsJson.MemberBegin(); i != verifiedModsJson.MemberEnd(); ++i)
{
// Format testing
- if (!i->value.HasMember("DependencyPrefix") || !i->value.HasMember("Versions"))
+ if (!i->value.HasMember("Repository") || !i->value.HasMember("Versions"))
{
spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading.");
return;
}
std::string name = i->name.GetString();
- std::string dependency = i->value["DependencyPrefix"].GetString();
-
std::unordered_map<std::string, VerifiedModVersion> modVersions;
+
rapidjson::Value& versions = i->value["Versions"];
assert(versions.IsArray());
for (auto& attribute : versions.GetArray())
{
assert(attribute.IsObject());
// Format testing
- if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum"))
+ if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum") || !attribute.HasMember("DownloadLink") ||
+ !attribute.HasMember("Platform"))
{
spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading.");
return;
@@ -127,10 +127,14 @@ void ModDownloader::FetchModsListFromAPI()
std::string version = attribute["Version"].GetString();
std::string checksum = attribute["Checksum"].GetString();
- modVersions.insert({version, {.checksum = checksum}});
+ std::string downloadLink = attribute["DownloadLink"].GetString();
+ std::string platformValue = attribute["Platform"].GetString();
+ VerifiedModPlatform platform =
+ platformValue.compare("thunderstore") == 0 ? VerifiedModPlatform::Thunderstore : VerifiedModPlatform::Unknown;
+ modVersions.insert({version, {.checksum = checksum, .downloadLink = downloadLink, .platform = platform}});
}
- VerifiedModDetails modConfig = {.dependencyPrefix = dependency, .versions = modVersions};
+ VerifiedModDetails modConfig = {.versions = modVersions};
verifiedMods.insert({name, modConfig});
spdlog::info("==> Loaded configuration for mod \"" + name + "\"");
}
@@ -164,13 +168,10 @@ int ModDownloader::ModFetchingProgressCallback(
return 0;
}
-std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion)
+std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
{
- // Retrieve mod prefix from local mods list, or use mod name as mod prefix if bypass flag is set
- std::string modPrefix = strstr(GetCommandLineA(), VERIFICATION_FLAG) ? modName.data() : verifiedMods[modName.data()].dependencyPrefix;
- // Build archive distant URI
- std::string archiveName = std::format("{}-{}.zip", modPrefix, modVersion.data());
- std::string url = STORE_URL + archiveName;
+ std::string url = version.downloadLink;
+ std::string archiveName = fs::path(url).filename().generic_string();
spdlog::info(std::format("Fetching mod archive from {}", url));
// Download destination
@@ -390,7 +391,7 @@ int GetModArchiveSize(unzFile file, unz_global_info64 info)
return totalSize;
}
-void ModDownloader::ExtractMod(fs::path modPath)
+void ModDownloader::ExtractMod(fs::path modPath, VerifiedModPlatform platform)
{
unzFile file;
std::string name;
@@ -428,6 +429,14 @@ void ModDownloader::ExtractMod(fs::path modPath)
modState.total = GetModArchiveSize(file, gi);
modState.progress = 0;
+ // Right now, we only know how to extract Thunderstore mods
+ if (platform != VerifiedModPlatform::Thunderstore)
+ {
+ spdlog::error("Failed extracting mod from unknown platform (value: {}).", platform);
+ modState.state = UNKNOWN_PLATFORM;
+ return;
+ }
+
// Mod directory name (removing the ".zip" fom the archive name)
name = modPath.filename().string();
name = name.substr(0, name.length() - 4);
@@ -598,8 +607,9 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
});
// Download mod archive
- std::string expectedHash = verifiedMods[modName].versions[modVersion].checksum;
- std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), std::string_view(modVersion));
+ VerifiedModVersion fullVersion = verifiedMods[modName].versions[modVersion];
+ std::string expectedHash = fullVersion.checksum;
+ std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), fullVersion);
if (!fetchingResult.has_value())
{
spdlog::error("Something went wrong while fetching archive, aborting.");
@@ -615,7 +625,7 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
}
// Extract downloaded mod archive
- ExtractMod(archiveLocation);
+ ExtractMod(archiveLocation, fullVersion.platform);
});
requestThread.detach();
diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h
index 98fc27ae..c7a88c19 100644
--- a/primedev/mods/autodownload/moddownloader.h
+++ b/primedev/mods/autodownload/moddownloader.h
@@ -5,17 +5,22 @@ class ModDownloader
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/main/verified-mods.json";
char* modsListUrl;
+ enum class VerifiedModPlatform
+ {
+ Unknown,
+ Thunderstore
+ };
struct VerifiedModVersion
{
std::string checksum;
+ std::string downloadLink;
+ VerifiedModPlatform platform;
};
struct VerifiedModDetails
{
- std::string dependencyPrefix;
std::unordered_map<std::string, VerifiedModVersion> versions = {};
};
std::unordered_map<std::string, VerifiedModDetails> verifiedMods = {};
@@ -45,7 +50,7 @@ private:
* @param modVersion version of the mod to be downloaded
* @returns location of the downloaded archive
*/
- std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, std::string_view modVersion);
+ std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, VerifiedModVersion modVersion);
/**
* Tells if a mod archive has not been corrupted.
@@ -65,12 +70,13 @@ private:
* Extracts a mod archive to the game folder.
*
* This extracts a downloaded mod archive from its original location to the
- * current game profile, in the remote mods folder.
+ * current game profile; the install folder is defined by the platform parameter.
*
* @param modPath location of the downloaded archive
+ * @param platform origin of the downloaded archive
* @returns nothing
*/
- void ExtractMod(fs::path modPath);
+ void ExtractMod(fs::path modPath, VerifiedModPlatform platform);
public:
ModDownloader();
@@ -131,7 +137,8 @@ public:
MOD_FETCHING_FAILED,
MOD_CORRUPTED, // Downloaded archive checksum does not match verified hash
NO_DISK_SPACE_AVAILABLE,
- NOT_FOUND // Mod is not currently being auto-downloaded
+ NOT_FOUND, // Mod is not currently being auto-downloaded
+ UNKNOWN_PLATFORM
};
struct MOD_STATE
diff --git a/primedev/mods/compiled/modkeyvalues.cpp b/primedev/mods/compiled/modkeyvalues.cpp
index e44a81d3..dfff706d 100644
--- a/primedev/mods/compiled/modkeyvalues.cpp
+++ b/primedev/mods/compiled/modkeyvalues.cpp
@@ -3,8 +3,6 @@
#include <fstream>
-AUTOHOOK_INIT()
-
void ModManager::TryBuildKeyValues(const char* filename)
{
spdlog::info("Building KeyValues for file {}", filename);
diff --git a/primedev/mods/modmanager.cpp b/primedev/mods/modmanager.cpp
index 45eddd3e..52fc6e8b 100644
--- a/primedev/mods/modmanager.cpp
+++ b/primedev/mods/modmanager.cpp
@@ -819,7 +819,7 @@ void ModManager::LoadMods()
modVpk.m_sVpkPath = (file.path().parent_path() / vpkName).string();
if (m_bHasLoadedMods && modVpk.m_bAutoLoad)
- (*g_pFilesystem)->m_vtable->MountVPK(*g_pFilesystem, vpkName.c_str());
+ g_pFilesystem->m_vtable->MountVPK(g_pFilesystem, vpkName.c_str());
}
}
}
@@ -866,7 +866,9 @@ void ModManager::LoadMods()
if (fs::is_regular_file(file) && file.path().extension() == ".rpak")
{
std::string pakName(file.path().filename().string());
- ModRpakEntry& modPak = mod.Rpaks.emplace_back();
+ ModRpakEntry& modPak = mod.Rpaks.emplace_back(mod);
+
+ modPak.m_pakName = pakName;
if (!bUseRpakJson)
{
@@ -874,19 +876,47 @@ void ModManager::LoadMods()
}
else
{
- modPak.m_bAutoLoad =
+ modPak.m_preload =
(dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() && dRpakJson["Preload"].HasMember(pakName) &&
dRpakJson["Preload"][pakName].IsTrue());
+ // only one load method can be used for an rpak.
+ if (modPak.m_preload)
+ goto REGISTER_STARPAK;
+
// postload things
if (dRpakJson.HasMember("Postload") && dRpakJson["Postload"].IsObject() && dRpakJson["Postload"].HasMember(pakName))
{
- modPak.m_sLoadAfterPak = dRpakJson["Postload"][pakName].GetString();
+ modPak.m_dependentPakHash = STR_HASH(dRpakJson["Postload"][pakName].GetString());
+
+ // only one load method can be used for an rpak.
+ goto REGISTER_STARPAK;
}
- }
- modPak.m_sPakName = pakName;
+ // this is the only bit of rpak.json that isn't really deprecated. Even so, it will be moved over to the mod.json
+ // eventually
+ if (dRpakJson.HasMember(pakName))
+ {
+ if (!dRpakJson[pakName].IsString())
+ {
+ spdlog::error("Mod {} has invalid rpak.json. Rpak entries must be strings.", mod.Name);
+ continue;
+ }
+
+ std::string loadStr = dRpakJson[pakName].GetString();
+ try
+ {
+ modPak.m_loadRegex = std::regex(loadStr);
+ }
+ catch (...)
+ {
+ spdlog::error("Mod {} has invalid rpak.json. Malformed regex \"{}\" for {}", mod.Name, loadStr, pakName);
+ return;
+ }
+ }
+ }
+ REGISTER_STARPAK:
// read header of file and get the starpak paths
// this is done here as opposed to on starpak load because multiple rpaks can load a starpak
// and there is seemingly no good way to tell which rpak is causing the load of a starpak :/
@@ -926,12 +956,11 @@ void ModManager::LoadMods()
}
}
}
-
- // not using atm because we need to resolve path to rpak
- // if (m_hasLoadedMods && modPak.m_bAutoLoad)
- // g_pPakLoadManager->LoadPakAsync(pakName.c_str());
}
}
+
+ if (g_pPakLoadManager != nullptr)
+ g_pPakLoadManager->TrackModPaks(mod);
}
// read keyvalues paths
@@ -1059,6 +1088,8 @@ void ModManager::UnloadMods()
fs::remove_all(GetCompiledAssetsPath());
g_CustomAudioManager.ClearAudioOverrides();
+ if (g_pPakLoadManager != nullptr)
+ g_pPakLoadManager->UnloadAllModPaks();
if (!m_bHasEnabledModsCfg)
m_EnabledModsCfg.SetObject();
diff --git a/primedev/mods/modmanager.h b/primedev/mods/modmanager.h
index 95a8fe12..7859d618 100644
--- a/primedev/mods/modmanager.h
+++ b/primedev/mods/modmanager.h
@@ -8,6 +8,7 @@
#include <vector>
#include <filesystem>
#include <unordered_set>
+#include <regex>
namespace fs = std::filesystem;
@@ -19,6 +20,8 @@ const std::string COMPILED_ASSETS_SUFFIX = "\\runtime\\compiled";
const std::set<std::string> MODS_BLACKLIST = {"Mod Settings"};
+class Mod;
+
struct ModConVar
{
public:
@@ -71,9 +74,22 @@ public:
struct ModRpakEntry
{
public:
- bool m_bAutoLoad;
- std::string m_sPakName;
- std::string m_sLoadAfterPak;
+ ModRpakEntry(Mod& parent)
+ : m_parent(parent)
+ , m_loadRegex("^thisMatchesNothing^") // discord couldnt give me a funny string
+ {
+ }
+
+ Mod& m_parent;
+ std::string m_pakName;
+ std::regex m_loadRegex;
+
+ // these exist purely for backwards compatibility, i don't really like them anymore
+
+ // Preload, loads before the first rpak is loaded
+ bool m_preload = false;
+ // Postload, this rpak depends on an rpak with this hash
+ size_t m_dependentPakHash;
};
class Mod
@@ -120,11 +136,12 @@ public:
std::string Pdiff; // only need one per mod
std::vector<ModRpakEntry> Rpaks;
- std::unordered_map<std::string, std::string>
- RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
- std::vector<size_t> StarpakPaths; // starpaks that this mod contains
+ // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
+ std::unordered_map<std::string, std::string> RpakAliases;
+ // starpaks that this mod contains
// there seems to be no nice way to get the rpak that is causing the load of a starpak?
// hashed with STR_HASH
+ std::vector<size_t> StarpakPaths;
std::unordered_map<std::string, std::string> DependencyConstants;
std::vector<std::string> PluginDependencyConstants;
diff --git a/primedev/mods/modsavefiles.cpp b/primedev/mods/modsavefiles.cpp
index a7075612..928f1dfd 100644
--- a/primedev/mods/modsavefiles.cpp
+++ b/primedev/mods/modsavefiles.cpp
@@ -555,7 +555,7 @@ ADD_SQFUNC("int", NSGetTotalSpaceRemaining, "", "", ScriptContext::CLIENT | Scri
// ok, I'm just gonna explain what the fuck is going on here because this
// is the pinnacle of my stupidity and I do not want to touch this ever
// again, yet someone will eventually have to maintain this.
-template <ScriptContext context> std::string EncodeJSON(HSquirrelVM* sqvm)
+template <ScriptContext context> std::string EncodeJSON(HSQUIRRELVM sqvm)
{
// new rapidjson
rapidjson_document doc;
diff --git a/primedev/plugins/interfaces/IPluginId.h b/primedev/plugins/interfaces/IPluginId.h
index dc4c548b..0b025224 100644
--- a/primedev/plugins/interfaces/IPluginId.h
+++ b/primedev/plugins/interfaces/IPluginId.h
@@ -18,6 +18,7 @@ enum class PluginString : int
enum class PluginField : int
{
CONTEXT = 0,
+ COLOR = 1,
};
// an interface that is required from every plugin to query data about it
diff --git a/primedev/plugins/interfaces/interface.cpp b/primedev/plugins/interfaces/interface.cpp
index 4c006f2c..e8200560 100644
--- a/primedev/plugins/interfaces/interface.cpp
+++ b/primedev/plugins/interfaces/interface.cpp
@@ -1,9 +1,11 @@
#include <string.h>
+#include "core/tier1.h"
#include "interface.h"
InterfaceReg* s_pInterfaceRegs;
-InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName) : m_pName(pName)
+InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName)
+ : m_pName(pName)
{
m_CreateFn = fn;
m_pNext = s_pInterfaceRegs;
diff --git a/primedev/plugins/interfaces/sys/ISys.cpp b/primedev/plugins/interfaces/sys/ISys.cpp
index 6b0b41dd..948e7d90 100644
--- a/primedev/plugins/interfaces/sys/ISys.cpp
+++ b/primedev/plugins/interfaces/sys/ISys.cpp
@@ -1,3 +1,4 @@
+#include "core/tier1.h"
#include "plugins/interfaces/interface.h"
#include "ISys.h"
#include "plugins/plugins.h"
diff --git a/primedev/plugins/plugins.cpp b/primedev/plugins/plugins.cpp
index 03dd2c9e..3e623167 100644
--- a/primedev/plugins/plugins.cpp
+++ b/primedev/plugins/plugins.cpp
@@ -2,7 +2,6 @@
#include "pluginmanager.h"
#include "squirrel/squirrel.h"
#include "util/wininfo.h"
-#include "core/sourceinterface.h"
#include "logging/logging.h"
#include "dedicated/dedicated.h"
@@ -22,7 +21,9 @@ bool isValidSquirrelIdentifier(std::string s)
return true;
}
-Plugin::Plugin(std::string path) : m_location(path)
+Plugin::Plugin(std::string path)
+ : m_location(path)
+ , m_logColor(NS::Colors::PLUGIN)
{
HMODULE pluginModule = GetModuleHandleA(path.c_str());
@@ -69,6 +70,13 @@ Plugin::Plugin(std::string path) : m_location(path)
m_runOnServer = context & PluginContext::DEDICATED;
m_runOnClient = context & PluginContext::CLIENT;
+ int64_t logColor = m_pluginId->GetField(PluginField::COLOR);
+ // Apply custom colour if plugin has specified one
+ if ((logColor & 0xFFFFFF) != 0)
+ {
+ m_logColor = Color((int)(logColor & 0xFF), (int)((logColor >> 8) & 0xFF), (int)((logColor >> 16) & 0xFF));
+ }
+
if (!name)
{
NS::log::PLUGINSYS->error("Could not load name of plugin at '{}'", path);
@@ -105,7 +113,7 @@ Plugin::Plugin(std::string path) : m_location(path)
return;
}
- m_logger = std::make_shared<ColoredLogger>(m_logName, NS::Colors::PLUGIN);
+ m_logger = std::make_shared<ColoredLogger>(m_logName, m_logColor);
RegisterLogger(m_logger);
if (IsDedicatedServer() && !m_runOnServer)
@@ -218,7 +226,6 @@ void Plugin::OnSqvmCreated(CSquirrelVM* sqvm) const
void Plugin::OnSqvmDestroying(CSquirrelVM* sqvm) const
{
- NS::log::PLUGINSYS->info("destroying sqvm {}", sqvm->vmContext);
m_callbacks->OnSqvmDestroying(sqvm);
}
diff --git a/primedev/plugins/plugins.h b/primedev/plugins/plugins.h
index d004038c..95ec08b5 100644
--- a/primedev/plugins/plugins.h
+++ b/primedev/plugins/plugins.h
@@ -1,5 +1,5 @@
#pragma once
-#include "core/sourceinterface.h"
+#include "core/tier1.h"
#include "plugins/interfaces/interface.h"
#include "plugins/interfaces/IPluginId.h"
#include "plugins/interfaces/IPluginCallbacks.h"
@@ -20,6 +20,7 @@ private:
std::string m_location; // path of the dll
bool m_runOnServer;
bool m_runOnClient;
+ Color m_logColor;
public:
HMODULE m_handle;
diff --git a/primedev/scripts/client/clientchathooks.cpp b/primedev/scripts/client/clientchathooks.cpp
index e084f47e..c0a06dd2 100644
--- a/primedev/scripts/client/clientchathooks.cpp
+++ b/primedev/scripts/client/clientchathooks.cpp
@@ -6,12 +6,8 @@
#include <rapidjson/document.h>
-AUTOHOOK_INIT()
-
-// clang-format off
-AUTOHOOK(CHudChat__AddGameLine, client.dll + 0x22E580,
-void, __fastcall, (void* self, const char* message, int inboxId, bool isTeam, bool isDead))
-// clang-format on
+static void(__fastcall* o_pCHudChat__AddGameLine)(void* self, const char* message, int inboxId, bool isTeam, bool isDead) = nullptr;
+static void __fastcall h_CHudChat__AddGameLine(void* self, const char* message, int inboxId, bool isTeam, bool isDead)
{
// This hook is called for each HUD, but we only want our logic to run once.
if (self != *CHudChat::allHuds)
@@ -36,7 +32,7 @@ void, __fastcall, (void* self, const char* message, int inboxId, bool isTeam, bo
"CHudChat_ProcessMessageStartThread", static_cast<int>(senderId) - 1, payload, isTeam, isDead, type);
if (result == SQRESULT_ERROR)
for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
- CHudChat__AddGameLine(hud, message, inboxId, isTeam, isDead);
+ o_pCHudChat__AddGameLine(hud, message, inboxId, isTeam, isDead);
}
ADD_SQFUNC("void", NSChatWrite, "int context, string text", "", ScriptContext::CLIENT)
@@ -68,5 +64,6 @@ ADD_SQFUNC("void", NSChatWriteLine, "int context, string text", "", ScriptContex
ON_DLL_LOAD_CLIENT("client.dll", ClientChatHooks, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCHudChat__AddGameLine = module.Offset(0x22E580).RCast<decltype(o_pCHudChat__AddGameLine)>();
+ HookAttach(&(PVOID&)o_pCHudChat__AddGameLine, (PVOID)h_CHudChat__AddGameLine);
}
diff --git a/primedev/scripts/scriptdatatables.cpp b/primedev/scripts/scriptdatatables.cpp
index 5e685b48..b3c59921 100644
--- a/primedev/scripts/scriptdatatables.cpp
+++ b/primedev/scripts/scriptdatatables.cpp
@@ -44,7 +44,7 @@ struct Datatable
ConVar* Cvar_ns_prefer_datatable_from_disk;
-template <ScriptContext context> Datatable* (*SQ_GetDatatableInternal)(HSquirrelVM* sqvm);
+template <ScriptContext context> Datatable* (*SQ_GetDatatableInternal)(HSQUIRRELVM sqvm);
struct CSVData
{
@@ -70,10 +70,11 @@ REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | Script
g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Asset \"{}\" doesn't start with \"datatable/\"", pAssetName).c_str());
return SQRESULT_ERROR;
}
- else if (!Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(pAssetName))
+ else if (!Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->OpenFile(pAssetName))
+ {
return g_pSquirrel<context>->m_funcOriginals["GetDataTable"](sqvm);
- // either we prefer disk datatables, or we're loading a datatable that wasn't found in rpak
- else
+ }
+ else // either we prefer disk datatables, or we're loading a datatable that wasn't found in rpak
{
std::string sAssetPath(fmt::format("scripts/{}", pAssetName));
@@ -96,7 +97,7 @@ REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | Script
diskAssetPath /= fs::path(pAssetName);
std::string sDiskAssetPath(diskAssetPath.string());
- if ((*g_pFilesystem)->m_vtable2->FileExists(&(*g_pFilesystem)->m_vtable2, sDiskAssetPath.c_str(), "GAME"))
+ if (g_pFilesystem->m_vtable2->FileExists(&g_pFilesystem->m_vtable2, sDiskAssetPath.c_str(), "GAME"))
{
std::string sTableCSV = ReadVPKFile(sDiskAssetPath.c_str());
if (!sTableCSV.size())
@@ -223,7 +224,7 @@ REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | Script
return SQRESULT_NOTNULL;
}
// the file doesn't exist on disk, check rpak if we haven't already
- else if (Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(pAssetName))
+ else if (Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->OpenFile(pAssetName))
return g_pSquirrel<context>->m_funcOriginals["GetDataTable"](sqvm);
// the file doesn't exist at all, error
else
@@ -750,7 +751,7 @@ std::string DataTableToString(Datatable* datatable)
void DumpDatatable(const char* pDatatablePath)
{
- Datatable* pDatatable = (Datatable*)g_pPakLoadManager->LoadFile(pDatatablePath);
+ Datatable* pDatatable = (Datatable*)g_pPakLoadManager->OpenFile(pDatatablePath);
if (!pDatatable)
{
spdlog::error("couldn't load datatable {} (rpak containing it may not be loaded?)", pDatatablePath);
@@ -852,12 +853,12 @@ void ConCommand_dump_datatables(const CCommand& args)
ON_DLL_LOAD_RELIESON("server.dll", ServerScriptDatatables, ServerSquirrel, (CModule module))
{
- SQ_GetDatatableInternal<ScriptContext::SERVER> = module.Offset(0x1250f0).RCast<Datatable* (*)(HSquirrelVM*)>();
+ SQ_GetDatatableInternal<ScriptContext::SERVER> = module.Offset(0x1250f0).RCast<Datatable* (*)(HSQUIRRELVM)>();
}
ON_DLL_LOAD_RELIESON("client.dll", ClientScriptDatatables, ClientSquirrel, (CModule module))
{
- SQ_GetDatatableInternal<ScriptContext::CLIENT> = module.Offset(0x1C9070).RCast<Datatable* (*)(HSquirrelVM*)>();
+ SQ_GetDatatableInternal<ScriptContext::CLIENT> = module.Offset(0x1C9070).RCast<Datatable* (*)(HSQUIRRELVM)>();
SQ_GetDatatableInternal<ScriptContext::UI> = SQ_GetDatatableInternal<ScriptContext::CLIENT>;
}
diff --git a/primedev/scripts/scripthttprequesthandler.cpp b/primedev/scripts/scripthttprequesthandler.cpp
index 69828a5a..f45e83f0 100644
--- a/primedev/scripts/scripthttprequesthandler.cpp
+++ b/primedev/scripts/scripthttprequesthandler.cpp
@@ -450,7 +450,7 @@ template <ScriptContext context> int HttpRequestHandler::MakeHttpRequest(const H
// int NS_InternalMakeHttpRequest(int method, string baseUrl, table<string, string> headers, table<string, string> queryParams,
// string contentType, string body, int timeout, string userAgent)
-template <ScriptContext context> SQRESULT SQ_InternalMakeHttpRequest(HSquirrelVM* sqvm)
+template <ScriptContext context> SQRESULT SQ_InternalMakeHttpRequest(HSQUIRRELVM sqvm)
{
if (!g_httpRequestHandler || !g_httpRequestHandler->IsRunning())
{
@@ -475,7 +475,7 @@ template <ScriptContext context> SQRESULT SQ_InternalMakeHttpRequest(HSquirrelVM
SQTable* headerTable = sqvm->_stackOfCurrentFunction[3]._VAL.asTable;
for (int idx = 0; idx < headerTable->_numOfNodes; ++idx)
{
- tableNode* node = &headerTable->_nodes[idx];
+ SQTable::_HashNode* node = &headerTable->_nodes[idx];
if (node->key._Type == OT_STRING && node->val._Type == OT_ARRAY)
{
@@ -497,7 +497,7 @@ template <ScriptContext context> SQRESULT SQ_InternalMakeHttpRequest(HSquirrelVM
SQTable* queryTable = sqvm->_stackOfCurrentFunction[4]._VAL.asTable;
for (int idx = 0; idx < queryTable->_numOfNodes; ++idx)
{
- tableNode* node = &queryTable->_nodes[idx];
+ SQTable::_HashNode* node = &queryTable->_nodes[idx];
if (node->key._Type == OT_STRING && node->val._Type == OT_ARRAY)
{
@@ -527,14 +527,14 @@ template <ScriptContext context> SQRESULT SQ_InternalMakeHttpRequest(HSquirrelVM
}
// bool NSIsHttpEnabled()
-template <ScriptContext context> SQRESULT SQ_IsHttpEnabled(HSquirrelVM* sqvm)
+template <ScriptContext context> SQRESULT SQ_IsHttpEnabled(HSQUIRRELVM sqvm)
{
g_pSquirrel<context>->pushbool(sqvm, !IsHttpDisabled());
return SQRESULT_NOTNULL;
}
// bool NSIsLocalHttpAllowed()
-template <ScriptContext context> SQRESULT SQ_IsLocalHttpAllowed(HSquirrelVM* sqvm)
+template <ScriptContext context> SQRESULT SQ_IsLocalHttpAllowed(HSQUIRRELVM sqvm)
{
g_pSquirrel<context>->pushbool(sqvm, IsLocalHttpAllowed());
return SQRESULT_NOTNULL;
diff --git a/primedev/scripts/scripthttprequesthandler.h b/primedev/scripts/scripthttprequesthandler.h
index f3921f4e..72f719ec 100644
--- a/primedev/scripts/scripthttprequesthandler.h
+++ b/primedev/scripts/scripthttprequesthandler.h
@@ -107,10 +107,7 @@ public:
void StopHttpRequestHandler();
// Whether or not this http request handler is currently running.
- bool IsRunning() const
- {
- return m_bIsHttpRequestHandlerRunning;
- }
+ bool IsRunning() const { return m_bIsHttpRequestHandlerRunning; }
/**
* Creates a new thread to execute an HTTP request.
diff --git a/primedev/scripts/scriptjson.cpp b/primedev/scripts/scriptjson.cpp
index 8959bf47..91553ae3 100644
--- a/primedev/scripts/scriptjson.cpp
+++ b/primedev/scripts/scriptjson.cpp
@@ -9,8 +9,8 @@
#undef GetObject // fuck microsoft developers
#endif
-template <ScriptContext context> void
-DecodeJsonArray(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr)
+template <ScriptContext context>
+void DecodeJsonArray(HSQUIRRELVM sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* arr)
{
g_pSquirrel<context>->newarray(sqvm, 0);
@@ -48,8 +48,8 @@ DecodeJsonArray(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>
}
}
-template <ScriptContext context> void
-DecodeJsonTable(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj)
+template <ScriptContext context>
+void DecodeJsonTable(HSQUIRRELVM sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj)
{
g_pSquirrel<context>->newtable(sqvm);
@@ -107,7 +107,7 @@ template <ScriptContext context> void EncodeJSONTable(
{
for (int i = 0; i < table->_numOfNodes; i++)
{
- tableNode* node = &table->_nodes[i];
+ SQTable::_HashNode* node = &table->_nodes[i];
if (node->key._Type == OT_STRING)
{
rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>> newObj(rapidjson::kObjectType);
@@ -240,7 +240,7 @@ ADD_SQFUNC(
doc.SetObject();
// temp until this is just the func parameter type
- HSquirrelVM* vm = (HSquirrelVM*)sqvm;
+ HSQUIRRELVM vm = (HSQUIRRELVM)sqvm;
SQTable* table = vm->_stackOfCurrentFunction[1]._VAL.asTable;
EncodeJSONTable<context>(table, &doc, doc.GetAllocator());
diff --git a/primedev/scripts/scriptjson.h b/primedev/scripts/scriptjson.h
index b747106b..f766e3f0 100644
--- a/primedev/scripts/scriptjson.h
+++ b/primedev/scripts/scriptjson.h
@@ -10,4 +10,4 @@ template <ScriptContext context> void EncodeJSONTable(
rapidjson::MemoryPoolAllocator<SourceAllocator>& allocator);
template <ScriptContext context> void
-DecodeJsonTable(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj);
+DecodeJsonTable(HSQUIRRELVM sqvm, rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<SourceAllocator>>* obj);
diff --git a/primedev/server/ai_navmesh.h b/primedev/server/ai_navmesh.h
index 65529f7a..c3339110 100644
--- a/primedev/server/ai_navmesh.h
+++ b/primedev/server/ai_navmesh.h
@@ -207,28 +207,16 @@ struct dtPoly
Vector3 org; //
/// Sets the user defined area id. [Limit: < #DT_MAX_AREAS]
- inline void setArea(unsigned char a)
- {
- areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f);
- }
+ 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);
- }
+ 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;
- }
+ inline unsigned char getArea() const { return areaAndtype & 0x3f; }
/// Gets the polygon type. (See: #dtPolyTypes)
- inline unsigned char getType() const
- {
- return areaAndtype >> 6;
- }
+ inline unsigned char getType() const { return areaAndtype >> 6; }
};
/// Defines a link between polygons.
diff --git a/primedev/server/auth/serverauthentication.cpp b/primedev/server/auth/serverauthentication.cpp
index d0d4c698..58268bcf 100644
--- a/primedev/server/auth/serverauthentication.cpp
+++ b/primedev/server/auth/serverauthentication.cpp
@@ -19,8 +19,6 @@
#include <string>
#include <thread>
-AUTOHOOK_INIT()
-
// global vars
ServerAuthenticationManager* g_pServerAuthentication;
CBaseServer__RejectConnectionType CBaseServer__RejectConnection;
@@ -207,9 +205,25 @@ void ServerAuthenticationManager::WritePersistentData(CBaseClient* pPlayer)
char* pNextPlayerToken;
uint64_t iNextPlayerUid;
-// clang-format off
-AUTOHOOK(CBaseServer__ConnectClient, engine.dll + 0x114430,
-void*,, (
+static void* (*o_pCBaseServer__ConnectClient)(
+ void* self,
+ void* addr,
+ void* a3,
+ uint32_t a4,
+ uint32_t a5,
+ int32_t a6,
+ void* a7,
+ char* playerName,
+ char* serverFilter,
+ void* a10,
+ char a11,
+ void* a12,
+ char a13,
+ char a14,
+ int64_t uid,
+ uint32_t a16,
+ uint32_t a17) = nullptr;
+static void* h_CBaseServer__ConnectClient(
void* self,
void* addr,
void* a3,
@@ -226,22 +240,21 @@ void*,, (
char a14,
int64_t uid,
uint32_t a16,
- uint32_t a17))
-// clang-format on
+ uint32_t a17)
{
// auth tokens are sent with serverfilter, can't be accessed from player struct to my knowledge, so have to do this here
pNextPlayerToken = serverFilter;
iNextPlayerUid = uid;
- return CBaseServer__ConnectClient(self, addr, a3, a4, a5, a6, a7, playerName, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17);
+ return o_pCBaseServer__ConnectClient(self, addr, a3, a4, a5, a6, a7, playerName, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17);
}
ConVar* Cvar_ns_allowuserclantags;
-// clang-format off
-AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740,
-bool,, (CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7))
-// clang-format on
+static bool (*o_pCBaseClient__Connect)(
+ CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7) = nullptr;
+static bool
+h_CBaseClient__Connect(CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7)
{
const char* pAuthenticationFailure = nullptr;
char pVerifiedName[64];
@@ -267,7 +280,7 @@ bool,, (CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, voi
}
// try to actually connect the player
- if (!CBaseClient__Connect(self, pVerifiedName, pNetChannel, bFakePlayer, a5, pDisconnectReason, a7))
+ if (!o_pCBaseClient__Connect(self, pVerifiedName, pNetChannel, bFakePlayer, a5, pDisconnectReason, a7))
return false;
// we already know this player's authentication data is legit, actually write it to them now
@@ -279,10 +292,8 @@ bool,, (CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, voi
return true;
}
-// clang-format off
-AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80,
-void,, (CBaseClient* self))
-// clang-format on
+static void (*o_pCBaseClient__ActivatePlayer)(CBaseClient* self) = nullptr;
+static void h_CBaseClient__ActivatePlayer(CBaseClient* self)
{
// if we're authed, write our persistent data
// RemovePlayerAuthData returns true if it removed successfully, i.e. on first call only, and we only want to write on >= second call
@@ -294,13 +305,11 @@ void,, (CBaseClient* self))
g_pServerPresence->SetPlayerCount((int)g_pServerAuthentication->m_PlayerAuthenticationData.size());
}
- CBaseClient__ActivatePlayer(self);
+ o_pCBaseClient__ActivatePlayer(self);
}
-// clang-format off
-AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0,
-void,, (CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...))
-// clang-format on
+static void (*o_pCBaseClient__Disconnect)(CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...) = nullptr;
+static void h_CBaseClient__Disconnect(CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...)
{
// have to manually format message because can't pass varargs to original func
char buf[1024];
@@ -328,7 +337,7 @@ void,, (CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...)
g_pServerPresence->SetPlayerCount((int)g_pServerAuthentication->m_PlayerAuthenticationData.size());
- _CBaseClient__Disconnect(self, unknownButAlways1, buf);
+ o_pCBaseClient__Disconnect(self, unknownButAlways1, buf);
}
void ConCommand_ns_resetpersistence(const CCommand& args)
@@ -346,7 +355,17 @@ void ConCommand_ns_resetpersistence(const CCommand& args)
ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCBaseServer__ConnectClient = module.Offset(0x114430).RCast<decltype(o_pCBaseServer__ConnectClient)>();
+ HookAttach(&(PVOID&)o_pCBaseServer__ConnectClient, (PVOID)h_CBaseServer__ConnectClient);
+
+ o_pCBaseClient__Connect = module.Offset(0x101740).RCast<decltype(o_pCBaseClient__Connect)>();
+ HookAttach(&(PVOID&)o_pCBaseClient__Connect, (PVOID)h_CBaseClient__Connect);
+
+ o_pCBaseClient__ActivatePlayer = module.Offset(0x100F80).RCast<decltype(o_pCBaseClient__ActivatePlayer)>();
+ HookAttach(&(PVOID&)o_pCBaseClient__ActivatePlayer, (PVOID)h_CBaseClient__ActivatePlayer);
+
+ o_pCBaseClient__Disconnect = module.Offset(0x1012C0).RCast<decltype(o_pCBaseClient__Disconnect)>();
+ HookAttach(&(PVOID&)o_pCBaseClient__Disconnect, (PVOID)h_CBaseClient__Disconnect);
g_pServerAuthentication = new ServerAuthenticationManager;
diff --git a/primedev/server/buildainfile.cpp b/primedev/server/buildainfile.cpp
index 19a6d0e3..456c84f0 100644
--- a/primedev/server/buildainfile.cpp
+++ b/primedev/server/buildainfile.cpp
@@ -7,8 +7,6 @@
namespace fs = std::filesystem;
-AUTOHOOK_INIT()
-
const int AINET_VERSION_NUMBER = 57;
const int AINET_SCRIPT_VERSION_NUMBER = 21;
const int PLACEHOLDER_CRC = 0;
@@ -359,22 +357,18 @@ void DumpAINInfo(CAI_Network* aiNetwork)
writeStream.close();
}
-// clang-format off
-AUTOHOOK(CAI_NetworkBuilder__Build, server.dll + 0x385E20,
-void, __fastcall, (void* builder, CAI_Network* aiNetwork, void* unknown))
-// clang-format on
+static void(__fastcall* o_pCAI_NetworkBuilder__Build)(void* builder, CAI_Network* aiNetwork, void* unknown) = nullptr;
+static void __fastcall h_CAI_NetworkBuilder__Build(void* builder, CAI_Network* aiNetwork, void* unknown)
{
- CAI_NetworkBuilder__Build(builder, aiNetwork, unknown);
+ o_pCAI_NetworkBuilder__Build(builder, aiNetwork, unknown);
DumpAINInfo(aiNetwork);
}
-// clang-format off
-AUTOHOOK(LoadAINFile, server.dll + 0x3933A0,
-void, __fastcall, (void* aimanager, void* buf, const char* filename))
-// clang-format on
+static void(__fastcall* o_pLoadAINFile)(void* aimanager, void* buf, const char* filename) = nullptr;
+static void __fastcall h_LoadAINFile(void* aimanager, void* buf, const char* filename)
{
- LoadAINFile(aimanager, buf, filename);
+ o_pLoadAINFile(aimanager, buf, filename);
if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
{
@@ -385,7 +379,11 @@ void, __fastcall, (void* aimanager, void* buf, const char* filename))
ON_DLL_LOAD("server.dll", BuildAINFile, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pCAI_NetworkBuilder__Build = module.Offset(0x385E20).RCast<decltype(o_pCAI_NetworkBuilder__Build)>();
+ HookAttach(&(PVOID&)o_pCAI_NetworkBuilder__Build, (PVOID)h_CAI_NetworkBuilder__Build);
+
+ o_pLoadAINFile = module.Offset(0x3933A0).RCast<decltype(o_pLoadAINFile)>();
+ HookAttach(&(PVOID&)o_pLoadAINFile, (PVOID)h_LoadAINFile);
Cvar_ns_ai_dumpAINfileFromLoad = new ConVar(
"ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk");
diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp
index 29540cce..4141d04d 100644
--- a/primedev/squirrel/squirrel.cpp
+++ b/primedev/squirrel/squirrel.cpp
@@ -11,6 +11,8 @@
#include "ns_version.h"
#include "core/vanilla.h"
+#include "vscript/vscript.h"
+
#include <any>
AUTOHOOK_INIT()
@@ -253,8 +255,8 @@ template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(cons
spdlog::info("Executing {} script code {} ", GetContextName(context), pCode);
- std::string strCode(pCode);
- CompileBufferState bufferState = CompileBufferState(strCode);
+ // NOTE: SQBufferState doesn't strdup pCode!
+ SQBufferState bufferState = SQBufferState(pCode);
SQRESULT compileResult = compilebuffer(&bufferState, "console");
spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult));
@@ -309,14 +311,14 @@ template <ScriptContext context> void SquirrelManager<context>::AddFuncOverride(
}
// hooks
-bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm)
+bool IsUIVM(ScriptContext context, HSQUIRRELVM pSqvm)
{
NOTE_UNUSED(context);
return ScriptContext(pSqvm->sharedState->cSquirrelVM->vmContext) == ScriptContext::UI;
}
-template <ScriptContext context> void* (*sq_compiler_create)(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError);
-template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError)
+template <ScriptContext context> void* (*sq_compiler_create)(HSQUIRRELVM sqvm, void* a2, void* a3, SQBool bShouldThrowError);
+template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSQUIRRELVM sqvm, void* a2, void* a3, SQBool bShouldThrowError)
{
// store whether errors generated from this compile should be fatal
if (IsUIVM(context, sqvm))
@@ -327,8 +329,8 @@ template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSquirr
return sq_compiler_create<context>(sqvm, a2, a3, bShouldThrowError);
}
-template <ScriptContext context> SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt);
-template <ScriptContext context> SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...)
+template <ScriptContext context> SQInteger (*SQPrint)(HSQUIRRELVM sqvm, const char* fmt);
+template <ScriptContext context> SQInteger SQPrintHook(HSQUIRRELVM sqvm, const char* fmt, ...)
{
NOTE_UNUSED(sqvm);
@@ -398,9 +400,9 @@ template <ScriptContext context> void __fastcall DestroyVMHook(void* a1, CSquirr
spdlog::info("DestroyVM {} {}", GetContextName(realContext), (void*)sqvm);
}
-template <ScriptContext context> void (*SQCompileError)(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column);
+template <ScriptContext context> void (*SQCompileError)(HSQUIRRELVM sqvm, const char* error, const char* file, int line, int column);
template <ScriptContext context>
-void __fastcall ScriptCompileErrorHook(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column)
+void __fastcall ScriptCompileErrorHook(HSQUIRRELVM sqvm, const char* error, const char* file, int line, int column)
{
bool bIsFatalError = g_pSquirrel<context>->m_bFatalCompilationErrors;
ScriptContext realContext = context; // ui and client use the same function so we use this for prints
@@ -544,7 +546,7 @@ template <ScriptContext context> void ConCommand_script(const CCommand& args)
g_pSquirrel<context>->ExecuteCode(args.ArgS());
}
-template <ScriptContext context> SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm)
+template <ScriptContext context> SQRESULT SQ_StubbedFunc(HSQUIRRELVM sqvm)
{
SQStackInfos si;
g_pSquirrel<context>->sq_stackinfos(sqvm, 0, si);
diff --git a/primedev/squirrel/squirrel.h b/primedev/squirrel/squirrel.h
index 547b1efc..ae6e80c9 100644
--- a/primedev/squirrel/squirrel.h
+++ b/primedev/squirrel/squirrel.h
@@ -126,138 +126,75 @@ public:
#pragma endregion
#pragma region SQVM func wrappers
- inline void defconst(CSquirrelVM* sqvm, const SQChar* pName, int nValue)
- {
- __sq_defconst(sqvm, pName, nValue);
- }
+ inline void defconst(CSquirrelVM* sqvm, const SQChar* pName, int nValue) { __sq_defconst(sqvm, pName, nValue); }
inline SQRESULT
- compilebuffer(CompileBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false)
+ compilebuffer(SQBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false)
{
return __sq_compilebuffer(m_pSQVM->sqvm, bufferState, bufferName, -1, bShouldThrowError);
}
- inline SQRESULT _call(HSquirrelVM* sqvm, const SQInteger args)
- {
- return __sq_call(sqvm, args + 1, false, true);
- }
+ inline SQRESULT _call(HSQUIRRELVM sqvm, const SQInteger args) { return __sq_call(sqvm, args + 1, false, true); }
- inline SQInteger raiseerror(HSquirrelVM* sqvm, const SQChar* sError)
- {
- return __sq_raiseerror(sqvm, sError);
- }
+ inline SQInteger raiseerror(HSQUIRRELVM sqvm, const SQChar* sError) { return __sq_raiseerror(sqvm, sError); }
inline bool compilefile(CSquirrelVM* sqvm, const char* path, const char* name, int a4)
{
return __sq_compilefile(sqvm, path, name, a4);
}
- inline void newarray(HSquirrelVM* sqvm, const SQInteger stackpos = 0)
- {
- __sq_newarray(sqvm, stackpos);
- }
+ inline void newarray(HSQUIRRELVM sqvm, const SQInteger stackpos = 0) { __sq_newarray(sqvm, stackpos); }
- inline SQRESULT arrayappend(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_arrayappend(sqvm, stackpos);
- }
+ inline SQRESULT arrayappend(HSQUIRRELVM sqvm, const SQInteger stackpos) { return __sq_arrayappend(sqvm, stackpos); }
- inline SQRESULT newtable(HSquirrelVM* sqvm)
- {
- return __sq_newtable(sqvm);
- }
+ inline SQRESULT newtable(HSQUIRRELVM sqvm) { return __sq_newtable(sqvm); }
- inline SQRESULT newslot(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic)
- {
- return __sq_newslot(sqvm, idx, bStatic);
- }
+ inline SQRESULT newslot(HSQUIRRELVM sqvm, SQInteger idx, SQBool bStatic) { return __sq_newslot(sqvm, idx, bStatic); }
- inline void pushroottable(HSquirrelVM* sqvm)
- {
- __sq_pushroottable(sqvm);
- }
+ inline void pushroottable(HSQUIRRELVM sqvm) { __sq_pushroottable(sqvm); }
- inline void pushstring(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
- {
- __sq_pushstring(sqvm, sVal, length);
- }
+ inline void pushstring(HSQUIRRELVM sqvm, const SQChar* sVal, int length = -1) { __sq_pushstring(sqvm, sVal, length); }
- inline void pushinteger(HSquirrelVM* sqvm, const SQInteger iVal)
- {
- __sq_pushinteger(sqvm, iVal);
- }
+ inline void pushinteger(HSQUIRRELVM sqvm, const SQInteger iVal) { __sq_pushinteger(sqvm, iVal); }
- inline void pushfloat(HSquirrelVM* sqvm, const SQFloat flVal)
- {
- __sq_pushfloat(sqvm, flVal);
- }
+ inline void pushfloat(HSQUIRRELVM sqvm, const SQFloat flVal) { __sq_pushfloat(sqvm, flVal); }
- inline void pushbool(HSquirrelVM* sqvm, const SQBool bVal)
- {
- __sq_pushbool(sqvm, bVal);
- }
+ inline void pushbool(HSQUIRRELVM sqvm, const SQBool bVal) { __sq_pushbool(sqvm, bVal); }
- inline void pushasset(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
- {
- __sq_pushasset(sqvm, sVal, length);
- }
+ inline void pushasset(HSQUIRRELVM sqvm, const SQChar* sVal, int length = -1) { __sq_pushasset(sqvm, sVal, length); }
- inline void pushvector(HSquirrelVM* sqvm, const Vector3 pVal)
- {
- __sq_pushvector(sqvm, (float*)&pVal);
- }
+ inline void pushvector(HSQUIRRELVM sqvm, const Vector3 pVal) { __sq_pushvector(sqvm, (float*)&pVal); }
- inline void pushobject(HSquirrelVM* sqvm, SQObject* obj)
- {
- __sq_pushobject(sqvm, obj);
- }
+ inline void pushobject(HSQUIRRELVM sqvm, SQObject* obj) { __sq_pushobject(sqvm, obj); }
- inline const SQChar* getstring(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getstring(sqvm, stackpos);
- }
+ inline const SQChar* getstring(HSQUIRRELVM sqvm, const SQInteger stackpos) { return __sq_getstring(sqvm, stackpos); }
- inline SQInteger getinteger(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getinteger(sqvm, stackpos);
- }
+ inline SQInteger getinteger(HSQUIRRELVM sqvm, const SQInteger stackpos) { return __sq_getinteger(sqvm, stackpos); }
- inline SQFloat getfloat(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getfloat(sqvm, stackpos);
- }
+ inline SQFloat getfloat(HSQUIRRELVM sqvm, const SQInteger stackpos) { return __sq_getfloat(sqvm, stackpos); }
- inline SQBool getbool(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_getbool(sqvm, stackpos);
- }
+ inline SQBool getbool(HSQUIRRELVM sqvm, const SQInteger stackpos) { return __sq_getbool(sqvm, stackpos); }
- inline SQRESULT get(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return __sq_get(sqvm, stackpos);
- }
+ inline SQRESULT get(HSQUIRRELVM sqvm, const SQInteger stackpos) { return __sq_get(sqvm, stackpos); }
- inline Vector3 getvector(HSquirrelVM* sqvm, const SQInteger stackpos)
- {
- return *(Vector3*)__sq_getvector(sqvm, stackpos);
- }
+ inline Vector3 getvector(HSQUIRRELVM sqvm, const SQInteger stackpos) { return *(Vector3*)__sq_getvector(sqvm, stackpos); }
- inline int sq_getfunction(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature)
+ inline int sq_getfunction(HSQUIRRELVM sqvm, const char* name, SQObject* returnObj, const char* signature)
{
return __sq_getfunction(sqvm, name, returnObj, signature);
}
- inline SQRESULT getasset(HSquirrelVM* sqvm, const SQInteger stackpos, const char** result)
+ inline SQRESULT getasset(HSQUIRRELVM sqvm, const SQInteger stackpos, const char** result)
{
return __sq_getasset(sqvm, stackpos, result);
}
- inline long long sq_stackinfos(HSquirrelVM* sqvm, int level, SQStackInfos& out)
+ inline long long sq_stackinfos(HSQUIRRELVM sqvm, int level, SQStackInfos& out)
{
return __sq_stackinfos(sqvm, level, &out, sqvm->_callstacksize);
}
- inline Mod* getcallingmod(HSquirrelVM* sqvm, int depth = 0)
+ inline Mod* getcallingmod(HSQUIRRELVM sqvm, int depth = 0)
{
SQStackInfos stackInfo {};
if (1 + depth >= sqvm->_callstacksize)
@@ -274,29 +211,26 @@ public:
}
return nullptr;
}
- template <typename T> inline SQRESULT getuserdata(HSquirrelVM* sqvm, const SQInteger stackpos, T* data, uint64_t* typeId)
+ template <typename T> inline SQRESULT getuserdata(HSQUIRRELVM sqvm, const SQInteger stackpos, T* data, uint64_t* typeId)
{
return __sq_getuserdata(sqvm, stackpos, (void**)data, typeId); // this sometimes crashes idk
}
- template <typename T> inline T* createuserdata(HSquirrelVM* sqvm, SQInteger size)
+ template <typename T> inline T* createuserdata(HSQUIRRELVM sqvm, SQInteger size)
{
void* ret = __sq_createuserdata(sqvm, size);
memset(ret, 0, size);
return (T*)ret;
}
- inline SQRESULT setuserdatatypeid(HSquirrelVM* sqvm, const SQInteger stackpos, uint64_t typeId)
+ inline SQRESULT setuserdatatypeid(HSQUIRRELVM sqvm, const SQInteger stackpos, uint64_t typeId)
{
return __sq_setuserdatatypeid(sqvm, stackpos, typeId);
}
- template <typename T> inline SQBool getthisentity(HSquirrelVM* sqvm, T* ppEntity)
- {
- return __sq_getthisentity(sqvm, (void**)ppEntity);
- }
+ template <typename T> inline SQBool getthisentity(HSQUIRRELVM sqvm, T* ppEntity) { return __sq_getthisentity(sqvm, (void**)ppEntity); }
- template <typename T> inline T* getentity(HSquirrelVM* sqvm, SQInteger iStackPos)
+ template <typename T> inline T* getentity(HSQUIRRELVM sqvm, SQInteger iStackPos)
{
SQObject obj;
__sq_getobject(sqvm, iStackPos, &obj);
@@ -305,15 +239,9 @@ public:
return (T*)__sq_getentityfrominstance(m_pSQVM, &obj, __sq_GetEntityConstant_CBaseEntity());
}
- inline SQRESULT pushnewstructinstance(HSquirrelVM* sqvm, const int fieldCount)
- {
- return __sq_pushnewstructinstance(sqvm, fieldCount);
- }
+ inline SQRESULT pushnewstructinstance(HSQUIRRELVM sqvm, const int fieldCount) { return __sq_pushnewstructinstance(sqvm, fieldCount); }
- inline SQRESULT sealstructslot(HSquirrelVM* sqvm, const int fieldIndex)
- {
- return __sq_sealstructslot(sqvm, fieldIndex);
- }
+ inline SQRESULT sealstructslot(HSQUIRRELVM sqvm, const int fieldIndex) { return __sq_sealstructslot(sqvm, fieldIndex); }
#pragma endregion
};
@@ -405,10 +333,7 @@ public:
#pragma endregion
public:
- SquirrelManager()
- {
- m_pSQVM = nullptr;
- }
+ SquirrelManager() { m_pSQVM = nullptr; }
void VMCreated(CSquirrelVM* newSqvm);
void VMDestroyed();
diff --git a/primedev/squirrel/squirrelautobind.h b/primedev/squirrel/squirrelautobind.h
index 0fc599f3..cb1d2108 100644
--- a/primedev/squirrel/squirrelautobind.h
+++ b/primedev/squirrel/squirrelautobind.h
@@ -15,7 +15,7 @@ extern SquirrelAutoBindContainer* g_pSqAutoBindContainer;
class __squirrelautobind;
#define ADD_SQFUNC(returnType, funcName, argTypes, helpText, runOnContext) \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm); \
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSQUIRRELVM sqvm); \
namespace \
{ \
__squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \
@@ -35,10 +35,10 @@ class __squirrelautobind;
returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \
}); \
} \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm)
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSQUIRRELVM sqvm)
#define REPLACE_SQFUNC(funcName, runOnContext) \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm); \
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSQUIRRELVM sqvm); \
namespace \
{ \
__squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \
@@ -57,7 +57,7 @@ class __squirrelautobind;
__STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \
}); \
} \
- template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm)
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSQUIRRELVM sqvm)
class __squirrelautobind
{
diff --git a/primedev/squirrel/squirrelclasstypes.h b/primedev/squirrel/squirrelclasstypes.h
index 3a39c957..f53f6d18 100644
--- a/primedev/squirrel/squirrelclasstypes.h
+++ b/primedev/squirrel/squirrelclasstypes.h
@@ -1,83 +1,14 @@
#pragma once
-#include "squirreldatatypes.h"
-#include <queue>
-
-enum SQRESULT : SQInteger
-{
- SQRESULT_ERROR = -1,
- SQRESULT_NULL = 0,
- SQRESULT_NOTNULL = 1,
-};
-
-typedef SQRESULT (*SQFunction)(HSquirrelVM* sqvm);
+#include "vscript/vscript.h"
-enum class eSQReturnType
-{
- Float = 0x1,
- Vector = 0x3,
- Integer = 0x5,
- Boolean = 0x6,
- Entity = 0xD,
- String = 0x21,
- Default = 0x20,
- Arrays = 0x25,
- Asset = 0x28,
- Table = 0x26,
-};
+#include <queue>
const std::map<SQRESULT, const char*> PrintSQRESULT = {
{SQRESULT::SQRESULT_ERROR, "SQRESULT_ERROR"},
{SQRESULT::SQRESULT_NULL, "SQRESULT_NULL"},
{SQRESULT::SQRESULT_NOTNULL, "SQRESULT_NOTNULL"}};
-struct CompileBufferState
-{
- const SQChar* buffer;
- const SQChar* bufferPlusLength;
- const SQChar* bufferAgain;
-
- CompileBufferState(const std::string& code)
- {
- buffer = code.c_str();
- bufferPlusLength = code.c_str() + code.size();
- bufferAgain = code.c_str();
- }
-};
-
-struct SQFuncRegistration
-{
- const char* squirrelFuncName;
- const char* cppFuncName;
- const char* helpText;
- const char* returnTypeString;
- const char* argTypes;
- uint32_t unknown1;
- uint32_t devLevel;
- const char* shortNameMaybe;
- uint32_t unknown2;
- eSQReturnType returnType;
- uint32_t* externalBufferPointer;
- uint64_t externalBufferSize;
- uint64_t unknown3;
- uint64_t unknown4;
- SQFunction funcPtr;
-
- SQFuncRegistration()
- {
- memset(this, 0, sizeof(SQFuncRegistration));
- this->returnType = eSQReturnType::Default;
- }
-};
-
-enum class ScriptContext : int
-{
- INVALID = -1,
- SERVER,
- CLIENT,
- UI,
-};
-
typedef std::vector<std::function<void()>> FunctionVector;
typedef std::function<void()> VoidFunction;
@@ -174,7 +105,8 @@ class SquirrelAsset
{
public:
std::string path;
- SquirrelAsset(std::string path) : path(path) {};
+ SquirrelAsset(std::string path)
+ : path(path) {};
};
#pragma region TypeDefs
@@ -184,56 +116,56 @@ typedef int64_t (*RegisterSquirrelFuncType)(CSquirrelVM* sqvm, SQFuncRegistratio
typedef void (*sq_defconstType)(CSquirrelVM* sqvm, const SQChar* name, int value);
typedef SQRESULT (*sq_compilebufferType)(
- HSquirrelVM* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, SQBool bShouldThrowError);
-typedef SQRESULT (*sq_callType)(HSquirrelVM* sqvm, SQInteger iArgs, SQBool bShouldReturn, SQBool bThrowError);
-typedef SQInteger (*sq_raiseerrorType)(HSquirrelVM* sqvm, const SQChar* pError);
+ HSQUIRRELVM sqvm, SQBufferState* compileBuffer, const char* file, int a1, SQBool bShouldThrowError);
+typedef SQRESULT (*sq_callType)(HSQUIRRELVM sqvm, SQInteger iArgs, SQBool bShouldReturn, SQBool bThrowError);
+typedef SQInteger (*sq_raiseerrorType)(HSQUIRRELVM sqvm, const SQChar* pError);
typedef bool (*sq_compilefileType)(CSquirrelVM* sqvm, const char* path, const char* name, int a4);
// sq stack array funcs
-typedef void (*sq_newarrayType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQRESULT (*sq_arrayappendType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+typedef void (*sq_newarrayType)(HSQUIRRELVM sqvm, SQInteger iStackpos);
+typedef SQRESULT (*sq_arrayappendType)(HSQUIRRELVM sqvm, SQInteger iStackpos);
// sq table funcs
-typedef SQRESULT (*sq_newtableType)(HSquirrelVM* sqvm);
-typedef SQRESULT (*sq_newslotType)(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic);
+typedef SQRESULT (*sq_newtableType)(HSQUIRRELVM sqvm);
+typedef SQRESULT (*sq_newslotType)(HSQUIRRELVM sqvm, SQInteger idx, SQBool bStatic);
// sq stack push funcs
-typedef void (*sq_pushroottableType)(HSquirrelVM* sqvm);
-typedef void (*sq_pushstringType)(HSquirrelVM* sqvm, const SQChar* pStr, SQInteger iLength);
-typedef void (*sq_pushintegerType)(HSquirrelVM* sqvm, SQInteger i);
-typedef void (*sq_pushfloatType)(HSquirrelVM* sqvm, SQFloat f);
-typedef void (*sq_pushboolType)(HSquirrelVM* sqvm, SQBool b);
-typedef void (*sq_pushassetType)(HSquirrelVM* sqvm, const SQChar* str, SQInteger iLength);
-typedef void (*sq_pushvectorType)(HSquirrelVM* sqvm, const SQFloat* pVec);
-typedef void (*sq_pushobjectType)(HSquirrelVM* sqvm, SQObject* pVec);
+typedef void (*sq_pushroottableType)(HSQUIRRELVM sqvm);
+typedef void (*sq_pushstringType)(HSQUIRRELVM sqvm, const SQChar* pStr, SQInteger iLength);
+typedef void (*sq_pushintegerType)(HSQUIRRELVM sqvm, SQInteger i);
+typedef void (*sq_pushfloatType)(HSQUIRRELVM sqvm, SQFloat f);
+typedef void (*sq_pushboolType)(HSQUIRRELVM sqvm, SQBool b);
+typedef void (*sq_pushassetType)(HSQUIRRELVM sqvm, const SQChar* str, SQInteger iLength);
+typedef void (*sq_pushvectorType)(HSQUIRRELVM sqvm, const SQFloat* pVec);
+typedef void (*sq_pushobjectType)(HSQUIRRELVM sqvm, SQObject* pVec);
// sq stack get funcs
-typedef const SQChar* (*sq_getstringType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQInteger (*sq_getintegerType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQFloat (*sq_getfloatType)(HSquirrelVM*, SQInteger iStackpos);
-typedef SQBool (*sq_getboolType)(HSquirrelVM*, SQInteger iStackpos);
-typedef SQRESULT (*sq_getType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQRESULT (*sq_getassetType)(HSquirrelVM* sqvm, SQInteger iStackpos, const char** pResult);
-typedef SQRESULT (*sq_getuserdataType)(HSquirrelVM* sqvm, SQInteger iStackpos, void** pData, uint64_t* pTypeId);
-typedef SQFloat* (*sq_getvectorType)(HSquirrelVM* sqvm, SQInteger iStackpos);
-typedef SQBool (*sq_getthisentityType)(HSquirrelVM*, void** ppEntity);
-typedef void (*sq_getobjectType)(HSquirrelVM*, SQInteger iStackPos, SQObject* pOutObj);
-
-typedef long long (*sq_stackinfosType)(HSquirrelVM* sqvm, int iLevel, SQStackInfos* pOutObj, int iCallStackSize);
+typedef const SQChar* (*sq_getstringType)(HSQUIRRELVM sqvm, SQInteger iStackpos);
+typedef SQInteger (*sq_getintegerType)(HSQUIRRELVM sqvm, SQInteger iStackpos);
+typedef SQFloat (*sq_getfloatType)(HSQUIRRELVM, SQInteger iStackpos);
+typedef SQBool (*sq_getboolType)(HSQUIRRELVM, SQInteger iStackpos);
+typedef SQRESULT (*sq_getType)(HSQUIRRELVM sqvm, SQInteger iStackpos);
+typedef SQRESULT (*sq_getassetType)(HSQUIRRELVM sqvm, SQInteger iStackpos, const char** pResult);
+typedef SQRESULT (*sq_getuserdataType)(HSQUIRRELVM sqvm, SQInteger iStackpos, void** pData, uint64_t* pTypeId);
+typedef SQFloat* (*sq_getvectorType)(HSQUIRRELVM sqvm, SQInteger iStackpos);
+typedef SQBool (*sq_getthisentityType)(HSQUIRRELVM, void** ppEntity);
+typedef void (*sq_getobjectType)(HSQUIRRELVM, SQInteger iStackPos, SQObject* pOutObj);
+
+typedef long long (*sq_stackinfosType)(HSQUIRRELVM sqvm, int iLevel, SQStackInfos* pOutObj, int iCallStackSize);
// sq stack userpointer funcs
-typedef void* (*sq_createuserdataType)(HSquirrelVM* sqvm, SQInteger iSize);
-typedef SQRESULT (*sq_setuserdatatypeidType)(HSquirrelVM* sqvm, SQInteger iStackpos, uint64_t iTypeId);
+typedef void* (*sq_createuserdataType)(HSQUIRRELVM sqvm, SQInteger iSize);
+typedef SQRESULT (*sq_setuserdatatypeidType)(HSQUIRRELVM sqvm, SQInteger iStackpos, uint64_t iTypeId);
// sq misc entity funcs
typedef void* (*sq_getentityfrominstanceType)(CSquirrelVM* sqvm, SQObject* pInstance, char** ppEntityConstant);
typedef SQObject* (*sq_createscriptinstanceType)(void* ent);
typedef char** (*sq_GetEntityConstantType)();
-typedef int (*sq_getfunctionType)(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature);
+typedef int (*sq_getfunctionType)(HSQUIRRELVM sqvm, const char* name, SQObject* returnObj, const char* signature);
// structs
-typedef SQRESULT (*sq_pushnewstructinstanceType)(HSquirrelVM* sqvm, int fieldCount);
-typedef SQRESULT (*sq_sealstructslotType)(HSquirrelVM* sqvm, int slotIndex);
+typedef SQRESULT (*sq_pushnewstructinstanceType)(HSQUIRRELVM sqvm, int fieldCount);
+typedef SQRESULT (*sq_sealstructslotType)(HSQUIRRELVM sqvm, int slotIndex);
#pragma endregion
diff --git a/primedev/squirrel/squirreldatatypes.h b/primedev/squirrel/squirreldatatypes.h
deleted file mode 100644
index 84ab15ec..00000000
--- a/primedev/squirrel/squirreldatatypes.h
+++ /dev/null
@@ -1,501 +0,0 @@
-#pragma once
-/*
- This file has been generated by IDA.
- It contains local type definitions from
- the type library 'server.dll'
-*/
-
-struct HSquirrelVM;
-struct CallInfo;
-struct SQTable;
-struct SQString;
-struct SQFunctionProto;
-struct SQClosure;
-struct SQSharedState;
-struct StringTable;
-struct SQStructInstance;
-struct SQStructDef;
-struct SQNativeClosure;
-struct SQArray;
-struct tableNode;
-struct SQUserData;
-struct CSquirrelVM;
-
-typedef void (*releasehookType)(void* val, int size);
-
-// stolen from ttf2sdk: sqvm types
-typedef float SQFloat;
-typedef long SQInteger;
-typedef unsigned long SQUnsignedInteger;
-typedef char SQChar;
-typedef SQUnsignedInteger SQBool;
-
-/* 127 */
-enum SQObjectType : int
-{
- _RT_NULL = 0x1,
- _RT_INTEGER = 0x2,
- _RT_FLOAT = 0x4,
- _RT_BOOL = 0x8,
- _RT_STRING = 0x10,
- _RT_TABLE = 0x20,
- _RT_ARRAY = 0x40,
- _RT_USERDATA = 0x80,
- _RT_CLOSURE = 0x100,
- _RT_NATIVECLOSURE = 0x200,
- _RT_GENERATOR = 0x400,
- OT_USERPOINTER = 0x800,
- _RT_USERPOINTER = 0x800,
- _RT_THREAD = 0x1000,
- _RT_FUNCPROTO = 0x2000,
- _RT_CLASS = 0x4000,
- _RT_INSTANCE = 0x8000,
- _RT_WEAKREF = 0x10000,
- OT_VECTOR = 0x40000,
- SQOBJECT_CANBEFALSE = 0x1000000,
- OT_NULL = 0x1000001,
- OT_BOOL = 0x1000008,
- SQOBJECT_DELEGABLE = 0x2000000,
- SQOBJECT_NUMERIC = 0x4000000,
- OT_INTEGER = 0x5000002,
- OT_FLOAT = 0x5000004,
- SQOBJECT_REF_COUNTED = 0x8000000,
- OT_STRING = 0x8000010,
- OT_ARRAY = 0x8000040,
- OT_CLOSURE = 0x8000100,
- OT_NATIVECLOSURE = 0x8000200,
- OT_ASSET = 0x8000400,
- OT_THREAD = 0x8001000,
- OT_FUNCPROTO = 0x8002000,
- OT_CLAAS = 0x8004000,
- OT_STRUCT = 0x8200000,
- OT_WEAKREF = 0x8010000,
- OT_TABLE = 0xA000020,
- OT_USERDATA = 0xA000080,
- OT_INSTANCE = 0xA008000,
- OT_ENTITY = 0xA400000,
-};
-
-/* 156 */
-union SQObjectValue
-{
- SQString* asString;
- SQTable* asTable;
- SQClosure* asClosure;
- SQFunctionProto* asFuncProto;
- SQStructDef* asStructDef;
- long long as64Integer;
- SQNativeClosure* asNativeClosure;
- SQArray* asArray;
- HSquirrelVM* asThread;
- float asFloat;
- int asInteger;
- SQUserData* asUserdata;
- SQStructInstance* asStructInstance;
-};
-
-/* 160 */
-struct SQVector
-{
- SQObjectType _Type;
- float x;
- float y;
- float z;
-};
-
-/* 128 */
-struct SQObject
-{
- SQObjectType _Type;
- int structNumber;
- SQObjectValue _VAL;
-};
-
-/* 138 */
-struct alignas(8) SQString
-{
- void* vftable;
- int uiRef;
- int padding;
- SQString* _next_maybe;
- SQSharedState* sharedState;
- int length;
- unsigned char gap_24[4];
- char _hash[8];
- char _val[1];
-};
-
-/* 137 */
-struct alignas(8) SQTable
-{
- void* vftable;
- unsigned char gap_08[4];
- int uiRef;
- unsigned char gap_10[8];
- void* pointer_18;
- void* pointer_20;
- void* _sharedState;
- long long field_30;
- tableNode* _nodes;
- int _numOfNodes;
- int size;
- int field_48;
- int _usedNodes;
- unsigned char _gap_50[20];
- int field_64;
- unsigned char _gap_68[80];
-};
-
-/* 140 */
-struct alignas(8) SQClosure
-{
- void* vftable;
- unsigned char gap_08[4];
- int uiRef;
- void* pointer_10;
- void* pointer_18;
- void* pointer_20;
- void* sharedState;
- SQObject obj_30;
- SQObject _function;
- SQObject* _outervalues;
- unsigned char gap_58[8];
- unsigned char gap_60[96];
- SQObject* objectPointer_C0;
- unsigned char gap_C8[16];
-};
-
-/* 139 */
-struct alignas(8) SQFunctionProto
-{
- void* vftable;
- unsigned char gap_08[4];
- int uiRef;
- unsigned char gap_10[8];
- void* pointer_18;
- void* pointer_20;
- void* sharedState;
- void* pointer_30;
- SQObjectType _fileNameType;
- SQString* _fileName;
- SQObjectType _funcNameType;
- SQString* _funcName;
- SQObject obj_58;
- unsigned char gap_68[12];
- int _stacksize;
- unsigned char gap_78[48];
- int nParameters;
- unsigned char gap_AC[60];
- int nDefaultParams;
- unsigned char gap_EC[200];
-};
-
-/* 152 */
-struct SQStructDef
-{
- void* vtable;
- int uiRef;
- unsigned char padding_C[4];
- unsigned char unknown[24];
- SQSharedState* sharedState;
- SQObjectType _nameType;
- SQString* _name;
- unsigned char gap_38[16];
- SQObjectType _variableNamesType;
- SQTable* _variableNames;
- unsigned char gap_[32];
-};
-
-/* 157 */
-struct alignas(8) SQNativeClosure
-{
- void* vftable;
- int uiRef;
- unsigned char gap_C[4];
- long long value_10;
- long long value_18;
- long long value_20;
- SQSharedState* sharedState;
- char unknown_30;
- unsigned char padding_34[7];
- long long value_38;
- long long value_40;
- long long value_48;
- long long value_50;
- long long value_58;
- SQObjectType _nameType;
- SQString* _name;
- long long value_70;
- long long value_78;
- unsigned char justInCaseGap_80[300];
-};
-
-/* 162 */
-struct SQArray
-{
- void* vftable;
- int uiRef;
- unsigned char gap_24[36];
- SQObject* _values;
- int _usedSlots;
- int _allocated;
-};
-
-/* 129 */
-struct alignas(8) HSquirrelVM
-{
- void* vftable;
- int uiRef;
- unsigned char gap_8[12];
- void* _toString;
- void* _roottable_pointer;
- void* pointer_28;
- CallInfo* ci;
- CallInfo* _callstack;
- int _callstacksize;
- int _stackbase;
- SQObject* _stackOfCurrentFunction;
- SQSharedState* sharedState;
- void* pointer_58;
- void* pointer_60;
- int _top;
- SQObject* _stack;
- unsigned char gap_78[8];
- SQObject* _vargvstack;
- unsigned char gap_88[8];
- SQObject temp_reg;
- unsigned char gapA0[8];
- void* pointer_A8;
- unsigned char gap_B0[8];
- SQObject _roottable_object;
- SQObject _lasterror;
- SQObject _errorHandler;
- long long field_E8;
- int traps;
- unsigned char gap_F4[12];
- int _nnativecalls;
- int _suspended;
- int _suspended_root;
- int _unk;
- int _suspended_target;
- int trapAmount;
- int _suspend_varargs;
- int unknown_field_11C;
- SQObject object_120;
-};
-
-/* 150 */
-struct SQStructInstance
-{
- void* vftable;
- __int32 uiRef;
- BYTE gap_C[4];
- __int64 unknown_10;
- void* pointer_18;
- __int64 unknown_20;
- SQSharedState* _sharedState;
- unsigned int size;
- BYTE gap_34[4];
- SQObject data[1]; // This struct is dynamically sized, so this size is unknown
-};
-
-/* 148 */
-struct SQSharedState
-{
- unsigned char gap_0[72];
- void* unknown;
- unsigned char gap_50[16344];
- SQObjectType _unknownTableType00;
- long long _unknownTableValue00;
- unsigned char gap_4038[16];
- StringTable* _stringTable;
- unsigned char gap_4050[32];
- SQObjectType _unknownTableType0;
- long long _unknownTableValue0;
- SQObjectType _unknownObjectType1;
- long long _unknownObjectValue1;
- unsigned char gap_4090[8];
- SQObjectType _unknownArrayType2;
- long long _unknownArrayValue2;
- SQObjectType _gobalsArrayType;
- SQStructInstance* _globalsArray;
- unsigned char gap_40B8[16];
- SQObjectType _nativeClosuresType;
- SQTable* _nativeClosures;
- SQObjectType _typedConstantsType;
- SQTable* _typedConstants;
- SQObjectType _untypedConstantsType;
- SQTable* _untypedConstants;
- SQObjectType _globalsMaybeType;
- SQTable* _globals;
- SQObjectType _functionsType;
- SQTable* _functions;
- SQObjectType _structsType;
- SQTable* _structs;
- SQObjectType _typeDefsType;
- SQTable* _typeDefs;
- SQObjectType unknownTableType;
- SQTable* unknownTable;
- SQObjectType _squirrelFilesType;
- SQTable* _squirrelFiles;
- unsigned char gap_4158[80];
- SQObjectType _nativeClosures2Type;
- SQTable* _nativeClosures2;
- SQObjectType _entityTypesMaybeType;
- SQTable* _entityTypesMaybe;
- SQObjectType unknownTable2Type;
- SQTable* unknownTable2;
- unsigned char gap_41D8[72];
- SQObjectType _compilerKeywordsType;
- SQTable* _compilerKeywords;
- HSquirrelVM* _currentThreadMaybe;
- unsigned char gap_4238[8];
- SQObjectType unknownTable3Type;
- SQTable* unknownTable3;
- unsigned char gap_4250[16];
- SQObjectType unknownThreadType;
- SQTable* unknownThread;
- SQObjectType _tableNativeFunctionsType;
- SQTable* _tableNativeFunctions;
- SQObjectType _unknownTableType4;
- long long _unknownObjectValue4;
- SQObjectType _unknownObjectType5;
- long long _unknownObjectValue5;
- SQObjectType _unknownObjectType6;
- long long _unknownObjectValue6;
- SQObjectType _unknownObjectType7;
- long long _unknownObjectValue7;
- SQObjectType _unknownObjectType8;
- long long _unknownObjectValue8;
- SQObjectType _unknownObjectType9;
- long long _unknownObjectValue9;
- SQObjectType _unknownObjectType10;
- long long _unknownObjectValue10;
- SQObjectType _unknownObjectType11;
- long long _unknownObjectValue11;
- SQObjectType _unknownObjectType12;
- long long _unknownObjectValue12;
- SQObjectType _unknownObjectType13;
- long long _unknownObjectValue13;
- SQObjectType _unknownObjectType14;
- long long _unknownObjectValue14;
- SQObjectType _unknownObjectType15;
- long long _unknownObjectValue15;
- unsigned char gap_4340[16];
- void* printFunction;
- unsigned char gap_4358[16];
- void* logEntityFunction;
- unsigned char gap_4370[40];
- SQObjectType _waitStringType;
- SQString* _waitStringValue;
- SQObjectType _SpinOffAndWaitForStringType;
- SQString* _SpinOffAndWaitForStringValue;
- SQObjectType _SpinOffAndWaitForSoloStringType;
- SQString* _SpinOffAndWaitForSoloStringValue;
- SQObjectType _SpinOffStringType;
- SQString* _SpinOffStringValue;
- SQObjectType _SpinOffDelayedStringType;
- SQString* _SpinOffDelayedStringValue;
- CSquirrelVM* cSquirrelVM;
- bool enableDebugInfo; // functionality stripped
- unsigned char gap_43F1[23];
-};
-
-/* 165 */
-struct tableNode
-{
- SQObject val;
- SQObject key;
- tableNode* next;
-};
-
-/* 136 */
-struct alignas(8) CallInfo
-{
- long long ip;
- SQObject* _literals;
- SQObject obj10;
- SQObject closure;
- int _etraps[4];
- int _root;
- short _vargs_size;
- short _vargs_base;
- unsigned char gap[16];
-};
-
-/* 149 */
-struct StringTable
-{
- unsigned char gap_0[12];
- int _numofslots;
- unsigned char gap_10[200];
-};
-
-/* 141 */
-struct alignas(8) SQStackInfos
-{
- char* _name;
- char* _sourceName;
- int _line;
-};
-
-/* 151 */
-struct alignas(4) SQInstruction
-{
- int op;
- int arg1;
- int output;
- short arg2;
- short arg3;
-};
-
-/* 154 */
-struct SQLexer
-{
- unsigned char gap_0[112];
-};
-
-/* 153 */
-struct SQCompiler
-{
- unsigned char gap_0[4];
- int _token;
- unsigned char gap_8[8];
- SQObject object_10;
- SQLexer lexer;
- unsigned char gap_90[752];
- HSquirrelVM* sqvm;
- unsigned char gap_288[8];
-};
-
-/* 155 */
-struct CSquirrelVM
-{
- BYTE gap_0[8];
- HSquirrelVM* sqvm;
- BYTE gap_10[8];
- SQObject unknownObject_18;
- __int64 unknown_28;
- BYTE gap_30[12];
- __int32 vmContext;
- BYTE gap_40[648];
- char* (*formatString)(__int64 a1, const char* format, ...);
- BYTE gap_2D0[24];
-};
-
-struct SQUserData
-{
- void* vftable;
- int uiRef;
- char gap_12[4];
- long long unknown_10;
- long long unknown_18;
- long long unknown_20;
- long long sharedState;
- long long unknown_30;
- int size;
- char padding1[4];
- releasehookType releaseHook;
- long long typeId;
- char data[1];
-};
diff --git a/primedev/thirdparty/silver-bun/module.cpp b/primedev/thirdparty/silver-bun/module.cpp
index 84f4da9e..26e9b6b6 100644
--- a/primedev/thirdparty/silver-bun/module.cpp
+++ b/primedev/thirdparty/silver-bun/module.cpp
@@ -66,6 +66,25 @@ void CModule::Init()
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.
}
+
+ // 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.
+
+ if (m_pNTHeaders->FileHeader.SizeOfOptionalHeader == 0)
+ return;
+
+ IMAGE_DATA_DIRECTORY& imageDirectory = m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+ if (imageDirectory.Size == 0 || imageDirectory.VirtualAddress == 0)
+ return;
+
+ IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptors = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(m_pModuleBase + imageDirectory.VirtualAddress);
+ 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));
+
+ m_vImportedModules.push_back(szImportedModuleName);
+ }
}
//-----------------------------------------------------------------------------
diff --git a/primedev/thirdparty/silver-bun/module.h b/primedev/thirdparty/silver-bun/module.h
index 5683ee14..cc513086 100644
--- a/primedev/thirdparty/silver-bun/module.h
+++ b/primedev/thirdparty/silver-bun/module.h
@@ -52,6 +52,7 @@ public:
ModuleSections_t GetSectionByName(const char* szSectionName) const;
inline const std::vector<CModule::ModuleSections_t>& GetSections() const { return m_ModuleSections; }
+ inline const std::vector<std::string>& GetImportedModules() const { return m_vImportedModules; }
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; }
@@ -73,4 +74,5 @@ private:
uintptr_t m_pModuleBase;
DWORD m_nModuleSize;
std::vector<ModuleSections_t> m_ModuleSections;
+ std::vector<std::string> m_vImportedModules;
};
diff --git a/primedev/util/utils.h b/primedev/util/utils.h
index c8cbc7e8..c613d16d 100644
--- a/primedev/util/utils.h
+++ b/primedev/util/utils.h
@@ -8,16 +8,16 @@ public:
auto operator=(ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&) = delete;
- ScopeGuard(T callback) : m_callback(callback) {}
+ ScopeGuard(T callback)
+ : m_callback(callback)
+ {
+ }
~ScopeGuard()
{
if (!m_dismissed)
m_callback();
}
- void Dismiss()
- {
- m_dismissed = true;
- }
+ void Dismiss() { m_dismissed = true; }
private:
bool m_dismissed = false;
diff --git a/primedev/vscript/languages/squirrel_re/include/squirrel.h b/primedev/vscript/languages/squirrel_re/include/squirrel.h
new file mode 100644
index 00000000..b068ff06
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/include/squirrel.h
@@ -0,0 +1,95 @@
+#pragma once
+
+class Mod;
+struct SQBufferState;
+class CBaseEntity;
+
+struct SQVM;
+struct SQObject;
+struct SQTable;
+struct SQArray;
+struct SQString;
+struct SQClosure;
+struct SQFunctionProto;
+struct SQStructDef;
+struct SQNativeClosure;
+struct SQStructInstance;
+struct SQUserData;
+struct SQSharedState;
+
+typedef float SQFloat;
+typedef long SQInteger;
+typedef unsigned long SQUnsignedInteger;
+typedef char SQChar;
+typedef SQUnsignedInteger SQBool;
+
+typedef SQVM* HSQUIRRELVM;
+
+enum SQRESULT : SQInteger
+{
+ SQRESULT_ERROR = -1,
+ SQRESULT_NULL = 0,
+ SQRESULT_NOTNULL = 1,
+};
+
+enum class eSQReturnType
+{
+ Float = 0x1,
+ Vector = 0x3,
+ Integer = 0x5,
+ Boolean = 0x6,
+ Entity = 0xD,
+ String = 0x21,
+ Default = 0x20,
+ Arrays = 0x25,
+ Asset = 0x28,
+ Table = 0x26,
+};
+
+struct SQBufferState
+{
+ const SQChar* buffer;
+ const SQChar* bufferPlusLength;
+ const SQChar* bufferAgain;
+
+ SQBufferState(const SQChar* pszCode)
+ {
+ buffer = pszCode;
+ bufferPlusLength = pszCode + strlen(pszCode);
+ bufferAgain = pszCode;
+ }
+};
+
+typedef SQRESULT (*SQFunction)(HSQUIRRELVM sqvm);
+
+struct SQFuncRegistration
+{
+ const char* squirrelFuncName;
+ const char* cppFuncName;
+ const char* helpText;
+ const char* returnTypeString;
+ const char* argTypes;
+ uint32_t unknown1;
+ uint32_t devLevel;
+ const char* shortNameMaybe;
+ uint32_t unknown2;
+ eSQReturnType returnType;
+ uint32_t* externalBufferPointer;
+ uint64_t externalBufferSize;
+ uint64_t unknown3;
+ uint64_t unknown4;
+ SQFunction funcPtr;
+
+ SQFuncRegistration()
+ {
+ memset(this, 0, sizeof(SQFuncRegistration));
+ this->returnType = eSQReturnType::Default;
+ }
+};
+
+struct alignas(8) SQStackInfos
+{
+ char* _name;
+ char* _sourceName;
+ int _line;
+};
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqarray.h b/primedev/vscript/languages/squirrel_re/squirrel/sqarray.h
new file mode 100644
index 00000000..3214dc81
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqarray.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+struct SQArray : public SQCollectable
+{
+ SQObject* _values;
+ int _usedSlots;
+ int _allocated;
+};
+static_assert(sizeof(SQArray) == 0x40);
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqclosure.h b/primedev/vscript/languages/squirrel_re/squirrel/sqclosure.h
new file mode 100644
index 00000000..85c3adef
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqclosure.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+struct alignas(8) SQClosure : public SQCollectable
+{
+ SQObject obj_30;
+ SQObject _function;
+ SQObject* _outervalues;
+ unsigned char gap_58[8];
+};
+static_assert(sizeof(SQClosure) == 96);
+
+struct alignas(8) SQNativeClosure : public SQCollectable
+{
+ char unknown_30;
+ unsigned char padding_34[7];
+ long long value_38;
+ long long value_40;
+ long long value_48;
+ long long value_50;
+ long long value_58;
+ SQObjectType _nameType;
+ SQString* _name;
+ long long value_70;
+ long long value_78;
+};
+static_assert(sizeof(SQNativeClosure) == 128);
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqcompiler.h b/primedev/vscript/languages/squirrel_re/squirrel/sqcompiler.h
new file mode 100644
index 00000000..5a54751c
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqcompiler.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqlexer.h"
+
+struct SQCompiler
+{
+ BYTE gap0[4];
+ int _token;
+ BYTE gap_8[8];
+ SQObject object_10;
+ SQLexer lexer;
+ int64_t qword90;
+ int64_t qword98;
+ BYTE gapA0[280];
+ bool bFatalError;
+ BYTE gap1B9[143];
+ int64_t qword248;
+ int64_t qword250;
+ int64_t qword258;
+ int64_t qword260;
+ BYTE gap268[280];
+ HSQUIRRELVM pSQVM;
+ unsigned char gap_288[8];
+};
+static_assert(sizeof(SQCompiler) == 0x390);
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqfunctionproto.h b/primedev/vscript/languages/squirrel_re/squirrel/sqfunctionproto.h
new file mode 100644
index 00000000..77bec7eb
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqfunctionproto.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+// NOTE [Fifty]: Variable sized struct
+struct alignas(8) SQFunctionProto : public SQCollectable
+{
+ void* pointer_30;
+ SQObjectType _fileNameType;
+ SQString* _fileName;
+ SQObjectType _funcNameType;
+ SQString* _funcName;
+ SQObject obj_58;
+ unsigned char gap_68[12];
+ int _stacksize;
+ unsigned char gap_78[48];
+ int nParameters;
+ unsigned char gap_AC[60];
+ int nDefaultParams;
+ unsigned char gap_EC[200];
+};
+// TODO [Fifty]: Find out the size of the base struct
+static_assert(offsetof(SQFunctionProto, _fileName) == 0x40); // Sanity this check for now
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqlexer.h b/primedev/vscript/languages/squirrel_re/squirrel/sqlexer.h
new file mode 100644
index 00000000..dc19bea8
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqlexer.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+
+// TODO [Fifty]: Verify size
+struct SQLexer
+{
+ unsigned char gap_0[112];
+};
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqobject.h b/primedev/vscript/languages/squirrel_re/squirrel/sqobject.h
new file mode 100644
index 00000000..ea5c0da9
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqobject.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+
+struct SQTable;
+
+struct SQRefCounted
+{
+ void* vftable;
+ SQInteger uiRef;
+ void* weakRef; // Probably
+};
+
+struct SQCollectable : public SQRefCounted
+{
+ SQCollectable* _next;
+ SQCollectable* _prev;
+ SQSharedState* _sharedstate;
+};
+
+struct SQDelegable : public SQCollectable
+{
+ SQTable* _delegate;
+};
+
+enum SQObjectType : int
+{
+ _RT_NULL = 0x1,
+ _RT_INTEGER = 0x2,
+ _RT_FLOAT = 0x4,
+ _RT_BOOL = 0x8,
+ _RT_STRING = 0x10,
+ _RT_TABLE = 0x20,
+ _RT_ARRAY = 0x40,
+ _RT_USERDATA = 0x80,
+ _RT_CLOSURE = 0x100,
+ _RT_NATIVECLOSURE = 0x200,
+ _RT_GENERATOR = 0x400,
+ OT_USERPOINTER = 0x800,
+ _RT_USERPOINTER = 0x800,
+ _RT_THREAD = 0x1000,
+ _RT_FUNCPROTO = 0x2000,
+ _RT_CLASS = 0x4000,
+ _RT_INSTANCE = 0x8000,
+ _RT_WEAKREF = 0x10000,
+ OT_VECTOR = 0x40000,
+ SQOBJECT_CANBEFALSE = 0x1000000,
+ OT_NULL = 0x1000001,
+ OT_BOOL = 0x1000008,
+ SQOBJECT_DELEGABLE = 0x2000000,
+ SQOBJECT_NUMERIC = 0x4000000,
+ OT_INTEGER = 0x5000002,
+ OT_FLOAT = 0x5000004,
+ SQOBJECT_REF_COUNTED = 0x8000000,
+ OT_STRING = 0x8000010,
+ OT_ARRAY = 0x8000040,
+ OT_CLOSURE = 0x8000100,
+ OT_NATIVECLOSURE = 0x8000200,
+ OT_ASSET = 0x8000400,
+ OT_THREAD = 0x8001000,
+ OT_FUNCPROTO = 0x8002000,
+ OT_CLAAS = 0x8004000,
+ OT_STRUCT = 0x8200000,
+ OT_WEAKREF = 0x8010000,
+ OT_TABLE = 0xA000020,
+ OT_USERDATA = 0xA000080,
+ OT_INSTANCE = 0xA008000,
+ OT_ENTITY = 0xA400000,
+};
+
+union SQObjectValue
+{
+ SQString* asString;
+ SQTable* asTable;
+ SQClosure* asClosure;
+ SQFunctionProto* asFuncProto;
+ SQStructDef* asStructDef;
+ long long as64Integer;
+ SQNativeClosure* asNativeClosure;
+ SQArray* asArray;
+ HSQUIRRELVM asThread;
+ float asFloat;
+ int asInteger;
+ SQUserData* asUserdata;
+ SQStructInstance* asStructInstance;
+};
+
+struct SQObject
+{
+ SQObjectType _Type;
+ int structNumber;
+ SQObjectValue _VAL;
+};
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqopcodes.h b/primedev/vscript/languages/squirrel_re/squirrel/sqopcodes.h
new file mode 100644
index 00000000..be0756c1
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqopcodes.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+
+// TODO [Fifty]: Verify size
+struct alignas(4) SQInstruction
+{
+ int op;
+ int arg1;
+ int output;
+ short arg2;
+ short arg3;
+};
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqstate.h b/primedev/vscript/languages/squirrel_re/squirrel/sqstate.h
new file mode 100644
index 00000000..d5282ac7
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqstate.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+
+class CSquirrelVM;
+struct SQCompiler;
+
+// TODO [Fifty]: Verify size
+struct StringTable
+{
+ unsigned char gap_0[12];
+ int _numofslots;
+ unsigned char gap_10[200];
+};
+
+struct SQSharedState
+{
+ unsigned char gap_0[72];
+ void* unknown;
+ unsigned char gap_50[16344];
+ SQObjectType _unknownTableType00;
+ long long _unknownTableValue00;
+ unsigned char gap_4038[16];
+ StringTable* _stringTable;
+ unsigned char gap_4050[32];
+ SQObjectType _unknownTableType0;
+ long long _unknownTableValue0;
+ SQObjectType _unknownObjectType1;
+ long long _unknownObjectValue1;
+ unsigned char gap_4090[8];
+ SQObjectType _unknownArrayType2;
+ long long _unknownArrayValue2;
+ SQObjectType _gobalsArrayType;
+ SQStructInstance* _globalsArray;
+ unsigned char gap_40B8[16];
+ SQObjectType _nativeClosuresType;
+ SQTable* _nativeClosures;
+ SQObjectType _typedConstantsType;
+ SQTable* _typedConstants;
+ SQObjectType _untypedConstantsType;
+ SQTable* _untypedConstants;
+ SQObjectType _globalsMaybeType;
+ SQTable* _globals;
+ SQObjectType _functionsType;
+ SQTable* _functions;
+ SQObjectType _structsType;
+ SQTable* _structs;
+ SQObjectType _typeDefsType;
+ SQTable* _typeDefs;
+ SQObjectType unknownTableType;
+ SQTable* unknownTable;
+ SQObjectType _squirrelFilesType;
+ SQTable* _squirrelFiles;
+ unsigned char gap_4158[80];
+ SQObjectType _nativeClosures2Type;
+ SQTable* _nativeClosures2;
+ SQObjectType _entityTypesMaybeType;
+ SQTable* _entityTypesMaybe;
+ SQObjectType unknownTable2Type;
+ SQTable* unknownTable2;
+ unsigned char gap_41D8[64];
+ SQCompiler* pCompiler;
+ SQObjectType _compilerKeywordsType;
+ SQTable* _compilerKeywords;
+ HSQUIRRELVM _currentThreadMaybe;
+ unsigned char gap_4238[8];
+ SQObjectType unknownTable3Type;
+ SQTable* unknownTable3;
+ unsigned char gap_4250[16];
+ SQObjectType unknownThreadType;
+ SQTable* unknownThread;
+ SQObjectType _tableNativeFunctionsType;
+ SQTable* _tableNativeFunctions;
+ SQObjectType _unknownTableType4;
+ long long _unknownObjectValue4;
+ SQObjectType _unknownObjectType5;
+ long long _unknownObjectValue5;
+ SQObjectType _unknownObjectType6;
+ long long _unknownObjectValue6;
+ SQObjectType _unknownObjectType7;
+ long long _unknownObjectValue7;
+ SQObjectType _unknownObjectType8;
+ long long _unknownObjectValue8;
+ SQObjectType _unknownObjectType9;
+ long long _unknownObjectValue9;
+ SQObjectType _unknownObjectType10;
+ long long _unknownObjectValue10;
+ SQObjectType _unknownObjectType11;
+ long long _unknownObjectValue11;
+ SQObjectType _unknownObjectType12;
+ long long _unknownObjectValue12;
+ SQObjectType _unknownObjectType13;
+ long long _unknownObjectValue13;
+ SQObjectType _unknownObjectType14;
+ long long _unknownObjectValue14;
+ SQObjectType _unknownObjectType15;
+ long long _unknownObjectValue15;
+ unsigned __int8 gap_4340[8];
+ void* fnFatalErrorCallback;
+ void* fnPrintCallback;
+ unsigned __int8 gap_4358[16];
+ void* logEntityFunction;
+ unsigned char gap_4370[1];
+ SQChar szContextName[8];
+ unsigned char gap[31];
+ SQObjectType _waitStringType;
+ SQString* _waitStringValue;
+ SQObjectType _SpinOffAndWaitForStringType;
+ SQString* _SpinOffAndWaitForStringValue;
+ SQObjectType _SpinOffAndWaitForSoloStringType;
+ SQString* _SpinOffAndWaitForSoloStringValue;
+ SQObjectType _SpinOffStringType;
+ SQString* _SpinOffStringValue;
+ SQObjectType _SpinOffDelayedStringType;
+ SQString* _SpinOffDelayedStringValue;
+ CSquirrelVM* cSquirrelVM;
+ bool enableDebugInfo; // functionality stripped
+ unsigned char gap_43F1[23];
+};
+static_assert(sizeof(SQSharedState) == 17416);
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqstring.h b/primedev/vscript/languages/squirrel_re/squirrel/sqstring.h
new file mode 100644
index 00000000..5b17f489
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqstring.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+// NOTE [Fifty]: Variable sized struct
+struct alignas(8) SQString : public SQRefCounted
+{
+ SQSharedState* sharedState;
+ int length;
+ unsigned char gap_24[4];
+ char _hash[8];
+ char _val[1];
+};
+static_assert(sizeof(SQString) == 56); // [Fifty]: Game allocates 56 + strlen
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqstruct.h b/primedev/vscript/languages/squirrel_re/squirrel/sqstruct.h
new file mode 100644
index 00000000..1e357df8
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqstruct.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+struct SQStructDef : public SQCollectable
+{
+ SQObjectType _nameType;
+ SQString* _name;
+ unsigned char gap_38[16];
+ SQObjectType _variableNamesType;
+ SQTable* _variableNames;
+ unsigned char gap_[32];
+};
+static_assert(sizeof(SQStructDef) == 128);
+
+// NOTE [Fifty]: Variable sized struct
+struct SQStructInstance : public SQCollectable
+{
+ unsigned int size;
+ BYTE gap_34[4];
+ SQObject data[1];
+};
+static_assert(sizeof(SQStructInstance) == 72);
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqtable.h b/primedev/vscript/languages/squirrel_re/squirrel/sqtable.h
new file mode 100644
index 00000000..e0ced436
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqtable.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+struct alignas(8) SQTable : public SQDelegable
+{
+ struct _HashNode
+ {
+ SQObject val;
+ SQObject key;
+ _HashNode* next;
+ };
+
+ _HashNode* _nodes;
+ int _numOfNodes;
+ int size;
+ int field_48;
+ int _usedNodes;
+};
+static_assert(sizeof(SQTable) == 80);
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/squserdata.h b/primedev/vscript/languages/squirrel_re/squirrel/squserdata.h
new file mode 100644
index 00000000..98ede887
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/squserdata.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+
+// NOTE [Fifty]: Variable sized struct
+struct SQUserData : public SQDelegable
+{
+ int size;
+ char padding1[4];
+ void* (*releasehook)(void* val, int size);
+ long long typeId;
+ char data[1];
+};
+static_assert(sizeof(SQUserData) == 88); // [Fifty]: Game allocates 87 + size (passed to the function)
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqvector.h b/primedev/vscript/languages/squirrel_re/squirrel/sqvector.h
new file mode 100644
index 00000000..63984e90
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqvector.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+
+// TODO [Fifty]: Verify size
+struct SQVector
+{
+ SQObjectType _Type;
+ float x;
+ float y;
+ float z;
+};
diff --git a/primedev/vscript/languages/squirrel_re/squirrel/sqvm.h b/primedev/vscript/languages/squirrel_re/squirrel/sqvm.h
new file mode 100644
index 00000000..d16092e7
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/squirrel/sqvm.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+#include "vscript/languages/squirrel_re/squirrel/sqstring.h"
+
+struct SQVM;
+
+enum class ScriptContext : int
+{
+ INVALID = -1,
+ SERVER,
+ CLIENT,
+ UI,
+};
+
+struct alignas(8) SQVM
+{
+ struct alignas(8) CallInfo
+ {
+ long long ip;
+ SQObject* _literals;
+ SQObject obj10;
+ SQObject closure;
+ int _etraps[4];
+ int _root;
+ short _vargs_size;
+ short _vargs_base;
+ unsigned char gap[16];
+ };
+
+ void* vftable;
+ int uiRef;
+ unsigned char gap_8[12];
+ void* _toString;
+ void* _roottable_pointer;
+ void* pointer_28;
+ CallInfo* ci;
+ CallInfo* _callstack;
+ int _callstacksize;
+ int _stackbase;
+ SQObject* _stackOfCurrentFunction;
+ SQSharedState* sharedState;
+ void* pointer_58;
+ void* pointer_60;
+ int _top;
+ SQObject* _stack;
+ unsigned char gap_78[8];
+ SQObject* _vargvstack;
+ unsigned char gap_88[8];
+ SQObject temp_reg;
+ unsigned char gapA0[8];
+ void* pointer_A8;
+ unsigned char gap_B0[8];
+ SQObject _roottable_object;
+ SQObject _lasterror;
+ SQObject _errorHandler;
+ long long field_E8;
+ int traps;
+ unsigned char gap_F4[12];
+ int _nnativecalls;
+ int _suspended;
+ int _suspended_root;
+ int _unk;
+ int _suspended_target;
+ int trapAmount;
+ int _suspend_varargs;
+ int unknown_field_11C;
+ SQObject object_120;
+};
diff --git a/primedev/vscript/languages/squirrel_re/vsquirrel.h b/primedev/vscript/languages/squirrel_re/vsquirrel.h
new file mode 100644
index 00000000..43be685e
--- /dev/null
+++ b/primedev/vscript/languages/squirrel_re/vsquirrel.h
@@ -0,0 +1,16 @@
+#pragma once
+
+struct CSquirrelVM
+{
+ BYTE gap_0[8];
+ HSQUIRRELVM sqvm;
+ BYTE gap_10[8];
+ SQObject unknownObject_18;
+ __int64 unknown_28;
+ BYTE gap_30[12];
+ __int32 vmContext;
+ BYTE gap_40[648];
+ char* (*formatString)(__int64 a1, const char* format, ...);
+ BYTE gap_2D0[24];
+};
+static_assert(sizeof(CSquirrelVM) == 744);
diff --git a/primedev/vscript/vscript.h b/primedev/vscript/vscript.h
new file mode 100644
index 00000000..4c9f072a
--- /dev/null
+++ b/primedev/vscript/vscript.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "vscript/languages/squirrel_re/include/squirrel.h"
+
+#include "vscript/languages/squirrel_re/squirrel/sqarray.h"
+#include "vscript/languages/squirrel_re/squirrel/sqclosure.h"
+#include "vscript/languages/squirrel_re/squirrel/sqcompiler.h"
+#include "vscript/languages/squirrel_re/squirrel/sqfunctionproto.h"
+#include "vscript/languages/squirrel_re/squirrel/sqlexer.h"
+#include "vscript/languages/squirrel_re/squirrel/sqobject.h"
+#include "vscript/languages/squirrel_re/squirrel/sqopcodes.h"
+#include "vscript/languages/squirrel_re/squirrel/sqstate.h"
+#include "vscript/languages/squirrel_re/squirrel/sqstring.h"
+#include "vscript/languages/squirrel_re/squirrel/sqstruct.h"
+#include "vscript/languages/squirrel_re/squirrel/sqtable.h"
+#include "vscript/languages/squirrel_re/squirrel/squserdata.h"
+#include "vscript/languages/squirrel_re/squirrel/sqvector.h"
+#include "vscript/languages/squirrel_re/squirrel/sqvm.h"
+
+#include "vscript/languages/squirrel_re/vsquirrel.h"
diff --git a/primedev/windows/libsys.cpp b/primedev/windows/libsys.cpp
index 501eae68..0aff820b 100644
--- a/primedev/windows/libsys.cpp
+++ b/primedev/windows/libsys.cpp
@@ -18,15 +18,31 @@ ILoadLibraryExW o_LoadLibraryExW = nullptr;
//-----------------------------------------------------------------------------
void LibSys_RunModuleCallbacks(HMODULE hModule)
{
+ // Modules that we have already ran callbacks for.
+ // Note: If we ever hook unloading modules, then this will need updating to handle removal etc.
+ static std::vector<HMODULE> vCalledModules;
+
if (!hModule)
{
return;
}
+ // If we have already ran callbacks for this module, don't run them again.
+ if (std::find(vCalledModules.begin(), vCalledModules.end(), hModule) != vCalledModules.end())
+ {
+ return;
+ }
+ vCalledModules.push_back(hModule);
+
// Get module base name in ASCII as noone wants to deal with unicode
CHAR szModuleName[MAX_PATH];
GetModuleBaseNameA(GetCurrentProcess(), hModule, szModuleName, MAX_PATH);
+ // Run calllbacks for all imported modules
+ CModule cModule(hModule);
+ for (const std::string& svImport : cModule.GetImportedModules())
+ LibSys_RunModuleCallbacks(GetModuleHandleA(svImport.c_str()));
+
// DevMsg(eLog::NONE, "%s\n", szModuleName);
// Call callbacks