Merge pull request #680 from catornot/bob-pr-fix

Resolves the merge conflicts for #408
This commit is contained in:
GeckoEidechse 2024-03-12 13:01:38 +01:00 committed by GitHub
commit 880d489803
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
355 changed files with 5520 additions and 3342 deletions

View File

@ -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

26
.cmake-format.json Normal file
View File

@ -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": {}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

6
.gitmodules vendored
View File

@ -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

View File

@ -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)

View File

@ -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"
)

View File

@ -1,8 +0,0 @@
public Audio_GetParentEvent
.code
Audio_GetParentEvent proc
mov rax, r12
ret
Audio_GetParentEvent endp
end

View File

@ -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

View File

@ -1,47 +0,0 @@
#include <cmath>
#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;
}
};

View File

@ -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<uintptr_t>(pAddress)) {}
// operators
CMemoryAddress::operator uintptr_t() const
{
return m_nAddress;
}
CMemoryAddress::operator void*() const
{
return reinterpret_cast<void*>(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<uintptr_t*>(ret);
return CMemoryAddress(ret);
}
// patching
void CMemoryAddress::Patch(const uint8_t* pBytes, const size_t nSize)
{
if (nSize)
WriteProcessMemory(GetCurrentProcess(), reinterpret_cast<LPVOID>(m_nAddress), pBytes, nSize, NULL);
}
void CMemoryAddress::Patch(const std::initializer_list<uint8_t> 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<uint8_t> HexBytesToString(const char* pHexString)
{
std::vector<uint8_t> 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<uint8_t> 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<LPCVOID>(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<size_t>(mInfo.SizeOfImage);
m_pModuleBase = reinterpret_cast<uintptr_t>(mInfo.lpBaseOfDll);
m_nAddress = m_pModuleBase;
if (!m_nModuleSize || !m_pModuleBase)
return;
m_pDOSHeader = reinterpret_cast<IMAGE_DOS_HEADER*>(m_pModuleBase);
m_pNTHeaders = reinterpret_cast<IMAGE_NT_HEADERS64*>(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<const char*>(hCurrentSection.Name)),
static_cast<uintptr_t>(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<uintptr_t>(GetProcAddress(reinterpret_cast<HMODULE>(m_nAddress), pExportName)));
}
CMemoryAddress CModule::FindPattern(const uint8_t* pPattern, const char* pMask)
{
if (!m_ExecutableCode.IsSectionValid())
return CMemoryAddress();
uint64_t nBase = static_cast<uint64_t>(m_ExecutableCode.m_pSectionBase);
uint64_t nSize = static_cast<uint64_t>(m_ExecutableCode.m_nSectionSize);
const uint8_t* pData = reinterpret_cast<uint8_t*>(nBase);
const uint8_t* pEnd = pData + static_cast<uint32_t>(nSize) - strlen(pMask);
int nMasks[64]; // 64*16 = enough masks for 1024 bytes.
int iNumMasks = static_cast<int>(ceil(static_cast<float>(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<LONG*>(&nMasks[i]), j);
}
}
}
__m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pPattern));
__m128i xmm2, xmm3, msks;
for (; pData != pEnd; _mm_prefetch(reinterpret_cast<const char*>(++pData + 64), _MM_HINT_NTA))
{
if (pPattern[0] == pData[0])
{
xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pData));
msks = _mm_cmpeq_epi8(xmm1, xmm2);
if ((_mm_movemask_epi8(msks) & nMasks[0]) == nMasks[0])
{
for (uintptr_t i = 1; i < static_cast<uintptr_t>(iNumMasks); ++i)
{
xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((pData + i * 16)));
xmm3 = _mm_loadu_si128(reinterpret_cast<const __m128i*>((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<uint8_t*>(pData));
}
}
else
goto CONTINUE;
}
return CMemoryAddress((&*(const_cast<uint8_t*>(pData))));
}
}
CONTINUE:;
}
return CMemoryAddress();
}
inline std::pair<std::vector<uint8_t>, std::string> MaskedBytesFromPattern(const char* pPatternString)
{
std::vector<uint8_t> 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());
}

View File

@ -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 <typename T> T RCast()
{
return reinterpret_cast<T>(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<uint8_t> 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<ModuleSections_t> 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);
};

View File

@ -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

View File

@ -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<CreateGlobalMemAllocType>(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::ErrorType>();
Tier0::CommandLine = module.GetExport("CommandLine").RCast<Tier0::CommandLineType>();
Tier0::Plat_FloatTime = module.GetExport("Plat_FloatTime").RCast<Tier0::Plat_FloatTimeType>();
Tier0::ThreadInServerFrameThread = module.GetExport("ThreadInServerFrameThread").RCast<Tier0::ThreadInServerFrameThreadType>();
}

