summaryrefslogtreecommitdiff
path: root/engine/cvar.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/cvar.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/cvar.cpp')
-rw-r--r--engine/cvar.cpp1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/engine/cvar.cpp b/engine/cvar.cpp
new file mode 100644
index 0000000..ccb8fc3
--- /dev/null
+++ b/engine/cvar.cpp
@@ -0,0 +1,1137 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+
+#include "cvar.h"
+#include "gl_cvars.h"
+
+#include "tier1/convar.h"
+#include "filesystem.h"
+#include "filesystem_engine.h"
+#include "client.h"
+#include "server.h"
+#include "GameEventManager.h"
+#include "netmessages.h"
+#include "sv_main.h"
+#include "demo.h"
+#include <ctype.h>
+#ifdef POSIX
+#include <wctype.h>
+#endif
+
+#ifndef SWDS
+#include <vgui_controls/Controls.h>
+#include <vgui/ILocalize.h>
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Singleton CCvarUtilities
+//-----------------------------------------------------------------------------
+static CCvarUtilities g_CvarUtilities;
+CCvarUtilities *cv = &g_CvarUtilities;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Update clients/server when FCVAR_REPLICATED etc vars change
+//-----------------------------------------------------------------------------
+static void ConVarNetworkChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue )
+{
+ ConVarRef var( pConVar );
+ if ( !pOldValue )
+ {
+ if ( var.GetFloat() == flOldValue )
+ return;
+ }
+ else
+ {
+ if ( !Q_strcmp( var.GetString(), pOldValue ) )
+ return;
+ }
+
+ if ( var.IsFlagSet( FCVAR_USERINFO ) )
+ {
+ // Are we not a server, but a client and have a change?
+ if ( cl.IsConnected() )
+ {
+ // send changed cvar to server
+ NET_SetConVar convar( var.GetName(), var.GetString() );
+ cl.m_NetChannel->SendNetMsg( convar );
+ }
+ }
+
+ // Log changes to server variables
+
+ // Print to clients
+ if ( var.IsFlagSet( FCVAR_NOTIFY ) )
+ {
+ IGameEvent *event = g_GameEventManager.CreateEvent( "server_cvar" );
+
+ if ( event )
+ {
+ event->SetString( "cvarname", var.GetName() );
+
+ if ( var.IsFlagSet( FCVAR_PROTECTED ) )
+ {
+ event->SetString("cvarvalue", "***PROTECTED***" );
+ }
+ else
+ {
+ event->SetString("cvarvalue", var.GetString() );
+ }
+
+ g_GameEventManager.FireEvent( event );
+ }
+ }
+
+ // Force changes down to clients (if running server)
+ if ( var.IsFlagSet( FCVAR_REPLICATED ) && sv.IsActive() )
+ {
+ SV_ReplicateConVarChange( static_cast< ConVar* >( pConVar ), var.GetString() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Implementation of the ICvarQuery interface
+//-----------------------------------------------------------------------------
+class CCvarQuery : public CBaseAppSystem< ICvarQuery >
+{
+public:
+ virtual bool Connect( CreateInterfaceFn factory )
+ {
+ ICvar *pCVar = (ICvar*)factory( CVAR_INTERFACE_VERSION, 0 );
+ if ( !pCVar )
+ return false;
+
+ pCVar->InstallCVarQuery( this );
+ return true;
+ }
+
+ virtual InitReturnVal_t Init()
+ {
+ // If the value has changed, notify clients/server based on ConVar flags.
+ // NOTE: this will only happen for non-FCVAR_NEVER_AS_STRING vars.
+ // Also, this happened in SetDirect for older clients that don't have the
+ // callback interface.
+ g_pCVar->InstallGlobalChangeCallback( ConVarNetworkChangeCallback );
+ return INIT_OK;
+ }
+
+ virtual void Shutdown()
+ {
+ g_pCVar->RemoveGlobalChangeCallback( ConVarNetworkChangeCallback );
+ }
+
+ virtual void *QueryInterface( const char *pInterfaceName )
+ {
+ if ( !Q_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) )
+ return (ICvarQuery*)this;
+ return NULL;
+
+ }
+
+ // Purpose: Returns true if the commands can be aliased to one another
+ // Either game/client .dll shared with engine,
+ // or game and client dll shared and marked FCVAR_REPLICATED
+ virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent )
+ {
+ // Both parent and child must be marked replicated for this to work
+ bool repchild = child->IsFlagSet( FCVAR_REPLICATED );
+ bool repparent = parent->IsFlagSet( FCVAR_REPLICATED );
+
+ if ( repchild && repparent )
+ {
+ // Never on protected vars
+ if ( child->IsFlagSet( FCVAR_PROTECTED ) || parent->IsFlagSet( FCVAR_PROTECTED ) )
+ {
+ ConMsg( "FCVAR_REPLICATED can't also be FCVAR_PROTECTED (%s)\n", child->GetName() );
+ return false;
+ }
+
+ // Only on ConVars
+ if ( child->IsCommand() || parent->IsCommand() )
+ {
+ ConMsg( "FCVAR_REPLICATED not valid on ConCommands (%s)\n", child->GetName() );
+ return false;
+ }
+
+ // One must be in client .dll and the other in the game .dll, or both in the engine
+ if ( child->IsFlagSet( FCVAR_GAMEDLL ) && !parent->IsFlagSet( FCVAR_CLIENTDLL ) )
+ {
+ ConMsg( "For FCVAR_REPLICATED, ConVar must be defined in client and game .dlls (%s)\n", child->GetName() );
+ return false;
+ }
+
+ if ( child->IsFlagSet( FCVAR_CLIENTDLL ) && !parent->IsFlagSet( FCVAR_GAMEDLL ) )
+ {
+ ConMsg( "For FCVAR_REPLICATED, ConVar must be defined in client and game .dlls (%s)\n", child->GetName() );
+ return false;
+ }
+
+ // Allowable
+ return true;
+ }
+
+ // Otherwise need both to allow linkage
+ if ( repchild || repparent )
+ {
+ ConMsg( "Both ConVars must be marked FCVAR_REPLICATED for linkage to work (%s)\n", child->GetName() );
+ return false;
+ }
+
+ if ( parent->IsFlagSet( FCVAR_CLIENTDLL ) )
+ {
+ ConMsg( "Parent cvar in client.dll not allowed (%s)\n", child->GetName() );
+ return false;
+ }
+
+ if ( parent->IsFlagSet( FCVAR_GAMEDLL ) )
+ {
+ ConMsg( "Parent cvar in server.dll not allowed (%s)\n", child->GetName() );
+ return false;
+ }
+
+ return true;
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton
+//-----------------------------------------------------------------------------
+static CCvarQuery s_CvarQuery;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvarQuery, ICvarQuery, CVAR_QUERY_INTERFACE_VERSION, s_CvarQuery );
+
+
+//-----------------------------------------------------------------------------
+//
+// CVar utilities begins here
+//
+//-----------------------------------------------------------------------------
+static bool IsAllSpaces( const wchar_t *str )
+{
+ const wchar_t *p = str;
+ while ( p && *p )
+ {
+ if ( !iswspace( *p ) )
+ return false;
+
+ ++p;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *var -
+// *value -
+//-----------------------------------------------------------------------------
+void CCvarUtilities::SetDirect( ConVar *var, const char *value )
+{
+ const char *pszValue;
+ char szNew[ 1024 ];
+
+ // Bail early if we're trying to set a FCVAR_USERINFO cvar on a dedicated server
+ if ( var->IsFlagSet( FCVAR_USERINFO ) )
+ {
+ if ( sv.IsDedicated() )
+ {
+ return;
+ }
+ }
+
+ pszValue = value;
+ // This cvar's string must only contain printable characters.
+ // Strip out any other crap.
+ // We'll fill in "empty" if nothing is left
+ if ( var->IsFlagSet( FCVAR_PRINTABLEONLY ) )
+ {
+ wchar_t unicode[ 512 ];
+#ifndef SWDS
+ if ( sv.IsDedicated() )
+ {
+ // Dedicated servers don't have g_pVGuiLocalize, so fall back
+ V_UTF8ToUnicode( pszValue, unicode, sizeof( unicode ) );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( pszValue, unicode, sizeof( unicode ) );
+ }
+#else
+ V_UTF8ToUnicode( pszValue, unicode, sizeof( unicode ) );
+#endif
+ wchar_t newUnicode[ 512 ];
+
+ const wchar_t *pS;
+ wchar_t *pD;
+
+ // Clear out new string
+ newUnicode[0] = L'\0';
+
+ pS = unicode;
+ pD = newUnicode;
+
+ // Step through the string, only copying back in characters that are printable
+ while ( *pS )
+ {
+ if ( iswcntrl( *pS ) || *pS == '~' )
+ {
+ pS++;
+ continue;
+ }
+
+ *pD++ = *pS++;
+ }
+
+ // Terminate the new string
+ *pD = L'\0';
+
+ // If it's empty or all spaces, then insert a marker string
+ if ( !wcslen( newUnicode ) || IsAllSpaces( newUnicode ) )
+ {
+ wcsncpy( newUnicode, L"#empty", ( sizeof( newUnicode ) / sizeof( wchar_t ) ) - 1 );
+ newUnicode[ ( sizeof( newUnicode ) / sizeof( wchar_t ) ) - 1 ] = L'\0';
+ }
+
+#ifndef SWDS
+ if ( sv.IsDedicated() )
+ {
+ V_UnicodeToUTF8( newUnicode, szNew, sizeof( szNew ) );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertUnicodeToANSI( newUnicode, szNew, sizeof( szNew ) );
+ }
+#else
+ V_UnicodeToUTF8( newUnicode, szNew, sizeof( szNew ) );
+#endif
+ // Point the value here.
+ pszValue = szNew;
+ }
+
+ if ( var->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
+ {
+ var->SetValue( (float)atof( pszValue ) );
+ }
+ else
+ {
+ var->SetValue( pszValue );
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+
+// If you are changing this, please take a look at IsValidToggleCommand()
+bool CCvarUtilities::IsCommand( const CCommand &args )
+{
+ int c = args.ArgC();
+ if ( c == 0 )
+ return false;
+
+ ConVar *v;
+
+ // check variables
+ v = g_pCVar->FindVar( args[0] );
+ if ( !v )
+ return false;
+
+ // NOTE: Not checking for 'HIDDEN' here so we can actually set hidden convars
+ if ( v->IsFlagSet(FCVAR_DEVELOPMENTONLY) )
+ return false;
+
+ // perform a variable print or set
+ if ( c == 1 )
+ {
+ ConVar_PrintDescription( v );
+ return true;
+ }
+
+ if ( v->IsFlagSet( FCVAR_SPONLY ) )
+ {
+#ifndef SWDS
+ // Connected to server?
+ if ( cl.IsConnected() )
+ {
+ // Is it not a single player game?
+ if ( cl.m_nMaxClients > 1 )
+ {
+ ConMsg( "Can't set %s in multiplayer\n", v->GetName() );
+ return true;
+ }
+ }
+#endif
+ }
+
+ if ( v->IsFlagSet( FCVAR_NOT_CONNECTED ) )
+ {
+#ifndef SWDS
+ // Connected to server?
+ if ( cl.IsConnected() )
+ {
+ extern IBaseClientDLL *g_ClientDLL;
+ if ( v->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( v ) )
+ {
+ // Client.dll is allowing the convar change
+ }
+ else
+ {
+ ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", v->GetName() );
+ return true;
+ }
+ }
+#endif
+ }
+
+ // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on
+ if ( v->IsFlagSet( FCVAR_CHEAT ) )
+ {
+ if ( !Host_IsSinglePlayerGame() && !CanCheat()
+#if !defined(SWDS)
+ && !cl.ishltv
+#if defined( REPLAY_ENABLED )
+ && !cl.isreplay
+#endif
+ && !demoplayer->IsPlayingBack()
+#endif
+ )
+ {
+ ConMsg( "Can't use cheat cvar %s in multiplayer, unless the server has sv_cheats set to 1.\n", v->GetName() );
+ return true;
+ }
+ }
+
+ // Text invoking the command was typed into the console, decide what to do with it
+ // if this is a replicated ConVar, except don't worry about restrictions if playing a .dem file
+ if ( v->IsFlagSet( FCVAR_REPLICATED )
+#if !defined(SWDS)
+ && !demoplayer->IsPlayingBack()
+#endif
+ )
+ {
+ // If not running a server but possibly connected as a client, then
+ // if the message came from console, don't process the command
+ if ( !sv.IsActive() &&
+ !sv.IsLoading() &&
+ (cmd_source == src_command) &&
+ cl.IsConnected() )
+ {
+ ConMsg( "Can't change replicated ConVar %s from console of client, only server operator can change its value\n", v->GetName() );
+ return true;
+ }
+
+ // FIXME: Do we need a case where cmd_source == src_client?
+ Assert( cmd_source != src_client );
+ }
+
+ // Note that we don't want the tokenized list, send down the entire string
+ // except for surrounding quotes
+ char remaining[1024];
+ const char *pArgS = args.ArgS();
+ int nLen = Q_strlen( pArgS );
+ bool bIsQuoted = pArgS[0] == '\"';
+ if ( !bIsQuoted )
+ {
+ Q_strncpy( remaining, args.ArgS(), sizeof(remaining) );
+ }
+ else
+ {
+ --nLen;
+ Q_strncpy( remaining, &pArgS[1], sizeof(remaining) );
+ }
+
+ // Now strip off any trailing spaces
+ char *p = remaining + nLen - 1;
+ while ( p >= remaining )
+ {
+ if ( *p > ' ' )
+ break;
+
+ *p-- = 0;
+ }
+
+ // Strip off ending quote
+ if ( bIsQuoted && p >= remaining )
+ {
+ if ( *p == '\"' )
+ {
+ *p = 0;
+ }
+ }
+
+ SetDirect( v, remaining );
+ return true;
+}
+
+// This is a band-aid copied directly from IsCommand().
+bool CCvarUtilities::IsValidToggleCommand( const char *cmd )
+{
+ ConVar *v;
+
+ // check variables
+ v = g_pCVar->FindVar ( cmd );
+ if (!v)
+ {
+ ConMsg( "%s is not a valid cvar\n", cmd );
+ return false;
+ }
+
+ if ( v->IsFlagSet(FCVAR_DEVELOPMENTONLY) || v->IsFlagSet(FCVAR_HIDDEN) )
+ return false;
+
+ if ( v->IsFlagSet( FCVAR_SPONLY ) )
+ {
+#ifndef SWDS
+ // Connected to server?
+ if ( cl.IsConnected() )
+ {
+ // Is it not a single player game?
+ if ( cl.m_nMaxClients > 1 )
+ {
+ ConMsg( "Can't set %s in multiplayer\n", v->GetName() );
+ return false;
+ }
+ }
+#endif
+ }
+
+ if ( v->IsFlagSet( FCVAR_NOT_CONNECTED ) )
+ {
+#ifndef SWDS
+ // Connected to server?
+ if ( cl.IsConnected() )
+ {
+ extern IBaseClientDLL *g_ClientDLL;
+ if ( v->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( v ) )
+ {
+ // Client.dll is allowing the convar change
+ }
+ else
+ {
+ ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", v->GetName() );
+ return false;
+ }
+ }
+#endif
+ }
+
+ // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on
+ if ( v->IsFlagSet( FCVAR_CHEAT ) )
+ {
+ if ( !Host_IsSinglePlayerGame() && !CanCheat()
+#if !defined(SWDS) && !defined(_XBOX)
+ && !demoplayer->IsPlayingBack()
+#endif
+ )
+ {
+ ConMsg( "Can't use cheat cvar %s in multiplayer, unless the server has sv_cheats set to 1.\n", v->GetName() );
+ return false;
+ }
+ }
+
+ // Text invoking the command was typed into the console, decide what to do with it
+ // if this is a replicated ConVar, except don't worry about restrictions if playing a .dem file
+ if ( v->IsFlagSet( FCVAR_REPLICATED )
+#if !defined(SWDS) && !defined(_XBOX)
+ && !demoplayer->IsPlayingBack()
+#endif
+ )
+ {
+ // If not running a server but possibly connected as a client, then
+ // if the message came from console, don't process the command
+ if ( !sv.IsActive() &&
+ !sv.IsLoading() &&
+ (cmd_source == src_command) &&
+ cl.IsConnected() )
+ {
+ ConMsg( "Can't change replicated ConVar %s from console of client, only server operator can change its value\n", v->GetName() );
+ return false;
+ }
+ }
+
+ // FIXME: Do we need a case where cmd_source == src_client?
+ Assert( cmd_source != src_client );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *f -
+//-----------------------------------------------------------------------------
+void CCvarUtilities::WriteVariables( CUtlBuffer &buff, bool bAllVars )
+{
+ const ConCommandBase *var;
+
+ for (var = g_pCVar->GetCommands() ; var ; var = var->GetNext())
+ {
+ if ( var->IsCommand() )
+ continue;
+
+ bool archive = var->IsFlagSet( IsX360() ? FCVAR_ARCHIVE_XBOX : FCVAR_ARCHIVE );
+ if ( archive )
+ {
+ const ConVar *pConvar = assert_cast<const ConVar *>( var );
+ // Only write out values that differ from the defaults.
+ if ( bAllVars || Q_strcmp( pConvar->GetString(), pConvar->GetDefault() ) != 0 )
+ {
+ buff.Printf( "%s \"%s\"\n", var->GetName(), ((ConVar *)var)->GetString() );
+ }
+ }
+ }
+}
+
+static char *StripTabsAndReturns( const char *inbuffer, char *outbuffer, int outbufferSize )
+{
+ char *out = outbuffer;
+ const char *i = inbuffer;
+ char *o = out;
+
+ out[ 0 ] = 0;
+
+ while ( *i && o - out < outbufferSize - 1 )
+ {
+ if ( *i == '\n' ||
+ *i == '\r' ||
+ *i == '\t' )
+ {
+ *o++ = ' ';
+ i++;
+ continue;
+ }
+ if ( *i == '\"' )
+ {
+ *o++ = '\'';
+ i++;
+ continue;
+ }
+
+ *o++ = *i++;
+ }
+
+ *o = '\0';
+
+ return out;
+}
+
+static char *StripQuotes( const char *inbuffer, char *outbuffer, int outbufferSize )
+{
+ char *out = outbuffer;
+ const char *i = inbuffer;
+ char *o = out;
+
+ out[ 0 ] = 0;
+
+ while ( *i && o - out < outbufferSize - 1 )
+ {
+ if ( *i == '\"' )
+ {
+ *o++ = '\'';
+ i++;
+ continue;
+ }
+
+ *o++ = *i++;
+ }
+
+ *o = '\0';
+
+ return out;
+}
+
+struct ConVarFlags_t
+{
+ int bit;
+ const char *desc;
+ const char *shortdesc;
+};
+
+#define CONVARFLAG( x, y ) { FCVAR_##x, #x, #y }
+
+static ConVarFlags_t g_ConVarFlags[]=
+{
+ // CONVARFLAG( UNREGISTERED, "u" ),
+ CONVARFLAG( ARCHIVE, "a" ),
+ CONVARFLAG( SPONLY, "sp" ),
+ CONVARFLAG( GAMEDLL, "sv" ),
+ CONVARFLAG( CHEAT, "cheat" ),
+ CONVARFLAG( USERINFO, "user" ),
+ CONVARFLAG( NOTIFY, "nf" ),
+ CONVARFLAG( PROTECTED, "prot" ),
+ CONVARFLAG( PRINTABLEONLY, "print" ),
+ CONVARFLAG( UNLOGGED, "log" ),
+ CONVARFLAG( NEVER_AS_STRING, "numeric" ),
+ CONVARFLAG( REPLICATED, "rep" ),
+ CONVARFLAG( DEMO, "demo" ),
+ CONVARFLAG( DONTRECORD, "norecord" ),
+ CONVARFLAG( SERVER_CAN_EXECUTE, "server_can_execute" ),
+ CONVARFLAG( CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" ),
+ CONVARFLAG( CLIENTDLL, "cl" ),
+};
+
+static void PrintListHeader( FileHandle_t& f )
+{
+ char csvflagstr[ 1024 ];
+
+ csvflagstr[ 0 ] = 0;
+
+ int c = ARRAYSIZE( g_ConVarFlags );
+ for ( int i = 0 ; i < c; ++i )
+ {
+ char csvf[ 64 ];
+
+ ConVarFlags_t & entry = g_ConVarFlags[ i ];
+ Q_snprintf( csvf, sizeof( csvf ), "\"%s\",", entry.desc );
+ Q_strncat( csvflagstr, csvf, sizeof( csvflagstr ), COPY_ALL_CHARACTERS );
+ }
+
+ g_pFileSystem->FPrintf( f,"\"%s\",\"%s\",%s,\"%s\"\n", "Name", "Value", csvflagstr, "Help Text" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *var -
+// *f -
+//-----------------------------------------------------------------------------
+static void PrintCvar( const ConVar *var, bool logging, FileHandle_t& fh )
+{
+ char flagstr[ 128 ];
+ char csvflagstr[ 1024 ];
+
+ flagstr[ 0 ] = 0;
+ csvflagstr[ 0 ] = 0;
+
+ int c = ARRAYSIZE( g_ConVarFlags );
+ for ( int i = 0 ; i < c; ++i )
+ {
+ char f[ 32 ];
+ char csvf[ 64 ];
+
+ ConVarFlags_t & entry = g_ConVarFlags[ i ];
+ if ( var->IsFlagSet( entry.bit ) )
+ {
+ Q_snprintf( f, sizeof( f ), ", %s", entry.shortdesc );
+
+ Q_strncat( flagstr, f, sizeof( flagstr ), COPY_ALL_CHARACTERS );
+
+ Q_snprintf( csvf, sizeof( csvf ), "\"%s\",", entry.desc );
+ }
+ else
+ {
+ Q_snprintf( csvf, sizeof( csvf ), "," );
+ }
+
+ Q_strncat( csvflagstr, csvf, sizeof( csvflagstr ), COPY_ALL_CHARACTERS );
+ }
+
+
+ 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
+ ConMsg( "%-40s : %-8s : %-16s : %s\n", var->GetName(), valstr, flagstr, StripTabsAndReturns( var->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
+ if ( logging )
+ {
+ g_pFileSystem->FPrintf( fh,"\"%s\",\"%s\",%s,\"%s\"\n", var->GetName(), valstr, csvflagstr, StripQuotes( var->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
+ }
+}
+
+static void PrintCommand( const ConCommand *cmd, bool logging, FileHandle_t& f )
+{
+ // Print to console
+ char tempbuff[512] = { 0 };
+ ConMsg ("%-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 );
+ for ( int i = 0; i < c; ++i )
+ {
+ char csvf[ 64 ];
+ Q_snprintf( csvf, sizeof( csvf ), "," );
+ Q_strncat( emptyflags, csvf, sizeof( emptyflags ), COPY_ALL_CHARACTERS );
+ }
+ // Names staring with +/- need to be wrapped in single quotes
+ char name[ 256 ];
+ Q_snprintf( name, sizeof( name ), "%s", cmd->GetName() );
+ if ( name[ 0 ] == '+' || name[ 0 ] == '-' )
+ {
+ Q_snprintf( name, sizeof( name ), "'%s'", cmd->GetName() );
+ }
+ g_pFileSystem->FPrintf( f, "\"%s\",\"%s\",%s,\"%s\"\n", name, "cmd", emptyflags, StripQuotes( cmd->GetHelpText(), tempbuff, sizeof(tempbuff) ) );
+ }
+}
+
+static bool ConCommandBaseLessFunc( const ConCommandBase * const &lhs, const 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 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : void CCvar::CvarList_f
+//-----------------------------------------------------------------------------
+void CCvarUtilities::CvarList( const CCommand &args )
+{
+ const ConCommandBase *var; // Temporary Pointer to cvars
+ int iArgs; // Argument count
+ const char *partial = NULL; // Partial cvar to search for...
+ // E.eg
+ int 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],"?" ) )
+ {
+ ConMsg( "cvarlist: [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 = g_pFileSystem->Open( fn,"wb" );
+ if ( f )
+ {
+ bLogging = true;
+ }
+ else
+ {
+ ConMsg( "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
+ ConMsg( "cvar list\n--------------\n" );
+
+ CUtlRBTree< const ConCommandBase * > sorted( 0, 0, ConCommandBaseLessFunc );
+
+ // Loop through cvars...
+ for ( var= g_pCVar->GetCommands(); var; var=var->GetNext() )
+ {
+ bool print = false;
+
+ if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
+ continue;
+
+ if (partial) // Partial string searching?
+ {
+ if ( !Q_strncasecmp( var->GetName(), partial, ipLen ) )
+ {
+ print = true;
+ }
+ }
+ else
+ {
+ print = true;
+ }
+
+ if ( !print )
+ continue;
+
+ sorted.Insert( var );
+ }
+
+ if ( bLogging )
+ {
+ PrintListHeader( f );
+ }
+ for ( int 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] )
+ {
+ ConMsg("--------------\n%3i convars/concommands for [%s]\n", sorted.Count(), partial );
+ }
+ else
+ {
+ ConMsg("--------------\n%3i total convars/concommands\n", sorted.Count() );
+ }
+
+ if ( bLogging )
+ {
+ g_pFileSystem->Close( f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output : int
+//-----------------------------------------------------------------------------
+int CCvarUtilities::CountVariablesWithFlags( int flags )
+{
+ int i = 0;
+ const ConCommandBase *var;
+
+ for ( var = g_pCVar->GetCommands(); var; var = var->GetNext() )
+ {
+ if ( var->IsCommand() )
+ continue;
+
+ if ( var->IsFlagSet( flags ) )
+ {
+ i++;
+ }
+ }
+
+ return i;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCvarUtilities::CvarHelp( const CCommand &args )
+{
+ const char *search;
+ const ConCommandBase *var;
+
+ if ( args.ArgC() != 2 )
+ {
+ ConMsg( "Usage: help <cvarname>\n" );
+ return;
+ }
+
+ // Get name of var to find
+ search = args[1];
+
+ // Search for it
+ var = g_pCVar->FindCommandBase( search );
+ if ( !var )
+ {
+ ConMsg( "help: no cvar or command named %s\n", search );
+ return;
+ }
+
+ // Show info
+ ConVar_PrintDescription( var );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCvarUtilities::CvarDifferences( const CCommand &args )
+{
+ const ConCommandBase *var;
+
+ // Loop through vars and print out findings
+ for ( var = g_pCVar->GetCommands(); var; var=var->GetNext() )
+ {
+ if ( var->IsCommand( ) )
+ continue;
+ if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
+ continue;
+
+ if ( !Q_stricmp( ((ConVar *)var)->GetDefault(), ((ConVar *)var)->GetString() ) )
+ continue;
+
+ ConVar_PrintDescription( (ConVar *)var );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggles a cvar on/off, or cycles through a set of values
+//-----------------------------------------------------------------------------
+void CCvarUtilities::CvarToggle( const CCommand &args )
+{
+ int i;
+
+ int c = args.ArgC();
+ if ( c < 2 )
+ {
+ ConMsg( "Usage: toggle <cvarname> [value1] [value2] [value3]...\n" );
+ return;
+ }
+
+ ConVar *var = g_pCVar->FindVar( args[1] );
+
+ if ( !IsValidToggleCommand( args[1] ) )
+ {
+ return;
+ }
+
+ if ( c == 2 )
+ {
+ // just toggle it on and off
+ var->SetValue( !var->GetBool() );
+ ConVar_PrintDescription( var );
+ }
+ else
+ {
+ // look for the current value in the command arguments
+ for( i = 2; i < c; i++ )
+ {
+ if ( !Q_strcmp( var->GetString(), args[ i ] ) )
+ break;
+ }
+
+ // choose the next one
+ i++;
+
+ // if we didn't find it, or were at the last value in the command arguments, use the 1st argument
+ if ( i >= c )
+ {
+ i = 2;
+ }
+
+ var->SetValue( args[ i ] );
+ ConVar_PrintDescription( var );
+ }
+}
+
+void CCvarUtilities::CvarFindFlags_f( const CCommand &args )
+{
+ if ( args.ArgC() < 2 )
+ {
+ ConMsg( "Usage: findflags <string>\n" );
+ ConMsg( "Available flags to search for: \n" );
+
+ for ( int i=0; i < ARRAYSIZE( g_ConVarFlags ); i++ )
+ {
+ ConMsg( " - %s\n", g_ConVarFlags[i].desc );
+ }
+ return;
+ }
+
+ // Get substring to find
+ const char *search = args[1];
+ const ConCommandBase *var;
+
+ // Loop through vars and print out findings
+ for (var=g_pCVar->GetCommands() ; var ; var=var->GetNext())
+ {
+ if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
+ continue;
+
+ for ( int i=0; i < ARRAYSIZE( g_ConVarFlags ); i++ )
+ {
+ if ( !var->IsFlagSet( g_ConVarFlags[i].bit ) )
+ continue;
+
+ if ( !V_stristr( g_ConVarFlags[i].desc, search ) )
+ continue;
+
+ ConVar_PrintDescription( var );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hook to command
+//-----------------------------------------------------------------------------
+CON_COMMAND( findflags, "Find concommands by flags." )
+{
+ cv->CvarFindFlags_f( args );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hook to command
+//-----------------------------------------------------------------------------
+CON_COMMAND( cvarlist, "Show the list of convars/concommands." )
+{
+ cv->CvarList( args );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Print help text for cvar
+//-----------------------------------------------------------------------------
+CON_COMMAND( help, "Find help about a convar/concommand." )
+{
+ cv->CvarHelp( args );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CON_COMMAND( differences, "Show all convars which are not at their default values." )
+{
+ cv->CvarDifferences( args );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CON_COMMAND( toggle, "Toggles a convar on or off, or cycles through a set of values." )
+{
+ cv->CvarToggle( args );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Send the cvars to VXConsole
+//-----------------------------------------------------------------------------
+#if defined( _X360 )
+CON_COMMAND( getcvars, "" )
+{
+ g_pCVar->PublishToVXConsole();
+}
+#endif
+