diff options
author | GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> | 2024-08-09 23:12:12 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-09 23:12:12 +0200 |
commit | d679e05049db1e70c40923aac6cefccd3fbe9ca0 (patch) | |
tree | 834c5fb41c20b3029b118ee62f1bde15eb0869d4 /primedev | |
parent | 59d19a8b539801b0b0ed614f343f12d22a762f07 (diff) | |
parent | a28c1cb10cc805907d22b656ccc300b39b720a88 (diff) | |
download | NorthstarLauncher-d679e05049db1e70c40923aac6cefccd3fbe9ca0.tar.gz NorthstarLauncher-d679e05049db1e70c40923aac6cefccd3fbe9ca0.zip |
Merge branch 'main' into feat/whitelist-safeio-file-extensions
Diffstat (limited to 'primedev')
40 files changed, 250 insertions, 114 deletions
diff --git a/primedev/CMakeLists.txt b/primedev/CMakeLists.txt index 31dda4b2..a33fd351 100644 --- a/primedev/CMakeLists.txt +++ b/primedev/CMakeLists.txt @@ -1,3 +1,3 @@ include(Northstar.cmake) -include(Launcher.cmake) +add_subdirectory(primelauncher) add_subdirectory(wsockproxy) diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index aef630c8..40583d28 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -172,13 +172,13 @@ target_link_libraries( libcurl minizip silver-bun - WS2_32.lib - Crypt32.lib - Cryptui.lib + ws2_32.lib + crypt32.lib + cryptui.lib dbghelp.lib - Wldap32.lib - Normaliz.lib - Bcrypt.lib + wldap32.lib + normaliz.lib + bcrypt.lib version.lib ) diff --git a/primedev/client/audio.cpp b/primedev/client/audio.cpp index 099fdcee..63501414 100644 --- a/primedev/client/audio.cpp +++ b/primedev/client/audio.cpp @@ -7,6 +7,9 @@ #include <iostream> #include <sstream> #include <random> +#include <ranges> + +namespace fs = std::filesystem; AUTOHOOK_INIT() @@ -28,7 +31,7 @@ unsigned char EMPTY_WAVE[45] = {0x52, 0x49, 0x46, 0x46, 0x25, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00}; -EventOverrideData::EventOverrideData(const std::string& data, const fs::path& path) +EventOverrideData::EventOverrideData(const std::string& data, const fs::path& path, const std::vector<std::string>& registeredEvents) { if (data.length() <= 0) { @@ -191,6 +194,14 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa { std::string pathString = file.path().string(); + // Retrieve event id from path (standard?) + std::string eventId = file.path().parent_path().filename().string(); + if (std::find(registeredEvents.begin(), registeredEvents.end(), eventId) != registeredEvents.end()) + { + spdlog::warn("{} couldn't be loaded because {} event has already been overrided, skipping.", pathString, eventId); + continue; + } + // Open the file. std::ifstream wavStream(pathString, std::ios::binary); @@ -259,7 +270,7 @@ EventOverrideData::EventOverrideData(const std::string& data, const fs::path& pa LoadedSuccessfully = true; } -bool CustomAudioManager::TryLoadAudioOverride(const fs::path& defPath) +bool CustomAudioManager::TryLoadAudioOverride(const fs::path& defPath, std::string modName) { if (IsDedicatedServer()) return true; // silently fail @@ -279,19 +290,35 @@ bool CustomAudioManager::TryLoadAudioOverride(const fs::path& defPath) jsonStream.close(); - std::shared_ptr<EventOverrideData> data = std::make_shared<EventOverrideData>(jsonStringStream.str(), defPath); + // Pass the list of overriden events to avoid multiple event registrations crash + auto kv = std::views::keys(m_loadedAudioOverrides); + std::vector<std::string> keys {kv.begin(), kv.end()}; + std::shared_ptr<EventOverrideData> data = std::make_shared<EventOverrideData>(jsonStringStream.str(), defPath, keys); if (!data->LoadedSuccessfully) return false; // no logging, the constructor has probably already logged for (const std::string& eventId : data->EventIds) { + if (m_loadedAudioOverrides.contains(eventId)) + { + spdlog::warn("\"{}\" mod tried to override sound event \"{}\" but it is already overriden, skipping.", modName, eventId); + continue; + } spdlog::info("Registering sound event {}", eventId); m_loadedAudioOverrides.insert({eventId, data}); } for (const auto& eventIdRegexData : data->EventIdsRegex) { + if (m_loadedAudioOverridesRegex.contains(eventIdRegexData.first)) + { + spdlog::warn( + "\"{}\" mod tried to override sound event regex \"{}\" but it is already overriden, skipping.", + modName, + eventIdRegexData.first); + continue; + } spdlog::info("Registering sound event regex {}", eventIdRegexData.first); m_loadedAudioOverridesRegex.insert({eventIdRegexData.first, data}); } diff --git a/primedev/client/audio.h b/primedev/client/audio.h index 15fd1a35..22bcf3f0 100644 --- a/primedev/client/audio.h +++ b/primedev/client/audio.h @@ -5,6 +5,8 @@ #include <regex> #include <shared_mutex> +namespace fs = std::filesystem; + enum class AudioSelectionStrategy { INVALID = -1, @@ -15,7 +17,7 @@ enum class AudioSelectionStrategy class EventOverrideData { public: - EventOverrideData(const std::string&, const fs::path&); + EventOverrideData(const std::string&, const fs::path&, const std::vector<std::string>& registeredEvents); EventOverrideData(); public: @@ -35,7 +37,7 @@ public: class CustomAudioManager { public: - bool TryLoadAudioOverride(const fs::path&); + bool TryLoadAudioOverride(const fs::path&, std::string modName); void ClearAudioOverrides(); std::shared_mutex m_loadingMutex; diff --git a/primedev/client/languagehooks.cpp b/primedev/client/languagehooks.cpp index 35ca5659..36b5d5ae 100644 --- a/primedev/client/languagehooks.cpp +++ b/primedev/client/languagehooks.cpp @@ -3,6 +3,8 @@ #include <filesystem> #include <regex> +namespace fs = std::filesystem; + AUTOHOOK_INIT() typedef LANGID (*Tier0_DetectDefaultLanguageType)(); @@ -41,7 +43,7 @@ std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern) std::string GetAnyInstalledAudioLanguage() { for (const auto& lang : file_list("r2\\sound\\", std::regex(".*?general_([a-z]+)_patch_1\\.mstr"))) - if (lang != "general" || lang != "") + if (lang != "general" && lang != "" && lang != "stream") return lang; return "NO LANGUAGE DETECTED"; } diff --git a/primedev/client/latencyflex.cpp b/primedev/client/latencyflex.cpp index 25e38c7a..39557870 100644 --- a/primedev/client/latencyflex.cpp +++ b/primedev/client/latencyflex.cpp @@ -24,10 +24,10 @@ ON_DLL_LOAD_CLIENT_RELIESON("client.dll", LatencyFlex, ConVar, (CModule module)) // https://ishitatsuyuki.github.io/post/latencyflex/ HMODULE pLfxModule; - if (pLfxModule = LoadLibraryA("latencyflex_layer.dll")) + 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")) + else if ((pLfxModule = LoadLibraryA("latencyflex_wine.dll"))) m_winelfx_WaitAndBeginFrame = reinterpret_cast<void (*)()>(reinterpret_cast<void*>(GetProcAddress(pLfxModule, "winelfx_WaitAndBeginFrame"))); else diff --git a/primedev/core/hooks.cpp b/primedev/core/hooks.cpp index 20f0cbef..fef8bbcf 100644 --- a/primedev/core/hooks.cpp +++ b/primedev/core/hooks.cpp @@ -12,6 +12,8 @@ #define XINPUT1_3_DLL "XInput1_3.dll" +namespace fs = std::filesystem; + AUTOHOOK_INIT() // called from the ON_DLL_LOAD macros @@ -104,7 +106,9 @@ bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig) if (orig) ppOrigFunc = orig; - if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK) + if (!addr) + spdlog::error("Address for hook {} is invalid", pFuncName); + else if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK) { if (MH_EnableHook(addr) == MH_OK) { diff --git a/primedev/core/hooks.h b/primedev/core/hooks.h index f842afbb..e5a65354 100644 --- a/primedev/core/hooks.h +++ b/primedev/core/hooks.h @@ -201,7 +201,9 @@ public: } } - if (MH_CreateHook(targetAddr, pHookFunc, ppOrigFunc) == MH_OK) + if (!targetAddr) + spdlog::error("Address for hook {} is invalid", pFuncName); + else if (MH_CreateHook(targetAddr, pHookFunc, ppOrigFunc) == MH_OK) { if (MH_EnableHook(targetAddr) == MH_OK) spdlog::info("Enabling hook {}", pFuncName); diff --git a/primedev/core/memalloc.cpp b/primedev/core/memalloc.cpp index 0a75bc2b..51167717 100644 --- a/primedev/core/memalloc.cpp +++ b/primedev/core/memalloc.cpp @@ -3,7 +3,7 @@ // TODO: rename to malloc and free after removing statically compiled .libs -extern "C" void* _malloc_base(size_t n) +void* _malloc_base(size_t n) { // allocate into static buffer if g_pMemAllocSingleton isn't initialised if (!g_pMemAllocSingleton) @@ -17,7 +17,7 @@ extern "C" void* _malloc_base(size_t n) return _malloc_base(n); }*/ -extern "C" void _free_base(void* p) +void _free_base(void* p) { if (!g_pMemAllocSingleton) TryCreateGlobalMemAlloc(); @@ -25,7 +25,7 @@ extern "C" void _free_base(void* p) g_pMemAllocSingleton->m_vtable->Free(g_pMemAllocSingleton, p); } -extern "C" void* _realloc_base(void* oldPtr, size_t size) +void* _realloc_base(void* oldPtr, size_t size) { if (!g_pMemAllocSingleton) TryCreateGlobalMemAlloc(); @@ -33,7 +33,7 @@ extern "C" void* _realloc_base(void* oldPtr, size_t size) return g_pMemAllocSingleton->m_vtable->Realloc(g_pMemAllocSingleton, oldPtr, size); } -extern "C" void* _calloc_base(size_t n, size_t size) +void* _calloc_base(size_t n, size_t size) { size_t bytes = n * size; void* memory = _malloc_base(bytes); @@ -44,7 +44,33 @@ extern "C" void* _calloc_base(size_t n, size_t size) return memory; } -extern "C" char* _strdup_base(const char* src) +void* _recalloc_base(void* const block, size_t const count, size_t const size) +{ + if (!block) + return _calloc_base(count, size); + + const size_t new_size = count * size; + const size_t old_size = _msize(block); + + void* const memory = _realloc_base(block, new_size); + + if (memory && old_size < new_size) + { + memset(static_cast<char*>(memory) + old_size, 0, new_size - old_size); + } + + return memory; +} + +size_t _msize(void* const block) +{ + if (!g_pMemAllocSingleton) + TryCreateGlobalMemAlloc(); + + return g_pMemAllocSingleton->m_vtable->GetSize(g_pMemAllocSingleton, block); +} + +char* _strdup_base(const char* src) { char* str; char* p; diff --git a/primedev/core/memalloc.h b/primedev/core/memalloc.h index 2f383335..73e078f5 100644 --- a/primedev/core/memalloc.h +++ b/primedev/core/memalloc.h @@ -1,14 +1,18 @@ #pragma once +#include <malloc.h> + #include "rapidjson/document.h" // #include "include/rapidjson/allocators.h" -extern "C" void* _malloc_base(size_t size); -extern "C" void* _calloc_base(size_t const count, size_t const size); -extern "C" void* _realloc_base(void* block, size_t size); -extern "C" void* _recalloc_base(void* const block, size_t const count, size_t const size); -extern "C" void _free_base(void* const block); -extern "C" char* _strdup_base(const char* src); +// The prelude is needed for these to be usable by the CRT +extern "C" __declspec(noinline) void* __cdecl _malloc_base(size_t const size); +extern "C" __declspec(noinline) void* __cdecl _calloc_base(size_t const count, size_t const size); +extern "C" __declspec(noinline) void* __cdecl _realloc_base(void* const block, size_t const size); +extern "C" __declspec(noinline) void* __cdecl _recalloc_base(void* const block, size_t const count, size_t const size); +extern "C" __declspec(noinline) void __cdecl _free_base(void* const block); +extern "C" __declspec(noinline) size_t __cdecl _msize(void* const block); +extern "C" __declspec(noinline) char* __cdecl _strdup_base(const char* src); void* operator new(size_t n); void operator delete(void* p) noexcept; diff --git a/primedev/dedicated/dedicatedlogtoclient.h b/primedev/dedicated/dedicatedlogtoclient.h index 82f4c56b..5678982e 100644 --- a/primedev/dedicated/dedicatedlogtoclient.h +++ b/primedev/dedicated/dedicatedlogtoclient.h @@ -5,7 +5,7 @@ class DedicatedServerLogToClientSink : public CustomSink { protected: - void custom_sink_it_(const custom_log_msg& msg); + void custom_sink_it_(const custom_log_msg& msg) override; void sink_it_(const spdlog::details::log_msg& msg) override; void flush_() override; }; diff --git a/primedev/logging/logging.cpp b/primedev/logging/logging.cpp index 72171e25..6d71eea0 100644 --- a/primedev/logging/logging.cpp +++ b/primedev/logging/logging.cpp @@ -124,11 +124,7 @@ void CustomSink::custom_log(const custom_log_msg& msg) void InitialiseConsole() { - if (AllocConsole() == FALSE) - { - std::cout << "[*] Failed to create a console window, maybe a console already exists?" << std::endl; - } - else + if (AllocConsole() != FALSE) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); diff --git a/primedev/logging/sourceconsole.h b/primedev/logging/sourceconsole.h index 44d73843..215dae1a 100644 --- a/primedev/logging/sourceconsole.h +++ b/primedev/logging/sourceconsole.h @@ -77,7 +77,7 @@ private: {spdlog::level::off, NS::Colors::OFF.ToSourceColor()}}; protected: - void custom_sink_it_(const custom_log_msg& msg); + void custom_sink_it_(const custom_log_msg& msg) override; void sink_it_(const spdlog::details::log_msg& msg) override; void flush_() override; }; diff --git a/primedev/mods/autodownload/moddownloader.cpp b/primedev/mods/autodownload/moddownloader.cpp index 3a946263..8e533dec 100644 --- a/primedev/mods/autodownload/moddownloader.cpp +++ b/primedev/mods/autodownload/moddownloader.cpp @@ -55,6 +55,8 @@ size_t WriteToString(void* ptr, size_t size, size_t count, void* stream) void ModDownloader::FetchModsListFromAPI() { + modState.state = MANIFESTO_FETCHING; + std::thread requestThread( [this]() { @@ -63,6 +65,9 @@ void ModDownloader::FetchModsListFromAPI() rapidjson::Document verifiedModsJson; std::string url = modsListUrl; + // Empty verified mods manifesto + verifiedMods = {}; + curl_global_init(CURL_GLOBAL_ALL); easyhandle = curl_easy_init(); std::string readBuffer; @@ -75,7 +80,12 @@ void ModDownloader::FetchModsListFromAPI() curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &readBuffer); curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteToString); result = curl_easy_perform(easyhandle); - ScopeGuard cleanup([&] { curl_easy_cleanup(easyhandle); }); + ScopeGuard cleanup( + [&] + { + curl_easy_cleanup(easyhandle); + modState.state = DOWNLOADING; + }); if (result == CURLcode::CURLE_OK) { @@ -92,6 +102,13 @@ void ModDownloader::FetchModsListFromAPI() verifiedModsJson.Parse(readBuffer); for (auto i = verifiedModsJson.MemberBegin(); i != verifiedModsJson.MemberEnd(); ++i) { + // Format testing + if (!i->value.HasMember("DependencyPrefix") || !i->value.HasMember("Versions")) + { + spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading."); + return; + } + std::string name = i->name.GetString(); std::string dependency = i->value["DependencyPrefix"].GetString(); @@ -101,6 +118,13 @@ void ModDownloader::FetchModsListFromAPI() for (auto& attribute : versions.GetArray()) { assert(attribute.IsObject()); + // Format testing + if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum")) + { + spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading."); + return; + } + std::string version = attribute["Version"].GetString(); std::string checksum = attribute["Checksum"].GetString(); modVersions.insert({version, {.checksum = checksum}}); @@ -600,7 +624,12 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion) ON_DLL_LOAD_RELIESON("engine.dll", ModDownloader, (ConCommand), (CModule module)) { g_pModDownloader = new ModDownloader(); +} + +ADD_SQFUNC("void", NSFetchVerifiedModsManifesto, "", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI) +{ g_pModDownloader->FetchModsListFromAPI(); + return SQRESULT_NULL; } ADD_SQFUNC( diff --git a/primedev/mods/autodownload/moddownloader.h b/primedev/mods/autodownload/moddownloader.h index 10df39ce..98fc27ae 100644 --- a/primedev/mods/autodownload/moddownloader.h +++ b/primedev/mods/autodownload/moddownloader.h @@ -1,3 +1,5 @@ +namespace fs = std::filesystem; + class ModDownloader { private: @@ -114,6 +116,8 @@ public: enum ModInstallState { + MANIFESTO_FETCHING, + // Normal installation process DOWNLOADING, CHECKSUMING, diff --git a/primedev/mods/modmanager.cpp b/primedev/mods/modmanager.cpp index 268a65a5..45eddd3e 100644 --- a/primedev/mods/modmanager.cpp +++ b/primedev/mods/modmanager.cpp @@ -601,6 +601,8 @@ auto ModConCommandCallback(const CCommand& command) case ScriptContext::UI: ModConCommandCallback_Internal<ScriptContext::UI>(found->Function, command); break; + default: + spdlog::error("ModConCommandCallback on invalid Context {}", found->Context); }; } @@ -746,7 +748,7 @@ void ModManager::LoadMods() continue; // Add mod entry to enabledmods.json if it doesn't exist - if (!mod.m_bIsRemote && !m_EnabledModsCfg.HasMember(mod.Name.c_str())) + if (!mod.m_bIsRemote && m_bHasEnabledModsCfg && !m_EnabledModsCfg.HasMember(mod.Name.c_str())) { m_EnabledModsCfg.AddMember(rapidjson_document::StringRefType(mod.Name.c_str()), true, m_EnabledModsCfg.GetAllocator()); newModsDetected = true; @@ -864,16 +866,24 @@ void ModManager::LoadMods() if (fs::is_regular_file(file) && file.path().extension() == ".rpak") { std::string pakName(file.path().filename().string()); - ModRpakEntry& modPak = mod.Rpaks.emplace_back(); - 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(); + if (!bUseRpakJson) + { + spdlog::warn("Mod {} contains rpaks without valid rpak.json, rpaks might not be loaded", mod.Name); + } + else + { + modPak.m_bAutoLoad = + (dRpakJson.HasMember("Preload") && dRpakJson["Preload"].IsObject() && dRpakJson["Preload"].HasMember(pakName) && + dRpakJson["Preload"][pakName].IsTrue()); + + // postload things + if (dRpakJson.HasMember("Postload") && dRpakJson["Postload"].IsObject() && dRpakJson["Postload"].HasMember(pakName)) + { + modPak.m_sLoadAfterPak = dRpakJson["Postload"][pakName].GetString(); + } + } modPak.m_sPakName = pakName; @@ -970,7 +980,7 @@ void ModManager::LoadMods() { if (fs::is_regular_file(file) && file.path().extension().string() == ".json") { - if (!g_CustomAudioManager.TryLoadAudioOverride(file.path())) + if (!g_CustomAudioManager.TryLoadAudioOverride(file.path(), mod.Name)) { spdlog::warn("Mod {} has an invalid audio def {}", mod.Name, file.path().filename().string()); continue; diff --git a/primedev/mods/modmanager.h b/primedev/mods/modmanager.h index 233f004d..95a8fe12 100644 --- a/primedev/mods/modmanager.h +++ b/primedev/mods/modmanager.h @@ -9,6 +9,8 @@ #include <filesystem> #include <unordered_set> +namespace fs = std::filesystem; + const std::string MOD_FOLDER_SUFFIX = "\\mods"; const std::string THUNDERSTORE_MOD_FOLDER_SUFFIX = "\\packages"; const std::string REMOTE_MOD_FOLDER_SUFFIX = "\\runtime\\remote\\mods"; diff --git a/primedev/pch.h b/primedev/pch.h index 577f803c..bfd25597 100644 --- a/primedev/pch.h +++ b/primedev/pch.h @@ -20,11 +20,6 @@ namespace fs = std::filesystem; -#define EXPORT extern "C" __declspec(dllexport) - -typedef void (*callable)(); -typedef void (*callable_v)(void* v); - // clang-format off #define assert_msg(exp, msg) assert((exp, msg)) //clang-format on diff --git a/primedev/plugins/interfaces/interface.h b/primedev/plugins/interfaces/interface.h index 440db5b2..c4f8b6ae 100644 --- a/primedev/plugins/interfaces/interface.h +++ b/primedev/plugins/interfaces/interface.h @@ -34,6 +34,6 @@ public: static className __g_##className##_singleton; \ EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) -EXPORT void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode); +extern "C" __declspec(dllexport) void* CreateInterface(const char* pName, InterfaceStatus* pReturnCode); #endif diff --git a/primedev/plugins/pluginmanager.cpp b/primedev/plugins/pluginmanager.cpp index 718e6956..14d5692b 100644 --- a/primedev/plugins/pluginmanager.cpp +++ b/primedev/plugins/pluginmanager.cpp @@ -5,6 +5,8 @@ #include "config/profile.h" #include "core/convar/concommand.h" +namespace fs = std::filesystem; + PluginManager* g_pPluginManager; const std::vector<Plugin>& PluginManager::GetLoadedPlugins() const diff --git a/primedev/plugins/pluginmanager.h b/primedev/plugins/pluginmanager.h index 8c021851..7993cbb8 100644 --- a/primedev/plugins/pluginmanager.h +++ b/primedev/plugins/pluginmanager.h @@ -3,6 +3,8 @@ #include <windows.h> +namespace fs = std::filesystem; + class Plugin; class PluginManager diff --git a/primedev/plugins/plugins.cpp b/primedev/plugins/plugins.cpp index ae6fd0cb..03dd2c9e 100644 --- a/primedev/plugins/plugins.cpp +++ b/primedev/plugins/plugins.cpp @@ -45,15 +45,15 @@ Plugin::Plugin(std::string path) : m_location(path) m_initData = {.pluginHandle = m_handle}; - CreateInterfaceFn CreatePluginInterface = (CreateInterfaceFn)GetProcAddress(m_handle, "CreateInterface"); + m_pCreateInterface = (CreateInterfaceFn)GetProcAddress(m_handle, "CreateInterface"); - if (!CreatePluginInterface) + if (!m_pCreateInterface) { NS::log::PLUGINSYS->error("Plugin at '{}' does not expose CreateInterface()", path); return; } - m_pluginId = (IPluginId*)CreatePluginInterface(PLUGIN_ID_VERSION, 0); + m_pluginId = (IPluginId*)m_pCreateInterface(PLUGIN_ID_VERSION, 0); if (!m_pluginId) { @@ -97,7 +97,7 @@ Plugin::Plugin(std::string path) : m_location(path) return; } - m_callbacks = (IPluginCallbacks*)CreatePluginInterface("PluginCallbacks001", 0); + m_callbacks = (IPluginCallbacks*)m_pCreateInterface("PluginCallbacks001", 0); if (!m_callbacks) { diff --git a/primedev/Launcher.cmake b/primedev/primelauncher/CMakeLists.txt index c04fc70b..f36781a9 100644 --- a/primedev/Launcher.cmake +++ b/primedev/primelauncher/CMakeLists.txt @@ -1,6 +1,6 @@ # NorthstarLauncher -add_executable(NorthstarLauncher "primelauncher/main.cpp" "primelauncher/resources.rc") +add_executable(NorthstarLauncher "main.cpp" "resources.rc") target_compile_definitions(NorthstarLauncher PRIVATE UNICODE _UNICODE) @@ -19,7 +19,7 @@ target_link_libraries( uuid.lib odbc32.lib odbccp32.lib - WS2_32.lib + ws2_32.lib ) set_target_properties( diff --git a/primedev/scripts/client/scriptmodmenu.cpp b/primedev/scripts/client/scriptmodmenu.cpp index 2e877db4..5ffe0fdf 100644 --- a/primedev/scripts/client/scriptmodmenu.cpp +++ b/primedev/scripts/client/scriptmodmenu.cpp @@ -49,6 +49,23 @@ ADD_SQFUNC("void", NSSetModEnabled, "string modName, bool enabled", "", ScriptCo return SQRESULT_NULL; } +ADD_SQFUNC("bool", NSIsModRemote, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI) +{ + const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); + + // manual lookup, not super performant but eh not a big deal + for (Mod& mod : g_pModManager->m_LoadedMods) + { + if (!mod.Name.compare(modName)) + { + g_pSquirrel<context>->pushbool(sqvm, mod.m_bIsRemote); + return SQRESULT_NOTNULL; + } + } + + return SQRESULT_NULL; +} + ADD_SQFUNC("string", NSGetModDescriptionByModName, "string modName", "", ScriptContext::SERVER | ScriptContext::CLIENT | ScriptContext::UI) { const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); diff --git a/primedev/scripts/scriptjson.cpp b/primedev/scripts/scriptjson.cpp index 06bda6f4..8959bf47 100644 --- a/primedev/scripts/scriptjson.cpp +++ b/primedev/scripts/scriptjson.cpp @@ -42,6 +42,8 @@ DecodeJsonArray(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char> g_pSquirrel<context>->pushinteger(sqvm, itr.GetInt()); g_pSquirrel<context>->arrayappend(sqvm, -2); break; + case rapidjson::kNullType: + break; } } } @@ -92,6 +94,8 @@ DecodeJsonTable(HSquirrelVM* sqvm, rapidjson::GenericValue<rapidjson::UTF8<char> } g_pSquirrel<context>->newslot(sqvm, -3, false); break; + case rapidjson::kNullType: + break; } } } diff --git a/primedev/server/buildainfile.cpp b/primedev/server/buildainfile.cpp index a7f59961..19a6d0e3 100644 --- a/primedev/server/buildainfile.cpp +++ b/primedev/server/buildainfile.cpp @@ -5,6 +5,8 @@ #include <fstream> #include <filesystem> +namespace fs = std::filesystem; + AUTOHOOK_INIT() const int AINET_VERSION_NUMBER = 57; diff --git a/primedev/server/serverpresence.cpp b/primedev/server/serverpresence.cpp index 509243f0..099f6e64 100644 --- a/primedev/server/serverpresence.cpp +++ b/primedev/server/serverpresence.cpp @@ -78,7 +78,7 @@ void ServerPresenceManager::CreateConVars() Cvar_ns_server_presence_update_rate = new ConVar( "ns_server_presence_update_rate", "5000", FCVAR_GAMEDLL, "How often we update our server's presence on server lists in ms"); - Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL | FCVAR_REPLICATED, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { NOTE_UNUSED(cvar); NOTE_UNUSED(pOldValue); NOTE_UNUSED(flOldValue); @@ -88,7 +88,7 @@ void ServerPresenceManager::CreateConVars() Cvar_hostname->SetValue(g_pServerPresence->Cvar_ns_server_name->GetString()); }); - Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "This server's description", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { + Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL | FCVAR_REPLICATED, "This server's description", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) { NOTE_UNUSED(cvar); NOTE_UNUSED(pOldValue); NOTE_UNUSED(flOldValue); diff --git a/primedev/server/serverpresence.h b/primedev/server/serverpresence.h index 94ecfe6a..07c6fb55 100644 --- a/primedev/server/serverpresence.h +++ b/primedev/server/serverpresence.h @@ -19,10 +19,7 @@ public: int m_iPlayerCount; int m_iMaxPlayers; - ServerPresence() - { - memset(this, 0, sizeof(this)); - } + ServerPresence() {} ServerPresence(const ServerPresence* obj) { diff --git a/primedev/shared/exploit_fixes/exploitfixes.cpp b/primedev/shared/exploit_fixes/exploitfixes.cpp index d96bc41e..1b3069f5 100644 --- a/primedev/shared/exploit_fixes/exploitfixes.cpp +++ b/primedev/shared/exploit_fixes/exploitfixes.cpp @@ -120,19 +120,31 @@ bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 if (!nameValid || !valValid) return BLOCKED_INFO("Missing null terminators"); - ConVar* pVar = g_pCVar->FindVar(entry->name); - - if (pVar) + // we only need to check if these cvars are valid on client as it will set actual cvars there + // on server this won't set any actual convars, only keyvalues in the player, which doesn't have really any potential for dumb + // stuff + if (!bIsServerFrame) { - memcpy( - entry->name, - pVar->m_ConCommandBase.m_pszName, - strlen(pVar->m_ConCommandBase.m_pszName) + 1); // Force name to match case - - int iFlags = bIsServerFrame ? FCVAR_USERINFO : FCVAR_REPLICATED; - if (!pVar->IsFlagSet(iFlags)) - return BLOCKED_INFO( - "Invalid flags (" << std::hex << "0x" << pVar->m_ConCommandBase.m_nFlags << "), var is " << entry->name); + ConVar* pVar = g_pCVar->FindVar(entry->name); + if (pVar) + { + memcpy( + entry->name, + pVar->m_ConCommandBase.m_pszName, + strlen(pVar->m_ConCommandBase.m_pszName) + 1); // Force name to match case + + if (!pVar->IsFlagSet(FCVAR_REPLICATED)) + { + spdlog::warn( + "Blocking replication of remote cvar {} from server (server's var has flag REPLICATED, while ours does not)", + entry->name); + + // don't block, as non-malicious servers might send bad cvars, and we still want those clients to be able to + // connect + memset(entry->name, 0, ENTRY_STR_LEN); + memset(entry->val, 0, ENTRY_STR_LEN); + } + } } } else diff --git a/primedev/shared/keyvalues.cpp b/primedev/shared/keyvalues.cpp index 36f891eb..46ce4e04 100644 --- a/primedev/shared/keyvalues.cpp +++ b/primedev/shared/keyvalues.cpp @@ -511,7 +511,7 @@ bool KeyValues::IsEmpty(const char* pszKeyName) KeyValues* KeyValues::GetFirstTrueSubKey(void) const { assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pSub : nullptr; + KeyValues* pRet = m_pSub; while (pRet && pRet->m_iDataType != TYPE_NONE) pRet = pRet->m_pPeer; @@ -525,7 +525,7 @@ KeyValues* KeyValues::GetFirstTrueSubKey(void) const KeyValues* KeyValues::GetNextTrueSubKey(void) const { assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pPeer : nullptr; + KeyValues* pRet = m_pPeer; while (pRet && pRet->m_iDataType != TYPE_NONE) pRet = pRet->m_pPeer; @@ -539,7 +539,7 @@ KeyValues* KeyValues::GetNextTrueSubKey(void) const KeyValues* KeyValues::GetFirstValue(void) const { assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pSub : nullptr; + KeyValues* pRet = m_pSub; while (pRet && pRet->m_iDataType == TYPE_NONE) pRet = pRet->m_pPeer; @@ -553,7 +553,7 @@ KeyValues* KeyValues::GetFirstValue(void) const KeyValues* KeyValues::GetNextValue(void) const { assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pPeer : nullptr; + KeyValues* pRet = m_pPeer; while (pRet && pRet->m_iDataType == TYPE_NONE) pRet = pRet->m_pPeer; @@ -566,7 +566,7 @@ KeyValues* KeyValues::GetNextValue(void) const KeyValues* KeyValues::GetFirstSubKey() const { assert_msg(this, "Member function called on NULL KeyValues"); - return this ? m_pSub : nullptr; + return m_pSub; } //----------------------------------------------------------------------------- @@ -575,7 +575,7 @@ KeyValues* KeyValues::GetFirstSubKey() const KeyValues* KeyValues::GetNextKey() const { assert_msg(this, "Member function called on NULL KeyValues"); - return this ? m_pPeer : nullptr; + return m_pPeer; } //----------------------------------------------------------------------------- diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp index 41a6a782..29540cce 100644 --- a/primedev/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -157,11 +157,6 @@ const char* SQTypeNameFromID(int type) 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(CSquirrelVM* newSqvm) { m_pSQVM = newSqvm; @@ -712,6 +707,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x3E49B0).RCast<sq_GetEntityConstantType>(); g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance = module.Offset(0x114F0).RCast<sq_getentityfrominstanceType>(); + g_pSquirrel<ScriptContext::CLIENT>->__sq_createscriptinstance = module.Offset(0xC20E0).RCast<sq_createscriptinstanceType>(); g_pSquirrel<ScriptContext::UI>->__sq_GetEntityConstant_CBaseEntity = g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity; g_pSquirrel<ScriptContext::UI>->__sq_getentityfrominstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance; @@ -805,6 +801,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) g_pSquirrel<ScriptContext::SERVER>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x418AF0).RCast<sq_GetEntityConstantType>(); g_pSquirrel<ScriptContext::SERVER>->__sq_getentityfrominstance = module.Offset(0x1E920).RCast<sq_getentityfrominstanceType>(); + g_pSquirrel<ScriptContext::SERVER>->__sq_createscriptinstance = module.Offset(0x43F2F0).RCast<sq_createscriptinstanceType>(); g_pSquirrel<ScriptContext::SERVER>->logger = NS::log::SCRIPT_SV; // Message buffer stuff @@ -845,3 +842,12 @@ void InitialiseSquirrelManagers() g_pSquirrel<ScriptContext::UI> = new SquirrelManager<ScriptContext::UI>; g_pSquirrel<ScriptContext::SERVER> = new SquirrelManager<ScriptContext::SERVER>; } + +// 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 std::shared_ptr<spdlog::logger> NS::log::squirrel_logger<ScriptContext::SERVER>(); +template std::shared_ptr<spdlog::logger> NS::log::squirrel_logger<ScriptContext::CLIENT>(); +template std::shared_ptr<spdlog::logger> NS::log::squirrel_logger<ScriptContext::UI>(); diff --git a/primedev/squirrel/squirrel.h b/primedev/squirrel/squirrel.h index 0c1f24d3..547b1efc 100644 --- a/primedev/squirrel/squirrel.h +++ b/primedev/squirrel/squirrel.h @@ -5,6 +5,8 @@ #include "core/math/vector.h" #include "mods/modmanager.h" +namespace fs = std::filesystem; + /* definitions from hell required to function @@ -115,6 +117,7 @@ public: sq_getfunctionType __sq_getfunction; sq_getentityfrominstanceType __sq_getentityfrominstance; + sq_createscriptinstanceType __sq_createscriptinstance; sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity; sq_pushnewstructinstanceType __sq_pushnewstructinstance; @@ -136,7 +139,7 @@ public: inline SQRESULT _call(HSquirrelVM* sqvm, const SQInteger args) { - return __sq_call(sqvm, args + 1, false, false); + return __sq_call(sqvm, args + 1, false, true); } inline SQInteger raiseerror(HSquirrelVM* sqvm, const SQChar* sError) @@ -447,7 +450,7 @@ inline VoidFunction SQMessageBufferPushArg(Vector3& arg) { // Vectors template <ScriptContext context> inline VoidFunction SQMessageBufferPushArg(SQObject* arg) { - return [arg]{ g_pSquirrel<context>->pushSQObject(g_pSquirrel<context>->m_pSQVM->sqvm, arg); }; + return [arg]{ g_pSquirrel<context>->pushobject(g_pSquirrel<context>->m_pSQVM->sqvm, arg); }; } // Ints template <ScriptContext context, typename T> diff --git a/primedev/squirrel/squirrelclasstypes.h b/primedev/squirrel/squirrelclasstypes.h index 91c3c468..3a39c957 100644 --- a/primedev/squirrel/squirrelclasstypes.h +++ b/primedev/squirrel/squirrelclasstypes.h @@ -227,6 +227,7 @@ typedef SQRESULT (*sq_setuserdatatypeidType)(HSquirrelVM* sqvm, SQInteger iStack // sq misc entity funcs typedef void* (*sq_getentityfrominstanceType)(CSquirrelVM* sqvm, SQObject* pInstance, char** ppEntityConstant); +typedef SQObject* (*sq_createscriptinstanceType)(void* ent); typedef char** (*sq_GetEntityConstantType)(); typedef int (*sq_getfunctionType)(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature); diff --git a/primedev/thirdparty/rapidjson/document.h b/primedev/thirdparty/rapidjson/document.h index 22fb2f56..a5465a3c 100644 --- a/primedev/thirdparty/rapidjson/document.h +++ b/primedev/thirdparty/rapidjson/document.h @@ -318,8 +318,6 @@ struct GenericStringRef { GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} - GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } - //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -330,6 +328,8 @@ private: //! Disallow construction from non-const array template<SizeType N> GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; //! Mark a character pointer as constant string diff --git a/primedev/wsockproxy/CMakeLists.txt b/primedev/wsockproxy/CMakeLists.txt index 0dbac745..3f6bce45 100644 --- a/primedev/wsockproxy/CMakeLists.txt +++ b/primedev/wsockproxy/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries( PRIVATE minhook mswsock.lib ws2_32.lib - ShLwApi.lib + shlwapi.lib imagehlp.lib dbghelp.lib kernel32.lib @@ -32,14 +32,13 @@ target_link_libraries( odbccp32.lib ) -target_precompile_headers( +target_compile_definitions( loader_wsock32_proxy - PRIVATE - pch.h + PRIVATE UNICODE + _UNICODE + WIN32_LEAN_AND_MEAN ) -target_compile_definitions(loader_wsock32_proxy PRIVATE UNICODE _UNICODE) - set_target_properties( loader_wsock32_proxy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NS_BINARY_DIR}/bin/x64_retail diff --git a/primedev/wsockproxy/dllmain.cpp b/primedev/wsockproxy/dllmain.cpp index 5a606e45..9716c1b8 100644 --- a/primedev/wsockproxy/dllmain.cpp +++ b/primedev/wsockproxy/dllmain.cpp @@ -1,5 +1,6 @@ #include "loader.h" +#include <windows.h> #include <filesystem> FARPROC p[73]; diff --git a/primedev/wsockproxy/loader.cpp b/primedev/wsockproxy/loader.cpp index a3abf11c..4664c20c 100644 --- a/primedev/wsockproxy/loader.cpp +++ b/primedev/wsockproxy/loader.cpp @@ -7,6 +7,8 @@ #include <filesystem> #include <iostream> +#include "MinHook.h" + namespace fs = std::filesystem; static wchar_t northstarPath[8192]; diff --git a/primedev/wsockproxy/loader.h b/primedev/wsockproxy/loader.h index 0c6fb053..6287e515 100644 --- a/primedev/wsockproxy/loader.h +++ b/primedev/wsockproxy/loader.h @@ -1,5 +1,7 @@ #pragma once +#include <windows.h> + extern wchar_t exePath[4096]; extern wchar_t buffer1[8192]; extern wchar_t buffer2[12288]; diff --git a/primedev/wsockproxy/pch.h b/primedev/wsockproxy/pch.h deleted file mode 100644 index ebc29547..00000000 --- a/primedev/wsockproxy/pch.h +++ /dev/null @@ -1,16 +0,0 @@ -// pch.h: This is a precompiled header file. -// Files listed below are compiled only once, improving build performance for future builds. -// This also affects IntelliSense performance, including code completion and many code browsing features. -// However, files listed here are ALL re-compiled if any one of them is updated between builds. -// Do not add files here that you will be updating frequently as this negates the performance advantage. - -#ifndef PCH_H -#define PCH_H - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -// Windows Header Files -#include <windows.h> - -#include "MinHook.h" - -#endif // PCH_H diff --git a/primedev/wsockproxy/wsock32.def b/primedev/wsockproxy/wsock32.def index 448440b4..187b2959 100644 --- a/primedev/wsockproxy/wsock32.def +++ b/primedev/wsockproxy/wsock32.def @@ -69,7 +69,6 @@ EXPORTS rresvport=ws2_32.rresvport s_perror=PROXY_s_perror select=ws2_32.select @18 - select=ws2_32.select @18 send=ws2_32.send @19 sendto=ws2_32.sendto @20 sethostname=ws2_32.sethostname |