This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+212
View File
@@ -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
+1302
View File
File diff suppressed because it is too large Load Diff
+620
View File
@@ -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;
}
+256
View File
@@ -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)
//-----------------------------------------------------------------------------
+316
View File
@@ -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;
}