aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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--Dockerfile24
-rw-r--r--README.md2
-rw-r--r--STANDARDS.md53
-rw-r--r--primedev/Northstar.cmake19
-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.cpp25
-rw-r--r--primedev/client/languagehooks.cpp15
-rw-r--r--primedev/client/latencyflex.cpp13
-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/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.cpp14
-rw-r--r--primedev/core/sourceinterface.cpp48
-rw-r--r--primedev/core/tier0.cpp29
-rw-r--r--primedev/dedicated/dedicated.cpp22
-rw-r--r--primedev/dedicated/dedicatedmaterialsystem.cpp25
-rw-r--r--primedev/engine/gl_matsysiface.cpp8
-rw-r--r--primedev/engine/host.cpp13
-rw-r--r--primedev/engine/hoststate.cpp64
-rw-r--r--primedev/game/client/clientmode_shared.cpp66
-rw-r--r--primedev/logging/logging.cpp2
-rw-r--r--primedev/logging/sourceconsole.cpp32
-rw-r--r--primedev/logging/sourceconsole.h2
-rw-r--r--primedev/mods/autodownload/moddownloader.cpp42
-rw-r--r--primedev/mods/autodownload/moddownloader.h19
-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/plugins.cpp1
-rw-r--r--primedev/scripts/scriptdatatables.cpp19
-rw-r--r--primedev/scripts/scripthttprequesthandler.cpp10
-rw-r--r--primedev/scripts/scriptjson.cpp12
-rw-r--r--primedev/scripts/scriptjson.h2
-rw-r--r--primedev/squirrel/squirrel.cpp22
-rw-r--r--primedev/squirrel/squirrel.h64
-rw-r--r--primedev/squirrel/squirrelautobind.h8
-rw-r--r--primedev/squirrel/squirrelclasstypes.h137
-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/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
75 files changed, 1851 insertions, 1191 deletions
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/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 b8038073..4e8ec973 100644
--- a/primedev/Northstar.cmake
+++ b/primedev/Northstar.cmake
@@ -69,6 +69,7 @@ add_library(
"engine/r2engine.cpp"
"engine/r2engine.h"
"engine/runframe.cpp"
+ "game/client/clientmode_shared.cpp"
"logging/crashhandler.cpp"
"logging/crashhandler.h"
"logging/logging.cpp"
@@ -151,7 +152,6 @@ add_library(
"squirrel/squirrelautobind.cpp"
"squirrel/squirrelautobind.h"
"squirrel/squirrelclasstypes.h"
- "squirrel/squirreldatatypes.h"
"util/printcommands.cpp"
"util/printcommands.h"
"util/printmaps.cpp"
@@ -162,6 +162,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 7f2e0901..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,
@@ -122,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);
@@ -205,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);
@@ -259,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;
@@ -280,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/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/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..ebb9085a 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; });
- m_vLoadedPaks.insert(std::make_pair(nPakHandle, pak));
- return &m_vLoadedPaks.at(nPakHandle);
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_handle == PakHandle::INVALID || !modPak.m_markedForDelete)
+ continue;
+
+ 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 > PakHandle::INVALID; 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->m_ModDirectory / "paks" / mod->RpakAliases[originalPath]).string();
+ 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();
- // dont load the pak if it's currently loaded already
- size_t nPathHash = STR_HASH(pPath);
- if (g_pPakLoadManager->GetPakHandle(nPathHash) != -1)
- return -1;
+ // strip file extension
+ const std::string mapName = fs::path(mapPath).replace_extension().string();
- bool bNeedToFreePakName = false;
+ // load mp_common, sp_common etc.
+ o_pLoadGametypeSpecificRpaks(mapName.c_str());
- static bool bShouldLoadPaks = true;
- if (bShouldLoadPaks)
+ // unload old modded map paks
+ g_pPakLoadManager->UnloadModPaks();
+ // load modded map paks
+ g_pPakLoadManager->LoadModPaksForMap(mapName.c_str());
+
+ // 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;
+
+ 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();
+ }
- // disable preloading while we're doing this
- bShouldLoadPaks = false;
+ char mapRpakStr[272];
+ snprintf(mapRpakStr, 272, "%s.rpak", mapName.c_str());
- LoadPreloadPaks();
- LoadCustomMapPaks(&pPath, &bNeedToFreePakName);
+ // if level being loaded is the same as current level, do nothing
+ if (!g_pPakLoadManager->GetForceReloadOnMapLoad() && !strcmp(mapRpakStr, pszCurrentMapRpakPath))
+ return true;
- bShouldLoadPaks = true;
+ strcpy(pszCurrentMapRpakPath, mapRpakStr);
+
+ (*o_pCleanMaterialSystemStuff)();
+ o_pLoadlevelLoadscreen(mapName.c_str());
+
+ // 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;
+ }
+
+ *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 7ce98331..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)
@@ -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/sourceinterface.cpp b/primedev/core/sourceinterface.cpp
index 5a72beb0..74e4a996 100644
--- a/primedev/core/sourceinterface.cpp
+++ b/primedev/core/sourceinterface.cpp
@@ -1,16 +1,12 @@
#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 +15,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/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/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
index 903a0113..075a56ac 100644
--- a/primedev/engine/gl_matsysiface.cpp
+++ b/primedev/engine/gl_matsysiface.cpp
@@ -2,9 +2,8 @@
CMaterialGlue* (*GetMaterialAtCrossHair)();
-AUTOHOOK_INIT()
-
-AUTOHOOK(CC_mat_crosshair_printmaterial_f, engine.dll + 0xB3C40, void, __fastcall, (const CCommand& args))
+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();
@@ -44,7 +43,8 @@ AUTOHOOK(CC_mat_crosshair_printmaterial_f, engine.dll + 0xB3C40, void, __fastcal
ON_DLL_LOAD("engine.dll", GlMatSysIFace, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ 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/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/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/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..35cc1723 100644
--- a/primedev/logging/sourceconsole.h
+++ b/primedev/logging/sourceconsole.h
@@ -61,8 +61,6 @@ public:
CConsoleDialog* m_pConsole;
};
-extern SourceInterface<CGameConsole>* g_pSourceGameConsole;
-
// spdlog logger
class SourceConsoleSink : public CustomSink
{
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/modmanager.cpp b/primedev/mods/modmanager.cpp
index cc6195c7..056ea136 100644
--- a/primedev/mods/modmanager.cpp
+++ b/primedev/mods/modmanager.cpp
@@ -836,7 +836,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());
}
}
}
@@ -883,7 +883,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)
{
@@ -891,19 +893,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 :/
@@ -943,12 +973,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
@@ -1076,6 +1105,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 3df7fe53..68a7c5d0 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;
@@ -20,6 +21,8 @@ const std::string COMPILED_ASSETS_SUFFIX = "\\runtime\\compiled";
const std::set<std::string> MODS_BLACKLIST = {"Mod Settings"};
+class Mod;
+
struct ModConVar
{
public:
@@ -72,9 +75,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
@@ -121,11 +137,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 68e33864..13239c99 100644
--- a/primedev/mods/modsavefiles.cpp
+++ b/primedev/mods/modsavefiles.cpp
@@ -537,7 +537,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/plugins.cpp b/primedev/plugins/plugins.cpp
index f0a9c3c5..92be9d5c 100644
--- a/primedev/plugins/plugins.cpp
+++ b/primedev/plugins/plugins.cpp
@@ -219,7 +219,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/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/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/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 086ab9c6..ae6e80c9 100644
--- a/primedev/squirrel/squirrel.h
+++ b/primedev/squirrel/squirrel.h
@@ -129,72 +129,72 @@ public:
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)
@@ -211,26 +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);
@@ -239,9 +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
};
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 55897b69..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;
@@ -185,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/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