diff --git a/lib/public/tier1.lib b/lib/public/tier1.lib index 4edd9816..1f875845 100644 Binary files a/lib/public/tier1.lib and b/lib/public/tier1.lib differ diff --git a/public/tier0/memalloc.h b/public/tier0/memalloc.h index e64f6688..2840d095 100644 --- a/public/tier0/memalloc.h +++ b/public/tier0/memalloc.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: This header should never be used directly from leaf code!!! // Instead, just add the file memoverride.cpp into your project and all this @@ -14,25 +14,31 @@ #pragma once #endif -#if !defined(NO_MALLOC_OVERRIDE) && defined(POSIX) -#define NO_MALLOC_OVERRIDE -#endif - +// These memory debugging switches aren't relevant under Linux builds since memoverride.cpp +// isn't built into Linux projects +#ifndef POSIX // Define this in release to get memory tracking even in release builds //#define USE_MEM_DEBUG 1 +#endif #if defined( _MEMTEST ) +#ifdef _WIN32 #define USE_MEM_DEBUG 1 #endif +#endif // Undefine this if using a compiler lacking threadsafe RTTI (like vc6) #define MEM_DEBUG_CLASSNAME 1 -#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) - #include +#if defined( OSX ) +#include +#endif + #include "tier0/mem.h" +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + struct _CrtMemState; #define MEMALLOC_VERSION 1 @@ -107,7 +113,17 @@ public: #endif // Returns 0 if no failure, otherwise the size_t of the last requested chunk + // "I'm sure this is completely thread safe!" Brian Deen 7/19/2012. virtual size_t MemoryAllocFailed() = 0; + + // handles storing allocation info for coroutines + virtual uint32 GetDebugInfoSize() = 0; + virtual void SaveDebugInfo( void *pvDebugInfo ) = 0; + virtual void RestoreDebugInfo( const void *pvDebugInfo ) = 0; + virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) = 0; + + // Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system + virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) = 0; }; //----------------------------------------------------------------------------- @@ -117,11 +133,52 @@ MEM_INTERFACE IMemAlloc *g_pMemAlloc; //----------------------------------------------------------------------------- +#ifdef MEMALLOC_REGIONS +#ifndef MEMALLOC_REGION +#define MEMALLOC_REGION 0 +#endif +inline void *MemAlloc_Alloc( size_t nSize ) +{ + return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize ); +} + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize, pFileName, nLine ); +} +#else +#undef MEMALLOC_REGION +inline void *MemAlloc_Alloc( size_t nSize ) +{ + return g_pMemAlloc->Alloc( nSize ); +} + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Alloc( nSize, pFileName, nLine ); +} +#endif +inline void MemAlloc_Free( void *ptr ) +{ + g_pMemAlloc->Free( ptr ); +} +inline void MemAlloc_Free( void *ptr, const char *pFileName, int nLine ) +{ + g_pMemAlloc->Free( ptr, pFileName, nLine ); +} + +//----------------------------------------------------------------------------- + +inline bool ValueIsPowerOfTwo( size_t value ) // don't clash with mathlib definition +{ + return (value & ( value - 1 )) == 0; +} + inline void *MemAlloc_AllocAligned( size_t size, size_t align ) { unsigned char *pAlloc, *pResult; - if (!IsPowerOfTwo(uint(align))) + if (!IsPowerOfTwo(align)) return NULL; align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; @@ -139,7 +196,7 @@ inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFi { unsigned char *pAlloc, *pResult; - if (!IsPowerOfTwo(uint(align))) + if (!IsPowerOfTwo(align)) return NULL; align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; @@ -153,9 +210,45 @@ inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFi return (void *)pResult; } +inline void *MemAlloc_AllocAlignedUnattributed( size_t size, size_t align ) +{ + unsigned char *pAlloc, *pResult; + + if (!ValueIsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +inline void *MemAlloc_AllocAlignedFileLine( size_t size, size_t align, const char *pszFile, int nLine ) +{ + unsigned char *pAlloc, *pResult; + + if (!ValueIsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size, pszFile, nLine ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + inline void *MemAlloc_ReallocAligned( void *ptr, size_t size, size_t align ) { - if ( !IsPowerOfTwo( uint(align) ) ) + if ( !IsPowerOfTwo( align ) ) return NULL; // Don't change alignment between allocation + reallocation. @@ -201,6 +294,23 @@ inline void MemAlloc_FreeAligned( void *pMemBlock ) g_pMemAlloc->Free( pAlloc ); } +inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pFileName, int nLine ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *( (void **)pAlloc ); + g_pMemAlloc->Free( pAlloc, pFileName, nLine ); +} + inline size_t MemAlloc_GetSizeAligned( void *pMemBlock ) { void *pAlloc; @@ -234,6 +344,20 @@ inline size_t MemAlloc_GetSizeAligned( void *pMemBlock ) #define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) #endif +#define MemAlloc_DumpStats() g_pMemAlloc->DumpStats() +#define MemAlloc_CompactHeap() g_pMemAlloc->CompactHeap() +#define MemAlloc_OutOfMemory() g_pMemAlloc->OutOfMemory() +#define MemAlloc_CompactIncremental() g_pMemAlloc->CompactIncremental() +#define MemAlloc_DumpStatsFileBase( _filename ) g_pMemAlloc->DumpStatsFileBase( _filename ) +#define MemAlloc_CrtCheckMemory() g_pMemAlloc->CrtCheckMemory() +#define MemAlloc_GlobalMemoryStatus( _usedMemory, _freeMemory ) g_pMemAlloc->GlobalMemoryStatus( _usedMemory, _freeMemory ) +#define MemAlloc_MemoryAllocFailed() g_pMemAlloc->MemoryAllocFailed() + +#define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize() +#define MemAlloc_SaveDebugInfo( pvDebugInfo ) g_pMemAlloc->SaveDebugInfo( pvDebugInfo ) +#define MemAlloc_RestoreDebugInfo( pvDebugInfo ) g_pMemAlloc->RestoreDebugInfo( pvDebugInfo ) +#define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) g_pMemAlloc->InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) +#define MemAlloc_GetSize( x ) g_pMemAlloc->GetSize( x ); //----------------------------------------------------------------------------- class CMemAllocAttributeAlloction @@ -339,6 +463,51 @@ struct MemAllocFileLine_t //----------------------------------------------------------------------------- +#elif defined( POSIX ) + +#if defined( OSX ) +// Mac always aligns allocs, don't need to call posix_memalign which doesn't exist in 10.5.8 which TF2 still needs to run on +//inline void *memalign(size_t alignment, size_t size) {void *pTmp=NULL; posix_memalign(&pTmp, alignment, size); return pTmp;} +inline void *memalign(size_t alignment, size_t size) {void *pTmp=NULL; pTmp = malloc(size); return pTmp;} +#endif + +inline void *_aligned_malloc( size_t nSize, size_t align ) { return memalign( align, nSize ); } +inline void _aligned_free( void *ptr ) { free( ptr ); } + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName = NULL, int nLine = 0 ) { return malloc( nSize ); } +inline void MemAlloc_Free( void *ptr, const char *pFileName = NULL, int nLine = 0 ) { free( ptr ); } + +inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFile = NULL, int nLine = 0 ) { return memalign( align, size ); } +inline void *MemAlloc_AllocAlignedFileLine( size_t size, size_t align, const char *pszFile = NULL, int nLine = 0 ) { return memalign( align, size ); } +inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pszFile = NULL, int nLine = 0 ) { free( pMemBlock ); } + +#if defined( OSX ) +inline size_t _msize( void *ptr ) { return malloc_size( ptr ); } +#else +inline size_t _msize( void *ptr ) { return malloc_usable_size( ptr ); } +#endif + +inline void *MemAlloc_ReallocAligned( void *ptr, size_t size, size_t align ) +{ + void *ptr_new_aligned = memalign( align, size ); + + if( ptr_new_aligned ) + { + size_t old_size = _msize( ptr ); + size_t copy_size = ( size < old_size ) ? size : old_size; + + memcpy( ptr_new_aligned, ptr, copy_size ); + free( ptr ); + } + + return ptr_new_aligned; +} +#else +#define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize() +#define MemAlloc_SaveDebugInfo( pvDebugInfo ) g_pMemAlloc->SaveDebugInfo( pvDebugInfo ) +#define MemAlloc_RestoreDebugInfo( pvDebugInfo ) g_pMemAlloc->RestoreDebugInfo( pvDebugInfo ) +#define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) g_pMemAlloc->InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) + #endif // !STEAM && !NO_MALLOC_OVERRIDE //----------------------------------------------------------------------------- @@ -347,12 +516,139 @@ struct MemAllocFileLine_t #define MEM_ALLOC_CREDIT_(tag) ((void)0) #define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__) +#define MEM_ALLOC_CREDIT_FUNCTION() #define MEM_ALLOC_CREDIT_CLASS() #define MEM_ALLOC_CLASSNAME(type) NULL -#define MEM_ALLOC_CREDIT_FUNCTION() + +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) +#define MemAlloc_PopAllocDbgInfo() +#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#define MemAlloc_DumpStats() ((void)0) +#define MemAlloc_CompactHeap() ((void)0) +#define MemAlloc_OutOfMemory() ((void)0) +#define MemAlloc_CompactIncremental() ((void)0) +#define MemAlloc_DumpStatsFileBase( _filename ) ((void)0) +inline bool MemAlloc_CrtCheckMemory() { return true; } +inline void MemAlloc_GlobalMemoryStatus( size_t *pusedMemory, size_t *pfreeMemory ) +{ + *pusedMemory = 0; + *pfreeMemory = 0; +} +#define MemAlloc_MemoryAllocFailed() 0 + +#define MemAlloc_GetDebugInfoSize() 0 +#define MemAlloc_SaveDebugInfo( pvDebugInfo ) ((void)0) +#define MemAlloc_RestoreDebugInfo( pvDebugInfo ) ((void)0) +#define MemAlloc_InitDebugInfo( pvDebugInfo, pchRootFileName, nLine ) ((void)0) + + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) #endif // !STEAM && NO_MALLOC_OVERRIDE //----------------------------------------------------------------------------- + + +// linux memory tracking via hooks. +#if defined( POSIX ) && !defined( NO_HOOK_MALLOC ) +PLATFORM_INTERFACE void MemoryLogMessage( char const *s ); // throw a message into the memory log +PLATFORM_INTERFACE void EnableMemoryLogging( bool bOnOff ); +PLATFORM_INTERFACE void DumpMemoryLog( int nThresh ); +PLATFORM_INTERFACE void DumpMemorySummary( void ); +PLATFORM_INTERFACE void SetMemoryMark( void ); +PLATFORM_INTERFACE void DumpChangedMemory( int nThresh ); + +#else +FORCEINLINE void MemoryLogMessage( char const *s ) +{ +} + +FORCEINLINE void EnableMemoryLogging( bool bOnOff ) +{ +} +FORCEINLINE void DumpMemoryLog( int nThresh ) +{ +} +FORCEINLINE void DumpMemorySummary( void ) +{ +} +FORCEINLINE void SetMemoryMark( void ) +{ +} +FORCEINLINE void DumpChangedMemory( int nThresh ) +{ +} + +#endif + +#ifdef POSIX +// ApproximateProcessMemoryUsage returns the approximate memory footprint of this process. +PLATFORM_INTERFACE size_t ApproximateProcessMemoryUsage( void ); +#else +FORCEINLINE size_t ApproximateProcessMemoryUsage( void ) +{ + return 0; +} + +#endif + +struct aligned_tmp_t +{ + // empty base class +}; + +/* +This class used to be required if you wanted an object to be allocated with a specific +alignment. ALIGN16 and ALIGN16_POST are not actually sufficient for this because they +guarantee that the globals, statics, locals, and function parameters are appropriately +aligned they do not affect memory allocation alignment. +However this class is usually not needed because as of 2012 our policy is that our +allocator should take care of this automatically. Any object whose size is a multiple +of 16 will be 16-byte aligned. Existing uses of this class were not changed because +the cost/benefit did not justify it. +*/ +// template here to allow adding alignment at levels of hierarchy that aren't the base +template< int bytesAlignment = 16, class T = aligned_tmp_t > +class CAlignedNewDelete : public T +{ + +public: + /* + Note that this class does not overload operator new[] and delete[] which means that + classes that depend on this for alignment may end up misaligned if an array is + allocated. This problem is now mostly theoretical because this class is mostly + obsolete. + */ + void *operator new( size_t nSize ) + { + return MemAlloc_AllocAligned( nSize, bytesAlignment ); + } + + void* operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine( nSize, bytesAlignment, pFileName, nLine ); + } + + void operator delete(void *pData) + { + if ( pData ) + { + MemAlloc_FreeAligned( pData ); + } + } + + void operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine ) + { + if ( pData ) + { + MemAlloc_FreeAligned( pData, pFileName, nLine ); + } + } +}; + + #endif /* TIER0_MEMALLOC_H */ diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 6f46723a..859ba16e 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -75,27 +75,58 @@ #ifdef _WIN32 #define IsLinux() false + #define IsOSX() false + #define IsPosix() false + #ifndef PLATFORM_WINDOWS + #define PLATFORM_WINDOWS 1 // Windows PC or Xbox 360 + #endif #ifndef _X360 + #define IsWindows() true #define IsPC() true #define IsConsole() false #define IsX360() false #define IsPS3() false #define IS_WINDOWS_PC - #else - #ifndef _CONSOLE - #define _CONSOLE + #define PLATFORM_WINDOWS_PC 1 // Windows PC + #ifdef _WIN64 + #define IsPlatformWindowsPC64() true + #define IsPlatformWindowsPC32() false + #define PLATFORM_WINDOWS_PC64 1 + #else + #define IsPlatformWindowsPC64() false + #define IsPlatformWindowsPC32() true + #define PLATFORM_WINDOWS_PC32 1 #endif + #else + #define PLATFORM_X360 1 + #ifndef _CONSOLE + #define _CONSOLE + #endif + #define IsWindows() false #define IsPC() false #define IsConsole() true #define IsX360() true #define IsPS3() false #endif -#elif defined(_LINUX) || defined(__APPLE__) +#elif defined(POSIX) #define IsPC() true + #define IsWindows() false #define IsConsole() false #define IsX360() false #define IsPS3() false - #define IsLinux() true + #if defined( LINUX ) + #define IsLinux() true + #else + #define IsLinux() false + #endif + + #if defined( OSX ) + #define IsOSX() true + #else + #define IsOSX() false + #endif + + #define IsPosix() true #else #error #endif diff --git a/public/tier1/KeyValues.h b/public/tier1/KeyValues.h index 7eb2f026..eb214b45 100644 --- a/public/tier1/KeyValues.h +++ b/public/tier1/KeyValues.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -25,10 +25,20 @@ #include "utlvector.h" #include "Color.h" +#define FOR_EACH_SUBKEY( kvRoot, kvSubKey ) \ + for ( KeyValues * kvSubKey = kvRoot->GetFirstSubKey(); kvSubKey != NULL; kvSubKey = kvSubKey->GetNextKey() ) + +#define FOR_EACH_TRUE_SUBKEY( kvRoot, kvSubKey ) \ + for ( KeyValues * kvSubKey = kvRoot->GetFirstTrueSubKey(); kvSubKey != NULL; kvSubKey = kvSubKey->GetNextTrueSubKey() ) + +#define FOR_EACH_VALUE( kvRoot, kvValue ) \ + for ( KeyValues * kvValue = kvRoot->GetFirstValue(); kvValue != NULL; kvValue = kvValue->GetNextValue() ) + class IBaseFileSystem; class CUtlBuffer; class Color; typedef void * FileHandle_t; +class CKeyValuesGrowableStringTable; //----------------------------------------------------------------------------- // Purpose: Simple recursive data access class @@ -39,7 +49,7 @@ typedef void * FileHandle_t; // About KeyValues Text File Format: // It has 3 control characters '{', '}' and '"'. Names and values may be quoted or -// not. The quote '"' charater must not be used within name or values, only for +// not. The quote '"' character must not be used within name or values, only for // quoting whole tokens. You may use escape sequences wile parsing and add within a // quoted token a \" to add quotes within your name or token. When using Escape // Sequence the parser must now that by setting KeyValues::UsesEscapeSequences( true ), @@ -49,11 +59,25 @@ typedef void * FileHandle_t; // with a closing bracket '}'. Subkeys use the same definitions recursively. // Whitespaces are space, return, newline and tabulator. Allowed Escape sequences // are \n, \t, \\, \n and \". The number character '#' is used for macro purposes -// (eg #include), don't use it as first charater in key names. +// (eg #include), don't use it as first character in key names. //----------------------------------------------------------------------------- class KeyValues { public: + // By default, the KeyValues class uses a string table for the key names that is + // limited to 4MB. The game will exit in error if this space is exhausted. In + // general this is preferable for game code for performance and memory fragmentation + // reasons. + // + // If this is not acceptable, you can use this call to switch to a table that can grow + // arbitrarily. This call must be made before any KeyValues objects are allocated or it + // will result in undefined behavior. If you use the growable string table, you cannot + // share KeyValues pointers directly with any other module. You can serialize them across + // module boundaries. These limitations are acceptable in the Steam backend code + // this option was written for, but may not be in other situations. Make sure to + // understand the implications before using this. + static void SetUseGrowableStringTable( bool bUseGrowableTable ); + KeyValues( const char *setName ); // @@ -68,8 +92,11 @@ public: { public: explicit inline AutoDelete( KeyValues *pKeyValues ) : m_pKeyValues( pKeyValues ) {} + explicit inline AutoDelete( const char *pchKVName ) : m_pKeyValues( new KeyValues( pchKVName ) ) {} inline ~AutoDelete( void ) { if( m_pKeyValues ) m_pKeyValues->deleteThis(); } inline void Assign( KeyValues *pKeyValues ) { m_pKeyValues = pKeyValues; } + KeyValues *operator->() { return m_pKeyValues; } + operator KeyValues *() { return m_pKeyValues; } private: AutoDelete( AutoDelete const &x ); // forbid AutoDelete & operator= ( AutoDelete const &x ); // forbid @@ -88,12 +115,13 @@ public: void SetName( const char *setName); // gets the name as a unique int - int GetNameSymbol() const; + int GetNameSymbol() const { return m_iKeyName; } // File access. Set UsesEscapeSequences true, if resource file/buffer uses Escape Sequences (eg \n, \t) void UsesEscapeSequences(bool state); // default false + void UsesConditionals(bool state); // default true bool LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL ); - bool SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL); + bool SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL, bool sortKeys = false, bool bAllowEmptyString = false ); // Read from a buffer... Note that the buffer must be null terminated bool LoadFromBuffer( char const *resourceName, const char *pBuffer, IBaseFileSystem* pFileSystem = NULL, const char *pPathID = NULL ); @@ -114,9 +142,10 @@ public: // NOTE: GetFirstSubKey/GetNextKey will iterate keys AND values. Use the functions // below if you want to iterate over just the keys or just the values. // - KeyValues *GetFirstSubKey(); // returns the first subkey in the list - KeyValues *GetNextKey(); // returns the next subkey + KeyValues *GetFirstSubKey() { return m_pSub; } // returns the first subkey in the list + KeyValues *GetNextKey() { return m_pPeer; } // returns the next subkey void SetNextKey( KeyValues * pDat); + KeyValues *FindLastSubKey(); // returns the LAST subkey in the list. This requires a linked list iteration to find the key. Returns NULL if we don't have any children // // These functions can be used to treat it like a true key/values tree instead of @@ -145,6 +174,7 @@ public: const char *GetString( const char *keyName = NULL, const char *defaultValue = "" ); const wchar_t *GetWString( const char *keyName = NULL, const wchar_t *defaultValue = L"" ); void *GetPtr( const char *keyName = NULL, void *defaultValue = (void*)0 ); + bool GetBool( const char *keyName = NULL, bool defaultValue = false, bool* optGotDefault = NULL ); Color GetColor( const char *keyName = NULL /* default value is all black */); bool IsEmpty(const char *keyName = NULL); @@ -165,6 +195,7 @@ public: void SetFloat( const char *keyName, float value ); void SetPtr( const char *keyName, void *value ); void SetColor( const char *keyName, Color value); + void SetBool( const char *keyName, bool value ) { SetInt( keyName, value ? 1 : 0 ); } // Memory allocation (optimized) void *operator new( size_t iAllocSize ); @@ -178,10 +209,10 @@ public: // in the one we're chained to. void ChainKeyValue( KeyValues* pChain ); - void RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel ); + void RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel, bool sortKeys = false, bool bAllowEmptyString = false ); bool WriteAsBinary( CUtlBuffer &buffer ); - bool ReadAsBinary( CUtlBuffer &buffer ); + bool ReadAsBinary( CUtlBuffer &buffer, int nStackDepth = 0 ); // Allocate & create a new copy of the keys KeyValues *MakeCopy( void ) const; @@ -213,11 +244,17 @@ public: void SetStringValue( char const *strValue ); // unpack a key values list into a structure - void UnpackIntoStructure( struct KeyValuesUnpackStructure const *pUnpackTable, void *pDest ); + void UnpackIntoStructure( struct KeyValuesUnpackStructure const *pUnpackTable, void *pDest, size_t DestSizeInBytes ); // Process conditional keys for widescreen support. bool ProcessResolutionKeys( const char *pResString ); + + // Dump keyvalues recursively into a dump context + bool Dump( class IKeyValuesDumpContext *pDump, int nIndentLevel = 0 ); + // Merge in another KeyValues, keeping "our" settings + void RecursiveMergeKeyValues( KeyValues *baseKV ); + private: KeyValues( KeyValues& ); // prevent copy constructor being used @@ -225,7 +262,14 @@ private: ~KeyValues(); KeyValues* CreateKey( const char *keyName ); - + + /// Create a child key, given that we know which child is currently the last child. + /// This avoids the O(N^2) behaviour when adding children in sequence to KV, + /// when CreateKey() wil have to re-locate the end of the list each time. This happens, + /// for example, every time we load any KV file whatsoever. + KeyValues* CreateKeyUsingKnownLastChild( const char *keyName, KeyValues *pLastChild ); + void AddSubkeyUsingKnownLastChild( KeyValues *pSubKey, KeyValues *pLastChild ); + void RecursiveCopyKeyValues( KeyValues& src ); void RemoveEverything(); // void RecursiveSaveToFile( IBaseFileSystem *filesystem, CUtlBuffer &buffer, int indentLevel ); @@ -233,7 +277,8 @@ private: // NOTE: If both filesystem and pBuf are non-null, it'll save to both of them. // If filesystem is null, it'll ignore f. - void RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel ); + void RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel, bool sortKeys, bool bAllowEmptyString ); + void SaveKeyToFile( KeyValues *dat, IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel, bool sortKeys, bool bAllowEmptyString ); void WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const char *pszString ); void RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf ); @@ -245,7 +290,6 @@ private: // For handling #base "filename" void MergeBaseKeys( CUtlVector< KeyValues * >& baseKeys ); - void RecursiveMergeKeyValues( KeyValues *baseKV ); // NOTE: If both filesystem and pBuf are non-null, it'll save to both of them. // If filesystem is null, it'll ignore f. @@ -275,13 +319,36 @@ private: char m_iDataType; char m_bHasEscapeSequences; // true, if while parsing this KeyValue, Escape Sequences are used (default false) - char unused[2]; + char m_bEvaluateConditionals; // true, if while parsing this KeyValue, conditionals blocks are evaluated (default true) + char unused[1]; KeyValues *m_pPeer; // pointer to next key in list KeyValues *m_pSub; // pointer to Start of a new sub key list KeyValues *m_pChain;// Search here if it's not in our list + +private: + // Statics to implement the optional growable string table + // Function pointers that will determine which mode we are in + static int (*s_pfGetSymbolForString)( const char *name, bool bCreate ); + static const char *(*s_pfGetStringForSymbol)( int symbol ); + static CKeyValuesGrowableStringTable *s_pGrowableStringTable; + +public: + // Functions that invoke the default behavior + static int GetSymbolForStringClassic( const char *name, bool bCreate = true ); + static const char *GetStringForSymbolClassic( int symbol ); + + // Functions that use the growable string table + static int GetSymbolForStringGrowable( const char *name, bool bCreate = true ); + static const char *GetStringForSymbolGrowable( int symbol ); + + // Functions to get external access to whichever of the above functions we're going to call. + static int CallGetSymbolForString( const char *name, bool bCreate = true ) { return s_pfGetSymbolForString( name, bCreate ); } + static const char *CallGetStringForSymbol( int symbol ) { return s_pfGetStringForSymbol( symbol ); } }; +typedef KeyValues::AutoDelete KeyValuesAD; + enum KeyValuesUnpackDestinationTypes_t { UNPACK_TYPE_FLOAT, // dest is a float @@ -354,4 +421,57 @@ inline bool KeyValues::IsEmpty( int keySymbol ) bool EvaluateConditional( const char *str ); +class CUtlSortVectorKeyValuesByName +{ +public: + bool Less( const KeyValues* lhs, const KeyValues* rhs, void * ) + { + return Q_stricmp( lhs->GetName(), rhs->GetName() ) < 0; + } +}; + +// +// KeyValuesDumpContext and generic implementations +// + +class IKeyValuesDumpContext +{ +public: + virtual bool KvBeginKey( KeyValues *pKey, int nIndentLevel ) = 0; + virtual bool KvWriteValue( KeyValues *pValue, int nIndentLevel ) = 0; + virtual bool KvEndKey( KeyValues *pKey, int nIndentLevel ) = 0; +}; + +class IKeyValuesDumpContextAsText : public IKeyValuesDumpContext +{ +public: + virtual bool KvBeginKey( KeyValues *pKey, int nIndentLevel ); + virtual bool KvWriteValue( KeyValues *pValue, int nIndentLevel ); + virtual bool KvEndKey( KeyValues *pKey, int nIndentLevel ); + +public: + virtual bool KvWriteIndent( int nIndentLevel ); + virtual bool KvWriteText( char const *szText ) = 0; +}; + +class CKeyValuesDumpContextAsDevMsg : public IKeyValuesDumpContextAsText +{ +public: + // Overrides developer level to dump in DevMsg, zero to dump as Msg + CKeyValuesDumpContextAsDevMsg( int nDeveloperLevel = 1 ) : m_nDeveloperLevel( nDeveloperLevel ) {} + +public: + virtual bool KvBeginKey( KeyValues *pKey, int nIndentLevel ); + virtual bool KvWriteText( char const *szText ); + +protected: + int m_nDeveloperLevel; +}; + +inline bool KeyValuesDumpAsDevMsg( KeyValues *pKeyValues, int nIndentLevel = 0, int nDeveloperLevel = 1 ) +{ + CKeyValuesDumpContextAsDevMsg ctx( nDeveloperLevel ); + return pKeyValues->Dump( &ctx, nIndentLevel ); +} + #endif // KEYVALUES_H diff --git a/public/tier1/strtools.h b/public/tier1/strtools.h index c11e35b4..b699c85e 100644 --- a/public/tier1/strtools.h +++ b/public/tier1/strtools.h @@ -110,6 +110,8 @@ int V_strcasecmp (const char *s1, const char *s2); int V_strncasecmp (const char *s1, const char *s2, int n); int V_strnicmp (const char *s1, const char *s2, int n); int V_atoi (const char *str); +int64 V_atoi64(const char *str); +uint64 V_atoui64(const char *str); float V_atof (const char *str); char* V_stristr( char* pStr, const char* pSearch ); const char* V_stristr( const char* pStr, const char* pSearch ); @@ -409,6 +411,8 @@ bool V_GenerateUniqueName( char *name, int memsize, const char *prefix, const Na #define Q_strncasecmp V_strncasecmp #define Q_strnicmp V_strnicmp #define Q_atoi V_atoi +#define Q_atoi64 V_atoi64 +#define Q_atoui64 V_atoui64 #define Q_atof V_atof #define Q_stristr V_stristr #define Q_strnistr V_strnistr diff --git a/public/tier1/utlbuffer.h b/public/tier1/utlbuffer.h index 151146ec..f4e7eef9 100644 --- a/public/tier1/utlbuffer.h +++ b/public/tier1/utlbuffer.h @@ -176,6 +176,7 @@ public: short GetShort( ); unsigned short GetUnsignedShort( ); int GetInt( ); + int64 GetInt64( ); int GetIntHex( ); unsigned int GetUnsignedInt( ); float GetFloat( ); @@ -249,6 +250,7 @@ public: void PutShort( short s ); void PutUnsignedShort( unsigned short us ); void PutInt( int i ); + void PutInt64( int64 i ); void PutUnsignedInt( unsigned int u ); void PutFloat( float f ); void PutDouble( double d ); @@ -700,6 +702,13 @@ inline int CUtlBuffer::GetInt( ) return i; } +inline int64 CUtlBuffer::GetInt64( ) +{ + int64 i; + GetType( i, "%lld" ); + return i; +} + inline int CUtlBuffer::GetIntHex( ) { int i; @@ -920,6 +929,11 @@ inline void CUtlBuffer::PutInt( int i ) PutType( i, "%d" ); } +inline void CUtlBuffer::PutInt64( int64 i ) +{ + PutType( i, "%llu" ); +} + inline void CUtlBuffer::PutUnsignedInt( unsigned int u ) { PutType( u, "%u" ); diff --git a/tier1/KeyValues.cpp b/tier1/KeyValues.cpp index 561bc649..897d53c0 100644 --- a/tier1/KeyValues.cpp +++ b/tier1/KeyValues.cpp @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -8,9 +8,11 @@ #if defined( _WIN32 ) && !defined( _X360 ) #include // for WideCharToMultiByte and MultiByteToWideChar -#elif defined( _LINUX ) || defined( __APPLE__ ) +#elif defined(POSIX) #include // wcslen() #define _alloca alloca +#define _wtoi(arg) wcstol(arg, NULL, 10) +#define _wtoi64(arg) wcstoll(arg, NULL, 10) #endif #include @@ -23,13 +25,32 @@ #include "tier0/mem.h" #include "utlvector.h" #include "utlbuffer.h" +#include "utlhash.h" +#include "UtlSortVector.h" +#include "convar.h" // memdbgon must be the last include file in a .cpp file!!! #include +template +T *KVStringAlloc(size_t nLength) +{ + return reinterpret_cast(MemAlloc_Alloc(sizeof(T) * nLength)); +} + +void KVStringDelete(void* pMem) +{ + MemAlloc_Free(pMem); +} + static const char * s_LastFileLoadingFrom = "unknown"; // just needed for error messages -#define KEYVALUES_TOKEN_SIZE 1024 +// Statics for the growable string table +int (*KeyValues::s_pfGetSymbolForString)( const char *name, bool bCreate ) = &KeyValues::GetSymbolForStringClassic; +const char *(*KeyValues::s_pfGetStringForSymbol)( int symbol ) = &KeyValues::GetStringForSymbolClassic; +CKeyValuesGrowableStringTable *KeyValues::s_pGrowableStringTable = NULL; + +#define KEYVALUES_TOKEN_SIZE 4096 static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; @@ -72,7 +93,8 @@ public: // Allows you to keep the same stack level, but change the name as you parse peers void Reset( int stackLevel, int symName ) { - Assert( stackLevel >= 0 && stackLevel < m_errorIndex ); + Assert( stackLevel >= 0 ); + Assert( stackLevel < m_errorIndex ); m_errorStack[stackLevel] = symName; } @@ -86,11 +108,11 @@ public: { if ( i < m_errorIndex ) { - Warning( "%s, ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) ); + Warning( "%s, ", KeyValues::CallGetStringForSymbol(m_errorStack[i]) ); } else { - Warning( "(*%s*), ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) ); + Warning( "(*%s*), ", KeyValues::CallGetStringForSymbol(m_errorStack[i]) ); } } } @@ -197,6 +219,155 @@ static CLeakTrack track; #endif + +//----------------------------------------------------------------------------- +// Purpose: An arbitrarily growable string table for KeyValues key names. +// See the comment in the header for more info. +//----------------------------------------------------------------------------- +class CKeyValuesGrowableStringTable +{ +public: + // Constructor + CKeyValuesGrowableStringTable() : + #ifdef PLATFORM_64BITS + m_vecStrings( 0, 4 * 512 * 1024 ) + #else + m_vecStrings( 0, 512 * 1024 ) + #endif + , m_hashLookup( 2048, 0, 0, m_Functor, m_Functor ) + { + m_vecStrings.AddToTail( '\0' ); + } + + // Translates a string to an index + int GetSymbolForString( const char *name, bool bCreate = true ) + { + AUTO_LOCK( m_mutex ); + + // Put the current details into our hash functor + m_Functor.SetCurString( name ); + m_Functor.SetCurStringBase( (const char *)m_vecStrings.Base() ); + + if ( bCreate ) + { + bool bInserted = false; + UtlHashHandle_t hElement = m_hashLookup.Insert( -1, &bInserted ); + if ( bInserted ) + { + int iIndex = m_vecStrings.AddMultipleToTail( V_strlen( name ) + 1, name ); + m_hashLookup[ hElement ] = iIndex; + } + + return m_hashLookup[ hElement ]; + } + else + { + UtlHashHandle_t hElement = m_hashLookup.Find( -1 ); + if ( m_hashLookup.IsValidHandle( hElement ) ) + return m_hashLookup[ hElement ]; + else + return -1; + } + } + + // Translates an index back to a string + const char *GetStringForSymbol( int symbol ) + { + return (const char *)m_vecStrings.Base() + symbol; + } + +private: + + // A class plugged into CUtlHash that allows us to change the behavior of the table + // and store only the index in the table. + class CLookupFunctor + { + public: + CLookupFunctor() : m_pchCurString( NULL ), m_pchCurBase( NULL ) {} + + // Sets what we are currently inserting or looking for. + void SetCurString( const char *pchCurString ) { m_pchCurString = pchCurString; } + void SetCurStringBase( const char *pchCurBase ) { m_pchCurBase = pchCurBase; } + + // The compare function. + bool operator()( int nLhs, int nRhs ) const + { + const char *pchLhs = nLhs > 0 ? m_pchCurBase + nLhs : m_pchCurString; + const char *pchRhs = nRhs > 0 ? m_pchCurBase + nRhs : m_pchCurString; + + return ( 0 == V_stricmp( pchLhs, pchRhs ) ); + } + + // The hash function. + unsigned int operator()( int nItem ) const + { + return HashStringCaseless( m_pchCurString ); + } + + private: + const char *m_pchCurString; + const char *m_pchCurBase; + }; + + CThreadFastMutex m_mutex; + CLookupFunctor m_Functor; + CUtlHash m_hashLookup; + CUtlVector m_vecStrings; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Sets whether the KeyValues system should use an arbitrarily growable +// string table. See the comment in the header for more info. +//----------------------------------------------------------------------------- +void KeyValues::SetUseGrowableStringTable( bool bUseGrowableTable ) +{ + if ( bUseGrowableTable ) + { + s_pfGetStringForSymbol = &(KeyValues::GetStringForSymbolGrowable); + s_pfGetSymbolForString = &(KeyValues::GetSymbolForStringGrowable); + + if ( NULL == s_pGrowableStringTable ) + { + s_pGrowableStringTable = new CKeyValuesGrowableStringTable; + } + } + else + { + s_pfGetStringForSymbol = &(KeyValues::GetStringForSymbolClassic); + s_pfGetSymbolForString = &(KeyValues::GetSymbolForStringClassic); + + delete s_pGrowableStringTable; + s_pGrowableStringTable = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Bodys of the function pointers used for interacting with the key +// name string table +//----------------------------------------------------------------------------- +int KeyValues::GetSymbolForStringClassic( const char *name, bool bCreate ) +{ + return KeyValuesSystem()->GetSymbolForString( name, bCreate ); +} + +const char *KeyValues::GetStringForSymbolClassic( int symbol ) +{ + return KeyValuesSystem()->GetStringForSymbol( symbol ); +} + +int KeyValues::GetSymbolForStringGrowable( const char *name, bool bCreate ) +{ + return s_pGrowableStringTable->GetSymbolForString( name, bCreate ); +} + +const char *KeyValues::GetStringForSymbolGrowable( int symbol ) +{ + return s_pGrowableStringTable->GetStringForSymbol( symbol ); +} + + + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- @@ -287,6 +458,7 @@ void KeyValues::Init() m_pValue = NULL; m_bHasEscapeSequences = false; + m_bEvaluateConditionals = true; // for future proof memset( unused, 0, sizeof(unused) ); @@ -323,9 +495,9 @@ void KeyValues::RemoveEverything() delete dat; } - delete [] m_sValue; + KVStringDelete(m_sValue); m_sValue = NULL; - delete [] m_wsValue; + KVStringDelete(m_wsValue); m_wsValue = NULL; } @@ -334,9 +506,9 @@ void KeyValues::RemoveEverything() // Input : *f - //----------------------------------------------------------------------------- -void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel ) +void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/ ) { - RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel ); + RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel, sortKeys, bAllowEmptyString ); } //----------------------------------------------------------------------------- @@ -354,22 +526,13 @@ void KeyValues::ChainKeyValue( KeyValues* pChain ) //----------------------------------------------------------------------------- const char *KeyValues::GetName( void ) const { - return KeyValuesSystem()->GetStringForSymbol(m_iKeyName); + return s_pfGetStringForSymbol( m_iKeyName ); } -//----------------------------------------------------------------------------- -// Purpose: Get the symbol name of the current key section -//----------------------------------------------------------------------------- -int KeyValues::GetNameSymbol() const -{ - return m_iKeyName; -} - - //----------------------------------------------------------------------------- // Purpose: Read a single token from buffer (0 terminated) //----------------------------------------------------------------------------- -#ifdef _MSC_VER +#ifdef _WIN32 #pragma warning (disable:4706) #endif const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasConditional ) @@ -418,7 +581,7 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon bool bReportedError = false; bool bConditionalStart = false; int nCount = 0; - while ( (c = (const char*)buf.PeekGet( sizeof(char), 0 )) ) + while ( ( c = (const char*)buf.PeekGet( sizeof(char), 0 ) ) ) { // end of file if ( *c == 0 ) @@ -455,9 +618,10 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon s_pTokenBuf[ nCount ] = 0; return s_pTokenBuf; } -#ifdef _MSC_VER +#ifdef _WIN32 #pragma warning (default:4706) #endif + //----------------------------------------------------------------------------- @@ -469,16 +633,24 @@ void KeyValues::UsesEscapeSequences(bool state) } +//----------------------------------------------------------------------------- +// Purpose: if parser should evaluate conditional blocks ( [$WINDOWS] etc. ) +//----------------------------------------------------------------------------- +void KeyValues::UsesConditionals(bool state) +{ + m_bEvaluateConditionals = state; +} + + //----------------------------------------------------------------------------- // Purpose: Load keyValues from disk //----------------------------------------------------------------------------- bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID ) { Assert(filesystem); -#ifndef _LINUX +#ifdef WIN32 Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) ); #endif - FileHandle_t f = filesystem->Open(resourceName, "rb", pathID); if ( !f ) return false; @@ -487,7 +659,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN // load file into a null-terminated buffer int fileSize = filesystem->Size( f ); - unsigned bufSize = ((IFileSystem *)filesystem)->GetOptimalReadSize( f, fileSize + 1 ); + unsigned bufSize = ((IFileSystem *)filesystem)->GetOptimalReadSize( f, fileSize + 2 ); char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize ); Assert( buffer ); @@ -500,6 +672,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN if ( bRetOK ) { buffer[fileSize] = 0; // null terminate file as EOF + buffer[fileSize+1] = 0; // double NULL terminating in case this is a unicode file bRetOK = LoadFromBuffer( resourceName, buffer, filesystem ); } @@ -512,7 +685,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN // 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 ) +bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/ ) { // create a write file FileHandle_t f = filesystem->Open(resourceName, "wb", pathID); @@ -524,7 +697,7 @@ bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceNam return false; } - RecursiveSaveToFile(filesystem, f, NULL, 0); + RecursiveSaveToFile(filesystem, f, NULL, 0, sortKeys, bAllowEmptyString ); filesystem->Close(f); return true; @@ -567,7 +740,7 @@ void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t j++; } - INTERNALWRITE(convertedString, strlen(convertedString)); + INTERNALWRITE(convertedString, Q_strlen(convertedString)); } @@ -582,14 +755,13 @@ void KeyValues::InternalWrite( IBaseFileSystem *filesystem, FileHandle_t f, CUtl { 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 ) +void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel, bool sortKeys, bool bAllowEmptyString ) { // write header WriteIndents( filesystem, f, pBuf, indentLevel ); @@ -600,118 +772,140 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f INTERNALWRITE("{\n", 2); // loop through all our keys writing them to disk - for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer ) + if ( sortKeys ) { - if ( dat->m_pSub ) + CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedKeys; + + for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer ) { - dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1 ); + vecSortedKeys.InsertNoSort(dat); } - else + vecSortedKeys.RedoSort(); + + FOR_EACH_VEC( vecSortedKeys, i ) { - // 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%016I64X", *( (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; - } + SaveKeyToFile( vecSortedKeys[i], filesystem, f, pBuf, indentLevel, sortKeys, bAllowEmptyString ); } } + else + { + for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer ) + SaveKeyToFile( dat, filesystem, f, pBuf, indentLevel, sortKeys, bAllowEmptyString ); + } // write tail WriteIndents(filesystem, f, pBuf, indentLevel); INTERNALWRITE("}\n", 2); } +void KeyValues::SaveKeyToFile( KeyValues *dat, IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel, bool sortKeys, bool bAllowEmptyString ) +{ + if ( dat->m_pSub ) + { + dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1, sortKeys, bAllowEmptyString ); + } + else + { + // only write non-empty keys + + switch (dat->m_iDataType) + { + case TYPE_STRING: + { + if ( dat->m_sValue && ( bAllowEmptyString || *(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: + { + if ( dat->m_wsValue ) + { + static char buf[KEYVALUES_TOKEN_SIZE]; + // make sure we have enough space + int result = Q_UnicodeToUTF8( dat->m_wsValue, buf, KEYVALUES_TOKEN_SIZE); + 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); + } + } + 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 +#ifdef WIN32 + Q_snprintf( buf, sizeof( buf ), "0x%016I64X", *( (uint64 *)dat->m_sValue ) ); +#else + Q_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) ); +#endif + + 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; + } + } +} + //----------------------------------------------------------------------------- // Purpose: looks up a key by symbol name //----------------------------------------------------------------------------- @@ -752,7 +946,8 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) } // lookup the symbol for the search string - HKeySymbol iSearchStr = KeyValuesSystem()->GetSymbolForString( searchStr, bCreate ); + HKeySymbol iSearchStr = s_pfGetSymbolForString( searchStr, bCreate ); + if ( iSearchStr == INVALID_KEY_SYMBOL ) { // not found, couldn't possibly be in key value list @@ -787,6 +982,9 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) dat = new KeyValues( searchStr ); // Assert(dat != NULL); + dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent + dat->UsesConditionals( m_bEvaluateConditionals != 0 ); + // insert new key at end of list if (lastItem) { @@ -828,6 +1026,7 @@ KeyValues *KeyValues::CreateNewKey() int newID = 1; // search for any key with higher values + KeyValues *pLastChild = NULL; for (KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer) { // case-insensitive string compare @@ -836,12 +1035,14 @@ KeyValues *KeyValues::CreateNewKey() { newID = val + 1; } + + pLastChild = dat; } char buf[12]; Q_snprintf( buf, sizeof(buf), "%d", newID ); - return CreateKey( buf ); + return CreateKeyUsingKnownLastChild( buf, pLastChild ); } @@ -850,17 +1051,57 @@ KeyValues *KeyValues::CreateNewKey() //----------------------------------------------------------------------------- KeyValues* KeyValues::CreateKey( const char *keyName ) { - // key wasn't found so just create a new one + KeyValues *pLastChild = FindLastSubKey(); + return CreateKeyUsingKnownLastChild( keyName, pLastChild ); +} + +//----------------------------------------------------------------------------- +KeyValues* KeyValues::CreateKeyUsingKnownLastChild( const char *keyName, KeyValues *pLastChild ) +{ + // Create a new key KeyValues* dat = new KeyValues( keyName ); dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does + dat->UsesConditionals( m_bEvaluateConditionals != 0 ); // add into subkey list - AddSubKey( dat ); + AddSubkeyUsingKnownLastChild( dat, pLastChild ); return dat; } +//----------------------------------------------------------------------------- +void KeyValues::AddSubkeyUsingKnownLastChild( KeyValues *pSubkey, KeyValues *pLastChild ) +{ + // Make sure the subkey isn't a child of some other keyvalues + Assert( pSubkey != NULL ); + Assert( pSubkey->m_pPeer == NULL ); + + // Empty child list? + if ( pLastChild == NULL ) + { + Assert( m_pSub == NULL ); + m_pSub = pSubkey; + } + else + { + Assert( m_pSub != NULL ); + Assert( pLastChild->m_pPeer == NULL ); + +// // In debug, make sure that they really do know which child is the last one +// #ifdef _DEBUG +// KeyValues *pTempDat = m_pSub; +// while ( pTempDat->GetNextKey() != NULL ) +// { +// pTempDat = pTempDat->GetNextKey(); +// } +// Assert( pTempDat == pLastChild ); +// #endif + + pLastChild->SetNextKey( pSubkey ); + } +} + //----------------------------------------------------------------------------- // Adds a subkey. Make sure the subkey isn't a child of some other keyvalues @@ -868,6 +1109,7 @@ KeyValues* KeyValues::CreateKey( const char *keyName ) void KeyValues::AddSubKey( KeyValues *pSubkey ) { // Make sure the subkey isn't a child of some other keyvalues + Assert( pSubkey != NULL ); Assert( pSubkey->m_pPeer == NULL ); // add into subkey list @@ -924,19 +1166,20 @@ void KeyValues::RemoveSubKey(KeyValues *subKey) //----------------------------------------------------------------------------- -// Purpose: Return the first subkey in the list +// Purpose: Locate last child. Returns NULL if we have no children //----------------------------------------------------------------------------- -KeyValues *KeyValues::GetFirstSubKey() +KeyValues *KeyValues::FindLastSubKey() { - return m_pSub; -} -//----------------------------------------------------------------------------- -// Purpose: Return the next subkey -//----------------------------------------------------------------------------- -KeyValues *KeyValues::GetNextKey() -{ - return m_pPeer; + // No children? + if ( m_pSub == NULL ) + return NULL; + + // Scan for the last one + KeyValues *pLastChild = m_pSub; + while ( pLastChild->m_pPeer ) + pLastChild = pLastChild->m_pPeer; + return pLastChild; } //----------------------------------------------------------------------------- @@ -999,12 +1242,7 @@ int KeyValues::GetInt( const char *keyName, int defaultValue ) 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: @@ -1032,14 +1270,9 @@ uint64 KeyValues::GetUint64( const char *keyName, uint64 defaultValue ) switch ( dat->m_iDataType ) { case TYPE_STRING: - return atoi(dat->m_sValue); + return (uint64)Q_atoi64(dat->m_sValue); case TYPE_WSTRING: -#ifdef _WIN32 - return _wtoi(dat->m_wsValue); -#else - AssertFatal( 0 ); - return 0; -#endif + return _wtoi64(dat->m_wsValue); case TYPE_FLOAT: return (int)dat->m_flValue; case TYPE_UINT64: @@ -1093,11 +1326,11 @@ float KeyValues::GetFloat( const char *keyName, float defaultValue ) case TYPE_STRING: return (float)atof(dat->m_sValue); case TYPE_WSTRING: -#ifdef _WIN32 +#ifdef WIN32 return (float) _wtof(dat->m_wsValue); // no wtof #else - Assert(0); - return 0.; + Assert( !"impl me" ); + return 0.0; #endif case TYPE_FLOAT: return dat->m_flValue; @@ -1130,22 +1363,24 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue Q_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue ); SetString( keyName, buf ); break; - case TYPE_INT: case TYPE_PTR: + Q_snprintf( buf, sizeof( buf ), "%lld", (int64)(size_t)dat->m_pValue ); + SetString( keyName, buf ); + break; + case TYPE_INT: Q_snprintf( buf, sizeof( buf ), "%d", dat->m_iValue ); SetString( keyName, buf ); break; case TYPE_UINT64: - Q_snprintf( buf, sizeof( buf ), "%I64i", *((uint64 *)(dat->m_sValue)) ); + Q_snprintf( buf, sizeof( buf ), "%lld", *((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); + int result = Q_UnicodeToUTF8(dat->m_wsValue, wideBuf, 512); if ( result ) { // note: this will copy wideBuf @@ -1155,7 +1390,6 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue { return defaultValue; } -#endif break; } case TYPE_STRING: @@ -1169,9 +1403,9 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue return defaultValue; } + const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaultValue) { -#ifdef _WIN32 KeyValues *dat = FindKey( keyName, false ); if ( dat ) { @@ -1179,17 +1413,20 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul switch ( dat->m_iDataType ) { case TYPE_FLOAT: - swprintf(wbuf, L"%f", dat->m_flValue); + swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%f", dat->m_flValue); SetWString( keyName, wbuf); break; - case TYPE_INT: case TYPE_PTR: - swprintf( wbuf, L"%d", dat->m_iValue ); + swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%lld", (int64)(size_t)dat->m_pValue ); + SetWString( keyName, wbuf ); + break; + case TYPE_INT: + swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%d", dat->m_iValue ); SetWString( keyName, wbuf ); break; case TYPE_UINT64: { - swprintf( wbuf, L"%I64i", *((uint64 *)(dat->m_sValue)) ); + swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%lld", *((uint64 *)(dat->m_sValue)) ); SetWString( keyName, wbuf ); } break; @@ -1198,16 +1435,19 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul 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 ) + int bufSize = Q_strlen(dat->m_sValue) + 1; + wchar_t *pWBuf = KVStringAlloc( bufSize ); + int result = Q_UTF8ToUnicode(dat->m_sValue, pWBuf, bufSize * sizeof( wchar_t ) ); + if ( result >= 0 ) // may be a zero length string { - SetWString( keyName, wbuftemp); + SetWString( keyName, pWBuf); } else { + KVStringDelete(pWBuf); return defaultValue; } + KVStringDelete(pWBuf); break; } default: @@ -1216,9 +1456,24 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul return (const wchar_t* )dat->m_wsValue; } -#else - DevMsg("TODO: implement wide char functions\n"); -#endif + return defaultValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Get a bool interpretation of the key. +//----------------------------------------------------------------------------- +bool KeyValues::GetBool( const char *keyName, bool defaultValue, bool* optGotDefault ) +{ + if ( FindKey( keyName ) ) + { + if ( optGotDefault ) + (*optGotDefault) = false; + return 0 != GetInt( keyName, 0 ); + } + + if ( optGotDefault ) + (*optGotDefault) = true; + return defaultValue; } @@ -1240,7 +1495,7 @@ Color KeyValues::GetColor( const char *keyName ) } else if ( dat->m_iDataType == TYPE_FLOAT ) { - color[0] = (unsigned char)dat->m_flValue; + color[0] = dat->m_flValue; } else if ( dat->m_iDataType == TYPE_INT ) { @@ -1249,7 +1504,7 @@ Color KeyValues::GetColor( const char *keyName ) else if ( dat->m_iDataType == TYPE_STRING ) { // parse the colors out of the string - float a, b, c, d; + float a = 0.0f, b = 0.0f, c = 0.0f, d = 0.0f; sscanf(dat->m_sValue, "%f %f %f %f", &a, &b, &c, &d); color[0] = (unsigned char)a; color[1] = (unsigned char)b; @@ -1280,9 +1535,9 @@ void KeyValues::SetColor( const char *keyName, Color value) void KeyValues::SetStringValue( char const *strValue ) { // delete the old value - delete [] m_sValue; + KVStringDelete(m_sValue); // make sure we're not storing the WSTRING - as we're converting over to STRING - delete [] m_wsValue; + KVStringDelete(m_wsValue); m_wsValue = NULL; if (!strValue) @@ -1293,7 +1548,7 @@ void KeyValues::SetStringValue( char const *strValue ) // allocate memory for the new value and copy it in int len = Q_strlen( strValue ); - m_sValue = new char[len + 1]; + m_sValue = KVStringAlloc(len + 1); Q_memcpy( m_sValue, strValue, len+1 ); m_iDataType = TYPE_STRING; @@ -1308,10 +1563,15 @@ void KeyValues::SetString( const char *keyName, const char *value ) if ( dat ) { + if ( dat->m_iDataType == TYPE_STRING && dat->m_sValue == value ) + { + return; + } + // delete the old value - delete [] dat->m_sValue; + KVStringDelete(dat->m_sValue); // make sure we're not storing the WSTRING - as we're converting over to STRING - delete [] dat->m_wsValue; + KVStringDelete(dat->m_wsValue); dat->m_wsValue = NULL; if (!value) @@ -1322,7 +1582,7 @@ void KeyValues::SetString( const char *keyName, const char *value ) // allocate memory for the new value and copy it in int len = Q_strlen( value ); - dat->m_sValue = new char[len + 1]; + dat->m_sValue = KVStringAlloc(len + 1); Q_memcpy( dat->m_sValue, value, len+1 ); dat->m_iDataType = TYPE_STRING; @@ -1338,9 +1598,9 @@ void KeyValues::SetWString( const char *keyName, const wchar_t *value ) if ( dat ) { // delete the old value - delete [] dat->m_wsValue; + KVStringDelete(dat->m_wsValue); // make sure we're not storing the STRING - as we're converting over to WSTRING - delete [] dat->m_sValue; + KVStringDelete(dat->m_sValue); dat->m_sValue = NULL; if (!value) @@ -1350,8 +1610,8 @@ void KeyValues::SetWString( const char *keyName, const wchar_t *value ) } // allocate memory for the new value and copy it in - int len = wcslen( value ); - dat->m_wsValue = new wchar_t[len + 1]; + int len = Q_wcslen( value ); + dat->m_wsValue = KVStringAlloc(len + 1); Q_memcpy( dat->m_wsValue, value, (len+1) * sizeof(wchar_t) ); dat->m_iDataType = TYPE_WSTRING; @@ -1382,12 +1642,12 @@ void KeyValues::SetUint64( const char *keyName, uint64 value ) if ( dat ) { // delete the old value - delete [] dat->m_sValue; + KVStringDelete(dat->m_sValue); // make sure we're not storing the WSTRING - as we're converting over to STRING - delete [] dat->m_wsValue; + KVStringDelete(dat->m_wsValue); dat->m_wsValue = NULL; - dat->m_sValue = new char[sizeof(uint64)]; + dat->m_sValue = KVStringAlloc(sizeof(uint64)); *((uint64 *)dat->m_sValue) = value; dat->m_iDataType = TYPE_UINT64; } @@ -1409,7 +1669,7 @@ void KeyValues::SetFloat( const char *keyName, float value ) void KeyValues::SetName( const char * setName ) { - m_iKeyName = KeyValuesSystem()->GetSymbolForString( setName ); + m_iKeyName = s_pfGetSymbolForString( setName, true ); } //----------------------------------------------------------------------------- @@ -1444,7 +1704,7 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) if( src.m_sValue ) { int len = Q_strlen(src.m_sValue) + 1; - m_sValue = new char[len]; + m_sValue = KVStringAlloc(len); Q_strncpy( m_sValue, src.m_sValue, len ); } break; @@ -1453,7 +1713,7 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) m_iValue = src.m_iValue; Q_snprintf( buf,sizeof(buf), "%d", m_iValue ); int len = Q_strlen(buf) + 1; - m_sValue = new char[len]; + m_sValue = KVStringAlloc(len); Q_strncpy( m_sValue, buf, len ); } break; @@ -1462,7 +1722,7 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) m_flValue = src.m_flValue; Q_snprintf( buf,sizeof(buf), "%f", m_flValue ); int len = Q_strlen(buf) + 1; - m_sValue = new char[len]; + m_sValue = KVStringAlloc(len); Q_strncpy( m_sValue, buf, len ); } break; @@ -1473,7 +1733,7 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) break; case TYPE_UINT64: { - m_sValue = new char[sizeof(uint64)]; + m_sValue = KVStringAlloc(sizeof(uint64)); Q_memcpy( m_sValue, src.m_sValue, sizeof(uint64) ); } break; @@ -1574,6 +1834,9 @@ KeyValues *KeyValues::MakeCopy( void ) const { KeyValues *newKeyValue = new KeyValues(GetName()); + newKeyValue->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); + newKeyValue->UsesConditionals( m_bEvaluateConditionals != 0 ); + // copy data newKeyValue->m_iDataType = m_iDataType; switch ( m_iDataType ) @@ -1584,7 +1847,7 @@ KeyValues *KeyValues::MakeCopy( void ) const { int len = Q_strlen( m_sValue ); Assert( !newKeyValue->m_sValue ); - newKeyValue->m_sValue = new char[len + 1]; + newKeyValue->m_sValue = KVStringAlloc(len + 1); Q_memcpy( newKeyValue->m_sValue, m_sValue, len+1 ); } } @@ -1593,8 +1856,8 @@ KeyValues *KeyValues::MakeCopy( void ) const { if ( m_wsValue ) { - int len = wcslen( m_wsValue ); - newKeyValue->m_wsValue = new wchar_t[len+1]; + int len = Q_wcslen( m_wsValue ); + newKeyValue->m_wsValue = KVStringAlloc(len + 1); Q_memcpy( newKeyValue->m_wsValue, m_wsValue, (len+1)*sizeof(wchar_t)); } } @@ -1620,7 +1883,7 @@ KeyValues *KeyValues::MakeCopy( void ) const break; case TYPE_UINT64: - newKeyValue->m_sValue = new char[sizeof(uint64)]; + newKeyValue->m_sValue = KVStringAlloc(sizeof(uint64)); Q_memcpy( newKeyValue->m_sValue, m_sValue, sizeof(uint64) ); break; }; @@ -1683,14 +1946,13 @@ void KeyValues::deleteThis() void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) { // Append any included keys, too... + KeyValues *insertSpot = this; int includeCount = includedKeys.Count(); - int i; - for ( i = 0; i < includeCount; i++ ) + for ( int i = 0; i < includeCount; i++ ) { KeyValues *kv = includedKeys[ i ]; Assert( kv ); - KeyValues *insertSpot = this; while ( insertSpot->GetNextKey() ) { insertSpot = insertSpot->GetNextKey(); @@ -1718,9 +1980,8 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi Q_strncpy( fullpath, resourceName, sizeof( fullpath ) ); // Strip off characters back to start or first / - bool done = false; int len = Q_strlen( fullpath ); - while ( !done ) + for (;;) { if ( len <= 0 ) { @@ -1746,6 +2007,7 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi // CUtlSymbol save = s_CurrentFileSymbol; // did that had any use ??? newKV->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent + newKV->UsesConditionals( m_bEvaluateConditionals != 0 ); if ( newKV->LoadFromFile( pFileSystem, fullpath, pPathID ) ) { @@ -1820,19 +2082,35 @@ void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV ) //----------------------------------------------------------------------------- bool EvaluateConditional( const char *str ) { - bool bResult = false; - bool bXboxUI = IsX360(); + if ( !str ) + return false; - if ( bXboxUI ) - { - bResult = !Q_stricmp( "[$X360]", str ); - } - else - { - bResult = !Q_stricmp( "[$WIN32]", str ); - } + if ( *str == '[' ) + str++; - return bResult; + bool bNot = false; // should we negate this command? + if ( *str == '!' ) + bNot = true; + + if ( Q_stristr( str, "$X360" ) ) + return IsX360() ^ bNot; + + if ( Q_stristr( str, "$WIN32" ) ) + return IsPC() ^ bNot; // hack hack - for now WIN32 really means IsPC + + if ( Q_stristr( str, "$WINDOWS" ) ) + return IsWindows() ^ bNot; + + if ( Q_stristr( str, "$OSX" ) ) + return IsOSX() ^ bNot; + + if ( Q_stristr( str, "$LINUX" ) ) + return IsLinux() ^ bNot; + + if ( Q_stristr( str, "$POSIX" ) ) + return IsPosix() ^ bNot; + + return false; } @@ -1896,6 +2174,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase Assert( pCurrentKey ); pCurrentKey->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // same format has parent use + pCurrentKey->UsesConditionals( m_bEvaluateConditionals != 0 ); if ( pPreviousKey ) { @@ -1912,7 +2191,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase if ( wasConditional ) { - bAccepted = EvaluateConditional( s ); + bAccepted = !m_bEvaluateConditionals || EvaluateConditional( s ); // Now get the '{' s = ReadToken( buf, wasQuoted, wasConditional ); @@ -1981,6 +2260,16 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, I int nLen = Q_strlen( pBuffer ); CUtlBuffer buf( pBuffer, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); + + // Translate Unicode files into UTF-8 before proceeding + if ( nLen > 2 && (uint8)pBuffer[0] == 0xFF && (uint8)pBuffer[1] == 0xFE ) + { + int nUTF8Len = V_UnicodeToUTF8( (wchar_t*)(pBuffer+2), NULL, 0 ); + char *pUTF8Buf = KVStringAlloc(nUTF8Len); + V_UnicodeToUTF8( (wchar_t*)(pBuffer+2), pUTF8Buf, nUTF8Len ); + buf.AssumeMemory( pUTF8Buf, nUTF8Len, nUTF8Len, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); + } + return LoadFromBuffer( resourceName, buf, pFileSystem, pPathID ); } @@ -1994,6 +2283,13 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b bool wasConditional; // keep this out of the stack until a key is parsed CKeyErrorContext errorKey( INVALID_KEY_SYMBOL ); + + // Locate the last child. (Almost always, we will not have any children.) + // We maintain the pointer to the last child here, so we don't have to re-locate + // it each time we append the next subkey, which causes O(N^2) time + KeyValues *pLastChild = FindLastSubKey();; + + // Keep parsing until we hit the closing brace which terminates this block, or a parse error while ( 1 ) { bool bAccepted = true; @@ -2018,7 +2314,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b // Always create the key; note that this could potentially // cause some duplication, but that's what we want sometimes - KeyValues *dat = CreateKey( name ); + KeyValues *dat = CreateKeyUsingKnownLastChild( name, pLastChild ); errorKey.Reset( dat->GetNameSymbol() ); @@ -2027,7 +2323,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b if ( wasConditional && value ) { - bAccepted = EvaluateConditional( value ); + bAccepted = !m_bEvaluateConditionals || EvaluateConditional( value ); // get the real value value = ReadToken( buf, wasQuoted, wasConditional ); @@ -2062,7 +2358,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b if (dat->m_sValue) { - delete[] dat->m_sValue; + KVStringDelete(dat->m_sValue); dat->m_sValue = NULL; } @@ -2075,7 +2371,17 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b int ival = strtol( value, &pIEnd, 10 ); float fval = (float)strtod( value, &pFEnd ); - + bool bOverflow = ( ival == LONG_MAX || ival == LONG_MIN ) && errno == ERANGE; +#ifdef POSIX + // strtod supports hex representation in strings under posix but we DON'T + // want that support in keyvalues, so undo it here if needed + if ( len > 1 && tolower(value[1]) == 'x' ) + { + fval = 0.0f; + pFEnd = (char *)value; + } +#endif + if ( *value == 0 ) { dat->m_iDataType = TYPE_STRING; @@ -2094,7 +2400,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b digit -= 'A' - ( '9' + 1 ); retVal = ( retVal * 16 ) + ( digit - '0' ); } - dat->m_sValue = new char[sizeof(uint64)]; + dat->m_sValue = KVStringAlloc(sizeof(uint64)); *((uint64 *)dat->m_sValue) = retVal; dat->m_iDataType = TYPE_UINT64; } @@ -2103,7 +2409,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b dat->m_flValue = fval; dat->m_iDataType = TYPE_FLOAT; } - else if (pIEnd == pSEnd) + else if (pIEnd == pSEnd && !bOverflow) { dat->m_iValue = ival; dat->m_iDataType = TYPE_INT; @@ -2116,7 +2422,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b if (dat->m_iDataType == TYPE_STRING) { // copy in the string information - dat->m_sValue = new char[len+1]; + dat->m_sValue = KVStringAlloc(len + 1); Q_memcpy( dat->m_sValue, value, len+1 ); } @@ -2125,7 +2431,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b const char *peek = ReadToken( buf, wasQuoted, wasConditional ); if ( wasConditional ) { - bAccepted = EvaluateConditional( peek ); + bAccepted = !m_bEvaluateConditionals || EvaluateConditional( peek ); } else { @@ -2133,9 +2439,26 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b } } - if ( !bAccepted ) + Assert( dat->m_pPeer == NULL ); + if ( bAccepted ) { - this->RemoveSubKey( dat ); + Assert( pLastChild == NULL || pLastChild->m_pPeer == dat ); + pLastChild = dat; + } + else + { + //this->RemoveSubKey( dat ); + if ( pLastChild == NULL ) + { + Assert( m_pSub == dat ); + m_pSub = NULL; + } + else + { + Assert( pLastChild->m_pPeer == dat ); + pLastChild->m_pPeer = NULL; + } + dat->deleteThis(); dat = NULL; } @@ -2232,7 +2555,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) } // read KeyValues from binary buffer, returns true if parsing was successful -bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) +bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) { if ( buffer.IsText() ) // must be a binary buffer return false; @@ -2243,7 +2566,12 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) RemoveEverything(); // remove current content Init(); // reset - char token[KEYVALUES_TOKEN_SIZE]; + if ( nStackDepth > 100 ) + { + AssertMsgOnce( false, "KeyValues::ReadAsBinary() stack depth > 100\n" ); + return false; + } + KeyValues *dat = this; types_t type = (types_t)buffer.GetUnsignedChar(); @@ -2255,26 +2583,29 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) dat->m_iDataType = type; - buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); - token[KEYVALUES_TOKEN_SIZE-1] = 0; + { + char token[KEYVALUES_TOKEN_SIZE]; + buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); + token[KEYVALUES_TOKEN_SIZE-1] = 0; + dat->SetName( token ); + } - dat->SetName( token ); - switch ( type ) { case TYPE_NONE: { dat->m_pSub = new KeyValues(""); - dat->m_pSub->ReadAsBinary( buffer ); + dat->m_pSub->ReadAsBinary( buffer, nStackDepth + 1 ); break; } case TYPE_STRING: { + char token[KEYVALUES_TOKEN_SIZE]; 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]; + dat->m_sValue = KVStringAlloc(len + 1); Q_memcpy( dat->m_sValue, token, len+1 ); break; @@ -2293,8 +2624,9 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) case TYPE_UINT64: { - dat->m_sValue = new char[sizeof(uint64)]; - *((double *)dat->m_sValue) = buffer.GetDouble(); + dat->m_sValue = KVStringAlloc(sizeof(uint64)); + *((uint64 *)dat->m_sValue) = buffer.GetInt64(); + break; } case TYPE_FLOAT: @@ -2343,18 +2675,14 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) void *KeyValues::operator new( size_t iAllocSize ) { MEM_ALLOC_CREDIT(); - return KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize); + return KeyValuesSystem()->AllocKeyValuesMemory( (int)iAllocSize ); } void *KeyValues::operator new( size_t iAllocSize, int nBlockUse, const char *pFileName, int nLine ) { -#ifndef NO_MALLOC_OVERRIDE MemAlloc_PushAllocDbgInfo( pFileName, nLine ); -#endif - void *p = KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize); -#ifndef NO_MALLOC_OVERRIDE + void *p = KeyValuesSystem()->AllocKeyValuesMemory( (int)iAllocSize ); MemAlloc_PopAllocDbgInfo(); -#endif return p; } @@ -2371,17 +2699,24 @@ void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileNam KeyValuesSystem()->FreeKeyValuesMemory(pMem); } -void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest ) +void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest, size_t DestSizeInBytes ) { +#ifdef DBGFLAG_ASSERT + void *pDestEnd = ( char * )pDest + DestSizeInBytes + 1; +#endif + 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: { + Assert( dest_field + sizeof( float ) < pDestEnd ); + float default_value=(pUnpackTable->m_pKeyDefault)?atof(pUnpackTable->m_pKeyDefault):0.0; *( ( float *) dest_field)=GetFloat( pUnpackTable->m_pKeyName, default_value ); break; @@ -2390,6 +2725,8 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl case UNPACK_TYPE_VECTOR: { + Assert( dest_field + sizeof( Vector ) < pDestEnd ); + Vector *dest_v=(Vector *) dest_field; char const *src_string= GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); @@ -2402,6 +2739,8 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl case UNPACK_TYPE_FOUR_FLOATS: { + Assert( dest_field + sizeof( float ) * 4 < pDestEnd ); + float *dest_f=(float *) dest_field; char const *src_string= GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); @@ -2414,6 +2753,8 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl case UNPACK_TYPE_TWO_FLOATS: { + Assert( dest_field + sizeof( float ) * 2 < pDestEnd ); + float *dest_f=(float *) dest_field; char const *src_string= GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); @@ -2426,6 +2767,8 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl case UNPACK_TYPE_STRING: { + Assert( dest_field + pUnpackTable->m_nFieldSize < pDestEnd ); + char *dest_s=(char *) dest_field; strncpy( dest_s, GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ), @@ -2436,6 +2779,8 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl case UNPACK_TYPE_INT: { + Assert( dest_field + sizeof( int ) < pDestEnd ); + int *dest_i=(int *) dest_field; int default_int=0; if ( pUnpackTable->m_pKeyDefault) @@ -2446,6 +2791,8 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl case UNPACK_TYPE_VECTOR_COLOR: { + Assert( dest_field + sizeof( Vector ) < pDestEnd ); + Vector *dest_v=(Vector *) dest_field; if (find_it) { @@ -2523,3 +2870,188 @@ bool KeyValues::ProcessResolutionKeys( const char *pResString ) return true; } + + + +// +// KeyValues dumping implementation +// +bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */ ) +{ + if ( !pDump->KvBeginKey( this, nIndentLevel ) ) + return false; + + // Dump values + for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() ) + { + if ( !pDump->KvWriteValue( val, nIndentLevel + 1 ) ) + return false; + } + + // Dump subkeys + for ( KeyValues *sub = this ? GetFirstTrueSubKey() : NULL; sub; sub = sub->GetNextTrueSubKey() ) + { + if ( !sub->Dump( pDump, nIndentLevel + 1 ) ) + return false; + } + + return pDump->KvEndKey( this, nIndentLevel ); +} + +bool IKeyValuesDumpContextAsText::KvBeginKey( KeyValues *pKey, int nIndentLevel ) +{ + if ( pKey ) + { + return + KvWriteIndent( nIndentLevel ) && + KvWriteText( pKey->GetName() ) && + KvWriteText( " {\n" ); + } + else + { + return + KvWriteIndent( nIndentLevel ) && + KvWriteText( "<< NULL >>\n" ); + } +} + +bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel ) +{ + if ( !val ) + { + return + KvWriteIndent( nIndentLevel ) && + KvWriteText( "<< NULL >>\n" ); + } + + if ( !KvWriteIndent( nIndentLevel ) ) + return false; + + if ( !KvWriteText( val->GetName() ) ) + return false; + + if ( !KvWriteText( " " ) ) + return false; + + switch ( val->GetDataType() ) + { + case KeyValues::TYPE_STRING: + { + if ( !KvWriteText( val->GetString() ) ) + return false; + } + break; + + case KeyValues::TYPE_INT: + { + int n = val->GetInt(); + char *chBuffer = ( char * ) stackalloc( 128 ); + V_snprintf( chBuffer, 128, "int( %d = 0x%X )", n, n ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; + + case KeyValues::TYPE_FLOAT: + { + float fl = val->GetFloat(); + char *chBuffer = ( char * ) stackalloc( 128 ); + V_snprintf( chBuffer, 128, "float( %f )", fl ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; + + case KeyValues::TYPE_PTR: + { + void *ptr = val->GetPtr(); + char *chBuffer = ( char * ) stackalloc( 128 ); + V_snprintf( chBuffer, 128, "ptr( 0x%p )", ptr ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; + + case KeyValues::TYPE_WSTRING: + { + wchar_t const *wsz = val->GetWString(); + int nLen = V_wcslen( wsz ); + int numBytes = nLen*2 + 64; + char *chBuffer = ( char * ) stackalloc( numBytes ); + V_snprintf( chBuffer, numBytes, "%ls [wstring, len = %d]", wsz, nLen ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; + + case KeyValues::TYPE_UINT64: + { + uint64 n = val->GetUint64(); + char *chBuffer = ( char * ) stackalloc( 128 ); + V_snprintf( chBuffer, 128, "u64( %lld = 0x%llX )", n, n ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; + + default: + break; + { + int n = val->GetDataType(); + char *chBuffer = ( char * ) stackalloc( 128 ); + V_snprintf( chBuffer, 128, "??kvtype[%d]", n ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; + } + + return KvWriteText( "\n" ); +} + +bool IKeyValuesDumpContextAsText::KvEndKey( KeyValues *pKey, int nIndentLevel ) +{ + if ( pKey ) + { + return + KvWriteIndent( nIndentLevel ) && + KvWriteText( "}\n" ); + } + else + { + return true; + } +} + +bool IKeyValuesDumpContextAsText::KvWriteIndent( int nIndentLevel ) +{ + int numIndentBytes = ( nIndentLevel * 2 + 1 ); + char *pchIndent = ( char * ) stackalloc( numIndentBytes ); + memset( pchIndent, ' ', numIndentBytes - 1 ); + pchIndent[ numIndentBytes - 1 ] = 0; + return KvWriteText( pchIndent ); +} + + +bool CKeyValuesDumpContextAsDevMsg::KvBeginKey( KeyValues *pKey, int nIndentLevel ) +{ + static ConVarRef r_developer( "developer" ); + if ( r_developer.IsValid() && r_developer.GetInt() < m_nDeveloperLevel ) + // If "developer" is not the correct level, then avoid evaluating KeyValues tree early + return false; + else + return IKeyValuesDumpContextAsText::KvBeginKey( pKey, nIndentLevel ); +} + +bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText ) +{ + if ( m_nDeveloperLevel > 0 ) + { + DevMsg( m_nDeveloperLevel, "%s", szText ); + } + else + { + Msg( "%s", szText ); + } + return true; +} \ No newline at end of file diff --git a/tier1/strtools.cpp b/tier1/strtools.cpp index 76b6da44..e7943983 100644 --- a/tier1/strtools.cpp +++ b/tier1/strtools.cpp @@ -316,13 +316,13 @@ const char *StringAfterPrefixCaseSensitive( const char *str, const char *prefix } -int V_atoi (const char *str) +int64 V_atoi64( const char *str ) { AssertValidStringPtr( str ); - int val; - int sign; - int c; + int64 val; + int64 sign; + int64 c; Assert( str ); if (*str == '-') @@ -377,6 +377,63 @@ int V_atoi (const char *str) return 0; } +uint64 V_atoui64( const char *str ) +{ + AssertValidStringPtr( str ); + + uint64 val; + uint64 c; + + Assert( str ); + + 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; + } + } + + // + // check for character + // + if (str[0] == '\'') + { + return str[1]; + } + + // + // assume decimal + // + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val; + val = val*10 + c - '0'; + } + + return 0; +} + +int V_atoi( const char *str ) +{ + return (int)V_atoi64( str ); +} float V_atof (const char *str) {