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