add some basic filesystem stuff and client/ui script callbacks

This commit is contained in:
BobTheBob 2021-07-21 16:40:01 +01:00
parent 8650a7520b
commit 8564c36b49
9 changed files with 234 additions and 9 deletions

View file

@ -174,6 +174,7 @@
<ClInclude Include="context.h" />
<ClInclude Include="convar.h" />
<ClInclude Include="dedicated.h" />
<ClInclude Include="filesystem.h" />
<ClInclude Include="hooks.h" />
<ClInclude Include="hookutils.h" />
<ClInclude Include="include\MinHook.h" />
@ -318,6 +319,7 @@
<ClCompile Include="convar.cpp" />
<ClCompile Include="dedicated.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="filesystem.cpp" />
<ClCompile Include="hooks.cpp" />
<ClCompile Include="hookutils.cpp" />
<ClCompile Include="logging.cpp" />

View file

@ -507,6 +507,9 @@
<ClInclude Include="include\rapidjson\msinttypes\stdint.h">
<Filter>Header Files\include\rapidjson\msinttypes</Filter>
</ClInclude>
<ClInclude Include="filesystem.h">
<Filter>Header Files\Shared</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@ -554,6 +557,9 @@
<ClCompile Include="modmanager.cpp">
<Filter>Source Files\Shared\Mods</Filter>
</ClCompile>
<ClCompile Include="filesystem.cpp">
<Filter>Source Files\Shared</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="include\spdlog\fmt\bundled\LICENSE.rst">

View file

@ -11,6 +11,15 @@ bool IsDedicated()
return false;
}
enum 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
};
void InitialiseDedicated(HMODULE engineAddress)
{
std::cout << "InitialiseDedicated()" << std::endl;

View file

@ -7,6 +7,7 @@
#include "logging.h"
#include "concommand.h"
#include "modmanager.h"
#include "filesystem.h"
#include <iostream>
bool initialised = false;
@ -55,6 +56,7 @@ void InitialiseNorthstar()
AddDllLoadCallback("server.dll", InitialiseServerSquirrel);
AddDllLoadCallback("filesystem_stdio.dll", InitialiseFilesystem);
// do this after all the other callbacks
AddDllLoadCallback("engine.dll", InitialiseModManager);

View file

@ -0,0 +1,53 @@
#include "pch.h"
#include "filesystem.h"
#include "hooks.h"
#include "hookutils.h"
#include "sourceinterface.h"
// hook forward declares
typedef FileHandle_t(*ReadFileFromVPKType)(VPKData* vpkInfo, __int64* b, const char* filename);
ReadFileFromVPKType readFileFromVPK;
FileHandle_t ReadFileFromVPKHook(VPKData* vpkInfo, __int64* b, const char* filename);
typedef bool(*ReadFromCacheType)(IFileSystem* filesystem, const char* path, void* result);
ReadFromCacheType readFromCache;
bool ReadFromCacheHook(IFileSystem* filesystem, const char* path, void* result);
SourceInterface<IFileSystem>* g_Filesystem;
void InitialiseFilesystem(HMODULE baseAddress)
{
g_Filesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017");
// create hooks
HookEnabler hook;
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x5CBA0, &ReadFileFromVPKHook, reinterpret_cast<LPVOID*>(&readFileFromVPK));
ENABLER_CREATEHOOK(hook, (*g_Filesystem)->m_vtable->ReadFromCache, &ReadFromCacheHook, reinterpret_cast<LPVOID*>(&readFromCache));
}
bool ShouldReplaceFile(const char* path)
{
return false;
}
FileHandle_t ReadFileFromVPKHook(VPKData* vpkInfo, __int64* b, const char* filename)
{
// move this to a convar at some point when we can read them in native
//spdlog::info("ReadFileFromVPKHook {} {}", filename, vpkInfo->path);
if (ShouldReplaceFile(filename))
{
*b = -1;
return b;
}
return readFileFromVPK(vpkInfo, b, filename);
}
bool ReadFromCacheHook(IFileSystem* filesystem, const char* path, void* result)
{
// move this to a convar at some point when we can read them in native
//spdlog::info("ReadFromCacheHook {}", path);
return readFromCache(filesystem, path, result);
}

