diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/tier1/strtools.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'external/vpc/tier1/strtools.cpp')
| -rw-r--r-- | external/vpc/tier1/strtools.cpp | 2632 |
1 files changed, 2632 insertions, 0 deletions
diff --git a/external/vpc/tier1/strtools.cpp b/external/vpc/tier1/strtools.cpp new file mode 100644 index 0000000..9ebe31a --- /dev/null +++ b/external/vpc/tier1/strtools.cpp @@ -0,0 +1,2632 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: String Tools +// +//===========================================================================// + +// These are redefined in the project settings to prevent anyone from using them. +// We in this module are of a higher caste and thus are privileged in their use. +#ifdef strncpy + #undef strncpy +#endif + +#ifdef _snprintf + #undef _snprintf +#endif + +#if defined( sprintf ) + #undef sprintf +#endif + +#if defined( vsprintf ) + #undef vsprintf +#endif + +#ifdef _vsnprintf +#ifdef _WIN32 + #undef _vsnprintf +#endif +#endif + +#ifdef vsnprintf +#ifndef _WIN32 + #undef vsnprintf +#endif +#endif + +#if defined( strcat ) + #undef strcat +#endif + +#ifdef strncat + #undef strncat +#endif + +// NOTE: I have to include stdio + stdarg first so vsnprintf gets compiled in +#include <stdio.h> +#include <stdarg.h> + +#include "tier0/basetypes.h" +#include "tier0/platform.h" + +#ifdef stricmp +#undef stricmp +#endif + +#ifdef POSIX + +#ifndef _PS3 +#include <iconv.h> +#endif // _PS3 + +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#define stricmp strcasecmp +#elif _WIN32 +#include <direct.h> +#if !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif +#endif + +#ifdef _WIN32 +#ifndef CP_UTF8 +#define CP_UTF8 65001 +#endif +#endif +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include <string.h> +#include <stdlib.h> +#include "tier1/utldict.h" +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#elif defined( _PS3 ) +#include "ps3_pathinfo.h" +#include <cell/l10n.h> // for UCS-2 to UTF-8 conversion +#endif +#include "tier0/vprof.h" +#include "tier0/memdbgon.h" + +#ifndef NDEBUG +static volatile char const *pDebugString; +#define DEBUG_LINK_CHECK pDebugString = "tier1.lib built debug!" +#else +#define DEBUG_LINK_CHECK +#endif + +void _V_memset (void *dest, int fill, int count) +{ + DEBUG_LINK_CHECK; + Assert( count >= 0 ); + AssertValidWritePtr( dest, count ); + + memset(dest,fill,count); +} + +void _V_memcpy (void *dest, const void *src, int count) +{ + Assert( count >= 0 ); + AssertValidReadPtr( src, count ); + AssertValidWritePtr( dest, count ); + + memcpy( dest, src, count ); +} + +void _V_memmove(void *dest, const void *src, int count) +{ + Assert( count >= 0 ); + AssertValidReadPtr( src, count ); + AssertValidWritePtr( dest, count ); + + memmove( dest, src, count ); +} + +int _V_memcmp (const void *m1, const void *m2, int count) +{ + DEBUG_LINK_CHECK; + Assert( count >= 0 ); + AssertValidReadPtr( m1, count ); + AssertValidReadPtr( m2, count ); + + return memcmp( m1, m2, count ); +} + +int _V_strlen(const char *str) +{ + AssertValidStringPtr(str); +#ifdef POSIX + if ( !str ) + return 0; +#endif + return strlen( str ); +} + +void _V_strcpy (char *dest, const char *src) +{ + DEBUG_LINK_CHECK; + AssertValidWritePtr(dest); + AssertValidStringPtr(src); + + strcpy( dest, src ); +} + +int _V_wcslen(const wchar_t *pwch) +{ + return wcslen( pwch ); +} + +char *_V_strrchr(const char *s, char c) +{ + AssertValidStringPtr( s ); + int len = V_strlen(s); + s += len; + while (len--) + if (*--s == c) return (char *)s; + return 0; +} + +int _V_strcmp (const char *s1, const char *s2) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + return strcmp( s1, s2 ); +} + +int _V_wcscmp (const wchar_t *s1, const wchar_t *s2) +{ + while (1) + { + if (*s1 != *s2) + return -1; // strings not equal + if (!*s1) + return 0; // strings are equal + s1++; + s2++; + } + + return -1; +} + + + +#define TOLOWERC( x ) (( ( x >= 'A' ) && ( x <= 'Z' ) )?( x + 32 ) : x ) +int _V_stricmp( const char *s1, const char *s2 ) +{ + VPROF_2( "V_stricmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); +#ifdef POSIX + if ( s1 == NULL && s2 == NULL ) + return 0; + if ( s1 == NULL ) + return -1; + if ( s2 == NULL ) + return 1; + + return stricmp( s1, s2 ); +#else + uint8 const *pS1 = ( uint8 const * ) s1; + uint8 const *pS2 = ( uint8 const * ) s2; + for(;;) + { + int c1 = *( pS1++ ); + int c2 = *( pS2++ ); + if ( c1 == c2 ) + { + if ( !c1 ) return 0; + } + else + { + if ( ! c2 ) + { + return c1 - c2; + } + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + return c1 - c2; + } + } + c1 = *( pS1++ ); + c2 = *( pS2++ ); + if ( c1 == c2 ) + { + if ( !c1 ) return 0; + } + else + { + if ( ! c2 ) + { + return c1 - c2; + } + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + return c1 - c2; + } + } + } +#endif +} + +// A special high-performance case-insensitive compare function +// returns 0 if strings match exactly +// returns >0 if strings match in a case-insensitive way, but do not match exactly +// returns <0 if strings do not match even in a case-insensitive way +int _V_stricmp_NegativeForUnequal( const char *s1, const char *s2 ) +{ + VPROF_2( "V_stricmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + uint8 const *pS1 = ( uint8 const * ) s1; + uint8 const *pS2 = ( uint8 const * ) s2; + int iExactMatchResult = 1; + for(;;) + { + int c1 = *( pS1++ ); + int c2 = *( pS2++ ); + if ( c1 == c2 ) + { + // strings are case-insensitive equal, coerce accumulated + // case-difference to 0/1 and return it + if ( !c1 ) return !iExactMatchResult; + } + else + { + if ( ! c2 ) + { + // c2=0 and != c1 => not equal + return -1; + } + iExactMatchResult = 0; + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + // strings are not equal + return -1; + } + } + c1 = *( pS1++ ); + c2 = *( pS2++ ); + if ( c1 == c2 ) + { + // strings are case-insensitive equal, coerce accumulated + // case-difference to 0/1 and return it + if ( !c1 ) return !iExactMatchResult; + } + else + { + if ( ! c2 ) + { + // c2=0 and != c1 => not equal + return -1; + } + iExactMatchResult = 0; + c1 = TOLOWERC( c1 ); + c2 = TOLOWERC( c2 ); + if ( c1 != c2 ) + { + // strings are not equal + return -1; + } + } + } +} + + +char *_V_strstr( const char *s1, const char *search ) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( search ); + +#if defined( _X360 ) + return (char *)strstr( (char *)s1, search ); +#else + return (char *)strstr( s1, search ); +#endif +} + +char *_V_strupr (char *start) +{ + AssertValidStringPtr( start ); + return strupr( start ); +} + + +char *_V_strlower (char *start) +{ + AssertValidStringPtr( start ); + return strlwr(start); +} + +wchar_t *_V_wcsupr (const char* file, int line, wchar_t *start) +{ + return _wcsupr( start ); +} + + +wchar_t *_V_wcslower (const char* file, int line, wchar_t *start) +{ + return _wcslwr(start); +} + + +int V_strncmp (const char *s1, const char *s2, int count) +{ + Assert( count >= 0 ); + AssertValidStringPtr( s1, count ); + AssertValidStringPtr( s2, count ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + while ( count-- > 0 ) + { + if ( *s1 != *s2 ) + return *s1 < *s2 ? -1 : 1; // string different + if ( *s1 == '\0' ) + return 0; // null terminator hit - strings the same + s1++; + s2++; + } + + return 0; // count characters compared the same +} + + +char *V_strnlwr(char *s, size_t count) +{ + Assert( count >= 0 ); + AssertValidStringPtr( s, count ); + + char* pRet = s; + if ( !s || !count ) + return s; + + while ( -- count > 0 ) + { + if ( !*s ) + return pRet; // reached end of string + + *s = tolower( *s ); + ++s; + } + + *s = 0; // null-terminate original string at "count-1" + return pRet; +} + + +int V_strncasecmp (const char *s1, const char *s2, int n) +{ + Assert( n >= 0 ); + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + while ( n-- > 0 ) + { + int c1 = *s1++; + int c2 = *s2++; + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return c1 < c2 ? -1 : 1; + } + if ( c1 == '\0' ) + return 0; // null terminator hit - strings the same + } + + return 0; // n characters compared the same +} + +int V_strcasecmp( const char *s1, const char *s2 ) +{ + AssertValidStringPtr( s1 ); + AssertValidStringPtr( s2 ); + VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL ); + + return V_stricmp( s1, s2 ); +} + +int V_strnicmp (const char *s1, const char *s2, int n) +{ + DEBUG_LINK_CHECK; + Assert( n >= 0 ); + AssertValidStringPtr(s1); + AssertValidStringPtr(s2); + + return V_strncasecmp( s1, s2, n ); +} + + +const char *StringAfterPrefix( const char *str, const char *prefix ) +{ + AssertValidStringPtr( str ); + AssertValidStringPtr( prefix ); + do + { + if ( !*prefix ) + return str; + } + while ( tolower( *str++ ) == tolower( *prefix++ ) ); + return NULL; +} + +const char *StringAfterPrefixCaseSensitive( const char *str, const char *prefix ) +{ + AssertValidStringPtr( str ); + AssertValidStringPtr( prefix ); + do + { + if ( !*prefix ) + return str; + } + while ( *str++ == *prefix++ ); + return NULL; +} + +uint64 V_atoui64( const char *str ) +{ + AssertValidStringPtr( str ); + + uint64 val; + uint64 c; + + Assert( str ); + + val = 0; + + // + // check for hex + // + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val<<4) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val<<4) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val<<4) + c - 'A' + 10; + else + return val; + } + } + + // + // check for character + // + if (str[0] == '\'') + { + return str[1]; + } + + // + // assume decimal + // + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val; + val = val*10 + c - '0'; + } + + return 0; +} + +int64 V_atoi64( const char *str ) +{ + AssertValidStringPtr( str ); + + int64 val; + int64 sign; + int64 c; + + Assert( str ); + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val<<4) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val<<4) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val<<4) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val*sign; + val = val*10 + c - '0'; + } + + return 0; +} + +int V_atoi( const char *str ) +{ + return (int)V_atoi64( str ); +} + +float V_atof (const char *str) +{ + DEBUG_LINK_CHECK; + AssertValidStringPtr( str ); + double val; + int sign; + int c; + int decimal, total; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val*16) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val*16) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val*16) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + decimal = -1; + total = 0; + int exponent = 0; + while (1) + { + c = *str++; + if (c == '.') + { + decimal = total; + continue; + } + if (c <'0' || c > '9') + { + if ( c == 'e' ) + { + exponent = V_atoi(str); + } + break; + } + val = val*10 + c - '0'; + total++; + } + + if ( exponent != 0 ) + { + val *= pow( 10.0, exponent ); + } + if (decimal == -1) + return val*sign; + while (total > decimal) + { + val /= 10; + total--; + } + + return val*sign; +} + +//----------------------------------------------------------------------------- +// Normalizes a float string in place. +// +// (removes leading zeros, trailing zeros after the decimal point, and the decimal point itself where possible) +//----------------------------------------------------------------------------- +void V_normalizeFloatString( char* pFloat ) +{ + // If we have a decimal point, remove trailing zeroes: + if( strchr( pFloat,'.' ) ) + { + int len = V_strlen(pFloat); + + while( len > 1 && pFloat[len - 1] == '0' ) + { + pFloat[len - 1] = '\0'; + len--; + } + + if( len > 1 && pFloat[ len - 1 ] == '.' ) + { + pFloat[len - 1] = '\0'; + len--; + } + } + + // TODO: Strip leading zeros + +} + +FORCEINLINE unsigned char tolower_fast(unsigned char c) +{ + if ( (c >= 'A') && (c <= 'Z') ) + return c + ('a' - 'A'); + return c; +} + +//----------------------------------------------------------------------------- +// Finds a string in another string with a case insensitive test +//----------------------------------------------------------------------------- +char const* V_stristr( char const* pStr, char const* pSearch ) +{ + AssertValidStringPtr(pStr); + AssertValidStringPtr(pSearch); + + if (!pStr || !pSearch) + return 0; + + char const* pLetter = pStr; + + // Check the entire string + while (*pLetter != 0) + { + // Skip over non-matches + if (tolower_fast((unsigned char)*pLetter) == tolower_fast((unsigned char)*pSearch)) + { + // Check for match + char const* pMatch = pLetter + 1; + char const* pTest = pSearch + 1; + while (*pTest != 0) + { + // We've run off the end; don't bother. + if (*pMatch == 0) + return 0; + + if (tolower_fast((unsigned char)*pMatch) != tolower_fast((unsigned char)*pTest)) + break; + + ++pMatch; + ++pTest; + } + + // Found a match! + if (*pTest == 0) + return pLetter; + } + + ++pLetter; + } + + return 0; +} + +char* V_stristr( char* pStr, char const* pSearch ) +{ + AssertValidStringPtr( pStr ); + AssertValidStringPtr( pSearch ); + + return (char*)V_stristr( (char const*)pStr, pSearch ); +} + +//----------------------------------------------------------------------------- +// Finds a string in another string with a case insensitive test w/ length validation +//----------------------------------------------------------------------------- +char const* V_strnistr( char const* pStr, char const* pSearch, int n ) +{ + AssertValidStringPtr(pStr); + AssertValidStringPtr(pSearch); + + if (!pStr || !pSearch) + return 0; + + char const* pLetter = pStr; + + // Check the entire string + while (*pLetter != 0) + { + if ( n <= 0 ) + return 0; + + // Skip over non-matches + if (tolower_fast(*pLetter) == tolower_fast(*pSearch)) + { + int n1 = n - 1; + + // Check for match + char const* pMatch = pLetter + 1; + char const* pTest = pSearch + 1; + while (*pTest != 0) + { + if ( n1 <= 0 ) + return 0; + + // We've run off the end; don't bother. + if (*pMatch == 0) + return 0; + + if (tolower_fast(*pMatch) != tolower_fast(*pTest)) + break; + + ++pMatch; + ++pTest; + --n1; + } + + // Found a match! + if (*pTest == 0) + return pLetter; + } + + ++pLetter; + --n; + } + + return 0; +} + +const char* V_strnchr( const char* pStr, char c, int n ) +{ + char const* pLetter = pStr; + char const* pLast = pStr + n; + + // Check the entire string + while ( (pLetter < pLast) && (*pLetter != 0) ) + { + if (*pLetter == c) + return pLetter; + ++pLetter; + } + return NULL; +} + + + +void V_strncpy( char *pDest, char const *pSrc, int maxLen ) +{ + Assert( maxLen >= 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pSrc ); + + DEBUG_LINK_CHECK; + + strncpy( pDest, pSrc, maxLen ); + if ( maxLen > 0 ) + { + pDest[maxLen-1] = 0; + } +} + +void V_wcsncpy( wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ) +{ + Assert( maxLenInBytes >= 0 ); + AssertValidWritePtr( pDest, maxLenInBytes ); + AssertValidReadPtr( pSrc ); + + int maxLen = maxLenInBytes / sizeof(wchar_t); + + wcsncpy( pDest, pSrc, maxLen ); + if( maxLen ) + { + pDest[maxLen-1] = 0; + } +} + + + +int V_snwprintf( wchar_t *pDest, int maxLenInNumWideCharacters, const wchar_t *pFormat, ... ) +{ + Assert( maxLenInNumWideCharacters >= 0 ); + AssertValidWritePtr( pDest, maxLenInNumWideCharacters ); + AssertValidReadPtr( pFormat ); + + va_list marker; + + va_start( marker, pFormat ); +#ifdef _WIN32 + int len = _vsnwprintf( pDest, maxLenInNumWideCharacters, pFormat, marker ); +#elif POSIX + int len = vswprintf( pDest, maxLenInNumWideCharacters, pFormat, marker ); +#else +#error "define vsnwprintf type." +#endif + va_end( marker ); + + // Len < 0 represents an overflow + if ( ( len < 0 ) || + ( maxLenInNumWideCharacters > 0 && len >= maxLenInNumWideCharacters ) ) + { + len = maxLenInNumWideCharacters; + pDest[maxLenInNumWideCharacters-1] = 0; + } + + return len; +} + + +int V_snprintf( char *pDest, int maxLen, char const *pFormat, ... ) +{ + Assert( maxLen >= 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pFormat ); + + va_list marker; + + va_start( marker, pFormat ); +#ifdef _WIN32 + int len = _vsnprintf( pDest, maxLen, pFormat, marker ); +#elif POSIX + int len = vsnprintf( pDest, maxLen, pFormat, marker ); +#else + #error "define vsnprintf type." +#endif + va_end( marker ); + + // Len < 0 represents an overflow + if( len < 0 ) + { + len = maxLen; + pDest[maxLen-1] = 0; + } + + return len; +} + + +int V_vsnprintf( char *pDest, int maxLen, char const *pFormat, va_list params ) +{ + Assert( maxLen > 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pFormat ); + + int len = _vsnprintf( pDest, maxLen, pFormat, params ); + + if( len < 0 ) + { + len = maxLen; + pDest[maxLen-1] = 0; + } + + return len; +} + + +int V_vsnprintfRet( char *pDest, int maxLen, const char *pFormat, va_list params, bool *pbTruncated ) +{ + Assert( maxLen > 0 ); + AssertValidWritePtr( pDest, maxLen ); + AssertValidStringPtr( pFormat ); + + int len = _vsnprintf( pDest, maxLen, pFormat, params ); + + if ( pbTruncated ) + { + *pbTruncated = len < 0; + } + + if( len < 0 ) + { + len = maxLen; + pDest[maxLen-1] = 0; + } + + return len; +} + + + +//----------------------------------------------------------------------------- +// Purpose: If COPY_ALL_CHARACTERS == max_chars_to_copy then we try to add the whole pSrc to the end of pDest, otherwise +// we copy only as many characters as are specified in max_chars_to_copy (or the # of characters in pSrc if thats's less). +// Input : *pDest - destination buffer +// *pSrc - string to append +// destBufferSize - sizeof the buffer pointed to by pDest +// max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy +// Output : char * the copied buffer +//----------------------------------------------------------------------------- +char *V_strncat(char *pDest, const char *pSrc, size_t maxLenInBytes, int max_chars_to_copy ) +{ + DEBUG_LINK_CHECK; + size_t charstocopy = (size_t)0; + + Assert( maxLenInBytes >= 0 ); + AssertValidStringPtr( pDest); + AssertValidStringPtr( pSrc ); + + size_t len = strlen(pDest); + size_t srclen = strlen( pSrc ); + if ( max_chars_to_copy <= COPY_ALL_CHARACTERS ) + { + charstocopy = srclen; + } + else + { + charstocopy = (size_t)MIN( max_chars_to_copy, (int)srclen ); + } + + if ( len + charstocopy >= maxLenInBytes ) + { + charstocopy = maxLenInBytes - len - 1; + } + + if ( !charstocopy ) + { + return pDest; + } + + char *pOut = strncat( pDest, pSrc, charstocopy ); + pOut[maxLenInBytes-1] = 0; + return pOut; +} + +//----------------------------------------------------------------------------- +// Purpose: If COPY_ALL_CHARACTERS == max_chars_to_copy then we try to add the whole pSrc to the end of pDest, otherwise +// we copy only as many characters as are specified in max_chars_to_copy (or the # of characters in pSrc if thats's less). +// Input : *pDest - destination buffer +// *pSrc - string to append +// maxLenInCharacters - sizeof the buffer in characters pointed to by pDest +// max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy +// Output : char * the copied buffer +//----------------------------------------------------------------------------- +wchar_t *V_wcsncat( wchar_t *pDest, const wchar_t *pSrc, int maxLenInBytes, int max_chars_to_copy ) +{ + DEBUG_LINK_CHECK; + size_t charstocopy = (size_t)0; + + Assert( maxLenInBytes >= 0 ); + + int maxLenInCharacters = maxLenInBytes / sizeof( wchar_t ); + + size_t len = wcslen(pDest); + size_t srclen = wcslen( pSrc ); + if ( max_chars_to_copy <= COPY_ALL_CHARACTERS ) + { + charstocopy = srclen; + } + else + { + charstocopy = (size_t)MIN( max_chars_to_copy, (int)srclen ); + } + + if ( len + charstocopy >= (size_t)maxLenInCharacters ) + { + charstocopy = maxLenInCharacters - len - 1; + } + + if ( !charstocopy ) + { + return pDest; + } + + wchar_t *pOut = wcsncat( pDest, pSrc, charstocopy ); + pOut[maxLenInCharacters-1] = 0; + return pOut; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Converts value into x.xx MB/ x.xx KB, x.xx bytes format, including commas +// Input : value - +// 2 - +// false - +// Output : char +//----------------------------------------------------------------------------- +#define NUM_PRETIFYMEM_BUFFERS 8 +char *V_pretifymem( float value, int digitsafterdecimal /*= 2*/, bool usebinaryonek /*= false*/ ) +{ + static char output[ NUM_PRETIFYMEM_BUFFERS ][ 32 ]; + static int current; + + float onekb = usebinaryonek ? 1024.0f : 1000.0f; + float onemb = onekb * onekb; + + char *out = output[ current ]; + current = ( current + 1 ) & ( NUM_PRETIFYMEM_BUFFERS -1 ); + + char suffix[ 8 ]; + + // First figure out which bin to use + if ( value > onemb ) + { + value /= onemb; + V_snprintf( suffix, sizeof( suffix ), " MB" ); + } + else if ( value > onekb ) + { + value /= onekb; + V_snprintf( suffix, sizeof( suffix ), " KB" ); + } + else + { + V_snprintf( suffix, sizeof( suffix ), " bytes" ); + } + + char val[ 32 ]; + + // Clamp to >= 0 + digitsafterdecimal = MAX( digitsafterdecimal, 0 ); + + // If it's basically integral, don't do any decimals + if ( FloatMakePositive( value - (int)value ) < 0.00001 ) + { + V_snprintf( val, sizeof( val ), "%i%s", (int)value, suffix ); + } + else + { + char fmt[ 32 ]; + + // Otherwise, create a format string for the decimals + V_snprintf( fmt, sizeof( fmt ), "%%.%if%s", digitsafterdecimal, suffix ); + V_snprintf( val, sizeof( val ), fmt, value ); + } + + // Copy from in to out + char *i = val; + char *o = out; + + // Search for decimal or if it was integral, find the space after the raw number + char *dot = strstr( i, "." ); + if ( !dot ) + { + dot = strstr( i, " " ); + } + + // Compute position of dot + int pos = dot - i; + // Don't put a comma if it's <= 3 long + pos -= 3; + + while ( *i ) + { + // If pos is still valid then insert a comma every third digit, except if we would be + // putting one in the first spot + if ( pos >= 0 && !( pos % 3 ) ) + { + // Never in first spot + if ( o != out ) + { + *o++ = ','; + } + } + + // Count down comma position + pos--; + + // Copy rest of data as normal + *o++ = *i++; + } + + // Terminate + *o = 0; + + return out; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a string representation of an integer with commas +// separating the 1000s (ie, 37,426,421) +// Input : value - Value to convert +// Output : Pointer to a static buffer containing the output +//----------------------------------------------------------------------------- +#define NUM_PRETIFYNUM_BUFFERS 8 +char *V_pretifynum( int64 value ) +{ + static char output[ NUM_PRETIFYMEM_BUFFERS ][ 32 ]; + static int current; + + char *out = output[ current ]; + current = ( current + 1 ) & ( NUM_PRETIFYMEM_BUFFERS -1 ); + + *out = 0; + + // Render the leading -, if necessary + if ( value < 0 ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "-" ); + value = -value; + } + + // Render quadrillions + if ( value >= 1000000000000ll ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000000000ll ) ); + } + + // Render trillions + if ( value >= 1000000000000ll ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000000000ll ) ); + } + + // Render billions + if ( value >= 1000000000 ) + { + char *pchRender = out + V_strlen( out ); + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000000 ) ); + } + + // Render millions + if ( value >= 1000000 ) + { + char *pchRender = out + V_strlen( out ); + if ( value >= 1000000000 ) + V_snprintf( pchRender, 32, "%03d,", ( int ) ( value / 1000000 ) % 1000 ); + else + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000000 ) % 1000 ); + } + + // Render thousands + if ( value >= 1000 ) + { + char *pchRender = out + V_strlen( out ); + if ( value >= 1000000 ) + V_snprintf( pchRender, 32, "%03d,", ( int ) ( value / 1000 ) % 1000 ); + else + V_snprintf( pchRender, 32, "%d,", ( int ) ( value / 1000 ) % 1000 ); + } + + // Render units + char *pchRender = out + V_strlen( out ); + if ( value > 1000 ) + V_snprintf( pchRender, 32, "%03d", ( int ) ( value % 1000 ) ); + else + V_snprintf( pchRender, 32, "%d", ( int ) ( value % 1000 ) ); + + return out; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Returns the 4 bit nibble for a hex character +// Input : c - +// Output : unsigned char +//----------------------------------------------------------------------------- +static unsigned char V_nibble( char c ) +{ + if ( ( c >= '0' ) && + ( c <= '9' ) ) + { + return (unsigned char)(c - '0'); + } + + if ( ( c >= 'A' ) && + ( c <= 'F' ) ) + { + return (unsigned char)(c - 'A' + 0x0a); + } + + if ( ( c >= 'a' ) && + ( c <= 'f' ) ) + { + return (unsigned char)(c - 'a' + 0x0a); + } + + return '0'; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *in - +// numchars - +// *out - +// maxoutputbytes - +//----------------------------------------------------------------------------- +void V_hextobinary( char const *in, int numchars, byte *out, int maxoutputbytes ) +{ + int len = V_strlen( in ); + numchars = MIN( len, numchars ); + // Make sure it's even + numchars = ( numchars ) & ~0x1; + + // Must be an even # of input characters (two chars per output byte) + Assert( numchars >= 2 ); + + memset( out, 0x00, maxoutputbytes ); + + byte *p; + int i; + + p = out; + for ( i = 0; + ( i < numchars ) && ( ( p - out ) < maxoutputbytes ); + i+=2, p++ ) + { + *p = ( V_nibble( in[i] ) << 4 ) | V_nibble( in[i+1] ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *in - +// inputbytes - +// *out - +// outsize - +//----------------------------------------------------------------------------- +void V_binarytohex( const byte *in, int inputbytes, char *out, int outsize ) +{ + Assert( outsize >= 1 ); + char doublet[10]; + int i; + + out[0]=0; + + for ( i = 0; i < inputbytes; i++ ) + { + unsigned char c = in[i]; + V_snprintf( doublet, sizeof( doublet ), "%02x", c ); + V_strncat( out, doublet, outsize, COPY_ALL_CHARACTERS ); + } +} + +#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/') + + +//----------------------------------------------------------------------------- +// Purpose: Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator) +// Input : *in - +// *out - +// maxlen - +//----------------------------------------------------------------------------- +void V_FileBase( const char *in, char *out, int maxlen ) +{ + Assert( maxlen >= 1 ); + Assert( in ); + Assert( out ); + + if ( !in || !in[ 0 ] ) + { + *out = 0; + return; + } + + int len, start, end; + + len = V_strlen( in ); + + // scan backward for '.' + end = len - 1; + while ( end&& in[end] != '.' && !PATHSEPARATOR( in[end] ) ) + { + end--; + } + + if ( in[end] != '.' ) // no '.', copy to end + { + end = len-1; + } + else + { + end--; // Found ',', copy to left of '.' + } + + // Scan backward for '/' + start = len-1; + while ( start >= 0 && !PATHSEPARATOR( in[start] ) ) + { + start--; + } + + if ( start < 0 || !PATHSEPARATOR( in[start] ) ) + { + start = 0; + } + else + { + start++; + } + + // Length of new sting + len = end - start + 1; + + int maxcopy = MIN( len + 1, maxlen ); + + // Copy partial string + V_strncpy( out, &in[start], maxcopy ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *ppath - +//----------------------------------------------------------------------------- +void V_StripTrailingSlash( char *ppath ) +{ + Assert( ppath ); + + int len = V_strlen( ppath ); + if ( len > 0 ) + { + if ( PATHSEPARATOR( ppath[ len - 1 ] ) ) + { + ppath[ len - 1 ] = 0; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *in - +// *out - +// outSize - +//----------------------------------------------------------------------------- +void V_StripExtension( const char *in, char *out, int outSize ) +{ + // Find the last dot. If it's followed by a dot or a slash, then it's part of a + // directory specifier like ../../somedir/./blah. + + // scan backward for '.' + int end = V_strlen( in ) - 1; + while ( end > 0 && in[end] != '.' && !PATHSEPARATOR( in[end] ) ) + { + --end; + } + + if (end > 0 && !PATHSEPARATOR( in[end] ) && end < outSize) + { + int nChars = MIN( end, outSize-1 ); + if ( out != in ) + { + memcpy( out, in, nChars ); + } + out[nChars] = 0; + } + else + { + // nothing found + if ( out != in ) + { + V_strncpy( out, in, outSize ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *path - +// *extension - +// pathStringLength - +//----------------------------------------------------------------------------- +void V_DefaultExtension( char *path, const char *extension, int pathStringLength ) +{ + Assert( path ); + Assert( pathStringLength >= 1 ); + Assert( extension ); + + char *src; + + // if path doesn't have a .EXT, append extension + // (extension should include the .) + src = path + V_strlen(path) - 1; + + while ( !PATHSEPARATOR( *src ) && ( src > path ) ) + { + if (*src == '.') + { + // it has an extension + return; + } + src--; + } + + // Concatenate the desired extension + char pTemp[MAX_PATH]; + if ( extension[0] != '.' ) + { + pTemp[0] = '.'; + V_strncpy( &pTemp[1], extension, sizeof(pTemp) - 1 ); + extension = pTemp; + } + V_strncat( path, extension, pathStringLength, COPY_ALL_CHARACTERS ); +} + +//----------------------------------------------------------------------------- +// Purpose: Force extension... +// Input : *path - +// *extension - +// pathStringLength - +//----------------------------------------------------------------------------- +void V_SetExtension( char *path, const char *extension, int pathStringLength ) +{ + V_StripExtension( path, path, pathStringLength ); + V_DefaultExtension( path, extension, pathStringLength ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove final filename from string +// Input : *path - +// Output : void V_StripFilename +//----------------------------------------------------------------------------- +void V_StripFilename (char *path) +{ + int length; + + length = V_strlen( path )-1; + if ( length <= 0 ) + return; + + while ( length > 0 && + !PATHSEPARATOR( path[length] ) ) + { + length--; + } + + path[ length ] = 0; +} + +#ifdef _WIN32 +#define CORRECT_PATH_SEPARATOR '\\' +#define INCORRECT_PATH_SEPARATOR '/' +#elif POSIX +#define CORRECT_PATH_SEPARATOR '/' +#define INCORRECT_PATH_SEPARATOR '\\' +#endif + +//----------------------------------------------------------------------------- +// Purpose: Changes all '/' or '\' characters into separator +// Input : *pname - +// separator - +//----------------------------------------------------------------------------- +void V_FixSlashes( char *pname, char separator /* = CORRECT_PATH_SEPARATOR */ ) +{ + while ( *pname ) + { + if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR ) + { + *pname = separator; + } + pname++; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: This function fixes cases of filenames like materials\\blah.vmt or somepath\otherpath\\ and removes the extra double slash. +//----------------------------------------------------------------------------- +void V_FixDoubleSlashes( char *pStr ) +{ + int len = V_strlen( pStr ); + + for ( int i=1; i < len-1; i++ ) + { + if ( (pStr[i] == '/' || pStr[i] == '\\') && (pStr[i+1] == '/' || pStr[i+1] == '\\') ) + { + // This means there's a double slash somewhere past the start of the filename. That + // can happen in Hammer if they use a material in the root directory. You'll get a filename + // that looks like 'materials\\blah.vmt' + V_memmove( &pStr[i], &pStr[i+1], len - i ); + --len; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Strip off the last directory from dirName +// Input : *dirName - +// maxlen - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool V_StripLastDir( char *dirName, int maxlen ) +{ + if( dirName[0] == 0 || + !V_stricmp( dirName, "./" ) || + !V_stricmp( dirName, ".\\" ) ) + return false; + + int len = V_strlen( dirName ); + + Assert( len < maxlen ); + + // skip trailing slash + if ( PATHSEPARATOR( dirName[len-1] ) ) + { + len--; + } + + bool bHitColon = false; + while ( len > 0 ) + { + if ( PATHSEPARATOR( dirName[len-1] ) ) + { + dirName[len] = 0; + V_FixSlashes( dirName, CORRECT_PATH_SEPARATOR ); + return true; + } + else if ( dirName[len-1] == ':' ) + { + bHitColon = true; + } + + len--; + } + + // If we hit a drive letter, then we're done. + // Ex: If they passed in c:\, then V_StripLastDir should return "" and false. + if ( bHitColon ) + { + dirName[0] = 0; + return false; + } + + // Allow it to return an empty string and true. This can happen if something like "tf2/" is passed in. + // The correct behavior is to strip off the last directory ("tf2") and return true. + if ( len == 0 && !bHitColon ) + { + V_snprintf( dirName, maxlen, ".%c", CORRECT_PATH_SEPARATOR ); + return true; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the beginning of the unqualified file name +// (no path information) +// Input: in - file name (may be unqualified, relative or absolute path) +// Output: pointer to unqualified file name +//----------------------------------------------------------------------------- +const char * V_UnqualifiedFileName( const char * in ) +{ + if ( !in || !in[0] ) + return in; + + // back up until the character after the first path separator we find, + // or the beginning of the string + const char * out = in + strlen( in ) - 1; + while ( ( out > in ) && ( !PATHSEPARATOR( *( out-1 ) ) ) ) + out--; + return out; +} + +char *V_UnqualifiedFileName( char *in ) +{ + return const_cast<char *>( V_UnqualifiedFileName( const_cast<const char *>(in) ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Composes a path and filename together, inserting a path separator +// if need be +// Input: path - path to use +// filename - filename to use +// dest - buffer to compose result in +// destSize - size of destination buffer +//----------------------------------------------------------------------------- +void V_ComposeFileName( const char *path, const char *filename, char *dest, int destSize ) +{ + V_strncpy( dest, path, destSize ); + V_FixSlashes( dest ); + V_AppendSlash( dest, destSize ); + V_strncat( dest, filename, destSize, COPY_ALL_CHARACTERS ); + V_FixSlashes( dest ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *path - +// *dest - +// destSize - +// Output : void V_ExtractFilePath +//----------------------------------------------------------------------------- +bool V_ExtractFilePath (const char *path, char *dest, int destSize ) +{ + Assert( destSize >= 1 ); + if ( destSize < 1 ) + { + return false; + } + + // Last char + int len = V_strlen(path); + const char *src = path + (len ? len-1 : 0); + + // back up until a \ or the start + while ( src != path && !PATHSEPARATOR( *(src-1) ) ) + { + src--; + } + + int copysize = MIN( src - path, destSize - 1 ); + memcpy( dest, path, copysize ); + dest[copysize] = 0; + + return copysize != 0 ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *path - +// *dest - +// destSize - +// Output : void V_ExtractFileExtension +//----------------------------------------------------------------------------- +void V_ExtractFileExtension( const char *path, char *dest, int destSize ) +{ + *dest = 0; + const char * extension = V_GetFileExtension( path ); + if ( NULL != extension ) + V_strncpy( dest, extension, destSize ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the file extension within a file name string +// Input: in - file name +// Output: pointer to beginning of extension (after the "."), or NULL +// if there is no extension +//----------------------------------------------------------------------------- +const char * V_GetFileExtension( const char * path ) +{ + const char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && !PATHSEPARATOR( *src ) && *(src-1) != '.' ) + src--; + + // check to see if the '.' is part of a pathname + if (src == path || PATHSEPARATOR( *src ) ) + { + return NULL; // no extension + } + + return src; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a pointer to the filename part of a path string +// Input: in - file name +// Output: pointer to beginning of filename (after the "/"). If there were no /, +// output is identical to input +//----------------------------------------------------------------------------- +const char *V_GetFileName( const char *pPath ) +{ + if ( !pPath || !pPath[0] ) + return pPath; + + const char *pSrc; + pSrc = pPath + strlen( pPath ) - 1; + + // back up until a / or the start + while ( pSrc > pPath && !PATHSEPARATOR( *( pSrc-1 ) ) ) + --pSrc; + + return pSrc; +} + +bool V_RemoveDotSlashes( char *pFilename, char separator ) +{ + // Remove '//' or '\\' + char *pIn = pFilename; + char *pOut = pFilename; + bool bPrevPathSep = false; + while ( *pIn ) + { + bool bIsPathSep = PATHSEPARATOR( *pIn ); + if ( !bIsPathSep || !bPrevPathSep ) + { + *pOut++ = *pIn; + } + bPrevPathSep = bIsPathSep; + ++pIn; + } + *pOut = 0; + + // Get rid of "./"'s + pIn = pFilename; + pOut = pFilename; + while ( *pIn ) + { + // The logic on the second line is preventing it from screwing up "../" + if ( pIn[0] == '.' && PATHSEPARATOR( pIn[1] ) && + (pIn == pFilename || pIn[-1] != '.') ) + { + pIn += 2; + } + else + { + *pOut = *pIn; + ++pIn; + ++pOut; + } + } + *pOut = 0; + + // Get rid of a trailing "/." (needless). + int len = strlen( pFilename ); + if ( len > 2 && pFilename[len-1] == '.' && PATHSEPARATOR( pFilename[len-2] ) ) + { + pFilename[len-2] = 0; + } + + // Each time we encounter a "..", back up until we've read the previous directory name, + // then get rid of it. + pIn = pFilename; + while ( *pIn ) + { + if ( pIn[0] == '.' && + pIn[1] == '.' && + (pIn == pFilename || PATHSEPARATOR(pIn[-1])) && // Preceding character must be a slash. + (pIn[2] == 0 || PATHSEPARATOR(pIn[2])) ) // Following character must be a slash or the end of the string. + { + char *pEndOfDots = pIn + 2; + char *pStart = pIn - 2; + + // Ok, now scan back for the path separator that starts the preceding directory. + while ( 1 ) + { + if ( pStart < pFilename ) + return false; + + if ( PATHSEPARATOR( *pStart ) ) + break; + + --pStart; + } + + // Now slide the string down to get rid of the previous directory and the ".." + memmove( pStart, pEndOfDots, strlen( pEndOfDots ) + 1 ); + + // Start over. + pIn = pFilename; + } + else + { + ++pIn; + } + } + + V_FixSlashes( pFilename, separator ); + return true; +} + + +void V_AppendSlash( char *pStr, int strSize ) +{ + int len = V_strlen( pStr ); + if ( len > 0 && !PATHSEPARATOR(pStr[len-1]) ) + { + if ( len+1 >= strSize ) + Error( "V_AppendSlash: ran out of space on %s.", pStr ); + + pStr[len] = CORRECT_PATH_SEPARATOR; + pStr[len+1] = 0; + } +} + + +void V_MakeAbsolutePath( char *pOut, int outLen, const char *pPath, const char *pStartingDir ) +{ + if ( V_IsAbsolutePath( pPath ) ) + { + // pPath is not relative.. just copy it. + V_strncpy( pOut, pPath, outLen ); + } + else + { + // Make sure the starting directory is absolute.. + if ( pStartingDir && V_IsAbsolutePath( pStartingDir ) ) + { + V_strncpy( pOut, pStartingDir, outLen ); + } + else + { +#ifdef _PS3 + { + V_strncpy( pOut, g_pPS3PathInfo->GameImagePath(), outLen ); + } +#else + { + if ( !_getcwd( pOut, outLen ) ) + Error( "V_MakeAbsolutePath: _getcwd failed." ); + } +#endif + + if ( pStartingDir ) + { + V_AppendSlash( pOut, outLen ); + V_strncat( pOut, pStartingDir, outLen, COPY_ALL_CHARACTERS ); + } + } + + // Concatenate the paths. + V_AppendSlash( pOut, outLen ); + V_strncat( pOut, pPath, outLen, COPY_ALL_CHARACTERS ); + } + + if ( !V_RemoveDotSlashes( pOut ) ) + Error( "V_MakeAbsolutePath: tried to \"..\" past the root." ); + + V_FixSlashes( pOut ); +} + + +//----------------------------------------------------------------------------- +// Makes a relative path +//----------------------------------------------------------------------------- +bool V_MakeRelativePath( const char *pFullPath, const char *pDirectory, char *pRelativePath, int nBufLen ) +{ + pRelativePath[0] = 0; + + const char *pPath = pFullPath; + const char *pDir = pDirectory; + + // Strip out common parts of the path + const char *pLastCommonPath = NULL; + const char *pLastCommonDir = NULL; + while ( *pPath && ( tolower( *pPath ) == tolower( *pDir ) || + ( PATHSEPARATOR( *pPath ) && ( PATHSEPARATOR( *pDir ) || (*pDir == 0) ) ) ) ) + { + if ( PATHSEPARATOR( *pPath ) ) + { + pLastCommonPath = pPath + 1; + pLastCommonDir = pDir + 1; + } + if ( *pDir == 0 ) + { + --pLastCommonDir; + break; + } + ++pDir; ++pPath; + } + + // Nothing in common + if ( !pLastCommonPath ) + return false; + + // For each path separator remaining in the dir, need a ../ + int nOutLen = 0; + bool bLastCharWasSeparator = true; + for ( ; *pLastCommonDir; ++pLastCommonDir ) + { + if ( PATHSEPARATOR( *pLastCommonDir ) ) + { + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR; + bLastCharWasSeparator = true; + } + else + { + bLastCharWasSeparator = false; + } + } + + // Deal with relative paths not specified with a trailing slash + if ( !bLastCharWasSeparator ) + { + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = '.'; + pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR; + } + + // Copy the remaining part of the relative path over, fixing the path separators + for ( ; *pLastCommonPath; ++pLastCommonPath ) + { + if ( PATHSEPARATOR( *pLastCommonPath ) ) + { + pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR; + } + else + { + pRelativePath[nOutLen++] = *pLastCommonPath; + } + + // Check for overflow + if ( nOutLen == nBufLen - 1 ) + break; + } + + pRelativePath[nOutLen] = 0; + return true; +} + + +//----------------------------------------------------------------------------- +// small helper function shared by lots of modules +//----------------------------------------------------------------------------- +bool V_IsAbsolutePath( const char *pStr ) +{ + bool bIsAbsolute = ( pStr[0] && pStr[1] == ':' ) || pStr[0] == '/' || pStr[0] == '\\'; + if ( IsX360() && !bIsAbsolute ) + { + bIsAbsolute = ( V_stristr( pStr, ":" ) != NULL ); + } + return bIsAbsolute; +} + + +//----------------------------------------------------------------------------- +// Fixes up a file name, removing dot slashes, fixing slashes, converting to lowercase, etc. +//----------------------------------------------------------------------------- +void V_FixupPathName( char *pOut, size_t nOutLen, const char *pPath ) +{ + V_strncpy( pOut, pPath, nOutLen ); + V_FixSlashes( pOut ); + V_RemoveDotSlashes( pOut ); + V_FixDoubleSlashes( pOut ); + V_strlower( pOut ); +} + + +// Copies at most nCharsToCopy bytes from pIn into pOut. +// Returns false if it would have overflowed pOut's buffer. +static bool CopyToMaxChars( char *pOut, int outSize, const char *pIn, int nCharsToCopy ) +{ + if ( outSize == 0 ) + return false; + + int iOut = 0; + while ( *pIn && nCharsToCopy > 0 ) + { + if ( iOut == (outSize-1) ) + { + pOut[iOut] = 0; + return false; + } + pOut[iOut] = *pIn; + ++iOut; + ++pIn; + --nCharsToCopy; + } + + pOut[iOut] = 0; + return true; +} + + +// Returns true if it completed successfully. +// If it would overflow pOut, it fills as much as it can and returns false. +bool V_StrSubst( + const char *pIn, + const char *pMatch, + const char *pReplaceWith, + char *pOut, + int outLen, + bool bCaseSensitive + ) +{ + int replaceFromLen = strlen( pMatch ); + int replaceToLen = strlen( pReplaceWith ); + + const char *pInStart = pIn; + char *pOutPos = pOut; + pOutPos[0] = 0; + + while ( 1 ) + { + int nRemainingOut = outLen - (pOutPos - pOut); + + const char *pTestPos = ( bCaseSensitive ? strstr( pInStart, pMatch ) : V_stristr( pInStart, pMatch ) ); + if ( pTestPos ) + { + // Found an occurence of pMatch. First, copy whatever leads up to the string. + int copyLen = pTestPos - pInStart; + if ( !CopyToMaxChars( pOutPos, nRemainingOut, pInStart, copyLen ) ) + return false; + + // Did we hit the end of the output string? + if ( copyLen > nRemainingOut-1 ) + return false; + + pOutPos += strlen( pOutPos ); + nRemainingOut = outLen - (pOutPos - pOut); + + // Now add the replacement string. + if ( !CopyToMaxChars( pOutPos, nRemainingOut, pReplaceWith, replaceToLen ) ) + return false; + + pInStart += copyLen + replaceFromLen; + pOutPos += replaceToLen; + } + else + { + // We're at the end of pIn. Copy whatever remains and get out. + int copyLen = strlen( pInStart ); + V_strncpy( pOutPos, pInStart, nRemainingOut ); + return ( copyLen <= nRemainingOut-1 ); + } + } +} + + +char* AllocString( const char *pStr, int nMaxChars ) +{ + int allocLen; + if ( nMaxChars == -1 ) + allocLen = strlen( pStr ) + 1; + else + allocLen = MIN( (int)strlen(pStr), nMaxChars ) + 1; + + char *pOut = new char[allocLen]; + V_strncpy( pOut, pStr, allocLen ); + return pOut; +} + + + + +void V_SplitString2( const char *pString, const char **pSeparators, int nSeparators, CUtlVector<char*> &outStrings ) +{ + outStrings.Purge(); + const char *pCurPos = pString; + while ( 1 ) + { + int iFirstSeparator = -1; + const char *pFirstSeparator = 0; + for ( int i=0; i < nSeparators; i++ ) + { + const char *pTest = V_stristr( pCurPos, pSeparators[i] ); + if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) ) + { + iFirstSeparator = i; + pFirstSeparator = pTest; + } + } + + if ( pFirstSeparator ) + { + // Split on this separator and continue on. + int separatorLen = strlen( pSeparators[iFirstSeparator] ); + if ( pFirstSeparator > pCurPos ) + { + outStrings.AddToTail( AllocString( pCurPos, pFirstSeparator-pCurPos ) ); + } + + pCurPos = pFirstSeparator + separatorLen; + } + else + { + // Copy the rest of the string + if ( strlen( pCurPos ) ) + { + outStrings.AddToTail( AllocString( pCurPos, -1 ) ); + } + return; + } + } +} + + +void V_SplitString( const char *pString, const char *pSeparator, CUtlVector<char*> &outStrings ) +{ + V_SplitString2( pString, &pSeparator, 1, outStrings ); +} + + + +bool V_GetCurrentDirectory( char *pOut, int maxLen ) +{ +#if defined( _PS3 ) + Assert( 0 ); + return false; // not supported +#else // !_PS3 + return _getcwd( pOut, maxLen ) == pOut; +#endif // _PS3 +} + + +bool V_SetCurrentDirectory( const char *pDirName ) +{ +#if defined( _PS3 ) + Assert( 0 ); + return false; // not supported +#else // !_PS3 + return _chdir( pDirName ) == 0; +#endif // _PS3 +} + + +// This function takes a slice out of pStr and stores it in pOut. +// It follows the Python slice convention: +// Negative numbers wrap around the string (-1 references the last character). +// Numbers are clamped to the end of the string. +void V_StrSlice( const char *pStr, int firstChar, int lastCharNonInclusive, char *pOut, int outSize ) +{ + if ( outSize == 0 ) + return; + + int length = strlen( pStr ); + + // Fixup the string indices. + if ( firstChar < 0 ) + { + firstChar = length - (-firstChar % length); + } + else if ( firstChar >= length ) + { + pOut[0] = 0; + return; + } + + if ( lastCharNonInclusive < 0 ) + { + lastCharNonInclusive = length - (-lastCharNonInclusive % length); + } + else if ( lastCharNonInclusive > length ) + { + lastCharNonInclusive %= length; + } + + if ( lastCharNonInclusive <= firstChar ) + { + pOut[0] = 0; + return; + } + + int copyLen = lastCharNonInclusive - firstChar; + if ( copyLen <= (outSize-1) ) + { + memcpy( pOut, &pStr[firstChar], copyLen ); + pOut[copyLen] = 0; + } + else + { + memcpy( pOut, &pStr[firstChar], outSize-1 ); + pOut[outSize-1] = 0; + } +} + + +void V_StrLeft( const char *pStr, int nChars, char *pOut, int outSize ) +{ + if ( nChars == 0 ) + { + if ( outSize != 0 ) + pOut[0] = 0; + + return; + } + + V_StrSlice( pStr, 0, nChars, pOut, outSize ); +} + + +void V_StrRight( const char *pStr, int nChars, char *pOut, int outSize ) +{ + int len = strlen( pStr ); + if ( nChars >= len ) + { + V_strncpy( pOut, pStr, outSize ); + } + else + { + V_StrSlice( pStr, -nChars, strlen( pStr ), pOut, outSize ); + } +} + +//----------------------------------------------------------------------------- +// Convert multibyte to wchar + back +//----------------------------------------------------------------------------- +void V_strtowcs( const char *pString, int nInSize, wchar_t *pWString, int nOutSize ) +{ +#ifdef _WIN32 + if ( !MultiByteToWideChar( CP_UTF8, 0, pString, nInSize, pWString, nOutSize ) ) + { + *pWString = L'\0'; + } +#elif POSIX + if ( mbstowcs( pWString, pString, nOutSize / sizeof(wchar_t) ) <= 0 ) + { + *pWString = 0; + } +#endif +} + +void V_wcstostr( const wchar_t *pWString, int nInSize, char *pString, int nOutSize ) +{ +#ifdef _WIN32 + if ( !WideCharToMultiByte( CP_UTF8, 0, pWString, nInSize, pString, nOutSize, NULL, NULL ) ) + { + *pString = '\0'; + } +#elif POSIX + if ( wcstombs( pString, pWString, nOutSize ) <= 0 ) + { + *pString = '\0'; + } +#endif +} + + + +//-------------------------------------------------------------------------------- +// backslashification +//-------------------------------------------------------------------------------- + +static char s_BackSlashMap[]="\tt\nn\rr\"\"\\\\"; + +char *V_AddBackSlashesToSpecialChars( char const *pSrc ) +{ + // first, count how much space we are going to need + int nSpaceNeeded = 0; + for( char const *pScan = pSrc; *pScan; pScan++ ) + { + nSpaceNeeded++; + for(char const *pCharSet=s_BackSlashMap; *pCharSet; pCharSet += 2 ) + { + if ( *pCharSet == *pScan ) + nSpaceNeeded++; // we need to store a bakslash + } + } + char *pRet = new char[ nSpaceNeeded + 1 ]; // +1 for null + char *pOut = pRet; + + for( char const *pScan = pSrc; *pScan; pScan++ ) + { + bool bIsSpecial = false; + for(char const *pCharSet=s_BackSlashMap; *pCharSet; pCharSet += 2 ) + { + if ( *pCharSet == *pScan ) + { + *( pOut++ ) = '\\'; + *( pOut++ ) = pCharSet[1]; + bIsSpecial = true; + break; + } + } + if (! bIsSpecial ) + { + *( pOut++ ) = *pScan; + } + } + *( pOut++ ) = 0; + return pRet; +} + +void V_StringToIntArray( int *pVector, int count, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + V_strncpy( tempString, pString, sizeof(tempString) ); + pstr = pfront = tempString; + + for ( j = 0; j < count; j++ ) // lifted from pr_edict.c + { + pVector[j] = atoi( pfront ); + + while ( *pstr && *pstr != ' ' ) + pstr++; + if (!*pstr) + break; + pstr++; + pfront = pstr; + } + + for ( j++; j < count; j++ ) + { + pVector[j] = 0; + } +} + +void V_StringToColor32( color32 *color, const char *pString ) +{ + int tmp[4]; + V_StringToIntArray( tmp, 4, pString ); + color->r = tmp[0]; + color->g = tmp[1]; + color->b = tmp[2]; + color->a = tmp[3]; +} + + + +// 3d memory copy +void CopyMemory3D( void *pDest, void const *pSrc, + int nNumCols, int nNumRows, int nNumSlices, // dimensions of copy + int nSrcBytesPerRow, int nSrcBytesPerSlice, // strides for source. + int nDestBytesPerRow, int nDestBytesPerSlice // strides for dest + ) +{ + if ( nNumSlices && nNumRows && nNumCols ) + { + uint8 *pDestAdr = reinterpret_cast<uint8 *>( pDest ); + uint8 const *pSrcAdr = reinterpret_cast<uint8 const *>( pSrc ); + // first check for optimized cases + if ( ( nNumCols == nSrcBytesPerRow ) && ( nNumCols == nDestBytesPerRow ) ) // no row-to-row stride? + { + int n2DSize = nNumCols * nNumRows; + if ( nSrcBytesPerSlice == nDestBytesPerSlice ) // can we do one memcpy? + { + memcpy( pDestAdr, pSrcAdr, n2DSize * nNumSlices ); + } + else + { + // there might be some slice-to-slice stride + do + { + memcpy( pDestAdr, pSrcAdr, n2DSize ); + pDestAdr += nDestBytesPerSlice; + pSrcAdr += nSrcBytesPerSlice; + } while( nNumSlices-- ); + } + } + else + { + // there is row-by-row stride - we have to do the full nested loop + do + { + int nRowCtr = nNumRows; + uint8 const *pSrcRow = pSrcAdr; + uint8 *pDestRow = pDestAdr; + do + { + memcpy( pDestRow, pSrcRow, nNumCols ); + pDestRow += nDestBytesPerRow; + pSrcRow += nSrcBytesPerRow; + } while( --nRowCtr ); + pSrcAdr += nSrcBytesPerSlice; + pDestAdr += nDestBytesPerSlice; + } while( nNumSlices-- ); + } + } +} + +void V_TranslateLineFeedsToUnix( char *pStr ) +{ + char *pIn = pStr; + char *pOut = pStr; + while ( *pIn ) + { + if ( pIn[0] == '\r' && pIn[1] == '\n' ) + { + ++pIn; + } + *pOut++ = *pIn++; + } + *pOut = 0; +} + +static char s_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + +int HexToValue( char hex ) +{ + if( hex >= '0' && hex <= '9' ) + { + return hex - '0'; + } + if( hex >= 'A' && hex <= 'F' ) + { + return hex - 'A' + 10; + } + if( hex >= 'a' && hex <= 'f' ) + { + return hex - 'a' + 10; + } + // report error here + return -1; +} + +bool V_StringToBin( const char*pString, void *pBin, uint nBinSize ) +{ + if ( (uint)V_strlen( pString ) != nBinSize * 2 ) + { + return false; + } + + for ( uint i = 0; i < nBinSize; ++i ) + { + int high = HexToValue( pString[i*2+0] ); + int low = HexToValue( pString[i*2+1] ) ; + if( high < 0 || low < 0 ) + { + return false; + } + + ( ( uint8* )pBin )[i] = uint8( ( high << 4 ) | low ); + } + return true; +} + + +bool V_BinToString( char*pString, void *pBin, uint nBinSize ) +{ + for ( uint i = 0; i < nBinSize; ++i ) + { + pString[i*2+0] = s_hex[( ( uint8* )pBin )[i] >> 4 ]; + pString[i*2+1] = s_hex[( ( uint8* )pBin )[i] & 0xF]; + } + pString[nBinSize*2] = '\0'; + return true; +} + +// The following characters are not allowed to begin a line for Asian language line-breaking +// purposes. They include the right parenthesis/bracket, space character, period, exclamation, +// question mark, and a number of language-specific characters for Chinese, Japanese, and Korean +static const wchar_t wszCantBeginLine[] = +{ + 0x0020, 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, + 0x003e, 0x003f, 0x005d, 0x007d, 0x00a2, 0x00a8, 0x00b0, 0x00b7, + 0x00bb, 0x02c7, 0x02c9, 0x2010, 0x2013, 0x2014, 0x2015, 0x2016, + 0x2019, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2025, + 0x2026, 0x2027, 0x203a, 0x203c, 0x2047, 0x2048, 0x2049, 0x2103, + 0x2236, 0x2574, 0x3001, 0x3002, 0x3003, 0x3005, 0x3006, 0x3009, + 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0x3019, 0x301b, + 0x301c, 0x301e, 0x301f, 0x303b, 0x3041, 0x3043, 0x3045, 0x3047, + 0x3049, 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x3095, 0x3096, + 0x30a0, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3, + 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fd, 0x30fe, + 0x30fc, 0x31f0, 0x31f1, 0x31f2, 0x31f3, 0x31f4, 0x31f5, 0x31f6, + 0x31f7, 0x31f8, 0x31f9, 0x31fa, 0x31fb, 0x31fc, 0x31fd, 0x31fe, + 0x31ff, 0xfe30, 0xfe31, 0xfe32, 0xfe33, 0xfe36, 0xfe38, 0xfe3a, + 0xfe3c, 0xfe3e, 0xfe40, 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, + 0xfe52, 0xfe53, 0xfe54, 0xfe55, 0xfe56, 0xfe57, 0xfe58, 0xfe5a, + 0xfe5c, 0xfe5e, 0xff01, 0xff02, 0xff05, 0xff07, 0xff09, 0xff0c, + 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d, 0xff40, 0xff5c, 0xff5d, + 0xff5e, 0xff60, 0xff64 +}; + +// The following characters are not allowed to end a line for Asian Language line-breaking +// purposes. They include left parenthesis/bracket, currency symbols, and an number +// of language-specific characters for Chinese, Japanese, and Korean +static const wchar_t wszCantEndLine[] = +{ + 0x0024, 0x0028, 0x002a, 0x003c, 0x005b, 0x005c, 0x007b, 0x00a3, + 0x00a5, 0x00ab, 0x00ac, 0x00b7, 0x02c6, 0x2018, 0x201c, 0x201f, + 0x2035, 0x2039, 0x3005, 0x3007, 0x3008, 0x300a, 0x300c, 0x300e, + 0x3010, 0x3014, 0x3016, 0x3018, 0x301a, 0x301d, 0xfe34, 0xfe35, + 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59, + 0xfe5b, 0xfe5d, 0xff04, 0xff08, 0xff0e, 0xff3b, 0xff5b, 0xff5f, + 0xffe1, 0xffe5, 0xffe6 +}; + +// Can't break between some repeated punctuation patterns ("--", "...", "<asian period repeated>") +static const wchar_t wszCantBreakRepeated[] = +{ + 0x002d, 0x002e, 0x3002 +}; + +bool AsianWordWrap::CanEndLine( wchar_t wcCandidate ) +{ + for( int i = 0; i < SIZE_OF_ARRAY( wszCantEndLine ); ++i ) + { + if( wcCandidate == wszCantEndLine[i] ) + return false; + } + + return true; +} + +bool AsianWordWrap::CanBeginLine( wchar_t wcCandidate ) +{ + for( int i = 0; i < SIZE_OF_ARRAY( wszCantBeginLine ); ++i ) + { + if( wcCandidate == wszCantBeginLine[i] ) + return false; + } + + return true; +} + +bool AsianWordWrap::CanBreakRepeated( wchar_t wcCandidate ) +{ + for( int i = 0; i < SIZE_OF_ARRAY( wszCantBreakRepeated ); ++i ) + { + if( wcCandidate == wszCantBreakRepeated[i] ) + return false; + } + + return true; +} + +#if defined( _PS3 ) || defined( LINUX ) +inline int __cdecl iswascii(wchar_t c) { return ((unsigned)(c) < 0x80); } // not defined in wctype.h on the PS3 +#endif + +// Used to determine if we can break a line between the first two characters passed +bool AsianWordWrap::CanBreakAfter( const wchar_t* wsz ) +{ + if( wsz == NULL || wsz[0] == '\0' || wsz[1] == '\0' ) + { + return false; + } + + wchar_t first_char = wsz[0]; + wchar_t second_char = wsz[1]; + if( ( iswascii( first_char ) && iswascii( second_char ) ) // If not both CJK, return early + || ( iswalnum( first_char ) && iswalnum( second_char ) ) ) // both characters are alphanumeric - Don't split a number or a word! + { + return false; + } + + if( !CanEndLine( first_char ) ) + { + return false; + } + + if( !CanBeginLine( second_char) ) + { + return false; + } + + // don't allow line wrapping in the middle of "--" or "..." + if( ( first_char == second_char ) && ( !CanBreakRepeated( first_char ) ) ) + { + return false; + } + + // If no rules would prevent us from breaking, assume it's safe to break here + return true; +} + +// We use this function to determine where it is permissible to break lines +// of text while wrapping them. On some platforms, the native iswspace() function +// returns FALSE for the "non-breaking space" characters 0x00a0 and 0x202f, and so we don't +// break on them. On others (including the X360 and PC), iswspace returns TRUE for them. +// We get rid of the platform dependency by defining this wrapper which returns false +// for and calls through to the library function for everything else. +int isbreakablewspace( wchar_t ch ) +{ + // 0x00a0 and 0x202f are the wide and narrow non-breaking space UTF-16 values, respectively + return ch != 0x00a0 && ch != 0x202f && iswspace(ch); +} |