View File

@ -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();

View File

@ -1,3 +0,0 @@
#pragma once
extern "C" __declspec(dllexport) bool InitialiseNorthstar();

View File

@ -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

View File

@ -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

View File

@ -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;
};
/// <summary> 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.
/// </summary>
// 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)();

View File

@ -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 <filesystem>
#define EXPORT extern "C" __declspec(dllexport)
AUTOHOOK_INIT()
PluginCommunicationHandler* g_pPluginCommunicationhandler;
static PluginDataRequest storedRequest {PluginDataRequestType::END, (PluginRespondDataCallable) nullptr};
void PluginCommunicationHandler::RunFrame()
{
std::lock_guard<std::mutex> lock(requestMutex);
if (!requestQueue.empty())
{
storedRequest = requestQueue.front();
switch (storedRequest.type)
{
default:
spdlog::error("{} was called with invalid request type '{}'", __FUNCTION__, static_cast<int>(storedRequest.type));
}
requestQueue.pop();
}
}
void PluginCommunicationHandler::PushRequest(PluginDataRequestType type, PluginRespondDataCallable func)
{
std::lock_guard<std::mutex> 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);
}

View File

@ -1,40 +0,0 @@
#pragma once
#include "plugin_abi.h"
#include <queue>
#include <mutex>
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<PluginDataRequest> requestQueue = {};
std::mutex requestMutex;
PluginEngineData m_sEngineData {};
};
void InformPluginsDLLLoad(fs::path dllPath, void* address);
extern PluginCommunicationHandler* g_pPluginCommunicationhandler;

View File

@ -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 <optional>
#include <regex>
#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<Plugin> 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<ColoredLogger>(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<fs::path>& 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<fs::path> 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();
}
}
}

View File

@ -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<ColoredLogger> 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<Plugin> m_vLoadedPlugins;
public:
bool LoadPlugins();
std::optional<Plugin> 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;

View File

@ -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

View File

@ -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

View File

@ -1,6 +0,0 @@
#pragma once
namespace NS::Utils
{
void RemoveAsciiControlSequences(char* str, bool allow_color_codes);
}

View File

@ -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"
)

View File

@ -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.

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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"
)

View File

@ -1,182 +0,0 @@
#include "loader.h"
#include <shlwapi.h>
#include <filesystem>
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();
}
}

View File

@ -1,7 +0,0 @@
.data
extern PA : qword
.code
RunASM proc
jmp qword ptr [PA]
RunASM endp
end

3
primedev/CMakeLists.txt Normal file
View File

@ -0,0 +1,3 @@
include(Northstar.cmake)
include(Launcher.cmake)
add_subdirectory(wsockproxy)

28
primedev/Launcher.cmake Normal file
View File

@ -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"
)

203
primedev/Northstar.cmake Normal file
View File

@ -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"
)

4
primedev/Northstar.def Normal file
View File

@ -0,0 +1,4 @@
LIBRARY dedicated
EXPORTS
InitialiseNorthstar @1

View File

