aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/util
diff options
context:
space:
mode:
authorEmma Miler <emma.pi@protonmail.com>2022-12-19 19:32:16 +0100
committerGeckoEidechse <gecko.eidechse+git@pm.me>2023-01-04 14:45:48 +0100
commit27afb0ba38dcf0e74a4d09ba43e73261542b8e96 (patch)
tree28c737bdecc761fc8ca5257adfafaf2b325e1918 /NorthstarDLL/util
parentaf64117f09ba9b70d27c6b6da885d0474180849b (diff)
downloadNorthstarLauncher-27afb0ba38dcf0e74a4d09ba43e73261542b8e96.tar.gz
NorthstarLauncher-27afb0ba38dcf0e74a4d09ba43e73261542b8e96.zip
Restructuring (#365)
* Remove launcher proxy * Restructuring * More restructuring * Fix include dirs * Fix merge * Remove clang thing * Filters * Oops
Diffstat (limited to 'NorthstarDLL/util')
-rw-r--r--NorthstarDLL/util/printcommands.cpp174
-rw-r--r--NorthstarDLL/util/printcommands.h6
-rw-r--r--NorthstarDLL/util/printmaps.cpp168
-rw-r--r--NorthstarDLL/util/printmaps.h2
-rw-r--r--NorthstarDLL/util/version.cpp96
-rw-r--r--NorthstarDLL/util/version.h6
6 files changed, 452 insertions, 0 deletions
diff --git a/NorthstarDLL/util/printcommands.cpp b/NorthstarDLL/util/printcommands.cpp
new file mode 100644
index 00000000..5552c50c
--- /dev/null
+++ b/NorthstarDLL/util/printcommands.cpp
@@ -0,0 +1,174 @@
+#include "pch.h"
+#include "printcommands.h"
+#include "core/convar/convar.h"
+#include "core/convar/concommand.h"
+
+void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name)
+{
+ if (!command)
+ {
+ spdlog::info("unknown command {}", name);
+ return;
+ }
+
+ // temp because command->IsCommand does not currently work
+ ConVar* cvar = R2::g_pCVar->FindVar(command->m_pszName);
+
+ // build string for flags if not FCVAR_NONE
+ std::string flagString;
+ if (command->GetFlags() != FCVAR_NONE)
+ {
+ flagString = "( ";
+
+ for (auto& flagPair : g_PrintCommandFlags)
+ {
+ if (command->GetFlags() & flagPair.first)
+ {
+ // special case, slightly hacky: PRINTABLEONLY is for commands, GAMEDLL_FOR_REMOTE_CLIENTS is for concommands, both have the
+ // same value
+ if (flagPair.first == FCVAR_PRINTABLEONLY)
+ {
+ if (cvar && !strcmp(flagPair.second, "GAMEDLL_FOR_REMOTE_CLIENTS"))
+ continue;
+
+ if (!cvar && !strcmp(flagPair.second, "PRINTABLEONLY"))
+ continue;
+ }
+
+ flagString += flagPair.second;
+ flagString += " ";
+ }
+ }
+
+ flagString += ") ";
+ }
+
+ if (cvar)
+ spdlog::info("\"{}\" = \"{}\" {}- {}", cvar->GetBaseName(), cvar->GetString(), flagString, cvar->GetHelpText());
+ else
+ spdlog::info("\"{}\" {} - {}", command->m_pszName, flagString, command->GetHelpText());
+}
+
+void TryPrintCvarHelpForCommand(const char* pCommand)
+{
+ // try to display help text for an inputted command string from the console
+ int pCommandLen = strlen(pCommand);
+ char* pCvarStr = new char[pCommandLen];
+ strcpy(pCvarStr, pCommand);
+
+ // trim whitespace from right
+ for (int i = pCommandLen - 1; i; i--)
+ {
+ if (isspace(pCvarStr[i]))
+ pCvarStr[i] = '\0';
+ else
+ break;
+ }
+
+ // check if we're inputting a cvar, but not setting it at all
+ ConVar* cvar = R2::g_pCVar->FindVar(pCvarStr);
+ if (cvar)
+ PrintCommandHelpDialogue(&cvar->m_ConCommandBase, pCvarStr);
+
+ delete[] pCvarStr;
+}
+
+void ConCommand_help(const CCommand& arg)
+{
+ if (arg.ArgC() < 2)
+ {
+ spdlog::info("Usage: help <cvarname>");
+ return;
+ }
+
+ PrintCommandHelpDialogue(R2::g_pCVar->FindCommandBase(arg.Arg(1)), arg.Arg(1));
+}
+
+void ConCommand_find(const CCommand& arg)
+{
+ if (arg.ArgC() < 2)
+ {
+ spdlog::info("Usage: find <string> [<string>...]");
+ return;
+ }
+
+ char pTempName[256];
+ char pTempSearchTerm[256];
+
+ for (auto& map : R2::g_pCVar->DumpToMap())
+ {
+ bool bPrintCommand = true;
+ for (int i = 0; i < arg.ArgC() - 1; i++)
+ {
+ // make lowercase to avoid case sensitivity
+ strncpy_s(pTempName, sizeof(pTempName), map.second->m_pszName, sizeof(pTempName) - 1);
+ strncpy_s(pTempSearchTerm, sizeof(pTempSearchTerm), arg.Arg(i + 1), sizeof(pTempSearchTerm) - 1);
+
+ for (int i = 0; pTempName[i]; i++)
+ pTempName[i] = tolower(pTempName[i]);
+
+ for (int i = 0; pTempSearchTerm[i]; i++)
+ pTempSearchTerm[i] = tolower(pTempSearchTerm[i]);
+
+ if (!strstr(pTempName, pTempSearchTerm))
+ {
+ bPrintCommand = false;
+ break;
+ }
+ }
+
+ if (bPrintCommand)
+ PrintCommandHelpDialogue(map.second, map.second->m_pszName);
+ }
+}
+
+void ConCommand_findflags(const CCommand& arg)
+{
+ if (arg.ArgC() < 2)
+ {
+ spdlog::info("Usage: findflags <string>");
+ for (auto& flagPair : g_PrintCommandFlags)
+ spdlog::info(" - {}", flagPair.second);
+
+ return;
+ }
+
+ // convert input flag to uppercase
+ char* upperFlag = new char[strlen(arg.Arg(1))];
+ strcpy(upperFlag, arg.Arg(1));
+
+ for (int i = 0; upperFlag[i]; i++)
+ upperFlag[i] = toupper(upperFlag[i]);
+
+ // resolve flag name => int flags
+ int resolvedFlag = FCVAR_NONE;
+ for (auto& flagPair : g_PrintCommandFlags)
+ {
+ if (!strcmp(flagPair.second, upperFlag))
+ {
+ resolvedFlag |= flagPair.first;
+ break;
+ }
+ }
+
+ // print cvars
+ for (auto& map : R2::g_pCVar->DumpToMap())
+ {
+ if (map.second->m_nFlags & resolvedFlag)
+ PrintCommandHelpDialogue(map.second, map.second->m_pszName);
+ }
+
+ delete[] upperFlag;
+}
+
+void InitialiseCommandPrint()
+{
+ RegisterConCommand("find", ConCommand_find, "Find concommands with the specified string in their name/help text.", FCVAR_NONE);
+ RegisterConCommand("findflags", ConCommand_findflags, "Find concommands by flags.", FCVAR_NONE);
+
+ // help is already a command, so we need to modify the preexisting command to use our func instead
+ // and clear the flags also
+ ConCommand* helpCommand = R2::g_pCVar->FindCommand("help");
+ helpCommand->m_nFlags = FCVAR_NONE;
+ helpCommand->m_pCommandCallback = ConCommand_help;
+}
diff --git a/NorthstarDLL/util/printcommands.h b/NorthstarDLL/util/printcommands.h
new file mode 100644
index 00000000..cb72e5cc
--- /dev/null
+++ b/NorthstarDLL/util/printcommands.h
@@ -0,0 +1,6 @@
+#pragma once
+#include "core/convar/concommand.h"
+
+void PrintCommandHelpDialogue(const ConCommandBase* command, const char* name);
+void TryPrintCvarHelpForCommand(const char* pCommand);
+void InitialiseCommandPrint();
diff --git a/NorthstarDLL/util/printmaps.cpp b/NorthstarDLL/util/printmaps.cpp
new file mode 100644
index 00000000..e0192d69
--- /dev/null
+++ b/NorthstarDLL/util/printmaps.cpp
@@ -0,0 +1,168 @@
+#include "pch.h"
+#include "printmaps.h"
+#include "core/convar/convar.h"
+#include "core/convar/concommand.h"
+#include "mods/modmanager.h"
+#include "core/tier0.h"
+#include "engine/r2engine.h"
+
+#include <filesystem>
+#include <regex>
+
+AUTOHOOK_INIT()
+
+enum class MapSource_t
+{
+ VPK,
+ GAMEDIR,
+ MOD
+};
+
+const std::unordered_map<MapSource_t, const char*> PrintMapSource = {
+ {MapSource_t::VPK, "VPK"}, {MapSource_t::MOD, "MOD"}, {MapSource_t::GAMEDIR, "R2"}};
+
+struct MapVPKInfo
+{
+ std::string name;
+ std::string parent;
+ MapSource_t source;
+};
+
+// our current list of maps in the game
+std::vector<MapVPKInfo> vMapList;
+
+void RefreshMapList()
+{
+ vMapList.clear();
+
+ // get modded maps
+ // TODO: could probably check mod vpks to get mapnames from there too?
+ for (auto& modFilePair : g_pModManager->m_ModFiles)
+ {
+ ModOverrideFile file = modFilePair.second;
+ if (file.m_Path.extension() == ".bsp" && file.m_Path.parent_path().string() == "maps") // only allow mod maps actually in /maps atm
+ {
+ MapVPKInfo& map = vMapList.emplace_back();
+ map.name = file.m_Path.stem().string();
+ map.parent = file.m_pOwningMod->Name;
+ map.source = MapSource_t::MOD;
+ }
+ }
+
+ // get maps in vpk
+ {
+ const int iNumRetailNonMapVpks = 1;
+ static const char* const ppRetailNonMapVpks[] = {
+ "englishclient_frontend.bsp.pak000_dir.vpk"}; // don't include mp_common here as it contains mp_lobby
+
+ // matches directory vpks, and captures their map name in the first group
+ static const std::regex rVpkMapRegex("englishclient_([a-zA-Z_]+)\\.bsp\\.pak000_dir\\.vpk", std::regex::icase);
+
+ for (fs::directory_entry file : fs::directory_iterator("./vpk"))
+ {
+ std::string pathString = file.path().filename().string();
+
+ bool bIsValidMapVpk = true;
+ for (int i = 0; i < iNumRetailNonMapVpks; i++)
+ {
+ if (!pathString.compare(ppRetailNonMapVpks[i]))
+ {
+ bIsValidMapVpk = false;
+ break;
+ }
+ }
+
+ if (!bIsValidMapVpk)
+ continue;
+
+ // run our map vpk regex on the filename
+ std::smatch match;
+ std::regex_match(pathString, match, rVpkMapRegex);
+
+ if (match.length() < 2)
+ continue;
+
+ std::string mapName = match[1].str();
+ // special case: englishclient_mp_common contains mp_lobby, so hardcode the name here
+ if (mapName == "mp_common")
+ mapName = "mp_lobby";
+
+ MapVPKInfo& map = vMapList.emplace_back();
+ map.name = mapName;
+ map.parent = pathString;
+ map.source = MapSource_t::VPK;
+ }
+ }
+
+ // get maps in game dir
+ for (fs::directory_entry file : fs::directory_iterator(fmt::format("{}/maps", R2::g_pModName)))
+ {
+ if (file.path().extension() == ".bsp")
+ {
+ MapVPKInfo& map = vMapList.emplace_back();
+ map.name = file.path().stem().string();
+ map.parent = "R2";
+ map.source = MapSource_t::GAMEDIR;
+ }
+ }
+}
+
+// clang-format off
+AUTOHOOK(_Host_Map_f_CompletionFunc, engine.dll + 0x161AE0,
+int, __fastcall, (const char const* cmdname, const char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]))
+// clang-format on
+{
+ // don't update our map list often from this func, only refresh every 10 seconds so we avoid constantly reading fs
+ static double flLastAutocompleteRefresh = -999;
+
+ if (flLastAutocompleteRefresh + 10.0 < Tier0::Plat_FloatTime())
+ {
+ RefreshMapList();
+ flLastAutocompleteRefresh = Tier0::Plat_FloatTime();
+ }
+
+ // use a custom autocomplete func for all map loading commands
+ const int cmdLength = strlen(cmdname);
+ const char* query = partial + cmdLength;
+ const int queryLength = strlen(query);
+
+ int numMaps = 0;
+ for (int i = 0; i < vMapList.size() && numMaps < COMMAND_COMPLETION_MAXITEMS; i++)
+ {
+ if (!strncmp(query, vMapList[i].name.c_str(), queryLength))
+ {
+ strcpy(commands[numMaps], cmdname);
+ strncpy_s(
+ commands[numMaps++] + cmdLength,
+ COMMAND_COMPLETION_ITEM_LENGTH,
+ &vMapList[i].name[0],
+ COMMAND_COMPLETION_ITEM_LENGTH - cmdLength);
+ }
+ }
+
+ return numMaps;
+}
+
+void ConCommand_maps(const CCommand& args)
+{
+ if (args.ArgC() < 2)
+ {
+ spdlog::info("Usage: maps <substring>");
+ spdlog::info("maps * for full listing");
+ return;
+ }
+
+ RefreshMapList();
+
+ for (MapVPKInfo& map : vMapList) // need to figure out a nice way to include parent path without making the formatting awful
+ if ((*args.Arg(1) == '*' && !args.Arg(1)[1]) || strstr(map.name.c_str(), args.Arg(1)))
+ spdlog::info("({}) {}", PrintMapSource.at(map.source), map.name);
+}
+
+void InitialiseMapsPrint()
+{
+ AUTOHOOK_DISPATCH()
+
+ ConCommand* mapsCommand = R2::g_pCVar->FindCommand("maps");
+ mapsCommand->m_pCommandCallback = ConCommand_maps;
+}
diff --git a/NorthstarDLL/util/printmaps.h b/NorthstarDLL/util/printmaps.h
new file mode 100644
index 00000000..b01761c0
--- /dev/null
+++ b/NorthstarDLL/util/printmaps.h
@@ -0,0 +1,2 @@
+#pragma once
+void InitialiseMapsPrint();
diff --git a/NorthstarDLL/util/version.cpp b/NorthstarDLL/util/version.cpp
new file mode 100644
index 00000000..8697b61f
--- /dev/null
+++ b/NorthstarDLL/util/version.cpp
@@ -0,0 +1,96 @@
+#include "util/version.h"
+#include "pch.h"
+#include "ns_version.h"
+#include "dedicated/dedicated.h"
+
+char version[16];
+char NSUserAgent[256];
+
+void InitialiseVersion()
+{
+ constexpr int northstar_version[4] {NORTHSTAR_VERSION};
+ int ua_len = 0;
+
+ // We actually use the rightmost integer do determine whether or not we're a debug/dev build
+ // If it is set to 1, we are a dev build
+ // On github CI, we set this 1 to a 0 automatically as we replace the 0,0,0,1 with the real version number
+ if (northstar_version[3] == 1)
+ {
+ sprintf(version, "%d.%d.%d.%d+dev", northstar_version[0], northstar_version[1], northstar_version[2], northstar_version[3]);
+ ua_len += snprintf(
+ NSUserAgent + ua_len,
+ sizeof(NSUserAgent) - ua_len,
+ "R2Northstar/%d.%d.%d+dev",
+ northstar_version[0],
+ northstar_version[1],
+ northstar_version[2]);
+ }
+ else
+ {
+ sprintf(version, "%d.%d.%d.%d", northstar_version[0], northstar_version[1], northstar_version[2], northstar_version[3]);
+ ua_len += snprintf(
+ NSUserAgent + ua_len,
+ sizeof(NSUserAgent) - ua_len,
+ "R2Northstar/%d.%d.%d",
+ northstar_version[0],
+ northstar_version[1],
+ northstar_version[2]);
+ }
+
+ if (IsDedicatedServer())
+ ua_len += snprintf(NSUserAgent + ua_len, sizeof(NSUserAgent) - ua_len, " (Dedicated)");
+
+ // Add the host platform info to the user agent.
+ //
+ // note: ntdll will always be loaded
+ HMODULE ntdll = GetModuleHandleA("ntdll");
+ if (ntdll)
+ {
+ // real win32 version info (i.e., ignore manifest)
+ DWORD(WINAPI * RtlGetVersion)(LPOSVERSIONINFOEXW);
+ *(FARPROC*)(&RtlGetVersion) = GetProcAddress(ntdll, "RtlGetVersion");
+
+ // wine version (7.0-rc1, 7.0, 7.11, etc)
+ const char*(CDECL * wine_get_version)(void);
+ *(FARPROC*)(&wine_get_version) = GetProcAddress(ntdll, "wine_get_version");
+
+ // human-readable build string (e.g., "wine-7.22 (Staging)")
+ const char*(CDECL * wine_get_build_id)(void);
+ *(FARPROC*)(&wine_get_build_id) = GetProcAddress(ntdll, "wine_get_build_id");
+
+ // uname sysname (Darwin, Linux, etc) and release (kernel version string)
+ void(CDECL * wine_get_host_version)(const char** sysname, const char** release);
+ *(FARPROC*)(&wine_get_host_version) = GetProcAddress(ntdll, "wine_get_host_version");
+
+ OSVERSIONINFOEXW osvi = {};
+ const char *wine_version = NULL, *wine_build_id = NULL, *wine_sysname = NULL, *wine_release = NULL;
+ if (RtlGetVersion)
+ RtlGetVersion(&osvi);
+ if (wine_get_version)
+ wine_version = wine_get_version();
+ if (wine_get_build_id)
+ wine_build_id = wine_get_build_id();
+ if (wine_get_host_version)
+ wine_get_host_version(&wine_sysname, &wine_release);
+
+ // windows version
+ if (osvi.dwMajorVersion)
+ ua_len += snprintf(
+ NSUserAgent + ua_len,
+ sizeof(NSUserAgent) - ua_len,
+ " Windows/%d.%d.%d",
+ osvi.dwMajorVersion,
+ osvi.dwMinorVersion,
+ osvi.dwBuildNumber);
+
+ // wine version
+ if (wine_version && wine_build_id)
+ ua_len += snprintf(NSUserAgent + ua_len, sizeof(NSUserAgent) - ua_len, " Wine/%s (%s)", wine_version, wine_build_id);
+
+ // wine host system version
+ if (wine_sysname && wine_release)
+ ua_len += snprintf(NSUserAgent + ua_len, sizeof(NSUserAgent) - ua_len, " %s/%s", wine_sysname, wine_release);
+ }
+
+ return;
+}
diff --git a/NorthstarDLL/util/version.h b/NorthstarDLL/util/version.h
new file mode 100644
index 00000000..261a4cf1
--- /dev/null
+++ b/NorthstarDLL/util/version.h
@@ -0,0 +1,6 @@
+#pragma once
+
+extern char version[16];
+extern char NSUserAgent[32];
+
+void InitialiseVersion();