summaryrefslogtreecommitdiff
path: root/external/vpc/vstdlib/keyvaluessystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'external/vpc/vstdlib/keyvaluessystem.cpp')
-rw-r--r--external/vpc/vstdlib/keyvaluessystem.cpp620
1 files changed, 620 insertions, 0 deletions
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;
+}