@ -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_ptr<EventOver
return true; // good to go
}
// forward declare
bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal(
uintptr_t parentEvent, void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType);
// DO NOT TOUCH THIS FUNCTION
// The actual logic of it in a separate function (forcefully not inlined) to preserve the r12 register, which holds the event pointer.
// clang-format off
AUTOHOOK(LoadSampleMetadata, mileswin64.dll + 0xF110,
bool, __fastcall, (void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType))
// clang-format on
{
uintptr_t parentEvent = (uintptr_t)Audio_GetParentEvent();
// Raw source, used for voice data only
if (audioType == 0)
return LoadSampleMetadata(sample, audioBuffer, audioBufferLength, audioType);
return LoadSampleMetadata_Internal(parentEvent, sample, audioBuffer, audioBufferLength, audioType);
}
// DO NOT INLINE THIS FUNCTION
// See comment below.
bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal(
uintptr_t parentEvent, void* sample, void* audioBuffer, unsigned int audioBufferLength, int audioType)
{
char* eventName = (char*)parentEvent + 0x110;
const char* eventName = pszAudioEventName;
if (Cvar_ns_print_played_sounds->GetInt() > 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<const char*>((*((__int64*)a2 + 6)));
return sub_1800294C0(a1, a2);
}
// clang-format off
AUTOHOOK(MilesLog, client.dll + 0x57DAD0,
void, __fastcall, (int level, const char* string))

View File

@ -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<std::string> EventIds = {};
@ -34,7 +34,7 @@ class EventOverrideData
class CustomAudioManager
{
public:
public:
bool TryLoadAudioOverride(const fs::path&);
void ClearAudioOverrides();

View File

@ -0,0 +1,5 @@
#pragma once
#include "toolframework/itoolentity.h"
inline IClientTools* g_pClientTools = nullptr;

View File

@ -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");

View File

@ -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__IsDeadType>();
OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<OverlayBase_t__DestroyOverlayType>();
OverlayBase_t__IsDead = module.Offset(0xACAC0).RCast<decltype(OverlayBase_t__IsDead)>();
OverlayBase_t__DestroyOverlay = module.Offset(0xAB680).RCast<decltype(OverlayBase_t__DestroyOverlay)>();
RenderLine = module.Offset(0x192A70).RCast<RenderLineType>();
RenderBox = module.Offset(0x192520).RCast<RenderBoxType>();
RenderWireframeBox = module.Offset(0x193DA0).RCast<RenderBoxType>();
RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<RenderWireframeSweptBoxType>();
RenderTriangle = module.Offset(0x193940).RCast<RenderTriangleType>();
RenderAxis = module.Offset(0x1924D0).RCast<RenderAxisType>();
RenderSphere = module.Offset(0x194170).RCast<RenderSphereType>();
RenderUnknown = module.Offset(0x1924E0).RCast<RenderUnknownType>();
RenderLine = module.Offset(0x192A70).RCast<decltype(RenderLine)>();
RenderBox = module.Offset(0x192520).RCast<decltype(RenderBox)>();
RenderWireframeBox = module.Offset(0x193DA0).RCast<decltype(RenderWireframeBox)>();
RenderWireframeSweptBox = module.Offset(0x1945A0).RCast<decltype(RenderWireframeSweptBox)>();
RenderTriangle = module.Offset(0x193940).RCast<decltype(RenderTriangle)>();
RenderAxis = module.Offset(0x1924D0).RCast<decltype(RenderAxis)>();
RenderSphere = module.Offset(0x194170).RCast<decltype(RenderSphere)>();
RenderUnknown = module.Offset(0x1924E0).RCast<decltype(RenderUnknown)>();
s_OverlayMutex = module.Offset(0x10DB0A38).RCast<LPCRITICAL_SECTION>();

View File

@ -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);

View File

@ -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);
}

View File

@ -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<IClientTools*>();
}

View File

@ -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))
{

View File

@ -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,

View File

@ -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);

View File

@ -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))
{

View File

@ -0,0 +1,7 @@
#pragma once
extern char* g_pLocalPlayerUserID;
extern char* g_pLocalPlayerOriginToken;
typedef void* (*GetBaseLocalClientType)();
extern GetBaseLocalClientType GetBaseLocalClient;

View File

@ -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);

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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;
}

View File

@ -2,9 +2,6 @@
#include "shared/misccommands.h"
#include "engine/r2engine.h"
#include "plugins/pluginbackend.h"
#include "plugins/plugin_abi.h"
#include <iostream>
//-----------------------------------------------------------------------------
@ -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<ConCommandConstructorType>();
AddMiscConCommands();
g_pPluginCommunicationhandler->m_sEngineData.ConCommandConstructor =
reinterpret_cast<PluginConCommandConstructorType>(ConCommandConstructor);
}

View File

@ -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);

View File

@ -3,9 +3,6 @@
#include "convar.h"
#include "core/sourceinterface.h"
#include "plugins/pluginbackend.h"
#include "plugins/plugin_abi.h"
#include <float.h>
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<CCvar>("vstdlib.dll", "VEngineCvar007");
R2::g_pCVar = *R2::g_pCVarInterface;
g_pPluginCommunicationhandler->m_sEngineData.conVarMalloc = reinterpret_cast<PluginConVarMallocType>(conVarMalloc);
g_pPluginCommunicationhandler->m_sEngineData.conVarRegister = reinterpret_cast<PluginConVarRegisterType>(conVarRegister);
g_pPluginCommunicationhandler->m_sEngineData.ConVar_Vtable = reinterpret_cast<void*>(g_pConVar_Vtable);
g_pPluginCommunicationhandler->m_sEngineData.IConVar_Vtable = reinterpret_cast<void*>(g_pIConVar_Vtable);
g_pPluginCommunicationhandler->m_sEngineData.g_pCVar = reinterpret_cast<void*>(R2::g_pCVar);
g_pCVarInterface = new SourceInterface<CCvar>("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)
{

View File

@ -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(

View File

@ -22,9 +22,5 @@ std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
return allConVars;
}
// use the R2 namespace for game funcs
namespace R2
{
SourceInterface<CCvar>* g_pCVarInterface;
CCvar* g_pCVar;
} // namespace R2
SourceInterface<CCvar>* g_pCVarInterface;
CCvar* g_pCVar;

View File

@ -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<std::string, ConCommandBase*> DumpToMap();
};
// use the R2 namespace for game funcs
namespace R2
{
extern SourceInterface<CCvar>* g_pCVarInterface;
extern CCvar* g_pCVar;
} // namespace R2
extern SourceInterface<CCvar>* g_pCVarInterface;
extern CCvar* g_pCVar;

View File

@ -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<IFileSystem>* g_pFilesystem;
std::string ReadVPKFile(const char* path)
{
SourceInterface<IFileSystem>* 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<IFileSystem>("filesystem_stdio.dll", "VFileSystem017");
g_pFilesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017");
AddSearchPathHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->AddSearchPath);
ReadFromCacheHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->ReadFromCache);

