mirror of
https://github.com/celisej567/source-engine.git
synced 2026-01-05 22:09:59 +03:00
1
This commit is contained in:
620
external/vpc/vstdlib/keyvaluessystem.cpp
vendored
Normal file
620
external/vpc/vstdlib/keyvaluessystem.cpp
vendored
Normal file
@@ -0,0 +1,620 @@
|
||||
//========= Copyright <20> 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;
|
||||
}
|
||||
Reference in New Issue
Block a user