View file

@ -0,0 +1,70 @@
#pragma once
#include "sourceinterface.h"
// taken from ttf2sdk
typedef void* FileHandle_t;
#pragma pack(push,1)
struct VPKFileEntry
{
char* directory;
char* filename;
char* extension;
unsigned char unknown[0x38];
};
#pragma pack(pop)
#pragma pack(push,1)
struct VPKData
{
unsigned char unknown[5];
char path[255];
unsigned char unknown2[0x134];
int32_t numEntries;
unsigned char unknown3[12];
VPKFileEntry* entries;
};
#pragma pack(pop)
enum SearchPathAdd_t
{
PATH_ADD_TO_HEAD, // First path searched
PATH_ADD_TO_TAIL, // Last path searched
};
class CSearchPath
{
public:
unsigned char unknown[0x18];
const char* debugPath;
};
class IFileSystem
{
public:
struct VTable
{
void* unknown[10];
void(*AddSearchPath) (IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType);
void* unknown2[84];
bool(*ReadFromCache) (IFileSystem* fileSystem, const char* path, void* result);
void* unknown3[15];
VPKData* (*MountVPK) (IFileSystem* fileSystem, const char* vpkPath);
};
struct VTable2
{
int(*Read) (IFileSystem::VTable2** fileSystem, void* pOutput, int size, FileHandle_t file);
void* unknown[1];
FileHandle_t(*Open) (IFileSystem::VTable2** fileSystem, const char* pFileName, const char* pOptions, const char* pathID, int64_t unknown);
void(*Close) (IFileSystem* fileSystem, FileHandle_t file);
void* unknown2[6];
bool(*FileExists)(IFileSystem::VTable2** fileSystem, const char* pFileName, const char* pPathID);
};
VTable* m_vtable;
VTable2* m_vtable2;
};
void InitialiseFilesystem(HMODULE baseAddress);
extern SourceInterface<IFileSystem>* g_Filesystem;

View file

@ -1,6 +1,5 @@
#include "pch.h"
#include "logging.h"
#include "context.h"
#include "sourceconsole.h"
#include "spdlog/sinks/basic_file_sink.h"
#include <iomanip>
@ -14,11 +13,14 @@ void InitialiseLogging()
spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v");
spdlog::flush_on(spdlog::level::info);
// log file stuff
// generate log file, format should be nslog%d-%m-%Y %H-%M-%S.txt in gamedir/R2Northstar/logs
// 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(&currentTime, "R2Northstar/logs/nslog%d-%m-%Y %H-%M-%S.txt");
// create logger
spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false));
}

View file

