diff options
Diffstat (limited to 'NorthstarDLL')
28 files changed, 1018 insertions, 251 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj index fcfbde57..f9f11ba7 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj +++ b/NorthstarDLL/NorthstarDLL.vcxproj @@ -91,7 +91,7 @@ <ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
- <LanguageStandard>stdcpp17</LanguageStandard>
+ <LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
@@ -614,10 +614,12 @@ <ClCompile Include="rpakfilesystem.cpp" />
<ClCompile Include="runframe.cpp" />
<ClCompile Include="scriptbrowserhooks.cpp" />
+ <ClCompile Include="scriptjson.cpp" />
<ClCompile Include="scriptmainmenupromos.cpp" />
<ClCompile Include="scriptmodmenu.cpp" />
<ClCompile Include="scriptserverbrowser.cpp" />
<ClCompile Include="scriptsrson.cpp" />
+ <ClCompile Include="scriptutility.cpp" />
<ClCompile Include="serverauthentication.cpp" />
<ClCompile Include="miscserverscript.cpp" />
<ClCompile Include="serverchathooks.cpp" />
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters index 555e6481..5f40a53c 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj.filters +++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters @@ -148,6 +148,9 @@ <Filter Include="Header Files\Dedicated Server">
<UniqueIdentifier>{0f1ba4c4-78ee-4b05-afa5-6f598063f5c1}</UniqueIdentifier>
</Filter>
+ <Filter Include="Source Files\Scripted">
+ <UniqueIdentifier>{a3afb0d7-6129-40e5-87a5-2f6758f7e4f6}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
@@ -1682,6 +1685,12 @@ <ClCompile Include="exploitfixes_lzss.cpp">
<Filter>Source Files\Exploit Fixes</Filter>
</ClCompile>
+ <ClCompile Include="scriptutility.cpp">
+ <Filter>Source Files\Scripted</Filter>
+ </ClCompile>
+ <ClCompile Include="scriptjson.cpp">
+ <Filter>Source Files\Scripted</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDLL/audio.cpp b/NorthstarDLL/audio.cpp index 30bf92fb..c298687a 100644 --- a/NorthstarDLL/audio.cpp +++ b/NorthstarDLL/audio.cpp @@ -196,7 +196,7 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa std::string pathString = file.path().string(); // Open the file. - std::basic_ifstream<uint8_t> wavStream(pathString, std::ios::binary); + std::ifstream wavStream(pathString, std::ios::binary); if (wavStream.fail()) { @@ -221,7 +221,7 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa [pathString, fileSize, data] { std::shared_lock lock(g_CustomAudioManager.m_loadingMutex); - std::basic_ifstream<uint8_t> wavStream(pathString, std::ios::binary); + std::ifstream wavStream(pathString, std::ios::binary); // would be weird if this got hit, since it would've worked previously if (wavStream.fail()) @@ -232,7 +232,7 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa // read from after the header first to preserve the empty header, then read the header last wavStream.seekg(0, std::ios::beg); - wavStream.read(data, fileSize); + wavStream.read(reinterpret_cast<char*>(data), fileSize); wavStream.close(); spdlog::info("Finished async read of audio sample {}", pathString); diff --git a/NorthstarDLL/bansystem.cpp b/NorthstarDLL/bansystem.cpp index cddc4837..4a5ef30b 100644 --- a/NorthstarDLL/bansystem.cpp +++ b/NorthstarDLL/bansystem.cpp @@ -11,21 +11,39 @@ #include <filesystem> const char* BANLIST_PATH_SUFFIX = "/banlist.txt"; +const char BANLIST_COMMENT_CHAR = '#'; ServerBanSystem* g_pBanSystem; void ServerBanSystem::OpenBanlist() { - std::ifstream enabledModsStream(GetNorthstarPrefix() + "/banlist.txt"); - std::stringstream enabledModsStringStream; + std::ifstream banlistStream(GetNorthstarPrefix() + "/banlist.txt"); - if (!enabledModsStream.fail()) + if (!banlistStream.fail()) { std::string line; - while (std::getline(enabledModsStream, line)) - m_vBannedUids.push_back(strtoull(line.c_str(), nullptr, 10)); + while (std::getline(banlistStream, line)) + { + // ignore line if first char is # or line is empty + if (line == "" || line.front() == BANLIST_COMMENT_CHAR) + continue; + + // remove tabs which shouldnt be there but maybe someone did the funny + line.erase(std::remove(line.begin(), line.end(), '\t'), line.end()); + // remove spaces to allow for spaces before uids + line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); + + // check if line is empty to allow for newlines in the file + if (line == "") + continue; + + // for inline comments like: 123123123 #banned for unfunny + std::string uid = line.substr(0, line.find(BANLIST_COMMENT_CHAR)); + + m_vBannedUids.push_back(strtoull(uid.c_str(), nullptr, 10)); + } - enabledModsStream.close(); + banlistStream.close(); } // open write stream for banlist @@ -43,6 +61,14 @@ void ServerBanSystem::ClearBanlist() void ServerBanSystem::BanUID(uint64_t uid) { + // checking if last char is \n to make sure uids arent getting fucked + std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt"); + std::string content((std::istreambuf_iterator<char>(fsBanlist)), (std::istreambuf_iterator<char>())); + fsBanlist.close(); + + if (content.back() != '\n') + m_sBanlistStream << std::endl; + m_vBannedUids.push_back(uid); m_sBanlistStream << std::to_string(uid) << std::endl; spdlog::info("{} was banned", uid); @@ -55,9 +81,76 @@ void ServerBanSystem::UnbanUID(uint64_t uid) return; m_vBannedUids.erase(findResult); + + std::vector<std::string> banlistText; + std::ifstream fs_readBanlist(GetNorthstarPrefix() + "/banlist.txt"); + + if (!fs_readBanlist.fail()) + { + std::string line; + while (std::getline(fs_readBanlist, line)) + { + // support for comments and newlines added in https://github.com/R2Northstar/NorthstarLauncher/pull/227 + + std::string modLine = line; // copy the line into a free var that we can fuck with, line will be the original + + // remove tabs which shouldnt be there but maybe someone did the funny + modLine.erase(std::remove(modLine.begin(), modLine.end(), '\t'), modLine.end()); + // remove spaces to allow for spaces before uids + modLine.erase(std::remove(modLine.begin(), modLine.end(), ' '), modLine.end()); + + // ignore line if first char is # or empty line, just add it + if (line.front() == BANLIST_COMMENT_CHAR || modLine == "") + { + banlistText.push_back(line); + continue; + } + + // for inline comments like: 123123123 #banned for unfunny + std::string lineUid = line.substr(0, line.find(BANLIST_COMMENT_CHAR)); + // have to erase spaces or else inline comments will fuck up the uid finding + lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), '\t'), lineUid.end()); + lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), ' '), lineUid.end()); + + // if the uid in the line is the uid we wanna unban + if (std::to_string(uid) == lineUid) + { + // comment the uid out + line.insert(0, "# "); + + // add a comment with unban date + // not necessary but i feel like this makes it better + std::time_t t = std::time(0); + std::tm* now = std::localtime(&t); + + std::ostringstream unbanComment; + + //{y}/{m}/{d} {h}:{m} + unbanComment << " # unban date: "; + unbanComment << now->tm_year + 1900 << "-"; // this lib is so fucking awful + unbanComment << std::setw(2) << std::setfill('0') << now->tm_mon + 1 << "-"; + unbanComment << std::setw(2) << std::setfill('0') << now->tm_mday << " "; + unbanComment << std::setw(2) << std::setfill('0') << now->tm_hour << ":"; + unbanComment << std::setw(2) << std::setfill('0') << now->tm_min; + + line.append(unbanComment.str()); + } + + banlistText.push_back(line); + } + + fs_readBanlist.close(); + } + + // open write stream for banlist // without append so we clear the file + if (m_sBanlistStream.is_open()) + m_sBanlistStream.close(); + m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary); + + for (std::string updatedLine : banlistText) + m_sBanlistStream << updatedLine << std::endl; + spdlog::info("{} was unbanned", uid); - // todo: this needs to erase from the banlist file - // atm unsure how to do this aside from just clearing and fully rewriting the file } bool ServerBanSystem::IsUIDAllowed(uint64_t uid) @@ -76,7 +169,7 @@ void ConCommand_ban(const CCommand& args) if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1))) { - g_pBanSystem->BanUID(strtoull((char*)player + 0xF500, nullptr, 10)); + g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10)); R2::CBaseClient__Disconnect(player, 1, "Banned from server"); break; } diff --git a/NorthstarDLL/chatcommand.cpp b/NorthstarDLL/chatcommand.cpp index 83b352ad..05404363 100644 --- a/NorthstarDLL/chatcommand.cpp +++ b/NorthstarDLL/chatcommand.cpp @@ -5,7 +5,7 @@ // note: isIngameChat is an int64 because the whole register the arg is stored in needs to be 0'd out to work // if isIngameChat is false, we use network chat instead -typedef void(__fastcall* ClientSayTextType)(void* a1, const char* message, __int64 isIngameChat, bool isTeamChat); +typedef void(__fastcall* ClientSayTextType)(void* a1, const char* message, uint64_t isIngameChat, bool isTeamChat); ClientSayTextType ClientSayText; void ConCommand_say(const CCommand& args) diff --git a/NorthstarDLL/clientvideooverrides.cpp b/NorthstarDLL/clientvideooverrides.cpp index 11ca3df4..6d5c9053 100644 --- a/NorthstarDLL/clientvideooverrides.cpp +++ b/NorthstarDLL/clientvideooverrides.cpp @@ -33,4 +33,8 @@ void*,, (const char* path, uint32_t flags)) ON_DLL_LOAD_CLIENT("client.dll", BinkVideo, (CModule module)) { AUTOHOOK_DISPATCH() + + // remove engine check for whether the bik we're trying to load exists in r2/media, as this will fail for biks in mods + // note: the check in engine is actually unnecessary, so it's just useless in practice and we lose nothing by removing it + module.Offset(0x459AD).NOP(6); } diff --git a/NorthstarDLL/convar.cpp b/NorthstarDLL/convar.cpp index 538f27bd..c8f63922 100644 --- a/NorthstarDLL/convar.cpp +++ b/NorthstarDLL/convar.cpp @@ -4,6 +4,8 @@ #include "convar.h" #include "sourceinterface.h" +#include <float.h> + typedef void (*ConVarRegisterType)( ConVar* pConVar, const char* pszName, diff --git a/NorthstarDLL/debugoverlay.cpp b/NorthstarDLL/debugoverlay.cpp index b97b2dd8..18975830 100644 --- a/NorthstarDLL/debugoverlay.cpp +++ b/NorthstarDLL/debugoverlay.cpp @@ -41,7 +41,7 @@ struct OverlayBase_t int m_nServerCount; // Latch server count, too float m_flEndTime; // When does this box go away OverlayBase_t* m_pNextOverlay; - __int64 m_pUnk; + void* m_pUnk; }; struct OverlayLine_t : public OverlayBase_t diff --git a/NorthstarDLL/dllmain.cpp b/NorthstarDLL/dllmain.cpp index 6e3c313e..aa808bf5 100644 --- a/NorthstarDLL/dllmain.cpp +++ b/NorthstarDLL/dllmain.cpp @@ -18,7 +18,7 @@ #include <filesystem> namespace fs = std::filesystem; -typedef void (*initPluginFuncPtr)(void* getPluginObject); + typedef void (*initPluginFuncPtr)(void* (*getPluginObject)(PluginObject)); BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { @@ -79,7 +79,7 @@ bool LoadPlugins() spdlog::info("Failed to load library {}: ", std::system_category().message(GetLastError())); continue; } - HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCE(101), MAKEINTRESOURCE(RT_RCDATA)); + HRSRC manifestResource = FindResourceW(datafile, MAKEINTRESOURCEW(101), MAKEINTRESOURCEW(RT_RCDATA)); if (manifestResource == NULL) { diff --git a/NorthstarDLL/exploitfixes.cpp b/NorthstarDLL/exploitfixes.cpp index a5d377b4..4ce9f351 100644 --- a/NorthstarDLL/exploitfixes.cpp +++ b/NorthstarDLL/exploitfixes.cpp @@ -33,35 +33,17 @@ bool ValidateFloats(float a, float b = 0, float c = 0) return !isnan(a) && !isnan(b) && !isnan(c); } -struct Vector +struct Float3 { - float x, y, z; + float vals[3]; - Vector(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {} - - bool IsValid() - { - return ValidateFloats(x, y, z); - } -}; - -struct Angle -{ - float pitch, yaw, roll; - - Angle(float pitch = 0, float yaw = 0, float roll = 0) : pitch(pitch), yaw(yaw), roll(roll) {} - - bool IsInvalid() + void MakeValid() { - return !ValidateFloats(pitch, yaw, roll); - - if (!ValidateFloats(pitch, yaw, roll)) - return false; - - return (pitch > 90 || pitch < -90) || (yaw > 180 || yaw < -180) || (roll > 180 || roll < -180); + for (auto& val : vals) + if (isnan(val)) + val = 0; } }; - // block bad netmessages // Servers can literally request a screenshot from any client, yeah no AUTOHOOK(CLC_Screenshot_WriteToBuffer, engine.dll + 0x22AF20, @@ -232,16 +214,16 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 ReadUsercmd(buf, pCmd_move, pCmd_from); // Now let's make sure the CMD we read isnt messed up to prevent numerous exploits (including server crashing) - struct __declspec(align(4)) SV_CUserCmd + struct alignas(4) SV_CUserCmd { DWORD command_number; DWORD tick_count; float command_time; - Angle worldViewAngles; + Float3 worldViewAngles; BYTE gap18[4]; - Angle localViewAngles; - Angle attackangles; - Vector move; + Float3 localViewAngles; + Float3 attackangles; + Float3 move; DWORD buttons; BYTE impulse; short weaponselect; @@ -249,8 +231,8 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 BYTE gap4C[24]; char headoffset; BYTE gap65[11]; - Vector cameraPos; - Angle cameraAngles; + Float3 cameraPos; + Float3 cameraAngles; BYTE gap88[4]; int tickSomething; DWORD dword90; @@ -265,29 +247,17 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 std::string BLOCK_PREFIX = "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; - if (cmd->worldViewAngles.IsInvalid()) - { - BLOCKED_INFO("CMD has invalid worldViewAngles"); - goto INVALID_CMD; - } + // fix invalid player angles + cmd->worldViewAngles.MakeValid(); + cmd->attackangles.MakeValid(); + cmd->localViewAngles.MakeValid(); - if (cmd->attackangles.IsInvalid()) - { - BLOCKED_INFO("CMD has invalid attackangles"); - goto INVALID_CMD; - } + // Fix invalid camera angles + cmd->cameraPos.MakeValid(); + cmd->cameraAngles.MakeValid(); - if (cmd->localViewAngles.IsInvalid()) - { - BLOCKED_INFO("CMD has invalid localViewAngles"); - goto INVALID_CMD; - } - - if (cmd->cameraAngles.IsInvalid()) - { - BLOCKED_INFO("CMD has invalid cameraAngles"); - goto INVALID_CMD; - } + // Fix invaid movement vector + cmd->move.MakeValid(); if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) { @@ -297,27 +267,15 @@ void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 goto INVALID_CMD; // No simulation of bogus-timed cmds } - if (!cmd->move.IsValid()) - { - BLOCKED_INFO("Invalid move vector"); - goto INVALID_CMD; - } - - if (!cmd->cameraPos.IsValid()) - { - BLOCKED_INFO("Invalid cameraPos"); // IIRC this can crash spectating clients or anyone watching replays - goto INVALID_CMD; - } - return; INVALID_CMD: // Fix any gameplay-affecting cmd properties // NOTE: Currently tickcount/frametime is set to 0, this ~shouldn't~ cause any problems - cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = Angle(0, 0, 0); + cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = {0, 0, 0}; cmd->tick_count = cmd->frameTime = 0; - cmd->move = cmd->cameraPos = Vector(0, 0, 0); + cmd->move = cmd->cameraPos = {0, 0, 0}; cmd->buttons = 0; cmd->meleetarget = 0; } diff --git a/NorthstarDLL/exploitfixes_utf8parser.cpp b/NorthstarDLL/exploitfixes_utf8parser.cpp index 84b7cbc6..581596a7 100644 --- a/NorthstarDLL/exploitfixes_utf8parser.cpp +++ b/NorthstarDLL/exploitfixes_utf8parser.cpp @@ -176,7 +176,13 @@ bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) // 48 89 5C 24 ? 48 89 static void* targetRetAddr = CModule("engine.dll").FindPattern("84 C0 75 2C 49 8B 16"); // only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues - if (_ReturnAddress() == targetRetAddr && !CheckUTF8Valid(a1, a2, strData)) + if ( +#ifdef _MSC_VER + _ReturnAddress() +#else + __builtin_return_address(0) +#endif + == targetRetAddr && !CheckUTF8Valid(a1, a2, strData)) return false; return Rson_ParseUTF8(a1, a2, strData); diff --git a/NorthstarDLL/filesystem.cpp b/NorthstarDLL/filesystem.cpp index 7fa65d17..9fba160f 100644 --- a/NorthstarDLL/filesystem.cpp +++ b/NorthstarDLL/filesystem.cpp @@ -115,7 +115,7 @@ bool,, (IFileSystem* filesystem, char* pPath, void* result)) // force modded files to be read from mods, not vpk AUTOHOOK(ReadFileFromVPK, filesystem_stdio.dll + 0x5CBA0, -FileHandle_t,, (VPKData* vpkInfo, __int64* b, char* filename)) +FileHandle_t,, (VPKData* vpkInfo, uint64_t* b, char* filename)) { // don't compile here because this is only ever called from OpenEx, which already compiles if (TryReplaceFile(filename, false)) diff --git a/NorthstarDLL/hoststate.cpp b/NorthstarDLL/hoststate.cpp index 287b4625..c0d6b3b9 100644 --- a/NorthstarDLL/hoststate.cpp +++ b/NorthstarDLL/hoststate.cpp @@ -39,6 +39,9 @@ void,, (CHostState* self)) if (g_pServerAuthentication->m_bNeedLocalAuthForNewgame) SetCurrentPlaylist("tdm"); + // don't require authentication on singleplayer startup + g_pServerAuthentication->m_bRequireClientAuth = strncmp(g_pHostState->m_levelName, "sp_", 3); + ServerStartingOrChangingMap(); double dStartTime = Tier0::Plat_FloatTime(); @@ -90,7 +93,6 @@ void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime)) // update server presence g_pServerPresence->RunFrame(flCurrentTime); } - } ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module)) diff --git a/NorthstarDLL/latencyflex.cpp b/NorthstarDLL/latencyflex.cpp index b9d232b2..7b5a8ebf 100644 --- a/NorthstarDLL/latencyflex.cpp +++ b/NorthstarDLL/latencyflex.cpp @@ -5,14 +5,12 @@ AUTOHOOK_INIT() ConVar* Cvar_r_latencyflex; -HMODULE m_lfxModule {}; -typedef void (*PFN_winelfx_WaitAndBeginFrame)(); -PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame {}; +void (*m_winelfx_WaitAndBeginFrame)(); AUTOHOOK(OnRenderStart, client.dll + 0x1952C0, void,, ()) { - if (Cvar_r_latencyflex->GetInt()) + if (Cvar_r_latencyflex->GetBool() && m_winelfx_WaitAndBeginFrame) m_winelfx_WaitAndBeginFrame(); OnRenderStart(); @@ -23,9 +21,15 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, (CModule module)) // Connect to the LatencyFleX service // LatencyFleX is an open source vendor agnostic replacement for Nvidia Reflex input latency reduction technology. // https://ishitatsuyuki.github.io/post/latencyflex/ - m_lfxModule = LoadLibraryA("latencyflex_wine.dll"); - - if (m_lfxModule == nullptr) + HMODULE pLfxModule; + + if (pLfxModule = LoadLibraryA("latencyflex_layer.dll")) + m_winelfx_WaitAndBeginFrame = reinterpret_cast<void (*)()>( + reinterpret_cast<void*>(GetProcAddress(pLfxModule, "lfx_WaitAndBeginFrame"))); + else if (pLfxModule = LoadLibraryA("latencyflex_wine.dll")) + m_winelfx_WaitAndBeginFrame = reinterpret_cast<void (*)()>( + reinterpret_cast<void*>(GetProcAddress(pLfxModule, "winelfx_WaitAndBeginFrame"))); + else { spdlog::info("Unable to load LatencyFleX library, LatencyFleX disabled."); return; @@ -33,9 +37,6 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, (CModule module)) 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."); } diff --git a/NorthstarDLL/logging.cpp b/NorthstarDLL/logging.cpp index d238394b..e82c2131 100644 --- a/NorthstarDLL/logging.cpp +++ b/NorthstarDLL/logging.cpp @@ -8,7 +8,6 @@ #include <iomanip> #include <sstream> -#include <Psapi.h> AUTOHOOK_INIT() @@ -127,7 +126,7 @@ void,, (const char* text, ...)) } AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530, -bool,, (__int64 thisptr, __int64 msg)) +bool,, (void* thisptr, uintptr_t msg)) { char* text = *(char**)(msg + 0x20); diff --git a/NorthstarDLL/masterserver.h b/NorthstarDLL/masterserver.h index efa938cc..5a4e2a91 100644 --- a/NorthstarDLL/masterserver.h +++ b/NorthstarDLL/masterserver.h @@ -1,7 +1,7 @@ #pragma once #include "convar.h" #include "serverpresence.h" -#include <WinSock2.h> +#include <winsock2.h> #include <string> #include <cstring> diff --git a/NorthstarDLL/maxplayers.cpp b/NorthstarDLL/maxplayers.cpp index c340541a..ba165bee 100644 --- a/NorthstarDLL/maxplayers.cpp +++ b/NorthstarDLL/maxplayers.cpp @@ -71,7 +71,7 @@ template <class T> void ChangeOffset(MemoryAddress addr, unsigned int offset) } AUTOHOOK(StringTables_CreateStringTable, engine.dll + 0x22E220, -void*,, (__int64 thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags)) +void*,, (void* thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags)) { // Change the amount of entries to account for a bigger player amount if (!strcmp(name, "userinfo")) @@ -181,7 +181,7 @@ void,, (bool a1, float a2)) v3 = *(unsigned char*)(g_pGlobals + 73); if (*(DWORD*)(qword_1814D9648 + 92) && - ((*(unsigned __int8(__fastcall**)(__int64))(*(__int64*)g_pEngineServer + 32i64))(g_pEngineServer) || + ((*(unsigned __int8(__fastcall**)(__int64))(*(__int64*)g_pEngineServer + 32))(g_pEngineServer) || !*(DWORD*)(qword_1814DA408 + 92)) && v3) { @@ -251,7 +251,7 @@ void,, (bool a1, float a2)) if (v23) v19 = 1; else - *v21 = 0i64; + *v21 = 0; } ++v20; ++v21; diff --git a/NorthstarDLL/memalloc.cpp b/NorthstarDLL/memalloc.cpp index f16aaed0..8c0fafc8 100644 --- a/NorthstarDLL/memalloc.cpp +++ b/NorthstarDLL/memalloc.cpp @@ -68,7 +68,7 @@ void* operator new(size_t n) return _malloc_base(n); } -void operator delete(void* p) +void operator delete(void* p) noexcept { _free_base(p); } // /FORCE:MULTIPLE diff --git a/NorthstarDLL/memalloc.h b/NorthstarDLL/memalloc.h index a1b16ad4..fdd6474b 100644 --- a/NorthstarDLL/memalloc.h +++ b/NorthstarDLL/memalloc.h @@ -11,7 +11,7 @@ extern "C" void _free_base(void* const block); extern "C" char* _strdup_base(const char* src); void* operator new(size_t n); -void operator delete(void* p); +void operator delete(void* p) noexcept; // void* malloc(size_t n); diff --git a/NorthstarDLL/modmanager.cpp b/NorthstarDLL/modmanager.cpp index 76280ef9..8ba883a3 100644 --- a/NorthstarDLL/modmanager.cpp +++ b/NorthstarDLL/modmanager.cpp @@ -231,6 +231,26 @@ Mod::Mod(fs::path modDir, char* jsonBuf) } } + if (modJson.HasMember("Dependencies") && modJson["Dependencies"].IsObject()) + { + for (auto v = modJson["Dependencies"].MemberBegin(); v != modJson["Dependencies"].MemberEnd(); v++) + { + if (!v->name.IsString() || !v->value.IsString()) + continue; + + spdlog::info("Constant {} defined by {} for mod {}", v->name.GetString(), Name, v->value.GetString()); + if (DependencyConstants.find(v->name.GetString()) != DependencyConstants.end() && + v->value.GetString() != DependencyConstants[v->name.GetString()]) + { + spdlog::error("A dependency constant with the same name already exists for another mod. Change the constant name."); + return; + } + + if (DependencyConstants.find(v->name.GetString()) == DependencyConstants.end()) + DependencyConstants.emplace(v->name.GetString(), v->value.GetString()); + } + } + m_bWasReadSuccessfully = true; } @@ -257,6 +277,8 @@ void ModManager::LoadMods() fs::remove_all(GetCompiledAssetsPath()); fs::create_directories(GetModFolderPath()); + m_DependencyConstants.clear(); + // read enabled mods cfg std::ifstream enabledModsStream(GetNorthstarPrefix() + "/enabledmods.json"); std::stringstream enabledModsStringStream; @@ -298,6 +320,19 @@ void ModManager::LoadMods() Mod mod(modDir, (char*)jsonStringStream.str().c_str()); + + for (auto& pair : mod.DependencyConstants) + { + if (m_DependencyConstants.find(pair.first) != m_DependencyConstants.end() && m_DependencyConstants[pair.first] != pair.second) + { + spdlog::error("Constant {} in mod {} already exists in another mod.", pair.first, mod.Name); + mod.m_bWasReadSuccessfully = false; + break; + } + if (m_DependencyConstants.find(pair.first) == m_DependencyConstants.end()) + m_DependencyConstants.emplace(pair); + } + if (m_bHasEnabledModsCfg && m_EnabledModsCfg.HasMember(mod.Name.c_str())) mod.m_bEnabled = m_EnabledModsCfg[mod.Name.c_str()].IsTrue(); else @@ -428,6 +463,12 @@ void ModManager::LoadMods() modPak.m_bAutoLoad = !bUseRpakJson || (dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() && dRpakJson["Preload"].HasMember(pakName) && dRpakJson["Preload"][pakName].IsTrue()); + + // postload things + if (!bUseRpakJson || + (dRpakJson.HasMember("Postload") && dRpakJson["Postload"].IsObject() && dRpakJson["Postload"].HasMember(pakName))) + modPak.m_sLoadAfterPak = dRpakJson["Postload"][pakName].GetString(); + modPak.m_sPakName = pakName; // not using atm because we need to resolve path to rpak diff --git a/NorthstarDLL/modmanager.h b/NorthstarDLL/modmanager.h index 4cd335ff..336ef804 100644 --- a/NorthstarDLL/modmanager.h +++ b/NorthstarDLL/modmanager.h @@ -57,6 +57,7 @@ struct ModRpakEntry public: bool m_bAutoLoad; std::string m_sPakName; + std::string m_sLoadAfterPak; }; class Mod @@ -102,6 +103,8 @@ class Mod std::unordered_map<std::string, std::string> RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite + std::unordered_map<std::string, std::string> DependencyConstants; + public: Mod(fs::path modPath, char* jsonBuf); }; @@ -127,6 +130,7 @@ class ModManager public: std::vector<Mod> m_LoadedMods; std::unordered_map<std::string, ModOverrideFile> m_ModFiles; + std::unordered_map<std::string, std::string> m_DependencyConstants; public: ModManager(); diff --git a/NorthstarDLL/pch.h b/NorthstarDLL/pch.h index 88835dd1..66543859 100644 --- a/NorthstarDLL/pch.h +++ b/NorthstarDLL/pch.h @@ -11,8 +11,8 @@ // add headers that you want to pre-compile here #include "memalloc.h" -#include <Windows.h> -#include <Psapi.h> +#include <windows.h> +#include <psapi.h> #include <set> #include <map> #include <filesystem> diff --git a/NorthstarDLL/rpakfilesystem.cpp b/NorthstarDLL/rpakfilesystem.cpp index bc2195ba..6c01a2e0 100644 --- a/NorthstarDLL/rpakfilesystem.cpp +++ b/NorthstarDLL/rpakfilesystem.cpp @@ -112,6 +112,23 @@ void LoadPreloadPaks() } } +void LoadPostloadPaks(const char* pPath) +{ + // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks + for (Mod& mod : g_pModManager->m_LoadedMods) + { + if (!mod.m_bEnabled) + continue; + + // need to get a relative path of mod to mod folder + fs::path modPakPath("./" / mod.m_ModDirectory / "paks"); + + for (ModRpakEntry& pak : mod.Rpaks) + if (pak.m_sLoadAfterPak == pPath) + g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT); + } +} + void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName) { // whether the vanilla game has this rpak @@ -192,6 +209,7 @@ int,, (char* pPath, void* unknownSingleton, int flags, void* pCallback0, void* p // trak the pak g_pPakLoadManager->TrackLoadedPak(ePakLoadSource::UNTRACKED, iPakHandle, nPathHash); + LoadPostloadPaks(pPath); if (bNeedToFreePakName) delete[] pPath; diff --git a/NorthstarDLL/rpakfilesystem.h b/NorthstarDLL/rpakfilesystem.h index f8774083..4a9a4343 100644 --- a/NorthstarDLL/rpakfilesystem.h +++ b/NorthstarDLL/rpakfilesystem.h @@ -1,38 +1,38 @@ -#pragma once
-
-enum class ePakLoadSource
-{
- UNTRACKED = -1, // not a pak we loaded, we shouldn't touch this one
-
- CONSTANT, // should be loaded at all times
- MAP // loaded from a map, should be unloaded when the map is unloaded
-};
-
-struct LoadedPak
-{
- ePakLoadSource m_nLoadSource;
- int m_nPakHandle;
- size_t m_nPakNameHash;
-};
-
-class PakLoadManager
-{
- private:
- std::map<int, LoadedPak> m_vLoadedPaks {};
- std::unordered_map<size_t, int> m_HashToPakHandle {};
-
- public:
- int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource);
- void UnloadPak(const int nPakHandle);
- void UnloadMapPaks();
-
- LoadedPak* TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash);
- void RemoveLoadedPak(int nPakHandle);
-
- LoadedPak* GetPakInfo(const int nPakHandle);
-
- int GetPakHandle(const size_t nPakNameHash);
- int GetPakHandle(const char* pPath);
-};
-
-extern PakLoadManager* g_pPakLoadManager;
+#pragma once + +enum class ePakLoadSource +{ + UNTRACKED = -1, // not a pak we loaded, we shouldn't touch this one + + CONSTANT, // should be loaded at all times + MAP // loaded from a map, should be unloaded when the map is unloaded +}; + +struct LoadedPak +{ + ePakLoadSource m_nLoadSource; + int m_nPakHandle; + size_t m_nPakNameHash; +}; + +class PakLoadManager +{ + private: + std::map<int, LoadedPak> m_vLoadedPaks {}; + std::unordered_map<size_t, int> m_HashToPakHandle {}; + + public: + int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource); + void UnloadPak(const int nPakHandle); + void UnloadMapPaks(); + + LoadedPak* TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash); + void RemoveLoadedPak(int nPakHandle); + + LoadedPak* GetPakInfo(const int nPakHandle); + + int GetPakHandle(const size_t nPakNameHash); + int GetPakHandle(const char* pPath); +}; + +extern PakLoadManager* g_pPakLoadManager; diff --git a/NorthstarDLL/serverauthentication.cpp b/NorthstarDLL/serverauthentication.cpp index 953321e6..e7a7ff00 100644 --- a/NorthstarDLL/serverauthentication.cpp +++ b/NorthstarDLL/serverauthentication.cpp @@ -265,7 +265,7 @@ void*,, ( } AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740, -bool,, (R2::CBaseClient* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7)) +bool,, (R2::CBaseClient* self, char* name, void* netchan_ptr_arg, char b_fake_player_arg, void* a5, char* Buffer, void* a7)) { // try changing name before all else g_pServerAuthentication->VerifyPlayerName(self, pNextPlayerToken, name); @@ -284,9 +284,8 @@ bool,, (R2::CBaseClient* self, char* name, __int64 netchan_ptr_arg, char b_fake_ if (strlen(name) >= 64) // fix for name overflow bug R2::CBaseClient__Disconnect(self, 1, "Invalid name"); - else if ( - !g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken) && - g_pMasterServerManager->m_bRequireClientAuth) + else if (!g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken) && + g_pServerAuthentication->m_bRequireClientAuth) R2::CBaseClient__Disconnect(self, 1, "Authentication Failed"); g_pServerAuthentication->AddPlayerData(self, pNextPlayerToken); diff --git a/NorthstarDLL/serverauthentication.h b/NorthstarDLL/serverauthentication.h index a4c9c470..b010e6b1 100644 --- a/NorthstarDLL/serverauthentication.h +++ b/NorthstarDLL/serverauthentication.h @@ -36,6 +36,7 @@ class ServerAuthenticationManager std::mutex m_AuthDataMutex; std::unordered_map<std::string, RemoteAuthData> m_RemoteAuthenticationData; std::unordered_map<R2::CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData; + bool m_bRequireClientAuth = true; bool m_bAllowDuplicateAccounts = false; bool m_bRunningPlayerAuthThread = false; bool m_bNeedLocalAuthForNewgame = false; diff --git a/NorthstarDLL/squirrel.cpp b/NorthstarDLL/squirrel.cpp index bede510e..8beee760 100644 --- a/NorthstarDLL/squirrel.cpp +++ b/NorthstarDLL/squirrel.cpp @@ -20,6 +20,178 @@ const char* GetContextName(ScriptContext context) } } +eSQReturnType SQReturnTypeFromString(const char* pReturnType) +{ + static const std::map<std::string, eSQReturnType> sqReturnTypeNameToString = { + {"bool", eSQReturnType::Boolean}, + {"float", eSQReturnType::Float}, + {"vector", eSQReturnType::Vector}, + {"int", eSQReturnType::Integer}, + {"entity", eSQReturnType::Entity}, + {"string", eSQReturnType::String}, + {"array", eSQReturnType::Arrays}, + {"asset", eSQReturnType::Asset}, + {"table", eSQReturnType::Table}}; + + if (sqReturnTypeNameToString.find(pReturnType) != sqReturnTypeNameToString.end()) + return sqReturnTypeNameToString.at(pReturnType); + else + return eSQReturnType::Default; // previous default value +} + +const char* SQTypeNameFromID(int iTypeId) +{ + switch (iTypeId) + { + case OT_ASSET: + return "asset"; + case OT_INTEGER: + return "int"; + case OT_BOOL: + return "bool"; + case SQOBJECT_NUMERIC: + return "float or int"; + case OT_NULL: + return "null"; + case OT_VECTOR: + return "vector"; + case 0: + return "var"; + case OT_USERDATA: + return "userdata"; + case OT_FLOAT: + return "float"; + case OT_STRING: + return "string"; + case 0x8000040: + return "array"; + case 0x8000200: + return "function"; + case 0x8100000: + return "structdef"; + case OT_THREAD: + return "thread"; + case OT_FUNCPROTO: + return "function"; + case OT_CLAAS: + return "class"; + case OT_WEAKREF: + return "weakref"; + case 0x8080000: + return "unimplemented function"; + case 0x8200000: + return "struct instance"; + case 0xA000020: + return "table"; + case 0xA008000: + return "instance"; + case 0xA400000: + return "entity"; + default: + return ""; + } +} + +// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors +template class SquirrelManager<ScriptContext::SERVER>; +template class SquirrelManager<ScriptContext::CLIENT>; +template class SquirrelManager<ScriptContext::UI>; + +template <ScriptContext context> void SquirrelManager<context>::VMCreated(void* newSqvm) +{ + sqvm = newSqvm; + sqvm2 = *((void**)((char*)sqvm + 8)); // honestly not 100% sure on what this is, but alot of functions take it + + for (SQFuncRegistration* funcReg : m_funcRegistrations) + { + spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName); + RegisterSquirrelFunc(sqvm, funcReg, 1); + } + + for (auto& pair : g_pModManager->m_DependencyConstants) + { + bool bWasFound = false; + for (Mod& dependency : g_pModManager->m_LoadedMods) + { + if (!dependency.m_bEnabled) + continue; + + if (dependency.Name == pair.second) + { + bWasFound = true; + break; + } + } + + defconst(sqvm, pair.first.c_str(), bWasFound); + } +} + +template <ScriptContext context> void SquirrelManager<context>::VMDestroyed() +{ + sqvm = nullptr; +} + +template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(const char* pCode) +{ + if (!sqvm) + { + spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context)); + return; + } + + spdlog::info("Executing {} script code {} ", GetContextName(context), pCode); + + std::string strCode(pCode); + CompileBufferState bufferState = CompileBufferState(strCode); + + SQRESULT compileResult = compilebuffer(&bufferState, "console"); + spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult)); + + if (compileResult != SQRESULT_ERROR) + { + pushroottable(sqvm2); + SQRESULT callResult = call(sqvm2, 0); + spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult)); + } +} + +template <ScriptContext context> void SquirrelManager<context>::AddFuncRegistration( + std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func) +{ + SQFuncRegistration* reg = new SQFuncRegistration; + + reg->squirrelFuncName = new char[name.size() + 1]; + strcpy((char*)reg->squirrelFuncName, name.c_str()); + reg->cppFuncName = reg->squirrelFuncName; + + reg->helpText = new char[helpText.size() + 1]; + strcpy((char*)reg->helpText, helpText.c_str()); + + reg->returnTypeString = new char[returnType.size() + 1]; + strcpy((char*)reg->returnTypeString, returnType.c_str()); + reg->returnType = SQReturnTypeFromString(returnType.c_str()); + + reg->argTypes = new char[argTypes.size() + 1]; + strcpy((char*)reg->argTypes, argTypes.c_str()); + + reg->funcPtr = func; + + m_funcRegistrations.push_back(reg); +} + +template <ScriptContext context> SQRESULT SquirrelManager<context>::setupfunc(const SQChar* funcname) +{ + pushroottable(sqvm2); + pushstring(sqvm2, funcname, -1); + + SQRESULT result = get(sqvm2, -2); + if (result != SQRESULT_ERROR) + pushroottable(sqvm2); + + return result; +} + // hooks template <ScriptContext context> void* (*sq_compiler_create)(void* sqvm, void* a2, void* a3, SQBool bShouldThrowError); template <ScriptContext context> void* sq_compiler_createHook(void* sqvm, void* a2, void* a3, SQBool bShouldThrowError) @@ -202,7 +374,9 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) g_pSquirrel<ScriptContext::UI> = new SquirrelManager<ScriptContext::UI>; g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc = module.Offset(0x108E0).As<RegisterSquirrelFuncType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst = module.Offset(0x12120).As<sq_defconstType>(); g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc = g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc; + g_pSquirrel<ScriptContext::UI>->__sq_defconst = g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst; g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable = module.Offset(0x5860).As<sq_pushroottableType>(); @@ -217,16 +391,23 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) g_pSquirrel<ScriptContext::UI>->__sq_newarray = g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray; g_pSquirrel<ScriptContext::UI>->__sq_arrayappend = g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend; + g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot = module.Offset(0x70B0).As<sq_newslotType>(); + g_pSquirrel<ScriptContext::UI>->__sq_newtable = g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable; + g_pSquirrel<ScriptContext::UI>->__sq_newslot = g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot; + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror = module.Offset(0x8470).As<sq_raiseerrorType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset = module.Offset(0x3560).As<sq_pushAssetType>(); g_pSquirrel<ScriptContext::UI>->__sq_pushstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring; g_pSquirrel<ScriptContext::UI>->__sq_pushinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger; g_pSquirrel<ScriptContext::UI>->__sq_pushfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat; g_pSquirrel<ScriptContext::UI>->__sq_pushbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool; g_pSquirrel<ScriptContext::UI>->__sq_raiseerror = g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror; + g_pSquirrel<ScriptContext::UI>->__sq_pushasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset; g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring = module.Offset(0x60C0).As<sq_getstringType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger = module.Offset(0x60E0).As<sq_getintegerType>(); @@ -272,6 +453,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) g_pSquirrel<ScriptContext::SERVER> = new SquirrelManager<ScriptContext::SERVER>; g_pSquirrel<ScriptContext::SERVER>->RegisterSquirrelFunc = module.Offset(0x1DD10).As<RegisterSquirrelFuncType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_defconst = module.Offset(0x1F550).As<sq_defconstType>(); g_pSquirrel<ScriptContext::SERVER>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>(); g_pSquirrel<ScriptContext::SERVER>->__sq_pushroottable = module.Offset(0x5840).As<sq_pushroottableType>(); @@ -280,6 +462,9 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) g_pSquirrel<ScriptContext::SERVER>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>(); g_pSquirrel<ScriptContext::SERVER>->__sq_arrayappend = module.Offset(0x3C70).As<sq_arrayappendType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_newslot = module.Offset(0x7080).As<sq_newslotType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>(); g_pSquirrel<ScriptContext::SERVER>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>(); g_pSquirrel<ScriptContext::SERVER>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>(); diff --git a/NorthstarDLL/squirrel.h b/NorthstarDLL/squirrel.h index fbb645a1..a899ed9b 100644 --- a/NorthstarDLL/squirrel.h +++ b/NorthstarDLL/squirrel.h @@ -14,14 +14,28 @@ enum SQRESULT : SQInteger SQRESULT_NOTNULL = 1, }; +typedef SQRESULT (*SQFunction)(void* sqvm); + +enum class eSQReturnType +{ + Float = 0x1, + Vector = 0x3, + Integer = 0x5, + Boolean = 0x6, + Entity = 0xD, + String = 0x21, + Default = 0x20, + Arrays = 0x25, + Asset = 0x28, + Table = 0x26, +}; + const std::map<SQRESULT, const char*> PrintSQRESULT = { {SQRESULT_ERROR, "ERROR"}, {SQRESULT_NULL, "NULL"}, {SQRESULT_NOTNULL, "NOTNULL"} }; -typedef SQRESULT (*SQFunction)(void* sqvm); - struct CompileBufferState { const SQChar* buffer; @@ -36,30 +50,496 @@ struct CompileBufferState } }; +struct CallInfo; +struct SQTable; +struct SQString; +struct SQFunctionProto; +struct SQClosure; +struct SQSharedState; +struct StringTable; +struct SQStructInstance; +struct SQStructDef; +struct SQNativeClosure; +struct SQArray; +struct SQInstruction; + +/* 127 */ +enum SQObjectType : uint32_t +{ + _RT_NULL = 0x1, + _RT_INTEGER = 0x2, + _RT_FLOAT = 0x4, + _RT_BOOL = 0x8, + _RT_STRING = 0x10, + _RT_TABLE = 0x20, + _RT_ARRAY = 0x40, + _RT_USERDATA = 0x80, + _RT_CLOSURE = 0x100, + _RT_NATIVECLOSURE = 0x200, + _RT_GENERATOR = 0x400, + OT_USERPOINTER = 0x800, + _RT_USERPOINTER = 0x800, + _RT_THREAD = 0x1000, + _RT_FUNCPROTO = 0x2000, + _RT_CLASS = 0x4000, + _RT_INSTANCE = 0x8000, + _RT_WEAKREF = 0x10000, + OT_VECTOR = 0x40000, + SQOBJECT_CANBEFALSE = 0x1000000, + OT_NULL = 0x1000001, + OT_BOOL = 0x1000008, + SQOBJECT_DELEGABLE = 0x2000000, + SQOBJECT_NUMERIC = 0x4000000, + OT_INTEGER = 0x5000002, + OT_FLOAT = 0x5000004, + SQOBJECT_REF_COUNTED = 0x8000000, + OT_STRING = 0x8000010, + OT_ARRAY = 0x8000040, + OT_CLOSURE = 0x8000100, + OT_NATIVECLOSURE = 0x8000200, + OT_ASSET = 0x8000400, + OT_THREAD = 0x8001000, + OT_FUNCPROTO = 0x8002000, + OT_CLAAS = 0x8004000, + OT_STRUCT = 0x8200000, + OT_WEAKREF = 0x8010000, + OT_TABLE = 0xA000020, + OT_USERDATA = 0xA000080, + OT_INSTANCE = 0xA008000, + OT_ENTITY = 0xA400000, +}; + +/* 156 */ +union alignas(8) SQObjectValue +{ + SQString* asString; + SQTable* asTable; + SQClosure* asClosure; + SQFunctionProto* asFuncProto; + SQStructDef* asStructDef; + uint64_t asInteger; + SQStructInstance* asStructInstance; + float asFloat; + SQNativeClosure* asNativeClosure; + SQArray* asArray; +}; + +/* 128 */ +struct alignas(8) SQObject +{ + SQObjectType _Type; + uint32_t _structOffset; + SQObjectValue _VAL; +}; + +struct tableNode +{ + SQObject val; + SQObject key; + tableNode* next; +}; + +/* 138 */ +struct alignas(8) SQString +{ + void* vftable; + uint32_t uiRef; + uint32_t uiRef1; + SQString* _next_maybe; + SQSharedState* sharedState; + uint32_t length; + uint8_t gap_24[4]; + char _hash[8]; + char _val[1]; +}; + +/* 137 */ +struct alignas(8) SQTable +{ + void* vftable; + uint8_t gap_08[4]; + uint32_t uiRef; + uint8_t gap_10[8]; + void* pointer_18; + void* pointer_20; + void* _sharedState; + uint64_t field_30; + tableNode* _nodes; + uint32_t _numOfNodes; + uint32_t size; + uint32_t field_48; + uint32_t _usedNodes; + uint8_t _gap_50[20]; + uint32_t field_64; + uint8_t _gap_68[80]; +}; + +/* 140 */ +struct alignas(8) SQClosure +{ + void* vftable; + uint8_t gap_08[4]; + uint32_t uiRef; + void* pointer_10; + void* pointer_18; + void* pointer_20; + void* sharedState; + SQObject obj_30; + SQObject _function; + SQObject* _outervalues; + uint8_t gap_58[8]; + uint8_t gap_60[96]; + SQObject* objectPointer_C0; +}; + +/* 139 */ +struct alignas(8) SQFunctionProto +{ + void* vftable; + uint8_t gap_08[4]; + uint32_t uiRef; + uint8_t gap_10[8]; + void* pointer_18; + void* pointer_20; + void* sharedState; + void* pointer_30; + SQObject fileName; + SQObject funcName; + SQObject obj_58; + uint8_t gap_68[64]; + uint32_t nParameters; + uint8_t gap_AC[60]; + uint32_t nDefaultParams; + uint8_t gap_EC[200]; +}; + +/* 152 */ +struct SQStructDef +{ + uint8_t gap_0[56]; + SQString* name; + uint8_t gap_[300]; +}; + +/* 150 */ +struct SQStructInstance +{ + void* vftable; + uint8_t gap_8[16]; + void* pointer_18; + uint8_t gap_20[8]; + SQSharedState* _sharedState; + uint8_t gap_30[8]; + SQObject data[1]; +}; + +/* 157 */ +struct alignas(8) SQNativeClosure +{ + void* vftable; + uint8_t gap_08[4]; + uint32_t uiRef; + uint8_t gap_10[88]; + SQString* _name; + uint8_t gap_0[300]; +}; + +/* 148 */ +struct SQSharedState +{ + uint8_t gap_0[72]; + StringTable* _stringtable; + uint8_t gap_50[30000]; +}; + +/* 149 */ +struct StringTable +{ + uint8_t gap_0[12]; + int _numofslots; + uint8_t gap_10[200]; +}; + +/* 129 */ +struct alignas(8) HSquirrelVM +{ + void* vftable; + uint32_t uiRef; + uint8_t gap_8[12]; + void* _toString; + void* _roottable_pointer; + void* pointer_28; + CallInfo* ci; + CallInfo* _callsstack; + uint32_t _callsstacksize; + uint32_t _stackbase; + SQObject* _stackOfCurrentFunction; + SQSharedState* sharedState; + void* pointer_58; + void* pointer_60; + uint32_t _top; + SQObject* _stack; + uint8_t gap_78[8]; + SQObject* _vargsstack; + uint8_t gap_88[8]; + SQObject temp_reg; + uint8_t gapA0[8]; + void* pointer_A8; + uint8_t gap_B0[8]; + SQObject _roottable_object; + SQObject _lasterror; + SQObject _errorHandler; + uint64_t field_E8; + uint32_t traps; + uint8_t gap_F4[12]; + uint32_t _nnativecalls; + uint32_t _suspended; + uint32_t _suspended_root; + uint32_t _callstacksize; + uint32_t _suspended_target; + uint32_t field_114; + uint32_t _suspend_varargs; + SQObject* _object_pointer_120; +}; + +/* 136 */ +struct alignas(8) CallInfo +{ + SQInstruction* ip; + SQObject* _literals; + SQObject obj10; + SQObject closure; + uint32_t _etraps[4]; + uint32_t _root; + short _vargs_size; + short _vargs_base; +}; + +/* 135 */ +enum SQOpcode : int +{ + _OP_LOAD = 0x0, + _OP_LOADCOPY = 0x1, + _OP_LOADINT = 0x2, + _OP_LOADFLOAT = 0x3, + _OP_DLOAD = 0x4, + _OP_TAILCALL = 0x5, + _OP_CALL = 0x6, + _OP_PREPCALL = 0x7, + _OP_PREPCALLK = 0x8, + _OP_GETK = 0x9, + _OP_MOVE = 0xA, + _OP_NEWSLOT = 0xB, + _OP_DELETE = 0xC, + _OP_SET = 0xD, + _OP_GET = 0xE, + _OP_EQ = 0xF, + _OP_NE = 0x10, + _OP_ARITH = 0x11, + _OP_BITW = 0x12, + _OP_RETURN = 0x13, + _OP_LOADNULLS = 0x14, + _OP_LOADROOTTABLE = 0x15, + _OP_LOADBOOL = 0x16, + _OP_DMOVE = 0x17, + _OP_JMP = 0x18, + _OP_JNZ = 0x19, + _OP_JZ = 0x1A, + _OP_LOADFREEVAR = 0x1B, + _OP_VARGC = 0x1C, + _OP_GETVARGV = 0x1D, + _OP_NEWTABLE = 0x1E, + _OP_NEWARRAY = 0x1F, + _OP_APPENDARRAY = 0x20, + _OP_GETPARENT = 0x21, + _OP_COMPOUND_ARITH = 0x22, + _OP_COMPOUND_ARITH_LOCAL = 0x23, + _OP_INCREMENT_PREFIX = 0x24, + _OP_INCREMENT_PREFIX_LOCAL = 0x25, + _OP_INCREMENT_PREFIX_STRUCTFIELD = 0x26, + _OP_INCREMENT_POSTFIX = 0x27, + _OP_INCREMENT_POSTFIX_LOCAL = 0x28, + _OP_INCREMENT_POSTFIX_STRUCTFIELD = 0x29, + _OP_CMP = 0x2A, + _OP_EXISTS = 0x2B, + _OP_INSTANCEOF = 0x2C, + _OP_NEG = 0x2D, + _OP_NOT = 0x2E, + _OP_BWNOT = 0x2F, + _OP_CLOSURE = 0x30, + _OP_FOREACH = 0x31, + _OP_FOREACH_STATICARRAY_START = 0x32, + _OP_FOREACH_STATICARRAY_NEXT = 0x33, + _OP_FOREACH_STATICARRAY_NESTEDSTRUCT_START = 0x34, + _OP_FOREACH_STATICARRAY_NESTEDSTRUCT_NEXT = 0x35, + _OP_DELEGATE = 0x36, + _OP_CLONE = 0x37, + _OP_TYPEOF = 0x38, + _OP_PUSHTRAP = 0x39, + _OP_POPTRAP = 0x3A, + _OP_THROW = 0x3B, + _OP_CLASS = 0x3C, + _OP_NEWSLOTA = 0x3D, + _OP_EQ_LITERAL = 0x3E, + _OP_NE_LITERAL = 0x3F, + _OP_FOREACH_SETUP = 0x40, + _OP_ASSERT_FAILED = 0x41, + _OP_ADD = 0x42, + _OP_SUB = 0x43, + _OP_MUL = 0x44, + _OP_DIV = 0x45, + _OP_MOD = 0x46, + _OP_PREPCALLK_CALL = 0x47, + _OP_PREPCALLK_MOVE_CALL = 0x48, + _OP_PREPCALLK_LOADINT_CALL = 0x49, + _OP_CMP_JZ = 0x4A, + _OP_INCREMENT_LOCAL_DISCARD_JMP = 0x4B, + _OP_JZ_RETURN = 0x4C, + _OP_JZ_LOADBOOL_RETURN = 0x4D, + _OP_NEWVECTOR = 0x4E, + _OP_ZEROVECTOR = 0x4F, + _OP_GET_VECTOR_COMPONENT = 0x50, + _OP_SET_VECTOR_COMPONENT = 0x51, + _OP_VECTOR_COMPONENT_MINUSEQ = 0x52, + _OP_VECTOR_COMPONENT_PLUSEQ = 0x53, + _OP_VECTOR_COMPONENT_MULEQ = 0x54, + _OP_VECTOR_COMPONENT_DIVEQ = 0x55, + _OP_VECTOR_NORMALIZE = 0x56, + _OP_VECTOR_NORMALIZE_IN_PLACE = 0x57, + _OP_VECTOR_DOT_PRODUCT = 0x58, + _OP_VECTOR_DOT_PRODUCT2D = 0x59, + _OP_VECTOR_CROSS_PRODUCT = 0x5A, + _OP_VECTOR_CROSS_PRODUCT2D = 0x5B, + _OP_VECTOR_LENGTH = 0x5C, + _OP_VECTOR_LENGTHSQR = 0x5D, + _OP_VECTOR_LENGTH2D = 0x5E, + _OP_VECTOR_LENGTH2DSQR = 0x5F, + _OP_VECTOR_DISTANCE = 0x60, + _OP_VECTOR_DISTANCESQR = 0x61, + _OP_VECTOR_DISTANCE2D = 0x62, + _OP_VECTOR_DISTANCE2DSQR = 0x63, + _OP_INCREMENT_LOCAL_DISCARD = 0x64, + _OP_FASTCALL = 0x65, + _OP_FASTCALL_NATIVE = 0x66, + _OP_FASTCALL_NATIVE_ARGTYPECHECK = 0x67, + _OP_FASTCALL_ENV = 0x68, + _OP_FASTCALL_NATIVE_ENV = 0x69, + _OP_FASTCALL_NATIVE_ENV_ARGTYPECHECK = 0x6A, + _OP_LOADGLOBALARRAY = 0x6B, + _OP_GETGLOBAL = 0x6C, + _OP_SETGLOBAL = 0x6D, + _OP_COMPOUND_ARITH_GLOBAL = 0x6E, + _OP_GETSTRUCTFIELD = 0x6F, + _OP_SETSTRUCTFIELD = 0x70, + _OP_COMPOUND_ARITH_STRUCTFIELD = 0x71, + _OP_NEWSTRUCT = 0x72, + _OP_GETSUBSTRUCT = 0x73, + _OP_GETSUBSTRUCT_DYNAMIC = 0x74, + _OP_TYPECAST = 0x75, + _OP_TYPECHECK = 0x76, + _OP_TYPECHECK_ORNULL = 0x77, + _OP_TYPECHECK_NOTNULL = 0x78, + _OP_CHECK_ENTITY_CLASS = 0x79, + _OP_UNREACHABLE = 0x7A, + _OP_ARRAY_RESIZE = 0x7B, +}; + +/* 141 */ +struct alignas(8) SQStackInfos +{ + char* _name; + char* _sourceName; + uint32_t _line; +}; + +/* 151 */ +struct alignas(4) SQInstruction +{ + int op; + int arg1; + int output; + uint16_t arg2; + uint16_t arg3; +}; + +/* 154 */ +struct SQLexer +{ + uint8_t gap_0[112]; +}; + +/* 153 */ +struct SQCompiler +{ + uint8_t gap_0[4]; + uint32_t _token; + uint8_t gap_8[8]; + SQObject object_10; + SQLexer lexer; + uint8_t gap_1[768]; +}; + +/* 155 */ +struct CSquirrelVM +{ + uint8_t gap_0[8]; + HSquirrelVM* sqvm; +}; + +struct SQVector +{ + SQObjectType _Type; + float x; + float y; + float z; +}; + +struct SQArray +{ + void* vftable; + uint32_t uiRef; + uint8_t gap_24[36]; + SQObject* _values; + uint32_t _usedSlots; + uint32_t _allocated; +}; + +#define SQOBJ_INCREMENT_REFERENCECOUNT(val) \ + if ((val->_Type & SQOBJECT_REF_COUNTED) != 0) \ + ++val->_VAL.asString->uiRef; + +#define SQOBJ_DECREMENT_REFERENCECOUNT(val) \ + if ((val->_Type & SQOBJECT_REF_COUNTED) != 0) \ + { \ + if (val->_VAL.asString->uiRef-- == 1) \ + { \ + spdlog::info("Deleted SQObject of type {} with address {:X}", sq_getTypeName(val->_Type), val->_VAL.asInteger); \ + (*(void(__fastcall**)(SQString*))(&val->_VAL.asString->vftable[1]))(val->_VAL.asString); \ + } \ + } + struct SQFuncRegistration { const char* squirrelFuncName; const char* cppFuncName; const char* helpText; - const char* returnValueType; + const char* returnTypeString; const char* argTypes; - int16_t somethingThatsZero; - int16_t padding1; - int32_t unknown1; - int64_t unknown2; - int32_t unknown3; - int32_t padding2; - int64_t unknown4; - int64_t unknown5; - int64_t unknown6; - int32_t unknown7; - int32_t padding3; + uint32_t unknown1; + uint32_t devLevel; + const char* shortNameMaybe; + uint32_t unknown2; + eSQReturnType returnType; + uint32_t* externalBufferPointer; + uint64_t externalBufferSize; + uint64_t unknown3; + uint64_t unknown4; void* funcPtr; SQFuncRegistration() { memset(this, 0, sizeof(SQFuncRegistration)); - this->padding2 = 32; + this->returnType = eSQReturnType::Default; } }; @@ -68,28 +548,35 @@ enum class ScriptContext : int SERVER, CLIENT, UI, - NONE }; const char* GetContextName(ScriptContext context); +eSQReturnType SQReturnTypeFromString(const char* pReturnType); +const char* SQTypeNameFromID(const int iTypeId); // core sqvm funcs typedef int64_t (*RegisterSquirrelFuncType)(void* sqvm, SQFuncRegistration* funcReg, char unknown); +typedef void (*sq_defconstType)(void* sqvm, const SQChar* name, int value); typedef SQRESULT (*sq_compilebufferType)(void* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, SQBool bShouldThrowError); typedef SQRESULT (*sq_callType)(void* sqvm, SQInteger iArgs, SQBool bShouldReturn, SQBool bThrowError); +typedef SQInteger (*sq_raiseerrorType)(void* sqvm, const SQChar* pError); // sq stack array funcs typedef void (*sq_newarrayType)(void* sqvm, SQInteger iStackpos); typedef SQRESULT (*sq_arrayappendType)(void* sqvm, SQInteger iStackpos); +// sq table funcs +typedef SQRESULT (*sq_newtableType)(void* sqvm); +typedef SQRESULT (*sq_newslotType)(void* sqvm, SQInteger idx, SQBool bStatic); + // sq stack push funcs typedef void (*sq_pushroottableType)(void* sqvm); typedef void (*sq_pushstringType)(void* sqvm, const SQChar* pStr, SQInteger iLength); typedef void (*sq_pushintegerType)(void* sqvm, SQInteger i); typedef void (*sq_pushfloatType)(void* sqvm, SQFloat f); typedef void (*sq_pushboolType)(void* sqvm, SQBool b); -typedef SQInteger (*sq_raiseerrorType)(void* sqvm, const SQChar* pError); +typedef void (*sq_pushAssetType)(void* sqvm, const SQChar* pName, SQInteger iLength); // sq stack get funcs typedef const SQChar* (*sq_getstringType)(void* sqvm, SQInteger stackpos); @@ -111,6 +598,7 @@ template <ScriptContext context> class SquirrelManager #pragma region SQVM funcs RegisterSquirrelFuncType RegisterSquirrelFunc; + sq_defconstType __sq_defconst; sq_compilebufferType __sq_compilebuffer; sq_callType __sq_call; @@ -119,11 +607,15 @@ template <ScriptContext context> class SquirrelManager sq_newarrayType __sq_newarray; sq_arrayappendType __sq_arrayappend; + sq_newtableType __sq_newtable; + sq_newslotType __sq_newslot; + sq_pushroottableType __sq_pushroottable; sq_pushstringType __sq_pushstring; sq_pushintegerType __sq_pushinteger; sq_pushfloatType __sq_pushfloat; sq_pushboolType __sq_pushbool; + sq_pushAssetType __sq_pushasset; sq_getstringType __sq_getstring; sq_getintegerType __sq_getinteger; @@ -135,153 +627,104 @@ template <ScriptContext context> class SquirrelManager public: SquirrelManager() : sqvm(nullptr) {} - void VMCreated(void* newSqvm) - { - sqvm = newSqvm; - sqvm2 = *((void**)((char*)sqvm + 8)); // honestly not 100% sure on what this is, but alot of functions take it - - for (SQFuncRegistration* funcReg : m_funcRegistrations) - { - spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName); - RegisterSquirrelFunc(sqvm, funcReg, 1); - } - } - - void VMDestroyed() - { - sqvm = nullptr; - } + void VMCreated(void* newSqvm); + void VMDestroyed(); + void ExecuteCode(const char* code); + void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func); + SQRESULT setupfunc(const SQChar* funcname); - void ExecuteCode(const char* code) + #pragma region SQVM func wrappers + inline void defconst(void* sqvm, const SQChar* pName, int nValue) { - if (!sqvm) - { - spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context)); - return; - } - - spdlog::info("Executing {} script code {} ", GetContextName(context), code); - - std::string strCode(code); - CompileBufferState bufferState = CompileBufferState(strCode); - - SQRESULT compileResult = compilebuffer(&bufferState, "console"); - spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult)); - - if (compileResult != SQRESULT_ERROR) - { - pushroottable(sqvm2); - SQRESULT callResult = call(sqvm2, 0); - spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult)); - } + __sq_defconst(sqvm, pName, nValue); } - void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func) + inline SQRESULT compilebuffer(CompileBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false) { - SQFuncRegistration* reg = new SQFuncRegistration; - - reg->squirrelFuncName = new char[name.size() + 1]; - strcpy((char*)reg->squirrelFuncName, name.c_str()); - reg->cppFuncName = reg->squirrelFuncName; - - reg->helpText = new char[helpText.size() + 1]; - strcpy((char*)reg->helpText, helpText.c_str()); - - reg->returnValueType = new char[returnType.size() + 1]; - strcpy((char*)reg->returnValueType, returnType.c_str()); - - reg->argTypes = new char[argTypes.size() + 1]; - strcpy((char*)reg->argTypes, argTypes.c_str()); - - reg->funcPtr = func; - - m_funcRegistrations.push_back(reg); + return __sq_compilebuffer(sqvm2, bufferState, bufferName, -1, bShouldThrowError); } - SQRESULT setupfunc(const SQChar* funcname) + inline SQRESULT call(void* sqvm, const SQInteger args) { - pushroottable(sqvm2); - pushstring(sqvm2, funcname, -1); - - SQRESULT result = get(sqvm2, -2); - if (result != SQRESULT_ERROR) - pushroottable(sqvm2); - - return result; + return __sq_call(sqvm, args + 1, false, false); } - #pragma region SQVM func wrappers - SQRESULT compilebuffer(CompileBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false) + inline SQInteger raiseerror(void* sqvm, const const SQChar* sError) { - return __sq_compilebuffer(sqvm2, bufferState, bufferName, -1, bShouldThrowError); + return __sq_raiseerror(sqvm, sError); } - SQRESULT call(void* sqvm, const SQInteger args) + inline void newarray(void* sqvm, const SQInteger stackpos = 0) { - return __sq_call(sqvm, args + 1, false, false); + __sq_newarray(sqvm, stackpos); } - SQInteger raiseerror(void* sqvm, const const SQChar* sError) + inline SQRESULT arrayappend(void* sqvm, const SQInteger stackpos) { - return __sq_raiseerror(sqvm, sError); + return __sq_arrayappend(sqvm, stackpos); } - void newarray(void* sqvm, const SQInteger stackpos = 0) + inline SQRESULT newtable(void* sqvm) { - __sq_newarray(sqvm, stackpos); + return __sq_newtable(sqvm); } - SQRESULT arrayappend(void* sqvm, const SQInteger stackpos) + inline SQRESULT newslot(void* sqvm, SQInteger idx, SQBool bStatic) { - return __sq_arrayappend(sqvm, stackpos); + return __sq_newslot(sqvm, idx, bStatic); } - void pushroottable(void* sqvm) + inline void pushroottable(void* sqvm) { __sq_pushroottable(sqvm); } - void pushstring(void* sqvm, const SQChar* sVal, int length = -1) + inline void pushstring(void* sqvm, const SQChar* sVal, int length = -1) { __sq_pushstring(sqvm, sVal, length); } - void pushinteger(void* sqvm, const SQInteger iVal) + inline void pushinteger(void* sqvm, const SQInteger iVal) { __sq_pushinteger(sqvm, iVal); } - void pushfloat(void* sqvm, const SQFloat flVal) + inline void pushfloat(void* sqvm, const SQFloat flVal) { __sq_pushfloat(sqvm, flVal); } - void pushbool(void* sqvm, const SQBool bVal) + inline void pushbool(void* sqvm, const SQBool bVal) { __sq_pushbool(sqvm, bVal); } - const SQChar* getstring(void* sqvm, const SQInteger stackpos) + inline void pushasset(void* sqvm, const SQChar* sVal, int length = -1) + { + __sq_pushasset(sqvm, sVal, length); + } + + inline const SQChar* getstring(void* sqvm, const SQInteger stackpos) { return __sq_getstring(sqvm, stackpos); } - SQInteger getinteger(void* sqvm, const SQInteger stackpos) + inline SQInteger getinteger(void* sqvm, const SQInteger stackpos) { return __sq_getinteger(sqvm, stackpos); } - SQFloat getfloat(void* sqvm, const SQInteger stackpos) + inline SQFloat getfloat(void* sqvm, const SQInteger stackpos) { return __sq_getfloat(sqvm, stackpos); } - SQBool getbool(void* sqvm, const SQInteger stackpos) + inline SQBool getbool(void* sqvm, const SQInteger stackpos) { return __sq_getbool(sqvm, stackpos); } - SQRESULT get(void* sqvm, const SQInteger stackpos) + inline SQRESULT get(void* sqvm, const SQInteger stackpos) { return __sq_get(sqvm, stackpos); } |