diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/tier1 | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'external/vpc/tier1')
| -rw-r--r-- | external/vpc/tier1/characterset.cpp | 41 | ||||
| -rw-r--r-- | external/vpc/tier1/checksum_crc.cpp | 182 | ||||
| -rw-r--r-- | external/vpc/tier1/checksum_md5.cpp | 291 | ||||
| -rw-r--r-- | external/vpc/tier1/convar.cpp | 1521 | ||||
| -rw-r--r-- | external/vpc/tier1/exprevaluator.cpp | 501 | ||||
| -rw-r--r-- | external/vpc/tier1/generichash.cpp | 429 | ||||
| -rw-r--r-- | external/vpc/tier1/interface.cpp | 705 | ||||
| -rw-r--r-- | external/vpc/tier1/keyvalues.cpp | 3427 | ||||
| -rw-r--r-- | external/vpc/tier1/mempool.cpp | 336 | ||||
| -rw-r--r-- | external/vpc/tier1/memstack.cpp | 641 | ||||
| -rw-r--r-- | external/vpc/tier1/splitstring.cpp | 91 | ||||
| -rw-r--r-- | external/vpc/tier1/stringpool.cpp | 426 | ||||
| -rw-r--r-- | external/vpc/tier1/strtools.cpp | 2632 | ||||
| -rw-r--r-- | external/vpc/tier1/tier1.cpp | 29 | ||||
| -rw-r--r-- | external/vpc/tier1/utlbuffer.cpp | 1795 | ||||
| -rw-r--r-- | external/vpc/tier1/utlstring.cpp | 554 | ||||
| -rw-r--r-- | external/vpc/tier1/utlsymbol.cpp | 513 |
17 files changed, 14114 insertions, 0 deletions
diff --git a/external/vpc/tier1/characterset.cpp b/external/vpc/tier1/characterset.cpp new file mode 100644 index 0000000..8f6e594 --- /dev/null +++ b/external/vpc/tier1/characterset.cpp @@ -0,0 +1,41 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//============================================================================= + +#include <string.h> +#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[ pszSetString[i] ] = 1; + i++; + } + +} diff --git a/external/vpc/tier1/checksum_crc.cpp b/external/vpc/tier1/checksum_crc.cpp new file mode 100644 index 0000000..6ddcb17 --- /dev/null +++ b/external/vpc/tier1/checksum_crc.cpp @@ -0,0 +1,182 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Generic CRC functions +// +//=============================================================================// + +#include "tier0/platform.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; + unsigned char *pb = (unsigned char *)pBuffer; + unsigned int nFront; + int nMain; + +JustAfew: + + switch (nBuffer) + { + case 7: + ulCrc = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 6: + ulCrc = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 5: + ulCrc = pulCRCTable[*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[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 2: + ulCrc = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + + case 1: + ulCrc = pulCRCTable[*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 = ((unsigned int)pb) & 3; + nBuffer -= nFront; + switch (nFront) + { + case 3: + ulCrc = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + case 2: + ulCrc = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8); + case 1: + ulCrc = pulCRCTable[*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/external/vpc/tier1/checksum_md5.cpp b/external/vpc/tier1/checksum_md5.cpp new file mode 100644 index 0000000..891cee4 --- /dev/null +++ b/external/vpc/tier1/checksum_md5.cpp @@ -0,0 +1,291 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#include "basetypes.h" +#include "commonmacros.h" +#include "checksum_md5.h" +#include <string.h> +#include <stdio.h> +#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<<s | 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 +//----------------------------------------------------------------------------- +#if ( PLAT_BIG_ENDIAN == 1 ) +static void MD5Transform(unsigned int buf[4], unsigned int const in_big[16]) +{ + + unsigned int in[16]; + for( int i = 0; i != 16; ++i ) + { + in[i] = LittleDWord(in_big[i]); + } +#else +static void MD5Transform(unsigned int buf[4], unsigned int const in[16]) +{ +#endif + 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] = LittleDWord( ctx->bits[0] ); + ((unsigned int *) ctx->in)[15] = LittleDWord( ctx->bits[1] ); + + MD5Transform(ctx->buf, (unsigned int *) ctx->in); + //byteReverse((unsigned char *) ctx->buf, 4); +#if ( PLAT_BIG_ENDIAN == 1 ) + COMPILE_TIME_ASSERT( MD5_DIGEST_LENGTH == (sizeof(unsigned int) * 4) ); + ((unsigned int *)digest)[0] = LittleDWord( ctx->buf[0] ); + ((unsigned int *)digest)[1] = LittleDWord( ctx->buf[1] ); + ((unsigned int *)digest)[2] = LittleDWord( ctx->buf[2] ); + ((unsigned int *)digest)[3] = LittleDWord( ctx->buf[3] ); +#else + memcpy(digest, ctx->buf, MD5_DIGEST_LENGTH); +#endif + memset(ctx, 0, sizeof(ctx)); /* 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 ); + + V_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) +{ + nSeed = LittleDWord( 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 LittleDWord(*(unsigned int*)(digest+6)); // use 4 middle bytes for random value +} diff --git a/external/vpc/tier1/convar.cpp b/external/vpc/tier1/convar.cpp new file mode 100644 index 0000000..944b9e0 --- /dev/null +++ b/external/vpc/tier1/convar.cpp @@ -0,0 +1,1521 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#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" +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#endif +#include "tier0/memdbgon.h" + + +// Comment this out when we release. +//#define ALLOW_DEVELOPMENT_CVARS +// This enables the l4d style of culling all cvars that are not marked FCVAR_RELEASE : +// #define CULL_ALL_CVARS_NOT_FCVAR_RELEASE + + + +//----------------------------------------------------------------------------- +// Statically constructed list of ConCommandBases, +// used for registering them with the ICVar interface +//----------------------------------------------------------------------------- +ConCommandBase *ConCommandBase::s_pConCommandBases = NULL; +IConCommandBaseAccessor *ConCommandBase::s_pAccessor = NULL; +static int 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( int 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; + } + + g_pCVar->AddSplitScreenConVars(); + g_pCVar->ProcessQueuedMaterialThreadConVarSets(); + + ConCommandBase::s_pConCommandBases = NULL; +} + +void ConVar_Unregister( ) +{ + if ( !g_pCVar || !s_bRegistered ) + return; + + Assert( s_nDLLIdentifier >= 0 ); + + // Do this after unregister!!! + g_pCVar->RemoveSplitScreenConVars( s_nDLLIdentifier ); + 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*/, int 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*/, int flags /*= 0*/ ) +{ + static 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( int flag ) const +{ + return ( flag & m_nFlags ) ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flags - +//----------------------------------------------------------------------------- +void ConCommandBase::AddFlags( int flags ) +{ + m_nFlags |= flags; + +#ifdef ALLOW_DEVELOPMENT_CVARS + m_nFlags &= ~FCVAR_DEVELOPMENTONLY; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: removes specified flags +//----------------------------------------------------------------------------- +void ConCommandBase::RemoveFlags( int flags ) +{ + m_nFlags &= ~flags; +} + +// Returns current flags +int ConCommandBase::GetFlags() 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]; + V_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; +} + + +//----------------------------------------------------------------------------- +// +// 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 = V_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 = V_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 ( !V_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, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ) +{ + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructs a console command +//----------------------------------------------------------------------------- +//ConCommand::ConCommand() +//{ +// m_bIsNewConCommand = true; +//} + +ConCommand::ConCommand( const char *pName, FnCommandCallbackV1_t callback, const char *pHelpString /*= 0*/, int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ ) +{ + // Set the callback + m_fnCommandCallbackV1 = callback; + m_bUsingNewCommandCallback = false; + m_bUsingCommandCallbackInterface = false; + m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; + m_bHasCompletionCallback = completionFunc != 0 ? true : false; + + // Setup the rest + BaseClass::Create( pName, pHelpString, flags ); +} + +ConCommand::ConCommand( const char *pName, FnCommandCallback_t callback, const char *pHelpString /*= 0*/, int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ ) +{ + // Set the callback + m_fnCommandCallback = callback; + m_bUsingNewCommandCallback = true; + m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; + m_bHasCompletionCallback = completionFunc != 0 ? true : false; + m_bUsingCommandCallbackInterface = false; + + // Setup the rest + BaseClass::Create( pName, pHelpString, flags ); +} + +ConCommand::ConCommand( const char *pName, ICommandCallback *pCallback, const char *pHelpString /*= 0*/, int flags /*= 0*/, ICommandCompletionCallback *pCompletionCallback /*= 0*/ ) +{ + // Set the callback + m_pCommandCallback = pCallback; + m_bUsingNewCommandCallback = false; + m_pCommandCompletionCallback = pCompletionCallback; + m_bHasCompletionCallback = ( pCompletionCallback != 0 ); + m_bUsingCommandCallbackInterface = true; + + // 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 CCommand &command ) +{ + if ( m_bUsingNewCommandCallback ) + { + if ( m_fnCommandCallback ) + { + ( *m_fnCommandCallback )( command ); + return; + } + } + else if ( m_bUsingCommandCallbackInterface ) + { + if ( m_pCommandCallback ) + { + m_pCommandCallback->CommandCallback( command ); + return; + } + } + else + { + if ( m_fnCommandCallbackV1 ) + { + ( *m_fnCommandCallbackV1 )(); + return; + } + } + + // Command without callback!!! + AssertMsg1( 0, "Encountered ConCommand '%s' without a callback!\n", GetName() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Calls the autocompletion method to get autocompletion suggestions +//----------------------------------------------------------------------------- +int ConCommand::AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands ) +{ + if ( m_bUsingCommandCallbackInterface ) + { + if ( !m_pCommandCompletionCallback ) + return 0; + return m_pCommandCompletionCallback->CommandCompletionCallback( partial, commands ); + } + + Assert( m_fnCompletionCallback ); + if ( !m_fnCompletionCallback ) + return 0; + + char rgpchCommands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ]; + int iret = ( m_fnCompletionCallback )( partial, rgpchCommands ); + for ( int i = 0 ; i < iret; ++i ) + { + CUtlString str = rgpchCommands[ i ]; + commands.AddToTail( str ); + } + return iret; +} + + +//----------------------------------------------------------------------------- +// 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, int flags /* = 0 */ ) +{ + Create( pName, pDefaultValue, flags ); +} + +ConVar::ConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString ) +{ + Create( pName, pDefaultValue, flags, pHelpString ); +} + +ConVar::ConVar( const char *pName, const char *pDefaultValue, int 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, int 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, int 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 /*=true*/ ) +{ + if ( !callback ) + { + Warning( "InstallChangeCallback called with NULL callback, ignoring!!!\n" ); + return; + } + + if ( m_pParent->m_fnChangeCallbacks.Find( callback ) != m_pParent->m_fnChangeCallbacks.InvalidIndex() ) + { + // Same ptr added twice, sigh... + Warning( "InstallChangeCallback ignoring duplicate change callback!!!\n" ); + return; + } + + m_pParent->m_fnChangeCallbacks.AddToTail( callback ); + + // Call it immediately to set the initial value... + if ( bInvoke ) + { + callback( this, m_Value.m_pszString, m_Value.m_fValue ); + } +} + +void ConVar::RemoveChangeCallback( FnChangeCallback_t callback ) +{ + m_pParent->m_fnChangeCallbacks.FindAndRemove( callback ); +} + +bool ConVar::IsFlagSet( int flag ) const +{ + return ( flag & m_pParent->m_nFlags ) ? true : false; +} + +int ConVar::GetFlags() const +{ + return m_pParent->m_nFlags; +} + +const char *ConVar::GetHelpText( void ) const +{ + return m_pParent->m_pszHelpString; +} + +void ConVar::AddFlags( int flags ) +{ + m_pParent->m_nFlags |= flags; + +#ifdef ALLOW_DEVELOPMENT_CVARS + m_pParent->m_nFlags &= ~FCVAR_DEVELOPMENTONLY; +#endif +} + +bool ConVar::IsRegistered( void ) const +{ + return m_pParent->m_bRegistered; +} + +const char *ConVar::GetName( void ) const +{ + return m_pParent->m_pszName; +} + +const char *ConVar::GetBaseName( void ) const +{ + return m_pParent->m_pszName; +} + +int ConVar::GetSplitScreenPlayerSlot( void ) const +{ + // Default implementation (certain FCVAR_USERINFO derive a new type of convar and set this) + return 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool ConVar::IsCommand( void ) const +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +//----------------------------------------------------------------------------- +void ConVar::Init() +{ + BaseClass::Init(); +} + +bool ConVar::InternalSetColorFromString( const char *value ) +{ + bool bColor = false; + + // Try pulling RGBA color values out of the string + int nRGBA[4]; + int nParamsRead = sscanf( value, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]) ); + + if ( nParamsRead >= 3 ) + { + // This is probably a color! + if ( nParamsRead == 3 ) + { + // Assume they wanted full alpha + nRGBA[3] = 255; + } + + if ( nRGBA[0] >= 0 && nRGBA[0] <= 255 && + nRGBA[1] >= 0 && nRGBA[1] <= 255 && + nRGBA[2] >= 0 && nRGBA[2] <= 255 && + nRGBA[3] >= 0 && nRGBA[3] <= 255 ) + { + // This is definitely a color! + bColor = true; + + // Stuff all the values into each byte of our int + unsigned char *pColorElement = ((unsigned char*)&m_Value.m_nValue); + pColorElement[0] = nRGBA[0]; + pColorElement[1] = nRGBA[1]; + pColorElement[2] = nRGBA[2]; + pColorElement[3] = nRGBA[3]; + + // Copy that value into a float (even though this has little meaning) + m_Value.m_fValue = ( float )( m_Value.m_nValue ); + } + } + + return bColor; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *value - +//----------------------------------------------------------------------------- +void ConVar::InternalSetValue( const char *value ) +{ + if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) ) + { + if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() ) + { + g_pCVar->QueueMaterialThreadSetValue( this, value ); + return; + } + } + + char tempVal[ 32 ]; + char *val; + + Assert(m_pParent == this); // Only valid for root convars. + + float flOldValue = m_Value.m_fValue; + val = (char *)value; + if ( !val ) + val = ""; + + if ( !InternalSetColorFromString( value ) ) + { + // Not a color, do the standard thing + float fNewValue = ( float )atof( value ); + if ( !IsFinite( fNewValue ) ) + { + Warning( "Warning: %s = '%s' is infinite, clamping value.\n", GetName(), value ); + fNewValue = FLT_MAX; + } + + if ( ClampValue( fNewValue ) ) + { + V_snprintf( tempVal,sizeof(tempVal), "%f", fNewValue ); + val = tempVal; + } + + // Redetermine value + m_Value.m_fValue = fNewValue; + m_Value.m_nValue = ( int )( m_Value.m_fValue ); + } + + 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 = V_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 ); + } + + stackfree( pszOldValue ); +} + +//----------------------------------------------------------------------------- +// 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; + + if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) ) + { + if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() ) + { + g_pCVar->QueueMaterialThreadSetValue( this, fNewValue ); + 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 )m_Value.m_fValue; + + if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) + { + char tempVal[ 32 ]; + V_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; + + if ( IsFlagSet( FCVAR_MATERIAL_THREAD_MASK ) ) + { + if ( g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed() ) + { + g_pCVar->QueueMaterialThreadSetValue( this, 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 ]; + V_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 value ) +{ + // Stuff color values into an int + int nValue; + + unsigned char *pColorElement = ((unsigned char*)&nValue); + pColorElement[0] = value[0]; + pColorElement[1] = value[1]; + pColorElement[2] = value[2]; + pColorElement[3] = value[3]; + + // Call the int internal set + InternalSetIntValue( nValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: Private creation +//----------------------------------------------------------------------------- +void ConVar::Create( const char *pName, const char *pDefaultValue, int flags /*= 0*/, + const char *pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/, + bool bMax /*= false*/, float fMax /*= false*/, FnChangeCallback_t callback /*= NULL*/ ) +{ + static char *empty_string = ""; + + m_pParent = this; + + // Name should be static data + m_pszDefaultValue = pDefaultValue ? pDefaultValue : empty_string; + Assert( m_pszDefaultValue ); + + m_bHasMin = bMin; + m_fMinVal = fMin; + m_bHasMax = bMax; + m_fMaxVal = fMax; + + if ( callback ) + { + m_fnChangeCallbacks.AddToTail( callback ); + } + + 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 ); + + if ( !InternalSetColorFromString( m_Value.m_pszString ) ) + { + m_Value.m_fValue = ( float )atof( m_Value.m_pszString ); + if ( !IsFinite( m_Value.m_fValue ) ) + { + Warning( "ConVar(%s) defined with infinite float value (%s)\n", pName, m_Value.m_pszString ); + m_Value.m_fValue = FLT_MAX; + Assert( 0 ); + } + + // 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; + } + + //If we're not tagged as cheat, archive or release then hide us. +#if CULL_ALL_CVARS_NOT_FCVAR_RELEASE +// FIXMEL4DTOMAINMERGE: will need to assess if this hides too many convars for TF and other projects in main + if ( !( flags & ( FCVAR_CHEAT | FCVAR_ARCHIVE | FCVAR_RELEASE | FCVAR_USERINFO ) ) ) + { + flags |= FCVAR_DEVELOPMENTONLY; + } +#else + +#endif + + + 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; +} + +float ConVar::GetMinValue() const +{ + return m_pParent->m_fMinVal; +} + +float ConVar::GetMaxValue() const +{ + return m_pParent->m_fMaxVal;; +} + +bool ConVar::HasMin() const +{ + return m_pParent->m_bHasMin; +} + +bool ConVar::HasMax() const +{ + return m_pParent->m_bHasMax; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *ConVar::GetDefault( void ) const +{ + return m_pParent->m_pszDefaultValue; +} + +void ConVar::SetDefault( const char *pszDefault ) +{ + static char *empty_string = ""; + m_pszDefaultValue = pszDefault ? pszDefault : empty_string; + Assert( 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 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; +} + +// Helper for splitscreen ConVars +SplitScreenConVarRef::SplitScreenConVarRef( const char *pName ) +{ + Init( pName, false ); +} + +SplitScreenConVarRef::SplitScreenConVarRef( const char *pName, bool bIgnoreMissing ) +{ + Init( pName, bIgnoreMissing ); +} + +void SplitScreenConVarRef::Init( const char *pName, bool bIgnoreMissing ) +{ + for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i ) + { + cv_t &info = m_Info[ i ]; + char pchName[ 256 ]; + if ( i != 0 ) + { + V_snprintf( pchName, sizeof( pchName ), "%s%d", pName, i + 1 ); + } + else + { + V_strncpy( pchName, pName, sizeof( pchName ) ); + } + + info.m_pConVar = g_pCVar ? g_pCVar->FindVar( pchName ) : &s_EmptyConVar; + if ( !info.m_pConVar ) + { + info.m_pConVar = &s_EmptyConVar; + if ( i > 0 ) + { + // Point at slot zero instead, in case we got in here with a non FCVAR_SS var... + info.m_pConVar = m_Info[ 0 ].m_pConVar; + } + } + info.m_pConVarState = static_cast< ConVar * >( info.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; + } + } +} + +SplitScreenConVarRef::SplitScreenConVarRef( IConVar *pConVar ) +{ + cv_t &info = m_Info[ 0 ]; + info.m_pConVar = pConVar ? pConVar : &s_EmptyConVar; + info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar ); + + for ( int i = 1; i < MAX_SPLITSCREEN_CLIENTS; ++i ) + { + info = m_Info[ i ]; + char pchName[ 256 ]; + V_snprintf( pchName, sizeof( pchName ), "%s%d", pConVar->GetName(), i + 1 ); + + info.m_pConVar = g_pCVar ? g_pCVar->FindVar( pchName ) : &s_EmptyConVar; + if ( !info.m_pConVar ) + { + info.m_pConVar = &s_EmptyConVar; + if ( i > 0 ) + { + // Point at slot zero instead, in case we got in here with a non FCVAR_SS var... + info.m_pConVar = m_Info[ 0 ].m_pConVar; + } + } + info.m_pConVarState = static_cast< ConVar * >( info.m_pConVar ); + } +} + +bool SplitScreenConVarRef::IsValid() const +{ + return m_Info[ 0 ].m_pConVar != &s_EmptyConVar; +} + +struct PrintConVarFlags_t +{ + int flag; + const char *desc; +}; + +static PrintConVarFlags_t g_PrintConVarFlags[] = +{ + { FCVAR_GAMEDLL, "game" }, + { FCVAR_CLIENTDLL, "client" }, + { FCVAR_ARCHIVE, "archive" }, + { FCVAR_NOTIFY, "notify" }, + { FCVAR_SPONLY, "singleplayer" }, + { FCVAR_NOT_CONNECTED, "notconnected" }, + { FCVAR_CHEAT, "cheat" }, + { FCVAR_REPLICATED, "replicated" }, + { FCVAR_SERVER_CAN_EXECUTE, "server_can_execute" }, + { FCVAR_CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" }, + { FCVAR_USERINFO, "user" }, + { FCVAR_SS, "ss" }, + { FCVAR_SS_ADDED, "ss_added" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void ConVar_AppendFlags( const ConCommandBase *var, char *buf, size_t bufsize ) +{ + for ( int i = 0; i < ARRAYSIZE( g_PrintConVarFlags ) ; ++i ) + { + const PrintConVarFlags_t &info = g_PrintConVarFlags[ i ]; + if ( var->IsFlagSet( info.flag ) ) + { + char append[ 128 ]; + V_snprintf( append, sizeof( append ), " %s", info.desc ); + V_strncat( buf, append, bufsize, COPY_ALL_CHARACTERS ); + } + } +} + +static void AppendPrintf( char *buf, size_t bufsize, char const *fmt, ... ) +{ + char scratch[ 1024 ]; + va_list argptr; + va_start( argptr, fmt ); + _vsnprintf( scratch, sizeof( scratch ) - 1, fmt, argptr ); + va_end( argptr ); + scratch[ sizeof( scratch ) - 1 ] = 0; + + V_strncat( buf, scratch, bufsize, COPY_ALL_CHARACTERS ); +} + +//----------------------------------------------------------------------------- +// 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 ); + + char outstr[ 4096 ]; + outstr[ 0 ] = 0; + + if ( !pVar->IsCommand() ) + { + ConVar *var = ( ConVar * )pVar; + const ConVar_ServerBounded *pBounded = dynamic_cast<const ConVar_ServerBounded*>( 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 ) + { + V_snprintf( tempVal, sizeof( tempVal ), "%d", intVal ); + } + else + { + V_snprintf( tempVal, sizeof( tempVal ), "%f", floatVal ); + } + } + else + { + value = var->GetString(); + } + + if ( value ) + { + AppendPrintf( outstr, sizeof( outstr ), "\"%s\" = \"%s\"", var->GetName(), value ); + + if ( V_stricmp( value, var->GetDefault() ) ) + { + AppendPrintf( outstr, sizeof( outstr ), " ( def. \"%s\" )", var->GetDefault() ); + } + } + + if ( bMin ) + { + AppendPrintf( outstr, sizeof( outstr ), " min. %f", fMin ); + } + if ( bMax ) + { + AppendPrintf( outstr, sizeof( outstr ), " max. %f", fMax ); + } + + // Handle virtualized cvars. + if ( pBounded && fabs( pBounded->GetFloat() - var->GetFloat() ) > 0.0001f ) + { + AppendPrintf( outstr, sizeof( outstr ), " [%.3f server clamped to %.3f]", + var->GetFloat(), pBounded->GetFloat() ); + } + } + else + { + ConCommand *var = ( ConCommand * )pVar; + + AppendPrintf( outstr, sizeof( outstr ), "\"%s\" ", var->GetName() ); + } + + ConVar_AppendFlags( pVar, outstr, sizeof( outstr ) ); + + pStr = pVar->GetHelpText(); + if ( pStr && *pStr ) + { + ConMsg( "%-80s - %.80s\n", outstr, pStr ); + } + else + { + ConMsg( "%-80s\n", outstr ); + } +} diff --git a/external/vpc/tier1/exprevaluator.cpp b/external/vpc/tier1/exprevaluator.cpp new file mode 100644 index 0000000..ea43366 --- /dev/null +++ b/external/vpc/tier1/exprevaluator.cpp @@ -0,0 +1,501 @@ +//===== Copyright � 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: ExprSimplifier builds a binary tree from an infix expression (in the +// form of a character array). Evaluates C style infix parenthetic logical +// expressions. Supports !, ||, &&, (). Symbols are resolved via callback. +// Syntax is $<name>. $0 evaluates to false. $<number> evaluates to true. +// e.g: ( $1 || ( $FOO || $WHATEVER ) && !$BAR ) +//===========================================================================// + +#include <ctype.h> +#include <vstdlib/ikeyvaluessystem.h> +#include "tier1/exprevaluator.h" +#include "tier1/convar.h" +#include "tier1/fmtstr.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Default conditional symbol handler callback. Symbols are the form $<name>. +// Return true or false for the value of the symbol. +//----------------------------------------------------------------------------- +bool DefaultConditionalSymbolProc( const char *pKey ) +{ + if ( pKey[0] == '$' ) + { + pKey++; + } + + if ( !V_stricmp( pKey, "WIN32" ) ) + { + return IsPC(); + } + + if ( !V_stricmp( pKey, "WINDOWS" ) ) + { + return IsPlatformWindowsPC(); + } + + if ( !V_stricmp( pKey, "X360" ) ) + { + return IsX360(); + } + + if ( !V_stricmp( pKey, "PS3" ) ) + { + return IsPS3(); + } + + if ( !V_stricmp( pKey, "OSX" ) ) + { + return IsPlatformOSX(); + } + + if ( !V_stricmp( pKey, "LINUX" ) ) + { + return IsPlatformLinux(); + } + + if ( !V_stricmp( pKey, "POSIX" ) ) + { + return IsPlatformPosix(); + } + + if ( !V_stricmp( pKey, "GAMECONSOLE" ) ) + { + return IsGameConsole(); + } + + if ( !V_stricmp( pKey, "DEMO" ) ) + { +#if defined( _DEMO ) + return true; +#else + return false; +#endif + } + + if ( !V_stricmp( pKey, "LOWVIOLENCE" ) ) + { +#if defined( _LOWVIOLENCE ) + return true; +#endif + // If it is not a LOWVIOLENCE binary build, then fall through + // and check if there was a run-time symbol installed for it + } + + // don't know it at compile time, so fall through to installed symbol values + return KeyValuesSystem()->GetKeyValuesExpressionSymbol( pKey ); +} + +void DefaultConditionalErrorProc( const char *pReason ) +{ + Warning( "Conditional Error: %s\n", pReason ); +} + +CExpressionEvaluator::CExpressionEvaluator() +{ + m_ExprTree = NULL; +} + +CExpressionEvaluator::~CExpressionEvaluator() +{ + FreeTree( m_ExprTree ); +} + +//----------------------------------------------------------------------------- +// Sets mCurToken to the next token in the input string. Skips all whitespace. +//----------------------------------------------------------------------------- +char CExpressionEvaluator::GetNextToken( void ) +{ + // while whitespace, Increment CurrentPosition + while ( m_pExpression[m_CurPosition] == ' ' ) + ++m_CurPosition; + + // CurrentToken = Expression[CurrentPosition] + m_CurToken = m_pExpression[m_CurPosition++]; + + return m_CurToken; +} + + +//----------------------------------------------------------------------------- +// Utility funcs +//----------------------------------------------------------------------------- +void CExpressionEvaluator::FreeNode( ExprNode *pNode ) +{ + delete pNode; +} + +ExprNode *CExpressionEvaluator::AllocateNode( void ) +{ + return new ExprNode; +} + +void CExpressionEvaluator::FreeTree( ExprTree& node ) +{ + if ( !node ) + return; + + FreeTree( node->left ); + FreeTree( node->right ); + FreeNode( node ); + node = 0; +} + +bool CExpressionEvaluator::IsConditional( bool &bConditional, const char token ) +{ + char nextchar = ' '; + if ( token == OR_OP || token == AND_OP ) + { + // expect || or && + nextchar = m_pExpression[m_CurPosition++]; + if ( (token & nextchar) == token ) + { + bConditional = true; + } + else if ( m_pSyntaxErrorProc ) + { + m_pSyntaxErrorProc( CFmtStr( "Bad expression operator: '%c%c', expected C style operator", token, nextchar ) ); + return false; + } + } + else + { + bConditional = false; + } + + // valid + return true; +} + +bool CExpressionEvaluator::IsNotOp( const char token ) +{ + if ( token == NOT_OP ) + return true; + else + return false; +} + +bool CExpressionEvaluator::IsIdentifierOrConstant( const char token ) +{ + bool success = false; + if ( token == '$' ) + { + // store the entire identifier + int i = 0; + m_Identifier[i++] = token; + while( (isalnum( m_pExpression[m_CurPosition] ) || m_pExpression[m_CurPosition] == '_') && i < MAX_IDENTIFIER_LEN ) + { + m_Identifier[i] = m_pExpression[m_CurPosition]; + ++m_CurPosition; + ++i; + } + + if ( i < MAX_IDENTIFIER_LEN - 1 ) + { + m_Identifier[i] = '\0'; + success = true; + } + } + else + { + if ( isdigit( token ) ) + { + int i = 0; + m_Identifier[i++] = token; + while( isdigit( m_pExpression[m_CurPosition] ) && ( i < MAX_IDENTIFIER_LEN ) ) + { + m_Identifier[i] = m_pExpression[m_CurPosition]; + ++m_CurPosition; + ++i; + } + if ( i < MAX_IDENTIFIER_LEN - 1 ) + { + m_Identifier[i] = '\0'; + success = true; + } + } + } + + return success; +} + +bool CExpressionEvaluator::MakeExprNode( ExprTree &tree, char token, Kind kind, ExprTree left, ExprTree right ) +{ + tree = AllocateNode(); + tree->left = left; + tree->right = right; + tree->kind = kind; + + switch ( kind ) + { + case CONDITIONAL: + tree->data.cond = token; + break; + + case LITERAL: + if ( isdigit( m_Identifier[0] ) ) + { + tree->data.value = ( atoi( m_Identifier ) != 0 ); + } + else + { + tree->data.value = m_pGetSymbolProc( m_Identifier ); + } + break; + + case NOT: + break; + + default: + if ( m_pSyntaxErrorProc ) + { + Assert( 0 ); + m_pSyntaxErrorProc( CFmtStr( "Logic Error in CExpressionEvaluator" ) ); + } + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Makes a factor :: { <expression> } | <identifier>. +//----------------------------------------------------------------------------- +bool CExpressionEvaluator::MakeFactor( ExprTree &tree ) +{ + if ( m_CurToken == '(' ) + { + // Get the next token + GetNextToken(); + + // Make an expression, setting Tree to point to it + if ( !MakeExpression( tree ) ) + { + return false; + } + } + else if ( IsIdentifierOrConstant( m_CurToken ) ) + { + // Make a literal node, set Tree to point to it, set left/right children to NULL. + if ( !MakeExprNode( tree, m_CurToken, LITERAL, NULL, NULL ) ) + { + return false; + } + } + else if ( IsNotOp( m_CurToken ) ) + { + // do nothing + return true; + } + else + { + // This must be a bad token + if ( m_pSyntaxErrorProc ) + { + m_pSyntaxErrorProc( CFmtStr( "Bad expression token: %c", m_CurToken ) ); + } + return false; + } + + // Get the next token + GetNextToken(); + return true; +} + +//----------------------------------------------------------------------------- +// Makes a term :: <factor> { <not> }. +//----------------------------------------------------------------------------- +bool CExpressionEvaluator::MakeTerm( ExprTree &tree ) +{ + // Make a factor, setting Tree to point to it + if ( !MakeFactor( tree ) ) + { + return false; + } + + // while the next token is ! + while ( IsNotOp( m_CurToken ) ) + { + // Make an operator node, setting left child to Tree and right to NULL. (Tree points to new node) + if ( !MakeExprNode( tree, m_CurToken, NOT, tree, NULL ) ) + { + return false; + } + + // Get the next token. + GetNextToken(); + + // Make a factor, setting the right child of Tree to point to it. + if ( !MakeFactor( tree->right ) ) + { + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Makes a complete expression :: <term> { <cond> <term> }. +//----------------------------------------------------------------------------- +bool CExpressionEvaluator::MakeExpression( ExprTree &tree ) +{ + // Make a term, setting Tree to point to it + if ( !MakeTerm( tree ) ) + { + return false; + } + + // while the next token is a conditional + while ( 1 ) + { + bool bConditional = false; + bool bValid = IsConditional( bConditional, m_CurToken ); + if ( !bValid ) + { + return false; + } + + if ( !bConditional ) + { + break; + } + + // Make a conditional node, setting left child to Tree and right to NULL. (Tree points to new node) + if ( !MakeExprNode( tree, m_CurToken, CONDITIONAL, tree, NULL ) ) + { + return false; + } + + // Get the next token. + GetNextToken(); + + // Make a term, setting the right child of Tree to point to it. + if ( !MakeTerm( tree->right ) ) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// returns true for success, false for failure +//----------------------------------------------------------------------------- +bool CExpressionEvaluator::BuildExpression( void ) +{ + // Get the first token, and build the tree. + GetNextToken(); + + return ( MakeExpression( m_ExprTree ) ); +} + +//----------------------------------------------------------------------------- +// returns the value of the node after resolving all children +//----------------------------------------------------------------------------- +bool CExpressionEvaluator::SimplifyNode( ExprTree& node ) +{ + if ( !node ) + return false; + + // Simplify the left and right children of this node + bool leftVal = SimplifyNode(node->left); + bool rightVal = SimplifyNode(node->right); + + // Simplify this node + switch( node->kind ) + { + case NOT: + // the child of '!' is always to the right + node->data.value = !rightVal; + break; + + case CONDITIONAL: + if ( node->data.cond == AND_OP ) + { + node->data.value = leftVal && rightVal; + } + else // OR_OP + { + node->data.value = leftVal || rightVal; + } + break; + + default: // LITERAL + break; + } + + // This node has beed resolved + node->kind = LITERAL; + return node->data.value; +} + +//----------------------------------------------------------------------------- +// Interface to solve a conditional expression. Returns false on failure, Result is undefined. +//----------------------------------------------------------------------------- +bool CExpressionEvaluator::Evaluate( bool &bResult, const char *pInfixExpression, GetSymbolProc_t pGetSymbolProc, SyntaxErrorProc_t pSyntaxErrorProc ) +{ + if ( !pInfixExpression ) + { + return false; + } + + // for caller simplicity, we strip of any enclosing braces + // strip the bracketing [] if present + char szCleanToken[512]; + if ( pInfixExpression[0] == '[' ) + { + int len = V_strlen( pInfixExpression ); + + // SECURITY: Bail on input buffers that are too large, they're used for RCEs and we don't + // need to support them. + if ( len + 1 > ARRAYSIZE( szCleanToken ) ) + { + return false; + } + + // SECURIY: Because this starts one character late, it picks up the null termination from pInfixExpression. + V_strncpy( szCleanToken, pInfixExpression + 1, len ); + len--; + if ( szCleanToken[len-1] == ']' ) + { + szCleanToken[len-1] = '\0'; + } + pInfixExpression = szCleanToken; + } + + // reset state + m_pExpression = pInfixExpression; + m_pGetSymbolProc = pGetSymbolProc ? pGetSymbolProc : DefaultConditionalSymbolProc; + m_pSyntaxErrorProc = pSyntaxErrorProc ? pSyntaxErrorProc : DefaultConditionalErrorProc; + m_ExprTree = 0; + m_CurPosition = 0; + m_CurToken = 0; + + // Building the expression tree will fail on bad syntax + bool bValid = BuildExpression(); + if ( bValid ) + { + bResult = SimplifyNode( m_ExprTree ); + } + + // don't leak + FreeTree( m_ExprTree ); + m_ExprTree = NULL; + + return bValid; +} + + + + + + + + diff --git a/external/vpc/tier1/generichash.cpp b/external/vpc/tier1/generichash.cpp new file mode 100644 index 0000000..16bd7b0 --- /dev/null +++ b/external/vpc/tier1/generichash.cpp @@ -0,0 +1,429 @@ +//======= 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 <stdlib.h> +#include "tier0/basetypes.h" +#include "tier0/platform.h" +#include "generichash.h" +#include <ctype.h> +#include "tier0/dbg.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// +// 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 ) +{ + unsigned even, odd; + odd = g_nRandomValues[(((unsigned)n >> 8) & 0xff)]; + even = g_nRandomValues[odd ^ ((unsigned)n >> 24)]; + odd = g_nRandomValues[even ^ ((unsigned)n >> 16) & 0xff]; + even = g_nRandomValues[odd ^ ((unsigned)n >> 8) & 0xff]; + odd = g_nRandomValues[even ^ ((unsigned)n & 0xff)]; + + return (even << 8) | odd; +} + +//----------------------------------------------------------------------------- +// 4-byte hash +//----------------------------------------------------------------------------- +unsigned FASTCALL Hash4( const void *pKey ) +{ + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; + n = *p; + 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 ) +{ + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; + n = *p; + 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 ) +{ + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; + n = *p; + 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 ) +{ + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; + n = *p; + 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; +} + + +//----------------------------------------------------------------------------- +// Murmur hash +//----------------------------------------------------------------------------- +uint32 MurmurHash2( const void * key, int len, uint32 seed ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + const uint32 m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + + uint32 h = seed ^ len; + + // Mix 4 bytes at a time into the hash + + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + uint32 k = LittleDWord( *(uint32 *)data ); + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +#define TOLOWERU( c ) ( ( uint32 ) ( ( ( c >= 'A' ) && ( c <= 'Z' ) )? c + 32 : c ) ) +uint32 MurmurHash2LowerCase( char const *pString, uint32 nSeed ) +{ + int nLen = strlen( pString ); + char *p = ( char * ) stackalloc( nLen + 1 ); + for( int i = 0; i < nLen ; i++ ) + { + p[i] = TOLOWERU( pString[i] ); + } + return MurmurHash2( p, nLen, nSeed ); +} + + +//----------------------------------------------------------------------------- +// Murmur hash, 64 bit- endian neutral +//----------------------------------------------------------------------------- +uint64 MurmurHash64( const void * key, int len, uint32 seed ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + const uint32 m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + + uint32 h1 = seed ^ len; + uint32 h2 = 0; + + // Mix 4 bytes at a time into the hash + + const uint32 * data = (const uint32 *)key; + while ( len >= 8 ) + { + uint32 k1 = LittleDWord( *data++ ); + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + + uint32 k2 = LittleDWord( *data++ ); + k2 *= m; k2 ^= k2 >> r; k2 *= m; + h2 *= m; h2 ^= k2; + len -= 4; + } + + if(len >= 4) + { + uint32 k1 = LittleDWord( *data++ ); + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + } + + // Handle the last few bytes of the input array + switch(len) + { + case 3: h2 ^= ((uint8*)data)[2] << 16; + case 2: h2 ^= ((uint8*)data)[1] << 8; + case 1: h2 ^= ((uint8*)data)[0]; + h2 *= m; + }; + + h1 ^= h2 >> 18; h1 *= m; + h2 ^= h1 >> 22; h2 *= m; + h1 ^= h2 >> 17; h1 *= m; + h2 ^= h1 >> 19; h2 *= m; + + uint64 h = h1; + + h = (h << 32) | h2; + + return h; +} + diff --git a/external/vpc/tier1/interface.cpp b/external/vpc/tier1/interface.cpp new file mode 100644 index 0000000..9e7b77b --- /dev/null +++ b/external/vpc/tier1/interface.cpp @@ -0,0 +1,705 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// +#if defined( _WIN32 ) && !defined( _X360 ) +#include <windows.h> +#endif + +#if !defined( DONT_PROTECT_FILEIO_FUNCTIONS ) +#define DONT_PROTECT_FILEIO_FUNCTIONS // for protected_things.h +#endif + +#if defined( PROTECTED_THINGS_ENABLE ) +#undef PROTECTED_THINGS_ENABLE // from protected_things.h +#endif + +#include <stdio.h> +#include "tier1/interface.h" +#include "basetypes.h" +#include "tier0/dbg.h" +#include <string.h> +#include <stdlib.h> +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "tier0/dbg.h" +#include "tier0/stacktools.h" +#include "tier0/threadtools.h" +#ifdef _WIN32 +#include <direct.h> // getcwd +#endif +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +#ifdef _PS3 +#include "sys/prx.h" +#include "tier1/utlvector.h" +#include "ps3/ps3_platform.h" +#include "ps3/ps3_win32stubs.h" +#include "ps3/ps3_helpers.h" +#include "ps3_pathinfo.h" +#elif defined(POSIX) +#include "tier0/platform.h" +#endif // _PS3 + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// ------------------------------------------------------------------------------------ // +// InterfaceReg. +// ------------------------------------------------------------------------------------ // +#ifdef POSIX +DLL_GLOBAL_EXPORT +#endif +InterfaceReg *s_pInterfaceRegs; + +InterfaceReg::InterfaceReg( InstantiateInterfaceFn fn, const char *pName ) : + m_pName(pName) +{ + m_CreateFn = fn; + m_pNext = s_pInterfaceRegs; + s_pInterfaceRegs = this; +} + +// ------------------------------------------------------------------------------------ // +// CreateInterface. +// This is the primary exported function by a dll, referenced by name via dynamic binding +// that exposes an opqaue function pointer to the interface. +// +// We have the Internal variant so Sys_GetFactoryThis() returns the correct internal +// symbol under GCC/Linux/Mac as CreateInterface is DLL_EXPORT so its global so the loaders +// on those OS's pick exactly 1 of the CreateInterface symbols to be the one that is process wide and +// all Sys_GetFactoryThis() calls find that one, which doesn't work. Using the internal walkthrough here +// makes sure Sys_GetFactoryThis() has the dll specific symbol and GetProcAddress() returns the module specific +// function for CreateInterface again getting the dll specific symbol we need. +// ------------------------------------------------------------------------------------ // +void* CreateInterfaceInternal( const char *pName, int *pReturnCode ) +{ + InterfaceReg *pCur; + + for (pCur=s_pInterfaceRegs; pCur; pCur=pCur->m_pNext) + { + if (strcmp(pCur->m_pName, pName) == 0) + { + if (pReturnCode) + { + *pReturnCode = IFACE_OK; + } + return pCur->m_CreateFn(); + } + } + + if (pReturnCode) + { + *pReturnCode = IFACE_FAILED; + } + return NULL; +} + +void* CreateInterface( const char *pName, int *pReturnCode ) +{ + return CreateInterfaceInternal( pName, pReturnCode ); +} + + + +#if defined( POSIX ) && !defined( _PS3 ) +// Linux doesn't have this function so this emulates its functionality +void *GetModuleHandle(const char *name) +{ + void *handle; + + if( name == NULL ) + { + // hmm, how can this be handled under linux.... + // is it even needed? + return NULL; + } + + if( (handle=dlopen(name, RTLD_NOW))==NULL) + { + printf("DLOPEN Error:%s\n",dlerror()); + // couldn't open this file + return NULL; + } + + // read "man dlopen" for details + // in short dlopen() inc a ref count + // so dec the ref count by performing the close + dlclose(handle); + return handle; +} +#endif + +#if defined( _WIN32 ) && !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a function, given a module +// Input : pModuleName - module name +// *pName - proc name +//----------------------------------------------------------------------------- +static void *Sys_GetProcAddress( const char *pModuleName, const char *pName ) +{ +#if defined( _PS3 ) + Assert( !"Unsupported, use HMODULE" ); + return NULL; +#else // !_PS3 + HMODULE hModule = (HMODULE)GetModuleHandle( pModuleName ); +#if defined( WIN32 ) + return (void *)GetProcAddress( hModule, pName ); +#else // !WIN32 + return (void *)dlsym( (void *)hModule, pName ); +#endif // WIN32 +#endif // _PS3 +} + +static void *Sys_GetProcAddress( HMODULE hModule, const char *pName ) +{ +#if defined( WIN32 ) + return (void *)GetProcAddress( hModule, pName ); +#elif defined( _PS3 ) + PS3_LoadAppSystemInterface_Parameters_t *pPRX = reinterpret_cast< PS3_LoadAppSystemInterface_Parameters_t * >( hModule ); + if ( !pPRX ) + return NULL; + if ( !strcmp( pName, CREATEINTERFACE_PROCNAME ) ) + return reinterpret_cast< void * >( pPRX->pfnCreateInterface ); + Assert( !"Unknown PRX function requested!" ); + return NULL; +#else + return (void *)dlsym( (void *)hModule, pName ); +#endif +} + +bool Sys_IsDebuggerPresent() +{ + return Plat_IsInDebugSession(); +} + +struct ThreadedLoadLibaryContext_t +{ + const char *m_pLibraryName; + HMODULE m_hLibrary; + DWORD m_nError; + ThreadedLoadLibaryContext_t() : m_pLibraryName(NULL), m_hLibrary(0), m_nError(0) {} +}; + +#ifdef _WIN32 + +// wraps LoadLibraryEx() since 360 doesn't support that +static HMODULE InternalLoadLibrary( const char *pName ) +{ +#if defined(_X360) + return LoadLibrary( pName ); +#else + return LoadLibraryEx( pName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); +#endif +} +unsigned ThreadedLoadLibraryFunc( void *pParam ) +{ + ThreadedLoadLibaryContext_t *pContext = (ThreadedLoadLibaryContext_t*)pParam; + pContext->m_hLibrary = InternalLoadLibrary(pContext->m_pLibraryName); + return 0; +} +#endif + + +// global to propagate a library load error from thread into Sys_LoadModule +static DWORD g_nLoadLibraryError = 0; + +static HMODULE Sys_LoadLibraryGuts( const char *pLibraryName ) +{ +#ifdef PLATFORM_PS3 + + PS3_LoadAppSystemInterface_Parameters_t *pPRX = new PS3_LoadAppSystemInterface_Parameters_t; + V_memset( pPRX, 0, sizeof( PS3_LoadAppSystemInterface_Parameters_t ) ); + pPRX->cbSize = sizeof( PS3_LoadAppSystemInterface_Parameters_t ); + int iResult = PS3_PrxLoad( pLibraryName, pPRX ); + if ( iResult < CELL_OK ) + { + delete pPRX; + return NULL; + } + return reinterpret_cast< HMODULE >( pPRX ); + +#else + + char str[1024]; + + // How to get a string out of a #define on the command line. + const char *pModuleExtension = DLL_EXT_STRING; + const char *pModuleAddition = pModuleExtension; + + V_strncpy( str, pLibraryName, sizeof(str) ); + if ( !V_stristr( str, pModuleExtension ) ) + { + if ( IsX360() ) + { + V_StripExtension( str, str, sizeof(str) ); + } + V_strncat( str, pModuleAddition, sizeof(str) ); + } + V_FixSlashes( str ); + +#ifdef _WIN32 + ThreadedLoadLibraryFunc_t threadFunc = GetThreadedLoadLibraryFunc(); + if ( !threadFunc ) + { + HMODULE retVal = InternalLoadLibrary( str ); + if( retVal ) + { + StackToolsNotify_LoadedLibrary( str ); + } +#if 0 // you can enable this block to help track down why a module isn't loading: + else + { +#ifdef _WINDOWS + char buf[1024]; + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPTSTR) buf, + 1023, + NULL // no insert arguments + ); + Warning( "Could not load %s: %s\n", str, buf ); +#endif + } +#endif + + return retVal; + } + + ThreadedLoadLibaryContext_t context; + context.m_pLibraryName = str; + context.m_hLibrary = 0; + + ThreadHandle_t h = CreateSimpleThread( ThreadedLoadLibraryFunc, &context ); + +#ifdef _X360 + ThreadSetAffinity( h, XBOX_PROCESSOR_3 ); +#endif + + unsigned int nTimeout = 0; + while( WaitForSingleObject( (HANDLE)h, nTimeout ) == WAIT_TIMEOUT ) + { + nTimeout = threadFunc(); + } + + ReleaseThreadHandle( h ); + + if( context.m_hLibrary ) + { + g_nLoadLibraryError = 0; + StackToolsNotify_LoadedLibrary( str ); + } + else + { + g_nLoadLibraryError = context.m_nError; + } + + return context.m_hLibrary; + +#elif defined( POSIX ) && !defined( _PS3 ) + HMODULE ret = (HMODULE)dlopen( str, RTLD_NOW ); + if ( ! ret ) + { + const char *pError = dlerror(); + if ( pError && ( strstr( pError, "No such file" ) == 0 ) && ( strstr( pError, "image not found") == 0 ) ) + { + Msg( " failed to dlopen %s error=%s\n", str, pError ); + + } + } + +// if( ret ) +// StackToolsNotify_LoadedLibrary( str ); + + return ret; +#endif + +#endif +} + +static HMODULE Sys_LoadLibrary( const char *pLibraryName ) +{ + // load a library. If a library suffix is set, look for the library first with that name + char *pSuffix = NULL; + + if ( CommandLine()->FindParm( "-xlsp" ) ) + { + pSuffix = "_xlsp"; + } +#ifdef POSIX + else if ( CommandLine()->FindParm( "-valveinternal" ) ) + { + pSuffix = "_valveinternal"; + } +#endif +#ifdef IS_WINDOWS_PC + else if ( CommandLine()->FindParm( "-ds" ) ) // windows DS bins + { + pSuffix = "_ds"; + } +#endif + if ( pSuffix ) + { + char nameBuf[MAX_PATH]; + strcpy( nameBuf, pLibraryName ); + char *pDot = strchr( nameBuf, '.' ); + if ( pDot ) + *pDot = 0; + V_strncat( nameBuf, pSuffix, sizeof( nameBuf ), COPY_ALL_CHARACTERS ); + HMODULE hRet = Sys_LoadLibraryGuts( nameBuf ); + if ( hRet ) + return hRet; + } + return Sys_LoadLibraryGuts( pLibraryName ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Keeps a flag if the current dll/exe loaded any debug modules +// This flag can also get set if the current process discovers any other debug +// modules loaded by other dlls +//----------------------------------------------------------------------------- +static bool s_bRunningWithDebugModules = false; + +#ifdef IS_WINDOWS_PC +//----------------------------------------------------------------------------- +// Purpose: Construct a process-specific name for kernel object to track +// if any debug modules were loaded +//----------------------------------------------------------------------------- +static void DebugKernelMemoryObjectName( char *pszNameBuffer ) +{ + sprintf( pszNameBuffer, "VALVE-MODULE-DEBUG-%08X", GetCurrentProcessId() ); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Loads a DLL/component from disk and returns a handle to it +// Input : *pModuleName - filename of the component +// Output : opaque handle to the module (hides system dependency) +//----------------------------------------------------------------------------- +CSysModule *Sys_LoadModule( const char *pModuleName ) +{ + // If using the Steam filesystem, either the DLL must be a minimum footprint + // file in the depot (MFP) or a filesystem GetLocalCopy() call must be made + // prior to the call to this routine. + HMODULE hDLL = NULL; + + char alteredFilename[ MAX_PATH ]; + if ( IsPS3() ) + { + // PS3's load module *must* be fed extensions. If the extension is missing, add it. + if (!( strstr(pModuleName, ".sprx") || strstr(pModuleName, ".prx") )) + { + strncpy( alteredFilename, pModuleName, MAX_PATH ); + strncat( alteredFilename, DLL_EXT_STRING, MAX_PATH ); + pModuleName = alteredFilename; + } + } + else + { + alteredFilename; // just to quash the warning + } + + if ( !V_IsAbsolutePath( pModuleName ) ) + { + // full path wasn't passed in, using the current working dir + char szAbsoluteModuleName[1024]; +#if defined( _PS3 ) + // getcwd not supported on ps3; use PRX PATCH path if patched + if ( g_pPS3PathInfo->IsPatched() ) + { + V_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/bin/%s", + g_pPS3PathInfo->GamePatchBasePath(), pModuleName ); + hDLL = Sys_LoadLibrary( szAbsoluteModuleName ); + } + if ( !hDLL ) // use base PRX path + { + V_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s", + g_pPS3PathInfo->PrxPath(), pModuleName ); + hDLL = Sys_LoadLibrary( szAbsoluteModuleName ); + } +#else // !_PS3 + char szCwd[1024]; + _getcwd( szCwd, sizeof( szCwd ) ); + if ( IsX360() ) + { + int i = CommandLine()->FindParm( "-basedir" ); + if ( i ) + { + strcpy( szCwd, CommandLine()->GetParm( i+1 ) ); + } + } + if (szCwd[strlen(szCwd) - 1] == '/' || szCwd[strlen(szCwd) - 1] == '\\' ) + { + szCwd[strlen(szCwd) - 1] = 0; + } + + size_t cCwd = strlen( szCwd ); + if ( strstr( pModuleName, "bin/") == pModuleName || ( szCwd[ cCwd - 1 ] == 'n' && szCwd[ cCwd - 2 ] == 'i' && szCwd[ cCwd - 3 ] == 'b' ) ) + { + // don't make bin/bin path + V_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s", szCwd, pModuleName ); + } + else + { + V_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/bin/%s", szCwd, pModuleName ); + } + hDLL = Sys_LoadLibrary( szAbsoluteModuleName ); +#endif // _PS3 + } + + if ( !hDLL ) + { + // full path failed, let LoadLibrary() try to search the PATH now + hDLL = Sys_LoadLibrary( pModuleName ); +#if defined( _DEBUG ) + if ( !hDLL ) + { +// So you can see what the error is in the debugger... +#if defined( _WIN32 ) && !defined( _X360 ) + char *lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + LocalFree( (HLOCAL)lpMsgBuf ); +#elif defined( _X360 ) + DWORD error = g_nLoadLibraryError ? g_nLoadLibraryError : GetLastError(); + Msg( "Error(%d) - Failed to load %s:\n", error, pModuleName ); +#elif defined( _PS3 ) + Msg( "Failed to load %s:\n", pModuleName ); +#else + Msg( "Failed to load %s: %s\n", pModuleName, dlerror() ); +#endif // _WIN32 + } +#endif // DEBUG + } + + // If running in the debugger, assume debug binaries are okay, otherwise they must run with -allowdebug + if ( !IsGameConsole() && Sys_GetProcAddress( hDLL, "BuiltDebug" ) ) + { + if ( hDLL && !CommandLine()->FindParm( "-allowdebug" ) && + !Sys_IsDebuggerPresent() ) + { + Error( "Module %s is a debug build\n", pModuleName ); + } + + DevWarning( "Module %s is a debug build\n", pModuleName ); + + if ( !s_bRunningWithDebugModules ) + { + s_bRunningWithDebugModules = true; + +#ifdef IS_WINDOWS_PC + char chMemoryName[ MAX_PATH ]; + DebugKernelMemoryObjectName( chMemoryName ); + + (void) CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, chMemoryName ); + // Created a shared memory kernel object specific to process id + // Existence of this object indicates that we have debug modules loaded +#endif + } + } + + return reinterpret_cast<CSysModule *>(hDLL); +} + +//----------------------------------------------------------------------------- +// Purpose: Determine if any debug modules were loaded +//----------------------------------------------------------------------------- +bool Sys_RunningWithDebugModules() +{ + if ( !s_bRunningWithDebugModules ) + { +#ifdef IS_WINDOWS_PC + char chMemoryName[ MAX_PATH ]; + DebugKernelMemoryObjectName( chMemoryName ); + + HANDLE hObject = OpenFileMapping( FILE_MAP_READ, FALSE, chMemoryName ); + if ( hObject && hObject != INVALID_HANDLE_VALUE ) + { + CloseHandle( hObject ); + s_bRunningWithDebugModules = true; + } +#endif + } + return s_bRunningWithDebugModules; +} + + +//----------------------------------------------------------------------------- +// Purpose: Unloads a DLL/component from +// Input : *pModuleName - filename of the component +// Output : opaque handle to the module (hides system dependency) +//----------------------------------------------------------------------------- +void Sys_UnloadModule( CSysModule *pModule ) +{ + if ( !pModule ) + return; + + HMODULE hDLL = reinterpret_cast<HMODULE>(pModule); + +#ifdef _WIN32 + FreeLibrary( hDLL ); +#elif defined( _PS3 ) + PS3_PrxUnload( ( ( PS3_PrxLoadParametersBase_t *)pModule )->sysPrxId ); + delete ( PS3_PrxLoadParametersBase_t *)pModule; +#elif defined( POSIX ) + dlclose((void *)hDLL); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a function, given a module +// Input : module - windows HMODULE from Sys_LoadModule() +// *pName - proc name +// Output : factory for this module +//----------------------------------------------------------------------------- +CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ) +{ + if ( !pModule ) + return NULL; + + HMODULE hDLL = reinterpret_cast<HMODULE>(pModule); +#ifdef _WIN32 + return reinterpret_cast<CreateInterfaceFn>(GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME )); +#elif defined( _PS3 ) + return reinterpret_cast<CreateInterfaceFn>(Sys_GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME )); +#elif defined( POSIX ) + // Linux gives this error: + //../public/interface.cpp: In function `IBaseInterface *(*Sys_GetFactory + //(CSysModule *)) (const char *, int *)': + //../public/interface.cpp:154: ISO C++ forbids casting between + //pointer-to-function and pointer-to-object + // + // so lets get around it :) + return (CreateInterfaceFn)(GetProcAddress( (void *)hDLL, CREATEINTERFACE_PROCNAME )); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: returns the instance of this module +// Output : interface_instance_t +//----------------------------------------------------------------------------- +CreateInterfaceFn Sys_GetFactoryThis( void ) +{ + return &CreateInterfaceInternal; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the instance of the named module +// Input : *pModuleName - name of the module +// Output : interface_instance_t - instance of that module +//----------------------------------------------------------------------------- +CreateInterfaceFn Sys_GetFactory( const char *pModuleName ) +{ +#ifdef _WIN32 + return static_cast<CreateInterfaceFn>( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) ); +#elif defined( _PS3 ) + Assert( 0 ); + return NULL; +#elif defined(POSIX) + // see Sys_GetFactory( CSysModule *pModule ) for an explanation + return (CreateInterfaceFn)( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: get the interface for the specified module and version +// Input : +// Output : +//----------------------------------------------------------------------------- +bool Sys_LoadInterface( + const char *pModuleName, + const char *pInterfaceVersionName, + CSysModule **pOutModule, + void **pOutInterface ) +{ + CSysModule *pMod = Sys_LoadModule( pModuleName ); + if ( !pMod ) + return false; + + CreateInterfaceFn fn = Sys_GetFactory( pMod ); + if ( !fn ) + { + Sys_UnloadModule( pMod ); + return false; + } + + *pOutInterface = fn( pInterfaceVersionName, NULL ); + if ( !( *pOutInterface ) ) + { + Sys_UnloadModule( pMod ); + return false; + } + + if ( pOutModule ) + *pOutModule = pMod; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Place this as a singleton at module scope (e.g.) and use it to get the factory from the specified module name. +// +// When the singleton goes out of scope (.dll unload if at module scope), +// then it'll call Sys_UnloadModule on the module so that the refcount is decremented +// and the .dll actually can unload from memory. +//----------------------------------------------------------------------------- +CDllDemandLoader::CDllDemandLoader( char const *pchModuleName ) : + m_pchModuleName( pchModuleName ), + m_hModule( 0 ), + m_bLoadAttempted( false ) +{ +} + +CDllDemandLoader::~CDllDemandLoader() +{ + Unload(); +} + +CreateInterfaceFn CDllDemandLoader::GetFactory() +{ + if ( !m_hModule && !m_bLoadAttempted ) + { + m_bLoadAttempted = true; + m_hModule = Sys_LoadModule( m_pchModuleName ); + } + + if ( !m_hModule ) + { + return NULL; + } + + return Sys_GetFactory( m_hModule ); +} + +void CDllDemandLoader::Unload() +{ + if ( m_hModule ) + { + Sys_UnloadModule( m_hModule ); + m_hModule = 0; + } +} diff --git a/external/vpc/tier1/keyvalues.cpp b/external/vpc/tier1/keyvalues.cpp new file mode 100644 index 0000000..3012ba8 --- /dev/null +++ b/external/vpc/tier1/keyvalues.cpp @@ -0,0 +1,3427 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if defined( _WIN32 ) && !defined( _X360 ) +#include <windows.h> // for widechartomultibyte and multibytetowidechar +#elif defined(POSIX) +#include <wchar.h> // wcslen() +#define _alloca alloca +#define _wtoi(arg) wcstol(arg, NULL, 10) +#define _wtoi64(arg) wcstoll(arg, NULL, 10) +#endif + +#include <keyvalues.h> +#include "filesystem.h" +#include <vstdlib/ikeyvaluessystem.h> + +#include <color.h> +#include <stdlib.h> +#include <ctype.h> +#include "tier1/convar.h" +#include "tier0/dbg.h" +#include "tier0/mem.h" +#include "utlvector.h" +#include "utlbuffer.h" +#include "utlhash.h" +#include <vstdlib/vstrtools.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//////// VPROF? ////////////////// +// For an example of how to mark up this file with VPROF nodes, see +// changelist 702984. However, be aware that calls to FindKey and Init +// may occur outside of Vprof's usual hierarchy, which can cause strange +// duplicate KeyValues::FindKey nodes at the root level and other +// confusing effects. +////////////////////////////////// + +static char * s_LastFileLoadingFrom = "unknown"; // just needed for error messages + +// 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 1024 +static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; + +#define INTERNALWRITE( pData, len ) InternalWrite( filesystem, f, pBuf, pData, len ) + +#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( uint16 )x2) << 8 ) | (uint8)(x1)) +#define SPLIT_3_BYTES_INTO_1_AND_2( x1, x2, x3 ) do { x1 = (uint8)(x3); x2 = (uint16)( (x3) >> 8 ); } while( 0 ) + +CExpressionEvaluator g_ExpressionEvaluator; + +// 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( int 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, int 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: + int 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() + { + g_KeyValuesErrorStack.Pop(); + } + explicit CKeyErrorContext( int symName ) + { + Init( symName ); + } + void Reset( int symName ) + { + g_KeyValuesErrorStack.Reset( m_stackLevel, symName ); + } +private: + void Init( int 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; + V_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: An arbitrarily growable string table for KeyValues key names. +// See the comment in the header for more info. +//----------------------------------------------------------------------------- +class CKeyValuesGrowableStringTable +{ +public: + // Constructor + CKeyValuesGrowableStringTable() : + m_vecStrings( 0, 512 * 1024 ), + 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<int, CLookupFunctor &, CLookupFunctor &> m_hashLookup; + CUtlVector<char> 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); + } +} + + +//----------------------------------------------------------------------------- +// 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 +//----------------------------------------------------------------------------- +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 = 0; + m_iKeyNameCaseSensitive1 = 0; + m_iKeyNameCaseSensitive2 = 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 = 0; +} + +//----------------------------------------------------------------------------- +// 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 this ? KeyValuesSystem()->GetStringForSymbol( MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) ) : ""; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the symbol name of the current key section +//----------------------------------------------------------------------------- +int KeyValues::GetNameSymbol() const +{ + return this ? m_iKeyName : INVALID_KEY_SYMBOL; +} + +int KeyValues::GetNameSymbolCaseSensitive() const +{ + return this ? MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) : INVALID_KEY_SYMBOL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Read a single token from buffer (0 terminated) +//----------------------------------------------------------------------------- +#pragma warning (disable:4706) +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 ) + { + bConditionalStart = false; + wasConditional = true; + } + + // conditionals need to get tokenized as delimited by [] + // othwerwise break on whitespace + if ( V_isspace(*c) && !bConditionalStart ) + 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; +} +#pragma warning (default:4706) + + + +//----------------------------------------------------------------------------- +// 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 ) +{ +#if defined( _WIN32 ) + Assert( IsGameConsole() || ( _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 + 2 ); + + 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 + buffer[fileSize+1] = 0; // double NULL terminating in case this is a unicode file + + CUtlBuffer utlBuffer; + if ( IsGameConsole() && (unsigned int)((unsigned char *)buffer)[0] == KV_BINARY_POOLED_FORMAT ) + { + // kv contents are compiled binary + utlBuffer.SetExternalBuffer( buffer, bufSize, fileSize, CUtlBuffer::READ_ONLY ); + } + else + { + // kv contents are text + int nLen = V_strlen( buffer ); + utlBuffer.SetExternalBuffer( buffer, bufSize, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); + } + + bRetOK = LoadFromBuffer( resourceName, utlBuffer, 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( "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 = V_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: + { + if ( dat->m_wsValue ) + { + static char buf[KEYVALUES_TOKEN_SIZE]; + // make sure we have enough space + int result = V_UnicodeToUTF8( dat->m_wsValue, buf, KEYVALUES_TOKEN_SIZE); + if (result) + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), V_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(), V_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + char buf[32]; + V_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue); + + INTERNALWRITE(buf, V_strlen(buf)); + INTERNALWRITE("\"\n", 2); + break; + } + + case TYPE_UINT64: + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + char buf[32]; + // write "0x" + 16 char 0-padded hex encoded 64 bit value + V_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) ); + + INTERNALWRITE(buf, V_strlen(buf)); + INTERNALWRITE("\"\n", 2); + break; + } + + case TYPE_FLOAT: + { + WriteIndents(filesystem, f, pBuf, indentLevel + 1); + INTERNALWRITE("\"", 1); + INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName())); + INTERNALWRITE("\"\t\t\"", 4); + + char buf[48]; + V_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue); + + INTERNALWRITE(buf, V_strlen(buf)); + INTERNALWRITE("\"\n", 2); + break; + } + case TYPE_COLOR: + DevMsg( "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(int keySymbol) const +{ + for (KeyValues *dat = this ? m_pSub : NULL; dat != NULL; dat = dat->m_pPeer) + { + if ( dat->m_iKeyName == (uint32) 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) +{ + // Validate NULL == this early out + if ( !this ) + { + Assert( !bCreate ); + return NULL; + } + + // 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; + V_memcpy( szBuf, keyName, size ); + szBuf[size] = 0; + searchStr = szBuf; + } + + // lookup the symbol for the search string, + // we do not need the case-sensitive symbol at this time + // because if the key is found, then it will be found by case-insensitive lookup + // if the key is not found and needs to be created we will pass the actual searchStr + // and have the new KeyValues constructor get/create the case-sensitive symbol + 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 == ( uint32 ) 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]; + V_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; +} + +void KeyValues::InsertSubKey( int nIndex, KeyValues *pSubKey ) +{ + // Sub key must be valid and not part of another chain + Assert( pSubKey && pSubKey->m_pPeer == NULL ); + + if ( nIndex == 0 ) + { + pSubKey->m_pPeer = m_pSub; + m_pSub = pSubKey; + return; + } + else + { + int nCurrentIndex = 0; + for ( KeyValues *pIter = GetFirstSubKey(); pIter != NULL; pIter = pIter->GetNextKey() ) + { + ++ nCurrentIndex; + if ( nCurrentIndex == nIndex) + { + pSubKey->m_pPeer = pIter->m_pPeer; + pIter->m_pPeer = pSubKey; + return; + } + } + // Index is out of range if we get here + Assert( 0 ); + return; + } +} + +bool KeyValues::ContainsSubKey( KeyValues *pSubKey ) +{ + for ( KeyValues *pIter = GetFirstSubKey(); pIter != NULL; pIter = pIter->GetNextKey() ) + { + if ( pSubKey == pIter ) + { + return true; + } + } + return false; +} + +void KeyValues::SwapSubKey( KeyValues *pExistingSubkey, KeyValues *pNewSubKey ) +{ + Assert( pExistingSubkey != NULL && pNewSubKey != NULL ); + + // Make sure the new sub key isn't a child of some other keyvalues + Assert( pNewSubKey->m_pPeer == NULL ); + + // Check the list pointer + if ( m_pSub == pExistingSubkey ) + { + pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; + pExistingSubkey->m_pPeer = NULL; + m_pSub = pNewSubKey; + } + else + { + // Look through the list + KeyValues *kv = m_pSub; + while ( kv->m_pPeer ) + { + if ( kv->m_pPeer == pExistingSubkey ) + { + pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; + pExistingSubkey->m_pPeer = NULL; + kv->m_pPeer = pNewSubKey; + break; + } + + kv = kv->m_pPeer; + } + // Existing sub key should always be found, otherwise it's a bug in the calling code. + Assert( kv->m_pPeer != NULL ); + } +} + +void KeyValues::ElideSubKey( KeyValues *pSubKey ) +{ + // This pointer's "next" pointer needs to be fixed up when we elide the key + KeyValues **ppPointerToFix = &m_pSub; + for ( KeyValues *pKeyIter = m_pSub; pKeyIter != NULL; ppPointerToFix = &pKeyIter->m_pPeer, pKeyIter = pKeyIter->GetNextKey() ) + { + if ( pKeyIter == pSubKey ) + { + if ( pSubKey->m_pSub == NULL ) + { + // No children, simply remove the key + *ppPointerToFix = pSubKey->m_pPeer; + pSubKey->deleteThis(); + } + else + { + *ppPointerToFix = pSubKey->m_pSub; + // Attach the remainder of this chain to the last child of pSubKey + KeyValues *pChildIter = pSubKey->m_pSub; + while ( pChildIter->m_pPeer != NULL ) + { + pChildIter = pChildIter->m_pPeer; + } + // Now points to the last child of pSubKey + pChildIter->m_pPeer = pSubKey->m_pPeer; + // Detach the node to be elided + pSubKey->m_pSub = NULL; + pSubKey->m_pPeer = NULL; + pSubKey->deleteThis(); + } + return; + } + } + // Key not found; that's caller error. + Assert( 0 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the first subkey in the list +//----------------------------------------------------------------------------- +KeyValues *KeyValues::GetFirstSubKey() +{ + return this ? m_pSub : NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the next subkey +//----------------------------------------------------------------------------- +KeyValues *KeyValues::GetNextKey() +{ + return this ? m_pPeer : NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets this key's peer to the KeyValues passed in +//----------------------------------------------------------------------------- +void KeyValues::SetNextKey( KeyValues *pDat ) +{ + m_pPeer = pDat; +} + + +KeyValues* KeyValues::GetFirstTrueSubKey() +{ + KeyValues *pRet = this ? m_pSub : NULL; + while ( pRet && pRet->m_iDataType != TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + +KeyValues* KeyValues::GetNextTrueSubKey() +{ + KeyValues *pRet = this ? m_pPeer : NULL; + while ( pRet && pRet->m_iDataType != TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + +KeyValues* KeyValues::GetFirstValue() +{ + KeyValues *pRet = this ? m_pSub : NULL; + while ( pRet && pRet->m_iDataType == TYPE_NONE ) + pRet = pRet->m_pPeer; + + return pRet; +} + +KeyValues* KeyValues::GetNextValue() +{ + KeyValues *pRet = this ? m_pPeer : NULL; + 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: + return _wtoi(dat->m_wsValue); + 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: + { + uint64 uiResult = 0ull; + sscanf( dat->m_sValue, "%lld", &uiResult ); + return uiResult; + } + case TYPE_WSTRING: + { + uint64 uiResult = 0ull; + swscanf( dat->m_wsValue, L"%lld", &uiResult ); + return uiResult; + } + 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 + return (float) wcstof( dat->m_wsValue, (wchar_t **)NULL ); +#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: + V_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue ); + SetString( keyName, buf ); + break; + case TYPE_INT: + case TYPE_PTR: + V_snprintf( buf, sizeof( buf ), "%d", dat->m_iValue ); + SetString( keyName, buf ); + break; + case TYPE_UINT64: + V_snprintf( buf, sizeof( buf ), "%lld", *((uint64 *)(dat->m_sValue)) ); + SetString( keyName, buf ); + break; + + case TYPE_WSTRING: + { + // convert the string to char *, set it for future use, and return it + char wideBuf[512]; + int result = V_UnicodeToUTF8(dat->m_wsValue, wideBuf, 512); + if ( result ) + { + // note: this will copy wideBuf + SetString( keyName, wideBuf ); + } + else + { + return defaultValue; + } + 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) +{ + KeyValues *dat = FindKey( keyName, false ); + if ( dat ) + { + wchar_t wbuf[64]; + switch ( dat->m_iDataType ) + { + case TYPE_FLOAT: + swprintf(wbuf, V_ARRAYSIZE(wbuf), L"%f", dat->m_flValue); + SetWString( keyName, wbuf); + break; + case TYPE_INT: + case TYPE_PTR: + swprintf( wbuf, V_ARRAYSIZE(wbuf), L"%d", dat->m_iValue ); + SetWString( keyName, wbuf ); + break; + case TYPE_UINT64: + { + swprintf( wbuf, V_ARRAYSIZE(wbuf), L"%lld", *((uint64 *)(dat->m_sValue)) ); + SetWString( keyName, wbuf ); + } + break; + + case TYPE_WSTRING: + break; + case TYPE_STRING: + { + int bufSize = V_strlen(dat->m_sValue) + 1; + wchar_t *pWBuf = new wchar_t[ bufSize ]; + int result = V_UTF8ToUnicode(dat->m_sValue, pWBuf, bufSize * sizeof( wchar_t ) ); + if ( result >= 0 ) // may be a zero length string + { + SetWString( keyName, pWBuf); + } + else + { + delete [] pWBuf; + return defaultValue; + } + delete [] pWBuf; + break; + } + default: + return defaultValue; + }; + + return (const wchar_t* )dat->m_wsValue; + } + 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] = (unsigned char)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 = V_strlen( strValue ); + m_sValue = new char[len + 1]; + V_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 ) +{ + if ( KeyValues *dat = FindKey( keyName, true ) ) + { + dat->SetStringValue( value ); + } +} + +//----------------------------------------------------------------------------- +// 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]; + V_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 ) +{ + HKeySymbol hCaseSensitiveKeyName = INVALID_KEY_SYMBOL, hCaseInsensitiveKeyName = INVALID_KEY_SYMBOL; + hCaseSensitiveKeyName = KeyValuesSystem()->GetSymbolForStringCaseSensitive( hCaseInsensitiveKeyName, setName ); + + m_iKeyName = hCaseInsensitiveKeyName; + SPLIT_3_BYTES_INTO_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2, hCaseSensitiveKeyName ); +} + +//----------------------------------------------------------------------------- +// 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.m_iKeyName; + m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1; + m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2; + + 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 = V_strlen(src.m_sValue) + 1; + m_sValue = new char[len]; + V_strncpy( m_sValue, src.m_sValue, len ); + } + break; + case TYPE_INT: + { + m_iValue = src.m_iValue; + V_snprintf( buf,sizeof(buf), "%d", m_iValue ); + int len = V_strlen(buf) + 1; + m_sValue = new char[len]; + V_strncpy( m_sValue, buf, len ); + } + break; + case TYPE_FLOAT: + { + m_flValue = src.m_flValue; + V_snprintf( buf,sizeof(buf), "%f", m_flValue ); + int len = V_strlen(buf) + 1; + m_sValue = new char[len]; + V_strncpy( m_sValue, buf, len ); + } + break; + case TYPE_PTR: + { + m_pValue = src.m_pValue; + } + break; + case TYPE_UINT64: + { + m_sValue = new char[sizeof(uint64)]; + V_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 = V_strlen( m_sValue ); + Assert( !newKeyValue->m_sValue ); + newKeyValue->m_sValue = new char[len + 1]; + V_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]; + V_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)]; + V_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 ]; + V_strncpy( fullpath, resourceName, sizeof( fullpath ) ); + + // Strip off characters back to start or first / + bool done = false; + int len = V_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 + V_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 ( !V_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 expression string evaluates to true or false +//----------------------------------------------------------------------------- +bool KeyValues::EvaluateConditional( const char *pExpressionString, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + // evaluate the infix expression, calling the symbol proc to resolve each symbol's value + bool bResult = false; + bool bValid = g_ExpressionEvaluator.Evaluate( bResult, pExpressionString, pfnEvaluateSymbolProc ); + if ( !bValid ) + { + g_KeyValuesErrorStack.ReportError( "KV Conditional Evaluation Error" ); + } + + return bResult; +} + +// prevent two threads from entering this at the same time and trying to share the global error reporting and parse buffers +static CThreadFastMutex g_KVMutex; +//----------------------------------------------------------------------------- +// Read from a buffer... +//----------------------------------------------------------------------------- +bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + AUTO_LOCK_FM( g_KVMutex ); + + if ( IsGameConsole() ) + { + // Let's not crash if the buffer is empty + unsigned char *pData = buf.Size() > 0 ? (unsigned char *)buf.PeekGet() : NULL; + if ( pData && (unsigned int)pData[0] == KV_BINARY_POOLED_FORMAT ) + { + // skip past binary marker + buf.GetUnsignedChar(); + // get the pool identifier, allows the fs to bind the expected string pool + unsigned int poolKey = buf.GetUnsignedInt(); + + RemoveEverything(); + Init(); + + return ReadAsBinaryPooledFormat( buf, pFileSystem, poolKey, 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 ) + break; + + if ( !wasQuoted && *s == '\0' ) + { + // non quoted empty strings stop parsing + // quoted empty strings are allowed to support unnnamed KV sections + break; + } + + if ( !V_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 ( !V_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; + + if ( IsGameConsole() && (unsigned int)((unsigned char *)pBuffer)[0] == KV_BINARY_POOLED_FORMAT ) + { + // bad, got a binary compiled KV file through an unexpected text path + // not all paths support binary compiled kv, needs to get fixed + // need to have caller supply buffer length (strlen not valid), this interface change was never plumbed + Warning( "ERROR! Binary compiled KV '%s' in an unexpected handler\n", resourceName ); + Assert( 0 ); + return false; + } + + int nLen = V_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 = new char[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, pfnEvaluateSymbolProc ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + CKeyErrorContext errorReport( GetNameSymbolCaseSensitive() ); + 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->GetNameSymbolCaseSensitive() ); + + // 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 = V_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 ); + 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; + } + 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 && !bOverflow) + { + 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]; + V_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 ) const +{ + 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 ( const 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: + { + int nLength = dat->m_wsValue ? V_wcslen( dat->m_wsValue ) : 0; + buffer.PutShort( nLength ); + for( int k = 0; k < nLength; ++ k ) + { + buffer.PutShort( ( unsigned short ) dat->m_wsValue[k] ); + } + break; + } + + case TYPE_INT: + { + buffer.PutInt( dat->m_iValue ); + break; + } + + case TYPE_UINT64: + { + buffer.PutInt64( *((int64 *)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.PutUnsignedInt( (int)dat->m_pValue ); + break; + } + + 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 = V_strlen( token ); + dat->m_sValue = new char[len + 1]; + V_memcpy( dat->m_sValue, token, len+1 ); + + break; + } + case TYPE_WSTRING: + { + int nLength = buffer.GetShort(); + + dat->m_wsValue = new wchar_t[nLength + 1]; + + for( int k = 0; k < nLength; ++ k ) + { + dat->m_wsValue[k] = buffer.GetShort(); + } + dat->m_wsValue[ nLength ] = 0; + break; + } + + case TYPE_INT: + { + dat->m_iValue = buffer.GetInt(); + break; + } + + case TYPE_UINT64: + { + dat->m_sValue = new char[sizeof(uint64)]; + *((uint64 *)dat->m_sValue) = buffer.GetInt64(); + break; + } + + 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 = (void*)buffer.GetUnsignedInt(); + break; + } + + 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(); +} + +//----------------------------------------------------------------------------- +// Alternate dense binary format that pools all the strings, the xbox supports +// this during creation of each mod dir's zip processing of kv files. +//----------------------------------------------------------------------------- +bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *pFileSystem, unsigned int poolKey, GetSymbolProc_t pfnEvaluateSymbolProc ) +{ + // xbox only support + if ( !IsGameConsole() ) + { + Assert( 0 ); + return false; + } + + if ( buffer.IsText() ) // must be a binary buffer + return false; + + if ( !buffer.IsValid() ) // must be valid, no overflows etc + return false; + + 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; + + unsigned int stringKey = buffer.GetUnsignedInt(); + if ( !((IFileSystem*)pFileSystem)->GetStringFromKVPool( poolKey, stringKey, token, sizeof( token ) ) ) + return false; + dat->SetName( token ); + + switch ( type ) + { + case TYPE_NONE: + { + dat->m_pSub = new KeyValues( "" ); + if ( !dat->m_pSub->ReadAsBinaryPooledFormat( buffer, pFileSystem, poolKey, pfnEvaluateSymbolProc ) ) + return false; + break; + } + + case TYPE_STRING: + { + unsigned int stringKey = buffer.GetUnsignedInt(); + if ( !((IFileSystem*)pFileSystem)->GetStringFromKVPool( poolKey, stringKey, token, sizeof( token ) ) ) + return false; + int len = V_strlen( token ); + dat->m_sValue = new char[len + 1]; + V_memcpy( dat->m_sValue, token, len+1 ); + break; + } + + case TYPE_WSTRING: + { + int nLength = buffer.GetShort(); + dat->m_wsValue = new wchar_t[nLength + 1]; + for ( int k = 0; k < nLength; ++k ) + { + dat->m_wsValue[k] = buffer.GetShort(); + } + dat->m_wsValue[nLength] = 0; + break; + } + + case TYPE_INT: + { + dat->m_iValue = buffer.GetInt(); + break; + } + + case TYPE_UINT64: + { + dat->m_sValue = new char[sizeof(uint64)]; + *((uint64 *)dat->m_sValue) = buffer.GetInt64(); + break; + } + + 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 = (void*)buffer.GetUnsignedInt(); + break; + } + + case TYPE_COMPILED_INT_0: + { + // only for dense storage purposes, flip back to preferred internal format + dat->m_iDataType = TYPE_INT; + dat->m_iValue = 0; + break; + } + + case TYPE_COMPILED_INT_1: + { + // only for dense storage purposes, flip back to preferred internal format + dat->m_iDataType = TYPE_INT; + dat->m_iValue = 1; + break; + } + + case TYPE_COMPILED_INT_BYTE: + { + // only for dense storage purposes, flip back to preferred internal format + dat->m_iDataType = TYPE_INT; + dat->m_iValue = buffer.GetChar(); + break; + } + + default: + break; + } + + if ( !buffer.IsValid() ) // error occured + return false; + + if ( !buffer.GetBytesRemaining() ) + break; + + 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" + +//----------------------------------------------------------------------------- +// Purpose: memory allocator +//----------------------------------------------------------------------------- +void *KeyValues::operator new( size_t iAllocSize ) +{ + MEM_ALLOC_CREDIT(); + return KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize); +} + +void *KeyValues::operator new( size_t iAllocSize, int nBlockUse, const char *pFileName, int nLine ) +{ + MemAlloc_PushAllocDbgInfo( pFileName, nLine ); + void *p = KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize); + MemAlloc_PopAllocDbgInfo(); + return p; +} + +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: deallocator +//----------------------------------------------------------------------------- +void KeyValues::operator delete( void *pMem ) +{ + KeyValuesSystem()->FreeKeyValuesMemory(pMem); +} + +void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine ) +{ + KeyValuesSystem()->FreeKeyValuesMemory(pMem); +} + +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; + char const *pDefault = ""; + if ( pUnpackTable->m_pKeyDefault ) + { + pDefault = pUnpackTable->m_pKeyDefault; + } + strncpy( dest_s, + GetString( pUnpackTable->m_pKeyName, pDefault ), + 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 ( V_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 = V_stristr( normalKeyName, pResString ); + if ( pString && !V_stricmp( pString, pResString ) ) + { + *pString = '\0'; + + // find and delete the original key (if any) + KeyValues *pKey = FindKey( normalKeyName ); + if ( pKey ) + { + // remove the key + RemoveSubKey( pKey ); + pKey->deleteThis(); + } + + // rename the marked key + pSubKey->SetName( normalKeyName ); + } + } + } + + return true; +} + +// +// KeyValues merge operations +// + +void KeyValues::MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp /* = MERGE_KV_ALL */ ) +{ + if ( !this || !kvMerge ) + return; + + switch ( eOp ) + { + case MERGE_KV_ALL: + MergeFrom( kvMerge->FindKey( "update" ), MERGE_KV_UPDATE ); + MergeFrom( kvMerge->FindKey( "delete" ), MERGE_KV_DELETE ); + MergeFrom( kvMerge->FindKey( "borrow" ), MERGE_KV_BORROW ); + return; + + case MERGE_KV_UPDATE: + { + for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) + { + char const *szName = sub->GetName(); + + KeyValues *subStorage = this->FindKey( szName, false ); + if ( !subStorage ) + { + AddSubKey( sub->MakeCopy() ); + } + else + { + subStorage->MergeFrom( sub, eOp ); + } + } + for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() ) + { + char const *szName = val->GetName(); + + if ( KeyValues *valStorage = this->FindKey( szName, false ) ) + { + this->RemoveSubKey( valStorage ); + valStorage->deleteThis(); + } + this->AddSubKey( val->MakeCopy() ); + } + } + return; + + case MERGE_KV_BORROW: + { + for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) + { + char const *szName = sub->GetName(); + + KeyValues *subStorage = this->FindKey( szName, false ); + if ( !subStorage ) + continue; + + subStorage->MergeFrom( sub, eOp ); + } + for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() ) + { + char const *szName = val->GetName(); + + if ( KeyValues *valStorage = this->FindKey( szName, false ) ) + { + this->RemoveSubKey( valStorage ); + valStorage->deleteThis(); + } + else + continue; + + this->AddSubKey( val->MakeCopy() ); + } + } + return; + + case MERGE_KV_DELETE: + { + for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) + { + char const *szName = sub->GetName(); + if ( KeyValues *subStorage = this->FindKey( szName, false ) ) + { + subStorage->MergeFrom( sub, eOp ); + } + } + for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() ) + { + char const *szName = val->GetName(); + + if ( KeyValues *valStorage = this->FindKey( szName, false ) ) + { + this->RemoveSubKey( valStorage ); + valStorage->deleteThis(); + } + } + } + return; + } +} + + +// +// KeyValues from string parsing +// + +static char const * ParseStringToken( char const *szStringVal, char const **ppEndOfParse ) +{ + // Eat whitespace + while ( V_isspace( *szStringVal ) ) + ++ szStringVal; + + char const *pszResult = szStringVal; + + while ( *szStringVal && !V_isspace( *szStringVal ) ) + ++ szStringVal; + + if ( ppEndOfParse ) + { + *ppEndOfParse = szStringVal; + } + + return pszResult; +} + +KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, char const **ppEndOfParse ) +{ + if ( !szName ) + szName = ""; + + if ( !szStringVal ) + szStringVal = ""; + + KeyValues *kv = new KeyValues( szName ); + if ( !kv ) + return NULL; + + char chName[256] = {0}; + char chValue[256] = {0}; + + for ( ; ; ) + { + char const *szEnd; + + char const *szVarValue = NULL; + char const *szVarName = ParseStringToken( szStringVal, &szEnd ); + if ( !*szVarName ) + break; + if ( *szVarName == '}' ) + { + szStringVal = szVarName + 1; + break; + } + V_strncpy( chName, szVarName, MIN( sizeof( chName ), szEnd - szVarName + 1 ) ); + szVarName = chName; + szStringVal = szEnd; + + if ( *szVarName == '{' ) + { + szVarName = ""; + goto do_sub_key; + } + + szVarValue = ParseStringToken( szStringVal, &szEnd ); + if ( *szVarValue == '}' ) + { + szStringVal = szVarValue + 1; + kv->SetString( szVarName, "" ); + break; + } + V_strncpy( chValue, szVarValue, MIN( sizeof( chValue ), szEnd - szVarValue + 1 ) ); + szVarValue = chValue; + szStringVal = szEnd; + + if ( *szVarValue == '{' ) + { + goto do_sub_key; + } + + // Try to recognize some known types + if ( char const *szInt = StringAfterPrefix( szVarValue, "#int#" ) ) + { + kv->SetInt( szVarName, atoi( szInt ) ); + } + else if ( !V_stricmp( szVarValue, "#empty#" ) ) + { + kv->SetString( szVarName, "" ); + } + else + { + kv->SetString( szVarName, szVarValue ); + } + continue; + +do_sub_key: + { + KeyValues *pSubKey = KeyValues::FromString( szVarName, szStringVal, &szEnd ); + if ( pSubKey ) + { + kv->AddSubKey( pSubKey ); + } + szStringVal = szEnd; + continue; + } + } + + if ( ppEndOfParse ) + { + *ppEndOfParse = szStringVal; + } + + return kv; +} + + + +// +// 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; +#if 0 // this code was accidentally stubbed out by a mis-integration in CL722860; it hasn't been tested + { + int n = val->GetDataType(); + char *chBuffer = ( char * ) stackalloc( 128 ); + V_snprintf( chBuffer, 128, "??kvtype[%d]", n ); + if ( !KvWriteText( chBuffer ) ) + return false; + } + break; +#endif + } + + 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( "%s", szText ); + } + else + { + Msg( "%s", szText ); + } + return true; +} + + + diff --git a/external/vpc/tier1/mempool.cpp b/external/vpc/tier1/mempool.cpp new file mode 100644 index 0000000..ddfcb91 --- /dev/null +++ b/external/vpc/tier1/mempool.cpp @@ -0,0 +1,336 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#include "tier1/mempool.h" +#include <stdio.h> +#include <memory.h> +#include "tier0/dbg.h" +#include <ctype.h> +#include "tier1/strtools.h" + +#ifndef _PS3 +#include <malloc.h> +#endif + +// 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 < 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(); +} + + +//----------------------------------------------------------------------------- +// Is an allocation within the pool? +//----------------------------------------------------------------------------- +bool CUtlMemoryPool::IsAllocationWithinPool( void *pMem ) const +{ + // Free everything.. + for( CBlob *pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pCur->m_pNext ) + { + // Is the allocation within the blob? + if ( ( pMem < pCur->m_Data ) || ( pMem >= pCur->m_Data + pCur->m_NumBytes ) ) + continue; + + // Make sure the allocation is on a block boundary + intp nOffset = (intp)pMem - (intp)pCur->m_Data; + return ( nOffset % m_BlockSize ) == 0; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// 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 > (size_t)m_BlockSize ) + return NULL; + + if ( !m_pHeadOfFreeList ) + { + // returning NULL is fine in GROW_NONE + if ( m_GrowMode == GROW_NONE && m_NumBlobs > 0 ) + { + //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/external/vpc/tier1/memstack.cpp b/external/vpc/tier1/memstack.cpp new file mode 100644 index 0000000..9ab0b6f --- /dev/null +++ b/external/vpc/tier1/memstack.cpp @@ -0,0 +1,641 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#if defined( _WIN32 ) && !defined( _X360 ) +#define WIN_32_LEAN_AND_MEAN +#include <windows.h> +#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) +#elif defined( _PS3 ) +#include "sys/memory.h" +#include "sys/mempool.h" +#include "sys/process.h" +#include <sys/vm.h> +#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 + +static volatile bool bSpewAllocations = false; // TODO: Register CMemoryStacks with g_pMemAlloc, so it can spew a summary + +//----------------------------------------------------------------------------- + +MEMALLOC_DEFINE_EXTERNAL_TRACKING(CMemoryStack); + +//----------------------------------------------------------------------------- + +CMemoryStack::CMemoryStack() + : m_pBase( NULL ), + m_pNextAlloc( NULL ), + m_pAllocLimit( NULL ), + m_pCommitLimit( NULL ), + m_alignment( 16 ), +#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE + m_commitSize( 0 ), + m_minCommit( 0 ), + #ifdef _PS3 + m_pVirtualMemorySection( NULL ), + #endif +#endif + m_maxSize( 0 ), + m_bRegisteredAllocation( false ) +{ + m_pszAllocOwner = strdup( "CMemoryStack unattributed" ); +} + +//------------------------------------- + +CMemoryStack::~CMemoryStack() +{ + if ( m_pBase ) + Term(); + free( m_pszAllocOwner ); +} + +//------------------------------------- + +bool CMemoryStack::Init( const char *pszAllocOwner, unsigned maxSize, unsigned commitSize, unsigned initialCommit, unsigned alignment ) +{ + Assert( !m_pBase ); + + m_bPhysical = false; + + m_maxSize = maxSize; + m_alignment = AlignValue( alignment, 4 ); + + Assert( m_alignment == alignment ); + Assert( m_maxSize > 0 ); + + SetAllocOwner( pszAllocOwner ); + +#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE + +#ifdef _PS3 + // Memory can only be committed in page-size increments on PS3 + static const unsigned PS3_PAGE_SIZE = 64*1024; + if ( commitSize < PS3_PAGE_SIZE ) + commitSize = PS3_PAGE_SIZE; +#endif + + if ( commitSize != 0 ) + { + m_commitSize = commitSize; + } + + unsigned pageSize; + +#ifdef _PS3 + pageSize = PS3_PAGE_SIZE; +#elif defined( _X360 ) + pageSize = 64 * 1024; +#else + SYSTEM_INFO sysInfo; + GetSystemInfo( &sysInfo ); + Assert( !( sysInfo.dwPageSize & (sysInfo.dwPageSize-1)) ); + pageSize = sysInfo.dwPageSize; +#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 ); + +#ifdef _WIN32 + m_pBase = (unsigned char *)VirtualAlloc( NULL, m_maxSize, VA_RESERVE_FLAGS, PAGE_NOACCESS ); +#else + m_pVirtualMemorySection = g_pMemAlloc->AllocateVirtualMemorySection( m_maxSize ); + if ( !m_pVirtualMemorySection ) + { + Warning( "AllocateVirtualMemorySection failed( size=%d )\n", m_maxSize ); + Assert( 0 ); + m_pBase = NULL; + } + else + { + m_pBase = ( byte* ) m_pVirtualMemorySection->GetBaseAddress(); + } +#endif + if ( !m_pBase ) + { +#if !defined( NO_MALLOC_OVERRIDE ) + g_pMemAlloc->OutOfMemory(); +#endif + return false; + } + m_pCommitLimit = m_pNextAlloc = m_pBase; + + if ( initialCommit ) + { + initialCommit = AlignValue( initialCommit, m_commitSize ); + Assert( initialCommit <= m_maxSize ); + bool bInitialCommitSucceeded = false; +#ifdef _WIN32 + bInitialCommitSucceeded = !!VirtualAlloc( m_pCommitLimit, initialCommit, VA_COMMIT_FLAGS, PAGE_READWRITE ); +#else + m_pVirtualMemorySection->CommitPages( m_pCommitLimit, initialCommit ); + bInitialCommitSucceeded = true; +#endif + if ( !bInitialCommitSucceeded ) + { +#if !defined( NO_MALLOC_OVERRIDE ) + g_pMemAlloc->OutOfMemory( initialCommit ); +#endif + return false; + } + m_minCommit = initialCommit; + m_pCommitLimit += initialCommit; + RegisterAllocation(); + } + +#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 _GAMECONSOLE +bool CMemoryStack::InitPhysical( const char *pszAllocOwner, uint size, uint nBaseAddrAlignment, uint alignment, uint32 nFlags ) +{ + m_bPhysical = true; + + m_maxSize = m_commitSize = size; + m_alignment = AlignValue( alignment, 4 ); + + SetAllocOwner( pszAllocOwner ); + +#ifdef _X360 + int flags = PAGE_READWRITE | nFlags; + if ( size >= 16*1024*1024 ) + { + flags |= MEM_16MB_PAGES; + } + else + { + flags |= MEM_LARGE_PAGES; + } + m_pBase = (unsigned char *)XPhysicalAlloc( m_maxSize, MAXULONG_PTR, nBaseAddrAlignment, flags ); +#elif defined (_PS3) + m_pBase = (byte*)nFlags; + m_pBase = (byte*)AlignValue( (uintp)m_pBase, m_alignment ); +#else +#pragma error +#endif + + Assert( m_pBase ); + m_pNextAlloc = m_pBase; + m_pCommitLimit = m_pBase + m_maxSize; + m_pAllocLimit = m_pBase + m_maxSize; + + RegisterAllocation(); + return ( m_pBase != NULL ); +} +#endif + +//------------------------------------- + +void CMemoryStack::Term() +{ + FreeAll(); + if ( m_pBase ) + { +#ifdef _GAMECONSOLE + if ( m_bPhysical ) + { +#if defined( _X360 ) + XPhysicalFree( m_pBase ); +#elif defined( _PS3 ) +#else +#pragma error +#endif + m_pCommitLimit = m_pBase = NULL; + m_maxSize = 0; + RegisterDeallocation(true); + m_bPhysical = false; + return; + } +#endif // _GAMECONSOLE + +#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE +#if defined(_WIN32) + VirtualFree( m_pBase, 0, MEM_RELEASE ); +#else + m_pVirtualMemorySection->Release(); + m_pVirtualMemorySection = NULL; +#endif +#else + MemAlloc_FreeAligned( m_pBase ); +#endif + m_pCommitLimit = m_pBase = NULL; + m_maxSize = 0; + RegisterDeallocation(true); + } +} + +//------------------------------------- + +int CMemoryStack::GetSize() +{ + if ( m_bPhysical ) + return m_maxSize; + +#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE + return m_pCommitLimit - m_pBase; +#else + return m_maxSize; +#endif +} + + +//------------------------------------- + +bool CMemoryStack::CommitTo( byte *pNextAlloc ) RESTRICT +{ + if ( m_bPhysical ) + { + return NULL; + } + +#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE + unsigned char * pNewCommitLimit = AlignValue( pNextAlloc, m_commitSize ); + ptrdiff_t commitSize = pNewCommitLimit - m_pCommitLimit; + + if( m_pCommitLimit + commitSize > m_pAllocLimit ) + { + return false; + } + + if ( pNewCommitLimit > m_pCommitLimit ) + { + RegisterDeallocation(false); + bool bAllocationSucceeded = false; +#ifdef _WIN32 + bAllocationSucceeded = !!VirtualAlloc( m_pCommitLimit, commitSize, VA_COMMIT_FLAGS, PAGE_READWRITE ); +#else + bAllocationSucceeded = m_pVirtualMemorySection->CommitPages( m_pCommitLimit, commitSize ); +#endif + if ( !bAllocationSucceeded ) + { +#if !defined( NO_MALLOC_OVERRIDE ) + g_pMemAlloc->OutOfMemory( commitSize ); +#endif + return false; + } + m_pCommitLimit = pNewCommitLimit; + RegisterAllocation(); + } + else if ( pNewCommitLimit < m_pCommitLimit ) + { + if ( m_pNextAlloc > pNewCommitLimit ) + { + Warning( "ATTEMPTED TO DECOMMIT OWNED MEMORY STACK SPACE\n" ); + pNewCommitLimit = AlignValue( m_pNextAlloc, m_commitSize ); + } + + if ( pNewCommitLimit < m_pCommitLimit ) + { + RegisterDeallocation(false); + ptrdiff_t decommitSize = m_pCommitLimit - pNewCommitLimit; +#ifdef _WIN32 + VirtualFree( pNewCommitLimit, decommitSize, MEM_DECOMMIT ); +#else + m_pVirtualMemorySection->DecommitPages( pNewCommitLimit, decommitSize ); +#endif + m_pCommitLimit = pNewCommitLimit; + RegisterAllocation(); + } + } + + return true; +#else + return false; +#endif +} + +// Identify the owner of this memory stack's memory +void CMemoryStack::SetAllocOwner( const char *pszAllocOwner ) +{ + if ( !pszAllocOwner || !V_strcmp( m_pszAllocOwner, pszAllocOwner ) ) + return; + free( m_pszAllocOwner ); + m_pszAllocOwner = strdup( pszAllocOwner ); +} + +void CMemoryStack::RegisterAllocation() +{ + // 'physical' allocations on PS3 come from RSX local memory, so we don't count them here: + if ( IsPS3() && m_bPhysical ) + return; + + if ( GetSize() ) + { + if ( m_bRegisteredAllocation ) + Warning( "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" ); + + // NOTE: we deliberately don't use MemAlloc_RegisterExternalAllocation. CMemoryStack needs to bypass 'GetActualDbgInfo' + // due to the way it allocates memory: there's just one representative memory address (m_pBase), it grows at unpredictable + // times (in CommitTo, not every Alloc call) and it is freed en-masse (instead of freeing each individual allocation). + MemAlloc_RegisterAllocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 ); + } + m_bRegisteredAllocation = true; + + // Temp memorystack spew: very useful when we crash out of memory + if ( IsGameConsole() && bSpewAllocations ) Msg( "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner ); +} + +void CMemoryStack::RegisterDeallocation( bool bShouldSpewSize ) +{ + // 'physical' allocations on PS3 come from RSX local memory, so we don't count them here: + if ( IsPS3() && m_bPhysical ) + return; + + if ( GetSize() ) + { + if ( !m_bRegisteredAllocation ) + Warning( "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" ); + MemAlloc_RegisterDeallocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 ); + } + m_bRegisteredAllocation = false; + + // Temp memorystack spew: very useful when we crash out of memory + if ( bShouldSpewSize && IsGameConsole() && bSpewAllocations ) Msg( "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner ); +} + +//------------------------------------- + +void CMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit ) +{ + mark = AlignValue( mark, m_alignment ); + byte *pAllocPoint = m_pBase + mark; + + Assert( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc ); + if ( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc ) + { + m_pNextAlloc = pAllocPoint; +#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE + if ( bDecommit && !m_bPhysical ) + { + CommitTo( MAX( m_pNextAlloc, (m_pBase + m_minCommit) ) ); + } +#endif + } +} + +//------------------------------------- + +void CMemoryStack::FreeAll( bool bDecommit ) +{ + if ( m_pBase && ( m_pBase < m_pCommitLimit ) ) + { + FreeToAllocPoint( 0, bDecommit ); + } +} + +//------------------------------------- + +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() ); +} + +#ifdef _X360 + +//----------------------------------------------------------------------------- +// +// A memory stack used for allocating physical memory on the 360 (can't commit/decommit) +// +//----------------------------------------------------------------------------- + +MEMALLOC_DEFINE_EXTERNAL_TRACKING(CPhysicalMemoryStack); + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CPhysicalMemoryStack::CPhysicalMemoryStack() : + m_nAlignment( 16 ), m_nAdditionalFlags( 0 ), m_nUsage( 0 ), m_nPeakUsage( 0 ), m_pLastAllocedChunk( NULL ), + m_nFirstAvailableChunk( 0 ), m_nChunkSizeInBytes( 0 ), m_ExtraChunks( 32, 32 ), m_nFramePeakUsage( 0 ) +{ + m_InitialChunk.m_pBase = NULL; + m_InitialChunk.m_pNextAlloc = NULL; + m_InitialChunk.m_pAllocLimit = NULL; +} + +CPhysicalMemoryStack::~CPhysicalMemoryStack() +{ + Term(); +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +bool CPhysicalMemoryStack::Init( size_t nChunkSizeInBytes, size_t nAlignment, int nInitialChunkCount, uint32 nAdditionalFlags ) +{ + Assert( !m_InitialChunk.m_pBase ); + + m_pLastAllocedChunk = NULL; + m_nAdditionalFlags = nAdditionalFlags; + m_nFirstAvailableChunk = 0; + m_nUsage = 0; + m_nFramePeakUsage = 0; + m_nPeakUsage = 0; + m_nAlignment = AlignValue( nAlignment, 4 ); + + // Chunk size must be aligned to the 360 page size + size_t nInitMemorySize = nChunkSizeInBytes * nInitialChunkCount; + nChunkSizeInBytes = AlignValue( nChunkSizeInBytes, 64 * 1024 ); + m_nChunkSizeInBytes = nChunkSizeInBytes; + + // Fix up initial chunk count to get at least as much memory as requested + // based on changes to the chunk size owing to page alignment issues + nInitialChunkCount = ( nInitMemorySize + nChunkSizeInBytes - 1 ) / nChunkSizeInBytes; + + int nFlags = PAGE_READWRITE | nAdditionalFlags; + int nAllocationSize = m_nChunkSizeInBytes * nInitialChunkCount; + if ( nAllocationSize >= 16*1024*1024 ) + { + nFlags |= MEM_16MB_PAGES; + } + else + { + nFlags |= MEM_LARGE_PAGES; + } + m_InitialChunk.m_pBase = (uint8*)XPhysicalAlloc( nAllocationSize, MAXULONG_PTR, 0, nFlags ); + if ( !m_InitialChunk.m_pBase ) + { + m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pAllocLimit = NULL; + g_pMemAlloc->OutOfMemory(); + return false; + } + + m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pBase; + m_InitialChunk.m_pAllocLimit = m_InitialChunk.m_pBase + nAllocationSize; + + MemAlloc_RegisterExternalAllocation( CPhysicalMemoryStack, m_InitialChunk.m_pBase, XPhysicalSize( m_InitialChunk.m_pBase ) ); + return true; +} + +void CPhysicalMemoryStack::Term() +{ + FreeAll(); + if ( m_InitialChunk.m_pBase ) + { + MemAlloc_RegisterExternalDeallocation( CPhysicalMemoryStack, m_InitialChunk.m_pBase, XPhysicalSize( m_InitialChunk.m_pBase ) ); + XPhysicalFree( m_InitialChunk.m_pBase ); + m_InitialChunk.m_pBase = m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pAllocLimit = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Returns the total allocation size +//----------------------------------------------------------------------------- +size_t CPhysicalMemoryStack::GetSize() const +{ + size_t nBaseSize = (intp)m_InitialChunk.m_pAllocLimit - (intp)m_InitialChunk.m_pBase; + return nBaseSize + m_nChunkSizeInBytes * m_ExtraChunks.Count(); +} + + +//----------------------------------------------------------------------------- +// Allocate from the 'overflow' buffers, only happens if the initial allocation +// isn't good enough +//----------------------------------------------------------------------------- +void *CPhysicalMemoryStack::AllocFromOverflow( size_t nSizeInBytes ) +{ + // Completely full chunks are moved to the front and skipped + int nCount = m_ExtraChunks.Count(); + for ( int i = m_nFirstAvailableChunk; i < nCount; ++i ) + { + PhysicalChunk_t &chunk = m_ExtraChunks[i]; + + // Here we can check if a chunk is full and move it to the head + // of the list. We can't do it immediately *after* allocation + // because something may later free up some of the memory + if ( chunk.m_pNextAlloc == chunk.m_pAllocLimit ) + { + if ( i > 0 ) + { + m_ExtraChunks.FastRemove( i ); + m_ExtraChunks.InsertBefore( 0 ); + } + ++m_nFirstAvailableChunk; + continue; + } + + void *pResult = chunk.m_pNextAlloc; + uint8 *pNextAlloc = chunk.m_pNextAlloc + nSizeInBytes; + if ( pNextAlloc > chunk.m_pAllocLimit ) + continue; + + chunk.m_pNextAlloc = pNextAlloc; + m_pLastAllocedChunk = &chunk; + return pResult; + } + + // No extra chunks to use; add a new one + int i = m_ExtraChunks.AddToTail(); + PhysicalChunk_t &chunk = m_ExtraChunks[i]; + + int nFlags = PAGE_READWRITE | MEM_LARGE_PAGES | m_nAdditionalFlags; + chunk.m_pBase = (uint8*)XPhysicalAlloc( m_nChunkSizeInBytes, MAXULONG_PTR, 0, nFlags ); + if ( !chunk.m_pBase ) + { + chunk.m_pNextAlloc = chunk.m_pAllocLimit = NULL; + m_pLastAllocedChunk = NULL; + g_pMemAlloc->OutOfMemory(); + return NULL; + } + MemAlloc_RegisterExternalAllocation( CPhysicalMemoryStack, chunk.m_pBase, XPhysicalSize( chunk.m_pBase ) ); + + m_pLastAllocedChunk = &chunk; + chunk.m_pNextAlloc = chunk.m_pBase + nSizeInBytes; + chunk.m_pAllocLimit = chunk.m_pBase + m_nChunkSizeInBytes; + return chunk.m_pBase; +} + + +//----------------------------------------------------------------------------- +// Allows us to free a portion of the previous allocation +//----------------------------------------------------------------------------- +void CPhysicalMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bUnused ) +{ + mark = AlignValue( mark, m_nAlignment ); + uint8 *pAllocPoint = m_pLastAllocedChunk->m_pBase + mark; + Assert( pAllocPoint >= m_pLastAllocedChunk->m_pBase && pAllocPoint <= m_pLastAllocedChunk->m_pNextAlloc ); + if ( pAllocPoint >= m_pLastAllocedChunk->m_pBase && pAllocPoint <= m_pLastAllocedChunk->m_pNextAlloc ) + { + m_nUsage -= (intp)m_pLastAllocedChunk->m_pNextAlloc - (intp)pAllocPoint; + m_pLastAllocedChunk->m_pNextAlloc = pAllocPoint; + } +} + + +//----------------------------------------------------------------------------- +// Free overflow buffers, mark initial buffer as empty +//----------------------------------------------------------------------------- +void CPhysicalMemoryStack::FreeAll( bool bUnused ) +{ + m_nUsage = 0; + m_nFramePeakUsage = 0; + m_InitialChunk.m_pNextAlloc = m_InitialChunk.m_pBase; + m_pLastAllocedChunk = NULL; + m_nFirstAvailableChunk = 0; + int nCount = m_ExtraChunks.Count(); + for ( int i = 0; i < nCount; ++i ) + { + PhysicalChunk_t &chunk = m_ExtraChunks[i]; + MemAlloc_RegisterExternalDeallocation( CPhysicalMemoryStack, chunk.m_pBase, XPhysicalSize( chunk.m_pBase ) ); + XPhysicalFree( chunk.m_pBase ); + } + m_ExtraChunks.RemoveAll(); +} + + +//------------------------------------- + +void CPhysicalMemoryStack::PrintContents() +{ + Msg( "Total used memory: %8d\n", GetUsed() ); + Msg( "Peak used memory: %8d\n", GetPeakUsed() ); + Msg( "Total allocated memory: %8d\n", GetSize() ); +} + + +#endif // _X360 diff --git a/external/vpc/tier1/splitstring.cpp b/external/vpc/tier1/splitstring.cpp new file mode 100644 index 0000000..cad9e11 --- /dev/null +++ b/external/vpc/tier1/splitstring.cpp @@ -0,0 +1,91 @@ +//================ Copyright (c) 1996-2009 Valve Corporation. All Rights Reserved. ================= +// +// +// +//================================================================================================== + +#include "strtools.h" +#include "utlvector.h" + +CSplitString::CSplitString(const char *pString, const char **pSeparators, int nSeparators) +{ + Construct(pString, pSeparators, nSeparators); +}; + +CSplitString::CSplitString( const char *pString, const char *pSeparator) +{ + Construct( pString, &pSeparator, 1 ); +} + +CSplitString::~CSplitString() +{ + if(m_szBuffer) + delete [] m_szBuffer; +} + +void CSplitString::Construct( const char *pString, const char **pSeparators, int nSeparators ) +{ + ////////////////////////////////////////////////////////////////////////// + // make a duplicate of the original string. We'll use pieces of this duplicate to tokenize the string + // and create NULL-terminated tokens of the original string + // + int nOriginalStringLength = V_strlen(pString); + m_szBuffer = new char[nOriginalStringLength + 1]; + memcpy(m_szBuffer, pString, nOriginalStringLength + 1); + + this->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 ) + { + ////////////////////////////////////////////////////////////////////////// + /// Cut the token out of the duplicate string + char *pTokenInDuplicate = m_szBuffer + (pCurPos - pString); + int nTokenLength = pFirstSeparator-pCurPos; + Assert(nTokenLength > 0 && !memcmp(pTokenInDuplicate,pCurPos,nTokenLength)); + pTokenInDuplicate[nTokenLength] = '\0'; + + this->AddToTail( pTokenInDuplicate /*AllocString( pCurPos, pFirstSeparator-pCurPos )*/ ); + } + + pCurPos = pFirstSeparator + separatorLen; + } + else + { + // Copy the rest of the string + if ( int nTokenLength = strlen( pCurPos ) ) + { + ////////////////////////////////////////////////////////////////////////// + // There's no need to cut this token, because there's no separator after it. + // just add its copy in the buffer to the tail + char *pTokenInDuplicate = m_szBuffer + (pCurPos - pString); + Assert(!memcmp(pTokenInDuplicate, pCurPos, nTokenLength)); + + this->AddToTail( pTokenInDuplicate/*AllocString( pCurPos, -1 )*/ ); + } + return; + } + } +} + +void CSplitString::PurgeAndDeleteElements() +{ + Purge(); +} diff --git a/external/vpc/tier1/stringpool.cpp b/external/vpc/tier1/stringpool.cpp new file mode 100644 index 0000000..a48016a --- /dev/null +++ b/external/vpc/tier1/stringpool.cpp @@ -0,0 +1,426 @@ +//===== 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 StrLessInsensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( V_stricmp( pszLeft, pszRight) < 0 ); +} + +bool StrLessSensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( V_strcmp( 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 ); + 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 CCountedStringPool::Hash( const char *pszKey ) +{ + if ( m_caseSensitivity == StringPoolCaseInsensitive ) + { + return HashStringCaseless( pszKey ); + } + return HashString( pszKey ); +} + +unsigned short CCountedStringPool::FindStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + unsigned short nHashBucketIndex = ( Hash( 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( !V_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 = ( Hash( 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( !V_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[V_strlen( pIntrinsic ) + 1]; + V_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 = (Hash( 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( !V_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; +} + +void CCountedStringPool::SpewStrings() +{ + int i; + for ( i = 0; i < m_Elements.Count(); i++ ) + { + char* string; + string = m_Elements[i].pString; + Msg("String %d: ref:%d %s\n", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string); + } + + Msg("\n%d total counted strings.", m_Elements.Count()); +} + +#ifdef _DEBUG +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 + +#define STRING_POOL_VERSION MAKEID( 'C', 'S', 'P', '1' ) +#define MAX_STRING_SAVE 1024 + +bool CCountedStringPool::SaveToBuffer( CUtlBuffer &buffer ) +{ + if ( m_Elements.Count() <= 1 ) + { + // pool is empty, saving nothing + // caller can check put position of buffer to detect + return true; + } + + // signature/version + buffer.PutInt( STRING_POOL_VERSION ); + + buffer.PutUnsignedShort( m_FreeListStart ); + + buffer.PutInt( m_HashTable.Count() ); + for ( int i = 0; i < m_HashTable.Count(); i++ ) + { + buffer.PutUnsignedShort( m_HashTable[i] ); + } + + buffer.PutInt( m_Elements.Count() ); + for ( int i = 1; i < m_Elements.Count(); i++ ) + { + buffer.PutUnsignedShort( m_Elements[i].nNextElement ); + buffer.PutUnsignedChar( m_Elements[i].nReferenceCount ); + + const char *pString = m_Elements[i].pString; + if ( strlen( pString ) >= MAX_STRING_SAVE ) + { + return false; + } + buffer.PutString( pString ? pString : "" ); + } + + return buffer.IsValid(); +} + +bool CCountedStringPool::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + int signature = buffer.GetInt(); + if ( signature != STRING_POOL_VERSION ) + { + // wrong version + return false; + } + + FreeAll(); + + m_FreeListStart = buffer.GetUnsignedShort(); + + int hashCount = buffer.GetInt(); + m_HashTable.SetCount( hashCount ); + + for ( int i = 0; i < hashCount; i++ ) + { + m_HashTable[i] = buffer.GetUnsignedShort(); + } + + int tableCount = buffer.GetInt(); + if ( tableCount > 1 ) + { + m_Elements.AddMultipleToTail( tableCount-1 ); + } + + char tempString[MAX_STRING_SAVE]; + for ( int i = 1; i < tableCount; i++ ) + { + m_Elements[i].nNextElement = buffer.GetUnsignedShort(); + m_Elements[i].nReferenceCount = buffer.GetUnsignedChar(); + buffer.GetString( tempString, sizeof( tempString ) ); + m_Elements[i].pString = strdup( tempString ); + } + + return buffer.IsValid(); +} diff --git a/external/vpc/tier1/strtools.cpp b/external/vpc/tier1/strtools.cpp new file mode 100644 index 0000000..9ebe31a --- /dev/null +++ b/external/vpc/tier1/strtools.cpp @@ -0,0 +1,2632 @@ +//===== 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 <stdio.h> +#include <stdarg.h> + +#include "tier0/basetypes.h" +#include "tier0/platform.h" + +#ifdef stricmp +#undef stricmp +#endif + +#ifdef POSIX + +#ifndef _PS3 +#include <iconv.h> +#endif // _PS3 + +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#define stricmp strcasecmp +#elif _WIN32 +#include <direct.h> +#if !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif +#endif + +#ifdef _WIN32 +#ifndef CP_UTF8 +#define CP_UTF8 65001 +#endif +#endif +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include <string.h> +#include <stdlib.h> +#include "tier1/utldict.h" +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#elif defined( _PS3 ) +#include "ps3_pathinfo.h" +#include <cell/l10n.h> // for UCS-2 to UTF-8 conversion +#endif +#include "tier0/vprof.h" +#include "tier0/memdbgon.h" + +#ifndef NDEBUG +static volatile char const *pDebugString; +#define DEBUG_LINK_CHECK pDebugString = "tier1.lib built debug!" +#else +#define DEBUG_LINK_CHECK +#endif + +void _V_memset (void *dest, int fill, int count) +{ + DEBUG_LINK_CHECK; + 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) +{ + DEBUG_LINK_CHECK; + Assert( count >= 0 ); + AssertValidReadPtr( m1, count ); + AssertValidReadPtr( m2, count ); + + return memcmp( m1, m2, count ); +} + +int _V_strlen(const char *str) +{ + AssertValidStringPtr(str); +#ifdef POSIX + if ( !str ) + return 0; +#endif + return strlen( str ); +} + +void _V_strcpy (char *dest, const char *src) +{ + DEBUG_LINK_CHECK; + 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 ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + 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; +} + + + +#define TOLOWERC( x ) (( ( x >= 'A' ) && ( x <= 'Z' ) )?( x + 32 ) : x ) +int _V_stricmp( const char *s1, const char *s2 ) +{ + VPROF_2( "V_stricmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); +#ifdef POSIX + if ( s1 == NULL && s2 == NULL ) + return 0; + if ( s1 == NULL ) + return -1; + if ( s2 == NULL ) + return 1; + + return stricmp( s1, s2 ); +#else + uint8 const *pS1 = ( uint8 const * ) s1; + uint8 const *pS2 = ( uint8 const * ) s2; + for(;;) + { + int c1 = *( pS1++ ); + int c2 = *( pS2++ ); + if ( c1 == c2 ) + { + if ( !c1 ) return 0; + } + else + { + if ( ! c2 ) + { + return c1 - c2; + } + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + return c1 - c2; + } + } + c1 = *( pS1++ ); + c2 = *( pS2++ ); + if ( c1 == c2 ) + { + if ( !c1 ) return 0; + } + else + { + if ( ! c2 ) + { + return c1 - c2; + } + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + return c1 - c2; + } + } + } +#endif +} + +// A special high-performance case-insensitive compare function +// returns 0 if strings match exactly +// returns >0 if strings match in a case-insensitive way, but do not match exactly +// returns <0 if strings do not match even in a case-insensitive way +int _V_stricmp_NegativeForUnequal( const char *s1, const char *s2 ) +{ + VPROF_2( "V_stricmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + uint8 const *pS1 = ( uint8 const * ) s1; + uint8 const *pS2 = ( uint8 const * ) s2; + int iExactMatchResult = 1; + for(;;) + { + int c1 = *( pS1++ ); + int c2 = *( pS2++ ); + if ( c1 == c2 ) + { + // strings are case-insensitive equal, coerce accumulated + // case-difference to 0/1 and return it + if ( !c1 ) return !iExactMatchResult; + } + else + { + if ( ! c2 ) + { + // c2=0 and != c1 => not equal + return -1; + } + iExactMatchResult = 0; + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + // strings are not equal + return -1; + } + } + c1 = *( pS1++ ); + c2 = *( pS2++ ); + if ( c1 == c2 ) + { + // strings are case-insensitive equal, coerce accumulated + // case-difference to 0/1 and return it + if ( !c1 ) return !iExactMatchResult; + } + else + { + if ( ! c2 ) + { + // c2=0 and != c1 => not equal + return -1; + } + iExactMatchResult = 0; + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + // strings are not equal + return -1; + } + } + } +} + + +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); +} + +wchar_t *_V_wcsupr (const char* file, int line, wchar_t *start) +{ + return _wcsupr( start ); +} + + +wchar_t *_V_wcslower (const char* file, int line, wchar_t *start) +{ + return _wcslwr(start); +} + + +int V_strncmp (const char *s1, const char *s2, int count) +{ + Assert( count >= 0 ); + AssertValidStringPtr( s1, count ); + AssertValidStringPtr( s2, count ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + 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 || !count ) + return s; + + while ( -- count > 0 ) + { + if ( !*s ) + return pRet; // reached end of string + + *s = tolower( *s ); + ++s; + } + + *s = 0; // null-terminate original string at "count-1" + return pRet; +} + + +int V_strncasecmp (const char *s1, const char *s2, int n) +{ + Assert( n >= 0 ); + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + 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 ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + return V_stricmp( s1, s2 ); +} + +int V_strnicmp (const char *s1, const char *s2, int n) +{ + DEBUG_LINK_CHECK; + 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; +} + +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; +} + +int64 V_atoi64( const char *str ) +{ + AssertValidStringPtr( str ); + + int64 val; + int64 sign; + int64 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; +} + +int V_atoi( const char *str ) +{ + return (int)V_atoi64( str ); +} + +float V_atof (const char *str) +{ + DEBUG_LINK_CHECK; + 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; + int exponent = 0; + while (1) + { + c = *str++; + if (c == '.') + { + decimal = total; + continue; + } + if (c <'0' || c > '9') + { + if ( c == 'e' ) + { + exponent = V_atoi(str); + } + break; + } + val = val*10 + c - '0'; + total++; + } + + if ( exponent != 0 ) + { + val *= pow( 10.0, exponent ); + } + 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 + +} + +FORCEINLINE unsigned char tolower_fast(unsigned char c) +{ + if ( (c >= 'A') && (c <= 'Z') ) + return c + ('a' - 'A'); + return c; +} + +//----------------------------------------------------------------------------- +// 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_fast((unsigned char)*pLetter) == tolower_fast((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_fast((unsigned char)*pMatch) != tolower_fast((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_fast(*pLetter) == tolower_fast(*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_fast(*pMatch) != tolower_fast(*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 ); + + DEBUG_LINK_CHECK; + + 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 maxLenInNumWideCharacters, const wchar_t *pFormat, ... ) +{ + Assert( maxLenInNumWideCharacters >= 0 ); + AssertValidWritePtr( pDest, maxLenInNumWideCharacters ); + AssertValidReadPtr( pFormat ); + + va_list marker; + + va_start( marker, pFormat ); +#ifdef _WIN32 + int len = _vsnwprintf( pDest, maxLenInNumWideCharacters, pFormat, marker ); +#elif POSIX + int len = vswprintf( pDest, maxLenInNumWideCharacters, pFormat, marker ); +#else +#error "define vsnwprintf type." +#endif + va_end( marker ); + + // Len < 0 represents an overflow + if ( ( len < 0 ) || + ( maxLenInNumWideCharacters > 0 && len >= maxLenInNumWideCharacters ) ) + { + len = maxLenInNumWideCharacters; + pDest[maxLenInNumWideCharacters-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 POSIX + 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; +} + + +int V_vsnprintfRet( char *pDest, int maxLen, const char *pFormat, va_list params, bool *pbTruncated ) +{ + Assert( maxLen > 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pFormat ); + + int len = _vsnprintf( pDest, maxLen, pFormat, params ); + + if ( pbTruncated ) + { + *pbTruncated = len < 0; + } + + 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 maxLenInBytes, int max_chars_to_copy ) +{ + DEBUG_LINK_CHECK; + size_t charstocopy = (size_t)0; + + Assert( maxLenInBytes >= 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 >= maxLenInBytes ) + { + charstocopy = maxLenInBytes - len - 1; + } + + if ( !charstocopy ) + { + return pDest; + } + + char *pOut = strncat( pDest, pSrc, charstocopy ); + pOut[maxLenInBytes-1] = 0; + return pOut; +} + +//----------------------------------------------------------------------------- +// 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 +// maxLenInCharacters - sizeof the buffer in characters pointed to by pDest +// max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy +// Output : char * the copied buffer +//----------------------------------------------------------------------------- +wchar_t *V_wcsncat( wchar_t *pDest, const wchar_t *pSrc, int maxLenInBytes, int max_chars_to_copy ) +{ + DEBUG_LINK_CHECK; + size_t charstocopy = (size_t)0; + + Assert( maxLenInBytes >= 0 ); + + int maxLenInCharacters = maxLenInBytes / sizeof( wchar_t ); + + size_t len = wcslen(pDest); + size_t srclen = wcslen( 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 >= (size_t)maxLenInCharacters ) + { + charstocopy = maxLenInCharacters - len - 1; + } + + if ( !charstocopy ) + { + return pDest; + } + + wchar_t *pOut = wcsncat( pDest, pSrc, charstocopy ); + pOut[maxLenInCharacters-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, "%d,", ( int ) ( value / 1000000000000ll ) ); + } + + // Render trillions + if ( value >= 1000000000000ll ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000000000ll ) ); + } + + // Render billions + if ( value >= 1000000000 ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000000 ) ); + } + + // Render millions + if ( value >= 1000000 ) + { + char *pchRender = out + V_strlen( out ); + if ( value >= 1000000000 ) + V_snprintf( pchRender, 32, "%03d,", ( int ) ( value / 1000000 ) % 1000 ); + else + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000 ) % 1000 ); + } + + // Render thousands + if ( value >= 1000 ) + { + char *pchRender = out + V_strlen( out ); + if ( value >= 1000000 ) + V_snprintf( pchRender, 32, "%03d,", ( int ) ( value / 1000 ) % 1000 ); + else + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000 ) % 1000 ); + } + + // Render units + char *pchRender = out + V_strlen( out ); + if ( value > 1000 ) + V_snprintf( pchRender, 32, "%03d", ( int ) ( value % 1000 ) ); + else + V_snprintf( pchRender, 32, "%d", ( int ) ( value % 1000 ) ); + + return out; +} + + + +//----------------------------------------------------------------------------- +// 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 ); + } +} + +#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/') + + +//----------------------------------------------------------------------------- +// 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 ); + + 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 + char pTemp[MAX_PATH]; + if ( extension[0] != '.' ) + { + pTemp[0] = '.'; + V_strncpy( &pTemp[1], extension, sizeof(pTemp) - 1 ); + extension = pTemp; + } + 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 POSIX +#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--; + } + + bool bHitColon = false; + while ( len > 0 ) + { + if ( PATHSEPARATOR( dirName[len-1] ) ) + { + dirName[len] = 0; + V_FixSlashes( dirName, CORRECT_PATH_SEPARATOR ); + return true; + } + else if ( dirName[len-1] == ':' ) + { + bHitColon = true; + } + + len--; + } + + // If we hit a drive letter, then we're done. + // Ex: If they passed in c:\, then V_StripLastDir should return "" and false. + if ( bHitColon ) + { + dirName[0] = 0; + return false; + } + + // 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 && !bHitColon ) + { + 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 ) +{ + if ( !in || !in[0] ) + return 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; +} + +char *V_UnqualifiedFileName( char *in ) +{ + return const_cast<char *>( V_UnqualifiedFileName( const_cast<const char *>(in) ) ); +} + + +//----------------------------------------------------------------------------- +// 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_FixSlashes( dest ); + 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 && !PATHSEPARATOR( *src ) && *(src-1) != '.' ) + src--; + + // check to see if the '.' is part of a pathname + if (src == path || PATHSEPARATOR( *src ) ) + { + return NULL; // no extension + } + + return src; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the filename part of a path string +// Input: in - file name +// Output: pointer to beginning of filename (after the "/"). If there were no /, +// output is identical to input +//----------------------------------------------------------------------------- +const char *V_GetFileName( const char *pPath ) +{ + if ( !pPath || !pPath[0] ) + return pPath; + + const char *pSrc; + pSrc = pPath + strlen( pPath ) - 1; + + // back up until a / or the start + while ( pSrc > pPath && !PATHSEPARATOR( *( pSrc-1 ) ) ) + --pSrc; + + return pSrc; +} + +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 + { +#ifdef _PS3 + { + V_strncpy( pOut, g_pPS3PathInfo->GameImagePath(), outLen ); + } +#else + { + if ( !_getcwd( pOut, outLen ) ) + Error( "V_MakeAbsolutePath: _getcwd failed." ); + } +#endif + + 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; +} + + +//----------------------------------------------------------------------------- +// 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 ); +} + + +// 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; +} + + +// 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<char*> &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<char*> &outStrings ) +{ + V_SplitString2( pString, &pSeparator, 1, outStrings ); +} + + + +bool V_GetCurrentDirectory( char *pOut, int maxLen ) +{ +#if defined( _PS3 ) + Assert( 0 ); + return false; // not supported +#else // !_PS3 + return _getcwd( pOut, maxLen ) == pOut; +#endif // _PS3 +} + + +bool V_SetCurrentDirectory( const char *pDirName ) +{ +#if defined( _PS3 ) + Assert( 0 ); + return false; // not supported +#else // !_PS3 + return _chdir( pDirName ) == 0; +#endif // _PS3 +} + + +// 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 POSIX + 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 POSIX + 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; +} + +void V_StringToIntArray( int *pVector, int count, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + V_strncpy( tempString, pString, sizeof(tempString) ); + pstr = pfront = tempString; + + for ( j = 0; j < count; j++ ) // lifted from pr_edict.c + { + pVector[j] = atoi( pfront ); + + while ( *pstr && *pstr != ' ' ) + pstr++; + if (!*pstr) + break; + pstr++; + pfront = pstr; + } + + for ( j++; j < count; j++ ) + { + pVector[j] = 0; + } +} + +void V_StringToColor32( color32 *color, const char *pString ) +{ + int tmp[4]; + V_StringToIntArray( tmp, 4, pString ); + color->r = tmp[0]; + color->g = tmp[1]; + color->b = tmp[2]; + color->a = tmp[3]; +} + + + +// 3d memory copy +void CopyMemory3D( void *pDest, void const *pSrc, + int nNumCols, int nNumRows, int nNumSlices, // dimensions of copy + int nSrcBytesPerRow, int nSrcBytesPerSlice, // strides for source. + int nDestBytesPerRow, int nDestBytesPerSlice // strides for dest + ) +{ + if ( nNumSlices && nNumRows && nNumCols ) + { + uint8 *pDestAdr = reinterpret_cast<uint8 *>( pDest ); + uint8 const *pSrcAdr = reinterpret_cast<uint8 const *>( pSrc ); + // first check for optimized cases + if ( ( nNumCols == nSrcBytesPerRow ) && ( nNumCols == nDestBytesPerRow ) ) // no row-to-row stride? + { + int n2DSize = nNumCols * nNumRows; + if ( nSrcBytesPerSlice == nDestBytesPerSlice ) // can we do one memcpy? + { + memcpy( pDestAdr, pSrcAdr, n2DSize * nNumSlices ); + } + else + { + // there might be some slice-to-slice stride + do + { + memcpy( pDestAdr, pSrcAdr, n2DSize ); + pDestAdr += nDestBytesPerSlice; + pSrcAdr += nSrcBytesPerSlice; + } while( nNumSlices-- ); + } + } + else + { + // there is row-by-row stride - we have to do the full nested loop + do + { + int nRowCtr = nNumRows; + uint8 const *pSrcRow = pSrcAdr; + uint8 *pDestRow = pDestAdr; + do + { + memcpy( pDestRow, pSrcRow, nNumCols ); + pDestRow += nDestBytesPerRow; + pSrcRow += nSrcBytesPerRow; + } while( --nRowCtr ); + pSrcAdr += nSrcBytesPerSlice; + pDestAdr += nDestBytesPerSlice; + } while( nNumSlices-- ); + } + } +} + +void V_TranslateLineFeedsToUnix( char *pStr ) +{ + char *pIn = pStr; + char *pOut = pStr; + while ( *pIn ) + { + if ( pIn[0] == '\r' && pIn[1] == '\n' ) + { + ++pIn; + } + *pOut++ = *pIn++; + } + *pOut = 0; +} + +static char s_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + +int HexToValue( char hex ) +{ + if( hex >= '0' && hex <= '9' ) + { + return hex - '0'; + } + if( hex >= 'A' && hex <= 'F' ) + { + return hex - 'A' + 10; + } + if( hex >= 'a' && hex <= 'f' ) + { + return hex - 'a' + 10; + } + // report error here + return -1; +} + +bool V_StringToBin( const char*pString, void *pBin, uint nBinSize ) +{ + if ( (uint)V_strlen( pString ) != nBinSize * 2 ) + { + return false; + } + + for ( uint i = 0; i < nBinSize; ++i ) + { + int high = HexToValue( pString[i*2+0] ); + int low = HexToValue( pString[i*2+1] ) ; + if( high < 0 || low < 0 ) + { + return false; + } + + ( ( uint8* )pBin )[i] = uint8( ( high << 4 ) | low ); + } + return true; +} + + +bool V_BinToString( char*pString, void *pBin, uint nBinSize ) +{ + for ( uint i = 0; i < nBinSize; ++i ) + { + pString[i*2+0] = s_hex[( ( uint8* )pBin )[i] >> 4 ]; + pString[i*2+1] = s_hex[( ( uint8* )pBin )[i] & 0xF]; + } + pString[nBinSize*2] = '\0'; + return true; +} + +// The following characters are not allowed to begin a line for Asian language line-breaking +// purposes. They include the right parenthesis/bracket, space character, period, exclamation, +// question mark, and a number of language-specific characters for Chinese, Japanese, and Korean +static const wchar_t wszCantBeginLine[] = +{ + 0x0020, 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, + 0x003e, 0x003f, 0x005d, 0x007d, 0x00a2, 0x00a8, 0x00b0, 0x00b7, + 0x00bb, 0x02c7, 0x02c9, 0x2010, 0x2013, 0x2014, 0x2015, 0x2016, + 0x2019, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2025, + 0x2026, 0x2027, 0x203a, 0x203c, 0x2047, 0x2048, 0x2049, 0x2103, + 0x2236, 0x2574, 0x3001, 0x3002, 0x3003, 0x3005, 0x3006, 0x3009, + 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0x3019, 0x301b, + 0x301c, 0x301e, 0x301f, 0x303b, 0x3041, 0x3043, 0x3045, 0x3047, + 0x3049, 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x3095, 0x3096, + 0x30a0, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3, + 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fd, 0x30fe, + 0x30fc, 0x31f0, 0x31f1, 0x31f2, 0x31f3, 0x31f4, 0x31f5, 0x31f6, + 0x31f7, 0x31f8, 0x31f9, 0x31fa, 0x31fb, 0x31fc, 0x31fd, 0x31fe, + 0x31ff, 0xfe30, 0xfe31, 0xfe32, 0xfe33, 0xfe36, 0xfe38, 0xfe3a, + 0xfe3c, 0xfe3e, 0xfe40, 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, + 0xfe52, 0xfe53, 0xfe54, 0xfe55, 0xfe56, 0xfe57, 0xfe58, 0xfe5a, + 0xfe5c, 0xfe5e, 0xff01, 0xff02, 0xff05, 0xff07, 0xff09, 0xff0c, + 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d, 0xff40, 0xff5c, 0xff5d, + 0xff5e, 0xff60, 0xff64 +}; + +// The following characters are not allowed to end a line for Asian Language line-breaking +// purposes. They include left parenthesis/bracket, currency symbols, and an number +// of language-specific characters for Chinese, Japanese, and Korean +static const wchar_t wszCantEndLine[] = +{ + 0x0024, 0x0028, 0x002a, 0x003c, 0x005b, 0x005c, 0x007b, 0x00a3, + 0x00a5, 0x00ab, 0x00ac, 0x00b7, 0x02c6, 0x2018, 0x201c, 0x201f, + 0x2035, 0x2039, 0x3005, 0x3007, 0x3008, 0x300a, 0x300c, 0x300e, + 0x3010, 0x3014, 0x3016, 0x3018, 0x301a, 0x301d, 0xfe34, 0xfe35, + 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59, + 0xfe5b, 0xfe5d, 0xff04, 0xff08, 0xff0e, 0xff3b, 0xff5b, 0xff5f, + 0xffe1, 0xffe5, 0xffe6 +}; + +// Can't break between some repeated punctuation patterns ("--", "...", "<asian period repeated>") +static const wchar_t wszCantBreakRepeated[] = +{ + 0x002d, 0x002e, 0x3002 +}; + +bool AsianWordWrap::CanEndLine( wchar_t wcCandidate ) +{ + for( int i = 0; i < SIZE_OF_ARRAY( wszCantEndLine ); ++i ) + { + if( wcCandidate == wszCantEndLine[i] ) + return false; + } + + return true; +} + +bool AsianWordWrap::CanBeginLine( wchar_t wcCandidate ) +{ + for( int i = 0; i < SIZE_OF_ARRAY( wszCantBeginLine ); ++i ) + { + if( wcCandidate == wszCantBeginLine[i] ) + return false; + } + + return true; +} + +bool AsianWordWrap::CanBreakRepeated( wchar_t wcCandidate ) +{ + for( int i = 0; i < SIZE_OF_ARRAY( wszCantBreakRepeated ); ++i ) + { + if( wcCandidate == wszCantBreakRepeated[i] ) + return false; + } + + return true; +} + +#if defined( _PS3 ) || defined( LINUX ) +inline int __cdecl iswascii(wchar_t c) { return ((unsigned)(c) < 0x80); } // not defined in wctype.h on the PS3 +#endif + +// Used to determine if we can break a line between the first two characters passed +bool AsianWordWrap::CanBreakAfter( const wchar_t* wsz ) +{ + if( wsz == NULL || wsz[0] == '\0' || wsz[1] == '\0' ) + { + return false; + } + + wchar_t first_char = wsz[0]; + wchar_t second_char = wsz[1]; + if( ( iswascii( first_char ) && iswascii( second_char ) ) // If not both CJK, return early + || ( iswalnum( first_char ) && iswalnum( second_char ) ) ) // both characters are alphanumeric - Don't split a number or a word! + { + return false; + } + + if( !CanEndLine( first_char ) ) + { + return false; + } + + if( !CanBeginLine( second_char) ) + { + return false; + } + + // don't allow line wrapping in the middle of "--" or "..." + if( ( first_char == second_char ) && ( !CanBreakRepeated( first_char ) ) ) + { + return false; + } + + // If no rules would prevent us from breaking, assume it's safe to break here + return true; +} + +// We use this function to determine where it is permissible to break lines +// of text while wrapping them. On some platforms, the native iswspace() function +// returns FALSE for the "non-breaking space" characters 0x00a0 and 0x202f, and so we don't +// break on them. On others (including the X360 and PC), iswspace returns TRUE for them. +// We get rid of the platform dependency by defining this wrapper which returns false +// for and calls through to the library function for everything else. +int isbreakablewspace( wchar_t ch ) +{ + // 0x00a0 and 0x202f are the wide and narrow non-breaking space UTF-16 values, respectively + return ch != 0x00a0 && ch != 0x202f && iswspace(ch); +} diff --git a/external/vpc/tier1/tier1.cpp b/external/vpc/tier1/tier1.cpp new file mode 100644 index 0000000..187c34e --- /dev/null +++ b/external/vpc/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 <tier1/tier1.h> +#include "tier0/dbg.h" +#include "interfaces/interfaces.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + + + +//----------------------------------------------------------------------------- +// 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/external/vpc/tier1/utlbuffer.cpp b/external/vpc/tier1/utlbuffer.cpp new file mode 100644 index 0000000..5c575a4 --- /dev/null +++ b/external/vpc/tier1/utlbuffer.cpp @@ -0,0 +1,1795 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// $Header: $ +// $NoKeywords: $ +// +// Serialization buffer +//===========================================================================// + +#pragma warning (disable : 4514) + +#include "utlbuffer.h" +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdlib.h> +#include <limits.h> +#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[256]; +}; + + +//----------------------------------------------------------------------------- +// 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[ (unsigned char)(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[ (unsigned char)( 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 = V_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[ (unsigned char)( m_pList[i] ) ]; + Assert( info.m_pReplacementString == 0 ); + info.m_pReplacementString = pArray[i].m_pReplacementString; + info.m_nLength = V_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[ (unsigned char)c ].m_pReplacementString; +} + +int CUtlCharConversion::GetConversionLength( char c ) const +{ + return m_pReplacements[ (unsigned char)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 ( !V_strcmp( pString, m_pReplacements[ (unsigned char)( m_pList[i] ) ].m_pReplacementString ) ) + { + *pLength = m_pReplacements[ (unsigned char)( m_pList[i] ) ].m_nLength; + return m_pList[i]; + } + } + + *pLength = 0; + return '\0'; +} + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- +CUtlBuffer::CUtlBuffer( int growSize, int initSize, int nFlags ) : + m_Error(0) +{ + MEM_ALLOC_CREDIT(); + m_Memory.Init( growSize, initSize ); + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_nOffset = 0; + m_Flags = nFlags; + if ( (initSize != 0) && !IsReadOnly() ) + { + m_nMaxPut = -1; + AddNullTermination( m_Put ); + } + 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 = m_Put = nSize; + } + else + { + m_nMaxPut = -1; + AddNullTermination( m_Put ); + } + 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( m_Put ); +} + +//----------------------------------------------------------------------------- +// 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( m_Put ); +} + + +//----------------------------------------------------------------------------- +// Allows the caller to control memory +//----------------------------------------------------------------------------- +void* CUtlBuffer::DetachMemory() +{ + // Reset all indices; we just changed memory + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_Error = 0; + m_nOffset = 0; + return m_Memory.DetachMemory( ); +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +void CUtlBuffer::EnsureCapacity( int num ) +{ + MEM_ALLOC_CREDIT(); + // 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 ( size > 0 && 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 ( !V_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 ( !V_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 ( V_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 !V_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; + // skip the remaining characters, EXCEPT the terminating null + // thus it's ( nLen - ( nMaxChars - 1 ) - 1 ) + SeekGet( SEEK_CURRENT, nLen - 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... +//----------------------------------------------------------------------------- + +#pragma warning ( disable : 4706 ) + +int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) +{ + Assert( pFmt ); + if ( m_Error || !IsText() ) + return 0; + + int numScanned = 0; + char c; + 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 'h': + { + if ( *pFmt == 'd' || *pFmt == 'i' ) + { + if ( !GetTypeText( *va_arg( list, int16 * ) ) ) + return numScanned; // only support short ints, don't bother with hex + } + else if ( *pFmt == 'u' ) + { + if ( !GetTypeText( *va_arg( list, uint16 * ) ) ) + return numScanned; + } + else + return numScanned; + ++pFmt; + } + break; + + case 'I': + { + if ( *pFmt++ != '6' || *pFmt++ != '4' ) + return numScanned; // only support "I64d" and "I64u" + + if ( *pFmt == 'd' ) + { + if ( !GetTypeText( *va_arg( list, int64 * ) ) ) + return numScanned; + } + else if ( *pFmt == 'u' ) + { + if ( !GetTypeText( *va_arg( list, uint64 * ) ) ) + return numScanned; + } + else + { + return numScanned; + } + + ++pFmt; + } + break; + + case 'i': + case 'd': + { + int32 *pArg = va_arg( list, int32 * ); + if ( !GetTypeText( *pArg ) ) + return numScanned; + } + break; + + case 'x': + { + uint32 *pArg = va_arg( list, uint32 * ); + if ( !GetTypeText( *pArg, 16 ) ) + return numScanned; + } + break; + + case 'u': + { + uint32 *pArg = va_arg( list, uint32 * ); + if ( !GetTypeText( *pArg ) ) + return numScanned; + } + break; + + case 'l': + { + // we currently support %lf and %lld + if ( *pFmt == 'f' ) + { + if ( !GetTypeText( *va_arg( list, double * ) ) ) + return numScanned; + } + else if ( *pFmt == 'l' && *++pFmt == 'd' ) + { + if ( !GetTypeText( *va_arg( list, int64 * ) ) ) + return numScanned; + } + else + return numScanned; + } + break; + + case 'f': + { + float *pArg = va_arg( list, float * ); + if ( !GetTypeText( *pArg ) ) + return numScanned; + } + 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; +} + +#pragma warning ( default : 4706 ) + +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 = V_strlen( pToken ); + + // First time through on streaming, check what we already have loaded + // if we have enough loaded to do the check + int nMaxSize = Size() - ( TellGet() - m_nOffset ); + if ( nMaxSize <= nLen ) + { + nMaxSize = Size(); + } + int nSizeRemaining = TellMaxPut() - TellGet(); + + int nGet = TellGet(); + while ( nSizeRemaining >= nLen ) + { + bool bOverFlow = ( nSizeRemaining > nMaxSize ); + int nSizeToCheck = bOverFlow ? nMaxSize : nSizeRemaining; + if ( !CheckPeekGet( 0, nSizeToCheck ) ) + break; + + const char *pBufStart = (const char*)PeekGet(); + const char *pFoundEnd = V_strnistr( pBufStart, pToken, nSizeToCheck ); + + // Time to be careful: if we are in a state of overflow + // (namely, there's more of the buffer beyond the current window) + // we could be looking for 'foo' for example, and find 'foobar' + // if 'foo' happens to be the last 3 characters of the current window + size_t nOffset = (size_t)pFoundEnd - (size_t)pBufStart; + bool bPotentialMismatch = ( bOverFlow && ( (int)nOffset == Size() - nLen ) ); + if ( !pFoundEnd || bPotentialMismatch ) + { + nSizeRemaining -= nSizeToCheck; + if ( !pFoundEnd && ( nSizeRemaining < nLen ) ) + break; + + // Second time through, stream as much in as possible + // But keep the last portion of the current buffer + // since we couldn't check it against stuff outside the window + nSizeRemaining += nLen; + nMaxSize = Size(); + SeekGet( CUtlBuffer::SEEK_CURRENT, nSizeToCheck - nLen ); + continue; + } + + // Seek past the end of the found string + SeekGet( CUtlBuffer::SEEK_CURRENT, (int)( nOffset + nLen ) ); + return true; + } + + // Didn't find a match, leave the get index where it was to start with + 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 = V_strlen( pEndingDelim ); + + int nStartGet = TellGet(); + char nCurrChar; + int nTokenStart = -1; + EatWhiteSpace( ); + while ( *pStartingDelim ) + { + nCurrChar = *pStartingDelim++; + if ( !V_isspace((unsigned char)nCurrChar) ) + { + if ( tolower( GetChar() ) != tolower( nCurrChar ) ) + goto parseFailed; + } + else + { + EatWhiteSpace(); + } + } + + EatWhiteSpace(); + nTokenStart = TellGet(); + if ( !GetToken( pEndingDelim ) ) + goto parseFailed; + + nCurrentGet = TellGet(); + nCharsToCopy = (int)( (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 ( !V_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( m_Put ); + } +} + + +//----------------------------------------------------------------------------- +// Writes a null-terminated string +//----------------------------------------------------------------------------- +void CUtlBuffer::PutString( const char* pString ) +{ + if (!IsText()) + { + if ( pString ) + { + // Not text? append a null at the end. + int nLen = (int)V_strlen( pString ) + 1; + Put( pString, nLen * sizeof(char) ); + return; + } + else + { + PutTypeBin<char>( 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, (int)nSize ); + pString = pEndl + 1; + if ( *pString ) + { + PutTabs(); + pEndl = strchr( pString, '\n' ); + } + else + { + pEndl = NULL; + } + } + } + int nLen = (int)V_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 ? V_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[8192]; + int nLen = V_vsnprintf( temp, sizeof( temp ), pFmt, list ); + ErrorIfNot( nLen < sizeof( temp ), ( "CUtlBuffer::VaPrintf: String overflowed buffer [%d]\n", sizeof( temp ) ) ); + 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 ) +{ + MEM_ALLOC_CREDIT(); + + 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( m_Put ); +} + + +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 +// NOTE: Pass in nPut here even though it is just a copy of m_Put. This is almost always called immediately +// after modifying m_Put and this lets it stay in a register and avoid LHS on PPC. +//----------------------------------------------------------------------------- +void CUtlBuffer::AddNullTermination( int nPut ) +{ + if ( nPut > m_nMaxPut ) + { + if ( !IsReadOnly() && ((m_Error & PUT_OVERFLOW) == 0) ) + { + // Add null termination value + if ( CheckPut( 1 ) ) + { + m_Memory[nPut - m_nOffset] = 0; + } + else + { + // Restore the overflow state, it was valid before... + m_Error &= ~PUT_OVERFLOW; + } + } + m_nMaxPut = nPut; + } +} + + +//----------------------------------------------------------------------------- +// 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(); + intp nCurrGet = 0; + while ( nCurrGet < nInCount ) + { + const char *pCurr = &pBase[nCurrGet]; + if ( bFromCRLF ) + { + const char *pNext = V_strnistr( pCurr, "\r\n", nInCount - nCurrGet ); + if ( !pNext ) + { + outBuf.Put( pCurr, nInCount - nCurrGet ); + break; + } + + intp nBytes = (intp)pNext - (intp)pCurr; + outBuf.Put( pCurr, (int)nBytes ); + outBuf.PutChar( '\n' ); + nCurrGet += nBytes + 2; + if ( nGet >= nCurrGet - 1 ) + { + --nGetDelta; + } + if ( nPut >= nCurrGet - 1 ) + { + --nPutDelta; + } + } + else + { + const char *pNext = V_strnchr( pCurr, '\n', nInCount - nCurrGet ); + if ( !pNext ) + { + outBuf.Put( pCurr, nInCount - nCurrGet ); + break; + } + + intp nBytes = (intp)pNext - (intp)pCurr; + outBuf.Put( pCurr, (int)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 ) +{ + NULL; +} + +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/external/vpc/tier1/utlstring.cpp b/external/vpc/tier1/utlstring.cpp new file mode 100644 index 0000000..3afb215 --- /dev/null +++ b/external/vpc/tier1/utlstring.cpp @@ -0,0 +1,554 @@ +//====== Copyright 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "tier1/utlstring.h" +#include "tier1/strtools.h" +#include <ctype.h> + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Base class, containing simple memory management +//----------------------------------------------------------------------------- +CUtlBinaryBlock::CUtlBinaryBlock( int growSize, int initSize ) +{ + MEM_ALLOC_CREDIT(); + m_Memory.Init( growSize, initSize ); + + m_nActualLength = 0; +} + +CUtlBinaryBlock::CUtlBinaryBlock( void* pMemory, int nSizeInBytes, int nInitialLength ) : m_Memory( (unsigned char*)pMemory, nSizeInBytes ) +{ + m_nActualLength = nInitialLength; +} + +CUtlBinaryBlock::CUtlBinaryBlock( const void* pMemory, int nSizeInBytes ) : m_Memory( (const unsigned char*)pMemory, nSizeInBytes ) +{ + m_nActualLength = nSizeInBytes; +} + +CUtlBinaryBlock::CUtlBinaryBlock( const CUtlBinaryBlock& src ) +{ + Set( src.Get(), src.Length() ); +} + +void CUtlBinaryBlock::Get( void *pValue, int nLen ) const +{ + Assert( nLen > 0 ); + if ( m_nActualLength < nLen ) + { + nLen = m_nActualLength; + } + + if ( nLen > 0 ) + { + memcpy( pValue, m_Memory.Base(), nLen ); + } +} + +void CUtlBinaryBlock::SetLength( int nLength ) +{ + MEM_ALLOC_CREDIT(); + Assert( !m_Memory.IsReadOnly() ); + + m_nActualLength = nLength; + if ( nLength > m_Memory.NumAllocated() ) + { + int nOverFlow = nLength - m_Memory.NumAllocated(); + m_Memory.Grow( nOverFlow ); + + // If the reallocation failed, clamp length + if ( nLength > m_Memory.NumAllocated() ) + { + m_nActualLength = m_Memory.NumAllocated(); + } + } + +#ifdef _DEBUG + if ( m_Memory.NumAllocated() > m_nActualLength ) + { + memset( ( ( char * )m_Memory.Base() ) + m_nActualLength, 0xEB, m_Memory.NumAllocated() - m_nActualLength ); + } +#endif +} + + +void CUtlBinaryBlock::Set( const void *pValue, int nLen ) +{ + Assert( !m_Memory.IsReadOnly() ); + + if ( !pValue ) + { + nLen = 0; + } + + SetLength( nLen ); + + if ( m_nActualLength ) + { + if ( ( ( const char * )m_Memory.Base() ) >= ( ( const char * )pValue ) + nLen || + ( ( const char * )m_Memory.Base() ) + m_nActualLength <= ( ( const char * )pValue ) ) + { + memcpy( m_Memory.Base(), pValue, m_nActualLength ); + } + else + { + memmove( m_Memory.Base(), pValue, m_nActualLength ); + } + } +} + + +CUtlBinaryBlock &CUtlBinaryBlock::operator=( const CUtlBinaryBlock &src ) +{ + Assert( !m_Memory.IsReadOnly() ); + Set( src.Get(), src.Length() ); + return *this; +} + + +bool CUtlBinaryBlock::operator==( const CUtlBinaryBlock &src ) const +{ + if ( src.Length() != Length() ) + return false; + + return !memcmp( src.Get(), Get(), Length() ); +} + + +//----------------------------------------------------------------------------- +// Simple string class. +//----------------------------------------------------------------------------- +CUtlString::CUtlString() +{ +} + +CUtlString::CUtlString( const char *pString ) +{ + Set( pString ); +} + +CUtlString::CUtlString( const CUtlString& string ) +{ + Set( string.Get() ); +} + +// Attaches the string to external memory. Useful for avoiding a copy +CUtlString::CUtlString( void* pMemory, int nSizeInBytes, int nInitialLength ) : m_Storage( pMemory, nSizeInBytes, nInitialLength ) +{ +} + +CUtlString::CUtlString( const void* pMemory, int nSizeInBytes ) : m_Storage( pMemory, nSizeInBytes ) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Set directly and don't look for a null terminator in pValue. +//----------------------------------------------------------------------------- +void CUtlString::SetDirect( const char *pValue, int nChars ) +{ + if ( nChars > 0 ) + { + m_Storage.SetLength( nChars+1 ); + m_Storage.Set( pValue, nChars ); + m_Storage[nChars] = 0; + } + else + { + m_Storage.SetLength( 0 ); + } +} + + +void CUtlString::Set( const char *pValue ) +{ + Assert( !m_Storage.IsReadOnly() ); + int nLen = pValue ? V_strlen(pValue) + 1 : 0; + m_Storage.Set( pValue, nLen ); +} + + +// Returns strlen +int CUtlString::Length() const +{ + return m_Storage.Length() ? m_Storage.Length() - 1 : 0; +} + +// Sets the length (used to serialize into the buffer ) +void CUtlString::SetLength( int nLen ) +{ + Assert( !m_Storage.IsReadOnly() ); + + // Add 1 to account for the NULL + m_Storage.SetLength( nLen > 0 ? nLen + 1 : 0 ); +} + +const char *CUtlString::Get( ) const +{ + if ( m_Storage.Length() == 0 ) + { + return ""; + } + + return reinterpret_cast< const char* >( m_Storage.Get() ); +} + +// Converts to c-strings +CUtlString::operator const char*() const +{ + return Get(); +} + +char *CUtlString::Get() +{ + Assert( !m_Storage.IsReadOnly() ); + + if ( m_Storage.Length() == 0 ) + { + // 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 + m_Storage.SetLength( 1 ); + m_Storage[ 0 ] = '\0'; + } + + return reinterpret_cast< char* >( m_Storage.Get() ); +} + +void CUtlString::Purge() +{ + m_Storage.Purge(); +} + + +void CUtlString::ToLower() +{ + for( int nLength = Length() - 1; nLength >= 0; nLength-- ) + { + m_Storage[ nLength ] = tolower( m_Storage[ nLength ] ); + } +} + + +CUtlString &CUtlString::operator=( const CUtlString &src ) +{ + Assert( !m_Storage.IsReadOnly() ); + m_Storage = src.m_Storage; + return *this; +} + +CUtlString &CUtlString::operator=( const char *src ) +{ + Assert( !m_Storage.IsReadOnly() ); + Set( src ); + return *this; +} + +bool CUtlString::operator==( const CUtlString &src ) const +{ + return m_Storage == src.m_Storage; +} + +bool CUtlString::operator==( const char *src ) const +{ + return ( strcmp( Get(), src ) == 0 ); +} + +CUtlString &CUtlString::operator+=( const CUtlString &rhs ) +{ + Assert( !m_Storage.IsReadOnly() ); + + const int lhsLength( Length() ); + const int rhsLength( rhs.Length() ); + const int requestedLength( lhsLength + rhsLength ); + + SetLength( requestedLength ); + const int allocatedLength( Length() ); + const int copyLength( allocatedLength - lhsLength < rhsLength ? allocatedLength - lhsLength : rhsLength ); + memcpy( Get() + lhsLength, rhs.Get(), copyLength ); + m_Storage[ allocatedLength ] = '\0'; + + return *this; +} + +CUtlString &CUtlString::operator+=( const char *rhs ) +{ + Assert( !m_Storage.IsReadOnly() ); + + const int lhsLength( Length() ); + const int rhsLength( V_strlen( rhs ) ); + const int requestedLength( lhsLength + rhsLength ); + + SetLength( requestedLength ); + const int allocatedLength( Length() ); + const int copyLength( allocatedLength - lhsLength < rhsLength ? allocatedLength - lhsLength : rhsLength ); + memcpy( Get() + lhsLength, rhs, copyLength ); + m_Storage[ allocatedLength ] = '\0'; + + return *this; +} + +CUtlString &CUtlString::operator+=( char c ) +{ + Assert( !m_Storage.IsReadOnly() ); + + int nLength = Length(); + SetLength( nLength + 1 ); + m_Storage[ nLength ] = c; + m_Storage[ nLength+1 ] = '\0'; + return *this; +} + +CUtlString &CUtlString::operator+=( int rhs ) +{ + Assert( !m_Storage.IsReadOnly() ); + 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 ) +{ + Assert( !m_Storage.IsReadOnly() ); + + 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, ... ) +{ + Assert( !m_Storage.IsReadOnly() ); + + char tmpBuf[ 4096 ]; //< Nice big 4k buffer, as much memory as my first computer had, a Radio Shack Color Computer + + va_list marker; + + va_start( marker, pFormat ); +#ifdef _WIN32 + int len = _vsnprintf( tmpBuf, sizeof( tmpBuf ) - 1, pFormat, marker ); +#elif POSIX + int len = vsnprintf( tmpBuf, sizeof( tmpBuf ) - 1, pFormat, marker ); +#else +#error "define vsnprintf type." +#endif + va_end( marker ); + + // Len > maxLen represents an overflow on POSIX, < 0 is an overflow on windows + if( len < 0 || len >= sizeof( tmpBuf ) - 1 ) + { + len = sizeof( tmpBuf ) - 1; + tmpBuf[sizeof( tmpBuf ) - 1] = 0; + } + + Set( tmpBuf ); + + return len; +} + +//----------------------------------------------------------------------------- +// Strips the trailing slash +//----------------------------------------------------------------------------- +void CUtlString::StripTrailingSlash() +{ + if ( IsEmpty() ) + return; + + int nLastChar = Length() - 1; + char c = m_Storage[ nLastChar ]; + if ( c == '\\' || c == '/' ) + { + m_Storage[ nLastChar ] = 0; + m_Storage.SetLength( m_Storage.Length() - 1 ); + } +} + +CUtlString CUtlString::Slice( int32 nStart, int32 nEnd ) const +{ + 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.m_Storage.SetLength( nEnd - nStart + 1 ); + char *pOut = (char*)ret.m_Storage.Get(); + + memcpy( ret.m_Storage.Get(), &pIn[nStart], nEnd - nStart ); + pOut[nEnd - nStart] = 0; + + 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 +{ + CUtlString ret = *this; + int len = ret.Length(); + for ( int i=0; i < len; i++ ) + { + if ( ret.m_Storage[i] == cFrom ) + ret.m_Storage[i] = cTo; + } + + return ret; +} + +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.m_Storage.Get(), ret.m_Storage.Length() ); + V_StripTrailingSlash( (char*)ret.m_Storage.Get() ); + return ret; +} + +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; +} + + +//----------------------------------------------------------------------------- +// Purpose: concatenate the provided string to our current content +//----------------------------------------------------------------------------- +void CUtlString::Append( const char *pchAddition ) +{ + CUtlString s = *this; + s += pchAddition; +} diff --git a/external/vpc/tier1/utlsymbol.cpp b/external/vpc/tier1/utlsymbol.cpp new file mode 100644 index 0000000..f9dc84e --- /dev/null +++ b/external/vpc/tier1/utlsymbol.cpp @@ -0,0 +1,513 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines a symbol table +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#pragma warning (disable:4514) + +#include "utlsymbol.h" +#include "tier0/threadtools.h" +#include "stringpool.h" +#include "generichash.h" +#include "tier0/vprof.h" +#include <stddef.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; + } +} + +void CUtlSymbol::LockTableForRead() +{ + Initialize(); + s_pSymbolTable->LockForRead(); +} + +void CUtlSymbol::UnlockTableForRead() +{ + s_pSymbolTable->UnlockForRead(); +} + +//----------------------------------------------------------------------------- +// 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); +} + +const char* CUtlSymbol::StringNoLock( ) const +{ + return CurrTable()->StringNoLock(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::DecoratedStringFromIndex( const CStringPoolIndex &index ) const +{ + Assert( index.m_iPool < m_StringPools.Count() ); + Assert( index.m_iOffset < m_StringPools[index.m_iPool]->m_TotalLen ); + + // step over the hash decorating the beginning of the string + return (&m_StringPools[index.m_iPool]->m_Data[index.m_iOffset]); +} + +inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const +{ + // step over the hash decorating the beginning of the string + return DecoratedStringFromIndex(index)+sizeof(hashDecoration_t); +} + +// The first two bytes of each string in the pool are actually the hash for that string. +// Thus we compare hashes rather than entire strings for a significant perf benefit. +// However since there is a high rate of hash collision we must still compare strings +// if the hashes match. +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 ); + +#if 1 // using the hashes + const char *str1, *str2; + hashDecoration_t hash1, hash2; + + if (i1 == INVALID_STRING_INDEX) + { + str1 = pTable->m_pUserSearchString; + hash1 = pTable->m_nUserSearchStringHash; + } + else + { + str1 = pTable->DecoratedStringFromIndex( i1 ); + hashDecoration_t storedHash = *reinterpret_cast<const hashDecoration_t *>(str1); + str1 += sizeof(hashDecoration_t); + AssertMsg2( storedHash == ( !pTable->m_bInsensitive ? HashString(str1) : HashStringCaseless(str1) ), + "The stored hash (%d) for symbol %s is not correct.", storedHash, str1 ); + hash1 = storedHash; + } + + if (i2 == INVALID_STRING_INDEX) + { + str2 = pTable->m_pUserSearchString; + hash2 = pTable->m_nUserSearchStringHash; + } + else + { + str2 = pTable->DecoratedStringFromIndex( i2 ); + hashDecoration_t storedHash = *reinterpret_cast<const hashDecoration_t *>(str2); + str2 += sizeof(hashDecoration_t); + AssertMsg2( storedHash == ( !pTable->m_bInsensitive ? HashString(str2) : HashStringCaseless(str2) ), + "The stored hash (%d) for symbol '%s' is not correct.", storedHash, str2 ); + hash2 = storedHash; + } + + // compare the hashes + if ( hash1 == hash2 ) + { + if ( !str1 && str2 ) + return false; + if ( !str2 && str1 ) + return true; + if ( !str1 && !str2 ) + return false; + + // if the hashes match compare the strings + if ( !pTable->m_bInsensitive ) + return strcmp( str1, str2 ) < 0; + else + return V_stricmp( str1, str2 ) < 0; + } + else + { + return hash1 < hash2; + } + +#else // not using the hashes, just comparing strings + 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 ( !str1 && str2 ) + return false; + if ( !str2 && str1 ) + return true; + if ( !str1 && !str2 ) + return false; + if ( !pTable->m_bInsensitive ) + return strcmp( str1, str2 ) < 0; + else + return strcmpi( str1, str2 ) < 0; +#endif +} + + +//----------------------------------------------------------------------------- +// 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 +{ + VPROF( "CUtlSymbol::Find" ); + if (!pString) + return CUtlSymbol(); + + // Store a special context used to help with insertion + m_pUserSearchString = pString; + m_nUserSearchStringHash = m_bInsensitive ? HashStringCaseless(pString) : HashString(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; + m_nUserSearchStringHash = 0; +#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 ) +{ + VPROF("CUtlSymbol::AddString"); + if (!pString) + return CUtlSymbol( UTL_INVAL_SYMBOL ); + + CUtlSymbol id = Find( pString ); + + if (id.IsValid()) + return id; + + int lenString = strlen(pString) + 1; // length of just the string + int lenDecorated = lenString + sizeof(hashDecoration_t); // and with its hash decoration + // make sure that all strings are aligned on 2-byte boundaries so the hashes will read correctly + COMPILE_TIME_ASSERT(sizeof(hashDecoration_t) == 2); + lenDecorated = (lenDecorated + 1) & (~0x01); // round up to nearest multiple of 2 + + // Find a pool with space for this string, or allocate a new one. + int iPool = FindPoolWithSpace( lenDecorated ); + if ( iPool == -1 ) + { + // Add a new pool. + int newPoolSize = MAX( lenDecorated + sizeof( StringPool_t ), MIN_STRING_POOL_SIZE ); + StringPool_t *pPool = (StringPool_t*)malloc( newPoolSize ); + pPool->m_TotalLen = newPoolSize - sizeof( StringPool_t ); + pPool->m_SpaceUsed = 0; + iPool = m_StringPools.AddToTail( pPool ); + } + + // Compute a hash + hashDecoration_t hash = m_bInsensitive ? HashStringCaseless(pString) : HashString(pString) ; + + // 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; + const char *startingAddr = &pPool->m_Data[pPool->m_SpaceUsed]; + + // store the hash at the head of the string + *((hashDecoration_t *)(startingAddr)) = hash; + // and then the string's data + memcpy( (void *)(startingAddr + sizeof(hashDecoration_t)), pString, lenString ); + pPool->m_SpaceUsed += lenDecorated; + + // insert the string into the vector. + CStringPoolIndex index; + index.m_iPool = iPool; + index.m_iOffset = iStringOffset; + + MEM_ALLOC_CREDIT(); + 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 ]; + V_strncpy( fn, pFileName, sizeof( fn ) ); + V_RemoveDotSlashes( fn ); + + // Split the filename into constituent parts + char basepath[ MAX_PATH ]; + V_ExtractFilePath( fn, basepath, sizeof( basepath ) ); + char filename[ MAX_PATH ]; + V_strncpy( filename, fn + V_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 ]; + V_strncpy( fn, pFileName, sizeof( fn ) ); + V_RemoveDotSlashes( fn ); + + // Split the filename into constituent parts + char basepath[ MAX_PATH ]; + V_ExtractFilePath( fn, basepath, sizeof( basepath ) ); + char filename[ MAX_PATH ]; + V_strncpy( filename, fn + V_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; + } + + V_strncpy( buf, path, buflen ); + V_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(); +} + +bool CUtlFilenameSymbolTable::SaveToBuffer( CUtlBuffer &buffer ) +{ + m_lock.LockForRead(); + bool bResult = m_StringPool.SaveToBuffer( buffer ); + m_lock.UnlockRead(); + + return bResult; +} + +bool CUtlFilenameSymbolTable::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + m_lock.LockForWrite(); + bool bResult = m_StringPool.RestoreFromBuffer( buffer ); + m_lock.UnlockWrite(); + + return bResult; +} |