@ -220,6 +220,11 @@ void ModManager::LoadMods()
}
std::vector<Mod*> ModManager::GetMods()
{
return loadedMods;
}
void InitialiseModManager(HMODULE baseAddress)
{
g_ModManager = new ModManager();

View file

@ -4,6 +4,7 @@
#include "hookutils.h"
#include "sigscanning.h"
#include "concommand.h"
#include "modmanager.h"
#include <iostream>
// hook forward declarations
@ -23,10 +24,15 @@ DestroyVMType ClientDestroyVM; // only need a client one since ui doesn't have i
DestroyVMType ServerDestroyVM;
template<Context context> void DestroyVMHook(void* a1, void* sqvm);
typedef void (*SQCompileErrorType)(void* sqvm, const char* error, const char* file, int line, int column);
SQCompileErrorType ClientSQCompileError; // only need a client one since ui doesn't have its own func for this
SQCompileErrorType ServerSQCompileError;
template<Context context> void SQCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column);
typedef void(*ScriptCompileError)(void* sqvm, const char* error, const char* file, int line, int column);
ScriptCompileError ClientSQCompileError; // only need a client one since ui doesn't have its own func for this
ScriptCompileError ServerSQCompileError;
template<Context context> void ScriptCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column);
typedef char(*CallScriptInitCallbackType)(void* sqvm, const char* callback);
CallScriptInitCallbackType ClientCallScriptInitCallback;
CallScriptInitCallbackType ServerCallScriptInitCallback;
template<Context context> char CallScriptInitCallbackHook(void* sqvm, const char* callback);
sq_compilebufferType ClientSq_compilebuffer;
sq_compilebufferType ServerSq_compilebuffer;
@ -65,7 +71,8 @@ void InitialiseClientSquirrel(HMODULE baseAddress)
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26130, &CreateNewVMHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientCreateNewVM)); // client createnewvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E70, &DestroyVMHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientDestroyVM)); // client destroyvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x79A50, &SQCompileErrorHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientSQCompileError)); // client compileerror function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x79A50, &ScriptCompileErrorHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientSQCompileError)); // client compileerror function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x10190, &CallScriptInitCallbackHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientCallScriptInitCallback)); // client callscriptinitcallback function
}
void InitialiseServerSquirrel(HMODULE baseAddress)
@ -81,7 +88,7 @@ void InitialiseServerSquirrel(HMODULE baseAddress)
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1FE90, &SQPrintHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQPrint)); // server print function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x260E0, &CreateNewVMHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerCreateNewVM)); // server createnewvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E20, &DestroyVMHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerDestroyVM)); // server destroyvm function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x799E0, &SQCompileErrorHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x799E0, &ScriptCompileErrorHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function
RegisterConCommand("script", ExecuteCodeCommand<SERVER>, "Executes script code in the server vm", FCVAR_GAMEDLL);
}
@ -155,12 +162,12 @@ template<Context context> void DestroyVMHook(void* a1, void* sqvm)
spdlog::info("DestroyVM {} {}", GetContextName(realContext), sqvm);
}
template<Context context> void SQCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column)
template<Context context> void ScriptCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column)
{
// note: i think vanilla might actually show the script line that errored, might be nice to implement that if it's a thing
// look into client.dll+79540 for way better errors too
Context realContext = context;
Context realContext = context; // ui and client use the same function so we use this for prints
if (context == CLIENT && sqvm == g_UISquirrelManager->sqvm)
realContext = UI;
@ -173,6 +180,75 @@ template<Context context> void SQCompileErrorHook(void* sqvm, const char* error,
// though, that also has potential to be REALLY bad if we're compiling ui scripts lol
}
template<Context context> char CallScriptInitCallbackHook(void* sqvm, const char* callback)
{
char ret;
if (context == CLIENT)
{
Context realContext = context; // ui and client use the same function so we use this for prints
bool shouldCallCustomCallbacks = false;
// since we don't hook arbitrary callbacks yet, make sure we're only doing callbacks on inits
if (!strcmp(callback, "UICodeCallback_UIInit"))
{
realContext = UI;
shouldCallCustomCallbacks = true;
}
else if (!strcmp(callback, "ClientCodeCallback_MapSpawn"))
shouldCallCustomCallbacks = true;
// run before callbacks
if (shouldCallCustomCallbacks)
{
for (Mod* mod : g_ModManager->GetMods())
{
for (ModScript* script : mod->Scripts)
{
for (ModScriptCallback* modCallback : script->Callbacks)
{
if (modCallback->Context == realContext && modCallback->BeforeCallback.length())
{
spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback->BeforeCallback);
ClientCallScriptInitCallback(sqvm, modCallback->BeforeCallback.c_str());
}
}
}
}
}
spdlog::info("{} CodeCallback {} called", GetContextName(realContext), callback);
if (!shouldCallCustomCallbacks)
spdlog::info("Not executing custom callbacks for CodeCallback {}", callback);
ret = ClientCallScriptInitCallback(sqvm, callback);
// run after callbacks
if (shouldCallCustomCallbacks)
{
for (Mod* mod : g_ModManager->GetMods())
{
for (ModScript* script : mod->Scripts)
{
for (ModScriptCallback* modCallback : script->Callbacks)
{
if (modCallback->Context == realContext && modCallback->AfterCallback.length())
{
spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback->AfterCallback);
ClientCallScriptInitCallback(sqvm, modCallback->AfterCallback.c_str());
}
}
}
}
}
}
else if (context == SERVER)
{
// todo lol
}
return ret;
}
template<Context context> void ExecuteCodeCommand(const CCommand& args)
{
if (context == CLIENT)