//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: String Tools // //===========================================================================// // These are redefined in the project settings to prevent anyone from using them. // We in this module are of a higher caste and thus are privileged in their use. #ifdef strncpy #undef strncpy #endif #ifdef _snprintf #undef _snprintf #endif #if defined( sprintf ) #undef sprintf #endif #if defined( vsprintf ) #undef vsprintf #endif #ifdef _vsnprintf #ifdef _WIN32 #undef _vsnprintf #endif #endif #ifdef vsnprintf #ifndef _WIN32 #undef vsnprintf #endif #endif #if defined( strcat ) #undef strcat #endif #ifdef strncat #undef strncat #endif // NOTE: I have to include stdio + stdarg first so vsnprintf gets compiled in #include #include #include "tier0/basetypes.h" #include "tier0/platform.h" #ifdef stricmp #undef stricmp #endif #ifdef POSIX #ifndef _PS3 #include #endif // _PS3 #include #include #include #define stricmp strcasecmp #elif _WIN32 #include #if !defined( _X360 ) #define WIN32_LEAN_AND_MEAN #include #endif #endif #ifdef _WIN32 #ifndef CP_UTF8 #define CP_UTF8 65001 #endif #endif #include "tier0/dbg.h" #include "tier1/strtools.h" #include #include #include "tier1/utldict.h" #if defined( _X360 ) #include "xbox/xbox_win32stubs.h" #elif defined( _PS3 ) #include "ps3_pathinfo.h" #include // 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( V_UnqualifiedFileName( const_cast(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 &outStrings ) { outStrings.Purge(); const char *pCurPos = pString; while ( 1 ) { int iFirstSeparator = -1; const char *pFirstSeparator = 0; for ( int i=0; i < nSeparators; i++ ) { const char *pTest = V_stristr( pCurPos, pSeparators[i] ); if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) ) { iFirstSeparator = i; pFirstSeparator = pTest; } } if ( pFirstSeparator ) { // Split on this separator and continue on. int separatorLen = strlen( pSeparators[iFirstSeparator] ); if ( pFirstSeparator > pCurPos ) { outStrings.AddToTail( AllocString( pCurPos, pFirstSeparator-pCurPos ) ); } pCurPos = pFirstSeparator + separatorLen; } else { // Copy the rest of the string if ( strlen( pCurPos ) ) { outStrings.AddToTail( AllocString( pCurPos, -1 ) ); } return; } } } void V_SplitString( const char *pString, const char *pSeparator, CUtlVector &outStrings ) { V_SplitString2( pString, &pSeparator, 1, outStrings ); } bool V_GetCurrentDirectory( char *pOut, int maxLen ) { #if defined( _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( pDest ); uint8 const *pSrcAdr = reinterpret_cast( 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 ("--", "...", "") 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); }