summaryrefslogtreecommitdiff
path: root/external/vpc/tier1
diff options
context:
space:
mode:
Diffstat (limited to 'external/vpc/tier1')
-rw-r--r--external/vpc/tier1/characterset.cpp41
-rw-r--r--external/vpc/tier1/checksum_crc.cpp182
-rw-r--r--external/vpc/tier1/checksum_md5.cpp291
-rw-r--r--external/vpc/tier1/convar.cpp1521
-rw-r--r--external/vpc/tier1/exprevaluator.cpp501
-rw-r--r--external/vpc/tier1/generichash.cpp429
-rw-r--r--external/vpc/tier1/interface.cpp705
-rw-r--r--external/vpc/tier1/keyvalues.cpp3427
-rw-r--r--external/vpc/tier1/mempool.cpp336
-rw-r--r--external/vpc/tier1/memstack.cpp641
-rw-r--r--external/vpc/tier1/splitstring.cpp91
-rw-r--r--external/vpc/tier1/stringpool.cpp426
-rw-r--r--external/vpc/tier1/strtools.cpp2632
-rw-r--r--external/vpc/tier1/tier1.cpp29
-rw-r--r--external/vpc/tier1/utlbuffer.cpp1795
-rw-r--r--external/vpc/tier1/utlstring.cpp554
-rw-r--r--external/vpc/tier1/utlsymbol.cpp513
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 &nbsp; 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;
+}