diff --git a/lib/linux64/tier1.a b/lib/linux64/tier1.a new file mode 100644 index 00000000..9f18416f Binary files /dev/null and b/lib/linux64/tier1.a differ diff --git a/lib/public/win64/tier1.lib b/lib/public/win64/tier1.lib new file mode 100644 index 00000000..3b8803e0 Binary files /dev/null and b/lib/public/win64/tier1.lib differ diff --git a/tier1/KeyValues.cpp b/tier1/KeyValues.cpp new file mode 100644 index 00000000..4ac50926 --- /dev/null +++ b/tier1/KeyValues.cpp @@ -0,0 +1,2521 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if defined( _WIN32 ) && !defined( _X360 ) +#include // for WideCharToMultiByte and MultiByteToWideChar +#elif defined( _LINUX ) || defined( __APPLE__ ) +#include // wcslen() +#define _alloca alloca +#endif + +#include +#include "filesystem.h" +#include + +#include +#include +#include "tier0/dbg.h" +#include "tier0/mem.h" +#include "utlvector.h" +#include "utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +static const char * s_LastFileLoadingFrom = "unknown"; // just needed for error messages + +#define KEYVALUES_TOKEN_SIZE 1024 +static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; + + +#define INTERNALWRITE( pData, len ) InternalWrite( filesystem, f, pBuf, pData, len ) + + +// a simple class to keep track of a stack of valid parsed symbols +const int MAX_ERROR_STACK = 64; +class CKeyValuesErrorStack +{ +public: + CKeyValuesErrorStack() : m_pFilename("NULL"), m_errorIndex(0), m_maxErrorIndex(0) {} + + void SetFilename( const char *pFilename ) + { + m_pFilename = pFilename; + m_maxErrorIndex = 0; + } + + // entering a new keyvalues block, save state for errors + // Not save symbols instead of pointers because the pointers can move! + int Push(HKeySymbol symName) + { + if ( m_errorIndex < MAX_ERROR_STACK ) + { + m_errorStack[m_errorIndex] = symName; + } + m_errorIndex++; + m_maxErrorIndex = MAX( m_maxErrorIndex, (m_errorIndex-1) ); + return m_errorIndex-1; + } + + // exiting block, error isn't in this block, remove. + void Pop() + { + m_errorIndex--; + Assert(m_errorIndex>=0); + } + + // Allows you to keep the same stack level, but change the name as you parse peers + void Reset(int stackLevel, HKeySymbol symName) + { + Assert( stackLevel >= 0 && stackLevel < m_errorIndex ); + m_errorStack[stackLevel] = symName; + } + + // Hit an error, report it and the parsing stack for context + void ReportError( const char *pError ) + { + Warning( "KeyValues Error: %s in file %s\n", pError, m_pFilename ); + for ( int i = 0; i < m_maxErrorIndex; i++ ) + { + if ( m_errorStack[i] != INVALID_KEY_SYMBOL ) + { + if ( i < m_errorIndex ) + { + Warning( "%s, ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) ); + } + else + { + Warning( "(*%s*), ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) ); + } + } + } + Warning( "\n" ); + } + +private: + HKeySymbol m_errorStack[MAX_ERROR_STACK]; + const char *m_pFilename; + int m_errorIndex; + int m_maxErrorIndex; +} g_KeyValuesErrorStack; + + +// a simple helper that creates stack entries as it goes in & out of scope +class CKeyErrorContext +{ +public: + CKeyErrorContext( KeyValues *pKv ) + { + Init( pKv->GetNameSymbol() ); + } + + ~CKeyErrorContext() + { + g_KeyValuesErrorStack.Pop(); + } + CKeyErrorContext( HKeySymbol symName ) + { + Init( symName ); + } + void Reset( HKeySymbol symName ) + { + g_KeyValuesErrorStack.Reset( m_stackLevel, symName ); + } +private: + void Init( HKeySymbol symName ) + { + m_stackLevel = g_KeyValuesErrorStack.Push( symName ); + } + + int m_stackLevel; +}; + +// Uncomment this line to hit the ~CLeakTrack assert to see what's looking like it's leaking +// #define LEAKTRACK + +#ifdef LEAKTRACK + +class CLeakTrack +{ +public: + CLeakTrack() + { + } + ~CLeakTrack() + { + if ( keys.Count() != 0 ) + { + Assert( 0 ); + } + } + + struct kve + { + KeyValues *kv; + char name[ 256 ]; + }; + + void AddKv( KeyValues *kv, char const *name ) + { + kve k; + Q_strncpy( k.name, name ? name : "NULL", sizeof( k.name ) ); + k.kv = kv; + + keys.AddToTail( k ); + } + + void RemoveKv( KeyValues *kv ) + { + int c = keys.Count(); + for ( int i = 0; i < c; i++ ) + { + if ( keys[i].kv == kv ) + { + keys.Remove( i ); + break; + } + } + } + + CUtlVector< kve > keys; +}; + +static CLeakTrack track; + +#define TRACK_KV_ADD( ptr, name ) track.AddKv( ptr, name ) +#define TRACK_KV_REMOVE( ptr ) track.RemoveKv( ptr ) + +#else + +#define TRACK_KV_ADD( ptr, name ) +#define TRACK_KV_REMOVE( ptr ) + +#endif + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +KeyValues::KeyValues( const char *setName ) +{ + TRACK_KV_ADD( this, setName ); + + Init(); + SetName ( setName ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +KeyValues::KeyValues( const char *setName, const char *firstKey, const char *firstValue ) +{ + TRACK_KV_ADD( this, setName ); + + Init(); + SetName( setName ); + SetString( firstKey, firstValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +KeyValues::KeyValues( const char *setName, const char *firstKey, const wchar_t *firstValue ) +{ + TRACK_KV_ADD( this, setName ); + + Init(); + SetName( setName ); + SetWString( firstKey, firstValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +KeyValues::KeyValues( const char *setName, const char *firstKey, int firstValue ) +{ + TRACK_KV_ADD( this, setName ); + + Init(); + SetName( setName ); + SetInt( firstKey, firstValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +KeyValues::KeyValues( const char *setName, const char *firstKey, const char *firstValue, const char *secondKey, const char *secondValue ) +{ + TRACK_KV_ADD( this, setName ); + + Init(); + SetName( setName ); + SetString( firstKey, firstValue ); + SetString( secondKey, secondValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +KeyValues::KeyValues( const char *setName, const char *firstKey, int firstValue, const char *secondKey, int secondValue ) +{ + TRACK_KV_ADD( this, setName ); + + Init(); + SetName( setName ); + SetInt( firstKey, firstValue ); + SetInt( secondKey, secondValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize member variables +//----------------------------------------------------------------------------- +void KeyValues::Init() +{ + m_iKeyName = HKeySymbol::MakeHandle(0); + m_iDataType = TYPE_NONE; + + m_pSub = NULL; + m_pPeer = NULL; + m_pChain = NULL; + + m_sValue = NULL; + m_wsValue = NULL; + m_pValue = NULL; + + m_bHasEscapeSequences = false; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +KeyValues::~KeyValues() +{ + TRACK_KV_REMOVE( this ); + + RemoveEverything(); +} + +//----------------------------------------------------------------------------- +// Purpose: remove everything +//----------------------------------------------------------------------------- +void KeyValues::RemoveEverything() +{ + KeyValues *dat; + KeyValues *datNext = NULL; + for ( dat = m_pSub; dat != NULL; dat = datNext ) + { + datNext = dat->m_pPeer; + dat->m_pPeer = NULL; + delete dat; + } + + for ( dat = m_pPeer; dat && dat != this; dat = datNext ) + { + datNext = dat->m_pPeer; + dat->m_pPeer = NULL; + delete dat; + } + + delete [] m_sValue; + m_sValue = NULL; + delete [] m_wsValue; + m_wsValue = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *f - +//----------------------------------------------------------------------------- + +void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel ) +{ + RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel ); +} + +//----------------------------------------------------------------------------- +// Adds a chain... if we don't find stuff in this keyvalue, we'll look +// in the one we're chained to. +//----------------------------------------------------------------------------- + +void KeyValues::ChainKeyValue( KeyValues* pChain ) +{ + m_pChain = pChain; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the name of the current key section +//----------------------------------------------------------------------------- +const char *KeyValues::GetName( void ) const +{ + return KeyValuesSystem()->GetStringForSymbol(m_iKeyName); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the symbol name of the current key section +//----------------------------------------------------------------------------- +HKeySymbol KeyValues::GetNameSymbol() const +{ + return m_iKeyName; +} + + +//----------------------------------------------------------------------------- +// Purpose: Read a single token from buffer (0 terminated) +//----------------------------------------------------------------------------- +#ifdef _MSC_VER +#pragma warning (disable:4706) +#endif +const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasConditional ) +{ + wasQuoted = false; + wasConditional = false; + + if ( !buf.IsValid() ) + return NULL; + + // eating white spaces and remarks loop + while ( true ) + { + buf.EatWhiteSpace(); + if ( !buf.IsValid() ) + return NULL; // file ends after reading whitespaces + + // stop if it's not a comment; a new token starts here + if ( !buf.EatCPPComment() ) + break; + } + + const char *c = (const char*)buf.PeekGet( sizeof(char), 0 ); + if ( !c ) + return NULL; + + // read quoted strings specially + if ( *c == '\"' ) + { + wasQuoted = true; + buf.GetDelimitedString( m_bHasEscapeSequences ? GetCStringCharConversion() : GetNoEscCharConversion(), + s_pTokenBuf, KEYVALUES_TOKEN_SIZE ); + return s_pTokenBuf; + } + + if ( *c == '{' || *c == '}' ) + { + // it's a control char, just add this one char and stop reading + s_pTokenBuf[0] = *c; + s_pTokenBuf[1] = 0; + buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 1 ); + return s_pTokenBuf; + } + + // read in the token until we hit a whitespace or a control character + bool bReportedError = false; + bool bConditionalStart = false; + int nCount = 0; + while ( (c = (const char*)buf.PeekGet( sizeof(char), 0 )) ) + { + // end of file + if ( *c == 0 ) + break; + + // break if any control character appears in non quoted tokens + if ( *c == '"' || *c == '{' || *c == '}' ) + break; + + if ( *c == '[' ) + bConditionalStart = true; + + if ( *c == ']' && bConditionalStart ) + { + wasConditional = true; + } + + // break on whitespace + if ( isspace(*c) ) + break; + + if (nCount < (KEYVALUES_TOKEN_SIZE-1) ) + { + s_pTokenBuf[nCount++] = *c; // add char to buffer + } + else if ( !bReportedError ) + { + bReportedError = true; + g_KeyValuesErrorStack.ReportError(" ReadToken overflow" ); + } + + buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 1 ); + } + s_pTokenBuf[ nCount ] = 0; + return s_pTokenBuf; +} +#ifdef _MSC_VER +#pragma warning (default:4706) +#endif + + +//----------------------------------------------------------------------------- +// Purpose: if parser should translate escape sequences ( /n, /t etc), set to true +//----------------------------------------------------------------------------- +void KeyValues::UsesEscapeSequences(bool state) +{ + m_bHasEscapeSequences = state; +} + + +//----------------------------------------------------------------------------- +// Purpose: Load keyValues from disk +//----------------------------------------------------------------------------- +bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + Assert(filesystem); +#ifndef _LINUX + Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); +#endif + + FileHandle_t f = filesystem->Open(resourceName, "rb", pathID); + if ( !f ) + return false; + + s_LastFileLoadingFrom = (char*)resourceName; + + // load file into a null-terminated buffer + int fileSize = filesystem->Size( f ); + unsigned bufSize = ((IFileSystem *)filesystem)->GetOptimalReadSize( f, fileSize + 1 ); + + char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize ); + Assert( buffer ); + + // read into local buffer + bool bRetOK = ( ((IFileSystem *)filesystem)->ReadEx( buffer, bufSize, fileSize, f ) != 0 ); + + filesystem->Close( f ); // close file after reading + + if ( bRetOK ) + { + buffer[fileSize] = 0; // null terminate file as EOF + bRetOK = LoadFromBuffer( resourceName, buffer, filesystem, pathID, pfnEvaluateSymbolProc); + } + + ((IFileSystem *)filesystem)->FreeOptimalReadBuffer( buffer ); + + return bRetOK; +} + +//----------------------------------------------------------------------------- +// Purpose: Save the keyvalues to disk +// Creates the path to the file if it doesn't exist +//----------------------------------------------------------------------------- +bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID ) +{ + // create a write file + FileHandle_t f = filesystem->Open(resourceName, "wb", pathID); + + if ( f == FILESYSTEM_INVALID_HANDLE ) + { + DevMsg(1, "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n", + resourceName?resourceName:"NULL", pathID?pathID:"NULL" ); + return false; + } + + RecursiveSaveToFile(filesystem, f, NULL, 0); + filesystem->Close(f); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Write out a set of indenting +//----------------------------------------------------------------------------- +void KeyValues::WriteIndents( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel ) +{ + for ( int i = 0; i < indentLevel; i++ ) + { + INTERNALWRITE( "\t", 1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Write out a string where we convert the double quotes to backslash double quote +//----------------------------------------------------------------------------- +void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const char *pszString ) +{ + // handle double quote chars within the string + // the worst possible case is that the whole string is quotes + int len = Q_strlen(pszString); + char *convertedString = (char *) _alloca ((len + 1) * sizeof(char) * 2); + int j=0; + for (int i=0; i <= len; i++) + { + if (pszString[i] == '\"') + { + convertedString[j] = '\\'; + j++; + } + else if ( m_bHasEscapeSequences && pszString[i] == '\\' ) + { + convertedString[j] = '\\'; + j++; + } + convertedString[j] = pszString[i]; + j++; + } + + INTERNALWRITE(convertedString, strlen(convertedString)); +} + + +void KeyValues::InternalWrite( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const void *pData, int len ) +{ + if ( filesystem ) + { + filesystem->Write( pData, len, f ); + } + + if ( pBuf ) + { + pBuf->Put( pData, len ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Save keyvalues from disk, if subkey values are detected, calls +// itself to save those +//----------------------------------------------------------------------------- +void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel ) +{ + // write header + WriteIndents( filesystem, f, pBuf, indentLevel ); + INTERNALWRITE("\"", 1); + WriteConvertedString(filesystem, f, pBuf, GetName()); + INTERNALWRITE("\"\n", 2); + WriteIndents( filesystem, f, pBuf, indentLevel ); + INTERNALWRITE("{\n", 2); + + // loop through all our keys writing them to disk + for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer ) + { + if ( dat->m_pSub ) + { + dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1 ); + } + else + { + // only write non-empty keys + + switch (dat->m_iDataType) + { + case TYPE_STRING: + { + if (dat->m_sValue && *(dat->m_sValue)) + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + WriteConvertedString(filesystem, f, pBuf, dat->GetName()); + INTERNALWRITE("\"\t\t\"", 4); + + WriteConvertedString(filesystem, f, pBuf, dat->m_sValue); + + INTERNALWRITE("\"\n", 2); + } + break; + } + case TYPE_WSTRING: + { +#ifdef _WIN32 + if ( dat->m_wsValue ) + { + static char buf[KEYVALUES_TOKEN_SIZE]; + // make sure we have enough space + Assert(::WideCharToMultiByte(CP_UTF8, 0, dat->m_wsValue, -1, NULL, 0, NULL, NULL) < KEYVALUES_TOKEN_SIZE); + int result = ::WideCharToMultiByte(CP_UTF8, 0, dat->m_wsValue, -1, buf, KEYVALUES_TOKEN_SIZE, NULL, NULL); + if (result) + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + WriteConvertedString(filesystem, f, pBuf, buf); + + INTERNALWRITE("\"\n", 2); + } + } +#endif + break; + } + + case TYPE_INT: + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + char buf[32]; + Q_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue); + + INTERNALWRITE(buf, Q_strlen(buf)); + INTERNALWRITE("\"\n", 2); + break; + } + + case TYPE_UINT64: + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + char buf[32]; + // write "0x" + 16 char 0-padded hex encoded 64 bit value + Q_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) ); + + INTERNALWRITE(buf, Q_strlen(buf)); + INTERNALWRITE("\"\n", 2); + break; + } + + case TYPE_FLOAT: + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + char buf[48]; + Q_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue); + + INTERNALWRITE(buf, Q_strlen(buf)); + INTERNALWRITE("\"\n", 2); + break; + } + case TYPE_COLOR: + DevMsg(1, "KeyValues::RecursiveSaveToFile: TODO, missing code for TYPE_COLOR.\n"); + break; + + default: + break; + } + } + } + + // write tail + WriteIndents(filesystem, f, pBuf, indentLevel); + INTERNALWRITE("}\n", 2); +} + +//----------------------------------------------------------------------------- +// Purpose: looks up a key by symbol name +//----------------------------------------------------------------------------- +KeyValues *KeyValues::FindKey(HKeySymbol keySymbol) const +{ + for (KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer) + { + if (dat->m_iKeyName == keySymbol) + return dat; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Find a keyValue, create it if it is not found. +// Set bCreate to true to create the key if it doesn't already exist +// (which ensures a valid pointer will be returned) +//----------------------------------------------------------------------------- +KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) +{ + // return the current key if a NULL subkey is asked for + if (!keyName || !keyName[0]) + return this; + + // look for '/' characters deliminating sub fields + char szBuf[256]; + const char *subStr = strchr(keyName, '/'); + const char *searchStr = keyName; + + // pull out the substring if it exists + if (subStr) + { + int size = subStr - keyName; + Q_memcpy( szBuf, keyName, size ); + szBuf[size] = 0; + searchStr = szBuf; + } + + // lookup the symbol for the search string + HKeySymbol iSearchStr = KeyValuesSystem()->GetSymbolForString( searchStr, bCreate ); + if ( iSearchStr == INVALID_KEY_SYMBOL ) + { + // not found, couldn't possibly be in key value list + return NULL; + } + + KeyValues *lastItem = NULL; + KeyValues *dat; + // find the searchStr in the current peer list + for (dat = m_pSub; dat != NULL; dat = dat->m_pPeer) + { + lastItem = dat; // record the last item looked at (for if we need to append to the end of the list) + + // symbol compare + if (dat->m_iKeyName == iSearchStr) + { + break; + } + } + + if ( !dat && m_pChain ) + { + dat = m_pChain->FindKey(keyName, false); + } + + // make sure a key was found + if (!dat) + { + if (bCreate) + { + // we need to create a new key + dat = new KeyValues( searchStr ); +// Assert(dat != NULL); + + // insert new key at end of list + if (lastItem) + { + lastItem->m_pPeer = dat; + } + else + { + m_pSub = dat; + } + dat->m_pPeer = NULL; + + // a key graduates to be a submsg as soon as it's m_pSub is set + // this should be the only place m_pSub is set + m_iDataType = TYPE_NONE; + } + else + { + return NULL; + } + } + + // if we've still got a subStr we need to keep looking deeper in the tree + if ( subStr ) + { + // recursively chain down through the paths in the string + return dat->FindKey(subStr + 1, bCreate); + } + + return dat; +} + +//----------------------------------------------------------------------------- +// Purpose: Create a new key, with an autogenerated name. +// Name is guaranteed to be an integer, of value 1 higher than the highest +// other integer key name +//----------------------------------------------------------------------------- +KeyValues *KeyValues::CreateNewKey() +{ + int newID = 1; + + // search for any key with higher values + for (KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer) + { + // case-insensitive string compare + int val = atoi(dat->GetName()); + if (newID <= val) + { + newID = val + 1; + } + } + + char buf[12]; + Q_snprintf( buf, sizeof(buf), "%d", newID ); + + return CreateKey( buf ); +} + + +//----------------------------------------------------------------------------- +// Create a key +//----------------------------------------------------------------------------- +KeyValues* KeyValues::CreateKey( const char *keyName ) +{ + // key wasn't found so just create a new one + KeyValues* dat = new KeyValues( keyName ); + + dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does + + // add into subkey list + AddSubKey( dat ); + + return dat; +} + + +//----------------------------------------------------------------------------- +// Adds a subkey. Make sure the subkey isn't a child of some other keyvalues +//----------------------------------------------------------------------------- +void KeyValues::AddSubKey( KeyValues *pSubkey ) +{ + // Make sure the subkey isn't a child of some other keyvalues + Assert( pSubkey->m_pPeer == NULL ); + + // add into subkey list + if ( m_pSub == NULL ) + { + m_pSub = pSubkey; + } + else + { + KeyValues *pTempDat = m_pSub; + while ( pTempDat->GetNextKey() != NULL ) + { + pTempDat = pTempDat->GetNextKey(); + } + + pTempDat->SetNextKey( pSubkey ); + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: Remove a subkey from the list +//----------------------------------------------------------------------------- +void KeyValues::RemoveSubKey(KeyValues *subKey) +{ + if (!subKey) + return; + + // check the list pointer + if (m_pSub == subKey) + { + m_pSub = subKey->m_pPeer; + } + else + { + // look through the list + KeyValues *kv = m_pSub; + while (kv->m_pPeer) + { + if (kv->m_pPeer == subKey) + { + kv->m_pPeer = subKey->m_pPeer; + break; + } + + kv = kv->m_pPeer; + } + } + + subKey->m_pPeer = NULL; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Return the first subkey in the list +//----------------------------------------------------------------------------- +KeyValues *KeyValues::GetFirstSubKey() +{ + return m_pSub; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the next subkey +//----------------------------------------------------------------------------- +KeyValues *KeyValues::GetNextKey() +{ + return m_pPeer; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets this key's peer to the KeyValues passed in +//----------------------------------------------------------------------------- +void KeyValues::SetNextKey( KeyValues *pDat ) +{ + m_pPeer = pDat; +} + + +KeyValues* KeyValues::GetFirstTrueSubKey() +{ + KeyValues *pRet = m_pSub; + while ( pRet && pRet->m_iDataType != TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + +KeyValues* KeyValues::GetNextTrueSubKey() +{ + KeyValues *pRet = m_pPeer; + while ( pRet && pRet->m_iDataType != TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + +KeyValues* KeyValues::GetFirstValue() +{ + KeyValues *pRet = m_pSub; + while ( pRet && pRet->m_iDataType == TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + +KeyValues* KeyValues::GetNextValue() +{ + KeyValues *pRet = m_pPeer; + while ( pRet && pRet->m_iDataType == TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + + +//----------------------------------------------------------------------------- +// Purpose: Get the integer value of a keyName. Default value is returned +// if the keyName can't be found. +//----------------------------------------------------------------------------- +int KeyValues::GetInt( const char *keyName, int defaultValue ) +{ + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + switch ( dat->m_iDataType ) + { + case TYPE_STRING: + return atoi(dat->m_sValue); + case TYPE_WSTRING: +#ifdef _WIN32 + return _wtoi(dat->m_wsValue); +#else + DevMsg( "TODO: implement _wtoi\n"); + return 0; +#endif + case TYPE_FLOAT: + return (int)dat->m_flValue; + case TYPE_UINT64: + // can't convert, since it would lose data + Assert(0); + return 0; + case TYPE_INT: + case TYPE_PTR: + default: + return dat->m_iValue; + }; + } + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the integer value of a keyName. Default value is returned +// if the keyName can't be found. +//----------------------------------------------------------------------------- +uint64 KeyValues::GetUint64( const char *keyName, uint64 defaultValue ) +{ + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + switch ( dat->m_iDataType ) + { + case TYPE_STRING: + return atoi(dat->m_sValue); + case TYPE_WSTRING: +#ifdef _WIN32 + return _wtoi(dat->m_wsValue); +#else + AssertFatal( 0 ); + return 0; +#endif + case TYPE_FLOAT: + return (int)dat->m_flValue; + case TYPE_UINT64: + return *((uint64 *)dat->m_sValue); + case TYPE_INT: + case TYPE_PTR: + default: + return dat->m_iValue; + }; + } + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the pointer value of a keyName. Default value is returned +// if the keyName can't be found. +//----------------------------------------------------------------------------- +void *KeyValues::GetPtr( const char *keyName, void *defaultValue ) +{ + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + switch ( dat->m_iDataType ) + { + case TYPE_PTR: + return dat->m_pValue; + + case TYPE_WSTRING: + case TYPE_STRING: + case TYPE_FLOAT: + case TYPE_INT: + case TYPE_UINT64: + default: + return NULL; + }; + } + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the float value of a keyName. Default value is returned +// if the keyName can't be found. +//----------------------------------------------------------------------------- +float KeyValues::GetFloat( const char *keyName, float defaultValue ) +{ + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + switch ( dat->m_iDataType ) + { + case TYPE_STRING: + return (float)atof(dat->m_sValue); + case TYPE_WSTRING: +#ifdef _WIN32 + return (float) _wtof(dat->m_wsValue); // no wtof +#else + Assert(0); + return 0.; +#endif + case TYPE_FLOAT: + return dat->m_flValue; + case TYPE_INT: + return (float)dat->m_iValue; + case TYPE_UINT64: + return (float)(*((uint64 *)dat->m_sValue)); + case TYPE_PTR: + default: + return 0.0f; + }; + } + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the string pointer of a keyName. Default value is returned +// if the keyName can't be found. +//----------------------------------------------------------------------------- +const char *KeyValues::GetString( const char *keyName, const char *defaultValue ) +{ + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + // convert the data to string form then return it + char buf[64]; + switch ( dat->m_iDataType ) + { + case TYPE_FLOAT: + Q_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue ); + SetString( keyName, buf ); + break; + case TYPE_INT: + case TYPE_PTR: + Q_snprintf( buf, sizeof( buf ), "%d", dat->m_iValue ); + SetString( keyName, buf ); + break; + case TYPE_UINT64: + Q_snprintf( buf, sizeof( buf ), "%llu", *((uint64 *)(dat->m_sValue)) ); + SetString( keyName, buf ); + break; + + case TYPE_WSTRING: + { +#ifdef _WIN32 + // convert the string to char *, set it for future use, and return it + char wideBuf[512]; + int result = ::WideCharToMultiByte(CP_UTF8, 0, dat->m_wsValue, -1, wideBuf, 512, NULL, NULL); + if ( result ) + { + // note: this will copy wideBuf + SetString( keyName, wideBuf ); + } + else + { + return defaultValue; + } +#endif + break; + } + case TYPE_STRING: + break; + default: + return defaultValue; + }; + + return dat->m_sValue; + } + return defaultValue; +} + +const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaultValue) +{ +#ifdef _WIN32 + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + wchar_t wbuf[64]; + switch ( dat->m_iDataType ) + { + case TYPE_FLOAT: + swprintf(wbuf, L"%f", dat->m_flValue); + SetWString( keyName, wbuf); + break; + case TYPE_INT: + case TYPE_PTR: + swprintf( wbuf, L"%d", dat->m_iValue ); + SetWString( keyName, wbuf ); + break; + case TYPE_UINT64: + { + swprintf( wbuf, L"%I64i", *((uint64 *)(dat->m_sValue)) ); + SetWString( keyName, wbuf ); + } + break; + + case TYPE_WSTRING: + break; + case TYPE_STRING: + { + static wchar_t wbuftemp[512]; // convert to wide + int result = ::MultiByteToWideChar(CP_UTF8, 0, dat->m_sValue, -1, wbuftemp, 512); + if ( result ) + { + SetWString( keyName, wbuftemp); + } + else + { + return defaultValue; + } + break; + } + default: + return defaultValue; + }; + + return (const wchar_t* )dat->m_wsValue; + } +#else + DevMsg("TODO: implement wide char functions\n"); +#endif + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets a color +//----------------------------------------------------------------------------- +Color KeyValues::GetColor( const char *keyName, const Color &defaultColor ) +{ + Color color = defaultColor; + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + if ( dat->m_iDataType == TYPE_COLOR ) + { + color[0] = dat->m_Color[0]; + color[1] = dat->m_Color[1]; + color[2] = dat->m_Color[2]; + color[3] = dat->m_Color[3]; + } + else if ( dat->m_iDataType == TYPE_FLOAT ) + { + color[0] = (unsigned char)dat->m_flValue; + } + else if ( dat->m_iDataType == TYPE_INT ) + { + color[0] = dat->m_iValue; + } + else if ( dat->m_iDataType == TYPE_STRING ) + { + // parse the colors out of the string + float a, b, c, d; + sscanf(dat->m_sValue, "%f %f %f %f", &a, &b, &c, &d); + color[0] = (unsigned char)a; + color[1] = (unsigned char)b; + color[2] = (unsigned char)c; + color[3] = (unsigned char)d; + } + } + return color; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a color +//----------------------------------------------------------------------------- +void KeyValues::SetColor( const char *keyName, Color value) +{ + KeyValues *dat = FindKey( keyName, true ); + + if ( dat ) + { + dat->m_iDataType = TYPE_COLOR; + dat->m_Color[0] = value[0]; + dat->m_Color[1] = value[1]; + dat->m_Color[2] = value[2]; + dat->m_Color[3] = value[3]; + } +} + +void KeyValues::SetStringValue( char const *strValue ) +{ + // delete the old value + delete [] m_sValue; + // make sure we're not storing the WSTRING - as we're converting over to STRING + delete [] m_wsValue; + m_wsValue = NULL; + + if (!strValue) + { + // ensure a valid value + strValue = ""; + } + + // allocate memory for the new value and copy it in + int len = Q_strlen( strValue ); + m_sValue = new char[len + 1]; + Q_memcpy( m_sValue, strValue, len+1 ); + + m_iDataType = TYPE_STRING; +} + +//----------------------------------------------------------------------------- +// Purpose: Set the string value of a keyName. +//----------------------------------------------------------------------------- +void KeyValues::SetString( const char *keyName, const char *value ) +{ + KeyValues *dat = FindKey( keyName, true ); + + if ( dat ) + { + // delete the old value + delete [] dat->m_sValue; + // make sure we're not storing the WSTRING - as we're converting over to STRING + delete [] dat->m_wsValue; + dat->m_wsValue = NULL; + + if (!value) + { + // ensure a valid value + value = ""; + } + + // allocate memory for the new value and copy it in + int len = Q_strlen( value ); + dat->m_sValue = new char[len + 1]; + Q_memcpy( dat->m_sValue, value, len+1 ); + + dat->m_iDataType = TYPE_STRING; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the string value of a keyName. +//----------------------------------------------------------------------------- +void KeyValues::SetWString( const char *keyName, const wchar_t *value ) +{ + KeyValues *dat = FindKey( keyName, true ); + if ( dat ) + { + // delete the old value + delete [] dat->m_wsValue; + // make sure we're not storing the STRING - as we're converting over to WSTRING + delete [] dat->m_sValue; + dat->m_sValue = NULL; + + if (!value) + { + // ensure a valid value + value = L""; + } + + // allocate memory for the new value and copy it in + int len = wcslen( value ); + dat->m_wsValue = new wchar_t[len + 1]; + Q_memcpy( dat->m_wsValue, value, (len+1) * sizeof(wchar_t) ); + + dat->m_iDataType = TYPE_WSTRING; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the integer value of a keyName. +//----------------------------------------------------------------------------- +void KeyValues::SetInt( const char *keyName, int value ) +{ + KeyValues *dat = FindKey( keyName, true ); + + if ( dat ) + { + dat->m_iValue = value; + dat->m_iDataType = TYPE_INT; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the integer value of a keyName. +//----------------------------------------------------------------------------- +void KeyValues::SetUint64( const char *keyName, uint64 value ) +{ + KeyValues *dat = FindKey( keyName, true ); + + if ( dat ) + { + // delete the old value + delete [] dat->m_sValue; + // make sure we're not storing the WSTRING - as we're converting over to STRING + delete [] dat->m_wsValue; + dat->m_wsValue = NULL; + + dat->m_sValue = new char[sizeof(uint64)]; + *((uint64 *)dat->m_sValue) = value; + dat->m_iDataType = TYPE_UINT64; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Set the float value of a keyName. +//----------------------------------------------------------------------------- +void KeyValues::SetFloat( const char *keyName, float value ) +{ + KeyValues *dat = FindKey( keyName, true ); + + if ( dat ) + { + dat->m_flValue = value; + dat->m_iDataType = TYPE_FLOAT; + } +} + +void KeyValues::SetName( const char * setName ) +{ + m_iKeyName = KeyValuesSystem()->GetSymbolForString( setName ); +} + +//----------------------------------------------------------------------------- +// Purpose: Set the pointer value of a keyName. +//----------------------------------------------------------------------------- +void KeyValues::SetPtr( const char *keyName, void *value ) +{ + KeyValues *dat = FindKey( keyName, true ); + + if ( dat ) + { + dat->m_pValue = value; + dat->m_iDataType = TYPE_PTR; + } +} + +void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) +{ + // garymcthack - need to check this code for possible buffer overruns. + + m_iKeyName = src.GetNameSymbol(); + + if( !src.m_pSub ) + { + m_iDataType = src.m_iDataType; + char buf[256]; + switch( src.m_iDataType ) + { + case TYPE_NONE: + break; + case TYPE_STRING: + if( src.m_sValue ) + { + int len = Q_strlen(src.m_sValue) + 1; + m_sValue = new char[len]; + Q_strncpy( m_sValue, src.m_sValue, len ); + } + break; + case TYPE_INT: + { + m_iValue = src.m_iValue; + Q_snprintf( buf,sizeof(buf), "%d", m_iValue ); + int len = Q_strlen(buf) + 1; + m_sValue = new char[len]; + Q_strncpy( m_sValue, buf, len ); + } + break; + case TYPE_FLOAT: + { + m_flValue = src.m_flValue; + Q_snprintf( buf,sizeof(buf), "%f", m_flValue ); + int len = Q_strlen(buf) + 1; + m_sValue = new char[len]; + Q_strncpy( m_sValue, buf, len ); + } + break; + case TYPE_PTR: + { + m_pValue = src.m_pValue; + } + break; + case TYPE_UINT64: + { + m_sValue = new char[sizeof(uint64)]; + Q_memcpy( m_sValue, src.m_sValue, sizeof(uint64) ); + } + break; + case TYPE_COLOR: + { + m_Color[0] = src.m_Color[0]; + m_Color[1] = src.m_Color[1]; + m_Color[2] = src.m_Color[2]; + m_Color[3] = src.m_Color[3]; + } + break; + + default: + { + // do nothing . .what the heck is this? + Assert( 0 ); + } + break; + } + + } +#if 0 + KeyValues *pDst = this; + for ( KeyValues *pSrc = src.m_pSub; pSrc; pSrc = pSrc->m_pPeer ) + { + if ( pSrc->m_pSub ) + { + pDst->m_pSub = new KeyValues( pSrc->m_pSub->getName() ); + pDst->m_pSub->RecursiveCopyKeyValues( *pSrc->m_pSub ); + } + else + { + // copy non-empty keys + if ( pSrc->m_sValue && *(pSrc->m_sValue) ) + { + pDst->m_pPeer = new KeyValues( + } + } + } +#endif + + // Handle the immediate child + if( src.m_pSub ) + { + m_pSub = new KeyValues( NULL ); + m_pSub->RecursiveCopyKeyValues( *src.m_pSub ); + } + + // Handle the immediate peer + if( src.m_pPeer ) + { + m_pPeer = new KeyValues( NULL ); + m_pPeer->RecursiveCopyKeyValues( *src.m_pPeer ); + } +} + +KeyValues& KeyValues::operator=( KeyValues& src ) +{ + RemoveEverything(); + Init(); // reset all values + RecursiveCopyKeyValues( src ); + return *this; +} + + +//----------------------------------------------------------------------------- +// Make a new copy of all subkeys, add them all to the passed-in keyvalues +//----------------------------------------------------------------------------- +void KeyValues::CopySubkeys( KeyValues *pParent ) const +{ + // recursively copy subkeys + // Also maintain ordering.... + KeyValues *pPrev = NULL; + for ( KeyValues *sub = m_pSub; sub != NULL; sub = sub->m_pPeer ) + { + // take a copy of the subkey + KeyValues *dat = sub->MakeCopy(); + + // add into subkey list + if (pPrev) + { + pPrev->m_pPeer = dat; + } + else + { + pParent->m_pSub = dat; + } + dat->m_pPeer = NULL; + pPrev = dat; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Makes a copy of the whole key-value pair set +//----------------------------------------------------------------------------- +KeyValues *KeyValues::MakeCopy( void ) const +{ + KeyValues *newKeyValue = new KeyValues(GetName()); + + // copy data + newKeyValue->m_iDataType = m_iDataType; + switch ( m_iDataType ) + { + case TYPE_STRING: + { + if ( m_sValue ) + { + int len = Q_strlen( m_sValue ); + Assert( !newKeyValue->m_sValue ); + newKeyValue->m_sValue = new char[len + 1]; + Q_memcpy( newKeyValue->m_sValue, m_sValue, len+1 ); + } + } + break; + case TYPE_WSTRING: + { + if ( m_wsValue ) + { + int len = wcslen( m_wsValue ); + newKeyValue->m_wsValue = new wchar_t[len+1]; + Q_memcpy( newKeyValue->m_wsValue, m_wsValue, (len+1)*sizeof(wchar_t)); + } + } + break; + + case TYPE_INT: + newKeyValue->m_iValue = m_iValue; + break; + + case TYPE_FLOAT: + newKeyValue->m_flValue = m_flValue; + break; + + case TYPE_PTR: + newKeyValue->m_pValue = m_pValue; + break; + + case TYPE_COLOR: + newKeyValue->m_Color[0] = m_Color[0]; + newKeyValue->m_Color[1] = m_Color[1]; + newKeyValue->m_Color[2] = m_Color[2]; + newKeyValue->m_Color[3] = m_Color[3]; + break; + + case TYPE_UINT64: + newKeyValue->m_sValue = new char[sizeof(uint64)]; + Q_memcpy( newKeyValue->m_sValue, m_sValue, sizeof(uint64) ); + break; + }; + + // recursively copy subkeys + CopySubkeys( newKeyValue ); + return newKeyValue; +} + + +//----------------------------------------------------------------------------- +// Purpose: Check if a keyName has no value assigned to it. +//----------------------------------------------------------------------------- +bool KeyValues::IsEmpty(const char *keyName) +{ + KeyValues *dat = FindKey(keyName, false); + if (!dat) + return true; + + if (dat->m_iDataType == TYPE_NONE && dat->m_pSub == NULL) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Clear out all subkeys, and the current value +//----------------------------------------------------------------------------- +void KeyValues::Clear( void ) +{ + delete m_pSub; + m_pSub = NULL; + m_iDataType = TYPE_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the data type of the value stored in a keyName +//----------------------------------------------------------------------------- +KeyValues::types_t KeyValues::GetDataType(const char *keyName) +{ + KeyValues *dat = FindKey(keyName, false); + if (dat) + return (types_t)dat->m_iDataType; + + return TYPE_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: Deletion, ensures object gets deleted from correct heap +//----------------------------------------------------------------------------- +void KeyValues::deleteThis() +{ + delete this; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : includedKeys - +//----------------------------------------------------------------------------- +void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) +{ + // Append any included keys, too... + int includeCount = includedKeys.Count(); + int i; + for ( i = 0; i < includeCount; i++ ) + { + KeyValues *kv = includedKeys[ i ]; + Assert( kv ); + + KeyValues *insertSpot = this; + while ( insertSpot->GetNextKey() ) + { + insertSpot = insertSpot->GetNextKey(); + } + + insertSpot->SetNextKey( kv ); + } +} + +void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoinclude, + IBaseFileSystem* pFileSystem, const char *pPathID, CUtlVector< KeyValues * >& includedKeys, + GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + Assert( resourceName ); + Assert( filetoinclude ); + Assert( pFileSystem ); + + // Load it... + if ( !pFileSystem ) + { + return; + } + + // Get relative subdirectory + char fullpath[ 512 ]; + Q_strncpy( fullpath, resourceName, sizeof( fullpath ) ); + + // Strip off characters back to start or first / + bool done = false; + int len = Q_strlen( fullpath ); + while ( !done ) + { + if ( len <= 0 ) + { + break; + } + + if ( fullpath[ len - 1 ] == '\\' || + fullpath[ len - 1 ] == '/' ) + { + break; + } + + // zero it + fullpath[ len - 1 ] = 0; + --len; + } + + // Append included file + Q_strncat( fullpath, filetoinclude, sizeof( fullpath ), COPY_ALL_CHARACTERS ); + + KeyValues *newKV = new KeyValues( fullpath ); + + // CUtlSymbol save = s_CurrentFileSymbol; // did that had any use ??? + + newKV->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent + + if ( newKV->LoadFromFile( pFileSystem, fullpath, pPathID, pfnEvaluateSymbolProc ) ) + { + includedKeys.AddToTail( newKV ); + } + else + { + DevMsg( "KeyValues::ParseIncludedKeys: Couldn't load included keyvalue file %s\n", fullpath ); + newKV->deleteThis(); + } + + // s_CurrentFileSymbol = save; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : baseKeys - +//----------------------------------------------------------------------------- +void KeyValues::MergeBaseKeys( CUtlVector< KeyValues * >& baseKeys ) +{ + int includeCount = baseKeys.Count(); + int i; + for ( i = 0; i < includeCount; i++ ) + { + KeyValues *kv = baseKeys[ i ]; + Assert( kv ); + + RecursiveMergeKeyValues( kv ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : baseKV - keyvalues we're basing ourselves on +//----------------------------------------------------------------------------- +void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV ) +{ + // Merge ourselves + // we always want to keep our value, so nothing to do here + + // Now merge our children + for ( KeyValues *baseChild = baseKV->m_pSub; baseChild != NULL; baseChild = baseChild->m_pPeer ) + { + // for each child in base, see if we have a matching kv + + bool bFoundMatch = false; + + // If we have a child by the same name, merge those keys + for ( KeyValues *newChild = m_pSub; newChild != NULL; newChild = newChild->m_pPeer ) + { + if ( !Q_strcmp( baseChild->GetName(), newChild->GetName() ) ) + { + newChild->RecursiveMergeKeyValues( baseChild ); + bFoundMatch = true; + break; + } + } + + // If not merged, append this key + if ( !bFoundMatch ) + { + KeyValues *dat = baseChild->MakeCopy(); + Assert( dat ); + AddSubKey( dat ); + } + } +} + +//----------------------------------------------------------------------------- +// Returns whether a keyvalues conditional evaluates to true or false +// Needs more flexibility with conditionals, checking convars would be nice. +//----------------------------------------------------------------------------- +bool KeyValues::EvaluateConditional( const char *str, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + bool bResult = false; + bool bXboxUI = IsX360(); + + if ( bXboxUI ) + { + bResult = !Q_stricmp( "[$X360]", str ); + } + else + { + bResult = !Q_stricmp( "[$WIN32]", str ); + } + + return bResult; +} + + +//----------------------------------------------------------------------------- +// Read from a buffer... +//----------------------------------------------------------------------------- +bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + KeyValues *pPreviousKey = NULL; + KeyValues *pCurrentKey = this; + CUtlVector< KeyValues * > includedKeys; + CUtlVector< KeyValues * > baseKeys; + bool wasQuoted; + bool wasConditional; + g_KeyValuesErrorStack.SetFilename( resourceName ); + do + { + bool bAccepted = true; + + // the first thing must be a key + const char *s = ReadToken( buf, wasQuoted, wasConditional ); + if ( !buf.IsValid() || !s || *s == 0 ) + break; + + if ( !Q_stricmp( s, "#include" ) ) // special include macro (not a key name) + { + s = ReadToken( buf, wasQuoted, wasConditional ); + // Name of subfile to load is now in s + + if ( !s || *s == 0 ) + { + g_KeyValuesErrorStack.ReportError("#include is NULL " ); + } + else + { + ParseIncludedKeys( resourceName, s, pFileSystem, pPathID, includedKeys, pfnEvaluateSymbolProc ); + } + + continue; + } + else if ( !Q_stricmp( s, "#base" ) ) + { + s = ReadToken( buf, wasQuoted, wasConditional ); + // Name of subfile to load is now in s + + if ( !s || *s == 0 ) + { + g_KeyValuesErrorStack.ReportError("#base is NULL " ); + } + else + { + ParseIncludedKeys( resourceName, s, pFileSystem, pPathID, baseKeys, pfnEvaluateSymbolProc ); + } + + continue; + } + + if ( !pCurrentKey ) + { + pCurrentKey = new KeyValues( s ); + Assert( pCurrentKey ); + + pCurrentKey->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // same format has parent use + + if ( pPreviousKey ) + { + pPreviousKey->SetNextKey( pCurrentKey ); + } + } + else + { + pCurrentKey->SetName( s ); + } + + // get the '{' + s = ReadToken( buf, wasQuoted, wasConditional ); + + if ( wasConditional ) + { + bAccepted = EvaluateConditional( s, pfnEvaluateSymbolProc ); + + // Now get the '{' + s = ReadToken( buf, wasQuoted, wasConditional ); + } + + if ( s && *s == '{' && !wasQuoted ) + { + // header is valid so load the file + pCurrentKey->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc ); + } + else + { + g_KeyValuesErrorStack.ReportError("LoadFromBuffer: missing {" ); + } + + if ( !bAccepted ) + { + if ( pPreviousKey ) + { + pPreviousKey->SetNextKey( NULL ); + } + pCurrentKey->Clear(); + } + else + { + pPreviousKey = pCurrentKey; + pCurrentKey = NULL; + } + } while ( buf.IsValid() ); + + AppendIncludedKeys( includedKeys ); + { + // delete included keys! + int i; + for ( i = includedKeys.Count() - 1; i > 0; i-- ) + { + KeyValues *kv = includedKeys[ i ]; + kv->deleteThis(); + } + } + + MergeBaseKeys( baseKeys ); + { + // delete base keys! + int i; + for ( i = baseKeys.Count() - 1; i >= 0; i-- ) + { + KeyValues *kv = baseKeys[ i ]; + kv->deleteThis(); + } + } + + g_KeyValuesErrorStack.SetFilename( "" ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Read from a buffer... +//----------------------------------------------------------------------------- +bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + if ( !pBuffer ) + return true; + + int nLen = Q_strlen( pBuffer ); + CUtlBuffer buf( pBuffer, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); + return LoadFromBuffer( resourceName, buf, pFileSystem, pPathID, pfnEvaluateSymbolProc ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + CKeyErrorContext errorReport(this); + bool wasQuoted; + bool wasConditional; + // keep this out of the stack until a key is parsed + CKeyErrorContext errorKey( INVALID_KEY_SYMBOL ); + while ( 1 ) + { + bool bAccepted = true; + + // get the key name + const char * name = ReadToken( buf, wasQuoted, wasConditional ); + + if ( !name ) // EOF stop reading + { + g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got EOF instead of keyname" ); + break; + } + + if ( !*name ) // empty token, maybe "" or EOF + { + g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got empty keyname" ); + break; + } + + if ( *name == '}' && !wasQuoted ) // top level closed, stop reading + break; + + // Always create the key; note that this could potentially + // cause some duplication, but that's what we want sometimes + KeyValues *dat = CreateKey( name ); + + errorKey.Reset( dat->GetNameSymbol() ); + + // get the value + const char * value = ReadToken( buf, wasQuoted, wasConditional ); + + if ( wasConditional && value ) + { + bAccepted = EvaluateConditional( value, pfnEvaluateSymbolProc ); + + // get the real value + value = ReadToken( buf, wasQuoted, wasConditional ); + } + + if ( !value ) + { + g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got NULL key" ); + break; + } + + if ( *value == '}' && !wasQuoted ) + { + g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got } in key" ); + break; + } + + if ( *value == '{' && !wasQuoted ) + { + // this isn't a key, it's a section + errorKey.Reset( INVALID_KEY_SYMBOL ); + // sub value list + dat->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc ); + } + else + { + if ( wasConditional ) + { + g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got conditional between key and value" ); + break; + } + + if (dat->m_sValue) + { + delete[] dat->m_sValue; + dat->m_sValue = NULL; + } + + int len = Q_strlen( value ); + + // Here, let's determine if we got a float or an int.... + char* pIEnd; // pos where int scan ended + char* pFEnd; // pos where float scan ended + const char* pSEnd = value + len ; // pos where token ends + + int ival = strtol( value, &pIEnd, 10 ); + float fval = (float)strtod( value, &pFEnd ); + + if ( *value == 0 ) + { + dat->m_iDataType = TYPE_STRING; + } + else if ( ( 18 == len ) && ( value[0] == '0' ) && ( value[1] == 'x' ) ) + { + // an 18-byte value prefixed with "0x" (followed by 16 hex digits) is an int64 value + int64 retVal = 0; + for( int i=2; i < 2 + 16; i++ ) + { + char digit = value[i]; + if ( digit >= 'a' ) + digit -= 'a' - ( '9' + 1 ); + else + if ( digit >= 'A' ) + digit -= 'A' - ( '9' + 1 ); + retVal = ( retVal * 16 ) + ( digit - '0' ); + } + dat->m_sValue = new char[sizeof(uint64)]; + *((uint64 *)dat->m_sValue) = retVal; + dat->m_iDataType = TYPE_UINT64; + } + else if ( (pFEnd > pIEnd) && (pFEnd == pSEnd) ) + { + dat->m_flValue = fval; + dat->m_iDataType = TYPE_FLOAT; + } + else if (pIEnd == pSEnd) + { + dat->m_iValue = ival; + dat->m_iDataType = TYPE_INT; + } + else + { + dat->m_iDataType = TYPE_STRING; + } + + if (dat->m_iDataType == TYPE_STRING) + { + // copy in the string information + dat->m_sValue = new char[len+1]; + Q_memcpy( dat->m_sValue, value, len+1 ); + } + + // Look ahead one token for a conditional tag + int prevPos = buf.TellGet(); + const char *peek = ReadToken( buf, wasQuoted, wasConditional ); + if ( wasConditional ) + { + bAccepted = EvaluateConditional( peek, pfnEvaluateSymbolProc ); + } + else + { + buf.SeekGet( CUtlBuffer::SEEK_HEAD, prevPos ); + } + } + + if ( !bAccepted ) + { + this->RemoveSubKey( dat ); + dat->deleteThis(); + dat = NULL; + } + } +} + + + +// writes KeyValue as binary data to buffer +bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) +{ + if ( buffer.IsText() ) // must be a binary buffer + return false; + + if ( !buffer.IsValid() ) // must be valid, no overflows etc + return false; + + // Write subkeys: + + // loop through all our peers + for ( KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer ) + { + // write type + buffer.PutUnsignedChar( dat->m_iDataType ); + + // write name + buffer.PutString( dat->GetName() ); + + // write type + switch (dat->m_iDataType) + { + case TYPE_NONE: + { + dat->m_pSub->WriteAsBinary( buffer ); + break; + } + case TYPE_STRING: + { + if (dat->m_sValue && *(dat->m_sValue)) + { + buffer.PutString( dat->m_sValue ); + } + else + { + buffer.PutString( "" ); + } + break; + } + case TYPE_WSTRING: + { + Assert( !"TYPE_WSTRING" ); + break; + } + + case TYPE_INT: + { + buffer.PutInt( dat->m_iValue ); + break; + } + + case TYPE_UINT64: + { + buffer.PutDouble( *((double *)dat->m_sValue) ); + break; + } + + case TYPE_FLOAT: + { + buffer.PutFloat( dat->m_flValue ); + break; + } + case TYPE_COLOR: + { + buffer.PutUnsignedChar( dat->m_Color[0] ); + buffer.PutUnsignedChar( dat->m_Color[1] ); + buffer.PutUnsignedChar( dat->m_Color[2] ); + buffer.PutUnsignedChar( dat->m_Color[3] ); + break; + } + case TYPE_PTR: + { + buffer.PutPtr( dat->m_pValue ); + } + + default: + break; + } + } + + // write tail, marks end of peers + buffer.PutUnsignedChar( TYPE_NUMTYPES ); + + return buffer.IsValid(); +} + +// read KeyValues from binary buffer, returns true if parsing was successful +bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) +{ + if ( buffer.IsText() ) // must be a binary buffer + return false; + + if ( !buffer.IsValid() ) // must be valid, no overflows etc + return false; + + RemoveEverything(); // remove current content + Init(); // reset + + char token[KEYVALUES_TOKEN_SIZE]; + KeyValues *dat = this; + types_t type = (types_t)buffer.GetUnsignedChar(); + + // loop through all our peers + while ( true ) + { + if ( type == TYPE_NUMTYPES ) + break; // no more peers + + dat->m_iDataType = type; + + buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); + token[KEYVALUES_TOKEN_SIZE-1] = 0; + + dat->SetName( token ); + + switch ( type ) + { + case TYPE_NONE: + { + dat->m_pSub = new KeyValues(""); + dat->m_pSub->ReadAsBinary( buffer ); + break; + } + case TYPE_STRING: + { + buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); + token[KEYVALUES_TOKEN_SIZE-1] = 0; + + int len = Q_strlen( token ); + dat->m_sValue = new char[len + 1]; + Q_memcpy( dat->m_sValue, token, len+1 ); + + break; + } + case TYPE_WSTRING: + { + Assert( !"TYPE_WSTRING" ); + break; + } + + case TYPE_INT: + { + dat->m_iValue = buffer.GetInt(); + break; + } + + case TYPE_UINT64: + { + dat->m_sValue = new char[sizeof(uint64)]; + *((double *)dat->m_sValue) = buffer.GetDouble(); + } + + case TYPE_FLOAT: + { + dat->m_flValue = buffer.GetFloat(); + break; + } + case TYPE_COLOR: + { + dat->m_Color[0] = buffer.GetUnsignedChar(); + dat->m_Color[1] = buffer.GetUnsignedChar(); + dat->m_Color[2] = buffer.GetUnsignedChar(); + dat->m_Color[3] = buffer.GetUnsignedChar(); + break; + } + case TYPE_PTR: + { + dat->m_pValue = buffer.GetPtr(); + } + + default: + break; + } + + if ( !buffer.IsValid() ) // error occured + return false; + + type = (types_t)buffer.GetUnsignedChar(); + + if ( type == TYPE_NUMTYPES ) + break; + + // new peer follows + dat->m_pPeer = new KeyValues(""); + dat = dat->m_pPeer; + } + + return buffer.IsValid(); +} + +#include "tier0/memdbgoff.h" + +#if 0 +//----------------------------------------------------------------------------- +// Purpose: memory allocator +//----------------------------------------------------------------------------- +void *KeyValues::operator new( size_t iAllocSize ) +{ + MEM_ALLOC_CREDIT(); + return KeyValuesSystem()->AllocKeyValuesMemory(); +} + +void *KeyValues::operator new( size_t iAllocSize, int nBlockUse, const char *pFileName, int nLine ) +{ + MemAlloc_PushAllocDbgInfo( pFileName, nLine ); + void *p = KeyValuesSystem()->AllocKeyValuesMemory(); + MemAlloc_PopAllocDbgInfo(); + return p; +} + +//----------------------------------------------------------------------------- +// Purpose: deallocator +//----------------------------------------------------------------------------- +void KeyValues::operator delete( void *pMem ) +{ + KeyValuesSystem()->FreeKeyValuesMemory((KeyValues *)pMem); +} + +void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine ) +{ + KeyValuesSystem()->FreeKeyValuesMemory((KeyValues *)pMem); +} +#endif + +void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest ) +{ + uint8 *dest=(uint8 *) pDest; + while( pUnpackTable->m_pKeyName ) + { + uint8 *dest_field=dest+pUnpackTable->m_nFieldOffset; + KeyValues *find_it=FindKey( pUnpackTable->m_pKeyName ); + switch( pUnpackTable->m_eDataType ) + { + case UNPACK_TYPE_FLOAT: + { + float default_value=(pUnpackTable->m_pKeyDefault)?atof(pUnpackTable->m_pKeyDefault):0.0; + *( ( float *) dest_field)=GetFloat( pUnpackTable->m_pKeyName, default_value ); + break; + } + break; + + case UNPACK_TYPE_VECTOR: + { + Vector *dest_v=(Vector *) dest_field; + char const *src_string= + GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); + if ( (!src_string) || + ( sscanf(src_string,"%f %f %f", + &(dest_v->x), &(dest_v->y), &(dest_v->z)) != 3)) + dest_v->Init( 0, 0, 0 ); + } + break; + + case UNPACK_TYPE_FOUR_FLOATS: + { + float *dest_f=(float *) dest_field; + char const *src_string= + GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); + if ( (!src_string) || + ( sscanf(src_string,"%f %f %f %f", + dest_f,dest_f+1,dest_f+2,dest_f+3)) != 4) + memset( dest_f, 0, 4*sizeof(float) ); + } + break; + + case UNPACK_TYPE_TWO_FLOATS: + { + float *dest_f=(float *) dest_field; + char const *src_string= + GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); + if ( (!src_string) || + ( sscanf(src_string,"%f %f", + dest_f,dest_f+1)) != 2) + memset( dest_f, 0, 2*sizeof(float) ); + } + break; + + case UNPACK_TYPE_STRING: + { + char *dest_s=(char *) dest_field; + strncpy( dest_s, GetString( pUnpackTable->m_pKeyName, + pUnpackTable->m_pKeyDefault ), + pUnpackTable->m_nFieldSize ); + + } + break; + + case UNPACK_TYPE_INT: + { + int *dest_i=(int *) dest_field; + int default_int=0; + if ( pUnpackTable->m_pKeyDefault) + default_int = atoi( pUnpackTable->m_pKeyDefault ); + *(dest_i)=GetInt( pUnpackTable->m_pKeyName, default_int ); + } + break; + + case UNPACK_TYPE_VECTOR_COLOR: + { + Vector *dest_v=(Vector *) dest_field; + if (find_it) + { + Color c=GetColor( pUnpackTable->m_pKeyName ); + dest_v->x = c.r(); + dest_v->y = c.g(); + dest_v->z = c.b(); + } + else + { + if ( pUnpackTable->m_pKeyDefault ) + sscanf(pUnpackTable->m_pKeyDefault,"%f %f %f", + &(dest_v->x), &(dest_v->y), &(dest_v->z)); + else + dest_v->Init( 0, 0, 0 ); + } + *(dest_v) *= (1.0/255); + } + } + pUnpackTable++; + } +} + +//----------------------------------------------------------------------------- +// Helper function for processing a keyvalue tree for console resolution support. +// Alters key/values for easier console video resolution support. +// If running SD (640x480), the presence of "???_lodef" creates or slams "???". +// If running HD (1280x720), the presence of "???_hidef" creates or slams "???". +//----------------------------------------------------------------------------- +bool KeyValues::ProcessResolutionKeys( const char *pResString ) +{ + if ( !pResString ) + { + // not for pc, console only + return false; + } + + KeyValues *pSubKey = GetFirstSubKey(); + if ( !pSubKey ) + { + // not a block + return false; + } + + for ( ; pSubKey != NULL; pSubKey = pSubKey->GetNextKey() ) + { + // recursively descend each sub block + pSubKey->ProcessResolutionKeys( pResString ); + + // check to see if our substring is present + if ( Q_stristr( pSubKey->GetName(), pResString ) != NULL ) + { + char normalKeyName[128]; + V_strncpy( normalKeyName, pSubKey->GetName(), sizeof( normalKeyName ) ); + + // substring must match exactly, otherwise keys like "_lodef" and "_lodef_wide" would clash. + char *pString = Q_stristr( normalKeyName, pResString ); + if ( pString && !Q_stricmp( pString, pResString ) ) + { + *pString = '\0'; + + // find and delete the original key (if any) + KeyValues *pKey = FindKey( normalKeyName ); + if ( pKey ) + { + // remove the key + RemoveSubKey( pKey ); + } + + // rename the marked key + pSubKey->SetName( normalKeyName ); + } + } + } + + return true; +} diff --git a/tier1/NetAdr.cpp b/tier1/NetAdr.cpp new file mode 100644 index 00000000..78657a5d --- /dev/null +++ b/tier1/NetAdr.cpp @@ -0,0 +1,331 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// NetAdr.cpp: implementation of the CNetAdr class. +// +//===========================================================================// +#if defined( _WIN32 ) && !defined( _X360 ) +#include +#endif + +#include "tier0/dbg.h" +#include "netadr.h" +#include "tier1/strtools.h" + +#if defined( _WIN32 ) && !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include +typedef int socklen_t; +#elif !defined( _X360 ) +#include // ntohs() +#include // gethostbyname() +#include // getsockname() +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +bool netadr_t::CompareAdr (const netadr_t &a, bool onlyBase) const +{ + if ( a.type != type ) + return false; + + if ( type == NA_LOOPBACK ) + return true; + + if ( type == NA_BROADCAST ) + return true; + + if ( type == NA_IP ) + { + if ( !onlyBase && (port != a.port) ) + return false; + + if ( a.ip[0] == ip[0] && a.ip[1] == ip[1] && a.ip[2] == ip[2] && a.ip[3] == ip[3] ) + return true; + } + + return false; +} + +bool netadr_t::CompareClassBAdr (const netadr_t &a) const +{ + if ( a.type != type ) + return false; + + if ( type == NA_LOOPBACK ) + return true; + + if ( type == NA_IP ) + { + if (a.ip[0] == ip[0] && a.ip[1] == ip[1] ) + return true; + } + + return false; +} + +bool netadr_t::CompareClassCAdr (const netadr_t &a) const +{ + if ( a.type != type ) + return false; + + if ( type == NA_LOOPBACK ) + return true; + + if ( type == NA_IP ) + { + if (a.ip[0] == ip[0] && a.ip[1] == ip[1] && a.ip[2] == ip[2] ) + return true; + } + + return false; +} +// reserved addresses are not routeable, so they can all be used in a LAN game +bool netadr_t::IsReservedAdr () const +{ + if ( type == NA_LOOPBACK ) + return true; + + if ( type == NA_IP ) + { + if ( (ip[0] == 10) || // 10.x.x.x is reserved + (ip[0] == 127) || // 127.x.x.x + (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) || // 172.16.x.x - 172.31.x.x + (ip[0] == 192 && ip[1] >= 168) ) // 192.168.x.x + return true; + } + return false; +} + +const char * netadr_t::ToString(bool baseOnly) const +{ + static char s[64]; + + Q_strncpy (s, "unknown", sizeof( s ) ); + + if (type == NA_LOOPBACK) + { + Q_strncpy (s, "loopback", sizeof( s ) ); + } + else if (type == NA_BROADCAST) + { + Q_strncpy (s, "broadcast", sizeof( s ) ); + } + else if (type == NA_IP) + { + if ( baseOnly) + { + Q_snprintf (s, sizeof( s ), "%i.%i.%i.%i", ip[0], ip[1], ip[2], ip[3]); + } + else + { + Q_snprintf (s, sizeof( s ), "%i.%i.%i.%i:%i", ip[0], ip[1], ip[2], ip[3], ntohs(port)); + } + } + + return s; +} + +bool netadr_t::IsLocalhost() const +{ + // are we 127.0.0.1 ? + return (ip[0] == 127) && (ip[1] == 0) && (ip[2] == 0) && (ip[3] == 1); +} + +bool netadr_t::IsLoopback() const +{ + // are we useding engine loopback buffers + return type == NA_LOOPBACK; +} + +void netadr_t::Clear() +{ + ip[0] = ip[1] = ip[2] = ip[3] = 0; + port = 0; + type = NA_NULL; +} + +void netadr_t::SetIP(uint8 b1, uint8 b2, uint8 b3, uint8 b4) +{ + ip[0] = b1; + ip[1] = b2; + ip[2] = b3; + ip[3] = b4; +} + +void netadr_t::SetIP(uint unIP) +{ + *((uint*)ip) = BigLong( unIP ); +} + +void netadr_t::SetType(netadrtype_t newtype) +{ + type = newtype; +} + +netadrtype_t netadr_t::GetType() const +{ + return type; +} + +unsigned short netadr_t::GetPort() const +{ + return BigShort( port ); +} + +unsigned int netadr_t::GetIP() const +{ + return *(unsigned int *)&ip;; +} + +unsigned long netadr_t::addr_ntohl() const +{ + return ntohl( GetIP() ); +} + +unsigned long netadr_t::addr_htonl() const +{ + return htonl( GetIP() ); +} + + +void netadr_t::ToSockadr (struct sockaddr * s) const +{ + Q_memset ( s, 0, sizeof(struct sockaddr)); + + if (type == NA_BROADCAST) + { + ((struct sockaddr_in*)s)->sin_family = AF_INET; + ((struct sockaddr_in*)s)->sin_port = port; + ((struct sockaddr_in*)s)->sin_addr.s_addr = INADDR_BROADCAST; + } + else if (type == NA_IP) + { + ((struct sockaddr_in*)s)->sin_family = AF_INET; + ((struct sockaddr_in*)s)->sin_addr.s_addr = *(int *)&ip; + ((struct sockaddr_in*)s)->sin_port = port; + } + else if (type == NA_LOOPBACK ) + { + ((struct sockaddr_in*)s)->sin_family = AF_INET; + ((struct sockaddr_in*)s)->sin_port = port; + ((struct sockaddr_in*)s)->sin_addr.s_addr = INADDR_LOOPBACK ; + } +} + +bool netadr_t::SetFromSockadr(const struct sockaddr * s) +{ + if (s->sa_family == AF_INET) + { + type = NA_IP; + *(int *)&ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; + port = ((struct sockaddr_in *)s)->sin_port; + return true; + } + else + { + Clear(); + return false; + } +} + +bool netadr_t::IsValid() const +{ + return ( (port !=0 ) && (type != NA_NULL) && + ( ip[0] != 0 || ip[1] != 0 || ip[2] != 0 || ip[3] != 0 ) ); +} + +#ifdef _WIN32 +#undef SetPort // get around stupid WINSPOOL.H macro +#endif + +void netadr_t::SetPort(unsigned short newport) +{ + port = BigShort( newport ); +} + +void netadr_t::SetFromString( const char *pch, bool bUseDNS ) +{ + Clear(); + type = NA_IP; + + Assert( pch ); // invalid to call this with NULL pointer; fix your code bug! + if ( !pch ) // but let's not crash + return; + + + if ( pch[0] >= '0' && pch[0] <= '9' && strchr( pch, '.' ) ) + { + int n1, n2, n3, n4, n5; + int nRes = sscanf( pch, "%d.%d.%d.%d:%d", &n1, &n2, &n3, &n4, &n5 ); + if ( nRes >= 4 ) + { + SetIP( n1, n2, n3, n4 ); + } + + if ( nRes == 5 ) + { + SetPort( ( uint16 ) n5 ); + } + } + else if ( bUseDNS ) + { +// X360TBD: +#if !defined( _X360 ) + char szHostName[ 256 ]; + Q_strncpy( szHostName, pch, sizeof(szHostName) ); + char *pchColon = strchr( szHostName, ':' ); + if ( pchColon ) + { + *pchColon = 0; + } + + // DNS it + struct hostent *h = gethostbyname( szHostName ); + if ( !h ) + return; + + SetIP( ntohl( *(int *)h->h_addr_list[0] ) ); + + if ( pchColon ) + { + SetPort( atoi( ++pchColon ) ); + } +#else + Assert( 0 ); +#endif + } +} + +bool netadr_t::operator<(const netadr_t &netadr) const +{ + if ( *((uint *)netadr.ip) < *((uint *)ip) ) + return true; + else if ( *((uint *)netadr.ip) > *((uint *)ip) ) + return false; + return ( netadr.port < port ); +} + + +void netadr_t::SetFromSocket( int hSocket ) +{ +#if !defined(_X360) + Clear(); + type = NA_IP; + + struct sockaddr address; + int namelen = sizeof(address); + if ( getsockname( hSocket, (struct sockaddr *)&address, (socklen_t *)&namelen) == 0 ) + { + SetFromSockadr( &address ); + } +#else + Assert(0); +#endif +} diff --git a/tier1/bitbuf.cpp b/tier1/bitbuf.cpp new file mode 100644 index 00000000..f59956f1 --- /dev/null +++ b/tier1/bitbuf.cpp @@ -0,0 +1,1330 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "bitbuf.h" +#include "coordsize.h" +#include "mathlib/vector.h" +#include "mathlib/mathlib.h" +#include "tier1/strtools.h" +#include "bitvec.h" + +// FIXME: Can't use this until we get multithreaded allocations in tier0 working for tools +// This is used by VVIS and fails to link +// NOTE: This must be the last file included!!! +//#include "tier0/memdbgon.h" + +#ifdef _X360 +// mandatory ... wary of above comment and isolating, tier0 is built as MT though +#include "tier0/memdbgon.h" +#endif + +#if _WIN32 +#define FAST_BIT_SCAN 1 +#if _X360 +#define CountLeadingZeros(x) _CountLeadingZeros(x) +inline unsigned int CountTrailingZeros( unsigned int elem ) +{ + // this implements CountTrailingZeros() / BitScanForward() + unsigned int mask = elem-1; + unsigned int comp = ~elem; + elem = mask & comp; + return (32 - _CountLeadingZeros(elem)); +} +#else +#include +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) + +inline unsigned int CountLeadingZeros(unsigned int x) +{ + unsigned long firstBit; + if ( _BitScanReverse(&firstBit,x) ) + return 31 - firstBit; + return 32; +} +inline unsigned int CountTrailingZeros(unsigned int elem) +{ + unsigned long out; + if ( _BitScanForward(&out, elem) ) + return out; + return 32; +} + +#endif +#else +#define FAST_BIT_SCAN 0 +#endif + + +static BitBufErrorHandler g_BitBufErrorHandler = 0; + +inline int BitForBitnum(int bitnum) +{ + return GetBitForBitnum(bitnum); +} + +void InternalBitBufErrorHandler( BitBufErrorType errorType, const char *pDebugName ) +{ + if ( g_BitBufErrorHandler ) + g_BitBufErrorHandler( errorType, pDebugName ); +} + + +void SetBitBufErrorHandler( BitBufErrorHandler fn ) +{ + g_BitBufErrorHandler = fn; +} + + +// #define BB_PROFILING + + +// Precalculated bit masks for WriteUBitLong. Using these tables instead of +// doing the calculations gives a 33% speedup in WriteUBitLong. +unsigned long g_BitWriteMasks[32][33]; + +// (1 << i) - 1 +unsigned long g_ExtraMasks[32]; + +class CBitWriteMasksInit +{ +public: + CBitWriteMasksInit() + { + for( unsigned int startbit=0; startbit < 32; startbit++ ) + { + for( unsigned int nBitsLeft=0; nBitsLeft < 33; nBitsLeft++ ) + { + unsigned int endbit = startbit + nBitsLeft; + g_BitWriteMasks[startbit][nBitsLeft] = BitForBitnum(startbit) - 1; + if(endbit < 32) + g_BitWriteMasks[startbit][nBitsLeft] |= ~(BitForBitnum(endbit) - 1); + } + } + + for ( unsigned int maskBit=0; maskBit < 32; maskBit++ ) + g_ExtraMasks[maskBit] = BitForBitnum(maskBit) - 1; + } +}; +CBitWriteMasksInit g_BitWriteMasksInit; + + +// ---------------------------------------------------------------------------------------- // +// bf_write +// ---------------------------------------------------------------------------------------- // + +bf_write::bf_write() +{ + m_pData = NULL; + m_nDataBytes = 0; + m_nDataBits = -1; // set to -1 so we generate overflow on any operation + m_iCurBit = 0; + m_bOverflow = false; + m_bAssertOnOverflow = true; + m_pDebugName = NULL; +} + +bf_write::bf_write( void *pData, int nBytes, int nMaxBits ) +{ + m_bAssertOnOverflow = true; + m_pDebugName = NULL; + StartWriting( pData, nBytes, 0, nMaxBits ); +} + +bf_write::bf_write( const char *pDebugName, void *pData, int nBytes, int nMaxBits ) +{ + m_bAssertOnOverflow = true; + m_pDebugName = pDebugName; + StartWriting( pData, nBytes, 0, nMaxBits ); +} + +void bf_write::StartWriting( void *pData, int nBytes, int iStartBit, int nMaxBits ) +{ + // Make sure it's dword aligned and padded. + Assert( (nBytes % 4) == 0 ); + Assert(((unsigned long)pData & 3) == 0); + + // The writing code will overrun the end of the buffer if it isn't dword aligned, so truncate to force alignment + nBytes &= ~3; + + m_pData = (unsigned char*)pData; + m_nDataBytes = nBytes; + + if ( nMaxBits == -1 ) + { + m_nDataBits = nBytes << 3; + } + else + { + Assert( nMaxBits <= nBytes*8 ); + m_nDataBits = nMaxBits; + } + + m_iCurBit = iStartBit; + m_bOverflow = false; +} + +void bf_write::Reset() +{ + m_iCurBit = 0; + m_bOverflow = false; +} + + +void bf_write::SetAssertOnOverflow( bool bAssert ) +{ + m_bAssertOnOverflow = bAssert; +} + + +const char* bf_write::GetDebugName() +{ + return m_pDebugName; +} + + +void bf_write::SetDebugName( const char *pDebugName ) +{ + m_pDebugName = pDebugName; +} + + +void bf_write::SeekToBit( int bitPos ) +{ + m_iCurBit = bitPos; +} + + +// Sign bit comes first +void bf_write::WriteSBitLong( int data, int numbits ) +{ + // Do we have a valid # of bits to encode with? + Assert( numbits >= 1 ); + + // Note: it does this wierdness here so it's bit-compatible with regular integer data in the buffer. + // (Some old code writes direct integers right into the buffer). + if(data < 0) + { +#ifdef _DEBUG + if( numbits < 32 ) + { + // Make sure it doesn't overflow. + + if( data < 0 ) + { + Assert( data >= -(BitForBitnum(numbits-1)) ); + } + else + { + Assert( data < (BitForBitnum(numbits-1)) ); + } + } +#endif + + WriteUBitLong( (unsigned int)(0x80000000 + data), numbits - 1, false ); + WriteOneBit( 1 ); + } + else + { + WriteUBitLong((unsigned int)data, numbits - 1); + WriteOneBit( 0 ); + } +} + +void bf_write::WriteVarInt32( uint32 data ) +{ + while ( data > 0x7F ) + { + WriteUBitLong( (data & 0x7F) | 0x80, 8 ); + data >>= 7; + } + WriteUBitLong( data & 0x7F, 8 ); +} + +int bf_write::ByteSizeVarInt32( uint32 data ) +{ + int size = 1; + while ( data > 0x7F ) + { + size++; + data >>= 7; + } + return size; +} + +#if _WIN32 +inline unsigned int BitCountNeededToEncode(unsigned int data) +{ +#if defined(_X360) + return (32 - CountLeadingZeros(data+1)) - 1; +#else + unsigned long firstBit; + _BitScanReverse(&firstBit,data+1); + return firstBit; +#endif +} +#endif // _WIN32 + +// writes an unsigned integer with variable bit length +void bf_write::WriteUBitVar( unsigned int data ) +{ + if ( ( data &0xf ) == data ) + { + WriteUBitLong( 0, 2 ); + WriteUBitLong( data, 4 ); + } + else + { + if ( ( data & 0xff ) == data ) + { + WriteUBitLong( 1, 2 ); + WriteUBitLong( data, 8 ); + } + else + { + if ( ( data & 0xfff ) == data ) + { + WriteUBitLong( 2, 2 ); + WriteUBitLong( data, 12 ); + } + else + { + WriteUBitLong( 0x3, 2 ); + WriteUBitLong( data, 32 ); + } + } + } +#if 0 +#if !FAST_BIT_SCAN + unsigned int bits = 0; + unsigned int base = 0; + + while (data > (base<<1)) + { + bits++; + base = BitForBitnum(bits)-1; + } +#else + unsigned int bits = BitCountNeededToEncode(data); + unsigned int base = GetBitForBitnum(bits)-1; +#endif + + // how many bits do we use + WriteUBitLong( 0, bits ); + + // end marker + WriteOneBit( 1 ); + + // write the value + if ( bits > 0) + WriteUBitLong( data - base , bits ); +#endif +} + +void bf_write::WriteBitLong(unsigned int data, int numbits, bool bSigned) +{ + if(bSigned) + WriteSBitLong((int)data, numbits); + else + WriteUBitLong(data, numbits); +} + +bool bf_write::WriteBits(const void *pInData, int nBits) +{ +#if defined( BB_PROFILING ) + VPROF( "bf_write::WriteBits" ); +#endif + + unsigned char *pOut = (unsigned char*)pInData; + int nBitsLeft = nBits; + + // Bounds checking.. + if ( (m_iCurBit+nBits) > m_nDataBits ) + { + SetOverflowFlag(); + CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); + return false; + } + + // Align output to dword boundary + while (((unsigned long)pOut & 3) != 0 && nBitsLeft >= 8) + { + + WriteUBitLong( *pOut, 8, false ); + ++pOut; + nBitsLeft -= 8; + } + + if ( IsPC() && (nBitsLeft >= 32) && (m_iCurBit & 7) == 0 ) + { + // current bit is byte aligned, do block copy + int numbytes = nBitsLeft >> 3; + int numbits = numbytes << 3; + + Q_memcpy( m_pData+(m_iCurBit>>3), pOut, numbytes ); + pOut += numbytes; + nBitsLeft -= numbits; + m_iCurBit += numbits; + } + + // X360TBD: Can't write dwords in WriteBits because they'll get swapped + if ( IsPC() && nBitsLeft >= 32 ) + { + unsigned long iBitsRight = (m_iCurBit & 31); + unsigned long iBitsLeft = 32 - iBitsRight; + unsigned long bitMaskLeft = g_BitWriteMasks[iBitsRight][32]; + unsigned long bitMaskRight = g_BitWriteMasks[0][iBitsRight]; + + unsigned long *pData = &((unsigned long*)m_pData)[m_iCurBit>>5]; + + // Read dwords. + while(nBitsLeft >= 32) + { + unsigned long curData = *(unsigned long*)pOut; + pOut += sizeof(unsigned long); + + *pData &= bitMaskLeft; + *pData |= curData << iBitsRight; + + pData++; + + if ( iBitsLeft < 32 ) + { + curData >>= iBitsLeft; + *pData &= bitMaskRight; + *pData |= curData; + } + + nBitsLeft -= 32; + m_iCurBit += 32; + } + } + + + // write remaining bytes + while ( nBitsLeft >= 8 ) + { + WriteUBitLong( *pOut, 8, false ); + ++pOut; + nBitsLeft -= 8; + } + + // write remaining bits + if ( nBitsLeft ) + { + WriteUBitLong( *pOut, nBitsLeft, false ); + } + + return !IsOverflowed(); +} + + +bool bf_write::WriteBitsFromBuffer( bf_read *pIn, int nBits ) +{ + // This could be optimized a little by + while ( nBits > 32 ) + { + WriteUBitLong( pIn->ReadUBitLong( 32 ), 32 ); + nBits -= 32; + } + + WriteUBitLong( pIn->ReadUBitLong( nBits ), nBits ); + return !IsOverflowed() && !pIn->IsOverflowed(); +} + + +void bf_write::WriteBitAngle( float fAngle, int numbits ) +{ + int d; + unsigned int mask; + unsigned int shift; + + shift = BitForBitnum(numbits); + mask = shift - 1; + + d = (int)( (fAngle / 360.0) * shift ); + d &= mask; + + WriteUBitLong((unsigned int)d, numbits); +} + +void bf_write::WriteBitCoordMP( const float f, EBitCoordType coordType ) +{ +#if defined( BB_PROFILING ) + VPROF( "bf_write::WriteBitCoordMP" ); +#endif + int signbit = (f <= -( coordType == kCW_LowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION )); + int intval = (int)fabs(f); + int fractval = coordType == kCW_LowPrecision ? + ( abs((int)(f*COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION-1) ) : + ( abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1) ); + + + bool bInBounds = intval < (1 << COORD_INTEGER_BITS_MP ); + + WriteOneBit( bInBounds ); + + if ( coordType == kCW_Integral ) + { + // Send the sign bit + WriteOneBit( intval ); + if ( intval ) + { + WriteOneBit( signbit ); + // Send the integer if we have one. + // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1] + intval--; + if ( bInBounds ) + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS_MP ); + } + else + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); + } + } + } + else + { + // Send the bit flags that indicate whether we have an integer part and/or a fraction part. + WriteOneBit( intval ); + // Send the sign bit + WriteOneBit( signbit ); + + if ( intval ) + { + // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1] + intval--; + if ( bInBounds ) + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS_MP ); + } + else + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); + } + } + WriteUBitLong( (unsigned int)fractval, coordType == kCW_LowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); + } +} + +void bf_write::WriteBitCellCoord( const float f, int bits, EBitCoordType coordType ) +{ +#if defined( BB_PROFILING ) + VPROF( "bf_write::WriteCellBitCoordMP" ); +#endif + int intval = (int)fabs(f); + int fractval = coordType == kCW_LowPrecision ? + ( abs((int)(f*COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION-1) ) : + ( abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1) ); + + if ( coordType == kCW_Integral ) + { + WriteUBitLong( (unsigned int)intval, bits ); + } + else + { + WriteUBitLong( (unsigned int)intval, bits ); + WriteUBitLong( (unsigned int)fractval, coordType == kCW_LowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); + } +} + +void bf_write::WriteBitCoord (const float f) +{ +#if defined( BB_PROFILING ) + VPROF( "bf_write::WriteBitCoord" ); +#endif + int signbit = (f <= -COORD_RESOLUTION); + int intval = (int)fabs(f); + int fractval = abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1); + + + // Send the bit flags that indicate whether we have an integer part and/or a fraction part. + WriteOneBit( intval ); + WriteOneBit( fractval ); + + if ( intval || fractval ) + { + // Send the sign bit + WriteOneBit( signbit ); + + // Send the integer if we have one. + if ( intval ) + { + // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1] + intval--; + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); + } + + // Send the fraction if we have one + if ( fractval ) + { + WriteUBitLong( (unsigned int)fractval, COORD_FRACTIONAL_BITS ); + } + } +} + +void bf_write::WriteBitFloat(float val) +{ + long intVal; + + Assert(sizeof(long) == sizeof(float)); + Assert(sizeof(float) == 4); + + intVal = *((long*)&val); + WriteUBitLong( intVal, 32 ); +} + +void bf_write::WriteBitVec3Coord( const Vector& fa ) +{ + int xflag, yflag, zflag; + + xflag = (fa[0] >= COORD_RESOLUTION) || (fa[0] <= -COORD_RESOLUTION); + yflag = (fa[1] >= COORD_RESOLUTION) || (fa[1] <= -COORD_RESOLUTION); + zflag = (fa[2] >= COORD_RESOLUTION) || (fa[2] <= -COORD_RESOLUTION); + + WriteOneBit( xflag ); + WriteOneBit( yflag ); + WriteOneBit( zflag ); + + if ( xflag ) + WriteBitCoord( fa[0] ); + if ( yflag ) + WriteBitCoord( fa[1] ); + if ( zflag ) + WriteBitCoord( fa[2] ); +} + +void bf_write::WriteBitNormal( float f ) +{ + int signbit = (f <= -NORMAL_RESOLUTION); + + // NOTE: Since +/-1 are valid values for a normal, I'm going to encode that as all ones + unsigned int fractval = abs( (int)(f*NORMAL_DENOMINATOR) ); + + // clamp.. + if (fractval > NORMAL_DENOMINATOR) + fractval = NORMAL_DENOMINATOR; + + // Send the sign bit + WriteOneBit( signbit ); + + // Send the fractional component + WriteUBitLong( fractval, NORMAL_FRACTIONAL_BITS ); +} + +void bf_write::WriteBitVec3Normal( const Vector& fa ) +{ + int xflag, yflag; + + xflag = (fa[0] >= NORMAL_RESOLUTION) || (fa[0] <= -NORMAL_RESOLUTION); + yflag = (fa[1] >= NORMAL_RESOLUTION) || (fa[1] <= -NORMAL_RESOLUTION); + + WriteOneBit( xflag ); + WriteOneBit( yflag ); + + if ( xflag ) + WriteBitNormal( fa[0] ); + if ( yflag ) + WriteBitNormal( fa[1] ); + + // Write z sign bit + int signbit = (fa[2] <= -NORMAL_RESOLUTION); + WriteOneBit( signbit ); +} + +void bf_write::WriteBitAngles( const QAngle& fa ) +{ + // FIXME: + Vector tmp( fa.x, fa.y, fa.z ); + WriteBitVec3Coord( tmp ); +} + +void bf_write::WriteChar(int val) +{ + WriteSBitLong(val, sizeof(char) << 3); +} + +void bf_write::WriteByte(unsigned int val) +{ + WriteUBitLong(val, sizeof(unsigned char) << 3); +} + +void bf_write::WriteShort(int val) +{ + WriteSBitLong(val, sizeof(short) << 3); +} + +void bf_write::WriteWord(unsigned int val) +{ + WriteUBitLong(val, sizeof(unsigned short) << 3); +} + +void bf_write::WriteLong(long val) +{ + WriteSBitLong(val, sizeof(long) << 3); +} + +void bf_write::WriteLongLong(int64 val) +{ + uint *pLongs = (uint*)&val; + + // Insert the two DWORDS according to network endian + const short endianIndex = 0x0100; + byte *idx = (byte*)&endianIndex; + WriteUBitLong(pLongs[*idx++], sizeof(long) << 3); + WriteUBitLong(pLongs[*idx], sizeof(long) << 3); +} + +void bf_write::WriteFloat(float val) +{ + // Pre-swap the float, since WriteBits writes raw data + LittleFloat( &val, &val ); + + WriteBits(&val, sizeof(val) << 3); +} + +bool bf_write::WriteBytes( const void *pBuf, int nBytes ) +{ + return WriteBits(pBuf, nBytes << 3); +} + +bool bf_write::WriteString(const char *pStr) +{ + if(pStr) + { + do + { + WriteChar( *pStr ); + ++pStr; + } while( *(pStr-1) != 0 ); + } + else + { + WriteChar( 0 ); + } + + return !IsOverflowed(); +} + +bool bf_write::WriteString(const wchar_t *pStr) +{ + if(pStr) + { + do + { + WriteSBitLong( *pStr, 16 ); + ++pStr; + } while ( (*pStr-1) != 0 ); + } + else + { + WriteUBitLong( 0, 15 ); + WriteOneBit( 0 ); + } + + return !IsOverflowed(); +} + +// ---------------------------------------------------------------------------------------- // +// old_bf_read +// ---------------------------------------------------------------------------------------- // + +old_bf_read::old_bf_read() +{ + m_pData = NULL; + m_nDataBytes = 0; + m_nDataBits = -1; // set to -1 so we overflow on any operation + m_iCurBit = 0; + m_bOverflow = false; + m_bAssertOnOverflow = true; + m_pDebugName = NULL; +} + +old_bf_read::old_bf_read( const void *pData, int nBytes, int nBits ) +{ + m_bAssertOnOverflow = true; + StartReading( pData, nBytes, 0, nBits ); +} + +old_bf_read::old_bf_read( const char *pDebugName, const void *pData, int nBytes, int nBits ) +{ + m_bAssertOnOverflow = true; + m_pDebugName = pDebugName; + StartReading( pData, nBytes, 0, nBits ); +} + +void old_bf_read::StartReading( const void *pData, int nBytes, int iStartBit, int nBits ) +{ + // Make sure we're dword aligned. + Assert(((unsigned long)pData & 3) == 0); + + m_pData = (unsigned char*)pData; + m_nDataBytes = nBytes; + + if ( nBits == -1 ) + { + m_nDataBits = m_nDataBytes << 3; + } + else + { + Assert( nBits <= nBytes*8 ); + m_nDataBits = nBits; + } + + m_iCurBit = iStartBit; + m_bOverflow = false; +} + +void old_bf_read::Reset() +{ + m_iCurBit = 0; + m_bOverflow = false; +} + +void old_bf_read::SetAssertOnOverflow( bool bAssert ) +{ + m_bAssertOnOverflow = bAssert; +} + +const char* old_bf_read::GetDebugName() +{ + return m_pDebugName; +} + +void old_bf_read::SetDebugName( const char *pName ) +{ + m_pDebugName = pName; +} + +unsigned int old_bf_read::CheckReadUBitLong(int numbits) +{ + // Ok, just read bits out. + int i, nBitValue; + unsigned int r = 0; + + for(i=0; i < numbits; i++) + { + nBitValue = ReadOneBitNoCheck(); + r |= nBitValue << i; + } + m_iCurBit -= numbits; + + return r; +} + +void old_bf_read::ReadBits(void *pOutData, int nBits) +{ +#if defined( BB_PROFILING ) + VPROF( "old_bf_write::ReadBits" ); +#endif + + unsigned char *pOut = (unsigned char*)pOutData; + int nBitsLeft = nBits; + + + // align output to dword boundary + while( ((unsigned long)pOut & 3) != 0 && nBitsLeft >= 8 ) + { + *pOut = (unsigned char)ReadUBitLong(8); + ++pOut; + nBitsLeft -= 8; + } + + // X360TBD: Can't read dwords in ReadBits because they'll get swapped + if ( IsPC() ) + { + // read dwords + while ( nBitsLeft >= 32 ) + { + *((unsigned long*)pOut) = ReadUBitLong(32); + pOut += sizeof(unsigned long); + nBitsLeft -= 32; + } + } + + // read remaining bytes + while ( nBitsLeft >= 8 ) + { + *pOut = ReadUBitLong(8); + ++pOut; + nBitsLeft -= 8; + } + + // read remaining bits + if ( nBitsLeft ) + { + *pOut = ReadUBitLong(nBitsLeft); + } + +} + +float old_bf_read::ReadBitAngle( int numbits ) +{ + float fReturn; + int i; + float shift; + + shift = (float)( BitForBitnum(numbits) ); + + i = ReadUBitLong( numbits ); + fReturn = (float)i * (360.0 / shift); + + return fReturn; +} + +unsigned int old_bf_read::PeekUBitLong( int numbits ) +{ + unsigned int r; + int i, nBitValue; +#ifdef BIT_VERBOSE + int nShifts = numbits; +#endif + + old_bf_read savebf; + + savebf = *this; // Save current state info + + r = 0; + for(i=0; i < numbits; i++) + { + nBitValue = ReadOneBit(); + + // Append to current stream + if ( nBitValue ) + { + r |= BitForBitnum(i); + } + } + + *this = savebf; + +#ifdef BIT_VERBOSE + Con_Printf( "PeekBitLong: %i %i\n", nShifts, (unsigned int)r ); +#endif + + return r; +} + +// Append numbits least significant bits from data to the current bit stream +int old_bf_read::ReadSBitLong( int numbits ) +{ + int r, sign; + + r = ReadUBitLong(numbits - 1); + + // Note: it does this wierdness here so it's bit-compatible with regular integer data in the buffer. + // (Some old code writes direct integers right into the buffer). + sign = ReadOneBit(); + if(sign) + r = -((BitForBitnum(numbits-1)) - r); + + return r; +} + +const byte g_BitMask[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; +const byte g_TrailingMask[8] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80}; + +inline int old_bf_read::CountRunOfZeros() +{ + int bits = 0; + if ( m_iCurBit + 32 < m_nDataBits ) + { +#if !FAST_BIT_SCAN + while (true) + { + int value = (m_pData[m_iCurBit >> 3] & g_BitMask[m_iCurBit & 7]); + ++m_iCurBit; + if ( value ) + return bits; + ++bits; + } +#else + while (true) + { + int value = (m_pData[m_iCurBit >> 3] & g_TrailingMask[m_iCurBit & 7]); + if ( !value ) + { + int zeros = (8-(m_iCurBit&7)); + bits += zeros; + m_iCurBit += zeros; + } + else + { + int zeros = CountTrailingZeros(value) - (m_iCurBit & 7); + m_iCurBit += zeros + 1; + bits += zeros; + return bits; + } + } +#endif + } + else + { + while ( ReadOneBit() == 0 ) + bits++; + } + return bits; +} + +unsigned int old_bf_read::ReadUBitVar() +{ + switch( ReadUBitLong( 2 ) ) + { + case 0: + return ReadUBitLong( 4 ); + + case 1: + return ReadUBitLong( 8 ); + + case 2: + return ReadUBitLong( 12 ); + + default: + case 3: + return ReadUBitLong( 32 ); + } +#if 0 + int bits = CountRunOfZeros(); + + unsigned int data = BitForBitnum(bits)-1; + + // read the value + if ( bits > 0) + data += ReadUBitLong( bits ); + + return data; +#endif +} + + +unsigned int old_bf_read::ReadBitLong(int numbits, bool bSigned) +{ + if(bSigned) + return (unsigned int)ReadSBitLong(numbits); + else + return ReadUBitLong(numbits); +} + + +// Basic Coordinate Routines (these contain bit-field size AND fixed point scaling constants) +float old_bf_read::ReadBitCoord (void) +{ +#if defined( BB_PROFILING ) + VPROF( "old_bf_write::ReadBitCoord" ); +#endif + int intval=0,fractval=0,signbit=0; + float value = 0.0; + + + // Read the required integer and fraction flags + intval = ReadOneBit(); + fractval = ReadOneBit(); + + // If we got either parse them, otherwise it's a zero. + if ( intval || fractval ) + { + // Read the sign bit + signbit = ReadOneBit(); + + // If there's an integer, read it in + if ( intval ) + { + // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE] + intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1; + } + + // If there's a fraction, read it in + if ( fractval ) + { + fractval = ReadUBitLong( COORD_FRACTIONAL_BITS ); + } + + // Calculate the correct floating point value + value = intval + ((float)fractval * COORD_RESOLUTION); + + // Fixup the sign if negative. + if ( signbit ) + value = -value; + } + + return value; +} + +float old_bf_read::ReadBitCoordMP( EBitCoordType coordType ) +{ +#if defined( BB_PROFILING ) + VPROF( "old_bf_write::ReadBitCoordMP" ); +#endif + int intval=0,fractval=0,signbit=0; + float value = 0.0; + + + bool bInBounds = ReadOneBit() ? true : false; + + if ( coordType == kCW_Integral ) + { + // Read the required integer and fraction flags + intval = ReadOneBit(); + // If we got either parse them, otherwise it's a zero. + if ( intval ) + { + // Read the sign bit + signbit = ReadOneBit(); + + // If there's an integer, read it in + // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE] + if ( bInBounds ) + { + value = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1; + } + else + { + value = ReadUBitLong( COORD_INTEGER_BITS ) + 1; + } + } + } + else + { + // Read the required integer and fraction flags + intval = ReadOneBit(); + + // Read the sign bit + signbit = ReadOneBit(); + + // If we got either parse them, otherwise it's a zero. + if ( intval ) + { + if ( bInBounds ) + { + intval = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1; + } + else + { + intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1; + } + } + + // If there's a fraction, read it in + fractval = ReadUBitLong( coordType == kCW_LowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); + + // Calculate the correct floating point value + value = intval + ((float)fractval * ( coordType == kCW_LowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ) ); + } + + // Fixup the sign if negative. + if ( signbit ) + value = -value; + + return value; +} + +void old_bf_read::ReadBitVec3Coord( Vector& fa ) +{ + int xflag, yflag, zflag; + + // This vector must be initialized! Otherwise, If any of the flags aren't set, + // the corresponding component will not be read and will be stack garbage. + fa.Init( 0, 0, 0 ); + + xflag = ReadOneBit(); + yflag = ReadOneBit(); + zflag = ReadOneBit(); + + if ( xflag ) + fa[0] = ReadBitCoord(); + if ( yflag ) + fa[1] = ReadBitCoord(); + if ( zflag ) + fa[2] = ReadBitCoord(); +} + +float old_bf_read::ReadBitNormal (void) +{ + // Read the sign bit + int signbit = ReadOneBit(); + + // Read the fractional part + unsigned int fractval = ReadUBitLong( NORMAL_FRACTIONAL_BITS ); + + // Calculate the correct floating point value + float value = (float)fractval * NORMAL_RESOLUTION; + + // Fixup the sign if negative. + if ( signbit ) + value = -value; + + return value; +} + +void old_bf_read::ReadBitVec3Normal( Vector& fa ) +{ + int xflag = ReadOneBit(); + int yflag = ReadOneBit(); + + if (xflag) + fa[0] = ReadBitNormal(); + else + fa[0] = 0.0f; + + if (yflag) + fa[1] = ReadBitNormal(); + else + fa[1] = 0.0f; + + // The first two imply the third (but not its sign) + int znegative = ReadOneBit(); + + float fafafbfb = fa[0] * fa[0] + fa[1] * fa[1]; + if (fafafbfb < 1.0f) + fa[2] = sqrt( 1.0f - fafafbfb ); + else + fa[2] = 0.0f; + + if (znegative) + fa[2] = -fa[2]; +} + +void old_bf_read::ReadBitAngles( QAngle& fa ) +{ + Vector tmp; + ReadBitVec3Coord( tmp ); + fa.Init( tmp.x, tmp.y, tmp.z ); +} + +int old_bf_read::ReadChar() +{ + return ReadSBitLong(sizeof(char) << 3); +} + +int old_bf_read::ReadByte() +{ + return ReadUBitLong(sizeof(unsigned char) << 3); +} + +int old_bf_read::ReadShort() +{ + return ReadSBitLong(sizeof(short) << 3); +} + +int old_bf_read::ReadWord() +{ + return ReadUBitLong(sizeof(unsigned short) << 3); +} + +long old_bf_read::ReadLong() +{ + return ReadSBitLong(sizeof(long) << 3); +} + +int64 old_bf_read::ReadLongLong() +{ + int64 retval; + uint *pLongs = (uint*)&retval; + + // Read the two DWORDs according to network endian + const short endianIndex = 0x0100; + byte *idx = (byte*)&endianIndex; + pLongs[*idx++] = ReadUBitLong(sizeof(long) << 3); + pLongs[*idx] = ReadUBitLong(sizeof(long) << 3); + + return retval; +} + +float old_bf_read::ReadFloat() +{ + float ret; + Assert( sizeof(ret) == 4 ); + ReadBits(&ret, 32); + + // Swap the float, since ReadBits reads raw data + LittleFloat( &ret, &ret ); + return ret; +} + +bool old_bf_read::ReadBytes(void *pOut, int nBytes) +{ + ReadBits(pOut, nBytes << 3); + return !IsOverflowed(); +} + +bool old_bf_read::ReadString( char *pStr, int maxLen, bool bLine, int *pOutNumChars ) +{ + Assert( maxLen != 0 ); + + bool bTooSmall = false; + int iChar = 0; + while(1) + { + char val = ReadChar(); + if ( val == 0 ) + break; + else if ( bLine && val == '\n' ) + break; + + if ( iChar < (maxLen-1) ) + { + pStr[iChar] = val; + ++iChar; + } + else + { + bTooSmall = true; + } + } + + // Make sure it's null-terminated. + Assert( iChar < maxLen ); + pStr[iChar] = 0; + + if ( pOutNumChars ) + *pOutNumChars = iChar; + + return !IsOverflowed() && !bTooSmall; +} + + +char* old_bf_read::ReadAndAllocateString( bool *pOverflow ) +{ + char str[2048]; + + int nChars; + bool bOverflow = !ReadString( str, sizeof( str ), false, &nChars ); + if ( pOverflow ) + *pOverflow = bOverflow; + + // Now copy into the output and return it; + char *pRet = new char[ nChars + 1 ]; + for ( int i=0; i <= nChars; i++ ) + pRet[i] = str[i]; + + return pRet; +} + +void old_bf_read::ExciseBits( int startbit, int bitstoremove ) +{ + int endbit = startbit + bitstoremove; + int remaining_to_end = m_nDataBits - endbit; + + bf_write temp; + temp.StartWriting( (void *)m_pData, m_nDataBits << 3, startbit ); + + Seek( endbit ); + + for ( int i = 0; i < remaining_to_end; i++ ) + { + temp.WriteOneBit( ReadOneBit() ); + } + + Seek( startbit ); + + m_nDataBits -= bitstoremove; + m_nDataBytes = m_nDataBits >> 3; +} + + diff --git a/tier1/byteswap.cpp b/tier1/byteswap.cpp new file mode 100644 index 00000000..0c42a38d --- /dev/null +++ b/tier1/byteswap.cpp @@ -0,0 +1,90 @@ +//========= Copyright © 1996-2006, Valve LLC, All rights reserved. ============ +// +// Purpose: Low level byte swapping routines. +// +// $NoKeywords: $ +//============================================================================= + +#include "byteswap.h" + +//----------------------------------------------------------------------------- +// Copy a single field from the input buffer to the output buffer, swapping the bytes if necessary +//----------------------------------------------------------------------------- +void CByteswap::SwapFieldToTargetEndian( void* pOutputBuffer, void *pData, typedescription_t *pField ) +{ + switch ( pField->fieldType ) + { + case FIELD_CHARACTER: + SwapBufferToTargetEndian( (char*)pOutputBuffer, (char*)pData, pField->fieldSize ); + break; + + case FIELD_BOOLEAN: + SwapBufferToTargetEndian( (bool*)pOutputBuffer, (bool*)pData, pField->fieldSize ); + break; + + case FIELD_SHORT: + SwapBufferToTargetEndian( (short*)pOutputBuffer, (short*)pData, pField->fieldSize ); + break; + + case FIELD_FLOAT: + SwapBufferToTargetEndian( (uint*)pOutputBuffer, (uint*)pData, pField->fieldSize ); + break; + + case FIELD_INTEGER: + SwapBufferToTargetEndian( (int*)pOutputBuffer, (int*)pData, pField->fieldSize ); + break; + + case FIELD_VECTOR: + SwapBufferToTargetEndian( (uint*)pOutputBuffer, (uint*)pData, pField->fieldSize * 3 ); + break; + + case FIELD_VECTOR2D: + SwapBufferToTargetEndian( (uint*)pOutputBuffer, (uint*)pData, pField->fieldSize * 2 ); + break; + + case FIELD_QUATERNION: + SwapBufferToTargetEndian( (uint*)pOutputBuffer, (uint*)pData, pField->fieldSize * 4 ); + break; + + case FIELD_EMBEDDED: + { + typedescription_t *pEmbed = pField->td->dataDesc; + for ( int i = 0; i < pField->fieldSize; ++i ) + { + SwapFieldsToTargetEndian( (byte*)pOutputBuffer + pEmbed->fieldOffset, + (byte*)pData + pEmbed->fieldOffset, + pField->td ); + + pOutputBuffer = (byte*)pOutputBuffer + pField->fieldSizeInBytes; + pData = (byte*)pData + pField->fieldSizeInBytes; + } + } + break; + + default: + Assert(0); + } +} + +//----------------------------------------------------------------------------- +// Write a block of fields. Works a bit like the saverestore code. +//----------------------------------------------------------------------------- +void CByteswap::SwapFieldsToTargetEndian( void *pOutputBuffer, void *pBaseData, datamap_t *pDataMap ) +{ + // deal with base class first + if ( pDataMap->baseMap ) + { + SwapFieldsToTargetEndian( pOutputBuffer, pBaseData, pDataMap->baseMap ); + } + + typedescription_t *pFields = pDataMap->dataDesc; + int fieldCount = pDataMap->dataNumFields; + for ( int i = 0; i < fieldCount; ++i ) + { + typedescription_t *pField = &pFields[i]; + SwapFieldToTargetEndian( (BYTE*)pOutputBuffer + pField->fieldOffset, + (BYTE*)pBaseData + pField->fieldOffset, + pField ); + } +} + diff --git a/tier1/characterset.cpp b/tier1/characterset.cpp new file mode 100644 index 00000000..3914ae0a --- /dev/null +++ b/tier1/characterset.cpp @@ -0,0 +1,41 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//============================================================================= + +#include +#include "characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: builds a simple lookup table of a group of important characters +// Input : *pParseGroup - pointer to the buffer for the group +// *pGroupString - null terminated list of characters to flag +//----------------------------------------------------------------------------- +void CharacterSetBuild( characterset_t *pSetBuffer, const char *pszSetString ) +{ + int i = 0; + + // Test our pointers + if ( !pSetBuffer || !pszSetString ) + return; + + memset( pSetBuffer->set, 0, sizeof(pSetBuffer->set) ); + + while ( pszSetString[i] ) + { + pSetBuffer->set[ static_cast(pszSetString[i]) ] = 1; + i++; + } + +} diff --git a/tier1/checksum_crc.cpp b/tier1/checksum_crc.cpp new file mode 100644 index 00000000..cb1d34a3 --- /dev/null +++ b/tier1/checksum_crc.cpp @@ -0,0 +1,180 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Generic CRC functions +// +//=============================================================================// + +#include "basetypes.h" +#include "commonmacros.h" +#include "checksum_crc.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define CRC32_INIT_VALUE 0xFFFFFFFFUL +#define CRC32_XOR_VALUE 0xFFFFFFFFUL + +#define NUM_BYTES 256 +static const CRC32_t pulCRCTable[NUM_BYTES] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +void CRC32_Init(CRC32_t *pulCRC) +{ + *pulCRC = CRC32_INIT_VALUE; +} + +void CRC32_Final(CRC32_t *pulCRC) +{ + *pulCRC ^= CRC32_XOR_VALUE; +} + +CRC32_t CRC32_GetTableEntry( unsigned int slot ) +{ + return pulCRCTable[(unsigned char)slot]; +} + +void CRC32_ProcessBuffer(CRC32_t *pulCRC, const void *pBuffer, int nBuffer) +{ + CRC32_t ulCrc = *pulCRC; + uintp pb = (uintp)pBuffer; + unsigned int nFront; + int nMain; + +JustAfew: + + switch (nBuffer) + { + case 7: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 6: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 5: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 4: + ulCrc ^= LittleLong( *(CRC32_t *)pb ); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + *pulCRC = ulCrc; + return; + + case 3: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 2: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 1: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 0: + *pulCRC = ulCrc; + return; + } + + // We may need to do some alignment work up front, and at the end, so that + // the main loop is aligned and only has to worry about 8 byte at a time. + // + // The low-order two bits of pb and nBuffer in total control the + // upfront work. + // + nFront = pb & 3; + nBuffer -= nFront; + switch (nFront) + { + case 3: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + case 2: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + case 1: + ulCrc = pulCRCTable[*(unsigned char *)(pb++) ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + } + + nMain = nBuffer >> 3; + while (nMain--) + { + ulCrc ^= LittleLong( *(CRC32_t *)pb ); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc ^= LittleLong( *(CRC32_t *)(pb + 4) ); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + ulCrc = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8); + pb += 8; + } + + nBuffer &= 7; + goto JustAfew; +} diff --git a/tier1/checksum_md5.cpp b/tier1/checksum_md5.cpp new file mode 100644 index 00000000..0c4aeefa --- /dev/null +++ b/tier1/checksum_md5.cpp @@ -0,0 +1,271 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#include "basetypes.h" +#include "commonmacros.h" +#include "checksum_md5.h" +#include +#include +#include "tier1/strtools.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// The four core functions - F1 is optimized somewhat +// #define F1(x, y, z) (x & y | ~x & z) +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +// This is the central step in the MD5 algorithm. +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +//----------------------------------------------------------------------------- +// Purpose: The core of the MD5 algorithm, this alters an existing MD5 hash to +// reflect the addition of 16 longwords of new data. MD5Update blocks +// the data and converts bytes into longwords for this routine. +// Input : buf[4] - +// in[16] - +// Output : static void +//----------------------------------------------------------------------------- +static void MD5Transform(unsigned int buf[4], unsigned int const in[16]) +{ + register unsigned int a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +//----------------------------------------------------------------------------- +// Purpose: Start MD5 accumulation. Set bit count to 0 and buffer to mysterious initialization constants. + +// Input : *ctx - +//----------------------------------------------------------------------------- +void MD5Init(MD5Context_t *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Update context to reflect the concatenation of another buffer full of bytes. +// Input : *ctx - +// *buf - +// len - +//----------------------------------------------------------------------------- +void MD5Update(MD5Context_t *ctx, unsigned char const *buf, unsigned int len) +{ + unsigned int t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((unsigned int) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) + { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) + { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + //byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (unsigned int *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) + { + memcpy(ctx->in, buf, 64); + //byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (unsigned int *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +//----------------------------------------------------------------------------- +// Purpose: Final wrapup - pad to 64-byte boundary with the bit pattern +// 1 0* (64-bit count of bits processed, MSB-first) +// Input : digest[MD5_DIGEST_LENGTH] - +// *ctx - +//----------------------------------------------------------------------------- +void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5Context_t *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + //byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (unsigned int *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } + else + { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + //byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((unsigned int *) ctx->in)[14] = ctx->bits[0]; + ((unsigned int *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (unsigned int *) ctx->in); + //byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, MD5_DIGEST_LENGTH); + memset(ctx, 0, sizeof(MD5Context_t)); /* In case it's sensitive */ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *hash - +// hashlen - +// Output : char +//----------------------------------------------------------------------------- +char *MD5_Print( unsigned char *hash, int hashlen ) +{ + static char szReturn[64]; + + Assert( hashlen <= 32 ); + + Q_binarytohex( hash, hashlen, szReturn, sizeof( szReturn ) ); + return szReturn; +} + +//----------------------------------------------------------------------------- +// Purpose: generate pseudo random number from a seed number +// Input : seed number +// Output : pseudo random number +//----------------------------------------------------------------------------- +unsigned int MD5_PseudoRandom(unsigned int nSeed) +{ + MD5Context_t ctx; + unsigned char digest[MD5_DIGEST_LENGTH]; // The MD5 Hash + + memset( &ctx, 0, sizeof( ctx ) ); + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)&nSeed, sizeof(nSeed) ); + MD5Final(digest, &ctx); + + return *(unsigned int*)(digest+6); // use 4 middle bytes for random value +} diff --git a/tier1/commandbuffer.cpp b/tier1/commandbuffer.cpp new file mode 100644 index 00000000..a8f600a7 --- /dev/null +++ b/tier1/commandbuffer.cpp @@ -0,0 +1,636 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#include "tier1/CommandBuffer.h" +#include "tier1/utlbuffer.h" +#include "tier1/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MAX_ALIAS_NAME 32 +#define MAX_COMMAND_LENGTH 1024 + +struct cmdalias_t +{ + cmdalias_t *next; + char name[ MAX_ALIAS_NAME ]; + char *value; +}; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CCommandBuffer::CCommandBuffer( ) : m_Commands( 32, 32 ) +{ + m_hNextCommand = m_Commands.InvalidIndex(); + m_nWaitDelayTicks = 1; + m_nCurrentTick = 0; + m_nLastTickToProcess = -1; + m_nArgSBufferSize = 0; + m_bIsProcessingCommands = false; + m_nMaxArgSBufferLength = ARGS_BUFFER_LENGTH; +} + +CCommandBuffer::~CCommandBuffer() +{ +} + + +//----------------------------------------------------------------------------- +// Indicates how long to delay when encoutering a 'wait' command +//----------------------------------------------------------------------------- +void CCommandBuffer::SetWaitDelayTime( int nTickDelay ) +{ + Assert( nTickDelay >= 0 ); + m_nWaitDelayTicks = nTickDelay; +} + + +//----------------------------------------------------------------------------- +// Specifies a max limit of the args buffer. For unittesting. Size == 0 means use default +//----------------------------------------------------------------------------- +void CCommandBuffer::LimitArgumentBufferSize( int nSize ) +{ + if ( nSize > ARGS_BUFFER_LENGTH ) + { + nSize = ARGS_BUFFER_LENGTH; + } + + m_nMaxArgSBufferLength = ( nSize == 0 ) ? ARGS_BUFFER_LENGTH : nSize; +} + + +//----------------------------------------------------------------------------- +// Parses argv0 out of the buffer +//----------------------------------------------------------------------------- +bool CCommandBuffer::ParseArgV0( CUtlBuffer &buf, char *pArgV0, int nMaxLen, const char **pArgS ) +{ + pArgV0[0] = 0; + *pArgS = NULL; + + if ( !buf.IsValid() ) + return false; + + int nSize = buf.ParseToken( CCommand::DefaultBreakSet(), pArgV0, nMaxLen ); + if ( ( nSize <= 0 ) || ( nMaxLen == nSize ) ) + return false; + + int nArgSLen = buf.TellMaxPut() - buf.TellGet(); + *pArgS = (nArgSLen > 0) ? (const char*)buf.PeekGet() : NULL; + return true; +} + + +//----------------------------------------------------------------------------- +// Insert a command into the command queue +//----------------------------------------------------------------------------- +void CCommandBuffer::InsertCommandAtAppropriateTime( int hCommand ) +{ + int i; + Command_t &command = m_Commands[hCommand]; + for ( i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) + { + if ( m_Commands[i].m_nTick > command.m_nTick ) + break; + } + m_Commands.LinkBefore( i, hCommand ); +} + + +//----------------------------------------------------------------------------- +// Insert a command into the command queue at the appropriate time +//----------------------------------------------------------------------------- +void CCommandBuffer::InsertImmediateCommand( int hCommand ) +{ + m_Commands.LinkBefore( m_hNextCommand, hCommand ); +} + + +//----------------------------------------------------------------------------- +// Insert a command into the command queue +//----------------------------------------------------------------------------- +bool CCommandBuffer::InsertCommand( const char *pArgS, int nCommandSize, int nTick ) +{ + if ( nCommandSize >= CCommand::MaxCommandLength() ) + { + Warning( "WARNING: Command too long... ignoring!\n%s\n", pArgS ); + return false; + } + + // Add one for null termination + if ( m_nArgSBufferSize + nCommandSize + 1 > m_nMaxArgSBufferLength ) + { + Compact(); + if ( m_nArgSBufferSize + nCommandSize + 1 > m_nMaxArgSBufferLength ) + return false; + } + + memcpy( &m_pArgSBuffer[m_nArgSBufferSize], pArgS, nCommandSize ); + m_pArgSBuffer[m_nArgSBufferSize + nCommandSize] = 0; + ++nCommandSize; + + int hCommand = m_Commands.Alloc(); + Command_t &command = m_Commands[hCommand]; + command.m_nTick = nTick; + command.m_nFirstArgS = m_nArgSBufferSize; + command.m_nBufferSize = nCommandSize; + + m_nArgSBufferSize += nCommandSize; + + if ( !m_bIsProcessingCommands || ( nTick > m_nCurrentTick ) ) + { + InsertCommandAtAppropriateTime( hCommand ); + } + else + { + InsertImmediateCommand( hCommand ); + } + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the length of the next command +//----------------------------------------------------------------------------- +void CCommandBuffer::GetNextCommandLength( const char *pText, int nMaxLen, int *pCommandLength, int *pNextCommandOffset ) +{ + int nCommandLength = 0; + int nNextCommandOffset; + bool bIsQuoted = false; + bool bIsCommented = false; + for ( nNextCommandOffset=0; nNextCommandOffset < nMaxLen; ++nNextCommandOffset, nCommandLength += bIsCommented ? 0 : 1 ) + { + char c = pText[nNextCommandOffset]; + if ( !bIsCommented ) + { + if ( c == '"' ) + { + bIsQuoted = !bIsQuoted; + continue; + } + + // don't break if inside a C++ style comment + if ( !bIsQuoted && c == '/' ) + { + bIsCommented = ( nNextCommandOffset < nMaxLen-1 ) && pText[nNextCommandOffset+1] == '/'; + if ( bIsCommented ) + { + ++nNextCommandOffset; + continue; + } + } + + // don't break if inside a quoted string + if ( !bIsQuoted && c == ';' ) + break; + } + + // FIXME: This is legacy behavior; should we not break if a \n is inside a quoted string? + if ( c == '\n' ) + break; + } + + *pCommandLength = nCommandLength; + *pNextCommandOffset = nNextCommandOffset; +} + + +//----------------------------------------------------------------------------- +// Add text to command buffer, return false if it couldn't owing to overflow +//----------------------------------------------------------------------------- +bool CCommandBuffer::AddText( const char *pText, int nTickDelay ) +{ + Assert( nTickDelay >= 0 ); + + int nLen = Q_strlen( pText ); + int nTick = m_nCurrentTick + nTickDelay; + + // Parse the text into distinct commands + const char *pCurrentCommand = pText; + int nOffsetToNextCommand; + for( ; nLen > 0; nLen -= nOffsetToNextCommand+1, pCurrentCommand += nOffsetToNextCommand+1 ) + { + // find a \n or ; line break + int nCommandLength; + GetNextCommandLength( pCurrentCommand, nLen, &nCommandLength, &nOffsetToNextCommand ); + if ( nCommandLength <= 0 ) + continue; + + const char *pArgS; + char *pArgV0 = (char*)_alloca( nCommandLength+1 ); + CUtlBuffer bufParse( pCurrentCommand, nCommandLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY ); + ParseArgV0( bufParse, pArgV0, nCommandLength+1, &pArgS ); + if ( pArgV0[0] == 0 ) + continue; + + // Deal with the special 'wait' command + if ( !Q_stricmp( pArgV0, "wait" ) ) + { + int nDelay = pArgS ? atoi( pArgS ) : m_nWaitDelayTicks; + nTick += nDelay; + continue; + } + + if ( !InsertCommand( pCurrentCommand, nCommandLength, nTick ) ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Are we in the middle of processing commands? +//----------------------------------------------------------------------------- +bool CCommandBuffer::IsProcessingCommands() +{ + return m_bIsProcessingCommands; +} + + +//----------------------------------------------------------------------------- +// Delays all queued commands to execute at a later time +//----------------------------------------------------------------------------- +void CCommandBuffer::DelayAllQueuedCommands( int nDelay ) +{ + if ( nDelay <= 0 ) + return; + + for ( int i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) + { + m_Commands[i].m_nTick += nDelay; + } +} + + +//----------------------------------------------------------------------------- +// Call this to begin iterating over all commands up to flCurrentTime +//----------------------------------------------------------------------------- +void CCommandBuffer::BeginProcessingCommands( int nDeltaTicks ) +{ + if ( nDeltaTicks == 0 ) + return; + + Assert( !m_bIsProcessingCommands ); + m_bIsProcessingCommands = true; + m_nLastTickToProcess = m_nCurrentTick + nDeltaTicks - 1; + + // Necessary to insert commands while commands are being processed + m_hNextCommand = m_Commands.Head(); +} + + +//----------------------------------------------------------------------------- +// Returns the next command +//----------------------------------------------------------------------------- +bool CCommandBuffer::DequeueNextCommand( ) +{ + m_CurrentCommand.Reset(); + + Assert( m_bIsProcessingCommands ); + if ( m_Commands.Count() == 0 ) + return false; + + int nHead = m_Commands.Head(); + Command_t &command = m_Commands[ nHead ]; + if ( command.m_nTick > m_nLastTickToProcess ) + return false; + + m_nCurrentTick = command.m_nTick; + + // Copy the current command into a temp buffer + // NOTE: This is here to avoid the pointers returned by DequeueNextCommand + // to become invalid by calling AddText. Is there a way we can avoid the memcpy? + if ( command.m_nBufferSize > 0 ) + { + m_CurrentCommand.Tokenize( &m_pArgSBuffer[command.m_nFirstArgS] ); + } + + m_Commands.Remove( nHead ); + + // Necessary to insert commands while commands are being processed + m_hNextCommand = m_Commands.Head(); + +// Msg("Dequeue : "); +// for ( int i = 0; i < nArgc; ++i ) +// { +// Msg("%s ", m_pCurrentArgv[i] ); +// } +// Msg("\n"); + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the next command +//----------------------------------------------------------------------------- +int CCommandBuffer::DequeueNextCommand( const char **& ppArgv ) +{ + DequeueNextCommand(); + ppArgv = ArgV(); + return ArgC(); +} + + +//----------------------------------------------------------------------------- +// Compacts the command buffer +//----------------------------------------------------------------------------- +void CCommandBuffer::Compact() +{ + // Compress argvbuffer + argv + // NOTE: I'm using this choice instead of calling malloc + free + // per command to allocate arguments because I expect to post a + // bunch of commands but not have many delayed commands; + // avoiding the allocation cost seems more important that the memcpy + // cost here since I expect to not have much to copy. + m_nArgSBufferSize = 0; + + char pTempBuffer[ ARGS_BUFFER_LENGTH ]; + for ( int i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) + { + Command_t &command = m_Commands[ i ]; + + memcpy( &pTempBuffer[m_nArgSBufferSize], &m_pArgSBuffer[command.m_nFirstArgS], command.m_nBufferSize ); + command.m_nFirstArgS = m_nArgSBufferSize; + m_nArgSBufferSize += command.m_nBufferSize; + } + + // NOTE: We could also store 2 buffers in the command buffer and switch + // between the two to avoid the 2nd memcpy; but again I'm guessing the memory + // tradeoff isn't worth it + memcpy( m_pArgSBuffer, pTempBuffer, m_nArgSBufferSize ); +} + + +//----------------------------------------------------------------------------- +// Call this to finish iterating over all commands +//----------------------------------------------------------------------------- +void CCommandBuffer::EndProcessingCommands() +{ + Assert( m_bIsProcessingCommands ); + m_bIsProcessingCommands = false; + m_nCurrentTick = m_nLastTickToProcess + 1; + m_hNextCommand = m_Commands.InvalidIndex(); + + // Extract commands that are before the end time + // NOTE: This is a bug for this to + int i = m_Commands.Head(); + if ( i == m_Commands.InvalidIndex() ) + { + m_nArgSBufferSize = 0; + return; + } + + while ( i != m_Commands.InvalidIndex() ) + { + if ( m_Commands[i].m_nTick >= m_nCurrentTick ) + break; + + AssertMsgOnce( false, "CCommandBuffer::EndProcessingCommands() called before all appropriate commands were dequeued.\n" ); + int nNext = i; + Msg( "Warning: Skipping command %s\n", m_pArgSBuffer[ m_Commands[i].m_nFirstArgS ] ); + m_Commands.Remove( i ); + i = nNext; + } + + Compact(); +} + + +//----------------------------------------------------------------------------- +// Returns a handle to the next command to process +//----------------------------------------------------------------------------- +CommandHandle_t CCommandBuffer::GetNextCommandHandle() +{ + Assert( m_bIsProcessingCommands ); + return m_Commands.Head(); +} + + +#if 0 +/* +=============== +Cmd_Alias_f + +Creates a new command that executes a command string (possibly ; seperated) +=============== +*/ +void Cmd_Alias_f (void) +{ + cmdalias_t *a; + char cmd[MAX_COMMAND_LENGTH]; + int i, c; + char *s; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Current alias commands:\n"); + for (a = cmd_alias ; a ; a=a->next) + Con_Printf ("%s : %s\n", a->name, a->value); + return; + } + + s = Cmd_Argv(1); + if (strlen(s) >= MAX_ALIAS_NAME) + { + Con_Printf ("Alias name is too long\n"); + return; + } + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + c = Cmd_Argc(); + for (i=2 ; i< c ; i++) + { + Q_strncat(cmd, Cmd_Argv(i), sizeof( cmd ), COPY_ALL_CHARACTERS); + if (i != c) + { + Q_strncat (cmd, " ", sizeof( cmd ), COPY_ALL_CHARACTERS ); + } + } + Q_strncat (cmd, "\n", sizeof( cmd ), COPY_ALL_CHARACTERS); + + // if the alias already exists, reuse it + for (a = cmd_alias ; a ; a=a->next) + { + if (!strcmp(s, a->name)) + { + if ( !strcmp( a->value, cmd ) ) // Re-alias the same thing + return; + + delete[] a->value; + break; + } + } + + if (!a) + { + a = (cmdalias_t *)new cmdalias_t; + a->next = cmd_alias; + cmd_alias = a; + } + Q_strncpy (a->name, s, sizeof( a->name ) ); + + a->value = COM_StringCopy(cmd); +} + + + +/* +============================================================================= + + COMMAND EXECUTION + +============================================================================= +*/ + +#define MAX_ARGS 80 + +static int cmd_argc; +static char *cmd_argv[MAX_ARGS]; +static char *cmd_null_string = ""; +static const char *cmd_args = NULL; + +cmd_source_t cmd_source; + +//----------------------------------------------------------------------------- +// Purpose: +// Output : void Cmd_Init +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Cmd_Shutdown( void ) +{ + // TODO, cleanup + while ( cmd_alias ) + { + cmdalias_t *next = cmd_alias->next; + delete cmd_alias->value; // created by StringCopy() + delete cmd_alias; + cmd_alias = next; + } +} + + + +/* +============ +Cmd_ExecuteString + +A complete command line has been parsed, so try to execute it +FIXME: lookupnoadd the token to speed search? +============ +*/ +const ConCommandBase *Cmd_ExecuteString (const char *text, cmd_source_t src) +{ + cmdalias_t *a; + + cmd_source = src; + Cmd_TokenizeString (text); + +// execute the command line + if (!Cmd_Argc()) + return NULL; // no tokens + +// check alias + for (a=cmd_alias ; a ; a=a->next) + { + if (!Q_strcasecmp (cmd_argv[0], a->name)) + { + Cbuf_InsertText (a->value); + return NULL; + } + } + +// check ConCommands + ConCommandBase const *pCommand = ConCommandBase::FindCommand( cmd_argv[ 0 ] ); + if ( pCommand && pCommand->IsCommand() ) + { + bool isServerCommand = ( pCommand->IsBitSet( FCVAR_GAMEDLL ) && + // Typed at console + cmd_source == src_command && + // Not HLDS + !sv.IsDedicated() ); + + // Hook to allow game .dll to figure out who type the message on a listen server + if ( serverGameClients ) + { + // We're actually the server, so set it up locally + if ( sv.IsActive() ) + { + g_pServerPluginHandler->SetCommandClient( -1 ); + +#ifndef SWDS + // Special processing for listen server player + if ( isServerCommand ) + { + g_pServerPluginHandler->SetCommandClient( cl.m_nPlayerSlot ); + } +#endif + } + // We're not the server, but we've been a listen server (game .dll loaded) + // forward this command tot he server instead of running it locally if we're still + // connected + // Otherwise, things like "say" won't work unless you quit and restart + else if ( isServerCommand ) + { + if ( cl.IsConnected() ) + { + Cmd_ForwardToServer(); + return NULL; + } + else + { + // It's a server command, but we're not connected to a server. Don't try to execute it. + return NULL; + } + } + } + + // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on +#ifndef _DEBUG + if ( pCommand->IsBitSet( FCVAR_CHEAT ) ) + { + if ( !Host_IsSinglePlayerGame() && sv_cheats.GetInt() == 0 ) + { + Msg( "Can't use cheat command %s in multiplayer, unless the server has sv_cheats set to 1.\n", pCommand->GetName() ); + return NULL; + } + } +#endif + + (( ConCommand * )pCommand )->Dispatch(); + return pCommand; + } + + // check cvars + if ( cv->IsCommand() ) + { + return pCommand; + } + + // forward the command line to the server, so the entity DLL can parse it + if ( cmd_source == src_command ) + { + if ( cl.IsConnected() ) + { + Cmd_ForwardToServer(); + return NULL; + } + } + + Msg("Unknown command \"%s\"\n", Cmd_Argv(0)); + + return NULL; +} +#endif diff --git a/tier1/convar.cpp b/tier1/convar.cpp new file mode 100644 index 00000000..678fba6d --- /dev/null +++ b/tier1/convar.cpp @@ -0,0 +1,1360 @@ + +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include +#include +#include +#include "basetypes.h" +#include "tier1/convar.h" +#include "tier1/strtools.h" +#include "tier1/characterset.h" +#include "tier1/utlbuffer.h" +#include "tier1/tier1.h" +#include "tier1/convar_serverbounded.h" +#include "icvar.h" +#include "tier0/dbg.h" +#include "Color.h" +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#endif +#include "tier0/memdbgon.h" + + +// Comment this out when we release. +//#define ALLOW_DEVELOPMENT_CVARS + + + +//----------------------------------------------------------------------------- +// Statically constructed list of ConCommandBases, +// used for registering them with the ICVar interface +//----------------------------------------------------------------------------- +ConCommandBase *ConCommandBase::s_pConCommandBases = NULL; +IConCommandBaseAccessor *ConCommandBase::s_pAccessor = NULL; +static int64 s_nCVarFlag = 0; +static int s_nDLLIdentifier = -1; // A unique identifier indicating which DLL this convar came from +static bool s_bRegistered = false; + +class CDefaultAccessor : public IConCommandBaseAccessor +{ +public: + virtual bool RegisterConCommandBase( ConCommandBase *pVar ) + { + // Link to engine's list instead + g_pCVar->RegisterConCommand( pVar ); + return true; + } +}; + +static CDefaultAccessor s_DefaultAccessor; + +//----------------------------------------------------------------------------- +// Called by the framework to register ConCommandBases with the ICVar +//----------------------------------------------------------------------------- +void ConVar_Register( int64 nCVarFlag, IConCommandBaseAccessor *pAccessor ) +{ + if ( !g_pCVar || s_bRegistered ) + return; + + Assert( s_nDLLIdentifier < 0 ); + s_bRegistered = true; + s_nCVarFlag = nCVarFlag; + s_nDLLIdentifier = g_pCVar->AllocateDLLIdentifier(); + + ConCommandBase *pCur, *pNext; + + ConCommandBase::s_pAccessor = pAccessor ? pAccessor : &s_DefaultAccessor; + pCur = ConCommandBase::s_pConCommandBases; + while ( pCur ) + { + pNext = pCur->m_pNext; + pCur->AddFlags( s_nCVarFlag ); + pCur->Init(); + pCur = pNext; + } + + ConCommandBase::s_pConCommandBases = NULL; +} + +void ConVar_Unregister( ) +{ + if ( !g_pCVar || !s_bRegistered ) + return; + + Assert( s_nDLLIdentifier >= 0 ); + g_pCVar->UnregisterConCommands( s_nDLLIdentifier ); + s_nDLLIdentifier = -1; + s_bRegistered = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Default constructor +//----------------------------------------------------------------------------- +ConCommandBase::ConCommandBase( void ) +{ + m_bRegistered = false; + m_pszName = NULL; + m_pszHelpString = NULL; + + m_nFlags = 0; + m_pNext = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: The base console invoked command/cvar interface +// Input : *pName - name of variable/command +// *pHelpString - help text +// flags - flags +//----------------------------------------------------------------------------- +ConCommandBase::ConCommandBase( const char *pName, const char *pHelpString /*=0*/, int64 flags /*= 0*/ ) +{ + Create( pName, pHelpString, flags ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ConCommandBase::~ConCommandBase( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ConCommandBase::IsCommand( void ) const +{ +// Assert( 0 ); This can't assert. . causes a recursive assert in Sys_Printf, etc. + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the DLL identifier +//----------------------------------------------------------------------------- +CVarDLLIdentifier_t ConCommandBase::GetDLLIdentifier() const +{ + return s_nDLLIdentifier; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pName - +// callback - +// *pHelpString - +// flags - +//----------------------------------------------------------------------------- +void ConCommandBase::Create( const char *pName, const char *pHelpString /*= 0*/, int64 flags /*= 0*/ ) +{ + static const char *empty_string = ""; + + m_bRegistered = false; + + // Name should be static data + Assert( pName ); + m_pszName = pName; + m_pszHelpString = pHelpString ? pHelpString : empty_string; + + m_nFlags = flags; + +#ifdef ALLOW_DEVELOPMENT_CVARS + m_nFlags &= ~FCVAR_DEVELOPMENTONLY; +#endif + + if ( !( m_nFlags & FCVAR_UNREGISTERED ) ) + { + m_pNext = s_pConCommandBases; + s_pConCommandBases = this; + } + else + { + // It's unregistered + m_pNext = NULL; + } + + // If s_pAccessor is already set (this ConVar is not a global variable), + // register it. + if ( s_pAccessor ) + { + Init(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Used internally by OneTimeInit to initialize. +//----------------------------------------------------------------------------- +void ConCommandBase::Init() +{ + if ( s_pAccessor ) + { + s_pAccessor->RegisterConCommandBase( this ); + } +} + +void ConCommandBase::Shutdown() +{ + if ( g_pCVar ) + { + g_pCVar->UnregisterConCommand( this ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Return name of the command/var +// Output : const char +//----------------------------------------------------------------------------- +const char *ConCommandBase::GetName( void ) const +{ + return m_pszName; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flag - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ConCommandBase::IsFlagSet( int64 flag ) const +{ + return ( flag & m_nFlags ) ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flags - +//----------------------------------------------------------------------------- +void ConCommandBase::AddFlags( int64 flags ) +{ + m_nFlags |= flags; + +#ifdef ALLOW_DEVELOPMENT_CVARS + m_nFlags &= ~FCVAR_DEVELOPMENTONLY; +#endif +} + +void ConCommandBase::RemoveFlags( int64 flags ) +{ + m_nFlags &= ~flags; +} + +int64 ConCommandBase::GetFlags( void ) const +{ + return m_nFlags; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const ConCommandBase +//----------------------------------------------------------------------------- +const ConCommandBase *ConCommandBase::GetNext( void ) const +{ + return m_pNext; +} + +ConCommandBase *ConCommandBase::GetNext( void ) +{ + return m_pNext; +} + + +//----------------------------------------------------------------------------- +// Purpose: Copies string using local new/delete operators +// Input : *from - +// Output : char +//----------------------------------------------------------------------------- +char *ConCommandBase::CopyString( const char *from ) +{ + int len; + char *to; + + len = strlen( from ); + if ( len <= 0 ) + { + to = new char[1]; + to[0] = 0; + } + else + { + to = new char[len+1]; + Q_strncpy( to, from, len+1 ); + } + return to; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *ConCommandBase::GetHelpText( void ) const +{ + return m_pszHelpString; +} + +//----------------------------------------------------------------------------- +// Purpose: Has this cvar been registered +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ConCommandBase::IsRegistered( void ) const +{ + return m_bRegistered; +} + +bool ConCommandBase::IsBoundedVar(void) const +{ + return false; +} + +//----------------------------------------------------------------------------- +// +// Con Commands start here +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Global methods +//----------------------------------------------------------------------------- +static characterset_t s_BreakSet; +static bool s_bBuiltBreakSet = false; + + +//----------------------------------------------------------------------------- +// Tokenizer class +//----------------------------------------------------------------------------- +CCommand::CCommand() +{ + if ( !s_bBuiltBreakSet ) + { + s_bBuiltBreakSet = true; + CharacterSetBuild( &s_BreakSet, "{}()':" ); + } + + Reset(); +} + +CCommand::CCommand( int nArgC, const char **ppArgV ) +{ + Assert( nArgC > 0 ); + + if ( !s_bBuiltBreakSet ) + { + s_bBuiltBreakSet = true; + CharacterSetBuild( &s_BreakSet, "{}()':" ); + } + + Reset(); + + char *pBuf = m_pArgvBuffer; + char *pSBuf = m_pArgSBuffer; + m_nArgc = nArgC; + for ( int i = 0; i < nArgC; ++i ) + { + m_ppArgv[i] = pBuf; + int nLen = Q_strlen( ppArgV[i] ); + memcpy( pBuf, ppArgV[i], nLen+1 ); + if ( i == 0 ) + { + m_nArgv0Size = nLen; + } + pBuf += nLen+1; + + bool bContainsSpace = strchr( ppArgV[i], ' ' ) != NULL; + if ( bContainsSpace ) + { + *pSBuf++ = '\"'; + } + memcpy( pSBuf, ppArgV[i], nLen ); + pSBuf += nLen; + if ( bContainsSpace ) + { + *pSBuf++ = '\"'; + } + + if ( i != nArgC - 1 ) + { + *pSBuf++ = ' '; + } + } +} + +void CCommand::Reset() +{ + m_nArgc = 0; + m_nArgv0Size = 0; + m_pArgSBuffer[0] = 0; +} + +characterset_t* CCommand::DefaultBreakSet() +{ + return &s_BreakSet; +} + +bool CCommand::Tokenize( const char *pCommand, characterset_t *pBreakSet ) +{ + Reset(); + if ( !pCommand ) + return false; + + // Use default break set + if ( !pBreakSet ) + { + pBreakSet = &s_BreakSet; + } + + // Copy the current command into a temp buffer + // NOTE: This is here to avoid the pointers returned by DequeueNextCommand + // to become invalid by calling AddText. Is there a way we can avoid the memcpy? + int nLen = Q_strlen( pCommand ); + if ( nLen >= COMMAND_MAX_LENGTH - 1 ) + { + Warning( "CCommand::Tokenize: Encountered command which overflows the tokenizer buffer.. Skipping!\n" ); + return false; + } + + memcpy( m_pArgSBuffer, pCommand, nLen + 1 ); + + // Parse the current command into the current command buffer + CUtlBuffer bufParse( m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY ); + int nArgvBufferSize = 0; + while ( bufParse.IsValid() && ( m_nArgc < COMMAND_MAX_ARGC ) ) + { + char *pArgvBuf = &m_pArgvBuffer[nArgvBufferSize]; + int nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize; + int nStartGet = bufParse.TellGet(); + int nSize = bufParse.ParseToken( pBreakSet, pArgvBuf, nMaxLen ); + if ( nSize < 0 ) + break; + + // Check for overflow condition + if ( nMaxLen == nSize ) + { + Reset(); + return false; + } + + if ( m_nArgc == 1 ) + { + // Deal with the case where the arguments were quoted + m_nArgv0Size = bufParse.TellGet(); + bool bFoundEndQuote = m_pArgSBuffer[m_nArgv0Size-1] == '\"'; + if ( bFoundEndQuote ) + { + --m_nArgv0Size; + } + m_nArgv0Size -= nSize; + Assert( m_nArgv0Size != 0 ); + + // The StartGet check is to handle this case: "foo"bar + // which will parse into 2 different args. ArgS should point to bar. + bool bFoundStartQuote = ( m_nArgv0Size > nStartGet ) && ( m_pArgSBuffer[m_nArgv0Size-1] == '\"' ); + Assert( bFoundEndQuote == bFoundStartQuote ); + if ( bFoundStartQuote ) + { + --m_nArgv0Size; + } + } + + m_ppArgv[ m_nArgc++ ] = pArgvBuf; + if( m_nArgc >= COMMAND_MAX_ARGC ) + { + Warning( "CCommand::Tokenize: Encountered command which overflows the argument buffer.. Clamped!\n" ); + } + + nArgvBufferSize += nSize + 1; + Assert( nArgvBufferSize <= COMMAND_MAX_LENGTH ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Helper function to parse arguments to commands. +//----------------------------------------------------------------------------- +const char* CCommand::FindArg( const char *pName ) const +{ + int nArgC = ArgC(); + for ( int i = 1; i < nArgC; i++ ) + { + if ( !Q_stricmp( Arg(i), pName ) ) + return (i+1) < nArgC ? Arg( i+1 ) : ""; + } + return 0; +} + +int CCommand::FindArgInt( const char *pName, int nDefaultVal ) const +{ + const char *pVal = FindArg( pName ); + if ( pVal ) + return atoi( pVal ); + else + return nDefaultVal; +} + + +//----------------------------------------------------------------------------- +// Default console command autocompletion function +//----------------------------------------------------------------------------- +int DefaultCompletionFunc( const char *partial, CUtlVector< CUtlString > &commands ) +{ + return 0; +} + + +ConCommand::ConCommand( const char *pName, FnCommandCallback_t callback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ ) +{ + // Add the callback + if (callback) + { + ConCommandCB cb; + cb.m_fnCommandCallback = callback; + cb.m_bUsingOldCommandCallback = true; + cb.m_bUsingV1CommandCallback = false; + cb.m_bUsingV2CommandCallback = false; + cb.m_bUsingCommandCallbackInterface = false; + m_Callbacks.AddToTail(cb); + } + + m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; + m_bHasCompletionCallback = completionFunc != 0 ? true : false; + m_bUsingCommandCompletionInterface = false; + + m_pParent = this; + + // Setup the rest + BaseClass::Create( pName, pHelpString, flags ); +} + +ConCommand::ConCommand(const char *pName, FnCommandCallbackV1_t callback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/) +{ + // Add the callback + if (callback) + { + ConCommandCB cb; + cb.m_fnCommandCallbackV1 = callback; + cb.m_bUsingOldCommandCallback = false; + cb.m_bUsingV1CommandCallback = true; + cb.m_bUsingV2CommandCallback = false; + cb.m_bUsingCommandCallbackInterface = false; + m_Callbacks.AddToTail(cb); + } + + m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; + m_bHasCompletionCallback = completionFunc != 0 ? true : false; + m_bUsingCommandCompletionInterface = false; + + m_pParent = this; + + // Setup the rest + BaseClass::Create(pName, pHelpString, flags); +} + +ConCommand::ConCommand(const char *pName, FnCommandCallbackV2_t callback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/) +{ + // Add the callback + if (callback) + { + ConCommandCB cb; + cb.m_fnCommandCallbackV2 = callback; + cb.m_bUsingOldCommandCallback = false; + cb.m_bUsingV1CommandCallback = false; + cb.m_bUsingV2CommandCallback = true; + cb.m_bUsingCommandCallbackInterface = false; + m_Callbacks.AddToTail(cb); + } + + m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; + m_bHasCompletionCallback = completionFunc != 0 ? true : false; + m_bUsingCommandCompletionInterface = false; + + m_pParent = this; + + // Setup the rest + BaseClass::Create(pName, pHelpString, flags); +} + +ConCommand::ConCommand( const char *pName, ICommandCallback *pCallback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, ICommandCompletionCallback *pCompletionCallback /*= 0*/ ) +{ + // Add the callback iface + if (pCallback) + { + ConCommandCB cb; + cb.m_pCommandCallback = pCallback; + cb.m_bUsingOldCommandCallback = false; + cb.m_bUsingV1CommandCallback = false; + cb.m_bUsingV2CommandCallback = false; + cb.m_bUsingCommandCallbackInterface = true; + m_Callbacks.AddToTail(cb); + } + + m_pCommandCompletionCallback = pCompletionCallback; + m_bHasCompletionCallback = ( pCompletionCallback != 0 ); + m_bUsingCommandCompletionInterface = true; + + m_pParent = this; + + // Setup the rest + BaseClass::Create( pName, pHelpString, flags ); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +ConCommand::~ConCommand( void ) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if this is a command +//----------------------------------------------------------------------------- +bool ConCommand::IsCommand( void ) const +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Invoke the function if there is one +//----------------------------------------------------------------------------- +void ConCommand::Dispatch( const CCommandContext &context, const CCommand &command ) +{ + FOR_EACH_VEC(m_Callbacks, i) + { + ConCommandCB cb = m_Callbacks[i]; + if (cb.m_fnCallbackAny) + { + if (cb.m_bUsingOldCommandCallback) + { + (*cb.m_fnCommandCallback)(command); + return; + } + else if (cb.m_bUsingCommandCallbackInterface) + { + cb.m_pCommandCallback->CommandCallback(context, command); + return; + } + else if (cb.m_bUsingV1CommandCallback) + { + (*cb.m_fnCommandCallbackV1)(context); + return; + } + else if (cb.m_bUsingV2CommandCallback) + { + cb.m_fnCommandCallbackV2(context, command); + } + } + + // Command without callback!!! + AssertMsg(0, ("Encountered ConCommand without a callback!\n")); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Calls the autocompletion method to get autocompletion suggestions +//----------------------------------------------------------------------------- +int ConCommand::AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands ) +{ + if (m_bUsingCommandCompletionInterface) + { + if ( !m_pCommandCompletionCallback ) + return 0; + return m_pCommandCompletionCallback->CommandCompletionCallback( partial, commands ); + } + + Assert( m_fnCompletionCallback ); + if ( !m_fnCompletionCallback ) + return 0; + + return m_fnCompletionCallback( partial, commands ); +} + + +//----------------------------------------------------------------------------- +// Returns true if the console command can autocomplete +//----------------------------------------------------------------------------- +bool ConCommand::CanAutoComplete( void ) +{ + return m_bHasCompletionCallback; +} + + + +//----------------------------------------------------------------------------- +// +// Console Variables +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Various constructors +//----------------------------------------------------------------------------- +ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags /* = 0 */ ) +{ + Create( pName, pDefaultValue, flags ); +} + +ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString ) +{ + Create( pName, pDefaultValue, flags, pHelpString ); +} + +ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) +{ + Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax ); +} + +ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString, FnChangeCallback_t callback ) +{ + Create( pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0, callback ); +} + +ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t callback ) +{ + Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax, callback ); +} + + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +ConVar::~ConVar( void ) +{ + if ( m_Value.m_pszString ) + { + delete[] m_Value.m_pszString; + m_Value.m_pszString = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Install a change callback (there shouldn't already be one....) +//----------------------------------------------------------------------------- +void ConVar::InstallChangeCallback( FnChangeCallback_t callback, bool bInvoke ) +{ + if (callback) + { + if (m_fnChangeCallbacks.Find(callback) != -1) + { + m_fnChangeCallbacks.AddToTail(callback); + if (bInvoke) + callback(this, m_Value.m_pszString, m_Value.m_fValue); + } + else + { + Warning("InstallChangeCallback ignoring duplicate change callback!!!\n"); + } + } + else + { + Warning("InstallChangeCallback called with NULL callback, ignoring!!!\n"); + } +} + +bool ConVar::IsFlagSet( int64 flag ) const +{ + return ( flag & m_pParent->m_nFlags ) ? true : false; +} + +const char *ConVar::GetHelpText( void ) const +{ + return m_pParent->m_pszHelpString; +} + +void ConVar::AddFlags( int64 flags ) +{ + m_pParent->m_nFlags |= flags; + +#ifdef ALLOW_DEVELOPMENT_CVARS + m_pParent->m_nFlags &= ~FCVAR_DEVELOPMENTONLY; +#endif +} + +int64 ConVar::GetFlags( void ) const +{ + return m_pParent->m_nFlags; +} + +bool ConVar::IsRegistered( void ) const +{ + return m_pParent->m_bRegistered; +} + +const char *ConVar::GetName( void ) const +{ + return m_pParent->m_pszName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ConVar::IsCommand( void ) const +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +//----------------------------------------------------------------------------- +void ConVar::Init() +{ + BaseClass::Init(); +} + +const char *ConVar::GetBaseName( void ) const +{ + return m_pParent->m_pszName; +} + +int ConVar::GetSplitScreenPlayerSlot( void ) const +{ + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *value - +//----------------------------------------------------------------------------- +void ConVar::InternalSetValue( const char *value ) +{ + float fNewValue; + char tempVal[ 32 ]; + char *val; + + Assert(m_pParent == this); // Only valid for root convars. + + float flOldValue = m_Value.m_fValue; + + val = (char *)value; + fNewValue = ( float )atof( value ); + + if ( ClampValue( fNewValue ) ) + { + Q_snprintf( tempVal,sizeof(tempVal), "%f", fNewValue ); + val = tempVal; + } + + // Redetermine value + m_Value.m_fValue = fNewValue; + m_Value.m_nValue = ( int )( fNewValue ); + + if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) + { + ChangeStringValue( val, flOldValue ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tempVal - +//----------------------------------------------------------------------------- +void ConVar::ChangeStringValue( const char *tempVal, float flOldValue ) +{ + Assert( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ); + + char* pszOldValue = (char*)stackalloc( m_Value.m_StringLength ); + memcpy( pszOldValue, m_Value.m_pszString, m_Value.m_StringLength ); + + int len = Q_strlen(tempVal) + 1; + + if ( len > m_Value.m_StringLength) + { + if (m_Value.m_pszString) + { + delete[] m_Value.m_pszString; + } + + m_Value.m_pszString = new char[len]; + m_Value.m_StringLength = len; + } + + memcpy( m_Value.m_pszString, tempVal, len ); + + // Invoke any necessary callback function + for (int i = 0; i < m_fnChangeCallbacks.Count(); i++) + { + m_fnChangeCallbacks[i]( this, pszOldValue, flOldValue ); + } + + if (g_pCVar) + g_pCVar->CallGlobalChangeCallbacks( this, pszOldValue, flOldValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Check whether to clamp and then perform clamp +// Input : value - +// Output : Returns true if value changed +//----------------------------------------------------------------------------- +bool ConVar::ClampValue( float& value ) +{ + if ( m_bHasMin && ( value < m_fMinVal ) ) + { + value = m_fMinVal; + return true; + } + + if ( m_bHasMax && ( value > m_fMaxVal ) ) + { + value = m_fMaxVal; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *value - +//----------------------------------------------------------------------------- +void ConVar::InternalSetFloatValue( float fNewValue ) +{ + if ( fNewValue == m_Value.m_fValue ) + return; + + Assert( m_pParent == this ); // Only valid for root convars. + + // Check bounds + ClampValue( fNewValue ); + + // Redetermine value + float flOldValue = m_Value.m_fValue; + m_Value.m_fValue = fNewValue; + m_Value.m_nValue = ( int )fNewValue; + + if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) + { + char tempVal[ 32 ]; + Q_snprintf( tempVal, sizeof( tempVal), "%f", m_Value.m_fValue ); + ChangeStringValue( tempVal, flOldValue ); + } + else + { + Assert( m_fnChangeCallbacks.Count() == 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *value - +//----------------------------------------------------------------------------- +void ConVar::InternalSetIntValue( int nValue ) +{ + if ( nValue == m_Value.m_nValue ) + return; + + Assert( m_pParent == this ); // Only valid for root convars. + + float fValue = (float)nValue; + if ( ClampValue( fValue ) ) + { + nValue = ( int )( fValue ); + } + + // Redetermine value + float flOldValue = m_Value.m_fValue; + m_Value.m_fValue = fValue; + m_Value.m_nValue = nValue; + + if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) + { + char tempVal[ 32 ]; + Q_snprintf( tempVal, sizeof( tempVal ), "%d", m_Value.m_nValue ); + ChangeStringValue( tempVal, flOldValue ); + } + else + { + Assert( m_fnChangeCallbacks.Count() == 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *value - +//----------------------------------------------------------------------------- +void ConVar::InternalSetColorValue( Color cValue ) +{ + int color = cValue.GetRawColor(); + InternalSetIntValue( color ); +} + +bool ConVar::GetBoolVirtualized() const +{ + return m_Value.m_nValue != 0; +} + +int ConVar::GetIntVirtualized() const +{ + return m_Value.m_nValue; +} + +float ConVar::GetFloatVirtualized() const +{ + return m_Value.m_fValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Private creation +//----------------------------------------------------------------------------- +void ConVar::Create( const char *pName, const char *pDefaultValue, int64 flags /*= 0*/, + const char *pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/, + bool bMax /*= false*/, float fMax /*= false*/, FnChangeCallback_t callback /*= NULL*/ ) +{ + static const char *empty_string = ""; + + m_pParent = this; + + // Name should be static data + m_pszDefaultValue = pDefaultValue ? pDefaultValue : empty_string; + Assert( m_pszDefaultValue ); + + m_Value.m_StringLength = strlen( m_pszDefaultValue ) + 1; + m_Value.m_pszString = new char[m_Value.m_StringLength]; + memcpy( m_Value.m_pszString, m_pszDefaultValue, m_Value.m_StringLength ); + + m_bHasMin = bMin; + m_fMinVal = fMin; + m_bHasMax = bMax; + m_fMaxVal = fMax; + + if (callback) + m_fnChangeCallbacks.AddToTail(callback); + + m_Value.m_fValue = ( float )atof( m_Value.m_pszString ); + + // Bounds Check, should never happen, if it does, no big deal + if ( m_bHasMin && ( m_Value.m_fValue < m_fMinVal ) ) + { + Assert( 0 ); + } + + if ( m_bHasMax && ( m_Value.m_fValue > m_fMaxVal ) ) + { + Assert( 0 ); + } + + m_Value.m_nValue = ( int )m_Value.m_fValue; + + BaseClass::Create( pName, pHelpString, flags ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *value - +//----------------------------------------------------------------------------- +void ConVar::SetValue(const char *value) +{ + ConVar *var = ( ConVar * )m_pParent; + var->InternalSetValue( value ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : value - +//----------------------------------------------------------------------------- +void ConVar::SetValue( float value ) +{ + ConVar *var = ( ConVar * )m_pParent; + var->InternalSetFloatValue( value ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : value - +//----------------------------------------------------------------------------- +void ConVar::SetValue( int value ) +{ + ConVar *var = ( ConVar * )m_pParent; + var->InternalSetIntValue( value ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : value - +//----------------------------------------------------------------------------- +void ConVar::SetValue( Color value ) +{ + ConVar *var = ( ConVar * )m_pParent; + var->InternalSetColorValue( value ); +} + +//----------------------------------------------------------------------------- +// Purpose: Reset to default value +//----------------------------------------------------------------------------- +void ConVar::Revert( void ) +{ + // Force default value again + ConVar *var = ( ConVar * )m_pParent; + var->SetValue( var->m_pszDefaultValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : minVal - +// Output : true if there is a min set +//----------------------------------------------------------------------------- +bool ConVar::GetMin( float& minVal ) const +{ + minVal = m_pParent->m_fMinVal; + return m_pParent->m_bHasMin; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : maxVal - +//----------------------------------------------------------------------------- +bool ConVar::GetMax( float& maxVal ) const +{ + maxVal = m_pParent->m_fMaxVal; + return m_pParent->m_bHasMax; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *ConVar::GetDefault( void ) const +{ + return m_pParent->m_pszDefaultValue; +} + + +//----------------------------------------------------------------------------- +// This version is simply used to make reading convars simpler. +// Writing convars isn't allowed in this mode +//----------------------------------------------------------------------------- +class CEmptyConVar : public ConVar +{ +public: + CEmptyConVar() : ConVar( "", "0" ) {} + // Used for optimal read access + virtual void SetValue( const char *pValue ) {} + virtual void SetValue( float flValue ) {} + virtual void SetValue( int nValue ) {} + virtual void SetValue( Color cValue ) {} + virtual const char *GetName( void ) const { return ""; } + virtual bool IsFlagSet( int nFlags ) const { return false; } +}; + +static CEmptyConVar s_EmptyConVar; + +ConVarRef::ConVarRef( const char *pName ) +{ + Init( pName, false ); +} + +ConVarRef::ConVarRef( const char *pName, bool bIgnoreMissing ) +{ + Init( pName, bIgnoreMissing ); +} + +void ConVarRef::Init( const char *pName, bool bIgnoreMissing ) +{ + m_pConVar = g_pCVar ? g_pCVar->FindVar( pName ) : &s_EmptyConVar; + if ( !m_pConVar ) + { + m_pConVar = &s_EmptyConVar; + } + m_pConVarState = static_cast< ConVar * >( m_pConVar ); + if( !IsValid() ) + { + static bool bFirst = true; + if ( g_pCVar || bFirst ) + { + if ( !bIgnoreMissing ) + { + Warning( "ConVarRef %s doesn't point to an existing ConVar\n", pName ); + } + bFirst = false; + } + } +} + +ConVarRef::ConVarRef( IConVar *pConVar ) +{ + m_pConVar = pConVar ? pConVar : &s_EmptyConVar; + m_pConVarState = static_cast< ConVar * >( m_pConVar ); +} + +bool ConVarRef::IsValid() const +{ + return m_pConVar != &s_EmptyConVar; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ConVar_PrintFlags( const ConCommandBase *var ) +{ + bool any = false; + if ( var->IsFlagSet( FCVAR_GAMEDLL ) ) + { + ConMsg( " game" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_CLIENTDLL ) ) + { + ConMsg( " client" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_ARCHIVE ) ) + { + ConMsg( " archive" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_NOTIFY ) ) + { + ConMsg( " notify" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_SPONLY ) ) + { + ConMsg( " singleplayer" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_NOT_CONNECTED ) ) + { + ConMsg( " notconnected" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_CHEAT ) ) + { + ConMsg( " cheat" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_REPLICATED ) ) + { + ConMsg( " replicated" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_SERVER_CAN_EXECUTE ) ) + { + ConMsg( " server_can_execute" ); + any = true; + } + + if ( var->IsFlagSet( FCVAR_CLIENTCMD_CAN_EXECUTE ) ) + { + ConMsg( " clientcmd_can_execute" ); + any = true; + } + + if ( any ) + { + ConMsg( "\n" ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ConVar_PrintDescription( const ConCommandBase *pVar ) +{ + bool bMin, bMax; + float fMin, fMax; + const char *pStr; + + Assert( pVar ); + + Color clr; + clr.SetColor( 255, 100, 100, 255 ); + + if ( !pVar->IsCommand() ) + { + ConVar *var = ( ConVar * )pVar; + const ConVar_ServerBounded *pBounded = dynamic_cast( var ); + + bMin = var->GetMin( fMin ); + bMax = var->GetMax( fMax ); + + const char *value = NULL; + char tempVal[ 32 ]; + + if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) + { + value = tempVal; + + int intVal = pBounded ? pBounded->GetInt() : var->GetInt(); + float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat(); + + if ( fabs( (float)intVal - floatVal ) < 0.000001 ) + { + Q_snprintf( tempVal, sizeof( tempVal ), "%d", intVal ); + } + else + { + Q_snprintf( tempVal, sizeof( tempVal ), "%f", floatVal ); + } + } + else + { + value = var->GetString(); + } + + if ( value ) + { + ConColorMsg( clr, "\"%s\" = \"%s\"", var->GetName(), value ); + + if ( stricmp( value, var->GetDefault() ) ) + { + ConMsg( " ( def. \"%s\" )", var->GetDefault() ); + } + } + + if ( bMin ) + { + ConMsg( " min. %f", fMin ); + } + if ( bMax ) + { + ConMsg( " max. %f", fMax ); + } + + ConMsg( "\n" ); + + // Handled virtualized cvars. + if ( pBounded && fabs( pBounded->GetFloat() - var->GetFloat() ) > 0.0001f ) + { + ConColorMsg( clr, "** NOTE: The real value is %.3f but the server has temporarily restricted it to %.3f **\n", + var->GetFloat(), pBounded->GetFloat() ); + } + } + else + { + ConCommand *var = ( ConCommand * )pVar; + + ConColorMsg( clr, "\"%s\"\n", var->GetName() ); + } + + ConVar_PrintFlags( pVar ); + + pStr = pVar->GetHelpText(); + if ( pStr && pStr[0] ) + { + ConMsg( " - %s\n", pStr ); + } +} diff --git a/tier1/datamanager.cpp b/tier1/datamanager.cpp new file mode 100644 index 00000000..ff48504b --- /dev/null +++ b/tier1/datamanager.cpp @@ -0,0 +1,406 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "basetypes.h" +#include "datamanager.h" + +#define AUTO_LOCK_DM() AUTO_LOCK_( CDataManagerBase, *this ) + +CDataManagerBase::CDataManagerBase( unsigned int maxSize ) +{ + m_targetMemorySize = maxSize; + m_memUsed = 0; + m_lruList = m_memoryLists.CreateList(); + m_lockList = m_memoryLists.CreateList(); + m_freeList = m_memoryLists.CreateList(); + m_listsAreFreed = 0; +} + +CDataManagerBase::~CDataManagerBase() +{ + Assert( m_listsAreFreed ); +} + +void CDataManagerBase::NotifySizeChanged( datamanhandle_t handle, unsigned int oldSize, unsigned int newSize ) +{ + Lock(); + m_memUsed += (int)newSize - (int)oldSize; + Unlock(); +} + +void CDataManagerBase::SetTargetSize( unsigned int targetSize ) +{ + m_targetMemorySize = targetSize; +} + +unsigned int CDataManagerBase::FlushAllUnlocked() +{ + Lock(); + + int nFlush = m_memoryLists.Count( m_lruList ); + void **pScratch = (void **)_alloca( nFlush * sizeof(void *) ); + CUtlVector destroyList( pScratch, nFlush ); + + unsigned nBytesInitial = MemUsed_Inline(); + + int node = m_memoryLists.Head(m_lruList); + while ( node != m_memoryLists.InvalidIndex() ) + { + int next = m_memoryLists.Next(node); + m_memoryLists.Unlink( m_lruList, node ); + destroyList.AddToTail( GetForFreeByIndex( node ) ); + node = next; + } + + Unlock(); + + for ( int i = 0; i < nFlush; i++ ) + { + DestroyResourceStorage( destroyList[i] ); + } + + return ( nBytesInitial - MemUsed_Inline() ); +} + +unsigned int CDataManagerBase::FlushToTargetSize() +{ + return EnsureCapacity(0); +} + +// Frees everything! The LRU AND the LOCKED items. This is only used to forcibly free the resources, +// not to make space. + +unsigned int CDataManagerBase::FlushAll() +{ + Lock(); + + int nFlush = m_memoryLists.Count( m_lruList ) + m_memoryLists.Count( m_lockList ); + void **pScratch = (void **)_alloca( nFlush * sizeof(void *) ); + CUtlVector destroyList( pScratch, nFlush ); + + unsigned result = MemUsed_Inline(); + int node; + int nextNode; + + node = m_memoryLists.Head(m_lruList); + while ( node != m_memoryLists.InvalidIndex() ) + { + nextNode = m_memoryLists.Next(node); + m_memoryLists.Unlink( m_lruList, node ); + destroyList.AddToTail( GetForFreeByIndex( node ) ); + node = nextNode; + } + + node = m_memoryLists.Head(m_lockList); + while ( node != m_memoryLists.InvalidIndex() ) + { + nextNode = m_memoryLists.Next(node); + m_memoryLists.Unlink( m_lockList, node ); + m_memoryLists[node].lockCount = 0; + destroyList.AddToTail( GetForFreeByIndex( node ) ); + node = nextNode; + } + + m_listsAreFreed = false; + Unlock(); + + for ( int i = 0; i < nFlush; i++ ) + { + DestroyResourceStorage( destroyList[i] ); + } + + return result; +} + +unsigned int CDataManagerBase::Purge( unsigned int nBytesToPurge ) +{ + unsigned int nTargetSize = MemUsed_Inline() - nBytesToPurge; + unsigned int nImpliedCapacity = MemTotal_Inline() - nTargetSize; + return EnsureCapacity( nImpliedCapacity ); +} + + +void CDataManagerBase::DestroyResource( datamanhandle_t handle ) +{ + Lock(); + unsigned short index = FromHandle( handle ); + if ( !m_memoryLists.IsValidIndex(index) ) + { + Unlock(); + return; + } + + Assert( m_memoryLists[index].lockCount == 0 ); + if ( m_memoryLists[index].lockCount ) + BreakLock( handle ); + m_memoryLists.Unlink( m_lruList, index ); + void *p = GetForFreeByIndex( index ); + Unlock(); + + DestroyResourceStorage( p ); +} + + +void *CDataManagerBase::LockResource( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + if ( m_memoryLists[memoryIndex].lockCount == 0 ) + { + m_memoryLists.Unlink( m_lruList, memoryIndex ); + m_memoryLists.LinkToTail( m_lockList, memoryIndex ); + } + Assert(m_memoryLists[memoryIndex].lockCount != (unsigned short)-1); + m_memoryLists[memoryIndex].lockCount++; + return m_memoryLists[memoryIndex].pStore; + } + + return NULL; +} + +int CDataManagerBase::UnlockResource( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + Assert( m_memoryLists[memoryIndex].lockCount > 0 ); + if ( m_memoryLists[memoryIndex].lockCount > 0 ) + { + m_memoryLists[memoryIndex].lockCount--; + if ( m_memoryLists[memoryIndex].lockCount == 0 ) + { + m_memoryLists.Unlink( m_lockList, memoryIndex ); + m_memoryLists.LinkToTail( m_lruList, memoryIndex ); + } + } + return m_memoryLists[memoryIndex].lockCount; + } + + return 0; +} + +void *CDataManagerBase::GetResource_NoLockNoLRUTouch( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + return m_memoryLists[memoryIndex].pStore; + } + return NULL; +} + + +void *CDataManagerBase::GetResource_NoLock( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + TouchByIndex( memoryIndex ); + return m_memoryLists[memoryIndex].pStore; + } + return NULL; +} + +void CDataManagerBase::TouchResource( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + TouchByIndex( FromHandle(handle) ); +} + +void CDataManagerBase::MarkAsStale( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + if ( m_memoryLists[memoryIndex].lockCount == 0 ) + { + m_memoryLists.Unlink( m_lruList, memoryIndex ); + m_memoryLists.LinkToHead( m_lruList, memoryIndex ); + } + } +} + +int CDataManagerBase::BreakLock( datamanhandle_t handle ) +{ + AUTO_LOCK_DM(); + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() && m_memoryLists[memoryIndex].lockCount ) + { + int nBroken = m_memoryLists[memoryIndex].lockCount; + m_memoryLists[memoryIndex].lockCount = 0; + m_memoryLists.Unlink( m_lockList, memoryIndex ); + m_memoryLists.LinkToTail( m_lruList, memoryIndex ); + + return nBroken; + } + return 0; +} + +int CDataManagerBase::BreakAllLocks() +{ + AUTO_LOCK_DM(); + int nBroken = 0; + int node; + int nextNode; + + node = m_memoryLists.Head(m_lockList); + while ( node != m_memoryLists.InvalidIndex() ) + { + nBroken++; + nextNode = m_memoryLists.Next(node); + m_memoryLists[node].lockCount = 0; + m_memoryLists.Unlink( m_lockList, node ); + m_memoryLists.LinkToTail( m_lruList, node ); + node = nextNode; + } + + return nBroken; + +} + +unsigned short CDataManagerBase::CreateHandle( bool bCreateLocked ) +{ + AUTO_LOCK_DM(); + int memoryIndex = m_memoryLists.Head(m_freeList); + unsigned short list = ( bCreateLocked ) ? m_lockList : m_lruList; + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + m_memoryLists.Unlink( m_freeList, memoryIndex ); + m_memoryLists.LinkToTail( list, memoryIndex ); + } + else + { + memoryIndex = m_memoryLists.AddToTail( list ); + } + + if ( bCreateLocked ) + { + m_memoryLists[memoryIndex].lockCount++; + } + + return memoryIndex; +} + +datamanhandle_t CDataManagerBase::StoreResourceInHandle( unsigned short memoryIndex, void *pStore, unsigned int realSize ) +{ + AUTO_LOCK_DM(); + resource_lru_element_t &mem = m_memoryLists[memoryIndex]; + mem.pStore = pStore; + m_memUsed += realSize; + return ToHandle(memoryIndex); +} + +void CDataManagerBase::TouchByIndex( unsigned short memoryIndex ) +{ + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + if ( m_memoryLists[memoryIndex].lockCount == 0 ) + { + m_memoryLists.Unlink( m_lruList, memoryIndex ); + m_memoryLists.LinkToTail( m_lruList, memoryIndex ); + } + } +} + +datamanhandle_t CDataManagerBase::ToHandle( unsigned short index ) +{ + unsigned int hiword = m_memoryLists.Element(index).serial; + hiword <<= 16; + index++; + return datamanhandle_t::MakeHandle( hiword|index ); +} + +unsigned int CDataManagerBase::TargetSize() +{ + return MemTotal_Inline(); +} + +unsigned int CDataManagerBase::AvailableSize() +{ + return MemAvailable_Inline(); +} + + +unsigned int CDataManagerBase::UsedSize() +{ + return MemUsed_Inline(); +} + +// free resources until there is enough space to hold "size" +unsigned int CDataManagerBase::EnsureCapacity( unsigned int size ) +{ + unsigned nBytesInitial = MemUsed_Inline(); + while ( MemUsed_Inline() > MemTotal_Inline() || MemAvailable_Inline() < size ) + { + Lock(); + int lruIndex = m_memoryLists.Head( m_lruList ); + if ( lruIndex == m_memoryLists.InvalidIndex() ) + { + Unlock(); + break; + } + m_memoryLists.Unlink( m_lruList, lruIndex ); + void *p = GetForFreeByIndex( lruIndex ); + Unlock(); + DestroyResourceStorage( p ); + } + return ( nBytesInitial - MemUsed_Inline() ); +} + +// free this resource and move the handle to the free list +void *CDataManagerBase::GetForFreeByIndex( unsigned short memoryIndex ) +{ + void *p = NULL; + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + Assert( m_memoryLists[memoryIndex].lockCount == 0 ); + + resource_lru_element_t &mem = m_memoryLists[memoryIndex]; + unsigned size = GetRealSize( mem.pStore ); + if ( size > m_memUsed ) + { + ExecuteOnce( Warning( "Data manager 'used' memory incorrect\n" ) ); + size = m_memUsed; + } + m_memUsed -= size; + p = mem.pStore; + mem.pStore = NULL; + mem.serial++; + m_memoryLists.LinkToTail( m_freeList, memoryIndex ); + } + return p; +} + +// get a list of everything in the LRU +void CDataManagerBase::GetLRUHandleList( CUtlVector< datamanhandle_t >& list ) +{ + for ( int node = m_memoryLists.Tail(m_lruList); + node != m_memoryLists.InvalidIndex(); + node = m_memoryLists.Previous(node) ) + { + list.AddToTail( ToHandle( node ) ); + } +} + +// get a list of everything locked +void CDataManagerBase::GetLockHandleList( CUtlVector< datamanhandle_t >& list ) +{ + for ( int node = m_memoryLists.Head(m_lockList); + node != m_memoryLists.InvalidIndex(); + node = m_memoryLists.Next(node) ) + { + list.AddToTail( ToHandle( node ) ); + } +} + diff --git a/tier1/diff.cpp b/tier1/diff.cpp new file mode 100644 index 00000000..151a1b6a --- /dev/null +++ b/tier1/diff.cpp @@ -0,0 +1,547 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier1/diff.h" +#include "mathlib/mathlib.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +// format of diff output: +// 0NN (N=1..127) copy next N literaly +// +// 1NN (N=1..127) ofs (-128..127) copy next N bytes from original, changin offset by N bytes from +// last copy end +// 100 N ofs(-32768..32767) copy next N, with larger delta offset +// 00 NNNN(1..65535) ofs(-32768..32767) big copy from old +// 80 00 NN NN NN big raw copy +// +// available codes (could be used for additonal compression ops) +// long offset form whose offset could have fit in short offset + +// note - this algorithm uses storage equal to 8* the old buffer size. 64k=.5mb + + +#define MIN_MATCH_LEN 8 +#define ACCEPTABLE_MATCH_LEN 4096 + +struct BlockPtr +{ + BlockPtr *Next; + uint8 const *dataptr; +}; + +template static inline void AddToHead(T * & head, V * node) +{ + node->Next=head; + head=node; +} + +void Fail(char const *msg) +{ + Assert(0); +} + +void ApplyDiffs(uint8 const *OldBlock, uint8 const *DiffList, + int OldSize, int DiffListSize, int &ResultListSize,uint8 *Output,uint32 OutSize) +{ + uint8 const *copy_src=OldBlock; + uint8 const *end_of_diff_list=DiffList+DiffListSize; + uint8 const *obuf=Output; + while(DiffList32767) + copy_ofs|=0xffff0000; + // printf("long cp from %x to %x len=%d\n", copy_src+copy_ofs-OldBlock,Output-obuf,copy_sz); + + memcpy(Output,copy_src+copy_ofs,copy_sz); + Output+=copy_sz; + copy_src=copy_src+copy_ofs+copy_sz; + DiffList+=4; + } + else + { + if (op & 0x80) + { + int copy_sz=op & 0x7f; + int copy_ofs; + if (copy_sz==0) + { + copy_sz=DiffList[0]; + if (copy_sz==0) + { + // big raw copy + copy_sz=DiffList[1]+256*DiffList[2]+65536*DiffList[3]; + memcpy(Output,DiffList+4,copy_sz); + // printf("big rawcopy to %x len=%d\n", Output-obuf,copy_sz); + + DiffList+=copy_sz+4; + Output+=copy_sz; + } + else + { + copy_ofs=DiffList[1]+(DiffList[2]*256); + if (copy_ofs>32767) + copy_ofs|=0xffff0000; + // printf("long ofs cp from %x to %x len=%d\n", copy_src+copy_ofs-OldBlock,Output-obuf,copy_sz); + + memcpy(Output,copy_src+copy_ofs,copy_sz); + Output+=copy_sz; + copy_src=copy_src+copy_ofs+copy_sz; + DiffList+=3; + } + } + else + { + copy_ofs=DiffList[0]; + if (copy_ofs>127) + copy_ofs|=0xffffff80; + // printf("cp from %x to %x len=%d\n", copy_src+copy_ofs-OldBlock,Output-obuf,copy_sz); + + memcpy(Output,copy_src+copy_ofs,copy_sz); + Output+=copy_sz; + copy_src=copy_src+copy_ofs+copy_sz; + DiffList++; + } + } + else + { + // printf("raw copy %d to %x\n",op & 127,Output-obuf); + memcpy(Output,DiffList,op & 127); + Output+=op & 127; + DiffList+=(op & 127); + } + } + } + ResultListSize=Output-obuf; + +} + +static void CopyPending(int len, uint8 const *rawbytes,uint8 * &outbuf, uint8 const *limit) +{ +// printf("copy raw len=%d\n",len); + if (len<128) + { + if (limit-outbuf < len+1) + Fail("diff buffer overrun"); + *(outbuf++)=len; + memcpy(outbuf,rawbytes,len); + outbuf+=len; + } + else + { + if (limit-outbuf < len+5) + Fail("diff buffer overrun"); + *(outbuf++)=0x80; + *(outbuf++)=0x00; + *(outbuf++)=(len & 255); + *(outbuf++)=((len>>8) & 255); + *(outbuf++)=((len>>16) & 255); + memcpy(outbuf,rawbytes,len); + outbuf+=len; + } +} + +static uint32 hasher(uint8 const *mdata) +{ + // attempt to scramble the bits of h1 and h2 together + uint32 ret=0; + for(int i=0;idataptr=walk; + AddToHead(HashedMatches[hash1],newnode); + walk++; + } + else + ret=1; + // now, we have the hash table which may be used to search. begin the output step + int pending_raw_len=0; + walk=NewBlock; + uint8 *outbuf=Output; + uint8 const *lastmatchend=OldBlock; + while(walkMIN_MATCH_LEN, take it + for(BlockPtr *b=HashedMatches[hash1];b;b=b->Next) + { + // find the match length + int match_of=b->dataptr-lastmatchend; + if ((match_of>-32768) && (match_of<32767)) + { + int max_mlength=MIN(65535,OldBlock+OldSize-b->dataptr); + max_mlength=MIN(max_mlength,NewBlock+NewSize-walk); + int i; + for(i=0;idataptr[i]) + break; + if ((i>MIN_MATCH_LEN) && (i>longest)) + { + longest=i; + longest_block=b; + if (longest>ACCEPTABLE_MATCH_LEN) + break; + } + } + } + } + // now, we have a match maybe + if (longest_block) + { + if (pending_raw_len) // must output + { + ret=1; + CopyPending(pending_raw_len,walk-pending_raw_len,outbuf,Output+OutSize); + pending_raw_len=0; + } + // now, output copy block + int match_of=longest_block->dataptr-lastmatchend; + int nremaining=OutSize-(outbuf-Output); + + if (match_of) + ret=1; +// printf("copy from %x to %x len=%d\n", match_of,outbuf-Output,longest); + if (longest>127) + { + // use really long encoding + if (nremaining<5) + Fail("diff buff needs increase"); + *(outbuf++)=00; + *(outbuf++)=(longest & 255); + *(outbuf++)=((longest>>8) & 255); + *(outbuf++)=(match_of & 255); + *(outbuf++)=((match_of>>8) & 255); + + } + else + { + if ((match_of>=-128) && (match_of<128)) + { + if (nremaining<2) + Fail("diff buff needs increase"); + *(outbuf++)=128+longest; + *(outbuf++)=(match_of&255); + } + else + { + // use long encoding + if (nremaining<4) + Fail("diff buff needs increase"); + *(outbuf++)=0x80; + *(outbuf++)=longest; + *(outbuf++)=(match_of & 255); + *(outbuf++)=((match_of>>8) & 255); + } + } + lastmatchend=longest_block->dataptr+longest; + walk+=longest; + } + else + { + walk++; + pending_raw_len++; + } + } + // now, flush pending raw copy + if (pending_raw_len) // must output + { + ret=1; + CopyPending(pending_raw_len,walk-pending_raw_len,outbuf,Output+OutSize); + pending_raw_len=0; + } + delete[] HashedMatches; + if (Blocks) + delete[] Blocks; + DiffListSize=outbuf-Output; + return ret; +} + + +int FindDiffs(uint8 const *NewBlock, uint8 const *OldBlock, + int NewSize, int OldSize, int &DiffListSize,uint8 *Output,uint32 OutSize) +{ + + int ret=0; + if (OldSize!=NewSize) + ret=1; + // first, build the hash table + BlockPtr *HashedMatches[65536]; + memset(HashedMatches,0,sizeof(HashedMatches)); + BlockPtr *Blocks=0; + if (OldSize) + Blocks=new BlockPtr[OldSize]; + BlockPtr *FreeList=Blocks; + // now, build the hash table + uint8 const *walk=OldBlock; + if (OldBlock && OldSize) + while(walkdataptr=walk; + AddToHead(HashedMatches[hash1],newnode); + walk++; + } + else + ret=1; + // now, we have the hash table which may be used to search. begin the output step + int pending_raw_len=0; + walk=NewBlock; + uint8 *outbuf=Output; + uint8 const *lastmatchend=OldBlock; + while(walkMIN_MATCH_LEN, take it + for(BlockPtr *b=HashedMatches[hash1];b;b=b->Next) + { + // find the match length + int match_of=b->dataptr-lastmatchend; + if ((match_of>-32768) && (match_of<32767)) + { + int max_mlength=MIN(65535,OldBlock+OldSize-b->dataptr); + max_mlength=MIN(max_mlength,NewBlock+NewSize-walk); + int i; + for(i=0;idataptr[i]) + break; + if ((i>MIN_MATCH_LEN) && (i>longest)) + { + longest=i; + longest_block=b; + } + } + } + } + // now, we have a match maybe + if (longest_block) + { + if (pending_raw_len) // must output + { + ret=1; + CopyPending(pending_raw_len,walk-pending_raw_len,outbuf,Output+OutSize); + pending_raw_len=0; + } + // now, output copy block + int match_of=longest_block->dataptr-lastmatchend; + int nremaining=OutSize-(outbuf-Output); + if (match_of) + ret=1; + if (longest>127) + { + // use really long encoding + if (nremaining<5) + Fail("diff buff needs increase"); + *(outbuf++)=00; + *(outbuf++)=(longest & 255); + *(outbuf++)=((longest>>8) & 255); + *(outbuf++)=(match_of & 255); + *(outbuf++)=((match_of>>8) & 255); + } + else + { + if ((match_of>=-128) && (match_of<128)) + { + if (nremaining<2) + Fail("diff buff needs increase"); + *(outbuf++)=128+longest; + *(outbuf++)=(match_of&255); + } + else + { + // use long encoding + if (nremaining<4) + Fail("diff buff needs increase"); + *(outbuf++)=0x80; + *(outbuf++)=longest; + *(outbuf++)=(match_of & 255); + *(outbuf++)=((match_of>>8) & 255); + } + } + lastmatchend=longest_block->dataptr+longest; + walk+=longest; + } + else + { + walk++; + pending_raw_len++; + } + } + // now, flush pending raw copy + if (pending_raw_len) // must output + { + ret=1; + CopyPending(pending_raw_len,walk-pending_raw_len,outbuf,Output+OutSize); + pending_raw_len=0; + } + if (Blocks) + delete[] Blocks; + DiffListSize=outbuf-Output; + return ret; +} + + +int FindDiffsLowMemory(uint8 const *NewBlock, uint8 const *OldBlock, + int NewSize, int OldSize, int &DiffListSize,uint8 *Output,uint32 OutSize) +{ + + int ret=0; + if (OldSize!=NewSize) + ret=1; + uint8 const *old_data_hash[256]; + memset(old_data_hash,0,sizeof(old_data_hash)); + int pending_raw_len=0; + uint8 const *walk=NewBlock; + uint8 const *oldptr=OldBlock; + uint8 *outbuf=Output; + uint8 const *lastmatchend=OldBlock; + while(walkMIN_MATCH_LEN) + { + longest_block=old_data_hash[hash1]; + longest=nmatches; + } + } + } + // now, we have a match maybe + if (longest_block) + { + if (pending_raw_len) // must output + { + ret=1; + CopyPending(pending_raw_len,walk-pending_raw_len,outbuf,Output+OutSize); + pending_raw_len=0; + } + // now, output copy block + int match_of=longest_block-lastmatchend; + int nremaining=OutSize-(outbuf-Output); + if (match_of) + ret=1; + if (longest>127) + { + // use really long encoding + if (nremaining<5) + Fail("diff buff needs increase"); + *(outbuf++)=00; + *(outbuf++)=(longest & 255); + *(outbuf++)=((longest>>8) & 255); + *(outbuf++)=(match_of & 255); + *(outbuf++)=((match_of>>8) & 255); + } + else + { + if ((match_of>=-128) && (match_of<128)) + { + if (nremaining<2) + Fail("diff buff needs increase"); + *(outbuf++)=128+longest; + *(outbuf++)=(match_of&255); + } + else + { + // use long encoding + if (nremaining<4) + Fail("diff buff needs increase"); + *(outbuf++)=0x80; + *(outbuf++)=longest; + *(outbuf++)=(match_of & 255); + *(outbuf++)=((match_of>>8) & 255); + } + } + lastmatchend=longest_block+longest; + walk+=longest; + } + else + { + walk++; + pending_raw_len++; + } + } + // now, flush pending raw copy + if (pending_raw_len) // must output + { + ret=1; + CopyPending(pending_raw_len,walk-pending_raw_len,outbuf,Output+OutSize); + pending_raw_len=0; + } + DiffListSize=outbuf-Output; + return ret; +} + + diff --git a/tier1/generichash.cpp b/tier1/generichash.cpp new file mode 100644 index 00000000..f251f1c4 --- /dev/null +++ b/tier1/generichash.cpp @@ -0,0 +1,303 @@ +//======= Copyright © 2005, , Valve Corporation, All rights reserved. ========= +// +// Purpose: Variant Pearson Hash general purpose hashing algorithm described +// by Cargill in C++ Report 1994. Generates a 16-bit result. +// +//============================================================================= + +#include +#include "tier0/basetypes.h" +#include "tier0/platform.h" +#include "generichash.h" +#include + +//----------------------------------------------------------------------------- +// +// Table of randomly shuffled values from 0-255 generated by: +// +//----------------------------------------------------------------------------- +/* +void MakeRandomValues() +{ + int i, j, r; + unsigned t; + srand( 0xdeadbeef ); + + for ( i = 0; i < 256; i++ ) + { + g_nRandomValues[i] = (unsigned )i; + } + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 256; i++) + { + r = rand() & 0xff; + t = g_nRandomValues[i]; + g_nRandomValues[i] = g_nRandomValues[r]; + g_nRandomValues[r] = t; + } + } + + printf("static unsigned g_nRandomValues[256] =\n{\n"); + + for (i = 0; i < 256; i += 16) + { + printf("\t"); + for (j = 0; j < 16; j++) + printf(" %3d,", g_nRandomValues[i+j]); + printf("\n"); + } + printf("};\n"); +} +*/ + +static unsigned g_nRandomValues[256] = +{ + 238, 164, 191, 168, 115, 16, 142, 11, 213, 214, 57, 151, 248, 252, 26, 198, + 13, 105, 102, 25, 43, 42, 227, 107, 210, 251, 86, 66, 83, 193, 126, 108, + 131, 3, 64, 186, 192, 81, 37, 158, 39, 244, 14, 254, 75, 30, 2, 88, + 172, 176, 255, 69, 0, 45, 116, 139, 23, 65, 183, 148, 33, 46, 203, 20, + 143, 205, 60, 197, 118, 9, 171, 51, 233, 135, 220, 49, 71, 184, 82, 109, + 36, 161, 169, 150, 63, 96, 173, 125, 113, 67, 224, 78, 232, 215, 35, 219, + 79, 181, 41, 229, 149, 153, 111, 217, 21, 72, 120, 163, 133, 40, 122, 140, + 208, 231, 211, 200, 160, 182, 104, 110, 178, 237, 15, 101, 27, 50, 24, 189, + 177, 130, 187, 92, 253, 136, 100, 212, 19, 174, 70, 22, 170, 206, 162, 74, + 247, 5, 47, 32, 179, 117, 132, 195, 124, 123, 245, 128, 236, 223, 12, 84, + 54, 218, 146, 228, 157, 94, 106, 31, 17, 29, 194, 34, 56, 134, 239, 246, + 241, 216, 127, 98, 7, 204, 154, 152, 209, 188, 48, 61, 87, 97, 225, 85, + 90, 167, 155, 112, 145, 114, 141, 93, 250, 4, 201, 156, 38, 89, 226, 196, + 1, 235, 44, 180, 159, 121, 119, 166, 190, 144, 10, 91, 76, 230, 221, 80, + 207, 55, 58, 53, 175, 8, 6, 52, 68, 242, 18, 222, 103, 249, 147, 129, + 138, 243, 28, 185, 62, 59, 240, 202, 234, 99, 77, 73, 199, 137, 95, 165, +}; + +//----------------------------------------------------------------------------- +// String +//----------------------------------------------------------------------------- +unsigned FASTCALL HashString( const char *pszKey ) +{ + const uint8 *k = (const uint8 *)pszKey; + unsigned even = 0, + odd = 0, + n; + + while ((n = *k++) != 0) + { + even = g_nRandomValues[odd ^ n]; + if ((n = *k++) != 0) + odd = g_nRandomValues[even ^ n]; + else + break; + } + + return (even << 8) | odd ; +} + + +//----------------------------------------------------------------------------- +// Case-insensitive string +//----------------------------------------------------------------------------- +unsigned FASTCALL HashStringCaseless( const char *pszKey ) +{ + const uint8 *k = (const uint8 *) pszKey; + unsigned even = 0, + odd = 0, + n; + + while ((n = toupper(*k++)) != 0) + { + even = g_nRandomValues[odd ^ n]; + if ((n = toupper(*k++)) != 0) + odd = g_nRandomValues[even ^ n]; + else + break; + } + + return (even << 8) | odd; +} + +//----------------------------------------------------------------------------- +// 32 bit conventional case-insensitive string +//----------------------------------------------------------------------------- +unsigned FASTCALL HashStringCaselessConventional( const char *pszKey ) +{ + unsigned hash = 0xAAAAAAAA; // Alternating 1's and 0's to maximize the effect of the later multiply and add + + for( ; *pszKey ; pszKey++ ) + { + hash = ( ( hash << 5 ) + hash ) + (uint8)tolower(*pszKey); + } + + return hash; +} + +//----------------------------------------------------------------------------- +// int hash +//----------------------------------------------------------------------------- +unsigned FASTCALL HashInt( const int n ) +{ + register unsigned even, odd; + even = g_nRandomValues[n & 0xff]; + odd = g_nRandomValues[((n >> 8) & 0xff)]; + + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + return (even << 8) | odd; +} + +//----------------------------------------------------------------------------- +// 4-byte hash +//----------------------------------------------------------------------------- +unsigned FASTCALL Hash4( const void *pKey ) +{ + register const uint32 * p = (const uint32 *) pKey; + register unsigned even, + odd, + n; + n = *p; + even = g_nRandomValues[n & 0xff]; + odd = g_nRandomValues[((n >> 8) & 0xff)]; + + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + return (even << 8) | odd; +} + + +//----------------------------------------------------------------------------- +// 8-byte hash +//----------------------------------------------------------------------------- +unsigned FASTCALL Hash8( const void *pKey ) +{ + register const uint32 * p = (const uint32 *) pKey; + register unsigned even, + odd, + n; + n = *p; + even = g_nRandomValues[n & 0xff]; + odd = g_nRandomValues[((n >> 8) & 0xff)]; + + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + n = *(p+1); + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + return (even << 8) | odd; +} + + +//----------------------------------------------------------------------------- +// 12-byte hash +//----------------------------------------------------------------------------- +unsigned FASTCALL Hash12( const void *pKey ) +{ + register const uint32 * p = (const uint32 *) pKey; + register unsigned even, + odd, + n; + n = *p; + even = g_nRandomValues[n & 0xff]; + odd = g_nRandomValues[((n >> 8) & 0xff)]; + + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + n = *(p+1); + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + n = *(p+2); + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + return (even << 8) | odd; +} + + +//----------------------------------------------------------------------------- +// 16-byte hash +//----------------------------------------------------------------------------- +unsigned FASTCALL Hash16( const void *pKey ) +{ + register const uint32 * p = (const uint32 *) pKey; + register unsigned even, + odd, + n; + n = *p; + even = g_nRandomValues[n & 0xff]; + odd = g_nRandomValues[((n >> 8) & 0xff)]; + + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + n = *(p+1); + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + n = *(p+2); + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + n = *(p+3); + even = g_nRandomValues[odd ^ (n >> 24)]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; + even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; + odd = g_nRandomValues[even ^ (n & 0xff)]; + + return (even << 8) | odd; +} + + +//----------------------------------------------------------------------------- +// Arbitrary fixed length hash +//----------------------------------------------------------------------------- +unsigned FASTCALL HashBlock( const void *pKey, unsigned size ) +{ + const uint8 * k = (const uint8 *) pKey; + unsigned even = 0, + odd = 0, + n; + + while (size) + { + --size; + n = *k++; + even = g_nRandomValues[odd ^ n]; + if (size) + { + --size; + n = *k++; + odd = g_nRandomValues[even ^ n]; + } + else + break; + } + + return (even << 8) | odd; +} + diff --git a/tier1/mempool.cpp b/tier1/mempool.cpp new file mode 100644 index 00000000..8a69a0ea --- /dev/null +++ b/tier1/mempool.cpp @@ -0,0 +1,316 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#include "mempool.h" +#include +#ifdef __APPLE__ +#include +#else +#include +#endif +#include +#include "tier0/dbg.h" +#include +#include "tier1/strtools.h" + +// Should be last include +#include "tier0/memdbgon.h" + +MemoryPoolReportFunc_t CUtlMemoryPool::g_ReportFunc = 0; + +//----------------------------------------------------------------------------- +// Error reporting... (debug only) +//----------------------------------------------------------------------------- + +void CUtlMemoryPool::SetErrorReportFunc( MemoryPoolReportFunc_t func ) +{ + g_ReportFunc = func; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CUtlMemoryPool::CUtlMemoryPool( int blockSize, int numElements, int growMode, const char *pszAllocOwner, int nAlignment ) +{ +#ifdef _X360 + if( numElements > 0 && growMode != GROW_NONE ) + { + numElements = 1; + } +#endif + + m_nAlignment = ( nAlignment != 0 ) ? nAlignment : 1; + Assert( IsPowerOfTwo( m_nAlignment ) ); + m_BlockSize = blockSize < (int)sizeof(void*) ? sizeof(void*) : blockSize; + m_BlockSize = AlignValue( m_BlockSize, m_nAlignment ); + m_BlocksPerBlob = numElements; + m_PeakAlloc = 0; + m_GrowMode = growMode; + if ( !pszAllocOwner ) + { + pszAllocOwner = __FILE__; + } + m_pszAllocOwner = pszAllocOwner; + Init(); + AddNewBlob(); +} + +//----------------------------------------------------------------------------- +// Purpose: Frees the memory contained in the mempool, and invalidates it for +// any further use. +// Input : *memPool - the mempool to shutdown +//----------------------------------------------------------------------------- +CUtlMemoryPool::~CUtlMemoryPool() +{ + if (m_BlocksAllocated > 0) + { + ReportLeaks(); + } + Clear(); +} + + +//----------------------------------------------------------------------------- +// Resets the pool +//----------------------------------------------------------------------------- +void CUtlMemoryPool::Init() +{ + m_NumBlobs = 0; + m_BlocksAllocated = 0; + m_pHeadOfFreeList = 0; + m_BlobHead.m_pNext = m_BlobHead.m_pPrev = &m_BlobHead; +} + + +//----------------------------------------------------------------------------- +// Frees everything +//----------------------------------------------------------------------------- +void CUtlMemoryPool::Clear() +{ + // Free everything.. + CBlob *pNext; + for( CBlob *pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pNext ) + { + pNext = pCur->m_pNext; + free( pCur ); + } + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: Reports memory leaks +//----------------------------------------------------------------------------- + +void CUtlMemoryPool::ReportLeaks() +{ + if (!g_ReportFunc) + return; + + g_ReportFunc("Memory leak: mempool blocks left in memory: %d\n", m_BlocksAllocated); + +#ifdef _DEBUG + // walk and destroy the free list so it doesn't intefere in the scan + while (m_pHeadOfFreeList != NULL) + { + void *next = *((void**)m_pHeadOfFreeList); + memset(m_pHeadOfFreeList, 0, m_BlockSize); + m_pHeadOfFreeList = next; + } + + g_ReportFunc("Dumping memory: \'"); + + for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext ) + { + // scan the memory block and dump the leaks + char *scanPoint = (char *)pCur->m_Data; + char *scanEnd = pCur->m_Data + pCur->m_NumBytes; + bool needSpace = false; + + while (scanPoint < scanEnd) + { + // search for and dump any strings + if ((unsigned)(*scanPoint + 1) <= 256 && isprint(*scanPoint)) + { + g_ReportFunc("%c", *scanPoint); + needSpace = true; + } + else if (needSpace) + { + needSpace = false; + g_ReportFunc(" "); + } + + scanPoint++; + } + } + + g_ReportFunc("\'\n"); +#endif // _DEBUG +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CUtlMemoryPool::AddNewBlob() +{ + MEM_ALLOC_CREDIT_(m_pszAllocOwner); + + int sizeMultiplier; + + if( m_GrowMode == GROW_SLOW ) + { + sizeMultiplier = 1; + } + else + { + if ( m_GrowMode == GROW_NONE ) + { + // Can only have one allocation when we're in this mode + if( m_NumBlobs != 0 ) + { + Assert( !"CUtlMemoryPool::AddNewBlob: mode == GROW_NONE" ); + return; + } + } + + // GROW_FAST and GROW_NONE use this. + sizeMultiplier = m_NumBlobs + 1; + } + + // maybe use something other than malloc? + int nElements = m_BlocksPerBlob * sizeMultiplier; + int blobSize = m_BlockSize * nElements; + CBlob *pBlob = (CBlob*)malloc( sizeof(CBlob) - 1 + blobSize + ( m_nAlignment - 1 ) ); + Assert( pBlob ); + + // Link it in at the end of the blob list. + pBlob->m_NumBytes = blobSize; + pBlob->m_pNext = &m_BlobHead; + pBlob->m_pPrev = pBlob->m_pNext->m_pPrev; + pBlob->m_pNext->m_pPrev = pBlob->m_pPrev->m_pNext = pBlob; + + // setup the free list + m_pHeadOfFreeList = AlignValue( pBlob->m_Data, m_nAlignment ); + Assert (m_pHeadOfFreeList); + + void **newBlob = (void**)m_pHeadOfFreeList; + for (int j = 0; j < nElements-1; j++) + { + newBlob[0] = (char*)newBlob + m_BlockSize; + newBlob = (void**)newBlob[0]; + } + + // null terminate list + newBlob[0] = NULL; + m_NumBlobs++; +} + + +void* CUtlMemoryPool::Alloc() +{ + return Alloc( m_BlockSize ); +} + + +void* CUtlMemoryPool::AllocZero() +{ + return AllocZero( m_BlockSize ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Allocs a single block of memory from the pool. +// Input : amount - +//----------------------------------------------------------------------------- +void *CUtlMemoryPool::Alloc( size_t amount ) +{ + void *returnBlock; + + if ( amount > (unsigned int)m_BlockSize ) + return NULL; + + if( !m_pHeadOfFreeList ) + { + // returning NULL is fine in GROW_NONE + if( m_GrowMode == GROW_NONE ) + { + //Assert( !"CUtlMemoryPool::Alloc: tried to make new blob with GROW_NONE" ); + return NULL; + } + + // overflow + AddNewBlob(); + + // still failure, error out + if( !m_pHeadOfFreeList ) + { + Assert( !"CUtlMemoryPool::Alloc: ran out of memory" ); + return NULL; + } + } + m_BlocksAllocated++; + m_PeakAlloc = MAX(m_PeakAlloc, m_BlocksAllocated); + + returnBlock = m_pHeadOfFreeList; + + // move the pointer the next block + m_pHeadOfFreeList = *((void**)m_pHeadOfFreeList); + + return returnBlock; +} + +//----------------------------------------------------------------------------- +// Purpose: Allocs a single block of memory from the pool, zeroes the memory before returning +// Input : amount - +//----------------------------------------------------------------------------- +void *CUtlMemoryPool::AllocZero( size_t amount ) +{ + void *mem = Alloc( amount ); + if ( mem ) + { + V_memset( mem, 0x00, amount ); + } + return mem; +} + +//----------------------------------------------------------------------------- +// Purpose: Frees a block of memory +// Input : *memBlock - the memory to free +//----------------------------------------------------------------------------- +void CUtlMemoryPool::Free( void *memBlock ) +{ + if ( !memBlock ) + return; // trying to delete NULL pointer, ignore + +#ifdef _DEBUG + // check to see if the memory is from the allocated range + bool bOK = false; + for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext ) + { + if (memBlock >= pCur->m_Data && (char*)memBlock < (pCur->m_Data + pCur->m_NumBytes)) + { + bOK = true; + } + } + Assert (bOK); +#endif // _DEBUG + +#ifdef _DEBUG + // invalidate the memory + memset( memBlock, 0xDD, m_BlockSize ); +#endif + + m_BlocksAllocated--; + + // make the block point to the first item in the list + *((void**)memBlock) = m_pHeadOfFreeList; + + // the list head is now the new block + m_pHeadOfFreeList = memBlock; +} + + diff --git a/tier1/memstack.cpp b/tier1/memstack.cpp new file mode 100644 index 00000000..e4888f1f --- /dev/null +++ b/tier1/memstack.cpp @@ -0,0 +1,300 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#if defined( _WIN32 ) && !defined( _X360 ) +#define WIN_32_LEAN_AND_MEAN +#include +#define VA_COMMIT_FLAGS MEM_COMMIT +#define VA_RESERVE_FLAGS MEM_RESERVE +#elif defined( _X360 ) +#define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES) +#define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES) +#endif + +#include "tier0/dbg.h" +#include "memstack.h" +#include "utlmap.h" +#include "tier0/memdbgon.h" + +#ifdef _WIN32 +#pragma warning(disable:4073) +#pragma init_seg(lib) +#endif + +//----------------------------------------------------------------------------- + +MEMALLOC_DEFINE_EXTERNAL_TRACKING(CMemoryStack); + +//----------------------------------------------------------------------------- + +CMemoryStack::CMemoryStack() + : m_pNextAlloc( NULL ), + m_pCommitLimit( NULL ), + m_pAllocLimit( NULL ), + m_pBase( NULL ), + m_maxSize( 0 ), +#if defined (_LINUX) || defined (__APPLE__) + m_alignment( 16 ) +#elif defined(_WIN32) + m_alignment( 16 ), + m_commitSize( 0 ), + m_minCommit( 0 ) +#endif + +{ +} + +//------------------------------------- + +CMemoryStack::~CMemoryStack() +{ + if ( m_pBase ) + Term(); +} + +//------------------------------------- + +bool CMemoryStack::Init( unsigned maxSize, unsigned commitSize, unsigned initialCommit, unsigned alignment ) +{ + Assert( !m_pBase ); + +#ifdef _X360 + m_bPhysical = false; +#endif + + m_maxSize = maxSize; + m_alignment = AlignValue( alignment, 4 ); + + Assert( m_alignment == alignment ); + Assert( m_maxSize > 0 ); + +#if defined(_WIN32) + if ( commitSize != 0 ) + { + m_commitSize = commitSize; + } + + unsigned pageSize; + +#ifndef _X360 + SYSTEM_INFO sysInfo; + GetSystemInfo( &sysInfo ); + Assert( !( sysInfo.dwPageSize & (sysInfo.dwPageSize-1)) ); + pageSize = sysInfo.dwPageSize; +#else + pageSize = 64*1024; +#endif + + if ( m_commitSize == 0 ) + { + m_commitSize = pageSize; + } + else + { + m_commitSize = AlignValue( m_commitSize, pageSize ); + } + + m_maxSize = AlignValue( m_maxSize, m_commitSize ); + + Assert( m_maxSize % pageSize == 0 && m_commitSize % pageSize == 0 && m_commitSize <= m_maxSize ); + + m_pBase = (unsigned char *)VirtualAlloc( NULL, m_maxSize, VA_RESERVE_FLAGS, PAGE_NOACCESS ); + Assert( m_pBase ); + m_pCommitLimit = m_pNextAlloc = m_pBase; + + if ( initialCommit ) + { + initialCommit = AlignValue( initialCommit, m_commitSize ); + Assert( initialCommit < m_maxSize ); + if ( !VirtualAlloc( m_pCommitLimit, initialCommit, VA_COMMIT_FLAGS, PAGE_READWRITE ) ) + return false; + m_minCommit = initialCommit; + m_pCommitLimit += initialCommit; + MemAlloc_RegisterExternalAllocation( CMemoryStack, GetBase(), GetSize() ); + } + +#else + m_pBase = (byte *)MemAlloc_AllocAligned( m_maxSize, alignment ? alignment : 1 ); + m_pNextAlloc = m_pBase; + m_pCommitLimit = m_pBase + m_maxSize; +#endif + + m_pAllocLimit = m_pBase + m_maxSize; + + return ( m_pBase != NULL ); +} + +//------------------------------------- + +#ifdef _X360 +bool CMemoryStack::InitPhysical( unsigned size, unsigned alignment ) +{ + m_bPhysical = true; + + m_maxSize = m_commitSize = size; + m_alignment = AlignValue( alignment, 4 ); + + int flags = PAGE_READWRITE; + if ( size >= 16*1024*1024 ) + { + flags |= MEM_16MB_PAGES; + } + else + { + flags |= MEM_LARGE_PAGES; + } + m_pBase = (unsigned char *)XPhysicalAlloc( m_maxSize, MAXULONG_PTR, 4096, flags ); + Assert( m_pBase ); + m_pNextAlloc = m_pBase; + m_pCommitLimit = m_pBase + m_maxSize; + m_pAllocLimit = m_pBase + m_maxSize; + + MemAlloc_RegisterExternalAllocation( CMemoryStack, GetBase(), GetSize() ); + return ( m_pBase != NULL ); +} +#endif + +//------------------------------------- + +void CMemoryStack::Term() +{ + FreeAll(); + if ( m_pBase ) + { +#if defined(_WIN32) + VirtualFree( m_pBase, 0, MEM_RELEASE ); +#else + MemAlloc_FreeAligned( m_pBase ); +#endif + m_pBase = NULL; + } +} + +//------------------------------------- + +int CMemoryStack::GetSize() +{ +#ifdef _WIN32 + return m_pCommitLimit - m_pBase; +#else + return m_maxSize; +#endif +} + + +//------------------------------------- + +bool CMemoryStack::CommitTo( byte *pNextAlloc ) RESTRICT +{ +#ifdef _X360 + if ( m_bPhysical ) + { + return NULL; + } +#endif +#if defined(_WIN32) + unsigned char * pNewCommitLimit = AlignValue( pNextAlloc, m_commitSize ); + unsigned commitSize = pNewCommitLimit - m_pCommitLimit; + + if ( GetSize() ) + MemAlloc_RegisterExternalDeallocation( CMemoryStack, GetBase(), GetSize() ); + + if( m_pCommitLimit + commitSize > m_pAllocLimit ) + { + return false; + } + + if ( !VirtualAlloc( m_pCommitLimit, commitSize, VA_COMMIT_FLAGS, PAGE_READWRITE ) ) + { + Assert( 0 ); + return false; + } + m_pCommitLimit = pNewCommitLimit; + + if ( GetSize() ) + MemAlloc_RegisterExternalAllocation( CMemoryStack, GetBase(), GetSize() ); + return true; +#else + Assert( 0 ); + return false; +#endif +} + +//------------------------------------- + +void CMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit ) +{ + void *pAllocPoint = m_pBase + mark; + Assert( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc ); + + if ( pAllocPoint >= m_pBase && pAllocPoint < m_pNextAlloc ) + { + if ( bDecommit ) + { +#if defined(_WIN32) + unsigned char *pDecommitPoint = AlignValue( (unsigned char *)pAllocPoint, m_commitSize ); + + if ( pDecommitPoint < m_pBase + m_minCommit ) + { + pDecommitPoint = m_pBase + m_minCommit; + } + + unsigned decommitSize = m_pCommitLimit - pDecommitPoint; + + if ( decommitSize > 0 ) + { + MemAlloc_RegisterExternalDeallocation( CMemoryStack, GetBase(), GetSize() ); + + VirtualFree( pDecommitPoint, decommitSize, MEM_DECOMMIT ); + m_pCommitLimit = pDecommitPoint; + + if ( mark > 0 ) + { + MemAlloc_RegisterExternalAllocation( CMemoryStack, GetBase(), GetSize() ); + } + } +#endif + } + m_pNextAlloc = (unsigned char *)pAllocPoint; + } +} + +//------------------------------------- + +void CMemoryStack::FreeAll( bool bDecommit ) +{ + if ( m_pBase && m_pCommitLimit - m_pBase > 0 ) + { + if ( bDecommit ) + { +#if defined(_WIN32) + MemAlloc_RegisterExternalDeallocation( CMemoryStack, GetBase(), GetSize() ); + + VirtualFree( m_pBase, m_pCommitLimit - m_pBase, MEM_DECOMMIT ); + m_pCommitLimit = m_pBase; +#endif + } + m_pNextAlloc = m_pBase; + } +} + +//------------------------------------- + +void CMemoryStack::Access( void **ppRegion, unsigned *pBytes ) +{ + *ppRegion = m_pBase; + *pBytes = ( m_pNextAlloc - m_pBase); +} + +//------------------------------------- + +void CMemoryStack::PrintContents() +{ + Msg( "Total used memory: %d\n", GetUsed() ); + Msg( "Total committed memory: %d\n", GetSize() ); +} + +//----------------------------------------------------------------------------- diff --git a/tier1/newbitbuf.cpp b/tier1/newbitbuf.cpp new file mode 100644 index 00000000..39cfd217 --- /dev/null +++ b/tier1/newbitbuf.cpp @@ -0,0 +1,713 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "bitbuf.h" +#include "coordsize.h" +#include "mathlib/vector.h" +#include "mathlib/mathlib.h" +#include "tier1/strtools.h" +#include "bitvec.h" + +// FIXME: Can't use this until we get multithreaded allocations in tier0 working for tools +// This is used by VVIS and fails to link +// NOTE: This must be the last file included!!! +//#include "tier0/memdbgon.h" + +#ifdef _X360 +// mandatory ... wary of above comment and isolating, tier0 is built as MT though +#include "tier0/memdbgon.h" +#endif + +#include "stdio.h" + +void CBitWrite::StartWriting( void *pData, int nBytes, int iStartBit, int nBits ) +{ + // Make sure it's dword aligned and padded. + Assert( (nBytes % 4) == 0 ); + Assert(((unsigned long)pData & 3) == 0); + Assert( iStartBit == 0 ); + m_pData = (uint32 *) pData; + m_pDataOut = m_pData; + m_nDataBytes = nBytes; + + if ( nBits == -1 ) + { + m_nDataBits = nBytes << 3; + } + else + { + Assert( nBits <= nBytes*8 ); + m_nDataBits = nBits; + } + m_bOverflow = false; + m_nOutBufWord = 0; + m_nOutBitsAvail = 32; + m_pBufferEnd = m_pDataOut + ( nBytes >> 2 ); +} + +const uint32 CBitBuffer::s_nMaskTable[33] = { + 0, + ( 1 << 1 ) - 1, + ( 1 << 2 ) - 1, + ( 1 << 3 ) - 1, + ( 1 << 4 ) - 1, + ( 1 << 5 ) - 1, + ( 1 << 6 ) - 1, + ( 1 << 7 ) - 1, + ( 1 << 8 ) - 1, + ( 1 << 9 ) - 1, + ( 1 << 10 ) - 1, + ( 1 << 11 ) - 1, + ( 1 << 12 ) - 1, + ( 1 << 13 ) - 1, + ( 1 << 14 ) - 1, + ( 1 << 15 ) - 1, + ( 1 << 16 ) - 1, + ( 1 << 17 ) - 1, + ( 1 << 18 ) - 1, + ( 1 << 19 ) - 1, + ( 1 << 20 ) - 1, + ( 1 << 21 ) - 1, + ( 1 << 22 ) - 1, + ( 1 << 23 ) - 1, + ( 1 << 24 ) - 1, + ( 1 << 25 ) - 1, + ( 1 << 26 ) - 1, + ( 1 << 27 ) - 1, + ( 1 << 28 ) - 1, + ( 1 << 29 ) - 1, + ( 1 << 30 ) - 1, + 0x7fffffff, + 0xffffffff, +}; + +bool CBitWrite::WriteString( const char *pStr ) +{ + if(pStr) + { + while( *pStr ) + { + WriteChar( * ( pStr++ ) ); + } + } + WriteChar( 0 ); + return !IsOverflowed(); +} + + +void CBitWrite::WriteLongLong(int64 val) +{ + uint *pLongs = (uint*)&val; + + // Insert the two DWORDS according to network endian + const short endianIndex = 0x0100; + byte *idx = (byte*)&endianIndex; + WriteUBitLong(pLongs[*idx++], sizeof(long) << 3); + WriteUBitLong(pLongs[*idx], sizeof(long) << 3); +} + +bool CBitWrite::WriteBits(const void *pInData, int nBits) +{ + unsigned char *pOut = (unsigned char*)pInData; + int nBitsLeft = nBits; + + // Bounds checking.. + if ( ( GetNumBitsWritten() + nBits) > m_nDataBits ) + { + SetOverflowFlag(); + CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, m_pDebugName ); + return false; + } + + // !! speed!! need fast paths + // write remaining bytes + while ( nBitsLeft >= 8 ) + { + WriteUBitLong( *pOut, 8, false ); + ++pOut; + nBitsLeft -= 8; + } + + // write remaining bits + if ( nBitsLeft ) + { + WriteUBitLong( *pOut, nBitsLeft, false ); + } + + return !IsOverflowed(); +} + +void CBitWrite::WriteBytes( const void *pBuf, int nBytes ) +{ + WriteBits(pBuf, nBytes << 3); +} + +void CBitWrite::WriteBitCoord (const float f) +{ + int signbit = (f <= -COORD_RESOLUTION); + int intval = (int)fabs(f); + int fractval = abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1); + + + // Send the bit flags that indicate whether we have an integer part and/or a fraction part. + WriteOneBit( intval ); + WriteOneBit( fractval ); + + if ( intval || fractval ) + { + // Send the sign bit + WriteOneBit( signbit ); + + // Send the integer if we have one. + if ( intval ) + { + // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1] + intval--; + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); + } + + // Send the fraction if we have one + if ( fractval ) + { + WriteUBitLong( (unsigned int)fractval, COORD_FRACTIONAL_BITS ); + } + } +} + +void CBitWrite::WriteBitCoordMP (const float f, EBitCoordType coordType ) +{ + int signbit = (f <= -( coordType == kCW_LowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION )); + int intval = (int)fabs(f); + int fractval = coordType == kCW_LowPrecision ? + ( abs((int)(f*COORD_DENOMINATOR_LOWPRECISION)) & (COORD_DENOMINATOR_LOWPRECISION-1) ) : + ( abs((int)(f*COORD_DENOMINATOR)) & (COORD_DENOMINATOR-1) ); + + bool bInBounds = intval < (1 << COORD_INTEGER_BITS_MP ); + + WriteOneBit( bInBounds ); + + if ( coordType == kCW_Integral ) + { + // Send the sign bit + WriteOneBit( intval ); + if ( intval ) + { + WriteOneBit( signbit ); + // Send the integer if we have one. + // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1] + intval--; + if ( bInBounds ) + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS_MP ); + } + else + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); + } + } + } + else + { + // Send the bit flags that indicate whether we have an integer part and/or a fraction part. + WriteOneBit( intval ); + // Send the sign bit + WriteOneBit( signbit ); + + // Send the integer if we have one. + if ( intval ) + { + // Adjust the integers from [1..MAX_COORD_VALUE] to [0..MAX_COORD_VALUE-1] + intval--; + if ( bInBounds ) + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS_MP ); + } + else + { + WriteUBitLong( (unsigned int)intval, COORD_INTEGER_BITS ); + } + } + WriteUBitLong( (unsigned int)fractval, coordType == kCW_LowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); + } +} + +void CBitWrite::SeekToBit( int nBit ) +{ + TempFlush(); + m_pDataOut = m_pData + ( nBit / 32 ); + m_nOutBufWord = *( m_pDataOut ); + m_nOutBitsAvail = 32 - ( nBit & 31 ); +} + + + +void CBitWrite::WriteBitVec3Coord( const Vector& fa ) +{ + int xflag, yflag, zflag; + + xflag = (fa[0] >= COORD_RESOLUTION) || (fa[0] <= -COORD_RESOLUTION); + yflag = (fa[1] >= COORD_RESOLUTION) || (fa[1] <= -COORD_RESOLUTION); + zflag = (fa[2] >= COORD_RESOLUTION) || (fa[2] <= -COORD_RESOLUTION); + + WriteOneBit( xflag ); + WriteOneBit( yflag ); + WriteOneBit( zflag ); + + if ( xflag ) + WriteBitCoord( fa[0] ); + if ( yflag ) + WriteBitCoord( fa[1] ); + if ( zflag ) + WriteBitCoord( fa[2] ); +} + +void CBitWrite::WriteBitNormal( float f ) +{ + int signbit = (f <= -NORMAL_RESOLUTION); + + // NOTE: Since +/-1 are valid values for a normal, I'm going to encode that as all ones + unsigned int fractval = abs( (int)(f*NORMAL_DENOMINATOR) ); + + // clamp.. + if (fractval > NORMAL_DENOMINATOR) + fractval = NORMAL_DENOMINATOR; + + // Send the sign bit + WriteOneBit( signbit ); + + // Send the fractional component + WriteUBitLong( fractval, NORMAL_FRACTIONAL_BITS ); +} + +void CBitWrite::WriteBitVec3Normal( const Vector& fa ) +{ + int xflag, yflag; + + xflag = (fa[0] >= NORMAL_RESOLUTION) || (fa[0] <= -NORMAL_RESOLUTION); + yflag = (fa[1] >= NORMAL_RESOLUTION) || (fa[1] <= -NORMAL_RESOLUTION); + + WriteOneBit( xflag ); + WriteOneBit( yflag ); + + if ( xflag ) + WriteBitNormal( fa[0] ); + if ( yflag ) + WriteBitNormal( fa[1] ); + + // Write z sign bit + int signbit = (fa[2] <= -NORMAL_RESOLUTION); + WriteOneBit( signbit ); +} + +void CBitWrite::WriteBitAngle( float fAngle, int numbits ) +{ + + unsigned int shift = GetBitForBitnum(numbits); + unsigned int mask = shift - 1; + + int d = (int)( (fAngle / 360.0) * shift ); + d &= mask; + + WriteUBitLong((unsigned int)d, numbits); +} + +bool CBitWrite::WriteBitsFromBuffer( bf_read *pIn, int nBits ) +{ +// This could be optimized a little by + while ( nBits > 32 ) + { + WriteUBitLong( pIn->ReadUBitLong( 32 ), 32 ); + nBits -= 32; + } + + WriteUBitLong( pIn->ReadUBitLong( nBits ), nBits ); + return !IsOverflowed() && !pIn->IsOverflowed(); +} + +void CBitWrite::WriteBitAngles( const QAngle& fa ) +{ + // FIXME: + Vector tmp( fa.x, fa.y, fa.z ); + WriteBitVec3Coord( tmp ); +} + +bool CBitRead::Seek( int nPosition ) +{ + bool bSucc = true; + if ( nPosition < 0 || nPosition > m_nDataBits) + { + SetOverflowFlag(); + bSucc = false; + nPosition = m_nDataBits; + } + int nHead = m_nDataBytes & 3; // non-multiple-of-4 bytes at head of buffer. We put the "round off" + // at the head to make reading and detecting the end efficient. + + int nByteOfs = nPosition / 8; + if ( ( m_nDataBytes < 4 ) || ( nHead && ( nByteOfs < nHead ) ) ) + { + // partial first dword + uint8 const *pPartial = ( uint8 const *) m_pData; + if ( m_pData ) + { + m_nInBufWord = *( pPartial++ ); + if ( nHead > 1 ) + m_nInBufWord |= ( *pPartial++ ) << 8; + if ( nHead > 2 ) + m_nInBufWord |= ( *pPartial++ ) << 16; + } + m_pDataIn = ( uint32 const * ) pPartial; + m_nInBufWord >>= ( nPosition & 31 ); + m_nBitsAvail = ( nHead << 3 ) - ( nPosition & 31 ); + } + else + { + int nAdjPosition = nPosition - ( nHead << 3 ); + m_pDataIn = reinterpret_cast ( + reinterpret_cast( m_pData ) + ( ( nAdjPosition / 32 ) << 2 ) + nHead ); + if ( m_pData ) + { + m_nBitsAvail = 32; + GrabNextDWord(); + } + else + { + m_nInBufWord = 0; + m_nBitsAvail = 1; + } + m_nInBufWord >>= ( nAdjPosition & 31 ); + m_nBitsAvail = MIN( m_nBitsAvail, 32 - ( nAdjPosition & 31 ) ); // in case grabnextdword overflowed + } + return bSucc; +} + + +void CBitRead::StartReading( const void *pData, int nBytes, int iStartBit, int nBits ) +{ +// Make sure it's dword aligned and padded. + Assert(((unsigned long)pData & 3) == 0); + m_pData = (uint32 *) pData; + m_pDataIn = m_pData; + m_nDataBytes = nBytes; + + if ( nBits == -1 ) + { + m_nDataBits = nBytes << 3; + } + else + { + Assert( nBits <= nBytes*8 ); + m_nDataBits = nBits; + } + m_bOverflow = false; + m_pBufferEnd = reinterpret_cast ( reinterpret_cast< uint8 const *> (m_pData) + nBytes ); + if ( m_pData ) + Seek( iStartBit ); + +} + +bool CBitRead::ReadString( char *pStr, int maxLen, bool bLine, int *pOutNumChars ) +{ + Assert( maxLen != 0 ); + + bool bTooSmall = false; + int iChar = 0; + while(1) + { + char val = ReadChar(); + if ( val == 0 ) + break; + else if ( bLine && val == '\n' ) + break; + + if ( iChar < (maxLen-1) ) + { + pStr[iChar] = val; + ++iChar; + } + else + { + bTooSmall = true; + } + } + + // Make sure it's null-terminated. + Assert( iChar < maxLen ); + pStr[iChar] = 0; + + if ( pOutNumChars ) + *pOutNumChars = iChar; + + return !IsOverflowed() && !bTooSmall; +} + +char* CBitRead::ReadAndAllocateString( bool *pOverflow ) +{ + char str[2048]; + + int nChars; + bool bOverflow = !ReadString( str, sizeof( str ), false, &nChars ); + if ( pOverflow ) + *pOverflow = bOverflow; + + // Now copy into the output and return it; + char *pRet = new char[ nChars + 1 ]; + for ( int i=0; i <= nChars; i++ ) + pRet[i] = str[i]; + + return pRet; +} + +int64 CBitRead::ReadLongLong( void ) +{ + int64 retval; + uint *pLongs = (uint*)&retval; + + // Read the two DWORDs according to network endian + const short endianIndex = 0x0100; + byte *idx = (byte*)&endianIndex; + pLongs[*idx++] = ReadUBitLong(sizeof(long) << 3); + pLongs[*idx] = ReadUBitLong(sizeof(long) << 3); + return retval; +} + +void CBitRead::ReadBits(void *pOutData, int nBits) +{ + unsigned char *pOut = (unsigned char*)pOutData; + int nBitsLeft = nBits; + + + // align output to dword boundary + while( ((unsigned long)pOut & 3) != 0 && nBitsLeft >= 8 ) + { + *pOut = (unsigned char)ReadUBitLong(8); + ++pOut; + nBitsLeft -= 8; + } + + // X360TBD: Can't read dwords in ReadBits because they'll get swapped + if ( IsPC() ) + { + // read dwords + while ( nBitsLeft >= 32 ) + { + *((unsigned long*)pOut) = ReadUBitLong(32); + pOut += sizeof(unsigned long); + nBitsLeft -= 32; + } + } + + // read remaining bytes + while ( nBitsLeft >= 8 ) + { + *pOut = ReadUBitLong(8); + ++pOut; + nBitsLeft -= 8; + } + + // read remaining bits + if ( nBitsLeft ) + { + *pOut = ReadUBitLong(nBitsLeft); + } + +} + +bool CBitRead::ReadBytes(void *pOut, int nBytes) +{ + ReadBits(pOut, nBytes << 3); + return !IsOverflowed(); +} + +float CBitRead::ReadBitAngle( int numbits ) +{ + float shift = (float)( GetBitForBitnum(numbits) ); + + int i = ReadUBitLong( numbits ); + float fReturn = (float)i * (360.0 / shift); + + return fReturn; +} + +// Basic Coordinate Routines (these contain bit-field size AND fixed point scaling constants) +float CBitRead::ReadBitCoord (void) +{ + int intval=0,fractval=0,signbit=0; + float value = 0.0; + + + // Read the required integer and fraction flags + intval = ReadOneBit(); + fractval = ReadOneBit(); + + // If we got either parse them, otherwise it's a zero. + if ( intval || fractval ) + { + // Read the sign bit + signbit = ReadOneBit(); + + // If there's an integer, read it in + if ( intval ) + { + // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE] + intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1; + } + + // If there's a fraction, read it in + if ( fractval ) + { + fractval = ReadUBitLong( COORD_FRACTIONAL_BITS ); + } + + // Calculate the correct floating point value + value = intval + ((float)fractval * COORD_RESOLUTION); + + // Fixup the sign if negative. + if ( signbit ) + value = -value; + } + + return value; +} + +float CBitRead::ReadBitCoordMP( EBitCoordType coordType ) +{ + int intval=0,fractval=0,signbit=0; + float value = 0.0; + + bool bInBounds = ReadOneBit() ? true : false; + + if ( coordType == kCW_Integral ) + { + // Read the required integer and fraction flags + intval = ReadOneBit(); + // If we got either parse them, otherwise it's a zero. + if ( intval ) + { + // Read the sign bit + signbit = ReadOneBit(); + + // If there's an integer, read it in + // Adjust the integers from [0..MAX_COORD_VALUE-1] to [1..MAX_COORD_VALUE] + if ( bInBounds ) + { + value = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1; + } + else + { + value = ReadUBitLong( COORD_INTEGER_BITS ) + 1; + } + } + } + else + { + // Read the required integer and fraction flags + intval = ReadOneBit(); + + // Read the sign bit + signbit = ReadOneBit(); + + // If we got either parse them, otherwise it's a zero. + if ( intval ) + { + if ( bInBounds ) + { + intval = ReadUBitLong( COORD_INTEGER_BITS_MP ) + 1; + } + else + { + intval = ReadUBitLong( COORD_INTEGER_BITS ) + 1; + } + } + + // If there's a fraction, read it in + fractval = ReadUBitLong( coordType == kCW_LowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS ); + + // Calculate the correct floating point value + value = intval + ((float)fractval * ( coordType == kCW_LowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION ) ); + } + + // Fixup the sign if negative. + if ( signbit ) + value = -value; + + return value; +} + +void CBitRead::ReadBitVec3Coord( Vector& fa ) +{ + int xflag, yflag, zflag; + + // This vector must be initialized! Otherwise, If any of the flags aren't set, + // the corresponding component will not be read and will be stack garbage. + fa.Init( 0, 0, 0 ); + + xflag = ReadOneBit(); + yflag = ReadOneBit(); + zflag = ReadOneBit(); + + if ( xflag ) + fa[0] = ReadBitCoord(); + if ( yflag ) + fa[1] = ReadBitCoord(); + if ( zflag ) + fa[2] = ReadBitCoord(); +} + +float CBitRead::ReadBitNormal (void) +{ + // Read the sign bit + int signbit = ReadOneBit(); + + // Read the fractional part + unsigned int fractval = ReadUBitLong( NORMAL_FRACTIONAL_BITS ); + + // Calculate the correct floating point value + float value = (float)fractval * NORMAL_RESOLUTION; + + // Fixup the sign if negative. + if ( signbit ) + value = -value; + + return value; +} + +void CBitRead::ReadBitVec3Normal( Vector& fa ) +{ + int xflag = ReadOneBit(); + int yflag = ReadOneBit(); + + if (xflag) + fa[0] = ReadBitNormal(); + else + fa[0] = 0.0f; + + if (yflag) + fa[1] = ReadBitNormal(); + else + fa[1] = 0.0f; + + // The first two imply the third (but not its sign) + int znegative = ReadOneBit(); + + float fafafbfb = fa[0] * fa[0] + fa[1] * fa[1]; + if (fafafbfb < 1.0f) + fa[2] = sqrt( 1.0f - fafafbfb ); + else + fa[2] = 0.0f; + + if (znegative) + fa[2] = -fa[2]; +} + +void CBitRead::ReadBitAngles( QAngle& fa ) +{ + Vector tmp; + ReadBitVec3Coord( tmp ); + fa.Init( tmp.x, tmp.y, tmp.z ); +} diff --git a/tier1/processor_detect.cpp b/tier1/processor_detect.cpp new file mode 100644 index 00000000..be81312d --- /dev/null +++ b/tier1/processor_detect.cpp @@ -0,0 +1,278 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: win32 dependant ASM code for CPU capability detection +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// + +#if defined _LINUX || defined __APPLE__ + +#include "processor_detect_linux.cpp" + +#elif defined( _X360 ) + +bool CheckMMXTechnology(void) { return false; } +bool CheckSSETechnology(void) { return false; } +bool CheckSSE2Technology(void) { return false; } +bool Check3DNowTechnology(void) { return false; } + +#elif defined( _WIN32 ) && !defined( _X360 ) && !defined( COMPILER_MSVC64 ) + +#pragma optimize( "", off ) +#pragma warning( disable: 4800 ) //'int' : forcing value to bool 'true' or 'false' (performance warning) + +// stuff from windows.h +#ifndef EXCEPTION_EXECUTE_HANDLER +#define EXCEPTION_EXECUTE_HANDLER 1 +#endif + +bool CheckMMXTechnology(void) +{ + int retval = true; + unsigned int RegEDX = 0; + +#ifdef CPUID + _asm pushad; +#endif + + __try + { + _asm + { +#ifdef CPUID + xor edx, edx // Clue the compiler that EDX is about to be used. +#endif + mov eax, 1 // set up CPUID to return processor version and features + // 0 = vendor string, 1 = version info, 2 = cache info + CPUID // code bytes = 0fh, 0a2h + mov RegEDX, edx // features returned in edx + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + retval = false; + } + + // If CPUID not supported, then certainly no MMX extensions. + if (retval) + { + if (RegEDX & 0x800000) // bit 23 is set for MMX technology + { + __try + { + // try executing the MMX instruction "emms" + _asm EMMS + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + retval = false; + } + } + + else + retval = false; // processor supports CPUID but does not support MMX technology + + // if retval == 0 here, it means the processor has MMX technology but + // floating-point emulation is on; so MMX technology is unavailable + } + +#ifdef CPUID + _asm popad; +#endif + + return retval; +} + +bool CheckSSETechnology(void) +{ + int retval = true; + unsigned int RegEDX = 0; + +#ifdef CPUID + _asm pushad; +#endif + + // Do we have support for the CPUID function? + __try + { + _asm + { +#ifdef CPUID + xor edx, edx // Clue the compiler that EDX is about to be used. +#endif + mov eax, 1 // set up CPUID to return processor version and features + // 0 = vendor string, 1 = version info, 2 = cache info + CPUID // code bytes = 0fh, 0a2h + mov RegEDX, edx // features returned in edx + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + retval = false; + } + + // If CPUID not supported, then certainly no SSE extensions. + if (retval) + { + // Do we have support for SSE in this processor? + if ( RegEDX & 0x2000000L ) // bit 25 is set for SSE technology + { + // Make sure that SSE is supported by executing an inline SSE instruction + +// BUGBUG, FIXME - Visual C Version 6.0 does not support SSE inline code YET (No macros from Intel either) +// Fix this if VC7 supports inline SSE instructinons like "xorps" as shown below. +#if 1 + __try + { + _asm + { + // Attempt execution of a SSE instruction to make sure OS supports SSE FPU context switches + xorps xmm0, xmm0 + // This will work on Win2k+ (Including masking SSE FPU exception to "normalized" values) + // This will work on Win98+ (But no "masking" of FPU exceptions provided) + } + } + __except(EXCEPTION_EXECUTE_HANDLER) +#endif + + { + retval = false; + } + } + else + retval = false; + } +#ifdef CPUID + _asm popad; +#endif + + return retval; +} + +bool CheckSSE2Technology(void) +{ + int retval = true; + unsigned int RegEDX = 0; + +#ifdef CPUID + _asm pushad; +#endif + + // Do we have support for the CPUID function? + __try + { + _asm + { +#ifdef CPUID + xor edx, edx // Clue the compiler that EDX is about to be used. +#endif + mov eax, 1 // set up CPUID to return processor version and features + // 0 = vendor string, 1 = version info, 2 = cache info + CPUID // code bytes = 0fh, 0a2h + mov RegEDX, edx // features returned in edx + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + retval = false; + } + + // If CPUID not supported, then certainly no SSE extensions. + if (retval) + { + // Do we have support for SSE in this processor? + if ( RegEDX & 0x04000000 ) // bit 26 is set for SSE2 technology + { + // Make sure that SSE is supported by executing an inline SSE instruction + + __try + { + _asm + { + // Attempt execution of a SSE2 instruction to make sure OS supports SSE FPU context switches + xorpd xmm0, xmm0 + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + + { + retval = false; + } + } + else + retval = false; + } +#ifdef CPUID + _asm popad; +#endif + + return retval; +} + +bool Check3DNowTechnology(void) +{ + int retval = true; + unsigned int RegEAX = 0; + +#ifdef CPUID + _asm pushad; +#endif + + // First see if we can execute CPUID at all + __try + { + _asm + { +#ifdef CPUID +// xor edx, edx // Clue the compiler that EDX is about to be used. +#endif + mov eax, 0x80000000 // setup CPUID to return whether AMD >0x80000000 function are supported. + // 0x80000000 = Highest 0x80000000+ function, 0x80000001 = 3DNow support + CPUID // code bytes = 0fh, 0a2h + mov RegEAX, eax // result returned in eax + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + retval = false; + } + + // If CPUID not supported, then there is definitely no 3DNow support + if (retval) + { + // Are there any "higher" AMD CPUID functions? + if (RegEAX > 0x80000000L ) + { + __try + { + _asm + { + mov eax, 0x80000001 // setup to test for CPU features + CPUID // code bytes = 0fh, 0a2h + shr edx, 31 // If bit 31 is set, we have 3DNow support! + mov retval, edx // Save the return value for end of function + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + retval = false; + } + } + else + { + // processor supports CPUID but does not support AMD CPUID functions + retval = false; + } + } + +#ifdef CPUID + _asm popad; +#endif + + return retval; +} + +#pragma optimize( "", on ) + +#endif // _WIN32 diff --git a/tier1/processor_detect_linux.cpp b/tier1/processor_detect_linux.cpp new file mode 100644 index 00000000..079d2731 --- /dev/null +++ b/tier1/processor_detect_linux.cpp @@ -0,0 +1,63 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: linux dependant ASM code for CPU capability detection +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// + +#define cpuid(in,a,b,c,d) \ + asm("pushl %%ebx\n\t" "cpuid\n\t" "movl %%ebx,%%esi\n\t" "pop %%ebx": "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (in)); + +bool CheckMMXTechnology(void) +{ +#ifndef PLATFORM_64BITS + unsigned long eax,ebx,edx,unused; + cpuid(1,eax,ebx,unused,edx); + + return edx & 0x800000; +#else + return true; +#endif +} + +bool CheckSSETechnology(void) +{ +#ifndef PLATFORM_64BITS + unsigned long eax,ebx,edx,unused; + cpuid(1,eax,ebx,unused,edx); + + return edx & 0x2000000L; +#else + return true; +#endif +} + +bool CheckSSE2Technology(void) +{ +#ifndef PLATFORM_64BITS + unsigned long eax,ebx,edx,unused; + cpuid(1,eax,ebx,unused,edx); + + return edx & 0x04000000; +#else + return true; +#endif +} + +bool Check3DNowTechnology(void) +{ +#ifndef PLATFORM_64BITS + unsigned long eax, unused; + cpuid(0x80000000,eax,unused,unused,unused); + + if ( eax > 0x80000000L ) + { + cpuid(0x80000001,unused,unused,unused,eax); + return ( eax & 1<<31 ); + } + return false; +#else + return true; +#endif +} diff --git a/tier1/rangecheckedvar.cpp b/tier1/rangecheckedvar.cpp new file mode 100644 index 00000000..1633fbab --- /dev/null +++ b/tier1/rangecheckedvar.cpp @@ -0,0 +1,42 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "mathlib/mathlib.h" +#include "rangecheckedvar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +bool g_bDoRangeChecks = true; + + +static int g_nDisables = 0; + + +CDisableRangeChecks::CDisableRangeChecks() +{ + if ( !ThreadInMainThread() ) + return; + g_nDisables++; + g_bDoRangeChecks = false; +} + + +CDisableRangeChecks::~CDisableRangeChecks() +{ + if ( !ThreadInMainThread() ) + return; + Assert( g_nDisables > 0 ); + --g_nDisables; + if ( g_nDisables == 0 ) + { + g_bDoRangeChecks = true; + } +} + + + + diff --git a/tier1/stringpool.cpp b/tier1/stringpool.cpp new file mode 100644 index 00000000..699f5cb2 --- /dev/null +++ b/tier1/stringpool.cpp @@ -0,0 +1,350 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "convar.h" +#include "tier0/dbg.h" +#include "stringpool.h" +#include "tier1/strtools.h" +#include "generichash.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Comparison function for string sorted associative data structures +//----------------------------------------------------------------------------- + +bool StrLessSensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( Q_strcmp( pszLeft, pszRight) < 0 ); +} + +bool StrLessInsensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( Q_stricmp( pszLeft, pszRight) < 0 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +CStringPool::CStringPool( StringPoolCase_t caseSensitivity ) + : m_Strings( 32, 256, caseSensitivity == StringPoolCaseInsensitive ? StrLessInsensitive : StrLessSensitive ) +{ +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +CStringPool::~CStringPool() +{ + FreeAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +unsigned int CStringPool::Count() const +{ + return m_Strings.Count(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char * CStringPool::Find( const char *pszValue ) +{ + unsigned short i = m_Strings.Find(pszValue); + if ( m_Strings.IsValidIndex(i) ) + return m_Strings[i]; + + return NULL; +} + +const char * CStringPool::Allocate( const char *pszValue ) +{ + char *pszNew; + + unsigned short i = m_Strings.Find(pszValue); + bool bNew = (i == m_Strings.InvalidIndex()); + + if ( !bNew ) + return m_Strings[i]; + + pszNew = strdup( pszValue ); + + if ( bNew ) + m_Strings.Insert( pszNew ); + + return pszNew; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CStringPool::FreeAll() +{ + unsigned short i = m_Strings.FirstInorder(); + while ( i != m_Strings.InvalidIndex() ) + { + free( (void *)m_Strings[i] ); + i = m_Strings.NextInorder(i); + } + m_Strings.RemoveAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +CCountedStringPool::CCountedStringPool( StringPoolCase_t caseSensitivity ) +{ + MEM_ALLOC_CREDIT(); + m_HashTable.EnsureCount(HASH_TABLE_SIZE); + + for( int i = 0; i < m_HashTable.Count(); i++ ) + { + m_HashTable[i] = INVALID_ELEMENT; + } + + m_FreeListStart = INVALID_ELEMENT; + m_Elements.AddToTail(); + m_Elements[0].pString = NULL; + m_Elements[0].nReferenceCount = 0; + m_Elements[0].nNextElement = INVALID_ELEMENT; + + m_caseSensitivity = caseSensitivity; +} + +CCountedStringPool::~CCountedStringPool() +{ + FreeAll(); +} + +void CCountedStringPool::FreeAll() +{ + int i; + + // Reset the hash table: + for( i = 0; i < m_HashTable.Count(); i++ ) + { + m_HashTable[i] = INVALID_ELEMENT; + } + + // Blow away the free list: + m_FreeListStart = INVALID_ELEMENT; + + for( i = 0; i < m_Elements.Count(); i++ ) + { + if( m_Elements[i].pString ) + { + delete [] m_Elements[i].pString; + m_Elements[i].pString = NULL; + m_Elements[i].nReferenceCount = 0; + m_Elements[i].nNextElement = INVALID_ELEMENT; + } + } + + // Remove all but the invalid element: + m_Elements.RemoveAll(); + m_Elements.AddToTail(); + m_Elements[0].pString = NULL; + m_Elements[0].nReferenceCount = 0; + m_Elements[0].nNextElement = INVALID_ELEMENT; +} + + +unsigned short CCountedStringPool::FindStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + unsigned short nHashBucketIndex = (m_caseSensitivity ? HashString( pIntrinsic ) : HashStringCaseless( pIntrinsic ) %HASH_TABLE_SIZE); + unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // Does the bucket already exist? + if( nCurrentBucket != INVALID_ELEMENT ) + { + for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + return nCurrentBucket; + } + } + } + + return 0; + +} + +char* CCountedStringPool::FindString( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return NULL; + + // Yes, this will be NULL on failure. + return m_Elements[FindStringHandle(pIntrinsic)].pString; +} + +unsigned short CCountedStringPool::ReferenceStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + unsigned short nHashBucketIndex = (m_caseSensitivity ? HashString( pIntrinsic ) : HashStringCaseless( pIntrinsic ) % HASH_TABLE_SIZE); + unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // Does the bucket already exist? + if( nCurrentBucket != INVALID_ELEMENT ) + { + for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + // Anyone who hits 65k references is permanant + if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE ) + { + m_Elements[nCurrentBucket].nReferenceCount ++ ; + } + return nCurrentBucket; + } + } + } + + if( m_FreeListStart != INVALID_ELEMENT ) + { + nCurrentBucket = m_FreeListStart; + m_FreeListStart = m_Elements[nCurrentBucket].nNextElement; + } + else + { + nCurrentBucket = m_Elements.AddToTail(); + } + + m_Elements[nCurrentBucket].nReferenceCount = 1; + + // Insert at the beginning of the bucket: + m_Elements[nCurrentBucket].nNextElement = m_HashTable[ nHashBucketIndex ]; + m_HashTable[ nHashBucketIndex ] = nCurrentBucket; + + m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1]; + Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic ); + + return nCurrentBucket; +} + + +char* CCountedStringPool::ReferenceString( const char* pIntrinsic ) +{ + if(!pIntrinsic) + return NULL; + + return m_Elements[ReferenceStringHandle( pIntrinsic)].pString; +} + +void CCountedStringPool::DereferenceString( const char* pIntrinsic ) +{ + // If we get a NULL pointer, just return + if (!pIntrinsic) + return; + + unsigned short nHashBucketIndex = (m_caseSensitivity ? HashString( pIntrinsic ) : HashStringCaseless( pIntrinsic ) % m_HashTable.Count()); + unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // If there isn't anything in the bucket, just return. + if ( nCurrentBucket == INVALID_ELEMENT ) + return; + + for( unsigned short previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + // Anyone who hits 65k references is permanant + if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE ) + { + m_Elements[nCurrentBucket].nReferenceCount --; + } + + if( m_Elements[nCurrentBucket].nReferenceCount == 0 ) + { + if( previous == INVALID_ELEMENT ) + { + m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement; + } + else + { + m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement; + } + + delete [] m_Elements[nCurrentBucket].pString; + m_Elements[nCurrentBucket].pString = NULL; + m_Elements[nCurrentBucket].nReferenceCount = 0; + + m_Elements[nCurrentBucket].nNextElement = m_FreeListStart; + m_FreeListStart = nCurrentBucket; + break; + + } + } + + previous = nCurrentBucket; + } +} + +char* CCountedStringPool::HandleToString( unsigned short handle ) +{ + return m_Elements[handle].pString; +} + +unsigned CCountedStringPool::Hash( const char *pszKey ) +{ + if (m_caseSensitivity == StringPoolCaseSensitive) + return HashString( pszKey ); + + return HashStringCaseless( pszKey ); +} + +void CCountedStringPool::SpewStrings() +{ + int i; + for ( i = 0; i < m_Elements.Count(); i++ ) + { + char* string = m_Elements[i].pString; + + Msg("String %d: ref:%d %s", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string); + } + + Msg("\n%d total counted strings.", m_Elements.Count()); +} + +#ifdef _DEBUG1111 +CON_COMMAND( test_stringpool, "Tests the class CStringPool" ) +{ + CStringPool pool; + + Assert(pool.Count() == 0); + + pool.Allocate("test"); + Assert(pool.Count() == 1); + + pool.Allocate("test"); + Assert(pool.Count() == 1); + + pool.Allocate("test2"); + Assert(pool.Count() == 2); + + Assert( pool.Find("test2") != NULL ); + Assert( pool.Find("TEST") != NULL ); + Assert( pool.Find("Test2") != NULL ); + Assert( pool.Find("test") != NULL ); + + pool.FreeAll(); + Assert(pool.Count() == 0); + + Msg("Pass."); +} +#endif diff --git a/tier1/strtools.cpp b/tier1/strtools.cpp new file mode 100644 index 00000000..cdb7ac07 --- /dev/null +++ b/tier1/strtools.cpp @@ -0,0 +1,2017 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: String Tools +// +//===========================================================================// + +// These are redefined in the project settings to prevent anyone from using them. +// We in this module are of a higher caste and thus are privileged in their use. +#ifdef strncpy + #undef strncpy +#endif + +#ifdef _snprintf + #undef _snprintf +#endif + +#if defined( sprintf ) + #undef sprintf +#endif + +#if defined( vsprintf ) + #undef vsprintf +#endif + +#ifdef _vsnprintf +#ifdef _WIN32 + #undef _vsnprintf +#endif +#endif + +#ifdef vsnprintf +#ifndef _WIN32 + #undef vsnprintf +#endif +#endif + +#if defined( strcat ) + #undef strcat +#endif + +#ifdef strncat + #undef strncat +#endif + +// NOTE: I have to include stdio + stdarg first so vsnprintf gets compiled in +#include +#include + +#if defined _LINUX || defined __APPLE__ +#include +#include +#include +#define _getcwd getcwd +#elif _WIN32 +#include +#if !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include +#endif +#endif + +#ifdef _WIN32 +#ifndef CP_UTF8 +#define CP_UTF8 65001 +#endif +#endif +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include +#include +#include "tier0/basetypes.h" +#include "tier1/utldict.h" +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif +#include "tier0/memdbgon.h" + +void _V_memset (void *dest, int fill, int count) +{ + Assert( count >= 0 ); + AssertValidWritePtr( dest, count ); + + memset(dest,fill,count); +} + +void _V_memcpy (void *dest, const void *src, int count) +{ + Assert( count >= 0 ); + AssertValidReadPtr( src, count ); + AssertValidWritePtr( dest, count ); + + memcpy( dest, src, count ); +} + +void _V_memmove(void *dest, const void *src, int count) +{ + Assert( count >= 0 ); + AssertValidReadPtr( src, count ); + AssertValidWritePtr( dest, count ); + + memmove( dest, src, count ); +} + +int _V_memcmp (const void *m1, const void *m2, int count) +{ + Assert( count >= 0 ); + AssertValidReadPtr( m1, count ); + AssertValidReadPtr( m2, count ); + + return memcmp( m1, m2, count ); +} + +int _V_strlen(const char *str) +{ + AssertValidStringPtr(str); + return strlen( str ); +} + +void _V_strcpy (char *dest, const char *src) +{ + AssertValidWritePtr(dest); + AssertValidStringPtr(src); + + strcpy( dest, src ); +} + +int _V_wcslen(const wchar_t *pwch) +{ + return wcslen( pwch ); +} + +char *_V_strrchr(const char *s, char c) +{ + AssertValidStringPtr( s ); + int len = V_strlen(s); + s += len; + while (len--) + if (*--s == c) return (char *)s; + return 0; +} + +int _V_strcmp (const char *s1, const char *s2) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + + return strcmp( s1, s2 ); +} + +int _V_wcscmp (const wchar_t *s1, const wchar_t *s2) +{ + while (1) + { + if (*s1 != *s2) + return -1; // strings not equal + if (!*s1) + return 0; // strings are equal + s1++; + s2++; + } + + return -1; +} + +#ifdef PLATFORM_WINDOWS +#undef stricmp +#endif + +int _V_stricmp( const char *s1, const char *s2 ) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + + return stricmp( s1, s2 ); +} + + +char *_V_strstr( const char *s1, const char *search ) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( search ); + +#if defined( _X360 ) + return (char *)strstr( (char *)s1, search ); +#else + return (char *)strstr( s1, search ); +#endif +} + +char *_V_strupr (char *start) +{ + AssertValidStringPtr( start ); + return strupr( start ); +} + + +char *_V_strlower (char *start) +{ + AssertValidStringPtr( start ); + return strlwr(start); +} + +int V_strncmp (const char *s1, const char *s2, int count) +{ + Assert( count >= 0 ); + AssertValidStringPtr( s1, count ); + AssertValidStringPtr( s2, count ); + + while ( count-- > 0 ) + { + if ( *s1 != *s2 ) + return *s1 < *s2 ? -1 : 1; // string different + if ( *s1 == '\0' ) + return 0; // null terminator hit - strings the same + s1++; + s2++; + } + + return 0; // count characters compared the same +} + +char *V_strnlwr(char *s, size_t count) +{ + Assert( count >= 0 ); + AssertValidStringPtr( s, count ); + + char* pRet = s; + if ( !s ) + return s; + + while ( count-- ) + { + if ( !*s ) + break; + + *s = tolower( *s ); + ++s; + } + + if ( count > 0 ) + { + s[count-1] = 0; + } + + return pRet; +} + + +int V_strncasecmp (const char *s1, const char *s2, int n) +{ + Assert( n >= 0 ); + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + + while ( n-- > 0 ) + { + int c1 = *s1++; + int c2 = *s2++; + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return c1 < c2 ? -1 : 1; + } + if ( c1 == '\0' ) + return 0; // null terminator hit - strings the same + } + + return 0; // n characters compared the same +} + +int V_strcasecmp( const char *s1, const char *s2 ) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + + return stricmp( s1, s2 ); +} + +int V_strnicmp (const char *s1, const char *s2, int n) +{ + Assert( n >= 0 ); + AssertValidStringPtr(s1); + AssertValidStringPtr(s2); + + return V_strncasecmp( s1, s2, n ); +} + + +const char *StringAfterPrefix( const char *str, const char *prefix ) +{ + AssertValidStringPtr( str ); + AssertValidStringPtr( prefix ); + do + { + if ( !*prefix ) + return str; + } + while ( tolower( *str++ ) == tolower( *prefix++ ) ); + return NULL; +} + +const char *StringAfterPrefixCaseSensitive( const char *str, const char *prefix ) +{ + AssertValidStringPtr( str ); + AssertValidStringPtr( prefix ); + do + { + if ( !*prefix ) + return str; + } + while ( *str++ == *prefix++ ); + return NULL; +} + + +int V_atoi (const char *str) +{ + AssertValidStringPtr( str ); + + int val; + int sign; + int c; + + Assert( str ); + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val<<4) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val<<4) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val<<4) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val*sign; + val = val*10 + c - '0'; + } + + return 0; +} + + +float V_atof (const char *str) +{ + AssertValidStringPtr( str ); + double val; + int sign; + int c; + int decimal, total; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val*16) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val*16) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val*16) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + decimal = -1; + total = 0; + while (1) + { + c = *str++; + if (c == '.') + { + decimal = total; + continue; + } + if (c <'0' || c > '9') + break; + val = val*10 + c - '0'; + total++; + } + + if (decimal == -1) + return val*sign; + while (total > decimal) + { + val /= 10; + total--; + } + + return val*sign; +} + +//----------------------------------------------------------------------------- +// Normalizes a float string in place. +// +// (removes leading zeros, trailing zeros after the decimal point, and the decimal point itself where possible) +//----------------------------------------------------------------------------- +void V_normalizeFloatString( char* pFloat ) +{ + // If we have a decimal point, remove trailing zeroes: + if( strchr( pFloat,'.' ) ) + { + int len = V_strlen(pFloat); + + while( len > 1 && pFloat[len - 1] == '0' ) + { + pFloat[len - 1] = '\0'; + len--; + } + + if( len > 1 && pFloat[ len - 1 ] == '.' ) + { + pFloat[len - 1] = '\0'; + len--; + } + } + + // TODO: Strip leading zeros + +} + + +//----------------------------------------------------------------------------- +// Finds a string in another string with a case insensitive test +//----------------------------------------------------------------------------- +char const* V_stristr( char const* pStr, char const* pSearch ) +{ + AssertValidStringPtr(pStr); + AssertValidStringPtr(pSearch); + + if (!pStr || !pSearch) + return 0; + + char const* pLetter = pStr; + + // Check the entire string + while (*pLetter != 0) + { + // Skip over non-matches + if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch)) + { + // Check for match + char const* pMatch = pLetter + 1; + char const* pTest = pSearch + 1; + while (*pTest != 0) + { + // We've run off the end; don't bother. + if (*pMatch == 0) + return 0; + + if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest)) + break; + + ++pMatch; + ++pTest; + } + + // Found a match! + if (*pTest == 0) + return pLetter; + } + + ++pLetter; + } + + return 0; +} + +char* V_stristr( char* pStr, char const* pSearch ) +{ + AssertValidStringPtr( pStr ); + AssertValidStringPtr( pSearch ); + + return (char*)V_stristr( (char const*)pStr, pSearch ); +} + +//----------------------------------------------------------------------------- +// Finds a string in another string with a case insensitive test w/ length validation +//----------------------------------------------------------------------------- +char const* V_strnistr( char const* pStr, char const* pSearch, int n ) +{ + AssertValidStringPtr(pStr); + AssertValidStringPtr(pSearch); + + if (!pStr || !pSearch) + return 0; + + char const* pLetter = pStr; + + // Check the entire string + while (*pLetter != 0) + { + if ( n <= 0 ) + return 0; + + // Skip over non-matches + if (tolower(*pLetter) == tolower(*pSearch)) + { + int n1 = n - 1; + + // Check for match + char const* pMatch = pLetter + 1; + char const* pTest = pSearch + 1; + while (*pTest != 0) + { + if ( n1 <= 0 ) + return 0; + + // We've run off the end; don't bother. + if (*pMatch == 0) + return 0; + + if (tolower(*pMatch) != tolower(*pTest)) + break; + + ++pMatch; + ++pTest; + --n1; + } + + // Found a match! + if (*pTest == 0) + return pLetter; + } + + ++pLetter; + --n; + } + + return 0; +} + +const char* V_strnchr( const char* pStr, char c, int n ) +{ + char const* pLetter = pStr; + char const* pLast = pStr + n; + + // Check the entire string + while ( (pLetter < pLast) && (*pLetter != 0) ) + { + if (*pLetter == c) + return pLetter; + ++pLetter; + } + return NULL; +} + +void V_strncpy( char *pDest, char const *pSrc, int maxLen ) +{ + Assert( maxLen >= 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pSrc ); + + strncpy( pDest, pSrc, maxLen ); + if ( maxLen > 0 ) + { + pDest[maxLen-1] = 0; + } +} + +void V_wcsncpy( wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ) +{ + Assert( maxLenInBytes >= 0 ); + AssertValidWritePtr( pDest, maxLenInBytes ); + AssertValidReadPtr( pSrc ); + + int maxLen = maxLenInBytes / sizeof(wchar_t); + + wcsncpy( pDest, pSrc, maxLen ); + if( maxLen ) + { + pDest[maxLen-1] = 0; + } +} + + + +int V_snwprintf( wchar_t *pDest, int maxLen, const wchar_t *pFormat, ... ) +{ + Assert( maxLen >= 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidReadPtr( pFormat ); + + va_list marker; + + va_start( marker, pFormat ); +#ifdef _WIN32 + int len = _snwprintf( pDest, maxLen, pFormat, marker ); +#elif defined _LINUX || defined __APPLE__ + int len = swprintf( pDest, maxLen, pFormat, marker ); +#else +#error "define vsnwprintf type." +#endif + va_end( marker ); + + // Len < 0 represents an overflow + if( len < 0 ) + { + len = maxLen; + pDest[maxLen-1] = 0; + } + + return len; +} + + +int V_snprintf( char *pDest, int maxLen, char const *pFormat, ... ) +{ + Assert( maxLen >= 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pFormat ); + + va_list marker; + + va_start( marker, pFormat ); +#ifdef _WIN32 + int len = _vsnprintf( pDest, maxLen, pFormat, marker ); +#elif defined _LINUX || defined __APPLE__ + int len = vsnprintf( pDest, maxLen, pFormat, marker ); +#else + #error "define vsnprintf type." +#endif + va_end( marker ); + + // Len < 0 represents an overflow + if( len < 0 ) + { + len = maxLen; + pDest[maxLen-1] = 0; + } + + return len; +} + + +int V_vsnprintf( char *pDest, int maxLen, char const *pFormat, va_list params ) +{ + Assert( maxLen > 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pFormat ); + + int len = _vsnprintf( pDest, maxLen, pFormat, params ); + + if( len < 0 ) + { + len = maxLen; + pDest[maxLen-1] = 0; + } + + return len; +} + + + +//----------------------------------------------------------------------------- +// Purpose: If COPY_ALL_CHARACTERS == max_chars_to_copy then we try to add the whole pSrc to the end of pDest, otherwise +// we copy only as many characters as are specified in max_chars_to_copy (or the # of characters in pSrc if thats's less). +// Input : *pDest - destination buffer +// *pSrc - string to append +// destBufferSize - sizeof the buffer pointed to by pDest +// max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy +// Output : char * the copied buffer +//----------------------------------------------------------------------------- +char *V_strncat(char *pDest, const char *pSrc, size_t destBufferSize, int max_chars_to_copy ) +{ + size_t charstocopy = (size_t)0; + + Assert( destBufferSize >= 0 ); + AssertValidStringPtr( pDest); + AssertValidStringPtr( pSrc ); + + size_t len = strlen(pDest); + size_t srclen = strlen( pSrc ); + if ( max_chars_to_copy <= COPY_ALL_CHARACTERS ) + { + charstocopy = srclen; + } + else + { + charstocopy = (size_t)MIN( max_chars_to_copy, (int)srclen ); + } + + if ( len + charstocopy >= destBufferSize ) + { + charstocopy = destBufferSize - len - 1; + } + + if ( !charstocopy ) + { + return pDest; + } + + char *pOut = strncat( pDest, pSrc, charstocopy ); + pOut[destBufferSize-1] = 0; + return pOut; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Converts value into x.xx MB/ x.xx KB, x.xx bytes format, including commas +// Input : value - +// 2 - +// false - +// Output : char +//----------------------------------------------------------------------------- +#define NUM_PRETIFYMEM_BUFFERS 8 +char *V_pretifymem( float value, int digitsafterdecimal /*= 2*/, bool usebinaryonek /*= false*/ ) +{ + static char output[ NUM_PRETIFYMEM_BUFFERS ][ 32 ]; + static int current; + + float onekb = usebinaryonek ? 1024.0f : 1000.0f; + float onemb = onekb * onekb; + + char *out = output[ current ]; + current = ( current + 1 ) & ( NUM_PRETIFYMEM_BUFFERS -1 ); + + char suffix[ 8 ]; + + // First figure out which bin to use + if ( value > onemb ) + { + value /= onemb; + V_snprintf( suffix, sizeof( suffix ), " MB" ); + } + else if ( value > onekb ) + { + value /= onekb; + V_snprintf( suffix, sizeof( suffix ), " KB" ); + } + else + { + V_snprintf( suffix, sizeof( suffix ), " bytes" ); + } + + char val[ 32 ]; + + // Clamp to >= 0 + digitsafterdecimal = MAX( digitsafterdecimal, 0 ); + + // If it's basically integral, don't do any decimals + if ( FloatMakePositive( value - (int)value ) < 0.00001 ) + { + V_snprintf( val, sizeof( val ), "%i%s", (int)value, suffix ); + } + else + { + char fmt[ 32 ]; + + // Otherwise, create a format string for the decimals + V_snprintf( fmt, sizeof( fmt ), "%%.%if%s", digitsafterdecimal, suffix ); + V_snprintf( val, sizeof( val ), fmt, value ); + } + + // Copy from in to out + char *i = val; + char *o = out; + + // Search for decimal or if it was integral, find the space after the raw number + char *dot = strstr( i, "." ); + if ( !dot ) + { + dot = strstr( i, " " ); + } + + // Compute position of dot + int pos = dot - i; + // Don't put a comma if it's <= 3 long + pos -= 3; + + while ( *i ) + { + // If pos is still valid then insert a comma every third digit, except if we would be + // putting one in the first spot + if ( pos >= 0 && !( pos % 3 ) ) + { + // Never in first spot + if ( o != out ) + { + *o++ = ','; + } + } + + // Count down comma position + pos--; + + // Copy rest of data as normal + *o++ = *i++; + } + + // Terminate + *o = 0; + + return out; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a string representation of an integer with commas +// separating the 1000s (ie, 37,426,421) +// Input : value - Value to convert +// Output : Pointer to a static buffer containing the output +//----------------------------------------------------------------------------- +#define NUM_PRETIFYNUM_BUFFERS 8 +char *V_pretifynum( int64 value ) +{ + static char output[ NUM_PRETIFYMEM_BUFFERS ][ 32 ]; + static int current; + + char *out = output[ current ]; + current = ( current + 1 ) & ( NUM_PRETIFYMEM_BUFFERS -1 ); + + *out = 0; + + // Render the leading -, if necessary + if ( value < 0 ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "-" ); + value = -value; + } + + // Render quadrillions + if ( value >= 1000000000000LL ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%lld,", value / 1000000000000LL ); + } + + // Render trillions + if ( value >= 1000000000000LL ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%lld,", value / 1000000000000LL ); + } + + // Render billions + if ( value >= 1000000000 ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%lld,", value / 1000000000 ); + } + + // Render millions + if ( value >= 1000000 ) + { + char *pchRender = out + V_strlen( out ); + if ( value >= 1000000000 ) + V_snprintf( pchRender, 32, "%03d,", (unsigned int)(( value / 1000000 ) % 1000 )); + else + V_snprintf( pchRender, 32, "%d,", (unsigned int)(( value / 1000000 ) % 1000 )); + } + + // Render thousands + if ( value >= 1000 ) + { + char *pchRender = out + V_strlen( out ); + if ( value >= 1000000 ) + V_snprintf( pchRender, 32, "%03d,", (unsigned int)(( value / 1000 ) % 1000 )); + else + V_snprintf( pchRender, 32, "%d,", (unsigned int)(( value / 1000 ) % 1000 )); + } + + // Render units + char *pchRender = out + V_strlen( out ); + if ( value > 1000 ) + V_snprintf( pchRender, 32, "%03d", (unsigned int)(value % 1000) ); + else + V_snprintf( pchRender, 32, "%d", (unsigned int)(value % 1000) ); + + return out; +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts a UTF8 string into a unicode string +//----------------------------------------------------------------------------- +int V_UTF8ToUnicode( const char *pUTF8, wchar_t *pwchDest, int cubDestSizeInBytes ) +{ + AssertValidStringPtr(pUTF8); + AssertValidWritePtr(pwchDest); + + pwchDest[0] = 0; +#ifdef _WIN32 + int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pwchDest, cubDestSizeInBytes / sizeof(wchar_t) ); +#elif defined _LINUX || defined __APPLE__ + int cchResult = mbstowcs( pwchDest, pUTF8, cubDestSizeInBytes / sizeof(wchar_t) ); +#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); + + pUTF8[0] = 0; +#ifdef _WIN32 + int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUnicode, -1, pUTF8, cubDestSizeInBytes, NULL, NULL ); +#elif defined _LINUX || defined __APPLE__ + int cchResult = wcstombs( pUTF8, pUnicode, cubDestSizeInBytes ); +#endif + pUTF8[cubDestSizeInBytes - 1] = 0; + return cchResult; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the 4 bit nibble for a hex character +// Input : c - +// Output : unsigned char +//----------------------------------------------------------------------------- +static unsigned char V_nibble( char c ) +{ + if ( ( c >= '0' ) && + ( c <= '9' ) ) + { + return (unsigned char)(c - '0'); + } + + if ( ( c >= 'A' ) && + ( c <= 'F' ) ) + { + return (unsigned char)(c - 'A' + 0x0a); + } + + if ( ( c >= 'a' ) && + ( c <= 'f' ) ) + { + return (unsigned char)(c - 'a' + 0x0a); + } + + return '0'; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *in - +// numchars - +// *out - +// maxoutputbytes - +//----------------------------------------------------------------------------- +void V_hextobinary( char const *in, int numchars, byte *out, int maxoutputbytes ) +{ + int len = V_strlen( in ); + numchars = MIN( len, numchars ); + // Make sure it's even + numchars = ( numchars ) & ~0x1; + + // Must be an even # of input characters (two chars per output byte) + Assert( numchars >= 2 ); + + memset( out, 0x00, maxoutputbytes ); + + byte *p; + int i; + + p = out; + for ( i = 0; + ( i < numchars ) && ( ( p - out ) < maxoutputbytes ); + i+=2, p++ ) + { + *p = ( V_nibble( in[i] ) << 4 ) | V_nibble( in[i+1] ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *in - +// inputbytes - +// *out - +// outsize - +//----------------------------------------------------------------------------- +void V_binarytohex( const byte *in, int inputbytes, char *out, int outsize ) +{ + Assert( outsize >= 1 ); + char doublet[10]; + int i; + + out[0]=0; + + for ( i = 0; i < inputbytes; i++ ) + { + unsigned char c = in[i]; + V_snprintf( doublet, sizeof( doublet ), "%02x", c ); + V_strncat( out, doublet, outsize, COPY_ALL_CHARACTERS ); + } +} + +#if defined( _WIN32 ) || defined( WIN32 ) +#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/') +#else //_WIN32 +#define PATHSEPARATOR(c) ((c) == '/') +#endif //_WIN32 + + +//----------------------------------------------------------------------------- +// Purpose: Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator) +// Input : *in - +// *out - +// maxlen - +//----------------------------------------------------------------------------- +void V_FileBase( const char *in, char *out, int maxlen ) +{ + Assert( maxlen >= 1 ); + Assert( in ); + Assert( out ); + + if ( !in || !in[ 0 ] ) + { + *out = 0; + return; + } + + int len, start, end; + + len = V_strlen( in ); + + // scan backward for '.' + end = len - 1; + while ( end&& in[end] != '.' && !PATHSEPARATOR( in[end] ) ) + { + end--; + } + + if ( in[end] != '.' ) // no '.', copy to end + { + end = len-1; + } + else + { + end--; // Found ',', copy to left of '.' + } + + // Scan backward for '/' + start = len-1; + while ( start >= 0 && !PATHSEPARATOR( in[start] ) ) + { + start--; + } + + if ( start < 0 || !PATHSEPARATOR( in[start] ) ) + { + start = 0; + } + else + { + start++; + } + + // Length of new sting + len = end - start + 1; + + int maxcopy = MIN( len + 1, maxlen ); + + // Copy partial string + V_strncpy( out, &in[start], maxcopy ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *ppath - +//----------------------------------------------------------------------------- +void V_StripTrailingSlash( char *ppath ) +{ + Assert( ppath ); + + int len = V_strlen( ppath ); + if ( len > 0 ) + { + if ( PATHSEPARATOR( ppath[ len - 1 ] ) ) + { + ppath[ len - 1 ] = 0; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *in - +// *out - +// outSize - +//----------------------------------------------------------------------------- +void V_StripExtension( const char *in, char *out, int outSize ) +{ + // Find the last dot. If it's followed by a dot or a slash, then it's part of a + // directory specifier like ../../somedir/./blah. + + // scan backward for '.' + int end = V_strlen( in ) - 1; + while ( end > 0 && in[end] != '.' && !PATHSEPARATOR( in[end] ) ) + { + --end; + } + + if (end > 0 && !PATHSEPARATOR( in[end] ) && end < outSize) + { + int nChars = MIN( end, outSize-1 ); + if ( out != in ) + { + memcpy( out, in, nChars ); + } + out[nChars] = 0; + } + else + { + // nothing found + if ( out != in ) + { + V_strncpy( out, in, outSize ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *path - +// *extension - +// pathStringLength - +//----------------------------------------------------------------------------- +void V_DefaultExtension( char *path, const char *extension, int pathStringLength ) +{ + Assert( path ); + Assert( pathStringLength >= 1 ); + Assert( extension ); + Assert( extension[0] == '.' ); + + char *src; + + // if path doesn't have a .EXT, append extension + // (extension should include the .) + src = path + V_strlen(path) - 1; + + while ( !PATHSEPARATOR( *src ) && ( src > path ) ) + { + if (*src == '.') + { + // it has an extension + return; + } + src--; + } + + // Concatenate the desired extension + V_strncat( path, extension, pathStringLength, COPY_ALL_CHARACTERS ); +} + +//----------------------------------------------------------------------------- +// Purpose: Force extension... +// Input : *path - +// *extension - +// pathStringLength - +//----------------------------------------------------------------------------- +void V_SetExtension( char *path, const char *extension, int pathStringLength ) +{ + V_StripExtension( path, path, pathStringLength ); + V_DefaultExtension( path, extension, pathStringLength ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove final filename from string +// Input : *path - +// Output : void V_StripFilename +//----------------------------------------------------------------------------- +void V_StripFilename (char *path) +{ + int length; + + length = V_strlen( path )-1; + if ( length <= 0 ) + return; + + while ( length > 0 && + !PATHSEPARATOR( path[length] ) ) + { + length--; + } + + path[ length ] = 0; +} + +#ifdef _WIN32 +#define CORRECT_PATH_SEPARATOR '\\' +#define INCORRECT_PATH_SEPARATOR '/' +#elif defined _LINUX || defined __APPLE__ +#define CORRECT_PATH_SEPARATOR '/' +#define INCORRECT_PATH_SEPARATOR '\\' +#endif + +//----------------------------------------------------------------------------- +// Purpose: Changes all '/' or '\' characters into separator +// Input : *pname - +// separator - +//----------------------------------------------------------------------------- +void V_FixSlashes( char *pname, char separator /* = CORRECT_PATH_SEPARATOR */ ) +{ + while ( *pname ) + { + if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR ) + { + *pname = separator; + } + pname++; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: This function fixes cases of filenames like materials\\blah.vmt or somepath\otherpath\\ and removes the extra double slash. +//----------------------------------------------------------------------------- +void V_FixDoubleSlashes( char *pStr ) +{ + int len = V_strlen( pStr ); + + for ( int i=1; i < len-1; i++ ) + { + if ( (pStr[i] == '/' || pStr[i] == '\\') && (pStr[i+1] == '/' || pStr[i+1] == '\\') ) + { + // This means there's a double slash somewhere past the start of the filename. That + // can happen in Hammer if they use a material in the root directory. You'll get a filename + // that looks like 'materials\\blah.vmt' + V_memmove( &pStr[i], &pStr[i+1], len - i ); + --len; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Strip off the last directory from dirName +// Input : *dirName - +// maxlen - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool V_StripLastDir( char *dirName, int maxlen ) +{ + if( dirName[0] == 0 || + !V_stricmp( dirName, "./" ) || + !V_stricmp( dirName, ".\\" ) ) + return false; + + int len = V_strlen( dirName ); + + Assert( len < maxlen ); + + // skip trailing slash + if ( PATHSEPARATOR( dirName[len-1] ) ) + { + len--; + } + + while ( len > 0 ) + { + if ( PATHSEPARATOR( dirName[len-1] ) ) + { + dirName[len] = 0; + V_FixSlashes( dirName, CORRECT_PATH_SEPARATOR ); + return true; + } + len--; + } + + // Allow it to return an empty string and true. This can happen if something like "tf2/" is passed in. + // The correct behavior is to strip off the last directory ("tf2") and return true. + if( len == 0 ) + { + V_snprintf( dirName, maxlen, ".%c", CORRECT_PATH_SEPARATOR ); + return true; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the beginning of the unqualified file name +// (no path information) +// Input: in - file name (may be unqualified, relative or absolute path) +// Output: pointer to unqualified file name +//----------------------------------------------------------------------------- +const char * V_UnqualifiedFileName( const char * in ) +{ + // back up until the character after the first path separator we find, + // or the beginning of the string + const char * out = in + strlen( in ) - 1; + while ( ( out > in ) && ( !PATHSEPARATOR( *( out-1 ) ) ) ) + out--; + return out; +} + + +//----------------------------------------------------------------------------- +// Purpose: Composes a path and filename together, inserting a path separator +// if need be +// Input: path - path to use +// filename - filename to use +// dest - buffer to compose result in +// destSize - size of destination buffer +//----------------------------------------------------------------------------- +void V_ComposeFileName( const char *path, const char *filename, char *dest, int destSize ) +{ + V_strncpy( dest, path, destSize ); + V_AppendSlash( dest, destSize ); + V_strncat( dest, filename, destSize, COPY_ALL_CHARACTERS ); + V_FixSlashes( dest ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *path - +// *dest - +// destSize - +// Output : void V_ExtractFilePath +//----------------------------------------------------------------------------- +bool V_ExtractFilePath (const char *path, char *dest, int destSize ) +{ + Assert( destSize >= 1 ); + if ( destSize < 1 ) + { + return false; + } + + // Last char + int len = V_strlen(path); + const char *src = path + (len ? len-1 : 0); + + // back up until a \ or the start + while ( src != path && !PATHSEPARATOR( *(src-1) ) ) + { + src--; + } + + int copysize = MIN( src - path, destSize - 1 ); + memcpy( dest, path, copysize ); + dest[copysize] = 0; + + return copysize != 0 ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *path - +// *dest - +// destSize - +// Output : void V_ExtractFileExtension +//----------------------------------------------------------------------------- +void V_ExtractFileExtension( const char *path, char *dest, int destSize ) +{ + *dest = '\0'; + const char * extension = V_GetFileExtension( path ); + if ( NULL != extension ) + V_strncpy( dest, extension, destSize ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the file extension within a file name string +// Input: in - file name +// Output: pointer to beginning of extension (after the "."), or NULL +// if there is no extension +//----------------------------------------------------------------------------- +const char * V_GetFileExtension( const char * path ) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.' ) + src--; + + // check to see if the '.' is part of a pathname + if (src == path || PATHSEPARATOR( *src ) ) + { + return NULL; // no extension + } + + return src; +} + +bool V_RemoveDotSlashes( char *pFilename, char separator ) +{ + // Remove '//' or '\\' + char *pIn = pFilename; + char *pOut = pFilename; + bool bPrevPathSep = false; + while ( *pIn ) + { + bool bIsPathSep = PATHSEPARATOR( *pIn ); + if ( !bIsPathSep || !bPrevPathSep ) + { + *pOut++ = *pIn; + } + bPrevPathSep = bIsPathSep; + ++pIn; + } + *pOut = 0; + + // Get rid of "./"'s + pIn = pFilename; + pOut = pFilename; + while ( *pIn ) + { + // The logic on the second line is preventing it from screwing up "../" + if ( pIn[0] == '.' && PATHSEPARATOR( pIn[1] ) && + (pIn == pFilename || pIn[-1] != '.') ) + { + pIn += 2; + } + else + { + *pOut = *pIn; + ++pIn; + ++pOut; + } + } + *pOut = 0; + + // Get rid of a trailing "/." (needless). + int len = strlen( pFilename ); + if ( len > 2 && pFilename[len-1] == '.' && PATHSEPARATOR( pFilename[len-2] ) ) + { + pFilename[len-2] = 0; + } + + // Each time we encounter a "..", back up until we've read the previous directory name, + // then get rid of it. + pIn = pFilename; + while ( *pIn ) + { + if ( pIn[0] == '.' && + pIn[1] == '.' && + (pIn == pFilename || PATHSEPARATOR(pIn[-1])) && // Preceding character must be a slash. + (pIn[2] == 0 || PATHSEPARATOR(pIn[2])) ) // Following character must be a slash or the end of the string. + { + char *pEndOfDots = pIn + 2; + char *pStart = pIn - 2; + + // Ok, now scan back for the path separator that starts the preceding directory. + while ( 1 ) + { + if ( pStart < pFilename ) + return false; + + if ( PATHSEPARATOR( *pStart ) ) + break; + + --pStart; + } + + // Now slide the string down to get rid of the previous directory and the ".." + memmove( pStart, pEndOfDots, strlen( pEndOfDots ) + 1 ); + + // Start over. + pIn = pFilename; + } + else + { + ++pIn; + } + } + + V_FixSlashes( pFilename, separator ); + return true; +} + + +void V_AppendSlash( char *pStr, int strSize ) +{ + int len = V_strlen( pStr ); + if ( len > 0 && !PATHSEPARATOR(pStr[len-1]) ) + { + //if ( len+1 >= strSize ) + //Error( "V_AppendSlash: ran out of space on %s.", pStr ); + + pStr[len] = CORRECT_PATH_SEPARATOR; + pStr[len+1] = 0; + } +} + + +void V_MakeAbsolutePath( char *pOut, int outLen, const char *pPath, const char *pStartingDir ) +{ + if ( V_IsAbsolutePath( pPath ) ) + { + // pPath is not relative.. just copy it. + V_strncpy( pOut, pPath, outLen ); + } + else + { + // Make sure the starting directory is absolute.. + if ( pStartingDir && V_IsAbsolutePath( pStartingDir ) ) + { + V_strncpy( pOut, pStartingDir, outLen ); + } + else + { + //if ( !_getcwd( pOut, outLen ) ) + //Error( "V_MakeAbsolutePath: _getcwd failed." ); + + if ( pStartingDir ) + { + V_AppendSlash( pOut, outLen ); + V_strncat( pOut, pStartingDir, outLen, COPY_ALL_CHARACTERS ); + } + } + + // Concatenate the paths. + V_AppendSlash( pOut, outLen ); + V_strncat( pOut, pPath, outLen, COPY_ALL_CHARACTERS ); + } + + //if ( !V_RemoveDotSlashes( pOut ) ) + //Error( "V_MakeAbsolutePath: tried to \"..\" past the root." ); + + V_FixSlashes( pOut ); +} + + +//----------------------------------------------------------------------------- +// Makes a relative path +//----------------------------------------------------------------------------- +bool V_MakeRelativePath( const char *pFullPath, const char *pDirectory, char *pRelativePath, int nBufLen ) +{ + pRelativePath[0] = 0; + + const char *pPath = pFullPath; + const char *pDir = pDirectory; + + // Strip out common parts of the path + const char *pLastCommonPath = NULL; + const char *pLastCommonDir = NULL; + while ( *pPath && ( tolower( *pPath ) == tolower( *pDir ) || + ( PATHSEPARATOR( *pPath ) && ( PATHSEPARATOR( *pDir ) || (*pDir == 0) ) ) ) ) + { + if ( PATHSEPARATOR( *pPath ) ) + { + pLastCommonPath = pPath + 1; + pLastCommonDir = pDir + 1; + } + if ( *pDir == 0 ) + { + --pLastCommonDir; + break; + } + ++pDir; ++pPath; + } + + // Nothing in common + if ( !pLastCommonPath ) + return false; + + // For each path separator remaining in the dir, need a ../ + int nOutLen = 0; + bool bLastCharWasSeparator = true; + for ( ; *pLastCommonDir; ++pLastCommonDir ) + { + if ( PATHSEPARATOR( *pLastCommonDir ) ) + { + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR; + bLastCharWasSeparator = true; + } + else + { + bLastCharWasSeparator = false; + } + } + + // Deal with relative paths not specified with a trailing slash + if ( !bLastCharWasSeparator ) + { + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR; + } + + // Copy the remaining part of the relative path over, fixing the path separators + for ( ; *pLastCommonPath; ++pLastCommonPath ) + { + if ( PATHSEPARATOR( *pLastCommonPath ) ) + { + pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR; + } + else + { + pRelativePath[nOutLen++] = *pLastCommonPath; + } + + // Check for overflow + if ( nOutLen == nBufLen - 1 ) + break; + } + + pRelativePath[nOutLen] = 0; + return true; +} + + +//----------------------------------------------------------------------------- +// small helper function shared by lots of modules +//----------------------------------------------------------------------------- +bool V_IsAbsolutePath( const char *pStr ) +{ + bool bIsAbsolute = ( pStr[0] && pStr[1] == ':' ) || pStr[0] == '/' || pStr[0] == '\\'; + if ( IsX360() && !bIsAbsolute ) + { + bIsAbsolute = ( V_stristr( pStr, ":" ) != NULL ); + } + return bIsAbsolute; +} + + +// Copies at most nCharsToCopy bytes from pIn into pOut. +// Returns false if it would have overflowed pOut's buffer. +static bool CopyToMaxChars( char *pOut, int outSize, const char *pIn, int nCharsToCopy ) +{ + if ( outSize == 0 ) + return false; + + int iOut = 0; + while ( *pIn && nCharsToCopy > 0 ) + { + if ( iOut == (outSize-1) ) + { + pOut[iOut] = 0; + return false; + } + pOut[iOut] = *pIn; + ++iOut; + ++pIn; + --nCharsToCopy; + } + + pOut[iOut] = 0; + return true; +} + + +//----------------------------------------------------------------------------- +// Fixes up a file name, removing dot slashes, fixing slashes, converting to lowercase, etc. +//----------------------------------------------------------------------------- +void V_FixupPathName( char *pOut, size_t nOutLen, const char *pPath ) +{ + V_strncpy( pOut, pPath, nOutLen ); + V_FixSlashes( pOut ); + V_RemoveDotSlashes( pOut ); + V_FixDoubleSlashes( pOut ); + V_strlower( pOut ); +} + + +// Returns true if it completed successfully. +// If it would overflow pOut, it fills as much as it can and returns false. +bool V_StrSubst( + const char *pIn, + const char *pMatch, + const char *pReplaceWith, + char *pOut, + int outLen, + bool bCaseSensitive + ) +{ + int replaceFromLen = strlen( pMatch ); + int replaceToLen = strlen( pReplaceWith ); + + const char *pInStart = pIn; + char *pOutPos = pOut; + pOutPos[0] = 0; + + while ( 1 ) + { + int nRemainingOut = outLen - (pOutPos - pOut); + + const char *pTestPos = ( bCaseSensitive ? strstr( pInStart, pMatch ) : V_stristr( pInStart, pMatch ) ); + if ( pTestPos ) + { + // Found an occurence of pMatch. First, copy whatever leads up to the string. + int copyLen = pTestPos - pInStart; + if ( !CopyToMaxChars( pOutPos, nRemainingOut, pInStart, copyLen ) ) + return false; + + // Did we hit the end of the output string? + if ( copyLen > nRemainingOut-1 ) + return false; + + pOutPos += strlen( pOutPos ); + nRemainingOut = outLen - (pOutPos - pOut); + + // Now add the replacement string. + if ( !CopyToMaxChars( pOutPos, nRemainingOut, pReplaceWith, replaceToLen ) ) + return false; + + pInStart += copyLen + replaceFromLen; + pOutPos += replaceToLen; + } + else + { + // We're at the end of pIn. Copy whatever remains and get out. + int copyLen = strlen( pInStart ); + V_strncpy( pOutPos, pInStart, nRemainingOut ); + return ( copyLen <= nRemainingOut-1 ); + } + } +} + + +char* AllocString( const char *pStr, int nMaxChars ) +{ + int allocLen; + if ( nMaxChars == -1 ) + allocLen = strlen( pStr ) + 1; + else + allocLen = MIN( (int)strlen(pStr), nMaxChars ) + 1; + + char *pOut = new char[allocLen]; + V_strncpy( pOut, pStr, allocLen ); + return pOut; +} + + +void V_SplitString2( const char *pString, const char **pSeparators, int nSeparators, CUtlVector &outStrings ) +{ + outStrings.Purge(); + const char *pCurPos = pString; + while ( 1 ) + { + int iFirstSeparator = -1; + const char *pFirstSeparator = 0; + for ( int i=0; i < nSeparators; i++ ) + { + const char *pTest = V_stristr( pCurPos, pSeparators[i] ); + if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) ) + { + iFirstSeparator = i; + pFirstSeparator = pTest; + } + } + + if ( pFirstSeparator ) + { + // Split on this separator and continue on. + int separatorLen = strlen( pSeparators[iFirstSeparator] ); + if ( pFirstSeparator > pCurPos ) + { + outStrings.AddToTail( AllocString( pCurPos, pFirstSeparator-pCurPos ) ); + } + + pCurPos = pFirstSeparator + separatorLen; + } + else + { + // Copy the rest of the string + if ( strlen( pCurPos ) ) + { + outStrings.AddToTail( AllocString( pCurPos, -1 ) ); + } + return; + } + } +} + + +void V_SplitString( const char *pString, const char *pSeparator, CUtlVector &outStrings ) +{ + V_SplitString2( pString, &pSeparator, 1, outStrings ); +} + + +bool V_GetCurrentDirectory( char *pOut, int maxLen ) +{ +#if defined _LINUX || defined __APPLE__ + return getcwd( pOut, maxLen ) == pOut; +#else + return _getcwd( pOut, maxLen ) == pOut; +#endif +} + + +bool V_SetCurrentDirectory( const char *pDirName ) +{ +#if defined _LINUX || defined __APPLE__ + return chdir( pDirName ) == 0; +#else + return _chdir( pDirName ) == 0; +#endif +} + + +// This function takes a slice out of pStr and stores it in pOut. +// It follows the Python slice convention: +// Negative numbers wrap around the string (-1 references the last character). +// Numbers are clamped to the end of the string. +void V_StrSlice( const char *pStr, int firstChar, int lastCharNonInclusive, char *pOut, int outSize ) +{ + if ( outSize == 0 ) + return; + + int length = strlen( pStr ); + + // Fixup the string indices. + if ( firstChar < 0 ) + { + firstChar = length - (-firstChar % length); + } + else if ( firstChar >= length ) + { + pOut[0] = 0; + return; + } + + if ( lastCharNonInclusive < 0 ) + { + lastCharNonInclusive = length - (-lastCharNonInclusive % length); + } + else if ( lastCharNonInclusive > length ) + { + lastCharNonInclusive %= length; + } + + if ( lastCharNonInclusive <= firstChar ) + { + pOut[0] = 0; + return; + } + + int copyLen = lastCharNonInclusive - firstChar; + if ( copyLen <= (outSize-1) ) + { + memcpy( pOut, &pStr[firstChar], copyLen ); + pOut[copyLen] = 0; + } + else + { + memcpy( pOut, &pStr[firstChar], outSize-1 ); + pOut[outSize-1] = 0; + } +} + + +void V_StrLeft( const char *pStr, int nChars, char *pOut, int outSize ) +{ + if ( nChars == 0 ) + { + if ( outSize != 0 ) + pOut[0] = 0; + + return; + } + + V_StrSlice( pStr, 0, nChars, pOut, outSize ); +} + + +void V_StrRight( const char *pStr, int nChars, char *pOut, int outSize ) +{ + int len = strlen( pStr ); + if ( nChars >= len ) + { + V_strncpy( pOut, pStr, outSize ); + } + else + { + V_StrSlice( pStr, -nChars, strlen( pStr ), pOut, outSize ); + } +} + +//----------------------------------------------------------------------------- +// Convert multibyte to wchar + back +//----------------------------------------------------------------------------- +void V_strtowcs( const char *pString, int nInSize, wchar_t *pWString, int nOutSize ) +{ +#ifdef _WIN32 + if ( !MultiByteToWideChar( CP_UTF8, 0, pString, nInSize, pWString, nOutSize ) ) + { + *pWString = L'\0'; + } +#elif defined _LINUX || defined __APPLE__ + if ( mbstowcs( pWString, pString, nOutSize / sizeof(wchar_t) ) <= 0 ) + { + *pWString = 0; + } +#endif +} + +void V_wcstostr( const wchar_t *pWString, int nInSize, char *pString, int nOutSize ) +{ +#ifdef _WIN32 + if ( !WideCharToMultiByte( CP_UTF8, 0, pWString, nInSize, pString, nOutSize, NULL, NULL ) ) + { + *pString = '\0'; + } +#elif defined _LINUX || defined __APPLE__ + if ( wcstombs( pString, pWString, nOutSize ) <= 0 ) + { + *pString = '\0'; + } +#endif +} + + + +//-------------------------------------------------------------------------------- +// backslashification +//-------------------------------------------------------------------------------- + +static char s_BackSlashMap[]="\tt\nn\rr\"\"\\\\"; + +char *V_AddBackSlashesToSpecialChars( char const *pSrc ) +{ + // first, count how much space we are going to need + int nSpaceNeeded = 0; + for( char const *pScan = pSrc; *pScan; pScan++ ) + { + nSpaceNeeded++; + for(char const *pCharSet=s_BackSlashMap; *pCharSet; pCharSet += 2 ) + { + if ( *pCharSet == *pScan ) + nSpaceNeeded++; // we need to store a bakslash + } + } + char *pRet = new char[ nSpaceNeeded + 1 ]; // +1 for null + char *pOut = pRet; + + for( char const *pScan = pSrc; *pScan; pScan++ ) + { + bool bIsSpecial = false; + for(char const *pCharSet=s_BackSlashMap; *pCharSet; pCharSet += 2 ) + { + if ( *pCharSet == *pScan ) + { + *( pOut++ ) = '\\'; + *( pOut++ ) = pCharSet[1]; + bIsSpecial = true; + break; + } + } + if (! bIsSpecial ) + { + *( pOut++ ) = *pScan; + } + } + *( pOut++ ) = 0; + return pRet; +} diff --git a/tier1/tier1-2005.vcproj b/tier1/tier1-2005.vcproj new file mode 100644 index 00000000..34cff4df --- /dev/null +++ b/tier1/tier1-2005.vcproj @@ -0,0 +1,568 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tier1/tier1.cpp b/tier1/tier1.cpp new file mode 100644 index 00000000..c29dfcfc --- /dev/null +++ b/tier1/tier1.cpp @@ -0,0 +1,29 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + +#include +#include "tier0/dbg.h" +#include "interfaces/interfaces.h" + +// for utlsortvector.h +#ifndef _WIN32 + void *g_pUtlSortVectorQSortContext = NULL; +#endif + + +//----------------------------------------------------------------------------- +// Call this to connect to all tier 1 libraries. +// It's up to the caller to check the globals it cares about to see if ones are missing +//----------------------------------------------------------------------------- +void ConnectTier1Libraries( CreateInterfaceFn *pFactoryList, int nFactoryCount ) +{ + ConnectInterfaces( pFactoryList, nFactoryCount); +} + +void DisconnectTier1Libraries() +{ + DisconnectInterfaces(); +} diff --git a/tier1/tier1.sln b/tier1/tier1.sln new file mode 100644 index 00000000..bcc127ca --- /dev/null +++ b/tier1/tier1.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tier1", "tier1.vcxproj", "{E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug|Win32.ActiveCfg = Debug|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug|Win32.Build.0 = Debug|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug|x64.ActiveCfg = Debug|x64 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Debug|x64.Build.0 = Debug|x64 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release|Win32.ActiveCfg = Release|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release|Win32.Build.0 = Release|Win32 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release|x64.ActiveCfg = Release|x64 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tier1/tier1.vcxproj b/tier1/tier1.vcxproj new file mode 100644 index 00000000..497be0cd --- /dev/null +++ b/tier1/tier1.vcxproj @@ -0,0 +1,409 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + tier1 + {E1DA8DB8-FB4C-4B14-91A6-98BCED6B9720} + tier1 + + + + StaticLibrary + v120_xp + + + StaticLibrary + v120 + + + StaticLibrary + v120_xp + + + StaticLibrary + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + ..\lib\public\ + true + true + true + true + true + true + ..\lib\public\ + true + true + true + true + true + true + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + ..\lib\public\win64\ + + + ..\lib\public\win64\ + + + + + + + + Disabled + ..\public;..\public\tier0;..\public\tier1;%(AdditionalIncludeDirectories) + _HAS_ITERATOR_DEBUGGING=0;_ALLOW_RUNTIME_LIBRARY_MISMATCH;_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH;_ALLOW_MSC_VER_MISMATCH;WIN32;_WIN32;COMPILER_MSVC;COMPILER_MSVC32;_DEBUG;DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TIER1_STATIC_LIB;%(PreprocessorDefinitions) + true + true + + + EnableFastChecks + MultiThreadedDebug + false + Fast + true + true + true + false + + + false + + + $(IntDir) + $(IntDir) + $(IntDir) + false + + + $(IntDir) + CompileAsCpp + Prompt + Level3 + + + + + + + false + Rpcrt4.lib;%(AdditionalDependencies) + true + + + true + + + true + $(OutDir)tier1.bsc + + + + + + + + + Disabled + ..\public;..\public\tier0;..\public\tier1;%(AdditionalIncludeDirectories) + _HAS_ITERATOR_DEBUGGING=0;_ALLOW_RUNTIME_LIBRARY_MISMATCH;_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH;_ALLOW_MSC_VER_MISMATCH;WIN32;_WIN32;COMPILER_MSVC;COMPILER_MSVC32;_DEBUG;DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TIER1_STATIC_LIB;%(PreprocessorDefinitions) + true + + + EnableFastChecks + MultiThreadedDebug + false + Fast + true + true + true + false + + + false + + + $(IntDir) + $(IntDir) + $(IntDir) + false + + + $(IntDir) + CompileAsCpp + Prompt + Level3 + + + + + + + false + Rpcrt4.lib;%(AdditionalDependencies) + true + + + true + + + true + $(OutDir)tier1.bsc + + + + + + + + + MaxSpeed + AnySuitable + true + Speed + ..\public;..\public\tier0;..\public\tier1;%(AdditionalIncludeDirectories) + _HAS_ITERATOR_DEBUGGING=0;_ALLOW_RUNTIME_LIBRARY_MISMATCH;_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH;_ALLOW_MSC_VER_MISMATCH;WIN32;_WIN32;COMPILER_MSVC;COMPILER_MSVC32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TIER1_STATIC_LIB;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + Fast + true + true + true + false + + + false + + + $(IntDir) + $(IntDir) + $(IntDir) + false + + + $(IntDir) + CompileAsCpp + Prompt + Level3 + + + /Zo %(AdditionalOptions) + + + + + + + false + Rpcrt4.lib;%(AdditionalDependencies) + true + + + true + + + true + $(OutDir)tier1.bsc + + + + + + + + + MaxSpeed + AnySuitable + true + Speed + ..\public;..\public\tier0;..\public\tier1;%(AdditionalIncludeDirectories) + _HAS_ITERATOR_DEBUGGING=0;_ALLOW_RUNTIME_LIBRARY_MISMATCH;_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH;_ALLOW_MSC_VER_MISMATCH;WIN64;_WIN64;COMPILER_MSVC;COMPILER_MSVC64;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TIER1_STATIC_LIB;%(PreprocessorDefinitions) + true + + + MultiThreaded + false + true + Fast + true + true + true + false + + + false + + + $(IntDir) + $(IntDir) + $(IntDir) + false + + + $(IntDir) + CompileAsCpp + Prompt + Level3 + + + /Zo %(AdditionalOptions) + + + + + + + false + Rpcrt4.lib;%(AdditionalDependencies) + true + + + true + + + true + $(OutDir)tier1.bsc + + + + + + + + + + + + + + + + + + + + Sync + Sync + Sync + Sync + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tier1/tier1.vcxproj.filters b/tier1/tier1.vcxproj.filters new file mode 100644 index 00000000..5f57bdbe --- /dev/null +++ b/tier1/tier1.vcxproj.filters @@ -0,0 +1,251 @@ + + + + + {aba1d919-d95c-4c3f-a495-be0375bab184} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {0295d938-2af6-4393-9785-4b7b122313c9} + h;hpp;hxx;hm;inl + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/tier1/tokenreader.cpp b/tier1/tokenreader.cpp new file mode 100644 index 00000000..46d9547c --- /dev/null +++ b/tier1/tokenreader.cpp @@ -0,0 +1,480 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include +#include +#include "tokenreader.h" +#include "tier0/platform.h" +#include "tier1/strtools.h" +#include "tier0/dbg.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +TokenReader::TokenReader(void) +{ + m_szFilename[0] = '\0'; + m_nLine = 1; + m_nErrorCount = 0; + m_bStuffed = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pszFilename - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool TokenReader::Open(const char *pszFilename) +{ + open(pszFilename, std::ios::in | std::ios::binary ); + Q_strncpy(m_szFilename, pszFilename, sizeof( m_szFilename ) ); + m_nLine = 1; + m_nErrorCount = 0; + m_bStuffed = false; + return(is_open() != 0); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TokenReader::Close() +{ + close(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *error - +// Output : const char +//----------------------------------------------------------------------------- +const char *TokenReader::Error(char *error, ...) +{ + static char szErrorBuf[256]; + Q_snprintf(szErrorBuf, sizeof( szErrorBuf ), "File %s, line %d: ", m_szFilename, m_nLine); + Q_strncat(szErrorBuf, error, sizeof( szErrorBuf ), COPY_ALL_CHARACTERS ); + m_nErrorCount++; + return(szErrorBuf); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszStore - +// nSize - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +trtoken_t TokenReader::GetString(char *pszStore, int nSize) +{ + if (nSize <= 0) + { + return TOKENERROR; + } + + char szBuf[1024]; + + // + // Until we reach the end of this string or run out of room in + // the destination buffer... + // + while (true) + { + // + // Fetch the next batch of text from the file. + // + get(szBuf, sizeof(szBuf), '\"'); + if (eof()) + { + return TOKENEOF; + } + + if (fail()) + { + // Just means nothing was read (empty string probably "") + clear(); + } + + // + // Transfer the text to the destination buffer. + // + char *pszSrc = szBuf; + while ((*pszSrc != '\0') && (nSize > 1)) + { + if (*pszSrc == 0x0d) + { + // + // Newline encountered before closing quote -- unterminated string. + // + *pszStore = '\0'; + return TOKENSTRINGTOOLONG; + } + else if (*pszSrc != '\\') + { + *pszStore = *pszSrc; + pszSrc++; + } + else + { + // + // Backslash sequence - replace with the appropriate character. + // + pszSrc++; + + if (*pszSrc == 'n') + { + *pszStore = '\n'; + } + + pszSrc++; + } + + pszStore++; + nSize--; + } + + if (*pszSrc != '\0') + { + // + // Ran out of room in the destination buffer. Skip to the close-quote, + // terminate the string, and exit. + // + ignore(1024, '\"'); + *pszStore = '\0'; + return TOKENSTRINGTOOLONG; + } + + // + // Check for closing quote. + // + if (peek() == '\"') + { + // + // Eat the close quote and any whitespace. + // + get(); + + bool bCombineStrings = SkipWhiteSpace(); + + // + // Combine consecutive quoted strings if the combine strings character was + // encountered between the two strings. + // + if (bCombineStrings && (peek() == '\"')) + { + // + // Eat the open quote and keep parsing this string. + // + get(); + } + else + { + // + // Done with this string, terminate the string and exit. + // + *pszStore = '\0'; + return STRING; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the next token, allocating enough memory to store the token +// plus a terminating NULL. +// Input : pszStore - Pointer to a string that will be allocated. +// Output : Returns the type of token that was read, or TOKENERROR. +//----------------------------------------------------------------------------- +trtoken_t TokenReader::NextTokenDynamic(char **ppszStore) +{ + char szTempBuffer[8192]; + trtoken_t eType = NextToken(szTempBuffer, sizeof(szTempBuffer)); + + int len = Q_strlen(szTempBuffer) + 1; + *ppszStore = new char [len]; + Assert( *ppszStore ); + Q_strncpy(*ppszStore, szTempBuffer, len ); + + return(eType); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the next token. +// Input : pszStore - Pointer to a string that will receive the token. +// Output : Returns the type of token that was read, or TOKENERROR. +//----------------------------------------------------------------------------- +trtoken_t TokenReader::NextToken(char *pszStore, int nSize) +{ + char *pStart = pszStore; + + if (!is_open()) + { + return TOKENEOF; + } + + // + // If they stuffed a token, return that token. + // + if (m_bStuffed) + { + m_bStuffed = false; + Q_strncpy( pszStore, m_szStuffed, nSize ); + return m_eStuffed; + } + + SkipWhiteSpace(); + + if (eof()) + { + return TOKENEOF; + } + + if (fail()) + { + return TOKENEOF; + } + + char ch = get(); + + // + // Look for all the valid operators. + // + switch (ch) + { + case '@': + case ',': + case '!': + case '+': + case '&': + case '*': + case '$': + case '.': + case '=': + case ':': + case '[': + case ']': + case '(': + case ')': + case '{': + case '}': + case '\\': + { + pszStore[0] = ch; + pszStore[1] = 0; + return OPERATOR; + } + } + + // + // Look for the start of a quoted string. + // + if (ch == '\"') + { + return GetString(pszStore, nSize); + } + + // + // Integers consist of numbers with an optional leading minus sign. + // + if (isdigit(ch) || (ch == '-')) + { + do + { + if ( (pszStore - pStart + 1) < nSize ) + { + *pszStore = ch; + pszStore++; + } + + ch = get(); + if (ch == '-') + { + return TOKENERROR; + } + } while (isdigit(ch)); + + // + // No identifier characters are allowed contiguous with numbers. + // + if (isalpha(ch) || (ch == '_')) + { + return TOKENERROR; + } + + // + // Put back the non-numeric character for the next call. + // + putback(ch); + *pszStore = '\0'; + return INTEGER; + } + + // + // Identifiers consist of a consecutive string of alphanumeric + // characters and underscores. + // + while ( isalpha(ch) || isdigit(ch) || (ch == '_') ) + { + if ( (pszStore - pStart + 1) < nSize ) + { + *pszStore = ch; + pszStore++; + } + + ch = get(); + } + + // + // Put back the non-identifier character for the next call. + // + putback(ch); + *pszStore = '\0'; + return IDENT; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ttype - +// *pszToken - +//----------------------------------------------------------------------------- +void TokenReader::IgnoreTill(trtoken_t ttype, const char *pszToken) +{ + trtoken_t _ttype; + char szBuf[1024]; + + while(1) + { + _ttype = NextToken(szBuf, sizeof(szBuf)); + if(_ttype == TOKENEOF) + return; + if(_ttype == ttype) + { + if(IsToken(pszToken, szBuf)) + { + Stuff(ttype, pszToken); + return; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ttype - +// pszToken - +//----------------------------------------------------------------------------- +void TokenReader::Stuff(trtoken_t eType, const char *pszToken) +{ + m_eStuffed = eType; + Q_strncpy(m_szStuffed, pszToken, sizeof( m_szStuffed ) ); + m_bStuffed = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ttype - +// pszToken - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool TokenReader::Expecting(trtoken_t ttype, const char *pszToken) +{ + char szBuf[1024]; + if (NextToken(szBuf, sizeof(szBuf)) != ttype || !IsToken(pszToken, szBuf)) + { + return false; + } + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszStore - +// Output : +//----------------------------------------------------------------------------- +trtoken_t TokenReader::PeekTokenType(char *pszStore, int maxlen ) +{ + if (!m_bStuffed) + { + m_eStuffed = NextToken(m_szStuffed, sizeof(m_szStuffed)); + m_bStuffed = true; + } + + if (pszStore) + { + Q_strncpy(pszStore, m_szStuffed, maxlen ); + } + + return(m_eStuffed); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the next non-whitespace character from the file. +// Input : ch - Receives the character. +// Output : Returns true if the whitespace contained the combine strings +// character '\', which is used to merge consecutive quoted strings. +//----------------------------------------------------------------------------- +bool TokenReader::SkipWhiteSpace(void) +{ + bool bCombineStrings = false; + + while (true) + { + char ch = get(); + + if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == 0)) + { + continue; + } + + if (ch == '+') + { + bCombineStrings = true; + continue; + } + + if (ch == '\n') + { + m_nLine++; + continue; + } + + if (eof()) + { + return(bCombineStrings); + } + + // + // Check for the start of a comment. + // + if (ch == '/') + { + if (peek() == '/') + { + ignore(1024, '\n'); + m_nLine++; + } + } + else + { + // + // It is a worthy character. Put it back. + // + putback(ch); + return(bCombineStrings); + } + } +} + diff --git a/tier1/undiff.cpp b/tier1/undiff.cpp new file mode 100644 index 00000000..17505b27 --- /dev/null +++ b/tier1/undiff.cpp @@ -0,0 +1,94 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// UnDiff - Apply difference block +// +//=============================================================================// + +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier1/diff.h" +#include "mathlib/mathlib.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void ApplyDiffs(uint8 const *OldBlock, uint8 const *DiffList, + int OldSize, int DiffListSize, int &ResultListSize,uint8 *Output,uint32 OutSize) +{ + uint8 const *copy_src=OldBlock; + uint8 const *end_of_diff_list=DiffList+DiffListSize; + uint8 const *obuf=Output; + while(DiffList32767) + copy_ofs|=0xffff0000; + // printf("long cp from %x to %x len=%d\n", copy_src+copy_ofs-OldBlock,Output-obuf,copy_sz); + + memcpy(Output,copy_src+copy_ofs,copy_sz); + Output+=copy_sz; + copy_src=copy_src+copy_ofs+copy_sz; + DiffList+=4; + } + else + { + if (op & 0x80) + { + int copy_sz=op & 0x7f; + int copy_ofs; + if (copy_sz==0) + { + copy_sz=DiffList[0]; + if (copy_sz==0) + { + // big raw copy + copy_sz=DiffList[1]+256*DiffList[2]+65536*DiffList[3]; + memcpy(Output,DiffList+4,copy_sz); + // printf("big rawcopy to %x len=%d\n", Output-obuf,copy_sz); + + DiffList+=copy_sz+4; + Output+=copy_sz; + } + else + { + copy_ofs=DiffList[1]+(DiffList[2]*256); + if (copy_ofs>32767) + copy_ofs|=0xffff0000; + // printf("long ofs cp from %x to %x len=%d\n", copy_src+copy_ofs-OldBlock,Output-obuf,copy_sz); + + memcpy(Output,copy_src+copy_ofs,copy_sz); + Output+=copy_sz; + copy_src=copy_src+copy_ofs+copy_sz; + DiffList+=3; + } + } + else + { + copy_ofs=DiffList[0]; + if (copy_ofs>127) + copy_ofs|=0xffffff80; + // printf("cp from %x to %x len=%d\n", copy_src+copy_ofs-OldBlock,Output-obuf,copy_sz); + + memcpy(Output,copy_src+copy_ofs,copy_sz); + Output+=copy_sz; + copy_src=copy_src+copy_ofs+copy_sz; + DiffList++; + } + } + else + { + // printf("raw copy %d to %x\n",op & 127,Output-obuf); + memcpy(Output,DiffList,op & 127); + Output+=op & 127; + DiffList+=(op & 127); + } + } + } + ResultListSize=Output-obuf; + +} diff --git a/tier1/uniqueid.cpp b/tier1/uniqueid.cpp new file mode 100644 index 00000000..feb2dc20 --- /dev/null +++ b/tier1/uniqueid.cpp @@ -0,0 +1,177 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Unique ID generation +//=============================================================================// + +#include "tier0/platform.h" + +#ifdef IS_WINDOWS_PC +#include // UUIDCreate +#else +#include "checksum_crc.h" +#endif +#include "tier1/uniqueid.h" +#include "tier1/utlbuffer.h" + +//----------------------------------------------------------------------------- +// Creates a new unique id +//----------------------------------------------------------------------------- +void CreateUniqueId( UniqueId_t *pDest ) +{ +#ifdef IS_WINDOWS_PC + Assert( sizeof( UUID ) == sizeof( *pDest ) ); + UuidCreate( (UUID *)pDest ); +#else + // X360/linux TBD: Need a real UUID Implementation + Q_memset( pDest, 0, sizeof( UniqueId_t ) ); +#endif +} + + +//----------------------------------------------------------------------------- +// Creates a new unique id from a string representation of one +//----------------------------------------------------------------------------- +bool UniqueIdFromString( UniqueId_t *pDest, const char *pBuf, int nMaxLen ) +{ + if ( nMaxLen == 0 ) + { + nMaxLen = Q_strlen( pBuf ); + } + + char *pTemp = (char*)stackalloc( nMaxLen + 1 ); + V_strncpy( pTemp, pBuf, nMaxLen + 1 ); + --nMaxLen; + while( (nMaxLen >= 0) && isspace( pTemp[nMaxLen] ) ) + { + --nMaxLen; + } + pTemp[ nMaxLen + 1 ] = 0; + + while( *pTemp && isspace( *pTemp ) ) + { + ++pTemp; + } + +#ifdef IS_WINDOWS_PC + Assert( sizeof( UUID ) == sizeof( *pDest ) ); + + if ( RPC_S_OK != UuidFromString( (unsigned char *)pTemp, (UUID *)pDest ) ) + { + InvalidateUniqueId( pDest ); + return false; + } +#else + // X360TBD: Need a real UUID Implementation + // For now, use crc to generate a unique ID from the UUID string. + Q_memset( pDest, 0, sizeof( UniqueId_t ) ); + if ( nMaxLen > 0 ) + { + CRC32_t crc; + CRC32_Init( &crc ); + CRC32_ProcessBuffer( &crc, pBuf, nMaxLen ); + CRC32_Final( &crc ); + Q_memcpy( pDest, &crc, sizeof( CRC32_t ) ); + } +#endif + + return true; +} + +//----------------------------------------------------------------------------- +// Sets an object ID to be an invalid state +//----------------------------------------------------------------------------- +void InvalidateUniqueId( UniqueId_t *pDest ) +{ + Assert( pDest ); + memset( pDest, 0, sizeof( UniqueId_t ) ); +} + +bool IsUniqueIdValid( const UniqueId_t &id ) +{ + UniqueId_t invalidId; + memset( &invalidId, 0, sizeof( UniqueId_t ) ); + return !IsUniqueIdEqual( invalidId, id ); +} + +bool IsUniqueIdEqual( const UniqueId_t &id1, const UniqueId_t &id2 ) +{ + return memcmp( &id1, &id2, sizeof( UniqueId_t ) ) == 0; +} + +void UniqueIdToString( const UniqueId_t &id, char *pBuf, int nMaxLen ) +{ + pBuf[ 0 ] = 0; + +// X360TBD: Need a real UUID Implementation +#ifdef IS_WINDOWS_PC + UUID *self = ( UUID * )&id; + + unsigned char *outstring = NULL; + + UuidToString( self, &outstring ); + if ( outstring && *outstring ) + { + Q_strncpy( pBuf, (const char *)outstring, nMaxLen ); + RpcStringFree( &outstring ); + } +#endif +} + +void CopyUniqueId( const UniqueId_t &src, UniqueId_t *pDest ) +{ + memcpy( pDest, &src, sizeof( UniqueId_t ) ); +} + +bool Serialize( CUtlBuffer &buf, const UniqueId_t &src ) +{ +// X360TBD: Need a real UUID Implementation +#ifdef IS_WINDOWS_PC + if ( buf.IsText() ) + { + UUID *pId = ( UUID * )&src; + + unsigned char *outstring = NULL; + + UuidToString( pId, &outstring ); + if ( outstring && *outstring ) + { + buf.PutString( (const char *)outstring ); + RpcStringFree( &outstring ); + } + else + { + buf.PutChar( '\0' ); + } + } + else + { + buf.Put( &src, sizeof(UniqueId_t) ); + } + return buf.IsValid(); +#else + return false; +#endif +} + +bool Unserialize( CUtlBuffer &buf, UniqueId_t &dest ) +{ + if ( buf.IsText() ) + { + int nTextLen = buf.PeekStringLength(); + char *pBuf = (char*)stackalloc( nTextLen ); + buf.GetString( pBuf, nTextLen ); + UniqueIdFromString( &dest, pBuf, nTextLen ); + } + else + { + buf.Get( &dest, sizeof(UniqueId_t) ); + } + return buf.IsValid(); +} + + + diff --git a/tier1/utlbuffer.cpp b/tier1/utlbuffer.cpp new file mode 100644 index 00000000..f71ec6f8 --- /dev/null +++ b/tier1/utlbuffer.cpp @@ -0,0 +1,1753 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// $Header: $ +// $NoKeywords: $ +// +// Serialization buffer +//===========================================================================// + +#ifdef _MSC_VER +#pragma warning (disable : 4514) +#endif + +#include "utlbuffer.h" +#include +#include +#include +#include +#include +#include "tier1/strtools.h" +#include "tier1/characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Character conversions for C strings +//----------------------------------------------------------------------------- +class CUtlCStringConversion : public CUtlCharConversion +{ +public: + CUtlCStringConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ); + + // Finds a conversion for the passed-in string, returns length + virtual char FindConversion( const char *pString, int *pLength ); + +private: + char m_pConversion[255]; +}; + + +//----------------------------------------------------------------------------- +// Character conversions for no-escape sequence strings +//----------------------------------------------------------------------------- +class CUtlNoEscConversion : public CUtlCharConversion +{ +public: + CUtlNoEscConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ) : + CUtlCharConversion( nEscapeChar, pDelimiter, nCount, pArray ) {} + + // Finds a conversion for the passed-in string, returns length + virtual char FindConversion( const char *pString, int *pLength ) { *pLength = 0; return 0; } +}; + + +//----------------------------------------------------------------------------- +// List of character conversions +//----------------------------------------------------------------------------- +BEGIN_CUSTOM_CHAR_CONVERSION( CUtlCStringConversion, s_StringCharConversion, "\"", '\\' ) + { '\n', "n" }, + { '\t', "t" }, + { '\v', "v" }, + { '\b', "b" }, + { '\r', "r" }, + { '\f', "f" }, + { '\a', "a" }, + { '\\', "\\" }, + { '\?', "\?" }, + { '\'', "\'" }, + { '\"', "\"" }, +END_CUSTOM_CHAR_CONVERSION( CUtlCStringConversion, s_StringCharConversion, "\"", '\\' ) + +CUtlCharConversion *GetCStringCharConversion() +{ + return &s_StringCharConversion; +} + +BEGIN_CUSTOM_CHAR_CONVERSION( CUtlNoEscConversion, s_NoEscConversion, "\"", 0x7F ) + { 0x7F, "" }, +END_CUSTOM_CHAR_CONVERSION( CUtlNoEscConversion, s_NoEscConversion, "\"", 0x7F ) + +CUtlCharConversion *GetNoEscCharConversion() +{ + return &s_NoEscConversion; +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CUtlCStringConversion::CUtlCStringConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ) : + CUtlCharConversion( nEscapeChar, pDelimiter, nCount, pArray ) +{ + memset( m_pConversion, 0x0, sizeof(m_pConversion) ); + for ( int i = 0; i < nCount; ++i ) + { + m_pConversion[ static_cast(pArray[i].m_pReplacementString[0]) ] = pArray[i].m_nActualChar; + } +} + +// Finds a conversion for the passed-in string, returns length +char CUtlCStringConversion::FindConversion( const char *pString, int *pLength ) +{ + char c = m_pConversion[ static_cast(pString[0]) ]; + *pLength = (c != '\0') ? 1 : 0; + return c; +} + + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CUtlCharConversion::CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ) +{ + m_nEscapeChar = nEscapeChar; + m_pDelimiter = pDelimiter; + m_nCount = nCount; + m_nDelimiterLength = Q_strlen( pDelimiter ); + m_nMaxConversionLength = 0; + + memset( m_pReplacements, 0, sizeof(m_pReplacements) ); + + for ( int i = 0; i < nCount; ++i ) + { + m_pList[i] = pArray[i].m_nActualChar; + ConversionInfo_t &info = m_pReplacements[ static_cast(m_pList[i]) ]; + Assert( info.m_pReplacementString == 0 ); + info.m_pReplacementString = pArray[i].m_pReplacementString; + info.m_nLength = Q_strlen( info.m_pReplacementString ); + if ( info.m_nLength > m_nMaxConversionLength ) + { + m_nMaxConversionLength = info.m_nLength; + } + } +} + + +//----------------------------------------------------------------------------- +// Escape character + delimiter +//----------------------------------------------------------------------------- +char CUtlCharConversion::GetEscapeChar() const +{ + return m_nEscapeChar; +} + +const char *CUtlCharConversion::GetDelimiter() const +{ + return m_pDelimiter; +} + +int CUtlCharConversion::GetDelimiterLength() const +{ + return m_nDelimiterLength; +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +const char *CUtlCharConversion::GetConversionString( char c ) const +{ + return m_pReplacements[ static_cast(c) ].m_pReplacementString; +} + +int CUtlCharConversion::GetConversionLength( char c ) const +{ + return m_pReplacements[ static_cast(c) ].m_nLength; +} + +int CUtlCharConversion::MaxConversionLength() const +{ + return m_nMaxConversionLength; +} + + +//----------------------------------------------------------------------------- +// Finds a conversion for the passed-in string, returns length +//----------------------------------------------------------------------------- +char CUtlCharConversion::FindConversion( const char *pString, int *pLength ) +{ + for ( int i = 0; i < m_nCount; ++i ) + { + if ( !Q_strcmp( pString, m_pReplacements[ static_cast(m_pList[i]) ].m_pReplacementString ) ) + { + *pLength = m_pReplacements[ static_cast(m_pList[i]) ].m_nLength; + return m_pList[i]; + } + } + + *pLength = 0; + return '\0'; +} + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- +CUtlBuffer::CUtlBuffer( int growSize, int initSize, int nFlags ) : + m_Memory( growSize, initSize ), m_Error(0) +{ + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_nOffset = 0; + m_Flags = nFlags; + if ( (initSize != 0) && !IsReadOnly() ) + { + m_nMaxPut = -1; + AddNullTermination(); + } + else + { + m_nMaxPut = 0; + } + SetOverflowFuncs( &CUtlBuffer::GetOverflow, &CUtlBuffer::PutOverflow ); +} + +CUtlBuffer::CUtlBuffer( const void *pBuffer, int nSize, int nFlags ) : + m_Memory( (unsigned char*)pBuffer, nSize ), m_Error(0) +{ + Assert( nSize != 0 ); + + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_nOffset = 0; + m_Flags = nFlags; + if ( IsReadOnly() ) + { + m_nMaxPut = nSize; + } + else + { + m_nMaxPut = -1; + AddNullTermination(); + } + SetOverflowFuncs( &CUtlBuffer::GetOverflow, &CUtlBuffer::PutOverflow ); +} + + +//----------------------------------------------------------------------------- +// Modifies the buffer to be binary or text; Blows away the buffer and the CONTAINS_CRLF value. +//----------------------------------------------------------------------------- +void CUtlBuffer::SetBufferType( bool bIsText, bool bContainsCRLF ) +{ +#ifdef _DEBUG + // If the buffer is empty, there is no opportunity for this stuff to fail + if ( TellMaxPut() != 0 ) + { + if ( IsText() ) + { + if ( bIsText ) + { + Assert( ContainsCRLF() == bContainsCRLF ); + } + else + { + Assert( ContainsCRLF() ); + } + } + else + { + if ( bIsText ) + { + Assert( bContainsCRLF ); + } + } + } +#endif + + if ( bIsText ) + { + m_Flags |= TEXT_BUFFER; + } + else + { + m_Flags &= ~TEXT_BUFFER; + } + if ( bContainsCRLF ) + { + m_Flags |= CONTAINS_CRLF; + } + else + { + m_Flags &= ~CONTAINS_CRLF; + } +} + + +//----------------------------------------------------------------------------- +// Attaches the buffer to external memory.... +//----------------------------------------------------------------------------- +void CUtlBuffer::SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags ) +{ + m_Memory.SetExternalBuffer( (unsigned char*)pMemory, nSize ); + + // Reset all indices; we just changed memory + m_Get = 0; + m_Put = nInitialPut; + m_nTab = 0; + m_Error = 0; + m_nOffset = 0; + m_Flags = nFlags; + m_nMaxPut = -1; + AddNullTermination(); +} + +//----------------------------------------------------------------------------- +// Assumes an external buffer but manages its deletion +//----------------------------------------------------------------------------- +void CUtlBuffer::AssumeMemory( void *pMemory, int nSize, int nInitialPut, int nFlags ) +{ + m_Memory.AssumeMemory( (unsigned char*) pMemory, nSize ); + + // Reset all indices; we just changed memory + m_Get = 0; + m_Put = nInitialPut; + m_nTab = 0; + m_Error = 0; + m_nOffset = 0; + m_Flags = nFlags; + m_nMaxPut = -1; + AddNullTermination(); +} + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +void CUtlBuffer::EnsureCapacity( int num ) +{ + // Add one extra for the null termination + num += 1; + if ( m_Memory.IsExternallyAllocated() ) + { + if ( IsGrowable() && ( m_Memory.NumAllocated() < num ) ) + { + m_Memory.ConvertToGrowableMemory( 0 ); + } + else + { + num -= 1; + } + } + + m_Memory.EnsureCapacity( num ); +} + + +//----------------------------------------------------------------------------- +// Base get method from which all others derive +//----------------------------------------------------------------------------- +void CUtlBuffer::Get( void* pMem, int size ) +{ + if ( CheckGet( size ) ) + { + memcpy( pMem, &m_Memory[m_Get - m_nOffset], size ); + m_Get += size; + } +} + + +//----------------------------------------------------------------------------- +// This will get at least 1 byte and up to nSize bytes. +// It will return the number of bytes actually read. +//----------------------------------------------------------------------------- +int CUtlBuffer::GetUpTo( void *pMem, int nSize ) +{ + if ( CheckArbitraryPeekGet( 0, nSize ) ) + { + memcpy( pMem, &m_Memory[m_Get - m_nOffset], nSize ); + m_Get += nSize; + return nSize; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// Eats whitespace +//----------------------------------------------------------------------------- +void CUtlBuffer::EatWhiteSpace() +{ + if ( IsText() && IsValid() ) + { + while ( CheckGet( sizeof(char) ) ) + { + if ( !isspace( *(const unsigned char*)PeekGet() ) ) + break; + m_Get += sizeof(char); + } + } +} + + +//----------------------------------------------------------------------------- +// Eats C++ style comments +//----------------------------------------------------------------------------- +bool CUtlBuffer::EatCPPComment() +{ + if ( IsText() && IsValid() ) + { + // If we don't have a a c++ style comment next, we're done + const char *pPeek = (const char *)PeekGet( 2 * sizeof(char), 0 ); + if ( !pPeek || ( pPeek[0] != '/' ) || ( pPeek[1] != '/' ) ) + return false; + + // Deal with c++ style comments + m_Get += 2; + + // read complete line + for ( char c = GetChar(); IsValid(); c = GetChar() ) + { + if ( c == '\n' ) + break; + } + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Peeks how much whitespace to eat +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekWhiteSpace( int nOffset ) +{ + if ( !IsText() || !IsValid() ) + return 0; + + while ( CheckPeekGet( nOffset, sizeof(char) ) ) + { + if ( !isspace( *(unsigned char*)PeekGet( nOffset ) ) ) + break; + nOffset += sizeof(char); + } + + return nOffset; +} + + +//----------------------------------------------------------------------------- +// Peek size of sting to come, check memory bound +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekStringLength() +{ + if ( !IsValid() ) + return 0; + + // Eat preceeding whitespace + int nOffset = 0; + if ( IsText() ) + { + nOffset = PeekWhiteSpace( nOffset ); + } + + int nStartingOffset = nOffset; + + do + { + int nPeekAmount = 128; + + // NOTE: Add 1 for the terminating zero! + if ( !CheckArbitraryPeekGet( nOffset, nPeekAmount ) ) + { + if ( nOffset == nStartingOffset ) + return 0; + return nOffset - nStartingOffset + 1; + } + + const char *pTest = (const char *)PeekGet( nOffset ); + + if ( !IsText() ) + { + for ( int i = 0; i < nPeekAmount; ++i ) + { + // The +1 here is so we eat the terminating 0 + if ( pTest[i] == 0 ) + return (i + nOffset - nStartingOffset + 1); + } + } + else + { + for ( int i = 0; i < nPeekAmount; ++i ) + { + // The +1 here is so we eat the terminating 0 + if ( isspace((unsigned char)pTest[i]) || (pTest[i] == 0) ) + return (i + nOffset - nStartingOffset + 1); + } + } + + nOffset += nPeekAmount; + + } while ( true ); +} + + +//----------------------------------------------------------------------------- +// Peek size of line to come, check memory bound +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekLineLength() +{ + if ( !IsValid() ) + return 0; + + int nOffset = 0; + int nStartingOffset = nOffset; + + do + { + int nPeekAmount = 128; + + // NOTE: Add 1 for the terminating zero! + if ( !CheckArbitraryPeekGet( nOffset, nPeekAmount ) ) + { + if ( nOffset == nStartingOffset ) + return 0; + return nOffset - nStartingOffset + 1; + } + + const char *pTest = (const char *)PeekGet( nOffset ); + + for ( int i = 0; i < nPeekAmount; ++i ) + { + // The +2 here is so we eat the terminating '\n' and 0 + if ( pTest[i] == '\n' || pTest[i] == '\r' ) + return (i + nOffset - nStartingOffset + 2); + // The +1 here is so we eat the terminating 0 + if ( pTest[i] == 0 ) + return (i + nOffset - nStartingOffset + 1); + } + + nOffset += nPeekAmount; + + } while ( true ); +} + + +//----------------------------------------------------------------------------- +// Does the next bytes of the buffer match a pattern? +//----------------------------------------------------------------------------- +bool CUtlBuffer::PeekStringMatch( int nOffset, const char *pString, int nLen ) +{ + if ( !CheckPeekGet( nOffset, nLen ) ) + return false; + return !Q_strncmp( (const char*)PeekGet(nOffset), pString, nLen ); +} + + +//----------------------------------------------------------------------------- +// This version of PeekStringLength converts \" to \\ and " to \, etc. +// It also reads a " at the beginning and end of the string +//----------------------------------------------------------------------------- +int CUtlBuffer::PeekDelimitedStringLength( CUtlCharConversion *pConv, bool bActualSize ) +{ + if ( !IsText() || !pConv ) + return PeekStringLength(); + + // Eat preceeding whitespace + int nOffset = 0; + if ( IsText() ) + { + nOffset = PeekWhiteSpace( nOffset ); + } + + if ( !PeekStringMatch( nOffset, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + return 0; + + // Try to read ending ", but don't accept \" + int nActualStart = nOffset; + nOffset += pConv->GetDelimiterLength(); + int nLen = 1; // Starts at 1 for the '\0' termination + + do + { + if ( PeekStringMatch( nOffset, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + break; + + if ( !CheckPeekGet( nOffset, 1 ) ) + break; + + char c = *(const char*)PeekGet( nOffset ); + ++nLen; + ++nOffset; + if ( c == pConv->GetEscapeChar() ) + { + int nLength = pConv->MaxConversionLength(); + if ( !CheckArbitraryPeekGet( nOffset, nLength ) ) + break; + + pConv->FindConversion( (const char*)PeekGet(nOffset), &nLength ); + nOffset += nLength; + } + } while (true); + + return bActualSize ? nLen : nOffset - nActualStart + pConv->GetDelimiterLength() + 1; +} + + +//----------------------------------------------------------------------------- +// Reads a null-terminated string +//----------------------------------------------------------------------------- +void CUtlBuffer::GetString( char* pString, int nMaxChars ) +{ + if (!IsValid()) + { + *pString = 0; + return; + } + + if ( nMaxChars == 0 ) + { + nMaxChars = INT_MAX; + } + + // Remember, this *includes* the null character + // It will be 0, however, if the buffer is empty. + int nLen = PeekStringLength(); + + if ( IsText() ) + { + EatWhiteSpace(); + } + + if ( nLen == 0 ) + { + *pString = 0; + m_Error |= GET_OVERFLOW; + return; + } + + // Strip off the terminating NULL + if ( nLen <= nMaxChars ) + { + Get( pString, nLen - 1 ); + pString[ nLen - 1 ] = 0; + } + else + { + Get( pString, nMaxChars - 1 ); + pString[ nMaxChars - 1 ] = 0; + SeekGet( SEEK_CURRENT, nLen - 1 - nMaxChars ); + } + + // Read the terminating NULL in binary formats + if ( !IsText() ) + { + VerifyEquals( GetChar(), 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Reads up to and including the first \n +//----------------------------------------------------------------------------- +void CUtlBuffer::GetLine( char* pLine, int nMaxChars ) +{ + Assert( IsText() && !ContainsCRLF() ); + + if ( !IsValid() ) + { + *pLine = 0; + return; + } + + if ( nMaxChars == 0 ) + { + nMaxChars = INT_MAX; + } + + // Remember, this *includes* the null character + // It will be 0, however, if the buffer is empty. + int nLen = PeekLineLength(); + if ( nLen == 0 ) + { + *pLine = 0; + m_Error |= GET_OVERFLOW; + return; + } + + // Strip off the terminating NULL + if ( nLen <= nMaxChars ) + { + Get( pLine, nLen - 1 ); + pLine[ nLen - 1 ] = 0; + } + else + { + Get( pLine, nMaxChars - 1 ); + pLine[ nMaxChars - 1 ] = 0; + SeekGet( SEEK_CURRENT, nLen - 1 - nMaxChars ); + } +} + + +//----------------------------------------------------------------------------- +// This version of GetString converts \ to \\ and " to \", etc. +// It also places " at the beginning and end of the string +//----------------------------------------------------------------------------- +char CUtlBuffer::GetDelimitedCharInternal( CUtlCharConversion *pConv ) +{ + char c = GetChar(); + if ( c == pConv->GetEscapeChar() ) + { + int nLength = pConv->MaxConversionLength(); + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + return '\0'; + + c = pConv->FindConversion( (const char *)PeekGet(), &nLength ); + SeekGet( SEEK_CURRENT, nLength ); + } + + return c; +} + +char CUtlBuffer::GetDelimitedChar( CUtlCharConversion *pConv ) +{ + if ( !IsText() || !pConv ) + return GetChar( ); + return GetDelimitedCharInternal( pConv ); +} + +void CUtlBuffer::GetDelimitedString( CUtlCharConversion *pConv, char *pString, int nMaxChars ) +{ + if ( !IsText() || !pConv ) + { + GetString( pString, nMaxChars ); + return; + } + + if (!IsValid()) + { + *pString = 0; + return; + } + + if ( nMaxChars == 0 ) + { + nMaxChars = INT_MAX; + } + + EatWhiteSpace(); + if ( !PeekStringMatch( 0, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + return; + + // Pull off the starting delimiter + SeekGet( SEEK_CURRENT, pConv->GetDelimiterLength() ); + + int nRead = 0; + while ( IsValid() ) + { + if ( PeekStringMatch( 0, pConv->GetDelimiter(), pConv->GetDelimiterLength() ) ) + { + SeekGet( SEEK_CURRENT, pConv->GetDelimiterLength() ); + break; + } + + char c = GetDelimitedCharInternal( pConv ); + + if ( nRead < nMaxChars ) + { + pString[nRead] = c; + ++nRead; + } + } + + if ( nRead >= nMaxChars ) + { + nRead = nMaxChars - 1; + } + pString[nRead] = '\0'; +} + + +//----------------------------------------------------------------------------- +// Checks if a get is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckGet( int nSize ) +{ + if ( m_Error & GET_OVERFLOW ) + return false; + + if ( TellMaxPut() < m_Get + nSize ) + { + m_Error |= GET_OVERFLOW; + return false; + } + + if ( ( m_Get < m_nOffset ) || ( m_Memory.NumAllocated() < m_Get - m_nOffset + nSize ) ) + { + if ( !OnGetOverflow( nSize ) ) + { + m_Error |= GET_OVERFLOW; + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Checks if a peek get is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckPeekGet( int nOffset, int nSize ) +{ + if ( m_Error & GET_OVERFLOW ) + return false; + + // Checking for peek can't set the overflow flag + bool bOk = CheckGet( nOffset + nSize ); + m_Error &= ~GET_OVERFLOW; + return bOk; +} + + +//----------------------------------------------------------------------------- +// Call this to peek arbitrarily long into memory. It doesn't fail unless +// it can't read *anything* new +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckArbitraryPeekGet( int nOffset, int &nIncrement ) +{ + if ( TellGet() + nOffset >= TellMaxPut() ) + { + nIncrement = 0; + return false; + } + + if ( TellGet() + nOffset + nIncrement > TellMaxPut() ) + { + nIncrement = TellMaxPut() - TellGet() - nOffset; + } + + // NOTE: CheckPeekGet could modify TellMaxPut for streaming files + // We have to call TellMaxPut again here + CheckPeekGet( nOffset, nIncrement ); + int nMaxGet = TellMaxPut() - TellGet(); + if ( nMaxGet < nIncrement ) + { + nIncrement = nMaxGet; + } + return (nIncrement != 0); +} + + +//----------------------------------------------------------------------------- +// Peek part of the butt +//----------------------------------------------------------------------------- +const void* CUtlBuffer::PeekGet( int nMaxSize, int nOffset ) +{ + if ( !CheckPeekGet( nOffset, nMaxSize ) ) + return NULL; + return &m_Memory[ m_Get + nOffset - m_nOffset ]; +} + + +//----------------------------------------------------------------------------- +// Change where I'm reading +//----------------------------------------------------------------------------- +void CUtlBuffer::SeekGet( SeekType_t type, int offset ) +{ + switch( type ) + { + case SEEK_HEAD: + m_Get = offset; + break; + + case SEEK_CURRENT: + m_Get += offset; + break; + + case SEEK_TAIL: + m_Get = m_nMaxPut - offset; + break; + } + + if ( m_Get > m_nMaxPut ) + { + m_Error |= GET_OVERFLOW; + } + else + { + m_Error &= ~GET_OVERFLOW; + if ( m_Get < m_nOffset || m_Get >= m_nOffset + Size() ) + { + OnGetOverflow( -1 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Parse... +//----------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma warning ( disable : 4706 ) +#endif + +int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) +{ + Assert( pFmt ); + if ( m_Error || !IsText() ) + return 0; + + int numScanned = 0; + int nLength; + char c; + char* pEnd; + while ( (c = *pFmt++) ) + { + // Stop if we hit the end of the buffer + if ( m_Get >= TellMaxPut() ) + { + m_Error |= GET_OVERFLOW; + break; + } + + switch (c) + { + case ' ': + // eat all whitespace + EatWhiteSpace(); + break; + + case '%': + { + // Conversion character... try to convert baby! + char type = *pFmt++; + if (type == 0) + return numScanned; + + switch(type) + { + case 'c': + { + char* ch = va_arg( list, char * ); + if ( CheckPeekGet( 0, sizeof(char) ) ) + { + *ch = *(const char*)PeekGet(); + ++m_Get; + } + else + { + *ch = 0; + return numScanned; + } + } + break; + + case 'i': + case 'd': + { + int* i = va_arg( list, int * ); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *i = 0; + return numScanned; + } + + *i = strtol( (char*)PeekGet(), &pEnd, 10 ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 'x': + { + int* i = va_arg( list, int * ); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *i = 0; + return numScanned; + } + + *i = strtol( (char*)PeekGet(), &pEnd, 16 ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 'u': + { + unsigned int* u = va_arg( list, unsigned int *); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *u = 0; + return numScanned; + } + + *u = strtoul( (char*)PeekGet(), &pEnd, 10 ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 'f': + { + float* f = va_arg( list, float *); + + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + *f = 0.0f; + return numScanned; + } + + *f = (float)strtod( (char*)PeekGet(), &pEnd ); + int nBytesRead = (int)( pEnd - (char*)PeekGet() ); + if ( nBytesRead == 0 ) + return numScanned; + m_Get += nBytesRead; + } + break; + + case 's': + { + char* s = va_arg( list, char * ); + GetString( s ); + } + break; + + default: + { + // unimplemented scanf type + Assert(0); + return numScanned; + } + break; + } + + ++numScanned; + } + break; + + default: + { + // Here we have to match the format string character + // against what's in the buffer or we're done. + if ( !CheckPeekGet( 0, sizeof(char) ) ) + return numScanned; + + if ( c != *(const char*)PeekGet() ) + return numScanned; + + ++m_Get; + } + } + } + return numScanned; +} + +#ifdef _MSC_VER +#pragma warning ( default : 4706 ) +#endif + +int CUtlBuffer::Scanf( const char* pFmt, ... ) +{ + va_list args; + + va_start( args, pFmt ); + int count = VaScanf( pFmt, args ); + va_end( args ); + + return count; +} + + +//----------------------------------------------------------------------------- +// Advance the get index until after the particular string is found +// Do not eat whitespace before starting. Return false if it failed +//----------------------------------------------------------------------------- +bool CUtlBuffer::GetToken( const char *pToken ) +{ + Assert( pToken ); + + // Look for the token + int nLen = Q_strlen( pToken ); + + int nSizeToCheck = Size() - TellGet() - m_nOffset; + + int nGet = TellGet(); + do + { + int nMaxSize = TellMaxPut() - TellGet(); + if ( nMaxSize < nSizeToCheck ) + { + nSizeToCheck = nMaxSize; + } + if ( nLen > nSizeToCheck ) + break; + + if ( !CheckPeekGet( 0, nSizeToCheck ) ) + break; + + const char *pBufStart = (const char*)PeekGet(); + const char *pFoundEnd = Q_strnistr( pBufStart, pToken, nSizeToCheck ); + if ( pFoundEnd ) + { + size_t nOffset = (size_t)pFoundEnd - (size_t)pBufStart; + SeekGet( CUtlBuffer::SEEK_CURRENT, nOffset + nLen ); + return true; + } + + SeekGet( CUtlBuffer::SEEK_CURRENT, nSizeToCheck - nLen - 1 ); + nSizeToCheck = Size() - (nLen-1); + + } while ( true ); + + SeekGet( CUtlBuffer::SEEK_HEAD, nGet ); + return false; +} + + +//----------------------------------------------------------------------------- +// (For text buffers only) +// Parse a token from the buffer: +// Grab all text that lies between a starting delimiter + ending delimiter +// (skipping whitespace that leads + trails both delimiters). +// Note the delimiter checks are case-insensitive. +// If successful, the get index is advanced and the function returns true, +// otherwise the index is not advanced and the function returns false. +//----------------------------------------------------------------------------- +bool CUtlBuffer::ParseToken( const char *pStartingDelim, const char *pEndingDelim, char* pString, int nMaxLen ) +{ + int nCharsToCopy = 0; + int nCurrentGet = 0; + + size_t nEndingDelimLen; + + // Starting delimiter is optional + char emptyBuf = '\0'; + if ( !pStartingDelim ) + { + pStartingDelim = &emptyBuf; + } + + // Ending delimiter is not + Assert( pEndingDelim && pEndingDelim[0] ); + nEndingDelimLen = Q_strlen( pEndingDelim ); + + int nStartGet = TellGet(); + char nCurrChar; + int nTokenStart = -1; + EatWhiteSpace( ); + while ( *pStartingDelim ) + { + nCurrChar = *pStartingDelim++; + if ( !isspace((unsigned char)nCurrChar) ) + { + if ( tolower( GetChar() ) != tolower( nCurrChar ) ) + goto parseFailed; + } + else + { + EatWhiteSpace(); + } + } + + EatWhiteSpace(); + nTokenStart = TellGet(); + if ( !GetToken( pEndingDelim ) ) + goto parseFailed; + + nCurrentGet = TellGet(); + nCharsToCopy = (nCurrentGet - nEndingDelimLen) - nTokenStart; + if ( nCharsToCopy >= nMaxLen ) + { + nCharsToCopy = nMaxLen - 1; + } + + if ( nCharsToCopy > 0 ) + { + SeekGet( CUtlBuffer::SEEK_HEAD, nTokenStart ); + Get( pString, nCharsToCopy ); + if ( !IsValid() ) + goto parseFailed; + + // Eat trailing whitespace + for ( ; nCharsToCopy > 0; --nCharsToCopy ) + { + if ( !isspace( (unsigned char)pString[ nCharsToCopy-1 ] ) ) + break; + } + } + pString[ nCharsToCopy ] = '\0'; + + // Advance the Get index + SeekGet( CUtlBuffer::SEEK_HEAD, nCurrentGet ); + return true; + +parseFailed: + // Revert the get index + SeekGet( SEEK_HEAD, nStartGet ); + pString[0] = '\0'; + return false; +} + + +//----------------------------------------------------------------------------- +// Parses the next token, given a set of character breaks to stop at +//----------------------------------------------------------------------------- +int CUtlBuffer::ParseToken( characterset_t *pBreaks, char *pTokenBuf, int nMaxLen, bool bParseComments ) +{ + Assert( nMaxLen > 0 ); + pTokenBuf[0] = 0; + + // skip whitespace + comments + while ( true ) + { + if ( !IsValid() ) + return -1; + EatWhiteSpace(); + if ( bParseComments ) + { + if ( !EatCPPComment() ) + break; + } + else + { + break; + } + } + + char c = GetChar(); + + // End of buffer + if ( c == 0 ) + return -1; + + // handle quoted strings specially + if ( c == '\"' ) + { + int nLen = 0; + while( IsValid() ) + { + c = GetChar(); + if ( c == '\"' || !c ) + { + pTokenBuf[nLen] = 0; + return nLen; + } + pTokenBuf[nLen] = c; + if ( ++nLen == nMaxLen ) + { + pTokenBuf[nLen-1] = 0; + return nMaxLen; + } + } + + // In this case, we hit the end of the buffer before hitting the end qoute + pTokenBuf[nLen] = 0; + return nLen; + } + + // parse single characters + if ( IN_CHARACTERSET( *pBreaks, c ) ) + { + pTokenBuf[0] = c; + pTokenBuf[1] = 0; + return 1; + } + + // parse a regular word + int nLen = 0; + while ( true ) + { + pTokenBuf[nLen] = c; + if ( ++nLen == nMaxLen ) + { + pTokenBuf[nLen-1] = 0; + return nMaxLen; + } + c = GetChar(); + if ( !IsValid() ) + break; + + if ( IN_CHARACTERSET( *pBreaks, c ) || c == '\"' || c <= ' ' ) + { + SeekGet( SEEK_CURRENT, -1 ); + break; + } + } + + pTokenBuf[nLen] = 0; + return nLen; +} + + + +//----------------------------------------------------------------------------- +// Serialization +//----------------------------------------------------------------------------- +void CUtlBuffer::Put( const void *pMem, int size ) +{ + if ( size && CheckPut( size ) ) + { + memcpy( &m_Memory[m_Put - m_nOffset], pMem, size ); + m_Put += size; + + AddNullTermination(); + } +} + + +//----------------------------------------------------------------------------- +// Writes a null-terminated string +//----------------------------------------------------------------------------- +void CUtlBuffer::PutString( const char* pString ) +{ + if (!IsText()) + { + if ( pString ) + { + // Not text? append a null at the end. + size_t nLen = Q_strlen( pString ) + 1; + Put( pString, nLen * sizeof(char) ); + return; + } + else + { + PutTypeBin( 0 ); + } + } + else if (pString) + { + int nTabCount = ( m_Flags & AUTO_TABS_DISABLED ) ? 0 : m_nTab; + if ( nTabCount > 0 ) + { + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + + const char* pEndl = strchr( pString, '\n' ); + while ( pEndl ) + { + size_t nSize = (size_t)pEndl - (size_t)pString + sizeof(char); + Put( pString, nSize ); + pString = pEndl + 1; + if ( *pString ) + { + PutTabs(); + pEndl = strchr( pString, '\n' ); + } + else + { + pEndl = NULL; + } + } + } + size_t nLen = Q_strlen( pString ); + if ( nLen ) + { + Put( pString, nLen * sizeof(char) ); + } + } +} + + +//----------------------------------------------------------------------------- +// This version of PutString converts \ to \\ and " to \", etc. +// It also places " at the beginning and end of the string +//----------------------------------------------------------------------------- +inline void CUtlBuffer::PutDelimitedCharInternal( CUtlCharConversion *pConv, char c ) +{ + int l = pConv->GetConversionLength( c ); + if ( l == 0 ) + { + PutChar( c ); + } + else + { + PutChar( pConv->GetEscapeChar() ); + Put( pConv->GetConversionString( c ), l ); + } +} + +void CUtlBuffer::PutDelimitedChar( CUtlCharConversion *pConv, char c ) +{ + if ( !IsText() || !pConv ) + { + PutChar( c ); + return; + } + + PutDelimitedCharInternal( pConv, c ); +} + +void CUtlBuffer::PutDelimitedString( CUtlCharConversion *pConv, const char *pString ) +{ + if ( !IsText() || !pConv ) + { + PutString( pString ); + return; + } + + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + Put( pConv->GetDelimiter(), pConv->GetDelimiterLength() ); + + int nLen = pString ? Q_strlen( pString ) : 0; + for ( int i = 0; i < nLen; ++i ) + { + PutDelimitedCharInternal( pConv, pString[i] ); + } + + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + Put( pConv->GetDelimiter(), pConv->GetDelimiterLength() ); +} + + +void CUtlBuffer::VaPrintf( const char* pFmt, va_list list ) +{ + char temp[2048]; +#ifdef _DEBUG + int nLen = +#endif + Q_vsnprintf( temp, sizeof( temp ), pFmt, list ); + Assert( nLen < 2048 ); + PutString( temp ); +} + +void CUtlBuffer::Printf( const char* pFmt, ... ) +{ + va_list args; + + va_start( args, pFmt ); + VaPrintf( pFmt, args ); + va_end( args ); +} + + +//----------------------------------------------------------------------------- +// Calls the overflow functions +//----------------------------------------------------------------------------- +void CUtlBuffer::SetOverflowFuncs( UtlBufferOverflowFunc_t getFunc, UtlBufferOverflowFunc_t putFunc ) +{ + m_GetOverflowFunc = getFunc; + m_PutOverflowFunc = putFunc; +} + + +//----------------------------------------------------------------------------- +// Calls the overflow functions +//----------------------------------------------------------------------------- +bool CUtlBuffer::OnPutOverflow( int nSize ) +{ + return (this->*m_PutOverflowFunc)( nSize ); +} + +bool CUtlBuffer::OnGetOverflow( int nSize ) +{ + return (this->*m_GetOverflowFunc)( nSize ); +} + + +//----------------------------------------------------------------------------- +// Checks if a put is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::PutOverflow( int nSize ) +{ + if ( m_Memory.IsExternallyAllocated() ) + { + if ( !IsGrowable() ) + return false; + + m_Memory.ConvertToGrowableMemory( 0 ); + } + + while( Size() < m_Put - m_nOffset + nSize ) + { + m_Memory.Grow(); + } + + return true; +} + +bool CUtlBuffer::GetOverflow( int nSize ) +{ + return false; +} + + +//----------------------------------------------------------------------------- +// Checks if a put is ok +//----------------------------------------------------------------------------- +bool CUtlBuffer::CheckPut( int nSize ) +{ + if ( ( m_Error & PUT_OVERFLOW ) || IsReadOnly() ) + return false; + + if ( ( m_Put < m_nOffset ) || ( m_Memory.NumAllocated() < m_Put - m_nOffset + nSize ) ) + { + if ( !OnPutOverflow( nSize ) ) + { + m_Error |= PUT_OVERFLOW; + return false; + } + } + return true; +} + +void CUtlBuffer::SeekPut( SeekType_t type, int offset ) +{ + int nNextPut = m_Put; + switch( type ) + { + case SEEK_HEAD: + nNextPut = offset; + break; + + case SEEK_CURRENT: + nNextPut += offset; + break; + + case SEEK_TAIL: + nNextPut = m_nMaxPut - offset; + break; + } + + // Force a write of the data + // FIXME: We could make this more optimal potentially by writing out + // the entire buffer if you seek outside the current range + + // NOTE: This call will write and will also seek the file to nNextPut. + OnPutOverflow( -nNextPut-1 ); + m_Put = nNextPut; + + AddNullTermination(); +} + + +void CUtlBuffer::ActivateByteSwapping( bool bActivate ) +{ + m_Byteswap.ActivateByteSwapping( bActivate ); +} + +void CUtlBuffer::SetBigEndian( bool bigEndian ) +{ + m_Byteswap.SetTargetBigEndian( bigEndian ); +} + +bool CUtlBuffer::IsBigEndian( void ) +{ + return m_Byteswap.IsTargetBigEndian(); +} + + +//----------------------------------------------------------------------------- +// null terminate the buffer +//----------------------------------------------------------------------------- +void CUtlBuffer::AddNullTermination( void ) +{ + if ( m_Put > m_nMaxPut ) + { + if ( !IsReadOnly() && ((m_Error & PUT_OVERFLOW) == 0) ) + { + // Add null termination value + if ( CheckPut( 1 ) ) + { + m_Memory[m_Put - m_nOffset] = 0; + } + else + { + // Restore the overflow state, it was valid before... + m_Error &= ~PUT_OVERFLOW; + } + } + m_nMaxPut = m_Put; + } +} + + +//----------------------------------------------------------------------------- +// Converts a buffer from a CRLF buffer to a CR buffer (and back) +// Returns false if no conversion was necessary (and outBuf is left untouched) +// If the conversion occurs, outBuf will be cleared. +//----------------------------------------------------------------------------- +bool CUtlBuffer::ConvertCRLF( CUtlBuffer &outBuf ) +{ + if ( !IsText() || !outBuf.IsText() ) + return false; + + if ( ContainsCRLF() == outBuf.ContainsCRLF() ) + return false; + + int nInCount = TellMaxPut(); + + outBuf.Purge(); + outBuf.EnsureCapacity( nInCount ); + + bool bFromCRLF = ContainsCRLF(); + + // Start reading from the beginning + int nGet = TellGet(); + int nPut = TellPut(); + int nGetDelta = 0; + int nPutDelta = 0; + + const char *pBase = (const char*)Base(); + int nCurrGet = 0; + while ( nCurrGet < nInCount ) + { + const char *pCurr = &pBase[nCurrGet]; + if ( bFromCRLF ) + { + const char *pNext = Q_strnistr( pCurr, "\r\n", nInCount - nCurrGet ); + if ( !pNext ) + { + outBuf.Put( pCurr, nInCount - nCurrGet ); + break; + } + + int nBytes = (size_t)pNext - (size_t)pCurr; + outBuf.Put( pCurr, nBytes ); + outBuf.PutChar( '\n' ); + nCurrGet += nBytes + 2; + if ( nGet >= nCurrGet - 1 ) + { + --nGetDelta; + } + if ( nPut >= nCurrGet - 1 ) + { + --nPutDelta; + } + } + else + { + const char *pNext = Q_strnchr( pCurr, '\n', nInCount - nCurrGet ); + if ( !pNext ) + { + outBuf.Put( pCurr, nInCount - nCurrGet ); + break; + } + + int nBytes = (size_t)pNext - (size_t)pCurr; + outBuf.Put( pCurr, nBytes ); + outBuf.PutChar( '\r' ); + outBuf.PutChar( '\n' ); + nCurrGet += nBytes + 1; + if ( nGet >= nCurrGet ) + { + ++nGetDelta; + } + if ( nPut >= nCurrGet ) + { + ++nPutDelta; + } + } + } + + Assert( nPut + nPutDelta <= outBuf.TellMaxPut() ); + + outBuf.SeekGet( SEEK_HEAD, nGet + nGetDelta ); + outBuf.SeekPut( SEEK_HEAD, nPut + nPutDelta ); + + return true; +} + + +//--------------------------------------------------------------------------- +// Implementation of CUtlInplaceBuffer +//--------------------------------------------------------------------------- + +CUtlInplaceBuffer::CUtlInplaceBuffer( int growSize /* = 0 */, int initSize /* = 0 */, int nFlags /* = 0 */ ) : + CUtlBuffer( growSize, initSize, nFlags ) +{ + +} + +bool CUtlInplaceBuffer::InplaceGetLinePtr( char **ppszInBufferPtr, int *pnLineLength ) +{ + Assert( IsText() && !ContainsCRLF() ); + + int nLineLen = PeekLineLength(); + if ( nLineLen <= 1 ) + { + SeekGet( SEEK_TAIL, 0 ); + return false; + } + + -- nLineLen; // because it accounts for putting a terminating null-character + + char *pszLine = ( char * ) const_cast< void * >( PeekGet() ); + SeekGet( SEEK_CURRENT, nLineLen ); + + // Set the out args + if ( ppszInBufferPtr ) + *ppszInBufferPtr = pszLine; + + if ( pnLineLength ) + *pnLineLength = nLineLen; + + return true; +} + +char * CUtlInplaceBuffer::InplaceGetLinePtr( void ) +{ + char *pszLine = NULL; + int nLineLen = 0; + + if ( InplaceGetLinePtr( &pszLine, &nLineLen ) ) + { + Assert( nLineLen >= 1 ); + + switch ( pszLine[ nLineLen - 1 ] ) + { + case '\n': + case '\r': + pszLine[ nLineLen - 1 ] = 0; + if ( -- nLineLen ) + { + switch ( pszLine[ nLineLen - 1 ] ) + { + case '\n': + case '\r': + pszLine[ nLineLen - 1 ] = 0; + break; + } + } + break; + + default: + Assert( pszLine[ nLineLen ] == 0 ); + break; + } + } + + return pszLine; +} + diff --git a/tier1/utlbufferutil.cpp b/tier1/utlbufferutil.cpp new file mode 100644 index 00000000..c1391dc4 --- /dev/null +++ b/tier1/utlbufferutil.cpp @@ -0,0 +1,562 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// $Header: $ +// $NoKeywords: $ +// +// Serialization buffer +//===========================================================================// + +#ifdef _MSC_VER +#pragma warning (disable : 4514) +#endif + +#include "tier1/utlbufferutil.h" +#include "tier1/utlbuffer.h" +#include "mathlib/vector.h" +#include "mathlib/vector2d.h" +#include "mathlib/vector4d.h" +#include "mathlib/vmatrix.h" +#include "Color.h" +#include +#include +#include +#include +#include +#include "tier1/utlbinaryblock.h" +#include "tier1/utlstring.h" +#include "tier1/strtools.h" +#include "tier1/characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// For serialization, set the delimiter rules +//----------------------------------------------------------------------------- +CUtlCharConversion *s_pConv = NULL; +const char *s_pUtlBufferUtilArrayDelim = NULL; +void SetSerializationDelimiter( CUtlCharConversion *pConv ) +{ + s_pConv = pConv; +} + +void SetSerializationArrayDelimiter( const char *pDelimiter ) +{ + s_pUtlBufferUtilArrayDelim = pDelimiter; +} + + +//----------------------------------------------------------------------------- +// Serialize a floating point number in text mode in a readably friendly fashion +//----------------------------------------------------------------------------- +static void SerializeFloat( CUtlBuffer &buf, float f ) +{ + Assert( buf.IsText() ); + + // FIXME: Print this in a way that we never lose precision + char pTemp[256]; + int nLen = Q_snprintf( pTemp, sizeof(pTemp), "%.10f", f ); + while ( nLen > 0 && pTemp[nLen-1] == '0' ) + { + --nLen; + pTemp[nLen] = 0; + } + if ( nLen > 0 && pTemp[nLen-1] == '.' ) + { + --nLen; + pTemp[nLen] = 0; + } + buf.PutString( pTemp ); +} + +static void SerializeFloats( CUtlBuffer &buf, int nCount, const float *pFloats ) +{ + for ( int i = 0; i < nCount; ++i ) + { + SerializeFloat( buf, pFloats[i] ); + if ( i != nCount-1 ) + { + buf.PutChar( ' ' ); + } + } +} + + +//----------------------------------------------------------------------------- +// Serialization methods for basic types +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const bool &src ) +{ + if ( buf.IsText() ) + { + buf.Printf( "%d", src ); + } + else + { + buf.PutChar( src ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, bool &dest ) +{ + if ( buf.IsText() ) + { + int nValue = 0; + int nRetVal = buf.Scanf( "%d", &nValue ); + dest = ( nValue != 0 ); + return (nRetVal == 1) && buf.IsValid(); + } + + dest = ( buf.GetChar( ) != 0 ); + return buf.IsValid(); +} + + +bool Serialize( CUtlBuffer &buf, const int &src ) +{ + if ( buf.IsText() ) + { + buf.Printf( "%d", src ); + } + else + { + buf.PutInt( src ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, int &dest ) +{ + if ( buf.IsText() ) + { + int nRetVal = buf.Scanf( "%d", &dest ); + return (nRetVal == 1) && buf.IsValid(); + } + + dest = buf.GetInt( ); + return buf.IsValid(); +} + +bool Serialize( CUtlBuffer &buf, const float &src ) +{ + if ( buf.IsText() ) + { + SerializeFloat( buf, src ); + } + else + { + buf.PutFloat( src ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, float &dest ) +{ + if ( buf.IsText() ) + { + // FIXME: Print this in a way that we never lose precision + int nRetVal = buf.Scanf( "%f", &dest ); + return (nRetVal == 1) && buf.IsValid(); + } + + dest = buf.GetFloat( ); + return buf.IsValid(); +} + + +//----------------------------------------------------------------------------- +// Attribute types related to vector math +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const Vector2D &src ) +{ + if ( buf.IsText() ) + { + SerializeFloats( buf, 2, src.Base() ); + } + else + { + buf.PutFloat( src.x ); + buf.PutFloat( src.y ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, Vector2D &dest ) +{ + if ( buf.IsText() ) + { + // FIXME: Print this in a way that we never lose precision + int nRetVal = buf.Scanf( "%f %f", &dest.x, &dest.y ); + return (nRetVal == 2) && buf.IsValid(); + } + + dest.x = buf.GetFloat( ); + dest.y = buf.GetFloat( ); + return buf.IsValid(); +} + +bool Serialize( CUtlBuffer &buf, const Vector &src ) +{ + if ( buf.IsText() ) + { + SerializeFloats( buf, 3, src.Base() ); + } + else + { + buf.PutFloat( src.x ); + buf.PutFloat( src.y ); + buf.PutFloat( src.z ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, Vector &dest ) +{ + if ( buf.IsText() ) + { + // FIXME: Print this in a way that we never lose precision + int nRetVal = buf.Scanf( "%f %f %f", &dest.x, &dest.y, &dest.z ); + return (nRetVal == 3) && buf.IsValid(); + } + + dest.x = buf.GetFloat( ); + dest.y = buf.GetFloat( ); + dest.z = buf.GetFloat( ); + return buf.IsValid(); +} + +bool Serialize( CUtlBuffer &buf, const Vector4D &src ) +{ + if ( buf.IsText() ) + { + SerializeFloats( buf, 4, src.Base() ); + } + else + { + buf.PutFloat( src.x ); + buf.PutFloat( src.y ); + buf.PutFloat( src.z ); + buf.PutFloat( src.w ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, Vector4D &dest ) +{ + if ( buf.IsText() ) + { + // FIXME: Print this in a way that we never lose precision + int nRetVal = buf.Scanf( "%f %f %f %f", &dest.x, &dest.y, &dest.z, &dest.w ); + return (nRetVal == 4) && buf.IsValid(); + } + + dest.x = buf.GetFloat( ); + dest.y = buf.GetFloat( ); + dest.z = buf.GetFloat( ); + dest.w = buf.GetFloat( ); + return buf.IsValid(); +} + +bool Serialize( CUtlBuffer &buf, const QAngle &src ) +{ + if ( buf.IsText() ) + { + SerializeFloats( buf, 3, src.Base() ); + } + else + { + buf.PutFloat( src.x ); + buf.PutFloat( src.y ); + buf.PutFloat( src.z ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, QAngle &dest ) +{ + if ( buf.IsText() ) + { + // FIXME: Print this in a way that we never lose precision + int nRetVal = buf.Scanf( "%f %f %f", &dest.x, &dest.y, &dest.z ); + return (nRetVal == 3) && buf.IsValid(); + } + + dest.x = buf.GetFloat( ); + dest.y = buf.GetFloat( ); + dest.z = buf.GetFloat( ); + return buf.IsValid(); +} + +bool Serialize( CUtlBuffer &buf, const Quaternion &src ) +{ + if ( buf.IsText() ) + { + SerializeFloats( buf, 4, &src.x ); + } + else + { + buf.PutFloat( src.x ); + buf.PutFloat( src.y ); + buf.PutFloat( src.z ); + buf.PutFloat( src.w ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, Quaternion &dest ) +{ + if ( buf.IsText() ) + { + // FIXME: Print this in a way that we never lose precision + int nRetVal = buf.Scanf( "%f %f %f %f", &dest.x, &dest.y, &dest.z, &dest.w ); + return (nRetVal == 4) && buf.IsValid(); + } + + dest.x = buf.GetFloat( ); + dest.y = buf.GetFloat( ); + dest.z = buf.GetFloat( ); + dest.w = buf.GetFloat( ); + return buf.IsValid(); +} + +bool Serialize( CUtlBuffer &buf, const VMatrix &src ) +{ + if ( buf.IsText() ) + { + buf.Printf( "\n" ); + SerializeFloats( buf, 4, src[0] ); + buf.Printf( "\n" ); + SerializeFloats( buf, 4, src[1] ); + buf.Printf( "\n" ); + SerializeFloats( buf, 4, src[2] ); + buf.Printf( "\n" ); + SerializeFloats( buf, 4, src[3] ); + buf.Printf( "\n" ); + } + else + { + buf.Put( &src, sizeof(VMatrix) ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, VMatrix &dest ) +{ + if ( !buf.IsValid() ) + return false; + + if ( buf.IsText() ) + { + int nRetVal = buf.Scanf( "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", + &dest[ 0 ][ 0 ], &dest[ 0 ][ 1 ], &dest[ 0 ][ 2 ], &dest[ 0 ][ 3 ], + &dest[ 1 ][ 0 ], &dest[ 1 ][ 1 ], &dest[ 1 ][ 2 ], &dest[ 1 ][ 3 ], + &dest[ 2 ][ 0 ], &dest[ 2 ][ 1 ], &dest[ 2 ][ 2 ], &dest[ 2 ][ 3 ], + &dest[ 3 ][ 0 ], &dest[ 3 ][ 1 ], &dest[ 3 ][ 2 ], &dest[ 3 ][ 3 ] ); + return (nRetVal == 16); + } + + buf.Get( &dest, sizeof(VMatrix) ); + return true; +} + + +//----------------------------------------------------------------------------- +// Color attribute +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const Color &src ) +{ + if ( buf.IsText() ) + { + buf.Printf( "%d %d %d %d", src[0], src[1], src[2], src[3] ); + } + else + { + buf.PutUnsignedChar( src[0] ); + buf.PutUnsignedChar( src[1] ); + buf.PutUnsignedChar( src[2] ); + buf.PutUnsignedChar( src[3] ); + } + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, Color &dest ) +{ + if ( buf.IsText() ) + { + int r = 0, g = 0, b = 0, a = 255; + int nRetVal = buf.Scanf( "%d %d %d %d", &r, &g, &b, &a ); + dest.SetColor( r, g, b, a ); + return (nRetVal == 4) && buf.IsValid(); + } + + dest[0] = buf.GetUnsignedChar( ); + dest[1] = buf.GetUnsignedChar( ); + dest[2] = buf.GetUnsignedChar( ); + dest[3] = buf.GetUnsignedChar( ); + return buf.IsValid(); +} + +/* +//----------------------------------------------------------------------------- +// Object ID attribute +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const DmObjectId_t &src ) +{ + return g_pDataModel->Serialize( buf, src ); +} + +bool Unserialize( CUtlBuffer &buf, DmObjectId_t &dest ) +{ + return g_pDataModel->Unserialize( buf, &dest ); +} +*/ + +//----------------------------------------------------------------------------- +// Binary buffer attribute +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const CUtlBinaryBlock &src ) +{ + int nLength = src.Length(); + if ( !buf.IsText() ) + { + buf.PutInt( nLength ); + if ( nLength != 0 ) + { + buf.Put( src.Get(), nLength ); + } + return buf.IsValid(); + } + + // Writes out uuencoded binaries + for ( int i = 0; i < nLength; ++i ) + { + if ( (i % 40) == 0 ) + { + buf.PutChar( '\n' ); + } + + char b1 = src[i] & 0xF; + char b2 = src[i] >> 4; + + char c1 = ( b1 <= 9 ) ? b1 + '0' : b1 - 10 + 'A'; + char c2 = ( b2 <= 9 ) ? b2 + '0' : b2 - 10 + 'A'; + + buf.PutChar( c2 ); + buf.PutChar( c1 ); + } + + buf.PutChar( '\n' ); + return buf.IsValid(); +} + +static int CountBinaryBytes( CUtlBuffer &buf, int *pEndGet ) +{ + // This counts the number of bytes in the uuencoded text + int nStartGet = buf.TellGet(); + buf.EatWhiteSpace(); + *pEndGet = buf.TellGet(); + int nByteCount = 0; + while ( buf.IsValid() ) + { + char c1 = buf.GetChar(); + char c2 = buf.GetChar(); + + bool bIsNum1 = ( c1 >= '0' ) && ( c1 <= '9' ); + bool bIsNum2 = ( c2 >= '0' ) && ( c2 <= '9' ); + + bool bIsAlpha1 = (( c1 >= 'A' ) && ( c1 <= 'F' )) || (( c1 >= 'a' ) && ( c1 <= 'f' )); + bool bIsAlpha2 = (( c2 >= 'A' ) && ( c2 <= 'F' )) || (( c2 >= 'a' ) && ( c2 <= 'f' )); + + if ( !(bIsNum1 || bIsAlpha1) || !(bIsNum2 || bIsAlpha2) ) + break; + + buf.EatWhiteSpace(); + *pEndGet = buf.TellGet(); + ++nByteCount; + } + buf.SeekGet( CUtlBuffer::SEEK_HEAD, nStartGet ); + return nByteCount; +} + +inline static unsigned char HexCharToInt( int c1 ) +{ + if (( c1 >= '0' ) && ( c1 <= '9' )) + return c1 - '0'; + + if (( c1 >= 'A' ) && ( c1 <= 'F' )) + return 10 + c1 - 'A'; + + if (( c1 >= 'a' ) && ( c1 <= 'f' )) + return 10 + c1 - 'a'; + + return 0xFF; +} + +bool Unserialize( CUtlBuffer &buf, CUtlBinaryBlock &dest ) +{ + if ( !buf.IsText() ) + { + int nLen = buf.GetInt( ); + dest.SetLength( nLen ); + if ( dest.Length() != 0 ) + { + buf.Get( dest.Get(), dest.Length() ); + } + + if ( nLen != dest.Length() ) + { + buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nLen - dest.Length() ); + return false; + } + + return buf.IsValid(); + } + + int nEndGet; + int nByteCount = CountBinaryBytes( buf, &nEndGet ); + if ( nByteCount < 0 ) + return false; + + buf.EatWhiteSpace(); + int nDest = 0; + dest.SetLength( nByteCount ); + while( buf.TellGet() < nEndGet ) + { + char c1 = buf.GetChar(); + char c2 = buf.GetChar(); + + unsigned char b1 = HexCharToInt( c1 ); + unsigned char b2 = HexCharToInt( c2 ); + if ( b1 == 0xFF || b2 == 0xFF ) + return false; + + dest[ nDest++ ] = b2 | ( b1 << 4 ); + buf.EatWhiteSpace(); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// String attribute +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const CUtlString &src ) +{ + buf.PutDelimitedString( s_pConv, src.Get() ); + return buf.IsValid(); +} + +bool Unserialize( CUtlBuffer &buf, CUtlString &dest ) +{ + int nLen = buf.PeekDelimitedStringLength( s_pConv ); + dest.SetLength( nLen - 1 ); // -1 because the length returned includes space for \0 + buf.GetDelimitedString( s_pConv, dest.GetForModify(), nLen ); + return buf.IsValid(); +} + + + + diff --git a/tier1/utlstring.cpp b/tier1/utlstring.cpp new file mode 100644 index 00000000..300766b9 --- /dev/null +++ b/tier1/utlstring.cpp @@ -0,0 +1,767 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#define __STDC_LIMIT_MACROS +#include + +#include "tier1/utlstring.h" +#include "tier1/strtools.h" +#include + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Simple string class. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Either allocates or reallocates memory to the length +// +// Allocated space for length characters. It automatically adds space for the +// nul and the cached length at the start of the memory block. Will adjust +// m_pString and explicitly set the nul at the end before returning. +void *CUtlString::AllocMemory( uint32 length ) +{ + void *pMemoryBlock; + if ( m_pString ) + { + pMemoryBlock = realloc( m_pString, length + 1 ); + } + else + { + pMemoryBlock = malloc( length + 1 ); + } + m_pString = (char*)pMemoryBlock; + m_pString[ length ] = 0; + + return pMemoryBlock; +} + +//----------------------------------------------------------------------------- +void CUtlString::SetDirect( const char *pValue, int nChars ) +{ + if ( pValue && nChars > 0 ) + { + if ( pValue == m_pString ) + { + AssertMsg( nChars == Q_strlen(m_pString), "CUtlString::SetDirect does not support resizing strings in place." ); + return; // Do nothing. Realloc in AllocMemory might move pValue's location resulting in a bad memcpy. + } + + Assert( nChars <= Min( strnlen(pValue, nChars) + 1, nChars ) ); + AllocMemory( nChars ); + Q_memcpy( m_pString, pValue, nChars ); + } + else + { + Purge(); + } + +} + + +void CUtlString::Set( const char *pValue ) +{ + int length = pValue ? V_strlen( pValue ) : 0; + SetDirect( pValue, length ); +} + +// Sets the length (used to serialize into the buffer ) +void CUtlString::SetLength( int nLen ) +{ + if ( nLen > 0 ) + { +#ifdef _DEBUG + int prevLen = m_pString ? Length() : 0; +#endif + AllocMemory( nLen ); +#ifdef _DEBUG + if ( nLen > prevLen ) + { + V_memset( m_pString + prevLen, 0xEB, nLen - prevLen ); + } +#endif + } + else + { + Purge(); + } +} + +const char *CUtlString::Get( ) const +{ + if (!m_pString) + { + return ""; + } + return m_pString; +} + +char *CUtlString::GetForModify() +{ + if ( !m_pString ) + { + // In general, we optimise away small mallocs for empty strings + // but if you ask for the non-const bytes, they must be writable + // so we can't return "" here, like we do for the const version - jd + void *pMemoryBlock = malloc( 1 ); + m_pString = (char *)pMemoryBlock; + *m_pString = 0; + } + + return m_pString; +} + +char CUtlString::operator[]( int i ) const +{ + if ( !m_pString ) + return '\0'; + + if ( i >= Length() ) + { + return '\0'; + } + + return m_pString[i]; +} + +void CUtlString::Clear() +{ + Purge(); +} + +void CUtlString::Purge() +{ + free( m_pString ); + m_pString = NULL; +} + +bool CUtlString::IsEqual_CaseSensitive( const char *src ) const +{ + if ( !src ) + { + return (Length() == 0); + } + return ( V_strcmp( Get(), src ) == 0 ); +} + +bool CUtlString::IsEqual_CaseInsensitive( const char *src ) const +{ + if ( !src ) + { + return (Length() == 0); + } + return ( V_stricmp( Get(), src ) == 0 ); +} + + +void CUtlString::ToLower() +{ + if ( !m_pString ) + { + return; + } + + V_strlower( m_pString ); +} + +void CUtlString::ToUpper() +{ + if ( !m_pString ) + { + return; + } + + V_strupr( m_pString ); +} + +CUtlString &CUtlString::operator=( const CUtlString &src ) +{ + SetDirect( src.Get(), src.Length() ); + return *this; +} + +CUtlString &CUtlString::operator=( const char *src ) +{ + Set( src ); + return *this; +} + +bool CUtlString::operator==( const CUtlString &src ) const +{ + if ( IsEmpty() ) + { + if ( src.IsEmpty() ) + { + return true; + } + + return false; + } + else + { + if ( src.IsEmpty() ) + { + return false; + } + + return Q_strcmp( m_pString, src.m_pString ) == 0; + } +} + +CUtlString &CUtlString::operator+=( const CUtlString &rhs ) +{ + const int lhsLength( Length() ); + const int rhsLength( rhs.Length() ); + + if (!rhsLength) + { + return *this; + } + + const int requestedLength( lhsLength + rhsLength ); + + AllocMemory( requestedLength ); + Q_memcpy( m_pString + lhsLength, rhs.m_pString, rhsLength ); + + return *this; +} + +CUtlString &CUtlString::operator+=( const char *rhs ) +{ + const int lhsLength( Length() ); + const int rhsLength( V_strlen( rhs ) ); + const int requestedLength( lhsLength + rhsLength ); + + if (!requestedLength) + { + return *this; + } + + AllocMemory( requestedLength ); + Q_memcpy( m_pString + lhsLength, rhs, rhsLength ); + + return *this; +} + +CUtlString &CUtlString::operator+=( char c ) +{ + const int lhsLength( Length() ); + + AllocMemory( lhsLength + 1 ); + m_pString[ lhsLength ] = c; + + return *this; +} + +CUtlString &CUtlString::operator+=( int rhs ) +{ + Assert( sizeof( rhs ) == 4 ); + + char tmpBuf[ 12 ]; // Sufficient for a signed 32 bit integer [ -2147483648 to +2147483647 ] + V_snprintf( tmpBuf, sizeof( tmpBuf ), "%d", rhs ); + tmpBuf[ sizeof( tmpBuf ) - 1 ] = '\0'; + + return operator+=( tmpBuf ); +} + +CUtlString &CUtlString::operator+=( double rhs ) +{ + char tmpBuf[ 256 ]; // How big can doubles be??? Dunno. + V_snprintf( tmpBuf, sizeof( tmpBuf ), "%lg", rhs ); + tmpBuf[ sizeof( tmpBuf ) - 1 ] = '\0'; + + return operator+=( tmpBuf ); +} + +bool CUtlString::MatchesPattern( const CUtlString &Pattern, int nFlags ) const +{ + const char *pszSource = String(); + const char *pszPattern = Pattern.String(); + bool bExact = true; + + while( 1 ) + { + if ( ( *pszPattern ) == 0 ) + { + return ( (*pszSource ) == 0 ); + } + + if ( ( *pszPattern ) == '*' ) + { + pszPattern++; + + if ( ( *pszPattern ) == 0 ) + { + return true; + } + + bExact = false; + continue; + } + + int nLength = 0; + + while( ( *pszPattern ) != '*' && ( *pszPattern ) != 0 ) + { + nLength++; + pszPattern++; + } + + while( 1 ) + { + const char *pszStartPattern = pszPattern - nLength; + const char *pszSearch = pszSource; + + for( int i = 0; i < nLength; i++, pszSearch++, pszStartPattern++ ) + { + if ( ( *pszSearch ) == 0 ) + { + return false; + } + + if ( ( *pszSearch ) != ( *pszStartPattern ) ) + { + break; + } + } + + if ( pszSearch - pszSource == nLength ) + { + break; + } + + if ( bExact == true ) + { + return false; + } + + if ( ( nFlags & PATTERN_DIRECTORY ) != 0 ) + { + if ( ( *pszPattern ) != '/' && ( *pszSource ) == '/' ) + { + return false; + } + } + + pszSource++; + } + + pszSource += nLength; + } +} + + +int CUtlString::Format( const char *pFormat, ... ) +{ + va_list marker; + + va_start( marker, pFormat ); + int len = FormatV( pFormat, marker ); + va_end( marker ); + + return len; +} + +//-------------------------------------------------------------------------------------------------- +// This can be called from functions that take varargs. +//-------------------------------------------------------------------------------------------------- + +int CUtlString::FormatV( const char *pFormat, va_list marker ) +{ + char tmpBuf[ 4096 ]; //< Nice big 4k buffer, as much memory as my first computer had, a Radio Shack Color Computer + + //va_start( marker, pFormat ); + int len = V_vsprintf_safe( tmpBuf, pFormat, marker ); + //va_end( marker ); + Set( tmpBuf ); + return len; +} + +//----------------------------------------------------------------------------- +// Strips the trailing slash +//----------------------------------------------------------------------------- +void CUtlString::StripTrailingSlash() +{ + if ( IsEmpty() ) + return; + + int nLastChar = Length() - 1; + char c = m_pString[ nLastChar ]; + if ( c == '\\' || c == '/' ) + { + SetLength( nLastChar ); + } +} + +void CUtlString::FixSlashes( char cSeparator/*=CORRECT_PATH_SEPARATOR*/ ) +{ + if ( m_pString ) + { + V_FixSlashes( m_pString, cSeparator ); + } +} + +//----------------------------------------------------------------------------- +// Trim functions +//----------------------------------------------------------------------------- +void CUtlString::TrimLeft( char cTarget ) +{ + int nIndex = 0; + + if ( IsEmpty() ) + { + return; + } + + while( m_pString[nIndex] == cTarget ) + { + ++nIndex; + } + + // We have some whitespace to remove + if ( nIndex > 0 ) + { + memcpy( m_pString, &m_pString[nIndex], Length() - nIndex ); + SetLength( Length() - nIndex ); + } +} + + +void CUtlString::TrimLeft( const char *szTargets ) +{ + int i; + + if ( IsEmpty() ) + { + return; + } + + for( i = 0; m_pString[i] != 0; i++ ) + { + bool bWhitespace = false; + + for( int j = 0; szTargets[j] != 0; j++ ) + { + if ( m_pString[i] == szTargets[j] ) + { + bWhitespace = true; + break; + } + } + + if ( !bWhitespace ) + { + break; + } + } + + // We have some whitespace to remove + if ( i > 0 ) + { + memcpy( m_pString, &m_pString[i], Length() - i ); + SetLength( Length() - i ); + } +} + + +void CUtlString::TrimRight( char cTarget ) +{ + const int nLastCharIndex = Length() - 1; + int nIndex = nLastCharIndex; + + while ( nIndex >= 0 && m_pString[nIndex] == cTarget ) + { + --nIndex; + } + + // We have some whitespace to remove + if ( nIndex < nLastCharIndex ) + { + m_pString[nIndex + 1] = 0; + SetLength( nIndex + 2 ); + } +} + + +void CUtlString::TrimRight( const char *szTargets ) +{ + const int nLastCharIndex = Length() - 1; + int i; + + for( i = nLastCharIndex; i > 0; i-- ) + { + bool bWhitespace = false; + + for( int j = 0; szTargets[j] != 0; j++ ) + { + if ( m_pString[i] == szTargets[j] ) + { + bWhitespace = true; + break; + } + } + + if ( !bWhitespace ) + { + break; + } + } + + // We have some whitespace to remove + if ( i < nLastCharIndex ) + { + m_pString[i + 1] = 0; + SetLength( i + 2 ); + } +} + + +void CUtlString::Trim( char cTarget ) +{ + TrimLeft( cTarget ); + TrimRight( cTarget ); +} + + +void CUtlString::Trim( const char *szTargets ) +{ + TrimLeft( szTargets ); + TrimRight( szTargets ); +} + + +CUtlString CUtlString::Slice( int32 nStart, int32 nEnd ) const +{ + int length = Length(); + if ( length == 0 ) + { + return CUtlString(); + } + + if ( nStart < 0 ) + nStart = length - (-nStart % length); + else if ( nStart >= length ) + nStart = length; + + if ( nEnd == INT32_MAX ) + nEnd = length; + else if ( nEnd < 0 ) + nEnd = length - (-nEnd % length); + else if ( nEnd >= length ) + nEnd = length; + + if ( nStart >= nEnd ) + return CUtlString(); + + const char *pIn = String(); + + CUtlString ret; + ret.SetDirect( pIn + nStart, nEnd - nStart ); + return ret; +} + +// Grab a substring starting from the left or the right side. +CUtlString CUtlString::Left( int32 nChars ) const +{ + return Slice( 0, nChars ); +} + +CUtlString CUtlString::Right( int32 nChars ) const +{ + return Slice( -nChars ); +} + +CUtlString CUtlString::Replace( char cFrom, char cTo ) const +{ + if (!m_pString) + { + return CUtlString(); + } + + CUtlString ret = *this; + int len = ret.Length(); + for ( int i=0; i < len; i++ ) + { + if ( ret.m_pString[i] == cFrom ) + ret.m_pString[i] = cTo; + } + + return ret; +} + +CUtlString CUtlString::Replace( const char *pszFrom, const char *pszTo ) const +{ + Assert( pszTo ); // Can be 0 length, but not null + Assert( pszFrom && *pszFrom ); // Must be valid and have one character. + + + const char *pos = V_strstr( String(), pszFrom ); + if ( !pos ) + { + return *this; + } + + const char *pFirstFound = pos; + + // count number of search string + int nSearchCount = 0; + int nSearchLength = V_strlen( pszFrom ); + while ( pos ) + { + nSearchCount++; + int nSrcOffset = ( pos - String() ) + nSearchLength; + pos = V_strstr( String() + nSrcOffset, pszFrom ); + } + + // allocate the new string + int nReplaceLength = V_strlen( pszTo ); + int nAllocOffset = nSearchCount * ( nReplaceLength - nSearchLength ); + size_t srcLength = Length(); + CUtlString strDest; + size_t destLength = srcLength + nAllocOffset; + strDest.SetLength( destLength ); + + // find and replace the search string + pos = pFirstFound; + int nDestOffset = 0; + int nSrcOffset = 0; + while ( pos ) + { + // Found an instance + int nCurrentSearchOffset = pos - String(); + int nCopyLength = nCurrentSearchOffset - nSrcOffset; + V_strncpy( strDest.GetForModify() + nDestOffset, String() + nSrcOffset, nCopyLength + 1 ); + nDestOffset += nCopyLength; + V_strncpy( strDest.GetForModify() + nDestOffset, pszTo, nReplaceLength + 1 ); + nDestOffset += nReplaceLength; + + nSrcOffset = nCurrentSearchOffset + nSearchLength; + pos = V_strstr( String() + nSrcOffset, pszFrom ); + } + + // making sure that the left over string from the source is the same size as the left over dest buffer + Assert( destLength - nDestOffset == srcLength - nSrcOffset ); + if ( destLength - nDestOffset > 0 ) + { + V_strncpy( strDest.GetForModify() + nDestOffset, String() + nSrcOffset, destLength - nDestOffset + 1 ); + } + + return strDest; +} + +CUtlString CUtlString::AbsPath( const char *pStartingDir ) const +{ + char szNew[MAX_PATH]; + V_MakeAbsolutePath( szNew, sizeof( szNew ), this->String(), pStartingDir ); + return CUtlString( szNew ); +} + +CUtlString CUtlString::UnqualifiedFilename() const +{ + const char *pFilename = V_UnqualifiedFileName( this->String() ); + return CUtlString( pFilename ); +} + +CUtlString CUtlString::DirName() const +{ + CUtlString ret( this->String() ); + V_StripLastDir( (char*)ret.Get(), ret.Length() + 1 ); + V_StripTrailingSlash( (char*)ret.Get() ); + return ret; +} + +CUtlString CUtlString::StripExtension() const +{ + char szTemp[MAX_PATH]; + V_StripExtension( String(), szTemp, sizeof( szTemp ) ); + return CUtlString( szTemp ); +} + +CUtlString CUtlString::StripFilename() const +{ + const char *pFilename = V_UnqualifiedFileName( Get() ); // NOTE: returns 'Get()' on failure, never NULL + int nCharsToCopy = pFilename - Get(); + CUtlString result; + result.SetDirect( Get(), nCharsToCopy ); + result.StripTrailingSlash(); + return result; +} + +CUtlString CUtlString::GetBaseFilename() const +{ + char szTemp[MAX_PATH]; + V_FileBase( String(), szTemp, sizeof( szTemp ) ); + return CUtlString( szTemp ); +} + +CUtlString CUtlString::GetExtension() const +{ + char szTemp[MAX_PATH]; + V_ExtractFileExtension( String(), szTemp, sizeof( szTemp ) ); + return CUtlString( szTemp ); +} + + +CUtlString CUtlString::PathJoin( const char *pStr1, const char *pStr2 ) +{ + char szPath[MAX_PATH]; + V_ComposeFileName( pStr1, pStr2, szPath, sizeof( szPath ) ); + return CUtlString( szPath ); +} + +CUtlString CUtlString::operator+( const char *pOther ) const +{ + CUtlString s = *this; + s += pOther; + return s; +} + +CUtlString CUtlString::operator+( const CUtlString &other ) const +{ + CUtlString s = *this; + s += other; + return s; +} + +CUtlString CUtlString::operator+( int rhs ) const +{ + CUtlString ret = *this; + ret += rhs; + return ret; +} + +//----------------------------------------------------------------------------- +// Purpose: concatenate the provided string to our current content +//----------------------------------------------------------------------------- +void CUtlString::Append( const char *pchAddition ) +{ + (*this) += pchAddition; +} + +void CUtlString::Append( const char *pchAddition, int nChars ) +{ + nChars = Min( nChars, V_strlen( pchAddition ) ); + + const int lhsLength( Length() ); + const int rhsLength( nChars ); + const int requestedLength( lhsLength + rhsLength ); + + AllocMemory( requestedLength ); + const int allocatedLength( requestedLength ); + const int copyLength( allocatedLength - lhsLength < rhsLength ? allocatedLength - lhsLength : rhsLength ); + memcpy( GetForModify() + lhsLength, pchAddition, copyLength ); + m_pString[ allocatedLength ] = '\0'; +} + +// Shared static empty string. +const CUtlString &CUtlString::GetEmptyString() +{ + static const CUtlString s_emptyString; + + return s_emptyString; +} diff --git a/tier1/utlsymbol.cpp b/tier1/utlsymbol.cpp new file mode 100644 index 00000000..385ccaf9 --- /dev/null +++ b/tier1/utlsymbol.cpp @@ -0,0 +1,397 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines a symbol table +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifdef _MSC_VER +#pragma warning (disable:4514) +#endif + +#include "utlsymbol.h" +#include "KeyValues.h" +#include "tier0/threadtools.h" +#include "tier0/memdbgon.h" +#include "stringpool.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define INVALID_STRING_INDEX CStringPoolIndex( 0xFFFF, 0xFFFF ) + +#define MIN_STRING_POOL_SIZE 2048 + +//----------------------------------------------------------------------------- +// globals +//----------------------------------------------------------------------------- + +CUtlSymbolTableMT* CUtlSymbol::s_pSymbolTable = 0; +bool CUtlSymbol::s_bAllowStaticSymbolTable = true; + + +//----------------------------------------------------------------------------- +// symbol methods +//----------------------------------------------------------------------------- + +void CUtlSymbol::Initialize() +{ + // If this assert fails, then the module that this call is in has chosen to disallow + // use of the static symbol table. Usually, it's to prevent confusion because it's easy + // to accidentally use the global symbol table when you really want to use a specific one. + Assert( s_bAllowStaticSymbolTable ); + + // necessary to allow us to create global symbols + static bool symbolsInitialized = false; + if (!symbolsInitialized) + { + s_pSymbolTable = new CUtlSymbolTableMT; + symbolsInitialized = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Singleton to delete table on exit from module +//----------------------------------------------------------------------------- +class CCleanupUtlSymbolTable +{ +public: + ~CCleanupUtlSymbolTable() + { + delete CUtlSymbol::s_pSymbolTable; + CUtlSymbol::s_pSymbolTable = NULL; + } +}; + +static CCleanupUtlSymbolTable g_CleanupSymbolTable; + +CUtlSymbolTableMT* CUtlSymbol::CurrTable() +{ + Initialize(); + return s_pSymbolTable; +} + + +//----------------------------------------------------------------------------- +// string->symbol->string +//----------------------------------------------------------------------------- + +CUtlSymbol::CUtlSymbol( const char* pStr ) +{ + m_Id = CurrTable()->AddString( pStr ); +} + +const char* CUtlSymbol::String( ) const +{ + return CurrTable()->String(m_Id); +} + +void CUtlSymbol::DisableStaticSymbolTable() +{ + s_bAllowStaticSymbolTable = false; +} + +//----------------------------------------------------------------------------- +// checks if the symbol matches a string +//----------------------------------------------------------------------------- + +bool CUtlSymbol::operator==( const char* pStr ) const +{ + if (m_Id == UTL_INVAL_SYMBOL) + return false; + return strcmp( String(), pStr ) == 0; +} + + + +//----------------------------------------------------------------------------- +// symbol table stuff +//----------------------------------------------------------------------------- + +inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const +{ + Assert( index.m_iPool < m_StringPools.Count() ); + Assert( index.m_iOffset < m_StringPools[index.m_iPool]->m_TotalLen ); + + return &m_StringPools[index.m_iPool]->m_Data[index.m_iOffset]; +} + + +bool CUtlSymbolTable::CLess::operator()( const CStringPoolIndex &i1, const CStringPoolIndex &i2 ) const +{ + // Need to do pointer math because CUtlSymbolTable is used in CUtlVectors, and hence + // can be arbitrarily moved in memory on a realloc. Yes, this is portable. In reality, + // right now at least, because m_LessFunc is the first member of CUtlRBTree, and m_Lookup + // is the first member of CUtlSymbolTabke, this == pTable + CUtlSymbolTable *pTable = (CUtlSymbolTable *)( (byte *)this - offsetof(CUtlSymbolTable::CTree, m_LessFunc) ) - offsetof(CUtlSymbolTable, m_Lookup ); + const char* str1 = (i1 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString : + pTable->StringFromIndex( i1 ); + const char* str2 = (i2 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString : + pTable->StringFromIndex( i2 ); + + if ( !pTable->m_bInsensitive ) + return strcmp( str1, str2 ) < 0; + else + return strcmpi( str1, str2 ) < 0; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CUtlSymbolTable::CUtlSymbolTable( int growSize, int initSize, bool caseInsensitive ) : + m_Lookup( growSize, initSize ), m_bInsensitive( caseInsensitive ), m_StringPools( 8 ) +{ +} + +CUtlSymbolTable::~CUtlSymbolTable() +{ + // Release the stringpool string data + RemoveAll(); +} + + +CUtlSymbol CUtlSymbolTable::Find( const char* pString ) const +{ + if (!pString) + return CUtlSymbol(); + + // Store a special context used to help with insertion + m_pUserSearchString = pString; + + // Passing this special invalid symbol makes the comparison function + // use the string passed in the context + UtlSymId_t idx = m_Lookup.Find( INVALID_STRING_INDEX ); + +#ifdef _DEBUG + m_pUserSearchString = NULL; +#endif + + return CUtlSymbol( idx ); +} + + +int CUtlSymbolTable::FindPoolWithSpace( int len ) const +{ + for ( int i=0; i < m_StringPools.Count(); i++ ) + { + StringPool_t *pPool = m_StringPools[i]; + + if ( (pPool->m_TotalLen - pPool->m_SpaceUsed) >= len ) + { + return i; + } + } + + return -1; +} + + +//----------------------------------------------------------------------------- +// Finds and/or creates a symbol based on the string +//----------------------------------------------------------------------------- + +CUtlSymbol CUtlSymbolTable::AddString( const char* pString ) +{ + if (!pString) + return CUtlSymbol( UTL_INVAL_SYMBOL ); + + CUtlSymbol id = Find( pString ); + + if (id.IsValid()) + return id; + + int len = strlen(pString) + 1; + + // Find a pool with space for this string, or allocate a new one. + int iPool = FindPoolWithSpace( len ); + if ( iPool == -1 ) + { + // Add a new pool. + int newPoolSize = MAX( len, MIN_STRING_POOL_SIZE ); + StringPool_t *pPool = (StringPool_t*)malloc( sizeof( StringPool_t ) + newPoolSize - 1 ); + pPool->m_TotalLen = newPoolSize; + pPool->m_SpaceUsed = 0; + iPool = m_StringPools.AddToTail( pPool ); + } + + // Copy the string in. + StringPool_t *pPool = m_StringPools[iPool]; + Assert( pPool->m_SpaceUsed < 0xFFFF ); // This should never happen, because if we had a string > 64k, it + // would have been given its entire own pool. + + unsigned short iStringOffset = pPool->m_SpaceUsed; + + memcpy( &pPool->m_Data[pPool->m_SpaceUsed], pString, len ); + pPool->m_SpaceUsed += len; + + // didn't find, insert the string into the vector. + CStringPoolIndex index; + index.m_iPool = iPool; + index.m_iOffset = iStringOffset; + + UtlSymId_t idx = m_Lookup.Insert( index ); + return CUtlSymbol( idx ); +} + + +//----------------------------------------------------------------------------- +// Look up the string associated with a particular symbol +//----------------------------------------------------------------------------- + +const char* CUtlSymbolTable::String( CUtlSymbol id ) const +{ + if (!id.IsValid()) + return ""; + + Assert( m_Lookup.IsValidIndex((UtlSymId_t)id) ); + return StringFromIndex( m_Lookup[id] ); +} + + +//----------------------------------------------------------------------------- +// Remove all symbols in the table. +//----------------------------------------------------------------------------- + +void CUtlSymbolTable::RemoveAll() +{ + m_Lookup.Purge(); + + for ( int i=0; i < m_StringPools.Count(); i++ ) + free( m_StringPools[i] ); + + m_StringPools.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pFileName - +// Output : FileNameHandle_t +//----------------------------------------------------------------------------- +FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( const char *pFileName ) +{ + if ( !pFileName ) + { + return NULL; + } + + // find first + FileNameHandle_t hFileName = FindFileName( pFileName ); + if ( hFileName ) + { + return hFileName; + } + + // Fix slashes+dotslashes and make lower case first.. + char fn[ MAX_PATH ]; + Q_strncpy( fn, pFileName, sizeof( fn ) ); + Q_RemoveDotSlashes( fn ); +#ifdef _WIN32 + strlwr( fn ); +#endif + + // Split the filename into constituent parts + char basepath[ MAX_PATH ]; + Q_ExtractFilePath( fn, basepath, sizeof( basepath ) ); + char filename[ MAX_PATH ]; + Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) ); + + // not found, lock and look again + FileNameHandleInternal_t handle; + m_lock.LockForWrite(); + handle.path = m_StringPool.FindStringHandle( basepath ); + handle.file = m_StringPool.FindStringHandle( filename ); + if ( handle.path && handle.file ) + { + // found + m_lock.UnlockWrite(); + return *( FileNameHandle_t * )( &handle ); + } + + // safely add it + handle.path = m_StringPool.ReferenceStringHandle( basepath ); + handle.file = m_StringPool.ReferenceStringHandle( filename ); + m_lock.UnlockWrite(); + + return *( FileNameHandle_t * )( &handle ); +} + +FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( const char *pFileName ) +{ + if ( !pFileName ) + { + return NULL; + } + + // Fix slashes+dotslashes and make lower case first.. + char fn[ MAX_PATH ]; + Q_strncpy( fn, pFileName, sizeof( fn ) ); + Q_RemoveDotSlashes( fn ); +#ifdef _WIN32 + strlwr( fn ); +#endif + + // Split the filename into constituent parts + char basepath[ MAX_PATH ]; + Q_ExtractFilePath( fn, basepath, sizeof( basepath ) ); + char filename[ MAX_PATH ]; + Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) ); + + FileNameHandleInternal_t handle; + + m_lock.LockForRead(); + handle.path = m_StringPool.FindStringHandle(basepath); + handle.file = m_StringPool.FindStringHandle(filename); + m_lock.UnlockRead(); + + if ( handle.path == 0 || handle.file == 0 ) + return NULL; + + return *( FileNameHandle_t * )( &handle ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handle - +// Output : const char +//----------------------------------------------------------------------------- +bool CUtlFilenameSymbolTable::String( const FileNameHandle_t& handle, char *buf, int buflen ) +{ + buf[ 0 ] = 0; + + FileNameHandleInternal_t *internal = ( FileNameHandleInternal_t * )&handle; + if ( !internal ) + { + return false; + } + + m_lock.LockForRead(); + const char *path = m_StringPool.HandleToString(internal->path); + const char *fn = m_StringPool.HandleToString(internal->file); + m_lock.UnlockRead(); + + if ( !path || !fn ) + { + return false; + } + + Q_strncpy( buf, path, buflen ); + Q_strncat( buf, fn, buflen, COPY_ALL_CHARACTERS ); + + return true; +} + +void CUtlFilenameSymbolTable::RemoveAll() +{ + m_StringPool.FreeAll(); +} + +void CUtlFilenameSymbolTable::SpewStrings() +{ + m_lock.LockForRead(); + m_StringPool.SpewStrings(); + m_lock.UnlockRead(); + +}