Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1538 lines
40 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "basetypes.h"
  12. #include "tier1/convar.h"
  13. #include "tier1/strtools.h"
  14. #include "tier1/characterset.h"
  15. #include "tier1/utlbuffer.h"
  16. #include "tier1/tier1.h"
  17. #include "tier1/convar_serverbounded.h"
  18. #include "icvar.h"
  19. #include "tier0/dbg.h"
  20. #if defined( _X360 )
  21. #include "xbox/xbox_console.h"
  22. #endif
  23. #include "tier0/memdbgon.h"
  24. // Comment this out when we release.
  25. #if defined ( CSTRIKE15 )
  26. #if !defined ( _CERT )
  27. // #define ALLOW_DEVELOPMENT_CVARS
  28. #endif
  29. #endif
  30. // This enables the l4d style of culling all cvars that are not marked FCVAR_RELEASE :
  31. #define CULL_ALL_CVARS_NOT_FCVAR_RELEASE
  32. //-----------------------------------------------------------------------------
  33. // Statically constructed list of ConCommandBases,
  34. // used for registering them with the ICVar interface
  35. //-----------------------------------------------------------------------------
  36. ConCommandBase *ConCommandBase::s_pConCommandBases = NULL;
  37. IConCommandBaseAccessor *ConCommandBase::s_pAccessor = NULL;
  38. static int s_nCVarFlag = 0;
  39. static int s_nDLLIdentifier = -1; // A unique identifier indicating which DLL this convar came from
  40. static bool s_bRegistered = false;
  41. class CDefaultAccessor : public IConCommandBaseAccessor
  42. {
  43. public:
  44. virtual bool RegisterConCommandBase( ConCommandBase *pVar )
  45. {
  46. // Link to engine's list instead
  47. g_pCVar->RegisterConCommand( pVar );
  48. return true;
  49. }
  50. };
  51. static CDefaultAccessor s_DefaultAccessor;
  52. //-----------------------------------------------------------------------------
  53. // Called by the framework to register ConCommandBases with the ICVar
  54. //-----------------------------------------------------------------------------
  55. void ConVar_Register( int nCVarFlag, IConCommandBaseAccessor *pAccessor )
  56. {
  57. if ( !g_pCVar || s_bRegistered )
  58. return;
  59. Assert( s_nDLLIdentifier < 0 );
  60. s_bRegistered = true;
  61. s_nCVarFlag = nCVarFlag;
  62. s_nDLLIdentifier = g_pCVar->AllocateDLLIdentifier();
  63. ConCommandBase *pCur, *pNext;
  64. ConCommandBase::s_pAccessor = pAccessor ? pAccessor : &s_DefaultAccessor;
  65. pCur = ConCommandBase::s_pConCommandBases;
  66. while ( pCur )
  67. {
  68. pNext = pCur->m_pNext;
  69. pCur->AddFlags( s_nCVarFlag );
  70. pCur->Init();
  71. pCur = pNext;
  72. }
  73. g_pCVar->AddSplitScreenConVars();
  74. g_pCVar->ProcessQueuedMaterialThreadConVarSets();
  75. ConCommandBase::s_pConCommandBases = NULL;
  76. }
  77. void ConVar_Unregister( )
  78. {
  79. if ( !g_pCVar || !s_bRegistered )
  80. return;
  81. Assert( s_nDLLIdentifier >= 0 );
  82. // Do this after unregister!!!
  83. g_pCVar->RemoveSplitScreenConVars( s_nDLLIdentifier );
  84. g_pCVar->UnregisterConCommands( s_nDLLIdentifier );
  85. s_nDLLIdentifier = -1;
  86. s_bRegistered = false;
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Purpose: Default constructor
  90. //-----------------------------------------------------------------------------
  91. ConCommandBase::ConCommandBase( void )
  92. {
  93. m_bRegistered = false;
  94. m_pszName = NULL;
  95. m_pszHelpString = NULL;
  96. m_nFlags = 0;
  97. m_pNext = NULL;
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose: The base console invoked command/cvar interface
  101. // Input : *pName - name of variable/command
  102. // *pHelpString - help text
  103. // flags - flags
  104. //-----------------------------------------------------------------------------
  105. ConCommandBase::ConCommandBase( const char *pName, const char *pHelpString /*=0*/, int flags /*= 0*/ )
  106. {
  107. Create( pName, pHelpString, flags );
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose:
  111. //-----------------------------------------------------------------------------
  112. ConCommandBase::~ConCommandBase( void )
  113. {
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Purpose:
  117. // Output : Returns true on success, false on failure.
  118. //-----------------------------------------------------------------------------
  119. bool ConCommandBase::IsCommand( void ) const
  120. {
  121. // Assert( 0 ); This can't assert. . causes a recursive assert in Sys_Printf, etc.
  122. return true;
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Returns the DLL identifier
  126. //-----------------------------------------------------------------------------
  127. CVarDLLIdentifier_t ConCommandBase::GetDLLIdentifier() const
  128. {
  129. return s_nDLLIdentifier;
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Purpose:
  133. // Input : *pName -
  134. // callback -
  135. // *pHelpString -
  136. // flags -
  137. //-----------------------------------------------------------------------------
  138. void ConCommandBase::Create( const char *pName, const char *pHelpString /*= 0*/, int flags /*= 0*/ )
  139. {
  140. static char *empty_string = "";
  141. m_bRegistered = false;
  142. // Name should be static data
  143. Assert( pName );
  144. m_pszName = pName;
  145. m_pszHelpString = pHelpString ? pHelpString : empty_string;
  146. m_nFlags = flags;
  147. #ifdef ALLOW_DEVELOPMENT_CVARS
  148. m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
  149. #endif
  150. if ( !( m_nFlags & FCVAR_UNREGISTERED ) )
  151. {
  152. m_pNext = s_pConCommandBases;
  153. s_pConCommandBases = this;
  154. }
  155. else
  156. {
  157. // It's unregistered
  158. m_pNext = NULL;
  159. }
  160. // If s_pAccessor is already set (this ConVar is not a global variable),
  161. // register it.
  162. if ( s_pAccessor )
  163. {
  164. Init();
  165. }
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose: Used internally by OneTimeInit to initialize.
  169. //-----------------------------------------------------------------------------
  170. void ConCommandBase::Init()
  171. {
  172. if ( s_pAccessor )
  173. {
  174. s_pAccessor->RegisterConCommandBase( this );
  175. }
  176. }
  177. void ConCommandBase::Shutdown()
  178. {
  179. if ( g_pCVar )
  180. {
  181. g_pCVar->UnregisterConCommand( this );
  182. }
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: Return name of the command/var
  186. // Output : const char
  187. //-----------------------------------------------------------------------------
  188. const char *ConCommandBase::GetName( void ) const
  189. {
  190. return m_pszName;
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose:
  194. // Input : flag -
  195. // Output : Returns true on success, false on failure.
  196. //-----------------------------------------------------------------------------
  197. bool ConCommandBase::IsFlagSet( int flag ) const
  198. {
  199. return ( flag & m_nFlags ) ? true : false;
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. // Input : flags -
  204. //-----------------------------------------------------------------------------
  205. void ConCommandBase::AddFlags( int flags )
  206. {
  207. m_nFlags |= flags;
  208. #ifdef ALLOW_DEVELOPMENT_CVARS
  209. m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
  210. #endif
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose: removes specified flags
  214. //-----------------------------------------------------------------------------
  215. void ConCommandBase::RemoveFlags( int flags )
  216. {
  217. m_nFlags &= ~flags;
  218. }
  219. // Returns current flags
  220. int ConCommandBase::GetFlags() const
  221. {
  222. return m_nFlags;
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. // Output : const ConCommandBase
  227. //-----------------------------------------------------------------------------
  228. const ConCommandBase *ConCommandBase::GetNext( void ) const
  229. {
  230. return m_pNext;
  231. }
  232. ConCommandBase *ConCommandBase::GetNext( void )
  233. {
  234. return m_pNext;
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Copies string using local new/delete operators
  238. // Input : *from -
  239. // Output : char
  240. //-----------------------------------------------------------------------------
  241. char *ConCommandBase::CopyString( const char *from )
  242. {
  243. int len;
  244. char *to;
  245. len = strlen( from );
  246. if ( len <= 0 )
  247. {
  248. to = new char[1];
  249. to[0] = 0;
  250. }
  251. else
  252. {
  253. to = new char[len+1];
  254. Q_strncpy( to, from, len+1 );
  255. }
  256. return to;
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose:
  260. // Output : const char
  261. //-----------------------------------------------------------------------------
  262. const char *ConCommandBase::GetHelpText( void ) const
  263. {
  264. return m_pszHelpString;
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Has this cvar been registered
  268. // Output : Returns true on success, false on failure.
  269. //-----------------------------------------------------------------------------
  270. bool ConCommandBase::IsRegistered( void ) const
  271. {
  272. return m_bRegistered;
  273. }
  274. //-----------------------------------------------------------------------------
  275. //
  276. // Con Commands start here
  277. //
  278. //-----------------------------------------------------------------------------
  279. //-----------------------------------------------------------------------------
  280. // Global methods
  281. //-----------------------------------------------------------------------------
  282. static characterset_t s_BreakSet;
  283. static bool s_bBuiltBreakSet = false;
  284. //-----------------------------------------------------------------------------
  285. // Tokenizer class
  286. //-----------------------------------------------------------------------------
  287. CCommand::CCommand()
  288. {
  289. if ( !s_bBuiltBreakSet )
  290. {
  291. s_bBuiltBreakSet = true;
  292. CharacterSetBuild( &s_BreakSet, "{}()':" );
  293. }
  294. Reset();
  295. }
  296. CCommand::CCommand( int nArgC, const char **ppArgV, cmd_source_t source )
  297. {
  298. Assert( nArgC > 0 );
  299. if ( !s_bBuiltBreakSet )
  300. {
  301. s_bBuiltBreakSet = true;
  302. CharacterSetBuild( &s_BreakSet, "{}()':" );
  303. }
  304. Reset();
  305. char *pBuf = m_pArgvBuffer;
  306. char *pSBuf = m_pArgSBuffer;
  307. m_nArgc = nArgC;
  308. for ( int i = 0; i < nArgC; ++i )
  309. {
  310. m_ppArgv[i] = pBuf;
  311. int nLen = Q_strlen( ppArgV[i] );
  312. memcpy( pBuf, ppArgV[i], nLen+1 );
  313. if ( i == 0 )
  314. {
  315. m_nArgv0Size = nLen;
  316. }
  317. pBuf += nLen+1;
  318. bool bContainsSpace = strchr( ppArgV[i], ' ' ) != NULL;
  319. if ( bContainsSpace )
  320. {
  321. *pSBuf++ = '\"';
  322. }
  323. memcpy( pSBuf, ppArgV[i], nLen );
  324. pSBuf += nLen;
  325. if ( bContainsSpace )
  326. {
  327. *pSBuf++ = '\"';
  328. }
  329. if ( i != nArgC - 1 )
  330. {
  331. *pSBuf++ = ' ';
  332. }
  333. }
  334. m_source = source;
  335. }
  336. void CCommand::Reset()
  337. {
  338. m_nArgc = 0;
  339. m_nArgv0Size = 0;
  340. m_pArgSBuffer[0] = 0;
  341. m_source = kCommandSrcInvalid;
  342. }
  343. characterset_t* CCommand::DefaultBreakSet()
  344. {
  345. return &s_BreakSet;
  346. }
  347. bool CCommand::Tokenize( const char *pCommand, cmd_source_t source, characterset_t *pBreakSet )
  348. {
  349. Reset();
  350. m_source = source;
  351. if ( !pCommand )
  352. return false;
  353. // Use default break set
  354. if ( !pBreakSet )
  355. {
  356. pBreakSet = &s_BreakSet;
  357. }
  358. // Copy the current command into a temp buffer
  359. // NOTE: This is here to avoid the pointers returned by DequeueNextCommand
  360. // to become invalid by calling AddText. Is there a way we can avoid the memcpy?
  361. int nLen = Q_strlen( pCommand );
  362. if ( nLen >= COMMAND_MAX_LENGTH - 1 )
  363. {
  364. Warning( "CCommand::Tokenize: Encountered command which overflows the tokenizer buffer.. Skipping!\n" );
  365. return false;
  366. }
  367. memcpy( m_pArgSBuffer, pCommand, nLen + 1 );
  368. // Parse the current command into the current command buffer
  369. CUtlBuffer bufParse( m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  370. int nArgvBufferSize = 0;
  371. while ( bufParse.IsValid() && ( m_nArgc < COMMAND_MAX_ARGC ) )
  372. {
  373. char *pArgvBuf = &m_pArgvBuffer[nArgvBufferSize];
  374. int nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize;
  375. int nStartGet = bufParse.TellGet();
  376. int nSize = bufParse.ParseToken( pBreakSet, pArgvBuf, nMaxLen );
  377. if ( nSize < 0 )
  378. break;
  379. // Check for overflow condition
  380. if ( nMaxLen == nSize )
  381. {
  382. Reset();
  383. return false;
  384. }
  385. if ( m_nArgc == 1 )
  386. {
  387. // Deal with the case where the arguments were quoted
  388. m_nArgv0Size = bufParse.TellGet();
  389. bool bFoundEndQuote = m_pArgSBuffer[m_nArgv0Size-1] == '\"';
  390. if ( bFoundEndQuote )
  391. {
  392. --m_nArgv0Size;
  393. }
  394. m_nArgv0Size -= nSize;
  395. Assert( m_nArgv0Size != 0 );
  396. // The StartGet check is to handle this case: "foo"bar
  397. // which will parse into 2 different args. ArgS should point to bar.
  398. bool bFoundStartQuote = ( m_nArgv0Size > nStartGet ) && ( m_pArgSBuffer[m_nArgv0Size-1] == '\"' );
  399. Assert( bFoundEndQuote == bFoundStartQuote );
  400. if ( bFoundStartQuote )
  401. {
  402. --m_nArgv0Size;
  403. }
  404. }
  405. m_ppArgv[ m_nArgc++ ] = pArgvBuf;
  406. if( m_nArgc >= COMMAND_MAX_ARGC )
  407. {
  408. Warning( "CCommand::Tokenize: Encountered command which overflows the argument buffer.. Clamped!\n" );
  409. }
  410. nArgvBufferSize += nSize + 1;
  411. Assert( nArgvBufferSize <= COMMAND_MAX_LENGTH );
  412. }
  413. return true;
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Helper function to parse arguments to commands.
  417. //-----------------------------------------------------------------------------
  418. const char* CCommand::FindArg( const char *pName ) const
  419. {
  420. int nArgC = ArgC();
  421. for ( int i = 1; i < nArgC; i++ )
  422. {
  423. if ( !Q_stricmp( Arg(i), pName ) )
  424. return (i+1) < nArgC ? Arg( i+1 ) : "";
  425. }
  426. return 0;
  427. }
  428. int CCommand::FindArgInt( const char *pName, int nDefaultVal ) const
  429. {
  430. const char *pVal = FindArg( pName );
  431. if ( pVal )
  432. return atoi( pVal );
  433. else
  434. return nDefaultVal;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Default console command autocompletion function
  438. //-----------------------------------------------------------------------------
  439. int DefaultCompletionFunc( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  440. {
  441. return 0;
  442. }
  443. //-----------------------------------------------------------------------------
  444. // Purpose: Constructs a console command
  445. //-----------------------------------------------------------------------------
  446. //ConCommand::ConCommand()
  447. //{
  448. // m_bIsNewConCommand = true;
  449. //}
  450. ConCommand::ConCommand( const char *pName, FnCommandCallbackV1_t callback, const char *pHelpString /*= 0*/, int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ )
  451. {
  452. // Set the callback
  453. m_fnCommandCallbackV1 = callback;
  454. m_bUsingNewCommandCallback = false;
  455. m_bUsingCommandCallbackInterface = false;
  456. m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc;
  457. m_bHasCompletionCallback = completionFunc != 0 ? true : false;
  458. // Setup the rest
  459. BaseClass::Create( pName, pHelpString, flags );
  460. }
  461. ConCommand::ConCommand( const char *pName, FnCommandCallback_t callback, const char *pHelpString /*= 0*/, int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ )
  462. {
  463. // Set the callback
  464. m_fnCommandCallback = callback;
  465. m_bUsingNewCommandCallback = true;
  466. m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc;
  467. m_bHasCompletionCallback = completionFunc != 0 ? true : false;
  468. m_bUsingCommandCallbackInterface = false;
  469. // Setup the rest
  470. BaseClass::Create( pName, pHelpString, flags );
  471. }
  472. ConCommand::ConCommand( const char *pName, ICommandCallback *pCallback, const char *pHelpString /*= 0*/, int flags /*= 0*/, ICommandCompletionCallback *pCompletionCallback /*= 0*/ )
  473. {
  474. // Set the callback
  475. m_pCommandCallback = pCallback;
  476. m_bUsingNewCommandCallback = false;
  477. m_pCommandCompletionCallback = pCompletionCallback;
  478. m_bHasCompletionCallback = ( pCompletionCallback != 0 );
  479. m_bUsingCommandCallbackInterface = true;
  480. // Setup the rest
  481. BaseClass::Create( pName, pHelpString, flags );
  482. }
  483. //-----------------------------------------------------------------------------
  484. // Destructor
  485. //-----------------------------------------------------------------------------
  486. ConCommand::~ConCommand( void )
  487. {
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose: Returns true if this is a command
  491. //-----------------------------------------------------------------------------
  492. bool ConCommand::IsCommand( void ) const
  493. {
  494. return true;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose: Invoke the function if there is one
  498. //-----------------------------------------------------------------------------
  499. void ConCommand::Dispatch( const CCommand &command )
  500. {
  501. if ( m_bUsingNewCommandCallback )
  502. {
  503. if ( m_fnCommandCallback )
  504. {
  505. ( *m_fnCommandCallback )( command );
  506. return;
  507. }
  508. }
  509. else if ( m_bUsingCommandCallbackInterface )
  510. {
  511. if ( m_pCommandCallback )
  512. {
  513. m_pCommandCallback->CommandCallback( command );
  514. return;
  515. }
  516. }
  517. else
  518. {
  519. if ( m_fnCommandCallbackV1 )
  520. {
  521. ( *m_fnCommandCallbackV1 )();
  522. return;
  523. }
  524. }
  525. // Command without callback!!!
  526. AssertMsg( 0, ( "Encountered ConCommand '%s' without a callback!\n", GetName() ) );
  527. }
  528. //-----------------------------------------------------------------------------
  529. // Purpose: Calls the autocompletion method to get autocompletion suggestions
  530. //-----------------------------------------------------------------------------
  531. int ConCommand::AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands )
  532. {
  533. if ( m_bUsingCommandCallbackInterface )
  534. {
  535. if ( !m_pCommandCompletionCallback )
  536. return 0;
  537. return m_pCommandCompletionCallback->CommandCompletionCallback( partial, commands );
  538. }
  539. Assert( m_fnCompletionCallback );
  540. if ( !m_fnCompletionCallback )
  541. return 0;
  542. char rgpchCommands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ];
  543. int iret = ( m_fnCompletionCallback )( partial, rgpchCommands );
  544. for ( int i = 0 ; i < iret; ++i )
  545. {
  546. CUtlString str = rgpchCommands[ i ];
  547. commands.AddToTail( str );
  548. }
  549. return iret;
  550. }
  551. //-----------------------------------------------------------------------------
  552. // Returns true if the console command can autocomplete
  553. //-----------------------------------------------------------------------------
  554. bool ConCommand::CanAutoComplete( void )
  555. {
  556. return m_bHasCompletionCallback;
  557. }
  558. //-----------------------------------------------------------------------------
  559. //
  560. // Console Variables
  561. //
  562. //-----------------------------------------------------------------------------
  563. //-----------------------------------------------------------------------------
  564. // Various constructors
  565. //-----------------------------------------------------------------------------
  566. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags /* = 0 */ )
  567. {
  568. Create( pName, pDefaultValue, flags );
  569. }
  570. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString )
  571. {
  572. Create( pName, pDefaultValue, flags, pHelpString );
  573. }
  574. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax )
  575. {
  576. Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax );
  577. }
  578. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, FnChangeCallback_t callback )
  579. {
  580. Create( pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0, callback );
  581. }
  582. ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t callback )
  583. {
  584. Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax, callback );
  585. }
  586. //-----------------------------------------------------------------------------
  587. // Destructor
  588. //-----------------------------------------------------------------------------
  589. ConVar::~ConVar( void )
  590. {
  591. if ( m_Value.m_pszString )
  592. {
  593. delete[] m_Value.m_pszString;
  594. m_Value.m_pszString = NULL;
  595. }
  596. }
  597. //-----------------------------------------------------------------------------
  598. // Install a change callback (there shouldn't already be one....)
  599. //-----------------------------------------------------------------------------
  600. void ConVar::InstallChangeCallback( FnChangeCallback_t callback, bool bInvoke /*=true*/ )
  601. {
  602. if ( !callback )
  603. {
  604. Warning( "InstallChangeCallback called with NULL callback, ignoring!!!\n" );
  605. return;
  606. }
  607. if ( m_pParent->m_fnChangeCallbacks.Find( callback ) != m_pParent->m_fnChangeCallbacks.InvalidIndex() )
  608. {
  609. // Same ptr added twice, sigh...
  610. Warning( "InstallChangeCallback ignoring duplicate change callback!!!\n" );
  611. return;
  612. }
  613. m_pParent->m_fnChangeCallbacks.AddToTail( callback );
  614. // Call it immediately to set the initial value...
  615. if ( bInvoke )
  616. {
  617. callback( this, m_Value.m_pszString, ObscureConvarValue( m_Value.m_fValue, ( intp ) this ) );
  618. }
  619. }
  620. void ConVar::RemoveChangeCallback( FnChangeCallback_t callback )
  621. {
  622. m_pParent->m_fnChangeCallbacks.FindAndRemove( callback );
  623. }
  624. bool ConVar::IsFlagSet( int flag ) const
  625. {
  626. return ( flag & m_pParent->m_nFlags ) ? true : false;
  627. }
  628. int ConVar::GetFlags() const
  629. {
  630. return m_pParent->m_nFlags;
  631. }
  632. const char *ConVar::GetHelpText( void ) const
  633. {
  634. return m_pParent->m_pszHelpString;
  635. }
  636. void ConVar::AddFlags( int flags )
  637. {
  638. m_pParent->m_nFlags |= flags;
  639. #ifdef ALLOW_DEVELOPMENT_CVARS
  640. m_pParent->m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
  641. #endif
  642. }
  643. bool ConVar::IsRegistered( void ) const
  644. {
  645. return m_pParent->m_bRegistered;
  646. }
  647. const char *ConVar::GetName( void ) const
  648. {
  649. return m_pParent->m_pszName;
  650. }
  651. const char *ConVar::GetBaseName( void ) const
  652. {
  653. return m_pParent->m_pszName;
  654. }
  655. int ConVar::GetSplitScreenPlayerSlot( void ) const
  656. {
  657. // Default implementation (certain FCVAR_USERINFO derive a new type of convar and set this)
  658. return 0;
  659. }
  660. //-----------------------------------------------------------------------------
  661. // Purpose:
  662. // Output : Returns true on success, false on failure.
  663. //-----------------------------------------------------------------------------
  664. bool ConVar::IsCommand( void ) const
  665. {
  666. return false;
  667. }
  668. //-----------------------------------------------------------------------------
  669. // Purpose:
  670. // Input :
  671. //-----------------------------------------------------------------------------
  672. void ConVar::Init()
  673. {
  674. BaseClass::Init();
  675. }
  676. bool ConVar::InternalSetColorFromString( const char *value )
  677. {
  678. bool bColor = false;
  679. // Try pulling RGBA color values out of the string
  680. int nRGBA[4];
  681. int nParamsRead = sscanf( value, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]) );
  682. if ( nParamsRead >= 3 )
  683. {
  684. // This is probably a color!
  685. if ( nParamsRead == 3 )
  686. {
  687. // Assume they wanted full alpha
  688. nRGBA[3] = 255;
  689. }
  690. if ( nRGBA[0] >= 0 && nRGBA[0] <= 255 &&
  691. nRGBA[1] >= 0 && nRGBA[1] <= 255 &&
  692. nRGBA[2] >= 0 && nRGBA[2] <= 255 &&
  693. nRGBA[3] >= 0 && nRGBA[3] <= 255 )
  694. {
  695. // This is definitely a color!
  696. bColor = true;
  697. // Stuff all the values into each byte of our int
  698. unsigned char *pColorElement = ((unsigned char*)&m_Value.m_nValue);
  699. pColorElement[0] = nRGBA[0];
  700. pColorElement[1] = nRGBA[1];
  701. pColorElement[2] = nRGBA[2];
  702. pColorElement[3] = nRGBA[3];
  703. // Obscure the value.
  704. m_Value.m_nValue = ObscureConvarValue( m_Value.m_nValue, ( intp ) this );
  705. // Copy that value into a float (even though this has little meaning)
  706. m_Value.m_fValue = ObscureConvarValue( ( float )( m_Value.m_nValue ), ( intp ) this );
  707. }
  708. }
  709. return bColor;
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose:
  713. // Input : *value -
  714. //-----------------------------------------------------------------------------
  715. void ConVar::InternalSetValue( const char *value )
  716. {
  717. if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) )
  718. {
  719. if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() )
  720. {
  721. g_pCVar->QueueMaterialThreadSetValue( this, value );
  722. return;
  723. }
  724. }
  725. char tempVal[ 32 ];
  726. char *val;
  727. Assert(m_pParent == this); // Only valid for root convars.
  728. float flOldValue = ObscureConvarValue( m_Value.m_fValue, ( intp ) this );
  729. val = (char *)value;
  730. if ( !val )
  731. val = "";
  732. if ( !InternalSetColorFromString( value ) )
  733. {
  734. // Not a color, do the standard thing
  735. double dblValue = V_atod( value ); // Use double to avoid 24-bit restriction on integers and allow storing timestamps or dates in convars
  736. float fNewValue = ( float ) dblValue;
  737. if ( !IsFinite( fNewValue ) )
  738. {
  739. Warning( "Warning: %s = '%s' is infinite, clamping value.\n", GetName(), value );
  740. fNewValue = FLT_MAX;
  741. dblValue = FLT_MAX;
  742. }
  743. if ( ClampValue( fNewValue ) )
  744. {
  745. dblValue = fNewValue;
  746. Q_snprintf( tempVal,sizeof(tempVal), "%f", fNewValue );
  747. val = tempVal;
  748. }
  749. // Redetermine value
  750. m_Value.m_fValue = ObscureConvarValue( fNewValue, (intp) this );
  751. m_Value.m_nValue = ObscureConvarValue( ( int ) dblValue, (intp) this ); // convert double to int to avoid precision loss
  752. }
  753. if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) )
  754. {
  755. ChangeStringValue( val, flOldValue );
  756. }
  757. }
  758. //-----------------------------------------------------------------------------
  759. // Purpose:
  760. // Input : *tempVal -
  761. //-----------------------------------------------------------------------------
  762. void ConVar::ChangeStringValue( const char *tempVal, float flOldValue )
  763. {
  764. Assert( !( m_nFlags & FCVAR_NEVER_AS_STRING ) );
  765. char* pszOldValue = (char*)stackalloc( m_Value.m_StringLength );
  766. memcpy( pszOldValue, m_Value.m_pszString, m_Value.m_StringLength );
  767. int len = Q_strlen(tempVal) + 1;
  768. if ( len > m_Value.m_StringLength)
  769. {
  770. if (m_Value.m_pszString)
  771. {
  772. delete[] m_Value.m_pszString;
  773. }
  774. m_Value.m_pszString = new char[len];
  775. m_Value.m_StringLength = len;
  776. }
  777. memcpy( m_Value.m_pszString, tempVal, len );
  778. // Invoke any necessary callback function
  779. for ( int i = 0; i < m_fnChangeCallbacks.Count(); ++i )
  780. {
  781. m_fnChangeCallbacks[ i ]( this, pszOldValue, flOldValue );
  782. }
  783. if ( g_pCVar )
  784. {
  785. g_pCVar->CallGlobalChangeCallbacks( this, pszOldValue, flOldValue );
  786. }
  787. stackfree( pszOldValue );
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Purpose: Check whether to clamp and then perform clamp
  791. // Input : value -
  792. // Output : Returns true if value changed
  793. //-----------------------------------------------------------------------------
  794. bool ConVar::ClampValue( float& value )
  795. {
  796. if ( m_bHasMin && ( value < m_fMinVal ) )
  797. {
  798. value = m_fMinVal;
  799. return true;
  800. }
  801. if ( m_bHasMax && ( value > m_fMaxVal ) )
  802. {
  803. value = m_fMaxVal;
  804. return true;
  805. }
  806. return false;
  807. }
  808. //-----------------------------------------------------------------------------
  809. // Purpose:
  810. // Input : *value -
  811. //-----------------------------------------------------------------------------
  812. void ConVar::InternalSetFloatValue( float fNewValue )
  813. {
  814. if ( fNewValue == ObscureConvarValue( m_Value.m_fValue, ( intp ) this ) )
  815. return;
  816. if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) )
  817. {
  818. if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() )
  819. {
  820. g_pCVar->QueueMaterialThreadSetValue( this, fNewValue );
  821. return;
  822. }
  823. }
  824. Assert( m_pParent == this ); // Only valid for root convars.
  825. // Check bounds
  826. ClampValue( fNewValue );
  827. // Redetermine value
  828. float flOldValue = ObscureConvarValue( m_Value.m_fValue, ( intp ) this );
  829. m_Value.m_fValue = ObscureConvarValue( fNewValue, ( intp) this );
  830. m_Value.m_nValue = ObscureConvarValue( ( int )fNewValue, ( intp) this );
  831. if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) )
  832. {
  833. char tempVal[ 32 ];
  834. Q_snprintf( tempVal, sizeof( tempVal), "%f", fNewValue );
  835. ChangeStringValue( tempVal, flOldValue );
  836. }
  837. else
  838. {
  839. Assert( m_fnChangeCallbacks.Count() == 0 );
  840. }
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose:
  844. // Input : *value -
  845. //-----------------------------------------------------------------------------
  846. void ConVar::InternalSetIntValue( int nValue )
  847. {
  848. if ( nValue == ObscureConvarValue( m_Value.m_nValue, (intp) this ) )
  849. return;
  850. if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) )
  851. {
  852. if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() )
  853. {
  854. g_pCVar->QueueMaterialThreadSetValue( this, nValue );
  855. return;
  856. }
  857. }
  858. Assert( m_pParent == this ); // Only valid for root convars.
  859. float fValue = (float)nValue;
  860. if ( ClampValue( fValue ) )
  861. {
  862. nValue = ( int )( fValue );
  863. }
  864. // Redetermine value
  865. float flOldValue = ObscureConvarValue( m_Value.m_fValue, ( intp ) this );
  866. m_Value.m_fValue = ObscureConvarValue( fValue, ( intp) this );
  867. m_Value.m_nValue = ObscureConvarValue( nValue, ( intp) this );
  868. if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) )
  869. {
  870. char tempVal[ 32 ];
  871. Q_snprintf( tempVal, sizeof( tempVal ), "%d", nValue );
  872. ChangeStringValue( tempVal, flOldValue );
  873. }
  874. else
  875. {
  876. Assert( m_fnChangeCallbacks.Count() == 0 );
  877. }
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose:
  881. // Input : *value -
  882. //-----------------------------------------------------------------------------
  883. void ConVar::InternalSetColorValue( Color value )
  884. {
  885. // Stuff color values into an int
  886. int nValue;
  887. unsigned char *pColorElement = ((unsigned char*)&nValue);
  888. pColorElement[0] = value[0];
  889. pColorElement[1] = value[1];
  890. pColorElement[2] = value[2];
  891. pColorElement[3] = value[3];
  892. // Call the int internal set
  893. InternalSetIntValue( nValue );
  894. }
  895. //-----------------------------------------------------------------------------
  896. // Purpose: Private creation
  897. //-----------------------------------------------------------------------------
  898. void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= 0*/,
  899. const char *pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/,
  900. bool bMax /*= false*/, float fMax /*= false*/, FnChangeCallback_t callback /*= NULL*/ )
  901. {
  902. static char *empty_string = "";
  903. m_pParent = this;
  904. // Name should be static data
  905. m_pszDefaultValue = pDefaultValue ? pDefaultValue : empty_string;
  906. Assert( m_pszDefaultValue );
  907. m_bHasMin = bMin;
  908. m_fMinVal = fMin;
  909. m_bHasMax = bMax;
  910. m_fMaxVal = fMax;
  911. if ( callback )
  912. {
  913. m_fnChangeCallbacks.AddToTail( callback );
  914. }
  915. m_Value.m_StringLength = strlen( m_pszDefaultValue ) + 1;
  916. m_Value.m_pszString = new char[m_Value.m_StringLength];
  917. memcpy( m_Value.m_pszString, m_pszDefaultValue, m_Value.m_StringLength );
  918. if ( !InternalSetColorFromString( m_Value.m_pszString ) )
  919. {
  920. double dblValue = V_atod( m_Value.m_pszString ); // Use double to avoid 24-bit restriction on integers and allow storing timestamps or dates in convars
  921. float fValue = ( float ) dblValue;
  922. if ( !IsFinite( fValue ) )
  923. {
  924. Warning( "ConVar(%s) defined with infinite float value (%s)\n", pName, m_Value.m_pszString );
  925. fValue = FLT_MAX;
  926. Assert( 0 );
  927. }
  928. // Bounds Check, should never happen, if it does, no big deal
  929. if ( m_bHasMin && ( fValue < m_fMinVal ) )
  930. {
  931. Assert( 0 );
  932. }
  933. if ( m_bHasMax && ( fValue > m_fMaxVal ) )
  934. {
  935. Assert( 0 );
  936. }
  937. m_Value.m_fValue = ObscureConvarValue( fValue, ( intp ) this );
  938. m_Value.m_nValue = ObscureConvarValue( ( int ) dblValue, ( intp) this );
  939. }
  940. //If we're not tagged as cheat, archive or release then hide us.
  941. #if defined( CULL_ALL_CVARS_NOT_FCVAR_RELEASE )
  942. // FIXMEL4DTOMAINMERGE: will need to assess if this hides too many convars for TF and other projects in main
  943. if ( !( flags & ( FCVAR_CHEAT | FCVAR_ARCHIVE | FCVAR_RELEASE | FCVAR_USERINFO ) ) )
  944. {
  945. flags |= FCVAR_DEVELOPMENTONLY;
  946. }
  947. #endif
  948. BaseClass::Create( pName, pHelpString, flags );
  949. }
  950. //-----------------------------------------------------------------------------
  951. // Purpose:
  952. // Input : *value -
  953. //-----------------------------------------------------------------------------
  954. void ConVar::SetValue(const char *value)
  955. {
  956. ConVar *var = ( ConVar * )m_pParent;
  957. var->InternalSetValue( value );
  958. }
  959. //-----------------------------------------------------------------------------
  960. // Purpose:
  961. // Input : value -
  962. //-----------------------------------------------------------------------------
  963. void ConVar::SetValue( float value )
  964. {
  965. ConVar *var = ( ConVar * )m_pParent;
  966. var->InternalSetFloatValue( value );
  967. }
  968. //-----------------------------------------------------------------------------
  969. // Purpose:
  970. // Input : value -
  971. //-----------------------------------------------------------------------------
  972. void ConVar::SetValue( int value )
  973. {
  974. ConVar *var = ( ConVar * )m_pParent;
  975. var->InternalSetIntValue( value );
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose:
  979. // Input : value -
  980. //-----------------------------------------------------------------------------
  981. void ConVar::SetValue( Color value )
  982. {
  983. ConVar *var = ( ConVar * )m_pParent;
  984. var->InternalSetColorValue( value );
  985. }
  986. //-----------------------------------------------------------------------------
  987. // Purpose: Reset to default value
  988. //-----------------------------------------------------------------------------
  989. void ConVar::Revert( void )
  990. {
  991. // Force default value again
  992. ConVar *var = ( ConVar * )m_pParent;
  993. var->SetValue( var->m_pszDefaultValue );
  994. }
  995. //-----------------------------------------------------------------------------
  996. // Purpose:
  997. // Input : minVal -
  998. // Output : true if there is a min set
  999. //-----------------------------------------------------------------------------
  1000. bool ConVar::GetMin( float& minVal ) const
  1001. {
  1002. minVal = m_pParent->m_fMinVal;
  1003. return m_pParent->m_bHasMin;
  1004. }
  1005. //-----------------------------------------------------------------------------
  1006. // Purpose:
  1007. // Input : maxVal -
  1008. //-----------------------------------------------------------------------------
  1009. bool ConVar::GetMax( float& maxVal ) const
  1010. {
  1011. maxVal = m_pParent->m_fMaxVal;
  1012. return m_pParent->m_bHasMax;
  1013. }
  1014. float ConVar::GetMinValue() const
  1015. {
  1016. return m_pParent->m_fMinVal;
  1017. }
  1018. float ConVar::GetMaxValue() const
  1019. {
  1020. return m_pParent->m_fMaxVal;;
  1021. }
  1022. bool ConVar::HasMin() const
  1023. {
  1024. return m_pParent->m_bHasMin;
  1025. }
  1026. bool ConVar::HasMax() const
  1027. {
  1028. return m_pParent->m_bHasMax;
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Purpose:
  1032. // Output : const char
  1033. //-----------------------------------------------------------------------------
  1034. const char *ConVar::GetDefault( void ) const
  1035. {
  1036. return m_pParent->m_pszDefaultValue;
  1037. }
  1038. void ConVar::SetDefault( const char *pszDefault )
  1039. {
  1040. static char *empty_string = "";
  1041. m_pszDefaultValue = pszDefault ? pszDefault : empty_string;
  1042. Assert( m_pszDefaultValue );
  1043. }
  1044. //-----------------------------------------------------------------------------
  1045. // This version is simply used to make reading convars simpler.
  1046. // Writing convars isn't allowed in this mode
  1047. //-----------------------------------------------------------------------------
  1048. class CEmptyConVar : public ConVar
  1049. {
  1050. public:
  1051. CEmptyConVar() : ConVar( "", "0" ) {}
  1052. // Used for optimal read access
  1053. virtual void SetValue( const char *pValue ) {}
  1054. virtual void SetValue( float flValue ) {}
  1055. virtual void SetValue( int nValue ) {}
  1056. virtual const char *GetName( void ) const { return ""; }
  1057. virtual bool IsFlagSet( int nFlags ) const { return false; }
  1058. };
  1059. static CEmptyConVar s_EmptyConVar;
  1060. ConVarRef::ConVarRef( const char *pName )
  1061. {
  1062. Init( pName, false );
  1063. }
  1064. ConVarRef::ConVarRef( const char *pName, bool bIgnoreMissing )
  1065. {
  1066. Init( pName, bIgnoreMissing );
  1067. }
  1068. void ConVarRef::Init( const char *pName, bool bIgnoreMissing )
  1069. {
  1070. m_pConVar = g_pCVar ? g_pCVar->FindVar( pName ) : &s_EmptyConVar;
  1071. if ( !m_pConVar )
  1072. {
  1073. m_pConVar = &s_EmptyConVar;
  1074. }
  1075. m_pConVarState = static_cast< ConVar * >( m_pConVar );
  1076. if( !IsValid() )
  1077. {
  1078. static bool bFirst = true;
  1079. if ( g_pCVar || bFirst )
  1080. {
  1081. if ( !bIgnoreMissing )
  1082. {
  1083. Warning( "ConVarRef %s doesn't point to an existing ConVar\n", pName );
  1084. }
  1085. bFirst = false;
  1086. }
  1087. }
  1088. }
  1089. ConVarRef::ConVarRef( IConVar *pConVar )
  1090. {
  1091. m_pConVar = pConVar ? pConVar : &s_EmptyConVar;
  1092. m_pConVarState = static_cast< ConVar * >( m_pConVar );
  1093. }
  1094. bool ConVarRef::IsValid() const
  1095. {
  1096. return m_pConVar != &s_EmptyConVar;
  1097. }
  1098. // Helper for splitscreen ConVars
  1099. SplitScreenConVarRef::SplitScreenConVarRef( const char *pName )
  1100. {
  1101. Init( pName, false );
  1102. }
  1103. SplitScreenConVarRef::SplitScreenConVarRef( const char *pName, bool bIgnoreMissing )
  1104. {
  1105. Init( pName, bIgnoreMissing );
  1106. }
  1107. void SplitScreenConVarRef::Init( const char *pName, bool bIgnoreMissing )
  1108. {
  1109. for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i )
  1110. {
  1111. cv_t &info = m_Info[ i ];
  1112. char pchName[ 256 ];
  1113. if ( i != 0 )
  1114. {
  1115. Q_snprintf( pchName, sizeof( pchName ), "%s%d", pName, i + 1 );
  1116. }
  1117. else
  1118. {
  1119. Q_strncpy( pchName, pName, sizeof( pchName ) );
  1120. }
  1121. info.m_pConVar = g_pCVar ? g_pCVar->FindVar( pchName ) : &s_EmptyConVar;
  1122. if ( !info.m_pConVar )
  1123. {
  1124. info.m_pConVar = &s_EmptyConVar;
  1125. if ( i > 0 )
  1126. {
  1127. // Point at slot zero instead, in case we got in here with a non FCVAR_SS var...
  1128. info.m_pConVar = m_Info[ 0 ].m_pConVar;
  1129. }
  1130. }
  1131. info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar );
  1132. }
  1133. if ( !IsValid() )
  1134. {
  1135. static bool bFirst = true;
  1136. if ( g_pCVar || bFirst )
  1137. {
  1138. if ( !bIgnoreMissing )
  1139. {
  1140. Warning( "ConVarRef %s doesn't point to an existing ConVar\n", pName );
  1141. }
  1142. bFirst = false;
  1143. }
  1144. }
  1145. }
  1146. SplitScreenConVarRef::SplitScreenConVarRef( IConVar *pConVar )
  1147. {
  1148. {
  1149. cv_t &info = m_Info[ 0 ];
  1150. info.m_pConVar = pConVar ? pConVar : &s_EmptyConVar;
  1151. info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar );
  1152. }
  1153. for ( int i = 1; i < MAX_SPLITSCREEN_CLIENTS; ++i )
  1154. {
  1155. cv_t &info = m_Info[ i ];
  1156. char pchName[ 256 ];
  1157. Q_snprintf( pchName, sizeof( pchName ), "%s%d", pConVar->GetName(), i + 1 );
  1158. info.m_pConVar = g_pCVar ? g_pCVar->FindVar( pchName ) : &s_EmptyConVar;
  1159. if ( !info.m_pConVar )
  1160. {
  1161. info.m_pConVar = &s_EmptyConVar;
  1162. if ( i > 0 )
  1163. {
  1164. // Point at slot zero instead, in case we got in here with a non FCVAR_SS var...
  1165. info.m_pConVar = m_Info[ 0 ].m_pConVar;
  1166. }
  1167. }
  1168. info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar );
  1169. }
  1170. }
  1171. bool SplitScreenConVarRef::IsValid() const
  1172. {
  1173. return m_Info[ 0 ].m_pConVar != &s_EmptyConVar;
  1174. }
  1175. struct PrintConVarFlags_t
  1176. {
  1177. int flag;
  1178. const char *desc;
  1179. };
  1180. static PrintConVarFlags_t g_PrintConVarFlags[] =
  1181. {
  1182. { FCVAR_GAMEDLL, "game" },
  1183. { FCVAR_CLIENTDLL, "client" },
  1184. { FCVAR_ARCHIVE, "archive" },
  1185. { FCVAR_NOTIFY, "notify" },
  1186. { FCVAR_SPONLY, "singleplayer" },
  1187. { FCVAR_NOT_CONNECTED, "notconnected" },
  1188. { FCVAR_CHEAT, "cheat" },
  1189. { FCVAR_REPLICATED, "replicated" },
  1190. { FCVAR_SERVER_CAN_EXECUTE, "server_can_execute" },
  1191. { FCVAR_CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" },
  1192. { FCVAR_USERINFO, "user" },
  1193. { FCVAR_SS, "ss" },
  1194. { FCVAR_SS_ADDED, "ss_added" },
  1195. };
  1196. //-----------------------------------------------------------------------------
  1197. // Purpose:
  1198. //-----------------------------------------------------------------------------
  1199. void ConVar_AppendFlags( const ConCommandBase *var, char *buf, size_t bufsize )
  1200. {
  1201. for ( int i = 0; i < ARRAYSIZE( g_PrintConVarFlags ) ; ++i )
  1202. {
  1203. const PrintConVarFlags_t &info = g_PrintConVarFlags[ i ];
  1204. if ( var->IsFlagSet( info.flag ) )
  1205. {
  1206. char append[ 128 ];
  1207. Q_snprintf( append, sizeof( append ), " %s", info.desc );
  1208. Q_strncat( buf, append, bufsize, COPY_ALL_CHARACTERS );
  1209. }
  1210. }
  1211. }
  1212. static void AppendPrintf( char *buf, size_t bufsize, char const *fmt, ... )
  1213. {
  1214. char scratch[ 1024 ];
  1215. va_list argptr;
  1216. va_start( argptr, fmt );
  1217. _vsnprintf( scratch, sizeof( scratch ) - 1, fmt, argptr );
  1218. va_end( argptr );
  1219. scratch[ sizeof( scratch ) - 1 ] = 0;
  1220. Q_strncat( buf, scratch, bufsize, COPY_ALL_CHARACTERS );
  1221. }
  1222. //-----------------------------------------------------------------------------
  1223. // Purpose:
  1224. //-----------------------------------------------------------------------------
  1225. void ConVar_PrintDescription( const ConCommandBase *pVar )
  1226. {
  1227. bool bMin, bMax;
  1228. float fMin, fMax;
  1229. const char *pStr;
  1230. Assert( pVar );
  1231. Color clr;
  1232. clr.SetColor( 255, 100, 100, 255 );
  1233. char outstr[ 4096 ];
  1234. outstr[ 0 ] = 0;
  1235. if ( !pVar->IsCommand() )
  1236. {
  1237. ConVar *var = ( ConVar * )pVar;
  1238. const ConVar_ServerBounded *pBounded = dynamic_cast<const ConVar_ServerBounded*>( var );
  1239. bMin = var->GetMin( fMin );
  1240. bMax = var->GetMax( fMax );
  1241. const char *value = NULL;
  1242. char tempVal[ 32 ];
  1243. if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
  1244. {
  1245. value = tempVal;
  1246. int intVal = pBounded ? pBounded->GetInt() : var->GetInt();
  1247. float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat();
  1248. if ( fabs( (float)intVal - floatVal ) < 0.000001 )
  1249. {
  1250. Q_snprintf( tempVal, sizeof( tempVal ), "%d", intVal );
  1251. }
  1252. else
  1253. {
  1254. Q_snprintf( tempVal, sizeof( tempVal ), "%f", floatVal );
  1255. }
  1256. }
  1257. else
  1258. {
  1259. value = var->GetString();
  1260. }
  1261. if ( value )
  1262. {
  1263. AppendPrintf( outstr, sizeof( outstr ), "\"%s\" = \"%s\"", var->GetName(), value );
  1264. if ( V_stricmp( value, var->GetDefault() ) )
  1265. {
  1266. AppendPrintf( outstr, sizeof( outstr ), " ( def. \"%s\" )", var->GetDefault() );
  1267. }
  1268. }
  1269. if ( bMin )
  1270. {
  1271. AppendPrintf( outstr, sizeof( outstr ), " min. %f", fMin );
  1272. }
  1273. if ( bMax )
  1274. {
  1275. AppendPrintf( outstr, sizeof( outstr ), " max. %f", fMax );
  1276. }
  1277. // Handle virtualized cvars.
  1278. if ( pBounded && fabs( pBounded->GetFloat() - var->GetFloat() ) > 0.0001f )
  1279. {
  1280. AppendPrintf( outstr, sizeof( outstr ), " [%.3f server clamped to %.3f]",
  1281. var->GetFloat(), pBounded->GetFloat() );
  1282. }
  1283. }
  1284. else
  1285. {
  1286. ConCommand *var = ( ConCommand * )pVar;
  1287. AppendPrintf( outstr, sizeof( outstr ), "\"%s\" ", var->GetName() );
  1288. }
  1289. ConVar_AppendFlags( pVar, outstr, sizeof( outstr ) );
  1290. pStr = pVar->GetHelpText();
  1291. if ( pStr && *pStr )
  1292. {
  1293. ConMsg( "%-80s - %.80s\n", outstr, pStr );
  1294. }
  1295. else
  1296. {
  1297. ConMsg( "%-80s\n", outstr );
  1298. }
  1299. }