diff options
author | GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> | 2024-09-20 14:24:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-20 14:24:56 +0200 |
commit | a4b09bc42d5f79ef86697c893efc9e3b7d966502 (patch) | |
tree | d4c7b2b8cc04361ddf7b9c942e5f9462bb55e0b9 | |
parent | a9d2ce8a692f7890f4e4bfc21458332890605a5f (diff) | |
parent | 6737a344c012c0f7fd19cd593949dd3dbe5a0cb7 (diff) | |
download | NorthstarLauncher-a4b09bc42d5f79ef86697c893efc9e3b7d966502.tar.gz NorthstarLauncher-a4b09bc42d5f79ef86697c893efc9e3b7d966502.zip |
Merge branch 'main' into feat/overhaul-mod-loading-locations
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') @@ -18,7 +18,7 @@ mono_crash.* # CMake output out/ -game/ +build/game/ build/ CMakeFiles/ cmake_install.cmake @@ -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/` @@ -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 @@ -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 |