diff options
Diffstat (limited to 'external/vpc/vstdlib')
| -rw-r--r-- | external/vpc/vstdlib/concommandhash.h | 212 | ||||
| -rw-r--r-- | external/vpc/vstdlib/cvar.cpp | 1302 | ||||
| -rw-r--r-- | external/vpc/vstdlib/keyvaluessystem.cpp | 620 | ||||
| -rw-r--r-- | external/vpc/vstdlib/random.cpp | 256 | ||||
| -rw-r--r-- | external/vpc/vstdlib/vstrtools.cpp | 316 |
5 files changed, 2706 insertions, 0 deletions
diff --git a/external/vpc/vstdlib/concommandhash.h b/external/vpc/vstdlib/concommandhash.h new file mode 100644 index 0000000..40c0074 --- /dev/null +++ b/external/vpc/vstdlib/concommandhash.h @@ -0,0 +1,212 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Special case hash table for console commands +// +// $NoKeywords: $ +// +//===========================================================================// + +#if !defined( CONCOMMANDHASH_H ) +#define CONCOMMANDHASH_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utllinkedlist.h" +#include "generichash.h" + +// This is a hash table class very similar to the CUtlHashFast, but +// modified specifically so that we can look up ConCommandBases +// by string names without having to actually store those strings in +// the dictionary, and also iterate over all of them. +// It uses separate chaining: each key hashes to a bucket, each +// bucket is a linked list of hashed commands. We store the hash of +// the command's string name as well as its pointer, so we can do +// the linked list march part of the Find() operation more quickly. +class CConCommandHash +{ +public: + typedef int CCommandHashHandle_t; + typedef unsigned int HashKey_t; + + // Constructor/Deconstructor. + CConCommandHash(); + ~CConCommandHash(); + + // Memory. + void Purge( bool bReinitialize ); + + // Invalid handle. + static CCommandHashHandle_t InvalidHandle( void ) { return ( CCommandHashHandle_t )~0; } + inline bool IsValidHandle( CCommandHashHandle_t hHash ) const; + + /// Initialize. + void Init( void ); // bucket count is hardcoded in enum below. + + /// Get hash value for a concommand + static inline HashKey_t Hash( const ConCommandBase *cmd ); + + // Size not available; count is meaningless for multilists. + // int Count( void ) const; + + // Insertion. + CCommandHashHandle_t Insert( ConCommandBase *cmd ); + CCommandHashHandle_t FastInsert( ConCommandBase *cmd ); + + // Removal. + void Remove( CCommandHashHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + inline CCommandHashHandle_t Find( const char *name ) const; + CCommandHashHandle_t Find( const ConCommandBase *cmd ) const; + // A convenience version of Find that skips the handle part + // and returns a pointer to a concommand, or NULL if none was found. + inline ConCommandBase * FindPtr( const char *name ) const; + + inline ConCommandBase * &operator[]( CCommandHashHandle_t hHash ); + inline ConCommandBase *const &operator[]( CCommandHashHandle_t hHash ) const; + +#ifdef _DEBUG + // Dump a report to MSG + void Report( void ); +#endif + + // Iteration + struct CCommandHashIterator_t + { + int bucket; + CCommandHashHandle_t handle; + + CCommandHashIterator_t(int _bucket, const CCommandHashHandle_t &_handle) + : bucket(_bucket), handle(_handle) {}; + // inline operator UtlHashFastHandle_t() const { return handle; }; + }; + inline CCommandHashIterator_t First() const; + inline CCommandHashIterator_t Next( const CCommandHashIterator_t &hHash ) const; + inline bool IsValidIterator( const CCommandHashIterator_t &iter ) const; + inline ConCommandBase * &operator[]( const CCommandHashIterator_t &iter ) { return (*this)[iter.handle]; } + inline ConCommandBase * const &operator[]( const CCommandHashIterator_t &iter ) const { return (*this)[iter.handle]; } +private: + // a find func where we've already computed the hash for the string. + // (hidden private in case we decide to invent a custom string hash func + // for this class) + CCommandHashHandle_t Find( const char *name, HashKey_t hash) const; + +protected: + enum + { + kNUM_BUCKETS = 256, + kBUCKETMASK = kNUM_BUCKETS - 1, + }; + + struct HashEntry_t + { + HashKey_t m_uiKey; + ConCommandBase *m_Data; + + HashEntry_t(unsigned int _hash, ConCommandBase * _cmd) + : m_uiKey(_hash), m_Data(_cmd) {}; + + HashEntry_t(){}; + }; + + typedef CUtlFixedLinkedList<HashEntry_t> datapool_t; + + CUtlVector<CCommandHashHandle_t> m_aBuckets; + datapool_t m_aDataPool; +}; + +inline bool CConCommandHash::IsValidHandle( CCommandHashHandle_t hHash ) const +{ + return m_aDataPool.IsValidIndex(hHash); +} + + +inline CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const char *name ) const +{ + return Find( name, HashStringCaseless(name) ); +} + +inline ConCommandBase * &CConCommandHash::operator[]( CCommandHashHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +inline ConCommandBase *const &CConCommandHash::operator[]( CCommandHashHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the index of the first element +//----------------------------------------------------------------------------- +CConCommandHash::CCommandHashIterator_t CConCommandHash::First() const +{ + // walk through the buckets to find the first one that has some data + int bucketCount = m_aBuckets.Count(); + const CCommandHashHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + for ( int bucket = 0 ; bucket < bucketCount ; ++bucket ) + { + CCommandHashHandle_t iElement = m_aBuckets[bucket]; // get the head of the bucket + if ( iElement != invalidIndex ) + return CCommandHashIterator_t( bucket, iElement ); + } + + // if we are down here, the list is empty + return CCommandHashIterator_t( -1, invalidIndex ); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the next element after +// the param one. Or an invalid iterator. +//----------------------------------------------------------------------------- +CConCommandHash::CCommandHashIterator_t +CConCommandHash::Next( const CConCommandHash::CCommandHashIterator_t &iter ) const +{ + // look for the next entry in the current bucket + CCommandHashHandle_t next = m_aDataPool.Next(iter.handle); + const CCommandHashHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + if ( next != invalidIndex ) + { + // this bucket still has more elements in it + return CCommandHashIterator_t(iter.bucket, next); + } + + // otherwise look for the next bucket with data + int bucketCount = m_aBuckets.Count(); + for ( int bucket = iter.bucket+1 ; bucket < bucketCount ; ++bucket ) + { + CCommandHashHandle_t next = m_aBuckets[bucket]; // get the head of the bucket + if (next != invalidIndex) + return CCommandHashIterator_t( bucket, next ); + } + + // if we're here, there's no more data to be had + return CCommandHashIterator_t(-1, invalidIndex); +} + +bool CConCommandHash::IsValidIterator( const CCommandHashIterator_t &iter ) const +{ + return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) ); +} + +inline CConCommandHash::HashKey_t CConCommandHash::Hash( const ConCommandBase *cmd ) +{ + return HashStringCaseless( cmd->GetName() ); +} + +inline ConCommandBase * CConCommandHash::FindPtr( const char *name ) const +{ + CCommandHashHandle_t handle = Find(name); + if (handle == InvalidHandle()) + { + return NULL; + } + else + { + return (*this)[handle]; + } +} + +#endif diff --git a/external/vpc/vstdlib/cvar.cpp b/external/vpc/vstdlib/cvar.cpp new file mode 100644 index 0000000..060b674 --- /dev/null +++ b/external/vpc/vstdlib/cvar.cpp @@ -0,0 +1,1302 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include "vstdlib/cvar.h" +#include <ctype.h> +#include "tier0/icommandline.h" +#include "tier1/utlrbtree.h" +#include "tier1/strtools.h" +#include "tier1/keyvalues.h" +#include "tier1/convar.h" +#include "tier0/vprof.h" +#include "tier1/tier1.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlmap.h" +#include "tier1/fmtstr.h" + +#ifdef _X360 +#include "xbox/xbox_console.h" +#elif defined( _PS3 ) +#include "ps3/ps3_console.h" +#endif + +#ifdef POSIX +#include <wctype.h> +#include <wchar.h> + +#define VPROJ_INCREMENT_COUNTER(a, b) /* */ +#define VPROJ(a) /* */ +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Default implementation of CvarQuery +//----------------------------------------------------------------------------- +class CDefaultCvarQuery : public CBaseAppSystem< ICvarQuery > +{ +public: + virtual void *QueryInterface( const char *pInterfaceName ) + { + if ( !V_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) ) + return (ICvarQuery*)this; + return NULL; + + } + + virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent ) + { + return true; + } +}; + +static CDefaultCvarQuery s_DefaultCvarQuery; +static ICvarQuery *s_pCVarQuery = NULL; + +#include "concommandhash.h" + +//----------------------------------------------------------------------------- +// Default implementation +//----------------------------------------------------------------------------- +class CCvar : public CBaseAppSystem< ICvar > +{ +public: + CCvar(); + + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ); + virtual void Disconnect(); + virtual void *QueryInterface( const char *pInterfaceName ); + virtual InitReturnVal_t Init(); + virtual void Shutdown(); + + // Inherited from ICVar + virtual CVarDLLIdentifier_t AllocateDLLIdentifier(); + virtual void RegisterConCommand( ConCommandBase *pCommandBase ); + virtual void UnregisterConCommand( ConCommandBase *pCommandBase ); + virtual void UnregisterConCommands( CVarDLLIdentifier_t id ); + virtual const char* GetCommandLineValue( const char *pVariableName ); + virtual ConCommandBase *FindCommandBase( const char *name ); + virtual const ConCommandBase *FindCommandBase( const char *name ) const; + virtual ConVar *FindVar ( const char *var_name ); + virtual const ConVar *FindVar ( const char *var_name ) const; + virtual ConCommand *FindCommand( const char *name ); + virtual const ConCommand *FindCommand( const char *name ) const; + virtual void InstallGlobalChangeCallback( FnChangeCallback_t callback ); + virtual void RemoveGlobalChangeCallback( FnChangeCallback_t callback ); + virtual void CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue ); + virtual void InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ); + virtual void RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ); + virtual void ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const; + virtual void ConsolePrintf( const char *pFormat, ... ) const; + virtual void ConsoleDPrintf( const char *pFormat, ... ) const; + virtual void RevertFlaggedConVars( int nFlag ); + virtual void InstallCVarQuery( ICvarQuery *pQuery ); + +#if defined( USE_VXCONSOLE ) + virtual void PublishToVXConsole( ); +#endif + + virtual void SetMaxSplitScreenSlots( int nSlots ); + virtual int GetMaxSplitScreenSlots() const; + + virtual void AddSplitScreenConVars(); + virtual void RemoveSplitScreenConVars( CVarDLLIdentifier_t id ); + + virtual int GetConsoleDisplayFuncCount() const; + virtual void GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const; + virtual bool IsMaterialThreadSetAllowed( ) const; + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue ); + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, int nValue ); + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, float flValue ); + virtual bool HasQueuedMaterialThreadConVarSets() const; + virtual int ProcessQueuedMaterialThreadConVarSets(); + +private: + enum + { + CONSOLE_COLOR_PRINT = 0, + CONSOLE_PRINT, + CONSOLE_DPRINT, + }; + + void DisplayQueuedMessages( ); + + CUtlVector< FnChangeCallback_t > m_GlobalChangeCallbacks; + CUtlVector< IConsoleDisplayFunc* > m_DisplayFuncs; + int m_nNextDLLIdentifier; + + ConCommandBase *m_pConCommandList; + CConCommandHash m_CommandHash; + + // temporary console area so we can store prints before console display funs are installed + mutable CUtlBuffer m_TempConsoleBuffer; + int m_nMaxSplitScreenSlots; + +protected: + + // internals for ICVarIterator + class CCVarIteratorInternal : public ICVarIteratorInternal + { + public: + CCVarIteratorInternal( CCvar *outer ) + : m_pOuter( outer ), m_pHash( &outer->m_CommandHash ), // remember my CCvar, + m_hashIter( -1, -1 ) // and invalid iterator + {} + virtual void SetFirst( void ); + virtual void Next( void ); + virtual bool IsValid( void ); + virtual ConCommandBase *Get( void ); + protected: + CCvar * const m_pOuter; + CConCommandHash * const m_pHash; + CConCommandHash::CCommandHashIterator_t m_hashIter; + }; + + virtual ICVarIteratorInternal *FactoryInternalIterator( void ); + friend class CCVarIteratorInternal; + + enum ConVarSetType_t + { + CONVAR_SET_STRING = 0, + CONVAR_SET_INT, + CONVAR_SET_FLOAT, + }; + + struct QueuedConVarSet_t + { + ConVar *m_pConVar; + ConVarSetType_t m_nType; + int m_nInt; + float m_flFloat; + CUtlString m_String; + }; + + struct SplitScreenConVar_t + { + SplitScreenConVar_t() + { + Reset(); + } + + void Reset() + { + m_VarName = ""; + m_pVar = NULL; + } + + CUtlString m_VarName; + CSplitScreenAddedConVar *m_pVar; + }; + + struct SplitScreenAddedConVars_t + { + SplitScreenAddedConVars_t() + { + for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS - 1; ++i ) + { + m_Vars[ i ].Reset(); + } + } + + // Var names need "static" buffers... + SplitScreenConVar_t m_Vars[ MAX_SPLITSCREEN_CLIENTS - 1 ]; + }; + + CUtlMap< ConVar *, SplitScreenAddedConVars_t > m_SplitScreenAddedConVarsMap; + CUtlVector< QueuedConVarSet_t > m_QueuedConVarSets; + bool m_bMaterialSystemThreadSetAllowed; + +private: + // Standard console commands -- DO NOT PLACE ANY HIGHER THAN HERE BECAUSE THESE MUST BE THE FIRST TO DESTRUCT + CON_COMMAND_MEMBER_F( CCvar, "find", Find, "Find concommands with the specified string in their name/help text.", 0 ) +#ifdef _DEBUG + CON_COMMAND_MEMBER_F( CCvar, "ccvar_hash_report", HashReport, "report info on bucket distribution of internal hash.", 0 ) +#endif +}; + +void CCvar::CCVarIteratorInternal::SetFirst( void ) +{ + m_hashIter = m_pHash->First(); +} + +void CCvar::CCVarIteratorInternal::Next( void ) +{ + m_hashIter = m_pHash->Next( m_hashIter ); +} + +bool CCvar::CCVarIteratorInternal::IsValid( void ) +{ + return m_pHash->IsValidIterator( m_hashIter ); +} + +ConCommandBase *CCvar::CCVarIteratorInternal::Get( void ) +{ + Assert( IsValid( ) ); + return (*m_pHash)[m_hashIter]; +} + +ICvar::ICVarIteratorInternal *CCvar::FactoryInternalIterator( void ) +{ + return new CCVarIteratorInternal( this ); +} + +//----------------------------------------------------------------------------- +// Factor for CVars +//----------------------------------------------------------------------------- +static CCvar s_Cvar; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvar, ICvar, CVAR_INTERFACE_VERSION, s_Cvar ); + + +//----------------------------------------------------------------------------- +// Returns a CVar dictionary for tool usage +//----------------------------------------------------------------------------- +CreateInterfaceFn VStdLib_GetICVarFactory() +{ + return Sys_GetFactoryThis(); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CCvar::CCvar() : m_TempConsoleBuffer( 0, 1024 ), m_SplitScreenAddedConVarsMap( 0, 0, DefLessFunc( ConVar * ) ) +{ + m_nNextDLLIdentifier = 0; + m_pConCommandList = NULL; + m_nMaxSplitScreenSlots = 1; + m_bMaterialSystemThreadSetAllowed = false; + m_CommandHash.Init(); +} + +//----------------------------------------------------------------------------- +// Methods of IAppSystem +//----------------------------------------------------------------------------- +bool CCvar::Connect( CreateInterfaceFn factory ) +{ + ConnectTier1Libraries( &factory, 1 ); + + s_pCVarQuery = (ICvarQuery*)factory( CVAR_QUERY_INTERFACE_VERSION, NULL ); + if ( !s_pCVarQuery ) + { + s_pCVarQuery = &s_DefaultCvarQuery; + } + + ConVar_Register(); + return true; +} + +void CCvar::Disconnect() +{ + ConVar_Unregister(); + s_pCVarQuery = NULL; + DisconnectTier1Libraries(); + + Assert( m_SplitScreenAddedConVarsMap.Count() == 0 ); +} + +InitReturnVal_t CCvar::Init() +{ + return INIT_OK; +} + +void CCvar::Shutdown() +{ +} + +void *CCvar::QueryInterface( const char *pInterfaceName ) +{ + // We implement the ICvar interface + if ( !V_strcmp( pInterfaceName, CVAR_INTERFACE_VERSION ) ) + return (ICvar*)this; + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Method allowing the engine ICvarQuery interface to take over +//----------------------------------------------------------------------------- +void CCvar::InstallCVarQuery( ICvarQuery *pQuery ) +{ + Assert( s_pCVarQuery == &s_DefaultCvarQuery ); + s_pCVarQuery = pQuery ? pQuery : &s_DefaultCvarQuery; +} + + +//----------------------------------------------------------------------------- +// Used by DLLs to be able to unregister all their commands + convars +//----------------------------------------------------------------------------- +CVarDLLIdentifier_t CCvar::AllocateDLLIdentifier() +{ + return m_nNextDLLIdentifier++; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *variable - +//----------------------------------------------------------------------------- +void CCvar::RegisterConCommand( ConCommandBase *variable ) +{ + // Already registered + if ( variable->IsRegistered() ) + return; + + variable->m_bRegistered = true; + + const char *pName = variable->GetName(); + if ( !pName || !pName[0] ) + { + variable->m_pNext = NULL; + return; + } + + // If the variable is already defined, then setup the new variable as a proxy to it. + const ConCommandBase *pOther = FindCommandBase( variable->GetName() ); + if ( pOther ) + { + if ( variable->IsCommand() || pOther->IsCommand() ) + { + Warning( "WARNING: unable to link %s and %s because one or more is a ConCommand.\n", variable->GetName(), pOther->GetName() ); + } + else + { + // This cast is ok because we make sure they're ConVars above. + ConVar *pChildVar = const_cast< ConVar* >( static_cast< const ConVar* >( variable ) ); + ConVar *pParentVar = const_cast< ConVar* >( static_cast< const ConVar* >( pOther ) ); + + // See if it's a valid linkage + if ( s_pCVarQuery->AreConVarsLinkable( pChildVar, pParentVar ) ) + { + // Make sure the default values are the same (but only spew about this for FCVAR_REPLICATED) + if( pChildVar->m_pszDefaultValue && pParentVar->m_pszDefaultValue && + pChildVar->IsFlagSet( FCVAR_REPLICATED ) && pParentVar->IsFlagSet( FCVAR_REPLICATED ) ) + { + if( V_stricmp( pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue ) != 0 ) + { + Warning( "Parent and child ConVars with different default values! %s child: %s parent: %s (parent wins)\n", + variable->GetName(), pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue ); + } + } + + pChildVar->m_pParent = pParentVar->m_pParent; + + // Absorb material thread related convar flags + pParentVar->m_nFlags |= pChildVar->m_nFlags & ( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS ); + + // Transfer children's callbacks to parent + if ( pChildVar->m_fnChangeCallbacks.Count() ) + { + for ( int i = 0; i < pChildVar->m_fnChangeCallbacks.Count(); ++i ) + { + pParentVar->m_fnChangeCallbacks.AddToTail( pChildVar->m_fnChangeCallbacks[ i ] ); + } + // Wipe child callbacks + pChildVar->m_fnChangeCallbacks.RemoveAll(); + } + + // make sure we don't have conflicting help strings. + if ( pChildVar->m_pszHelpString && V_strlen( pChildVar->m_pszHelpString ) != 0 ) + { + if ( pParentVar->m_pszHelpString && V_strlen( pParentVar->m_pszHelpString ) != 0 ) + { + if ( V_stricmp( pParentVar->m_pszHelpString, pChildVar->m_pszHelpString ) != 0 ) + { + Warning( "Convar %s has multiple help strings:\n\tparent (wins): \"%s\"\n\tchild: \"%s\"\n", + variable->GetName(), pParentVar->m_pszHelpString, pChildVar->m_pszHelpString ); + } + } + else + { + pParentVar->m_pszHelpString = pChildVar->m_pszHelpString; + } + } + + // make sure we don't have conflicting FCVAR_*** flags. + static int const nFlags[] = + { FCVAR_CHEAT, FCVAR_REPLICATED, FCVAR_DONTRECORD, FCVAR_ARCHIVE, FCVAR_ARCHIVE_GAMECONSOLE }; + static char const * const szFlags[] = + { "FCVAR_CHEAT", "FCVAR_REPLICATED", "FCVAR_DONTRECORD", "FCVAR_ARCHIVE", "FCVAR_ARCHIVE_GAMECONSOLE" }; + + COMPILE_TIME_ASSERT( ARRAYSIZE( nFlags ) == ARRAYSIZE( szFlags ) ); + + for ( int k = 0; k < ARRAYSIZE( nFlags ); ++ k ) + { + if ( ( pChildVar->m_nFlags & nFlags[k] ) != ( pParentVar->m_nFlags & nFlags[k] ) ) + { + Warning( "Convar %s has conflicting %s flags (child: %s%s, parent: %s%s, parent wins)\n", + variable->GetName(), szFlags[k], + ( pChildVar->m_nFlags & nFlags[k] ) ? "has " : "no ", szFlags[k], + ( pParentVar->m_nFlags & nFlags[k] ) ? "has " : "no ", szFlags[k] ); + } + } + } + } + + variable->m_pNext = NULL; + return; + } + + // link the variable in + variable->m_pNext = m_pConCommandList; + m_pConCommandList = variable; + + AssertMsg1(FindCommandBase(variable->GetName()) == NULL, "Console command %s added twice!", + variable->GetName()); + m_CommandHash.Insert(variable); +} + +void CCvar::AddSplitScreenConVars() +{ + if ( m_nMaxSplitScreenSlots == 1 ) + return; + + for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext ) + { + if ( pCommand->IsCommand() ) + continue; + + ConVar *pConVar = static_cast< ConVar * >( pCommand ); + + if ( !pConVar->IsFlagSet( FCVAR_SS ) ) + continue; + + // See if it's already mapped in + int idx = m_SplitScreenAddedConVarsMap.Find( pConVar ); + if ( idx == m_SplitScreenAddedConVarsMap.InvalidIndex() ) + { + idx = m_SplitScreenAddedConVarsMap.Insert( pConVar ); + } + + SplitScreenAddedConVars_t &info = m_SplitScreenAddedConVarsMap[ idx ]; + for ( int i = 1 ; i < m_nMaxSplitScreenSlots; ++i ) + { + // Already registered it + if ( info.m_Vars[ i - 1 ].m_pVar ) + continue; + + // start at name2, etc. + info.m_Vars[ i - 1 ].m_VarName = CFmtStr( "%s%d", pConVar->GetName(), i + 1 ); + + CSplitScreenAddedConVar *pVar = new CSplitScreenAddedConVar( i, info.m_Vars[ i - 1 ].m_VarName.Get(), pConVar ); + info.m_Vars[ i - 1 ].m_pVar = pVar; + pVar->SetSplitScreenPlayerSlot( i ); + + RegisterConCommand( pVar ); + } + } + + ConCommandBase::s_pConCommandBases = NULL; +} + +void CCvar::RemoveSplitScreenConVars( CVarDLLIdentifier_t id ) + +{ + if ( m_nMaxSplitScreenSlots == 1 ) + { + Assert( m_SplitScreenAddedConVarsMap.Count() == 0 ); + return; + } + + CUtlVector< ConVar * > deleted; + + FOR_EACH_MAP( m_SplitScreenAddedConVarsMap, i ) + { + ConVar *key = m_SplitScreenAddedConVarsMap.Key( i ); + + if ( key->GetDLLIdentifier() != id ) + { + continue; + } + + SplitScreenAddedConVars_t &info = m_SplitScreenAddedConVarsMap[ i ]; + + + + for ( int i = 1 ; i < m_nMaxSplitScreenSlots; ++i ) + { + + if ( info.m_Vars[ i - 1 ].m_pVar ) + { + UnregisterConCommand( info.m_Vars[ i - 1 ].m_pVar ); + delete info.m_Vars[ i - 1 ].m_pVar; + info.m_Vars[ i - 1 ].m_pVar = NULL; + } + } + deleted.AddToTail( key ); + } + + for ( int i = 0; i < deleted.Count(); ++i ) + { + m_SplitScreenAddedConVarsMap.Remove( deleted[ i ] ); + } +} + +void CCvar::UnregisterConCommand( ConCommandBase *pCommandToRemove ) +{ + // Not registered? Don't bother + if ( !pCommandToRemove->IsRegistered() ) + return; + + pCommandToRemove->m_bRegistered = false; + + // FIXME: Should we make this a doubly-linked list? Would remove faster + ConCommandBase *pPrev = NULL; + for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext ) + { + if ( pCommand != pCommandToRemove ) + { + pPrev = pCommand; + continue; + } + + if ( pPrev == NULL ) + { + m_pConCommandList = pCommand->m_pNext; + } + else + { + pPrev->m_pNext = pCommand->m_pNext; + } + pCommand->m_pNext = NULL; + m_CommandHash.Remove(m_CommandHash.Find(pCommand)); + break; + } +} + +void CCvar::UnregisterConCommands( CVarDLLIdentifier_t id ) +{ + ConCommandBase *pNewList; + ConCommandBase *pCommand, *pNext; + + pNewList = NULL; + + m_CommandHash.Purge( true ); + pCommand = m_pConCommandList; + while ( pCommand ) + { + pNext = pCommand->m_pNext; + if ( pCommand->GetDLLIdentifier() != id ) + { + pCommand->m_pNext = pNewList; + pNewList = pCommand; + + m_CommandHash.Insert( pCommand ); + } + else + { + // Unlink + pCommand->m_bRegistered = false; + pCommand->m_pNext = NULL; + + } + + pCommand = pNext; + } + + m_pConCommandList = pNewList; +} + + +//----------------------------------------------------------------------------- +// Finds base commands +//----------------------------------------------------------------------------- +const ConCommandBase *CCvar::FindCommandBase( const char *name ) const +{ + VPROF_INCREMENT_COUNTER( "CCvar::FindCommandBase", 1 ); + VPROF_BUDGET( "CCvar::FindCommandBase", VPROF_BUDGETGROUP_CVAR_FIND ); + + return m_CommandHash.FindPtr( name ); +} + +ConCommandBase *CCvar::FindCommandBase( const char *name ) +{ + VPROF_INCREMENT_COUNTER( "CCvar::FindCommandBase", 1 ); + VPROF_BUDGET( "CCvar::FindCommandBase", VPROF_BUDGETGROUP_CVAR_FIND ); + + return m_CommandHash.FindPtr( name ); +} + + +//----------------------------------------------------------------------------- +// Purpose Finds ConVars +//----------------------------------------------------------------------------- +const ConVar *CCvar::FindVar( const char *var_name ) const +{ + const ConCommandBase *var = FindCommandBase( var_name ); + if ( !var ) + { + return NULL; + } + else + { + if (var->IsCommand()) + { + AssertMsg1( false , "Tried to look up command %s as if it were a variable.\n", var_name ); + return NULL; + } + } + + return static_cast<const ConVar*>(var); +} + +ConVar *CCvar::FindVar( const char *var_name ) +{ + ConCommandBase *var = FindCommandBase( var_name ); + if ( !var ) + { + return NULL; + } + else + { + if (var->IsCommand()) + { + AssertMsg1( false , "Tried to look up command %s as if it were a variable.\n", var_name ); + return NULL; + } + } + + return static_cast<ConVar*>( var ); +} + + +//----------------------------------------------------------------------------- +// Purpose Finds ConCommands +//----------------------------------------------------------------------------- +const ConCommand *CCvar::FindCommand( const char *pCommandName ) const +{ + const ConCommandBase *var = FindCommandBase( pCommandName ); + if ( !var || !var->IsCommand() ) + return NULL; + + return static_cast<const ConCommand*>(var); +} + +ConCommand *CCvar::FindCommand( const char *pCommandName ) +{ + ConCommandBase *var = FindCommandBase( pCommandName ); + if ( !var || !var->IsCommand() ) + return NULL; + + return static_cast<ConCommand*>( var ); +} + + +const char* CCvar::GetCommandLineValue( const char *pVariableName ) +{ + int nLen = V_strlen(pVariableName); + char *pSearch = (char*)stackalloc( nLen + 2 ); + pSearch[0] = '+'; + memcpy( &pSearch[1], pVariableName, nLen + 1 ); + return CommandLine()->ParmValue( pSearch ); +} + +//----------------------------------------------------------------------------- +// Install, remove global callbacks +//----------------------------------------------------------------------------- +void CCvar::InstallGlobalChangeCallback( FnChangeCallback_t callback ) +{ + Assert( callback && m_GlobalChangeCallbacks.Find( callback ) < 0 ); + m_GlobalChangeCallbacks.AddToTail( callback ); +} + +void CCvar::RemoveGlobalChangeCallback( FnChangeCallback_t callback ) +{ + Assert( callback ); + m_GlobalChangeCallbacks.FindAndRemove( callback ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCvar::CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue ) +{ + int nCallbackCount = m_GlobalChangeCallbacks.Count(); + for ( int i = 0; i < nCallbackCount; ++i ) + { + (*m_GlobalChangeCallbacks[i])( var, pOldString, flOldValue ); + } +} + + +//----------------------------------------------------------------------------- +// Sets convars containing the flags to their default value +//----------------------------------------------------------------------------- +void CCvar::RevertFlaggedConVars( int nFlag ) +{ + for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ; + m_CommandHash.IsValidIterator( i ) ; + i = m_CommandHash.Next( i ) ) + { + ConCommandBase *var = m_CommandHash[ i ]; + if ( var->IsCommand() ) + continue; + + ConVar *cvar = ( ConVar * )var; + + if ( !cvar->IsFlagSet( nFlag ) ) + continue; + + // It's == to the default value, don't count + if ( !V_stricmp( cvar->GetDefault(), cvar->GetString() ) ) + continue; + + cvar->Revert(); + // DevMsg( "%s = \"%s\" (reverted)\n", cvar->GetName(), cvar->GetString() ); + } +} + + +//----------------------------------------------------------------------------- +// Deal with queued material system convars +//----------------------------------------------------------------------------- +bool CCvar::IsMaterialThreadSetAllowed( ) const +{ + Assert( ThreadInMainThread() ); + return m_bMaterialSystemThreadSetAllowed; +} + +void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue ) +{ + Assert( ThreadInMainThread() ); + int j = m_QueuedConVarSets.AddToTail(); + m_QueuedConVarSets[j].m_pConVar = pConVar; + m_QueuedConVarSets[j].m_nType = CONVAR_SET_STRING; + m_QueuedConVarSets[j].m_String = pValue; +} + +void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, int nValue ) +{ + Assert( ThreadInMainThread() ); + int j = m_QueuedConVarSets.AddToTail(); + m_QueuedConVarSets[j].m_pConVar = pConVar; + m_QueuedConVarSets[j].m_nType = CONVAR_SET_INT; + m_QueuedConVarSets[j].m_nInt = nValue; +} + +void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, float flValue ) +{ + Assert( ThreadInMainThread() ); + int j = m_QueuedConVarSets.AddToTail(); + m_QueuedConVarSets[j].m_pConVar = pConVar; + m_QueuedConVarSets[j].m_nType = CONVAR_SET_FLOAT; + m_QueuedConVarSets[j].m_flFloat = flValue; +} + +bool CCvar::HasQueuedMaterialThreadConVarSets() const +{ + Assert( ThreadInMainThread() ); + return m_QueuedConVarSets.Count() > 0; +} + +int CCvar::ProcessQueuedMaterialThreadConVarSets() +{ + Assert( ThreadInMainThread() ); + m_bMaterialSystemThreadSetAllowed = true; + + int nUpdateFlags = 0; + int nCount = m_QueuedConVarSets.Count(); + for ( int i = 0; i < nCount; ++i ) + { + const QueuedConVarSet_t& set = m_QueuedConVarSets[i]; + switch( set.m_nType ) + { + case CONVAR_SET_FLOAT: + set.m_pConVar->SetValue( set.m_flFloat ); + break; + case CONVAR_SET_INT: + set.m_pConVar->SetValue( set.m_nInt ); + break; + case CONVAR_SET_STRING: + set.m_pConVar->SetValue( set.m_String ); + break; + } + + nUpdateFlags |= set.m_pConVar->GetFlags() & FCVAR_MATERIAL_THREAD_MASK; + } + + m_QueuedConVarSets.RemoveAll(); + m_bMaterialSystemThreadSetAllowed = false; + return nUpdateFlags; +} + + +//----------------------------------------------------------------------------- +// Display queued messages +//----------------------------------------------------------------------------- +void CCvar::DisplayQueuedMessages( ) +{ + // Display any queued up messages + if ( m_TempConsoleBuffer.TellPut() == 0 ) + return; + + Color clr; + int nStringLength; + while( m_TempConsoleBuffer.IsValid() ) + { + int nType = m_TempConsoleBuffer.GetChar(); + if ( nType == CONSOLE_COLOR_PRINT ) + { + clr.SetRawColor( m_TempConsoleBuffer.GetInt() ); + } + nStringLength = m_TempConsoleBuffer.PeekStringLength(); + char* pTemp = (char*)stackalloc( nStringLength + 1 ); + m_TempConsoleBuffer.GetString( pTemp ); + + switch( nType ) + { + case CONSOLE_COLOR_PRINT: + ConsoleColorPrintf( clr, pTemp ); + break; + + case CONSOLE_PRINT: + ConsolePrintf( pTemp ); + break; + + case CONSOLE_DPRINT: + ConsoleDPrintf( pTemp ); + break; + } + } + + m_TempConsoleBuffer.Purge(); +} + + +//----------------------------------------------------------------------------- +// Install a console printer +//----------------------------------------------------------------------------- +void CCvar::InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ) +{ + Assert( m_DisplayFuncs.Find( pDisplayFunc ) < 0 ); + m_DisplayFuncs.AddToTail( pDisplayFunc ); + DisplayQueuedMessages(); +} + +void CCvar::RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ) +{ + m_DisplayFuncs.FindAndRemove( pDisplayFunc ); +} + +int CCvar::GetConsoleDisplayFuncCount() const +{ + return m_DisplayFuncs.Count(); +} + +void CCvar::GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const +{ + m_DisplayFuncs[ nDisplayFuncIndex ]->GetConsoleText( pchText, bufSize ); +} + +void CCvar::ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const +{ + char temp[ 8192 ]; + va_list argptr; + va_start( argptr, pFormat ); + _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr ); + va_end( argptr ); + temp[ sizeof( temp ) - 1 ] = 0; + + int c = m_DisplayFuncs.Count(); + if ( c == 0 ) + { + m_TempConsoleBuffer.PutChar( CONSOLE_COLOR_PRINT ); + m_TempConsoleBuffer.PutInt( clr.GetRawColor() ); + m_TempConsoleBuffer.PutString( temp ); + return; + } + + for ( int i = 0 ; i < c; ++i ) + { + m_DisplayFuncs[ i ]->ColorPrint( clr, temp ); + } +} + +void CCvar::ConsolePrintf( const char *pFormat, ... ) const +{ + char temp[ 8192 ]; + va_list argptr; + va_start( argptr, pFormat ); + _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr ); + va_end( argptr ); + temp[ sizeof( temp ) - 1 ] = 0; + + int c = m_DisplayFuncs.Count(); + if ( c == 0 ) + { + m_TempConsoleBuffer.PutChar( CONSOLE_PRINT ); + m_TempConsoleBuffer.PutString( temp ); + return; + } + + for ( int i = 0 ; i < c; ++i ) + { + m_DisplayFuncs[ i ]->Print( temp ); + } +} + +void CCvar::ConsoleDPrintf( const char *pFormat, ... ) const +{ + char temp[ 8192 ]; + va_list argptr; + va_start( argptr, pFormat ); + _vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr ); + va_end( argptr ); + temp[ sizeof( temp ) - 1 ] = 0; + + int c = m_DisplayFuncs.Count(); + if ( c == 0 ) + { + m_TempConsoleBuffer.PutChar( CONSOLE_DPRINT ); + m_TempConsoleBuffer.PutString( temp ); + return; + } + + for ( int i = 0 ; i < c; ++i ) + { + m_DisplayFuncs[ i ]->DPrint( temp ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +#if defined( USE_VXCONSOLE ) +#ifdef _PS3 +/* +Here's a terrible hack. +In porting the part of the game that speaks to VXConsole, EA chose to +write it as a cluster of global functions, instead of a class interface like +Aaron did with IXboxConsole. Some of these globals need access to symbols +inside the engine, so they are defined there. However, CCvar is inside vstdlib. +In the EA build this didn't make a difference because everything was a huge +monolithic executable, and you could just access any symbol from anywhere. +In our build, with its PRXes, that doesn't fly. +So, the proper solution to this problem is to wrap all of the PS3 vxconsole +stuff in an interface, put it inside vstlib, create the dcim connection there, +and then export the interface pointer. The engine meanwhile would export the +symbols the vxlib needs, and then we give that interface class inside +vstlib a pointer to the engine once the engine is available. +Right now however I just want to get the thing working with as little modification +as possible so I can fix the vxconsole windows app itself and hopefully get +bidirectional TTY to our game. So, instead of the proper solution, +I'm just duct-taping everything together by simply passing a pointer to the engine +symbol this function needs whenever I call it. +Blech. I'll fix it later. +-egr 4/29/10. (is it later than September 2010? go call egr and make fun of him.) +*/ +void CCvar::PublishToVXConsole() +#else +void CCvar::PublishToVXConsole() +#endif +{ + const char *commands[6*1024]; + const char *helptext[6*1024]; + int numCommands = 0; + + // iterate and publish commands to the remote console + for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ; + m_CommandHash.IsValidIterator( i ) ; + i = m_CommandHash.Next( i ) ) + { + ConCommandBase *pCur = m_CommandHash[ i ]; + // add unregistered commands to list + if ( numCommands < sizeof(commands)/sizeof(commands[0]) ) + { + commands[numCommands] = pCur->GetName(); + helptext[numCommands] = pCur->GetHelpText(); + numCommands++; + } + } + + if ( numCommands ) + { +#ifdef _PS3 + g_pValvePS3Console->AddCommands( numCommands, commands, helptext ); +#else + XBX_rAddCommands( numCommands, commands, helptext ); +#endif + } +} + +#endif + + +static bool ConVarSortFunc( ConCommandBase * const &lhs, ConCommandBase * const &rhs ) +{ + return CaselessStringLessThan( lhs->GetName(), rhs->GetName() ); +} + +//----------------------------------------------------------------------------- +// Console commands +//----------------------------------------------------------------------------- +void CCvar::Find( const CCommand &args ) +{ + const char *search; + + if ( args.ArgC() != 2 ) + { + ConMsg( "Usage: find <string>\n" ); + return; + } + + // Get substring to find + search = args[1]; + + CUtlRBTree< ConCommandBase *, int > sorted( 0, 0, ConVarSortFunc ); + + // Loop through vars and print out findings + for ( CConCommandHash::CCommandHashIterator_t i = m_CommandHash.First() ; + m_CommandHash.IsValidIterator(i) ; + i = m_CommandHash.Next(i) ) + { + ConCommandBase *var = m_CommandHash[ i ]; + if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) ) + continue; + + if ( !V_stristr( var->GetName(), search ) && + !V_stristr( var->GetHelpText(), search ) ) + continue; + + sorted.Insert( var ); + } + + for ( int i = sorted.FirstInorder(); i != sorted.InvalidIndex(); i = sorted.NextInorder( i ) ) + { + ConVar_PrintDescription( sorted[ i ] ); + } +} + +#ifdef _DEBUG +void CCvar::HashReport( const CCommand &args ) +{ + m_CommandHash.Report(); +} +#endif + + +void CCvar::SetMaxSplitScreenSlots( int nSlots ) +{ + m_nMaxSplitScreenSlots = nSlots; + + AddSplitScreenConVars(); +} + +int CCvar::GetMaxSplitScreenSlots() const +{ + return m_nMaxSplitScreenSlots; +} + + + +//----------------------------------------------------------------------------- +// 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. + int iHashData = m_aDataPool.Alloc( true ); + HashEntry_t * 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 ) +{ + HashEntry_t * 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 +{ + // 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, + V_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 +{ + // 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 nonfatally 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 + AssertMsg1( 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()); + + AssertMsg1( 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 ) +{ + Msg("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 ); + } + + Msg( "%d: %d\n", iBucket, count ); + total += count; + } + + Msg("\tAverage: %.1f\n", total / ((float)(kNUM_BUCKETS))); +} +#endif diff --git a/external/vpc/vstdlib/keyvaluessystem.cpp b/external/vpc/vstdlib/keyvaluessystem.cpp new file mode 100644 index 0000000..44c8e6d --- /dev/null +++ b/external/vpc/vstdlib/keyvaluessystem.cpp @@ -0,0 +1,620 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <vstdlib/ikeyvaluessystem.h> +#include <keyvalues.h> +#include "tier1/mempool.h" +#include "utlsymbol.h" +#include "utlmap.h" +#include "tier0/threadtools.h" +#include "tier1/memstack.h" +#include "tier1/convar.h" + +#ifdef _PS3 +#include "ps3/ps3_core.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#ifdef NO_SBH // no need to pool if using tier0 small block heap +#define KEYVALUES_USE_POOL 1 +#endif + +// +// Defines platform-endian-specific macros: +// MEM_4BYTES_AS_0_AND_3BYTES : present a 4 byte uint32 as a memory +// layout where first memory byte is zero +// and the other 3 bytes represent value +// MEM_4BYTES_FROM_0_AND_3BYTES: unpack from memory with first zero byte +// and 3 value bytes the original uint32 value +// +// used for efficiently reading/writing storing 3 byte values into memory +// region immediately following a null-byte-terminated string, essentially +// sharing the null-byte-terminator with the first memory byte +// +#if defined( PLAT_LITTLE_ENDIAN ) +// Number in memory has lowest-byte in front, use shifts to make it zero +#define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) << 8 ) +#define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) >> 8 ) +#endif +#if defined( PLAT_BIG_ENDIAN ) +// Number in memory has highest-byte in front, use masking to make it zero +#define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) & 0x00FFFFFF ) +#define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) & 0x00FFFFFF ) +#endif + +//----------------------------------------------------------------------------- +// Purpose: Central storage point for KeyValues memory and symbols +//----------------------------------------------------------------------------- +class CKeyValuesSystem : public IKeyValuesSystem +{ +public: + CKeyValuesSystem(); + ~CKeyValuesSystem(); + + // registers the size of the KeyValues in the specified instance + // so it can build a properly sized memory pool for the KeyValues objects + // the sizes will usually never differ but this is for versioning safety + void RegisterSizeofKeyValues(int size); + + // allocates/frees a KeyValues object from the shared mempool + void *AllocKeyValuesMemory(int size); + void FreeKeyValuesMemory(void *pMem); + + // symbol table access (used for key names) + HKeySymbol GetSymbolForString( const char *name, bool bCreate ); + const char *GetStringForSymbol(HKeySymbol symbol); + + // returns the wide version of ansi, also does the lookup on #'d strings + void GetLocalizedFromANSI( const char *ansi, wchar_t *outBuf, int unicodeBufferSizeInBytes); + void GetANSIFromLocalized( const wchar_t *wchar, char *outBuf, int ansiBufferSizeInBytes ); + + // for debugging, adds KeyValues record into global list so we can track memory leaks + virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name); + virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem); + + // set/get a value for keyvalues resolution symbol + // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] + virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue ); + virtual bool GetKeyValuesExpressionSymbol( const char *name ); + + // symbol table access from code with case-preserving requirements (used for key names) + virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true ); + +private: +#ifdef KEYVALUES_USE_POOL + CUtlMemoryPool *m_pMemPool; +#endif + int m_iMaxKeyValuesSize; + + // string hash table + /* + Here's the way key values system data structures are laid out: + hash table with 2047 hash buckets: + [0] { hash_item_t } + [1] + [2] + ... + each hash_item_t's stringIndex is an offset in m_Strings memory + at that offset we store the actual null-terminated string followed + by another 3 bytes for an alternative capitalization. + These 3 trailing bytes are set to 0 if no alternative capitalization + variants are present in the dictionary. + These trailing 3 bytes are interpreted as stringIndex into m_Strings + memory for the next alternative capitalization + + Getting a string value by HKeySymbol : constant time access at the + string memory represented by stringIndex + + Getting a symbol for a string value: + 1) compute the hash + 2) start walking the hash-bucket using special version of stricmp + until a case insensitive match is found + 3a) for case-insensitive lookup return the found stringIndex + 3b) for case-sensitive lookup keep walking the list of alternative + capitalizations using strcmp until exact case match is found + */ + CMemoryStack m_Strings; + struct hash_item_t + { + int stringIndex; + hash_item_t *next; + }; + CUtlMemoryPool m_HashItemMemPool; + CUtlVector<hash_item_t> m_HashTable; + int CaseInsensitiveHash(const char *string, int iBounds); + + struct MemoryLeakTracker_t + { + int nameIndex; + void *pMem; + }; + static bool MemoryLeakTrackerLessFunc( const MemoryLeakTracker_t &lhs, const MemoryLeakTracker_t &rhs ) + { + return lhs.pMem < rhs.pMem; + } + CUtlRBTree<MemoryLeakTracker_t, int> m_KeyValuesTrackingList; + + CUtlMap< HKeySymbol, bool > m_KvConditionalSymbolTable; + + CThreadFastMutex m_mutex; +}; + +// EXPOSE_SINGLE_INTERFACE(CKeyValuesSystem, IKeyValuesSystem, KEYVALUES_INTERFACE_VERSION); + +//----------------------------------------------------------------------------- +// Instance singleton and expose interface to rest of code +//----------------------------------------------------------------------------- +static CKeyValuesSystem g_KeyValuesSystem; + +IKeyValuesSystem *KeyValuesSystem() +{ + return &g_KeyValuesSystem; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CKeyValuesSystem::CKeyValuesSystem() : + m_HashItemMemPool(sizeof(hash_item_t), 64, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool"), + m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc), + m_KvConditionalSymbolTable( DefLessFunc( HKeySymbol ) ) +{ + MEM_ALLOC_CREDIT(); + // initialize hash table + m_HashTable.AddMultipleToTail(2047); + for (int i = 0; i < m_HashTable.Count(); i++) + { + m_HashTable[i].stringIndex = 0; + m_HashTable[i].next = NULL; + } + + m_Strings.Init( "CKeyValuesSystem::m_Strings", 4*1024*1024, 64*1024, 0, 4 ); + // Make 0 stringIndex to never be returned, by allocating + // and wasting minimal number of alignment bytes now: + char *pszEmpty = ((char *)m_Strings.Alloc(1)); + *pszEmpty = 0; + +#ifdef KEYVALUES_USE_POOL + m_pMemPool = NULL; +#endif + m_iMaxKeyValuesSize = sizeof(KeyValues); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CKeyValuesSystem::~CKeyValuesSystem() +{ +#ifdef KEYVALUES_USE_POOL +#ifdef _DEBUG + // display any memory leaks + if (m_pMemPool && m_pMemPool->Count() > 0) + { + DevMsg("Leaked KeyValues blocks: %d\n", m_pMemPool->Count()); + } + + // iterate all the existing keyvalues displaying their names + for (int i = 0; i < m_KeyValuesTrackingList.MaxElement(); i++) + { + if (m_KeyValuesTrackingList.IsValidIndex(i)) + { + DevMsg("\tleaked KeyValues(%s)\n", &m_Strings[m_KeyValuesTrackingList[i].nameIndex]); + } + } +#endif + + delete m_pMemPool; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: registers the size of the KeyValues in the specified instance +// so it can build a properly sized memory pool for the KeyValues objects +// the sizes will usually never differ but this is for versioning safety +//----------------------------------------------------------------------------- +void CKeyValuesSystem::RegisterSizeofKeyValues(int size) +{ + if (size > m_iMaxKeyValuesSize) + { + m_iMaxKeyValuesSize = size; + } +} + +static void KVLeak( char const *fmt, ... ) +{ + va_list argptr; + char data[1024]; + + va_start(argptr, fmt); + V_vsnprintf(data, sizeof( data ), fmt, argptr); + va_end(argptr); + + Msg( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: allocates a KeyValues object from the shared mempool +//----------------------------------------------------------------------------- +void *CKeyValuesSystem::AllocKeyValuesMemory(int size) +{ +#ifdef KEYVALUES_USE_POOL + // allocate, if we don't have one yet + if (!m_pMemPool) + { + m_pMemPool = new CUtlMemoryPool(m_iMaxKeyValuesSize, 1024, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_pMemPool" ); + m_pMemPool->SetErrorReportFunc( KVLeak ); + } + + return m_pMemPool->Alloc(size); +#else + return malloc( size ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: frees a KeyValues object from the shared mempool +//----------------------------------------------------------------------------- +void CKeyValuesSystem::FreeKeyValuesMemory(void *pMem) +{ +#ifdef KEYVALUES_USE_POOL + m_pMemPool->Free(pMem); +#else + free( pMem ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: symbol table access (used for key names) +//----------------------------------------------------------------------------- +HKeySymbol CKeyValuesSystem::GetSymbolForString( const char *name, bool bCreate ) +{ + if ( !name ) + { + return (-1); + } + + AUTO_LOCK( m_mutex ); + MEM_ALLOC_CREDIT(); + + int hash = CaseInsensitiveHash(name, m_HashTable.Count()); + int i = 0; + hash_item_t *item = &m_HashTable[hash]; + while (1) + { + if (!stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex )) + { + return (HKeySymbol)item->stringIndex; + } + + i++; + + if (item->next == NULL) + { + if ( !bCreate ) + { + // not found + return -1; + } + + // we're not in the table + if (item->stringIndex != 0) + { + // first item is used, an new item + item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t)); + item = item->next; + } + + // build up the new item + item->next = NULL; + int numStringBytes = strlen(name); + char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + item->stringIndex = pString - (char *)m_Strings.GetBase(); + V_memcpy( pString, name, numStringBytes ); + * reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes + return (HKeySymbol)item->stringIndex; + } + + item = item->next; + } + + // shouldn't be able to get here + Assert(0); + return (-1); +} + +//----------------------------------------------------------------------------- +// Purpose: symbol table access (used for key names) +//----------------------------------------------------------------------------- +HKeySymbol CKeyValuesSystem::GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate ) +{ + if ( !name ) + { + return (-1); + } + + AUTO_LOCK( m_mutex ); + MEM_ALLOC_CREDIT(); + + int hash = CaseInsensitiveHash(name, m_HashTable.Count()); + int numNameStringBytes = -1; + int i = 0; + hash_item_t *item = &m_HashTable[hash]; + while (1) + { + char *pCompareString = (char *)m_Strings.GetBase() + item->stringIndex; + int iResult = _V_stricmp_NegativeForUnequal( name, pCompareString ); + if ( iResult == 0 ) + { + // strings are exactly equal matching every letter's case + hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex; + return (HKeySymbol)item->stringIndex; + } + else if ( iResult > 0 ) + { + // strings are equal in a case-insensitive compare, but have different case for some letters + // Need to walk the case-resolving chain + numNameStringBytes = V_strlen( pCompareString ); + uint32 *pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes ); + hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex; + while ( int nAlternativeStringIndex = MEM_4BYTES_FROM_0_AND_3BYTES( *pnCaseResolveIndex ) ) + { + pCompareString = (char *)m_Strings.GetBase() + nAlternativeStringIndex; + int iResult = strcmp( name, pCompareString ); + if ( !iResult ) + { + // found an exact match + return (HKeySymbol)nAlternativeStringIndex; + } + // Keep traversing alternative case-resolving chain + pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes ); + } + // Reached the end of alternative case-resolving chain, pnCaseResolveIndex is pointing at 0 bytes + // indicating no further alternative stringIndex + if ( !bCreate ) + { + // If we aren't interested in creating the actual string index, + // then return symbol with default capitalization + // NOTE: this is not correct value, but it cannot be used to create a new value anyway, + // only for locating a pre-existing value and lookups are case-insensitive + return (HKeySymbol)item->stringIndex; + } + else + { + char *pString = (char *)m_Strings.Alloc( numNameStringBytes + 1 + 3 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + int nNewAlternativeStringIndex = pString - (char *)m_Strings.GetBase(); + V_memcpy( pString, name, numNameStringBytes ); + * reinterpret_cast< uint32 * >( pString + numNameStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes + *pnCaseResolveIndex = MEM_4BYTES_AS_0_AND_3BYTES( nNewAlternativeStringIndex ); // link previous spelling entry to the new entry + return (HKeySymbol)nNewAlternativeStringIndex; + } + } + + i++; + + if (item->next == NULL) + { + if ( !bCreate ) + { + // not found + return -1; + } + + // we're not in the table + if (item->stringIndex != 0) + { + // first item is used, an new item + item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t)); + item = item->next; + } + + // build up the new item + item->next = NULL; + int numStringBytes = strlen(name); + char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + item->stringIndex = pString - (char *)m_Strings.GetBase(); + V_memcpy( pString, name, numStringBytes ); + * reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes + hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex; + return (HKeySymbol)item->stringIndex; + } + + item = item->next; + } + + // shouldn't be able to get here + Assert(0); + return (-1); +} + +//----------------------------------------------------------------------------- +// Purpose: symbol table access +//----------------------------------------------------------------------------- +const char *CKeyValuesSystem::GetStringForSymbol(HKeySymbol symbol) +{ + if ( symbol == -1 ) + { + return ""; + } + return ((char *)m_Strings.GetBase() + (size_t)symbol); +} + +//----------------------------------------------------------------------------- +// Purpose: adds KeyValues record into global list so we can track memory leaks +//----------------------------------------------------------------------------- +void CKeyValuesSystem::AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name) +{ +#ifdef _DEBUG + // only track the memory leaks in debug builds + MemoryLeakTracker_t item = { name, pMem }; + m_KeyValuesTrackingList.Insert(item); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: used to track memory leaks +//----------------------------------------------------------------------------- +void CKeyValuesSystem::RemoveKeyValuesFromMemoryLeakList(void *pMem) +{ +#ifdef _DEBUG + // only track the memory leaks in debug builds + MemoryLeakTracker_t item = { 0, pMem }; + int index = m_KeyValuesTrackingList.Find(item); + m_KeyValuesTrackingList.RemoveAt(index); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: generates a simple hash value for a string +//----------------------------------------------------------------------------- +int CKeyValuesSystem::CaseInsensitiveHash(const char *string, int iBounds) +{ + unsigned int hash = 0; + + for ( ; *string != 0; string++ ) + { + if (*string >= 'A' && *string <= 'Z') + { + hash = (hash << 1) + (*string - 'A' + 'a'); + } + else + { + hash = (hash << 1) + *string; + } + } + + return hash % iBounds; +} + +//----------------------------------------------------------------------------- +// Purpose: set/get a value for keyvalues resolution symbol +// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] +//----------------------------------------------------------------------------- +void CKeyValuesSystem::SetKeyValuesExpressionSymbol( const char *name, bool bValue ) +{ + if ( !name ) + return; + + if ( name[0] == '$' ) + ++ name; + + HKeySymbol hSym = GetSymbolForString( name, true ); // find or create symbol + + { + AUTO_LOCK( m_mutex ); + m_KvConditionalSymbolTable.InsertOrReplace( hSym, bValue ); + } +} + +bool CKeyValuesSystem::GetKeyValuesExpressionSymbol( const char *name ) +{ + if ( !name ) + return false; + + if ( name[0] == '$' ) + ++ name; + + HKeySymbol hSym = GetSymbolForString( name, false ); // find or create symbol + if ( hSym != -1 ) + { + AUTO_LOCK( m_mutex ); + CUtlMap< HKeySymbol, bool >::IndexType_t idx = m_KvConditionalSymbolTable.Find( hSym ); + if ( idx != m_KvConditionalSymbolTable.InvalidIndex() ) + { + // Found the symbol value in conditional symbol table + return m_KvConditionalSymbolTable.Element( idx ); + } + } + + // + // Fallback conditionals + // + + if ( !V_stricmp( name, "GAMECONSOLESPLITSCREEN" ) ) + { +#if defined( _GAMECONSOLE ) + return ( XBX_GetNumGameUsers() > 1 ); +#else + return false; +#endif + } + + if ( !V_stricmp( name, "GAMECONSOLEGUEST" ) ) + { +#if defined( _GAMECONSOLE ) + return ( XBX_GetPrimaryUserIsGuest() != 0 ); +#else + return false; +#endif + } + + if ( !V_stricmp( name, "ENGLISH" ) || + !V_stricmp( name, "JAPANESE" ) || + !V_stricmp( name, "GERMAN" ) || + !V_stricmp( name, "FRENCH" ) || + !V_stricmp( name, "SPANISH" ) || + !V_stricmp( name, "ITALIAN" ) || + !V_stricmp( name, "KOREAN" ) || + !V_stricmp( name, "TCHINESE" ) || + !V_stricmp( name, "PORTUGUESE" ) || + !V_stricmp( name, "SCHINESE" ) || + !V_stricmp( name, "POLISH" ) || + !V_stricmp( name, "RUSSIAN" ) || + !V_stricmp( name, "TURKISH" ) ) + { + // the language symbols are true if we are in that language + // english is assumed when no language is present + const char *pLanguageString; +#ifdef _GAMECONSOLE + pLanguageString = XBX_GetLanguageString(); +#else + static ConVarRef cl_language( "cl_language" ); + pLanguageString = cl_language.GetString(); +#endif + if ( !pLanguageString || !pLanguageString[0] ) + { + pLanguageString = "english"; + } + if ( !V_stricmp( name, pLanguageString ) ) + { + return true; + } + else + { + return false; + } + } + + // very expensive, back door for DLC updates + if ( !V_strnicmp( name, "CVAR_", 5 ) ) + { + ConVarRef cvRef( name + 5 ); + if ( cvRef.IsValid() ) + return cvRef.GetBool(); + } + + // purposely warn on these to prevent syntax errors + // need to get these fixed asap, otherwise unintended false behavior + Warning( "KV Conditional: Unknown symbol %s\n", name ); + return false; +} diff --git a/external/vpc/vstdlib/random.cpp b/external/vpc/vstdlib/random.cpp new file mode 100644 index 0000000..eebb3e0 --- /dev/null +++ b/external/vpc/vstdlib/random.cpp @@ -0,0 +1,256 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Random number generator +// +// $Workfile: $ +// $NoKeywords: $ +//===========================================================================// + + +#include "vstdlib/random.h" +#include <math.h> +#include "dbg.h" + +#include "tier0/memdbgon.h" + +#define IA 16807 +#define IM 2147483647 +#define IQ 127773 +#define IR 2836 +#define NDIV (1+(IM-1)/NTAB) +#define MAX_RANDOM_RANGE 0x7FFFFFFFUL + +// fran1 -- return a random floating-point number on the interval [0,1) +// +#define AM (1.0/IM) +#define EPS 1.2e-7 +#define RNMX (1.0-EPS) + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- +static CUniformRandomStream s_UniformStream; +static CGaussianRandomStream s_GaussianStream; +static IUniformRandomStream *s_pUniformStream = &s_UniformStream; + + +//----------------------------------------------------------------------------- +// Installs a global random number generator, which will affect the Random functions above +//----------------------------------------------------------------------------- +void InstallUniformRandomStream( IUniformRandomStream *pStream ) +{ + s_pUniformStream = pStream ? pStream : &s_UniformStream; +} + + +//----------------------------------------------------------------------------- +// A couple of convenience functions to access the library's global uniform stream +//----------------------------------------------------------------------------- +void RandomSeed( int iSeed ) +{ + s_pUniformStream->SetSeed( iSeed ); +} + +float RandomFloat( float flMinVal, float flMaxVal ) +{ + return s_pUniformStream->RandomFloat( flMinVal, flMaxVal ); +} + +float RandomFloatExp( float flMinVal, float flMaxVal, float flExponent ) +{ + return s_pUniformStream->RandomFloatExp( flMinVal, flMaxVal, flExponent ); +} + +int RandomInt( int iMinVal, int iMaxVal ) +{ + return s_pUniformStream->RandomInt( iMinVal, iMaxVal ); +} + +float RandomGaussianFloat( float flMean, float flStdDev ) +{ + return s_GaussianStream.RandomFloat( flMean, flStdDev ); +} + + +//----------------------------------------------------------------------------- +// +// Implementation of the uniform random number stream +// +//----------------------------------------------------------------------------- +CUniformRandomStream::CUniformRandomStream() +{ + SetSeed(0); +} + +void CUniformRandomStream::SetSeed( int iSeed ) +{ + AUTO_LOCK( m_mutex ); + m_idum = ( ( iSeed < 0 ) ? iSeed : -iSeed ); + m_iy = 0; +} + +int CUniformRandomStream::GenerateRandomNumber() +{ + AUTO_LOCK( m_mutex ); + int j; + int k; + + if (m_idum <= 0 || !m_iy) + { + if (-(m_idum) < 1) + m_idum=1; + else + m_idum = -(m_idum); + + for ( j=NTAB+7; j>=0; j--) + { + k = (m_idum)/IQ; + m_idum = IA*(m_idum-k*IQ)-IR*k; + if (m_idum < 0) + m_idum += IM; + if (j < NTAB) + m_iv[j] = m_idum; + } + m_iy=m_iv[0]; + } + k=(m_idum)/IQ; + m_idum=IA*(m_idum-k*IQ)-IR*k; + if (m_idum < 0) + m_idum += IM; + j=m_iy/NDIV; + + // We're seeing some strange memory corruption in the contents of s_pUniformStream. + // Perhaps it's being caused by something writing past the end of this array? + // Bounds-check in release to see if that's the case. + if (j >= NTAB || j < 0) + { + DebuggerBreakIfDebugging(); + Warning("CUniformRandomStream had an array overrun: tried to write to element %d of 0..31. Contact Tom or Elan.\n", j); + j = ( j % NTAB ) & 0x7fffffff; + } + + m_iy=m_iv[j]; + m_iv[j] = m_idum; + + return m_iy; +} + +float CUniformRandomStream::RandomFloat( float flLow, float flHigh ) +{ + // float in [0,1) + float fl = AM * GenerateRandomNumber(); + if (fl > RNMX) + { + fl = RNMX; + } + return (fl * ( flHigh - flLow ) ) + flLow; // float in [low,high) +} + +float CUniformRandomStream::RandomFloatExp( float flMinVal, float flMaxVal, float flExponent ) +{ + // float in [0,1) + float fl = AM * GenerateRandomNumber(); + if (fl > RNMX) + { + fl = RNMX; + } + if ( flExponent != 1.0f ) + { + fl = powf( fl, flExponent ); + } + return (fl * ( flMaxVal - flMinVal ) ) + flMinVal; // float in [low,high) +} + +int CUniformRandomStream::RandomInt( int iLow, int iHigh ) +{ + //ASSERT(lLow <= lHigh); + unsigned int maxAcceptable; + unsigned int x = iHigh-iLow+1; + unsigned int n; + if (x <= 1 || MAX_RANDOM_RANGE < x-1) + { + return iLow; + } + + // The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE] + // to a smaller, client-specified range of [0,x-1] in a way that doesn't bias + // the uniform distribution unfavorably. Even for a worst case x, the loop is + // guaranteed to be taken no more than half the time, so for that worst case x, + // the average number of times through the loop is 2. For cases where x is + // much smaller than MAX_RANDOM_RANGE, the average number of times through the + // loop is very close to 1. + // + maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x ); + do + { + n = GenerateRandomNumber(); + } while (n > maxAcceptable); + + return iLow + (n % x); +} + + +//----------------------------------------------------------------------------- +// +// Implementation of the gaussian random number stream +// We're gonna use the Box-Muller method (which actually generates 2 +// gaussian-distributed numbers at once) +// +//----------------------------------------------------------------------------- +CGaussianRandomStream::CGaussianRandomStream( IUniformRandomStream *pUniformStream ) +{ + AttachToStream( pUniformStream ); +} + + +//----------------------------------------------------------------------------- +// Attaches to a random uniform stream +//----------------------------------------------------------------------------- +void CGaussianRandomStream::AttachToStream( IUniformRandomStream *pUniformStream ) +{ + AUTO_LOCK( m_mutex ); + m_pUniformStream = pUniformStream; + m_bHaveValue = false; +} + + +//----------------------------------------------------------------------------- +// Generates random numbers +//----------------------------------------------------------------------------- +float CGaussianRandomStream::RandomFloat( float flMean, float flStdDev ) +{ + AUTO_LOCK( m_mutex ); + IUniformRandomStream *pUniformStream = m_pUniformStream ? m_pUniformStream : s_pUniformStream; + float fac,rsq,v1,v2; + + if (!m_bHaveValue) + { + // Pick 2 random #s from -1 to 1 + // Make sure they lie inside the unit circle. If they don't, try again + do + { + v1 = 2.0f * pUniformStream->RandomFloat() - 1.0f; + v2 = 2.0f * pUniformStream->RandomFloat() - 1.0f; + rsq = v1*v1 + v2*v2; + } while ((rsq > 1.0f) || (rsq == 0.0f)); + + // The box-muller transformation to get the two gaussian numbers + fac = sqrtf( -2.0f * log(rsq) / rsq ); + + // Store off one value for later use + m_flRandomValue = v1 * fac; + m_bHaveValue = true; + + return flStdDev * (v2 * fac) + flMean; + } + else + { + m_bHaveValue = false; + return flStdDev * m_flRandomValue + flMean; + } +} + + +//----------------------------------------------------------------------------- +// Creates a histogram (for testing) +//----------------------------------------------------------------------------- diff --git a/external/vpc/vstdlib/vstrtools.cpp b/external/vpc/vstdlib/vstrtools.cpp new file mode 100644 index 0000000..7c4a5e9 --- /dev/null +++ b/external/vpc/vstdlib/vstrtools.cpp @@ -0,0 +1,316 @@ +#include "tier0/dbg.h" +#include "vstdlib/vstrtools.h" + +#if defined( _WIN32 ) && !defined( _X360 ) +#include <windows.h> +#endif +#if defined(POSIX) && !defined(_PS3) +#include <iconv.h> +#endif + +#ifdef _PS3 +#include <cell/sysmodule.h> +#include <cell/l10n.h> + +class DummyInitL10N +{ +public: + DummyInitL10N() + { + int ret = cellSysmoduleLoadModule( CELL_SYSMODULE_L10N ); + if( ret != CELL_OK ) + { + Warning( "Cannot initialize l10n, unicode services will not work. Error %d\n", ret ); + } + } + + ~DummyInitL10N() + { + cellSysmoduleUnloadModule( CELL_SYSMODULE_L10N ); + } +}s_dummyInitL10N; +#endif + +//----------------------------------------------------------------------------- +// Purpose: Converts a UTF8 string into a unicode string +//----------------------------------------------------------------------------- +int V_UTF8ToUnicode( const char *pUTF8, wchar_t *pwchDest, int cubDestSizeInBytes ) +{ + if ( !pUTF8 ) + return 0; + + AssertValidStringPtr(pUTF8); + AssertValidWritePtr(pwchDest); + + pwchDest[0] = 0; +#ifdef _WIN32 + int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pwchDest, cubDestSizeInBytes / sizeof(wchar_t) ); +#elif defined( _PS3 ) + size_t cchResult = cubDestSizeInBytes / sizeof( uint16 ), cchSrc = V_strlen( pUTF8 ) + 1; + L10nResult result = UTF8stoUCS2s( ( const uint8 *) pUTF8, &cchSrc, ( uint16 * ) pwchDest, &cchResult ); + Assert( result == ConversionOK ); + cchResult *= sizeof( uint16 ); +#elif POSIX + iconv_t conv_t = iconv_open( "UTF-32LE", "UTF-8" ); + int cchResult = -1; + size_t nLenUnicde = cubDestSizeInBytes; + size_t nMaxUTF8 = strlen(pUTF8) + 1; + char *pIn = (char *)pUTF8; + char *pOut = (char *)pwchDest; + if ( conv_t > 0 ) + { + cchResult = 0; + cchResult = iconv( conv_t, &pIn, &nMaxUTF8, &pOut, &nLenUnicde ); + iconv_close( conv_t ); + if ( (int)cchResult < 0 ) + cchResult = 0; + else + cchResult = nMaxUTF8; + } +#endif + pwchDest[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0; + return cchResult; +} + +//----------------------------------------------------------------------------- +// Purpose: Converts a unicode string into a UTF8 (standard) string +//----------------------------------------------------------------------------- +int V_UnicodeToUTF8( const wchar_t *pUnicode, char *pUTF8, int cubDestSizeInBytes ) +{ + AssertValidStringPtr(pUTF8, cubDestSizeInBytes); + AssertValidReadPtr(pUnicode); + + if ( cubDestSizeInBytes > 0 ) + { + pUTF8[0] = 0; + } + +#ifdef _WIN32 + int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUnicode, -1, pUTF8, cubDestSizeInBytes, NULL, NULL ); +#elif defined( _PS3 ) + size_t cchResult = cubDestSizeInBytes, cchSrc = V_wcslen( pUnicode ) + 1; + L10nResult result = UCS2stoUTF8s( ( const uint16 *) pUnicode, &cchSrc, ( uint8 * ) pUTF8, &cchResult ); + Assert( result == ConversionOK ); +#elif POSIX + int cchResult = 0; + if ( pUnicode && pUTF8 ) + { + iconv_t conv_t = iconv_open( "UTF-8", "UTF-32LE" ); + int cchResult = -1; + size_t nLenUnicde = ( wcslen(pUnicode) + 1 ) * sizeof(wchar_t); // 4 bytes per wchar vs. 1 byte for utf8 for simple english + size_t nMaxUTF8 = cubDestSizeInBytes; + char *pIn = (char *)pUnicode; + char *pOut = (char *)pUTF8; + if ( conv_t > 0 ) + { + cchResult = 0; + cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); + iconv_close( conv_t ); + if ( (int)cchResult < 0 ) + cchResult = 0; + else + cchResult = nMaxUTF8; + } + } +#endif + + if ( cubDestSizeInBytes > 0 ) + { + pUTF8[cubDestSizeInBytes - 1] = 0; + } + + return cchResult; +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts a ucs2 string to a unicode (wchar_t) one, no-op on win32 +//----------------------------------------------------------------------------- +int V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInBytes ) +{ + AssertValidWritePtr(pUnicode); + AssertValidReadPtr(pUCS2); + + pUnicode[0] = 0; +#if defined( _WIN32 ) || defined( _PS3 ) + int lenUCS2 = V_wcslen( pUCS2 ); + int cchResult = MIN( (lenUCS2+1)*( int )sizeof(ucs2), cubDestSizeInBytes ); + V_wcsncpy( (wchar_t*)pUCS2, pUnicode, cchResult ); +#else + iconv_t conv_t = iconv_open( "UCS-4LE", "UCS-2LE" ); + int cchResult = -1; + size_t nLenUnicde = cubDestSizeInBytes; + size_t nMaxUTF8 = cubDestSizeInBytes; + char *pIn = (char *)pUCS2; + char *pOut = (char *)pUnicode; + if ( conv_t > 0 ) + { + cchResult = 0; + cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); + iconv_close( conv_t ); + if ( (int)cchResult < 0 ) + cchResult = 0; + else + cchResult = nMaxUTF8; + } +#endif + pUnicode[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0; + return cchResult; + +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts a wchar_t string into a UCS2 string -noop on windows +//----------------------------------------------------------------------------- +int V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, int cubDestSizeInBytes ) +{ + // TODO: MACMERGE: Figure out how to convert from 2-byte Win32 wchars to platform wchar_t type that can be 4 bytes +#if defined( _WIN32 ) || defined( _PS3 ) + int cchResult = MIN( cubSrcInBytes, cubDestSizeInBytes ); + V_wcsncpy( (wchar_t*)pUCS2, pUnicode, cchResult ); +#elif defined (POSIX) + iconv_t conv_t = iconv_open( "UCS-2LE", "UTF-32LE" ); + size_t cchResult = -1; + size_t nLenUnicde = cubSrcInBytes; + size_t nMaxUCS2 = cubDestSizeInBytes; + char *pIn = (char*)pUnicode; + char *pOut = pUCS2; + if ( conv_t > 0 ) + { + cchResult = 0; + cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUCS2 ); + iconv_close( conv_t ); + if ( (int)cchResult < 0 ) + cchResult = 0; + else + cchResult = cubSrcInBytes / sizeof( wchar_t ); + } +#endif + return cchResult; +} + +// UTF-8 encodes each character (code point) in 1 to 4 octets (8-bit bytes). +// The first 128 characters of the Unicode character set (which correspond directly to the ASCII) use a single octet with the same binary value as in ASCII. +// url:http://en.wikipedia.org/wiki/UTF-8 +#define MAX_UTF8_CHARACTER_BYTES 4 + + +//----------------------------------------------------------------------------- +// Purpose: Converts a ucs-2 (windows wchar_t) string into a UTF8 (standard) string +//----------------------------------------------------------------------------- +VSTRTOOLS_INTERFACE int V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes ) +{ + AssertValidStringPtr(pUTF8, cubDestSizeInBytes); + AssertValidReadPtr(pUCS2); + Assert( cubDestSizeInBytes >= 1 ); // must have at least 1 byte to write the terminator character + + pUTF8[0] = '\0'; +#ifdef _WIN32 + // under win32 wchar_t == ucs2, sigh + int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUCS2, -1, pUTF8, cubDestSizeInBytes, NULL, NULL ); +#elif defined( _PS3 ) + size_t cchResult = cubDestSizeInBytes, cchSrc = V_wcslen( pUCS2 ) + 1; + L10nResult result = UCS2stoUTF8s( ( const uint16 *) pUCS2, &cchSrc, ( uint8 * ) pUTF8, &cchResult ); + Assert( result == ConversionOK ); +#elif defined(POSIX) + iconv_t conv_t = iconv_open( "UTF-8", "UCS-2LE" ); + size_t cchResult = -1; + size_t nLenUnicde = cubDestSizeInBytes; + size_t nMaxUTF8 = cubDestSizeInBytes; + char *pIn = (char *)pUCS2; + char *pOut = (char *)pUTF8; + if ( conv_t > 0 ) + { + cchResult = 0; + cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); + iconv_close( conv_t ); + if ( (int)cchResult < 0 ) + cchResult = 0; + else + cchResult = nMaxUTF8; + } +#endif + pUTF8[cubDestSizeInBytes - 1] = '\0'; + return cchResult; +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts a UTF8 to ucs-2 (windows wchar_t) +//----------------------------------------------------------------------------- +VSTRTOOLS_INTERFACE int V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDestSizeInBytes ) +{ + AssertValidStringPtr(pUTF8, cubDestSizeInBytes); + AssertValidReadPtr(pUCS2); + + pUCS2[0] = 0; +#ifdef _WIN32 + // under win32 wchar_t == ucs2, sigh + int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pUCS2, cubDestSizeInBytes / sizeof(wchar_t) ); +#elif defined( _PS3 ) + size_t cchResult = cubDestSizeInBytes / sizeof( uint16 ), cchSrc = cubSrcInBytes; + L10nResult result = UTF8stoUCS2s( ( const uint8 *) pUTF8, &cchSrc, ( uint16 * ) pUCS2, &cchResult ); + Assert( result == ConversionOK ); + cchResult *= sizeof( uint16 ); +#elif defined(POSIX) + iconv_t conv_t = iconv_open( "UCS-2LE", "UTF-8" ); + size_t cchResult = -1; + size_t nLenUnicde = cubSrcInBytes; + size_t nMaxUTF8 = cubDestSizeInBytes; + char *pIn = (char *)pUTF8; + char *pOut = (char *)pUCS2; + if ( conv_t > 0 ) + { + cchResult = 0; + cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 ); + iconv_close( conv_t ); + if ( (int)cchResult < 0 ) + cchResult = 0; + else + cchResult = cubSrcInBytes; + + } +#endif + pUCS2[ (cubDestSizeInBytes/sizeof(ucs2)) - 1] = 0; + return cchResult; +} + +//----------------------------------------------------------------------------- +// Purpose: copies at most nMaxBytes of the UTF-8 input data into the destination, +// ensuring that a trailing multi-byte sequence isn't truncated. +//----------------------------------------------------------------------------- +VSTRTOOLS_INTERFACE void * V_UTF8_strncpy( char *pDest, const char *pSrc, size_t nMaxBytes ) +{ + strncpy( pDest, pSrc, nMaxBytes ); + + // http://en.wikipedia.org/wiki/UTF-8 + int end = nMaxBytes-1; + pDest[end] = 0; + + int nBytesSeen = 0, nBytesExpected = 0; + // walk backwards, ignoring nulls + while ( pDest[end] == 0 ) + --end; + + // found a non-null - see if it's part of a multi-byte sequence + while ( ( pDest[end] & 0x80 ) && !( pDest[end] & 0x40 ) ) + { + nBytesSeen++; + --end; + } + + if ( ( pDest[end] & 0xC0 ) == 0xC0 ) + { + for ( int i = 6; i > 1; --i ) + { + if ( (char)( pDest[end] >> i ) & 0x1 ) + ++nBytesExpected; + } + } + + if ( nBytesExpected != nBytesSeen ) + pDest[end] = 0; + + return pDest; +}
\ No newline at end of file |