diff options
29 files changed, 666 insertions, 705 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index d6c77de4..5b8a7607 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -117,6 +117,7 @@ <ClInclude Include="bansystem.h" /> <ClInclude Include="bitbuf.h" /> <ClInclude Include="bits.h" /> + <ClInclude Include="crashhandler.h" /> <ClInclude Include="printcommand.h" /> <ClInclude Include="hoststate.h" /> <ClInclude Include="localchatwriter.h" /> @@ -571,6 +572,7 @@ <ClCompile Include="concommand.cpp" /> <ClCompile Include="configurables.cpp" /> <ClCompile Include="convar.cpp" /> + <ClCompile Include="crashhandler.cpp" /> <ClCompile Include="cvar.cpp" /> <ClCompile Include="debugoverlay.cpp" /> <ClCompile Include="dedicated.cpp" /> diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index 3ab95494..10bbced8 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1482,6 +1482,9 @@ <ClInclude Include="r2client.h"> <Filter>Header Files\Shared\Game Functions</Filter> </ClInclude> + <ClInclude Include="crashhandler.h"> + <Filter>Header Files\Shared</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp"> @@ -1670,6 +1673,9 @@ <ClCompile Include="r2engine.cpp"> <Filter>Source Files\Shared\Game Functions</Filter> </ClCompile> + <ClCompile Include="crashhandler.cpp"> + <Filter>Source Files\Shared</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <MASM Include="audio_asm.asm"> diff --git a/NorthstarDedicatedTest/buildainfile.cpp b/NorthstarDedicatedTest/buildainfile.cpp index ea9d69ef..a80185e5 100644 --- a/NorthstarDedicatedTest/buildainfile.cpp +++ b/NorthstarDedicatedTest/buildainfile.cpp @@ -1,8 +1,10 @@ #include "pch.h" #include "convar.h" +#include "hoststate.h" +#include "NSMem.h" + #include <fstream> #include <filesystem> -#include "NSMem.h" AUTOHOOK_INIT() @@ -161,14 +163,13 @@ struct CAI_Network }; char** pUnkServerMapversionGlobal; -char* pMapName; ConVar* Cvar_ns_ai_dumpAINfileFromLoad; void DumpAINInfo(CAI_Network* aiNetwork) { fs::path writePath("r2/maps/graphs"); - writePath /= pMapName; + writePath /= R2::g_pHostState->m_levelName; writePath += ".ain"; // dump from memory @@ -370,14 +371,14 @@ void,, (void* aimanager, void* buf, const char* filename), ON_DLL_LOAD("server.dll", BuildAINFile, [](HMODULE baseAddress) { + AUTOHOOK_DISPATCH() + Cvar_ns_ai_dumpAINfileFromLoad = new ConVar( "ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk"); pUnkStruct0Count = (int*)((char*)baseAddress + 0x1063BF8); pppUnkNodeStruct0s = (UnkNodeStruct0***)((char*)baseAddress + 0x1063BE0); - pUnkLinkStruct1Count = (int*)((char*)baseAddress + 0x1063AA8); pppUnkStruct1s = (UnkLinkStruct1***)((char*)baseAddress + 0x1063A90); pUnkServerMapversionGlobal = (char**)((char*)baseAddress + 0xBFBE08); - pMapName = (char*)baseAddress + 0x1053370; });
\ No newline at end of file diff --git a/NorthstarDedicatedTest/clientruihooks.cpp b/NorthstarDedicatedTest/clientruihooks.cpp index da556d26..5fb0b348 100644 --- a/NorthstarDedicatedTest/clientruihooks.cpp +++ b/NorthstarDedicatedTest/clientruihooks.cpp @@ -1,22 +1,22 @@ #include "pch.h" #include "convar.h" +AUTOHOOK_INIT() + ConVar* Cvar_rui_drawEnable; -typedef char (*DrawRUIFuncType)(void* a1, float* a2); -DrawRUIFuncType DrawRUIFunc; -char DrawRUIFuncHook(void* a1, float* a2) +AUTOHOOK(DrawRUIFunc, engine.dll + 0xFC500, +bool,, (void* a1, float* a2), { if (!Cvar_rui_drawEnable->GetBool()) return 0; - + return DrawRUIFunc(a1, a2); -} +}) ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", RUI, ConVar, [](HMODULE baseAddress) { - Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn"); + AUTOHOOK_DISPATCH() - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xFC500, &DrawRUIFuncHook, reinterpret_cast<LPVOID*>(&DrawRUIFunc)); + Cvar_rui_drawEnable = new ConVar("rui_drawEnable", "1", FCVAR_CLIENTDLL, "Controls whether RUI should be drawn"); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/clientvideooverrides.cpp b/NorthstarDedicatedTest/clientvideooverrides.cpp index b3609fc2..0bc6bb06 100644 --- a/NorthstarDedicatedTest/clientvideooverrides.cpp +++ b/NorthstarDedicatedTest/clientvideooverrides.cpp @@ -1,9 +1,10 @@ #include "pch.h" #include "modmanager.h" -typedef void* (*BinkOpenType)(const char* path, uint32_t flags); -BinkOpenType BinkOpen; -void* BinkOpenHook(const char* path, uint32_t flags) +AUTOHOOK_INIT() + +AUTOHOOK_PROCADDRESS(BinkOpen, bink2w64.dll, BinkOpen, +void*,, (const char* path, uint32_t flags), { std::string filename(fs::path(path).filename().string()); spdlog::info("BinkOpen {}", filename); @@ -27,11 +28,9 @@ void* BinkOpenHook(const char* path, uint32_t flags) } else return BinkOpen(path, flags); -} +}) ON_DLL_LOAD_CLIENT("client.dll", BinkVideo, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, GetProcAddress(GetModuleHandleA("bink2w64.dll"), "BinkOpen"), &BinkOpenHook, reinterpret_cast<LPVOID*>(&BinkOpen)); + AUTOHOOK_DISPATCH() })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/crashhandler.cpp b/NorthstarDedicatedTest/crashhandler.cpp new file mode 100644 index 00000000..90091883 --- /dev/null +++ b/NorthstarDedicatedTest/crashhandler.cpp @@ -0,0 +1,216 @@ +#include "pch.h" +#include "crashhandler.h" +#include "dedicated.h" +#include "configurables.h" + +#include <minidumpapiset.h> + +HANDLE hExceptionFilter; + +long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) +{ + static bool logged = false; + if (logged) + return EXCEPTION_CONTINUE_SEARCH; + + if (!IsDebuggerPresent()) + { + const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; + if (exceptionCode != EXCEPTION_ACCESS_VIOLATION && exceptionCode != EXCEPTION_ARRAY_BOUNDS_EXCEEDED && + exceptionCode != EXCEPTION_DATATYPE_MISALIGNMENT && exceptionCode != EXCEPTION_FLT_DENORMAL_OPERAND && + exceptionCode != EXCEPTION_FLT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_FLT_INEXACT_RESULT && + exceptionCode != EXCEPTION_FLT_INVALID_OPERATION && exceptionCode != EXCEPTION_FLT_OVERFLOW && + exceptionCode != EXCEPTION_FLT_STACK_CHECK && exceptionCode != EXCEPTION_FLT_UNDERFLOW && + exceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && exceptionCode != EXCEPTION_IN_PAGE_ERROR && + exceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_INT_OVERFLOW && + exceptionCode != EXCEPTION_INVALID_DISPOSITION && exceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION && + exceptionCode != EXCEPTION_PRIV_INSTRUCTION && exceptionCode != EXCEPTION_STACK_OVERFLOW) + return EXCEPTION_CONTINUE_SEARCH; + + std::stringstream exceptionCause; + exceptionCause << "Cause: "; + switch (exceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_IN_PAGE_ERROR: + { + exceptionCause << "Access Violation" << std::endl; + + auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; + auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; + + if (!exceptionInfo0) + exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1; + else if (exceptionInfo0 == 1) + exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; + else if (exceptionInfo0 == 8) + exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; + else + exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; + + break; + } + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + exceptionCause << "Array bounds exceeded"; + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + exceptionCause << "Datatype misalignment"; + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + exceptionCause << "Denormal operand"; + break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + exceptionCause << "Divide by zero (float)"; + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + exceptionCause << "Divide by zero (int)"; + break; + case EXCEPTION_FLT_INEXACT_RESULT: + exceptionCause << "Inexact result"; + break; + case EXCEPTION_FLT_INVALID_OPERATION: + exceptionCause << "Invalid operation"; + break; + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_INT_OVERFLOW: + exceptionCause << "Numeric overflow"; + break; + case EXCEPTION_FLT_UNDERFLOW: + exceptionCause << "Numeric underflow"; + break; + case EXCEPTION_FLT_STACK_CHECK: + exceptionCause << "Stack check"; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + exceptionCause << "Illegal instruction"; + break; + case EXCEPTION_INVALID_DISPOSITION: + exceptionCause << "Invalid disposition"; + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + exceptionCause << "Noncontinuable exception"; + break; + case EXCEPTION_PRIV_INSTRUCTION: + exceptionCause << "Priviledged instruction"; + break; + case EXCEPTION_STACK_OVERFLOW: + exceptionCause << "Stack overflow"; + break; + default: + exceptionCause << "Unknown"; + break; + } + + void* exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; + + HMODULE crashedModuleHandle; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(exceptionAddress), &crashedModuleHandle); + + MODULEINFO crashedModuleInfo; + GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo)); + + char crashedModuleFullName[MAX_PATH]; + GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); + char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; + + DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); + CONTEXT* exceptionContext = exceptionInfo->ContextRecord; + + spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); + spdlog::error(exceptionCause.str()); + spdlog::error("At: {} + {}", crashedModuleName, (void*)crashedModuleOffset); + + PVOID framesToCapture[62]; + int frames = RtlCaptureStackBackTrace(0, 62, framesToCapture, NULL); + for (int i = 0; i < frames; i++) + { + HMODULE backtraceModuleHandle; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(framesToCapture[i]), &backtraceModuleHandle); + + char backtraceModuleFullName[MAX_PATH]; + GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH); + char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1; + + void* actualAddress = (void*)framesToCapture[i]; + void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle)); + + spdlog::error(" {} + {} ({})", backtraceModuleName, relativeAddress, actualAddress); + } + + spdlog::error("RAX: 0x{0:x}", exceptionContext->Rax); + spdlog::error("RBX: 0x{0:x}", exceptionContext->Rbx); + spdlog::error("RCX: 0x{0:x}", exceptionContext->Rcx); + spdlog::error("RDX: 0x{0:x}", exceptionContext->Rdx); + spdlog::error("RSI: 0x{0:x}", exceptionContext->Rsi); + spdlog::error("RDI: 0x{0:x}", exceptionContext->Rdi); + spdlog::error("RBP: 0x{0:x}", exceptionContext->Rbp); + spdlog::error("RSP: 0x{0:x}", exceptionContext->Rsp); + spdlog::error("R8: 0x{0:x}", exceptionContext->R8); + spdlog::error("R9: 0x{0:x}", exceptionContext->R9); + spdlog::error("R10: 0x{0:x}", exceptionContext->R10); + spdlog::error("R11: 0x{0:x}", exceptionContext->R11); + spdlog::error("R12: 0x{0:x}", exceptionContext->R12); + spdlog::error("R13: 0x{0:x}", exceptionContext->R13); + spdlog::error("R14: 0x{0:x}", exceptionContext->R14); + spdlog::error("R15: 0x{0:x}", exceptionContext->R15); + + time_t time = std::time(nullptr); + tm currentTime = *std::localtime(&time); + std::stringstream stream; + stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str()); + + auto hMinidumpFile = CreateFileA(stream.str().c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hMinidumpFile) + { + MINIDUMP_EXCEPTION_INFORMATION dumpExceptionInfo; + dumpExceptionInfo.ThreadId = GetCurrentThreadId(); + dumpExceptionInfo.ExceptionPointers = exceptionInfo; + dumpExceptionInfo.ClientPointers = false; + + MiniDumpWriteDump( + GetCurrentProcess(), + GetCurrentProcessId(), + hMinidumpFile, + MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory), + &dumpExceptionInfo, + nullptr, + nullptr); + CloseHandle(hMinidumpFile); + } + else + spdlog::error("Failed to write minidump file {}!", stream.str()); + + if (!IsDedicatedServer()) + MessageBoxA( + 0, "Northstar has crashed! Crash info can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); + } + + logged = true; + return EXCEPTION_EXECUTE_HANDLER; +} + +BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode) +{ + switch (eventCode) + { + case CTRL_CLOSE_EVENT: + // User closed console, shut everything down + spdlog::info("Exiting due to console close..."); + RemoveCrashHandler(); + exit(EXIT_SUCCESS); + return FALSE; + } + + return TRUE; +} + +void InitialiseCrashHandler() +{ + hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); + SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); +} + +void RemoveCrashHandler() +{ + RemoveVectoredExceptionHandler(hExceptionFilter); +}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/crashhandler.h b/NorthstarDedicatedTest/crashhandler.h new file mode 100644 index 00000000..510372d2 --- /dev/null +++ b/NorthstarDedicatedTest/crashhandler.h @@ -0,0 +1,4 @@ +#pragma once + +void InitialiseCrashHandler(); +void RemoveCrashHandler();
\ No newline at end of file diff --git a/NorthstarDedicatedTest/debugoverlay.cpp b/NorthstarDedicatedTest/debugoverlay.cpp index 6cf7703f..e6601223 100644 --- a/NorthstarDedicatedTest/debugoverlay.cpp +++ b/NorthstarDedicatedTest/debugoverlay.cpp @@ -2,6 +2,8 @@ #include "dedicated.h" #include "cvar.h" +AUTOHOOK_INIT() + struct Vector3 { float x, y, z; @@ -83,10 +85,8 @@ typedef void (*RenderBoxType)(Vector3 vOrigin, QAngle angles, Vector3 vMins, Vec static RenderBoxType RenderBox; static RenderBoxType RenderWireframeBox; -// engine.dll+0xABCB0 -typedef void (*DrawOverlayType)(OverlayBase_t* a1); -DrawOverlayType DrawOverlay; -void __fastcall DrawOverlayHook(OverlayBase_t* pOverlay) +AUTOHOOK(DrawOverlay, engine.dll + 0xABCB0, +void, __fastcall, (OverlayBase_t * pOverlay), { EnterCriticalSection((LPCRITICAL_SECTION)((char*)sEngineModule + 0x10DB0A38)); // s_OverlayMutex @@ -129,12 +129,11 @@ void __fastcall DrawOverlayHook(OverlayBase_t* pOverlay) break; } LeaveCriticalSection((LPCRITICAL_SECTION)((char*)sEngineModule + 0x10DB0A38)); -} +}) ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", DebugOverlay, ConVar, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xABCB0, &DrawOverlayHook, reinterpret_cast<LPVOID*>(&DrawOverlay)); + AUTOHOOK_DISPATCH() RenderLine = reinterpret_cast<RenderLineType>((char*)baseAddress + 0x192A70); diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index 25e6d24b..4aa1b072 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -8,6 +8,8 @@ #include "masterserver.h" #include "printcommand.h" +AUTOHOOK_INIT() + using namespace R2; bool IsDedicatedServer() @@ -92,12 +94,11 @@ void RunServer(CDedicatedExports* dedicated) } } -typedef bool (*IsGameActiveWindowType)(); -IsGameActiveWindowType IsGameActiveWindow; -bool IsGameActiveWindowHook() +AUTOHOOK(IsGameActiveWindow, engine.dll + 0x1CDC80, +bool,, (), { - return true; -} + return true; +}) HANDLE consoleInputThreadHandle = NULL; @@ -131,6 +132,8 @@ ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, [](HMODULE engineAddress) { spdlog::info("InitialiseDedicated"); + AUTOHOOK_DISPATCH_MODULE("engine.dll") + uintptr_t ea = (uintptr_t)engineAddress; // Host_Init @@ -222,9 +225,6 @@ ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, [](HMODULE engineAddress) CDedicatedExports** exports = (CDedicatedExports**)((char*)engineAddress + 0x13F0B668); *exports = dedicatedExports; - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)engineAddress + 0x1CDC80, &IsGameActiveWindowHook, reinterpret_cast<LPVOID*>(&IsGameActiveWindow)); - // extra potential patches: // nop engine.dll+1c67d1 and +1c67d8 to skip videomode creategamewindow // also look into launcher.dll+d381, seems to cause renderthread to get made @@ -283,16 +283,14 @@ ON_DLL_LOAD_DEDI("tier0.dll", DedicatedServerOrigin, [](HMODULE baseAddress) }); }) -typedef void (*PrintFatalSquirrelErrorType)(void* sqvm); -PrintFatalSquirrelErrorType PrintFatalSquirrelError; -void PrintFatalSquirrelErrorHook(void* sqvm) +AUTOHOOK(PrintFatalSquirrelError, server.dll + 0x794D0, +void, , (void* sqvm), { PrintFatalSquirrelError(sqvm); g_pEngine->m_nQuitting = EngineQuitState::QUIT_TODESKTOP; -} +}) ON_DLL_LOAD_DEDI("server.dll", DedicatedServerGameDLL, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, baseAddress + 0x794D0, &PrintFatalSquirrelErrorHook, reinterpret_cast<LPVOID*>(&PrintFatalSquirrelError)); + AUTOHOOK_DISPATCH_MODULE("server.dll") })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index d01d381f..3f1bf323 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -4,20 +4,10 @@ #include "tier0.h" #include "NSMem.h" -typedef HRESULT (*__stdcall D3D11CreateDeviceType)( - void* pAdapter, - int DriverType, - HMODULE Software, - UINT Flags, - int* pFeatureLevels, - UINT FeatureLevels, - UINT SDKVersion, - void** ppDevice, - int* pFeatureLevel, - void** ppImmediateContext); -D3D11CreateDeviceType D3D11CreateDevice; +AUTOHOOK_INIT() -HRESULT __stdcall D3D11CreateDeviceHook( +AUTOHOOK(D3D11CreateDevice, materialsystem_dx11.dll + 0xD9A0E, +HRESULT, __stdcall, ( void* pAdapter, int DriverType, HMODULE Software, @@ -27,7 +17,7 @@ HRESULT __stdcall D3D11CreateDeviceHook( UINT SDKVersion, void** ppDevice, int* pFeatureLevel, - void** ppImmediateContext) + void** ppImmediateContext), { // note: this is super duper temp pretty much just messing around with it // does run surprisingly well on dedi for a software driver tho if you ignore the +1gb ram usage at times, seems like dedi doesn't @@ -41,41 +31,13 @@ HRESULT __stdcall D3D11CreateDeviceHook( return D3D11CreateDevice( pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, ppDevice, pFeatureLevel, ppImmediateContext); -} +}) ON_DLL_LOAD_DEDI("materialsystem_dx11.dll", DedicatedServerMaterialSystem, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xD9A0E, &D3D11CreateDeviceHook, reinterpret_cast<LPVOID*>(&D3D11CreateDevice)); - - // not using these for now since they're related to nopping renderthread/gamewindow i.e. very hard - //{ - // // function that launches renderthread - // char* ptr = (char*)baseAddress + 0x87047; - // TempReadWrite rw(ptr); - // - // // make it not launch renderthread - // *ptr = (char)0x90; - // *(ptr + 1) = (char)0x90; - // *(ptr + 2) = (char)0x90; - // *(ptr + 3) = (char)0x90; - // *(ptr + 4) = (char)0x90; - // *(ptr + 5) = (char)0x90; - //} - // - //{ - // // some function that waits on renderthread job - // char* ptr = (char*)baseAddress + 0x87d00; - // TempReadWrite rw(ptr); - // - // // return immediately - // *ptr = (char)0xC3; - //} + AUTOHOOK_DISPATCH() // CMaterialSystem::FindMaterial // make the game always use the error material NSMem::BytePatch((uintptr_t)baseAddress + 0x5F0F1, {0xE9, 0x34, 0x03, 0x00}); - - // previously had DisableDedicatedWindowCreation stuff here, removing for now since shit and unstable - // check commit history if needed })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/demofixes.cpp b/NorthstarDedicatedTest/demofixes.cpp index fba94183..ff06ae12 100644 --- a/NorthstarDedicatedTest/demofixes.cpp +++ b/NorthstarDedicatedTest/demofixes.cpp @@ -2,12 +2,15 @@ #include "convar.h" #include "NSMem.h" -ON_DLL_LOAD_CLIENT_RELIESON("client.dll", DemoFixes, ConVar, [](HMODULE baseAddress) +ON_DLL_LOAD_CLIENT("engine.dll", EngineDemoFixes, [](HMODULE baseAddress) { // allow demo recording on loopback - NSMem::NOP((uintptr_t)GetModuleHandleA("engine.dll") + 0x8E1B1, 2); - NSMem::NOP((uintptr_t)GetModuleHandleA("engine.dll") + 0x56CC3, 2); + NSMem::NOP((uintptr_t)baseAddress + 0x8E1B1, 2); + NSMem::NOP((uintptr_t)baseAddress + 0x56CC3, 2); +}) +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientDemoFixes, ConVar, [](HMODULE baseAddress) +{ // 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"); diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index 718e9413..e27cd529 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "main.h" #include "logging.h" +#include "crashhandler.h" #include "memalloc.h" #include "configurables.h" #include "plugin_abi.h" @@ -153,6 +154,7 @@ bool InitialiseNorthstar() curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base); + InitialiseCrashHandler(); InitialiseLogging(); InstallInitialHooks(); CreateLogFiles(); diff --git a/NorthstarDedicatedTest/filesystem.cpp b/NorthstarDedicatedTest/filesystem.cpp index f55fca82..a879b40e 100644 --- a/NorthstarDedicatedTest/filesystem.cpp +++ b/NorthstarDedicatedTest/filesystem.cpp @@ -6,6 +6,8 @@ #include <iostream> #include <sstream> +AUTOHOOK_INIT() + using namespace R2; bool bReadingOriginalFile = false; @@ -47,9 +49,8 @@ namespace R2 } } // namespace R2 -typedef void (*AddSearchPathType)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType); -AddSearchPathType AddSearchPath; -void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType) +HOOK(AddSearchPathHook, AddSearchPath, +void,, (IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType), { AddSearchPath(fileSystem, pPath, pathID, addType); @@ -59,7 +60,7 @@ void AddSearchPathHook(IFileSystem* fileSystem, const char* pPath, const char* p AddSearchPath(fileSystem, sCurrentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD); AddSearchPath(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD); } -} +}) void SetNewModSearchPaths(Mod* mod) { @@ -101,20 +102,18 @@ bool TryReplaceFile(const char* pPath, bool shouldCompile) } // force modded files to be read from mods, not cache -typedef bool (*ReadFromCacheType)(IFileSystem* filesystem, char* path, void* result); -ReadFromCacheType ReadFromCache; -bool ReadFromCacheHook(IFileSystem* filesystem, char* pPath, void* result) +HOOK(ReadFromCacheHook, ReadFromCache, +bool,, (IFileSystem* filesystem, char* pPath, void* result), { if (TryReplaceFile(pPath, true)) return false; return ReadFromCache(filesystem, pPath, result); -} +}) // force modded files to be read from mods, not vpk -typedef FileHandle_t (*ReadFileFromVPKType)(VPKData* vpkInfo, __int64* b, char* filename); -ReadFileFromVPKType ReadFileFromVPK; -FileHandle_t ReadFileFromVPKHook(VPKData* vpkInfo, __int64* b, char* filename) +AUTOHOOK(ReadFileFromVPK, filesystem_stdio.dll + 0x5CBA0, +FileHandle_t,, (VPKData* vpkInfo, __int64* b, char* filename), { // don't compile here because this is only ever called from OpenEx, which already compiles if (TryReplaceFile(filename, false)) @@ -124,23 +123,20 @@ FileHandle_t ReadFileFromVPKHook(VPKData* vpkInfo, __int64* b, char* filename) } return ReadFileFromVPK(vpkInfo, b, filename); -} +}) -typedef FileHandle_t (*CBaseFileSystem__OpenExType)( - IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char** ppszResolvedFilename); -CBaseFileSystem__OpenExType CBaseFileSystem__OpenEx; -FileHandle_t CBaseFileSystem__OpenExHook(IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char **ppszResolvedFilename) +AUTOHOOK(CBaseFileSystem__OpenEx, filesystem_stdio.dll + 0x15F50, +FileHandle_t,, (IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char **ppszResolvedFilename), { TryReplaceFile(pPath, true); return CBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename); -} +}) -typedef VPKData* (*MountVPKType)(IFileSystem* fileSystem, const char* vpkPath); -MountVPKType MountVPK; -VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) +HOOK(MountVPKHook, MountVPK, +VPKData*,, (IFileSystem * fileSystem, const char* pVpkPath), { - spdlog::info("MountVPK {}", vpkPath); - VPKData* ret = MountVPK(fileSystem, vpkPath); + spdlog::info("MountVPK {}", pVpkPath); + VPKData* ret = MountVPK(fileSystem, pVpkPath); for (Mod mod : g_pModManager->m_loadedMods) { @@ -154,7 +150,7 @@ VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) { // resolve vpk name and try to load one with the same name // todo: we should be unloading these on map unload manually - std::string mapName(fs::path(vpkPath).filename().string()); + std::string mapName(fs::path(pVpkPath).filename().string()); std::string modMapName(fs::path(vpkEntry.m_sVpkPath.c_str()).filename().string()); if (mapName.compare(modMapName)) continue; @@ -167,18 +163,15 @@ VPKData* MountVPKHook(IFileSystem* fileSystem, const char* vpkPath) } return ret; -} +}) ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, [](HMODULE baseAddress) { + AUTOHOOK_DISPATCH() + R2::g_pFilesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017"); - // create hooks - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (*g_pFilesystem)->m_vtable->ReadFromCache, &ReadFromCacheHook, reinterpret_cast<LPVOID*>(&ReadFromCache)); - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x5CBA0, &ReadFileFromVPKHook, reinterpret_cast<LPVOID*>(&ReadFileFromVPK)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x15F50, &CBaseFileSystem__OpenExHook, reinterpret_cast<LPVOID*>(&CBaseFileSystem__OpenEx)); - ENABLER_CREATEHOOK(hook, (*g_pFilesystem)->m_vtable->AddSearchPath, &AddSearchPathHook, reinterpret_cast<LPVOID*>(&AddSearchPath)); - ENABLER_CREATEHOOK(hook, (*g_pFilesystem)->m_vtable->MountVPK, &MountVPKHook, reinterpret_cast<LPVOID*>(&MountVPK)); + AddSearchPathHook.Dispatch((*g_pFilesystem)->m_vtable->AddSearchPath); + ReadFromCacheHook.Dispatch((*g_pFilesystem)->m_vtable->ReadFromCache); + MountVPKHook.Dispatch((*g_pFilesystem)->m_vtable->MountVPK); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp index d4608f85..2b117178 100644 --- a/NorthstarDedicatedTest/hooks.cpp +++ b/NorthstarDedicatedTest/hooks.cpp @@ -46,15 +46,37 @@ void __fileAutohook::Dispatch() hook->Dispatch(); } -ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : pHookFunc(func), ppOrigFunc(orig) +void __fileAutohook::DispatchForModule(const char* pModuleName) +{ + const int iModuleNameLen = strlen(pModuleName); + + for (__autohook* hook : hooks) + if ((hook->iAddressResolutionMode == __autohook::OFFSET_STRING && !strncmp(pModuleName, hook->pAddrString, iModuleNameLen)) || + (hook->iAddressResolutionMode == __autohook::PROCADDRESS && !strcmp(pModuleName, hook->pModuleName))) + hook->Dispatch(); +} + +ManualHook::ManualHook(const char* funcName, LPVOID func) + : pHookFunc(func), ppOrigFunc(nullptr) +{ + const int 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); pFuncName = new char[iFuncNameStrlen]; memcpy(pFuncName, funcName, iFuncNameStrlen); } -bool ManualHook::Dispatch(LPVOID addr) +bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig) { + if (orig) + ppOrigFunc = orig; + if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK) { if (MH_EnableHook(addr) == MH_OK) @@ -118,16 +140,15 @@ void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callba AddDllLoadCallback(dll, callback, tag, reliesOn); } -typedef LPSTR (*GetCommandLineAType)(); -GetCommandLineAType GetCommandLineAOriginal; -LPSTR GetCommandLineAHook() +AUTOHOOK_ABSOLUTEADDR(_GetCommandLineA, GetCommandLineA, +LPSTR, WINAPI, (), { static char* cmdlineModified; static char* cmdlineOrg; if (cmdlineOrg == nullptr || cmdlineModified == nullptr) { - cmdlineOrg = GetCommandLineAOriginal(); + cmdlineOrg = _GetCommandLineA(); bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument bool ignoreStartupArgs = strstr(cmdlineOrg, "-nostartupargs"); @@ -181,7 +202,7 @@ LPSTR GetCommandLineAHook() } return cmdlineModified; -} +}) std::vector<std::string> calledTags; void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) diff --git a/NorthstarDedicatedTest/hooks.h b/NorthstarDedicatedTest/hooks.h index 36c99b39..ba1bca3b 100644 --- a/NorthstarDedicatedTest/hooks.h +++ b/NorthstarDedicatedTest/hooks.h @@ -58,6 +58,7 @@ class __fileAutohook std::vector<__autohook*> hooks; void Dispatch(); + void DispatchForModule(const char* pModuleName); }; // initialise autohooks for this file @@ -68,6 +69,9 @@ namespace { __fileAutohook __FILEAUTOHOOK; } \ #define AUTOHOOK_DISPATCH() \ __FILEAUTOHOOK.Dispatch(); \ +#define AUTOHOOK_DISPATCH_MODULE(moduleName) \ +__FILEAUTOHOOK.DispatchForModule(__STR(moduleName)); \ + class __autohook { public: @@ -75,6 +79,7 @@ class __autohook { OFFSET_STRING, // we're using a string that of the format dllname.dll + offset ABSOLUTE_ADDR, // we're using an absolute address, we don't need to process it at all + PROCADDRESS // resolve using GetModuleHandle and GetProcAddress }; char* pFuncName; @@ -86,6 +91,8 @@ class __autohook AddressResolutionMode iAddressResolutionMode; char* pAddrString = nullptr; // for OFFSET_STRING LPVOID iAbsoluteAddress = nullptr; // for ABSOLUTE_ADDR + char* pModuleName; // for PROCADDRESS + char* pProcName; // for PROCADDRESS public: __autohook() = delete; @@ -118,12 +125,38 @@ class __autohook autohook->hooks.push_back(this); } + __autohook(__fileAutohook* autohook, const char* funcName, const char* moduleName, const char* procName, LPVOID* orig, LPVOID func) + : pHookFunc(func), ppOrigFunc(orig) + { + iAddressResolutionMode = PROCADDRESS; + + const int iFuncNameStrlen = strlen(funcName) + 1; + pFuncName = new char[iFuncNameStrlen]; + memcpy(pFuncName, funcName, iFuncNameStrlen); + + const int iModuleNameStrlen = strlen(moduleName) + 1; + pModuleName = new char[iModuleNameStrlen]; + memcpy(pModuleName, moduleName, iModuleNameStrlen); + + const int iProcNameStrlen = strlen(procName) + 1; + pProcName = new char[iProcNameStrlen]; + memcpy(pProcName, procName, iProcNameStrlen); + + autohook->hooks.push_back(this); + } + ~__autohook() { delete[] pFuncName; if (pAddrString) delete[] pAddrString; + + if (pModuleName) + delete[] pModuleName; + + if (pProcName) + delete[] pProcName; } void Dispatch() @@ -170,7 +203,13 @@ class __autohook else iOffset = std::stoi(pAddrString + iOffsetBegin); - targetAddr = (char*)pModuleAddr + iOffset; + targetAddr = (LPVOID)((uintptr_t)pModuleAddr + iOffset); + break; + } + + case PROCADDRESS: + { + targetAddr = GetProcAddress(GetModuleHandleA(pModuleName), pProcName); break; } } @@ -203,6 +242,14 @@ namespace { \ __autohook CONCAT2(__autohook, __LINE__)(&__FILEAUTOHOOK, __STR(name), addr, (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \ } \ +// hook a function at a given module and exported function to be dispatched with AUTOHOOK_DISPATCH() +#define AUTOHOOK_PROCADDRESS(name, moduleName, procName, type, callingConvention, args, func) \ +namespace { \ + type(*callingConvention name) args; \ + type callingConvention CONCAT2(__autohookfunc, name) args func \ + __autohook CONCAT2(__autohook, __LINE__)(&__FILEAUTOHOOK, __STR(name), __STR(moduleName), __STR(procName), (LPVOID*)&name, (LPVOID)CONCAT2(__autohookfunc, name)); \ +} \ + class ManualHook { public: @@ -213,8 +260,9 @@ class ManualHook public: ManualHook() = delete; + ManualHook(const char* funcName, LPVOID func); ManualHook(const char* funcName, LPVOID* orig, LPVOID func); - bool Dispatch(LPVOID addr); + bool Dispatch(LPVOID addr, LPVOID* orig = nullptr); }; // hook a function to be dispatched manually later @@ -223,4 +271,10 @@ namespace { \ type(*callingConvention originalFunc) args; \ type callingConvention CONCAT2(__manualhookfunc, varName) args func \ } \ -ManualHook<type(*callingConvention)args> varName = ManualHook<type(*callingConvention)args>(varName, (LPVOID*)&originalFunc, (LPVOID)CONCAT2(__manualhookfunc, varName)); \ +ManualHook varName = ManualHook(__STR(varName), (LPVOID*)&originalFunc, (LPVOID)CONCAT2(__manualhookfunc, varName)); \ + +#define HOOK_NOORIG(varName, type, callingConvention, args, func) \ +namespace { \ + type callingConvention CONCAT2(__manualhookfunc, varName) args func \ +} \ +ManualHook varName = ManualHook(__STR(varName), (LPVOID)CONCAT2(__manualhookfunc, varName)); \
\ No newline at end of file diff --git a/NorthstarDedicatedTest/host.cpp b/NorthstarDedicatedTest/host.cpp index fcc148b0..5221395a 100644 --- a/NorthstarDedicatedTest/host.cpp +++ b/NorthstarDedicatedTest/host.cpp @@ -5,9 +5,10 @@ #include "printmaps.h" #include "r2engine.h" -typedef void (*Host_InitType)(bool bDedicated); -Host_InitType Host_Init; -void Host_InitHook(bool bDedicated) +AUTOHOOK_INIT() + +AUTOHOOK(Host_Init, engine.dll + 0x155EA0, +void,, (bool bDedicated), { spdlog::info("Host_Init()"); Host_Init(bDedicated); @@ -62,10 +63,9 @@ void Host_InitHook(bool bDedicated) R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", R2::cmd_source_t::kCommandSrcCode); else R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", R2::cmd_source_t::kCommandSrcCode); -} +}) ON_DLL_LOAD("engine.dll", Host_Init, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x155EA0, &Host_InitHook, reinterpret_cast<LPVOID*>(&Host_Init)); + AUTOHOOK_DISPATCH() })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/hoststate.cpp b/NorthstarDedicatedTest/hoststate.cpp index 63d313f8..c3663a42 100644 --- a/NorthstarDedicatedTest/hoststate.cpp +++ b/NorthstarDedicatedTest/hoststate.cpp @@ -6,6 +6,8 @@ #include "tier0.h" #include "r2engine.h" +AUTOHOOK_INIT() + using namespace R2; // use the R2 namespace for game funcs @@ -14,9 +16,8 @@ namespace R2 CHostState* g_pHostState; } // namespace R2 -typedef void (*CHostState__State_NewGameType)(CHostState* hostState); -CHostState__State_NewGameType CHostState__State_NewGame; -void CHostState__State_NewGameHook(CHostState* hostState) +AUTOHOOK(CHostState__State_NewGame, engine.dll + 0x16E7D0, +void,, (CHostState* hostState), { spdlog::info("HostState: NewGame"); @@ -55,11 +56,10 @@ void CHostState__State_NewGameHook(CHostState* hostState) Cvar_ns_server_password->GetString()); g_ServerAuthenticationManager->StartPlayerAuthServer(); g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = false; -} +}) -typedef void (*CHostState__State_ChangeLevelMPType)(CHostState* hostState); -CHostState__State_ChangeLevelMPType CHostState__State_ChangeLevelMP; -void CHostState__State_ChangeLevelMPHook(CHostState* hostState) +AUTOHOOK(CHostState__State_ChangeLevelMP, engine.dll + 0x16E520, +void,, (CHostState* hostState), { spdlog::info("HostState: ChangeLevelMP"); @@ -78,11 +78,10 @@ void CHostState__State_ChangeLevelMPHook(CHostState* hostState) double dStartTime = Tier0::Plat_FloatTime(); CHostState__State_ChangeLevelMP(hostState); spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime); -} +}) -typedef void (*CHostState__State_GameShutdownType)(CHostState* hostState); -CHostState__State_GameShutdownType CHostState__State_GameShutdown; -void CHostState__State_GameShutdownHook(CHostState* hostState) +AUTOHOOK(CHostState__State_GameShutdown, engine.dll + 0x16E520, +void,, (CHostState* hostState), { spdlog::info("HostState: GameShutdown"); @@ -90,17 +89,11 @@ void CHostState__State_GameShutdownHook(CHostState* hostState) g_ServerAuthenticationManager->StopPlayerAuthServer(); CHostState__State_GameShutdown(hostState); -} +}) ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, [](HMODULE baseAddress) { - g_pHostState = (CHostState*)((char*)baseAddress + 0x7CF180); + AUTOHOOK_DISPATCH() - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x16E7D0, CHostState__State_NewGameHook, reinterpret_cast<LPVOID*>(&CHostState__State_NewGame)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x16E520, CHostState__State_ChangeLevelMPHook, reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelMP)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x16E640, CHostState__State_GameShutdownHook, reinterpret_cast<LPVOID*>(&CHostState__State_GameShutdown)); + g_pHostState = (CHostState*)((char*)baseAddress + 0x7CF180); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/keyvalues.cpp b/NorthstarDedicatedTest/keyvalues.cpp index 4ba11dad..1b711976 100644 --- a/NorthstarDedicatedTest/keyvalues.cpp +++ b/NorthstarDedicatedTest/keyvalues.cpp @@ -4,23 +4,7 @@ #include <fstream> -typedef char (*KeyValues__LoadFromBufferType)( - void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7); -KeyValues__LoadFromBufferType KeyValues__LoadFromBuffer; -char KeyValues__LoadFromBufferHook(void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7) -{ - static void* pSavedFilesystemPtr = nullptr; - - // this is just to allow playlists to get a valid pFileSystem ptr for kv building, other functions that call this particular overload of - // LoadFromBuffer seem to get called on network stuff exclusively not exactly sure what the address wanted here is, so just taking it - // from a function call that always happens before playlists is loaded - if (pFileSystem != nullptr) - pSavedFilesystemPtr = pFileSystem; - if (!pFileSystem && !strcmp(resourceName, "playlists")) - pFileSystem = pSavedFilesystemPtr; - - return KeyValues__LoadFromBuffer(self, resourceName, pBuffer, pFileSystem, a5, a6, a7); -} +AUTOHOOK_INIT() void ModManager::TryBuildKeyValues(const char* filename) { @@ -122,9 +106,23 @@ void ModManager::TryBuildKeyValues(const char* filename) m_modFiles[normalisedPath] = overrideFile; } +AUTOHOOK(KeyValues__LoadFromBuffer, engine.dll + 0x426C30, +char,, (void* self, const char* resourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7), +{ + static void* pSavedFilesystemPtr = nullptr; + + // this is just to allow playlists to get a valid pFileSystem ptr for kv building, other functions that call this particular overload of + // LoadFromBuffer seem to get called on network stuff exclusively not exactly sure what the address wanted here is, so just taking it + // from a function call that always happens before playlists is loaded + if (pFileSystem != nullptr) + pSavedFilesystemPtr = pFileSystem; + if (!pFileSystem && !strcmp(resourceName, "playlists")) + pFileSystem = pSavedFilesystemPtr; + + return KeyValues__LoadFromBuffer(self, resourceName, pBuffer, pFileSystem, a5, a6, a7); +}) + ON_DLL_LOAD("engine.dll", KeyValues, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x426C30, &KeyValues__LoadFromBufferHook, reinterpret_cast<LPVOID*>(&KeyValues__LoadFromBuffer)); + AUTOHOOK_DISPATCH() })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp index ac421077..4e708517 100644 --- a/NorthstarDedicatedTest/languagehooks.cpp +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -4,15 +4,12 @@ #include <filesystem> #include <regex> -namespace fs = std::filesystem; +AUTOHOOK_INIT() -typedef char* (*GetGameLanguageType)(); -char* GetGameLanguage(); +namespace fs = std::filesystem; typedef LANGID (*Tier0_DetectDefaultLanguageType)(); -GetGameLanguageType GetGameLanguageOriginal; - bool CheckLangAudioExists(char* lang) { std::string path {"r2\\sound\\general_"}; @@ -52,7 +49,8 @@ std::string GetAnyInstalledAudioLanguage() return "NO LANGUAGE DETECTED"; } -char* GetGameLanguageHook() +AUTOHOOK(GetGameLanguage, tier0.dll + 0xF560, +char*,, (), { auto tier0Handle = GetModuleHandleA("tier0.dll"); auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage"); @@ -79,7 +77,7 @@ char* GetGameLanguageHook() canOriginDictateLang = true; // let it try { - auto lang = GetGameLanguageOriginal(); + auto lang = GetGameLanguage(); if (!CheckLangAudioExists(lang)) { if (strcmp(lang, "russian") != @@ -97,7 +95,7 @@ char* GetGameLanguageHook() Tier0_DetectDefaultLanguageType(); // force the global in tier0 to be populated with language inferred from user's system rather than // defaulting to Russian canOriginDictateLang = false; // Origin has no say anymore, we will fallback to user's system setup language - auto lang = GetGameLanguageOriginal(); + auto lang = GetGameLanguage(); spdlog::info("Detected system language: {}", lang); if (!CheckLangAudioExists(lang)) { @@ -110,10 +108,9 @@ char* GetGameLanguageHook() } return lang; -} +}) ON_DLL_LOAD_CLIENT("tier0.dll", LanguageHooks, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xF560, &GetGameLanguageHook, reinterpret_cast<LPVOID*>(&GetGameLanguageOriginal)); + AUTOHOOK_DISPATCH() })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp index 87057a00..8c302df8 100644 --- a/NorthstarDedicatedTest/latencyflex.cpp +++ b/NorthstarDedicatedTest/latencyflex.cpp @@ -1,21 +1,22 @@ #include "pch.h" #include "convar.h" +AUTOHOOK_INIT() + ConVar* Cvar_r_latencyflex; HMODULE m_lfxModule {}; typedef void (*PFN_winelfx_WaitAndBeginFrame)(); PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame {}; -typedef void (*OnRenderStartType)(); -OnRenderStartType OnRenderStart; -void OnRenderStartHook() +AUTOHOOK(OnRenderStart, client.dll + 0x1952C0, +void,, (), { if (Cvar_r_latencyflex->GetInt()) m_winelfx_WaitAndBeginFrame(); OnRenderStart(); -} +}) ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, [](HMODULE baseAddress) { @@ -30,12 +31,11 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, [](HMODULE baseAd return; } + AUTOHOOK_DISPATCH() + m_winelfx_WaitAndBeginFrame = reinterpret_cast<PFN_winelfx_WaitAndBeginFrame>(reinterpret_cast<void*>(GetProcAddress(m_lfxModule, "winelfx_WaitAndBeginFrame"))); spdlog::info("LatencyFleX initialized."); Cvar_r_latencyflex = new ConVar("r_latencyflex", "1", FCVAR_ARCHIVE, "Whether or not to use LatencyFleX input latency reduction."); - - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1952C0, &OnRenderStartHook, reinterpret_cast<LPVOID*>(&OnRenderStart)); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index 909d34ce..0f0d76fe 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -1,250 +1,19 @@ #include "pch.h" #include "logging.h" -#include "sourceconsole.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "dedicated.h" #include "convar.h" +#include "configurables.h" +#include "bitbuf.h" +#include "spdlog/sinks/basic_file_sink.h" + #include <iomanip> #include <sstream> #include <Psapi.h> -#include <minidumpapiset.h> -#include "configurables.h" - -// This needs to be called after hooks are loaded so we can access the command line args -void CreateLogFiles() -{ - if (strstr(GetCommandLineA(), "-disablelogs")) - { - spdlog::default_logger()->set_level(spdlog::level::off); - } - else - { - // todo: might be good to delete logs that are too old - time_t time = std::time(nullptr); - tm currentTime = *std::localtime(&time); - std::stringstream stream; - - stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nslog%Y-%m-%d %H-%M-%S.txt").c_str()); - spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); - spdlog::flush_on(spdlog::level::info); - } -} - -long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) -{ - static bool logged = false; - if (logged) - return EXCEPTION_CONTINUE_SEARCH; - - if (!IsDebuggerPresent()) - { - const DWORD exceptionCode = exceptionInfo->ExceptionRecord->ExceptionCode; - if (exceptionCode != EXCEPTION_ACCESS_VIOLATION && exceptionCode != EXCEPTION_ARRAY_BOUNDS_EXCEEDED && - exceptionCode != EXCEPTION_DATATYPE_MISALIGNMENT && exceptionCode != EXCEPTION_FLT_DENORMAL_OPERAND && - exceptionCode != EXCEPTION_FLT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_FLT_INEXACT_RESULT && - exceptionCode != EXCEPTION_FLT_INVALID_OPERATION && exceptionCode != EXCEPTION_FLT_OVERFLOW && - exceptionCode != EXCEPTION_FLT_STACK_CHECK && exceptionCode != EXCEPTION_FLT_UNDERFLOW && - exceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION && exceptionCode != EXCEPTION_IN_PAGE_ERROR && - exceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO && exceptionCode != EXCEPTION_INT_OVERFLOW && - exceptionCode != EXCEPTION_INVALID_DISPOSITION && exceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION && - exceptionCode != EXCEPTION_PRIV_INSTRUCTION && exceptionCode != EXCEPTION_STACK_OVERFLOW) - return EXCEPTION_CONTINUE_SEARCH; - - std::stringstream exceptionCause; - exceptionCause << "Cause: "; - switch (exceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: - case EXCEPTION_IN_PAGE_ERROR: - { - exceptionCause << "Access Violation" << std::endl; - - auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; - auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; - - if (!exceptionInfo0) - exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1; - else if (exceptionInfo0 == 1) - exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; - else if (exceptionInfo0 == 8) - exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; - else - exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; - - break; - } - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - exceptionCause << "Array bounds exceeded"; - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - exceptionCause << "Datatype misalignment"; - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - exceptionCause << "Denormal operand"; - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - exceptionCause << "Divide by zero (float)"; - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - exceptionCause << "Divide by zero (int)"; - break; - case EXCEPTION_FLT_INEXACT_RESULT: - exceptionCause << "Inexact result"; - break; - case EXCEPTION_FLT_INVALID_OPERATION: - exceptionCause << "Invalid operation"; - break; - case EXCEPTION_FLT_OVERFLOW: - case EXCEPTION_INT_OVERFLOW: - exceptionCause << "Numeric overflow"; - break; - case EXCEPTION_FLT_UNDERFLOW: - exceptionCause << "Numeric underflow"; - break; - case EXCEPTION_FLT_STACK_CHECK: - exceptionCause << "Stack check"; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - exceptionCause << "Illegal instruction"; - break; - case EXCEPTION_INVALID_DISPOSITION: - exceptionCause << "Invalid disposition"; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - exceptionCause << "Noncontinuable exception"; - break; - case EXCEPTION_PRIV_INSTRUCTION: - exceptionCause << "Priviledged instruction"; - break; - case EXCEPTION_STACK_OVERFLOW: - exceptionCause << "Stack overflow"; - break; - default: - exceptionCause << "Unknown"; - break; - } - - void* exceptionAddress = exceptionInfo->ExceptionRecord->ExceptionAddress; - - HMODULE crashedModuleHandle; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(exceptionAddress), &crashedModuleHandle); - - MODULEINFO crashedModuleInfo; - GetModuleInformation(GetCurrentProcess(), crashedModuleHandle, &crashedModuleInfo, sizeof(crashedModuleInfo)); - char crashedModuleFullName[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); - char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; - - DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); - CONTEXT* exceptionContext = exceptionInfo->ContextRecord; - - spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); - spdlog::error(exceptionCause.str()); - spdlog::error("At: {} + {}", crashedModuleName, (void*)crashedModuleOffset); - - PVOID framesToCapture[62]; - int frames = RtlCaptureStackBackTrace(0, 62, framesToCapture, NULL); - for (int i = 0; i < frames; i++) - { - HMODULE backtraceModuleHandle; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(framesToCapture[i]), &backtraceModuleHandle); - - char backtraceModuleFullName[MAX_PATH]; - GetModuleFileNameExA(GetCurrentProcess(), backtraceModuleHandle, backtraceModuleFullName, MAX_PATH); - char* backtraceModuleName = strrchr(backtraceModuleFullName, '\\') + 1; - - void* actualAddress = (void*)framesToCapture[i]; - void* relativeAddress = (void*)(uintptr_t(actualAddress) - uintptr_t(backtraceModuleHandle)); - - spdlog::error(" {} + {} ({})", backtraceModuleName, relativeAddress, actualAddress); - } - - spdlog::error("RAX: 0x{0:x}", exceptionContext->Rax); - spdlog::error("RBX: 0x{0:x}", exceptionContext->Rbx); - spdlog::error("RCX: 0x{0:x}", exceptionContext->Rcx); - spdlog::error("RDX: 0x{0:x}", exceptionContext->Rdx); - spdlog::error("RSI: 0x{0:x}", exceptionContext->Rsi); - spdlog::error("RDI: 0x{0:x}", exceptionContext->Rdi); - spdlog::error("RBP: 0x{0:x}", exceptionContext->Rbp); - spdlog::error("RSP: 0x{0:x}", exceptionContext->Rsp); - spdlog::error("R8: 0x{0:x}", exceptionContext->R8); - spdlog::error("R9: 0x{0:x}", exceptionContext->R9); - spdlog::error("R10: 0x{0:x}", exceptionContext->R10); - spdlog::error("R11: 0x{0:x}", exceptionContext->R11); - spdlog::error("R12: 0x{0:x}", exceptionContext->R12); - spdlog::error("R13: 0x{0:x}", exceptionContext->R13); - spdlog::error("R14: 0x{0:x}", exceptionContext->R14); - spdlog::error("R15: 0x{0:x}", exceptionContext->R15); - - time_t time = std::time(nullptr); - tm currentTime = *std::localtime(&time); - std::stringstream stream; - stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nsdump%Y-%m-%d %H-%M-%S.dmp").c_str()); - - auto hMinidumpFile = CreateFileA(stream.str().c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - if (hMinidumpFile) - { - MINIDUMP_EXCEPTION_INFORMATION dumpExceptionInfo; - dumpExceptionInfo.ThreadId = GetCurrentThreadId(); - dumpExceptionInfo.ExceptionPointers = exceptionInfo; - dumpExceptionInfo.ClientPointers = false; - - MiniDumpWriteDump( - GetCurrentProcess(), - GetCurrentProcessId(), - hMinidumpFile, - MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory), - &dumpExceptionInfo, - nullptr, - nullptr); - CloseHandle(hMinidumpFile); - } - else - spdlog::error("Failed to write minidump file {}!", stream.str()); - - if (!IsDedicatedServer()) - MessageBoxA( - 0, "Northstar has crashed! Crash info can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); - } - - logged = true; - return EXCEPTION_EXECUTE_HANDLER; -} - -HANDLE hExceptionFilter; - -BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode) -{ - switch (eventCode) - { - case CTRL_CLOSE_EVENT: - // User closed console, shut everything down - spdlog::info("Exiting due to console close..."); - RemoveVectoredExceptionHandler(hExceptionFilter); - exit(EXIT_SUCCESS); - return FALSE; - } - - return TRUE; -} - -void InitialiseLogging() -{ - hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); - - AllocConsole(); - // seem to cause issues with console log initialisation occasionally - //freopen("CONOUT$", "w", stdout); - //freopen("CONOUT$", "w", stderr); - spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); - - SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); -} +AUTOHOOK_INIT() ConVar* Cvar_spewlog_enable; -enum SpewType_t +enum class SpewType_t { SPEW_MESSAGE = 0, @@ -256,56 +25,23 @@ enum SpewType_t SPEW_TYPE_COUNT }; -typedef void (*EngineSpewFuncType)(); -EngineSpewFuncType EngineSpewFunc; +const std::unordered_map<SpewType_t, const char*> PrintSpewTypes = { + {SpewType_t::SPEW_MESSAGE, "SPEW_MESSAGE"}, + {SpewType_t::SPEW_WARNING, "SPEW_WARNING"}, + {SpewType_t::SPEW_ASSERT, "SPEW_ASSERT"}, + {SpewType_t::SPEW_ERROR, "SPEW_ERROR"}, + {SpewType_t::SPEW_LOG, "SPEW_LOG"} +}; -void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, va_list args) +AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80, +void,, (void* pEngineServer, SpewType_t type, const char* format, va_list args), { if (!Cvar_spewlog_enable->GetBool()) return; - const char* typeStr; - switch (type) - { - case SPEW_MESSAGE: - { - typeStr = "SPEW_MESSAGE"; - break; - } - - case SPEW_WARNING: - { - typeStr = "SPEW_WARNING"; - break; - } - - case SPEW_ASSERT: - { - typeStr = "SPEW_ASSERT"; - break; - } - - case SPEW_ERROR: - { - typeStr = "SPEW_ERROR"; - break; - } - - case SPEW_LOG: - { - typeStr = "SPEW_LOG"; - break; - } - - default: - { - typeStr = "SPEW_UNKNOWN"; - break; - } - } - + const char* typeStr = PrintSpewTypes.at(type); char formatted[2048] = {0}; - bool shouldFormat = true; + bool bShouldFormat = true; // because titanfall 2 is quite possibly the worst thing to yet exist, it sometimes gives invalid specifiers which will crash // ttf2sdk had a way to prevent them from crashing but it doesnt work in debug builds @@ -352,14 +88,14 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, default: { - shouldFormat = false; + bShouldFormat = false; break; } } } } - if (shouldFormat) + if (bShouldFormat) vsnprintf(formatted, sizeof(formatted), format, args); else { @@ -371,12 +107,11 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, formatted[endpos - 1] = '\0'; // cut off repeated newline spdlog::info("[SERVER {}] {}", typeStr, formatted); -} - -typedef void (*Status_ConMsg_Type)(const char* text, ...); -Status_ConMsg_Type Status_ConMsg_Original; +}) -void Status_ConMsg_Hook(const char* text, ...) +// used for printing the output of status +AUTOHOOK(Status_ConMsg, engine.dll + 0x15ABD0, +void,, (const char* text, ...), { char formatted[2048]; va_list list; @@ -390,11 +125,10 @@ void Status_ConMsg_Hook(const char* text, ...) formatted[endpos - 1] = '\0'; // cut off repeated newline spdlog::info(formatted); -} +}) -typedef bool (*CClientState_ProcessPrint_Type)(__int64 thisptr, __int64 msg); -CClientState_ProcessPrint_Type CClientState_ProcessPrint_Original; -bool CClientState_ProcessPrint_Hook(__int64 thisptr, __int64 msg) +AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530, +bool,, (__int64 thisptr, __int64 msg), { char* text = *(char**)(msg + 0x20); @@ -404,34 +138,10 @@ bool CClientState_ProcessPrint_Hook(__int64 thisptr, __int64 msg) spdlog::info(text); return true; -} - -ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, [](HMODULE baseAddress) -{ - HookEnabler hook; - - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x11CA80, EngineSpewFuncHook, reinterpret_cast<LPVOID*>(&EngineSpewFunc)); - - // Hook print function that status concmd uses to actually print data - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x15ABD0, Status_ConMsg_Hook, reinterpret_cast<LPVOID*>(&Status_ConMsg_Original)); - - // Hook CClientState::ProcessPrint - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x1A1530, - CClientState_ProcessPrint_Hook, - reinterpret_cast<LPVOID*>(&CClientState_ProcessPrint_Original)); - - Cvar_spewlog_enable = new ConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged"); }) -#include "bitbuf.h" - ConVar* Cvar_cl_showtextmsg; -typedef void (*TextMsg_Type)(__int64); -TextMsg_Type TextMsg_Original; - class ICenterPrint { public: @@ -444,11 +154,20 @@ class ICenterPrint virtual void SetTextColor(int r, int g, int b, int a) = 0; }; -ICenterPrint* internalCenterPrint = NULL; +ICenterPrint* pInternalCenterPrint = NULL; -void TextMsgHook(BFRead* msg) +enum class TextMsgPrintType_t { - int msg_dest = msg->ReadByte(); + HUD_PRINTNOTIFY = 1, + HUD_PRINTCONSOLE, + HUD_PRINTTALK, + HUD_PRINTCENTER +}; + +AUTOHOOK(TextMsg, client.dll + 0x198710, +void,, (BFRead* msg), +{ + TextMsgPrintType_t msg_dest = (TextMsgPrintType_t)msg->ReadByte(); char text[256]; msg->ReadString(text, sizeof(text)); @@ -458,29 +177,64 @@ void TextMsgHook(BFRead* msg) switch (msg_dest) { - case 4: // HUD_PRINTCENTER - internalCenterPrint->Print(text); + case TextMsgPrintType_t::HUD_PRINTCENTER: + pInternalCenterPrint->Print(text); break; + default: spdlog::warn("Unimplemented TextMsg type {}! printing to console", msg_dest); [[fallthrough]]; - case 2: // HUD_PRINTCONSOLE + + case TextMsgPrintType_t::HUD_PRINTCONSOLE: auto endpos = strlen(text); if (text[endpos - 1] == '\n') text[endpos - 1] = '\0'; // cut off repeated newline + spdlog::info(text); break; } +}) + +// This needs to be called after hooks are loaded so we can access the command line args +void CreateLogFiles() +{ + if (strstr(GetCommandLineA(), "-disablelogs")) + { + spdlog::default_logger()->set_level(spdlog::level::off); + } + else + { + // todo: might be good to delete logs that are too old + time_t time = std::time(nullptr); + tm currentTime = *std::localtime(&time); + std::stringstream stream; + + stream << std::put_time(¤tTime, (GetNorthstarPrefix() + "/logs/nslog%Y-%m-%d %H-%M-%S.txt").c_str()); + spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); + spdlog::flush_on(spdlog::level::info); + } } -ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientPrintHooks, ConVar, [](HMODULE baseAddress) +void InitialiseLogging() +{ + AllocConsole(); + // seem to cause issues with console log initialisation occasionally + // freopen("CONOUT$", "w", stdout); + // freopen("CONOUT$", "w", stderr); + spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); +} + +ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, [](HMODULE baseAddress) { - HookEnabler hook; + AUTOHOOK_DISPATCH_MODULE(engine.dll) - internalCenterPrint = (ICenterPrint*)((char*)baseAddress + 0x216E940); + Cvar_spewlog_enable = new ConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged"); +}) - // "TextMsg" usermessage - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x198710, TextMsgHook, reinterpret_cast<LPVOID*>(&TextMsg_Original)); +ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientPrintHooks, ConVar, [](HMODULE baseAddress) +{ + AUTOHOOK_DISPATCH_MODULE(client.dll) Cvar_cl_showtextmsg = new ConVar("cl_showtextmsg", "1", FCVAR_NONE, "Enable/disable text messages printing on the screen."); + pInternalCenterPrint = (ICenterPrint*)((char*)baseAddress + 0x216E940); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/modlocalisation.cpp b/NorthstarDedicatedTest/modlocalisation.cpp index 85df8902..ad01192d 100644 --- a/NorthstarDedicatedTest/modlocalisation.cpp +++ b/NorthstarDedicatedTest/modlocalisation.cpp @@ -1,9 +1,10 @@ #include "pch.h" #include "modmanager.h" -typedef bool (*AddLocalisationFileType)(void* pVguiLocalize, const char* path, const char* pathId, char unknown); -AddLocalisationFileType AddLocalisationFile; -bool AddLocalisationFileHook(void* pVguiLocalize, const char* path, const char* pathId, char unknown) +AUTOHOOK_INIT() + +AUTOHOOK(AddLocalisationFile, localize.dll + 0x6D80, +bool,, (void* pVguiLocalize, const char* path, const char* pathId, char unknown), { static bool bLoadModLocalisationFiles = true; bool ret = AddLocalisationFile(pVguiLocalize, path, pathId, unknown); @@ -24,10 +25,9 @@ bool AddLocalisationFileHook(void* pVguiLocalize, const char* path, const char* bLoadModLocalisationFiles = true; return ret; -} +}) ON_DLL_LOAD_CLIENT("localize.dll", Localize, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x6D80, AddLocalisationFileHook, reinterpret_cast<LPVOID*>(&AddLocalisationFile)); + AUTOHOOK_DISPATCH() })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h index d1ee9c74..f6faa2c1 100644 --- a/NorthstarDedicatedTest/pch.h +++ b/NorthstarDedicatedTest/pch.h @@ -8,8 +8,6 @@ #define _WINSOCK_DEPRECATED_NO_WARNINGS // temp because i'm very lazy and want to use inet_addr, remove later #define RAPIDJSON_HAS_STDSTRING 1 -// httplib ssl - // add headers that you want to pre-compile here #include "memalloc.h" diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp index 0e0ed3c2..67e3a821 100644 --- a/NorthstarDedicatedTest/playlist.cpp +++ b/NorthstarDedicatedTest/playlist.cpp @@ -10,53 +10,62 @@ AUTOHOOK_INIT() // use the R2 namespace for game funcs namespace R2 { - GetCurrentPlaylistNameType GetCurrentPlaylistName; - SetCurrentPlaylistType SetCurrentPlaylist; - SetPlaylistVarOverrideType SetPlaylistVarOverride; - GetCurrentPlaylistVarType GetCurrentPlaylistVar; + const char* (*GetCurrentPlaylistName)(); + void (*SetCurrentPlaylist)(const char* pPlaylistName); + void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue); + const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides); } // namespace R2 ConVar* Cvar_ns_use_clc_SetPlaylistVarOverride; -typedef char (*Onclc_SetPlaylistVarOverrideType)(void* a1, void* a2); -Onclc_SetPlaylistVarOverrideType Onclc_SetPlaylistVarOverride; -char Onclc_SetPlaylistVarOverrideHook(void* a1, void* a2) +AUTOHOOK(clc_SetPlaylistVarOverride__Process, engine.dll + 0x222180, +char,, (void* a1, void* a2), { // the private_match playlist is the only situation where there should be any legitimate sending of this netmessage - // todo: check mp_lobby here too + // todo: check map == mp_lobby here too if (!Cvar_ns_use_clc_SetPlaylistVarOverride->GetBool() || strcmp(R2::GetCurrentPlaylistName(), "private_match")) return 1; - return Onclc_SetPlaylistVarOverride(a1, a2); -} + return clc_SetPlaylistVarOverride__Process(a1, a2); +}) + +AUTOHOOK(SetCurrentPlaylist, engine.dll + 0x18EB20, +void,, (const char* pPlaylistName), +{ + SetCurrentPlaylist(pPlaylistName); + spdlog::info("Set playlist to {}", pPlaylistName); +}) -void SetPlaylistVarOverrideHook(const char* varName, const char* value) +AUTOHOOK(SetPlaylistVarOverride, engine.dll + 0x18ED00, +void,, (const char* pVarName, const char* pValue), { - if (strlen(value) >= 64) + if (strlen(pValue) >= 64) return; - R2::SetPlaylistVarOverride(varName, value); -} + R2::SetPlaylistVarOverride(pVarName, pValue); +}) -const char* GetCurrentPlaylistVarHook(const char* varName, bool useOverrides) +AUTOHOOK(GetCurrentPlaylistVar, engine.dll + 0x18C680, +const char*,, (const char* pVarName, bool bUseOverrides), { - if (!useOverrides && !strcmp(varName, "max_players")) - useOverrides = true; + if (!bUseOverrides && !strcmp(pVarName, "max_players")) + bUseOverrides = true; + + return R2::GetCurrentPlaylistVar(pVarName, bUseOverrides); +}) - return R2::GetCurrentPlaylistVar(varName, useOverrides); -} -typedef int (*GetCurrentGamemodeMaxPlayersType)(); -GetCurrentGamemodeMaxPlayersType GetCurrentGamemodeMaxPlayers; -int GetCurrentGamemodeMaxPlayersHook() +AUTOHOOK(GetCurrentGamemodeMaxPlayers, engine.dll + 0x18C430, +int,, (), { - const char* maxPlayersStr = R2::GetCurrentPlaylistVar("max_players", 0); - if (!maxPlayersStr) + const char* pMaxPlayers = R2::GetCurrentPlaylistVar("max_players", 0); + if (!pMaxPlayers) return GetCurrentGamemodeMaxPlayers(); - int maxPlayers = atoi(maxPlayersStr); - return maxPlayers; -} + int iMaxPlayers = atoi(pMaxPlayers); + return iMaxPlayers; +}) + void ConCommand_playlist(const CCommand& args) { @@ -77,16 +86,18 @@ void ConCommand_setplaylistvaroverride(const CCommand& args) ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, ConCommand, [](HMODULE baseAddress) { + AUTOHOOK_DISPATCH() + + R2::GetCurrentPlaylistName = (const char* (*)())((char*)baseAddress + 0x18C640); + R2::SetCurrentPlaylist = (void (*)(const char*))((char*)baseAddress + 0x18EB20); + R2::SetPlaylistVarOverride = (void (*)(const char*, const char*))((char*)baseAddress + 0x18ED00); + R2::GetCurrentPlaylistVar = (const char* (*)(const char*, bool))((char*)baseAddress + 0x18C680); + // playlist is the name of the command on respawn servers, but we already use setplaylist so can't get rid of it RegisterConCommand("playlist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); RegisterConCommand("setplaylist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); RegisterConCommand("setplaylistvaroverrides", ConCommand_setplaylistvaroverride, "sets a playlist var override", FCVAR_NONE); - R2::GetCurrentPlaylistName = (R2::GetCurrentPlaylistNameType)((char*)baseAddress + 0x18C640); - R2::SetCurrentPlaylist = (R2::SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20); - R2::SetPlaylistVarOverride = (R2::SetPlaylistVarOverrideType)((char*)baseAddress + 0x18ED00); - R2::GetCurrentPlaylistVar = (R2::GetCurrentPlaylistVarType)((char*)baseAddress + 0x18C680); - // note: clc_SetPlaylistVarOverride is pretty insecure, since it allows for entirely arbitrary playlist var overrides to be sent to the // server, this is somewhat restricted on custom servers to prevent it being done outside of private matches, but ideally it should be // disabled altogether, since the custom menus won't use it anyway this should only really be accepted if you want vanilla client @@ -94,25 +105,10 @@ ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, ConCommand, [](HMODULE baseAdd Cvar_ns_use_clc_SetPlaylistVarOverride = new ConVar( "ns_use_clc_SetPlaylistVarOverride", "0", FCVAR_GAMEDLL, "Whether the server should accept clc_SetPlaylistVarOverride messages"); - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x222180, &Onclc_SetPlaylistVarOverrideHook, reinterpret_cast<LPVOID*>(&Onclc_SetPlaylistVarOverride)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x18ED00, &SetPlaylistVarOverrideHook, reinterpret_cast<LPVOID*>(&R2::SetPlaylistVarOverride)); - ENABLER_CREATEHOOK( - hook, (char*)baseAddress + 0x18C680, &GetCurrentPlaylistVarHook, reinterpret_cast<LPVOID*>(&R2::GetCurrentPlaylistVar)); - ENABLER_CREATEHOOK( - hook, - (char*)baseAddress + 0x18C430, - &GetCurrentGamemodeMaxPlayersHook, - reinterpret_cast<LPVOID*>(&GetCurrentGamemodeMaxPlayers)); - - uintptr_t ba = (uintptr_t)baseAddress; - // patch to prevent clc_SetPlaylistVarOverride from being able to crash servers if we reach max overrides due to a call to Error (why is // this possible respawn, wtf) todo: add a warning for this - NSMem::BytePatch(ba + 0x18ED8D, "C3"); + NSMem::BytePatch((uintptr_t)baseAddress + 0x18ED8D, "C3"); // patch to allow setplaylistvaroverride to be called before map init on dedicated and private match launched through the game - NSMem::NOP(ba + 0x18ED17, 6); + NSMem::NOP((uintptr_t)baseAddress + 0x18ED17, 6); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/playlist.h b/NorthstarDedicatedTest/playlist.h index 4ad48ee9..c77b37d9 100644 --- a/NorthstarDedicatedTest/playlist.h +++ b/NorthstarDedicatedTest/playlist.h @@ -3,15 +3,8 @@ // use the R2 namespace for game funcs namespace R2 { - typedef const char* (*GetCurrentPlaylistNameType)(); - extern GetCurrentPlaylistNameType GetCurrentPlaylistName; - - typedef void (*SetCurrentPlaylistType)(const char* playlistName); - extern SetCurrentPlaylistType SetCurrentPlaylist; - - typedef void (*SetPlaylistVarOverrideType)(const char* varName, const char* value); - extern SetPlaylistVarOverrideType SetPlaylistVarOverride; - - typedef const char* (*GetCurrentPlaylistVarType)(const char* varName, bool useOverrides); - extern GetCurrentPlaylistVarType GetCurrentPlaylistVar; + extern const char* (*GetCurrentPlaylistName)(); + extern void (*SetCurrentPlaylist)(const char* pPlaylistName); + extern void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue); + extern const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides); } // namespace R2 diff --git a/NorthstarDedicatedTest/rpakfilesystem.cpp b/NorthstarDedicatedTest/rpakfilesystem.cpp index e7c88674..bba69ff3 100644 --- a/NorthstarDedicatedTest/rpakfilesystem.cpp +++ b/NorthstarDedicatedTest/rpakfilesystem.cpp @@ -3,21 +3,18 @@ #include "modmanager.h" #include "dedicated.h" -typedef void* (*LoadPakSyncType)(const char* path, void* unknownSingleton, int flags); -typedef int (*LoadPakAsyncType)(const char* path, void* unknownSingleton, int flags, void* callback0, void* callback1); -typedef void* (*UnloadPakType)(int pakHandle, void* callback); -typedef void* (*ReadFullFileFromDiskType)(const char* requestedPath, void* a2); +AUTOHOOK_INIT() // there are more i'm just too lazy to add struct PakLoadFuncs { void* unk0[2]; - LoadPakSyncType LoadPakSync; - LoadPakAsyncType LoadPakAsync; + void* (*LoadPakSync)(const char* pPath, void* unknownSingleton, int flags); + int (*LoadPakAsync)(const char* pPath, void* unknownSingleton, int flags, void* callback0, void* callback1); void* unk1[2]; - UnloadPakType UnloadPak; + void* (*UnloadPak)(int iPakHandle, void* callback); void* unk2[17]; - ReadFullFileFromDiskType ReadFullFileFromDisk; + void* (*ReadFullFileFromDisk)(const char* pPath, void* a2); }; PakLoadFuncs* g_pakLoadApi; @@ -116,10 +113,10 @@ void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName) } } -LoadPakAsyncType LoadPakAsyncOriginal; -int LoadPakAsyncHook(char* path, void* unknownSingleton, int flags, void* callback0, void* callback1) +HOOK(LoadPakAsyncHook, LoadPakAsync, +int,, (char* pPath, void* unknownSingleton, int flags, void* callback0, void* callback1), { - HandlePakAliases(&path); + HandlePakAliases(&pPath); bool bNeedToFreePakName = false; @@ -127,13 +124,13 @@ int LoadPakAsyncHook(char* path, void* unknownSingleton, int flags, void* callba if (bShouldLoadPaks) { // make a copy of the path for comparing to determine whether we should load this pak on dedi, before it could get overwritten by LoadCustomMapPaks - std::string originalPath(path); + std::string originalPath(pPath); // disable preloading while we're doing this bShouldLoadPaks = false; LoadPreloadPaks(); - LoadCustomMapPaks(&path, &bNeedToFreePakName); + LoadCustomMapPaks(&pPath, &bNeedToFreePakName); bShouldLoadPaks = true; @@ -146,17 +143,17 @@ int LoadPakAsyncHook(char* path, void* unknownSingleton, int flags, void* callba } } - int ret = LoadPakAsyncOriginal(path, unknownSingleton, flags, callback0, callback1); - spdlog::info("LoadPakAsync {} {}", path, ret); + int ret = LoadPakAsync(pPath, unknownSingleton, flags, callback0, callback1); + spdlog::info("LoadPakAsync {} {}", pPath, ret); if (bNeedToFreePakName) - delete[] path; + delete[] pPath; return ret; -} +}) -UnloadPakType UnloadPakOriginal; -void* UnloadPakHook(int pakHandle, void* callback) +HOOK(UnloadPakHook, UnloadPak, +void*,, (int iPakHandle, void* callback), { static bool bShouldUnloadPaks = true; if (bShouldUnloadPaks) @@ -166,16 +163,17 @@ void* UnloadPakHook(int pakHandle, void* callback) bShouldUnloadPaks = true; } - spdlog::info("UnloadPak {}", pakHandle); - return UnloadPakOriginal(pakHandle, callback); -} + spdlog::info("UnloadPak {}", iPakHandle); + return UnloadPak(iPakHandle, callback); +}) // we hook this exclusively for resolving stbsp paths, but seemingly it's also used for other stuff like vpk and rpak loads -// possibly just async loading all together? -ReadFullFileFromDiskType ReadFullFileFromDiskOriginal; -void* ReadFullFileFromDiskHook(const char* requestedPath, void* a2) +// possibly just async loading altogether? + +HOOK(ReadFullFileFromDiskHook, ReadFullFileFromDisk, +void*, , (const char* pPath, void* a2), { - fs::path path(requestedPath); + fs::path path(pPath); char* allocatedNewPath = nullptr; if (path.extension() == ".stbsp") @@ -193,27 +191,28 @@ void* ReadFullFileFromDiskHook(const char* requestedPath, void* a2) allocatedNewPath = new char[newPath.size() + 1]; strncpy(allocatedNewPath, newPath.c_str(), newPath.size()); allocatedNewPath[newPath.size()] = '\0'; - requestedPath = allocatedNewPath; + pPath = allocatedNewPath; } } - void* ret = ReadFullFileFromDiskOriginal(requestedPath, a2); + void* ret = ReadFullFileFromDisk(pPath, a2); if (allocatedNewPath) delete[] allocatedNewPath; return ret; -} +}) + ON_DLL_LOAD("engine.dll", RpakFilesystem, [](HMODULE baseAddress) { + AUTOHOOK_DISPATCH(); + g_pPakLoadManager = new PakLoadManager; g_pakLoadApi = *(PakLoadFuncs**)((char*)baseAddress + 0x5BED78); pUnknownPakLoadSingleton = (void**)((char*)baseAddress + 0x7C5E20); - HookEnabler hook; - ENABLER_CREATEHOOK(hook, g_pakLoadApi->LoadPakAsync, &LoadPakAsyncHook, reinterpret_cast<LPVOID*>(&LoadPakAsyncOriginal)); - ENABLER_CREATEHOOK(hook, g_pakLoadApi->UnloadPak, &UnloadPakHook, reinterpret_cast<LPVOID*>(&UnloadPakOriginal)); - ENABLER_CREATEHOOK( - hook, g_pakLoadApi->ReadFullFileFromDisk, &ReadFullFileFromDiskHook, reinterpret_cast<LPVOID*>(&ReadFullFileFromDiskOriginal)); + LoadPakAsyncHook.Dispatch(g_pakLoadApi->LoadPakAsync); + UnloadPakHook.Dispatch(g_pakLoadApi->UnloadPak); + ReadFullFileFromDiskHook.Dispatch(g_pakLoadApi->ReadFullFileFromDisk); })
\ No newline at end of file diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp index 7627a2f1..55e78ad3 100644 --- a/NorthstarDedicatedTest/sourceconsole.cpp +++ b/NorthstarDedicatedTest/sourceconsole.cpp @@ -5,29 +5,41 @@ #include "concommand.h" #include "printcommand.h" -SourceInterface<CGameConsole>* g_SourceGameConsole; +SourceInterface<CGameConsole>* g_pSourceGameConsole; void ConCommand_toggleconsole(const CCommand& arg) { - if ((*g_SourceGameConsole)->IsConsoleVisible()) - (*g_SourceGameConsole)->Hide(); + if ((*g_pSourceGameConsole)->IsConsoleVisible()) + (*g_pSourceGameConsole)->Hide(); else - (*g_SourceGameConsole)->Activate(); + (*g_pSourceGameConsole)->Activate(); } void ConCommand_showconsole(const CCommand& arg) { - (*g_SourceGameConsole)->Activate(); + (*g_pSourceGameConsole)->Activate(); } void ConCommand_hideconsole(const CCommand& arg) { - (*g_SourceGameConsole)->Hide(); + (*g_pSourceGameConsole)->Hide(); } -typedef void (*OnCommandSubmittedType)(CConsoleDialog* consoleDialog, const char* pCommand); -OnCommandSubmittedType onCommandSubmittedOriginal; -void OnCommandSubmittedHook(CConsoleDialog* consoleDialog, const char* pCommand) +void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg) +{ + if (!(*g_pSourceGameConsole)->m_bInitialized) + return; + + spdlog::memory_buf_t formatted; + spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted); + (*g_pSourceGameConsole) + ->m_pConsole->m_pConsolePanel->ColorPrint(m_LogColours[msg.level], fmt::to_string(formatted).c_str()); +} + +void SourceConsoleSink::flush_() {} + +HOOK(OnCommandSubmittedHook, OnCommandSubmitted, +void,, (CConsoleDialog* consoleDialog, const char* pCommand), { consoleDialog->m_pConsolePanel->Print("] "); consoleDialog->m_pConsolePanel->Print(pCommand); @@ -35,55 +47,24 @@ void OnCommandSubmittedHook(CConsoleDialog* consoleDialog, const char* pCommand) TryPrintCvarHelpForCommand(pCommand); - onCommandSubmittedOriginal(consoleDialog, pCommand); -} + OnCommandSubmitted(consoleDialog, pCommand); +}) // called from sourceinterface.cpp in client createinterface hooks, on GameClientExports001 void InitialiseConsoleOnInterfaceCreation() { - (*g_SourceGameConsole)->Initialize(); + (*g_pSourceGameConsole)->Initialize(); + // hook OnCommandSubmitted so we print inputted commands + OnCommandSubmittedHook.Dispatch((*g_pSourceGameConsole)->m_pConsole->m_vtable->OnCommandSubmitted); auto consoleLogger = std::make_shared<SourceConsoleSink>(); consoleLogger->set_pattern("[%l] %v"); - spdlog::default_logger()->sinks().push_back(consoleLogger); - - // hook OnCommandSubmitted so we print inputted commands - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - (void*)((*g_SourceGameConsole)->m_pConsole->m_vtable->OnCommandSubmitted), - &OnCommandSubmittedHook, - reinterpret_cast<LPVOID*>(&onCommandSubmittedOriginal)); -} - -SourceConsoleSink::SourceConsoleSink() -{ - logColours.emplace(spdlog::level::trace, SourceColor(0, 255, 255, 255)); - logColours.emplace(spdlog::level::debug, SourceColor(0, 255, 255, 255)); - logColours.emplace(spdlog::level::info, SourceColor(255, 255, 255, 255)); - logColours.emplace(spdlog::level::warn, SourceColor(255, 255, 0, 255)); - logColours.emplace(spdlog::level::err, SourceColor(255, 0, 0, 255)); - logColours.emplace(spdlog::level::critical, SourceColor(255, 0, 0, 255)); - logColours.emplace(spdlog::level::off, SourceColor(0, 0, 0, 0)); -} - -void SourceConsoleSink::sink_it_(const spdlog::details::log_msg& msg) -{ - if (!(*g_SourceGameConsole)->m_bInitialized) - return; - - spdlog::memory_buf_t formatted; - spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted); - (*g_SourceGameConsole) - ->m_pConsole->m_pConsolePanel->ColorPrint(logColours[msg.level], fmt::to_string(formatted).c_str()); // todo needs colour support } -void SourceConsoleSink::flush_() {} - ON_DLL_LOAD_CLIENT_RELIESON("client.dll", SourceConsole, ConCommand, [](HMODULE baseAddress) { - g_SourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004"); + g_pSourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004"); RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "Show/hide the console.", FCVAR_DONTRECORD); RegisterConCommand("showconsole", ConCommand_showconsole, "Show the console.", FCVAR_DONTRECORD); RegisterConCommand("hideconsole", ConCommand_hideconsole, "Hide the console.", FCVAR_DONTRECORD); diff --git a/NorthstarDedicatedTest/sourceconsole.h b/NorthstarDedicatedTest/sourceconsole.h index e8d44a84..9f6c2bf8 100644 --- a/NorthstarDedicatedTest/sourceconsole.h +++ b/NorthstarDedicatedTest/sourceconsole.h @@ -86,16 +86,21 @@ class CGameConsole CConsoleDialog* m_pConsole; }; -extern SourceInterface<CGameConsole>* g_SourceGameConsole; +extern SourceInterface<CGameConsole>* g_pSourceGameConsole; // spdlog logger class SourceConsoleSink : public spdlog::sinks::base_sink<std::mutex> { private: - std::map<spdlog::level::level_enum, SourceColor> logColours; - - public: - SourceConsoleSink(); + std::map<spdlog::level::level_enum, SourceColor> m_LogColours = { + {spdlog::level::trace, SourceColor(0, 255, 255, 255)}, + {spdlog::level::debug, SourceColor(0, 255, 255, 255)}, + {spdlog::level::info, SourceColor(255, 255, 255, 255)}, + {spdlog::level::warn, SourceColor(255, 255, 0, 255)}, + {spdlog::level::err, SourceColor(255, 0, 0, 255)}, + {spdlog::level::critical, SourceColor(255, 0, 0, 255)}, + {spdlog::level::off, SourceColor(0, 0, 0, 0)} + }; protected: void sink_it_(const spdlog::details::log_msg& msg) override; diff --git a/NorthstarDedicatedTest/sourceinterface.cpp b/NorthstarDedicatedTest/sourceinterface.cpp index 05e83856..6528344d 100644 --- a/NorthstarDedicatedTest/sourceinterface.cpp +++ b/NorthstarDedicatedTest/sourceinterface.cpp @@ -2,64 +2,51 @@ #include "sourceinterface.h" #include "sourceconsole.h" +AUTOHOOK_INIT() + // really wanted to do a modular callback system here but honestly couldn't be bothered so hardcoding stuff for now: todo later -CreateInterfaceFn ClientCreateInterfaceOriginal; -void* ClientCreateInterfaceHook(const char* pName, int* pReturnCode) +AUTOHOOK_PROCADDRESS(ClientCreateInterface, "client.dll", CreateInterface, +void*,, (const char* pName, const int* pReturnCode), { - void* ret = ClientCreateInterfaceOriginal(pName, pReturnCode); + void* ret = ClientCreateInterface(pName, pReturnCode); spdlog::info("CreateInterface CLIENT {}", pName); if (!strcmp(pName, "GameClientExports001")) InitialiseConsoleOnInterfaceCreation(); return ret; -} +}) -CreateInterfaceFn ServerCreateInterfaceOriginal; -void* ServerCreateInterfaceHook(const char* pName, int* pReturnCode) +AUTOHOOK_PROCADDRESS(ServerCreateInterface, "server.dll", CreateInterface, +void*,, (const char* pName, const int* pReturnCode), { - void* ret = ServerCreateInterfaceOriginal(pName, pReturnCode); + void* ret = ServerCreateInterface(pName, pReturnCode); spdlog::info("CreateInterface SERVER {}", pName); return ret; -} +}) -CreateInterfaceFn EngineCreateInterfaceOriginal; -void* EngineCreateInterfaceHook(const char* pName, int* pReturnCode) +AUTOHOOK_PROCADDRESS(EngineCreateInterface, "engine.dll", CreateInterface, +void*,, (const char* pName, const int* pReturnCode), { - void* ret = EngineCreateInterfaceOriginal(pName, pReturnCode); + void* ret = EngineCreateInterface(pName, pReturnCode); spdlog::info("CreateInterface ENGINE {}", pName); return ret; -} +}) ON_DLL_LOAD("client.dll", ClientInterface, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - GetProcAddress(baseAddress, "CreateInterface"), - &ClientCreateInterfaceHook, - reinterpret_cast<LPVOID*>(&ClientCreateInterfaceOriginal)); + AUTOHOOK_DISPATCH_MODULE("client.dll") }) ON_DLL_LOAD("server.dll", ServerInterface, [](HMODULE baseAddress) { - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - GetProcAddress(baseAddress, "CreateInterface"), - &ServerCreateInterfaceHook, - reinterpret_cast<LPVOID*>(&ServerCreateInterfaceOriginal)); + AUTOHOOK_DISPATCH_MODULE("server.dll") }) ON_DLL_LOAD("engine.dll", EngineInterface, [](HMODULE baseAddress) -{ - HookEnabler hook; - ENABLER_CREATEHOOK( - hook, - GetProcAddress(baseAddress, "CreateInterface"), - &EngineCreateInterfaceHook, - reinterpret_cast<LPVOID*>(&EngineCreateInterfaceOriginal)); +{ + AUTOHOOK_DISPATCH_MODULE("engine.dll") })
\ No newline at end of file |