diff --git a/.clang-format b/.clang-format index 0c08cabd..2ad2ec20 100644 --- a/.clang-format +++ b/.clang-format @@ -3,6 +3,7 @@ Standard: Cpp11 IndentWidth: 4 TabWidth: 4 UseCRLF: false +AccessModifierOffset: -4 AlignTrailingComments: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: true @@ -34,3 +35,5 @@ IndentExternBlock: Indent PointerAlignment: Left SortIncludes: false NamespaceIndentation: All +PackConstructorInitializers: NextLine +BreakConstructorInitializersBeforeComma: true diff --git a/.cmake-format.json b/.cmake-format.json new file mode 100644 index 00000000..b819f8cd --- /dev/null +++ b/.cmake-format.json @@ -0,0 +1,26 @@ +{ + "format": { + "line_width": 120, + "tab_size": 4, + "use_tabchars": false, + "fractional_tab_policy": "use-space", + "max_subgroups_hwrap": 2, + "max_pargs_hwrap": 2, + "max_rows_cmdline": 2, + "separate_ctrl_name_with_space": false, + "separate_fn_name_with_space": false, + "dangle_parens": true, + "dangle_align": "child", + "min_prefix_chars": 4, + "max_prefix_chars": 10, + "max_lines_hwrap": 2, + "line_ending": "unix", + "command_case": "canonical", + "keyword_case": "unchanged", + "always_wrap": [], + "enable_sort": true, + "autosort": false, + "require_valid_layout": false, + "layout_passes": {} + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09af4df7..e6048a57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,10 +20,10 @@ jobs: - name: Setup resource file version shell: bash run: | - sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' NorthstarLauncher/resources.rc - sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' NorthstarDLL/resources.rc + sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' primedev/primelauncher/resources.rc + sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' primedev/resources.rc FILEVERSION=$(echo ${{ env.NORTHSTAR_VERSION }} | tr '.' ',' | sed -E 's/-rc[0-9]+//' | tr -d '[:alpha:]') - sed -i "s/0,0,0,1/${FILEVERSION}/g" NorthstarDLL/ns_version.h + sed -i "s/0,0,0,1/${FILEVERSION}/g" primedev/ns_version.h - name: Build run: cmake --build . - name: Extract Short Commit Hash @@ -41,10 +41,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: DoozyX/clang-format-lint-action@v0.13 + - uses: DoozyX/clang-format-lint-action@v0.16.2 with: - source: 'NorthstarDLL NorthstarLauncher' - exclude: 'NorthstarDLL/include loader_launcher_proxy loader_wsock32_proxy' + source: 'primedev' + exclude: 'primedev/include primedev/thirdparty primedev/wsockproxy' extensions: 'h,cpp' - clangFormatVersion: 13 + clangFormatVersion: 16 style: file + + format-check-cmake-files: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: puneetmatharu/cmake-format-lint-action@v1.0.4 + with: + args: "--in-place" + - run: | + git diff --exit-code diff --git a/.github/workflows/merge-conflict-auto-label.yml b/.github/workflows/merge-conflict-auto-label.yml new file mode 100644 index 00000000..cf5598a6 --- /dev/null +++ b/.github/workflows/merge-conflict-auto-label.yml @@ -0,0 +1,18 @@ +name: Merge Conflict Auto Label +on: + push: + branches: + - main + schedule: + - cron: "10 21 * * *" + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: mschilde/auto-label-merge-conflicts@master + with: + CONFLICT_LABEL_NAME: "merge conflicts" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MAX_RETRIES: 5 + WAIT_MS: 5000 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8aebf495..e6dd8cc3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,10 +25,10 @@ jobs: - name: Setup resource file version shell: bash run: | - sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' NorthstarLauncher/resources.rc - sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' NorthstarDLL/resources.rc + sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' primedev/primelauncher/resources.rc + sed -i 's/DEV/${{ env.NORTHSTAR_VERSION }}/g' primedev/resources.rc FILEVERSION=$(echo ${{ env.NORTHSTAR_VERSION }} | tr '.' ',' | sed -E 's/-rc[0-9]+//' | tr -d '[:alpha:]') - sed -i "s/0,0,0,1/${FILEVERSION}/g" NorthstarDLL/ns_version.h + sed -i "s/0,0,0,1/${FILEVERSION}/g" primedev/ns_version.h - name: Build run: cmake --build . - name: Upload launcher build as artifact diff --git a/.gitmodules b/.gitmodules index c4cfc0a1..41f118e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,11 +1,11 @@ [submodule "thirdparty/libcurl"] - path = thirdparty/libcurl + path = primedev/thirdparty/libcurl url = https://github.com/curl/curl ignore = untracked [submodule "thirdparty/minhook"] - path = thirdparty/minhook + path = primedev/thirdparty/minhook url = https://github.com/TsudaKageyu/minhook ignore = untracked [submodule "thirdparty/minizip"] - path = thirdparty/minizip + path = primedev/thirdparty/minizip url = https://github.com/zlib-ng/minizip-ng.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 83c38a56..ab461ae2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,18 @@ cmake_minimum_required(VERSION 3.15) -project(Northstar CXX ASM_MASM) +project( + Northstar + CXX + ASM_MASM + ) if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) + set(CMAKE_BUILD_TYPE + "Release" + CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE + ) endif() # Language specs @@ -15,19 +23,22 @@ set(CMAKE_VS_PLATFORM_TOOLSET v143) # This determines the real binary root directory set(NS_BINARY_DIR ${CMAKE_BINARY_DIR}/game) -# NOTE [Fifty]: Visual studio deems Build root with the value "${projectDir}" -# in CMakeSettings.json as invalid and defaults to using a temporary dir -# somewhere in %USER%/CMakeBuilds. To combat this we set it to "${projectDir}/build" -# and then link binaries in ${CMAKE_BINARY_DIR}/game. This means you can copy your -# game into ${CMAKE_BINARY_DIR}/game without it being cluttered up by cmake files. +# NOTE [Fifty]: Visual studio deems Build root with the value "${projectDir}" in CMakeSettings.json as invalid and +# defaults to using a temporary dir somewhere in %USER%/CMakeBuilds. To combat this we set it to "${projectDir}/build" +# and then link binaries in ${CMAKE_BINARY_DIR}/game. This means you can copy your game into ${CMAKE_BINARY_DIR}/game +# without it being cluttered up by cmake files. message(STATUS "NS: Building to ${NS_BINARY_DIR}") - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +list( + APPEND + CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/primedev/cmake" + ) include(utils) +include_directories(primedev) +include_directories(primedev/thirdparty) + # Targets -add_subdirectory(loader_wsock32_proxy) -add_subdirectory(NorthstarDLL) -add_subdirectory(NorthstarLauncher) +add_subdirectory(primedev) diff --git a/NorthstarDLL/CMakeLists.txt b/NorthstarDLL/CMakeLists.txt deleted file mode 100644 index 5c275887..00000000 --- a/NorthstarDLL/CMakeLists.txt +++ /dev/null @@ -1,188 +0,0 @@ -# NorthstarDLL - -find_package(minhook REQUIRED) -find_package(libcurl REQUIRED) -find_package(minizip REQUIRED) - -add_library(NorthstarDLL SHARED - "resources.rc" - "client/audio.cpp" - "client/audio.h" - "client/chatcommand.cpp" - "client/clientauthhooks.cpp" - "client/clientruihooks.cpp" - "client/clientvideooverrides.cpp" - "client/debugoverlay.cpp" - "client/demofixes.cpp" - "client/diskvmtfixes.cpp" - "client/languagehooks.cpp" - "client/latencyflex.cpp" - "client/localchatwriter.cpp" - "client/localchatwriter.h" - "client/modlocalisation.cpp" - "client/r2client.cpp" - "client/r2client.h" - "client/rejectconnectionfixes.cpp" - "config/profile.cpp" - "config/profile.h" - "core/convar/concommand.cpp" - "core/convar/concommand.h" - "core/convar/convar.cpp" - "core/convar/convar.h" - "core/convar/cvar.cpp" - "core/convar/cvar.h" - "core/filesystem/filesystem.cpp" - "core/filesystem/filesystem.h" - "core/filesystem/rpakfilesystem.cpp" - "core/filesystem/rpakfilesystem.h" - "core/math/bitbuf.h" - "core/math/bits.cpp" - "core/math/bits.h" - "core/math/color.cpp" - "core/math/color.h" - "core/math/vector.h" - "core/hooks.cpp" - "core/hooks.h" - "core/macros.h" - "core/memalloc.cpp" - "core/memalloc.h" - "core/memory.cpp" - "core/memory.h" - "core/sourceinterface.cpp" - "core/sourceinterface.h" - "core/structs.h" - "core/tier0.cpp" - "core/tier0.h" - "dedicated/dedicated.cpp" - "dedicated/dedicated.h" - "dedicated/dedicatedlogtoclient.cpp" - "dedicated/dedicatedlogtoclient.h" - "dedicated/dedicatedmaterialsystem.cpp" - "engine/host.cpp" - "engine/hoststate.cpp" - "engine/hoststate.h" - "engine/r2engine.cpp" - "engine/r2engine.h" - "engine/runframe.cpp" - "logging/crashhandler.cpp" - "logging/crashhandler.h" - "logging/logging.cpp" - "logging/logging.h" - "logging/loghooks.cpp" - "logging/loghooks.h" - "logging/sourceconsole.cpp" - "logging/sourceconsole.h" - "masterserver/masterserver.cpp" - "masterserver/masterserver.h" - "mods/autodownload/moddownloader.h" - "mods/autodownload/moddownloader.cpp" - "mods/compiled/kb_act.cpp" - "mods/compiled/modkeyvalues.cpp" - "mods/compiled/modpdef.cpp" - "mods/compiled/modscriptsrson.cpp" - "mods/modmanager.cpp" - "mods/modmanager.h" - "mods/modsavefiles.cpp" - "mods/modsavefiles.h" - "plugins/plugin_abi.h" - "plugins/pluginbackend.cpp" - "plugins/pluginbackend.h" - "plugins/plugins.cpp" - "plugins/plugins.h" - "scripts/client/clientchathooks.cpp" - "scripts/client/cursorposition.cpp" - "scripts/client/scriptbrowserhooks.cpp" - "scripts/client/scriptmainmenupromos.cpp" - "scripts/client/scriptmodmenu.cpp" - "scripts/client/scriptoriginauth.cpp" - "scripts/client/scriptserverbrowser.cpp" - "scripts/client/scriptservertoclientstringcommand.cpp" - "scripts/server/miscserverfixes.cpp" - "scripts/server/miscserverscript.cpp" - "scripts/server/scriptuserinfo.cpp" - "scripts/scriptdatatables.cpp" - "scripts/scripthttprequesthandler.cpp" - "scripts/scripthttprequesthandler.h" - "scripts/scriptjson.cpp" - "scripts/scriptjson.h" - "scripts/scriptutility.cpp" - "server/auth/bansystem.cpp" - "server/auth/bansystem.h" - "server/auth/serverauthentication.cpp" - "server/auth/serverauthentication.h" - "server/alltalk.cpp" - "server/buildainfile.cpp" - "server/r2server.cpp" - "server/r2server.h" - "server/serverchathooks.cpp" - "server/serverchathooks.h" - "server/servernethooks.cpp" - "server/serverpresence.cpp" - "server/serverpresence.h" - "shared/exploit_fixes/exploitfixes.cpp" - "shared/exploit_fixes/exploitfixes_lzss.cpp" - "shared/exploit_fixes/exploitfixes_utf8parser.cpp" - "shared/exploit_fixes/ns_limits.cpp" - "shared/exploit_fixes/ns_limits.h" - "shared/keyvalues.cpp" - "shared/keyvalues.h" - "shared/maxplayers.cpp" - "shared/maxplayers.h" - "shared/misccommands.cpp" - "shared/misccommands.h" - "shared/playlist.cpp" - "shared/playlist.h" - "squirrel/squirrel.cpp" - "squirrel/squirrel.h" - "squirrel/squirrelautobind.cpp" - "squirrel/squirrelautobind.h" - "squirrel/squirrelclasstypes.h" - "squirrel/squirreldatatypes.h" - "util/printcommands.cpp" - "util/printcommands.h" - "util/printmaps.cpp" - "util/printmaps.h" - "util/utils.cpp" - "util/utils.h" - "util/version.cpp" - "util/version.h" - "util/wininfo.cpp" - "util/wininfo.h" - "audio_asm.asm" - "dllmain.cpp" - "dllmain.h" - "ns_version.h" -) - -target_link_libraries(NorthstarDLL PRIVATE - minhook - libcurl - minizip - WS2_32.lib - Crypt32.lib - Cryptui.lib - dbghelp.lib - Wldap32.lib - Normaliz.lib - Bcrypt.lib - version.lib -) - -target_include_directories(NorthstarDLL PRIVATE - ${CMAKE_SOURCE_DIR}/NorthstarDLL - ${CMAKE_SOURCE_DIR}/thirdparty -) - -target_precompile_headers(NorthstarDLL PRIVATE pch.h) - -target_compile_definitions(NorthstarDLL PRIVATE - UNICODE - _UNICODE - CURL_STATICLIB -) - -set_target_properties(NorthstarDLL PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR} - OUTPUT_NAME Northstar - LINK_FLAGS "/MANIFEST:NO /DEBUG" -) diff --git a/NorthstarDLL/audio_asm.asm b/NorthstarDLL/audio_asm.asm deleted file mode 100644 index 1b2d3f8d..00000000 --- a/NorthstarDLL/audio_asm.asm +++ /dev/null @@ -1,8 +0,0 @@ -public Audio_GetParentEvent - -.code -Audio_GetParentEvent proc - mov rax, r12 - ret -Audio_GetParentEvent endp -end diff --git a/NorthstarDLL/client/r2client.h b/NorthstarDLL/client/r2client.h deleted file mode 100644 index 64ed6c61..00000000 --- a/NorthstarDLL/client/r2client.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// use the R2 namespace for game funcs -namespace R2 -{ - extern char* g_pLocalPlayerUserID; - extern char* g_pLocalPlayerOriginToken; - - typedef void* (*GetBaseLocalClientType)(); - extern GetBaseLocalClientType GetBaseLocalClient; -} // namespace R2 diff --git a/NorthstarDLL/core/math/vector.h b/NorthstarDLL/core/math/vector.h deleted file mode 100644 index 8684908f..00000000 --- a/NorthstarDLL/core/math/vector.h +++ /dev/null @@ -1,47 +0,0 @@ -#include - -#pragma once - -union Vector3 -{ - struct - { - float x; - float y; - float z; - }; - - float raw[3]; - - void MakeValid() - { - for (auto& fl : raw) - if (std::isnan(fl)) - fl = 0; - } - - // todo: more operators maybe - bool operator==(const Vector3& other) - { - return x == other.x && y == other.y && z == other.z; - } -}; - -union QAngle -{ - struct - { - float x; - float y; - float z; - float w; - }; - - float raw[4]; - - // todo: more operators maybe - bool operator==(const QAngle& other) - { - return x == other.x && y == other.y && z == other.z && w == other.w; - } -}; diff --git a/NorthstarDLL/core/memory.cpp b/NorthstarDLL/core/memory.cpp deleted file mode 100644 index 3770586f..00000000 --- a/NorthstarDLL/core/memory.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "memory.h" - -CMemoryAddress::CMemoryAddress() : m_nAddress(0) {} -CMemoryAddress::CMemoryAddress(const uintptr_t nAddress) : m_nAddress(nAddress) {} -CMemoryAddress::CMemoryAddress(const void* pAddress) : m_nAddress(reinterpret_cast(pAddress)) {} - -// operators -CMemoryAddress::operator uintptr_t() const -{ - return m_nAddress; -} - -CMemoryAddress::operator void*() const -{ - return reinterpret_cast(m_nAddress); -} - -CMemoryAddress::operator bool() const -{ - return m_nAddress != 0; -} - -bool CMemoryAddress::operator==(const CMemoryAddress& other) const -{ - return m_nAddress == other.m_nAddress; -} - -bool CMemoryAddress::operator!=(const CMemoryAddress& other) const -{ - return m_nAddress != other.m_nAddress; -} - -bool CMemoryAddress::operator==(const uintptr_t& addr) const -{ - return m_nAddress == addr; -} - -bool CMemoryAddress::operator!=(const uintptr_t& addr) const -{ - return m_nAddress != addr; -} - -CMemoryAddress CMemoryAddress::operator+(const CMemoryAddress& other) const -{ - return Offset(other.m_nAddress); -} - -CMemoryAddress CMemoryAddress::operator-(const CMemoryAddress& other) const -{ - return CMemoryAddress(m_nAddress - other.m_nAddress); -} - -CMemoryAddress CMemoryAddress::operator+(const uintptr_t& addr) const -{ - return Offset(addr); -} - -CMemoryAddress CMemoryAddress::operator-(const uintptr_t& addr) const -{ - return CMemoryAddress(m_nAddress - addr); -} - -CMemoryAddress CMemoryAddress::operator*() const -{ - return Deref(); -} - -// traversal -CMemoryAddress CMemoryAddress::Offset(const uintptr_t nOffset) const -{ - return CMemoryAddress(m_nAddress + nOffset); -} - -CMemoryAddress CMemoryAddress::Deref(const int nNumDerefs) const -{ - uintptr_t ret = m_nAddress; - for (int i = 0; i < nNumDerefs; i++) - ret = *reinterpret_cast(ret); - - return CMemoryAddress(ret); -} - -// patching -void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize) -{ - if (nSize) - WriteProcessMemory(GetCurrentProcess(), reinterpret_cast(m_nAddress), pBytes, nSize, NULL); -} - -void CMemoryAddress::Patch(const std::initializer_list bytes) -{ - uint8_t* pBytes = new uint8_t[bytes.size()]; - - int i = 0; - for (const uint8_t& byte : bytes) - pBytes[i++] = byte; - - Patch(pBytes, bytes.size()); - delete[] pBytes; -} - -inline std::vector HexBytesToString(const char* pHexString) -{ - std::vector ret; - - int size = strlen(pHexString); - for (int i = 0; i < size; i++) - { - // If this is a space character, ignore it - if (isspace(pHexString[i])) - continue; - - if (i < size - 1) - { - BYTE result = 0; - for (int j = 0; j < 2; j++) - { - int val = 0; - char c = *(pHexString + i + j); - if (c >= 'a') - { - val = c - 'a' + 0xA; - } - else if (c >= 'A') - { - val = c - 'A' + 0xA; - } - else if (isdigit(c)) - { - val = c - '0'; - } - else - { - assert_msg(false, "Failed to parse invalid hex string."); - val = -1; - } - - result += (j == 0) ? val * 16 : val; - } - ret.push_back(result); - } - - i++; - } - - return ret; -} - -void CMemoryAddress::Patch(const char* pBytes) -{ - std::vector vBytes = HexBytesToString(pBytes); - Patch(vBytes.data(), vBytes.size()); -} - -void CMemoryAddress::NOP(const size_t nSize) -{ - uint8_t* pBytes = new uint8_t[nSize]; - - memset(pBytes, 0x90, nSize); - Patch(pBytes, nSize); - - delete[] pBytes; -} - -bool CMemoryAddress::IsMemoryReadable(const size_t nSize) -{ - static SYSTEM_INFO sysInfo; - if (!sysInfo.dwPageSize) - GetSystemInfo(&sysInfo); - - MEMORY_BASIC_INFORMATION memInfo; - if (!VirtualQuery(reinterpret_cast(m_nAddress), &memInfo, sizeof(memInfo))) - return false; - - return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS); -} - -CModule::CModule(const HMODULE pModule) -{ - MODULEINFO mInfo {0}; - - if (pModule && pModule != INVALID_HANDLE_VALUE) - GetModuleInformation(GetCurrentProcess(), pModule, &mInfo, sizeof(MODULEINFO)); - - m_nModuleSize = static_cast(mInfo.SizeOfImage); - m_pModuleBase = reinterpret_cast(mInfo.lpBaseOfDll); - m_nAddress = m_pModuleBase; - - if (!m_nModuleSize || !m_pModuleBase) - return; - - m_pDOSHeader = reinterpret_cast(m_pModuleBase); - m_pNTHeaders = reinterpret_cast(m_pModuleBase + m_pDOSHeader->e_lfanew); - - const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(m_pNTHeaders); // Get first image section. - - for (WORD i = 0; i < m_pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections. - { - const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section. - - ModuleSections_t moduleSection = ModuleSections_t( - std::string(reinterpret_cast(hCurrentSection.Name)), - static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), - hCurrentSection.SizeOfRawData); - - if (!strcmp((const char*)hCurrentSection.Name, ".text")) - m_ExecutableCode = moduleSection; - else if (!strcmp((const char*)hCurrentSection.Name, ".pdata")) - m_ExceptionTable = moduleSection; - else if (!strcmp((const char*)hCurrentSection.Name, ".data")) - m_RunTimeData = moduleSection; - else if (!strcmp((const char*)hCurrentSection.Name, ".rdata")) - m_ReadOnlyData = moduleSection; - - m_vModuleSections.push_back(moduleSection); // Push back a struct with the section data. - } -} - -CModule::CModule(const char* pModuleName) : CModule(GetModuleHandleA(pModuleName)) {} - -CMemoryAddress CModule::GetExport(const char* pExportName) -{ - return CMemoryAddress(reinterpret_cast(GetProcAddress(reinterpret_cast(m_nAddress), pExportName))); -} - -CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask) -{ - if (!m_ExecutableCode.IsSectionValid()) - return CMemoryAddress(); - - uint64_t nBase = static_cast(m_ExecutableCode.m_pSectionBase); - uint64_t nSize = static_cast(m_ExecutableCode.m_nSectionSize); - - const uint8_t* pData = reinterpret_cast(nBase); - const uint8_t* pEnd = pData + static_cast(nSize) - strlen(pMask); - - int nMasks[64]; // 64*16 = enough masks for 1024 bytes. - int iNumMasks = static_cast(ceil(static_cast(strlen(pMask)) / 16.f)); - - memset(nMasks, '\0', iNumMasks * sizeof(int)); - for (intptr_t i = 0; i < iNumMasks; ++i) - { - for (intptr_t j = strnlen(pMask + i * 16, 16) - 1; j >= 0; --j) - { - if (pMask[i * 16 + j] == 'x') - { - _bittestandset(reinterpret_cast(&nMasks[i]), j); - } - } - } - __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(pPattern)); - __m128i xmm2, xmm3, msks; - for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) - { - if (pPattern[0] == pData[0]) - { - xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); - msks = _mm_cmpeq_epi8(xmm1, xmm2); - if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) - { - for (uintptr_t i = 1; i < static_cast(iNumMasks); ++i) - { - xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); - xmm3 = _mm_loadu_si128(reinterpret_cast((pPattern + i * 16))); - msks = _mm_cmpeq_epi8(xmm2, xmm3); - if ((_mm_movemask_epi8(msks) & nMasks[i]) == nMasks[i]) - { - if ((i + 1) == iNumMasks) - { - return CMemoryAddress(const_cast(pData)); - } - } - else - goto CONTINUE; - } - - return CMemoryAddress((&*(const_cast(pData)))); - } - } - - CONTINUE:; - } - - return CMemoryAddress(); -} - -inline std::pair, std::string> MaskedBytesFromPattern(const char* pPatternString) -{ - std::vector vRet; - std::string sMask; - - int size = strlen(pPatternString); - for (int i = 0; i < size; i++) - { - // If this is a space character, ignore it - if (isspace(pPatternString[i])) - continue; - - if (pPatternString[i] == '?') - { - // Add a wildcard - vRet.push_back(0); - sMask.append("?"); - } - else if (i < size - 1) - { - BYTE result = 0; - for (int j = 0; j < 2; j++) - { - int val = 0; - char c = *(pPatternString + i + j); - if (c >= 'a') - { - val = c - 'a' + 0xA; - } - else if (c >= 'A') - { - val = c - 'A' + 0xA; - } - else if (isdigit(c)) - { - val = c - '0'; - } - else - { - assert_msg(false, "Failed to parse invalid pattern string."); - val = -1; - } - - result += (j == 0) ? val * 16 : val; - } - - vRet.push_back(result); - sMask.append("x"); - } - - i++; - } - - return std::make_pair(vRet, sMask); -} - -CMemoryAddress CModule::FindPattern(const char* pPattern) -{ - const auto pattern = MaskedBytesFromPattern(pPattern); - return FindPattern(pattern.first.data(), pattern.second.c_str()); -} diff --git a/NorthstarDLL/core/memory.h b/NorthstarDLL/core/memory.h deleted file mode 100644 index db0a38b3..00000000 --- a/NorthstarDLL/core/memory.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -class CMemoryAddress -{ - public: - uintptr_t m_nAddress; - - public: - CMemoryAddress(); - CMemoryAddress(const uintptr_t nAddress); - CMemoryAddress(const void* pAddress); - - // operators - operator uintptr_t() const; - operator void*() const; - operator bool() const; - - bool operator==(const CMemoryAddress& other) const; - bool operator!=(const CMemoryAddress& other) const; - bool operator==(const uintptr_t& addr) const; - bool operator!=(const uintptr_t& addr) const; - - CMemoryAddress operator+(const CMemoryAddress& other) const; - CMemoryAddress operator-(const CMemoryAddress& other) const; - CMemoryAddress operator+(const uintptr_t& other) const; - CMemoryAddress operator-(const uintptr_t& other) const; - CMemoryAddress operator*() const; - - template T RCast() - { - return reinterpret_cast(m_nAddress); - } - - // traversal - CMemoryAddress Offset(const uintptr_t nOffset) const; - CMemoryAddress Deref(const int nNumDerefs = 1) const; - - // patching - void Patch(const uint8_t* pBytes, const size_t nSize); - void Patch(const std::initializer_list bytes); - void Patch(const char* pBytes); - void NOP(const size_t nSize); - - bool IsMemoryReadable(const size_t nSize); -}; - -// based on https://github.com/Mauler125/r5sdk/blob/master/r5dev/public/include/module.h -class CModule : public CMemoryAddress -{ - public: - struct ModuleSections_t - { - ModuleSections_t(void) = default; - ModuleSections_t(const std::string& svSectionName, uintptr_t pSectionBase, size_t nSectionSize) - : m_svSectionName(svSectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) - { - } - - bool IsSectionValid(void) const - { - return m_nSectionSize != 0; - } - - std::string m_svSectionName; // Name of section. - uintptr_t m_pSectionBase {}; // Start address of section. - size_t m_nSectionSize {}; // Size of section. - }; - - ModuleSections_t m_ExecutableCode; - ModuleSections_t m_ExceptionTable; - ModuleSections_t m_RunTimeData; - ModuleSections_t m_ReadOnlyData; - - private: - std::string m_svModuleName; - uintptr_t m_pModuleBase {}; - DWORD m_nModuleSize {}; - IMAGE_NT_HEADERS64* m_pNTHeaders = nullptr; - IMAGE_DOS_HEADER* m_pDOSHeader = nullptr; - std::vector m_vModuleSections; - - public: - CModule() = delete; // no default, we need a module name - CModule(const HMODULE pModule); - CModule(const char* pModuleName); - - CMemoryAddress GetExport(const char* pExportName); - CMemoryAddress FindPattern(const uint8_t* pPattern, const char* pMask); - CMemoryAddress FindPattern(const char* pPattern); -}; diff --git a/NorthstarDLL/core/structs.h b/NorthstarDLL/core/structs.h deleted file mode 100644 index 037233a6..00000000 --- a/NorthstarDLL/core/structs.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once -//clang-format off -// About this file: -// This file contains several macros used to define reversed structs -// The reason we use these macros is to make it easier to update existing structs -// when new fields are reversed -// This means we dont have to manually add padding, and recalculate when updating - -// Technical note: -// While functionally, these structs act like a regular struct, they are actually -// defined as unions with anonymous structs in them. -// This means that each field is essentially an offset into a union. -// We acknowledge that this goes against C++'s active-member guideline for unions -// However, this is not such a big deal here as these structs will not be constructed - -// Usage: -// To use these macros, define a struct like so: -/* -OFFSET_STRUCT(Name) -{ - STRUCT_SIZE(0x100) // Total in-memory struct size - FIELD(0x0, int field) // offset, signature -} -*/ - -#define OFFSET_STRUCT(name) union name -#define STRUCT_SIZE(size) char __size[size]; -#define STRUCT_FIELD_OFFSET(offset, signature) \ - struct \ - { \ - char CONCAT2(pad, __LINE__)[offset]; \ - signature; \ - }; - -// Special case for a 0-offset field -#define STRUCT_FIELD_NOOFFSET(offset, signature) signature; - -// Just puts two tokens next to each other, but -// allows us to force the preprocessor to do another pass -#define FX(f, x) f x - -// Macro used to detect if the given offset is 0 or not -#define TEST_0 , -// MSVC does no preprocessing of integer literals. -// On other compilers `0x0` gets processed into `0` -#define TEST_0x0 , - -// Concats the first and third argument and drops everything else -// Used with preprocessor expansion in later passes to move the third argument to the fourth and change the value -#define ZERO_P_I(a, b, c, ...) a##c - -// We use FX to prepare to use ZERO_P_I. -// The right block contains 3 arguments: -// NIF_ -// CONCAT2(TEST_, offset) -// 1 -// -// If offset is not 0 (or 0x0) the preprocessor replaces -// it with nothing and the third argument stays 1 -// -// If the offset is 0, TEST_0 expands to , and 1 becomes the fourth argument -// -// With those arguments we call ZERO_P_I and the first and third arugment get concat. -// We either end up with: -// NIF_ (if offset is 0) or -// NIF_1 (if offset is not 0) -#define IF_ZERO(m) FX(ZERO_P_I, (NIF_, CONCAT2(TEST_, m), 1)) - -// These macros are used to branch after we processed if the offset is zero or not -#define NIF_(t, ...) t -#define NIF_1(t, ...) __VA_ARGS__ - -// FIELD(S), generates an anonymous struct when a non 0 offset is given, otherwise just a signature -#define FIELD(offset, signature) IF_ZERO(offset)(STRUCT_FIELD_NOOFFSET, STRUCT_FIELD_OFFSET)(offset, signature) -#define FIELDS FIELD - -//clang-format on diff --git a/NorthstarDLL/core/tier0.cpp b/NorthstarDLL/core/tier0.cpp deleted file mode 100644 index 16709384..00000000 --- a/NorthstarDLL/core/tier0.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "tier0.h" - -// use the Tier0 namespace for tier0 funcs -namespace Tier0 -{ - IMemAlloc* g_pMemAllocSingleton = nullptr; - - ErrorType Error; - CommandLineType CommandLine; - Plat_FloatTimeType Plat_FloatTime; - ThreadInServerFrameThreadType ThreadInServerFrameThread; -} // namespace Tier0 - -typedef Tier0::IMemAlloc* (*CreateGlobalMemAllocType)(); -CreateGlobalMemAllocType CreateGlobalMemAlloc; - -// needs to be a seperate function, since memalloc.cpp calls it -void TryCreateGlobalMemAlloc() -{ - // init memalloc stuff - CreateGlobalMemAlloc = - reinterpret_cast(GetProcAddress(GetModuleHandleA("tier0.dll"), "CreateGlobalMemAlloc")); - Tier0::g_pMemAllocSingleton = CreateGlobalMemAlloc(); // if it already exists, this returns the preexisting IMemAlloc instance -} - -ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module)) -{ - // shouldn't be necessary, but do this just in case - TryCreateGlobalMemAlloc(); - - // setup tier0 funcs - Tier0::Error = module.GetExport("Error").RCast(); - Tier0::CommandLine = module.GetExport("CommandLine").RCast(); - Tier0::Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast(); - Tier0::ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast(); -} diff --git a/NorthstarDLL/core/tier0.h b/NorthstarDLL/core/tier0.h deleted file mode 100644 index eebe98f2..00000000 --- a/NorthstarDLL/core/tier0.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once -namespace Tier0 -{ - class IMemAlloc - { - public: - struct VTable - { - void* unknown[1]; // alloc debug - void* (*Alloc)(IMemAlloc* memAlloc, size_t nSize); - void* unknown2[1]; // realloc debug - void* (*Realloc)(IMemAlloc* memAlloc, void* pMem, size_t nSize); - void* unknown3[1]; // free #1 - void (*Free)(IMemAlloc* memAlloc, void* pMem); - void* unknown4[2]; // nullsubs, maybe CrtSetDbgFlag - size_t (*GetSize)(IMemAlloc* memAlloc, void* pMem); - void* unknown5[9]; // they all do literally nothing - void (*DumpStats)(IMemAlloc* memAlloc); - void (*DumpStatsFileBase)(IMemAlloc* memAlloc, const char* pchFileBase); - void* unknown6[4]; - int (*heapchk)(IMemAlloc* memAlloc); - }; - - VTable* m_vtable; - }; - - class CCommandLine - { - public: - // based on the defs in the 2013 source sdk, but for some reason has an extra function (may be another CreateCmdLine overload?) - // these seem to line up with what they should be though - virtual void CreateCmdLine(const char* commandline) = 0; - virtual void CreateCmdLine(int argc, char** argv) = 0; - virtual void unknown() = 0; - virtual const char* GetCmdLine(void) const = 0; - - virtual const char* CheckParm(const char* psz, const char** ppszValue = 0) const = 0; - virtual void RemoveParm() const = 0; - virtual void AppendParm(const char* pszParm, const char* pszValues) = 0; - - virtual const char* ParmValue(const char* psz, const char* pDefaultVal = 0) const = 0; - virtual int ParmValue(const char* psz, int nDefaultVal) const = 0; - virtual float ParmValue(const char* psz, float flDefaultVal) const = 0; - - virtual int ParmCount() const = 0; - virtual int FindParm(const char* psz) const = 0; - virtual const char* GetParm(int nIndex) const = 0; - virtual void SetParm(int nIndex, char const* pParm) = 0; - - // virtual const char** GetParms() const {} - }; - - extern IMemAlloc* g_pMemAllocSingleton; - - typedef void (*ErrorType)(const char* fmt, ...); - extern ErrorType Error; - - typedef CCommandLine* (*CommandLineType)(); - extern CommandLineType CommandLine; - - typedef double (*Plat_FloatTimeType)(); - extern Plat_FloatTimeType Plat_FloatTime; - - typedef bool (*ThreadInServerFrameThreadType)(); - extern ThreadInServerFrameThreadType ThreadInServerFrameThread; -} // namespace Tier0 - -void TryCreateGlobalMemAlloc(); diff --git a/NorthstarDLL/dllmain.h b/NorthstarDLL/dllmain.h deleted file mode 100644 index 0debf379..00000000 --- a/NorthstarDLL/dllmain.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -extern "C" __declspec(dllexport) bool InitialiseNorthstar(); diff --git a/NorthstarDLL/engine/hoststate.h b/NorthstarDLL/engine/hoststate.h deleted file mode 100644 index a77385ef..00000000 --- a/NorthstarDLL/engine/hoststate.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -// use the R2 namespace for game funxcs -namespace R2 -{ - enum class HostState_t - { - HS_NEW_GAME = 0, - HS_LOAD_GAME, - HS_CHANGE_LEVEL_SP, - HS_CHANGE_LEVEL_MP, - HS_RUN, - HS_GAME_SHUTDOWN, - HS_SHUTDOWN, - HS_RESTART, - }; - - struct CHostState - { - public: - HostState_t m_iCurrentState; - HostState_t m_iNextState; - - float m_vecLocation[3]; - float m_angLocation[3]; - - char m_levelName[32]; - char m_mapGroupName[32]; - char m_landmarkName[32]; - char m_saveName[32]; - float m_flShortFrameTime; // run a few one-tick frames to avoid large timesteps while loading assets - - bool m_activeGame; - bool m_bRememberLocation; - bool m_bBackgroundLevel; - bool m_bWaitingForConnection; - bool m_bLetToolsOverrideLoadGameEnts; // During a load game, this tells Foundry to override ents that are selected in Hammer. - bool m_bSplitScreenConnect; - bool m_bGameHasShutDownAndFlushedMemory; // This is false once we load a map into memory, and set to true once the map is unloaded - // and all memory flushed - bool m_bWorkshopMapDownloadPending; - }; - - extern CHostState* g_pHostState; -} // namespace R2 diff --git a/NorthstarDLL/engine/r2engine.h b/NorthstarDLL/engine/r2engine.h deleted file mode 100644 index df0cda74..00000000 --- a/NorthstarDLL/engine/r2engine.h +++ /dev/null @@ -1,264 +0,0 @@ -#pragma once -#include "shared/keyvalues.h" - -// use the R2 namespace for game funcs -namespace R2 -{ - // Cbuf - enum class ECommandTarget_t - { - CBUF_FIRST_PLAYER = 0, - CBUF_LAST_PLAYER = 1, // MAX_SPLITSCREEN_CLIENTS - 1, MAX_SPLITSCREEN_CLIENTS = 2 - CBUF_SERVER = CBUF_LAST_PLAYER + 1, - - CBUF_COUNT, - }; - - enum class cmd_source_t - { - // Added to the console buffer by gameplay code. Generally unrestricted. - kCommandSrcCode, - - // Sent from code via engine->ClientCmd, which is restricted to commands visible - // via FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS. - kCommandSrcClientCmd, - - // Typed in at the console or via a user key-bind. Generally unrestricted, although - // the client will throttle commands sent to the server this way to 16 per second. - kCommandSrcUserInput, - - // Came in over a net connection as a clc_stringcmd - // host_client will be valid during this state. - // - // Restricted to FCVAR_GAMEDLL commands (but not convars) and special non-ConCommand - // server commands hardcoded into gameplay code (e.g. "joingame") - kCommandSrcNetClient, - - // Received from the server as the client - // - // Restricted to commands with FCVAR_SERVER_CAN_EXECUTE - kCommandSrcNetServer, - - // Being played back from a demo file - // - // Not currently restricted by convar flag, but some commands manually ignore calls - // from this source. FIXME: Should be heavily restricted as demo commands can come - // from untrusted sources. - kCommandSrcDemoFile, - - // Invalid value used when cleared - kCommandSrcInvalid = -1 - }; - - typedef ECommandTarget_t (*Cbuf_GetCurrentPlayerType)(); - extern Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer; - - typedef void (*Cbuf_AddTextType)(ECommandTarget_t eTarget, const char* text, cmd_source_t source); - extern Cbuf_AddTextType Cbuf_AddText; - - typedef void (*Cbuf_ExecuteType)(); - extern Cbuf_ExecuteType Cbuf_Execute; - - extern bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); - - // CEngine - - enum EngineQuitState - { - QUIT_NOTQUITTING = 0, - QUIT_TODESKTOP, - QUIT_RESTART - }; - - enum class EngineState_t - { - DLL_INACTIVE = 0, // no dll - DLL_ACTIVE, // engine is focused - DLL_CLOSE, // closing down dll - DLL_RESTART, // engine is shutting down but will restart right away - DLL_PAUSED, // engine is paused, can become active from this state - }; - - class CEngine - { - public: - virtual void unknown() = 0; // unsure if this is where - virtual bool Load(bool dedicated, const char* baseDir) = 0; - virtual void Unload() = 0; - virtual void SetNextState(EngineState_t iNextState) = 0; - virtual EngineState_t GetState() = 0; - virtual void Frame() = 0; - virtual double GetFrameTime() = 0; - virtual float GetCurTime() = 0; - - EngineQuitState m_nQuitting; - EngineState_t m_nDllState; - EngineState_t m_nNextDllState; - double m_flCurrentTime; - float m_flFrameTime; - double m_flPreviousTime; - float m_flFilteredTime; - float m_flMinFrameTime; // Expected duration of a frame, or zero if it is unlimited. - }; - - extern CEngine* g_pEngine; - - extern void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...); - -#pragma once - typedef enum - { - NA_NULL = 0, - NA_LOOPBACK, - NA_IP, - } netadrtype_t; - -#pragma pack(push, 1) - typedef struct netadr_s - { - netadrtype_t type; - unsigned char ip[16]; // IPv6 - // IPv4's 127.0.0.1 is [::ffff:127.0.0.1], that is: - // 00 00 00 00 00 00 00 00 00 00 FF FF 7F 00 00 01 - unsigned short port; - } netadr_t; -#pragma pack(pop) - -#pragma pack(push, 1) - typedef struct netpacket_s - { - netadr_t adr; // sender address - // int source; // received source - char unk[10]; - double received_time; - unsigned char* data; // pointer to raw packet data - void* message; // easy bitbuf data access // 'inpacket.message' etc etc (pointer) - char unk2[16]; - int size; - - // bf_read message; // easy bitbuf data access // 'inpacket.message' etc etc (pointer) - // int size; // size in bytes - // int wiresize; // size in bytes before decompression - // bool stream; // was send as stream - // struct netpacket_s* pNext; // for internal use, should be NULL in public - } netpacket_t; -#pragma pack(pop) - - // #56169 $DB69 PData size - // #512 $200 Trailing data - // #100 $64 Safety buffer - const int PERSISTENCE_MAX_SIZE = 0xDDCD; - - // note: NOT_READY and READY are the only entries we have here that are defined by the vanilla game - // entries after this are custom and used to determine the source of persistence, e.g. whether it is local or remote - enum class ePersistenceReady : char - { - NOT_READY, - READY = 3, - READY_INSECURE = 3, - READY_REMOTE - }; - - enum class eSignonState : int - { - NONE = 0, // no state yet; about to connect - CHALLENGE = 1, // client challenging server; all OOB packets - CONNECTED = 2, // client is connected to server; netchans ready - NEW = 3, // just got serverinfo and string tables - PRESPAWN = 4, // received signon buffers - GETTINGDATA = 5, // respawn-defined signonstate, assumedly this is for persistence - SPAWN = 6, // ready to receive entity packets - FIRSTSNAP = 7, // another respawn-defined one - FULL = 8, // we are fully connected; first non-delta packet received - CHANGELEVEL = 9, // server is changing level; please wait - }; - - // clang-format off - OFFSET_STRUCT(CBaseClient) - { - STRUCT_SIZE(0x2D728) - FIELD(0x16, char m_Name[64]) - FIELD(0x258, KeyValues* m_ConVars) - FIELD(0x2A0, eSignonState m_Signon) - FIELD(0x358, char m_ClanTag[16]) - FIELD(0x484, bool m_bFakePlayer) - FIELD(0x4A0, ePersistenceReady m_iPersistenceReady) - FIELD(0x4FA, char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE]) - FIELD(0xF500, char m_UID[32]) - }; - // clang-format on - - extern CBaseClient* g_pClientArray; - - enum server_state_t - { - ss_dead = 0, // Dead - ss_loading, // Spawning - ss_active, // Running - ss_paused, // Running, but paused - }; - - extern server_state_t* g_pServerState; - - extern char* g_pModName; - - // clang-format off - OFFSET_STRUCT(CGlobalVars) - { - FIELD(0x0, - // Absolute time (per frame still - Use Plat_FloatTime() for a high precision real time - // perf clock, but not that it doesn't obey host_timescale/host_framerate) - double m_flRealTime); - - FIELDS(0x8, - // Absolute frame counter - continues to increase even if game is paused - int m_nFrameCount; - - // Non-paused frametime - float m_flAbsoluteFrameTime; - - // Current time - // - // On the client, this (along with tickcount) takes a different meaning based on what - // piece of code you're in: - // - // - While receiving network packets (like in PreDataUpdate/PostDataUpdate and proxies), - // this is set to the SERVER TICKCOUNT for that packet. There is no interval between - // the server ticks. - // [server_current_Tick * tick_interval] - // - // - While rendering, this is the exact client clock - // [client_current_tick * tick_interval + interpolation_amount] - // - // - During prediction, this is based on the client's current tick: - // [client_current_tick * tick_interval] - float m_flCurTime; - ) - - FIELDS(0x30, - // Time spent on last server or client frame (has nothing to do with think intervals) - float m_flFrameTime; - - // current maxplayers setting - int m_nMaxClients; - ) - - FIELDS(0x3C, - // Simulation ticks - does not increase when game is paused - uint32_t m_nTickCount; // this is weird and doesn't seem to increase once per frame? - - // Simulation tick interval - float m_flTickInterval; - ) - - FIELDS(0x60, - const char* m_pMapName; - int m_nMapVersion; - ) - - //FIELD(0x98, double m_flRealTime); // again? - }; - // clang-format on - - extern CGlobalVars* g_pGlobals; -} // namespace R2 diff --git a/NorthstarDLL/plugins/plugin_abi.h b/NorthstarDLL/plugins/plugin_abi.h deleted file mode 100644 index 16b26a1c..00000000 --- a/NorthstarDLL/plugins/plugin_abi.h +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once -#include "squirrel/squirrelclasstypes.h" - -#define ABI_VERSION 3 - -enum PluginLoadDLL -{ - ENGINE = 0, - CLIENT, - SERVER -}; - -enum ObjectType -{ - CONCOMMANDS = 0, - CONVAR = 1, -}; - -struct SquirrelFunctions -{ - RegisterSquirrelFuncType RegisterSquirrelFunc; - sq_defconstType __sq_defconst; - - sq_compilebufferType __sq_compilebuffer; - sq_callType __sq_call; - sq_raiseerrorType __sq_raiseerror; - sq_compilefileType __sq_compilefile; - - sq_newarrayType __sq_newarray; - sq_arrayappendType __sq_arrayappend; - - sq_newtableType __sq_newtable; - sq_newslotType __sq_newslot; - - sq_pushroottableType __sq_pushroottable; - sq_pushstringType __sq_pushstring; - sq_pushintegerType __sq_pushinteger; - sq_pushfloatType __sq_pushfloat; - sq_pushboolType __sq_pushbool; - sq_pushassetType __sq_pushasset; - sq_pushvectorType __sq_pushvector; - sq_pushobjectType __sq_pushobject; - - sq_getstringType __sq_getstring; - sq_getintegerType __sq_getinteger; - sq_getfloatType __sq_getfloat; - sq_getboolType __sq_getbool; - sq_getType __sq_get; - sq_getassetType __sq_getasset; - sq_getuserdataType __sq_getuserdata; - sq_getvectorType __sq_getvector; - sq_getthisentityType __sq_getthisentity; - sq_getobjectType __sq_getobject; - - sq_stackinfosType __sq_stackinfos; - - sq_createuserdataType __sq_createuserdata; - sq_setuserdatatypeidType __sq_setuserdatatypeid; - sq_getfunctionType __sq_getfunction; - - sq_schedule_call_externalType __sq_schedule_call_external; - - sq_getentityfrominstanceType __sq_getentityfrominstance; - sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity; - - sq_pushnewstructinstanceType __sq_pushnewstructinstance; - sq_sealstructslotType __sq_sealstructslot; -}; - -struct MessageSource -{ - const char* file; - const char* func; - int line; -}; - -// This is a modified version of spdlog::details::log_msg -// This is so that we can make it cross DLL boundaries -struct LogMsg -{ - int level; - uint64_t timestamp; - const char* msg; - MessageSource source; - int pluginHandle; -}; - -extern "C" -{ - typedef void (*loggerfunc_t)(LogMsg* msg); - typedef void (*PLUGIN_RELAY_INVITE_TYPE)(const char* invite); - typedef void* (*CreateObjectFunc)(ObjectType type); - - typedef void (*PluginFnCommandCallback_t)(void* command); - typedef void (*PluginConCommandConstructorType)( - void* newCommand, const char* name, PluginFnCommandCallback_t callback, const char* helpString, int flags, void* parent); - typedef void (*PluginConVarRegisterType)( - void* pConVar, - const char* pszName, - const char* pszDefaultValue, - int nFlags, - const char* pszHelpString, - bool bMin, - float fMin, - bool bMax, - float fMax, - void* pCallback); - typedef void (*PluginConVarMallocType)(void* pConVarMaloc, int a2, int a3); -} - -struct PluginNorthstarData -{ - const char* version; - HMODULE northstarModule; - int pluginHandle; -}; - -struct PluginInitFuncs -{ - loggerfunc_t logger; - PLUGIN_RELAY_INVITE_TYPE relayInviteFunc; - CreateObjectFunc createObject; -}; - -struct PluginEngineData -{ - PluginConCommandConstructorType ConCommandConstructor; - PluginConVarMallocType conVarMalloc; - PluginConVarRegisterType conVarRegister; - void* ConVar_Vtable; - void* IConVar_Vtable; - void* g_pCVar; -}; - -/// Async communication within the plugin system -/// Due to the asynchronous nature of plugins, combined with the limitations of multi-compiler support -/// and the custom memory allocator used by r2, is it difficult to safely get data across DLL boundaries -/// from Northstar to plugin unless Northstar can own that memory. -/// This means that plugins should manage their own memory and can only receive data from northstar using one of the functions below. -/// These should be exports of the plugin DLL. If they are not exported, they will not be called. -/// Note that it is not required to have these exports if you do not use them. -/// - -// Northstar -> Plugin -typedef void (*PLUGIN_INIT_TYPE)(PluginInitFuncs* funcs, PluginNorthstarData* data); -typedef void (*PLUGIN_INIT_SQVM_TYPE)(SquirrelFunctions* funcs); -typedef void (*PLUGIN_INFORM_SQVM_CREATED_TYPE)(ScriptContext context, CSquirrelVM* sqvm); -typedef void (*PLUGIN_INFORM_SQVM_DESTROYED_TYPE)(ScriptContext context); - -typedef void (*PLUGIN_INFORM_DLL_LOAD_TYPE)(const char* dll, PluginEngineData* data, void* dllPtr); -typedef void (*PLUGIN_RUNFRAME)(); diff --git a/NorthstarDLL/plugins/pluginbackend.cpp b/NorthstarDLL/plugins/pluginbackend.cpp deleted file mode 100644 index 850394ac..00000000 --- a/NorthstarDLL/plugins/pluginbackend.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "pluginbackend.h" -#include "plugin_abi.h" -#include "server/serverpresence.h" -#include "masterserver/masterserver.h" -#include "squirrel/squirrel.h" -#include "plugins.h" - -#include "core/convar/concommand.h" - -#include - -#define EXPORT extern "C" __declspec(dllexport) - -AUTOHOOK_INIT() - -PluginCommunicationHandler* g_pPluginCommunicationhandler; - -static PluginDataRequest storedRequest {PluginDataRequestType::END, (PluginRespondDataCallable) nullptr}; - -void PluginCommunicationHandler::RunFrame() -{ - std::lock_guard lock(requestMutex); - if (!requestQueue.empty()) - { - storedRequest = requestQueue.front(); - switch (storedRequest.type) - { - default: - spdlog::error("{} was called with invalid request type '{}'", __FUNCTION__, static_cast(storedRequest.type)); - } - requestQueue.pop(); - } -} - -void PluginCommunicationHandler::PushRequest(PluginDataRequestType type, PluginRespondDataCallable func) -{ - std::lock_guard lock(requestMutex); - requestQueue.push(PluginDataRequest {type, func}); -} - -void InformPluginsDLLLoad(fs::path dllPath, void* address) -{ - std::string dllName = dllPath.filename().string(); - - void* data = NULL; - if (strncmp(dllName.c_str(), "engine.dll", 10) == 0) - data = &g_pPluginCommunicationhandler->m_sEngineData; - - g_pPluginManager->InformDLLLoad(dllName.c_str(), data, address); -} diff --git a/NorthstarDLL/plugins/pluginbackend.h b/NorthstarDLL/plugins/pluginbackend.h deleted file mode 100644 index fc66a597..00000000 --- a/NorthstarDLL/plugins/pluginbackend.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include "plugin_abi.h" - -#include -#include - -enum PluginDataRequestType -{ - END = 0, -}; - -union PluginRespondDataCallable -{ - // Empty for now - void* UNUSED; -}; - -class PluginDataRequest -{ - public: - PluginDataRequestType type; - PluginRespondDataCallable func; - PluginDataRequest(PluginDataRequestType type, PluginRespondDataCallable func) : type(type), func(func) {} -}; - -class PluginCommunicationHandler -{ - public: - void RunFrame(); - void PushRequest(PluginDataRequestType type, PluginRespondDataCallable func); - - public: - std::queue requestQueue = {}; - std::mutex requestMutex; - - PluginEngineData m_sEngineData {}; -}; - -void InformPluginsDLLLoad(fs::path dllPath, void* address); -extern PluginCommunicationHandler* g_pPluginCommunicationhandler; diff --git a/NorthstarDLL/plugins/plugins.cpp b/NorthstarDLL/plugins/plugins.cpp deleted file mode 100644 index 121512e5..00000000 --- a/NorthstarDLL/plugins/plugins.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "plugins.h" -#include "config/profile.h" - -#include "squirrel/squirrel.h" -#include "plugins.h" -#include "masterserver/masterserver.h" -#include "core/convar/convar.h" -#include "server/serverpresence.h" -#include -#include - -#include "util/version.h" -#include "pluginbackend.h" -#include "util/wininfo.h" -#include "logging/logging.h" -#include "dedicated/dedicated.h" - -PluginManager* g_pPluginManager; - -void freeLibrary(HMODULE hLib) -{ - if (!FreeLibrary(hLib)) - { - spdlog::error("There was an error while trying to free library"); - } -} - -EXPORT void PLUGIN_LOG(LogMsg* msg) -{ - spdlog::source_loc src {}; - src.filename = msg->source.file; - src.funcname = msg->source.func; - src.line = msg->source.line; - auto&& logger = g_pPluginManager->m_vLoadedPlugins[msg->pluginHandle].logger; - logger->log(src, (spdlog::level::level_enum)msg->level, msg->msg); -} - -EXPORT void* CreateObject(ObjectType type) -{ - switch (type) - { - case ObjectType::CONVAR: - return (void*)new ConVar; - case ObjectType::CONCOMMANDS: - return (void*)new ConCommand; - default: - return NULL; - } -} - -std::optional PluginManager::LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data) -{ - - Plugin plugin {}; - - std::string pathstring = path.string(); - std::wstring wpath = path.wstring(); - - LPCWSTR wpptr = wpath.c_str(); - HMODULE datafile = LoadLibraryExW(wpptr, 0, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); // Load the DLL as a data file - if (datafile == NULL) - { - NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError())); - return std::nullopt; - } - HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCEW(IDR_RCDATA1), RT_RCDATA); - - if (manifestResource == NULL) - { - NS::log::PLUGINSYS->info("Could not find manifest for library '{}'", pathstring); - freeLibrary(datafile); - return std::nullopt; - } - HGLOBAL myResourceData = LoadResource(datafile, manifestResource); - if (myResourceData == NULL) - { - NS::log::PLUGINSYS->error("Failed to load manifest from library '{}'", pathstring); - freeLibrary(datafile); - return std::nullopt; - } - int manifestSize = SizeofResource(datafile, manifestResource); - std::string manifest = std::string((const char*)LockResource(myResourceData), 0, manifestSize); - freeLibrary(datafile); - - rapidjson_document manifestJSON; - manifestJSON.Parse(manifest.c_str()); - - if (manifestJSON.HasParseError()) - { - NS::log::PLUGINSYS->error("Manifest for '{}' was invalid", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("name")) - { - NS::log::PLUGINSYS->error("'{}' is missing a name in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("displayname")) - { - NS::log::PLUGINSYS->error("'{}' is missing a displayname in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("description")) - { - NS::log::PLUGINSYS->error("'{}' is missing a description in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("api_version")) - { - NS::log::PLUGINSYS->error("'{}' is missing a api_version in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("version")) - { - NS::log::PLUGINSYS->error("'{}' is missing a version in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("run_on_server")) - { - NS::log::PLUGINSYS->error("'{}' is missing 'run_on_server' in its manifest", pathstring); - return std::nullopt; - } - if (!manifestJSON.HasMember("run_on_client")) - { - NS::log::PLUGINSYS->error("'{}' is missing 'run_on_client' in its manifest", pathstring); - return std::nullopt; - } - auto test = manifestJSON["api_version"].GetString(); - if (strcmp(manifestJSON["api_version"].GetString(), std::to_string(ABI_VERSION).c_str())) - { - NS::log::PLUGINSYS->error( - "'{}' has an incompatible API version number in its manifest. Current ABI version is '{}'", pathstring, ABI_VERSION); - return std::nullopt; - } - // Passed all checks, going to actually load it now - - HMODULE pluginLib = LoadLibraryW(wpptr); // Load the DLL as a data file - if (pluginLib == NULL) - { - NS::log::PLUGINSYS->info("Failed to load library '{}': ", std::system_category().message(GetLastError())); - return std::nullopt; - } - plugin.init = (PLUGIN_INIT_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT"); - if (plugin.init == NULL) - { - NS::log::PLUGINSYS->info("Library '{}' has no function 'PLUGIN_INIT'", pathstring); - return std::nullopt; - } - NS::log::PLUGINSYS->info("Succesfully loaded {}", pathstring); - - plugin.name = manifestJSON["name"].GetString(); - plugin.displayName = manifestJSON["displayname"].GetString(); - plugin.description = manifestJSON["description"].GetString(); - plugin.api_version = manifestJSON["api_version"].GetString(); - plugin.version = manifestJSON["version"].GetString(); - - plugin.run_on_client = manifestJSON["run_on_client"].GetBool(); - plugin.run_on_server = manifestJSON["run_on_server"].GetBool(); - - if (!plugin.run_on_server && IsDedicatedServer()) - return std::nullopt; - - if (manifestJSON.HasMember("dependencyName")) - { - plugin.dependencyName = manifestJSON["dependencyName"].GetString(); - } - else - { - plugin.dependencyName = plugin.name; - } - - plugin.init_sqvm_client = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_CLIENT"); - plugin.init_sqvm_server = (PLUGIN_INIT_SQVM_TYPE)GetProcAddress(pluginLib, "PLUGIN_INIT_SQVM_SERVER"); - plugin.inform_sqvm_created = (PLUGIN_INFORM_SQVM_CREATED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_CREATED"); - plugin.inform_sqvm_destroyed = (PLUGIN_INFORM_SQVM_DESTROYED_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_SQVM_DESTROYED"); - - plugin.inform_dll_load = (PLUGIN_INFORM_DLL_LOAD_TYPE)GetProcAddress(pluginLib, "PLUGIN_INFORM_DLL_LOAD"); - - plugin.run_frame = (PLUGIN_RUNFRAME)GetProcAddress(pluginLib, "PLUGIN_RUNFRAME"); - - plugin.handle = m_vLoadedPlugins.size(); - plugin.logger = std::make_shared(plugin.displayName.c_str(), NS::Colors::PLUGIN); - RegisterLogger(plugin.logger); - NS::log::PLUGINSYS->info("Loading plugin {} version {}", plugin.displayName, plugin.version); - m_vLoadedPlugins.push_back(plugin); - - plugin.init(funcs, data); - - return plugin; -} - -inline void FindPlugins(fs::path pluginPath, std::vector& paths) -{ - // ensure dirs exist - if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath)) - { - return; - } - - for (const fs::directory_entry& entry : fs::recursive_directory_iterator(pluginPath)) - { - if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") - paths.emplace_back(entry.path()); - } -} - -bool PluginManager::LoadPlugins() -{ - if (strstr(GetCommandLineA(), "-noplugins") != NULL) - { - NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins"); - return false; - } - - fs::create_directories(GetThunderstoreModFolderPath()); - - std::vector paths; - - pluginPath = GetNorthstarPrefix() + "\\plugins"; - - PluginNorthstarData data {}; - std::string ns_version {version}; - - PluginInitFuncs funcs {}; - funcs.logger = PLUGIN_LOG; - funcs.relayInviteFunc = nullptr; - funcs.createObject = CreateObject; - - data.version = ns_version.c_str(); - data.northstarModule = g_NorthstarModule; - - FindPlugins(pluginPath, paths); - - // Special case for Thunderstore mods dir - std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath()); - // Set up regex for `AUTHOR-MOD-VERSION` pattern - std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))"); - for (fs::directory_entry dir : thunderstoreModsDir) - { - fs::path pluginsDir = dir.path() / "plugins"; - // Use regex to match `AUTHOR-MOD-VERSION` pattern - if (!std::regex_match(dir.path().string(), pattern)) - { - spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string()); - continue; // skip loading package that doesn't match - } - FindPlugins(pluginsDir, paths); - } - - if (paths.empty()) - { - NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins"); - return false; - } - - for (fs::path path : paths) - { - if (LoadPlugin(path, &funcs, &data)) - data.pluginHandle += 1; - } - return true; -} - -void PluginManager::InformSQVMLoad(ScriptContext context, SquirrelFunctions* s) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (context == ScriptContext::CLIENT && plugin.init_sqvm_client != NULL) - { - plugin.init_sqvm_client(s); - } - else if (context == ScriptContext::SERVER && plugin.init_sqvm_server != NULL) - { - plugin.init_sqvm_server(s); - } - } -} - -void PluginManager::InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.inform_sqvm_created != NULL) - { - plugin.inform_sqvm_created(context, sqvm); - } - } -} - -void PluginManager::InformSQVMDestroyed(ScriptContext context) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.inform_sqvm_destroyed != NULL) - { - plugin.inform_sqvm_destroyed(context); - } - } -} - -void PluginManager::InformDLLLoad(const char* dll, void* data, void* dllPtr) -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.inform_dll_load != NULL) - { - plugin.inform_dll_load(dll, (PluginEngineData*)data, dllPtr); - } - } -} - -void PluginManager::RunFrame() -{ - for (auto plugin : m_vLoadedPlugins) - { - if (plugin.run_frame != NULL) - { - plugin.run_frame(); - } - } -} diff --git a/NorthstarDLL/plugins/plugins.h b/NorthstarDLL/plugins/plugins.h deleted file mode 100644 index d91b2811..00000000 --- a/NorthstarDLL/plugins/plugins.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include "plugin_abi.h" - -const int IDR_RCDATA1 = 101; - -class Plugin -{ - public: - std::string name; - std::string displayName; - std::string dependencyName; - std::string description; - - std::string api_version; - std::string version; - - // For now this is just implemented as the index into the plugins array - // Maybe a bit shit but it works - int handle; - - std::shared_ptr logger; - - bool run_on_client = false; - bool run_on_server = false; - - public: - PLUGIN_INIT_TYPE init; - PLUGIN_INIT_SQVM_TYPE init_sqvm_client; - PLUGIN_INIT_SQVM_TYPE init_sqvm_server; - PLUGIN_INFORM_SQVM_CREATED_TYPE inform_sqvm_created; - PLUGIN_INFORM_SQVM_DESTROYED_TYPE inform_sqvm_destroyed; - - PLUGIN_INFORM_DLL_LOAD_TYPE inform_dll_load; - - PLUGIN_RUNFRAME run_frame; -}; - -class PluginManager -{ - public: - std::vector m_vLoadedPlugins; - - public: - bool LoadPlugins(); - std::optional LoadPlugin(fs::path path, PluginInitFuncs* funcs, PluginNorthstarData* data); - - void InformSQVMLoad(ScriptContext context, SquirrelFunctions* s); - void InformSQVMCreated(ScriptContext context, CSquirrelVM* sqvm); - void InformSQVMDestroyed(ScriptContext context); - - void InformDLLLoad(const char* dll, void* data, void* dllPtr); - - void RunFrame(); - - private: - std::string pluginPath; -}; - -extern PluginManager* g_pPluginManager; diff --git a/NorthstarDLL/server/r2server.h b/NorthstarDLL/server/r2server.h deleted file mode 100644 index 8fde7b9d..00000000 --- a/NorthstarDLL/server/r2server.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include "core/math/vector.h" - -// use the R2 namespace for game funcs -namespace R2 -{ - // server entity stuff - class CBaseEntity; - extern CBaseEntity* (*Server_GetEntityByIndex)(int index); - - // clang-format off - OFFSET_STRUCT(CBasePlayer) - { - FIELD(0x58, uint32_t m_nPlayerIndex) - - FIELD(0x23E8, bool m_grappleActive) - FIELD(0x1D08, uint32_t m_platformUserId) - FIELD(0x1D10, int32_t m_classModsActive) - FIELD(0x1D8C, int32_t m_posClassModsActive) - FIELD(0x1DCC, bool m_passives) - FIELD(0x4948, int32_t m_selectedOffhand) - FIELD(0x1358, int32_t m_selectedOffhandPendingHybridAction) - FIELD(0x1E88, int32_t m_playerFlags) - FIELD(0x26A8, int32_t m_lastUCmdSimulationTicks) - FIELD(0x26AC, float m_lastUCmdSimulationRemainderTime) - FIELD(0x1F04, int32_t m_remoteTurret) - FIELD(0x414, int32_t m_hGroundEntity) - FIELD(0x13B8, int32_t m_titanSoul) - FIELD(0x2054, int32_t m_petTitan) - FIELD(0x4D4, int32_t m_iHealth) - FIELD(0x4D0, int32_t m_iMaxHealth) - FIELD(0x4F1, int32_t m_lifeState) - FIELD(0x50C, float m_flMaxspeed) - FIELD(0x298, int32_t m_fFlags) - FIELD(0x1F64, int32_t m_iObserverMode) - FIELD(0x1F6C, int32_t m_hObserverTarget) - FIELD(0x2098, int32_t m_hViewModel) - FIELD(0x27E4, int32_t m_ubEFNointerpParity) - FIELD(0x1FA4, int32_t m_activeBurnCardIndex) - FIELD(0x1B68, int32_t m_hColorCorrectionCtrl) - FIELD(0x19E0, int32_t m_PlayerFog__m_hCtrl) - FIELD(0x26BC, bool m_bShouldDrawPlayerWhileUsingViewEntity) - FIELD(0x2848, char m_title[32]) - FIELD(0x2964, bool m_useCredit) - FIELD(0x1F40, float m_damageImpulseNoDecelEndTime) - FIELD(0x1E8C, bool m_hasMic) - FIELD(0x1E8D, bool m_inPartyChat) - FIELD(0x1E90, float m_playerMoveSpeedScale) - FIELD(0x1F58, float m_flDeathTime) - FIELD(0x25A8, bool m_iSpawnParity) - FIELD(0x102284, Vector3 m_upDir) - FIELD(0x259C, float m_lastDodgeTime) - FIELD(0x22E0, bool m_wallHanging) - FIELD(0x22EC, int32_t m_traversalType) - FIELD(0x22F0, int32_t m_traversalState) - FIELD(0x2328, Vector3 m_traversalRefPos) - FIELD(0x231C, Vector3 m_traversalForwardDir) - FIELD(0x2354, float m_traversalYawDelta) - FIELD(0x2358, int32_t m_traversalYawPoseParameter) - FIELD(0x2050, int32_t m_grappleHook) - FIELD(0x27C0, int32_t m_autoSprintForced) - FIELD(0x27C4, bool m_fIsSprinting) - FIELD(0x27CC, float m_sprintStartedTime) - FIELD(0x27D0, float m_sprintStartedFrac) - FIELD(0x27D4, float m_sprintEndedTime) - FIELD(0x27D8, float m_sprintEndedFrac) - FIELD(0x27DC, float m_stickySprintStartTime) - FIELD(0x2998, float m_smartAmmoPreviousHighestLockOnMeFractionValue) - FIELD(0x23FC, int32_t m_activeZipline) - FIELD(0x2400, bool m_ziplineReverse) - FIELD(0x2410, int32_t m_ziplineState) - FIELD(0x2250, int32_t m_duckState) - FIELD(0x2254, Vector3 m_StandHullMin) - FIELD(0x2260, Vector3 m_StandHullMax) - FIELD(0x226C, Vector3 m_DuckHullMin) - FIELD(0x2278, Vector3 m_DuckHullMax) - FIELD(0x205C, int32_t m_xp) - FIELD(0x2060, int32_t m_generation) - FIELD(0x2064, int32_t m_rank) - FIELD(0x2068, int32_t m_serverForceIncreasePlayerListGenerationParity) - FIELD(0x206C, bool m_isPlayingRanked) - FIELD(0x2070, float m_skill_mu) - FIELD(0x1E80, int32_t m_titanSoulBeingRodeoed) - FIELD(0x1E84, int32_t m_entitySyncingWithMe) - FIELD(0x2078, float m_nextTitanRespawnAvailable) - FIELD(0x1C90, bool m_hasBadReputation) - FIELD(0x1C91, char m_communityName[64]) - FIELD(0x1CD1, char m_communityClanTag[16]) - FIELD(0x1CE1, char m_factionName[16]) - FIELD(0x1CF1, char m_hardwareIcon[16]) - FIELD(0x1D01, bool m_happyHourActive) - FIELD(0x1EF4, int32_t m_gestureAutoKillBitfield) - FIELD(0x2EA8, int32_t m_pilotClassIndex) - FIELD(0x100490, Vector3 m_vecAbsOrigin) - FIELD(0x25BE, bool m_isPerformingBoostAction) - FIELD(0x240C, bool m_ziplineValid3pWeaponLayerAnim) - FIELD(0x345C, int32_t m_playerScriptNetDataGlobal) - FIELD(0x1598, int32_t m_bZooming) - FIELD(0x1599, bool m_zoomToggleOn) - FIELD(0x159C, float m_zoomBaseFrac) - FIELD(0x15A0, float m_zoomBaseTime) - FIELD(0x15A4, float m_zoomFullStartTime) - FIELD(0xA04, int32_t m_camoIndex) - FIELD(0xA08, int32_t m_decalIndex) - }; - // clang-format on - - extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); -} // namespace R2 diff --git a/NorthstarDLL/shared/maxplayers.h b/NorthstarDLL/shared/maxplayers.h deleted file mode 100644 index b251f6a6..00000000 --- a/NorthstarDLL/shared/maxplayers.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -// should we use R2 for this? not sure -namespace R2 // use R2 namespace for game funcs -{ - int GetMaxPlayers(); -} // namespace R2 diff --git a/NorthstarDLL/util/utils.h b/NorthstarDLL/util/utils.h deleted file mode 100644 index 97b92f18..00000000 --- a/NorthstarDLL/util/utils.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace NS::Utils -{ - void RemoveAsciiControlSequences(char* str, bool allow_color_codes); -} diff --git a/NorthstarLauncher/CMakeLists.txt b/NorthstarLauncher/CMakeLists.txt deleted file mode 100644 index f4d7bcb9..00000000 --- a/NorthstarLauncher/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# NorthstarLauncher - -add_executable(NorthstarLauncher - "main.cpp" - "resources.rc" -) - -target_compile_definitions(NorthstarLauncher PRIVATE - UNICODE - _UNICODE -) - -target_link_libraries(NorthstarLauncher PRIVATE - shlwapi.lib - kernel32.lib - user32.lib - gdi32.lib - winspool.lib - comdlg32.lib - advapi32.lib - shell32.lib - ole32.lib - oleaut32.lib - uuid.lib - odbc32.lib - odbccp32.lib - WS2_32.lib -) - -set_target_properties(NorthstarLauncher PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR} - LINK_FLAGS "/MANIFEST:NO /DEBUG /STACK:8000000" -) diff --git a/README.md b/README.md index f789b486..80be8fd0 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ Check [BUILD.md](BUILD.md) for instructions on how to compile, you can also down ## Format -This project uses [clang-format](https://clang.llvm.org/docs/ClangFormat.html), make sure you run `clang-format -i --style=file NorthstarLauncher/*.cpp NorthstarLauncher/*.h NorthstarDLL/*.cpp NorthstarDLL/*.h` when opening a Pull Request. Check the tool's website for instructions on how to integrate it with your IDE. +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/cmake/Findlibcurl.cmake b/cmake/Findlibcurl.cmake deleted file mode 100644 index a6f0b47c..00000000 --- a/cmake/Findlibcurl.cmake +++ /dev/null @@ -1,18 +0,0 @@ - - -if (NOT libcurl_FOUND) - check_init_submodule(${PROJECT_SOURCE_DIR}/thirdparty/libcurl) - - set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries") - set(BUILD_CURL_EXE OFF CACHE BOOL "Build curl EXE") - set(HTTP_ONLY ON CACHE BOOL "Only build HTTP and HTTPS") - set(CURL_ENABLE_SSL ON CACHE BOOL "Enable SSL support") - set(CURL_USE_OPENSSL OFF CACHE BOOL "Disable OpenSSL") - set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Disable libSSH2") - set(CURL_USE_SCHANNEL ON CACHE BOOL "Enable Secure Channel") - set(CURL_CA_BUNDLE "none" CACHE STRING "Disable CA Bundle") - set(CURL_CA_PATH "none" CACHE STRING "Disable CA Path") - - add_subdirectory(${PROJECT_SOURCE_DIR}/thirdparty/libcurl libcurl) - set(libcurl_FOUND 1 PARENT_SCOPE) -endif() diff --git a/cmake/Findminhook.cmake b/cmake/Findminhook.cmake deleted file mode 100644 index 8ec2e99a..00000000 --- a/cmake/Findminhook.cmake +++ /dev/null @@ -1,8 +0,0 @@ - -if(NOT minhook_FOUND) - check_init_submodule(${PROJECT_SOURCE_DIR}/thirdparty/minhook) - - add_subdirectory(${PROJECT_SOURCE_DIR}/thirdparty/minhook minhook) - set(minhook_FOUND 1 PARENT_SCOPE) -endif() - diff --git a/cmake/Findminizip.cmake b/cmake/Findminizip.cmake deleted file mode 100644 index 17489061..00000000 --- a/cmake/Findminizip.cmake +++ /dev/null @@ -1,10 +0,0 @@ - -if(NOT minizip_FOUND) - check_init_submodule(${PROJECT_SOURCE_DIR}/thirdparty/minizip) - - set(MZ_LZMA OFF CACHE BOOL "Disable LZMA & XZ compression") - - add_subdirectory(${PROJECT_SOURCE_DIR}/thirdparty/minizip minizip) - set(minizip_FOUND 1 PARENT_SCOPE) -endif() - diff --git a/cmake/Findspdlog.cmake b/cmake/Findspdlog.cmake deleted file mode 100644 index 38b52a53..00000000 --- a/cmake/Findspdlog.cmake +++ /dev/null @@ -1,8 +0,0 @@ - -if(NOT spdlog_FOUND) - check_init_submodule(${PROJECT_SOURCE_DIR}/thirdparty/spdlog) - - add_subdirectory(${PROJECT_SOURCE_DIR}/thirdparty/spdlog spdlog) - set(spdlog_FOUND 1 PARENT_SCOPE) -endif() - diff --git a/cmake/utils.cmake b/cmake/utils.cmake deleted file mode 100644 index d8450551..00000000 --- a/cmake/utils.cmake +++ /dev/null @@ -1,25 +0,0 @@ - -# Check if a dependency exist before trying to init git submodules -function(check_init_submodule path) - file(GLOB DIR_CONTENT "${path}/*") - list(LENGTH DIR_CONTENT CONTENT_COUNT) - if (CONTENT_COUNT EQUAL 0) - if (NOT EXISTS "${PROJECT_SOURCE_DIR}/.git") - message(FATAL_ERROR "Failed to find third party dependency in '${path}'") - endif() - - find_package(Git QUIET) - if (NOT Git_FOUND) - message(FATAL_ERROR "Failed to find Git, third party dependency could not be setup at `${path}") - endif() - - message(STATUS "Setting up dependencies as git submodules") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - - if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "Initializing Git submodules failed with ${GIT_SUBMOD_RESULT}") - endif() - endif() -endfunction() diff --git a/loader_wsock32_proxy/CMakeLists.txt b/loader_wsock32_proxy/CMakeLists.txt deleted file mode 100644 index 3157f6c5..00000000 --- a/loader_wsock32_proxy/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# loader_wsock32_proxy - -find_package(minhook REQUIRED) - -add_library(loader_wsock32_proxy SHARED - "dllmain.cpp" - "loader.cpp" - "loader.h" - "wsock32.asm" - "wsock32.def" -) - -target_link_libraries(loader_wsock32_proxy PRIVATE - minhook - mswsock.lib - ws2_32.lib - ShLwApi.lib - imagehlp.lib - dbghelp.lib - kernel32.lib - user32.lib - gdi32.lib - winspool.lib - comdlg32.lib - advapi32.lib - shell32.lib - ole32.lib - oleaut32.lib - uuid.lib - odbc32.lib - odbccp32.lib -) - -target_precompile_headers(loader_wsock32_proxy PRIVATE pch.h) - -target_compile_definitions(loader_wsock32_proxy PRIVATE - UNICODE - _UNICODE -) - -set_target_properties(loader_wsock32_proxy PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR}/bin/x64_retail - OUTPUT_NAME wsock32 - LINK_FLAGS "/MANIFEST:NO /DEBUG" -) diff --git a/loader_wsock32_proxy/dllmain.cpp b/loader_wsock32_proxy/dllmain.cpp deleted file mode 100644 index 4cc4f26e..00000000 --- a/loader_wsock32_proxy/dllmain.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "loader.h" - -#include -#include - -HINSTANCE hLThis = 0; -FARPROC p[857]; -HINSTANCE hL = 0; - -bool GetExePathWide(wchar_t* dest, DWORD destSize) -{ - if (!dest) - return NULL; - if (destSize < MAX_PATH) - return NULL; - - DWORD length = GetModuleFileNameW(NULL, dest, destSize); - return length && PathRemoveFileSpecW(dest); -} - -wchar_t exePath[4096]; -wchar_t buffer1[8192]; -wchar_t buffer2[12288]; - -BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) -{ - if (reason == DLL_PROCESS_ATTACH) - { - hLThis = hInst; - - if (!GetExePathWide(exePath, 4096)) - { - MessageBoxA( - GetForegroundWindow(), - "Failed getting game directory.\nThe game cannot continue and has to exit.", - "Northstar Wsock32 Proxy Error", - 0); - return true; - } - - SetCurrentDirectoryW(exePath); - - if (!ProvisionNorthstar()) // does not call InitialiseNorthstar yet, will do it on LauncherMain hook - return true; - - // copy the original library for system to our local directory, with changed name so that we can load it - swprintf_s(buffer1, L"%s\\bin\\x64_retail\\wsock32.org.dll", exePath); - GetSystemDirectoryW(buffer2, 4096); - swprintf_s(buffer2, L"%s\\wsock32.dll", buffer2); - try - { - std::filesystem::copy_file(buffer2, buffer1); - } - catch (const std::exception& e1) - { - if (!std::filesystem::exists(buffer1)) - { - // fallback by copying to temp dir... - // because apparently games installed by EA Desktop app don't have write permissions in their directories - auto temp_dir = std::filesystem::temp_directory_path() / L"wsock32.org.dll"; - try - { - std::filesystem::copy_file(buffer2, temp_dir); - } - catch (const std::exception& e2) - { - if (!std::filesystem::exists(temp_dir)) - { - swprintf_s( - buffer2, - L"Failed copying wsock32.dll from system32 to \"%s\"\n\n%S\n\nFurthermore, we failed copying wsock32.dll into " - L"temporary directory at \"%s\"\n\n%S", - buffer1, - e1.what(), - temp_dir.c_str(), - e2.what()); - MessageBoxW(GetForegroundWindow(), buffer2, L"Northstar Wsock32 Proxy Error", 0); - return false; - } - } - swprintf_s(buffer1, L"%s", temp_dir.c_str()); - } - } - hL = LoadLibraryExW(buffer1, 0, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hL) - { - LibraryLoadError(GetLastError(), L"wsock32.org.dll", buffer1); - return false; - } - - // load the functions to proxy - // it's only some of them, because in case of wsock32 most of the functions can actually be natively redirected - // (see wsock32.def and https://source.winehq.org/WineAPI/wsock32.html) - p[1] = GetProcAddress(hL, "EnumProtocolsA"); - p[2] = GetProcAddress(hL, "EnumProtocolsW"); - p[4] = GetProcAddress(hL, "GetAddressByNameA"); - p[5] = GetProcAddress(hL, "GetAddressByNameW"); - p[17] = GetProcAddress(hL, "WEP"); - p[30] = GetProcAddress(hL, "WSARecvEx"); - p[36] = GetProcAddress(hL, "__WSAFDIsSet"); - p[45] = GetProcAddress(hL, "getnetbyname"); - p[52] = GetProcAddress(hL, "getsockopt"); - p[56] = GetProcAddress(hL, "inet_network"); - p[67] = GetProcAddress(hL, "s_perror"); - p[72] = GetProcAddress(hL, "setsockopt"); - } - - if (reason == DLL_PROCESS_DETACH) - { - FreeLibrary(hL); - return true; - } - - return true; -} - -extern "C" -{ - FARPROC PA = NULL; - int RunASM(); - - void PROXY_EnumProtocolsA() - { - PA = p[1]; - RunASM(); - } - void PROXY_EnumProtocolsW() - { - PA = p[2]; - RunASM(); - } - void PROXY_GetAddressByNameA() - { - PA = p[4]; - RunASM(); - } - void PROXY_GetAddressByNameW() - { - PA = p[5]; - RunASM(); - } - void PROXY_WEP() - { - PA = p[17]; - RunASM(); - } - void PROXY_WSARecvEx() - { - PA = p[30]; - RunASM(); - } - void PROXY___WSAFDIsSet() - { - PA = p[36]; - RunASM(); - } - void PROXY_getnetbyname() - { - PA = p[45]; - RunASM(); - } - void PROXY_getsockopt() - { - PA = p[52]; - RunASM(); - } - void PROXY_inet_network() - { - PA = p[56]; - RunASM(); - } - void PROXY_s_perror() - { - PA = p[67]; - RunASM(); - } - void PROXY_setsockopt() - { - PA = p[72]; - RunASM(); - } -} diff --git a/loader_wsock32_proxy/wsock32.asm b/loader_wsock32_proxy/wsock32.asm deleted file mode 100644 index 22a9c384..00000000 --- a/loader_wsock32_proxy/wsock32.asm +++ /dev/null @@ -1,7 +0,0 @@ -.data -extern PA : qword -.code -RunASM proc -jmp qword ptr [PA] -RunASM endp -end diff --git a/primedev/CMakeLists.txt b/primedev/CMakeLists.txt new file mode 100644 index 00000000..31dda4b2 --- /dev/null +++ b/primedev/CMakeLists.txt @@ -0,0 +1,3 @@ +include(Northstar.cmake) +include(Launcher.cmake) +add_subdirectory(wsockproxy) diff --git a/primedev/Launcher.cmake b/primedev/Launcher.cmake new file mode 100644 index 00000000..c04fc70b --- /dev/null +++ b/primedev/Launcher.cmake @@ -0,0 +1,28 @@ +# NorthstarLauncher + +add_executable(NorthstarLauncher "primelauncher/main.cpp" "primelauncher/resources.rc") + +target_compile_definitions(NorthstarLauncher PRIVATE UNICODE _UNICODE) + +target_link_libraries( + NorthstarLauncher + PRIVATE shlwapi.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + comdlg32.lib + advapi32.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + odbc32.lib + odbccp32.lib + WS2_32.lib + ) + +set_target_properties( + NorthstarLauncher PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR} LINK_FLAGS + "/MANIFEST:NO /DEBUG /STACK:8000000" + ) diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake new file mode 100644 index 00000000..aef630c8 --- /dev/null +++ b/primedev/Northstar.cmake @@ -0,0 +1,203 @@ +# NorthstarDLL + +find_package(minhook REQUIRED) +find_package(libcurl REQUIRED) +find_package(minizip REQUIRED) +find_package(silver-bun REQUIRED) + +add_library( + NorthstarDLL SHARED + "resources.rc" + "client/audio.cpp" + "client/audio.h" + "client/chatcommand.cpp" + "client/clientauthhooks.cpp" + "client/clientruihooks.cpp" + "client/clientvideooverrides.cpp" + "client/debugoverlay.cpp" + "client/demofixes.cpp" + "client/diskvmtfixes.cpp" + "client/entity_client_tools.cpp" + "client/languagehooks.cpp" + "client/latencyflex.cpp" + "client/localchatwriter.cpp" + "client/localchatwriter.h" + "client/modlocalisation.cpp" + "client/r2client.cpp" + "client/r2client.h" + "client/rejectconnectionfixes.cpp" + "config/profile.cpp" + "config/profile.h" + "core/convar/concommand.cpp" + "core/convar/concommand.h" + "core/convar/convar.cpp" + "core/convar/convar.h" + "core/convar/cvar.cpp" + "core/convar/cvar.h" + "core/filesystem/filesystem.cpp" + "core/filesystem/filesystem.h" + "core/filesystem/rpakfilesystem.cpp" + "core/filesystem/rpakfilesystem.h" + "core/math/bitbuf.h" + "core/math/bits.cpp" + "core/math/bits.h" + "core/math/color.cpp" + "core/math/color.h" + "core/math/math_pfns.h" + "core/math/vector.h" + "core/math/vplane.h" + "core/hooks.cpp" + "core/hooks.h" + "core/macros.h" + "core/memalloc.cpp" + "core/memalloc.h" + "core/sourceinterface.cpp" + "core/sourceinterface.h" + "core/tier0.cpp" + "core/tier0.h" + "core/tier1.cpp" + "core/tier1.h" + "dedicated/dedicated.cpp" + "dedicated/dedicated.h" + "dedicated/dedicatedlogtoclient.cpp" + "dedicated/dedicatedlogtoclient.h" + "dedicated/dedicatedmaterialsystem.cpp" + "engine/host.cpp" + "engine/hoststate.cpp" + "engine/hoststate.h" + "engine/r2engine.cpp" + "engine/r2engine.h" + "engine/runframe.cpp" + "logging/crashhandler.cpp" + "logging/crashhandler.h" + "logging/logging.cpp" + "logging/logging.h" + "logging/loghooks.cpp" + "logging/loghooks.h" + "logging/sourceconsole.cpp" + "logging/sourceconsole.h" + "masterserver/masterserver.cpp" + "masterserver/masterserver.h" + "mods/autodownload/moddownloader.h" + "mods/autodownload/moddownloader.cpp" + "mods/compiled/kb_act.cpp" + "mods/compiled/modkeyvalues.cpp" + "mods/compiled/modpdef.cpp" + "mods/compiled/modscriptsrson.cpp" + "mods/modmanager.cpp" + "mods/modmanager.h" + "mods/modsavefiles.cpp" + "mods/modsavefiles.h" + "plugins/interfaces/interface.h" + "plugins/interfaces/interface.cpp" + "plugins/interfaces/sys/ISys.h" + "plugins/interfaces/sys/ISys.cpp" + "plugins/interfaces/IPluginId.h" + "plugins/interfaces/IPluginCallbacks.h" + "plugins/plugins.cpp" + "plugins/plugins.h" + "plugins/pluginmanager.h" + "plugins/pluginmanager.cpp" + "scripts/client/clientchathooks.cpp" + "scripts/client/cursorposition.cpp" + "scripts/client/scriptbrowserhooks.cpp" + "scripts/client/scriptmainmenupromos.cpp" + "scripts/client/scriptmodmenu.cpp" + "scripts/client/scriptoriginauth.cpp" + "scripts/client/scriptserverbrowser.cpp" + "scripts/client/scriptservertoclientstringcommand.cpp" + "scripts/server/miscserverfixes.cpp" + "scripts/server/miscserverscript.cpp" + "scripts/server/scriptuserinfo.cpp" + "scripts/scriptdatatables.cpp" + "scripts/scripthttprequesthandler.cpp" + "scripts/scripthttprequesthandler.h" + "scripts/scriptjson.cpp" + "scripts/scriptjson.h" + "scripts/scriptutility.cpp" + "server/auth/bansystem.cpp" + "server/auth/bansystem.h" + "server/auth/serverauthentication.cpp" + "server/auth/serverauthentication.h" + "server/alltalk.cpp" + "server/ai_helper.cpp" + "server/ai_helper.h" + "server/ai_navmesh.cpp" + "server/ai_navmesh.h" + "server/buildainfile.cpp" + "server/r2server.cpp" + "server/r2server.h" + "server/serverchathooks.cpp" + "server/serverchathooks.h" + "server/servernethooks.cpp" + "server/serverpresence.cpp" + "server/serverpresence.h" + "shared/exploit_fixes/exploitfixes.cpp" + "shared/exploit_fixes/exploitfixes_lzss.cpp" + "shared/exploit_fixes/exploitfixes_utf8parser.cpp" + "shared/exploit_fixes/ns_limits.cpp" + "shared/exploit_fixes/ns_limits.h" + "shared/keyvalues.cpp" + "shared/keyvalues.h" + "shared/maxplayers.cpp" + "shared/maxplayers.h" + "shared/misccommands.cpp" + "shared/misccommands.h" + "shared/playlist.cpp" + "shared/playlist.h" + "squirrel/squirrel.cpp" + "squirrel/squirrel.h" + "squirrel/squirrelautobind.cpp" + "squirrel/squirrelautobind.h" + "squirrel/squirrelclasstypes.h" + "squirrel/squirreldatatypes.h" + "util/printcommands.cpp" + "util/printcommands.h" + "util/printmaps.cpp" + "util/printmaps.h" + "util/utils.cpp" + "util/utils.h" + "util/version.cpp" + "util/version.h" + "util/wininfo.cpp" + "util/wininfo.h" + "dllmain.cpp" + "ns_version.h" + "Northstar.def" + ) + +target_link_libraries( + NorthstarDLL + PRIVATE minhook + libcurl + minizip + silver-bun + WS2_32.lib + Crypt32.lib + Cryptui.lib + dbghelp.lib + Wldap32.lib + Normaliz.lib + Bcrypt.lib + version.lib + ) + +target_precompile_headers( + NorthstarDLL + PRIVATE + pch.h + ) + +target_compile_definitions( + NorthstarDLL + PRIVATE UNICODE + _UNICODE + CURL_STATICLIB + ) + +set_target_properties( + NorthstarDLL + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR} + OUTPUT_NAME Northstar + LINK_FLAGS "/MANIFEST:NO /DEBUG" + ) diff --git a/primedev/Northstar.def b/primedev/Northstar.def new file mode 100644 index 00000000..4e3c5318 --- /dev/null +++ b/primedev/Northstar.def @@ -0,0 +1,4 @@ +LIBRARY dedicated + +EXPORTS + InitialiseNorthstar @1 diff --git a/NorthstarDLL/client/audio.cpp b/primedev/client/audio.cpp similarity index 93% rename from NorthstarDLL/client/audio.cpp rename to primedev/client/audio.cpp index 66d34404..099fdcee 100644 --- a/NorthstarDLL/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -10,11 +10,7 @@ AUTOHOOK_INIT() -extern "C" -{ - // should be called only in LoadSampleMetadata_Hook - extern void* __fastcall Audio_GetParentEvent(); -} +static const char* pszAudioEventName; ConVar* Cvar_mileslog_enable; ConVar* Cvar_ns_print_played_sounds; @@ -366,32 +362,16 @@ bool ShouldPlayAudioEvent(const char* eventName, const std::shared_ptrGetInt() > 0) spdlog::info("[AUDIO] Playing event {}", eventName); @@ -465,7 +445,7 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( else { data = dat->second.get(); - dataLength = dat->first; + dataLength = (unsigned int)dat->first; } } @@ -490,6 +470,15 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal( return res; } +// clang-format off +AUTOHOOK(sub_1800294C0, mileswin64.dll + 0x294C0, +void*, __fastcall, (void* a1, void* a2)) +// clang-format on +{ + pszAudioEventName = reinterpret_cast((*((__int64*)a2 + 6))); + return sub_1800294C0(a1, a2); +} + // clang-format off AUTOHOOK(MilesLog, client.dll + 0x57DAD0, void, __fastcall, (int level, const char* string)) diff --git a/NorthstarDLL/client/audio.h b/primedev/client/audio.h similarity index 97% rename from NorthstarDLL/client/audio.h rename to primedev/client/audio.h index 26cda205..15fd1a35 100644 --- a/NorthstarDLL/client/audio.h +++ b/primedev/client/audio.h @@ -14,11 +14,11 @@ enum class AudioSelectionStrategy class EventOverrideData { - public: +public: EventOverrideData(const std::string&, const fs::path&); EventOverrideData(); - public: +public: bool LoadedSuccessfully = false; std::vector EventIds = {}; @@ -34,7 +34,7 @@ class EventOverrideData class CustomAudioManager { - public: +public: bool TryLoadAudioOverride(const fs::path&); void ClearAudioOverrides(); diff --git a/primedev/client/cdll_client_int.h b/primedev/client/cdll_client_int.h new file mode 100644 index 00000000..a32ccbeb --- /dev/null +++ b/primedev/client/cdll_client_int.h @@ -0,0 +1,5 @@ +#pragma once + +#include "toolframework/itoolentity.h" + +inline IClientTools* g_pClientTools = nullptr; diff --git a/NorthstarDLL/client/chatcommand.cpp b/primedev/client/chatcommand.cpp similarity index 100% rename from NorthstarDLL/client/chatcommand.cpp rename to primedev/client/chatcommand.cpp diff --git a/NorthstarDLL/client/clientauthhooks.cpp b/primedev/client/clientauthhooks.cpp similarity index 75% rename from NorthstarDLL/client/clientauthhooks.cpp rename to primedev/client/clientauthhooks.cpp index e66da6c8..35ae3aa7 100644 --- a/NorthstarDLL/client/clientauthhooks.cpp +++ b/primedev/client/clientauthhooks.cpp @@ -1,6 +1,7 @@ #include "masterserver/masterserver.h" #include "core/convar/convar.h" #include "client/r2client.h" +#include "core/vanilla.h" AUTOHOOK_INIT() @@ -16,6 +17,14 @@ AUTOHOOK(AuthWithStryder, engine.dll + 0x1843A0, void, __fastcall, (void* a1)) // clang-format on { + // 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); + return; + } + // game will call this forever, until it gets a valid auth key // so, we need to manually invalidate our key until we're authed with northstar, then we'll allow game to auth with stryder if (!g_pMasterServerManager->m_bOriginAuthWithMasterServerDone && Cvar_ns_has_agreed_to_send_token->GetInt() != DISAGREED_TO_SEND_TOKEN) @@ -23,10 +32,10 @@ void, __fastcall, (void* a1)) // if player has agreed to send token and we aren't already authing, try to auth if (Cvar_ns_has_agreed_to_send_token->GetInt() == AGREED_TO_SEND_TOKEN && !g_pMasterServerManager->m_bOriginAuthWithMasterServerInProgress) - g_pMasterServerManager->AuthenticateOriginWithMasterServer(R2::g_pLocalPlayerUserID, R2::g_pLocalPlayerOriginToken); + g_pMasterServerManager->AuthenticateOriginWithMasterServer(g_pLocalPlayerUserID, g_pLocalPlayerOriginToken); // invalidate key so auth will fail - *R2::g_pLocalPlayerOriginToken = 0; + *g_pLocalPlayerOriginToken = 0; } AuthWithStryder(a1); @@ -39,7 +48,7 @@ AUTOHOOK(Auth3PToken, engine.dll + 0x183760, char*, __fastcall, ()) // clang-format on { - if (g_pMasterServerManager->m_sOwnClientAuthToken[0]) + if (!g_pVanillaCompatibility->GetVanillaCompatibility() && g_pMasterServerManager->m_sOwnClientAuthToken[0]) { memset(p3PToken, 0x0, 1024); strcpy(p3PToken, "Protocol 3: Protect the Pilot"); diff --git a/NorthstarDLL/client/clientruihooks.cpp b/primedev/client/clientruihooks.cpp similarity index 100% rename from NorthstarDLL/client/clientruihooks.cpp rename to primedev/client/clientruihooks.cpp diff --git a/NorthstarDLL/client/clientvideooverrides.cpp b/primedev/client/clientvideooverrides.cpp similarity index 100% rename from NorthstarDLL/client/clientvideooverrides.cpp rename to primedev/client/clientvideooverrides.cpp diff --git a/NorthstarDLL/client/debugoverlay.cpp b/primedev/client/debugoverlay.cpp similarity index 71% rename from NorthstarDLL/client/debugoverlay.cpp rename to primedev/client/debugoverlay.cpp index e231054d..a67b8355 100644 --- a/NorthstarDLL/client/debugoverlay.cpp +++ b/primedev/client/debugoverlay.cpp @@ -1,6 +1,9 @@ +#include "debugoverlay.h" + #include "dedicated/dedicated.h" #include "core/convar/cvar.h" #include "core/math/vector.h" +#include "server/ai_helper.h" AUTOHOOK_INIT() @@ -122,48 +125,13 @@ struct OverlaySphere_t : public OverlayBase_t bool m_bWireframe; }; -typedef bool (*OverlayBase_t__IsDeadType)(OverlayBase_t* a1); -static OverlayBase_t__IsDeadType OverlayBase_t__IsDead; -typedef void (*OverlayBase_t__DestroyOverlayType)(OverlayBase_t* a1); -static OverlayBase_t__DestroyOverlayType OverlayBase_t__DestroyOverlay; +static bool (*OverlayBase_t__IsDead)(OverlayBase_t* a1); +static void (*OverlayBase_t__DestroyOverlay)(OverlayBase_t* a1); static ConVar* Cvar_enable_debug_overlays; LPCRITICAL_SECTION s_OverlayMutex; -// Render Line -typedef void (*RenderLineType)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer); -static RenderLineType RenderLine; - -// Render box -typedef void (*RenderBoxType)( - const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); -static RenderBoxType RenderBox; - -// Render wireframe box -static RenderBoxType RenderWireframeBox; - -// Render swept box -typedef void (*RenderWireframeSweptBoxType)( - const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer); -RenderWireframeSweptBoxType RenderWireframeSweptBox; - -// Render Triangle -typedef void (*RenderTriangleType)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer); -static RenderTriangleType RenderTriangle; - -// Render Axis -typedef void (*RenderAxisType)(const Vector3& vOrigin, float flScale, bool bZBuffer); -static RenderAxisType RenderAxis; - -// I dont know -typedef void (*RenderUnknownType)(const Vector3& vUnk, float flUnk, bool bUnk); -static RenderUnknownType RenderUnknown; - -// Render Sphere -typedef void (*RenderSphereType)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer); -static RenderSphereType RenderSphere; - OverlayBase_t** s_pOverlays; int* g_nRenderTickCount; @@ -306,7 +274,10 @@ void, __fastcall, (bool bRender)) if (bShouldDraw && bRender && (Cvar_enable_debug_overlays->GetBool() || pCurrOverlay->m_Type == OVERLAY_SMARTAMMO)) { - DrawOverlay(pCurrOverlay); + // 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); } pPrevOverlay = pCurrOverlay; @@ -314,6 +285,11 @@ void, __fastcall, (bool bRender)) } } + if (bRender && Cvar_enable_debug_overlays->GetBool()) + { + g_pAIHelper->DrawNavmeshPolys(); + } + LeaveCriticalSection(s_OverlayMutex); } @@ -321,17 +297,17 @@ ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, (CModule module) { AUTOHOOK_DISPATCH() - OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast(); - OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast(); + OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast(); + OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast(); - RenderLine = module.Offset(0x192A70).RCast(); - RenderBox = module.Offset(0x192520).RCast(); - RenderWireframeBox = module.Offset(0x193DA0).RCast(); - RenderWireframeSweptBox = module.Offset(0x1945A0).RCast(); - RenderTriangle = module.Offset(0x193940).RCast(); - RenderAxis = module.Offset(0x1924D0).RCast(); - RenderSphere = module.Offset(0x194170).RCast(); - RenderUnknown = module.Offset(0x1924E0).RCast(); + RenderLine = module.Offset(0x192A70).RCast(); + RenderBox = module.Offset(0x192520).RCast(); + RenderWireframeBox = module.Offset(0x193DA0).RCast(); + RenderWireframeSweptBox = module.Offset(0x1945A0).RCast(); + RenderTriangle = module.Offset(0x193940).RCast(); + RenderAxis = module.Offset(0x1924D0).RCast(); + RenderSphere = module.Offset(0x194170).RCast(); + RenderUnknown = module.Offset(0x1924E0).RCast(); s_OverlayMutex = module.Offset(0x10DB0A38).RCast(); diff --git a/primedev/client/debugoverlay.h b/primedev/client/debugoverlay.h new file mode 100644 index 00000000..498bfafd --- /dev/null +++ b/primedev/client/debugoverlay.h @@ -0,0 +1,28 @@ +#pragma once + +// Render Line +inline void (*RenderLine)(const Vector3& v1, const Vector3& v2, Color c, bool bZBuffer); + +// Render box +inline void (*RenderBox)( + const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); + +// Render wireframe box +inline void (*RenderWireframeBox)( + const Vector3& vOrigin, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer, bool bInsideOut); + +// Render swept box +inline void (*RenderWireframeSweptBox)( + const Vector3& vStart, const Vector3& vEnd, const QAngle& angles, const Vector3& vMins, const Vector3& vMaxs, Color c, bool bZBuffer); + +// Render Triangle +inline void (*RenderTriangle)(const Vector3& p1, const Vector3& p2, const Vector3& p3, Color c, bool bZBuffer); + +// Render Axis +inline void (*RenderAxis)(const Vector3& vOrigin, float flScale, bool bZBuffer); + +// I dont know +inline void (*RenderUnknown)(const Vector3& vUnk, float flUnk, bool bUnk); + +// Render Sphere +inline void (*RenderSphere)(const Vector3& vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer); diff --git a/NorthstarDLL/client/demofixes.cpp b/primedev/client/demofixes.cpp similarity index 76% rename from NorthstarDLL/client/demofixes.cpp rename to primedev/client/demofixes.cpp index 65e48fc0..344764ba 100644 --- a/NorthstarDLL/client/demofixes.cpp +++ b/primedev/client/demofixes.cpp @@ -11,15 +11,15 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientDemoFixes, ConVar, (CModule modu { // change default values of demo cvars to enable them by default, but not autorecord // this is before Host_Init, the setvalue calls here will get overwritten by custom cfgs/launch options - ConVar* Cvar_demo_enableDemos = R2::g_pCVar->FindVar("demo_enabledemos"); + ConVar* Cvar_demo_enableDemos = g_pCVar->FindVar("demo_enabledemos"); Cvar_demo_enableDemos->m_pszDefaultValue = "1"; Cvar_demo_enableDemos->SetValue(true); - ConVar* Cvar_demo_writeLocalFile = R2::g_pCVar->FindVar("demo_writeLocalFile"); + ConVar* Cvar_demo_writeLocalFile = g_pCVar->FindVar("demo_writeLocalFile"); Cvar_demo_writeLocalFile->m_pszDefaultValue = "1"; Cvar_demo_writeLocalFile->SetValue(true); - ConVar* Cvar_demo_autoRecord = R2::g_pCVar->FindVar("demo_autoRecord"); + ConVar* Cvar_demo_autoRecord = g_pCVar->FindVar("demo_autoRecord"); Cvar_demo_autoRecord->m_pszDefaultValue = "0"; Cvar_demo_autoRecord->SetValue(false); } diff --git a/NorthstarDLL/client/diskvmtfixes.cpp b/primedev/client/diskvmtfixes.cpp similarity index 100% rename from NorthstarDLL/client/diskvmtfixes.cpp rename to primedev/client/diskvmtfixes.cpp diff --git a/primedev/client/entity_client_tools.cpp b/primedev/client/entity_client_tools.cpp new file mode 100644 index 00000000..d6f0be11 --- /dev/null +++ b/primedev/client/entity_client_tools.cpp @@ -0,0 +1,13 @@ +#include "toolframework/itoolentity.h" +#include "client/cdll_client_int.h" +#include "core/tier1.h" + +class CClientTools : public IClientTools +{ +public: +}; + +ON_DLL_LOAD("client.dll", ClientClientTools, (CModule module)) +{ + g_pClientTools = Sys_GetFactoryPtr("client.dll", "VCLIENTTOOLS001").RCast(); +} diff --git a/NorthstarDLL/client/languagehooks.cpp b/primedev/client/languagehooks.cpp similarity index 98% rename from NorthstarDLL/client/languagehooks.cpp rename to primedev/client/languagehooks.cpp index 4251dbbd..35ca5659 100644 --- a/NorthstarDLL/client/languagehooks.cpp +++ b/primedev/client/languagehooks.cpp @@ -57,7 +57,7 @@ char*, __fastcall, ()) bool& canOriginDictateLang = *(bool*)((char*)tier0Handle + 0xA9A90); const char* forcedLanguage; - if (Tier0::CommandLine()->CheckParm("-language", &forcedLanguage)) + if (CommandLine()->CheckParm("-language", &forcedLanguage)) { if (!CheckLangAudioExists((char*)forcedLanguage)) { diff --git a/NorthstarDLL/client/latencyflex.cpp b/primedev/client/latencyflex.cpp similarity index 100% rename from NorthstarDLL/client/latencyflex.cpp rename to primedev/client/latencyflex.cpp diff --git a/NorthstarDLL/client/localchatwriter.cpp b/primedev/client/localchatwriter.cpp similarity index 99% rename from NorthstarDLL/client/localchatwriter.cpp rename to primedev/client/localchatwriter.cpp index 848d898f..35cc065f 100644 --- a/NorthstarDLL/client/localchatwriter.cpp +++ b/primedev/client/localchatwriter.cpp @@ -4,13 +4,13 @@ class vgui_BaseRichText_vtable; class vgui_BaseRichText { - public: +public: vgui_BaseRichText_vtable* vtable; }; class vgui_BaseRichText_vtable { - public: +public: char unknown1[1880]; void(__fastcall* InsertChar)(vgui_BaseRichText* self, wchar_t ch); @@ -49,7 +49,7 @@ class vgui_BaseRichText_vtable class CGameSettings { - public: +public: char unknown1[92]; int isChatEnabled; }; @@ -58,7 +58,7 @@ class CGameSettings // have their value at the same offset class CGameFloatVar { - public: +public: char unknown1[88]; float value; }; @@ -101,7 +101,7 @@ Color lightColors[8] = { class AnsiEscapeParser { - public: +public: explicit AnsiEscapeParser(LocalChatWriter* writer) : m_writer(writer) {} void HandleVal(unsigned long val) @@ -129,7 +129,7 @@ class AnsiEscapeParser } } - private: +private: enum class Next { ControlType, diff --git a/NorthstarDLL/client/localchatwriter.h b/primedev/client/localchatwriter.h similarity index 97% rename from NorthstarDLL/client/localchatwriter.h rename to primedev/client/localchatwriter.h index de9e2f9b..acf6f87e 100644 --- a/NorthstarDLL/client/localchatwriter.h +++ b/primedev/client/localchatwriter.h @@ -5,7 +5,7 @@ class vgui_BaseRichText; class CHudChat { - public: +public: static CHudChat** allHuds; char unknown1[720]; @@ -29,7 +29,7 @@ class CHudChat class LocalChatWriter { - public: +public: enum Context { NetworkContext = 0, @@ -56,7 +56,7 @@ class LocalChatWriter void InsertColorChange(Color color); void InsertSwatchColorChange(SwatchColor color); - private: +private: Context m_context; const char* ApplyAnsiEscape(const char* escape); diff --git a/NorthstarDLL/client/modlocalisation.cpp b/primedev/client/modlocalisation.cpp similarity index 100% rename from NorthstarDLL/client/modlocalisation.cpp rename to primedev/client/modlocalisation.cpp diff --git a/NorthstarDLL/client/r2client.cpp b/primedev/client/r2client.cpp similarity index 60% rename from NorthstarDLL/client/r2client.cpp rename to primedev/client/r2client.cpp index fea97d8e..c8e59d74 100644 --- a/NorthstarDLL/client/r2client.cpp +++ b/primedev/client/r2client.cpp @@ -1,14 +1,8 @@ #include "r2client.h" -using namespace R2; - -// use the R2 namespace for game funcs -namespace R2 -{ - char* g_pLocalPlayerUserID; - char* g_pLocalPlayerOriginToken; - GetBaseLocalClientType GetBaseLocalClient; -} // namespace R2 +char* g_pLocalPlayerUserID; +char* g_pLocalPlayerOriginToken; +GetBaseLocalClientType GetBaseLocalClient; ON_DLL_LOAD("engine.dll", R2EngineClient, (CModule module)) { diff --git a/primedev/client/r2client.h b/primedev/client/r2client.h new file mode 100644 index 00000000..ea263dbc --- /dev/null +++ b/primedev/client/r2client.h @@ -0,0 +1,7 @@ +#pragma once + +extern char* g_pLocalPlayerUserID; +extern char* g_pLocalPlayerOriginToken; + +typedef void* (*GetBaseLocalClientType)(); +extern GetBaseLocalClientType GetBaseLocalClient; diff --git a/NorthstarDLL/client/rejectconnectionfixes.cpp b/primedev/client/rejectconnectionfixes.cpp similarity index 88% rename from NorthstarDLL/client/rejectconnectionfixes.cpp rename to primedev/client/rejectconnectionfixes.cpp index 6adde8b6..1b326a3c 100644 --- a/NorthstarDLL/client/rejectconnectionfixes.cpp +++ b/primedev/client/rejectconnectionfixes.cpp @@ -22,7 +22,7 @@ void,, (bool a1, const char* fmt, ...)) // not doing this gets our client in a pretty weird state so we need to shut it down manually here // don't call Cbuf_Execute because we don't need this called immediately - R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "disconnect", R2::cmd_source_t::kCommandSrcCode); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "disconnect", cmd_source_t::kCommandSrcCode); } return COM_ExplainDisconnection(a1, "%s", buf); diff --git a/primedev/cmake/Findlibcurl.cmake b/primedev/cmake/Findlibcurl.cmake new file mode 100644 index 00000000..7843aae3 --- /dev/null +++ b/primedev/cmake/Findlibcurl.cmake @@ -0,0 +1,46 @@ +if(NOT libcurl_FOUND) + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/libcurl) + + set(BUILD_SHARED_LIBS + OFF + CACHE BOOL "Build shared libraries" + ) + set(BUILD_CURL_EXE + OFF + CACHE BOOL "Build curl EXE" + ) + set(HTTP_ONLY + ON + CACHE BOOL "Only build HTTP and HTTPS" + ) + set(CURL_ENABLE_SSL + ON + CACHE BOOL "Enable SSL support" + ) + set(CURL_USE_OPENSSL + OFF + CACHE BOOL "Disable OpenSSL" + ) + set(CURL_USE_LIBSSH2 + OFF + CACHE BOOL "Disable libSSH2" + ) + set(CURL_USE_SCHANNEL + ON + CACHE BOOL "Enable Secure Channel" + ) + set(CURL_CA_BUNDLE + "none" + CACHE STRING "Disable CA Bundle" + ) + set(CURL_CA_PATH + "none" + CACHE STRING "Disable CA Path" + ) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/libcurl libcurl) + set(libcurl_FOUND + 1 + PARENT_SCOPE + ) +endif() diff --git a/primedev/cmake/Findminhook.cmake b/primedev/cmake/Findminhook.cmake new file mode 100644 index 00000000..155232a0 --- /dev/null +++ b/primedev/cmake/Findminhook.cmake @@ -0,0 +1,6 @@ +if(NOT minhook_FOUND) + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/minhook) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/minhook minhook) + set(minhook_FOUND 1) +endif() diff --git a/primedev/cmake/Findminizip.cmake b/primedev/cmake/Findminizip.cmake new file mode 100644 index 00000000..2884f909 --- /dev/null +++ b/primedev/cmake/Findminizip.cmake @@ -0,0 +1,43 @@ +if(NOT minizip_FOUND) + + # zlib 1.3.1 had a cmake change that broke stuff, so this branch on our fork reverts that one commit :) + set(ZLIB_TAG "fix-things") + set(ZLIB_REPOSITORY "https://github.com/R2Northstar/zlib") + + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/minizip) + + set(MZ_ZLIB + ON + CACHE BOOL "Enable ZLIB compression, needed for DEFLATE" + ) + set(MZ_BZIP2 + OFF + CACHE BOOL "Disable BZIP2 compression" + ) + set(MZ_LZMA + OFF + CACHE BOOL "Disable LZMA & XZ compression" + ) + set(MZ_PKCRYPT + OFF + CACHE BOOL "Disable PKWARE traditional encryption" + ) + set(MZ_WZAES + OFF + CACHE BOOL "Disable WinZIP AES encryption" + ) + set(MZ_ZSTD + OFF + CACHE BOOL "Disable ZSTD compression" + ) + set(MZ_SIGNING + OFF + CACHE BOOL "Disable zip signing support" + ) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/minizip minizip) + set(minizip_FOUND + 1 + PARENT_SCOPE + ) +endif() diff --git a/primedev/cmake/Findsilver-bun.cmake b/primedev/cmake/Findsilver-bun.cmake new file mode 100644 index 00000000..fa445aba --- /dev/null +++ b/primedev/cmake/Findsilver-bun.cmake @@ -0,0 +1,9 @@ +if(NOT silver-bun_FOUND) + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/silver-bun) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/silver-bun silver-bun) + set(silver-bun_FOUND + 1 + PARENT_SCOPE + ) +endif() diff --git a/primedev/cmake/Findspdlog.cmake b/primedev/cmake/Findspdlog.cmake new file mode 100644 index 00000000..660e1950 --- /dev/null +++ b/primedev/cmake/Findspdlog.cmake @@ -0,0 +1,6 @@ +if(NOT spdlog_FOUND) + check_init_submodule(${PROJECT_SOURCE_DIR}/primedev/thirdparty/spdlog) + + add_subdirectory(${PROJECT_SOURCE_DIR}/primedev/thirdparty/spdlog spdlog) + set(spdlog_FOUND 1) +endif() diff --git a/primedev/cmake/utils.cmake b/primedev/cmake/utils.cmake new file mode 100644 index 00000000..80a2288d --- /dev/null +++ b/primedev/cmake/utils.cmake @@ -0,0 +1,44 @@ +# Check if a dependency exist before trying to init git submodules +function(check_init_submodule path) + file( + GLOB + DIR_CONTENT + "${path}/*" + ) + list( + LENGTH + DIR_CONTENT + CONTENT_COUNT + ) + if(CONTENT_COUNT + EQUAL + 0 + ) + if(NOT + EXISTS + "${PROJECT_SOURCE_DIR}/.git" + ) + message(FATAL_ERROR "Failed to find third party dependency in '${path}'") + endif() + + find_package(Git QUIET) + if(NOT Git_FOUND) + message(FATAL_ERROR "Failed to find Git, third party dependency could not be setup at `${path}") + endif() + + message(STATUS "Setting up dependencies as git submodules") + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT + ) + + if(NOT + GIT_SUBMOD_RESULT + EQUAL + "0" + ) + message(FATAL_ERROR "Initializing Git submodules failed with ${GIT_SUBMOD_RESULT}") + endif() + endif() +endfunction() diff --git a/NorthstarDLL/config/profile.cpp b/primedev/config/profile.cpp similarity index 88% rename from NorthstarDLL/config/profile.cpp rename to primedev/config/profile.cpp index d5361efa..f1d3f81c 100644 --- a/NorthstarDLL/config/profile.cpp +++ b/primedev/config/profile.cpp @@ -15,15 +15,15 @@ void InitialiseNorthstarPrefix() std::string cla = std::string(clachar); if (strncmp(cla.substr(9, 1).c_str(), "\"", 1)) { - int space = cla.find(" "); + size_t space = cla.find(" "); std::string dirname = cla.substr(9, space - 9); NORTHSTAR_FOLDER_PREFIX = dirname; } else { std::string quote = "\""; - int quote1 = cla.find(quote); - int quote2 = (cla.substr(quote1 + 1)).find(quote); + size_t quote1 = cla.find(quote); + size_t quote2 = (cla.substr(quote1 + 1)).find(quote); std::string dirname = cla.substr(quote1 + 1, quote2); NORTHSTAR_FOLDER_PREFIX = dirname; } diff --git a/NorthstarDLL/config/profile.h b/primedev/config/profile.h similarity index 100% rename from NorthstarDLL/config/profile.h rename to primedev/config/profile.h diff --git a/NorthstarDLL/core/convar/concommand.cpp b/primedev/core/convar/concommand.cpp similarity index 95% rename from NorthstarDLL/core/convar/concommand.cpp rename to primedev/core/convar/concommand.cpp index 41f54c76..02c11ada 100644 --- a/NorthstarDLL/core/convar/concommand.cpp +++ b/primedev/core/convar/concommand.cpp @@ -2,9 +2,6 @@ #include "shared/misccommands.h" #include "engine/r2engine.h" -#include "plugins/pluginbackend.h" -#include "plugins/plugin_abi.h" - #include //----------------------------------------------------------------------------- @@ -145,13 +142,11 @@ void RegisterConCommand( ConCommand* newCommand = new ConCommand; ConCommandConstructor(newCommand, name, callback, helpString, flags, nullptr); newCommand->m_pCompletionCallback = completionCallback; + newCommand->m_nCallbackFlags |= 0x3; // seems to be correct?; derived from client.dll + 0x737267 } ON_DLL_LOAD("engine.dll", ConCommand, (CModule module)) { ConCommandConstructor = module.Offset(0x415F60).RCast(); AddMiscConCommands(); - - g_pPluginCommunicationhandler->m_sEngineData.ConCommandConstructor = - reinterpret_cast(ConCommandConstructor); } diff --git a/NorthstarDLL/core/convar/concommand.h b/primedev/core/convar/concommand.h similarity index 98% rename from NorthstarDLL/core/convar/concommand.h rename to primedev/core/convar/concommand.h index c11c7ea4..71a82fec 100644 --- a/NorthstarDLL/core/convar/concommand.h +++ b/primedev/core/convar/concommand.h @@ -4,7 +4,7 @@ class ConCommandBase; class IConCommandBaseAccessor { - public: +public: // Flags is a combination of FCVAR flags in cvar.h. // hOut is filled in with a handle to the variable. virtual bool RegisterConCommandBase(ConCommandBase* pVar) = 0; @@ -12,7 +12,7 @@ class IConCommandBaseAccessor class CCommand { - public: +public: CCommand() = delete; int64_t ArgC() const; @@ -24,7 +24,7 @@ class CCommand static int MaxCommandLength(); - private: +private: enum { COMMAND_MAX_ARGC = 64, @@ -88,7 +88,7 @@ typedef int (*FnCommandCompletionCallback)(const char* partial, char commands[CO // From r5reloaded class ConCommandBase { - public: +public: bool HasFlags(int nFlags); void AddFlags(int nFlags); void RemoveFlags(int nFlags); @@ -120,7 +120,7 @@ class ConCommand : public ConCommandBase { friend class CCVar; - public: +public: ConCommand(void) {}; // !TODO: Rebuild engine constructor in SDK instead. ConCommand(const char* szName, const char* szHelpString, int nFlags, void* pCallback, void* pCommandCompletionCallback); void Init(void); diff --git a/NorthstarDLL/core/convar/convar.cpp b/primedev/core/convar/convar.cpp similarity index 95% rename from NorthstarDLL/core/convar/convar.cpp rename to primedev/core/convar/convar.cpp index 594989c2..87a1e159 100644 --- a/NorthstarDLL/core/convar/convar.cpp +++ b/primedev/core/convar/convar.cpp @@ -3,9 +3,6 @@ #include "convar.h" #include "core/sourceinterface.h" -#include "plugins/pluginbackend.h" -#include "plugins/plugin_abi.h" - #include typedef void (*ConVarRegisterType)( @@ -38,14 +35,8 @@ ON_DLL_LOAD("engine.dll", ConVar, (CModule module)) g_pConVar_Vtable = module.Offset(0x67FD28); g_pIConVar_Vtable = module.Offset(0x67FDC8); - R2::g_pCVarInterface = new SourceInterface("vstdlib.dll", "VEngineCvar007"); - R2::g_pCVar = *R2::g_pCVarInterface; - - g_pPluginCommunicationhandler->m_sEngineData.conVarMalloc = reinterpret_cast(conVarMalloc); - g_pPluginCommunicationhandler->m_sEngineData.conVarRegister = reinterpret_cast(conVarRegister); - g_pPluginCommunicationhandler->m_sEngineData.ConVar_Vtable = reinterpret_cast(g_pConVar_Vtable); - g_pPluginCommunicationhandler->m_sEngineData.IConVar_Vtable = reinterpret_cast(g_pIConVar_Vtable); - g_pPluginCommunicationhandler->m_sEngineData.g_pCVar = reinterpret_cast(R2::g_pCVar); + g_pCVarInterface = new SourceInterface("vstdlib.dll", "VEngineCvar007"); + g_pCVar = *g_pCVarInterface; } //----------------------------------------------------------------------------- @@ -372,6 +363,7 @@ void ConVar::SetValue(Color clValue) //----------------------------------------------------------------------------- void ConVar::ChangeStringValue(const char* pszTempVal, float flOldValue) { + NOTE_UNUSED(flOldValue); assert(!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING)); char* pszOldValue = (char*)_malloca(m_Value.m_iStringLength); @@ -382,7 +374,7 @@ void ConVar::ChangeStringValue(const char* pszTempVal, float flOldValue) if (pszTempVal) { - int len = strlen(pszTempVal) + 1; + size_t len = strlen(pszTempVal) + 1; if (len > m_Value.m_iStringLength) { diff --git a/NorthstarDLL/core/convar/convar.h b/primedev/core/convar/convar.h similarity index 99% rename from NorthstarDLL/core/convar/convar.h rename to primedev/core/convar/convar.h index 4b00e25f..f0366b46 100644 --- a/NorthstarDLL/core/convar/convar.h +++ b/primedev/core/convar/convar.h @@ -124,7 +124,7 @@ typedef void (*FnChangeCallback_t)(ConVar* var, const char* pOldValue, float flO //----------------------------------------------------------------------------- class ConVar { - public: +public: ConVar(void) {}; ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString); ConVar( diff --git a/NorthstarDLL/core/convar/cvar.cpp b/primedev/core/convar/cvar.cpp similarity index 85% rename from NorthstarDLL/core/convar/cvar.cpp rename to primedev/core/convar/cvar.cpp index 21f8d2ec..aa5f0365 100644 --- a/NorthstarDLL/core/convar/cvar.cpp +++ b/primedev/core/convar/cvar.cpp @@ -22,9 +22,5 @@ std::unordered_map CCvar::DumpToMap() return allConVars; } -// use the R2 namespace for game funcs -namespace R2 -{ - SourceInterface* g_pCVarInterface; - CCvar* g_pCVar; -} // namespace R2 +SourceInterface* g_pCVarInterface; +CCvar* g_pCVar; diff --git a/NorthstarDLL/core/convar/cvar.h b/primedev/core/convar/cvar.h similarity index 89% rename from NorthstarDLL/core/convar/cvar.h rename to primedev/core/convar/cvar.h index 3a3e1815..beaa84f4 100644 --- a/NorthstarDLL/core/convar/cvar.h +++ b/primedev/core/convar/cvar.h @@ -13,7 +13,7 @@ class ConVar; //----------------------------------------------------------------------------- class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. { - public: +public: virtual void SetFirst(void) = 0; // 0 virtual void Next(void) = 0; // 1 virtual bool IsValid(void) = 0; // 2 @@ -25,7 +25,7 @@ class CCVarIteratorInternal // Fully reversed table, just look at the virtual fu //----------------------------------------------------------------------------- class CCvar { - public: +public: M_VMETHOD(ConCommandBase*, FindCommandBase, 14, (const char* pszCommandName), (this, pszCommandName)); M_VMETHOD(ConVar*, FindVar, 16, (const char* pszVarName), (this, pszVarName)); M_VMETHOD(ConCommand*, FindCommand, 18, (const char* pszCommandName), (this, pszCommandName)); @@ -34,9 +34,5 @@ class CCvar std::unordered_map DumpToMap(); }; -// use the R2 namespace for game funcs -namespace R2 -{ - extern SourceInterface* g_pCVarInterface; - extern CCvar* g_pCVar; -} // namespace R2 +extern SourceInterface* g_pCVarInterface; +extern CCvar* g_pCVar; diff --git a/NorthstarDLL/core/filesystem/filesystem.cpp b/primedev/core/filesystem/filesystem.cpp similarity index 80% rename from NorthstarDLL/core/filesystem/filesystem.cpp rename to primedev/core/filesystem/filesystem.cpp index e4da647f..b39939e4 100644 --- a/NorthstarDLL/core/filesystem/filesystem.cpp +++ b/primedev/core/filesystem/filesystem.cpp @@ -7,48 +7,42 @@ AUTOHOOK_INIT() -using namespace R2; - bool bReadingOriginalFile = false; std::string sCurrentModPath; ConVar* Cvar_ns_fs_log_reads; -// use the R2 namespace for game funcs -namespace R2 +SourceInterface* g_pFilesystem; + +std::string ReadVPKFile(const char* path) { - SourceInterface* g_pFilesystem; + // 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); - std::string ReadVPKFile(const char* path) + std::stringstream fileStream; + int bytesRead = 0; + char data[4096]; + do { - // 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); + 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)); - 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); - 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(); +} - return fileStream.str(); - } +std::string ReadVPKOriginalFile(const char* path) +{ + // todo: should probably set search path to be g_pModName here also - std::string ReadVPKOriginalFile(const char* path) - { - // todo: should probably set search path to be g_pModName here also + bReadingOriginalFile = true; + std::string ret = ReadVPKFile(path); + bReadingOriginalFile = false; - bReadingOriginalFile = true; - std::string ret = ReadVPKFile(path); - bReadingOriginalFile = false; - - return ret; - } -} // namespace R2 + return ret; +} // clang-format off HOOK(AddSearchPathHook, AddSearchPath, @@ -175,7 +169,7 @@ ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, (CModule module)) { AUTOHOOK_DISPATCH() - R2::g_pFilesystem = new SourceInterface("filesystem_stdio.dll", "VFileSystem017"); + g_pFilesystem = new SourceInterface("filesystem_stdio.dll", "VFileSystem017"); AddSearchPathHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->AddSearchPath); ReadFromCacheHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->ReadFromCache); diff --git a/NorthstarDLL/core/filesystem/filesystem.h b/primedev/core/filesystem/filesystem.h similarity index 72% rename from NorthstarDLL/core/filesystem/filesystem.h rename to primedev/core/filesystem/filesystem.h index ac1c5986..4e2c18d9 100644 --- a/NorthstarDLL/core/filesystem/filesystem.h +++ b/primedev/core/filesystem/filesystem.h @@ -4,21 +4,6 @@ // taken from ttf2sdk typedef void* FileHandle_t; -#pragma pack(push, 1) - -// clang-format off -OFFSET_STRUCT(VPKFileEntry) -{ - STRUCT_SIZE(0x44); - FIELDS(0x0, - char* directory; - char* filename; - char* extension; - ) -}; -// clang-format on -#pragma pack(pop) - struct VPKData; enum SearchPathAdd_t @@ -29,14 +14,14 @@ enum SearchPathAdd_t class CSearchPath { - public: +public: unsigned char unknown[0x18]; const char* debugPath; }; class IFileSystem { - public: +public: struct VTable { void* unknown[10]; @@ -63,11 +48,7 @@ class IFileSystem VTable2* m_vtable2; }; -// use the R2 namespace for game funcs -namespace R2 -{ - extern SourceInterface* g_pFilesystem; +extern SourceInterface* g_pFilesystem; - std::string ReadVPKFile(const char* path); - std::string ReadVPKOriginalFile(const char* path); -} // namespace R2 +std::string ReadVPKFile(const char* path); +std::string ReadVPKOriginalFile(const char* path); diff --git a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp b/primedev/core/filesystem/rpakfilesystem.cpp similarity index 98% rename from NorthstarDLL/core/filesystem/rpakfilesystem.cpp rename to primedev/core/filesystem/rpakfilesystem.cpp index 8d50b07a..da72646b 100644 --- a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp +++ b/primedev/core/filesystem/rpakfilesystem.cpp @@ -209,8 +209,8 @@ int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallbac // dedicated only needs common, common_mp, common_sp, and sp_ rpaks // sp_ 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() && (Tier0::CommandLine()->CheckParm("-nopakdedi") || - strncmp(&originalPath[0], "common", 6) && strncmp(&originalPath[0], "sp_", 3))) + if (IsDedicatedServer() && + (CommandLine()->CheckParm("-nopakdedi") || strncmp(&originalPath[0], "common", 6) && strncmp(&originalPath[0], "sp_", 3))) { if (bNeedToFreePakName) delete[] pPath; diff --git a/NorthstarDLL/core/filesystem/rpakfilesystem.h b/primedev/core/filesystem/rpakfilesystem.h similarity index 97% rename from NorthstarDLL/core/filesystem/rpakfilesystem.h rename to primedev/core/filesystem/rpakfilesystem.h index 3f608dba..bcd57a73 100644 --- a/NorthstarDLL/core/filesystem/rpakfilesystem.h +++ b/primedev/core/filesystem/rpakfilesystem.h @@ -17,11 +17,11 @@ struct LoadedPak class PakLoadManager { - private: +private: std::map m_vLoadedPaks {}; std::unordered_map m_HashToPakHandle {}; - public: +public: int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource); void UnloadPak(const int nPakHandle); void UnloadMapPaks(); diff --git a/NorthstarDLL/core/hooks.cpp b/primedev/core/hooks.cpp similarity index 94% rename from NorthstarDLL/core/hooks.cpp rename to primedev/core/hooks.cpp index da7f9f3e..20f0cbef 100644 --- a/NorthstarDLL/core/hooks.cpp +++ b/primedev/core/hooks.cpp @@ -1,5 +1,5 @@ #include "dedicated/dedicated.h" -#include "plugins/pluginbackend.h" +#include "plugins/pluginmanager.h" #include #include @@ -10,6 +10,8 @@ #include #include +#define XINPUT1_3_DLL "XInput1_3.dll" + AUTOHOOK_INIT() // called from the ON_DLL_LOAD macros @@ -75,7 +77,7 @@ void __fileAutohook::Dispatch() void __fileAutohook::DispatchForModule(const char* pModuleName) { - const int iModuleNameLen = strlen(pModuleName); + const size_t iModuleNameLen = strlen(pModuleName); for (__autohook* hook : hooks) if ((hook->iAddressResolutionMode == __autohook::OFFSET_STRING && !strncmp(pModuleName, hook->pAddrString, iModuleNameLen)) || @@ -85,14 +87,14 @@ void __fileAutohook::DispatchForModule(const char* pModuleName) ManualHook::ManualHook(const char* funcName, LPVOID func) : pHookFunc(func), ppOrigFunc(nullptr) { - const int iFuncNameStrlen = strlen(funcName); + 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) { - const int iFuncNameStrlen = strlen(funcName); + const size_t iFuncNameStrlen = strlen(funcName); pFuncName = new char[iFuncNameStrlen]; memcpy(pFuncName, funcName, iFuncNameStrlen); } @@ -139,7 +141,7 @@ uintptr_t ParseDLLOffsetString(const char* pAddrString) uintptr_t iOffset = 0; int iOffsetBegin = iDllNameEnd; - int iOffsetEnd = strlen(pAddrString); + size_t iOffsetEnd = strlen(pAddrString); // seek until we hit the start of the number offset for (; !(pAddrString[iOffsetBegin] >= '0' && pAddrString[iOffsetBegin] <= '9') && pAddrString[iOffsetBegin]; iOffsetBegin++) @@ -393,8 +395,11 @@ HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)) { HMODULE moduleAddress; + LPCSTR lpLibFileNameEnd = lpLibFileName + strlen(lpLibFileName); + LPCSTR lpLibName = lpLibFileNameEnd - strlen(XINPUT1_3_DLL); + // replace xinput dll with one that has ASLR - if (!strncmp(lpLibFileName, "XInput1_3.dll", 14)) + if (lpLibFileName <= lpLibName && !strncmp(lpLibName, XINPUT1_3_DLL, strlen(XINPUT1_3_DLL) + 1)) { moduleAddress = _LoadLibraryExA("XInput9_1_0.dll", hFile, dwFlags); @@ -412,7 +417,7 @@ HMODULE, WINAPI, (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)) if (moduleAddress) { CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); - InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress); + g_pPluginManager->InformDllLoad(moduleAddress, fs::path(lpLibFileName)); } return moduleAddress; @@ -454,7 +459,7 @@ HMODULE, WINAPI, (LPCWSTR lpLibFileName)) if (moduleAddress) { CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); - InformPluginsDLLLoad(fs::path(lpLibFileName), moduleAddress); + g_pPluginManager->InformDllLoad(moduleAddress, fs::path(lpLibFileName)); } return moduleAddress; diff --git a/NorthstarDLL/core/hooks.h b/primedev/core/hooks.h similarity index 97% rename from NorthstarDLL/core/hooks.h rename to primedev/core/hooks.h index 01244b3d..f842afbb 100644 --- a/NorthstarDLL/core/hooks.h +++ b/primedev/core/hooks.h @@ -1,5 +1,4 @@ #pragma once -#include "memory.h" #include #include @@ -25,7 +24,7 @@ enum class eDllLoadCallbackSide class __dllLoadCallback { - public: +public: __dllLoadCallback() = delete; __dllLoadCallback( eDllLoadCallbackSide side, @@ -67,7 +66,7 @@ class __autovar; class __fileAutohook { - public: +public: std::vector<__autohook*> hooks; std::vector<__autovar*> vars; @@ -91,7 +90,7 @@ uintptr_t ParseDLLOffsetString(const char* pAddrString); class __autohook { - public: +public: enum AddressResolutionMode { OFFSET_STRING, // we're using a string that of the format dllname.dll + offset @@ -111,7 +110,7 @@ class __autohook char* pModuleName; // for PROCADDRESS char* pProcName; // for PROCADDRESS - public: +public: __autohook() = delete; __autohook(__fileAutohook* autohook, const char* funcName, LPVOID absoluteAddress, LPVOID* orig, LPVOID func) @@ -119,7 +118,7 @@ class __autohook { iAddressResolutionMode = ABSOLUTE_ADDR; - const int iFuncNameStrlen = strlen(funcName) + 1; + const size_t iFuncNameStrlen = strlen(funcName) + 1; pFuncName = new char[iFuncNameStrlen]; memcpy(pFuncName, funcName, iFuncNameStrlen); @@ -131,11 +130,11 @@ class __autohook { iAddressResolutionMode = OFFSET_STRING; - const int iFuncNameStrlen = strlen(funcName) + 1; + const size_t iFuncNameStrlen = strlen(funcName) + 1; pFuncName = new char[iFuncNameStrlen]; memcpy(pFuncName, funcName, iFuncNameStrlen); - const int iAddrStrlen = strlen(addrString) + 1; + const size_t iAddrStrlen = strlen(addrString) + 1; pAddrString = new char[iAddrStrlen]; memcpy(pAddrString, addrString, iAddrStrlen); @@ -147,15 +146,15 @@ class __autohook { iAddressResolutionMode = PROCADDRESS; - const int iFuncNameStrlen = strlen(funcName) + 1; + const size_t iFuncNameStrlen = strlen(funcName) + 1; pFuncName = new char[iFuncNameStrlen]; memcpy(pFuncName, funcName, iFuncNameStrlen); - const int iModuleNameStrlen = strlen(moduleName) + 1; + const size_t iModuleNameStrlen = strlen(moduleName) + 1; pModuleName = new char[iModuleNameStrlen]; memcpy(pModuleName, moduleName, iModuleNameStrlen); - const int iProcNameStrlen = strlen(procName) + 1; + const size_t iProcNameStrlen = strlen(procName) + 1; pProcName = new char[iProcNameStrlen]; memcpy(pProcName, procName, iProcNameStrlen); @@ -250,13 +249,13 @@ class __autohook class ManualHook { - public: +public: char* pFuncName; LPVOID pHookFunc; LPVOID* ppOrigFunc; - public: +public: ManualHook() = delete; ManualHook(const char* funcName, LPVOID func); ManualHook(const char* funcName, LPVOID* orig, LPVOID func); @@ -284,16 +283,16 @@ void MakeHook(LPVOID pTarget, LPVOID pDetour, void* ppOriginal, const char* pFun class __autovar { - public: +public: char* m_pAddrString; void** m_pTarget; - public: +public: __autovar(__fileAutohook* pAutohook, const char* pAddrString, void** pTarget) { m_pTarget = pTarget; - const int iAddrStrlen = strlen(pAddrString) + 1; + const size_t iAddrStrlen = strlen(pAddrString) + 1; m_pAddrString = new char[iAddrStrlen]; memcpy(m_pAddrString, pAddrString, iAddrStrlen); diff --git a/NorthstarDLL/core/macros.h b/primedev/core/macros.h similarity index 100% rename from NorthstarDLL/core/macros.h rename to primedev/core/macros.h diff --git a/NorthstarDLL/core/math/bitbuf.h b/primedev/core/math/bitbuf.h similarity index 99% rename from NorthstarDLL/core/math/bitbuf.h rename to primedev/core/math/bitbuf.h index 8e8e216f..a06dab17 100644 --- a/NorthstarDLL/core/math/bitbuf.h +++ b/primedev/core/math/bitbuf.h @@ -89,13 +89,13 @@ enum EBitCoordType class BitBufferBase { - protected: +protected: INLINE void SetName(const char* name) { m_BufferName = name; } - public: +public: INLINE bool IsOverflowed() { return m_Overflow; @@ -110,16 +110,16 @@ class BitBufferBase return m_BufferName; } - private: +private: const char* m_BufferName = ""; - protected: +protected: u8 m_Overflow = false; }; class BFRead : public BitBufferBase { - public: +public: BFRead() = default; INLINE BFRead(uptr data, size_t byteLength, size_t startPos = 0, const char* bufferName = 0) @@ -130,7 +130,7 @@ class BFRead : public BitBufferBase SetName(bufferName); } - public: +public: INLINE void StartReading(uptr data, size_t byteLength, size_t startPos = 0) { m_Data = reinterpret_cast(data); @@ -630,7 +630,7 @@ class BFRead : public BitBufferBase // at the head to make reading and detecting the end efficient. int nHead = m_DataBytes & 3; - int posBytes = startPos / 8; + size_t posBytes = startPos / 8; if ((m_DataBytes < 4) || (nHead && (posBytes < nHead))) { // partial first dword @@ -652,7 +652,7 @@ class BFRead : public BitBufferBase } else { - int adjustedPos = startPos - (nHead << 3); + size_t adjustedPos = startPos - (nHead << 3); m_DataIn = reinterpret_cast(reinterpret_cast(m_Data) + ((adjustedPos / 32) << 2) + nHead); @@ -706,7 +706,7 @@ class BFRead : public BitBufferBase return GetNumBitsLeft() >> 3; } - private: +private: size_t m_DataBits; // 0x0010 size_t m_DataBytes; // 0x0018 @@ -720,7 +720,7 @@ class BFRead : public BitBufferBase class BFWrite : public BitBufferBase { - public: +public: BFWrite() = default; INLINE BFWrite(uptr data, size_t byteLength, const char* bufferName = 0) @@ -731,7 +731,7 @@ class BFWrite : public BitBufferBase SetName(bufferName); } - public: +public: INLINE void StartWriting(uptr data, size_t byteLength) { m_Data = reinterpret_cast(data); @@ -1131,7 +1131,7 @@ class BFWrite : public BitBufferBase WriteBitVec3Coord(tmp); }*/ - private: +private: size_t m_DataBits = 0; size_t m_DataBytes = 0; diff --git a/NorthstarDLL/core/math/bits.cpp b/primedev/core/math/bits.cpp similarity index 100% rename from NorthstarDLL/core/math/bits.cpp rename to primedev/core/math/bits.cpp diff --git a/NorthstarDLL/core/math/bits.h b/primedev/core/math/bits.h similarity index 100% rename from NorthstarDLL/core/math/bits.h rename to primedev/core/math/bits.h diff --git a/NorthstarDLL/core/math/color.cpp b/primedev/core/math/color.cpp similarity index 100% rename from NorthstarDLL/core/math/color.cpp rename to primedev/core/math/color.cpp diff --git a/NorthstarDLL/core/math/color.h b/primedev/core/math/color.h similarity index 99% rename from NorthstarDLL/core/math/color.h rename to primedev/core/math/color.h index 76cf8a47..013c4e4c 100644 --- a/NorthstarDLL/core/math/color.h +++ b/primedev/core/math/color.h @@ -57,7 +57,7 @@ struct SourceColor //----------------------------------------------------------------------------- class Color { - public: +public: Color(int r, int g, int b, int a = 255) { _color[0] = (unsigned char)r; @@ -169,7 +169,7 @@ class Color return SourceColor(_color[0], _color[1], _color[2], _color[3]); } - private: +private: unsigned char _color[4]; }; diff --git a/primedev/core/math/math_pfns.h b/primedev/core/math/math_pfns.h new file mode 100644 index 00000000..4dca9806 --- /dev/null +++ b/primedev/core/math/math_pfns.h @@ -0,0 +1,7 @@ +#pragma once + +inline float FastSqrt(float x) +{ + __m128 root = _mm_sqrt_ss(_mm_load_ss(&x)); + return *(reinterpret_cast(&root)); +} diff --git a/primedev/core/math/vector.h b/primedev/core/math/vector.h new file mode 100644 index 00000000..e62f2c93 --- /dev/null +++ b/primedev/core/math/vector.h @@ -0,0 +1,331 @@ +#pragma once + +#include "bits.h" +#include "math_pfns.h" + +#include + +#define DEG2RAD(a) (a) * (3.14159265358979323846f / 180.0f) +#define RAD2DEG(a) (a) * (180.0f / 3.14159265358979323846f) + +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() + { + return IsFinite(x) && IsFinite(y) && IsFinite(z); + } + + inline void Init(float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f) + { + x = fX; + y = fY; + z = fZ; + } + + inline float Length() + { + return FastSqrt(x * x + y * y + z * z); + } + + inline float DistTo(const Vector3& vOther) + { + Vector3 vDelta; + vDelta.x = vOther.x - x; + vDelta.y = vOther.y - y; + vDelta.z = vOther.z - z; + + return vDelta.Length(); + } + + float Dot(const Vector3& vOther) const + { + return x * vOther.x + y * vOther.y + z * vOther.z; + } + + // Cross product between two vectors. + Vector3 Cross(const Vector3& vOther) const; + + void Normalize(); + + bool operator==(const Vector3& v) const; + bool operator!=(const Vector3& v) const; + + FORCEINLINE Vector3& operator+=(const Vector3& v); + FORCEINLINE Vector3& operator-=(const Vector3& v); + FORCEINLINE Vector3& operator*=(const Vector3& v); + FORCEINLINE Vector3& operator*=(float s); + FORCEINLINE Vector3& operator/=(const Vector3& v); + FORCEINLINE Vector3& operator/=(float s); + FORCEINLINE Vector3& operator+=(float fl); ///< broadcast add + FORCEINLINE Vector3& operator-=(float fl); + + // arithmetic operations + Vector3 operator-(void) const; + + Vector3 operator+(const Vector3& v) const; + Vector3 operator-(const Vector3& v) const; + Vector3 operator*(const Vector3& v) const; + Vector3 operator/(const Vector3& v) const; + Vector3 operator*(float fl) const; + Vector3 operator/(float fl) const; +}; + +FORCEINLINE void VectorAdd(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorSubtract(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorMultiply(const Vector3& a, float b, Vector3& result); +FORCEINLINE void VectorMultiply(const Vector3& a, const Vector3& b, Vector3& result); +FORCEINLINE void VectorDivide(const Vector3& a, float b, Vector3& result); +FORCEINLINE void VectorDivide(const Vector3& a, const Vector3& b, Vector3& result); + +inline bool Vector3::operator==(const Vector3& src) const +{ + return (src.x == x) && (src.y == y) && (src.z == z); +} + +inline bool Vector3::operator!=(const Vector3& src) const +{ + return (src.x != x) || (src.y != y) || (src.z != z); +} + +FORCEINLINE Vector3& Vector3::operator+=(const Vector3& v) +{ + x += v.x; + y += v.y; + z += v.z; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator-=(const Vector3& v) +{ + x -= v.x; + y -= v.y; + z -= v.z; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator*=(float fl) +{ + x *= fl; + y *= fl; + z *= fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator*=(const Vector3& v) +{ + x *= v.x; + y *= v.y; + z *= v.z; + return *this; +} + +// this ought to be an opcode. +FORCEINLINE Vector3& Vector3::operator+=(float fl) +{ + x += fl; + y += fl; + z += fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator-=(float fl) +{ + x -= fl; + y -= fl; + z -= fl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator/=(float fl) +{ + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + z *= oofl; + return *this; +} + +FORCEINLINE Vector3& Vector3::operator/=(const Vector3& v) +{ + x /= v.x; + y /= v.y; + z /= v.z; + return *this; +} + +inline Vector3 Vector3::operator-(void) const +{ + return Vector3(-x, -y, -z); +} + +inline Vector3 Vector3::operator+(const Vector3& v) const +{ + Vector3 res; + VectorAdd(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator-(const Vector3& v) const +{ + Vector3 res; + VectorSubtract(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator*(float fl) const +{ + Vector3 res; + VectorMultiply(*this, fl, res); + return res; +} + +inline Vector3 Vector3::operator*(const Vector3& v) const +{ + Vector3 res; + VectorMultiply(*this, v, res); + return res; +} + +inline Vector3 Vector3::operator/(float fl) const +{ + Vector3 res; + VectorDivide(*this, fl, res); + return res; +} + +inline Vector3 Vector3::operator/(const Vector3& v) const +{ + Vector3 res; + VectorDivide(*this, v, res); + return res; +} + +inline Vector3 operator*(float fl, const Vector3& v) +{ + return v * fl; +} + +inline Vector3 Vector3::Cross(const Vector3& vOther) const +{ + return Vector3(y * vOther.z - z * vOther.y, z * vOther.x - x * vOther.z, x * vOther.y - y * vOther.x); +} + +inline void Vector3::Normalize() +{ + float fLen = Length(); + x /= fLen; + y /= fLen; + z /= fLen; +} + +inline Vector3 StringToVector(char* pString) +{ + Vector3 vRet; + + int length = 0; + while (pString[length]) + { + if ((pString[length] == '<') || (pString[length] == '>')) + pString[length] = '\0'; + length++; + } + + int startOfFloat = 1; + int currentIndex = 1; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.x = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.y = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + while (pString[currentIndex] && (pString[currentIndex] != ',')) + currentIndex++; + pString[currentIndex] = '\0'; + vRet.z = std::stof(&pString[startOfFloat]); + startOfFloat = ++currentIndex; + + return vRet; +} + +FORCEINLINE void VectorAdd(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x + b.x; + c.y = a.y + b.y; + c.z = a.z + b.z; +} + +FORCEINLINE void VectorSubtract(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x - b.x; + c.y = a.y - b.y; + c.z = a.z - b.z; +} + +FORCEINLINE void VectorMultiply(const Vector3& a, float b, Vector3& c) +{ + c.x = a.x * b; + c.y = a.y * b; + c.z = a.z * b; +} + +FORCEINLINE void VectorMultiply(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x * b.x; + c.y = a.y * b.y; + c.z = a.z * b.z; +} + +FORCEINLINE void VectorDivide(const Vector3& a, float b, Vector3& c) +{ + float oob = 1.0f / b; + c.x = a.x * oob; + c.y = a.y * oob; + c.z = a.z * oob; +} + +FORCEINLINE void VectorDivide(const Vector3& a, const Vector3& b, Vector3& c) +{ + c.x = a.x / b.x; + c.y = a.y / b.y; + c.z = a.z / b.z; +} + +class QAngle +{ +public: + float x; + float y; + float z; + + QAngle(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + QAngle(float _f) : x(_f), y(_f), z(_f) {} + QAngle() : x(0.0f), y(0.0f), z(0.0f) {} + + Vector3 GetNormal() const; + + // todo: more operators maybe + bool operator==(const QAngle& other) + { + return x == other.x && y == other.y && z == other.z; + } +}; + +inline Vector3 QAngle::GetNormal() const +{ + Vector3 ret(cos(DEG2RAD(y)), sin(DEG2RAD(y)), -sin(DEG2RAD(x))); + return ret; +} diff --git a/primedev/core/math/vplane.h b/primedev/core/math/vplane.h new file mode 100644 index 00000000..8b8de423 --- /dev/null +++ b/primedev/core/math/vplane.h @@ -0,0 +1,106 @@ +#pragma once + +typedef int SideType; + +// Used to represent sides of things like planes. +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + +#define VP_EPSILON 0.01f + +class VPlane +{ +public: + VPlane(); + VPlane(const Vector3& vNormal, float dist); + VPlane(const Vector3& vPoint, const QAngle& ang); + + void Init(const Vector3& vNormal, float dist); + + // Return the distance from the point to the plane. + float DistTo(const Vector3& vVec) const; + + // Copy. + VPlane& operator=(const VPlane& thePlane); + + // Returns SIDE_ON, SIDE_FRONT, or SIDE_BACK. + // The epsilon for SIDE_ON can be passed in. + SideType GetPointSide(const Vector3& vPoint, float sideEpsilon = VP_EPSILON) const; + + // Returns SIDE_FRONT or SIDE_BACK. + SideType GetPointSideExact(const Vector3& vPoint) const; + + // Get a point on the plane (normal*dist). + Vector3 GetPointOnPlane() const; + + // Snap the specified point to the plane (along the plane's normal). + Vector3 SnapPointToPlane(const Vector3& vPoint) const; + +public: + Vector3 m_Normal; + float m_Dist; +}; + +//----------------------------------------------------------------------------- +// Inlines. +//----------------------------------------------------------------------------- +inline VPlane::VPlane() {} + +inline VPlane::VPlane(const Vector3& vNormal, float dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline VPlane::VPlane(const Vector3& vPoint, const QAngle& ang) +{ + m_Normal = ang.GetNormal(); + m_Dist = vPoint.x * m_Normal.x + vPoint.y * m_Normal.y + vPoint.z * m_Normal.z; +} + +inline void VPlane::Init(const Vector3& vNormal, float dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline float VPlane::DistTo(const Vector3& vVec) const +{ + return vVec.Dot(m_Normal) - m_Dist; +} + +inline VPlane& VPlane::operator=(const VPlane& thePlane) +{ + m_Normal = thePlane.m_Normal; + m_Dist = thePlane.m_Dist; + return *this; +} + +inline Vector3 VPlane::GetPointOnPlane() const +{ + return m_Normal * m_Dist; +} + +inline Vector3 VPlane::SnapPointToPlane(const Vector3& vPoint) const +{ + return vPoint - m_Normal * DistTo(vPoint); +} + +inline SideType VPlane::GetPointSide(const Vector3& vPoint, float sideEpsilon) const +{ + float fDist; + + fDist = DistTo(vPoint); + if (fDist >= sideEpsilon) + return SIDE_FRONT; + else if (fDist <= -sideEpsilon) + return SIDE_BACK; + else + return SIDE_ON; +} + +inline SideType VPlane::GetPointSideExact(const Vector3& vPoint) const +{ + return DistTo(vPoint) > 0.0f ? SIDE_FRONT : SIDE_BACK; +} diff --git a/NorthstarDLL/core/memalloc.cpp b/primedev/core/memalloc.cpp similarity index 98% rename from NorthstarDLL/core/memalloc.cpp rename to primedev/core/memalloc.cpp index 69ce6f54..0a75bc2b 100644 --- a/NorthstarDLL/core/memalloc.cpp +++ b/primedev/core/memalloc.cpp @@ -1,8 +1,6 @@ #include "core/memalloc.h" #include "core/tier0.h" -using namespace Tier0; - // TODO: rename to malloc and free after removing statically compiled .libs extern "C" void* _malloc_base(size_t n) diff --git a/NorthstarDLL/core/memalloc.h b/primedev/core/memalloc.h similarity index 96% rename from NorthstarDLL/core/memalloc.h rename to primedev/core/memalloc.h index 7df68429..2f383335 100644 --- a/NorthstarDLL/core/memalloc.h +++ b/primedev/core/memalloc.h @@ -1,7 +1,7 @@ #pragma once #include "rapidjson/document.h" -//#include "include/rapidjson/allocators.h" +// #include "include/rapidjson/allocators.h" extern "C" void* _malloc_base(size_t size); extern "C" void* _calloc_base(size_t const count, size_t const size); @@ -17,7 +17,7 @@ void operator delete(void* p) noexcept; class SourceAllocator { - public: +public: static const bool kNeedFree = true; void* Malloc(size_t size) { diff --git a/NorthstarDLL/core/sourceinterface.cpp b/primedev/core/sourceinterface.cpp similarity index 100% rename from NorthstarDLL/core/sourceinterface.cpp rename to primedev/core/sourceinterface.cpp diff --git a/NorthstarDLL/core/sourceinterface.h b/primedev/core/sourceinterface.h similarity index 87% rename from NorthstarDLL/core/sourceinterface.h rename to primedev/core/sourceinterface.h index 474e961b..bbbbd3bc 100644 --- a/NorthstarDLL/core/sourceinterface.h +++ b/primedev/core/sourceinterface.h @@ -1,15 +1,22 @@ #pragma once #include +// 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 class SourceInterface { - private: +private: T* m_interface; - public: +public: SourceInterface(const std::string& moduleName, const std::string& interfaceName) { HMODULE handle = GetModuleHandleA(moduleName.c_str()); diff --git a/primedev/core/tier0.cpp b/primedev/core/tier0.cpp new file mode 100644 index 00000000..dd5ac245 --- /dev/null +++ b/primedev/core/tier0.cpp @@ -0,0 +1,30 @@ +#include "tier0.h" + +IMemAlloc* g_pMemAllocSingleton = nullptr; + +CommandLineType CommandLine; +Plat_FloatTimeType Plat_FloatTime; +ThreadInServerFrameThreadType ThreadInServerFrameThread; + +typedef IMemAlloc* (*CreateGlobalMemAllocType)(); +CreateGlobalMemAllocType CreateGlobalMemAlloc; + +// needs to be a seperate function, since memalloc.cpp calls it +void TryCreateGlobalMemAlloc() +{ + // init memalloc stuff + CreateGlobalMemAlloc = + reinterpret_cast(GetProcAddress(GetModuleHandleA("tier0.dll"), "CreateGlobalMemAlloc")); + g_pMemAllocSingleton = CreateGlobalMemAlloc(); // if it already exists, this returns the preexisting IMemAlloc instance +} + +ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module)) +{ + // shouldn't be necessary, but do this just in case + TryCreateGlobalMemAlloc(); + + // setup tier0 funcs + CommandLine = module.GetExportedFunction("CommandLine").RCast(); + Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast(); + ThreadInServerFrameThread = module.GetExportedFunction("ThreadInServerFrameThread").RCast(); +} diff --git a/primedev/core/tier0.h b/primedev/core/tier0.h new file mode 100644 index 00000000..cc9af39e --- /dev/null +++ b/primedev/core/tier0.h @@ -0,0 +1,63 @@ +#pragma once + +class IMemAlloc +{ +public: + struct VTable + { + void* unknown[1]; // alloc debug + void* (*Alloc)(IMemAlloc* memAlloc, size_t nSize); + void* unknown2[1]; // realloc debug + void* (*Realloc)(IMemAlloc* memAlloc, void* pMem, size_t nSize); + void* unknown3[1]; // free #1 + void (*Free)(IMemAlloc* memAlloc, void* pMem); + void* unknown4[2]; // nullsubs, maybe CrtSetDbgFlag + size_t (*GetSize)(IMemAlloc* memAlloc, void* pMem); + void* unknown5[9]; // they all do literally nothing + void (*DumpStats)(IMemAlloc* memAlloc); + void (*DumpStatsFileBase)(IMemAlloc* memAlloc, const char* pchFileBase); + void* unknown6[4]; + int (*heapchk)(IMemAlloc* memAlloc); + }; + + VTable* m_vtable; +}; + +class CCommandLine +{ +public: + // based on the defs in the 2013 source sdk, but for some reason has an extra function (may be another CreateCmdLine overload?) + // these seem to line up with what they should be though + virtual void CreateCmdLine(const char* commandline) = 0; + virtual void CreateCmdLine(int argc, char** argv) = 0; + virtual void unknown() = 0; + virtual const char* GetCmdLine(void) const = 0; + + virtual const char* CheckParm(const char* psz, const char** ppszValue = 0) const = 0; + virtual void RemoveParm() const = 0; + virtual void AppendParm(const char* pszParm, const char* pszValues) = 0; + + virtual const char* ParmValue(const char* psz, const char* pDefaultVal = 0) const = 0; + virtual int ParmValue(const char* psz, int nDefaultVal) const = 0; + virtual float ParmValue(const char* psz, float flDefaultVal) const = 0; + + virtual int ParmCount() const = 0; + virtual int FindParm(const char* psz) const = 0; + virtual const char* GetParm(int nIndex) const = 0; + virtual void SetParm(int nIndex, char const* pParm) = 0; + + // virtual const char** GetParms() const {} +}; + +extern IMemAlloc* g_pMemAllocSingleton; + +typedef CCommandLine* (*CommandLineType)(); +extern CommandLineType CommandLine; + +typedef double (*Plat_FloatTimeType)(); +extern Plat_FloatTimeType Plat_FloatTime; + +typedef bool (*ThreadInServerFrameThreadType)(); +extern ThreadInServerFrameThreadType ThreadInServerFrameThread; + +void TryCreateGlobalMemAlloc(); diff --git a/primedev/core/tier1.cpp b/primedev/core/tier1.cpp new file mode 100644 index 00000000..f857fdba --- /dev/null +++ b/primedev/core/tier1.cpp @@ -0,0 +1,19 @@ +#include "tier1.h" + +// Note: this file is tier1/interface.cpp in primedev, but given that tier0 is yet to be split +// I am following the existing "pattern" and putting this here + +CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFactoryName) +{ + HMODULE hModule = GetModuleHandleA(svModuleName.c_str()); + + if (!hModule) + { + spdlog::error("Failed to get module handle of '{}'!", svModuleName.c_str()); + exit(EXIT_FAILURE); + } + + CreateInterfaceFn fnCreateInterface = reinterpret_cast(GetProcAddress(hModule, CREATEINTERFACE_PROCNAME)); + + return fnCreateInterface(svFactoryName.c_str(), NULL); +} diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h new file mode 100644 index 00000000..d162e7c8 --- /dev/null +++ b/primedev/core/tier1.h @@ -0,0 +1,12 @@ +#pragma once + +// Note: this file is tier1/interface.h in primedev, but given that tier0 is yet to be split +// I am following the existing "pattern" and putting this here + +#include "memory.h" + +#define CREATEINTERFACE_PROCNAME "CreateInterface" + +typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); + +CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact); diff --git a/primedev/core/vanilla.h b/primedev/core/vanilla.h new file mode 100644 index 00000000..b0797803 --- /dev/null +++ b/primedev/core/vanilla.h @@ -0,0 +1,29 @@ +#pragma once + +/// Determines if we are in vanilla-compatibility mode. +/// In this mode we shouldn't auth with Atlas, which prevents users from joining a +/// non-trusted server. This means that we can unrestrict client/server commands +/// as well as various other small changes for compatibility +class VanillaCompatibility +{ +public: + void SetVanillaCompatibility(bool isVanilla) + { + static bool bInitialised = false; + if (bInitialised) + return; + + bInitialised = true; + m_bIsVanillaCompatible = isVanilla; + } + + bool GetVanillaCompatibility() + { + return m_bIsVanillaCompatible; + } + +private: + bool m_bIsVanillaCompatible = false; +}; + +inline VanillaCompatibility* g_pVanillaCompatibility; diff --git a/NorthstarDLL/dedicated/dedicated.cpp b/primedev/dedicated/dedicated.cpp similarity index 88% rename from NorthstarDLL/dedicated/dedicated.cpp rename to primedev/dedicated/dedicated.cpp index 34282f51..eca9b9f1 100644 --- a/NorthstarDLL/dedicated/dedicated.cpp +++ b/primedev/dedicated/dedicated.cpp @@ -10,8 +10,6 @@ AUTOHOOK_INIT() -using namespace R2; - bool IsDedicatedServer() { static bool result = strstr(GetCommandLineA(), "-dedicated"); @@ -37,21 +35,24 @@ struct CDedicatedExports void Sys_Printf(CDedicatedExports* dedicated, const char* msg) { + NOTE_UNUSED(dedicated); spdlog::info("[DEDICATED SERVER] {}", msg); } void RunServer(CDedicatedExports* dedicated) { + NOTE_UNUSED(dedicated); + spdlog::info("CDedicatedExports::RunServer(): starting"); - spdlog::info(Tier0::CommandLine()->GetCmdLine()); + spdlog::info(CommandLine()->GetCmdLine()); // initialise engine g_pEngine->Frame(); // add +map if no map loading command is present // don't manually execute this from cbuf as users may have it in their startup args anyway, easier just to run from stuffcmds if present - if (!Tier0::CommandLine()->CheckParm("+map") && !Tier0::CommandLine()->CheckParm("+launchplaylist")) - Tier0::CommandLine()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString()); + if (!CommandLine()->CheckParm("+map") && !CommandLine()->CheckParm("+launchplaylist")) + CommandLine()->AppendParm("+map", g_pCVar->FindVar("match_defaultMap")->GetString()); // re-run commandline Cbuf_AddText(Cbuf_GetCurrentPlayer(), "stuffcmds", cmd_source_t::kCommandSrcCode); @@ -61,11 +62,11 @@ void RunServer(CDedicatedExports* dedicated) double frameTitle = 0; while (g_pEngine->m_nQuitting == EngineQuitState::QUIT_NOTQUITTING) { - double frameStart = Tier0::Plat_FloatTime(); + double frameStart = Plat_FloatTime(); g_pEngine->Frame(); std::this_thread::sleep_for( - std::chrono::duration>(g_pGlobals->m_flTickInterval - fmin(Tier0::Plat_FloatTime() - frameStart, 0.25))); + std::chrono::duration>(g_pGlobals->m_flTickInterval - fmin(Plat_FloatTime() - frameStart, 0.25))); } } @@ -88,6 +89,8 @@ class DedicatedConsoleServerPresence : public ServerPresenceReporter HANDLE consoleInputThreadHandle = NULL; DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter) { + NOTE_UNUSED(pThreadParameter); + while (!g_pEngine || !g_pHostState || g_pHostState->m_iCurrentState != HostState_t::HS_RUN) Sleep(1000); @@ -137,7 +140,7 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul { // CModAppSystemGroup::Create // force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment - CMemoryAddress base = module.Offset(0x1C4EBD); + CMemory base = module.Offset(0x1C4EBD); // cmp => mov base.Offset(1).Patch("C6 87"); @@ -215,13 +218,13 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul // make sure it still gets registered // add cmdline args that are good for dedi - Tier0::CommandLine()->AppendParm("-nomenuvid", 0); - Tier0::CommandLine()->AppendParm("-nosound", 0); - Tier0::CommandLine()->AppendParm("-windowed", 0); - Tier0::CommandLine()->AppendParm("-nomessagebox", 0); - Tier0::CommandLine()->AppendParm("+host_preload_shaders", "0"); - Tier0::CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); - Tier0::CommandLine()->AppendParm("+community_frame_run", "0"); + CommandLine()->AppendParm("-nomenuvid", 0); + CommandLine()->AppendParm("-nosound", 0); + CommandLine()->AppendParm("-windowed", 0); + CommandLine()->AppendParm("-nomessagebox", 0); + CommandLine()->AppendParm("+host_preload_shaders", "0"); + CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); + CommandLine()->AppendParm("+community_frame_run", "0"); // use presence reporter for console title DedicatedConsoleServerPresence* presenceReporter = new DedicatedConsoleServerPresence; @@ -231,7 +234,7 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul RegisterCustomSink(std::make_shared()); // Disable Quick Edit mode to reduce chance of user unintentionally hanging their server by selecting something. - if (!Tier0::CommandLine()->CheckParm("-bringbackquickedit")) + if (!CommandLine()->CheckParm("-bringbackquickedit")) { HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; @@ -253,7 +256,7 @@ ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (CModul spdlog::info("Quick Edit enabled by user request"); // create console input thread - if (!Tier0::CommandLine()->CheckParm("-noconsoleinput")) + if (!CommandLine()->CheckParm("-noconsoleinput")) consoleInputThreadHandle = CreateThread(0, 0, ConsoleInputThread, 0, 0, NULL); else spdlog::info("Console input disabled by user request"); @@ -264,7 +267,7 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, (CModule module)) // disable origin on dedicated // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without // an origin id as a client for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server - module.GetExport("Tier0_InitOrigin").Patch("C3"); + module.GetExportedFunction("Tier0_InitOrigin").Patch("C3"); } // clang-format off @@ -288,7 +291,7 @@ ON_DLL_LOAD_DEDI("server.dll", DedicatedServerGameDLL, (CModule module)) { AUTOHOOK_DISPATCH_MODULE(server.dll) - if (Tier0::CommandLine()->CheckParm("-nopakdedi")) + if (CommandLine()->CheckParm("-nopakdedi")) { module.Offset(0x6BA350).Patch("C3"); // dont load skins.rson from rpak if we don't have rpaks, as loading it will cause a crash module.Offset(0x6BA300).Patch( diff --git a/NorthstarDLL/dedicated/dedicated.h b/primedev/dedicated/dedicated.h similarity index 100% rename from NorthstarDLL/dedicated/dedicated.h rename to primedev/dedicated/dedicated.h diff --git a/NorthstarDLL/dedicated/dedicatedlogtoclient.cpp b/primedev/dedicated/dedicatedlogtoclient.cpp similarity index 69% rename from NorthstarDLL/dedicated/dedicatedlogtoclient.cpp rename to primedev/dedicated/dedicatedlogtoclient.cpp index a48b1b39..48954d34 100644 --- a/NorthstarDLL/dedicated/dedicatedlogtoclient.cpp +++ b/primedev/dedicated/dedicatedlogtoclient.cpp @@ -1,11 +1,11 @@ #include "dedicatedlogtoclient.h" #include "engine/r2engine.h" -void (*CGameClient__ClientPrintf)(R2::CBaseClient* pClient, const char* fmt, ...); +void (*CGameClient__ClientPrintf)(CBaseClient* pClient, const char* fmt, ...); void DedicatedServerLogToClientSink::custom_sink_it_(const custom_log_msg& msg) { - if (*R2::g_pServerState == R2::server_state_t::ss_dead) + if (*g_pServerState == server_state_t::ss_dead) return; enum class eSendPrintsToClient @@ -15,17 +15,17 @@ void DedicatedServerLogToClientSink::custom_sink_it_(const custom_log_msg& msg) ALL }; - static const ConVar* Cvar_dedi_sendPrintsToClient = R2::g_pCVar->FindVar("dedi_sendPrintsToClient"); + static const ConVar* Cvar_dedi_sendPrintsToClient = g_pCVar->FindVar("dedi_sendPrintsToClient"); eSendPrintsToClient eSendPrints = static_cast(Cvar_dedi_sendPrintsToClient->GetInt()); if (eSendPrints == eSendPrintsToClient::NONE) return; std::string sLogMessage = fmt::format("[DEDICATED SERVER] [{}] {}", level_names[msg.level], msg.payload); - for (int i = 0; i < R2::g_pGlobals->m_nMaxClients; i++) + for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) { - R2::CBaseClient* pClient = &R2::g_pClientArray[i]; + CBaseClient* pClient = &g_pClientArray[i]; - if (pClient->m_Signon >= R2::eSignonState::CONNECTED) + if (pClient->m_Signon >= eSignonState::CONNECTED) { CGameClient__ClientPrintf(pClient, sLogMessage.c_str()); @@ -37,6 +37,7 @@ void DedicatedServerLogToClientSink::custom_sink_it_(const custom_log_msg& msg) void DedicatedServerLogToClientSink::sink_it_(const spdlog::details::log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("sink_it_ called on DedicatedServerLogToClientSink with pure log_msg. This is an error!"); } @@ -44,5 +45,5 @@ void DedicatedServerLogToClientSink::flush_() {} ON_DLL_LOAD_DEDI("engine.dll", DedicatedServerLogToClient, (CModule module)) { - CGameClient__ClientPrintf = module.Offset(0x1016A0).RCast(); + CGameClient__ClientPrintf = module.Offset(0x1016A0).RCast(); } diff --git a/NorthstarDLL/dedicated/dedicatedlogtoclient.h b/primedev/dedicated/dedicatedlogtoclient.h similarity index 95% rename from NorthstarDLL/dedicated/dedicatedlogtoclient.h rename to primedev/dedicated/dedicatedlogtoclient.h index 16f1e584..82f4c56b 100644 --- a/NorthstarDLL/dedicated/dedicatedlogtoclient.h +++ b/primedev/dedicated/dedicatedlogtoclient.h @@ -4,7 +4,7 @@ class DedicatedServerLogToClientSink : public CustomSink { - protected: +protected: void custom_sink_it_(const custom_log_msg& msg); void sink_it_(const spdlog::details::log_msg& msg) override; void flush_() override; diff --git a/NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp b/primedev/dedicated/dedicatedmaterialsystem.cpp similarity index 95% rename from NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp rename to primedev/dedicated/dedicatedmaterialsystem.cpp index 5bd6ea93..01078086 100644 --- a/NorthstarDLL/dedicated/dedicatedmaterialsystem.cpp +++ b/primedev/dedicated/dedicatedmaterialsystem.cpp @@ -23,7 +23,7 @@ HRESULT, __stdcall, ( // really call gpu much even with renderthread still being a thing will be using this hook for actual d3d stubbing and stuff later // note: this has been succeeded by the d3d11 and gfsdk stubs, and is only being kept around for posterity and as a fallback option - if (Tier0::CommandLine()->CheckParm("-softwared3d11")) + if (CommandLine()->CheckParm("-softwared3d11")) DriverType = 5; // D3D_DRIVER_TYPE_WARP return D3D11CreateDevice( diff --git a/NorthstarDLL/dllmain.cpp b/primedev/dllmain.cpp similarity index 82% rename from NorthstarDLL/dllmain.cpp rename to primedev/dllmain.cpp index d82c9ffb..1191307f 100644 --- a/NorthstarDLL/dllmain.cpp +++ b/primedev/dllmain.cpp @@ -1,12 +1,12 @@ -#include "dllmain.h" #include "logging/logging.h" #include "logging/crashhandler.h" #include "core/memalloc.h" +#include "core/vanilla.h" #include "config/profile.h" -#include "plugins/plugin_abi.h" #include "plugins/plugins.h" -#include "plugins/pluginbackend.h" +#include "plugins/pluginmanager.h" #include "util/version.h" +#include "util/wininfo.h" #include "squirrel/squirrel.h" #include "server/serverpresence.h" @@ -20,9 +20,13 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + NOTE_UNUSED(hModule); + NOTE_UNUSED(lpReserved); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: + g_NorthstarModule = hModule; + break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: @@ -53,6 +57,10 @@ bool InitialiseNorthstar() bool bAllFatal = strstr(GetCommandLineA(), "-crash_handle_all") != NULL; g_pCrashHandler->SetAllFatal(bAllFatal); + // determine if we are in vanilla-compatibility mode + g_pVanillaCompatibility = new VanillaCompatibility(); + g_pVanillaCompatibility->SetVanillaCompatibility(strstr(GetCommandLineA(), "-vanilla") != NULL); + // Write launcher version to log StartupLog(); @@ -61,7 +69,6 @@ bool InitialiseNorthstar() g_pServerPresence = new ServerPresenceManager(); g_pPluginManager = new PluginManager(); - g_pPluginCommunicationhandler = new PluginCommunicationHandler(); g_pPluginManager->LoadPlugins(); InitialiseSquirrelManagers(); diff --git a/NorthstarDLL/engine/host.cpp b/primedev/engine/host.cpp similarity index 79% rename from NorthstarDLL/engine/host.cpp rename to primedev/engine/host.cpp index 49cc3663..dacb8fc1 100644 --- a/NorthstarDLL/engine/host.cpp +++ b/primedev/engine/host.cpp @@ -22,9 +22,9 @@ void, __fastcall, (bool bDedicated)) // client/server autoexecs on necessary platforms // dedi needs autoexec_ns_server on boot, while non-dedi will run it on on listen server start if (bDedicated) - R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", R2::cmd_source_t::kCommandSrcCode); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); else - R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", R2::cmd_source_t::kCommandSrcCode); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", cmd_source_t::kCommandSrcCode); } ON_DLL_LOAD("engine.dll", Host_Init, (CModule module)) diff --git a/NorthstarDLL/engine/hoststate.cpp b/primedev/engine/hoststate.cpp similarity index 85% rename from NorthstarDLL/engine/hoststate.cpp rename to primedev/engine/hoststate.cpp index 1734a1c3..d5942551 100644 --- a/NorthstarDLL/engine/hoststate.cpp +++ b/primedev/engine/hoststate.cpp @@ -7,18 +7,11 @@ #include "engine/r2engine.h" #include "shared/exploit_fixes/ns_limits.h" #include "squirrel/squirrel.h" -#include "plugins/plugins.h" -#include "plugins/pluginbackend.h" +#include "plugins/pluginmanager.h" AUTOHOOK_INIT() -using namespace R2; - -// use the R2 namespace for game funcs -namespace R2 -{ - CHostState* g_pHostState; -} // namespace R2 +CHostState* g_pHostState; std::string sLastMode; @@ -35,15 +28,14 @@ void ServerStartingOrChangingMap() memset(commandBuf, 0, sizeof(commandBuf)); CCommand tempCommand = *(CCommand*)&commandBuf; if (sLastMode.length() && - CCommand__Tokenize( - tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), R2::cmd_source_t::kCommandSrcCode)) + CCommand__Tokenize(tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), cmd_source_t::kCommandSrcCode)) _Cmd_Exec_f(tempCommand, false, false); memset(commandBuf, 0, sizeof(commandBuf)); if (CCommand__Tokenize( tempCommand, fmt::format("exec server/setup_gamemode_{}", sLastMode = Cvar_mp_gamemode->GetString()).c_str(), - R2::cmd_source_t::kCommandSrcCode)) + cmd_source_t::kCommandSrcCode)) { _Cmd_Exec_f(tempCommand, false, false); } @@ -73,18 +65,18 @@ void, __fastcall, (CHostState* self)) // need to do this to ensure we don't go to private match if (g_pServerAuthentication->m_bNeedLocalAuthForNewgame) - SetCurrentPlaylist("tdm"); + R2::SetCurrentPlaylist("tdm"); ServerStartingOrChangingMap(); - double dStartTime = Tier0::Plat_FloatTime(); + double dStartTime = Plat_FloatTime(); CHostState__State_NewGame(self); - spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime); + spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime); // setup server presence g_pServerPresence->CreatePresence(); g_pServerPresence->SetMap(g_pHostState->m_levelName, true); - g_pServerPresence->SetPlaylist(GetCurrentPlaylistName()); + g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName()); g_pServerPresence->SetPort(Cvar_hostport->GetInt()); g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false; @@ -107,9 +99,9 @@ void, __fastcall, (CHostState* self)) g_pCVar->FindVar("net_data_block_enabled")->SetValue(true); g_pServerAuthentication->m_bStartingLocalSPGame = true; - double dStartTime = Tier0::Plat_FloatTime(); + double dStartTime = Plat_FloatTime(); CHostState__State_LoadGame(self); - spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime); + spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime); // no server presence, can't do it because no map name in hoststate // and also not super important for sp saves really @@ -126,9 +118,9 @@ void, __fastcall, (CHostState* self)) ServerStartingOrChangingMap(); - double dStartTime = Tier0::Plat_FloatTime(); + double dStartTime = Plat_FloatTime(); CHostState__State_ChangeLevelMP(self); - spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime); + spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime); g_pServerPresence->SetMap(g_pHostState->m_levelName); } @@ -151,7 +143,7 @@ void, __fastcall, (CHostState* self)) memset(commandBuf, 0, sizeof(commandBuf)); CCommand tempCommand = *(CCommand*)&commandBuf; if (CCommand__Tokenize( - tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), R2::cmd_source_t::kCommandSrcCode)) + tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), cmd_source_t::kCommandSrcCode)) { _Cmd_Exec_f(tempCommand, false, false); Cbuf_Execute(); @@ -168,7 +160,7 @@ void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime)) { CHostState__FrameUpdate(self, flCurrentTime, flFrameTime); - if (*R2::g_pServerState == R2::server_state_t::ss_active) + if (*g_pServerState == server_state_t::ss_active) { // update server presence g_pServerPresence->RunFrame(flCurrentTime); diff --git a/primedev/engine/hoststate.h b/primedev/engine/hoststate.h new file mode 100644 index 00000000..290903ab --- /dev/null +++ b/primedev/engine/hoststate.h @@ -0,0 +1,41 @@ +#pragma once + +enum class HostState_t +{ + HS_NEW_GAME = 0, + HS_LOAD_GAME, + HS_CHANGE_LEVEL_SP, + HS_CHANGE_LEVEL_MP, + HS_RUN, + HS_GAME_SHUTDOWN, + HS_SHUTDOWN, + HS_RESTART, +}; + +struct CHostState +{ +public: + HostState_t m_iCurrentState; + HostState_t m_iNextState; + + float m_vecLocation[3]; + float m_angLocation[3]; + + char m_levelName[32]; + char m_mapGroupName[32]; + char m_landmarkName[32]; + char m_saveName[32]; + float m_flShortFrameTime; // run a few one-tick frames to avoid large timesteps while loading assets + + bool m_activeGame; + bool m_bRememberLocation; + bool m_bBackgroundLevel; + bool m_bWaitingForConnection; + bool m_bLetToolsOverrideLoadGameEnts; // During a load game, this tells Foundry to override ents that are selected in Hammer. + bool m_bSplitScreenConnect; + bool m_bGameHasShutDownAndFlushedMemory; // This is false once we load a map into memory, and set to true once the map is unloaded + // and all memory flushed + bool m_bWorkshopMapDownloadPending; +}; + +extern CHostState* g_pHostState; diff --git a/NorthstarDLL/engine/r2engine.cpp b/primedev/engine/r2engine.cpp similarity index 51% rename from NorthstarDLL/engine/r2engine.cpp rename to primedev/engine/r2engine.cpp index 67a628fd..88500376 100644 --- a/NorthstarDLL/engine/r2engine.cpp +++ b/primedev/engine/r2engine.cpp @@ -1,28 +1,22 @@ #include "r2engine.h" -using namespace R2; +Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer; +Cbuf_AddTextType Cbuf_AddText; +Cbuf_ExecuteType Cbuf_Execute; -// use the R2 namespace for game funcs -namespace R2 -{ - Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer; - Cbuf_AddTextType Cbuf_AddText; - Cbuf_ExecuteType Cbuf_Execute; +bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, cmd_source_t commandSource); - bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); +CEngine* g_pEngine; - CEngine* g_pEngine; +void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...); +CBaseClient* g_pClientArray; - void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...); - CBaseClient* g_pClientArray; +server_state_t* g_pServerState; - server_state_t* g_pServerState; +char* g_pModName = + nullptr; // we cant set this up here atm since we dont have an offset to it in engine, instead we store it in IsRespawnMod - char* g_pModName = - nullptr; // we cant set this up here atm since we dont have an offset to it in engine, instead we store it in IsRespawnMod - - CGlobalVars* g_pGlobals; -} // namespace R2 +CGlobalVars* g_pGlobals; ON_DLL_LOAD("engine.dll", R2Engine, (CModule module)) { @@ -30,7 +24,7 @@ ON_DLL_LOAD("engine.dll", R2Engine, (CModule module)) Cbuf_AddText = module.Offset(0x1203B0).RCast(); Cbuf_Execute = module.Offset(0x1204B0).RCast(); - CCommand__Tokenize = module.Offset(0x418380).RCast(); + CCommand__Tokenize = module.Offset(0x418380).RCast(); g_pEngine = module.Offset(0x7D70C8).Deref().RCast(); diff --git a/primedev/engine/r2engine.h b/primedev/engine/r2engine.h new file mode 100644 index 00000000..c8b2e013 --- /dev/null +++ b/primedev/engine/r2engine.h @@ -0,0 +1,301 @@ +#pragma once +#include "shared/keyvalues.h" + +// Cbuf +enum class ECommandTarget_t +{ + CBUF_FIRST_PLAYER = 0, + CBUF_LAST_PLAYER = 1, // MAX_SPLITSCREEN_CLIENTS - 1, MAX_SPLITSCREEN_CLIENTS = 2 + CBUF_SERVER = CBUF_LAST_PLAYER + 1, + + CBUF_COUNT, +}; + +enum class cmd_source_t +{ + // Added to the console buffer by gameplay code. Generally unrestricted. + kCommandSrcCode, + + // Sent from code via engine->ClientCmd, which is restricted to commands visible + // via FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS. + kCommandSrcClientCmd, + + // Typed in at the console or via a user key-bind. Generally unrestricted, although + // the client will throttle commands sent to the server this way to 16 per second. + kCommandSrcUserInput, + + // Came in over a net connection as a clc_stringcmd + // host_client will be valid during this state. + // + // Restricted to FCVAR_GAMEDLL commands (but not convars) and special non-ConCommand + // server commands hardcoded into gameplay code (e.g. "joingame") + kCommandSrcNetClient, + + // Received from the server as the client + // + // Restricted to commands with FCVAR_SERVER_CAN_EXECUTE + kCommandSrcNetServer, + + // Being played back from a demo file + // + // Not currently restricted by convar flag, but some commands manually ignore calls + // from this source. FIXME: Should be heavily restricted as demo commands can come + // from untrusted sources. + kCommandSrcDemoFile, + + // Invalid value used when cleared + kCommandSrcInvalid = -1 +}; + +typedef ECommandTarget_t (*Cbuf_GetCurrentPlayerType)(); +extern Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer; + +typedef void (*Cbuf_AddTextType)(ECommandTarget_t eTarget, const char* text, cmd_source_t source); +extern Cbuf_AddTextType Cbuf_AddText; + +typedef void (*Cbuf_ExecuteType)(); +extern Cbuf_ExecuteType Cbuf_Execute; + +extern bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, cmd_source_t commandSource); + +// CEngine + +enum EngineQuitState +{ + QUIT_NOTQUITTING = 0, + QUIT_TODESKTOP, + QUIT_RESTART +}; + +enum class EngineState_t +{ + DLL_INACTIVE = 0, // no dll + DLL_ACTIVE, // engine is focused + DLL_CLOSE, // closing down dll + DLL_RESTART, // engine is shutting down but will restart right away + DLL_PAUSED, // engine is paused, can become active from this state +}; + +class CEngine +{ +public: + virtual void unknown() = 0; // unsure if this is where + virtual bool Load(bool dedicated, const char* baseDir) = 0; + virtual void Unload() = 0; + virtual void SetNextState(EngineState_t iNextState) = 0; + virtual EngineState_t GetState() = 0; + virtual void Frame() = 0; + virtual double GetFrameTime() = 0; + virtual float GetCurTime() = 0; + + EngineQuitState m_nQuitting; + EngineState_t m_nDllState; + EngineState_t m_nNextDllState; + double m_flCurrentTime; + float m_flFrameTime; + double m_flPreviousTime; + float m_flFilteredTime; + float m_flMinFrameTime; // Expected duration of a frame, or zero if it is unlimited. +}; + +extern CEngine* g_pEngine; + +extern void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...); + +#pragma once +typedef enum +{ + NA_NULL = 0, + NA_LOOPBACK, + NA_IP, +} netadrtype_t; + +#pragma pack(push, 1) +typedef struct netadr_s +{ + netadrtype_t type; + unsigned char ip[16]; // IPv6 + // IPv4's 127.0.0.1 is [::ffff:127.0.0.1], that is: + // 00 00 00 00 00 00 00 00 00 00 FF FF 7F 00 00 01 + unsigned short port; +} netadr_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct netpacket_s +{ + netadr_t adr; // sender address + // int source; // received source + char unk[10]; + double received_time; + unsigned char* data; // pointer to raw packet data + void* message; // easy bitbuf data access // 'inpacket.message' etc etc (pointer) + char unk2[16]; + int size; + + // bf_read message; // easy bitbuf data access // 'inpacket.message' etc etc (pointer) + // int size; // size in bytes + // int wiresize; // size in bytes before decompression + // bool stream; // was send as stream + // struct netpacket_s* pNext; // for internal use, should be NULL in public +} netpacket_t; +#pragma pack(pop) + +// #56169 $DB69 PData size +// #512 $200 Trailing data +// #100 $64 Safety buffer +const int PERSISTENCE_MAX_SIZE = 0xDDCD; + +// note: NOT_READY and READY are the only entries we have here that are defined by the vanilla game +// entries after this are custom and used to determine the source of persistence, e.g. whether it is local or remote +enum class ePersistenceReady : char +{ + NOT_READY, + READY = 3, + READY_INSECURE = 3, + READY_REMOTE +}; + +enum class eSignonState : int +{ + NONE = 0, // no state yet; about to connect + CHALLENGE = 1, // client challenging server; all OOB packets + CONNECTED = 2, // client is connected to server; netchans ready + NEW = 3, // just got serverinfo and string tables + PRESPAWN = 4, // received signon buffers + GETTINGDATA = 5, // respawn-defined signonstate, assumedly this is for persistence + SPAWN = 6, // ready to receive entity packets + FIRSTSNAP = 7, // another respawn-defined one + FULL = 8, // we are fully connected; first non-delta packet received + CHANGELEVEL = 9, // server is changing level; please wait +}; + +class CBaseClient +{ +public: + char _unk_0x0[22]; // 0x0 ( Size: 22 ) + char m_Name[64]; // 0x16 ( Size: 64 ) + char _unk_0x56[514]; // 0x56 ( Size: 514 ) + KeyValues* m_ConVars; // 0x258 ( Size: 8 ) + char _unk_0x260[64]; // 0x260 ( Size: 64 ) + eSignonState m_Signon; // 0x2a0 ( Size: 4 ) + int32_t m_nDeltaTick; // 0x2a4 ( Size: 4 ) + uint64_t m_nOriginID; // 0x2a8 ( Size: 8 ) + int32_t m_nStringTableAckTick; // 0x2b0 ( Size: 4 ) + int32_t m_nSignonTick; // 0x2b4 ( Size: 4 ) + char _unk_0x2b8[160]; // 0x2b8 ( Size: 180 ) + char m_ClanTag[16]; // 0x358 ( Size: 16 ) + char _unk_0x368[284]; // 0x368 ( Size: 284 ) + bool m_bFakePlayer; // 0x484 ( Size: 1 ) + bool m_bReceivedPacket; // 0x485 ( Size: 1 ) + bool m_bLowViolence; // 0x486 ( Size: 1 ) + bool m_bFullyAuthenticated; // 0x487 ( Size: 1 ) + char _unk_0x488[24]; // 0x488 ( Size: 24 ) + ePersistenceReady m_iPersistenceReady; // 0x4a0 ( Size: 1 ) + char _unk_0x4a1[89]; // 0x4a1 ( Size: 89 ) + char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE]; // 0x4fa ( Size: 56781 ) + char _unk_0xe2c7[4665]; // 0xe2c7 ( Size: 4665 ) + char m_UID[32]; // 0xf500 ( Size: 32 ) + char _unk_0xf520[123400]; // 0xf520 ( Size: 123400 ) +}; +static_assert(sizeof(CBaseClient) == 0x2D728); +static_assert(offsetof(CBaseClient, m_Name) == 0x16); +static_assert(offsetof(CBaseClient, m_ConVars) == 0x258); +static_assert(offsetof(CBaseClient, m_Signon) == 0x2A0); +static_assert(offsetof(CBaseClient, m_nDeltaTick) == 0x2A4); +static_assert(offsetof(CBaseClient, m_nOriginID) == 0x2A8); +static_assert(offsetof(CBaseClient, m_nStringTableAckTick) == 0x2B0); +static_assert(offsetof(CBaseClient, m_nSignonTick) == 0x2B4); +static_assert(offsetof(CBaseClient, m_ClanTag) == 0x358); +static_assert(offsetof(CBaseClient, m_bFakePlayer) == 0x484); +static_assert(offsetof(CBaseClient, m_bReceivedPacket) == 0x485); +static_assert(offsetof(CBaseClient, m_bLowViolence) == 0x486); +static_assert(offsetof(CBaseClient, m_bFullyAuthenticated) == 0x487); +static_assert(offsetof(CBaseClient, m_iPersistenceReady) == 0x4A0); +static_assert(offsetof(CBaseClient, m_PersistenceBuffer) == 0x4FA); +static_assert(offsetof(CBaseClient, m_UID) == 0xF500); + +extern CBaseClient* g_pClientArray; + +enum server_state_t +{ + ss_dead = 0, // Dead + ss_loading, // Spawning + ss_active, // Running + ss_paused, // Running, but paused +}; + +extern server_state_t* g_pServerState; + +extern char* g_pModName; + +enum class GameMode_t : int +{ + NO_MODE = 0, + MP_MODE, + SP_MODE, +}; + +class CGlobalVars +{ +public: + // Absolute time (per frame still - Use Plat_FloatTime() for a high precision real time + // perf clock, but not that it doesn't obey host_timescale/host_framerate) + double m_flRealTime; // 0x0 ( Size: 8 ) + + // Absolute frame counter - continues to increase even if game is paused + int m_nFrameCount; // 0x8 ( Size: 4 ) + + // Non-paused frametime + float m_flAbsoluteFrameTime; // 0xc ( Size: 4 ) + + // Current time + // + // On the client, this (along with tickcount) takes a different meaning based on what + // piece of code you're in: + // + // - While receiving network packets (like in PreDataUpdate/PostDataUpdate and proxies), + // this is set to the SERVER TICKCOUNT for that packet. There is no interval between + // the server ticks. + // [server_current_Tick * tick_interval] + // + // - While rendering, this is the exact client clock + // [client_current_tick * tick_interval + interpolation_amount] + // + // - During prediction, this is based on the client's current tick: + // [client_current_tick * tick_interval] + float m_flCurTime; // 0x10 ( Size: 4 ) + + char _unk_0x14[28]; // 0x14 ( Size: 28 ) + + // Time spent on last server or client frame (has nothing to do with think intervals) + float m_flFrameTime; // 0x30 ( Size: 4 ) + + // current maxplayers setting + int m_nMaxClients; // 0x34 ( Size: 4 ) + GameMode_t m_nGameMode; // 0x38 ( Size: 4 ) + + // Simulation ticks - does not increase when game is paused + // this is weird and doesn't seem to increase once per frame? + uint32_t m_nTickCount; // 0x3c ( Size: 4 ) + + // Simulation tick interval + float m_flTickInterval; // 0x40 ( Size: 4 ) + char _unk_0x44[28]; // 0x44 ( Size: 28 ) + + const char* m_pMapName; // 0x60 ( Size: 8 ) + int m_nMapVersion; // 0x68 ( Size: 4 ) +}; +static_assert(offsetof(CGlobalVars, m_flRealTime) == 0x0); +static_assert(offsetof(CGlobalVars, m_nFrameCount) == 0x8); +static_assert(offsetof(CGlobalVars, m_flAbsoluteFrameTime) == 0xc); +static_assert(offsetof(CGlobalVars, m_flCurTime) == 0x10); +static_assert(offsetof(CGlobalVars, m_flFrameTime) == 0x30); +static_assert(offsetof(CGlobalVars, m_nMaxClients) == 0x34); +static_assert(offsetof(CGlobalVars, m_nGameMode) == 0x38); +static_assert(offsetof(CGlobalVars, m_nTickCount) == 0x3c); +static_assert(offsetof(CGlobalVars, m_flTickInterval) == 0x40); +static_assert(offsetof(CGlobalVars, m_pMapName) == 0x60); +static_assert(offsetof(CGlobalVars, m_nMapVersion) == 0x68); + +extern CGlobalVars* g_pGlobals; diff --git a/NorthstarDLL/engine/runframe.cpp b/primedev/engine/runframe.cpp similarity index 89% rename from NorthstarDLL/engine/runframe.cpp rename to primedev/engine/runframe.cpp index 40a619bb..ddfd9253 100644 --- a/NorthstarDLL/engine/runframe.cpp +++ b/primedev/engine/runframe.cpp @@ -7,7 +7,7 @@ AUTOHOOK_INIT() // clang-format off AUTOHOOK(CEngine__Frame, engine.dll + 0x1C8650, -void, __fastcall, (R2::CEngine* self)) +void, __fastcall, (CEngine* self)) // clang-format on { CEngine__Frame(self); diff --git a/NorthstarDLL/logging/crashhandler.cpp b/primedev/logging/crashhandler.cpp similarity index 98% rename from NorthstarDLL/logging/crashhandler.cpp rename to primedev/logging/crashhandler.cpp index 5dec315a..75ca8544 100644 --- a/NorthstarDLL/logging/crashhandler.cpp +++ b/primedev/logging/crashhandler.cpp @@ -4,6 +4,7 @@ #include "util/version.h" #include "mods/modmanager.h" #include "plugins/plugins.h" +#include "plugins/pluginmanager.h" #include @@ -98,8 +99,12 @@ BOOL WINAPI ConsoleCtrlRoutine(DWORD dwCtrlType) // Purpose: Constructor //----------------------------------------------------------------------------- CCrashHandler::CCrashHandler() - : m_hExceptionFilter(nullptr), m_pExceptionInfos(nullptr), m_bHasSetConsolehandler(false), m_bAllExceptionsFatal(false), - m_bHasShownCrashMsg(false), m_bState(false) + : m_hExceptionFilter(nullptr) + , m_pExceptionInfos(nullptr) + , m_bHasSetConsolehandler(false) + , m_bAllExceptionsFatal(false) + , m_bHasShownCrashMsg(false) + , m_bState(false) { Init(); } @@ -520,9 +525,9 @@ void CCrashHandler::FormatLoadedPlugins() if (g_pPluginManager) { spdlog::error("Loaded Plugins:"); - for (const Plugin& plugin : g_pPluginManager->m_vLoadedPlugins) + for (const Plugin& plugin : g_pPluginManager->GetLoadedPlugins()) { - spdlog::error("\t{}", plugin.name); + spdlog::error("\t{}", plugin.GetName()); } } } diff --git a/NorthstarDLL/logging/crashhandler.h b/primedev/logging/crashhandler.h similarity index 99% rename from NorthstarDLL/logging/crashhandler.h rename to primedev/logging/crashhandler.h index e18de948..c059a8ca 100644 --- a/NorthstarDLL/logging/crashhandler.h +++ b/primedev/logging/crashhandler.h @@ -7,7 +7,7 @@ //----------------------------------------------------------------------------- class CCrashHandler { - public: +public: CCrashHandler(); ~CCrashHandler(); @@ -77,7 +77,7 @@ class CCrashHandler //----------------------------------------------------------------------------- void WriteMinidump(); - private: +private: PVOID m_hExceptionFilter; EXCEPTION_POINTERS* m_pExceptionInfos; diff --git a/NorthstarDLL/logging/logging.cpp b/primedev/logging/logging.cpp similarity index 97% rename from NorthstarDLL/logging/logging.cpp rename to primedev/logging/logging.cpp index 3416bb8c..72171e25 100644 --- a/NorthstarDLL/logging/logging.cpp +++ b/primedev/logging/logging.cpp @@ -70,6 +70,7 @@ void CreateLogFiles() void ExternalConsoleSink::sink_it_(const spdlog::details::log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("sink_it_ called on SourceConsoleSink with pure log_msg. This is an error!"); } @@ -95,18 +96,18 @@ void ExternalConsoleSink::custom_sink_it_(const custom_log_msg& msg) std::string name {msg.logger_name.begin(), msg.logger_name.end()}; std::string name_str = "[NAME]"; - int name_pos = str.find(name_str); + size_t name_pos = str.find(name_str); str.replace(name_pos, name_str.length(), msg.origin->ANSIColor + "[" + name + "]" + default_color); std::string level_str = "[LVL]"; - int level_pos = str.find(level_str); + size_t level_pos = str.find(level_str); str.replace(level_pos, level_str.length(), levelColor + "[" + std::string(level_names[msg.level]) + "]" + default_color); out += str; } // print the string to the console - this is definitely bad i think HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); - auto ignored = WriteConsoleA(handle, out.c_str(), std::strlen(out.c_str()), nullptr, nullptr); + auto ignored = WriteConsoleA(handle, out.c_str(), (DWORD)std::strlen(out.c_str()), nullptr, nullptr); (void)ignored; } diff --git a/NorthstarDLL/logging/logging.h b/primedev/logging/logging.h similarity index 98% rename from NorthstarDLL/logging/logging.h rename to primedev/logging/logging.h index af4b506c..be41cb39 100644 --- a/NorthstarDLL/logging/logging.h +++ b/primedev/logging/logging.h @@ -13,7 +13,7 @@ class ColoredLogger; struct custom_log_msg : spdlog::details::log_msg { - public: +public: custom_log_msg(ColoredLogger* origin, spdlog::details::log_msg msg) : origin(origin), spdlog::details::log_msg(msg) {} ColoredLogger* origin; @@ -21,17 +21,18 @@ struct custom_log_msg : spdlog::details::log_msg class CustomSink : public spdlog::sinks::base_sink { - public: +public: void custom_log(const custom_log_msg& msg); virtual void custom_sink_it_(const custom_log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("Pure virtual call to CustomSink::custom_sink_it_"); } }; class ColoredLogger : public spdlog::logger { - public: +public: std::string ANSIColor; SourceColor SRCColor; @@ -117,7 +118,7 @@ static const char* level_names[] {"trac", "dbug", "info", "warn", "errr", "crit" // spdlog logger, for cool colour things class ExternalConsoleSink : public CustomSink { - private: +private: std::map m_LogColours = { {spdlog::level::trace, NS::Colors::TRACE.ToANSIColor()}, {spdlog::level::debug, NS::Colors::DEBUG.ToANSIColor()}, @@ -129,7 +130,7 @@ class ExternalConsoleSink : public CustomSink std::string default_color = "\033[39;49m"; - protected: +protected: void sink_it_(const spdlog::details::log_msg& msg) override; void custom_sink_it_(const custom_log_msg& msg); void flush_() override; diff --git a/NorthstarDLL/logging/loghooks.cpp b/primedev/logging/loghooks.cpp similarity index 98% rename from NorthstarDLL/logging/loghooks.cpp rename to primedev/logging/loghooks.cpp index 41e1bce2..dcd9b85a 100644 --- a/NorthstarDLL/logging/loghooks.cpp +++ b/primedev/logging/loghooks.cpp @@ -24,7 +24,7 @@ enum class TextMsgPrintType_t class ICenterPrint { - public: +public: virtual void ctor() = 0; virtual void Clear(void) = 0; virtual void ColorPrint(int r, int g, int b, int a, wchar_t* text) = 0; @@ -108,6 +108,8 @@ AUTOHOOK(Hook_fprintf, engine.dll + 0x51B1F0, int,, (void* const stream, const char* const format, ...)) // clang-format on { + NOTE_UNUSED(stream); + va_list va; va_start(va, format); @@ -139,6 +141,7 @@ AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80, void, __fastcall, (void* pEngineServer, SpewType_t type, const char* format, va_list args)) // clang-format on { + NOTE_UNUSED(pEngineServer); if (!Cvar_spewlog_enable->GetBool()) return; @@ -235,6 +238,8 @@ AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530, bool,, (void* thisptr, uintptr_t msg)) // clang-format on { + NOTE_UNUSED(thisptr); + char* text = *(char**)(msg + 0x20); auto endpos = strlen(text); diff --git a/NorthstarDLL/logging/loghooks.h b/primedev/logging/loghooks.h similarity index 100% rename from NorthstarDLL/logging/loghooks.h rename to primedev/logging/loghooks.h diff --git a/NorthstarDLL/logging/sourceconsole.cpp b/primedev/logging/sourceconsole.cpp similarity index 97% rename from NorthstarDLL/logging/sourceconsole.cpp rename to primedev/logging/sourceconsole.cpp index e436d1d4..55be4723 100644 --- a/NorthstarDLL/logging/sourceconsole.cpp +++ b/primedev/logging/sourceconsole.cpp @@ -8,6 +8,7 @@ SourceInterface* g_pSourceGameConsole; void ConCommand_toggleconsole(const CCommand& arg) { + NOTE_UNUSED(arg); if ((*g_pSourceGameConsole)->IsConsoleVisible()) (*g_pSourceGameConsole)->Hide(); else @@ -16,11 +17,13 @@ void ConCommand_toggleconsole(const CCommand& arg) void ConCommand_showconsole(const CCommand& arg) { + NOTE_UNUSED(arg); (*g_pSourceGameConsole)->Activate(); } void ConCommand_hideconsole(const CCommand& arg) { + NOTE_UNUSED(arg); (*g_pSourceGameConsole)->Hide(); } @@ -47,6 +50,7 @@ void SourceConsoleSink::custom_sink_it_(const custom_log_msg& msg) void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg) { + NOTE_UNUSED(msg); throw std::runtime_error("sink_it_ called on SourceConsoleSink with pure log_msg. This is an error!"); } diff --git a/NorthstarDLL/logging/sourceconsole.h b/primedev/logging/sourceconsole.h similarity index 96% rename from NorthstarDLL/logging/sourceconsole.h rename to primedev/logging/sourceconsole.h index 3abcc470..44d73843 100644 --- a/NorthstarDLL/logging/sourceconsole.h +++ b/primedev/logging/sourceconsole.h @@ -5,14 +5,14 @@ class EditablePanel { - public: +public: virtual ~EditablePanel() = 0; unsigned char unknown[0x2B0]; }; class IConsoleDisplayFunc { - public: +public: virtual void ColorPrint(const SourceColor& clr, const char* pMessage) = 0; virtual void Print(const char* pMessage) = 0; virtual void DPrint(const char* pMessage) = 0; @@ -24,7 +24,7 @@ class CConsolePanel : public EditablePanel, public IConsoleDisplayFunc class CConsoleDialog { - public: +public: struct VTable { void* unknown[298]; @@ -38,7 +38,7 @@ class CConsoleDialog class CGameConsole { - public: +public: virtual ~CGameConsole() = 0; // activates the console, makes it visible and brings it to the foreground @@ -66,7 +66,7 @@ extern SourceInterface* g_pSourceGameConsole; // spdlog logger class SourceConsoleSink : public CustomSink { - private: +private: std::map m_LogColours = { {spdlog::level::trace, NS::Colors::TRACE.ToSourceColor()}, {spdlog::level::debug, NS::Colors::DEBUG.ToSourceColor()}, @@ -76,7 +76,7 @@ class SourceConsoleSink : public CustomSink {spdlog::level::critical, NS::Colors::CRIT.ToSourceColor()}, {spdlog::level::off, NS::Colors::OFF.ToSourceColor()}}; - protected: +protected: void custom_sink_it_(const custom_log_msg& msg); void sink_it_(const spdlog::details::log_msg& msg) override; void flush_() override; diff --git a/NorthstarDLL/masterserver/masterserver.cpp b/primedev/masterserver/masterserver.cpp similarity index 92% rename from NorthstarDLL/masterserver/masterserver.cpp rename to primedev/masterserver/masterserver.cpp index 64f172c9..e7bb1132 100644 --- a/NorthstarDLL/masterserver/masterserver.cpp +++ b/primedev/masterserver/masterserver.cpp @@ -3,11 +3,14 @@ #include "shared/playlist.h" #include "server/auth/serverauthentication.h" #include "core/tier0.h" +#include "core/vanilla.h" #include "engine/r2engine.h" #include "mods/modmanager.h" #include "shared/misccommands.h" +#include "util/utils.h" #include "util/version.h" #include "server/auth/bansystem.h" +#include "dedicated/dedicated.h" #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" @@ -62,7 +65,7 @@ void SetCommonHttpClientOptions(CURL* curl) // seconds. curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // curl_easy_setopt(curl, CURLOPT_STDERR, stdout); - if (Tier0::CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work + if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); @@ -87,7 +90,7 @@ size_t CurlWriteToStringBufferCallback(char* contents, size_t size, size_t nmemb void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, const char* originToken) { - if (m_bOriginAuthWithMasterServerInProgress) + if (m_bOriginAuthWithMasterServerInProgress || g_pVanillaCompatibility->GetVanillaCompatibility()) return; // do this here so it's instantly set @@ -116,6 +119,13 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, co curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); CURLcode result = curl_easy_perform(curl); + ScopeGuard cleanup( + [&] + { + m_bOriginAuthWithMasterServerInProgress = false; + m_bOriginAuthWithMasterServerDone = true; + curl_easy_cleanup(curl); + }); if (result == CURLcode::CURLE_OK) { @@ -129,13 +139,13 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, co spdlog::error( "Failed reading origin auth info response: encountered parse error \"{}\"", rapidjson::GetParseError_En(originAuthInfo.GetParseError())); - goto REQUEST_END_CLEANUP; + return; } if (!originAuthInfo.IsObject() || !originAuthInfo.HasMember("success")) { spdlog::error("Failed reading origin auth info response: malformed response object {}", readBuffer); - goto REQUEST_END_CLEANUP; + return; } if (originAuthInfo["success"].IsTrue() && originAuthInfo.HasMember("token") && originAuthInfo["token"].IsString()) @@ -172,12 +182,6 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, co spdlog::error("Failed performing northstar origin auth: error {}", curl_easy_strerror(result)); m_bSuccessfullyConnected = false; } - - // we goto this instead of returning so we always hit this - REQUEST_END_CLEANUP: - m_bOriginAuthWithMasterServerInProgress = false; - m_bOriginAuthWithMasterServerDone = true; - curl_easy_cleanup(curl); }); requestThread.detach(); @@ -211,6 +215,13 @@ void MasterServerManager::RequestServerList() curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); CURLcode result = curl_easy_perform(curl); + ScopeGuard cleanup( + [&] + { + m_bRequestingServerList = false; + m_bScriptRequestingServerList = false; + curl_easy_cleanup(curl); + }); if (result == CURLcode::CURLE_OK) { @@ -224,20 +235,20 @@ void MasterServerManager::RequestServerList() spdlog::error( "Failed reading masterserver response: encountered parse error \"{}\"", rapidjson::GetParseError_En(serverInfoJson.GetParseError())); - goto REQUEST_END_CLEANUP; + return; } if (serverInfoJson.IsObject() && serverInfoJson.HasMember("error")) { spdlog::error("Failed reading masterserver response: got fastify error response"); spdlog::error(readBuffer); - goto REQUEST_END_CLEANUP; + return; } if (!serverInfoJson.IsArray()) { spdlog::error("Failed reading masterserver response: root object is not an array"); - goto REQUEST_END_CLEANUP; + return; } rapidjson::GenericArray serverArray = serverInfoJson.GetArray(); @@ -249,7 +260,7 @@ void MasterServerManager::RequestServerList() if (!serverObj.IsObject()) { spdlog::error("Failed reading masterserver response: member of server array is not an object"); - goto REQUEST_END_CLEANUP; + return; } // todo: verify json props are fine before adding to m_remoteServers @@ -340,12 +351,6 @@ void MasterServerManager::RequestServerList() spdlog::error("Failed requesting servers: error {}", curl_easy_strerror(result)); m_bSuccessfullyConnected = false; } - - // we goto this instead of returning so we always hit this - REQUEST_END_CLEANUP: - m_bRequestingServerList = false; - m_bScriptRequestingServerList = false; - curl_easy_cleanup(curl); }); requestThread.detach(); @@ -372,6 +377,7 @@ void MasterServerManager::RequestMainMenuPromos() curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); CURLcode result = curl_easy_perform(curl); + ScopeGuard cleanup([&] { curl_easy_cleanup(curl); }); if (result == CURLcode::CURLE_OK) { @@ -385,20 +391,20 @@ void MasterServerManager::RequestMainMenuPromos() spdlog::error( "Failed reading masterserver main menu promos response: encountered parse error \"{}\"", rapidjson::GetParseError_En(mainMenuPromoJson.GetParseError())); - goto REQUEST_END_CLEANUP; + return; } if (!mainMenuPromoJson.IsObject()) { spdlog::error("Failed reading masterserver main menu promos response: root object is not an object"); - goto REQUEST_END_CLEANUP; + return; } if (mainMenuPromoJson.HasMember("error")) { spdlog::error("Failed reading masterserver response: got fastify error response"); spdlog::error(readBuffer); - goto REQUEST_END_CLEANUP; + return; } if (!mainMenuPromoJson.HasMember("newInfo") || !mainMenuPromoJson["newInfo"].IsObject() || @@ -426,7 +432,7 @@ void MasterServerManager::RequestMainMenuPromos() !mainMenuPromoJson["smallButton2"]["ImageIndex"].IsNumber()) { spdlog::error("Failed reading masterserver main menu promos response: malformed json object"); - goto REQUEST_END_CLEANUP; + return; } m_sMainMenuPromoData.newInfoTitle1 = mainMenuPromoJson["newInfo"]["Title1"].GetString(); @@ -453,10 +459,6 @@ void MasterServerManager::RequestMainMenuPromos() spdlog::error("Failed requesting main menu promos: error {}", curl_easy_strerror(result)); m_bSuccessfullyConnected = false; } - - REQUEST_END_CLEANUP: - // nothing lol - curl_easy_cleanup(curl); }); requestThread.detach(); @@ -465,7 +467,7 @@ void MasterServerManager::RequestMainMenuPromos() void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* playerToken) { // dont wait, just stop if we're trying to do 2 auth requests at once - if (m_bAuthenticatingWithGameServer) + if (m_bAuthenticatingWithGameServer || g_pVanillaCompatibility->GetVanillaCompatibility()) return; m_bAuthenticatingWithGameServer = true; @@ -493,6 +495,21 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); CURLcode result = curl_easy_perform(curl); + ScopeGuard cleanup( + [&] + { + m_bAuthenticatingWithGameServer = false; + m_bScriptAuthenticatingWithGameServer = false; + + if (m_bNewgameAfterSelfAuth) + { + // pretty sure this is threadsafe? + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "ns_end_reauth_and_leave_to_lobby", cmd_source_t::kCommandSrcCode); + m_bNewgameAfterSelfAuth = false; + } + + curl_easy_cleanup(curl); + }); if (result == CURLcode::CURLE_OK) { @@ -506,13 +523,13 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* spdlog::error( "Failed reading masterserver authentication response: encountered parse error \"{}\"", rapidjson::GetParseError_En(authInfoJson.GetParseError())); - goto REQUEST_END_CLEANUP; + return; } if (!authInfoJson.IsObject()) { spdlog::error("Failed reading masterserver authentication response: root object is not an object"); - goto REQUEST_END_CLEANUP; + return; } if (authInfoJson.HasMember("error")) @@ -527,13 +544,13 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* else m_sAuthFailureReason = "No error message provided"; - goto REQUEST_END_CLEANUP; + return; } if (!authInfoJson["success"].IsTrue()) { spdlog::error("Authentication with masterserver failed: \"success\" is not true"); - goto REQUEST_END_CLEANUP; + return; } if (!authInfoJson.HasMember("success") || !authInfoJson.HasMember("id") || !authInfoJson["id"].IsString() || @@ -541,7 +558,7 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* !authInfoJson.HasMember("persistentData") || !authInfoJson["persistentData"].IsArray()) { spdlog::error("Failed reading masterserver authentication response: malformed json object"); - goto REQUEST_END_CLEANUP; + return; } RemoteAuthData newAuthData {}; @@ -559,7 +576,7 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* if (!byte.IsUint() || byte.GetUint() > 255) { spdlog::error("Failed reading masterserver authentication response: malformed json object"); - goto REQUEST_END_CLEANUP; + return; } newAuthData.pdata[i++] = static_cast(byte.GetUint()); @@ -579,19 +596,6 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* m_bSuccessfullyAuthenticatedWithGameServer = false; m_bScriptAuthenticatingWithGameServer = false; } - - REQUEST_END_CLEANUP: - m_bAuthenticatingWithGameServer = false; - m_bScriptAuthenticatingWithGameServer = false; - - if (m_bNewgameAfterSelfAuth) - { - // pretty sure this is threadsafe? - R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "ns_end_reauth_and_leave_to_lobby", R2::cmd_source_t::kCommandSrcCode); - m_bNewgameAfterSelfAuth = false; - } - - curl_easy_cleanup(curl); }); requestThread.detach(); @@ -600,7 +604,7 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char* void MasterServerManager::AuthenticateWithServer(const char* uid, const char* playerToken, RemoteServerInfo server, const char* password) { // dont wait, just stop if we're trying to do 2 auth requests at once - if (m_bAuthenticatingWithGameServer) + if (m_bAuthenticatingWithGameServer || g_pVanillaCompatibility->GetVanillaCompatibility()) return; m_bAuthenticatingWithGameServer = true; @@ -631,7 +635,7 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); { - char* escapedPassword = curl_easy_escape(curl, passwordStr.c_str(), passwordStr.length()); + char* escapedPassword = curl_easy_escape(curl, passwordStr.c_str(), (int)passwordStr.length()); curl_easy_setopt( curl, @@ -649,6 +653,13 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl } CURLcode result = curl_easy_perform(curl); + ScopeGuard cleanup( + [&] + { + m_bAuthenticatingWithGameServer = false; + m_bScriptAuthenticatingWithGameServer = false; + curl_easy_cleanup(curl); + }); if (result == CURLcode::CURLE_OK) { @@ -662,13 +673,13 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl spdlog::error( "Failed reading masterserver authentication response: encountered parse error \"{}\"", rapidjson::GetParseError_En(connectionInfoJson.GetParseError())); - goto REQUEST_END_CLEANUP; + return; } if (!connectionInfoJson.IsObject()) { spdlog::error("Failed reading masterserver authentication response: root object is not an object"); - goto REQUEST_END_CLEANUP; + return; } if (connectionInfoJson.HasMember("error")) @@ -683,13 +694,13 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl else m_sAuthFailureReason = "No error message provided"; - goto REQUEST_END_CLEANUP; + return; } if (!connectionInfoJson["success"].IsTrue()) { spdlog::error("Authentication with masterserver failed: \"success\" is not true"); - goto REQUEST_END_CLEANUP; + return; } if (!connectionInfoJson.HasMember("success") || !connectionInfoJson.HasMember("ip") || @@ -698,7 +709,7 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl !connectionInfoJson["authToken"].IsString()) { spdlog::error("Failed reading masterserver authentication response: malformed json object"); - goto REQUEST_END_CLEANUP; + return; } m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString()); @@ -723,11 +734,6 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl m_bSuccessfullyAuthenticatedWithGameServer = false; m_bScriptAuthenticatingWithGameServer = false; } - - REQUEST_END_CLEANUP: - m_bAuthenticatingWithGameServer = false; - m_bScriptAuthenticatingWithGameServer = false; - curl_easy_cleanup(curl); }); requestThread.detach(); @@ -895,12 +901,12 @@ void MasterServerManager::ProcessConnectionlessPacketSigreq1(std::string data) return; } - if (pdata.length() > R2::PERSISTENCE_MAX_SIZE) + if (pdata.length() > PERSISTENCE_MAX_SIZE) { spdlog::error( "failed to make Atlas connect pdata request {}: pdata is too large (max={} len={})", token, - R2::PERSISTENCE_MAX_SIZE, + PERSISTENCE_MAX_SIZE, pdata.length()); return; } @@ -918,7 +924,7 @@ void MasterServerManager::ProcessConnectionlessPacketSigreq1(std::string data) CURL* curl = curl_easy_init(); SetCommonHttpClientOptions(curl); - char* rejectEnc = curl_easy_escape(curl, reject.c_str(), reject.length()); + char* rejectEnc = curl_easy_escape(curl, reject.c_str(), (int)reject.length()); if (!rejectEnc) { spdlog::error("failed to handle Atlas connect request {}: failed to escape reject", token); @@ -982,6 +988,7 @@ void MasterServerManager::ProcessConnectionlessPacketSigreq1(std::string data) void ConCommand_ns_fetchservers(const CCommand& args) { + NOTE_UNUSED(args); g_pMasterServerManager->RequestServerList(); } @@ -1002,6 +1009,7 @@ ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), ( void MasterServerPresenceReporter::CreatePresence(const ServerPresence* pServerPresence) { + NOTE_UNUSED(pServerPresence); m_nNumRegistrationAttempts = 0; } @@ -1020,7 +1028,7 @@ void MasterServerPresenceReporter::ReportPresence(const ServerPresence* pServerP } // Make sure to wait til the cooldown is over for DUPLICATE_SERVER failures. - if (Tier0::Plat_FloatTime() < m_fNextAddServerAttemptTime) + if (Plat_FloatTime() < m_fNextAddServerAttemptTime) { return; } @@ -1045,6 +1053,7 @@ void MasterServerPresenceReporter::ReportPresence(const ServerPresence* pServerP void MasterServerPresenceReporter::DestroyPresence(const ServerPresence* pServerPresence) { + NOTE_UNUSED(pServerPresence); // Don't call this if we don't have a server id. if (!*g_pMasterServerManager->m_sOwnServerId) { @@ -1080,6 +1089,8 @@ void MasterServerPresenceReporter::DestroyPresence(const ServerPresence* pServer void MasterServerPresenceReporter::RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) { + NOTE_UNUSED(flCurrentTime); + NOTE_UNUSED(pServerPresence); // Check if we're already running an InternalAddServer() call in the background. // If so, grab the result if it's ready. if (addServerFuture.valid()) @@ -1122,13 +1133,15 @@ void MasterServerPresenceReporter::RunFrame(double flCurrentTime, const ServerPr case MasterServerReportPresenceResult::FailedDuplicateServer: ++m_nNumRegistrationAttempts; // Wait at least twenty seconds until we re-attempt to add the server. - m_fNextAddServerAttemptTime = Tier0::Plat_FloatTime() + 20.0f; + m_fNextAddServerAttemptTime = Plat_FloatTime() + 20.0f; break; } if (m_nNumRegistrationAttempts >= MAX_REGISTRATION_ATTEMPTS) { - spdlog::error("Reached max ms server registration attempts."); + spdlog::log( + IsDedicatedServer() ? spdlog::level::level_enum::err : spdlog::level::level_enum::warn, + "Reached max ms server registration attempts."); } } else if (updateServerFuture.valid()) @@ -1181,7 +1194,7 @@ void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServ addServerFuture = std::async( std::launch::async, - [threadedPresence, modInfo, hostname] + [threadedPresence, modInfo, hostname, pServerPresence] { CURL* curl = curl_easy_init(); SetCommonHttpClientOptions(curl); @@ -1209,6 +1222,11 @@ void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServ return data; }; + // don't log errors if we wouldn't actually show up in the server list anyway (stop tickets) + // except for dedis, for which this error logging is actually pretty important + bool shouldLogError = IsDedicatedServer() || (!strstr(pServerPresence->m_MapName, "mp_lobby") && + strstr(pServerPresence->m_PlaylistName, "private_match")); + curl_mime_data(part, modInfo.c_str(), modInfo.size()); curl_mime_name(part, "modinfo"); curl_mime_filename(part, "modinfo.json"); @@ -1258,22 +1276,37 @@ void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServ // No retry. if (serverAddedJson.HasParseError()) { - spdlog::error( - "Failed reading masterserver authentication response: encountered parse error \"{}\"", - rapidjson::GetParseError_En(serverAddedJson.GetParseError())); + if (shouldLogError) + spdlog::error( + "Failed reading masterserver authentication response: encountered parse error \"{}\"", + rapidjson::GetParseError_En(serverAddedJson.GetParseError())); return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry); } if (!serverAddedJson.IsObject()) { - spdlog::error("Failed reading masterserver authentication response: root object is not an object"); + if (shouldLogError) + spdlog::error("Failed reading masterserver authentication response: root object is not an object"); return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry); } + // Log request id for easier debugging when combining with logs on masterserver + if (serverAddedJson.HasMember("id")) + { + spdlog::info("Request id: {}", serverAddedJson["id"].GetString()); + } + else + { + spdlog::error("Couldn't find request id in response"); + } + if (serverAddedJson.HasMember("error")) { - spdlog::error("Failed reading masterserver response: got fastify error response"); - spdlog::error(readBuffer); + if (shouldLogError) + { + spdlog::error("Failed reading masterserver response: got fastify error response"); + spdlog::error(readBuffer); + } // If this is DUPLICATE_SERVER, we'll retry adding the server every 20 seconds. // The master server will only update its internal server list and clean up dead servers on certain events. @@ -1282,7 +1315,8 @@ void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServ if (serverAddedJson["error"].HasMember("enum") && strcmp(serverAddedJson["error"]["enum"].GetString(), "DUPLICATE_SERVER") == 0) { - spdlog::error("Cooling down while the master server cleans the dead server entry, if any."); + if (shouldLogError) + spdlog::error("Cooling down while the master server cleans the dead server entry, if any."); return ReturnCleanup(MasterServerReportPresenceResult::FailedDuplicateServer); } @@ -1292,14 +1326,16 @@ void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServ if (!serverAddedJson["success"].IsTrue()) { - spdlog::error("Adding server to masterserver failed: \"success\" is not true"); + if (shouldLogError) + spdlog::error("Adding server to masterserver failed: \"success\" is not true"); return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry); } if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() || !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString()) { - spdlog::error("Failed reading masterserver response: malformed json object"); + if (shouldLogError) + spdlog::error("Failed reading masterserver response: malformed json object"); return ReturnCleanup(MasterServerReportPresenceResult::FailedNoRetry); } @@ -1311,7 +1347,8 @@ void MasterServerPresenceReporter::InternalAddServer(const ServerPresence* pServ } else { - spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result)); + if (shouldLogError) + spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result)); return ReturnCleanup(MasterServerReportPresenceResult::FailedNoConnect); } }); diff --git a/NorthstarDLL/masterserver/masterserver.h b/primedev/masterserver/masterserver.h similarity index 98% rename from NorthstarDLL/masterserver/masterserver.h rename to primedev/masterserver/masterserver.h index c4fa56c2..570db619 100644 --- a/NorthstarDLL/masterserver/masterserver.h +++ b/primedev/masterserver/masterserver.h @@ -13,14 +13,14 @@ extern ConVar* Cvar_ns_curl_log_enable; struct RemoteModInfo { - public: +public: std::string Name; std::string Version; }; class RemoteServerInfo { - public: +public: char id[33]; // 32 bytes + nullterminator // server info @@ -37,7 +37,7 @@ class RemoteServerInfo // connection stuff bool requiresPassword; - public: +public: RemoteServerInfo( const char* newId, const char* newName, @@ -52,7 +52,7 @@ class RemoteServerInfo struct RemoteServerConnectionInfo { - public: +public: char authToken[32]; in_addr ip; @@ -61,7 +61,7 @@ struct RemoteServerConnectionInfo struct MainMenuPromoData { - public: +public: std::string newInfoTitle1; std::string newInfoTitle2; std::string newInfoTitle3; @@ -82,11 +82,11 @@ struct MainMenuPromoData class MasterServerManager { - private: +private: bool m_bRequestingServerList = false; bool m_bAuthenticatingWithGameServer = false; - public: +public: char m_sOwnServerId[33]; char m_sOwnServerAuthToken[33]; char m_sOwnClientAuthToken[33]; @@ -123,7 +123,7 @@ class MasterServerManager std::unordered_set m_handledServerConnections; - public: +public: MasterServerManager(); void ClearServerList(); @@ -156,7 +156,7 @@ enum class MasterServerReportPresenceResult class MasterServerPresenceReporter : public ServerPresenceReporter { - public: +public: /** Full data returned in the std::future of a MasterServerPresenceReporter::ReportPresence() call. */ struct ReportPresenceResultData { @@ -180,7 +180,7 @@ class MasterServerPresenceReporter : public ServerPresenceReporter // Called every frame. void RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) override; - protected: +protected: // Contains the async logic to add the server to the MS. void InternalAddServer(const ServerPresence* pServerPresence); diff --git a/NorthstarDLL/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp similarity index 71% rename from NorthstarDLL/mods/autodownload/moddownloader.cpp rename to primedev/mods/autodownload/moddownloader.cpp index 9c1489c6..3a946263 100644 --- a/NorthstarDLL/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -1,4 +1,5 @@ #include "moddownloader.h" +#include "util/utils.h" #include #include #include @@ -22,18 +23,18 @@ ModDownloader::ModDownloader() if (clachar) { std::string url; - int iFlagStringLength = strlen(CUSTOM_MODS_URL_FLAG); + size_t iFlagStringLength = strlen(CUSTOM_MODS_URL_FLAG); std::string cla = std::string(clachar); if (strncmp(cla.substr(iFlagStringLength, 1).c_str(), "\"", 1)) { - int space = cla.find(" "); + size_t space = cla.find(" "); url = cla.substr(iFlagStringLength, space - iFlagStringLength); } else { std::string quote = "\""; - int quote1 = cla.find(quote); - int quote2 = (cla.substr(quote1 + 1)).find(quote); + size_t quote1 = cla.find(quote); + size_t quote2 = (cla.substr(quote1 + 1)).find(quote); url = cla.substr(quote1 + 1, quote2); } spdlog::info("Found custom verified mods URL in command line argument: {}", url); @@ -74,6 +75,7 @@ void ModDownloader::FetchModsListFromAPI() curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &readBuffer); curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteToString); result = curl_easy_perform(easyhandle); + ScopeGuard cleanup([&] { curl_easy_cleanup(easyhandle); }); if (result == CURLcode::CURLE_OK) { @@ -82,7 +84,7 @@ void ModDownloader::FetchModsListFromAPI() else { spdlog::error("Fetching mods list failed: {}", curl_easy_strerror(result)); - goto REQUEST_END_CLEANUP; + return; } // Load mods list into local state @@ -110,9 +112,6 @@ void ModDownloader::FetchModsListFromAPI() } spdlog::info("Done loading verified mods list."); - - REQUEST_END_CLEANUP: - curl_easy_cleanup(easyhandle); }); requestThread.detach(); } @@ -124,37 +123,21 @@ size_t WriteData(void* ptr, size_t size, size_t nmemb, FILE* stream) return written; } -void FetchModSync(std::promise>&& p, std::string_view url, fs::path downloadPath) +int ModDownloader::ModFetchingProgressCallback( + void* ptr, curl_off_t totalDownloadSize, curl_off_t finishedDownloadSize, curl_off_t totalToUpload, curl_off_t nowUploaded) { - bool failed = false; - FILE* fp = fopen(downloadPath.generic_string().c_str(), "wb"); - CURLcode result; - CURL* easyhandle; - easyhandle = curl_easy_init(); - - curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(easyhandle, CURLOPT_URL, url.data()); - curl_easy_setopt(easyhandle, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, fp); - curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteData); - result = curl_easy_perform(easyhandle); - - if (result == CURLcode::CURLE_OK) + NOTE_UNUSED(totalToUpload); + NOTE_UNUSED(nowUploaded); + if (totalDownloadSize != 0 && finishedDownloadSize != 0) { - spdlog::info("Mod archive successfully fetched."); - goto REQUEST_END_CLEANUP; - } - else - { - spdlog::error("Fetching mod archive failed: {}", curl_easy_strerror(result)); - failed = true; - goto REQUEST_END_CLEANUP; + ModDownloader* instance = static_cast(ptr); + auto currentDownloadProgress = roundf(static_cast(finishedDownloadSize) / totalDownloadSize * 100); + instance->modState.progress = finishedDownloadSize; + instance->modState.total = totalDownloadSize; + instance->modState.ratio = currentDownloadProgress; } -REQUEST_END_CLEANUP: - curl_easy_cleanup(easyhandle); - fclose(fp); - p.set_value(failed ? std::optional() : std::optional(downloadPath)); + return 0; } std::optional ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion) @@ -170,12 +153,46 @@ std::optional ModDownloader::FetchModFromDistantStore(std::string_view std::filesystem::path downloadPath = std::filesystem::temp_directory_path() / archiveName; spdlog::info(std::format("Downloading archive to {}", downloadPath.generic_string())); + // Update state + modState.state = DOWNLOADING; + // Download the actual archive - std::promise> promise; - auto f = promise.get_future(); - std::thread t(&FetchModSync, std::move(promise), std::string_view(url), downloadPath); - t.join(); - return f.get(); + bool failed = false; + FILE* fp = fopen(downloadPath.generic_string().c_str(), "wb"); + CURLcode result; + CURL* easyhandle; + easyhandle = curl_easy_init(); + + curl_easy_setopt(easyhandle, CURLOPT_URL, url.data()); + curl_easy_setopt(easyhandle, CURLOPT_FAILONERROR, 1L); + + // abort if slower than 30 bytes/sec during 10 seconds + curl_easy_setopt(easyhandle, CURLOPT_LOW_SPEED_TIME, 10L); + curl_easy_setopt(easyhandle, CURLOPT_LOW_SPEED_LIMIT, 30L); + + curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, fp); + curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteData); + curl_easy_setopt(easyhandle, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(easyhandle, CURLOPT_XFERINFOFUNCTION, ModDownloader::ModFetchingProgressCallback); + curl_easy_setopt(easyhandle, CURLOPT_XFERINFODATA, this); + result = curl_easy_perform(easyhandle); + ScopeGuard cleanup( + [&] + { + curl_easy_cleanup(easyhandle); + fclose(fp); + }); + + if (result == CURLcode::CURLE_OK) + { + spdlog::info("Mod archive successfully fetched."); + return std::optional(downloadPath); + } + else + { + spdlog::error("Fetching mod archive failed: {}", curl_easy_strerror(result)); + return std::optional(); + } } bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecksum) @@ -186,6 +203,9 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks return true; } + // Update state + modState.state = CHECKSUMING; + NTSTATUS status; BCRYPT_ALG_HANDLE algorithmHandle = NULL; BCRYPT_HASH_HANDLE hashHandle = NULL; @@ -198,6 +218,22 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks std::vector buffer(bufferSize, '\0'); std::ifstream fp(modPath.generic_string(), std::ios::binary); + ScopeGuard cleanup( + [&] + { + if (NULL != hashHandle) + { + BCryptDestroyHash(hashHandle); // Handle to hash/MAC object which needs to be destroyed + } + + if (NULL != algorithmHandle) + { + BCryptCloseAlgorithmProvider( + algorithmHandle, // Handle to the algorithm provider which needs to be closed + 0); // Flags + } + }); + // Open an algorithm handle // This sample passes BCRYPT_HASH_REUSABLE_FLAG with BCryptAlgorithmProvider(...) to load a provider which supports reusable hash status = BCryptOpenAlgorithmProvider( @@ -207,7 +243,8 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks BCRYPT_HASH_REUSABLE_FLAG); // Flags; Loads a provider which supports reusable hash if (!NT_SUCCESS(status)) { - goto cleanup; + modState.state = MOD_CORRUPTED; + return false; } // Obtain the length of the hash @@ -220,7 +257,7 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks 0); // Flags if (!NT_SUCCESS(status)) { - // goto cleanup; + modState.state = MOD_CORRUPTED; return false; } @@ -235,13 +272,15 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks 0); // Flags if (!NT_SUCCESS(status)) { - goto cleanup; + modState.state = MOD_CORRUPTED; + return false; } // Hash archive content if (!fp.is_open()) { spdlog::error("Unable to open archive."); + modState.state = FAILED_READING_ARCHIVE; return false; } fp.seekg(0, fp.beg); @@ -254,7 +293,8 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks status = BCryptHashData(hashHandle, (PBYTE)buffer.data(), bytesRead, 0); if (!NT_SUCCESS(status)) { - goto cleanup; + modState.state = MOD_CORRUPTED; + return false; } } } @@ -269,7 +309,8 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks 0); // Flags if (!NT_SUCCESS(status)) { - goto cleanup; + modState.state = MOD_CORRUPTED; + return false; } // Convert hash to string using bytes raw values @@ -282,21 +323,6 @@ bool ModDownloader::IsModLegit(fs::path modPath, std::string_view expectedChecks spdlog::info("Expected checksum: {}", expectedChecksum.data()); spdlog::info("Computed checksum: {}", ss.str()); return expectedChecksum.compare(ss.str()) == 0; - -cleanup: - if (NULL != hashHandle) - { - BCryptDestroyHash(hashHandle); // Handle to hash/MAC object which needs to be destroyed - } - - if (NULL != algorithmHandle) - { - BCryptCloseAlgorithmProvider( - algorithmHandle, // Handle to the algorithm provider which needs to be closed - 0); // Flags - } - - return false; } bool ModDownloader::IsModAuthorized(std::string_view modName, std::string_view modVersion) @@ -316,6 +342,30 @@ bool ModDownloader::IsModAuthorized(std::string_view modName, std::string_view m return versions.count(modVersion.data()) != 0; } +int GetModArchiveSize(unzFile file, unz_global_info64 info) +{ + int totalSize = 0; + + for (int i = 0; i < info.number_entry; i++) + { + char zipFilename[256]; + unz_file_info64 fileInfo; + unzGetCurrentFileInfo64(file, &fileInfo, zipFilename, sizeof(zipFilename), NULL, 0, NULL, 0); + + totalSize += fileInfo.uncompressed_size; + + if ((i + 1) < info.number_entry) + { + unzGoToNextFile(file); + } + } + + // Reset file pointer for archive extraction + unzGoToFirstFile(file); + + return totalSize; +} + void ModDownloader::ExtractMod(fs::path modPath) { unzFile file; @@ -323,10 +373,20 @@ void ModDownloader::ExtractMod(fs::path modPath) fs::path modDirectory; file = unzOpen(modPath.generic_string().c_str()); + ScopeGuard cleanup( + [&] + { + if (unzClose(file) != MZ_OK) + { + spdlog::error("Failed closing mod archive after extraction."); + } + }); + if (file == NULL) { spdlog::error("Cannot open archive located at {}.", modPath.generic_string()); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_READING_ARCHIVE; + return; } unz_global_info64 gi; @@ -335,9 +395,15 @@ void ModDownloader::ExtractMod(fs::path modPath) if (status != UNZ_OK) { spdlog::error("Failed getting information from archive (error code: {})", status); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_READING_ARCHIVE; + return; } + // Update state + modState.state = EXTRACTING; + modState.total = GetModArchiveSize(file, gi); + modState.progress = 0; + // Mod directory name (removing the ".zip" fom the archive name) name = modPath.filename().string(); name = name.substr(0, name.length() - 4); @@ -362,7 +428,8 @@ void ModDownloader::ExtractMod(fs::path modPath) if (!std::filesystem::create_directories(fileDestination.parent_path(), ec) && ec.value() != 0) { spdlog::error("Parent directory ({}) creation failed.", fileDestination.parent_path().generic_string()); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_WRITING_TO_DISK; + return; } } @@ -373,7 +440,8 @@ void ModDownloader::ExtractMod(fs::path modPath) if (!std::filesystem::create_directory(fileDestination, ec) && ec.value() != 0) { spdlog::error("Directory creation failed: {}", ec.message()); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_WRITING_TO_DISK; + return; } } // ...else create file @@ -383,7 +451,8 @@ void ModDownloader::ExtractMod(fs::path modPath) if (unzLocateFile(file, zipFilename, 0) != UNZ_OK) { spdlog::error("File \"{}\" was not found in archive.", zipFilename); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_READING_ARCHIVE; + return; } // Create file @@ -397,7 +466,8 @@ void ModDownloader::ExtractMod(fs::path modPath) if (status != UNZ_OK) { spdlog::error("Could not open file {} from archive.", zipFilename); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_READING_ARCHIVE; + return; } // Create destination file @@ -405,7 +475,8 @@ void ModDownloader::ExtractMod(fs::path modPath) if (fout == NULL) { spdlog::error("Failed creating destination file."); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_WRITING_TO_DISK; + return; } // Allocate memory for buffer @@ -413,7 +484,8 @@ void ModDownloader::ExtractMod(fs::path modPath) if (buffer == NULL) { spdlog::error("Error while allocating memory."); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_WRITING_TO_DISK; + return; } // Extract file to destination @@ -434,12 +506,17 @@ void ModDownloader::ExtractMod(fs::path modPath) break; } } + + // Update extraction stats + modState.progress += bufferSize; + modState.ratio = roundf(static_cast(modState.progress) / modState.total * 100); } while (err > 0); if (err != UNZ_OK) { spdlog::error("An error occurred during file extraction (code: {})", err); - goto EXTRACTION_CLEANUP; + modState.state = FAILED_WRITING_TO_DISK; + return; } err = unzCloseCurrentFile(file); if (err != UNZ_OK) @@ -460,16 +537,10 @@ void ModDownloader::ExtractMod(fs::path modPath) if (status != UNZ_OK) { spdlog::error("Error while browsing archive files (error code: {}).", status); - goto EXTRACTION_CLEANUP; + return; } } } - -EXTRACTION_CLEANUP: - if (unzClose(file) != MZ_OK) - { - spdlog::error("Failed closing mod archive after extraction."); - } } void ModDownloader::DownloadMod(std::string modName, std::string modVersion) @@ -486,67 +557,94 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion) { fs::path archiveLocation; + ScopeGuard cleanup( + [&] + { + try + { + remove(archiveLocation); + } + catch (const std::exception& a) + { + spdlog::error("Error while removing downloaded archive: {}", a.what()); + } + + modState.state = DONE; + spdlog::info("Done downloading {}.", modName); + }); + // Download mod archive std::string expectedHash = verifiedMods[modName].versions[modVersion].checksum; std::optional fetchingResult = FetchModFromDistantStore(std::string_view(modName), std::string_view(modVersion)); if (!fetchingResult.has_value()) { spdlog::error("Something went wrong while fetching archive, aborting."); - goto REQUEST_END_CLEANUP; + modState.state = MOD_FETCHING_FAILED; + return; } archiveLocation = fetchingResult.value(); if (!IsModLegit(archiveLocation, std::string_view(expectedHash))) { spdlog::warn("Archive hash does not match expected checksum, aborting."); - goto REQUEST_END_CLEANUP; + modState.state = MOD_CORRUPTED; + return; } // Extract downloaded mod archive ExtractMod(archiveLocation); - - REQUEST_END_CLEANUP: - try - { - remove(archiveLocation); - } - catch (const std::exception& a) - { - spdlog::error("Error while removing downloaded archive: {}", a.what()); - } - - spdlog::info("Done downloading {}.", modName); }); requestThread.detach(); } -void ConCommandFetchVerifiedMods(const CCommand& args) -{ - g_pModDownloader->FetchModsListFromAPI(); -} - -void ConCommandDownloadMod(const CCommand& args) -{ - if (args.ArgC() < 3) - { - return; - } - - // Split arguments string by whitespaces (https://stackoverflow.com/a/5208977) - std::string buffer; - std::stringstream ss(args.ArgS()); - std::vector tokens; - while (ss >> buffer) - tokens.push_back(buffer); - - std::string modName = tokens[0]; - std::string modVersion = tokens[1]; - g_pModDownloader->DownloadMod(modName, modVersion); -} - ON_DLL_LOAD_RELIESON("engine.dll", ModDownloader, (ConCommand), (CModule module)) { g_pModDownloader = new ModDownloader(); - RegisterConCommand("fetch_verified_mods", ConCommandFetchVerifiedMods, "fetches verified mods list from GitHub repository", FCVAR_NONE); - RegisterConCommand("download_mod", ConCommandDownloadMod, "downloads a mod from remote store", FCVAR_NONE); + g_pModDownloader->FetchModsListFromAPI(); +} + +ADD_SQFUNC( + "bool", NSIsModDownloadable, "string name, string version", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI) +{ + g_pSquirrel->newarray(sqvm, 0); + + const SQChar* modName = g_pSquirrel->getstring(sqvm, 1); + const SQChar* modVersion = g_pSquirrel->getstring(sqvm, 2); + + bool result = g_pModDownloader->IsModAuthorized(modName, modVersion); + g_pSquirrel->pushbool(sqvm, result); + + return SQRESULT_NOTNULL; +} + +ADD_SQFUNC("void", NSDownloadMod, "string name, string version", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI) +{ + const SQChar* modName = g_pSquirrel->getstring(sqvm, 1); + const SQChar* modVersion = g_pSquirrel->getstring(sqvm, 2); + g_pModDownloader->DownloadMod(modName, modVersion); + + return SQRESULT_NOTNULL; +} + +ADD_SQFUNC("ModInstallState", NSGetModInstallState, "", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI) +{ + g_pSquirrel->pushnewstructinstance(sqvm, 4); + + // state + g_pSquirrel->pushinteger(sqvm, g_pModDownloader->modState.state); + g_pSquirrel->sealstructslot(sqvm, 0); + + // progress + g_pSquirrel->pushinteger(sqvm, g_pModDownloader->modState.progress); + g_pSquirrel->sealstructslot(sqvm, 1); + + // total + g_pSquirrel->pushinteger(sqvm, g_pModDownloader->modState.total); + g_pSquirrel->sealstructslot(sqvm, 2); + + // ratio + g_pSquirrel->pushfloat(sqvm, g_pModDownloader->modState.ratio); + g_pSquirrel->sealstructslot(sqvm, 3); + + return SQRESULT_NOTNULL; } diff --git a/NorthstarDLL/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h similarity index 90% rename from NorthstarDLL/mods/autodownload/moddownloader.h rename to primedev/mods/autodownload/moddownloader.h index edaf090b..10df39ce 100644 --- a/NorthstarDLL/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -1,10 +1,10 @@ class ModDownloader { - private: +private: const char* VERIFICATION_FLAG = "-disablemodverification"; const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl="; const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/"; - const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/mods.json"; + const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json"; char* modsListUrl; struct VerifiedModVersion @@ -18,6 +18,16 @@ class ModDownloader }; std::unordered_map verifiedMods = {}; + /** + * Mod archive download callback. + * + * This function is called by curl as it's downloading the mod archive; this + * will retrieve the current `ModDownloader` instance and update its `modState` + * member accordingly. + */ + static int ModFetchingProgressCallback( + void* ptr, curl_off_t totalDownloadSize, curl_off_t finishedDownloadSize, curl_off_t totalToUpload, curl_off_t nowUploaded); + /** * Downloads a mod archive from distant store. * @@ -35,20 +45,6 @@ class ModDownloader */ std::optional FetchModFromDistantStore(std::string_view modName, std::string_view modVersion); - /** - * Checks whether a mod is verified. - * - * A mod is deemed verified/authorized through a manual validation process that is - * described here: https://github.com/R2Northstar/VerifiedMods; in practice, a mod - * is considered authorized if their name AND exact version appear in the - * `verifiedMods` variable. - * - * @param modName name of the mod to be checked - * @param modVersion version of the mod to be checked, must follow semantic versioning - * @returns whether the mod is authorized and can be auto-downloaded - */ - bool IsModAuthorized(std::string_view modName, std::string_view modVersion); - /** * Tells if a mod archive has not been corrupted. * @@ -74,7 +70,7 @@ class ModDownloader */ void ExtractMod(fs::path modPath); - public: +public: ModDownloader(); /** @@ -83,7 +79,7 @@ class ModDownloader * The Northstar auto-downloading feature does NOT allow automatically installing * all mods for various (notably security) reasons; mods that are candidate to * auto-downloading are rather listed on a GitHub repository - * (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/master/mods.json), + * (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json), * which this method gets via a HTTP call to load into local state. * * If list fetching fails, local mods list will be initialized as empty, thus @@ -93,6 +89,20 @@ class ModDownloader */ void FetchModsListFromAPI(); + /** + * Checks whether a mod is verified. + * + * A mod is deemed verified/authorized through a manual validation process that is + * described here: https://github.com/R2Northstar/VerifiedMods; in practice, a mod + * is considered authorized if their name AND exact version appear in the + * `verifiedMods` variable. + * + * @param modName name of the mod to be checked + * @param modVersion version of the mod to be checked, must follow semantic versioning + * @returns whether the mod is authorized and can be auto-downloaded + */ + bool IsModAuthorized(std::string_view modName, std::string_view modVersion); + /** * Downloads a given mod from Thunderstore API to local game profile. * diff --git a/NorthstarDLL/mods/compiled/kb_act.cpp b/primedev/mods/compiled/kb_act.cpp similarity index 95% rename from NorthstarDLL/mods/compiled/kb_act.cpp rename to primedev/mods/compiled/kb_act.cpp index 3fc7ee30..6117fd28 100644 --- a/NorthstarDLL/mods/compiled/kb_act.cpp +++ b/primedev/mods/compiled/kb_act.cpp @@ -14,7 +14,7 @@ void ModManager::BuildKBActionsList() std::ofstream soCompiledKeys(GetCompiledAssetsPath() / KB_ACT_PATH, std::ios::binary); // write vanilla file's content to compiled file - soCompiledKeys << R2::ReadVPKOriginalFile(KB_ACT_PATH); + soCompiledKeys << ReadVPKOriginalFile(KB_ACT_PATH); for (Mod& mod : m_LoadedMods) { diff --git a/NorthstarDLL/mods/compiled/modkeyvalues.cpp b/primedev/mods/compiled/modkeyvalues.cpp similarity index 98% rename from NorthstarDLL/mods/compiled/modkeyvalues.cpp rename to primedev/mods/compiled/modkeyvalues.cpp index fe262a60..e44a81d3 100644 --- a/NorthstarDLL/mods/compiled/modkeyvalues.cpp +++ b/primedev/mods/compiled/modkeyvalues.cpp @@ -56,7 +56,7 @@ void ModManager::TryBuildKeyValues(const char* filename) newKvs += "\"\n"; // load original file, so we can parse out the name of the root obj (e.g. WeaponData for weapons) - std::string originalFile = R2::ReadVPKOriginalFile(filename); + std::string originalFile = ReadVPKOriginalFile(filename); if (!originalFile.length()) { diff --git a/NorthstarDLL/mods/compiled/modpdef.cpp b/primedev/mods/compiled/modpdef.cpp similarity index 98% rename from NorthstarDLL/mods/compiled/modpdef.cpp rename to primedev/mods/compiled/modpdef.cpp index 4b1b12b7..d268a063 100644 --- a/NorthstarDLL/mods/compiled/modpdef.cpp +++ b/primedev/mods/compiled/modpdef.cpp @@ -15,7 +15,7 @@ void ModManager::BuildPdef() fs::path MOD_PDEF_PATH = fs::path(GetCompiledAssetsPath() / MOD_PDEF_SUFFIX); fs::remove(MOD_PDEF_PATH); - std::string pdef = R2::ReadVPKOriginalFile(VPK_PDEF_PATH); + std::string pdef = ReadVPKOriginalFile(VPK_PDEF_PATH); for (Mod& mod : m_LoadedMods) { diff --git a/NorthstarDLL/mods/compiled/modscriptsrson.cpp b/primedev/mods/compiled/modscriptsrson.cpp similarity index 96% rename from NorthstarDLL/mods/compiled/modscriptsrson.cpp rename to primedev/mods/compiled/modscriptsrson.cpp index cbe26651..d130745f 100644 --- a/NorthstarDLL/mods/compiled/modscriptsrson.cpp +++ b/primedev/mods/compiled/modscriptsrson.cpp @@ -13,7 +13,7 @@ void ModManager::BuildScriptsRson() fs::path MOD_SCRIPTS_RSON_PATH = fs::path(GetCompiledAssetsPath() / MOD_SCRIPTS_RSON_SUFFIX); fs::remove(MOD_SCRIPTS_RSON_PATH); - std::string scriptsRson = R2::ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); + std::string scriptsRson = ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); scriptsRson += "\n\n// START MODDED SCRIPT CONTENT\n\n"; // newline before we start custom stuff for (Mod& mod : m_LoadedMods) diff --git a/NorthstarDLL/mods/modmanager.cpp b/primedev/mods/modmanager.cpp similarity index 97% rename from NorthstarDLL/mods/modmanager.cpp rename to primedev/mods/modmanager.cpp index 8ad832c1..268a65a5 100644 --- a/NorthstarDLL/mods/modmanager.cpp +++ b/primedev/mods/modmanager.cpp @@ -104,6 +104,7 @@ Mod::Mod(fs::path modDir, char* jsonBuf) ParseScripts(modJson); ParseLocalization(modJson); ParseDependencies(modJson); + ParsePluginDependencies(modJson); ParseInitScript(modJson); // A mod is remote if it's located in the remote mods folder @@ -483,6 +484,28 @@ void Mod::ParseDependencies(rapidjson_document& json) } } +void Mod::ParsePluginDependencies(rapidjson_document& json) +{ + if (!json.HasMember("PluginDependencies")) + return; + + if (!json["PluginDependencies"].IsArray()) + { + spdlog::warn("'PluginDependencies' field is not an object, skipping..."); + return; + } + + for (auto& name : json["PluginDependencies"].GetArray()) + { + if (!name.IsString()) + continue; + + spdlog::info("Plugin Constant {} defined by {}", name.GetString(), Name); + + PluginDependencyConstants.push_back(name.GetString()); + } +} + void Mod::ParseInitScript(rapidjson_document& json) { if (!json.HasMember("InitScript")) @@ -688,6 +711,11 @@ void ModManager::LoadMods() m_DependencyConstants.emplace(pair); } + for (std::string& dependency : mod.PluginDependencyConstants) + { + m_PluginDependencyConstants.insert(dependency); + } + if (m_bHasEnabledModsCfg && m_EnabledModsCfg.HasMember(mod.Name.c_str())) mod.m_bEnabled = m_EnabledModsCfg[mod.Name.c_str()].IsTrue(); else @@ -696,9 +724,9 @@ void ModManager::LoadMods() if (mod.m_bWasReadSuccessfully) { if (mod.m_bEnabled) - spdlog::info("'{}' loaded successfully", mod.Name); + spdlog::info("'{}' loaded successfully, version {}", mod.Name, mod.Version); else - spdlog::info("'{}' loaded successfully (DISABLED)", mod.Name); + spdlog::info("'{}' loaded successfully, version {} (DISABLED)", mod.Name, mod.Version); m_LoadedMods.push_back(mod); } @@ -732,7 +760,7 @@ void ModManager::LoadMods() { // make sure convar isn't registered yet, unsure if necessary but idk what // behaviour is for defining same convar multiple times - if (!R2::g_pCVar->FindVar(convar->Name.c_str())) + if (!g_pCVar->FindVar(convar->Name.c_str())) { new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str()); } @@ -741,9 +769,8 @@ void ModManager::LoadMods() for (ModConCommand* command : mod.ConCommands) { // make sure command isnt't registered multiple times. - if (!R2::g_pCVar->FindCommand(command->Name.c_str())) + if (!g_pCVar->FindCommand(command->Name.c_str())) { - ConCommand* newCommand = new ConCommand(); std::string funcName = command->Function; RegisterConCommand(command->Name.c_str(), ModConCommandCallback, command->HelpString.c_str(), command->Flags); } @@ -790,7 +817,7 @@ void ModManager::LoadMods() modVpk.m_sVpkPath = (file.path().parent_path() / vpkName).string(); if (m_bHasLoadedMods && modVpk.m_bAutoLoad) - (*R2::g_pFilesystem)->m_vtable->MountVPK(*R2::g_pFilesystem, vpkName.c_str()); + (*g_pFilesystem)->m_vtable->MountVPK(*g_pFilesystem, vpkName.c_str()); } } } @@ -1094,6 +1121,7 @@ void ModManager::CompileAssetsForFile(const char* filename) void ConCommand_reload_mods(const CCommand& args) { + NOTE_UNUSED(args); g_pModManager->LoadMods(); } diff --git a/NorthstarDLL/mods/modmanager.h b/primedev/mods/modmanager.h similarity index 93% rename from NorthstarDLL/mods/modmanager.h rename to primedev/mods/modmanager.h index 813edec7..233f004d 100644 --- a/NorthstarDLL/mods/modmanager.h +++ b/primedev/mods/modmanager.h @@ -7,6 +7,7 @@ #include #include #include +#include const std::string MOD_FOLDER_SUFFIX = "\\mods"; const std::string THUNDERSTORE_MOD_FOLDER_SUFFIX = "\\packages"; @@ -18,7 +19,7 @@ const std::set MODS_BLACKLIST = {"Mod Settings"}; struct ModConVar { - public: +public: std::string Name; std::string DefaultValue; std::string HelpString; @@ -27,7 +28,7 @@ struct ModConVar struct ModConCommand { - public: +public: std::string Name; std::string Function; std::string HelpString; @@ -37,7 +38,7 @@ struct ModConCommand struct ModScriptCallback { - public: +public: ScriptContext Context; // called before the codecallback is executed @@ -50,7 +51,7 @@ struct ModScriptCallback struct ModScript { - public: +public: std::string Path; std::string RunOn; @@ -60,14 +61,14 @@ struct ModScript // these are pretty much identical, could refactor to use the same stuff? struct ModVPKEntry { - public: +public: bool m_bAutoLoad; std::string m_sVpkPath; }; struct ModRpakEntry { - public: +public: bool m_bAutoLoad; std::string m_sPakName; std::string m_sLoadAfterPak; @@ -75,7 +76,7 @@ struct ModRpakEntry class Mod { - public: +public: // runtime stuff bool m_bEnabled = true; bool m_bWasReadSuccessfully = false; @@ -124,29 +125,31 @@ class Mod // hashed with STR_HASH std::unordered_map DependencyConstants; + std::vector PluginDependencyConstants; - public: +public: Mod(fs::path modPath, char* jsonBuf); - private: +private: void ParseConVars(rapidjson_document& json); void ParseConCommands(rapidjson_document& json); void ParseScripts(rapidjson_document& json); void ParseLocalization(rapidjson_document& json); void ParseDependencies(rapidjson_document& json); + void ParsePluginDependencies(rapidjson_document& json); void ParseInitScript(rapidjson_document& json); }; struct ModOverrideFile { - public: +public: Mod* m_pOwningMod; fs::path m_Path; }; class ModManager { - private: +private: bool m_bHasLoadedMods = false; bool m_bHasEnabledModsCfg; rapidjson_document m_EnabledModsCfg; @@ -156,12 +159,13 @@ class ModManager size_t m_hPdefHash; size_t m_hKBActHash; - public: +public: std::vector m_LoadedMods; std::unordered_map m_ModFiles; std::unordered_map m_DependencyConstants; + std::unordered_set m_PluginDependencyConstants; - public: +public: ModManager(); void LoadMods(); void UnloadMods(); diff --git a/NorthstarDLL/mods/modsavefiles.cpp b/primedev/mods/modsavefiles.cpp similarity index 99% rename from NorthstarDLL/mods/modsavefiles.cpp rename to primedev/mods/modsavefiles.cpp index f8e5848c..68e33864 100644 --- a/NorthstarDLL/mods/modsavefiles.cpp +++ b/primedev/mods/modsavefiles.cpp @@ -561,9 +561,9 @@ ON_DLL_LOAD("engine.dll", ModSaveFFiles_Init, (CModule module)) { savePath = fs::path(GetNorthstarPrefix()) / "save_data"; g_pSaveFileManager = new SaveFileManager; - int parm = Tier0::CommandLine()->FindParm("-maxfoldersize"); + int parm = CommandLine()->FindParm("-maxfoldersize"); if (parm) - MAX_FOLDER_SIZE = std::stoi(Tier0::CommandLine()->GetParm(parm)); + MAX_FOLDER_SIZE = std::stoi(CommandLine()->GetParm(parm)); } int GetMaxSaveFolderSize() diff --git a/NorthstarDLL/mods/modsavefiles.h b/primedev/mods/modsavefiles.h similarity index 95% rename from NorthstarDLL/mods/modsavefiles.h rename to primedev/mods/modsavefiles.h index a50fe62c..f9d39723 100644 --- a/NorthstarDLL/mods/modsavefiles.h +++ b/primedev/mods/modsavefiles.h @@ -4,13 +4,13 @@ bool ContainsInvalidChars(std::string str); class SaveFileManager { - public: +public: template void SaveFileAsync(fs::path file, std::string content); template int LoadFileAsync(fs::path file); template void DeleteFileAsync(fs::path file); // Future proofed in that if we ever get multi-threaded SSDs this code will take advantage of them. std::mutex fileMutex; - private: +private: int m_iLastRequestHandle = 0; }; diff --git a/NorthstarDLL/ns_version.h b/primedev/ns_version.h similarity index 100% rename from NorthstarDLL/ns_version.h rename to primedev/ns_version.h diff --git a/NorthstarDLL/pch.h b/primedev/pch.h similarity index 88% rename from NorthstarDLL/pch.h rename to primedev/pch.h index b9ba0e08..577f803c 100644 --- a/NorthstarDLL/pch.h +++ b/primedev/pch.h @@ -29,16 +29,18 @@ typedef void (*callable_v)(void* v); #define assert_msg(exp, msg) assert((exp, msg)) //clang-format on +#define NOTE_UNUSED(var) do { (void)var; } while(false) + #include "core/macros.h" -#include "core/structs.h" #include "core/math/color.h" #include "spdlog/spdlog.h" #include "logging/logging.h" #include "MinHook.h" #include "curl/curl.h" +#include "silver-bun/module.h" +#include "silver-bun/memaddr.h" #include "core/hooks.h" -#include "core/memory.h" #endif diff --git a/primedev/plugins/interfaces/IPluginCallbacks.h b/primedev/plugins/interfaces/IPluginCallbacks.h new file mode 100644 index 00000000..c02ce8a6 --- /dev/null +++ b/primedev/plugins/interfaces/IPluginCallbacks.h @@ -0,0 +1,36 @@ +#ifndef IPLUGIN_CALLBACKS_H +#define IPLUGIN_CALLBACKS_H + +#include +#include +#include "squirrel/squirrel.h" + +// can't use bitwise ops on enum classes but I don't want these in the global namespace (user defined operators suck) +namespace PluginContext +{ + enum : uint64_t + { + DEDICATED = 0x1, + CLIENT = 0x2, + }; +} + +struct PluginNorthstarData +{ + HMODULE pluginHandle; +}; + +class IPluginCallbacks +{ +public: + virtual void + Init(HMODULE northstarModule, const PluginNorthstarData* initData, bool reloaded) = 0; // runs after the plugin is loaded and validated + virtual void Finalize() = 0; // runs after all plugins have been loaded + virtual bool Unload() = 0; // runs just before the library is freed + virtual void OnSqvmCreated(CSquirrelVM* sqvm) = 0; + virtual void OnSqvmDestroying(CSquirrelVM* sqvm) = 0; + virtual void OnLibraryLoaded(HMODULE module, const char* name) = 0; + virtual void RunFrame() = 0; +}; + +#endif diff --git a/primedev/plugins/interfaces/IPluginId.h b/primedev/plugins/interfaces/IPluginId.h new file mode 100644 index 00000000..dc4c548b --- /dev/null +++ b/primedev/plugins/interfaces/IPluginId.h @@ -0,0 +1,31 @@ +#ifndef IPLUGIN_ID_H +#define IPLUGIN_ID_H + +#include +#include "squirrel/squirrelclasstypes.h" + +#define PLUGIN_ID_VERSION "PluginId001" + +// an identifier for the type of string data requested from the plugin +enum class PluginString : int +{ + NAME = 0, + LOG_NAME = 1, + DEPENDENCY_NAME = 2, +}; + +// an identifier for the type of bitflag requested from the plugin +enum class PluginField : int +{ + CONTEXT = 0, +}; + +// an interface that is required from every plugin to query data about it +class IPluginId +{ +public: + virtual const char* GetString(PluginString prop) = 0; + virtual int64_t GetField(PluginField prop) = 0; +}; + +#endif diff --git a/primedev/plugins/interfaces/interface.cpp b/primedev/plugins/interfaces/interface.cpp new file mode 100644 index 00000000..4c006f2c --- /dev/null +++ b/primedev/plugins/interfaces/interface.cpp @@ -0,0 +1,36 @@ +#include +#include "interface.h" + +InterfaceReg* s_pInterfaceRegs; + +InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char* pName) : m_pName(pName) +{ + m_CreateFn = fn; + m_pNext = s_pInterfaceRegs; + s_pInterfaceRegs = this; +} + +void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode) +{ + for (InterfaceReg* pCur = s_pInterfaceRegs; pCur; pCur = pCur->m_pNext) + { + if (strcmp(pCur->m_pName, pName) == 0) + { + if (pReturnCode) + { + *pReturnCode = InterfaceStatus::IFACE_OK; + } + + NS::log::PLUGINSYS->info("creating interface {}", pName); + return pCur->m_CreateFn(); + } + } + + if (pReturnCode) + { + *pReturnCode = InterfaceStatus::IFACE_FAILED; + } + + NS::log::PLUGINSYS->error("could not find interface {}", pName); + return NULL; +} diff --git a/primedev/plugins/interfaces/interface.h b/primedev/plugins/interfaces/interface.h new file mode 100644 index 00000000..440db5b2 --- /dev/null +++ b/primedev/plugins/interfaces/interface.h @@ -0,0 +1,39 @@ +#ifndef INTERFACE_H +#define INTERFACE_H + +typedef void* (*InstantiateInterfaceFn)(); + +// Used internally to register classes. +class InterfaceReg +{ +public: + InterfaceReg(InstantiateInterfaceFn fn, const char* pName); + + InstantiateInterfaceFn m_CreateFn; + const char* m_pName; + InterfaceReg* m_pNext; +}; + +// Use this to expose an interface that can have multiple instances. +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + static void* __Create##className##_interface() \ + { \ + return static_cast(new className); \ + } \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName); + +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + static void* __Create##className##interfaceName##_interface() \ + { \ + return static_cast(&globalVarName); \ + } \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); + +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + static className __g_##className##_singleton; \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) + +EXPORT void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode); + +#endif diff --git a/primedev/plugins/interfaces/sys/ISys.cpp b/primedev/plugins/interfaces/sys/ISys.cpp new file mode 100644 index 00000000..6b0b41dd --- /dev/null +++ b/primedev/plugins/interfaces/sys/ISys.cpp @@ -0,0 +1,66 @@ +#include "plugins/interfaces/interface.h" +#include "ISys.h" +#include "plugins/plugins.h" +#include "plugins/pluginmanager.h" + +class CSys : public ISys +{ +public: + void Log(HMODULE handle, LogLevel level, char* msg) + { + spdlog::level::level_enum spdLevel; + + switch (level) + { + case LogLevel::WARN: + spdLevel = spdlog::level::level_enum::warn; + break; + case LogLevel::ERR: + spdLevel = spdlog::level::level_enum::err; + break; + default: + NS::log::PLUGINSYS->warn("Attempted to log with invalid level {}. Defaulting to info", (int)level); + case LogLevel::INFO: + spdLevel = spdlog::level::level_enum::info; + break; + } + + std::optional plugin = g_pPluginManager->GetPlugin(handle); + if (plugin) + { + plugin->Log(spdLevel, msg); + } + else + { + NS::log::PLUGINSYS->warn("Attempted to log message '{}' with invalid plugin handle {}", msg, static_cast(handle)); + } + } + + void Unload(HMODULE handle) + { + std::optional plugin = g_pPluginManager->GetPlugin(handle); + if (plugin) + { + plugin->Unload(); + } + else + { + NS::log::PLUGINSYS->warn("Attempted to unload plugin with invalid handle {}", static_cast(handle)); + } + } + + void Reload(HMODULE handle) + { + std::optional plugin = g_pPluginManager->GetPlugin(handle); + if (plugin) + { + plugin->Reload(); + } + else + { + NS::log::PLUGINSYS->warn("Attempted to reload plugin with invalid handle {}", static_cast(handle)); + } + } +}; + +EXPOSE_SINGLE_INTERFACE(CSys, ISys, SYS_VERSION); diff --git a/primedev/plugins/interfaces/sys/ISys.h b/primedev/plugins/interfaces/sys/ISys.h new file mode 100644 index 00000000..3e55a6d9 --- /dev/null +++ b/primedev/plugins/interfaces/sys/ISys.h @@ -0,0 +1,21 @@ +#ifndef ILOGGING_H +#define ILOGGING_H + +#define SYS_VERSION "NSSys001" + +enum class LogLevel : int +{ + INFO = 0, + WARN, + ERR, +}; + +class ISys +{ +public: + virtual void Log(HMODULE handle, LogLevel level, char* msg) = 0; + virtual void Unload(HMODULE handle) = 0; + virtual void Reload(HMODULE handle) = 0; +}; + +#endif diff --git a/primedev/plugins/pluginmanager.cpp b/primedev/plugins/pluginmanager.cpp new file mode 100644 index 00000000..718e6956 --- /dev/null +++ b/primedev/plugins/pluginmanager.cpp @@ -0,0 +1,184 @@ +#include "pluginmanager.h" + +#include +#include "plugins.h" +#include "config/profile.h" +#include "core/convar/concommand.h" + +PluginManager* g_pPluginManager; + +const std::vector& PluginManager::GetLoadedPlugins() const +{ + return this->plugins; +} + +const std::optional PluginManager::GetPlugin(HMODULE handle) const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + if (plugin.m_handle == handle) + return plugin; + return std::nullopt; +} + +void PluginManager::LoadPlugin(fs::path path, bool reloaded) +{ + Plugin plugin = Plugin(path.string()); + + if (!plugin.IsValid()) + { + NS::log::PLUGINSYS->warn("Unloading invalid plugin '{}'", path.string()); + plugin.Unload(); + return; + } + + plugins.push_back(plugin); + plugin.Init(reloaded); +} + +inline void FindPlugins(fs::path pluginPath, std::vector& paths) +{ + // ensure dirs exist + if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath)) + { + return; + } + + for (const fs::directory_entry& entry : fs::directory_iterator(pluginPath)) + { + if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") + paths.emplace_back(entry.path()); + } +} + +bool PluginManager::LoadPlugins(bool reloaded) +{ + if (strstr(GetCommandLineA(), "-noplugins") != NULL) + { + NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins"); + return false; + } + + fs::create_directories(GetThunderstoreModFolderPath()); + + std::vector paths; + + pluginPath = GetNorthstarPrefix() + "\\plugins"; + + fs::path libPath = fs::absolute(pluginPath + "\\lib"); + if (fs::exists(libPath) && fs::is_directory(libPath)) + AddDllDirectory(libPath.wstring().c_str()); + + FindPlugins(pluginPath, paths); + + // Special case for Thunderstore mods dir + std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath()); + // Set up regex for `AUTHOR-MOD-VERSION` pattern + std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))"); + for (fs::directory_entry dir : thunderstoreModsDir) + { + fs::path pluginsDir = dir.path() / "plugins"; + // Use regex to match `AUTHOR-MOD-VERSION` pattern + if (!std::regex_match(dir.path().string(), pattern)) + { + spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string()); + continue; // skip loading package that doesn't match + } + + fs::path libDir = fs::absolute(pluginsDir / "lib"); + if (fs::exists(libDir) && fs::is_directory(libDir)) + AddDllDirectory(libDir.wstring().c_str()); + + FindPlugins(pluginsDir, paths); + } + + if (paths.empty()) + { + NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins"); + return false; + } + + for (fs::path path : paths) + { + LoadPlugin(path, reloaded); + } + + InformAllPluginsInitialized(); + + return true; +} + +void PluginManager::ReloadPlugins() +{ + for (const Plugin& plugin : this->GetLoadedPlugins()) + { + plugin.Unload(); + } + + this->plugins.clear(); + this->LoadPlugins(true); +} + +void PluginManager::RemovePlugin(HMODULE handle) +{ + for (size_t i = 0; i < plugins.size(); i++) + { + Plugin* plugin = &plugins[i]; + if (plugin->m_handle == handle) + { + plugins.erase(plugins.begin() + i); + return; + } + } +} + +void PluginManager::InformAllPluginsInitialized() const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.Finalize(); + } +} + +void PluginManager::InformSqvmCreated(CSquirrelVM* sqvm) const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.OnSqvmCreated(sqvm); + } +} + +void PluginManager::InformSqvmDestroying(CSquirrelVM* sqvm) const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.OnSqvmDestroying(sqvm); + } +} + +void PluginManager::InformDllLoad(HMODULE module, fs::path path) const +{ + std::string fn = path.filename().string(); // without this the string gets freed immediately lmao + const char* filename = fn.c_str(); + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.OnLibraryLoaded(module, filename); + } +} + +void PluginManager::RunFrame() const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.RunFrame(); + } +} + +void ConCommand_reload_plugins(const CCommand& args) +{ + g_pPluginManager->ReloadPlugins(); +} + +ON_DLL_LOAD_RELIESON("engine.dll", PluginManager, ConCommand, (CModule module)) +{ + RegisterConCommand("reload_plugins", ConCommand_reload_plugins, "reloads plugins", FCVAR_NONE); +} diff --git a/primedev/plugins/pluginmanager.h b/primedev/plugins/pluginmanager.h new file mode 100644 index 00000000..8c021851 --- /dev/null +++ b/primedev/plugins/pluginmanager.h @@ -0,0 +1,33 @@ +#ifndef PLUGIN_MANAGER_H +#define PLUGIN_MANAGER_H + +#include + +class Plugin; + +class PluginManager +{ +public: + const std::vector& GetLoadedPlugins() const; + const std::optional GetPlugin(HMODULE handle) const; + bool LoadPlugins(bool reloaded = false); + void LoadPlugin(fs::path path, bool reloaded = false); + void ReloadPlugins(); + void RemovePlugin(HMODULE handle); + + // callback triggers + void InformSqvmCreated(CSquirrelVM* sqvm) const; + void InformSqvmDestroying(CSquirrelVM* sqvm) const; + void InformDllLoad(HMODULE module, fs::path path) const; + void RunFrame() const; + +private: + void InformAllPluginsInitialized() const; + + std::vector plugins; + std::string pluginPath; +}; + +extern PluginManager* g_pPluginManager; + +#endif diff --git a/primedev/plugins/plugins.cpp b/primedev/plugins/plugins.cpp new file mode 100644 index 00000000..eddaa8ac --- /dev/null +++ b/primedev/plugins/plugins.cpp @@ -0,0 +1,233 @@ +#include "plugins.h" +#include "pluginmanager.h" +#include "squirrel/squirrel.h" +#include "util/wininfo.h" +#include "core/sourceinterface.h" +#include "logging/logging.h" +#include "dedicated/dedicated.h" + +bool isValidSquirrelIdentifier(std::string s) +{ + if (!s.size()) + return false; // identifiers can't be empty + if (s[0] <= 57) + return false; // identifier can't start with a number + for (char& c : s) + { + // only allow underscores, 0-9, A-Z and a-z + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') + continue; + return false; + } + return true; +} + +Plugin::Plugin(std::string path) : m_location(path) +{ + HMODULE pluginModule = GetModuleHandleA(path.c_str()); + + if (pluginModule) + { + // plugins may refuse to get unloaded for any reason so we need to prevent them getting loaded twice when reloading plugins + NS::log::PLUGINSYS->warn("Plugin has already been loaded"); + return; + } + + m_handle = LoadLibraryExA(path.c_str(), 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + + NS::log::PLUGINSYS->info("loaded plugin handle {}", static_cast(m_handle)); + + if (!m_handle) + { + NS::log::PLUGINSYS->error("Failed to load plugin '{}' (Error: {})", path, GetLastError()); + return; + } + + m_initData = {.pluginHandle = m_handle}; + + CreateInterfaceFn CreatePluginInterface = (CreateInterfaceFn)GetProcAddress(m_handle, "CreateInterface"); + + if (!CreatePluginInterface) + { + NS::log::PLUGINSYS->error("Plugin at '{}' does not expose CreateInterface()", path); + return; + } + + m_pluginId = (IPluginId*)CreatePluginInterface(PLUGIN_ID_VERSION, 0); + + if (!m_pluginId) + { + NS::log::PLUGINSYS->error("Could not load IPluginId interface of plugin at '{}'", path); + return; + } + + const char* name = m_pluginId->GetString(PluginString::NAME); + const char* logName = m_pluginId->GetString(PluginString::LOG_NAME); + const char* dependencyName = m_pluginId->GetString(PluginString::DEPENDENCY_NAME); + int64_t context = m_pluginId->GetField(PluginField::CONTEXT); + + m_runOnServer = context & PluginContext::DEDICATED; + m_runOnClient = context & PluginContext::CLIENT; + + m_name = std::string(name); + m_logName = std::string(logName); + m_dependencyName = std::string(dependencyName); + + if (!name) + { + NS::log::PLUGINSYS->error("Could not load name of plugin at '{}'", path); + return; + } + + if (!logName) + { + NS::log::PLUGINSYS->error("Could not load logName of plugin {}", name); + return; + } + + if (!dependencyName) + { + NS::log::PLUGINSYS->error("Could not load dependencyName of plugin {}", name); + return; + } + + if (!isValidSquirrelIdentifier(m_dependencyName)) + { + NS::log::PLUGINSYS->error("Dependency name \"{}\" of plugin {} is not valid", dependencyName, name); + return; + } + + m_callbacks = (IPluginCallbacks*)CreatePluginInterface("PluginCallbacks001", 0); + + if (!m_callbacks) + { + NS::log::PLUGINSYS->error("Could not create callback interface of plugin {}", name); + return; + } + + m_logger = std::make_shared(m_logName, NS::Colors::PLUGIN); + RegisterLogger(m_logger); + + if (IsDedicatedServer() && !m_runOnServer) + { + NS::log::PLUGINSYS->info("Unloading {} because it's not supposed to run on dedicated servers", m_name); + return; + } + + if (!IsDedicatedServer() && !m_runOnClient) + { + NS::log::PLUGINSYS->info("Unloading {} because it's only supposed to run on dedicated servers", m_name); + return; + } + + m_valid = true; +} + +bool Plugin::Unload() const +{ + if (!m_handle) + return true; + + if (IsValid()) + { + bool unloaded = m_callbacks->Unload(); + + if (!unloaded) + return false; + } + + if (!FreeLibrary(m_handle)) + { + NS::log::PLUGINSYS->error("Failed to unload plugin at '{}'", m_location); + return false; + } + + g_pPluginManager->RemovePlugin(m_handle); + return true; +} + +void Plugin::Reload() const +{ + bool unloaded = Unload(); + + if (!unloaded) + return; + + g_pPluginManager->LoadPlugin(fs::path(m_location), true); +} + +void Plugin::Log(spdlog::level::level_enum level, char* msg) const +{ + m_logger->log(level, msg); +} + +bool Plugin::IsValid() const +{ + return m_valid && m_pCreateInterface && m_pluginId && m_callbacks && m_handle; +} + +const std::string& Plugin::GetName() const +{ + return m_name; +} + +const std::string& Plugin::GetLogName() const +{ + return m_logName; +} + +const std::string& Plugin::GetDependencyName() const +{ + return m_dependencyName; +} + +const std::string& Plugin::GetLocation() const +{ + return m_location; +} + +bool Plugin::ShouldRunOnServer() const +{ + return m_runOnServer; +} + +bool Plugin::ShouldRunOnClient() const +{ + return m_runOnClient; +} + +void* Plugin::CreateInterface(const char* name, int* status) const +{ + return m_pCreateInterface(name, status); +} + +void Plugin::Init(bool reloaded) const +{ + m_callbacks->Init(g_NorthstarModule, &m_initData, reloaded); +} + +void Plugin::Finalize() const +{ + m_callbacks->Finalize(); +} + +void Plugin::OnSqvmCreated(CSquirrelVM* sqvm) const +{ + m_callbacks->OnSqvmCreated(sqvm); +} + +void Plugin::OnSqvmDestroying(CSquirrelVM* sqvm) const +{ + NS::log::PLUGINSYS->info("destroying sqvm {}", sqvm->vmContext); + m_callbacks->OnSqvmDestroying(sqvm); +} + +void Plugin::OnLibraryLoaded(HMODULE module, const char* name) const +{ + m_callbacks->OnLibraryLoaded(module, name); +} + +void Plugin::RunFrame() const +{ + m_callbacks->RunFrame(); +} diff --git a/primedev/plugins/plugins.h b/primedev/plugins/plugins.h new file mode 100644 index 00000000..d004038c --- /dev/null +++ b/primedev/plugins/plugins.h @@ -0,0 +1,50 @@ +#pragma once +#include "core/sourceinterface.h" +#include "plugins/interfaces/interface.h" +#include "plugins/interfaces/IPluginId.h" +#include "plugins/interfaces/IPluginCallbacks.h" + +class Plugin +{ +private: + CreateInterfaceFn m_pCreateInterface; + IPluginId* m_pluginId = 0; + IPluginCallbacks* m_callbacks = 0; + + std::shared_ptr m_logger; + + bool m_valid = false; + std::string m_name; + std::string m_logName; + std::string m_dependencyName; + std::string m_location; // path of the dll + bool m_runOnServer; + bool m_runOnClient; + +public: + HMODULE m_handle; + PluginNorthstarData m_initData; + + Plugin(std::string path); + bool Unload() const; + void Reload() const; + + // sys + void Log(spdlog::level::level_enum level, char* msg) const; + + // callbacks + bool IsValid() const; + const std::string& GetName() const; + const std::string& GetLogName() const; + const std::string& GetDependencyName() const; + const std::string& GetLocation() const; + bool ShouldRunOnServer() const; + bool ShouldRunOnClient() const; + void* CreateInterface(const char* pName, int* pStatus) const; + void Init(bool reloaded) const; + void Finalize() const; + void OnSqvmCreated(CSquirrelVM* sqvm) const; + void OnSqvmDestroying(CSquirrelVM* sqvm) const; + void OnLibraryLoaded(HMODULE module, const char* name) const; + void RunFrame() const; +}; diff --git a/NorthstarLauncher/main.cpp b/primedev/primelauncher/main.cpp similarity index 98% rename from NorthstarLauncher/main.cpp rename to primedev/primelauncher/main.cpp index ecc18c45..96c96c04 100644 --- a/NorthstarLauncher/main.cpp +++ b/primedev/primelauncher/main.cpp @@ -255,7 +255,7 @@ void PrependPath() bool ShouldLoadNorthstar(int argc, char* argv[]) { for (int i = 0; i < argc; i++) - if (!strcmp(argv[i], "-vanilla")) + if (!strcmp(argv[i], "-nonorthstardll")) return false; auto runNorthstarFile = std::ifstream("run_northstar.txt"); @@ -281,7 +281,7 @@ bool LoadNorthstar() std::string cla = std::string(clachar); if (strncmp(cla.substr(9, 1).c_str(), "\"", 1)) { - int space = cla.find(" "); + size_t space = cla.find(" "); std::string dirname = cla.substr(9, space - 9); std::cout << "[*] Found profile in command line arguments: " << dirname << std::endl; strProfile = dirname.c_str(); @@ -289,8 +289,8 @@ bool LoadNorthstar() else { std::string quote = "\""; - int quote1 = cla.find(quote); - int quote2 = (cla.substr(quote1 + 1)).find(quote); + size_t quote1 = cla.find(quote); + size_t quote2 = (cla.substr(quote1 + 1)).find(quote); std::string dirname = cla.substr(quote1 + 1, quote2); std::cout << "[*] Found profile in command line arguments: " << dirname << std::endl; strProfile = dirname; diff --git a/NorthstarLauncher/ns_icon.ico b/primedev/primelauncher/ns_icon.ico similarity index 100% rename from NorthstarLauncher/ns_icon.ico rename to primedev/primelauncher/ns_icon.ico diff --git a/NorthstarDLL/resource1.h b/primedev/primelauncher/resource1.h similarity index 100% rename from NorthstarDLL/resource1.h rename to primedev/primelauncher/resource1.h diff --git a/NorthstarLauncher/resources.rc b/primedev/primelauncher/resources.rc similarity index 98% rename from NorthstarLauncher/resources.rc rename to primedev/primelauncher/resources.rc index 32038499..43646122 100644 --- a/NorthstarLauncher/resources.rc +++ b/primedev/primelauncher/resources.rc @@ -1,7 +1,7 @@ // Microsoft Visual C++ generated resource script. // #include "resource1.h" -#include "../NorthstarDLL/ns_version.h" +#include "../ns_version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// diff --git a/NorthstarLauncher/resource1.h b/primedev/resource1.h similarity index 100% rename from NorthstarLauncher/resource1.h rename to primedev/resource1.h diff --git a/NorthstarDLL/resources.rc b/primedev/resources.rc similarity index 97% rename from NorthstarDLL/resources.rc rename to primedev/resources.rc index 7e996617..32739daa 100644 --- a/NorthstarDLL/resources.rc +++ b/primedev/resources.rc @@ -1,7 +1,7 @@ // Microsoft Visual C++ generated resource script. // #include "resource1.h" -#include "../NorthstarDLL/ns_version.h" +#include "../primedev/ns_version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// diff --git a/NorthstarDLL/scripts/client/clientchathooks.cpp b/primedev/scripts/client/clientchathooks.cpp similarity index 96% rename from NorthstarDLL/scripts/client/clientchathooks.cpp rename to primedev/scripts/client/clientchathooks.cpp index df9497ef..e084f47e 100644 --- a/NorthstarDLL/scripts/client/clientchathooks.cpp +++ b/primedev/scripts/client/clientchathooks.cpp @@ -30,7 +30,7 @@ void, __fastcall, (void* self, const char* message, int inboxId, bool isTeam, bo payload = message + 1; } - NS::Utils::RemoveAsciiControlSequences(const_cast(message), true); + RemoveAsciiControlSequences(const_cast(message), true); SQRESULT result = g_pSquirrel->Call( "CHudChat_ProcessMessageStartThread", static_cast(senderId) - 1, payload, isTeam, isDead, type); diff --git a/NorthstarDLL/scripts/client/cursorposition.cpp b/primedev/scripts/client/cursorposition.cpp similarity index 100% rename from NorthstarDLL/scripts/client/cursorposition.cpp rename to primedev/scripts/client/cursorposition.cpp diff --git a/NorthstarDLL/scripts/client/scriptbrowserhooks.cpp b/primedev/scripts/client/scriptbrowserhooks.cpp similarity index 82% rename from NorthstarDLL/scripts/client/scriptbrowserhooks.cpp rename to primedev/scripts/client/scriptbrowserhooks.cpp index 86b4a356..dcf051d2 100644 --- a/NorthstarDLL/scripts/client/scriptbrowserhooks.cpp +++ b/primedev/scripts/client/scriptbrowserhooks.cpp @@ -9,7 +9,8 @@ void, __fastcall, (char* pUrl, char flags)) // clang-format on { bool bIsOriginOverlayEnabledOriginal = *bIsOriginOverlayEnabled; - if (flags & 2 && !strncmp(pUrl, "http", 4)) // custom force external browser flag + bool isHttp = !strncmp(pUrl, "http://", 7) || !strncmp(pUrl, "https://", 8); + if (flags & 2 && isHttp) // custom force external browser flag *bIsOriginOverlayEnabled = false; // if this bool is false, game will use an external browser rather than the origin overlay one OpenExternalWebBrowser(pUrl, flags); diff --git a/NorthstarDLL/scripts/client/scriptmainmenupromos.cpp b/primedev/scripts/client/scriptmainmenupromos.cpp similarity index 99% rename from NorthstarDLL/scripts/client/scriptmainmenupromos.cpp rename to primedev/scripts/client/scriptmainmenupromos.cpp index ecb47af7..91bc3002 100644 --- a/NorthstarDLL/scripts/client/scriptmainmenupromos.cpp +++ b/primedev/scripts/client/scriptmainmenupromos.cpp @@ -23,6 +23,7 @@ enum eMainMenuPromoDataProperty }; ADD_SQFUNC("void", NSRequestCustomMainMenuPromos, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pMasterServerManager->RequestMainMenuPromos(); return SQRESULT_NULL; } diff --git a/NorthstarDLL/scripts/client/scriptmodmenu.cpp b/primedev/scripts/client/scriptmodmenu.cpp similarity index 99% rename from NorthstarDLL/scripts/client/scriptmodmenu.cpp rename to primedev/scripts/client/scriptmodmenu.cpp index a88478fb..2e877db4 100644 --- a/NorthstarDLL/scripts/client/scriptmodmenu.cpp +++ b/primedev/scripts/client/scriptmodmenu.cpp @@ -160,6 +160,7 @@ ADD_SQFUNC( ADD_SQFUNC("void", NSReloadMods, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pModManager->LoadMods(); return SQRESULT_NULL; } diff --git a/NorthstarDLL/scripts/client/scriptoriginauth.cpp b/primedev/scripts/client/scriptoriginauth.cpp similarity index 100% rename from NorthstarDLL/scripts/client/scriptoriginauth.cpp rename to primedev/scripts/client/scriptoriginauth.cpp diff --git a/NorthstarDLL/scripts/client/scriptserverbrowser.cpp b/primedev/scripts/client/scriptserverbrowser.cpp similarity index 90% rename from NorthstarDLL/scripts/client/scriptserverbrowser.cpp rename to primedev/scripts/client/scriptserverbrowser.cpp index 1945b3dc..b946f7a9 100644 --- a/NorthstarDLL/scripts/client/scriptserverbrowser.cpp +++ b/primedev/scripts/client/scriptserverbrowser.cpp @@ -8,6 +8,7 @@ ADD_SQFUNC("void", NSRequestServerList, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pMasterServerManager->RequestServerList(); return SQRESULT_NULL; } @@ -26,12 +27,13 @@ ADD_SQFUNC("bool", NSMasterServerConnectionSuccessful, "", "", ScriptContext::UI ADD_SQFUNC("int", NSGetServerCount, "", "", ScriptContext::UI) { - g_pSquirrel->pushinteger(sqvm, g_pMasterServerManager->m_vRemoteServers.size()); + g_pSquirrel->pushinteger(sqvm, (SQInteger)g_pMasterServerManager->m_vRemoteServers.size()); return SQRESULT_NOTNULL; } ADD_SQFUNC("void", NSClearRecievedServerList, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); g_pMasterServerManager->ClearServerList(); return SQRESULT_NULL; } @@ -62,7 +64,7 @@ ADD_SQFUNC("void", NSTryAuthWithServer, "int serverIndex, string password = ''", // do auth g_pMasterServerManager->AuthenticateWithServer( - R2::g_pLocalPlayerUserID, + g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken, g_pMasterServerManager->m_vRemoteServers[serverIndex], (char*)password); @@ -95,9 +97,9 @@ ADD_SQFUNC("void", NSConnectToAuthedServer, "", "", ScriptContext::UI) // set auth token, then try to connect // i'm honestly not entirely sure how silentconnect works regarding ports and encryption so using connect for now - R2::g_pCVar->FindVar("serverfilter")->SetValue(info.authToken); - R2::Cbuf_AddText( - R2::Cbuf_GetCurrentPlayer(), + g_pCVar->FindVar("serverfilter")->SetValue(info.authToken); + Cbuf_AddText( + Cbuf_GetCurrentPlayer(), fmt::format( "connect {}.{}.{}.{}:{}", info.ip.S_un.S_un_b.s_b1, @@ -106,7 +108,7 @@ ADD_SQFUNC("void", NSConnectToAuthedServer, "", "", ScriptContext::UI) info.ip.S_un.S_un_b.s_b4, info.port) .c_str(), - R2::cmd_source_t::kCommandSrcCode); + cmd_source_t::kCommandSrcCode); g_pMasterServerManager->m_bHasPendingConnectionInfo = false; return SQRESULT_NULL; @@ -114,18 +116,20 @@ ADD_SQFUNC("void", NSConnectToAuthedServer, "", "", ScriptContext::UI) ADD_SQFUNC("void", NSTryAuthWithLocalServer, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); // do auth request - g_pMasterServerManager->AuthenticateWithOwnServer(R2::g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); + g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); return SQRESULT_NULL; } ADD_SQFUNC("void", NSCompleteAuthWithLocalServer, "", "", ScriptContext::UI) { + NOTE_UNUSED(sqvm); // literally just set serverfilter // note: this assumes we have no authdata other than our own if (g_pServerAuthentication->m_RemoteAuthenticationData.size()) - R2::g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str()); + g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str()); return SQRESULT_NULL; } @@ -146,7 +150,7 @@ ADD_SQFUNC("array", NSGetGameServers, "", "", ScriptContext::UI) g_pSquirrel->pushnewstructinstance(sqvm, 11); // index - g_pSquirrel->pushinteger(sqvm, i); + g_pSquirrel->pushinteger(sqvm, (SQInteger)i); g_pSquirrel->sealstructslot(sqvm, 0); // id diff --git a/NorthstarDLL/scripts/client/scriptservertoclientstringcommand.cpp b/primedev/scripts/client/scriptservertoclientstringcommand.cpp similarity index 100% rename from NorthstarDLL/scripts/client/scriptservertoclientstringcommand.cpp rename to primedev/scripts/client/scriptservertoclientstringcommand.cpp diff --git a/NorthstarDLL/scripts/scriptdatatables.cpp b/primedev/scripts/scriptdatatables.cpp similarity index 95% rename from NorthstarDLL/scripts/scriptdatatables.cpp rename to primedev/scripts/scriptdatatables.cpp index 532624f3..5e685b48 100644 --- a/NorthstarDLL/scripts/scriptdatatables.cpp +++ b/primedev/scripts/scriptdatatables.cpp @@ -59,42 +59,6 @@ struct CSVData std::unordered_map CSVCache; -Vector3 StringToVector(char* pString) -{ - Vector3 vRet; - - int length = 0; - while (pString[length]) - { - if ((pString[length] == '<') || (pString[length] == '>')) - pString[length] = '\0'; - length++; - } - - int startOfFloat = 1; - int currentIndex = 1; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.x = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.y = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.z = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - return vRet; -} - // var function GetDataTable( asset path ) REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) { @@ -132,9 +96,9 @@ REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | Script diskAssetPath /= fs::path(pAssetName); std::string sDiskAssetPath(diskAssetPath.string()); - if ((*R2::g_pFilesystem)->m_vtable2->FileExists(&(*R2::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 = R2::ReadVPKFile(sDiskAssetPath.c_str()); + std::string sTableCSV = ReadVPKFile(sDiskAssetPath.c_str()); if (!sTableCSV.size()) { g_pSquirrel->raiseerror(sqvm, fmt::format("Datatable \"{}\" is empty", pAssetName).c_str()); @@ -308,7 +272,7 @@ REPLACE_SQFUNC(GetDataTableRowCount, (ScriptContext::UI | ScriptContext::CLIENT return g_pSquirrel->m_funcOriginals["GetDatatableRowCount"](sqvm); CSVData* csv = *pData; - g_pSquirrel->pushinteger(sqvm, csv->dataPointers.size()); + g_pSquirrel->pushinteger(sqvm, (SQInteger)csv->dataPointers.size()); return SQRESULT_NOTNULL; } @@ -793,7 +757,7 @@ void DumpDatatable(const char* pDatatablePath) return; } - std::string sOutputPath(fmt::format("{}/scripts/datatable/{}.csv", R2::g_pModName, fs::path(pDatatablePath).stem().string())); + std::string sOutputPath(fmt::format("{}/scripts/datatable/{}.csv", g_pModName, fs::path(pDatatablePath).stem().string())); std::string sDatatableContents(DataTableToString(pDatatable)); fs::create_directories(fs::path(sOutputPath).remove_filename()); @@ -817,6 +781,7 @@ void ConCommand_dump_datatable(const CCommand& args) void ConCommand_dump_datatables(const CCommand& args) { + NOTE_UNUSED(args); // likely not a comprehensive list, might be missing a couple? static const std::vector VANILLA_DATATABLE_PATHS = { "datatable/burn_meter_rewards.rpak", @@ -900,7 +865,7 @@ ON_DLL_LOAD_RELIESON("engine.dll", SharedScriptDataTables, ConVar, (CModule modu { Cvar_ns_prefer_datatable_from_disk = new ConVar( "ns_prefer_datatable_from_disk", - IsDedicatedServer() && Tier0::CommandLine()->CheckParm("-nopakdedi") ? "1" : "0", + IsDedicatedServer() && CommandLine()->CheckParm("-nopakdedi") ? "1" : "0", FCVAR_NONE, "whether to prefer loading datatables from disk, rather than rpak"); diff --git a/NorthstarDLL/scripts/scripthttprequesthandler.cpp b/primedev/scripts/scripthttprequesthandler.cpp similarity index 97% rename from NorthstarDLL/scripts/scripthttprequesthandler.cpp rename to primedev/scripts/scripthttprequesthandler.cpp index 813bd50e..69828a5a 100644 --- a/NorthstarDLL/scripts/scripthttprequesthandler.cpp +++ b/primedev/scripts/scripthttprequesthandler.cpp @@ -7,19 +7,19 @@ HttpRequestHandler* g_httpRequestHandler; bool IsHttpDisabled() { - const static bool bIsHttpDisabled = Tier0::CommandLine()->FindParm("-disablehttprequests"); + const static bool bIsHttpDisabled = CommandLine()->FindParm("-disablehttprequests"); return bIsHttpDisabled; } bool IsLocalHttpAllowed() { - const static bool bIsLocalHttpAllowed = Tier0::CommandLine()->FindParm("-allowlocalhttp"); + const static bool bIsLocalHttpAllowed = CommandLine()->FindParm("-allowlocalhttp"); return bIsLocalHttpAllowed; } bool DisableHttpSsl() { - const static bool bDisableHttpSsl = Tier0::CommandLine()->FindParm("-disablehttpssl"); + const static bool bDisableHttpSsl = CommandLine()->FindParm("-disablehttpssl"); return bDisableHttpSsl; } @@ -320,11 +320,11 @@ template int HttpRequestHandler::MakeHttpRequest(const H bool isFirstValue = true; for (const auto& kv : requestParameters.queryParameters) { - char* key = curl_easy_escape(curl, kv.first.c_str(), kv.first.length()); + char* key = curl_easy_escape(curl, kv.first.c_str(), (int)kv.first.length()); for (const std::string& queryValue : kv.second) { - char* value = curl_easy_escape(curl, queryValue.c_str(), queryValue.length()); + char* value = curl_easy_escape(curl, queryValue.c_str(), (int)queryValue.length()); if (isFirstValue && !bUrlContainsQuery) { diff --git a/NorthstarDLL/scripts/scripthttprequesthandler.h b/primedev/scripts/scripthttprequesthandler.h similarity index 99% rename from NorthstarDLL/scripts/scripthttprequesthandler.h rename to primedev/scripts/scripthttprequesthandler.h index 1f237bac..f3921f4e 100644 --- a/NorthstarDLL/scripts/scripthttprequesthandler.h +++ b/primedev/scripts/scripthttprequesthandler.h @@ -99,7 +99,7 @@ struct HttpRequest */ class HttpRequestHandler { - public: +public: HttpRequestHandler(); // Start/Stop the HTTP request handler. Right now this doesn't do much. @@ -122,7 +122,7 @@ class HttpRequestHandler /** Registers the HTTP request Squirrel functions for the given script context. */ template void RegisterSQFuncs(); - private: +private: int m_iLastRequestHandle = 0; std::atomic_bool m_bIsHttpRequestHandlerRunning = false; }; diff --git a/NorthstarDLL/scripts/scriptjson.cpp b/primedev/scripts/scriptjson.cpp similarity index 100% rename from NorthstarDLL/scripts/scriptjson.cpp rename to primedev/scripts/scriptjson.cpp diff --git a/NorthstarDLL/scripts/scriptjson.h b/primedev/scripts/scriptjson.h similarity index 100% rename from NorthstarDLL/scripts/scriptjson.h rename to primedev/scripts/scriptjson.h diff --git a/NorthstarDLL/scripts/scriptutility.cpp b/primedev/scripts/scriptutility.cpp similarity index 87% rename from NorthstarDLL/scripts/scriptutility.cpp rename to primedev/scripts/scriptutility.cpp index 054836ca..4b92fa02 100644 --- a/NorthstarDLL/scripts/scriptutility.cpp +++ b/primedev/scripts/scriptutility.cpp @@ -18,9 +18,9 @@ ADD_SQFUNC( ADD_SQFUNC( "string", NSGetLocalPlayerUID, "", "Returns the local player's uid.", ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER) { - if (R2::g_pLocalPlayerUserID) + if (g_pLocalPlayerUserID) { - g_pSquirrel->pushstring(sqvm, R2::g_pLocalPlayerUserID); + g_pSquirrel->pushstring(sqvm, g_pLocalPlayerUserID); return SQRESULT_NOTNULL; } diff --git a/NorthstarDLL/scripts/server/miscserverfixes.cpp b/primedev/scripts/server/miscserverfixes.cpp similarity index 100% rename from NorthstarDLL/scripts/server/miscserverfixes.cpp rename to primedev/scripts/server/miscserverfixes.cpp diff --git a/NorthstarDLL/scripts/server/miscserverscript.cpp b/primedev/scripts/server/miscserverscript.cpp similarity index 74% rename from NorthstarDLL/scripts/server/miscserverscript.cpp rename to primedev/scripts/server/miscserverscript.cpp index 3ea44ceb..ed6e4800 100644 --- a/NorthstarDLL/scripts/server/miscserverscript.cpp +++ b/primedev/scripts/server/miscserverscript.cpp @@ -9,7 +9,7 @@ ADD_SQFUNC("void", NSEarlyWritePlayerPersistenceForLeave, "entity player", "", ScriptContext::SERVER) { - const R2::CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); if (!pPlayer) { spdlog::warn("NSEarlyWritePlayerPersistenceForLeave got null player"); @@ -18,7 +18,7 @@ ADD_SQFUNC("void", NSEarlyWritePlayerPersistenceForLeave, "entity player", "", S return SQRESULT_NOTNULL; } - R2::CBaseClient* pClient = &R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1]; + CBaseClient* pClient = &g_pClientArray[pPlayer->m_nPlayerIndex - 1]; if (g_pServerAuthentication->m_PlayerAuthenticationData.find(pClient) == g_pServerAuthentication->m_PlayerAuthenticationData.end()) { g_pSquirrel->pushbool(sqvm, false); @@ -38,7 +38,7 @@ ADD_SQFUNC("bool", NSIsWritingPlayerPersistence, "", "", ScriptContext::SERVER) ADD_SQFUNC("bool", NSIsPlayerLocalPlayer, "entity player", "", ScriptContext::SERVER) { - const R2::CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); if (!pPlayer) { spdlog::warn("NSIsPlayerLocalPlayer got null player"); @@ -47,8 +47,8 @@ ADD_SQFUNC("bool", NSIsPlayerLocalPlayer, "entity player", "", ScriptContext::SE return SQRESULT_NOTNULL; } - R2::CBaseClient* pClient = &R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1]; - g_pSquirrel->pushbool(sqvm, !strcmp(R2::g_pLocalPlayerUserID, pClient->m_UID)); + CBaseClient* pClient = &g_pClientArray[pPlayer->m_nPlayerIndex - 1]; + g_pSquirrel->pushbool(sqvm, !strcmp(g_pLocalPlayerUserID, pClient->m_UID)); return SQRESULT_NOTNULL; } @@ -65,7 +65,7 @@ ADD_SQFUNC( "Disconnects the player from the server with the given reason", ScriptContext::SERVER) { - const R2::CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); const char* reason = g_pSquirrel->getstring(sqvm, 2); if (!pPlayer) @@ -77,7 +77,7 @@ ADD_SQFUNC( } // Shouldn't happen but I like sanity checks. - R2::CBaseClient* pClient = &R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1]; + CBaseClient* pClient = &g_pClientArray[pPlayer->m_nPlayerIndex - 1]; if (!pClient) { spdlog::warn("NSDisconnectPlayer(): player entity has null CBaseClient!"); @@ -88,11 +88,11 @@ ADD_SQFUNC( if (reason) { - R2::CBaseClient__Disconnect(pClient, 1, reason); + CBaseClient__Disconnect(pClient, 1, reason); } else { - R2::CBaseClient__Disconnect(pClient, 1, "Disconnected by the server."); + CBaseClient__Disconnect(pClient, 1, "Disconnected by the server."); } g_pSquirrel->pushbool(sqvm, true); diff --git a/NorthstarDLL/scripts/server/scriptuserinfo.cpp b/primedev/scripts/server/scriptuserinfo.cpp similarity index 73% rename from NorthstarDLL/scripts/server/scriptuserinfo.cpp rename to primedev/scripts/server/scriptuserinfo.cpp index fac458a3..c53a9d22 100644 --- a/NorthstarDLL/scripts/server/scriptuserinfo.cpp +++ b/primedev/scripts/server/scriptuserinfo.cpp @@ -7,7 +7,7 @@ ADD_SQFUNC("string", GetUserInfoKVString_Internal, "entity player, string key, s "Gets the string value of a given player's userinfo convar by name", ScriptContext::SERVER) // clang-format on { - const R2::CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); if (!pPlayer) { g_pSquirrel->raiseerror(sqvm, "player is null"); @@ -17,7 +17,7 @@ ADD_SQFUNC("string", GetUserInfoKVString_Internal, "entity player, string key, s const char* pKey = g_pSquirrel->getstring(sqvm, 2); const char* pDefaultValue = g_pSquirrel->getstring(sqvm, 3); - const char* pResult = R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetString(pKey, pDefaultValue); + const char* pResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetString(pKey, pDefaultValue); g_pSquirrel->pushstring(sqvm, pResult); return SQRESULT_NOTNULL; } @@ -27,7 +27,7 @@ ADD_SQFUNC("asset", GetUserInfoKVAsset_Internal, "entity player, string key, ass "Gets the asset value of a given player's userinfo convar by name", ScriptContext::SERVER) // clang-format on { - const R2::CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); if (!pPlayer) { g_pSquirrel->raiseerror(sqvm, "player is null"); @@ -38,7 +38,7 @@ ADD_SQFUNC("asset", GetUserInfoKVAsset_Internal, "entity player, string key, ass const char* pDefaultValue; g_pSquirrel->getasset(sqvm, 3, &pDefaultValue); - const char* pResult = R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetString(pKey, pDefaultValue); + const char* pResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetString(pKey, pDefaultValue); g_pSquirrel->pushasset(sqvm, pResult); return SQRESULT_NOTNULL; } @@ -48,7 +48,7 @@ ADD_SQFUNC("int", GetUserInfoKVInt_Internal, "entity player, string key, int def "Gets the int value of a given player's userinfo convar by name", ScriptContext::SERVER) // clang-format on { - const R2::CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->template getentity(sqvm, 1); if (!pPlayer) { g_pSquirrel->raiseerror(sqvm, "player is null"); @@ -58,7 +58,7 @@ ADD_SQFUNC("int", GetUserInfoKVInt_Internal, "entity player, string key, int def const char* pKey = g_pSquirrel->getstring(sqvm, 2); const int iDefaultValue = g_pSquirrel->getinteger(sqvm, 3); - const int iResult = R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetInt(pKey, iDefaultValue); + const int iResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetInt(pKey, iDefaultValue); g_pSquirrel->pushinteger(sqvm, iResult); return SQRESULT_NOTNULL; } @@ -68,7 +68,7 @@ ADD_SQFUNC("float", GetUserInfoKVFloat_Internal, "entity player, string key, flo "Gets the float value of a given player's userinfo convar by name", ScriptContext::SERVER) // clang-format on { - const R2::CBasePlayer* pPlayer = g_pSquirrel->getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->getentity(sqvm, 1); if (!pPlayer) { g_pSquirrel->raiseerror(sqvm, "player is null"); @@ -78,7 +78,7 @@ ADD_SQFUNC("float", GetUserInfoKVFloat_Internal, "entity player, string key, flo const char* pKey = g_pSquirrel->getstring(sqvm, 2); const float flDefaultValue = g_pSquirrel->getfloat(sqvm, 3); - const float flResult = R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetFloat(pKey, flDefaultValue); + const float flResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetFloat(pKey, flDefaultValue); g_pSquirrel->pushfloat(sqvm, flResult); return SQRESULT_NOTNULL; } @@ -88,7 +88,7 @@ ADD_SQFUNC("bool", GetUserInfoKVBool_Internal, "entity player, string key, bool "Gets the bool value of a given player's userinfo convar by name", ScriptContext::SERVER) // clang-format on { - const R2::CBasePlayer* pPlayer = g_pSquirrel->getentity(sqvm, 1); + const CBasePlayer* pPlayer = g_pSquirrel->getentity(sqvm, 1); if (!pPlayer) { g_pSquirrel->raiseerror(sqvm, "player is null"); @@ -98,7 +98,7 @@ ADD_SQFUNC("bool", GetUserInfoKVBool_Internal, "entity player, string key, bool const char* pKey = g_pSquirrel->getstring(sqvm, 2); const bool bDefaultValue = g_pSquirrel->getbool(sqvm, 3); - const bool bResult = R2::g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetInt(pKey, bDefaultValue); + const bool bResult = g_pClientArray[pPlayer->m_nPlayerIndex - 1].m_ConVars->GetInt(pKey, bDefaultValue); g_pSquirrel->pushbool(sqvm, bResult); return SQRESULT_NOTNULL; } diff --git a/primedev/server/ai_helper.cpp b/primedev/server/ai_helper.cpp new file mode 100644 index 00000000..ebb56af2 --- /dev/null +++ b/primedev/server/ai_helper.cpp @@ -0,0 +1,159 @@ +#include "ai_helper.h" + +#include "client/debugoverlay.h" +#include "client/cdll_client_int.h" +#include "engine/hoststate.h" + +#include "core/math/vplane.h" + +#include + +const int AINET_VERSION_NUMBER = 57; +const int AINET_SCRIPT_VERSION_NUMBER = 21; +const int PLACEHOLDER_CRC = 0; + +static ConVar* Cvar_navmesh_debug_hull; +static ConVar* Cvar_navmesh_debug_camera_radius; +static ConVar* Cvar_navmesh_debug_lossy_optimization; + +//----------------------------------------------------------------------------- +// Purpose: Get navmesh pointer for hull +// Output : navmesh*, nullptr if out of range +//----------------------------------------------------------------------------- +dtNavMesh* GetNavMeshForHull(int nHull) +{ + if (nHull < 1 || nHull > 4) + return nullptr; + + return g_pNavMesh[nHull - 1]; +} + +//----------------------------------------------------------------------------- +// Purpose: Packs two vectors into a __m128i +// Input : &v1 - +// &v2 - +// Output : +//----------------------------------------------------------------------------- +__m128i PackVerticesSIMD16(const Vector3& v1, const Vector3& v2) +{ + short x1, x2, y1, y2, z1, z2; + x1 = static_cast(v1.x); + x2 = static_cast(v2.x); + y1 = static_cast(v1.y); + y2 = static_cast(v2.y); + z1 = static_cast(v1.z); + z2 = static_cast(v2.z); + + __m128i xRes = _mm_set_epi16(x1, x2, y1, y2, z1, z2, 0, 0); + + if (x1 < x2) + xRes = _mm_shufflehi_epi16(xRes, _MM_SHUFFLE(2, 3, 1, 0)); + + if (y1 < y2) + xRes = _mm_shufflehi_epi16(xRes, _MM_SHUFFLE(3, 2, 0, 1)); + + if (z1 < z2) + xRes = _mm_shufflelo_epi16(xRes, _MM_SHUFFLE(2, 3, 1, 0)); + + return xRes; +} + +//----------------------------------------------------------------------------- +// Purpose: Draw navmesh polys using debug overlay +// Input : *pNavMesh +//----------------------------------------------------------------------------- +void CAI_Helper::DrawNavmeshPolys(dtNavMesh* pNavMesh) +{ + if (!pNavMesh) + pNavMesh = GetNavMeshForHull(Cvar_navmesh_debug_hull->GetInt()); + if (!pNavMesh) + return; + + Vector3 vCamera; + QAngle aCamera; + float fFov; + g_pClientTools->GetLocalPlayerEyePosition(vCamera, aCamera, fFov); + + const VPlane CullPlane(vCamera - aCamera.GetNormal() * 256.0f, aCamera); + + const float fCamRadius = Cvar_navmesh_debug_camera_radius->GetFloat(); + const bool bOptimize = Cvar_navmesh_debug_lossy_optimization->GetBool(); + + // Used for lossy optimization ( z is ignored when checking for duplicates ) + // [Fifty]: On a release build i gained around 12 fps on a 1050 ti + std::unordered_set sOutlines; + + for (int i = 0; i < pNavMesh->m_maxTiles; ++i) + { + const dtMeshTile* pTile = &pNavMesh->m_tiles[i]; + + if (!pTile->header) + continue; + + for (int j = 0; j < pTile->header->polyCount; j++) + { + const dtPoly* pPoly = &pTile->polys[j]; + + if (vCamera.DistTo(pPoly->org) > fCamRadius) + continue; + + if (CullPlane.GetPointSide(pPoly->org) != SIDE_FRONT) + continue; + + const unsigned int ip = (unsigned int)(pPoly - pTile->polys); + + if (pPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + const dtOffMeshConnection* pCon = &pTile->offMeshConnections[ip - pTile->header->offMeshBase]; + RenderLine(pCon->origin, pCon->dest, Color(255, 250, 50, 255), true); + } + else + { + const dtPolyDetail* pDetail = &pTile->detailMeshes[ip]; + + Vector3 v[3]; + + for (int k = 0; k < pDetail->triCount; ++k) + { + const unsigned char* t = &pTile->detailTris[(pDetail->triBase + k) * 4]; + for (int l = 0; l < 3; ++l) + { + if (t[l] < pPoly->vertCount) + { + float* pfVerts = &pTile->verts[pPoly->verts[t[l]] * 3]; + v[l] = Vector3(pfVerts[0], pfVerts[1], pfVerts[2]); + } + else + { + float* pfVerts = &pTile->detailVerts[(pDetail->vertBase + t[l] - pPoly->vertCount) * 3]; + v[l] = Vector3(pfVerts[0], pfVerts[1], pfVerts[2]); + } + } + + RenderTriangle(v[0], v[1], v[2], Color(110, 200, 220, 160), true); + + auto r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[0], v[1]), 1)); + if (r.second || !bOptimize) + RenderLine(v[0], v[1], Color(0, 0, 150), true); + + r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[1], v[2]), 1)); + if (r.second || !bOptimize) + RenderLine(v[1], v[2], Color(0, 0, 150), true); + + r = sOutlines.insert(_mm_extract_epi64(PackVerticesSIMD16(v[2], v[0]), 1)); + if (r.second || !bOptimize) + RenderLine(v[2], v[0], Color(0, 0, 150), true); + } + } + } + } +} + +ON_DLL_LOAD("server.dll", ServerAIHelper, (CModule module)) +{ + Cvar_navmesh_debug_hull = new ConVar("navmesh_debug_hull", "0", FCVAR_RELEASE, "0 = NONE"); + Cvar_navmesh_debug_camera_radius = + new ConVar("navmesh_debug_camera_radius", "1000", FCVAR_RELEASE, "Radius in which to draw navmeshes"); + Cvar_navmesh_debug_lossy_optimization = + new ConVar("navmesh_debug_lossy_optimization", "1", FCVAR_RELEASE, "Whether to enable lossy navmesh debug draw optimizations"); +} diff --git a/primedev/server/ai_helper.h b/primedev/server/ai_helper.h new file mode 100644 index 00000000..0b3c3a53 --- /dev/null +++ b/primedev/server/ai_helper.h @@ -0,0 +1,13 @@ +#pragma once + +#include "server/ai_navmesh.h" + +dtNavMesh* GetNavMeshForHull(int nHull); + +class CAI_Helper +{ +public: + void DrawNavmeshPolys(dtNavMesh* pNavMesh = nullptr); +}; + +inline CAI_Helper* g_pAIHelper = nullptr; diff --git a/primedev/server/ai_navmesh.cpp b/primedev/server/ai_navmesh.cpp new file mode 100644 index 00000000..966726b2 --- /dev/null +++ b/primedev/server/ai_navmesh.cpp @@ -0,0 +1,6 @@ +#include "ai_navmesh.h" + +ON_DLL_LOAD("server.dll", ServerAiNavMesh, (CModule module)) +{ + g_pNavMesh = module.Offset(0x105F5D0).RCast(); +} diff --git a/primedev/server/ai_navmesh.h b/primedev/server/ai_navmesh.h new file mode 100644 index 00000000..65529f7a --- /dev/null +++ b/primedev/server/ai_navmesh.h @@ -0,0 +1,260 @@ +#pragma once + +// [Fifty]: Taken from https://github.com/ASpoonPlaysGames/r2recast + +#include "core/math/vector.h" + +struct dtMeshHeader; +struct dtMeshTile; +struct dtPoly; +struct dtBVNode; +struct dtLink; + +typedef unsigned int dtPolyRef; + +/// The maximum number of vertices per navigation polygon. +/// @ingroup detour +static const int DT_VERTS_PER_POLYGON = 6; + +/// Flags representing the type of a navigation mesh polygon. +enum dtPolyTypes +{ + /// The polygon is a standard convex polygon that is part of the surface of the mesh. + DT_POLYTYPE_GROUND = 0, + /// The polygon is an off-mesh connection consisting of two vertices. + DT_POLYTYPE_OFFMESH_CONNECTION = 1, +}; + +/// Configuration parameters used to define multi-tile navigation meshes. +/// The values are used to allocate space during the initialization of a navigation mesh. +/// @see dtNavMesh::init() +/// @ingroup detour +struct dtNavMeshParams +{ + float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)] + float tileWidth; ///< The width of each tile. (Along the x-axis.) + float tileHeight; ///< The height of each tile. (Along the z-axis.) + int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits + ///< are needed to identify tiles and polygons uniquely. + int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are + ///< needed to identify tiles and polygons uniquely. + // + //// i hate this + int disjointPolyGroupCount = 0; + int reachabilityTableSize = 0; + int reachabilityTableCount = 0; +}; + +/// Defines an navigation mesh off-mesh connection within a dtMeshTile object. +/// An off-mesh connection is a user defined traversable connection made up to two vertices. +struct dtOffMeshConnection +{ + /// The endpoints of the connection. + Vector3 origin; + Vector3 dest; + + /// The radius of the endpoints. [Limit: >= 0] + float rad; + + /// The polygon reference of the connection within the tile. + unsigned short poly; + + /// Link flags. + /// @note These are not the connection's user defined flags. Those are assigned via the + /// connection's dtPoly definition. These are link flags used for internal purposes. + unsigned char flags; + + /// End point side. + unsigned char side; + + /// The id of the offmesh connection. (User assigned when the navigation mesh is built.) + unsigned int userId; + + float unk[3]; + float another_unk; +}; + +/// A navigation mesh based on tiles of convex polygons. +/// @ingroup detour +class dtNavMesh +{ +public: + dtMeshTile** m_posLookup; ///< Tile hash lookup. + dtMeshTile* m_nextFree; ///< Freelist of tiles. + dtMeshTile* m_tiles; ///< List of tiles. + + void* disjointPolyGroup; + int** reachabilityTable; + + __int64 unk; + dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice. + float m_orig[3]; ///< Origin of the tile (0,0) + float m_tileWidth, m_tileHight; ///< Dimensions of each tile. + int m_pad; + int m_maxTiles; ///< Max number of tiles. + + int m_tileLutSize; ///< Tile hash lookup size (must be pot). + int m_tileLutMask; ///< Tile hash lookup mask. + + int m_saltBits; ///< Number of salt bits in the tile ID. + int m_tileBits; ///< Number of tile bits in the tile ID. + int m_polyBits; ///< Number of poly bits in the tile ID. +}; + +/// Defines the location of detail sub-mesh data within a dtMeshTile. +struct dtPolyDetail +{ + unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array. + unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array. + unsigned char vertCount; ///< The number of vertices in the sub-mesh. + unsigned char triCount; ///< The number of triangles in the sub-mesh. +}; + +/// Defines a navigation mesh tile. +/// @ingroup detour +struct dtMeshTile +{ + int salt; ///< Counter describing modifications to the tile. + unsigned int linksFreeList; ///< Index to the next free link. + dtMeshHeader* header; ///< The tile header. + dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount] + void* unkPolyThing; + float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount] + dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount] + dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount] + + /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount] + float* detailVerts; + + /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount]. + /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags. + unsigned char* detailTris; + + /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount] + /// (Will be null if bounding volumes are disabled.) + dtBVNode* bvTree; + + dtOffMeshConnection* offMeshConnections; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount] + void* data; ///< The tile data. (Not directly accessed under normal situations.) + int dataSize; ///< Size of the tile data. + int flags; ///< Tile flags. (See: #dtTileFlags) + dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid. + __int64 unk; +}; + +/// Provides high level information related to a dtMeshTile object. +/// @ingroup detour +struct dtMeshHeader +{ + int magic; ///< Tile magic number. (Used to identify the data format.) + int version; ///< Tile data format version number. + int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer) + int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer) + int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer) + unsigned int userId; ///< The user defined id of the tile. + int polyCount; ///< The number of polygons in the tile. + int sth_per_poly; + int vertCount; ///< The number of vertices in the tile. + int maxLinkCount; ///< The number of allocated links. + + int detailMeshCount; + + /// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.) + int detailVertCount; + + int detailTriCount; ///< The number of triangles in the detail mesh. + int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.) + int offMeshConCount; ///< The number of off-mesh connections. + // int unk1; + int offMeshBase; ///< The index of the first polygon which is an off-mesh connection. + + float walkableHeight; ///< The height of the agents using the tile. + float walkableRadius; ///< The radius of the agents using the tile. + float walkableClimb; ///< The maximum climb height of the agents using the tile. + float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)] + float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)] + + /// The bounding volume quantization factor. + float bvQuantFactor; +}; + +/// Defines a polygon within a dtMeshTile object. +/// @ingroup detour +struct dtPoly +{ + /// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.) + unsigned int firstLink; + + /// The indices of the polygon's vertices. + /// The actual vertices are located in dtMeshTile::verts. + unsigned short verts[DT_VERTS_PER_POLYGON]; + + /// Packed data representing neighbor polygons references and flags for each edge. + unsigned short neis[DT_VERTS_PER_POLYGON]; + + /// The user defined polygon flags. + unsigned short flags; + + /// The number of vertices in the polygon. + unsigned char vertCount; + + /// The bit packed area id and polygon type. + /// @note Use the structure's set and get methods to acess this value. + unsigned char areaAndtype; + + unsigned short disjointSetId; + unsigned short unk; // IDK but looks filled + Vector3 org; // + + /// Sets the user defined area id. [Limit: < #DT_MAX_AREAS] + inline void setArea(unsigned char a) + { + areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); + } + + /// Sets the polygon type. (See: #dtPolyTypes.) + inline void setType(unsigned char t) + { + areaAndtype = (areaAndtype & 0x3f) | (t << 6); + } + + /// Gets the user defined area id. + inline unsigned char getArea() const + { + return areaAndtype & 0x3f; + } + + /// Gets the polygon type. (See: #dtPolyTypes) + inline unsigned char getType() const + { + return areaAndtype >> 6; + } +}; + +/// Defines a link between polygons. +/// @note This structure is rarely if ever used by the end user. +/// @see dtMeshTile +struct dtLink +{ + dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.) + unsigned int next; ///< Index of the next link. + unsigned char edge; ///< Index of the polygon edge that owns this link. + unsigned char side; ///< If a boundary link, defines on which side the link is. + unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area. + unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area. + unsigned char jumpType; + unsigned char otherUnk; + unsigned short reverseLinkIndex; +}; + +/// Bounding volume node. +/// @note This structure is rarely if ever used by the end user. +/// @see dtMeshTile +struct dtBVNode +{ + unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)] + unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)] + int i; ///< The node's index. (Negative for escape sequence.) +}; + +inline dtNavMesh** g_pNavMesh = nullptr; diff --git a/NorthstarDLL/server/alltalk.cpp b/primedev/server/alltalk.cpp similarity index 83% rename from NorthstarDLL/server/alltalk.cpp rename to primedev/server/alltalk.cpp index d71b0bae..4eb5aef7 100644 --- a/NorthstarDLL/server/alltalk.cpp +++ b/primedev/server/alltalk.cpp @@ -4,18 +4,18 @@ size_t __fastcall ShouldAllowAlltalk() { // this needs to return a 64 bit integer where 0 = true and 1 = false - static ConVar* Cvar_sv_alltalk = R2::g_pCVar->FindVar("sv_alltalk"); + static ConVar* Cvar_sv_alltalk = g_pCVar->FindVar("sv_alltalk"); if (Cvar_sv_alltalk->GetBool()) return 0; // lobby should default to alltalk, otherwise don't allow it - return strcmp(R2::g_pGlobals->m_pMapName, "mp_lobby"); + return strcmp(g_pGlobals->m_pMapName, "mp_lobby"); } ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module)) { // replace strcmp function called in CClient::ProcessVoiceData with our own code that calls ShouldAllowAllTalk - CMemoryAddress base = module.Offset(0x1085FA); + CMemory base = module.Offset(0x1085FA); base.Patch("48 B8"); // mov rax, 64 bit int // (uint8_t*)&ShouldAllowAlltalk doesn't work for some reason? need to make it a uint64 first diff --git a/NorthstarDLL/server/auth/bansystem.cpp b/primedev/server/auth/bansystem.cpp similarity index 79% rename from NorthstarDLL/server/auth/bansystem.cpp rename to primedev/server/auth/bansystem.cpp index 9b9d24c4..20c07844 100644 --- a/NorthstarDLL/server/auth/bansystem.cpp +++ b/primedev/server/auth/bansystem.cpp @@ -5,8 +5,11 @@ #include "engine/r2engine.h" #include "client/r2client.h" #include "config/profile.h" +#include "shared/maxplayers.h" #include +#include +#include const char* BANLIST_PATH_SUFFIX = "/banlist.txt"; const char BANLIST_COMMENT_CHAR = '#'; @@ -173,7 +176,7 @@ void ServerBanSystem::UnbanUID(uint64_t uid) bool ServerBanSystem::IsUIDAllowed(uint64_t uid) { - uint64_t localPlayerUserID = strtoull(R2::g_pLocalPlayerUserID, nullptr, 10); + uint64_t localPlayerUserID = strtoull(g_pLocalPlayerUserID, nullptr, 10); if (localPlayerUserID == uid) return true; @@ -186,14 +189,14 @@ void ConCommand_ban(const CCommand& args) if (args.ArgC() < 2) return; - for (int i = 0; i < R2::g_pGlobals->m_nMaxClients; i++) + for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) { - R2::CBaseClient* player = &R2::g_pClientArray[i]; + CBaseClient* player = &g_pClientArray[i]; if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1))) { g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10)); - R2::CBaseClient__Disconnect(player, 1, "Banned from server"); + CBaseClient__Disconnect(player, 1, "Banned from server"); break; } } @@ -210,15 +213,63 @@ void ConCommand_unban(const CCommand& args) void ConCommand_clearbanlist(const CCommand& args) { + NOTE_UNUSED(args); g_pBanSystem->ClearBanlist(); } +int ConCommand_banCompletion(const char* const partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]) +{ + const char* space = strchr(partial, ' '); + const char* cmdName = partial; + const char* query = partial + (space == nullptr ? 0 : space - partial) + 1; + + const size_t queryLength = strlen(query); + const size_t cmdLength = strlen(cmdName) - queryLength; + + int numCompletions = 0; + for (int i = 0; i < GetMaxPlayers() && numCompletions < COMMAND_COMPLETION_MAXITEMS - 2; i++) + { + CBaseClient* client = &g_pClientArray[i]; + if (client->m_Signon < eSignonState::CONNECTED) + continue; + + if (!strncmp(query, client->m_Name, queryLength)) + { + strncpy(commands[numCompletions], cmdName, cmdLength); + strncpy_s( + commands[numCompletions++] + cmdLength, + COMMAND_COMPLETION_ITEM_LENGTH, + client->m_Name, + COMMAND_COMPLETION_ITEM_LENGTH - cmdLength); + } + + if (!strncmp(query, client->m_UID, queryLength)) + { + strncpy(commands[numCompletions], cmdName, cmdLength); + strncpy_s( + commands[numCompletions++] + cmdLength, + COMMAND_COMPLETION_ITEM_LENGTH, + client->m_UID, + COMMAND_COMPLETION_ITEM_LENGTH - cmdLength); + } + } + + return numCompletions; +} + ON_DLL_LOAD_RELIESON("engine.dll", BanSystem, ConCommand, (CModule module)) { g_pBanSystem = new ServerBanSystem; g_pBanSystem->OpenBanlist(); - RegisterConCommand("ban", ConCommand_ban, "bans a given player by uid or name", FCVAR_GAMEDLL); + RegisterConCommand("ban", ConCommand_ban, "bans a given player by uid or name", FCVAR_GAMEDLL, ConCommand_banCompletion); RegisterConCommand("unban", ConCommand_unban, "unbans a given player by uid", FCVAR_GAMEDLL); RegisterConCommand("clearbanlist", ConCommand_clearbanlist, "clears all uids on the banlist", FCVAR_GAMEDLL); } + +ON_DLL_LOAD_RELIESON("server.dll", KickCompletion, ConCommand, (CModule module)) +{ + ConCommand* kick = g_pCVar->FindCommand("kick"); + kick->m_pCompletionCallback = ConCommand_banCompletion; + kick->m_nCallbackFlags |= 0x3; +} diff --git a/NorthstarDLL/server/auth/bansystem.h b/primedev/server/auth/bansystem.h similarity index 94% rename from NorthstarDLL/server/auth/bansystem.h rename to primedev/server/auth/bansystem.h index 6f180126..d6ac5a4f 100644 --- a/NorthstarDLL/server/auth/bansystem.h +++ b/primedev/server/auth/bansystem.h @@ -3,11 +3,11 @@ class ServerBanSystem { - private: +private: std::ofstream m_sBanlistStream; std::vector m_vBannedUids; - public: +public: void OpenBanlist(); void ReloadBanlist(); void ClearBanlist(); diff --git a/NorthstarDLL/server/auth/serverauthentication.cpp b/primedev/server/auth/serverauthentication.cpp similarity index 84% rename from NorthstarDLL/server/auth/serverauthentication.cpp rename to primedev/server/auth/serverauthentication.cpp index d5653dcc..d0d4c698 100644 --- a/NorthstarDLL/server/auth/serverauthentication.cpp +++ b/primedev/server/auth/serverauthentication.cpp @@ -40,7 +40,7 @@ void ServerAuthenticationManager::AddRemotePlayer(std::string token, uint64_t ui m_RemoteAuthenticationData[token] = newAuthData; } -void ServerAuthenticationManager::AddPlayer(R2::CBaseClient* pPlayer, const char* pToken) +void ServerAuthenticationManager::AddPlayer(CBaseClient* pPlayer, const char* pToken) { PlayerAuthenticationData additionalData; @@ -48,14 +48,14 @@ void ServerAuthenticationManager::AddPlayer(R2::CBaseClient* pPlayer, const char if (remoteAuthData != m_RemoteAuthenticationData.end()) additionalData.pdataSize = remoteAuthData->second.pdataSize; else - additionalData.pdataSize = R2::PERSISTENCE_MAX_SIZE; + additionalData.pdataSize = PERSISTENCE_MAX_SIZE; - additionalData.usingLocalPdata = pPlayer->m_iPersistenceReady == R2::ePersistenceReady::READY_INSECURE; + additionalData.usingLocalPdata = pPlayer->m_iPersistenceReady == ePersistenceReady::READY_INSECURE; m_PlayerAuthenticationData.insert(std::make_pair(pPlayer, additionalData)); } -void ServerAuthenticationManager::RemovePlayer(R2::CBaseClient* pPlayer) +void ServerAuthenticationManager::RemovePlayer(CBaseClient* pPlayer) { if (m_PlayerAuthenticationData.count(pPlayer)) m_PlayerAuthenticationData.erase(pPlayer); @@ -88,20 +88,20 @@ bool ServerAuthenticationManager::VerifyPlayerName(const char* pAuthToken, const return true; } -bool ServerAuthenticationManager::IsDuplicateAccount(R2::CBaseClient* pPlayer, const char* pPlayerUid) +bool ServerAuthenticationManager::IsDuplicateAccount(CBaseClient* pPlayer, const char* pPlayerUid) { if (m_bAllowDuplicateAccounts) return false; bool bHasUidPlayer = false; - for (int i = 0; i < R2::g_pGlobals->m_nMaxClients; i++) - if (&R2::g_pClientArray[i] != pPlayer && !strcmp(pPlayerUid, R2::g_pClientArray[i].m_UID)) + for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) + if (&g_pClientArray[i] != pPlayer && !strcmp(pPlayerUid, g_pClientArray[i].m_UID)) return true; return false; } -bool ServerAuthenticationManager::CheckAuthentication(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken) +bool ServerAuthenticationManager::CheckAuthentication(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken) { std::string sUid = std::to_string(iUid); @@ -111,7 +111,7 @@ bool ServerAuthenticationManager::CheckAuthentication(R2::CBaseClient* pPlayer, return true; // local server that doesn't need auth (probably sp) and local player - if (m_bStartingLocalSPGame && !strcmp(sUid.c_str(), R2::g_pLocalPlayerUserID)) + if (m_bStartingLocalSPGame && !strcmp(sUid.c_str(), g_pLocalPlayerUserID)) return true; // don't allow duplicate accounts @@ -126,7 +126,7 @@ bool ServerAuthenticationManager::CheckAuthentication(R2::CBaseClient* pPlayer, return false; } -void ServerAuthenticationManager::AuthenticatePlayer(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken) +void ServerAuthenticationManager::AuthenticatePlayer(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken) { // for bot players, generate a new uid if (pPlayer->m_bFakePlayer) @@ -142,31 +142,31 @@ void ServerAuthenticationManager::AuthenticatePlayer(R2::CBaseClient* pPlayer, u if (authData != m_RemoteAuthenticationData.end()) { // if we're resetting let script handle the reset with InitPersistentData() on connect - if (!m_bForceResetLocalPlayerPersistence || strcmp(sUid.c_str(), R2::g_pLocalPlayerUserID)) + if (!m_bForceResetLocalPlayerPersistence || strcmp(sUid.c_str(), g_pLocalPlayerUserID)) { // copy pdata into buffer memcpy(pPlayer->m_PersistenceBuffer, authData->second.pdata, authData->second.pdataSize); } // set persistent data as ready - pPlayer->m_iPersistenceReady = R2::ePersistenceReady::READY_REMOTE; + pPlayer->m_iPersistenceReady = ePersistenceReady::READY_REMOTE; } // we probably allow insecure at this point, but make sure not to write anyway if not insecure else if (Cvar_ns_auth_allow_insecure->GetBool() || pPlayer->m_bFakePlayer) { // set persistent data as ready // note: actual placeholder persistent data is populated in script with InitPersistentData() - pPlayer->m_iPersistenceReady = R2::ePersistenceReady::READY_INSECURE; + pPlayer->m_iPersistenceReady = ePersistenceReady::READY_INSECURE; } } -bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBaseClient* pPlayer) +bool ServerAuthenticationManager::RemovePlayerAuthData(CBaseClient* pPlayer) { if (!Cvar_ns_erase_auth_info->GetBool()) // keep auth data forever return false; // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect - if (m_bNeedLocalAuthForNewgame && !strcmp(pPlayer->m_UID, R2::g_pLocalPlayerUserID)) + if (m_bNeedLocalAuthForNewgame && !strcmp(pPlayer->m_UID, g_pLocalPlayerUserID)) return false; // we don't have our auth token at this point, so lookup authdata by uid @@ -187,9 +187,9 @@ bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBaseClient* pPlayer) return false; } -void ServerAuthenticationManager::WritePersistentData(R2::CBaseClient* pPlayer) +void ServerAuthenticationManager::WritePersistentData(CBaseClient* pPlayer) { - if (pPlayer->m_iPersistenceReady == R2::ePersistenceReady::READY_REMOTE) + if (pPlayer->m_iPersistenceReady == ePersistenceReady::READY_REMOTE) { g_pMasterServerManager->WritePlayerPersistentData( pPlayer->m_UID, (const char*)pPlayer->m_PersistenceBuffer, m_PlayerAuthenticationData[pPlayer].pdataSize); @@ -240,7 +240,7 @@ ConVar* Cvar_ns_allowuserclantags; // clang-format off AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740, -bool,, (R2::CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7)) +bool,, (CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7)) // clang-format on { const char* pAuthenticationFailure = nullptr; @@ -281,17 +281,17 @@ bool,, (R2::CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, // clang-format off AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80, -void,, (R2::CBaseClient* self)) +void,, (CBaseClient* self)) // clang-format on { // 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 // (since this func is called on map loads) - if (self->m_iPersistenceReady >= R2::ePersistenceReady::READY && !g_pServerAuthentication->RemovePlayerAuthData(self)) + if (self->m_iPersistenceReady >= ePersistenceReady::READY && !g_pServerAuthentication->RemovePlayerAuthData(self)) { g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = false; g_pServerAuthentication->WritePersistentData(self); - g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size()); + g_pServerPresence->SetPlayerCount((int)g_pServerAuthentication->m_PlayerAuthenticationData.size()); } CBaseClient__ActivatePlayer(self); @@ -299,7 +299,7 @@ void,, (R2::CBaseClient* self)) // clang-format off AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0, -void,, (R2::CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...)) +void,, (CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...)) // clang-format on { // have to manually format message because can't pass varargs to original func @@ -326,14 +326,15 @@ void,, (R2::CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, g_pServerLimits->RemovePlayer(self); } - g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size()); + g_pServerPresence->SetPlayerCount((int)g_pServerAuthentication->m_PlayerAuthenticationData.size()); _CBaseClient__Disconnect(self, unknownButAlways1, buf); } void ConCommand_ns_resetpersistence(const CCommand& args) { - if (*R2::g_pServerState == R2::server_state_t::ss_active) + NOTE_UNUSED(args); + if (*g_pServerState == server_state_t::ss_active) { spdlog::error("ns_resetpersistence must be entered from the main menu"); return; @@ -370,7 +371,7 @@ ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), ( CBaseServer__RejectConnection = module.Offset(0x1182E0).RCast(); - if (Tier0::CommandLine()->CheckParm("-allowdupeaccounts")) + if (CommandLine()->CheckParm("-allowdupeaccounts")) { // patch to allow same of multiple account module.Offset(0x114510).Patch("EB"); diff --git a/NorthstarDLL/server/auth/serverauthentication.h b/primedev/server/auth/serverauthentication.h similarity index 68% rename from NorthstarDLL/server/auth/serverauthentication.h rename to primedev/server/auth/serverauthentication.h index dd0e13af..996d20e1 100644 --- a/NorthstarDLL/server/auth/serverauthentication.h +++ b/primedev/server/auth/serverauthentication.h @@ -26,33 +26,33 @@ extern CBaseServer__RejectConnectionType CBaseServer__RejectConnection; class ServerAuthenticationManager { - public: +public: ConVar* Cvar_ns_erase_auth_info; ConVar* Cvar_ns_auth_allow_insecure; ConVar* Cvar_ns_auth_allow_insecure_write; std::mutex m_AuthDataMutex; std::unordered_map m_RemoteAuthenticationData; - std::unordered_map m_PlayerAuthenticationData; + std::unordered_map m_PlayerAuthenticationData; bool m_bAllowDuplicateAccounts = false; bool m_bNeedLocalAuthForNewgame = false; bool m_bForceResetLocalPlayerPersistence = false; bool m_bStartingLocalSPGame = false; - public: +public: void AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata); - void AddPlayer(R2::CBaseClient* pPlayer, const char* pAuthToken); - void RemovePlayer(R2::CBaseClient* pPlayer); + void AddPlayer(CBaseClient* pPlayer, const char* pAuthToken); + void RemovePlayer(CBaseClient* pPlayer); bool VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64]); - bool IsDuplicateAccount(R2::CBaseClient* pPlayer, const char* pUid); - bool CheckAuthentication(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken); + bool IsDuplicateAccount(CBaseClient* pPlayer, const char* pUid); + bool CheckAuthentication(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken); - void AuthenticatePlayer(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken); - bool RemovePlayerAuthData(R2::CBaseClient* pPlayer); - void WritePersistentData(R2::CBaseClient* pPlayer); + void AuthenticatePlayer(CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken); + bool RemovePlayerAuthData(CBaseClient* pPlayer); + void WritePersistentData(CBaseClient* pPlayer); }; extern ServerAuthenticationManager* g_pServerAuthentication; diff --git a/NorthstarDLL/server/buildainfile.cpp b/primedev/server/buildainfile.cpp similarity index 98% rename from NorthstarDLL/server/buildainfile.cpp rename to primedev/server/buildainfile.cpp index d0143295..a7f59961 100644 --- a/NorthstarDLL/server/buildainfile.cpp +++ b/primedev/server/buildainfile.cpp @@ -177,8 +177,8 @@ ConVar* Cvar_ns_ai_dumpAINfileFromLoad; void DumpAINInfo(CAI_Network* aiNetwork) { - fs::path writePath(fmt::format("{}/maps/graphs", R2::g_pModName)); - writePath /= R2::g_pGlobals->m_pMapName; + fs::path writePath(fmt::format("{}/maps/graphs", g_pModName)); + writePath /= g_pGlobals->m_pMapName; writePath += ".ain"; // dump from memory @@ -193,7 +193,7 @@ void DumpAINInfo(CAI_Network* aiNetwork) spdlog::info("writing ainet version: {}", AINET_VERSION_NUMBER); writeStream.write((char*)&AINET_VERSION_NUMBER, sizeof(int)); - int mapVersion = R2::g_pGlobals->m_nMapVersion; + int mapVersion = g_pGlobals->m_nMapVersion; spdlog::info("writing map version: {}", mapVersion); writeStream.write((char*)&mapVersion, sizeof(int)); spdlog::info("writing placeholder crc: {}", PLACEHOLDER_CRC); diff --git a/NorthstarDLL/server/r2server.cpp b/primedev/server/r2server.cpp similarity index 54% rename from NorthstarDLL/server/r2server.cpp rename to primedev/server/r2server.cpp index cf7add0d..c52f396e 100644 --- a/NorthstarDLL/server/r2server.cpp +++ b/primedev/server/r2server.cpp @@ -1,13 +1,7 @@ #include "r2server.h" -using namespace R2; - -// use the R2 namespace for game funcs -namespace R2 -{ - CBaseEntity* (*Server_GetEntityByIndex)(int index); - CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); -} // namespace R2 +CBaseEntity* (*Server_GetEntityByIndex)(int index); +CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); ON_DLL_LOAD("server.dll", R2GameServer, (CModule module)) { diff --git a/primedev/server/r2server.h b/primedev/server/r2server.h new file mode 100644 index 00000000..1b7bc0b7 --- /dev/null +++ b/primedev/server/r2server.h @@ -0,0 +1,255 @@ +#pragma once + +#include "core/math/vector.h" + +// server entity stuff +class CBaseEntity; +extern CBaseEntity* (*Server_GetEntityByIndex)(int index); + +#pragma pack(push, 1) +class CBasePlayer +{ +public: + char _unk_0x0[88]; // 0x0 ( Size: 88 ) + uint32_t m_nPlayerIndex; // 0x58 ( Size: 4 ) + char _unk_0x5c[572]; // 0x5c ( Size: 572 ) + int32_t m_fFlags; // 0x298 ( Size: 4 ) + char _unk_0x29c[376]; // 0x29c ( Size: 376 ) + int32_t m_hGroundEntity; // 0x414 ( Size: 4 ) + char _unk_0x418[184]; // 0x418 ( Size: 184 ) + int32_t m_iMaxHealth; // 0x4d0 ( Size: 4 ) + int32_t m_iHealth; // 0x4d4 ( Size: 4 ) + char _unk_0x4d8[25]; // 0x4d8 ( Size: 25 ) + int32_t m_lifeState; // 0x4f1 ( Size: 4 ) + char _unk_0x4f5[23]; // 0x4f5 ( Size: 23 ) + float m_flMaxspeed; // 0x50c ( Size: 4 ) + char _unk_0x510[1268]; // 0x510 ( Size: 1268 ) + int32_t m_camoIndex; // 0xa04 ( Size: 4 ) + int32_t m_decalIndex; // 0xa08 ( Size: 4 ) + char _unk_0xa0c[2380]; // 0xa0c ( Size: 2380 ) + int32_t m_selectedOffhandPendingHybridAction; // 0x1358 ( Size: 4 ) + char _unk_0x135c[92]; // 0x135c ( Size: 92 ) + int32_t m_titanSoul; // 0x13b8 ( Size: 4 ) + char _unk_0x13bc[476]; // 0x13bc ( Size: 476 ) + bool m_bZooming; // 0x1598 ( Size: 1 ) + bool m_zoomToggleOn; // 0x1599 ( Size: 1 ) + char _unk_0x159a[2]; // 0x159a ( Size: 2 ) + float m_zoomBaseFrac; // 0x159c ( Size: 4 ) + float m_zoomBaseTime; // 0x15a0 ( Size: 4 ) + float m_zoomFullStartTime; // 0x15a4 ( Size: 4 ) + char _unk_0x15a8[1080]; // 0x15a8 ( Size: 1080 ) + int32_t m_PlayerFog__m_hCtrl; // 0x19e0 ( Size: 4 ) + char _unk_0x19e4[388]; // 0x19e4 ( Size: 388 ) + int32_t m_hColorCorrectionCtrl; // 0x1b68 ( Size: 4 ) + char _unk_0x1b6c[292]; // 0x1b6c ( Size: 292 ) + bool m_hasBadReputation; // 0x1c90 ( Size: 1 ) + char m_communityName[64]; // 0x1c91 ( Size: 64 ) + char m_communityClanTag[16]; // 0x1cd1 ( Size: 16 ) + char m_factionName[16]; // 0x1ce1 ( Size: 16 ) + char m_hardwareIcon[16]; // 0x1cf1 ( Size: 16 ) + bool m_happyHourActive; // 0x1d01 ( Size: 1 ) + char _unk_0x1d02[6]; // 0x1d02 ( Size: 6 ) + uint32_t m_platformUserId; // 0x1d08 ( Size: 4 ) + char _unk_0x1d0c[4]; // 0x1d0c ( Size: 4 ) + int32_t m_classModsActive; // 0x1d10 ( Size: 4 ) + char _unk_0x1d14[120]; // 0x1d14 ( Size: 120 ) + int32_t m_posClassModsActive; // 0x1d8c ( Size: 4 ) + char _unk_0x1d90[60]; // 0x1d90 ( Size: 60 ) + bool m_passives; // 0x1dcc ( Size: 1 ) + char _unk_0x1dcd[179]; // 0x1dcd ( Size: 179 ) + int32_t m_titanSoulBeingRodeoed; // 0x1e80 ( Size: 4 ) + int32_t m_entitySyncingWithMe; // 0x1e84 ( Size: 4 ) + int32_t m_playerFlags; // 0x1e88 ( Size: 4 ) + bool m_hasMic; // 0x1e8c ( Size: 1 ) + bool m_inPartyChat; // 0x1e8d ( Size: 1 ) + char _unk_0x1e8e[2]; // 0x1e8e ( Size: 2 ) + float m_playerMoveSpeedScale; // 0x1e90 ( Size: 4 ) + char _unk_0x1e94[96]; // 0x1e94 ( Size: 96 ) + int32_t m_gestureAutoKillBitfield; // 0x1ef4 ( Size: 4 ) + char _unk_0x1ef8[12]; // 0x1ef8 ( Size: 12 ) + int32_t m_remoteTurret; // 0x1f04 ( Size: 4 ) + char _unk_0x1f08[56]; // 0x1f08 ( Size: 56 ) + float m_damageImpulseNoDecelEndTime; // 0x1f40 ( Size: 4 ) + char _unk_0x1f44[20]; // 0x1f44 ( Size: 20 ) + float m_flDeathTime; // 0x1f58 ( Size: 4 ) + char _unk_0x1f5c[8]; // 0x1f5c ( Size: 8 ) + int32_t m_iObserverMode; // 0x1f64 ( Size: 4 ) + char _unk_0x1f68[4]; // 0x1f68 ( Size: 4 ) + int32_t m_hObserverTarget; // 0x1f6c ( Size: 4 ) + char _unk_0x1f70[52]; // 0x1f70 ( Size: 52 ) + int32_t m_activeBurnCardIndex; // 0x1fa4 ( Size: 4 ) + char _unk_0x1fa8[168]; // 0x1fa8 ( Size: 168 ) + int32_t m_grappleHook; // 0x2050 ( Size: 4 ) + int32_t m_petTitan; // 0x2054 ( Size: 4 ) + char _unk_0x2058[4]; // 0x2058 ( Size: 4 ) + int32_t m_xp; // 0x205c ( Size: 4 ) + int32_t m_generation; // 0x2060 ( Size: 4 ) + int32_t m_rank; // 0x2064 ( Size: 4 ) + int32_t m_serverForceIncreasePlayerListGenerationParity; // 0x2068 ( Size: 4 ) + bool m_isPlayingRanked; // 0x206c ( Size: 1 ) + char _unk_0x206d[3]; // 0x206d ( Size: 3 ) + float m_skill_mu; // 0x2070 ( Size: 4 ) + char _unk_0x2074[4]; // 0x2074 ( Size: 4 ) + float m_nextTitanRespawnAvailable; // 0x2078 ( Size: 4 ) + char _unk_0x207c[28]; // 0x207c ( Size: 28 ) + int32_t m_hViewModel; // 0x2098 ( Size: 4 ) + char _unk_0x209c[436]; // 0x209c ( Size: 436 ) + int32_t m_duckState; // 0x2250 ( Size: 4 ) + Vector3 m_StandHullMin; // 0x2254 ( Size: 12 ) + Vector3 m_StandHullMax; // 0x2260 ( Size: 12 ) + Vector3 m_DuckHullMin; // 0x226c ( Size: 12 ) + Vector3 m_DuckHullMax; // 0x2278 ( Size: 12 ) + char _unk_0x2284[92]; // 0x2284 ( Size: 92 ) + bool m_wallHanging; // 0x22e0 ( Size: 1 ) + char _unk_0x22e1[11]; // 0x22e1 ( Size: 11 ) + int32_t m_traversalType; // 0x22ec ( Size: 4 ) + int32_t m_traversalState; // 0x22f0 ( Size: 4 ) + char _unk_0x22f4[40]; // 0x22f4 ( Size: 40 ) + Vector3 m_traversalForwardDir; // 0x231c ( Size: 12 ) + Vector3 m_traversalRefPos; // 0x2328 ( Size: 12 ) + char _unk_0x2334[32]; // 0x2334 ( Size: 32 ) + float m_traversalYawDelta; // 0x2354 ( Size: 4 ) + int32_t m_traversalYawPoseParameter; // 0x2358 ( Size: 4 ) + char _unk_0x235c[140]; // 0x235c ( Size: 140 ) + bool m_grappleActive; // 0x23e8 ( Size: 1 ) + char _unk_0x23e9[19]; // 0x23e9 ( Size: 19 ) + int32_t m_activeZipline; // 0x23fc ( Size: 4 ) + bool m_ziplineReverse; // 0x2400 ( Size: 1 ) + char _unk_0x2401[11]; // 0x2401 ( Size: 11 ) + bool m_ziplineValid3pWeaponLayerAnim; // 0x240c ( Size: 1 ) + char _unk_0x240d[3]; // 0x240d ( Size: 3 ) + int32_t m_ziplineState; // 0x2410 ( Size: 4 ) + char _unk_0x2414[392]; // 0x2414 ( Size: 392 ) + float m_lastDodgeTime; // 0x259c ( Size: 4 ) + char _unk_0x25a0[8]; // 0x25a0 ( Size: 8 ) + bool m_iSpawnParity; // 0x25a8 ( Size: 1 ) + char _unk_0x25a9[21]; // 0x25a9 ( Size: 21 ) + bool m_isPerformingBoostAction; // 0x25be ( Size: 1 ) + char _unk_0x25bf[233]; // 0x25bf ( Size: 233 ) + int32_t m_lastUCmdSimulationTicks; // 0x26a8 ( Size: 4 ) + float m_lastUCmdSimulationRemainderTime; // 0x26ac ( Size: 4 ) + char _unk_0x26b0[12]; // 0x26b0 ( Size: 12 ) + bool m_bShouldDrawPlayerWhileUsingViewEntity; // 0x26bc ( Size: 1 ) + char _unk_0x26bd[259]; // 0x26bd ( Size: 259 ) + int32_t m_autoSprintForced; // 0x27c0 ( Size: 4 ) + bool m_fIsSprinting; // 0x27c4 ( Size: 1 ) + char _unk_0x27c5[7]; // 0x27c5 ( Size: 7 ) + float m_sprintStartedTime; // 0x27cc ( Size: 4 ) + float m_sprintStartedFrac; // 0x27d0 ( Size: 4 ) + float m_sprintEndedTime; // 0x27d4 ( Size: 4 ) + float m_sprintEndedFrac; // 0x27d8 ( Size: 4 ) + float m_stickySprintStartTime; // 0x27dc ( Size: 4 ) + char _unk_0x27e0[4]; // 0x27e0 ( Size: 4 ) + int32_t m_ubEFNointerpParity; // 0x27e4 ( Size: 4 ) + char _unk_0x27e8[96]; // 0x27e8 ( Size: 96 ) + char m_title[32]; // 0x2848 ( Size: 32 ) + char _unk_0x2868[252]; // 0x2868 ( Size: 252 ) + bool m_useCredit; // 0x2964 ( Size: 1 ) + char _unk_0x2965[51]; // 0x2965 ( Size: 51 ) + float m_smartAmmoPreviousHighestLockOnMeFractionValue; // 0x2998 ( Size: 4 ) + char _unk_0x299c[1292]; // 0x299c ( Size: 1292 ) + int32_t m_pilotClassIndex; // 0x2ea8 ( Size: 4 ) + char _unk_0x2eac[1456]; // 0x2eac ( Size: 1456 ) + int32_t m_playerScriptNetDataGlobal; // 0x345c ( Size: 4 ) + char _unk_0x3460[5352]; // 0x3460 ( Size: 5352 ) + int32_t m_selectedOffhand; // 0x4948 ( Size: 4 ) + char _unk_0x494c[1030980]; // 0x494c ( Size: 1030980 ) + Vector3 m_vecAbsOrigin; // 0x100490 ( Size: 12 ) + char _unk_0x10049c[7656]; // 0x10049c ( Size: 7656 ) + Vector3 m_upDir; // 0x102284 ( Size: 12 ) +}; +#pragma pack(pop) + +static_assert(offsetof(CBasePlayer, m_nPlayerIndex) == 0x58); + +static_assert(offsetof(CBasePlayer, m_grappleActive) == 0x23e8); +static_assert(offsetof(CBasePlayer, m_platformUserId) == 0x1D08); +static_assert(offsetof(CBasePlayer, m_classModsActive) == 0x1D10); +static_assert(offsetof(CBasePlayer, m_posClassModsActive) == 0x1D8C); +static_assert(offsetof(CBasePlayer, m_passives) == 0x1DCC); +static_assert(offsetof(CBasePlayer, m_selectedOffhand) == 0x4948); +static_assert(offsetof(CBasePlayer, m_selectedOffhandPendingHybridAction) == 0x1358); +static_assert(offsetof(CBasePlayer, m_playerFlags) == 0x1E88); +static_assert(offsetof(CBasePlayer, m_lastUCmdSimulationTicks) == 0x26A8); +static_assert(offsetof(CBasePlayer, m_lastUCmdSimulationRemainderTime) == 0x26AC); +static_assert(offsetof(CBasePlayer, m_remoteTurret) == 0x1F04); +static_assert(offsetof(CBasePlayer, m_hGroundEntity) == 0x414); +static_assert(offsetof(CBasePlayer, m_titanSoul) == 0x13B8); +static_assert(offsetof(CBasePlayer, m_petTitan) == 0x2054); +static_assert(offsetof(CBasePlayer, m_iHealth) == 0x4D4); +static_assert(offsetof(CBasePlayer, m_iMaxHealth) == 0x4D0); +static_assert(offsetof(CBasePlayer, m_lifeState) == 0x4F1); +static_assert(offsetof(CBasePlayer, m_flMaxspeed) == 0x50C); +static_assert(offsetof(CBasePlayer, m_fFlags) == 0x298); +static_assert(offsetof(CBasePlayer, m_iObserverMode) == 0x1F64); +static_assert(offsetof(CBasePlayer, m_hObserverTarget) == 0x1F6C); +static_assert(offsetof(CBasePlayer, m_hViewModel) == 0x2098); +static_assert(offsetof(CBasePlayer, m_ubEFNointerpParity) == 0x27E4); +static_assert(offsetof(CBasePlayer, m_activeBurnCardIndex) == 0x1FA4); +static_assert(offsetof(CBasePlayer, m_hColorCorrectionCtrl) == 0x1B68); +static_assert(offsetof(CBasePlayer, m_PlayerFog__m_hCtrl) == 0x19E0); +static_assert(offsetof(CBasePlayer, m_bShouldDrawPlayerWhileUsingViewEntity) == 0x26BC); +static_assert(offsetof(CBasePlayer, m_title) == 0x2848); +static_assert(offsetof(CBasePlayer, m_useCredit) == 0x2964); +static_assert(offsetof(CBasePlayer, m_damageImpulseNoDecelEndTime) == 0x1F40); +static_assert(offsetof(CBasePlayer, m_hasMic) == 0x1E8C); +static_assert(offsetof(CBasePlayer, m_inPartyChat) == 0x1E8D); +static_assert(offsetof(CBasePlayer, m_playerMoveSpeedScale) == 0x1E90); +static_assert(offsetof(CBasePlayer, m_flDeathTime) == 0x1F58); +static_assert(offsetof(CBasePlayer, m_iSpawnParity) == 0x25A8); +static_assert(offsetof(CBasePlayer, m_upDir) == 0x102284); +static_assert(offsetof(CBasePlayer, m_lastDodgeTime) == 0x259C); +static_assert(offsetof(CBasePlayer, m_wallHanging) == 0x22E0); +static_assert(offsetof(CBasePlayer, m_traversalType) == 0x22EC); +static_assert(offsetof(CBasePlayer, m_traversalState) == 0x22F0); +static_assert(offsetof(CBasePlayer, m_traversalRefPos) == 0x2328); +static_assert(offsetof(CBasePlayer, m_traversalForwardDir) == 0x231C); +static_assert(offsetof(CBasePlayer, m_traversalYawDelta) == 0x2354); +static_assert(offsetof(CBasePlayer, m_traversalYawPoseParameter) == 0x2358); +static_assert(offsetof(CBasePlayer, m_grappleHook) == 0x2050); +static_assert(offsetof(CBasePlayer, m_autoSprintForced) == 0x27C0); +static_assert(offsetof(CBasePlayer, m_fIsSprinting) == 0x27C4); +static_assert(offsetof(CBasePlayer, m_sprintStartedTime) == 0x27CC); +static_assert(offsetof(CBasePlayer, m_sprintStartedFrac) == 0x27D0); +static_assert(offsetof(CBasePlayer, m_sprintEndedTime) == 0x27D4); +static_assert(offsetof(CBasePlayer, m_sprintEndedFrac) == 0x27D8); +static_assert(offsetof(CBasePlayer, m_stickySprintStartTime) == 0x27DC); +static_assert(offsetof(CBasePlayer, m_smartAmmoPreviousHighestLockOnMeFractionValue) == 0x2998); +static_assert(offsetof(CBasePlayer, m_activeZipline) == 0x23FC); +static_assert(offsetof(CBasePlayer, m_ziplineReverse) == 0x2400); +static_assert(offsetof(CBasePlayer, m_ziplineState) == 0x2410); +static_assert(offsetof(CBasePlayer, m_duckState) == 0x2250); +static_assert(offsetof(CBasePlayer, m_StandHullMin) == 0x2254); +static_assert(offsetof(CBasePlayer, m_StandHullMax) == 0x2260); +static_assert(offsetof(CBasePlayer, m_DuckHullMin) == 0x226C); +static_assert(offsetof(CBasePlayer, m_DuckHullMax) == 0x2278); +static_assert(offsetof(CBasePlayer, m_xp) == 0x205C); +static_assert(offsetof(CBasePlayer, m_generation) == 0x2060); +static_assert(offsetof(CBasePlayer, m_rank) == 0x2064); +static_assert(offsetof(CBasePlayer, m_serverForceIncreasePlayerListGenerationParity) == 0x2068); +static_assert(offsetof(CBasePlayer, m_isPlayingRanked) == 0x206C); +static_assert(offsetof(CBasePlayer, m_skill_mu) == 0x2070); +static_assert(offsetof(CBasePlayer, m_titanSoulBeingRodeoed) == 0x1E80); +static_assert(offsetof(CBasePlayer, m_entitySyncingWithMe) == 0x1E84); +static_assert(offsetof(CBasePlayer, m_nextTitanRespawnAvailable) == 0x2078); +static_assert(offsetof(CBasePlayer, m_hasBadReputation) == 0x1C90); +static_assert(offsetof(CBasePlayer, m_communityName) == 0x1C91); +static_assert(offsetof(CBasePlayer, m_communityClanTag) == 0x1CD1); +static_assert(offsetof(CBasePlayer, m_factionName) == 0x1CE1); +static_assert(offsetof(CBasePlayer, m_hardwareIcon) == 0x1CF1); +static_assert(offsetof(CBasePlayer, m_happyHourActive) == 0x1D01); +static_assert(offsetof(CBasePlayer, m_gestureAutoKillBitfield) == 0x1EF4); +static_assert(offsetof(CBasePlayer, m_pilotClassIndex) == 0x2EA8); +static_assert(offsetof(CBasePlayer, m_vecAbsOrigin) == 0x100490); +static_assert(offsetof(CBasePlayer, m_isPerformingBoostAction) == 0x25BE); +static_assert(offsetof(CBasePlayer, m_ziplineValid3pWeaponLayerAnim) == 0x240C); +static_assert(offsetof(CBasePlayer, m_playerScriptNetDataGlobal) == 0x345C); +static_assert(offsetof(CBasePlayer, m_bZooming) == 0x1598); +static_assert(offsetof(CBasePlayer, m_zoomToggleOn) == 0x1599); +static_assert(offsetof(CBasePlayer, m_zoomBaseFrac) == 0x159C); +static_assert(offsetof(CBasePlayer, m_zoomBaseTime) == 0x15A0); +static_assert(offsetof(CBasePlayer, m_zoomFullStartTime) == 0x15A4); +static_assert(offsetof(CBasePlayer, m_camoIndex) == 0xA04); +static_assert(offsetof(CBasePlayer, m_decalIndex) == 0xA08); + +extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); diff --git a/NorthstarDLL/server/serverchathooks.cpp b/primedev/server/serverchathooks.cpp similarity index 94% rename from NorthstarDLL/server/serverchathooks.cpp rename to primedev/server/serverchathooks.cpp index cb3af244..d3ac4776 100644 --- a/NorthstarDLL/server/serverchathooks.cpp +++ b/primedev/server/serverchathooks.cpp @@ -25,7 +25,7 @@ void(__fastcall* CServerGameDLL__OnReceivedSayTextMessage)( void(__fastcall* CRecipientFilter__Construct)(CRecipientFilter* self); void(__fastcall* CRecipientFilter__Destruct)(CRecipientFilter* self); void(__fastcall* CRecipientFilter__AddAllPlayers)(CRecipientFilter* self); -void(__fastcall* CRecipientFilter__AddRecipient)(CRecipientFilter* self, const R2::CBasePlayer* player); +void(__fastcall* CRecipientFilter__AddRecipient)(CRecipientFilter* self, const CBasePlayer* player); void(__fastcall* CRecipientFilter__MakeReliable)(CRecipientFilter* self); void(__fastcall* UserMessageBegin)(CRecipientFilter* filter, const char* messagename); @@ -40,7 +40,7 @@ AUTOHOOK(_CServerGameDLL__OnReceivedSayTextMessage, server.dll + 0x1595C0, void, __fastcall, (CServerGameDLL* self, unsigned int senderPlayerId, const char* text, bool isTeam)) // clang-format on { - NS::Utils::RemoveAsciiControlSequences(const_cast(text), true); + RemoveAsciiControlSequences(const_cast(text), true); // MiniHook doesn't allow calling the base function outside of anywhere but the hook function. // To allow bypassing the hook, isSkippingHook can be set. @@ -52,7 +52,7 @@ void, __fastcall, (CServerGameDLL* self, unsigned int senderPlayerId, const char } // check chat ratelimits - if (!g_pServerLimits->CheckChatLimits(&R2::g_pClientArray[senderPlayerId - 1])) + if (!g_pServerLimits->CheckChatLimits(&g_pClientArray[senderPlayerId - 1])) return; SQRESULT result = g_pSquirrel->Call( @@ -75,10 +75,10 @@ void ChatSendMessage(unsigned int playerIndex, const char* text, bool isTeam) void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType) { - R2::CBasePlayer* toPlayer = NULL; + CBasePlayer* toPlayer = NULL; if (toPlayerIndex >= 0) { - toPlayer = R2::UTIL_PlayerByIndex(toPlayerIndex + 1); + toPlayer = UTIL_PlayerByIndex(toPlayerIndex + 1); if (toPlayer == NULL) return; } @@ -163,7 +163,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerChatHooks, ServerSquirrel, (CModule mod CRecipientFilter__Construct = module.Offset(0x1E9440).RCast(); CRecipientFilter__Destruct = module.Offset(0x1E9700).RCast(); CRecipientFilter__AddAllPlayers = module.Offset(0x1E9940).RCast(); - CRecipientFilter__AddRecipient = module.Offset(0x1E9B30).RCast(); + CRecipientFilter__AddRecipient = module.Offset(0x1E9B30).RCast(); CRecipientFilter__MakeReliable = module.Offset(0x1EA4E0).RCast(); UserMessageBegin = module.Offset(0x15C520).RCast(); diff --git a/NorthstarDLL/server/serverchathooks.h b/primedev/server/serverchathooks.h similarity index 100% rename from NorthstarDLL/server/serverchathooks.h rename to primedev/server/serverchathooks.h diff --git a/NorthstarDLL/server/servernethooks.cpp b/primedev/server/servernethooks.cpp similarity index 96% rename from NorthstarDLL/server/servernethooks.cpp rename to primedev/server/servernethooks.cpp index f74f2d38..148b735f 100644 --- a/NorthstarDLL/server/servernethooks.cpp +++ b/primedev/server/servernethooks.cpp @@ -84,7 +84,7 @@ cleanup: } // v1 HMACSHA256-signed masterserver request (HMAC-SHA256(JSONData, MasterServerToken) + JSONData) -static void ProcessAtlasConnectionlessPacketSigreq1(R2::netpacket_t* packet, bool dbg, std::string pType, std::string pData) +static void ProcessAtlasConnectionlessPacketSigreq1(netpacket_t* packet, bool dbg, std::string pType, std::string pData) { if (pData.length() < HMACSHA256_LEN) { @@ -135,7 +135,7 @@ static void ProcessAtlasConnectionlessPacketSigreq1(R2::netpacket_t* packet, boo return; } -static void ProcessAtlasConnectionlessPacket(R2::netpacket_t* packet) +static void ProcessAtlasConnectionlessPacket(netpacket_t* packet) { bool dbg = Cvar_net_debug_atlas_packet->GetBool(); @@ -168,7 +168,7 @@ static void ProcessAtlasConnectionlessPacket(R2::netpacket_t* packet) return; } -AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, R2::netpacket_t* packet)) +AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, netpacket_t* packet)) { // packet->data consists of 0xFFFFFFFF (int32 -1) to indicate packets aren't split, followed by a header consisting of a single // character, which is used to uniquely identify the packet kind. Most kinds follow this with a null-terminated string payload diff --git a/NorthstarDLL/server/serverpresence.cpp b/primedev/server/serverpresence.cpp similarity index 97% rename from NorthstarDLL/server/serverpresence.cpp rename to primedev/server/serverpresence.cpp index 30a46fcc..706cd70a 100644 --- a/NorthstarDLL/server/serverpresence.cpp +++ b/primedev/server/serverpresence.cpp @@ -79,6 +79,9 @@ void ServerPresenceManager::CreateConVars() "ns_server_presence_update_rate", "5000", FCVAR_GAMEDLL, "How often we update our server's presence on server lists in ms"); Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL | FCVAR_REPLICATED, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + NOTE_UNUSED(cvar); + NOTE_UNUSED(pOldValue); + NOTE_UNUSED(flOldValue); g_pServerPresence->SetName(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_name->GetString())); // update engine hostname cvar @@ -90,6 +93,9 @@ void ServerPresenceManager::CreateConVars() }); Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "This server's password", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + NOTE_UNUSED(cvar); + NOTE_UNUSED(pOldValue); + NOTE_UNUSED(flOldValue); g_pServerPresence->SetPassword(g_pServerPresence->Cvar_ns_server_password->GetString()); }); diff --git a/NorthstarDLL/server/serverpresence.h b/primedev/server/serverpresence.h similarity index 84% rename from NorthstarDLL/server/serverpresence.h rename to primedev/server/serverpresence.h index 3aabecde..94ecfe6a 100644 --- a/NorthstarDLL/server/serverpresence.h +++ b/primedev/server/serverpresence.h @@ -3,7 +3,7 @@ struct ServerPresence { - public: +public: int m_iPort; std::string m_sServerId; @@ -44,16 +44,16 @@ struct ServerPresence class ServerPresenceReporter { - public: - virtual void CreatePresence(const ServerPresence* pServerPresence) {} - virtual void ReportPresence(const ServerPresence* pServerPresence) {} - virtual void DestroyPresence(const ServerPresence* pServerPresence) {} - virtual void RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) {} +public: + virtual void CreatePresence(const ServerPresence* /*pServerPresence*/) {} + virtual void ReportPresence(const ServerPresence* /*pServerPresence*/) {} + virtual void DestroyPresence(const ServerPresence* /*pServerPresence*/) {} + virtual void RunFrame(double /*flCurrentTime*/, const ServerPresence* /*pServerPresence*/) {} }; class ServerPresenceManager { - private: +private: ServerPresence m_ServerPresence; bool m_bHasPresence = false; @@ -71,7 +71,7 @@ class ServerPresenceManager ConVar* Cvar_ns_report_server_to_masterserver; ConVar* Cvar_ns_report_sp_server_to_masterserver; - public: +public: void AddPresenceReporter(ServerPresenceReporter* reporter); void CreateConVars(); diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp similarity index 89% rename from NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp rename to primedev/shared/exploit_fixes/exploitfixes.cpp index f51b87ae..1b3069f5 100644 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -5,6 +5,7 @@ #include "engine/r2engine.h" #include "client/r2client.h" #include "core/math/vector.h" +#include "core/vanilla.h" AUTOHOOK_INIT() @@ -33,6 +34,8 @@ AUTOHOOK(CLC_Screenshot_WriteToBuffer, engine.dll + 0x22AF20, bool, __fastcall, (void* thisptr, void* buffer)) // 48 89 5C 24 ? 57 48 83 EC 20 8B 42 10 // clang-format on { + if (g_pVanillaCompatibility->GetVanillaCompatibility()) + return CLC_Screenshot_WriteToBuffer(thisptr, buffer); return false; } @@ -41,6 +44,8 @@ AUTOHOOK(CLC_Screenshot_ReadFromBuffer, engine.dll + 0x221F00, bool, __fastcall, (void* thisptr, void* buffer)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B DA 48 8B 52 38 // clang-format on { + if (g_pVanillaCompatibility->GetVanillaCompatibility()) + return CLC_Screenshot_ReadFromBuffer(thisptr, buffer); return false; } @@ -50,6 +55,8 @@ AUTOHOOK(Base_CmdKeyValues_ReadFromBuffer, engine.dll + 0x220040, bool, __fastcall, (void* thisptr, void* buffer)) // 40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70 // clang-format on { + NOTE_UNUSED(thisptr); + NOTE_UNUSED(buffer); return false; } @@ -79,7 +86,7 @@ bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 }; auto msg = (NET_SetConVar*)pMsg; - bool bIsServerFrame = Tier0::ThreadInServerFrameThread(); + bool bIsServerFrame = ThreadInServerFrameThread(); std::string BLOCK_PREFIX = std::string {"NET_SetConVar ("} + (bIsServerFrame ? "server" : "client") + "): Blocked dangerous/invalid msg: "; @@ -98,7 +105,7 @@ bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 auto entry = msg->m_ConVars + i; // Safety check for memory access - if (CMemoryAddress(entry).IsMemoryReadable(sizeof(*entry))) + if (CMemory(entry).IsMemoryReadable(sizeof(*entry))) { // Find null terminators bool nameValid = false, valValid = false; @@ -118,7 +125,7 @@ bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 // stuff if (!bIsServerFrame) { - ConVar* pVar = R2::g_pCVar->FindVar(entry->name); + ConVar* pVar = g_pCVar->FindVar(entry->name); if (pVar) { memcpy( @@ -229,16 +236,24 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; // fix invalid player angles - cmd->worldViewAngles.MakeValid(); - cmd->attackangles.MakeValid(); - cmd->localViewAngles.MakeValid(); + if (!cmd->worldViewAngles.IsValid()) + cmd->worldViewAngles.Init(); + + if (!cmd->attackangles.IsValid()) + cmd->attackangles.Init(); + + if (!cmd->localViewAngles.IsValid()) + cmd->localViewAngles.Init(); // Fix invalid camera angles - cmd->cameraPos.MakeValid(); - cmd->cameraAngles.MakeValid(); + if (!cmd->cameraPos.IsValid()) + cmd->cameraPos.Init(); + if (!cmd->cameraAngles.IsValid()) + cmd->cameraAngles.Init(); // Fix invaid movement vector - cmd->move.MakeValid(); + if (!cmd->move.IsValid()) + cmd->move.Init(); if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) { @@ -269,17 +284,20 @@ bool, __fastcall, (const char* pModName)) // 48 83 EC 28 48 8B 0D ? ? ? ? 48 8D // clang-format on { // somewhat temp, store the modname here, since we don't have a proper ptr in engine to it rn - int iSize = strlen(pModName); - R2::g_pModName = new char[iSize + 1]; - strcpy(R2::g_pModName, pModName); + size_t iSize = strlen(pModName); + g_pModName = new char[iSize + 1]; + strcpy(g_pModName, pModName); - return (!strcmp("r2", pModName) || !strcmp("r1", pModName)) && !Tier0::CommandLine()->CheckParm("-norestrictservercommands"); + if (g_pVanillaCompatibility->GetVanillaCompatibility()) + return false; + + return (!strcmp("r2", pModName) || !strcmp("r1", pModName)) && !CommandLine()->CheckParm("-norestrictservercommands"); } // ratelimit stringcmds, and prevent remote clients from calling commands that they shouldn't // clang-format off AUTOHOOK(CGameClient__ExecuteStringCommand, engine.dll + 0x1022E0, -bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommandString)) +bool, __fastcall, (CBaseClient* self, uint32_t unknown, const char* pCommandString)) // clang-format on { if (Cvar_ns_should_log_all_clientcommands->GetBool()) @@ -287,7 +305,7 @@ bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommand if (!g_pServerLimits->CheckStringCommandLimits(self)) { - R2::CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands"); + CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands"); return false; } @@ -296,10 +314,10 @@ bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommand memset(commandBuf, 0, sizeof(commandBuf)); CCommand tempCommand = *(CCommand*)&commandBuf; - if (!R2::CCommand__Tokenize(tempCommand, pCommandString, R2::cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) + if (!CCommand__Tokenize(tempCommand, pCommandString, cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) return false; - ConCommand* command = R2::g_pCVar->FindCommand(tempCommand.Arg(0)); + ConCommand* command = g_pCVar->FindCommand(tempCommand.Arg(0)); // if the command doesn't exist pass it on to ExecuteStringCommand for script clientcommands and stuff if (command && !command->IsFlagSet(FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS)) @@ -308,7 +326,7 @@ bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommand if (IsDedicatedServer()) return false; - if (strcmp(self->m_UID, R2::g_pLocalPlayerUserID)) + if (strcmp(self->m_UID, g_pLocalPlayerUserID)) return false; } @@ -327,7 +345,7 @@ bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommand "load_recent_checkpoint" // This is the instant-respawn exploit, literally just calls RespawnPlayer() }; - int iCmdLength = strlen(tempCommand.Arg(0)); + size_t iCmdLength = strlen(tempCommand.Arg(0)); bool bIsBadCommand = false; for (auto& blockedCommand : blockedCommands) @@ -359,7 +377,7 @@ void, __fastcall, (void* self)) bWasWritingStringTableSuccessful = true; CBaseClient__SendServerInfo(self); if (!bWasWritingStringTableSuccessful) - R2::CBaseClient__Disconnect( + CBaseClient__Disconnect( self, 1, "Overflowed CNetworkStringTableContainer::WriteBaselines, try restarting your client and reconnecting"); } @@ -417,9 +435,9 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module)) // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails { - CMemoryAddress writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).m_nAddress); + CMemory writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).GetPtr()); - CMemoryAddress addr = module.Offset(0x234ED2); + CMemory addr = module.Offset(0x234ED2); addr.Patch("C7 05"); addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress)); @@ -447,7 +465,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerExploitFixes, ConVar, (CModule module)) // Prevent these from actually doing anything for (auto exportName : ANTITAMPER_EXPORTS) { - CMemoryAddress exportAddr = module.GetExport(exportName); + CMemory exportAddr = module.GetExportedFunction(exportName); if (exportAddr) { // Just return, none of them have any args or are userpurge @@ -461,5 +479,5 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerExploitFixes, ConVar, (CModule module)) Cvar_ns_should_log_all_clientcommands = new ConVar("ns_should_log_all_clientcommands", "0", FCVAR_NONE, "Whether to log all clientcommands"); - Cvar_sv_cheats = R2::g_pCVar->FindVar("sv_cheats"); + Cvar_sv_cheats = g_pCVar->FindVar("sv_cheats"); } diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp b/primedev/shared/exploit_fixes/exploitfixes_lzss.cpp similarity index 98% rename from NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp rename to primedev/shared/exploit_fixes/exploitfixes_lzss.cpp index ccb6ac18..9a9a5691 100644 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes_lzss.cpp @@ -16,6 +16,7 @@ AUTOHOOK(CLZSS__SafeDecompress, engine.dll + 0x432A10, unsigned int, __fastcall, (void* self, const unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize)) // clang-format on { + NOTE_UNUSED(self); unsigned int totalBytes = 0; int getCmdByte = 0; int cmdByte = 0; diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp b/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp similarity index 94% rename from NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp rename to primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp index 3d97f750..d63ba38a 100644 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes_utf8parser.cpp @@ -67,7 +67,7 @@ bool __fastcall CheckUTF8Valid(INT64* a1, DWORD* a2, char* strData) { while (1) { - if (!CMemoryAddress(v4).IsMemoryReadable(1)) + if (!CMemory(v4).IsMemoryReadable(1)) return false; // INVALID v11 = *v4++; // crash potential @@ -174,7 +174,7 @@ AUTOHOOK(Rson_ParseUTF8, engine.dll + 0xEF670, bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A // clang-format on { - static void* targetRetAddr = CModule("engine.dll").FindPattern("84 C0 75 2C 49 8B 16"); + static void* targetRetAddr = CModule("engine.dll").FindPatternSIMD("84 C0 75 2C 49 8B 16"); // only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues void* pReturnAddress = @@ -195,5 +195,5 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes_UTF8Parser, (CModule module)) { AUTOHOOK_DISPATCH() - sub_F1320 = module.FindPattern("83 F9 7F 77 08 88 0A").RCast(); + sub_F1320 = module.FindPatternSIMD("83 F9 7F 77 08 88 0A").RCast(); } diff --git a/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp b/primedev/shared/exploit_fixes/ns_limits.cpp similarity index 78% rename from NorthstarDLL/shared/exploit_fixes/ns_limits.cpp rename to primedev/shared/exploit_fixes/ns_limits.cpp index c9085cb0..63abbf69 100644 --- a/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp +++ b/primedev/shared/exploit_fixes/ns_limits.cpp @@ -16,50 +16,51 @@ float (*CEngineServer__GetTimescale)(); // todo: make this work on higher timescales, also possibly disable when sv_cheats is set void ServerLimitsManager::RunFrame(double flCurrentTime, float flFrameTime) { + NOTE_UNUSED(flCurrentTime); if (Cvar_sv_antispeedhack_enable->GetBool()) { // for each player, set their usercmd processing budget for the frame to the last frametime for the server - for (int i = 0; i < R2::g_pGlobals->m_nMaxClients; i++) + for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) { - R2::CBaseClient* player = &R2::g_pClientArray[i]; + CBaseClient* player = &g_pClientArray[i]; if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end()) { PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[player]; - if (pLimitData->flFrameUserCmdBudget < R2::g_pGlobals->m_flTickInterval * Cvar_sv_antispeedhack_maxtickbudget->GetFloat()) + if (pLimitData->flFrameUserCmdBudget < g_pGlobals->m_flTickInterval * Cvar_sv_antispeedhack_maxtickbudget->GetFloat()) { pLimitData->flFrameUserCmdBudget += g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier->GetFloat() * - fmax(flFrameTime, R2::g_pGlobals->m_flFrameTime * CEngineServer__GetTimescale()); + fmax(flFrameTime, g_pGlobals->m_flFrameTime * CEngineServer__GetTimescale()); } } } } } -void ServerLimitsManager::AddPlayer(R2::CBaseClient* player) +void ServerLimitsManager::AddPlayer(CBaseClient* player) { PlayerLimitData limitData; limitData.flFrameUserCmdBudget = - R2::g_pGlobals->m_flTickInterval * CEngineServer__GetTimescale() * Cvar_sv_antispeedhack_maxtickbudget->GetFloat(); + g_pGlobals->m_flTickInterval * CEngineServer__GetTimescale() * Cvar_sv_antispeedhack_maxtickbudget->GetFloat(); m_PlayerLimitData.insert(std::make_pair(player, limitData)); } -void ServerLimitsManager::RemovePlayer(R2::CBaseClient* player) +void ServerLimitsManager::RemovePlayer(CBaseClient* player) { if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end()) m_PlayerLimitData.erase(player); } -bool ServerLimitsManager::CheckStringCommandLimits(R2::CBaseClient* player) +bool ServerLimitsManager::CheckStringCommandLimits(CBaseClient* player) { if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1) { // note: this isn't super perfect, legit clients can trigger it in lobby if they try, mostly good enough tho imo - if (Tier0::Plat_FloatTime() - m_PlayerLimitData[player].lastClientCommandQuotaStart >= 1.0) + if (Plat_FloatTime() - m_PlayerLimitData[player].lastClientCommandQuotaStart >= 1.0) { // reset quota - m_PlayerLimitData[player].lastClientCommandQuotaStart = Tier0::Plat_FloatTime(); + m_PlayerLimitData[player].lastClientCommandQuotaStart = Plat_FloatTime(); m_PlayerLimitData[player].numClientCommandsInQuota = 0; } @@ -74,11 +75,11 @@ bool ServerLimitsManager::CheckStringCommandLimits(R2::CBaseClient* player) return true; } -bool ServerLimitsManager::CheckChatLimits(R2::CBaseClient* player) +bool ServerLimitsManager::CheckChatLimits(CBaseClient* player) { - if (Tier0::Plat_FloatTime() - m_PlayerLimitData[player].lastSayTextLimitStart >= 1.0) + if (Plat_FloatTime() - m_PlayerLimitData[player].lastSayTextLimitStart >= 1.0) { - m_PlayerLimitData[player].lastSayTextLimitStart = Tier0::Plat_FloatTime(); + m_PlayerLimitData[player].lastSayTextLimitStart = Plat_FloatTime(); m_PlayerLimitData[player].sayTextLimitCount = 0; } @@ -100,14 +101,14 @@ char, __fastcall, (void* self, void* buf)) NETCHANLIMIT_KICK }; - double startTime = Tier0::Plat_FloatTime(); + double startTime = Plat_FloatTime(); char ret = CNetChan__ProcessMessages(self, buf); // check processing limits, unless we're in a level transition - if (R2::g_pHostState->m_iCurrentState == R2::HostState_t::HS_RUN && Tier0::ThreadInServerFrameThread()) + if (g_pHostState->m_iCurrentState == HostState_t::HS_RUN && ThreadInServerFrameThread()) { // player that sent the message - R2::CBaseClient* sender = *(R2::CBaseClient**)((char*)self + 368); + CBaseClient* sender = *(CBaseClient**)((char*)self + 368); // if no sender, return // relatively certain this is fine? @@ -121,7 +122,7 @@ char, __fastcall, (void* self, void* buf)) g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart = startTime; g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime = 0.0; } - g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime += (Tier0::Plat_FloatTime() * 1000) - (startTime * 1000); + g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime += (Plat_FloatTime() * 1000) - (startTime * 1000); if (g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime >= g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt()) @@ -133,9 +134,9 @@ char, __fastcall, (void* self, void* buf)) g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt()); // never kick local player - if (g_pServerLimits->Cvar_net_chan_limit_mode->GetInt() != NETCHANLIMIT_WARN && strcmp(R2::g_pLocalPlayerUserID, sender->m_UID)) + if (g_pServerLimits->Cvar_net_chan_limit_mode->GetInt() != NETCHANLIMIT_WARN && strcmp(g_pLocalPlayerUserID, sender->m_UID)) { - R2::CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); + CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); return false; } } @@ -144,12 +145,12 @@ char, __fastcall, (void* self, void* buf)) return ret; } -bool ServerLimitsManager::CheckConnectionlessPacketLimits(R2::netpacket_t* packet) +bool ServerLimitsManager::CheckConnectionlessPacketLimits(netpacket_t* packet) { - static const ConVar* Cvar_net_data_block_enabled = R2::g_pCVar->FindVar("net_data_block_enabled"); + static const ConVar* Cvar_net_data_block_enabled = g_pCVar->FindVar("net_data_block_enabled"); // don't ratelimit datablock packets as long as datablock is enabled - if (packet->adr.type == R2::NA_IP && + if (packet->adr.type == NA_IP && (!(packet->data[4] == 'N' && Cvar_net_data_block_enabled->GetBool()) || !Cvar_net_data_block_enabled->GetBool())) { // bad lookup: optimise later tm @@ -169,12 +170,12 @@ bool ServerLimitsManager::CheckConnectionlessPacketLimits(R2::netpacket_t* packe memcpy(sendData->ip, packet->adr.ip, 16); } - if (Tier0::Plat_FloatTime() < sendData->timeoutEnd) + if (Plat_FloatTime() < sendData->timeoutEnd) return false; - if (Tier0::Plat_FloatTime() - sendData->lastQuotaStart >= 1.0) + if (Plat_FloatTime() - sendData->lastQuotaStart >= 1.0) { - sendData->lastQuotaStart = Tier0::Plat_FloatTime(); + sendData->lastQuotaStart = Plat_FloatTime(); sendData->packetCount = 0; } @@ -188,7 +189,7 @@ bool ServerLimitsManager::CheckConnectionlessPacketLimits(R2::netpacket_t* packe packet->data[4]); // timeout for a minute - sendData->timeoutEnd = Tier0::Plat_FloatTime() + 60.0; + sendData->timeoutEnd = Plat_FloatTime() + 60.0; return false; } } @@ -232,12 +233,12 @@ struct alignas(4) SV_CUserCmd // clang-format off AUTOHOOK(CPlayerMove__RunCommand, server.dll + 0x5B8100, -void, __fastcall, (void* self, R2::CBasePlayer* player, SV_CUserCmd* pUserCmd, uint64_t a4)) +void, __fastcall, (void* self, CBasePlayer* player, SV_CUserCmd* pUserCmd, uint64_t a4)) // clang-format on { if (g_pServerLimits->Cvar_sv_antispeedhack_enable->GetBool()) { - R2::CBaseClient* pClient = &R2::g_pClientArray[player->m_nPlayerIndex - 1]; + CBaseClient* pClient = &g_pClientArray[player->m_nPlayerIndex - 1]; if (g_pServerLimits->m_PlayerLimitData.find(pClient) != g_pServerLimits->m_PlayerLimitData.end()) { diff --git a/NorthstarDLL/shared/exploit_fixes/ns_limits.h b/primedev/shared/exploit_fixes/ns_limits.h similarity index 75% rename from NorthstarDLL/shared/exploit_fixes/ns_limits.h rename to primedev/shared/exploit_fixes/ns_limits.h index d1f7f2ed..546fec6f 100644 --- a/NorthstarDLL/shared/exploit_fixes/ns_limits.h +++ b/primedev/shared/exploit_fixes/ns_limits.h @@ -27,7 +27,7 @@ struct UnconnectedPlayerLimitData class ServerLimitsManager { - public: +public: ConVar* CVar_sv_quota_stringcmdspersecond; ConVar* Cvar_net_chan_limit_mode; ConVar* Cvar_net_chan_limit_msec_per_sec; @@ -37,16 +37,16 @@ class ServerLimitsManager ConVar* Cvar_sv_antispeedhack_maxtickbudget; ConVar* Cvar_sv_antispeedhack_budgetincreasemultiplier; - std::unordered_map m_PlayerLimitData; + std::unordered_map m_PlayerLimitData; std::vector m_UnconnectedPlayerLimitData; - public: +public: void RunFrame(double flCurrentTime, float flFrameTime); - void AddPlayer(R2::CBaseClient* player); - void RemovePlayer(R2::CBaseClient* player); - bool CheckStringCommandLimits(R2::CBaseClient* player); - bool CheckChatLimits(R2::CBaseClient* player); - bool CheckConnectionlessPacketLimits(R2::netpacket_t* packet); + void AddPlayer(CBaseClient* player); + void RemovePlayer(CBaseClient* player); + bool CheckStringCommandLimits(CBaseClient* player); + bool CheckChatLimits(CBaseClient* player); + bool CheckConnectionlessPacketLimits(netpacket_t* packet); }; extern ServerLimitsManager* g_pServerLimits; diff --git a/NorthstarDLL/shared/keyvalues.cpp b/primedev/shared/keyvalues.cpp similarity index 99% rename from NorthstarDLL/shared/keyvalues.cpp rename to primedev/shared/keyvalues.cpp index aa22ca65..36f891eb 100644 --- a/NorthstarDLL/shared/keyvalues.cpp +++ b/primedev/shared/keyvalues.cpp @@ -17,7 +17,7 @@ typedef int HKeySymbol; struct CKeyValuesSystem { - public: +public: struct __VTable { char pad0[8 * 3]; // 2 methods @@ -1289,9 +1289,9 @@ KeyValues* KeyValues::MakeCopy(void) const ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module)) { - V_UTF8ToUnicode = module.GetExport("V_UTF8ToUnicode").RCast(); - V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast(); - KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast(); + V_UTF8ToUnicode = module.GetExportedFunction("V_UTF8ToUnicode").RCast(); + V_UnicodeToUTF8 = module.GetExportedFunction("V_UnicodeToUTF8").RCast(); + KeyValuesSystem = module.GetExportedFunction("KeyValuesSystem").RCast(); } AUTOHOOK_INIT() diff --git a/NorthstarDLL/shared/keyvalues.h b/primedev/shared/keyvalues.h similarity index 99% rename from NorthstarDLL/shared/keyvalues.h rename to primedev/shared/keyvalues.h index 64ca0cc7..bd62797e 100644 --- a/NorthstarDLL/shared/keyvalues.h +++ b/primedev/shared/keyvalues.h @@ -48,10 +48,10 @@ enum MergeKeyValuesOp_t //----------------------------------------------------------------------------- class KeyValues { - private: +private: KeyValues(); // for internal use only - public: +public: // Constructors/destructors KeyValues(const char* pszSetName); KeyValues(const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue); @@ -112,7 +112,7 @@ class KeyValues void CopySubkeys(KeyValues* pParent) const; KeyValues* MakeCopy(void) const; - public: +public: uint32_t m_iKeyName : 24; // 0x0000 uint32_t m_iKeyNameCaseSensitive1 : 8; // 0x0003 char* m_sValue; // 0x0008 diff --git a/NorthstarDLL/shared/maxplayers.cpp b/primedev/shared/maxplayers.cpp similarity index 98% rename from NorthstarDLL/shared/maxplayers.cpp rename to primedev/shared/maxplayers.cpp index 4af8ea1c..69d625f2 100644 --- a/NorthstarDLL/shared/maxplayers.cpp +++ b/primedev/shared/maxplayers.cpp @@ -48,23 +48,19 @@ constexpr int Team_AddedSize = Team_PlayerArray_AddedSize; bool MaxPlayersIncreaseEnabled() { - static bool bMaxPlayersIncreaseEnabled = Tier0::CommandLine()->CheckParm("-experimentalmaxplayersincrease"); + static bool bMaxPlayersIncreaseEnabled = CommandLine()->CheckParm("-experimentalmaxplayersincrease"); return bMaxPlayersIncreaseEnabled; } -// should we use R2 for this? not sure -namespace R2 // use R2 namespace for game funcs +int GetMaxPlayers() { - int GetMaxPlayers() - { - if (MaxPlayersIncreaseEnabled()) - return NEW_MAX_PLAYERS; + if (MaxPlayersIncreaseEnabled()) + return NEW_MAX_PLAYERS; - return 32; - } -} // namespace R2 + return 32; +} -template void ChangeOffset(CMemoryAddress addr, unsigned int offset) +template void ChangeOffset(CMemory addr, unsigned int offset) { addr.Patch((BYTE*)&offset, sizeof(T)); } @@ -300,7 +296,7 @@ ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module)) AUTOHOOK_DISPATCH_MODULE(server.dll) // get required data - serverBase = (HMODULE)module.m_nAddress; + serverBase = (HMODULE)module.GetModuleBase(); RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax")); // patch max players amount diff --git a/primedev/shared/maxplayers.h b/primedev/shared/maxplayers.h new file mode 100644 index 00000000..40a3ac58 --- /dev/null +++ b/primedev/shared/maxplayers.h @@ -0,0 +1,3 @@ +#pragma once + +int GetMaxPlayers(); diff --git a/NorthstarDLL/shared/misccommands.cpp b/primedev/shared/misccommands.cpp similarity index 92% rename from NorthstarDLL/shared/misccommands.cpp rename to primedev/shared/misccommands.cpp index 5d9ced99..29d92d2d 100644 --- a/NorthstarDLL/shared/misccommands.cpp +++ b/primedev/shared/misccommands.cpp @@ -15,21 +15,23 @@ void ConCommand_force_newgame(const CCommand& arg) if (arg.ArgC() < 2) return; - R2::g_pHostState->m_iNextState = R2::HostState_t::HS_NEW_GAME; - strncpy(R2::g_pHostState->m_levelName, arg.Arg(1), sizeof(R2::g_pHostState->m_levelName)); + g_pHostState->m_iNextState = HostState_t::HS_NEW_GAME; + strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName)); } void ConCommand_ns_start_reauth_and_leave_to_lobby(const CCommand& arg) { + NOTE_UNUSED(arg); // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect g_pMasterServerManager->m_bNewgameAfterSelfAuth = true; - g_pMasterServerManager->AuthenticateWithOwnServer(R2::g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); + g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); } void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) { + NOTE_UNUSED(arg); if (g_pServerAuthentication->m_RemoteAuthenticationData.size()) - R2::g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str()); + g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str()); // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this if (g_pSquirrel->m_pSQVM) @@ -39,8 +41,8 @@ void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta // fucks things should maybe set this in HostState_NewGame? R2::SetCurrentPlaylist("tdm"); - strcpy(R2::g_pHostState->m_levelName, "mp_lobby"); - R2::g_pHostState->m_iNextState = R2::HostState_t::HS_NEW_GAME; + strcpy(g_pHostState->m_levelName, "mp_lobby"); + g_pHostState->m_iNextState = HostState_t::HS_NEW_GAME; } } @@ -52,7 +54,7 @@ void ConCommand_cvar_setdefaultvalue(const CCommand& arg) return; } - ConVar* pCvar = R2::g_pCVar->FindVar(arg.Arg(1)); + ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1)); if (!pCvar) { spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm"); @@ -60,7 +62,7 @@ void ConCommand_cvar_setdefaultvalue(const CCommand& arg) } // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default - int nLen = strlen(arg.Arg(2)); + size_t nLen = strlen(arg.Arg(2)); char* pBuf = new char[nLen + 1]; strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen); @@ -75,7 +77,7 @@ void ConCommand_cvar_setvalueanddefaultvalue(const CCommand& arg) return; } - ConVar* pCvar = R2::g_pCVar->FindVar(arg.Arg(1)); + ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1)); if (!pCvar) { spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm"); @@ -83,7 +85,7 @@ void ConCommand_cvar_setvalueanddefaultvalue(const CCommand& arg) } // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default - int nLen = strlen(arg.Arg(2)); + size_t nLen = strlen(arg.Arg(2)); char* pBuf = new char[nLen + 1]; strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen); @@ -99,7 +101,7 @@ void ConCommand_cvar_reset(const CCommand& arg) return; } - ConVar* pCvar = R2::g_pCVar->FindVar(arg.Arg(1)); + ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1)); if (!pCvar) { spdlog::info("usage: cvar_reset mp_gamemode"); @@ -143,11 +145,11 @@ void AddMiscConCommands() // fixes up various cvar flags to have more sane values void FixupCvarFlags() { - if (Tier0::CommandLine()->CheckParm("-allowdevcvars")) + if (CommandLine()->CheckParm("-allowdevcvars")) { // strip hidden and devonly cvar flags int iNumCvarsAltered = 0; - for (auto& pair : R2::g_pCVar->DumpToMap()) + for (auto& pair : g_pCVar->DumpToMap()) { // strip flags int flags = pair.second->GetFlags(); @@ -177,7 +179,7 @@ void FixupCvarFlags() int i = 0; do { - ConCommandBase* pCommand = R2::g_pCVar->FindCommandBase(ppEngineClientCommands[i]); + ConCommandBase* pCommand = g_pCVar->FindCommandBase(ppEngineClientCommands[i]); if (pCommand) // not all the commands in this array actually exist in respawn source pCommand->m_nFlags |= FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS; } while (ppEngineClientCommands[++i]); @@ -367,21 +369,21 @@ void FixupCvarFlags() for (auto& fixup : CVAR_FIXUP_ADD_FLAGS) { - ConCommandBase* command = R2::g_pCVar->FindCommandBase(std::get<0>(fixup)); + ConCommandBase* command = g_pCVar->FindCommandBase(std::get<0>(fixup)); if (command) command->m_nFlags |= std::get<1>(fixup); } for (auto& fixup : CVAR_FIXUP_REMOVE_FLAGS) { - ConCommandBase* command = R2::g_pCVar->FindCommandBase(std::get<0>(fixup)); + ConCommandBase* command = g_pCVar->FindCommandBase(std::get<0>(fixup)); if (command) command->m_nFlags &= ~std::get<1>(fixup); } for (auto& fixup : CVAR_FIXUP_DEFAULT_VALUES) { - ConVar* cvar = R2::g_pCVar->FindVar(std::get<0>(fixup)); + ConVar* cvar = g_pCVar->FindVar(std::get<0>(fixup)); if (cvar && !strcmp(cvar->GetString(), cvar->m_pszDefaultValue)) { cvar->SetValue(std::get<1>(fixup)); diff --git a/NorthstarDLL/shared/misccommands.h b/primedev/shared/misccommands.h similarity index 100% rename from NorthstarDLL/shared/misccommands.h rename to primedev/shared/misccommands.h diff --git a/NorthstarDLL/shared/playlist.cpp b/primedev/shared/playlist.cpp similarity index 98% rename from NorthstarDLL/shared/playlist.cpp rename to primedev/shared/playlist.cpp index ab7aab22..2b9ad979 100644 --- a/NorthstarDLL/shared/playlist.cpp +++ b/primedev/shared/playlist.cpp @@ -26,7 +26,7 @@ char, __fastcall, (void* a1, void* a2)) { // the private_match playlist on mp_lobby is the only situation where there should be any legitimate sending of this netmessage if (!Cvar_ns_use_clc_SetPlaylistVarOverride->GetBool() || strcmp(R2::GetCurrentPlaylistName(), "private_match") || - strcmp(R2::g_pGlobals->m_pMapName, "mp_lobby")) + strcmp(g_pGlobals->m_pMapName, "mp_lobby")) return 1; return clc_SetPlaylistVarOverride__Process(a1, a2); diff --git a/NorthstarDLL/shared/playlist.h b/primedev/shared/playlist.h similarity index 100% rename from NorthstarDLL/shared/playlist.h rename to primedev/shared/playlist.h diff --git a/NorthstarDLL/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp similarity index 88% rename from NorthstarDLL/squirrel/squirrel.cpp rename to primedev/squirrel/squirrel.cpp index e43686d7..41a6a782 100644 --- a/NorthstarDLL/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -6,9 +6,10 @@ #include "dedicated/dedicated.h" #include "engine/r2engine.h" #include "core/tier0.h" -#include "plugins/plugin_abi.h" #include "plugins/plugins.h" +#include "plugins/pluginmanager.h" #include "ns_version.h" +#include "core/vanilla.h" #include @@ -156,79 +157,6 @@ const char* SQTypeNameFromID(int type) return ""; } -template void SquirrelManager::GenerateSquirrelFunctionsStruct(SquirrelFunctions* s) -{ - s->RegisterSquirrelFunc = RegisterSquirrelFunc; - s->__sq_defconst = __sq_defconst; - - s->__sq_compilebuffer = __sq_compilebuffer; - s->__sq_call = __sq_call; - s->__sq_raiseerror = __sq_raiseerror; - s->__sq_compilefile = __sq_compilefile; - - s->__sq_newarray = __sq_newarray; - s->__sq_arrayappend = __sq_arrayappend; - - s->__sq_newtable = __sq_newtable; - s->__sq_newslot = __sq_newslot; - - s->__sq_pushroottable = __sq_pushroottable; - s->__sq_pushstring = __sq_pushstring; - s->__sq_pushinteger = __sq_pushinteger; - s->__sq_pushfloat = __sq_pushfloat; - s->__sq_pushbool = __sq_pushbool; - s->__sq_pushasset = __sq_pushasset; - s->__sq_pushvector = __sq_pushvector; - s->__sq_pushobject = __sq_pushobject; - - s->__sq_getstring = __sq_getstring; - s->__sq_getinteger = __sq_getinteger; - s->__sq_getfloat = __sq_getfloat; - s->__sq_getbool = __sq_getbool; - s->__sq_get = __sq_get; - s->__sq_getasset = __sq_getasset; - s->__sq_getuserdata = __sq_getuserdata; - s->__sq_getvector = __sq_getvector; - s->__sq_getthisentity = __sq_getthisentity; - s->__sq_getobject = __sq_getobject; - - s->__sq_stackinfos = __sq_stackinfos; - - s->__sq_createuserdata = __sq_createuserdata; - s->__sq_setuserdatatypeid = __sq_setuserdatatypeid; - s->__sq_getfunction = __sq_getfunction; - - s->__sq_schedule_call_external = AsyncCall_External; - - s->__sq_getentityfrominstance = __sq_getentityfrominstance; - s->__sq_GetEntityConstant_CBaseEntity = __sq_GetEntityConstant_CBaseEntity; - - s->__sq_pushnewstructinstance = __sq_pushnewstructinstance; - s->__sq_sealstructslot = __sq_sealstructslot; -} - -// Allows for generating squirrelmessages from plugins. -void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata) -{ - SquirrelMessage message {}; - message.functionName = func_name; - message.isExternal = true; - message.externalFunc = function; - message.userdata = userdata; - switch (context) - { - case ScriptContext::CLIENT: - g_pSquirrel->messageBuffer->push(message); - break; - case ScriptContext::SERVER: - g_pSquirrel->messageBuffer->push(message); - break; - case ScriptContext::UI: - g_pSquirrel->messageBuffer->push(message); - break; - } -} - // needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors template class SquirrelManager; template class SquirrelManager; @@ -263,6 +191,13 @@ template void SquirrelManager::VMCreated(CSquir defconst(m_pSQVM, pair.first.c_str(), bWasFound); } + std::vector loadedPlugins = g_pPluginManager->GetLoadedPlugins(); + for (const auto& pluginName : g_pModManager->m_PluginDependencyConstants) + { + auto f = [&](Plugin plugin) -> bool { return plugin.GetDependencyName() == pluginName; }; + defconst(m_pSQVM, pluginName.c_str(), std::find_if(loadedPlugins.begin(), loadedPlugins.end(), f) != loadedPlugins.end()); + } + defconst(m_pSQVM, "MAX_FOLDER_SIZE", GetMaxSaveFolderSize() / 1024); // define squirrel constants for northstar(.dll) version @@ -272,8 +207,11 @@ template void SquirrelManager::VMCreated(CSquir defconst(m_pSQVM, "NS_VERSION_PATCH", version[2]); defconst(m_pSQVM, "NS_VERSION_DEV", version[3]); + // define squirrel constant for if we are in vanilla-compatibility mode + defconst(m_pSQVM, "VANILLA", g_pVanillaCompatibility->GetVanillaCompatibility()); + g_pSquirrel->messageBuffer = new SquirrelMessageBuffer(); - g_pPluginManager->InformSQVMCreated(context, newSqvm); + g_pPluginManager->InformSqvmCreated(newSqvm); } template void SquirrelManager::VMDestroyed() @@ -301,7 +239,7 @@ template void SquirrelManager::VMDestroyed() } } - g_pPluginManager->InformSQVMDestroyed(context); + g_pPluginManager->InformSqvmDestroying(m_pSQVM); // Discard the previous vm and delete the message buffer. m_pSQVM = nullptr; @@ -378,6 +316,7 @@ template void SquirrelManager::AddFuncOverride( // hooks bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm) { + NOTE_UNUSED(context); return ScriptContext(pSqvm->sharedState->cSquirrelVM->vmContext) == ScriptContext::UI; } @@ -396,6 +335,8 @@ template void* __fastcall sq_compiler_createHook(HSquirr template SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt); template SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...) { + NOTE_UNUSED(sqvm); + va_list va; va_start(va, fmt); @@ -493,17 +434,17 @@ void __fastcall ScriptCompileErrorHook(HSquirrelVM* sqvm, const char* error, con } else { - R2::Cbuf_AddText( - R2::Cbuf_GetCurrentPlayer(), + Cbuf_AddText( + Cbuf_GetCurrentPlayer(), fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext)) .c_str(), - R2::cmd_source_t::kCommandSrcCode); + cmd_source_t::kCommandSrcCode); // likely temp: show console so user can see any errors, as error message wont display if ui is dead // maybe we could disable all mods other than the coremods and try a reload before doing this? // could also maybe do some vgui bullshit to show something visually rather than console if (realContext == ScriptContext::UI) - R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "showconsole", R2::cmd_source_t::kCommandSrcCode); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "showconsole", cmd_source_t::kCommandSrcCode); } } @@ -608,33 +549,25 @@ template void ConCommand_script(const CCommand& args) g_pSquirrel->ExecuteCode(args.ArgS()); } -// literal class type that wraps a constant expression string -template struct TemplateStringLiteral +template SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm) { - constexpr TemplateStringLiteral(const char (&str)[N]) - { - std::copy_n(str, N, value); - } + SQStackInfos si; + g_pSquirrel->sq_stackinfos(sqvm, 0, si); - char value[N]; -}; - -template SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm) -{ - spdlog::info("Blocking call to stubbed function {} in {}", funcName.value, GetContextName(context)); + spdlog::warn("Blocking call to stubbed function {} in {}", si._name, GetContextName(context)); return SQRESULT_NULL; } template void StubUnsafeSQFuncs() { - if (!Tier0::CommandLine()->CheckParm("-allowunsafesqfuncs")) + if (!CommandLine()->CheckParm("-allowunsafesqfuncs")) { - g_pSquirrel->AddFuncOverride("DevTextBufferWrite", SQ_StubbedFunc); - g_pSquirrel->AddFuncOverride("DevTextBufferClear", SQ_StubbedFunc); - g_pSquirrel->AddFuncOverride("DevTextBufferDumpToFile", SQ_StubbedFunc); - g_pSquirrel->AddFuncOverride("Dev_CommandLineAddParam", SQ_StubbedFunc); - g_pSquirrel->AddFuncOverride("DevP4Checkout", SQ_StubbedFunc); - g_pSquirrel->AddFuncOverride("DevP4Add", SQ_StubbedFunc); + g_pSquirrel->AddFuncOverride("DevTextBufferWrite", SQ_StubbedFunc); + g_pSquirrel->AddFuncOverride("DevTextBufferClear", SQ_StubbedFunc); + g_pSquirrel->AddFuncOverride("DevTextBufferDumpToFile", SQ_StubbedFunc); + g_pSquirrel->AddFuncOverride("Dev_CommandLineAddParam", SQ_StubbedFunc); + g_pSquirrel->AddFuncOverride("DevP4Checkout", SQ_StubbedFunc); + g_pSquirrel->AddFuncOverride("DevP4Add", SQ_StubbedFunc); } } @@ -656,22 +589,15 @@ template void SquirrelManager::ProcessMessageBu pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object pushroottable(m_pSQVM->sqvm); - int argsAmount = message.args.size(); + size_t argsAmount = message.args.size(); - if (message.isExternal && message.externalFunc != NULL) + for (auto& v : message.args) { - argsAmount = message.externalFunc(m_pSQVM->sqvm, message.userdata); - } - else - { - for (auto& v : message.args) - { - // Execute lambda to push arg to stack - v(); - } + // Execute lambda to push arg to stack + v(); } - _call(m_pSQVM->sqvm, argsAmount); + _call(m_pSQVM->sqvm, (SQInteger)argsAmount); } } @@ -792,7 +718,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) // Message buffer stuff g_pSquirrel->messageBuffer = g_pSquirrel->messageBuffer; - g_pSquirrel->__sq_getfunction = module.Offset(0x572FB0).RCast(); + g_pSquirrel->__sq_getfunction = module.Offset(0x6CB0).RCast(); g_pSquirrel->__sq_getfunction = g_pSquirrel->__sq_getfunction; g_pSquirrel->__sq_stackinfos = module.Offset(0x35970).RCast(); g_pSquirrel->__sq_stackinfos = g_pSquirrel->__sq_stackinfos; @@ -833,13 +759,6 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) StubUnsafeSQFuncs(); StubUnsafeSQFuncs(); - - g_pSquirrel->__sq_getfunction = module.Offset(0x6CB0).RCast(); - g_pSquirrel->__sq_getfunction = g_pSquirrel->__sq_getfunction; - - SquirrelFunctions s = {}; - g_pSquirrel->GenerateSquirrelFunctionsStruct(&s); - g_pPluginManager->InformSQVMLoad(ScriptContext::CLIENT, &s); } ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) @@ -889,7 +808,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) g_pSquirrel->logger = NS::log::SCRIPT_SV; // Message buffer stuff - g_pSquirrel->__sq_getfunction = module.Offset(0x6C85).RCast(); + g_pSquirrel->__sq_getfunction = module.Offset(0x6C80).RCast(); g_pSquirrel->__sq_stackinfos = module.Offset(0x35920).RCast(); // Structs @@ -918,10 +837,6 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS | FCVAR_CHEAT); StubUnsafeSQFuncs(); - - SquirrelFunctions s = {}; - g_pSquirrel->GenerateSquirrelFunctionsStruct(&s); - g_pPluginManager->InformSQVMLoad(ScriptContext::SERVER, &s); } void InitialiseSquirrelManagers() diff --git a/NorthstarDLL/squirrel/squirrel.h b/primedev/squirrel/squirrel.h similarity index 97% rename from NorthstarDLL/squirrel/squirrel.h rename to primedev/squirrel/squirrel.h index 50dd31d0..0c1f24d3 100644 --- a/NorthstarDLL/squirrel/squirrel.h +++ b/primedev/squirrel/squirrel.h @@ -3,7 +3,6 @@ #include "squirrelclasstypes.h" #include "squirrelautobind.h" #include "core/math/vector.h" -#include "plugins/plugin_abi.h" #include "mods/modmanager.h" /* @@ -51,8 +50,6 @@ const char* GetContextName_Short(ScriptContext context); eSQReturnType SQReturnTypeFromString(const char* pReturnType); const char* SQTypeNameFromID(const int iTypeId); -void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function, void* userdata); - ScriptContext ScriptContextFromString(std::string string); namespace NS::log @@ -64,10 +61,10 @@ namespace NS::log // Cuts down on compile time by ~5 seconds class SquirrelManagerBase { - protected: +protected: std::vector m_funcRegistrations; - public: +public: CSquirrelVM* m_pSQVM; std::map m_funcOverrides = {}; std::map m_funcOriginals = {}; @@ -319,7 +316,7 @@ class SquirrelManagerBase template class SquirrelManager : public virtual SquirrelManagerBase { - public: +public: #pragma region MessageBuffer SquirrelMessageBuffer* messageBuffer; @@ -399,12 +396,12 @@ template class SquirrelManager : public virtual Squirrel v(); } - return _call(m_pSQVM->sqvm, functionVector.size()); + return _call(m_pSQVM->sqvm, (SQInteger)functionVector.size()); } #pragma endregion - public: +public: SquirrelManager() { m_pSQVM = nullptr; @@ -417,7 +414,6 @@ template class SquirrelManager : public virtual Squirrel SQRESULT setupfunc(const SQChar* funcname); void AddFuncOverride(std::string name, SQFunction func); void ProcessMessageBuffer(); - void GenerateSquirrelFunctionsStruct(SquirrelFunctions* s); }; template SquirrelManager* g_pSquirrel; @@ -470,7 +466,7 @@ template requires (std::convertible_to || std::is_constructible_v) inline VoidFunction SQMessageBufferPushArg(T& arg) { auto converted = std::string(arg); - return [converted]{ g_pSquirrel->pushstring(g_pSquirrel->m_pSQVM->sqvm, converted.c_str(), converted.length()); }; + return [converted]{ g_pSquirrel->pushstring(g_pSquirrel->m_pSQVM->sqvm, converted.c_str(), (int)converted.length()); }; } // Assets template @@ -483,7 +479,7 @@ requires is_iterable inline VoidFunction SQMessageBufferPushArg(T& arg) { FunctionVector localv = {}; localv.push_back([]{g_pSquirrel->newarray(g_pSquirrel->m_pSQVM->sqvm, 0);}); - + for (const auto& item : arg) { localv.push_back(SQMessageBufferPushArg(item)); localv.push_back([]{g_pSquirrel->arrayappend(g_pSquirrel->m_pSQVM->sqvm, -2);}); @@ -497,7 +493,7 @@ requires is_map inline VoidFunction SQMessageBufferPushArg(T& map) { FunctionVector localv = {}; localv.push_back([]{g_pSquirrel->newtable(g_pSquirrel->m_pSQVM->sqvm);}); - + for (const auto& item : map) { localv.push_back(SQMessageBufferPushArg(item.first)); localv.push_back(SQMessageBufferPushArg(item.second)); diff --git a/NorthstarDLL/squirrel/squirrelautobind.cpp b/primedev/squirrel/squirrelautobind.cpp similarity index 100% rename from NorthstarDLL/squirrel/squirrelautobind.cpp rename to primedev/squirrel/squirrelautobind.cpp diff --git a/NorthstarDLL/squirrel/squirrelautobind.h b/primedev/squirrel/squirrelautobind.h similarity index 93% rename from NorthstarDLL/squirrel/squirrelautobind.h rename to primedev/squirrel/squirrelautobind.h index 865479a1..0fc599f3 100644 --- a/NorthstarDLL/squirrel/squirrelautobind.h +++ b/primedev/squirrel/squirrelautobind.h @@ -5,7 +5,7 @@ typedef void (*SqAutoBindFunc)(); class SquirrelAutoBindContainer { - public: +public: std::vector> clientSqAutoBindFuncs; std::vector> serverSqAutoBindFuncs; }; @@ -21,16 +21,16 @@ class __squirrelautobind; __squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \ []() \ { \ - if constexpr (runOnContext & ScriptContext::UI) \ + if constexpr ((runOnContext)&ScriptContext::UI) \ g_pSquirrel->AddFuncRegistration( \ returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::UI >); \ - if constexpr (runOnContext & ScriptContext::CLIENT) \ + if constexpr ((runOnContext)&ScriptContext::CLIENT) \ g_pSquirrel->AddFuncRegistration( \ returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::CLIENT >); \ }, \ []() \ { \ - if constexpr (runOnContext & ScriptContext::SERVER) \ + if constexpr ((runOnContext)&ScriptContext::SERVER) \ g_pSquirrel->AddFuncRegistration( \ returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \ }); \ @@ -44,15 +44,15 @@ class __squirrelautobind; __squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \ []() \ { \ - if constexpr (runOnContext & ScriptContext::UI) \ + if constexpr ((runOnContext)&ScriptContext::UI) \ g_pSquirrel->AddFuncOverride(__STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::UI >); \ - if constexpr (runOnContext & ScriptContext::CLIENT) \ + if constexpr ((runOnContext)&ScriptContext::CLIENT) \ g_pSquirrel->AddFuncOverride( \ __STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::CLIENT >); \ }, \ []() \ { \ - if constexpr (runOnContext & ScriptContext::SERVER) \ + if constexpr ((runOnContext)&ScriptContext::SERVER) \ g_pSquirrel->AddFuncOverride( \ __STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \ }); \ @@ -61,7 +61,7 @@ class __squirrelautobind; class __squirrelautobind { - public: +public: __squirrelautobind() = delete; __squirrelautobind(std::function clientAutoBindFunc, std::function serverAutoBindFunc) diff --git a/NorthstarDLL/squirrel/squirrelclasstypes.h b/primedev/squirrel/squirrelclasstypes.h similarity index 93% rename from NorthstarDLL/squirrel/squirrelclasstypes.h rename to primedev/squirrel/squirrelclasstypes.h index c193f2fc..91c3c468 100644 --- a/NorthstarDLL/squirrel/squirrelclasstypes.h +++ b/primedev/squirrel/squirrelclasstypes.h @@ -116,27 +116,20 @@ concept is_iterable = requires(std::ranges::range_value_t x) // clang-format on -typedef int (*SquirrelMessage_External_Pop)(HSquirrelVM* sqvm, void* userdata); -typedef void (*sq_schedule_call_externalType)( - ScriptContext context, const char* funcname, SquirrelMessage_External_Pop function, void* userdata); - class SquirrelMessage { - public: +public: std::string functionName; FunctionVector args; - bool isExternal = false; - void* userdata = NULL; - SquirrelMessage_External_Pop externalFunc = NULL; }; class SquirrelMessageBuffer { - private: +private: std::queue messages = {}; - public: +public: std::mutex mutex; std::optional pop() { @@ -179,7 +172,7 @@ class SquirrelMessageBuffer // Super simple wrapper class to allow pushing Assets via call class SquirrelAsset { - public: +public: std::string path; SquirrelAsset(std::string path) : path(path) {}; }; @@ -243,6 +236,3 @@ typedef SQRESULT (*sq_pushnewstructinstanceType)(HSquirrelVM* sqvm, int fieldCou typedef SQRESULT (*sq_sealstructslotType)(HSquirrelVM* sqvm, int slotIndex); #pragma endregion - -// These "external" versions of the types are for plugins -typedef int64_t (*RegisterSquirrelFuncType_External)(ScriptContext context, SQFuncRegistration* funcReg, char unknown); diff --git a/NorthstarDLL/squirrel/squirreldatatypes.h b/primedev/squirrel/squirreldatatypes.h similarity index 100% rename from NorthstarDLL/squirrel/squirreldatatypes.h rename to primedev/squirrel/squirreldatatypes.h diff --git a/thirdparty/libcurl b/primedev/thirdparty/libcurl similarity index 100% rename from thirdparty/libcurl rename to primedev/thirdparty/libcurl diff --git a/thirdparty/minhook b/primedev/thirdparty/minhook similarity index 100% rename from thirdparty/minhook rename to primedev/thirdparty/minhook diff --git a/thirdparty/minizip b/primedev/thirdparty/minizip similarity index 100% rename from thirdparty/minizip rename to primedev/thirdparty/minizip diff --git a/thirdparty/rapidjson/allocators.h b/primedev/thirdparty/rapidjson/allocators.h similarity index 100% rename from thirdparty/rapidjson/allocators.h rename to primedev/thirdparty/rapidjson/allocators.h diff --git a/thirdparty/rapidjson/document.h b/primedev/thirdparty/rapidjson/document.h similarity index 98% rename from thirdparty/rapidjson/document.h rename to primedev/thirdparty/rapidjson/document.h index e3e20dfb..22fb2f56 100644 --- a/thirdparty/rapidjson/document.h +++ b/primedev/thirdparty/rapidjson/document.h @@ -198,17 +198,19 @@ private: // class-based member iterator implementation disabled, use plain pointers template -struct GenericMemberIterator; +class GenericMemberIterator; //! non-const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { +public: //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { +public: //! use plain const pointer as iterator type typedef const GenericMember* Iterator; }; @@ -1822,17 +1824,18 @@ private: // Initial flags of different types. kNullFlag = kNullType, - kTrueFlag = kTrueType | kBoolFlag, - kFalseFlag = kFalseType | kBoolFlag, - kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, - kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, - kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, - kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, - kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, - kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, - kConstStringFlag = kStringType | kStringFlag, - kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, - kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), kObjectFlag = kObjectType, kArrayFlag = kArrayType, diff --git a/thirdparty/rapidjson/encodedstream.h b/primedev/thirdparty/rapidjson/encodedstream.h similarity index 100% rename from thirdparty/rapidjson/encodedstream.h rename to primedev/thirdparty/rapidjson/encodedstream.h diff --git a/thirdparty/rapidjson/encodings.h b/primedev/thirdparty/rapidjson/encodings.h similarity index 100% rename from thirdparty/rapidjson/encodings.h rename to primedev/thirdparty/rapidjson/encodings.h diff --git a/thirdparty/rapidjson/error/en.h b/primedev/thirdparty/rapidjson/error/en.h similarity index 100% rename from thirdparty/rapidjson/error/en.h rename to primedev/thirdparty/rapidjson/error/en.h diff --git a/thirdparty/rapidjson/error/error.h b/primedev/thirdparty/rapidjson/error/error.h similarity index 100% rename from thirdparty/rapidjson/error/error.h rename to primedev/thirdparty/rapidjson/error/error.h diff --git a/thirdparty/rapidjson/filereadstream.h b/primedev/thirdparty/rapidjson/filereadstream.h similarity index 100% rename from thirdparty/rapidjson/filereadstream.h rename to primedev/thirdparty/rapidjson/filereadstream.h diff --git a/thirdparty/rapidjson/filewritestream.h b/primedev/thirdparty/rapidjson/filewritestream.h similarity index 100% rename from thirdparty/rapidjson/filewritestream.h rename to primedev/thirdparty/rapidjson/filewritestream.h diff --git a/thirdparty/rapidjson/fwd.h b/primedev/thirdparty/rapidjson/fwd.h similarity index 100% rename from thirdparty/rapidjson/fwd.h rename to primedev/thirdparty/rapidjson/fwd.h diff --git a/thirdparty/rapidjson/internal/biginteger.h b/primedev/thirdparty/rapidjson/internal/biginteger.h similarity index 100% rename from thirdparty/rapidjson/internal/biginteger.h rename to primedev/thirdparty/rapidjson/internal/biginteger.h diff --git a/thirdparty/rapidjson/internal/diyfp.h b/primedev/thirdparty/rapidjson/internal/diyfp.h similarity index 100% rename from thirdparty/rapidjson/internal/diyfp.h rename to primedev/thirdparty/rapidjson/internal/diyfp.h diff --git a/thirdparty/rapidjson/internal/dtoa.h b/primedev/thirdparty/rapidjson/internal/dtoa.h similarity index 100% rename from thirdparty/rapidjson/internal/dtoa.h rename to primedev/thirdparty/rapidjson/internal/dtoa.h diff --git a/thirdparty/rapidjson/internal/ieee754.h b/primedev/thirdparty/rapidjson/internal/ieee754.h similarity index 100% rename from thirdparty/rapidjson/internal/ieee754.h rename to primedev/thirdparty/rapidjson/internal/ieee754.h diff --git a/thirdparty/rapidjson/internal/itoa.h b/primedev/thirdparty/rapidjson/internal/itoa.h similarity index 100% rename from thirdparty/rapidjson/internal/itoa.h rename to primedev/thirdparty/rapidjson/internal/itoa.h diff --git a/thirdparty/rapidjson/internal/meta.h b/primedev/thirdparty/rapidjson/internal/meta.h similarity index 100% rename from thirdparty/rapidjson/internal/meta.h rename to primedev/thirdparty/rapidjson/internal/meta.h diff --git a/thirdparty/rapidjson/internal/pow10.h b/primedev/thirdparty/rapidjson/internal/pow10.h similarity index 100% rename from thirdparty/rapidjson/internal/pow10.h rename to primedev/thirdparty/rapidjson/internal/pow10.h diff --git a/thirdparty/rapidjson/internal/regex.h b/primedev/thirdparty/rapidjson/internal/regex.h similarity index 100% rename from thirdparty/rapidjson/internal/regex.h rename to primedev/thirdparty/rapidjson/internal/regex.h diff --git a/thirdparty/rapidjson/internal/stack.h b/primedev/thirdparty/rapidjson/internal/stack.h similarity index 100% rename from thirdparty/rapidjson/internal/stack.h rename to primedev/thirdparty/rapidjson/internal/stack.h diff --git a/thirdparty/rapidjson/internal/strfunc.h b/primedev/thirdparty/rapidjson/internal/strfunc.h similarity index 100% rename from thirdparty/rapidjson/internal/strfunc.h rename to primedev/thirdparty/rapidjson/internal/strfunc.h diff --git a/thirdparty/rapidjson/internal/strtod.h b/primedev/thirdparty/rapidjson/internal/strtod.h similarity index 100% rename from thirdparty/rapidjson/internal/strtod.h rename to primedev/thirdparty/rapidjson/internal/strtod.h diff --git a/thirdparty/rapidjson/internal/swap.h b/primedev/thirdparty/rapidjson/internal/swap.h similarity index 100% rename from thirdparty/rapidjson/internal/swap.h rename to primedev/thirdparty/rapidjson/internal/swap.h diff --git a/thirdparty/rapidjson/istreamwrapper.h b/primedev/thirdparty/rapidjson/istreamwrapper.h similarity index 100% rename from thirdparty/rapidjson/istreamwrapper.h rename to primedev/thirdparty/rapidjson/istreamwrapper.h diff --git a/thirdparty/rapidjson/memorybuffer.h b/primedev/thirdparty/rapidjson/memorybuffer.h similarity index 100% rename from thirdparty/rapidjson/memorybuffer.h rename to primedev/thirdparty/rapidjson/memorybuffer.h diff --git a/thirdparty/rapidjson/memorystream.h b/primedev/thirdparty/rapidjson/memorystream.h similarity index 100% rename from thirdparty/rapidjson/memorystream.h rename to primedev/thirdparty/rapidjson/memorystream.h diff --git a/thirdparty/rapidjson/msinttypes/inttypes.h b/primedev/thirdparty/rapidjson/msinttypes/inttypes.h similarity index 100% rename from thirdparty/rapidjson/msinttypes/inttypes.h rename to primedev/thirdparty/rapidjson/msinttypes/inttypes.h diff --git a/thirdparty/rapidjson/msinttypes/stdint.h b/primedev/thirdparty/rapidjson/msinttypes/stdint.h similarity index 100% rename from thirdparty/rapidjson/msinttypes/stdint.h rename to primedev/thirdparty/rapidjson/msinttypes/stdint.h diff --git a/thirdparty/rapidjson/ostreamwrapper.h b/primedev/thirdparty/rapidjson/ostreamwrapper.h similarity index 100% rename from thirdparty/rapidjson/ostreamwrapper.h rename to primedev/thirdparty/rapidjson/ostreamwrapper.h diff --git a/thirdparty/rapidjson/pointer.h b/primedev/thirdparty/rapidjson/pointer.h similarity index 100% rename from thirdparty/rapidjson/pointer.h rename to primedev/thirdparty/rapidjson/pointer.h diff --git a/thirdparty/rapidjson/prettywriter.h b/primedev/thirdparty/rapidjson/prettywriter.h similarity index 100% rename from thirdparty/rapidjson/prettywriter.h rename to primedev/thirdparty/rapidjson/prettywriter.h diff --git a/thirdparty/rapidjson/rapidjson.h b/primedev/thirdparty/rapidjson/rapidjson.h similarity index 100% rename from thirdparty/rapidjson/rapidjson.h rename to primedev/thirdparty/rapidjson/rapidjson.h diff --git a/thirdparty/rapidjson/reader.h b/primedev/thirdparty/rapidjson/reader.h similarity index 100% rename from thirdparty/rapidjson/reader.h rename to primedev/thirdparty/rapidjson/reader.h diff --git a/thirdparty/rapidjson/schema.h b/primedev/thirdparty/rapidjson/schema.h similarity index 100% rename from thirdparty/rapidjson/schema.h rename to primedev/thirdparty/rapidjson/schema.h diff --git a/thirdparty/rapidjson/stream.h b/primedev/thirdparty/rapidjson/stream.h similarity index 100% rename from thirdparty/rapidjson/stream.h rename to primedev/thirdparty/rapidjson/stream.h diff --git a/thirdparty/rapidjson/stringbuffer.h b/primedev/thirdparty/rapidjson/stringbuffer.h similarity index 100% rename from thirdparty/rapidjson/stringbuffer.h rename to primedev/thirdparty/rapidjson/stringbuffer.h diff --git a/thirdparty/rapidjson/writer.h b/primedev/thirdparty/rapidjson/writer.h similarity index 100% rename from thirdparty/rapidjson/writer.h rename to primedev/thirdparty/rapidjson/writer.h diff --git a/primedev/thirdparty/silver-bun/CMakeLists.txt b/primedev/thirdparty/silver-bun/CMakeLists.txt new file mode 100644 index 00000000..c39dd8bc --- /dev/null +++ b/primedev/thirdparty/silver-bun/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library( + silver-bun STATIC + "memaddr.cpp" + "memaddr.h" + "module.cpp" + "module.h" + "utils.cpp" + "utils.h" + ) diff --git a/primedev/thirdparty/silver-bun/memaddr.cpp b/primedev/thirdparty/silver-bun/memaddr.cpp new file mode 100644 index 00000000..929d1682 --- /dev/null +++ b/primedev/thirdparty/silver-bun/memaddr.cpp @@ -0,0 +1,368 @@ +//===========================================================================// +// +// Purpose: Implementation of the CMemory class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// + +#include "memaddr.h" +#include "utils.h" + +//----------------------------------------------------------------------------- +// Purpose: check array of opcodes starting from current address +// Input : &vOpcodeArray - +// Output : true if equal, false otherwise +//----------------------------------------------------------------------------- +bool CMemory::CheckOpCodes(const std::vector& vOpcodeArray) const +{ + uintptr_t ref = ptr; + + // Loop forward in the ptr class member. + for (auto [byteAtCurrentAddress, i] = std::tuple{ uint8_t(), (size_t)0 }; i < vOpcodeArray.size(); i++, ref++) + { + byteAtCurrentAddress = *reinterpret_cast(ref); + + // If byte at ptr doesn't equal in the byte array return false. + if (byteAtCurrentAddress != vOpcodeArray[i]) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Checx if memory is readable +// Input : nSize - +//----------------------------------------------------------------------------- +bool CMemory::IsMemoryReadable(const size_t nSize) const +{ + static SYSTEM_INFO sysInfo; + if (!sysInfo.dwPageSize) + GetSystemInfo(&sysInfo); + + MEMORY_BASIC_INFORMATION memInfo; + if (!VirtualQuery(reinterpret_cast(GetPtr()), &memInfo, sizeof(memInfo))) + return false; + + return memInfo.RegionSize >= nSize && memInfo.State & MEM_COMMIT && !(memInfo.Protect & PAGE_NOACCESS); +} + +//----------------------------------------------------------------------------- +// Purpose: patch size with nop opcodes +// Input : nSize - +//----------------------------------------------------------------------------- +void CMemory::NOP(const size_t nSize) const +{ + std::vector vOpcodeArray; + vOpcodeArray.resize(nSize); + memset(vOpcodeArray.data(), 0x90, nSize); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes +// Input : *pszOpcodes - +//----------------------------------------------------------------------------- +void CMemory::Patch(const char* pszOpcodes) const +{ + const std::vector vOpcodeArray = Utils::StringPatternToBytes(pszOpcodes); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes starting from current address +// Input : *pOpcodeArray - +// nSize - +//----------------------------------------------------------------------------- +void CMemory::Patch(const uint8_t* pOpcodeArray, const size_t nSize) const +{ + const std::vector vOpcodeArray(pOpcodeArray, pOpcodeArray + nSize * sizeof(uint8_t)); + Patch(vOpcodeArray); +} + +//----------------------------------------------------------------------------- +// Purpose: patch array of opcodes starting from current address +// Input : &vOpcodeArray - +//----------------------------------------------------------------------------- +void CMemory::Patch(const std::vector& vOpcodeArray) const +{ + DWORD oldProt = NULL; + + SIZE_T dwSize = vOpcodeArray.size(); + VirtualProtect(reinterpret_cast(ptr), dwSize, PAGE_EXECUTE_READWRITE, &oldProt); // Patch page to be able to read and write to it. + + for (size_t i = 0; i < vOpcodeArray.size(); i++) + { + *reinterpret_cast(ptr + i) = vOpcodeArray[i]; // Write opcodes to Address. + } + + dwSize = vOpcodeArray.size(); + VirtualProtect(reinterpret_cast(ptr), dwSize, oldProt, &oldProt); // Restore protection. +} + +//----------------------------------------------------------------------------- +// Purpose: patch string constant at current address +// Input : *szString - +//----------------------------------------------------------------------------- +void CMemory::PatchString(const char* szString) const +{ + DWORD oldProt = NULL; + SIZE_T dwSize = strlen(szString); + + VirtualProtect(reinterpret_cast(ptr), dwSize, PAGE_EXECUTE_READWRITE, &oldProt); // Patch page to be able to read and write to it. + + for (size_t i = 0; i < dwSize; i++) + { + *reinterpret_cast(ptr + i) = szString[i]; // Write string to Address. + } + + VirtualProtect(reinterpret_cast(ptr), dwSize, oldProt, &oldProt); // Restore protection. +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory +// Input : *szPattern - +// searchDirect - +// opCodesToScan - +// occurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FindPattern(const char* szPattern, const Direction searchDirect, const int opCodesToScan, const ptrdiff_t occurrence) const +{ + uint8_t* pScanBytes = reinterpret_cast(ptr); // Get the base of the module. + + const std::vector PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array. + const std::pair bytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. + ptrdiff_t occurrences = 0; + + for (long i = 01; i < opCodesToScan + bytesInfo.first; i++) + { + bool bFound = true; + int nMemOffset = searchDirect == Direction::DOWN ? i : -i; + + for (DWORD j = 0ul; j < bytesInfo.first; j++) + { + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + uint8_t currentByte = *(pScanBytes + nMemOffset + j); + _mm_prefetch(reinterpret_cast(static_cast(currentByte + nMemOffset + 64)), _MM_HINT_T0); // precache some data in L1. + if (currentByte != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + occurrences++; + if (occurrence == occurrences) + { + return CMemory(&*(pScanBytes + nMemOffset)); + } + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory starting from current address +// Input : *szPattern - +// searchDirect - +// opCodesToScan - +// occurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FindPatternSelf(const char* szPattern, const Direction searchDirect, const int opCodesToScan, const ptrdiff_t occurrence) +{ + uint8_t* pScanBytes = reinterpret_cast(ptr); // Get the base of the module. + + const std::vector PatternBytes = Utils::PatternToBytes(szPattern); // Convert our pattern to a byte array. + const std::pair bytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. + ptrdiff_t occurrences = 0; + + for (long i = 01; i < opCodesToScan + bytesInfo.first; i++) + { + bool bFound = true; + int nMemOffset = searchDirect == Direction::DOWN ? i : -i; + + for (DWORD j = 0ul; j < bytesInfo.first; j++) + { + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + uint8_t currentByte = *(pScanBytes + nMemOffset + j); + _mm_prefetch(reinterpret_cast(static_cast(currentByte + nMemOffset + 64)), _MM_HINT_T0); // precache some data in L1. + if (currentByte != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + occurrences++; + if (occurrence == occurrences) + { + ptr = uintptr_t(&*(pScanBytes + nMemOffset)); + return *this; + } + } + } + + ptr = uintptr_t(); + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddress wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCall(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) const +{ + return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: ResolveRelativeAddressSelf wrapper +// Input : opcodeOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::FollowNearCallSelf(const ptrdiff_t opcodeOffset, const ptrdiff_t nextInstructionOffset) +{ + return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddress(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) const +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + return CMemory(nextInstruction + relativeAddress); +} + +//----------------------------------------------------------------------------- +// Purpose: resolves the relative pointer to offset from current address +// Input : registerOffset - +// nextInstructionOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CMemory::ResolveRelativeAddressSelf(const ptrdiff_t registerOffset, const ptrdiff_t nextInstructionOffset) +{ + // Skip register. + const uintptr_t skipRegister = ptr + registerOffset; + + // Get 4-byte long relative Address. + const int32_t relativeAddress = *reinterpret_cast(skipRegister); + + // Get location of next instruction. + const uintptr_t nextInstruction = ptr + nextInstructionOffset; + + // Get function location via adding relative Address to next instruction. + ptr = nextInstruction + relativeAddress; + return *this; +} + +//----------------------------------------------------------------------------- +// Purpose: resolve all 'call' references to ptr +// (This is very slow only use for mass patching.) +// Input : sectionBase - +// sectionSize - +// Output : std::vector +//----------------------------------------------------------------------------- +std::vector CMemory::FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize) +{ + std::vector referencesInfo = {}; + + uint8_t* pTextStart = reinterpret_cast(sectionBase); + for (size_t i = 0ull; i < sectionSize - 0x5; i++, _mm_prefetch(reinterpret_cast(pTextStart + 64), _MM_HINT_NTA)) + { + if (pTextStart[i] == 0xE8) + { + CMemory memAddr = CMemory(&pTextStart[i]); + if (!memAddr.Offset(0x1).CheckOpCodes({ 0x00, 0x00, 0x00, 0x00 })) // Check if its not a dynamic resolved call. + { + if (memAddr.FollowNearCall() == *this) + referencesInfo.push_back(memAddr); + } + } + } + + return referencesInfo; +} + +//----------------------------------------------------------------------------- +// Purpose: patch virtual method to point to a user set function +// Input : virtualTable - +// pHookMethod - +// methodIndex - +// ppOriginalMethod - +// Output : void** via ppOriginalMethod +//----------------------------------------------------------------------------- +void CMemory::HookVirtualMethod(const uintptr_t virtualTable, const void* pHookMethod, const ptrdiff_t methodIndex, void** ppOriginalMethod) +{ + DWORD oldProt = NULL; + + // Calculate delta to next virtual method. + const uintptr_t virtualMethod = virtualTable + (methodIndex * sizeof(ptrdiff_t)); + + // Preserve original function. + const uintptr_t originalFunction = *reinterpret_cast(virtualMethod); + + // Set page for current virtual method to execute n read n write. + VirtualProtect(reinterpret_cast(virtualMethod), sizeof(virtualMethod), PAGE_EXECUTE_READWRITE, &oldProt); + + // Set virtual method to our hook. + *reinterpret_cast(virtualMethod) = reinterpret_cast(pHookMethod); + + // Restore original page. + VirtualProtect(reinterpret_cast(virtualMethod), sizeof(virtualMethod), oldProt, &oldProt); + + // Move original function into argument. + *ppOriginalMethod = reinterpret_cast(originalFunction); +} + +//----------------------------------------------------------------------------- +// Purpose: patch iat entry to point to a user set function +// Input : pImportedMethod - +// pHookMethod - +// ppOriginalMethod - +// Output : void** via ppOriginalMethod +//----------------------------------------------------------------------------- +void CMemory::HookImportedFunction(const uintptr_t pImportedMethod, const void* pHookMethod, void** ppOriginalMethod) +{ + DWORD oldProt = NULL; + + // Preserve original function. + const uintptr_t originalFunction = *reinterpret_cast(pImportedMethod); + + // Set page for current iat entry to execute n read n write. + VirtualProtect(reinterpret_cast(pImportedMethod), sizeof(void*), PAGE_EXECUTE_READWRITE, &oldProt); + + // Set method to our hook. + *reinterpret_cast(pImportedMethod) = reinterpret_cast(pHookMethod); + + // Restore original page. + VirtualProtect(reinterpret_cast(pImportedMethod), sizeof(void*), oldProt, &oldProt); + + // Move original function into argument. + *ppOriginalMethod = reinterpret_cast(originalFunction); +} diff --git a/primedev/thirdparty/silver-bun/memaddr.h b/primedev/thirdparty/silver-bun/memaddr.h new file mode 100644 index 00000000..3060c0cd --- /dev/null +++ b/primedev/thirdparty/silver-bun/memaddr.h @@ -0,0 +1,154 @@ +//===========================================================================// +// +// Purpose: Implementation of the CMemory class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// +#pragma once + +#include +#include +#include +#include +#include + +class CMemory +{ +public: + enum class Direction : int + { + DOWN = 0, + UP, + }; + + CMemory(void) = default; + CMemory(const uintptr_t ptr) : ptr(ptr) {} + CMemory(const void* ptr) : ptr(uintptr_t(ptr)) {} + + inline operator uintptr_t(void) const + { + return ptr; + } + + inline operator void*(void) const + { + return reinterpret_cast(ptr); + } + + inline operator bool(void) const + { + return ptr != NULL; + } + + inline bool operator!= (const CMemory& addr) const + { + return ptr != addr.ptr; + } + + inline bool operator== (const CMemory& addr) const + { + return ptr == addr.ptr; + } + + inline bool operator== (const uintptr_t& addr) const + { + return ptr == addr; + } + + inline uintptr_t GetPtr(void) const + { + return ptr; + } + + template inline T GetValue(void) const + { + return *reinterpret_cast(ptr); + } + + template inline T GetVirtualFunctionIndex(void) const + { + return *reinterpret_cast(ptr) / 8; + } + + template inline T CCast(void) const + { + return (T)ptr; + } + + template inline T RCast(void) const + { + return reinterpret_cast(ptr); + } + + inline CMemory Offset(ptrdiff_t offset) const + { + return CMemory(ptr + offset); + } + + inline CMemory OffsetSelf(ptrdiff_t offset) + { + ptr += offset; + return *this; + } + + inline CMemory Deref(int deref = 1) const + { + uintptr_t reference = ptr; + + while (deref--) + { + if (reference) + reference = *reinterpret_cast(reference); + } + + return CMemory(reference); + } + + inline CMemory DerefSelf(int deref = 1) + { + while (deref--) + { + if (ptr) + ptr = *reinterpret_cast(ptr); + } + + return *this; + } + + inline CMemory WalkVTable(ptrdiff_t vfuncIndex) + { + uintptr_t reference = ptr + (sizeof(uintptr_t) * vfuncIndex); + return CMemory(reference); + } + + inline CMemory WalkVTableSelf(ptrdiff_t vfuncIndex) + { + ptr += (sizeof(uintptr_t) * vfuncIndex); + return *this; + } + + bool CheckOpCodes(const std::vector& vOpcodeArray) const; + bool IsMemoryReadable(const size_t nSize) const; + + void NOP(const size_t nSize) const; + void Patch(const char* pszOpcodes) const; + void Patch(const uint8_t* pOpcodeArray, const size_t nSize) const; + void Patch(const std::vector& vOpcodeArray) const; + void PatchString(const char* szString) const; + + CMemory FindPattern(const char* szPattern, const Direction searchDirect = Direction::DOWN, const int opCodesToScan = 512, const ptrdiff_t occurrence = 1) const; + CMemory FindPatternSelf(const char* szPattern, const Direction searchDirect = Direction::DOWN, const int opCodesToScan = 512, const ptrdiff_t occurrence = 1); + std::vector FindAllCallReferences(const uintptr_t sectionBase, const size_t sectionSize); + + CMemory FollowNearCall(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5) const; + CMemory FollowNearCallSelf(const ptrdiff_t opcodeOffset = 0x1, const ptrdiff_t nextInstructionOffset = 0x5); + CMemory ResolveRelativeAddress(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4) const; + CMemory ResolveRelativeAddressSelf(const ptrdiff_t registerOffset = 0x0, const ptrdiff_t nextInstructionOffset = 0x4); + + static void HookVirtualMethod(const uintptr_t virtualTable, const void* pHookMethod, const ptrdiff_t methodIndex, void** ppOriginalMethod); + static void HookImportedFunction(const uintptr_t pImportedMethod, const void* pHookMethod, void** ppOriginalMethod); + +private: + uintptr_t ptr = 0; +}; diff --git a/primedev/thirdparty/silver-bun/module.cpp b/primedev/thirdparty/silver-bun/module.cpp new file mode 100644 index 00000000..84f4da9e --- /dev/null +++ b/primedev/thirdparty/silver-bun/module.cpp @@ -0,0 +1,484 @@ +//===========================================================================// +// +// Purpose: Implementation of the CModule class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// + +#include "module.h" +#include "utils.h" + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : *szModuleName - +//----------------------------------------------------------------------------- +CModule::CModule(HMODULE hModule) +{ + MODULEINFO mInfo {0}; + + if (hModule && hModule != INVALID_HANDLE_VALUE) + GetModuleInformation(GetCurrentProcess(), hModule, &mInfo, sizeof(MODULEINFO)); + + m_nModuleSize = static_cast(mInfo.SizeOfImage); + m_pModuleBase = reinterpret_cast(mInfo.lpBaseOfDll); + + if (!m_nModuleSize || !m_pModuleBase) + return; + + CHAR szModuleName[MAX_PATH]; + DWORD dwSize = GetModuleFileNameA(hModule, szModuleName, sizeof(szModuleName)); + char* chLast = strrchr(szModuleName, '\\'); + m_ModuleName = chLast == nullptr ? szModuleName : chLast + 1; + + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : *szModuleName - +//----------------------------------------------------------------------------- +CModule::CModule(const char* szModuleName) +{ + m_pModuleBase = reinterpret_cast(GetModuleHandleA(szModuleName)); + m_ModuleName = szModuleName; + + Init(); + LoadSections(); +} + +//----------------------------------------------------------------------------- +// Purpose: initializes module descriptors +//----------------------------------------------------------------------------- +void CModule::Init() +{ + m_pDOSHeader = reinterpret_cast(m_pModuleBase); + m_pNTHeaders = reinterpret_cast(m_pModuleBase + m_pDOSHeader->e_lfanew); + m_nModuleSize = static_cast(m_pNTHeaders->OptionalHeader.SizeOfImage); + + const IMAGE_SECTION_HEADER* hSection = IMAGE_FIRST_SECTION(m_pNTHeaders); // Get first image section. + + for (WORD i = 0; i < m_pNTHeaders->FileHeader.NumberOfSections; i++) // Loop through the sections. + { + const IMAGE_SECTION_HEADER& hCurrentSection = hSection[i]; // Get current section. + m_ModuleSections.push_back(ModuleSections_t(reinterpret_cast(hCurrentSection.Name), + static_cast(m_pModuleBase + hCurrentSection.VirtualAddress), hCurrentSection.SizeOfRawData)); // Push back a struct with the section data. + } +} + +//----------------------------------------------------------------------------- +// Purpose: initializes the default executable segments +//----------------------------------------------------------------------------- +void CModule::LoadSections() +{ + m_ExecutableCode = GetSectionByName(".text"); + m_ExceptionTable = GetSectionByName(".pdata"); + m_RunTimeData = GetSectionByName(".data"); + m_ReadOnlyData = GetSectionByName(".rdata"); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets memory at relative offset +// Input : nOffset - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::Offset(const uintptr_t nOffset) const +{ + return CMemory(m_pModuleBase + nOffset); +} + +//----------------------------------------------------------------------------- +// Purpose: find array of bytes in process memory using SIMD instructions +// Input : *pPattern - +// *szMask - +// *moduleSection - +// nOccurrence - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const uint8_t* pPattern, const char* szMask, + const ModuleSections_t* moduleSection, const size_t nOccurrence) const +{ + if (!m_ExecutableCode.IsSectionValid()) + return CMemory(); + + const bool bSectionValid = moduleSection ? moduleSection->IsSectionValid() : false; + + const uintptr_t nBase = bSectionValid ? moduleSection->m_pSectionBase : m_ExecutableCode.m_pSectionBase; + const uintptr_t nSize = bSectionValid ? moduleSection->m_nSectionSize : m_ExecutableCode.m_nSectionSize; + + const size_t nMaskLen = strlen(szMask); + const uint8_t* pData = reinterpret_cast(nBase); + const uint8_t* pEnd = pData + nSize - nMaskLen; + + size_t nOccurrenceCount = 0; + int nMasks[64]; // 64*16 = enough masks for 1024 bytes. + const int iNumMasks = static_cast(ceil(static_cast(nMaskLen) / 16.f)); + + memset(nMasks, '\0', iNumMasks * sizeof(int)); + for (intptr_t i = 0; i < iNumMasks; ++i) + { + for (intptr_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + _bittestandset(reinterpret_cast(&nMasks[i]), static_cast(j)); + } + } + } + const __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(pPattern)); + __m128i xmm2, xmm3, msks; + for (; pData != pEnd; _mm_prefetch(reinterpret_cast(++pData + 64), _MM_HINT_NTA)) + { + if (pPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128(reinterpret_cast(pData)); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0]) + { + for (uintptr_t i = 1; i < static_cast(iNumMasks); ++i) + { + xmm2 = _mm_loadu_si128(reinterpret_cast((pData + i * 16))); + xmm3 = _mm_loadu_si128(reinterpret_cast((pPattern + i * 16))); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & nMasks[i]) == nMasks[i]) + { + if ((i + 1) == iNumMasks) + { + if (nOccurrenceCount == nOccurrence) + { + return static_cast(const_cast(pData)); + } + nOccurrenceCount++; + } + } + else + { + goto cont; + } + } + if (nOccurrenceCount == nOccurrence) + { + return static_cast((&*(const_cast(pData)))); + } + nOccurrenceCount++; + } + }cont:; + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find a string pattern in process memory using SIMD instructions +// Input : *szPattern - +// *moduleSection - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindPatternSIMD(const char* szPattern, const ModuleSections_t* moduleSection) const +{ + const std::pair, std::string> patternInfo = Utils::PatternToMaskedBytes(szPattern); + return FindPatternSIMD(patternInfo.first.data(), patternInfo.second.c_str(), moduleSection); +} + +//----------------------------------------------------------------------------- +// Purpose: find address of reference to string constant in executable memory +// Input : *szString - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindString(const char* szString, const ptrdiff_t nOccurrence, bool bNullTerminator) const +{ + if (!m_ExecutableCode.IsSectionValid()) + return CMemory(); + + const CMemory stringAddress = FindStringReadOnly(szString, bNullTerminator); // Get Address for the string in the .rdata section. + + if (!stringAddress) + return CMemory(); + + uint8_t* pLatestOccurrence = nullptr; + uint8_t* pTextStart = reinterpret_cast(m_ExecutableCode.m_pSectionBase); // Get the start of the .text section. + ptrdiff_t dOccurrencesFound = 0; + CMemory resultAddress; + + for (size_t i = 0ull; i < m_ExecutableCode.m_nSectionSize - 0x5; i++) + { + byte byte = pTextStart[i]; + if (byte == 0x8D) // 0x8D = LEA + { + const CMemory skipOpCode = CMemory(reinterpret_cast(&pTextStart[i])).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and the register. + const int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative Address + const uintptr_t nextInstruction = skipOpCode.Offset(0x4).GetPtr(); // Get location of next instruction. + const CMemory potentialLocation = CMemory(nextInstruction + relativeAddress); // Get potential string location. + + if (potentialLocation == stringAddress) + { + dOccurrencesFound++; + if (nOccurrence == dOccurrencesFound) + { + return CMemory(&pTextStart[i]); + } + + pLatestOccurrence = &pTextStart[i]; // Stash latest occurrence. + } + } + } + + return CMemory(pLatestOccurrence); +} + +//----------------------------------------------------------------------------- +// Purpose: find address of input string constant in read only memory +// Input : *szString - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindStringReadOnly(const char* szString, bool bNullTerminator) const +{ + if (!m_ReadOnlyData.IsSectionValid()) + return CMemory(); + + const std::vector vBytes = Utils::StringToBytes(szString, bNullTerminator); // Convert our string to a byte array. + const std::pair bytesInfo = std::make_pair(vBytes.size(), vBytes.data()); // Get the size and data of our bytes. + + const uint8_t* pBase = reinterpret_cast(m_ReadOnlyData.m_pSectionBase); // Get start of .rdata section. + + for (size_t i = 0ull; i < m_ReadOnlyData.m_nSectionSize - bytesInfo.first; i++) + { + bool bFound = true; + + // If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard + // our if clause will be false. + for (size_t j = 0ull; j < bytesInfo.first; j++) + { + if (pBase[i + j] != bytesInfo.second[j] && bytesInfo.second[j] != -1) + { + bFound = false; + break; + } + } + + if (bFound) + { + return CMemory(&pBase[i]); + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: find 'free' page in r/w/x sections +// Input : nSize - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::FindFreeDataPage(const size_t nSize) const +{ + auto checkDataSection = [](const void* address, const std::size_t size) + { + MEMORY_BASIC_INFORMATION membInfo = { 0 }; + + VirtualQuery(address, &membInfo, sizeof(membInfo)); + + if (membInfo.AllocationBase && membInfo.BaseAddress && membInfo.State == MEM_COMMIT && !(membInfo.Protect & PAGE_GUARD) && membInfo.Protect != PAGE_NOACCESS) + { + if ((membInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)) && membInfo.RegionSize >= size) + { + return ((membInfo.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)) && membInfo.RegionSize >= size) ? true : false; + } + } + return false; + }; + + // This is very unstable, this doesn't check for the actual 'page' sizes. + // Also can be optimized to search per 'section'. + const uintptr_t endOfModule = m_pModuleBase + m_pNTHeaders->OptionalHeader.SizeOfImage - sizeof(uintptr_t); + for (uintptr_t currAddr = endOfModule; m_pModuleBase < currAddr; currAddr -= sizeof(uintptr_t)) + { + if (*reinterpret_cast(currAddr) == 0 && checkDataSection(reinterpret_cast(currAddr), nSize)) + { + bool bIsGoodPage = true; + uint32_t nPageCount = 0; + + for (; nPageCount < nSize && bIsGoodPage; nPageCount += sizeof(uintptr_t)) + { + const uintptr_t pageData = *reinterpret_cast(currAddr + nPageCount); + if (pageData != 0) + bIsGoodPage = false; + } + + if (bIsGoodPage && nPageCount >= nSize) + return currAddr; + } + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of a virtual method table by rtti type descriptor name +// Input : *szTableName - +// nRefIndex - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetVirtualMethodTable(const char* szTableName, const size_t nRefIndex) +{ + if (!m_ReadOnlyData.IsSectionValid()) // Process decided to rename the readonlydata section if this fails. + return CMemory(); + + ModuleSections_t moduleSection(".data", m_RunTimeData.m_pSectionBase, m_RunTimeData.m_nSectionSize); + + const auto tableNameInfo = Utils::StringToMaskedBytes(szTableName, false); + CMemory rttiTypeDescriptor = FindPatternSIMD(tableNameInfo.first.data(), tableNameInfo.second.c_str(), &moduleSection).OffsetSelf(-0x10); + if (!rttiTypeDescriptor) + return CMemory(); + + uintptr_t scanStart = m_ReadOnlyData.m_pSectionBase; // Get the start address of our scan. + + const uintptr_t scanEnd = (m_ReadOnlyData.m_pSectionBase + m_ReadOnlyData.m_nSectionSize) - 0x4; // Calculate the end of our scan. + const uintptr_t rttiTDRva = rttiTypeDescriptor.GetPtr() - m_pModuleBase; // The RTTI gets referenced by a 4-Byte RVA address. We need to scan for that address. + while (scanStart < scanEnd) + { + moduleSection = { ".rdata", scanStart, m_ReadOnlyData.m_nSectionSize }; + CMemory reference = FindPatternSIMD(reinterpret_cast(&rttiTDRva), "xxxx", &moduleSection, nRefIndex); + if (!reference) + break; + + CMemory referenceOffset = reference.Offset(-0xC); + if (referenceOffset.GetValue() != 1) // Check if we got a RTTI Object Locator for this reference by checking if -0xC is 1, which is the 'signature' field which is always 1 on x64. + { + scanStart = reference.Offset(0x4).GetPtr(); // Set location to current reference + 0x4 so we avoid pushing it back again into the vector. + continue; + } + + moduleSection = { ".rdata", m_ReadOnlyData.m_pSectionBase, m_ReadOnlyData.m_nSectionSize }; + return FindPatternSIMD(reinterpret_cast(&referenceOffset), "xxxxxxxx", &moduleSection).OffsetSelf(0x8); + } + + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of imported function in this module +// Input : *szModuleName - +// *szFunctionName - +// bGetFunctionReference - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetImportedFunction(const char* szModuleName, const char* szFunctionName, const bool bGetFunctionReference) const +{ + if (!m_pDOSHeader || m_pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? + return CMemory(); + + if (!m_pNTHeaders || m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? + return CMemory(); + + // Get the location of IMAGE_IMPORT_DESCRIPTOR for this module by adding the IMAGE_DIRECTORY_ENTRY_IMPORT relative virtual address onto our module base address. + IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptors = reinterpret_cast(m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + if (!pImageImportDescriptors) + return CMemory(); + + for (IMAGE_IMPORT_DESCRIPTOR* pIID = pImageImportDescriptors; pIID->Name != 0; pIID++) + { + // Get virtual relative Address of the imported module name. Then add module base Address to get the actual location. + const char* szImportedModuleName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pIID->Name)); + + if (_stricmp(szImportedModuleName, szModuleName) == 0) // Is this our wanted imported module?. + { + // Original First Thunk to get function name. + IMAGE_THUNK_DATA* pOgFirstThunk = reinterpret_cast(m_pModuleBase + pIID->OriginalFirstThunk); + + // To get actual function address. + IMAGE_THUNK_DATA* pFirstThunk = reinterpret_cast(m_pModuleBase + pIID->FirstThunk); + for (; pOgFirstThunk->u1.AddressOfData; ++pOgFirstThunk, ++pFirstThunk) + { + // Get image import by name. + const IMAGE_IMPORT_BY_NAME* pImageImportByName = reinterpret_cast(m_pModuleBase + pOgFirstThunk->u1.AddressOfData); + + if (strcmp(pImageImportByName->Name, szFunctionName) == 0) // Is this our wanted imported function? + { + // Grab function address from firstThunk. +#if _WIN64 + uintptr_t* pFunctionAddress = &pFirstThunk->u1.Function; +#else + uintptr_t* pFunctionAddress = reinterpret_cast(&pFirstThunk->u1.Function); +#endif // #if _WIN64 + + // Reference or address? + return bGetFunctionReference ? CMemory(pFunctionAddress) : CMemory(*pFunctionAddress); // Return as CMemory class. + } + } + + } + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get address of exported function in this module +// Input : *szFunctionName - +// bNullTerminator - +// Output : CMemory +//----------------------------------------------------------------------------- +CMemory CModule::GetExportedFunction(const char* szFunctionName) const +{ + if (!m_pDOSHeader || m_pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? + return CMemory(); + + if (!m_pNTHeaders || m_pNTHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? + return CMemory(); + + // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual address onto our module base address. + const IMAGE_EXPORT_DIRECTORY* pImageExportDirectory = reinterpret_cast(m_pModuleBase + m_pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + if (!pImageExportDirectory) + return CMemory(); + + // Are there any exported functions? + if (!pImageExportDirectory->NumberOfFunctions) + return CMemory(); + + // Get the location of the functions via adding the relative virtual address from the struct into our module base address. + const DWORD* pAddressOfFunctions = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfFunctions); + if (!pAddressOfFunctions) + return CMemory(); + + // Get the names of the functions via adding the relative virtual address from the struct into our module base Address. + const DWORD* pAddressOfName = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfNames); + if (!pAddressOfName) + return CMemory(); + + // Get the ordinals of the functions via adding the relative virtual Address from the struct into our module base address. + DWORD* pAddressOfOrdinals = reinterpret_cast(m_pModuleBase + pImageExportDirectory->AddressOfNameOrdinals); + if (!pAddressOfOrdinals) + return CMemory(); + + for (DWORD i = 0; i < pImageExportDirectory->NumberOfNames; i++) // Iterate through all the functions. + { + // Get virtual relative Address of the function name. Then add module base Address to get the actual location. + const char* ExportFunctionName = reinterpret_cast(reinterpret_cast(m_pModuleBase + pAddressOfName[i])); + + if (strcmp(ExportFunctionName, szFunctionName) == 0) // Is this our wanted exported function? + { + // Get the function ordinal. Then grab the relative virtual address of our wanted function. Then add module base address so we get the actual location. + return CMemory(m_pModuleBase + pAddressOfFunctions[reinterpret_cast(pAddressOfOrdinals)[i]]); // Return as CMemory class. + } + } + return CMemory(); +} + +//----------------------------------------------------------------------------- +// Purpose: get the module section by name (example: '.rdata', '.text') +// Input : *szSectionName - +// Output : ModuleSections_t +//----------------------------------------------------------------------------- +CModule::ModuleSections_t CModule::GetSectionByName(const char* szSectionName) const +{ + for (const ModuleSections_t& section : m_ModuleSections) + { + if (section.m_SectionName.compare(szSectionName) == 0) + return section; + } + + return ModuleSections_t(); +} diff --git a/primedev/thirdparty/silver-bun/module.h b/primedev/thirdparty/silver-bun/module.h new file mode 100644 index 00000000..5683ee14 --- /dev/null +++ b/primedev/thirdparty/silver-bun/module.h @@ -0,0 +1,76 @@ +//===========================================================================// +// +// Purpose: Implementation of the CModule class. +// +// Original commit: https://github.com/IcePixelx/silver-bun/commit/72c74b455bf4d02b424096ad2f30cd65535f814c +// +//===========================================================================// +#pragma once + +#include "memaddr.h" +#include +#include +#include +#include +#include +#include +#include + +class CModule +{ +public: + struct ModuleSections_t + { + ModuleSections_t(void) = default; + ModuleSections_t(const char* sectionName, uintptr_t pSectionBase, size_t nSectionSize) : + m_SectionName(sectionName), m_pSectionBase(pSectionBase), m_nSectionSize(nSectionSize) {} + + inline bool IsSectionValid(void) const { return m_nSectionSize != 0; } + + std::string m_SectionName; // Name of section. + uintptr_t m_pSectionBase; // Start address of section. + size_t m_nSectionSize; // Size of section. + }; + + CModule(void) = default; + CModule(HMODULE hModule); + CModule(const char* szModuleName); + + void Init(); + void LoadSections(); + + CMemory Offset(const uintptr_t nOffset) const; + + CMemory FindPatternSIMD(const char* szPattern, const ModuleSections_t* moduleSection = nullptr) const; + CMemory FindString(const char* szString, const ptrdiff_t occurrence = 1, bool nullTerminator = false) const; + CMemory FindStringReadOnly(const char* szString, bool nullTerminator) const; + CMemory FindFreeDataPage(const size_t nSize) const; + + CMemory GetVirtualMethodTable(const char* szTableName, const size_t nRefIndex = 0); + CMemory GetImportedFunction(const char* szModuleName, const char* szFunctionName, const bool bGetFunctionReference) const; + CMemory GetExportedFunction(const char* szFunctionName) const; + ModuleSections_t GetSectionByName(const char* szSectionName) const; + + inline const std::vector& GetSections() const { return m_ModuleSections; } + inline uintptr_t GetModuleBase(void) const { return m_pModuleBase; } + inline DWORD GetModuleSize(void) const { return m_nModuleSize; } + inline const std::string& GetModuleName(void) const { return m_ModuleName; } + inline uintptr_t GetRVA(const uintptr_t nAddress) const { return (nAddress - GetModuleBase()); } + + IMAGE_NT_HEADERS64* m_pNTHeaders; + IMAGE_DOS_HEADER* m_pDOSHeader; + + ModuleSections_t m_ExecutableCode; + ModuleSections_t m_ExceptionTable; + ModuleSections_t m_RunTimeData; + ModuleSections_t m_ReadOnlyData; + +private: + CMemory FindPatternSIMD(const uint8_t* pPattern, const char* szMask, + const ModuleSections_t* moduleSection = nullptr, const size_t nOccurrence = 0) const; + + std::string m_ModuleName; + uintptr_t m_pModuleBase; + DWORD m_nModuleSize; + std::vector m_ModuleSections; +}; diff --git a/primedev/thirdparty/silver-bun/utils.cpp b/primedev/thirdparty/silver-bun/utils.cpp new file mode 100644 index 00000000..6f7b397e --- /dev/null +++ b/primedev/thirdparty/silver-bun/utils.cpp @@ -0,0 +1,127 @@ +#include "utils.h" + +namespace Utils +{ + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern to an array of bytes. Doesnt support wildcards + //---------------------------------------------------------------------------------------- + std::vector StringPatternToBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + vBytes.push_back(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16)); + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern with wildcards to an array of bytes. + //---------------------------------------------------------------------------------------- + std::vector PatternToBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + vBytes.push_back(-1); // Push the byte back as invalid. + } + else + { + vBytes.push_back(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16)); + } + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string pattern with wildcards to an array of bytes and mask. + //---------------------------------------------------------------------------------------- + std::pair, std::string> PatternToMaskedBytes(const char* szInput) + { + const char* pszPatternStart = const_cast(szInput); + const char* pszPatternEnd = pszPatternStart + strlen(szInput); + + std::vector vBytes; + std::string svMask; + + for (const char* pszCurrentByte = pszPatternStart; pszCurrentByte < pszPatternEnd; ++pszCurrentByte) + { + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; + if (*pszCurrentByte == '?') + { + ++pszCurrentByte; // Skip double wildcard. + } + vBytes.push_back(0); // Push the byte back as invalid. + svMask += '?'; + } + else + { + vBytes.push_back(uint8_t(strtoul(pszCurrentByte, const_cast(&pszCurrentByte), 16))); + svMask += 'x'; + } + } + return make_pair(vBytes, svMask); + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string to an array of bytes. + //---------------------------------------------------------------------------------------- + std::vector StringToBytes(const char* szInput, bool bNullTerminator) + { + const char* pszStringStart = const_cast(szInput); + const char* pszStringEnd = pszStringStart + strlen(szInput); + std::vector vBytes; + + for (const char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + } + + if (bNullTerminator) + { + vBytes.push_back('\0'); + } + return vBytes; + }; + + //---------------------------------------------------------------------------------------- + // Purpose: For converting a string to an array of masked bytes. + //---------------------------------------------------------------------------------------- + std::pair, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator) + { + const char* pszStringStart = const_cast(szInput); + const char* pszStringEnd = pszStringStart + strlen(szInput); + std::vector vBytes; + std::string svMask; + + for (const char* pszCurrentByte = pszStringStart; pszCurrentByte < pszStringEnd; ++pszCurrentByte) + { + // Dereference character and push back the byte. + vBytes.push_back(*pszCurrentByte); + svMask += 'x'; + } + + if (bNullTerminator) + { + vBytes.push_back(0x0); + svMask += 'x'; + } + return make_pair(vBytes, svMask); + }; +} diff --git a/primedev/thirdparty/silver-bun/utils.h b/primedev/thirdparty/silver-bun/utils.h new file mode 100644 index 00000000..15e43a92 --- /dev/null +++ b/primedev/thirdparty/silver-bun/utils.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Utils +{ + std::vector StringPatternToBytes(const char* szInput); + std::vector PatternToBytes(const char* szInput); + std::pair, std::string> PatternToMaskedBytes(const char* szInput); + std::vector StringToBytes(const char* szInput, bool bNullTerminator); + std::pair, std::string> StringToMaskedBytes(const char* szInput, bool bNullTerminator); +} + +typedef const unsigned char* rsig_t; diff --git a/thirdparty/spdlog/async.h b/primedev/thirdparty/spdlog/async.h similarity index 100% rename from thirdparty/spdlog/async.h rename to primedev/thirdparty/spdlog/async.h diff --git a/thirdparty/spdlog/async_logger-inl.h b/primedev/thirdparty/spdlog/async_logger-inl.h similarity index 100% rename from thirdparty/spdlog/async_logger-inl.h rename to primedev/thirdparty/spdlog/async_logger-inl.h diff --git a/thirdparty/spdlog/async_logger.h b/primedev/thirdparty/spdlog/async_logger.h similarity index 100% rename from thirdparty/spdlog/async_logger.h rename to primedev/thirdparty/spdlog/async_logger.h diff --git a/thirdparty/spdlog/cfg/argv.h b/primedev/thirdparty/spdlog/cfg/argv.h similarity index 100% rename from thirdparty/spdlog/cfg/argv.h rename to primedev/thirdparty/spdlog/cfg/argv.h diff --git a/thirdparty/spdlog/cfg/env.h b/primedev/thirdparty/spdlog/cfg/env.h similarity index 100% rename from thirdparty/spdlog/cfg/env.h rename to primedev/thirdparty/spdlog/cfg/env.h diff --git a/thirdparty/spdlog/cfg/helpers-inl.h b/primedev/thirdparty/spdlog/cfg/helpers-inl.h similarity index 100% rename from thirdparty/spdlog/cfg/helpers-inl.h rename to primedev/thirdparty/spdlog/cfg/helpers-inl.h diff --git a/thirdparty/spdlog/cfg/helpers.h b/primedev/thirdparty/spdlog/cfg/helpers.h similarity index 100% rename from thirdparty/spdlog/cfg/helpers.h rename to primedev/thirdparty/spdlog/cfg/helpers.h diff --git a/thirdparty/spdlog/common-inl.h b/primedev/thirdparty/spdlog/common-inl.h similarity index 100% rename from thirdparty/spdlog/common-inl.h rename to primedev/thirdparty/spdlog/common-inl.h diff --git a/thirdparty/spdlog/common.h b/primedev/thirdparty/spdlog/common.h similarity index 100% rename from thirdparty/spdlog/common.h rename to primedev/thirdparty/spdlog/common.h diff --git a/thirdparty/spdlog/details/backtracer-inl.h b/primedev/thirdparty/spdlog/details/backtracer-inl.h similarity index 100% rename from thirdparty/spdlog/details/backtracer-inl.h rename to primedev/thirdparty/spdlog/details/backtracer-inl.h diff --git a/thirdparty/spdlog/details/backtracer.h b/primedev/thirdparty/spdlog/details/backtracer.h similarity index 100% rename from thirdparty/spdlog/details/backtracer.h rename to primedev/thirdparty/spdlog/details/backtracer.h diff --git a/thirdparty/spdlog/details/circular_q.h b/primedev/thirdparty/spdlog/details/circular_q.h similarity index 100% rename from thirdparty/spdlog/details/circular_q.h rename to primedev/thirdparty/spdlog/details/circular_q.h diff --git a/thirdparty/spdlog/details/console_globals.h b/primedev/thirdparty/spdlog/details/console_globals.h similarity index 100% rename from thirdparty/spdlog/details/console_globals.h rename to primedev/thirdparty/spdlog/details/console_globals.h diff --git a/thirdparty/spdlog/details/file_helper-inl.h b/primedev/thirdparty/spdlog/details/file_helper-inl.h similarity index 100% rename from thirdparty/spdlog/details/file_helper-inl.h rename to primedev/thirdparty/spdlog/details/file_helper-inl.h diff --git a/thirdparty/spdlog/details/file_helper.h b/primedev/thirdparty/spdlog/details/file_helper.h similarity index 100% rename from thirdparty/spdlog/details/file_helper.h rename to primedev/thirdparty/spdlog/details/file_helper.h diff --git a/thirdparty/spdlog/details/fmt_helper.h b/primedev/thirdparty/spdlog/details/fmt_helper.h similarity index 100% rename from thirdparty/spdlog/details/fmt_helper.h rename to primedev/thirdparty/spdlog/details/fmt_helper.h diff --git a/thirdparty/spdlog/details/log_msg-inl.h b/primedev/thirdparty/spdlog/details/log_msg-inl.h similarity index 100% rename from thirdparty/spdlog/details/log_msg-inl.h rename to primedev/thirdparty/spdlog/details/log_msg-inl.h diff --git a/thirdparty/spdlog/details/log_msg.h b/primedev/thirdparty/spdlog/details/log_msg.h similarity index 100% rename from thirdparty/spdlog/details/log_msg.h rename to primedev/thirdparty/spdlog/details/log_msg.h diff --git a/thirdparty/spdlog/details/log_msg_buffer-inl.h b/primedev/thirdparty/spdlog/details/log_msg_buffer-inl.h similarity index 100% rename from thirdparty/spdlog/details/log_msg_buffer-inl.h rename to primedev/thirdparty/spdlog/details/log_msg_buffer-inl.h diff --git a/thirdparty/spdlog/details/log_msg_buffer.h b/primedev/thirdparty/spdlog/details/log_msg_buffer.h similarity index 100% rename from thirdparty/spdlog/details/log_msg_buffer.h rename to primedev/thirdparty/spdlog/details/log_msg_buffer.h diff --git a/thirdparty/spdlog/details/mpmc_blocking_q.h b/primedev/thirdparty/spdlog/details/mpmc_blocking_q.h similarity index 100% rename from thirdparty/spdlog/details/mpmc_blocking_q.h rename to primedev/thirdparty/spdlog/details/mpmc_blocking_q.h diff --git a/thirdparty/spdlog/details/null_mutex.h b/primedev/thirdparty/spdlog/details/null_mutex.h similarity index 100% rename from thirdparty/spdlog/details/null_mutex.h rename to primedev/thirdparty/spdlog/details/null_mutex.h diff --git a/thirdparty/spdlog/details/os-inl.h b/primedev/thirdparty/spdlog/details/os-inl.h similarity index 100% rename from thirdparty/spdlog/details/os-inl.h rename to primedev/thirdparty/spdlog/details/os-inl.h diff --git a/thirdparty/spdlog/details/os.h b/primedev/thirdparty/spdlog/details/os.h similarity index 100% rename from thirdparty/spdlog/details/os.h rename to primedev/thirdparty/spdlog/details/os.h diff --git a/thirdparty/spdlog/details/periodic_worker-inl.h b/primedev/thirdparty/spdlog/details/periodic_worker-inl.h similarity index 100% rename from thirdparty/spdlog/details/periodic_worker-inl.h rename to primedev/thirdparty/spdlog/details/periodic_worker-inl.h diff --git a/thirdparty/spdlog/details/periodic_worker.h b/primedev/thirdparty/spdlog/details/periodic_worker.h similarity index 100% rename from thirdparty/spdlog/details/periodic_worker.h rename to primedev/thirdparty/spdlog/details/periodic_worker.h diff --git a/thirdparty/spdlog/details/registry-inl.h b/primedev/thirdparty/spdlog/details/registry-inl.h similarity index 100% rename from thirdparty/spdlog/details/registry-inl.h rename to primedev/thirdparty/spdlog/details/registry-inl.h diff --git a/thirdparty/spdlog/details/registry.h b/primedev/thirdparty/spdlog/details/registry.h similarity index 100% rename from thirdparty/spdlog/details/registry.h rename to primedev/thirdparty/spdlog/details/registry.h diff --git a/thirdparty/spdlog/details/synchronous_factory.h b/primedev/thirdparty/spdlog/details/synchronous_factory.h similarity index 100% rename from thirdparty/spdlog/details/synchronous_factory.h rename to primedev/thirdparty/spdlog/details/synchronous_factory.h diff --git a/thirdparty/spdlog/details/tcp_client-windows.h b/primedev/thirdparty/spdlog/details/tcp_client-windows.h similarity index 100% rename from thirdparty/spdlog/details/tcp_client-windows.h rename to primedev/thirdparty/spdlog/details/tcp_client-windows.h diff --git a/thirdparty/spdlog/details/tcp_client.h b/primedev/thirdparty/spdlog/details/tcp_client.h similarity index 100% rename from thirdparty/spdlog/details/tcp_client.h rename to primedev/thirdparty/spdlog/details/tcp_client.h diff --git a/thirdparty/spdlog/details/thread_pool-inl.h b/primedev/thirdparty/spdlog/details/thread_pool-inl.h similarity index 100% rename from thirdparty/spdlog/details/thread_pool-inl.h rename to primedev/thirdparty/spdlog/details/thread_pool-inl.h diff --git a/thirdparty/spdlog/details/thread_pool.h b/primedev/thirdparty/spdlog/details/thread_pool.h similarity index 100% rename from thirdparty/spdlog/details/thread_pool.h rename to primedev/thirdparty/spdlog/details/thread_pool.h diff --git a/thirdparty/spdlog/details/windows_include.h b/primedev/thirdparty/spdlog/details/windows_include.h similarity index 100% rename from thirdparty/spdlog/details/windows_include.h rename to primedev/thirdparty/spdlog/details/windows_include.h diff --git a/thirdparty/spdlog/fmt/bin_to_hex.h b/primedev/thirdparty/spdlog/fmt/bin_to_hex.h similarity index 100% rename from thirdparty/spdlog/fmt/bin_to_hex.h rename to primedev/thirdparty/spdlog/fmt/bin_to_hex.h diff --git a/thirdparty/spdlog/fmt/bundled/LICENSE.rst b/primedev/thirdparty/spdlog/fmt/bundled/LICENSE.rst similarity index 100% rename from thirdparty/spdlog/fmt/bundled/LICENSE.rst rename to primedev/thirdparty/spdlog/fmt/bundled/LICENSE.rst diff --git a/thirdparty/spdlog/fmt/bundled/chrono.h b/primedev/thirdparty/spdlog/fmt/bundled/chrono.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/chrono.h rename to primedev/thirdparty/spdlog/fmt/bundled/chrono.h diff --git a/thirdparty/spdlog/fmt/bundled/color.h b/primedev/thirdparty/spdlog/fmt/bundled/color.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/color.h rename to primedev/thirdparty/spdlog/fmt/bundled/color.h diff --git a/thirdparty/spdlog/fmt/bundled/compile.h b/primedev/thirdparty/spdlog/fmt/bundled/compile.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/compile.h rename to primedev/thirdparty/spdlog/fmt/bundled/compile.h diff --git a/thirdparty/spdlog/fmt/bundled/core.h b/primedev/thirdparty/spdlog/fmt/bundled/core.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/core.h rename to primedev/thirdparty/spdlog/fmt/bundled/core.h diff --git a/thirdparty/spdlog/fmt/bundled/format-inl.h b/primedev/thirdparty/spdlog/fmt/bundled/format-inl.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/format-inl.h rename to primedev/thirdparty/spdlog/fmt/bundled/format-inl.h diff --git a/thirdparty/spdlog/fmt/bundled/format.h b/primedev/thirdparty/spdlog/fmt/bundled/format.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/format.h rename to primedev/thirdparty/spdlog/fmt/bundled/format.h diff --git a/thirdparty/spdlog/fmt/bundled/locale.h b/primedev/thirdparty/spdlog/fmt/bundled/locale.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/locale.h rename to primedev/thirdparty/spdlog/fmt/bundled/locale.h diff --git a/thirdparty/spdlog/fmt/bundled/os.h b/primedev/thirdparty/spdlog/fmt/bundled/os.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/os.h rename to primedev/thirdparty/spdlog/fmt/bundled/os.h diff --git a/thirdparty/spdlog/fmt/bundled/ostream.h b/primedev/thirdparty/spdlog/fmt/bundled/ostream.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/ostream.h rename to primedev/thirdparty/spdlog/fmt/bundled/ostream.h diff --git a/thirdparty/spdlog/fmt/bundled/posix.h b/primedev/thirdparty/spdlog/fmt/bundled/posix.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/posix.h rename to primedev/thirdparty/spdlog/fmt/bundled/posix.h diff --git a/thirdparty/spdlog/fmt/bundled/printf.h b/primedev/thirdparty/spdlog/fmt/bundled/printf.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/printf.h rename to primedev/thirdparty/spdlog/fmt/bundled/printf.h diff --git a/thirdparty/spdlog/fmt/bundled/ranges.h b/primedev/thirdparty/spdlog/fmt/bundled/ranges.h similarity index 100% rename from thirdparty/spdlog/fmt/bundled/ranges.h rename to primedev/thirdparty/spdlog/fmt/bundled/ranges.h diff --git a/thirdparty/spdlog/fmt/chrono.h b/primedev/thirdparty/spdlog/fmt/chrono.h similarity index 100% rename from thirdparty/spdlog/fmt/chrono.h rename to primedev/thirdparty/spdlog/fmt/chrono.h diff --git a/thirdparty/spdlog/fmt/fmt.h b/primedev/thirdparty/spdlog/fmt/fmt.h similarity index 100% rename from thirdparty/spdlog/fmt/fmt.h rename to primedev/thirdparty/spdlog/fmt/fmt.h diff --git a/thirdparty/spdlog/fmt/ostr.h b/primedev/thirdparty/spdlog/fmt/ostr.h similarity index 100% rename from thirdparty/spdlog/fmt/ostr.h rename to primedev/thirdparty/spdlog/fmt/ostr.h diff --git a/thirdparty/spdlog/formatter.h b/primedev/thirdparty/spdlog/formatter.h similarity index 100% rename from thirdparty/spdlog/formatter.h rename to primedev/thirdparty/spdlog/formatter.h diff --git a/thirdparty/spdlog/fwd.h b/primedev/thirdparty/spdlog/fwd.h similarity index 100% rename from thirdparty/spdlog/fwd.h rename to primedev/thirdparty/spdlog/fwd.h diff --git a/thirdparty/spdlog/logger-inl.h b/primedev/thirdparty/spdlog/logger-inl.h similarity index 100% rename from thirdparty/spdlog/logger-inl.h rename to primedev/thirdparty/spdlog/logger-inl.h diff --git a/thirdparty/spdlog/logger.h b/primedev/thirdparty/spdlog/logger.h similarity index 100% rename from thirdparty/spdlog/logger.h rename to primedev/thirdparty/spdlog/logger.h diff --git a/thirdparty/spdlog/pattern_formatter-inl.h b/primedev/thirdparty/spdlog/pattern_formatter-inl.h similarity index 100% rename from thirdparty/spdlog/pattern_formatter-inl.h rename to primedev/thirdparty/spdlog/pattern_formatter-inl.h diff --git a/thirdparty/spdlog/pattern_formatter.h b/primedev/thirdparty/spdlog/pattern_formatter.h similarity index 100% rename from thirdparty/spdlog/pattern_formatter.h rename to primedev/thirdparty/spdlog/pattern_formatter.h diff --git a/thirdparty/spdlog/sinks/android_sink.h b/primedev/thirdparty/spdlog/sinks/android_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/android_sink.h rename to primedev/thirdparty/spdlog/sinks/android_sink.h diff --git a/thirdparty/spdlog/sinks/ansicolor_sink-inl.h b/primedev/thirdparty/spdlog/sinks/ansicolor_sink-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/ansicolor_sink-inl.h rename to primedev/thirdparty/spdlog/sinks/ansicolor_sink-inl.h diff --git a/thirdparty/spdlog/sinks/ansicolor_sink.h b/primedev/thirdparty/spdlog/sinks/ansicolor_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/ansicolor_sink.h rename to primedev/thirdparty/spdlog/sinks/ansicolor_sink.h diff --git a/thirdparty/spdlog/sinks/base_sink-inl.h b/primedev/thirdparty/spdlog/sinks/base_sink-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/base_sink-inl.h rename to primedev/thirdparty/spdlog/sinks/base_sink-inl.h diff --git a/thirdparty/spdlog/sinks/base_sink.h b/primedev/thirdparty/spdlog/sinks/base_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/base_sink.h rename to primedev/thirdparty/spdlog/sinks/base_sink.h diff --git a/thirdparty/spdlog/sinks/basic_file_sink-inl.h b/primedev/thirdparty/spdlog/sinks/basic_file_sink-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/basic_file_sink-inl.h rename to primedev/thirdparty/spdlog/sinks/basic_file_sink-inl.h diff --git a/thirdparty/spdlog/sinks/basic_file_sink.h b/primedev/thirdparty/spdlog/sinks/basic_file_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/basic_file_sink.h rename to primedev/thirdparty/spdlog/sinks/basic_file_sink.h diff --git a/thirdparty/spdlog/sinks/daily_file_sink.h b/primedev/thirdparty/spdlog/sinks/daily_file_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/daily_file_sink.h rename to primedev/thirdparty/spdlog/sinks/daily_file_sink.h diff --git a/thirdparty/spdlog/sinks/dist_sink.h b/primedev/thirdparty/spdlog/sinks/dist_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/dist_sink.h rename to primedev/thirdparty/spdlog/sinks/dist_sink.h diff --git a/thirdparty/spdlog/sinks/dup_filter_sink.h b/primedev/thirdparty/spdlog/sinks/dup_filter_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/dup_filter_sink.h rename to primedev/thirdparty/spdlog/sinks/dup_filter_sink.h diff --git a/thirdparty/spdlog/sinks/hourly_file_sink.h b/primedev/thirdparty/spdlog/sinks/hourly_file_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/hourly_file_sink.h rename to primedev/thirdparty/spdlog/sinks/hourly_file_sink.h diff --git a/thirdparty/spdlog/sinks/msvc_sink.h b/primedev/thirdparty/spdlog/sinks/msvc_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/msvc_sink.h rename to primedev/thirdparty/spdlog/sinks/msvc_sink.h diff --git a/thirdparty/spdlog/sinks/null_sink.h b/primedev/thirdparty/spdlog/sinks/null_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/null_sink.h rename to primedev/thirdparty/spdlog/sinks/null_sink.h diff --git a/thirdparty/spdlog/sinks/ostream_sink.h b/primedev/thirdparty/spdlog/sinks/ostream_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/ostream_sink.h rename to primedev/thirdparty/spdlog/sinks/ostream_sink.h diff --git a/thirdparty/spdlog/sinks/ringbuffer_sink.h b/primedev/thirdparty/spdlog/sinks/ringbuffer_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/ringbuffer_sink.h rename to primedev/thirdparty/spdlog/sinks/ringbuffer_sink.h diff --git a/thirdparty/spdlog/sinks/rotating_file_sink-inl.h b/primedev/thirdparty/spdlog/sinks/rotating_file_sink-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/rotating_file_sink-inl.h rename to primedev/thirdparty/spdlog/sinks/rotating_file_sink-inl.h diff --git a/thirdparty/spdlog/sinks/rotating_file_sink.h b/primedev/thirdparty/spdlog/sinks/rotating_file_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/rotating_file_sink.h rename to primedev/thirdparty/spdlog/sinks/rotating_file_sink.h diff --git a/thirdparty/spdlog/sinks/sink-inl.h b/primedev/thirdparty/spdlog/sinks/sink-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/sink-inl.h rename to primedev/thirdparty/spdlog/sinks/sink-inl.h diff --git a/thirdparty/spdlog/sinks/sink.h b/primedev/thirdparty/spdlog/sinks/sink.h similarity index 100% rename from thirdparty/spdlog/sinks/sink.h rename to primedev/thirdparty/spdlog/sinks/sink.h diff --git a/thirdparty/spdlog/sinks/stdout_color_sinks-inl.h b/primedev/thirdparty/spdlog/sinks/stdout_color_sinks-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/stdout_color_sinks-inl.h rename to primedev/thirdparty/spdlog/sinks/stdout_color_sinks-inl.h diff --git a/thirdparty/spdlog/sinks/stdout_color_sinks.h b/primedev/thirdparty/spdlog/sinks/stdout_color_sinks.h similarity index 100% rename from thirdparty/spdlog/sinks/stdout_color_sinks.h rename to primedev/thirdparty/spdlog/sinks/stdout_color_sinks.h diff --git a/thirdparty/spdlog/sinks/stdout_sinks-inl.h b/primedev/thirdparty/spdlog/sinks/stdout_sinks-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/stdout_sinks-inl.h rename to primedev/thirdparty/spdlog/sinks/stdout_sinks-inl.h diff --git a/thirdparty/spdlog/sinks/stdout_sinks.h b/primedev/thirdparty/spdlog/sinks/stdout_sinks.h similarity index 100% rename from thirdparty/spdlog/sinks/stdout_sinks.h rename to primedev/thirdparty/spdlog/sinks/stdout_sinks.h diff --git a/thirdparty/spdlog/sinks/syslog_sink.h b/primedev/thirdparty/spdlog/sinks/syslog_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/syslog_sink.h rename to primedev/thirdparty/spdlog/sinks/syslog_sink.h diff --git a/thirdparty/spdlog/sinks/systemd_sink.h b/primedev/thirdparty/spdlog/sinks/systemd_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/systemd_sink.h rename to primedev/thirdparty/spdlog/sinks/systemd_sink.h diff --git a/thirdparty/spdlog/sinks/tcp_sink.h b/primedev/thirdparty/spdlog/sinks/tcp_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/tcp_sink.h rename to primedev/thirdparty/spdlog/sinks/tcp_sink.h diff --git a/thirdparty/spdlog/sinks/win_eventlog_sink.h b/primedev/thirdparty/spdlog/sinks/win_eventlog_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/win_eventlog_sink.h rename to primedev/thirdparty/spdlog/sinks/win_eventlog_sink.h diff --git a/thirdparty/spdlog/sinks/wincolor_sink-inl.h b/primedev/thirdparty/spdlog/sinks/wincolor_sink-inl.h similarity index 100% rename from thirdparty/spdlog/sinks/wincolor_sink-inl.h rename to primedev/thirdparty/spdlog/sinks/wincolor_sink-inl.h diff --git a/thirdparty/spdlog/sinks/wincolor_sink.h b/primedev/thirdparty/spdlog/sinks/wincolor_sink.h similarity index 100% rename from thirdparty/spdlog/sinks/wincolor_sink.h rename to primedev/thirdparty/spdlog/sinks/wincolor_sink.h diff --git a/thirdparty/spdlog/spdlog-inl.h b/primedev/thirdparty/spdlog/spdlog-inl.h similarity index 100% rename from thirdparty/spdlog/spdlog-inl.h rename to primedev/thirdparty/spdlog/spdlog-inl.h diff --git a/thirdparty/spdlog/spdlog.h b/primedev/thirdparty/spdlog/spdlog.h similarity index 100% rename from thirdparty/spdlog/spdlog.h rename to primedev/thirdparty/spdlog/spdlog.h diff --git a/thirdparty/spdlog/stopwatch.h b/primedev/thirdparty/spdlog/stopwatch.h similarity index 100% rename from thirdparty/spdlog/stopwatch.h rename to primedev/thirdparty/spdlog/stopwatch.h diff --git a/thirdparty/spdlog/tweakme.h b/primedev/thirdparty/spdlog/tweakme.h similarity index 100% rename from thirdparty/spdlog/tweakme.h rename to primedev/thirdparty/spdlog/tweakme.h diff --git a/thirdparty/spdlog/version.h b/primedev/thirdparty/spdlog/version.h similarity index 100% rename from thirdparty/spdlog/version.h rename to primedev/thirdparty/spdlog/version.h diff --git a/primedev/toolframework/itoolentity.h b/primedev/toolframework/itoolentity.h new file mode 100644 index 00000000..332004f2 --- /dev/null +++ b/primedev/toolframework/itoolentity.h @@ -0,0 +1,46 @@ +#pragma once + +#include "core/math/vector.h" + +class IClientTools // : public IBaseInterface +{ +public: + virtual void sub_1805E4960() = 0; + virtual void sub_1805E4B10() = 0; + virtual void sub_1805E4C50() = 0; + virtual void sub_1805E5670() = 0; + virtual void sub_1805E66C0() = 0; + virtual void sub_1805E5910() = 0; + virtual void sub_1805E59A0() = 0; + virtual void sub_1805E6B10() = 0; + virtual void sub_1805E8580() = 0; + virtual void sub_1805E59D0() = 0; + virtual void sub_1805E57B0() = 0; + virtual void sub_1805E5860() = 0; + virtual void sub_1805E55A0() = 0; + virtual void sub_1805E49C0() = 0; + virtual void sub_1805E7580() = 0; + virtual void sub_1805E86A0() = 0; + virtual void sub_1805E69B0() = 0; + virtual void sub_1805E4D70() = 0; // IClientTools::DrawSprite + virtual void* GetLocalPlayer() = 0; // return type unknown probably CBasePlayer* + virtual bool GetLocalPlayerEyePosition(Vector3& org, QAngle& ang, float& fov) = 0; + virtual void sub_1805E5960() = 0; + virtual void sub_1805E5650() = 0; + virtual void sub_1805E5920() = 0; + virtual void sub_1805E64E0() = 0; + virtual void sub_1805E63C0() = 0; + virtual void sub_1805E64C0() = 0; + virtual void sub_1805E6520() = 0; + virtual void sub_1805E6770() = 0; + virtual void sub_1805E67B0() = 0; + virtual void sub_1805E67F0() = 0; + virtual void sub_1805E66A0() = 0; + virtual void sub_1805E6500() = 0; + virtual void sub_1805E63B0() = 0; + virtual void sub_1805E54C0() = 0; + virtual void sub_1805E53E0() = 0; + virtual void sub_1805E6D70() = 0; + virtual void sub_1805E50D0() = 0; + virtual void sub_1805E65C0() = 0; +}; diff --git a/NorthstarDLL/util/printcommands.cpp b/primedev/util/printcommands.cpp similarity index 87% rename from NorthstarDLL/util/printcommands.cpp rename to primedev/util/printcommands.cpp index 7c915318..03ccce5e 100644 --- a/NorthstarDLL/util/printcommands.cpp +++ b/primedev/util/printcommands.cpp @@ -12,7 +12,7 @@ void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name) } // temp because command->IsCommand does not currently work - ConVar* cvar = R2::g_pCVar->FindVar(command->m_pszName); + ConVar* cvar = g_pCVar->FindVar(command->m_pszName); // build string for flags if not FCVAR_NONE std::string flagString; @@ -52,12 +52,12 @@ void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name) void TryPrintCvarHelpForCommand(const char* pCommand) { // try to display help text for an inputted command string from the console - int pCommandLen = strlen(pCommand); + size_t pCommandLen = strlen(pCommand); char* pCvarStr = new char[pCommandLen]; strcpy(pCvarStr, pCommand); // trim whitespace from right - for (int i = pCommandLen - 1; i; i--) + for (size_t i = pCommandLen - 1; i; i--) { if (isspace(pCvarStr[i])) pCvarStr[i] = '\0'; @@ -66,7 +66,7 @@ void TryPrintCvarHelpForCommand(const char* pCommand) } // check if we're inputting a cvar, but not setting it at all - ConVar* cvar = R2::g_pCVar->FindVar(pCvarStr); + ConVar* cvar = g_pCVar->FindVar(pCvarStr); if (cvar) PrintCommandHelpDialogue(&cvar->m_ConCommandBase, pCvarStr); @@ -81,7 +81,7 @@ void ConCommand_help(const CCommand& arg) return; } - PrintCommandHelpDialogue(R2::g_pCVar->FindCommandBase(arg.Arg(1)), arg.Arg(1)); + PrintCommandHelpDialogue(g_pCVar->FindCommandBase(arg.Arg(1)), arg.Arg(1)); } void ConCommand_find(const CCommand& arg) @@ -96,7 +96,7 @@ void ConCommand_find(const CCommand& arg) char pTempSearchTerm[256]; ConCommandBase* var; - CCVarIteratorInternal* itint = R2::g_pCVar->FactoryInternalIterator(); + CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); std::map sorted; for (itint->SetFirst(); itint->IsValid(); itint->Next()) { @@ -165,7 +165,7 @@ void ConCommand_findflags(const CCommand& arg) } ConCommandBase* var; - CCVarIteratorInternal* itint = R2::g_pCVar->FactoryInternalIterator(); + CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); std::map sorted; for (itint->SetFirst(); itint->IsValid(); itint->Next()) { @@ -188,8 +188,9 @@ void ConCommand_findflags(const CCommand& arg) void ConCommand_list(const CCommand& arg) { + NOTE_UNUSED(arg); ConCommandBase* var; - CCVarIteratorInternal* itint = R2::g_pCVar->FactoryInternalIterator(); + CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); std::map sorted; for (itint->SetFirst(); itint->IsValid(); itint->Next()) { @@ -210,7 +211,8 @@ void ConCommand_list(const CCommand& arg) void ConCommand_differences(const CCommand& arg) { - CCVarIteratorInternal* itint = R2::g_pCVar->FactoryInternalIterator(); + NOTE_UNUSED(arg); + CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); std::map sorted; for (itint->SetFirst(); itint->IsValid(); itint->Next()) @@ -225,7 +227,7 @@ void ConCommand_differences(const CCommand& arg) for (auto& map : sorted) { - ConVar* cvar = R2::g_pCVar->FindVar(map.second->m_pszName); + ConVar* cvar = g_pCVar->FindVar(map.second->m_pszName); if (!cvar) { @@ -267,19 +269,19 @@ void InitialiseCommandPrint() // these commands already exist, so we need to modify the preexisting command to use our func instead // and clear the flags also - ConCommand* helpCommand = R2::g_pCVar->FindCommand("help"); + ConCommand* helpCommand = g_pCVar->FindCommand("help"); helpCommand->m_nFlags = FCVAR_NONE; helpCommand->m_pCommandCallback = ConCommand_help; - ConCommand* findCommand = R2::g_pCVar->FindCommand("convar_findByFlags"); + ConCommand* findCommand = g_pCVar->FindCommand("convar_findByFlags"); findCommand->m_nFlags = FCVAR_NONE; findCommand->m_pCommandCallback = ConCommand_findflags; - ConCommand* listCommand = R2::g_pCVar->FindCommand("convar_list"); + ConCommand* listCommand = g_pCVar->FindCommand("convar_list"); listCommand->m_nFlags = FCVAR_NONE; listCommand->m_pCommandCallback = ConCommand_list; - ConCommand* diffCommand = R2::g_pCVar->FindCommand("convar_differences"); + ConCommand* diffCommand = g_pCVar->FindCommand("convar_differences"); diffCommand->m_nFlags = FCVAR_NONE; diffCommand->m_pCommandCallback = ConCommand_differences; } diff --git a/NorthstarDLL/util/printcommands.h b/primedev/util/printcommands.h similarity index 100% rename from NorthstarDLL/util/printcommands.h rename to primedev/util/printcommands.h diff --git a/NorthstarDLL/util/printmaps.cpp b/primedev/util/printmaps.cpp similarity index 76% rename from NorthstarDLL/util/printmaps.cpp rename to primedev/util/printmaps.cpp index 5b406c4c..28325db9 100644 --- a/NorthstarDLL/util/printmaps.cpp +++ b/primedev/util/printmaps.cpp @@ -31,15 +31,21 @@ struct MapVPKInfo // our current list of maps in the game std::vector vMapList; +typedef void (*Host_Map_helperType)(const CCommand&, void*); +typedef void (*Host_Changelevel_fType)(const CCommand&); + +Host_Map_helperType Host_Map_helper; +Host_Changelevel_fType Host_Changelevel_f; + void RefreshMapList() { // Only update the maps list every 10 seconds max to we avoid constantly reading fs static double fLastRefresh = -999; - if (fLastRefresh + 10.0 > R2::g_pGlobals->m_flRealTime) + if (fLastRefresh + 10.0 > g_pGlobals->m_flRealTime) return; - fLastRefresh = R2::g_pGlobals->m_flRealTime; + fLastRefresh = g_pGlobals->m_flRealTime; // Rebuild map list vMapList.clear(); @@ -104,7 +110,7 @@ void RefreshMapList() } // get maps in game dir - std::string gameDir = fmt::format("{}/maps", R2::g_pModName); + std::string gameDir = fmt::format("{}/maps", g_pModName); if (!std::filesystem::exists(gameDir)) { return; @@ -130,9 +136,9 @@ int, __fastcall, (const char *const cmdname, const char *const partial, char com RefreshMapList(); // use a custom autocomplete func for all map loading commands - const int cmdLength = strlen(cmdname); + const size_t cmdLength = strlen(cmdname); const char* query = partial + cmdLength; - const int queryLength = strlen(query); + const size_t queryLength = strlen(query); int numMaps = 0; for (int i = 0; i < vMapList.size() && numMaps < COMMAND_COMPLETION_MAXITEMS; i++) @@ -188,10 +194,46 @@ void ConCommand_maps(const CCommand& args) spdlog::info("({}) {}", PrintMapSource.at(map.source), map.name); } +// clang-format off +AUTOHOOK(Host_Map_f, engine.dll + 0x15B340, void, __fastcall, (const CCommand& args)) +// clang-format on +{ + RefreshMapList(); + + if (args.ArgC() > 2) + { + spdlog::warn("Map load failed: too many arguments provided"); + return; + } + else if ( + args.ArgC() == 2 && + std::find_if(vMapList.begin(), vMapList.end(), [&](MapVPKInfo map) -> bool { return map.name == args.Arg(1); }) == vMapList.end()) + { + spdlog::warn("Map load failed: {} not found or invalid", args.Arg(1)); + return; + } + else if (args.ArgC() == 1) + { + spdlog::warn("Map load failed: no map name provided"); + return; + } + + if (*g_pServerState >= server_state_t::ss_active) + return Host_Changelevel_f(args); + else + return Host_Map_helper(args, nullptr); +} + void InitialiseMapsPrint() { AUTOHOOK_DISPATCH() - ConCommand* mapsCommand = R2::g_pCVar->FindCommand("maps"); + ConCommand* mapsCommand = g_pCVar->FindCommand("maps"); mapsCommand->m_pCommandCallback = ConCommand_maps; } + +ON_DLL_LOAD("engine.dll", Host_Map_f, (CModule module)) +{ + Host_Map_helper = module.Offset(0x15AEF0).RCast(); + Host_Changelevel_f = module.Offset(0x15AAD0).RCast(); +} diff --git a/NorthstarDLL/util/printmaps.h b/primedev/util/printmaps.h similarity index 100% rename from NorthstarDLL/util/printmaps.h rename to primedev/util/printmaps.h diff --git a/NorthstarDLL/util/utils.cpp b/primedev/util/utils.cpp similarity index 95% rename from NorthstarDLL/util/utils.cpp rename to primedev/util/utils.cpp index 2c9dc85a..c3f90cfa 100644 --- a/NorthstarDLL/util/utils.cpp +++ b/primedev/util/utils.cpp @@ -20,7 +20,7 @@ bool skip_valid_ansi_csi_sgr(char*& str) return true; } -void NS::Utils::RemoveAsciiControlSequences(char* str, bool allow_color_codes) +void RemoveAsciiControlSequences(char* str, bool allow_color_codes) { for (char *pc = str, c = *pc; c = *pc; pc++) { diff --git a/primedev/util/utils.h b/primedev/util/utils.h new file mode 100644 index 00000000..c8cbc7e8 --- /dev/null +++ b/primedev/util/utils.h @@ -0,0 +1,25 @@ +#pragma once + +void RemoveAsciiControlSequences(char* str, bool allow_color_codes); + +template class ScopeGuard +{ +public: + auto operator=(ScopeGuard&) = delete; + ScopeGuard(ScopeGuard&) = delete; + + ScopeGuard(T callback) : m_callback(callback) {} + ~ScopeGuard() + { + if (!m_dismissed) + m_callback(); + } + void Dismiss() + { + m_dismissed = true; + } + +private: + bool m_dismissed = false; + T m_callback; +}; diff --git a/NorthstarDLL/util/version.cpp b/primedev/util/version.cpp similarity index 100% rename from NorthstarDLL/util/version.cpp rename to primedev/util/version.cpp diff --git a/NorthstarDLL/util/version.h b/primedev/util/version.h similarity index 100% rename from NorthstarDLL/util/version.h rename to primedev/util/version.h diff --git a/NorthstarDLL/util/wininfo.cpp b/primedev/util/wininfo.cpp similarity index 100% rename from NorthstarDLL/util/wininfo.cpp rename to primedev/util/wininfo.cpp diff --git a/NorthstarDLL/util/wininfo.h b/primedev/util/wininfo.h similarity index 100% rename from NorthstarDLL/util/wininfo.h rename to primedev/util/wininfo.h diff --git a/primedev/wsockproxy/CMakeLists.txt b/primedev/wsockproxy/CMakeLists.txt new file mode 100644 index 00000000..0dbac745 --- /dev/null +++ b/primedev/wsockproxy/CMakeLists.txt @@ -0,0 +1,48 @@ +# loader_wsock32_proxy + +find_package(minhook REQUIRED) + +add_library( + loader_wsock32_proxy SHARED + "dllmain.cpp" + "loader.cpp" + "loader.h" + "wsock32.def" + ) + +target_link_libraries( + loader_wsock32_proxy + PRIVATE minhook + mswsock.lib + ws2_32.lib + ShLwApi.lib + imagehlp.lib + dbghelp.lib + kernel32.lib + user32.lib + gdi32.lib + winspool.lib + comdlg32.lib + advapi32.lib + shell32.lib + ole32.lib + oleaut32.lib + uuid.lib + odbc32.lib + odbccp32.lib + ) + +target_precompile_headers( + loader_wsock32_proxy + PRIVATE + pch.h + ) + +target_compile_definitions(loader_wsock32_proxy PRIVATE UNICODE _UNICODE) + +set_target_properties( + loader_wsock32_proxy + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR}/bin/x64_retail + OUTPUT_NAME wsock32 + LINK_FLAGS "/MANIFEST:NO /DEBUG" + ) diff --git a/primedev/wsockproxy/dllmain.cpp b/primedev/wsockproxy/dllmain.cpp new file mode 100644 index 00000000..5a606e45 --- /dev/null +++ b/primedev/wsockproxy/dllmain.cpp @@ -0,0 +1,73 @@ +#include "loader.h" + +#include + +FARPROC p[73]; +HMODULE hL = 0; + +static wchar_t wsockPath[4096]; + +BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID) +{ + if (reason == DLL_PROCESS_ATTACH) + { + // Tell the OS we don't need to know about threads + DisableThreadLibraryCalls(hInst); + + if (!ProvisionNorthstar()) // does not call InitialiseNorthstar yet, will do it on LauncherMain hook + return true; + + GetSystemDirectoryW(wsockPath, 4096); + swprintf_s(wsockPath, 4096, L"%s\\wsock32.dll", wsockPath); + + hL = LoadLibraryExW(wsockPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hL) + { + LibraryLoadError(GetLastError(), L"wsock32.dll", wsockPath); + return false; + } + + // load the functions to proxy + // it's only some of them, because in case of wsock32 most of the functions can actually be natively redirected + // (see wsock32.def and https://source.winehq.org/WineAPI/wsock32.html) + p[1] = GetProcAddress(hL, "EnumProtocolsA"); + p[2] = GetProcAddress(hL, "EnumProtocolsW"); + p[4] = GetProcAddress(hL, "GetAddressByNameA"); + p[5] = GetProcAddress(hL, "GetAddressByNameW"); + p[17] = GetProcAddress(hL, "WEP"); + p[30] = GetProcAddress(hL, "WSARecvEx"); + p[36] = GetProcAddress(hL, "__WSAFDIsSet"); + p[45] = GetProcAddress(hL, "getnetbyname"); + p[52] = GetProcAddress(hL, "getsockopt"); + p[56] = GetProcAddress(hL, "inet_network"); + p[67] = GetProcAddress(hL, "s_perror"); + p[72] = GetProcAddress(hL, "setsockopt"); + } + + if (reason == DLL_PROCESS_DETACH) + { + FreeLibrary(hL); + return true; + } + + return true; +} + +extern "C" +{ + FARPROC PA = NULL; + int RunASM(); + + void PROXY_EnumProtocolsA() { p[1](); } + void PROXY_EnumProtocolsW() { p[2](); } + void PROXY_GetAddressByNameA() { p[4](); } + void PROXY_GetAddressByNameW() { p[5](); } + void PROXY_WEP() { p[17](); } + void PROXY_WSARecvEx() { p[30](); } + void PROXY___WSAFDIsSet() { p[36](); } + void PROXY_getnetbyname() { p[45](); } + void PROXY_getsockopt() { p[52](); } + void PROXY_inet_network() { p[56](); } + void PROXY_s_perror() { p[67](); } + void PROXY_setsockopt() { p[72](); } +} diff --git a/loader_wsock32_proxy/loader.cpp b/primedev/wsockproxy/loader.cpp similarity index 76% rename from loader_wsock32_proxy/loader.cpp rename to primedev/wsockproxy/loader.cpp index 3e46c1a6..a3abf11c 100644 --- a/loader_wsock32_proxy/loader.cpp +++ b/primedev/wsockproxy/loader.cpp @@ -1,4 +1,5 @@ #include "loader.h" +#include #include #include #include @@ -8,6 +9,21 @@ namespace fs = std::filesystem; +static wchar_t northstarPath[8192]; +static wchar_t exePath[4096]; + +bool GetExePathWide(wchar_t* dest, DWORD destSize) +{ + if (!dest) + return NULL; + if (destSize < MAX_PATH) + return NULL; + + DWORD length = GetModuleFileNameW(NULL, dest, destSize); + return length && PathRemoveFileSpecW(dest); +} + + void LibraryLoadError(DWORD dwMessageId, const wchar_t* libName, const wchar_t* location) { char text[4096]; @@ -54,7 +70,7 @@ bool LoadNorthstar() std::string cla = std::string(clachar); if (strncmp(cla.substr(9, 1).c_str(), "\"", 1)) { - int space = cla.find(" "); + size_t space = cla.find(" "); std::string dirname = cla.substr(9, space - 9); std::cout << "[*] Found profile in command line arguments: " << dirname << std::endl; strProfile = dirname.c_str(); @@ -62,8 +78,8 @@ bool LoadNorthstar() else { std::string quote = "\""; - int quote1 = cla.find(quote); - int quote2 = (cla.substr(quote1 + 1)).find(quote); + size_t quote1 = cla.find(quote); + size_t quote2 = (cla.substr(quote1 + 1)).find(quote); std::string dirname = cla.substr(quote1 + 1, quote2); std::cout << "[*] Found profile in command line arguments: " << dirname << std::endl; strProfile = dirname; @@ -75,22 +91,30 @@ bool LoadNorthstar() strProfile = "R2Northstar"; } - wchar_t buffer[8192]; + if (!GetExePathWide(exePath, 4096)) + { + MessageBoxA( + GetForegroundWindow(), + "Failed getting game directory.\nThe game cannot continue and has to exit.", + "Northstar Wsock32 Proxy Error", + 0); + return true; + } // Check if "Northstar.dll" exists in profile directory, if it doesnt fall back to root - swprintf_s(buffer, L"%s\\%s\\Northstar.dll", exePath, std::wstring(strProfile.begin(), strProfile.end()).c_str()); + swprintf_s(northstarPath, L"%s\\%s\\Northstar.dll", exePath, std::wstring(strProfile.begin(), strProfile.end()).c_str()); - if (!fs::exists(fs::path(buffer))) - swprintf_s(buffer, L"%s\\Northstar.dll", exePath); + if (!fs::exists(fs::path(northstarPath))) + swprintf_s(northstarPath, L"%s\\Northstar.dll", exePath); - std::wcout << L"[*] Using: " << buffer << std::endl; + std::wcout << L"[*] Using: " << northstarPath << std::endl; - HMODULE hHookModule = LoadLibraryExW(buffer, 0, 8u); + HMODULE hHookModule = LoadLibraryExW(northstarPath, 0, 8u); if (hHookModule) Hook_Init = GetProcAddress(hHookModule, "InitialiseNorthstar"); if (!hHookModule || Hook_Init == nullptr) { - LibraryLoadError(GetLastError(), L"Northstar.dll", buffer); + LibraryLoadError(GetLastError(), L"Northstar.dll", northstarPath); return false; } } diff --git a/loader_wsock32_proxy/loader.h b/primedev/wsockproxy/loader.h similarity index 100% rename from loader_wsock32_proxy/loader.h rename to primedev/wsockproxy/loader.h diff --git a/loader_wsock32_proxy/pch.h b/primedev/wsockproxy/pch.h similarity index 100% rename from loader_wsock32_proxy/pch.h rename to primedev/wsockproxy/pch.h diff --git a/loader_wsock32_proxy/wsock32.def b/primedev/wsockproxy/wsock32.def similarity index 100% rename from loader_wsock32_proxy/wsock32.def rename to primedev/wsockproxy/wsock32.def