#include "tier1/utlrbtree.h" #include "tier1/utlmap.h" #include "tier1/NetAdr.h" #include "tier1/cvar.h" #include "public/const.h" #include "engine/sys_dll2.h" #include "filesystem/filesystem.h" #include "vstdlib/concommandhash.h" //----------------------------------------------------------------------------- // Purpose: create //----------------------------------------------------------------------------- ConVar* ConVar::StaticCreate(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t pCallback, const char* pszUsageString) { ConVar* pNewConVar = (ConVar*)malloc(sizeof(ConVar)); pNewConVar->m_bRegistered = false; *(ConVar**)pNewConVar = g_pConVarVBTable; char* pConVarVFTable = (char*)pNewConVar + sizeof(ConCommandBase); *(IConVar**)pConVarVFTable = g_pConVarVFTable; pNewConVar->m_pszName = nullptr; pNewConVar->m_pszHelpString = nullptr; pNewConVar->m_pszUsageString = nullptr; pNewConVar->s_pAccessor = nullptr; pNewConVar->m_nFlags = FCVAR_NONE; pNewConVar->m_pNext = nullptr; pNewConVar->m_fnChangeCallbacks.Init(); v_ConVar_Register(pNewConVar, pszName, pszDefaultValue, nFlags, pszHelpString, bMin, fMin, bMax, fMax, pCallback, pszUsageString); return pNewConVar; } //----------------------------------------------------------------------------- // Purpose: destroy //----------------------------------------------------------------------------- void ConVar::Destroy(void) { v_ConVar_Unregister(this); } //----------------------------------------------------------------------------- // Purpose: construct/allocate //----------------------------------------------------------------------------- ConVar::ConVar(void) : m_pParent(nullptr) , m_pszDefaultValue(nullptr) , m_bHasMin(false) , m_fMinVal(0.f) , m_bHasMax(false) , m_fMaxVal(0.f) { m_Value.m_pszString = nullptr; m_Value.m_iStringLength = 0; m_Value.m_fValue = 0.0f; m_Value.m_nValue = 0; } //----------------------------------------------------------------------------- // Purpose: destructor //----------------------------------------------------------------------------- //ConVar::~ConVar(void) //{ // if (m_Value.m_pszString) // { // delete[] m_Value.m_pszString); // m_Value.m_pszString = NULL; // } //} ////----------------------------------------------------------------------------- //// Purpose: Returns the base ConVar name. //// Output : const char* ////----------------------------------------------------------------------------- //const char* ConVar::GetBaseName(void) const //{ // return m_pParent->m_pszName; //} // ////----------------------------------------------------------------------------- //// Purpose: Returns the ConVar help text. //// Output : const char* ////----------------------------------------------------------------------------- //const char* ConVar::GetHelpText(void) const //{ // return m_pParent->m_pszHelpString; //} // ////----------------------------------------------------------------------------- //// Purpose: Returns the ConVar usage text. //// Output : const char* ////----------------------------------------------------------------------------- //const char* ConVar::GetUsageText(void) const //{ // return m_pParent->m_pszUsageString; //} //----------------------------------------------------------------------------- // Purpose: // Input : flMaxVal - //----------------------------------------------------------------------------- void ConVar::SetMax(float flMaxVal) { m_pParent->m_fMaxVal = flMaxVal; m_pParent->m_bHasMax = true; } //----------------------------------------------------------------------------- // Purpose: // Input : flMinVal - //----------------------------------------------------------------------------- void ConVar::SetMin(float flMinVal) { m_pParent->m_fMinVal = flMinVal; m_pParent->m_bHasMin = true; } //----------------------------------------------------------------------------- // Purpose: // Input : flMinVal - // Output : true if there is a min set. //----------------------------------------------------------------------------- bool ConVar::GetMin(float& flMinVal) const { flMinVal = m_pParent->m_fMinVal; return m_pParent->m_bHasMin; } //----------------------------------------------------------------------------- // Purpose: // Input : flMaxVal - // Output : true if there is a max set. //----------------------------------------------------------------------------- bool ConVar::GetMax(float& flMaxVal) const { flMaxVal = m_pParent->m_fMaxVal; return m_pParent->m_bHasMax; } //----------------------------------------------------------------------------- // Purpose: returns the min value. // Output : float //----------------------------------------------------------------------------- float ConVar::GetMinValue(void) const { return m_pParent->m_fMinVal; } //----------------------------------------------------------------------------- // Purpose: returns the max value. // Output : float //----------------------------------------------------------------------------- float ConVar::GetMaxValue(void) const { return m_pParent->m_fMaxVal; } //----------------------------------------------------------------------------- // Purpose: checks if ConVar has min value. // Output : bool //----------------------------------------------------------------------------- bool ConVar::HasMin(void) const { return m_pParent->m_bHasMin; } //----------------------------------------------------------------------------- // Purpose: checks if ConVar has max value. // Output : bool //----------------------------------------------------------------------------- bool ConVar::HasMax(void) const { return m_pParent->m_bHasMax; } //----------------------------------------------------------------------------- // Purpose: sets the ConVar int value. // Input : nValue - //----------------------------------------------------------------------------- void ConVar::SetValue(int nValue) { ConVar* pCVar = reinterpret_cast(m_pParent); pCVar->InternalSetIntValue(nValue); } //----------------------------------------------------------------------------- // Purpose: sets the ConVar float value. // Input : flValue - //----------------------------------------------------------------------------- void ConVar::SetValue(float flValue) { ConVar* pCVar = reinterpret_cast(m_pParent); pCVar->InternalSetFloatValue(flValue); } //----------------------------------------------------------------------------- // Purpose: sets the ConVar string value. // Input : *szValue - //----------------------------------------------------------------------------- void ConVar::SetValue(const char* pszValue) { ConVar* pCVar = reinterpret_cast(m_pParent); pCVar->InternalSetValue(pszValue); } //----------------------------------------------------------------------------- // Purpose: sets the ConVar color value. // Input : value - //----------------------------------------------------------------------------- void ConVar::SetValue(Color value) { ConVar* pCVar = reinterpret_cast(m_pParent); pCVar->InternalSetColorValue(value); } //----------------------------------------------------------------------------- // Purpose: // Input : *value - //----------------------------------------------------------------------------- void ConVar::InternalSetColorValue(Color value) { // Stuff color values into an int int nValue = 0; unsigned char* pColorElement = (reinterpret_cast(&nValue)); pColorElement[0] = value[0]; pColorElement[1] = value[1]; pColorElement[2] = value[2]; pColorElement[3] = value[3]; // Call the int internal set InternalSetIntValue(nValue); } //----------------------------------------------------------------------------- // Purpose: Reset to default value. //----------------------------------------------------------------------------- void ConVar::Revert(void) { SetValue(m_pszDefaultValue); } //----------------------------------------------------------------------------- // Purpose: returns the default ConVar value. // Output : const char //----------------------------------------------------------------------------- const char* ConVar::GetDefault(void) const { return m_pParent->m_pszDefaultValue; } //----------------------------------------------------------------------------- // Purpose: sets the default ConVar value. // Input : *pszDefault - //----------------------------------------------------------------------------- void ConVar::SetDefault(const char* pszDefault) { static const char* pszEmpty = ""; m_pszDefaultValue = pszDefault ? pszDefault : pszEmpty; assert(m_pszDefaultValue); } //----------------------------------------------------------------------------- // Purpose: sets the ConVar color value from string. // Input : *pszValue - //----------------------------------------------------------------------------- bool ConVar::SetColorFromString(const char* pszValue) { bool bColor = false; // Try pulling RGBA color values out of the string. int nRGBA[4]; int nParamsRead = sscanf_s(pszValue, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3])); if (nParamsRead >= 3) { // This is probably a color! if (nParamsRead == 3) { // Assume they wanted full alpha. nRGBA[3] = 255; } if (nRGBA[0] >= 0 && nRGBA[0] <= 255 && nRGBA[1] >= 0 && nRGBA[1] <= 255 && nRGBA[2] >= 0 && nRGBA[2] <= 255 && nRGBA[3] >= 0 && nRGBA[3] <= 255) { //printf("*** WOW! Found a color!! ***\n"); // This is definitely a color! bColor = true; // Stuff all the values into each byte of our int. unsigned char* pColorElement = (reinterpret_cast(&m_Value.m_nValue)); pColorElement[0] = (unsigned char)nRGBA[0]; pColorElement[1] = (unsigned char)nRGBA[1]; pColorElement[2] = (unsigned char)nRGBA[2]; pColorElement[3] = (unsigned char)nRGBA[3]; // Copy that value into our float. m_Value.m_fValue = static_cast(m_Value.m_nValue); } } return bColor; } //----------------------------------------------------------------------------- // Purpose: changes the ConVar string value. // Input : *pszTempVal - flOldValue //----------------------------------------------------------------------------- void ConVar::ChangeStringValue(const char* pszTempVal) { Assert(!(m_nFlags & FCVAR_NEVER_AS_STRING)); char* pszOldValue = (char*)stackalloc(m_Value.m_iStringLength); memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength); size_t len = strlen(pszTempVal) + 1; if (len > m_Value.m_iStringLength) { if (m_Value.m_pszString) { delete[] m_Value.m_pszString; } m_Value.m_pszString = new char[len]; m_Value.m_iStringLength = len; } memcpy(reinterpret_cast(m_Value.m_pszString), pszTempVal, len); // Invoke any necessary callback function for (int i = 0; i < m_fnChangeCallbacks.Count(); ++i) { m_fnChangeCallbacks[i](this, pszOldValue, NULL); } if (g_pCVar) { g_pCVar->CallGlobalChangeCallbacks(this, pszOldValue); } stackfree(pszOldValue); } //----------------------------------------------------------------------------- // Purpose: Install a change callback (there shouldn't already be one....) // Input : callback - // bInvoke - //----------------------------------------------------------------------------- void ConVar::InstallChangeCallback(FnChangeCallback_t callback, bool bInvoke /*=true*/) { if (!callback) { Warning(eDLL_T::ENGINE, "%s: Called with NULL callback; ignoring!!!\n", __FUNCTION__); return; } if (m_pParent->m_fnChangeCallbacks.Find(callback) != m_pParent->m_fnChangeCallbacks.InvalidIndex()) { // Same ptr added twice, sigh... Warning(eDLL_T::ENGINE, "%s: Ignoring duplicate change callback!!!\n", __FUNCTION__); return; } m_pParent->m_fnChangeCallbacks.AddToTail(callback); // Call it immediately to set the initial value... if (bInvoke) { callback(this, m_Value.m_pszString, m_Value.m_fValue); } } //----------------------------------------------------------------------------- // Purpose: Install a change callback (there shouldn't already be one....) // Input : callback - //----------------------------------------------------------------------------- void ConVar::RemoveChangeCallback(FnChangeCallback_t callback) { m_pParent->m_fnChangeCallbacks.FindAndRemove(callback); } #define SET_CONVARFLAG(x, y) SetFlag(FCVAR_##x, #x, y) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- ConVarFlags::ConVarFlags() : m_StringToFlags(DefLessFunc(const char*)) { m_Count = 0; SET_CONVARFLAG(NONE, "none"); SET_CONVARFLAG(UNREGISTERED, "unregistered"); SET_CONVARFLAG(DEVELOPMENTONLY, "development_only"); SET_CONVARFLAG(GAMEDLL, "server"); SET_CONVARFLAG(CLIENTDLL, "client"); SET_CONVARFLAG(HIDDEN, "hidden"); SET_CONVARFLAG(PROTECTED, "protected"); SET_CONVARFLAG(SPONLY, "singleplayer"); SET_CONVARFLAG(ARCHIVE, "archive"); SET_CONVARFLAG(NOTIFY, "notify"); SET_CONVARFLAG(USERINFO, "userinfo"); SET_CONVARFLAG(PRINTABLEONLY, "printable_only"); SET_CONVARFLAG(UNLOGGED, "unlogged"); SET_CONVARFLAG(NEVER_AS_STRING, "never_as_string"); SET_CONVARFLAG(REPLICATED, "replicated"); SET_CONVARFLAG(CHEAT, "cheat"); SET_CONVARFLAG(SS, "splitscreen"); SET_CONVARFLAG(DEMO, "demo"); SET_CONVARFLAG(DONTRECORD, "dont_record"); SET_CONVARFLAG(SS_ADDED, "splitscreen_added"); SET_CONVARFLAG(RELEASE, "release"); SET_CONVARFLAG(RELOAD_MATERIALS, "reload_materials"); SET_CONVARFLAG(RELOAD_TEXTURES, "reload_textures"); SET_CONVARFLAG(NOT_CONNECTED, "not_connected"); SET_CONVARFLAG(MATERIAL_SYSTEM_THREAD, "materialsystem_thread"); SET_CONVARFLAG(MATERIAL_THREAD_MASK, "material_thread_mask"); SET_CONVARFLAG(ARCHIVE_PLAYERPROFILE, "playerprofile"); SET_CONVARFLAG(SERVER_CAN_EXECUTE, "server_can_execute"); SET_CONVARFLAG(SERVER_CANNOT_QUERY, "server_cannot_query"); SET_CONVARFLAG(CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute"); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ConVarFlags::SetFlag(const int nFlag, const char* szDesc, const char* szShortDesc) { Assert(m_Count < SDK_ARRAYSIZE(m_FlagsToDesc)); m_StringToFlags.Insert(szDesc, nFlag); m_FlagsToDesc[m_Count] = { nFlag, szDesc, szShortDesc }; m_Count++; } ConVarFlags g_ConVarFlags; //----------------------------------------------------------------------------- // Purpose: Parse input flag string into bitfield // Input : *pszFlags - // &nFlags - // *pszConVarName - //----------------------------------------------------------------------------- bool ConVar_ParseFlagString(const char* pszFlags, int& nFlags, const char* pszConVarName) { size_t len = V_strlen(pszFlags); int flags = FCVAR_NONE; CUtlString sFlag; for (size_t i = 0; i < len; ++i) { char c = pszFlags[i]; if (V_isspace(c)) continue; if (c != '|') sFlag += c; if (c == '|' || i == len - 1) { if (sFlag == "") continue; int find = g_ConVarFlags.m_StringToFlags.FindElement(sFlag.Get(), -1); if (find == -1) { Warning(eDLL_T::ENGINE, "%s: Attempted to parse invalid flag '%s' for convar '%s'\n", __FUNCTION__, sFlag.Get(), pszConVarName); return false; } flags |= find; sFlag = ""; } } nFlags = flags; return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ConVar_AppendFlags(ConCommandBase* var, char* buf, size_t bufsize) { for (int i = 0; i < ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); ++i) { const ConVarFlags::FlagDesc_t& info = g_ConVarFlags.m_FlagsToDesc[i]; if (var->IsFlagSet(info.bit)) { char append[128]; V_snprintf(append, sizeof(append), " %s", info.shortdesc); V_strncat(buf, append, bufsize); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ConVar_PrintDescription(ConCommandBase* pVar) { bool bMin, bMax; float fMin, fMax; const char* pStr; Assert(pVar); Color clr(255, 100, 100, 255); char outstr[4096]; outstr[0] = 0; if (!pVar->IsCommand()) { ConVar* var = (ConVar*)pVar; bMin = var->GetMin(fMin); bMax = var->GetMax(fMax); const char* value = NULL; char tempVal[256]; if (var->IsFlagSet(FCVAR_NEVER_AS_STRING)) { value = tempVal; int intVal = var->GetInt(); float floatVal = var->GetFloat(); if (fabs((float)intVal - floatVal) < 0.000001) { V_snprintf(tempVal, sizeof(tempVal), "%d", intVal); } else { V_snprintf(tempVal, sizeof(tempVal), "%f", floatVal); } } else { value = var->GetString(); } if (value) { AppendPrintf(outstr, sizeof(outstr), "\"%s\" = \"%s\"", var->GetName(), value); if (V_stricmp(value, var->GetDefault())) { AppendPrintf(outstr, sizeof(outstr), " ( def. \"%s\" )", var->GetDefault()); } } if (bMin) { AppendPrintf(outstr, sizeof(outstr), " min. %f", fMin); } if (bMax) { AppendPrintf(outstr, sizeof(outstr), " max. %f", fMax); } } else { ConCommand* var = (ConCommand*)pVar; AppendPrintf(outstr, sizeof(outstr), "\"%s\" ", var->GetName()); } ConVar_AppendFlags(pVar, outstr, sizeof(outstr)); pStr = pVar->GetHelpText(); if (pStr && *pStr) { DevMsg(eDLL_T::ENGINE, "%-80s - %.80s\n", outstr, pStr); } else { DevMsg(eDLL_T::ENGINE, "%-80s\n", outstr); } } static void PrintListHeader(FileHandle_t& f) { char csvflagstr[1024]; csvflagstr[0] = 0; int c = ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); for (int i = 0; i < c; ++i) { char csvf[64]; ConVarFlags::FlagDesc_t& entry = g_ConVarFlags.m_FlagsToDesc[i]; Q_snprintf(csvf, sizeof(csvf), "\"%s\",", entry.desc); Q_strncat(csvflagstr, csvf, sizeof(csvflagstr) - strlen(csvflagstr) - 1); } FileSystem()->FPrintf(f, "\"%s\",\"%s\",%s,\"%s\"\n", "Name", "Value", csvflagstr, "Help Text"); } //----------------------------------------------------------------------------- // Purpose: // Input : *var - // *f - //----------------------------------------------------------------------------- static void PrintCvar(ConVar* var, bool logging, FileHandle_t& fh) { char flagstr[128]; char csvflagstr[1024]; flagstr[0] = 0; csvflagstr[0] = 0; int c = ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); for (int i = 0; i < c; ++i) { char f[32]; char csvf[64]; size_t flen = sizeof(csvflagstr) - strlen(csvflagstr) - 1; ConVarFlags::FlagDesc_t& entry = g_ConVarFlags.m_FlagsToDesc[i]; if (var->IsFlagSet(entry.bit)) { Q_snprintf(f, sizeof(f), ", %s", entry.shortdesc); Q_strncat(flagstr, f, sizeof(flagstr) - strlen(flagstr) - 1); Q_snprintf(csvf, sizeof(csvf), "\"%s\",", entry.desc); } else { Q_snprintf(csvf, sizeof(csvf), ","); } Q_strncat(csvflagstr, csvf, flen); } char valstr[32]; char tempbuff[512] = { 0 }; // Clean up integers if (var->GetInt() == (int)var->GetFloat()) { Q_snprintf(valstr, sizeof(valstr), "%-8i", var->GetInt()); } else { Q_snprintf(valstr, sizeof(valstr), "%-8.3f", var->GetFloat()); } // Print to console DevMsg(eDLL_T::ENGINE, "%-40s : %-8s : %-16s : %s\n", var->GetName(), valstr, flagstr, StripTabsAndReturns(var->GetHelpText(), tempbuff, sizeof(tempbuff))); if (logging) { FileSystem()->FPrintf(fh, "\"%s\",\"%s\",%s,\"%s\"\n", var->GetName(), valstr, csvflagstr, StripQuotes(var->GetHelpText(), tempbuff, sizeof(tempbuff))); } } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- static void PrintCommand(const ConCommand* cmd, bool logging, FileHandle_t& f) { // Print to console char tempbuff[512] = { 0 }; DevMsg(eDLL_T::ENGINE, "%-40s : %-8s : %-16s : %s\n", cmd->GetName(), "cmd", "", StripTabsAndReturns(cmd->GetHelpText(), tempbuff, sizeof(tempbuff))); if (logging) { char emptyflags[256]; emptyflags[0] = 0; int c = ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); for (int i = 0; i < c; ++i) { char csvf[64]; size_t len = sizeof(emptyflags) - strlen(emptyflags) - 1; Q_snprintf(csvf, sizeof(csvf), ","); Q_strncat(emptyflags, csvf, len); } // Names staring with +/- need to be wrapped in single quotes char nameBuf[256]; Q_snprintf(nameBuf, sizeof(nameBuf), "%s", cmd->GetName()); if (nameBuf[0] == '+' || nameBuf[0] == '-') { Q_snprintf(nameBuf, sizeof(nameBuf), "'%s'", cmd->GetName()); } FileSystem()->FPrintf(f, "\"%s\",\"%s\",%s,\"%s\"\n", nameBuf, "cmd", emptyflags, StripQuotes(cmd->GetHelpText(), tempbuff, sizeof(tempbuff))); } } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : bool //----------------------------------------------------------------------------- static bool ConCommandBaseLessFunc(ConCommandBase* const& lhs, ConCommandBase* const& rhs) { const char* left = lhs->GetName(); const char* right = rhs->GetName(); if (*left == '-' || *left == '+') left++; if (*right == '-' || *right == '+') right++; return (Q_stricmp(left, right) < 0); } //----------------------------------------------------------------------------- // Singleton CCvarUtilities //----------------------------------------------------------------------------- static CCvarUtilities g_CvarUtilities; CCvarUtilities* cv = &g_CvarUtilities; //----------------------------------------------------------------------------- // Purpose: // Input : // Output : int //----------------------------------------------------------------------------- int CCvarUtilities::CountVariablesWithFlags(int flags) { int i = 0; ConCommandBase* var; CCvar::CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through cvars... { var = itint->Get(); if (!var->IsCommand()) { if (var->IsFlagSet(flags)) { i++; } } } delete itint; return i; } //----------------------------------------------------------------------------- // Purpose: Removes the FCVAR_DEVELOPMENTONLY flag from all cvars, making them accessible //----------------------------------------------------------------------------- void CCvarUtilities::EnableDevCvars() { // Loop through cvars... CCvar::CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); for (itint->SetFirst(); itint->IsValid(); itint->Next()) { // remove flag from all cvars ConCommandBase* pCommandBase = itint->Get(); pCommandBase->RemoveFlags(FCVAR_DEVELOPMENTONLY); } delete itint; } //----------------------------------------------------------------------------- // Purpose: // Output : void CCvar::CvarList_f //----------------------------------------------------------------------------- void CCvarUtilities::CvarList(const CCommand& args) { ConCommandBase* var; // Temporary Pointer to cvars int64 iArgs; // Argument count const char* partial = NULL; // Partial cvar to search for... // E.eg size_t ipLen = 0; // Length of the partial cvar FileHandle_t f = FILESYSTEM_INVALID_HANDLE; // FilePointer for logging bool bLogging = false; // Are we logging? iArgs = args.ArgC(); // Get count // Print usage? if (iArgs == 2 && !Q_strcasecmp(args[1], "?")) { DevMsg(eDLL_T::ENGINE, "convar_list: [ log logfile ] [ partial ]\n"); return; } if (!Q_strcasecmp(args[1], "log") && iArgs >= 3) { char fn[256]; Q_snprintf(fn, sizeof(fn), "%s", args[2]); f = FileSystem()->Open(fn, "wb", nullptr, 0); if (f) { bLogging = true; } else { DevMsg(eDLL_T::ENGINE, "Couldn't open '%s' for writing!\n", fn); return; } if (iArgs == 4) { partial = args[3]; ipLen = Q_strlen(partial); } } else { partial = args[1]; ipLen = Q_strlen(partial); } // Banner DevMsg(eDLL_T::ENGINE, "convar list\n--------------\n"); CUtlRBTree< ConCommandBase* > sorted(0, 0, ConCommandBaseLessFunc); CCvar::CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. { var = itint->Get(); if (!var->IsFlagSet(FCVAR_DEVELOPMENTONLY) && !var->IsFlagSet(FCVAR_HIDDEN)) { bool print = false; if (partial) // Partial string searching? { if (!Q_strncasecmp(var->GetName(), partial, ipLen)) { print = true; } } else { print = true; } if (print) { sorted.Insert(var); } } } delete itint; if (bLogging) { PrintListHeader(f); } for (unsigned short i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder(i)) { var = sorted[i]; if (var->IsCommand()) { PrintCommand((ConCommand*)var, bLogging, f); } else { PrintCvar((ConVar*)var, bLogging, f); } } // Show total and syntax help... if (partial && partial[0]) { DevMsg(eDLL_T::ENGINE, "--------------\n%3i convars/concommands for [%s]\n", sorted.Count(), partial); } else { DevMsg(eDLL_T::ENGINE, "--------------\n%3i total convars/concommands\n", sorted.Count()); } if (bLogging) { FileSystem()->Close(f); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCvarUtilities::CvarHelp(const CCommand& args) { const char* search; ConCommandBase* var; if (args.ArgC() != 2) { DevMsg(eDLL_T::ENGINE, "Usage: help \n"); return; } // Get name of var to find search = args[1]; // Search for it var = g_pCVar->FindCommandBase(search); if (!var) { DevMsg(eDLL_T::ENGINE, "help: no cvar or command named %s\n", search); return; } // Show info ConVar_PrintDescription(var); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCvarUtilities::CvarDifferences(const CCommand& args) { CCvar::CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); int i = 0; for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. { ConCommandBase* pCommandBase = itint->Get(); if (!pCommandBase->IsCommand() && !pCommandBase->IsFlagSet(FCVAR_HIDDEN)) { ConVar* pConVar = reinterpret_cast(pCommandBase); if (V_strcmp(pConVar->GetString(), "FCVAR_NEVER_AS_STRING") != NULL) { if (V_stricmp(pConVar->GetString(), pConVar->GetDefault()) != NULL) { ConVar_PrintDescription(pConVar); i++; } } } } delete itint; DevMsg(eDLL_T::ENGINE, "--------------\n%3i changed convars\n", i); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCvarUtilities::CvarFindFlags_f(const CCommand& args) { if (args.ArgC() < 2) { DevMsg(eDLL_T::ENGINE, "Usage: convar_findByFlags \n"); DevMsg(eDLL_T::ENGINE, "Available flags to search for: \n"); for (int i = 0; i < ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); i++) { DevMsg(eDLL_T::ENGINE, " - %s\n", g_ConVarFlags.m_FlagsToDesc[i].desc); } return; } // Get substring to find const char* search = args[1]; ConCommandBase* var; // Loop through vars and print out findings CCvar::CCVarIteratorInternal* itint = g_pCVar->FactoryInternalIterator(); for (itint->SetFirst(); itint->IsValid(); itint->Next()) { var = itint->Get(); if (!var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || !var->IsFlagSet(FCVAR_HIDDEN)) { for (int i = 0; i < ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); i++) { if (var->IsFlagSet(g_ConVarFlags.m_FlagsToDesc[i].bit)) { if (V_stristr(g_ConVarFlags.m_FlagsToDesc[i].desc, search)) { ConVar_PrintDescription(var); } } } } } delete itint; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CCvarUtilities::CvarFindFlagsCompletionCallback(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]) { int flagC = ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); char const* pcmd = "findflags "; size_t len = Q_strlen(partial); if (len < Q_strlen(pcmd)) { int i = 0; for (; i < MIN(flagC, COMMAND_COMPLETION_MAXITEMS); i++) { Q_snprintf(commands[i], sizeof(commands[i]), "%s %s", pcmd, g_ConVarFlags.m_FlagsToDesc[i].desc); Q_strlower(commands[i]); } return i; } char const* pSub = partial + Q_strlen(pcmd); size_t nSubLen = Q_strlen(pSub); int values = 0; for (int i = 0; i < flagC; ++i) { if (Q_strnicmp(g_ConVarFlags.m_FlagsToDesc[i].desc, pSub, nSubLen)) continue; Q_snprintf(commands[values], sizeof(commands[values]), "%s %s", pcmd, g_ConVarFlags.m_FlagsToDesc[i].desc); Q_strlower(commands[values]); ++values; if (values >= COMMAND_COMPLETION_MAXITEMS) break; } return values; } //----------------------------------------------------------------------------- // Purpose: returns all ConVars //----------------------------------------------------------------------------- unordered_map CCvar::DumpToMap(void) { stringstream ss; CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocate new InternalIterator. unordered_map allConVars; for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. { ConCommandBase* pCommand = itint->Get(); const char* pszCommandName = pCommand->m_pszName; allConVars[pszCommandName] = pCommand; } delete itint; return allConVars; } /////////////////////////////////////////////////////////////////////////////// CCvar* g_pCVar = nullptr; //----------------------------------------------------------------------------- // Console command hash data structure //----------------------------------------------------------------------------- CConCommandHash::CConCommandHash() { Purge(true); } CConCommandHash::~CConCommandHash() { Purge(false); } void CConCommandHash::Purge(bool bReinitialize) { m_aBuckets.Purge(); m_aDataPool.Purge(); if (bReinitialize) { Init(); } } // Initialize. void CConCommandHash::Init(void) { // kNUM_BUCKETS must be a power of two. COMPILE_TIME_ASSERT((kNUM_BUCKETS & (kNUM_BUCKETS - 1)) == 0); // Set the bucket size. m_aBuckets.SetSize(kNUM_BUCKETS); for (int iBucket = 0; iBucket < kNUM_BUCKETS; ++iBucket) { m_aBuckets[iBucket] = m_aDataPool.InvalidIndex(); } // Calculate the grow size. int nGrowSize = 4 * kNUM_BUCKETS; m_aDataPool.SetGrowSize(nGrowSize); } //----------------------------------------------------------------------------- // Purpose: Insert data into the hash table given its key (unsigned int), // WITH a check to see if the element already exists within the hash. //----------------------------------------------------------------------------- CConCommandHash::CCommandHashHandle_t CConCommandHash::Insert(ConCommandBase* cmd) { // Check to see if that key already exists in the buckets (should be unique). CCommandHashHandle_t hHash = Find(cmd); if (hHash != InvalidHandle()) return hHash; return FastInsert(cmd); } //----------------------------------------------------------------------------- // Purpose: Insert data into the hash table given its key (unsigned int), // WITHOUT a check to see if the element already exists within the hash. //----------------------------------------------------------------------------- CConCommandHash::CCommandHashHandle_t CConCommandHash::FastInsert(ConCommandBase* cmd) { // Get a new element from the pool. intptr_t iHashData = m_aDataPool.Alloc(true); HashEntry_t* RESTRICT pHashData = &m_aDataPool[iHashData]; if (!pHashData) return InvalidHandle(); HashKey_t key = Hash(cmd); // Add data to new element. pHashData->m_uiKey = key; pHashData->m_Data = cmd; // Link element. int iBucket = key & kBUCKETMASK; // HashFuncs::Hash( uiKey, m_uiBucketMask ); m_aDataPool.LinkBefore(m_aBuckets[iBucket], iHashData); m_aBuckets[iBucket] = iHashData; return iHashData; } //----------------------------------------------------------------------------- // Purpose: Remove a given element from the hash. //----------------------------------------------------------------------------- void CConCommandHash::Remove(CCommandHashHandle_t hHash) /*RESTRICT*/ { HashEntry_t* /*RESTRICT*/ entry = &m_aDataPool[hHash]; HashKey_t iBucket = entry->m_uiKey & kBUCKETMASK; if (m_aBuckets[iBucket] == hHash) { // It is a bucket head. m_aBuckets[iBucket] = m_aDataPool.Next(hHash); } else { // Not a bucket head. m_aDataPool.Unlink(hHash); } // Remove the element. m_aDataPool.Remove(hHash); } //----------------------------------------------------------------------------- // Purpose: Remove all elements from the hash //----------------------------------------------------------------------------- void CConCommandHash::RemoveAll(void) { m_aBuckets.RemoveAll(); m_aDataPool.RemoveAll(); } //----------------------------------------------------------------------------- // Find hash entry corresponding to a string name //----------------------------------------------------------------------------- CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const char* name, HashKey_t hashkey) const /*RESTRICT*/ { // hash the "key" - get the correct hash table "bucket" int iBucket = hashkey & kBUCKETMASK; for (datapool_t::IndexLocalType_t iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next(iElement)) { const HashEntry_t& element = m_aDataPool[iElement]; if (element.m_uiKey == hashkey && // if hashes of strings match, Q_stricmp(name, element.m_Data->GetName()) == 0) // then test the actual strings { return iElement; } } // found nuffink return InvalidHandle(); } //----------------------------------------------------------------------------- // Find a command in the hash. //----------------------------------------------------------------------------- CConCommandHash::CCommandHashHandle_t CConCommandHash::Find(const ConCommandBase* cmd) const /*RESTRICT*/ { // Set this #if to 1 if the assert at bottom starts whining -- // that indicates that a console command is being double-registered, // or something similarly non-fatally bad. With this #if 1, we'll search // by name instead of by pointer, which is more robust in the face // of double registered commands, but obviously slower. #if 0 return Find(cmd->GetName()); #else HashKey_t hashkey = Hash(cmd); int iBucket = hashkey & kBUCKETMASK; // hunt through all entries in that bucket for (datapool_t::IndexLocalType_t iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next(iElement)) { const HashEntry_t& element = m_aDataPool[iElement]; if (element.m_uiKey == hashkey && // if the hashes match... element.m_Data == cmd) // and the pointers... { // in debug, test to make sure we don't have commands under the same name // or something goofy like that Assert(iElement == Find(cmd->GetName()), "ConCommand %s had two entries in the hash!", cmd->GetName()); // return this element return iElement; } } // found nothing. #ifdef DBGFLAG_ASSERT // double check against search by name CCommandHashHandle_t dbghand = Find(cmd->GetName()); Assert(InvalidHandle() == dbghand, "ConCommand %s couldn't be found by pointer, but was found by name!", cmd->GetName()); #endif return InvalidHandle(); #endif } //#ifdef _DEBUG // Dump a report to MSG void CConCommandHash::Report(void) { DevMsg(eDLL_T::ENGINE, "Console command hash bucket load:\n"); int total = 0; for (int iBucket = 0; iBucket < kNUM_BUCKETS; ++iBucket) { int count = 0; CCommandHashHandle_t iElement = m_aBuckets[iBucket]; // get the head of the bucket while (iElement != m_aDataPool.InvalidIndex()) { ++count; iElement = m_aDataPool.Next(iElement); } DevMsg(eDLL_T::ENGINE, "%d: %d\n", iBucket, count); total += count; } DevMsg(eDLL_T::ENGINE, "\tAverage: %.1f\n", total / ((float)(kNUM_BUCKETS))); } //#endif /////////////////////////////////////////////////////////////////////////////// void VCVar::Attach() const { DetourAttach((LPVOID*)&v_ConVar_PrintDescription, &ConVar_PrintDescription); } void VCVar::Detach() const { DetourDetach((LPVOID*)&v_ConVar_PrintDescription, &ConVar_PrintDescription); }