summaryrefslogtreecommitdiff
path: root/external/vpc/vstdlib
diff options
context:
space:
mode:
Diffstat (limited to 'external/vpc/vstdlib')
-rw-r--r--external/vpc/vstdlib/concommandhash.h212
-rw-r--r--external/vpc/vstdlib/cvar.cpp1302
-rw-r--r--external/vpc/vstdlib/keyvaluessystem.cpp620
-rw-r--r--external/vpc/vstdlib/random.cpp256
-rw-r--r--external/vpc/vstdlib/vstrtools.cpp316
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