View File

@ -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<IFileSystem>* g_pFilesystem;
extern SourceInterface<IFileSystem>* 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);

View File

@ -209,8 +209,8 @@ int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallbac
// dedicated only needs common, common_mp, common_sp, and sp_<map> rpaks
// sp_<map> rpaks contain tutorial ghost data
// sucks to have to load the entire rpak for that but sp was never meant to be done on dedi
if (IsDedicatedServer() && (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;

View File

@ -17,11 +17,11 @@ struct LoadedPak
class PakLoadManager
{
private:
private:
std::map<int, LoadedPak> m_vLoadedPaks {};
std::unordered_map<size_t, int> m_HashToPakHandle {};
public:
public:
int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource);
void UnloadPak(const int nPakHandle);
void UnloadMapPaks();

View File

@ -1,5 +1,5 @@
#include "dedicated/dedicated.h"
#include "plugins/pluginbackend.h"
#include "plugins/pluginmanager.h"
#include <iostream>
#include <wchar.h>
@ -10,6 +10,8 @@
#include <filesystem>
#include <Psapi.h>
#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;

View File

@ -1,5 +1,4 @@
#pragma once
#include "memory.h"
#include <string>
#include <iostream>
@ -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);

View File

@ -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<u32 const*>(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<u32 const*>(reinterpret_cast<u8 const*>(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<u32*>(data);
@ -1131,7 +1131,7 @@ class BFWrite : public BitBufferBase
WriteBitVec3Coord(tmp);
}*/
private:
private:
size_t m_DataBits = 0;
size_t m_DataBytes = 0;

View File

@ -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];
};

View File

@ -0,0 +1,7 @@
#pragma once
inline float FastSqrt(float x)
{
__m128 root = _mm_sqrt_ss(_mm_load_ss(&x));
return *(reinterpret_cast<float*>(&root));
}

331
primedev/core/math/vector.h Normal file
View File

@ -0,0 +1,331 @@
#pragma once
#include "bits.h"
#include "math_pfns.h"
#include <cmath>
#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;
}

106
primedev/core/math/vplane.h Normal file
View File

@ -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;
}

View File

@ -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)

View File

@ -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)
{

View File

@ -1,15 +1,22 @@
#pragma once
#include <string>
// interface return status
enum class InterfaceStatus : int
{
IFACE_OK = 0,
IFACE_FAILED,
};
// literally just copied from ttf2sdk definition
typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
template <typename T> class SourceInterface
{
private:
private:
T* m_interface;
public:
public:
SourceInterface(const std::string& moduleName, const std::string& interfaceName)
{
HMODULE handle = GetModuleHandleA(moduleName.c_str());

30
primedev/core/tier0.cpp Normal file
View File

@ -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<CreateGlobalMemAllocType>(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<CommandLineType>();
Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast<Plat_FloatTimeType>();
ThreadInServerFrameThread = module.GetExportedFunction("ThreadInServerFrameThread").RCast<ThreadInServerFrameThreadType>();
}

63
primedev/core/tier0.h Normal file
View File

@ -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();

19
primedev/core/tier1.cpp Normal file
View File

@ -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<CreateInterfaceFn>(GetProcAddress(hModule, CREATEINTERFACE_PROCNAME));
return fnCreateInterface(svFactoryName.c_str(), NULL);
}

12
primedev/core/tier1.h Normal file
View File

@ -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);

29
primedev/core/vanilla.h Normal file
View File

@ -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;

View File

@ -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<double, std::ratio<1>>(g_pGlobals->m_flTickInterval - fmin(Tier0::Plat_FloatTime() - frameStart, 0.25)));
std::chrono::duration<double, std::ratio<1>>(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<DedicatedServerLogToClientSink>());
// 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(

Some files were not shown because too many files have changed in this diff Show More