Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5118 lines
125 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======//
  2. //
  3. // Purpose: String Tools
  4. //
  5. //===========================================================================//
  6. // These are redefined in the project settings to prevent anyone from using them.
  7. // We in this module are of a higher caste and thus are privileged in their use.
  8. #ifdef strncpy
  9. #undef strncpy
  10. #endif
  11. #ifdef _snprintf
  12. #undef _snprintf
  13. #endif
  14. #if defined( sprintf )
  15. #undef sprintf
  16. #endif
  17. #if defined( vsprintf )
  18. #undef vsprintf
  19. #endif
  20. #ifdef _vsnprintf
  21. #ifdef _WIN32
  22. #undef _vsnprintf
  23. #endif
  24. #endif
  25. #ifdef vsnprintf
  26. #ifndef _WIN32
  27. #undef vsnprintf
  28. #endif
  29. #endif
  30. #if defined( strcat )
  31. #undef strcat
  32. #endif
  33. #ifdef strncat
  34. #undef strncat
  35. #endif
  36. // NOTE: I have to include stdio + stdarg first so vsnprintf gets compiled in
  37. #include <stdio.h>
  38. #include <stdarg.h>
  39. #include "tier0/basetypes.h"
  40. #include "tier0/platform.h"
  41. #ifdef stricmp
  42. #undef stricmp
  43. #endif
  44. #ifdef POSIX
  45. #ifndef _PS3
  46. #include <iconv.h>
  47. #endif // _PS3
  48. #include <ctype.h>
  49. #include <unistd.h>
  50. #include <stdlib.h>
  51. #define stricmp strcasecmp
  52. #define _strtoi64 strtoll
  53. #define _strtoui64 strtoull
  54. #elif _WIN32
  55. #include <direct.h>
  56. #if !defined( _X360 )
  57. #define WIN32_LEAN_AND_MEAN
  58. #include <windows.h>
  59. #endif
  60. #endif
  61. #ifdef _WIN32
  62. #ifndef CP_UTF8
  63. #define CP_UTF8 65001
  64. #endif
  65. #endif
  66. #include "tier0/dbg.h"
  67. #include "tier1/strtools.h"
  68. #include <string.h>
  69. #include <stdlib.h>
  70. #include "tier1/utldict.h"
  71. #include "tier1/characterset.h"
  72. #include "tier1/utlstring.h"
  73. #include "tier1/fmtstr.h"
  74. #if defined( _X360 )
  75. #include "xbox/xbox_win32stubs.h"
  76. #elif defined( _PS3 )
  77. #include "ps3_pathinfo.h"
  78. #include <cell/l10n.h> // for UCS-2 to UTF-8 conversion
  79. #endif
  80. #include "tier0/vprof.h"
  81. #include "tier0/memdbgon.h"
  82. #ifndef NDEBUG
  83. static volatile const char *pDebugString;
  84. #define DEBUG_LINK_CHECK pDebugString = "tier1.lib built debug!"
  85. #else
  86. #define DEBUG_LINK_CHECK
  87. #endif
  88. void _V_memset (void *dest, int fill, int count)
  89. {
  90. DEBUG_LINK_CHECK;
  91. Assert( count >= 0 );
  92. memset(dest,fill,count);
  93. }
  94. void _V_memcpy (void *dest, const void *src, int count)
  95. {
  96. Assert( count >= 0 );
  97. memcpy( dest, src, count );
  98. }
  99. void _V_memmove(void *dest, const void *src, int count)
  100. {
  101. Assert( count >= 0 );
  102. memmove( dest, src, count );
  103. }
  104. int _V_memcmp (const void *m1, const void *m2, int count)
  105. {
  106. DEBUG_LINK_CHECK;
  107. Assert( count >= 0 );
  108. return memcmp( m1, m2, count );
  109. }
  110. int _V_strlen(const char *str)
  111. {
  112. #ifdef POSIX
  113. if ( !str )
  114. return 0;
  115. #endif
  116. return ( int )strlen( str );
  117. }
  118. #ifdef OSX
  119. size_t strnlen( const char *s, size_t n )
  120. {
  121. const char *p = (const char *)memchr( s, 0, n );
  122. return (p ? p - s : n);
  123. }
  124. #endif
  125. int _V_strnlen(const char *str, int count )
  126. {
  127. #ifdef POSIX
  128. if ( !str )
  129. return 0;
  130. #endif
  131. return ( int )strnlen( str, count );
  132. }
  133. void _V_strcpy (char *dest, const char *src)
  134. {
  135. DEBUG_LINK_CHECK;
  136. strcpy( dest, src );
  137. }
  138. int _V_wcslen(const wchar_t *pwch)
  139. {
  140. return ( int )wcslen( pwch );
  141. }
  142. char *_V_strrchr(const char *s, char c)
  143. {
  144. int len = V_strlen(s);
  145. s += len;
  146. while (len--)
  147. if (*--s == c) return (char *)s;
  148. return 0;
  149. }
  150. int _V_strcmp (const char *s1, const char *s2)
  151. {
  152. VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL );
  153. return strcmp( s1, s2 );
  154. }
  155. int _V_wcscmp (const wchar_t *s1, const wchar_t *s2)
  156. {
  157. while (1)
  158. {
  159. if (*s1 != *s2)
  160. return *s1 < *s2 ? -1 : 1; // strings not equal
  161. if (!*s1)
  162. return 0; // strings are equal
  163. s1++;
  164. s2++;
  165. }
  166. return -1;
  167. }
  168. int _V_stricmp( const char *s1, const char *s2 )
  169. {
  170. VPROF_2( "V_stricmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL );
  171. // It is not uncommon to compare a string to itself. Since stricmp
  172. // is expensive and pointer comparison is cheap, this simple test
  173. // can save a lot of cycles, and cache pollution.
  174. // This also implicitly does the s1 and s2 both equal to NULL check
  175. // that the POSIX code used to have.
  176. if ( s1 == s2 )
  177. return 0;
  178. #ifdef POSIX
  179. if ( s1 == NULL )
  180. return -1;
  181. if ( s2 == NULL )
  182. return 1;
  183. return stricmp( s1, s2 );
  184. #else
  185. uint8 const *pS1 = ( uint8 const * ) s1;
  186. uint8 const *pS2 = ( uint8 const * ) s2;
  187. for(;;)
  188. {
  189. int c1 = *( pS1++ );
  190. int c2 = *( pS2++ );
  191. if ( c1 == c2 )
  192. {
  193. if ( !c1 ) return 0;
  194. }
  195. else
  196. {
  197. if ( ! c2 )
  198. {
  199. return c1 - c2;
  200. }
  201. c1 = FastASCIIToLower( c1 );
  202. c2 = FastASCIIToLower( c2 );
  203. if ( c1 != c2 )
  204. {
  205. return c1 - c2;
  206. }
  207. }
  208. c1 = *( pS1++ );
  209. c2 = *( pS2++ );
  210. if ( c1 == c2 )
  211. {
  212. if ( !c1 ) return 0;
  213. }
  214. else
  215. {
  216. if ( ! c2 )
  217. {
  218. return c1 - c2;
  219. }
  220. c1 = FastASCIIToLower( c1 );
  221. c2 = FastASCIIToLower( c2 );
  222. if ( c1 != c2 )
  223. {
  224. return c1 - c2;
  225. }
  226. }
  227. }
  228. #endif
  229. }
  230. // A special high-performance case-insensitive compare function
  231. // returns 0 if strings match exactly
  232. // returns >0 if strings match in a case-insensitive way, but do not match exactly
  233. // returns <0 if strings do not match even in a case-insensitive way
  234. int _V_stricmp_NegativeForUnequal( const char *s1, const char *s2 )
  235. {
  236. VPROF_2( "V_stricmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL );
  237. // It is not uncommon to compare a string to itself. Since stricmp
  238. // is expensive and pointer comparison is cheap, this simple test
  239. // can save a lot of cycles, and cache pollution.
  240. if ( s1 == s2 )
  241. return 0;
  242. uint8 const *pS1 = ( uint8 const * ) s1;
  243. uint8 const *pS2 = ( uint8 const * ) s2;
  244. int iExactMatchResult = 1;
  245. for(;;)
  246. {
  247. int c1 = *( pS1++ );
  248. int c2 = *( pS2++ );
  249. if ( c1 == c2 )
  250. {
  251. // strings are case-insensitive equal, coerce accumulated
  252. // case-difference to 0/1 and return it
  253. if ( !c1 ) return !iExactMatchResult;
  254. }
  255. else
  256. {
  257. if ( ! c2 )
  258. {
  259. // c2=0 and != c1 => not equal
  260. return -1;
  261. }
  262. iExactMatchResult = 0;
  263. c1 = FastASCIIToLower( c1 );
  264. c2 = FastASCIIToLower( c2 );
  265. if ( c1 != c2 )
  266. {
  267. // strings are not equal
  268. return -1;
  269. }
  270. }
  271. c1 = *( pS1++ );
  272. c2 = *( pS2++ );
  273. if ( c1 == c2 )
  274. {
  275. // strings are case-insensitive equal, coerce accumulated
  276. // case-difference to 0/1 and return it
  277. if ( !c1 ) return !iExactMatchResult;
  278. }
  279. else
  280. {
  281. if ( ! c2 )
  282. {
  283. // c2=0 and != c1 => not equal
  284. return -1;
  285. }
  286. iExactMatchResult = 0;
  287. c1 = FastASCIIToLower( c1 );
  288. c2 = FastASCIIToLower( c2 );
  289. if ( c1 != c2 )
  290. {
  291. // strings are not equal
  292. return -1;
  293. }
  294. }
  295. }
  296. }
  297. char *_V_strstr( const char *s1, const char *search )
  298. {
  299. #if defined( _X360 )
  300. return (char *)strstr( (char *)s1, search );
  301. #else
  302. return (char *)strstr( s1, search );
  303. #endif
  304. }
  305. char *_V_strupr( char *start )
  306. {
  307. return strupr( start );
  308. }
  309. char *_V_strlower( char *start )
  310. {
  311. return strlwr( start );
  312. }
  313. wchar_t *_V_wcsupr (wchar_t *start)
  314. {
  315. return _wcsupr( start );
  316. }
  317. wchar_t *_V_wcslower (wchar_t *start)
  318. {
  319. return _wcslwr( start );
  320. }
  321. int V_strncmp(const char *s1, const char *s2, int count)
  322. {
  323. Assert( count >= 0 );
  324. VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL );
  325. while ( count-- > 0 )
  326. {
  327. if ( *s1 != *s2 )
  328. return *s1 < *s2 ? -1 : 1; // string different
  329. if ( *s1 == '\0' )
  330. return 0; // null terminator hit - strings the same
  331. s1++;
  332. s2++;
  333. }
  334. return 0; // count characters compared the same
  335. }
  336. char *V_strnlwr(char *s, size_t count)
  337. {
  338. Assert( count >= 0 );
  339. char* pRet = s;
  340. if ( !s || !count )
  341. return s;
  342. while ( -- count > 0 )
  343. {
  344. if ( !*s )
  345. return pRet; // reached end of string
  346. *s = tolower( *s );
  347. ++s;
  348. }
  349. *s = 0; // null-terminate original string at "count-1"
  350. return pRet;
  351. }
  352. int V_strncasecmp (const char *s1, const char *s2, int n)
  353. {
  354. Assert( n >= 0 );
  355. VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL );
  356. while ( n-- > 0 )
  357. {
  358. int c1 = *s1++;
  359. int c2 = *s2++;
  360. if (c1 != c2)
  361. {
  362. if (c1 >= 'a' && c1 <= 'z')
  363. c1 -= ('a' - 'A');
  364. if (c2 >= 'a' && c2 <= 'z')
  365. c2 -= ('a' - 'A');
  366. if (c1 != c2)
  367. return c1 < c2 ? -1 : 1;
  368. }
  369. if ( c1 == '\0' )
  370. return 0; // null terminator hit - strings the same
  371. }
  372. return 0; // n characters compared the same
  373. }
  374. int V_strcasecmp( const char *s1, const char *s2 )
  375. {
  376. VPROF_2( "V_strcmp", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, BUDGETFLAG_ALL );
  377. return V_stricmp( s1, s2 );
  378. }
  379. int V_strnicmp (const char *s1, const char *s2, int n)
  380. {
  381. DEBUG_LINK_CHECK;
  382. Assert( n >= 0 );
  383. return V_strncasecmp( s1, s2, n );
  384. }
  385. const char *StringAfterPrefix( const char *str, const char *prefix )
  386. {
  387. do
  388. {
  389. if ( !*prefix )
  390. return str;
  391. }
  392. while ( tolower( *str++ ) == tolower( *prefix++ ) );
  393. return NULL;
  394. }
  395. const char *StringAfterPrefixCaseSensitive( const char *str, const char *prefix )
  396. {
  397. do
  398. {
  399. if ( !*prefix )
  400. return str;
  401. }
  402. while ( *str++ == *prefix++ );
  403. return NULL;
  404. }
  405. int64 V_atoi64( const char *str )
  406. {
  407. int64 val;
  408. int64 sign;
  409. int64 c;
  410. Assert( str );
  411. if (*str == '-')
  412. {
  413. sign = -1;
  414. str++;
  415. }
  416. else if (*str == '+')
  417. {
  418. sign = 1;
  419. str++;
  420. }
  421. else
  422. {
  423. sign = 1;
  424. }
  425. val = 0;
  426. //
  427. // check for hex
  428. //
  429. if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
  430. {
  431. str += 2;
  432. while (1)
  433. {
  434. c = *str++;
  435. if (c >= '0' && c <= '9')
  436. val = (val<<4) + c - '0';
  437. else if (c >= 'a' && c <= 'f')
  438. val = (val<<4) + c - 'a' + 10;
  439. else if (c >= 'A' && c <= 'F')
  440. val = (val<<4) + c - 'A' + 10;
  441. else
  442. return val*sign;
  443. }
  444. }
  445. //
  446. // check for character
  447. //
  448. if (str[0] == '\'')
  449. {
  450. return sign * str[1];
  451. }
  452. //
  453. // assume decimal
  454. //
  455. while (1)
  456. {
  457. c = *str++;
  458. if (c <'0' || c > '9')
  459. return val*sign;
  460. val = val*10 + c - '0';
  461. }
  462. return 0;
  463. }
  464. uint64 V_atoui64( const char *str )
  465. {
  466. uint64 val;
  467. uint64 c;
  468. Assert( str );
  469. val = 0;
  470. //
  471. // check for hex
  472. //
  473. if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
  474. {
  475. str += 2;
  476. while (1)
  477. {
  478. c = *str++;
  479. if (c >= '0' && c <= '9')
  480. val = (val<<4) + c - '0';
  481. else if (c >= 'a' && c <= 'f')
  482. val = (val<<4) + c - 'a' + 10;
  483. else if (c >= 'A' && c <= 'F')
  484. val = (val<<4) + c - 'A' + 10;
  485. else
  486. return val;
  487. }
  488. }
  489. //
  490. // check for character
  491. //
  492. if (str[0] == '\'')
  493. {
  494. return str[1];
  495. }
  496. //
  497. // assume decimal
  498. //
  499. while (1)
  500. {
  501. c = *str++;
  502. if (c <'0' || c > '9')
  503. return val;
  504. val = val*10 + c - '0';
  505. }
  506. return 0;
  507. }
  508. int V_atoi( const char *str )
  509. {
  510. return (int)V_atoi64( str );
  511. }
  512. float V_atof (const char *str)
  513. {
  514. return (float)V_atod( str );
  515. }
  516. double V_atod(const char *str)
  517. {
  518. DEBUG_LINK_CHECK;
  519. double val;
  520. int sign;
  521. int c;
  522. int decimal, total;
  523. if (*str == '-')
  524. {
  525. sign = -1;
  526. str++;
  527. }
  528. else if (*str == '+')
  529. {
  530. sign = 1;
  531. str++;
  532. }
  533. else
  534. {
  535. sign = 1;
  536. }
  537. val = 0;
  538. //
  539. // check for hex
  540. //
  541. if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )
  542. {
  543. str += 2;
  544. while (1)
  545. {
  546. c = *str++;
  547. if (c >= '0' && c <= '9')
  548. val = (val*16) + c - '0';
  549. else if (c >= 'a' && c <= 'f')
  550. val = (val*16) + c - 'a' + 10;
  551. else if (c >= 'A' && c <= 'F')
  552. val = (val*16) + c - 'A' + 10;
  553. else
  554. return val*sign;
  555. }
  556. }
  557. //
  558. // check for character
  559. //
  560. if (str[0] == '\'')
  561. {
  562. return sign * str[1];
  563. }
  564. //
  565. // assume decimal
  566. //
  567. decimal = -1;
  568. total = 0;
  569. int exponent = 0;
  570. while (1)
  571. {
  572. c = *str++;
  573. if (c == '.')
  574. {
  575. if ( decimal != -1 )
  576. {
  577. break;
  578. }
  579. decimal = total;
  580. continue;
  581. }
  582. if (c <'0' || c > '9')
  583. {
  584. if ( c == 'e' || c == 'E' )
  585. {
  586. exponent = V_atoi(str);
  587. }
  588. break;
  589. }
  590. val = val*10 + c - '0';
  591. total++;
  592. }
  593. if ( exponent != 0 )
  594. {
  595. val *= pow( 10.0, exponent );
  596. }
  597. if (decimal == -1)
  598. return val*sign;
  599. while (total > decimal)
  600. {
  601. val /= 10;
  602. total--;
  603. }
  604. return val*sign;
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Normalizes a float string in place.
  608. //
  609. // (removes leading zeros, trailing zeros after the decimal point, and the decimal point itself where possible)
  610. //-----------------------------------------------------------------------------
  611. void V_normalizeFloatString( char* pFloat )
  612. {
  613. // If we have a decimal point, remove trailing zeroes:
  614. if( strchr( pFloat,'.' ) )
  615. {
  616. int len = V_strlen(pFloat);
  617. while( len > 1 && pFloat[len - 1] == '0' )
  618. {
  619. pFloat[len - 1] = '\0';
  620. len--;
  621. }
  622. if( len > 1 && pFloat[ len - 1 ] == '.' )
  623. {
  624. pFloat[len - 1] = '\0';
  625. len--;
  626. }
  627. }
  628. // TODO: Strip leading zeros
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Finds a string in another string with a case insensitive test
  632. //-----------------------------------------------------------------------------
  633. const char* V_stristr( const char* pStr, const char* pSearch )
  634. {
  635. Assert( pStr );
  636. Assert( pSearch );
  637. if (!pStr || !pSearch)
  638. return 0;
  639. const char* pLetter = pStr;
  640. // Check the entire string
  641. while (*pLetter != 0)
  642. {
  643. // Skip over non-matches
  644. if ( FastASCIIToLower( *pLetter ) == FastASCIIToLower( *pSearch) )
  645. {
  646. // Check for match
  647. const char* pMatch = pLetter + 1;
  648. const char* pTest = pSearch + 1;
  649. while (*pTest != 0)
  650. {
  651. // We've run off the end; don't bother.
  652. if (*pMatch == 0)
  653. return 0;
  654. if ( FastASCIIToLower( *pMatch) != FastASCIIToLower( *pTest ) )
  655. break;
  656. ++pMatch;
  657. ++pTest;
  658. }
  659. // Found a match!
  660. if ( *pTest == 0 )
  661. return pLetter;
  662. }
  663. ++pLetter;
  664. }
  665. return 0;
  666. }
  667. char* V_stristr( char* pStr, const char* pSearch )
  668. {
  669. return (char*)V_stristr( (const char*)pStr, pSearch );
  670. }
  671. const wchar_t* V_wcsistr( const wchar_t* pStr, const wchar_t* pSearch )
  672. {
  673. Assert(pStr);
  674. Assert(pSearch);
  675. if (!pStr || !pSearch)
  676. return 0;
  677. wchar_t const* pLetter = pStr;
  678. // Check the entire string
  679. while (*pLetter != 0)
  680. {
  681. // Skip over non-matches
  682. if (towlower((wchar_t)*pLetter) == towlower((wchar_t)*pSearch))
  683. {
  684. // Check for match
  685. wchar_t const* pMatch = pLetter + 1;
  686. wchar_t const* pTest = pSearch + 1;
  687. while (*pTest != 0)
  688. {
  689. // We've run off the end; don't bother.
  690. if (*pMatch == 0)
  691. return 0;
  692. if (towlower((wchar_t)*pMatch) != towlower((wchar_t)*pTest))
  693. break;
  694. ++pMatch;
  695. ++pTest;
  696. }
  697. // Found a match!
  698. if (*pTest == 0)
  699. return pLetter;
  700. }
  701. ++pLetter;
  702. }
  703. return 0;
  704. }
  705. wchar_t* V_wcsistr( wchar_t* pStr, const wchar_t* pSearch )
  706. {
  707. return (wchar_t*)V_wcsistr( (wchar_t const*)pStr, pSearch );
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Finds a string in another string with a case insensitive test w/ length validation
  711. //-----------------------------------------------------------------------------
  712. const char* V_strnistr( const char* pStr, const char* pSearch, int n )
  713. {
  714. Assert( pStr );
  715. Assert( pSearch );
  716. if (!pStr || !pSearch)
  717. return 0;
  718. const char* pLetter = pStr;
  719. // Check the entire string
  720. while (*pLetter != 0)
  721. {
  722. if ( n <= 0 )
  723. return 0;
  724. // Skip over non-matches
  725. if (FastASCIIToLower(*pLetter) == FastASCIIToLower(*pSearch))
  726. {
  727. int n1 = n - 1;
  728. // Check for match
  729. const char* pMatch = pLetter + 1;
  730. const char* pTest = pSearch + 1;
  731. while (*pTest != 0)
  732. {
  733. if ( n1 <= 0 )
  734. return 0;
  735. // We've run off the end; don't bother.
  736. if (*pMatch == 0)
  737. return 0;
  738. if (FastASCIIToLower(*pMatch) != FastASCIIToLower(*pTest))
  739. break;
  740. ++pMatch;
  741. ++pTest;
  742. --n1;
  743. }
  744. // Found a match!
  745. if (*pTest == 0)
  746. return pLetter;
  747. }
  748. ++pLetter;
  749. --n;
  750. }
  751. return 0;
  752. }
  753. const char* V_strnchr( const char* pStr, char c, int n )
  754. {
  755. const char* pLetter = pStr;
  756. const char* pLast = pStr + n;
  757. // Check the entire string
  758. while ( (pLetter < pLast) && (*pLetter != 0) )
  759. {
  760. if (*pLetter == c)
  761. return pLetter;
  762. ++pLetter;
  763. }
  764. return NULL;
  765. }
  766. void V_strncpy( char *pDest, const char *pSrc, int maxLen )
  767. {
  768. Assert( maxLen >= sizeof( *pDest ) );
  769. DEBUG_LINK_CHECK;
  770. // NOTE: Never never use strncpy! Here's what it actually does, which is not what we want!
  771. // (from MSDN)
  772. // The strncpy function copies the initial count characters of strSource to strDest
  773. // and returns strDest. If count is less than or equal to the length of strSource,
  774. // a null character is not appended automatically to the copied string. If count
  775. // is greater than the length of strSource, the destination string is padded with
  776. // null characters up to length count. The behavior of strncpy is undefined
  777. // if the source and destination strings overlap.
  778. // strncpy( pDest, pSrc, maxLen );
  779. // FIXME: This could be optimized to do copies a dword at a time maybe?
  780. char *pLast = pDest + maxLen - 1;
  781. while ( (pDest < pLast) && (*pSrc != 0) )
  782. {
  783. *pDest = *pSrc;
  784. ++pDest; ++pSrc;
  785. }
  786. *pDest = 0;
  787. }
  788. // warning C6053: Call to 'wcsncpy' might not zero-terminate string 'pDest'
  789. // warning C6059: Incorrect length parameter in call to 'strncat'. Pass the number of remaining characters, not the buffer size of 'argument 1'
  790. // warning C6386: Buffer overrun: accessing 'argument 1', the writable size is 'destBufferSize' bytes, but '1000' bytes might be written
  791. // These warnings were investigated through code inspection and writing of tests and they are
  792. // believed to all be spurious.
  793. #ifdef _PREFAST_
  794. #pragma warning( push )
  795. #pragma warning( disable : 6053 6059 6386 )
  796. #endif
  797. void V_wcsncpy( OUT_Z_BYTECAP(maxLenInBytes) wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes )
  798. {
  799. Assert( maxLenInBytes >= sizeof( *pDest ) );
  800. int maxLen = maxLenInBytes / sizeof(wchar_t);
  801. wcsncpy( pDest, pSrc, maxLen );
  802. if( maxLen )
  803. {
  804. pDest[maxLen-1] = 0;
  805. }
  806. }
  807. #ifdef _PREFAST_
  808. // Suppress warnings about _vsnwprintf and _vsnprintf not zero-terminating the buffers.
  809. // We explicitly null-terminate in the cases that matter.
  810. #pragma warning( disable : 6053 )
  811. #endif
  812. int V_snwprintf( OUT_Z_CAP(maxLenInNumWideCharacters) wchar_t *pDest, int maxLenInNumWideCharacters, PRINTF_FORMAT_STRING const wchar_t *pFormat, ... )
  813. {
  814. Assert( maxLenInNumWideCharacters >= 0 );
  815. va_list marker;
  816. va_start( marker, pFormat );
  817. #ifdef _WIN32
  818. int len = _vsnwprintf( pDest, maxLenInNumWideCharacters, pFormat, marker );
  819. #elif POSIX
  820. int len = vswprintf( pDest, maxLenInNumWideCharacters, pFormat, marker );
  821. #else
  822. #error "define vsnwprintf type."
  823. #endif
  824. va_end( marker );
  825. // Len < 0 represents an overflow
  826. // Len == maxLen represents exactly fitting with no NULL termination
  827. // Len can be > maxLen on Linux systems when the output was truncated
  828. if ( ( len < 0 ) ||
  829. ( maxLenInNumWideCharacters > 0 && len >= maxLenInNumWideCharacters ) )
  830. {
  831. len = maxLenInNumWideCharacters - 1;
  832. pDest[maxLenInNumWideCharacters-1] = 0;
  833. }
  834. return len;
  835. }
  836. int V_vsnwprintf( OUT_Z_CAP(maxLenInChars) wchar_t *pDest, int maxLenInChars, PRINTF_FORMAT_STRING const wchar_t *pFormat, va_list params )
  837. {
  838. Assert( maxLenInChars >= 0 );
  839. AssertValidWritePtr( pDest, maxLenInChars );
  840. AssertValidReadPtr( pFormat );
  841. #ifdef _WIN32
  842. int len = _vsnwprintf( pDest, maxLenInChars, pFormat, params );
  843. #elif POSIX
  844. int len = vswprintf( pDest, maxLenInChars, pFormat, params );
  845. #else
  846. #error "define vsnwprintf type."
  847. #endif
  848. // Len < 0 represents an overflow
  849. if ( ( len < 0 ) ||
  850. ( maxLenInChars > 0 && len >= maxLenInChars ) )
  851. {
  852. len = maxLenInChars - 1;
  853. pDest[maxLenInChars-1] = 0;
  854. }
  855. return len;
  856. }
  857. int V_snprintf( char *pDest, int maxLen, char const *pFormat, ... )
  858. {
  859. Assert( maxLen > 0 );
  860. va_list marker;
  861. va_start( marker, pFormat );
  862. #ifdef _WIN32
  863. int len = _vsnprintf( pDest, maxLen, pFormat, marker );
  864. #elif POSIX
  865. int len = vsnprintf( pDest, maxLen, pFormat, marker );
  866. #else
  867. #error "define vsnprintf type."
  868. #endif
  869. va_end( marker );
  870. // Len < 0 represents an overflow
  871. // Len == maxLen represents exactly fitting with no NULL termination
  872. if ( ( len < 0 ) ||
  873. ( maxLen > 0 && len >= maxLen ) )
  874. {
  875. len = maxLen - 1;
  876. pDest[maxLen-1] = 0;
  877. }
  878. return len;
  879. }
  880. int V_vsnprintf( char *pDest, int maxLen, const char *pFormat, va_list params )
  881. {
  882. Assert( maxLen > 0 );
  883. int len = _vsnprintf( pDest, maxLen, pFormat, params );
  884. if ( ( len < 0 ) ||
  885. ( maxLen > 0 && len >= maxLen ) )
  886. {
  887. len = maxLen - 1;
  888. pDest[maxLen-1] = 0;
  889. }
  890. return len;
  891. }
  892. int V_vsnprintfRet( char *pDest, int maxLen, const char *pFormat, va_list params, bool *pbTruncated )
  893. {
  894. Assert( maxLen > 0 );
  895. int len = _vsnprintf( pDest, maxLen, pFormat, params );
  896. bool bTruncated = ( len < 0 ) || ( len >= maxLen );
  897. if ( pbTruncated )
  898. {
  899. *pbTruncated = bTruncated;
  900. }
  901. if( bTruncated && maxLen > 0 )
  902. {
  903. len = maxLen - 1;
  904. pDest[maxLen-1] = 0;
  905. }
  906. return len;
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Purpose: If COPY_ALL_CHARACTERS == max_chars_to_copy then we try to add the whole pSrc to the end of pDest, otherwise
  910. // we copy only as many characters as are specified in max_chars_to_copy (or the # of characters in pSrc if thats's less).
  911. // Input : *pDest - destination buffer
  912. // *pSrc - string to append
  913. // destBufferSize - sizeof the buffer pointed to by pDest
  914. // max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy
  915. // Output : char * the copied buffer
  916. //-----------------------------------------------------------------------------
  917. char *V_strncat( char *pDest, const char *pSrc, size_t maxLenInBytes, int nMaxCharsToCopy )
  918. {
  919. DEBUG_LINK_CHECK;
  920. size_t charstocopy = (size_t)0;
  921. Assert( nMaxCharsToCopy >= 0 || nMaxCharsToCopy == COPY_ALL_CHARACTERS );
  922. size_t len = V_strlen(pDest);
  923. size_t srclen = V_strlen( pSrc );
  924. if ( nMaxCharsToCopy == COPY_ALL_CHARACTERS )
  925. {
  926. charstocopy = srclen;
  927. }
  928. else
  929. {
  930. charstocopy = MIN( nMaxCharsToCopy, (int)srclen );
  931. }
  932. if ( len + charstocopy >= maxLenInBytes )
  933. {
  934. charstocopy = maxLenInBytes - len - 1;
  935. }
  936. // charstocopy can end up negative if you fill a buffer and then pass in a smaller
  937. // buffer size. Yes, this actually happens.
  938. // Cast to ptrdiff_t is necessary in order to check for negative (size_t is unsigned)
  939. if ( charstocopy <= 0 )
  940. {
  941. return pDest;
  942. }
  943. ANALYZE_SUPPRESS( 6059 ); // warning C6059: : Incorrect length parameter in call to 'strncat'. Pass the number of remaining characters, not the buffer size of 'argument 1'
  944. char *pOut = strncat( pDest, pSrc, charstocopy );
  945. pOut[maxLenInBytes-1] = 0;
  946. return pOut;
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Purpose: If COPY_ALL_CHARACTERS == max_chars_to_copy then we try to add the whole pSrc to the end of pDest, otherwise
  950. // we copy only as many characters as are specified in max_chars_to_copy (or the # of characters in pSrc if thats's less).
  951. // Input : *pDest - destination buffer
  952. // *pSrc - string to append
  953. // maxLenInCharacters - sizeof the buffer in characters pointed to by pDest
  954. // max_chars_to_copy - COPY_ALL_CHARACTERS in pSrc or max # to copy
  955. // Output : char * the copied buffer
  956. //-----------------------------------------------------------------------------
  957. wchar_t *V_wcsncat( INOUT_Z_BYTECAP(maxLenInBytes) wchar_t *pDest, const wchar_t *pSrc, int maxLenInBytes, int nMaxCharsToCopy )
  958. {
  959. DEBUG_LINK_CHECK;
  960. size_t charstocopy = (size_t)0;
  961. Assert( maxLenInBytes >= 0 );
  962. int maxLenInCharacters = maxLenInBytes / sizeof( wchar_t );
  963. size_t len = wcslen(pDest);
  964. size_t srclen = wcslen( pSrc );
  965. if ( nMaxCharsToCopy <= COPY_ALL_CHARACTERS )
  966. {
  967. charstocopy = srclen;
  968. }
  969. else
  970. {
  971. charstocopy = (size_t)MIN( nMaxCharsToCopy, (int)srclen );
  972. }
  973. if ( len + charstocopy >= (size_t)maxLenInCharacters )
  974. {
  975. charstocopy = maxLenInCharacters - len - 1;
  976. }
  977. if ( !charstocopy )
  978. {
  979. return pDest;
  980. }
  981. wchar_t *pOut = wcsncat( pDest, pSrc, charstocopy );
  982. pOut[maxLenInCharacters-1] = 0;
  983. return pOut;
  984. }
  985. //-----------------------------------------------------------------------------
  986. // Purpose: Converts value into x.xx MB/ x.xx KB, x.xx bytes format, including commas
  987. // Input : value -
  988. // 2 -
  989. // false -
  990. // Output : char
  991. //-----------------------------------------------------------------------------
  992. #define NUM_PRETIFYMEM_BUFFERS 8
  993. char *V_pretifymem( float value, int digitsafterdecimal /*= 2*/, bool usebinaryonek /*= false*/ )
  994. {
  995. static char output[ NUM_PRETIFYMEM_BUFFERS ][ 32 ];
  996. static int current;
  997. float onekb = usebinaryonek ? 1024.0f : 1000.0f;
  998. float onemb = onekb * onekb;
  999. char *out = output[ current ];
  1000. current = ( current + 1 ) & ( NUM_PRETIFYMEM_BUFFERS -1 );
  1001. char suffix[ 8 ];
  1002. // First figure out which bin to use
  1003. if ( value > onemb )
  1004. {
  1005. value /= onemb;
  1006. V_snprintf( suffix, sizeof( suffix ), " MB" );
  1007. }
  1008. else if ( value > onekb )
  1009. {
  1010. value /= onekb;
  1011. V_snprintf( suffix, sizeof( suffix ), " KB" );
  1012. }
  1013. else
  1014. {
  1015. V_snprintf( suffix, sizeof( suffix ), " bytes" );
  1016. }
  1017. char val[ 32 ];
  1018. // Clamp to >= 0
  1019. digitsafterdecimal = MAX( digitsafterdecimal, 0 );
  1020. // If it's basically integral, don't do any decimals
  1021. if ( FloatMakePositive( value - (int)value ) < 0.00001 )
  1022. {
  1023. V_snprintf( val, sizeof( val ), "%i%s", (int)value, suffix );
  1024. }
  1025. else
  1026. {
  1027. char fmt[ 32 ];
  1028. // Otherwise, create a format string for the decimals
  1029. V_snprintf( fmt, sizeof( fmt ), "%%.%if%s", digitsafterdecimal, suffix );
  1030. V_snprintf( val, sizeof( val ), fmt, value );
  1031. }
  1032. // Copy from in to out
  1033. char *i = val;
  1034. char *o = out;
  1035. // Search for decimal or if it was integral, find the space after the raw number
  1036. char *dot = strstr( i, "." );
  1037. if ( !dot )
  1038. {
  1039. dot = strstr( i, " " );
  1040. }
  1041. // Compute position of dot
  1042. int pos = dot - i;
  1043. // Don't put a comma if it's <= 3 long
  1044. pos -= 3;
  1045. while ( *i )
  1046. {
  1047. // If pos is still valid then insert a comma every third digit, except if we would be
  1048. // putting one in the first spot
  1049. if ( pos >= 0 && !( pos % 3 ) )
  1050. {
  1051. // Never in first spot
  1052. if ( o != out )
  1053. {
  1054. *o++ = ',';
  1055. }
  1056. }
  1057. // Count down comma position
  1058. pos--;
  1059. // Copy rest of data as normal
  1060. *o++ = *i++;
  1061. }
  1062. // Terminate
  1063. *o = 0;
  1064. return out;
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose: Returns a string representation of an integer with commas
  1068. // separating the 1000s (ie, 37,426,421)
  1069. // Input : value - Value to convert
  1070. // Output : Pointer to a static buffer containing the output
  1071. //-----------------------------------------------------------------------------
  1072. #define NUM_PRETIFYNUM_BUFFERS 8 // Must be a power of two
  1073. char *V_pretifynum( int64 inputValue )
  1074. {
  1075. static char output[ NUM_PRETIFYMEM_BUFFERS ][ 32 ];
  1076. static int current;
  1077. // Point to the output buffer.
  1078. char * const out = output[ current ];
  1079. // Track the output buffer end for easy calculation of bytes-remaining.
  1080. const char* const outEnd = out + sizeof( output[ current ] );
  1081. // Point to the current output location in the output buffer.
  1082. char *pchRender = out;
  1083. // Move to the next output pointer.
  1084. current = ( current + 1 ) & ( NUM_PRETIFYMEM_BUFFERS -1 );
  1085. *out = 0;
  1086. // In order to handle the most-negative int64 we need to negate it
  1087. // into a uint64.
  1088. uint64 value;
  1089. // Render the leading minus sign, if necessary
  1090. if ( inputValue < 0 )
  1091. {
  1092. V_snprintf( pchRender, 32, "-" );
  1093. value = (uint64)-inputValue;
  1094. // Advance our output pointer.
  1095. pchRender += V_strlen( pchRender );
  1096. }
  1097. else
  1098. {
  1099. value = (uint64)inputValue;
  1100. }
  1101. // Now let's find out how big our number is. The largest number we can fit
  1102. // into 63 bits is about 9.2e18. So, there could potentially be six
  1103. // three-digit groups.
  1104. // We need the initial value of 'divisor' to be big enough to divide our
  1105. // number down to 1-999 range.
  1106. uint64 divisor = 1;
  1107. // Loop more than six times to avoid integer overflow.
  1108. for ( int i = 0; i < 6; ++i )
  1109. {
  1110. // If our divisor is already big enough then stop.
  1111. if ( value < divisor * 1000 )
  1112. break;
  1113. divisor *= 1000;
  1114. }
  1115. // Print the leading batch of one to three digits.
  1116. int toPrint = value / divisor;
  1117. V_snprintf( pchRender, outEnd - pchRender, "%d", toPrint );
  1118. for (;;)
  1119. {
  1120. // Advance our output pointer.
  1121. pchRender += V_strlen( pchRender );
  1122. // Adjust our value to be printed and our divisor.
  1123. value -= toPrint * divisor;
  1124. divisor /= 1000;
  1125. if ( !divisor )
  1126. break;
  1127. // The remaining blocks of digits always include a comma and three digits.
  1128. toPrint = value / divisor;
  1129. V_snprintf( pchRender, outEnd - pchRender, ",%03d", toPrint );
  1130. }
  1131. return out;
  1132. }
  1133. //-----------------------------------------------------------------------------
  1134. // Purpose: Converts a UTF8 string into a unicode string
  1135. //-----------------------------------------------------------------------------
  1136. int _V_UTF8ToUnicode( const char *pUTF8, wchar_t *pwchDest, int cubDestSizeInBytes )
  1137. {
  1138. Assert( cubDestSizeInBytes >= sizeof( *pwchDest ) );
  1139. pwchDest[0] = 0;
  1140. if ( !pUTF8 )
  1141. return 0;
  1142. #ifdef _WIN32
  1143. int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pwchDest, cubDestSizeInBytes / sizeof(wchar_t) );
  1144. #elif POSIX
  1145. int cchResult = mbstowcs( pwchDest, pUTF8, cubDestSizeInBytes / sizeof(wchar_t) );
  1146. #endif
  1147. pwchDest[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0;
  1148. return cchResult;
  1149. }
  1150. //-----------------------------------------------------------------------------
  1151. // Purpose: Converts a unicode string into a UTF8 (standard) string
  1152. //-----------------------------------------------------------------------------
  1153. int _V_UnicodeToUTF8( const wchar_t *pUnicode, char *pUTF8, int cubDestSizeInBytes )
  1154. {
  1155. if ( cubDestSizeInBytes > 0 )
  1156. {
  1157. pUTF8[0] = 0;
  1158. }
  1159. #ifdef _WIN32
  1160. int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUnicode, -1, pUTF8, cubDestSizeInBytes, NULL, NULL );
  1161. #elif POSIX
  1162. int cchResult = 0;
  1163. if ( pUnicode && pUTF8 )
  1164. cchResult = wcstombs( pUTF8, pUnicode, cubDestSizeInBytes );
  1165. #endif
  1166. if ( cubDestSizeInBytes > 0 )
  1167. {
  1168. pUTF8[cubDestSizeInBytes - 1] = 0;
  1169. }
  1170. return cchResult;
  1171. }
  1172. //-----------------------------------------------------------------------------
  1173. // Purpose: Converts a ucs2 string to a unicode (wchar_t) one, no-op on win32
  1174. //-----------------------------------------------------------------------------
  1175. int _V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInBytes )
  1176. {
  1177. Assert( cubDestSizeInBytes >= sizeof( *pUnicode ) );
  1178. pUnicode[0] = 0;
  1179. #ifdef _WIN32
  1180. int cchResult = V_wcslen( pUCS2 );
  1181. V_memcpy( pUnicode, pUCS2, cubDestSizeInBytes );
  1182. #else
  1183. iconv_t conv_t = iconv_open( "UCS-4LE", "UCS-2LE" );
  1184. int cchResult = -1;
  1185. size_t nLenUnicde = cubDestSizeInBytes;
  1186. size_t nMaxUTF8 = cubDestSizeInBytes;
  1187. char *pIn = (char *)pUCS2;
  1188. char *pOut = (char *)pUnicode;
  1189. if ( conv_t > 0 )
  1190. {
  1191. cchResult = 0;
  1192. cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
  1193. iconv_close( conv_t );
  1194. if ( (int)cchResult < 0 )
  1195. cchResult = 0;
  1196. else
  1197. cchResult = nMaxUTF8;
  1198. }
  1199. #endif
  1200. pUnicode[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0;
  1201. return cchResult;
  1202. }
  1203. #ifdef _PREFAST_
  1204. #pragma warning( pop ) // Restore the /analyze warnings
  1205. #endif
  1206. //-----------------------------------------------------------------------------
  1207. // Purpose: Converts a wchar_t string into a UCS2 string -noop on windows
  1208. //-----------------------------------------------------------------------------
  1209. int _V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, int cubDestSizeInBytes )
  1210. {
  1211. // TODO: MACMERGE: Figure out how to convert from 2-byte Win32 wchars to platform wchar_t type that can be 4 bytes
  1212. #if defined( _WIN32 ) || defined( _PS3 )
  1213. // Figure out which buffer is smaller and convert from bytes to character
  1214. // counts.
  1215. int cchResult = MIN(cubSrcInBytes/sizeof(wchar_t), cubDestSizeInBytes/sizeof(wchar_t) );
  1216. wchar_t *pDest = (wchar_t*)pUCS2;
  1217. wcsncpy( pDest, pUnicode, cchResult );
  1218. // Make sure we NULL-terminate.
  1219. pDest[ cchResult - 1 ] = 0;
  1220. #elif defined (POSIX)
  1221. iconv_t conv_t = iconv_open( "UCS-2LE", "UTF-32LE" );
  1222. size_t cchResult = -1;
  1223. size_t nLenUnicde = cubSrcInBytes;
  1224. size_t nMaxUCS2 = cubDestSizeInBytes;
  1225. char *pIn = (char*)pUnicode;
  1226. char *pOut = pUCS2;
  1227. if ( conv_t > 0 )
  1228. {
  1229. cchResult = 0;
  1230. cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUCS2 );
  1231. iconv_close( conv_t );
  1232. if ( (int)cchResult < 0 )
  1233. cchResult = 0;
  1234. else
  1235. cchResult = cubSrcInBytes / sizeof( wchar_t );
  1236. }
  1237. #else
  1238. #error Must be implemented for this platform
  1239. #endif
  1240. return cchResult;
  1241. }
  1242. //-----------------------------------------------------------------------------
  1243. // Purpose: Converts a ucs-2 (windows wchar_t) string into a UTF8 (standard) string
  1244. //-----------------------------------------------------------------------------
  1245. int _V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes )
  1246. {
  1247. pUTF8[0] = 0;
  1248. #ifdef _WIN32
  1249. // under win32 wchar_t == ucs2, sigh
  1250. int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUCS2, -1, pUTF8, cubDestSizeInBytes, NULL, NULL );
  1251. #elif defined(POSIX)
  1252. iconv_t conv_t = iconv_open( "UTF-8", "UCS-2LE" );
  1253. size_t cchResult = -1;
  1254. size_t nLenUnicde = cubDestSizeInBytes;
  1255. size_t nMaxUTF8 = cubDestSizeInBytes;
  1256. char *pIn = (char *)pUCS2;
  1257. char *pOut = (char *)pUTF8;
  1258. if ( conv_t > 0 )
  1259. {
  1260. cchResult = 0;
  1261. cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
  1262. iconv_close( conv_t );
  1263. if ( (int)cchResult < 0 )
  1264. cchResult = 0;
  1265. else
  1266. cchResult = nMaxUTF8;
  1267. }
  1268. #endif
  1269. pUTF8[cubDestSizeInBytes - 1] = 0;
  1270. return cchResult;
  1271. }
  1272. //-----------------------------------------------------------------------------
  1273. // Purpose: Converts a UTF8 to ucs-2 (windows wchar_t)
  1274. //-----------------------------------------------------------------------------
  1275. int _V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDestSizeInBytes )
  1276. {
  1277. Assert( cubDestSizeInBytes >= sizeof(pUCS2[0]) );
  1278. pUCS2[0] = 0;
  1279. #ifdef _WIN32
  1280. // under win32 wchar_t == ucs2, sigh
  1281. int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pUCS2, cubDestSizeInBytes / sizeof(wchar_t) );
  1282. #elif defined( _PS3 ) // bugbug JLB
  1283. int cchResult = 0;
  1284. Assert( 0 );
  1285. #elif defined(POSIX)
  1286. iconv_t conv_t = iconv_open( "UCS-2LE", "UTF-8" );
  1287. size_t cchResult = -1;
  1288. size_t nLenUnicde = cubSrcInBytes;
  1289. size_t nMaxUTF8 = cubDestSizeInBytes;
  1290. char *pIn = (char *)pUTF8;
  1291. char *pOut = (char *)pUCS2;
  1292. if ( conv_t > 0 )
  1293. {
  1294. cchResult = 0;
  1295. cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
  1296. iconv_close( conv_t );
  1297. if ( (int)cchResult < 0 )
  1298. cchResult = 0;
  1299. else
  1300. cchResult = cubSrcInBytes;
  1301. }
  1302. #endif
  1303. pUCS2[ (cubDestSizeInBytes/sizeof(ucs2)) - 1] = 0;
  1304. return cchResult;
  1305. }
  1306. //-----------------------------------------------------------------------------
  1307. // Purpose: Returns the 4 bit nibble for a hex character
  1308. // Input : c -
  1309. // Output : unsigned char
  1310. //-----------------------------------------------------------------------------
  1311. static unsigned char V_nibble( char c )
  1312. {
  1313. if ( ( c >= '0' ) &&
  1314. ( c <= '9' ) )
  1315. {
  1316. return (unsigned char)(c - '0');
  1317. }
  1318. if ( ( c >= 'A' ) &&
  1319. ( c <= 'F' ) )
  1320. {
  1321. return (unsigned char)(c - 'A' + 0x0a);
  1322. }
  1323. if ( ( c >= 'a' ) &&
  1324. ( c <= 'f' ) )
  1325. {
  1326. return (unsigned char)(c - 'a' + 0x0a);
  1327. }
  1328. return '0';
  1329. }
  1330. //-----------------------------------------------------------------------------
  1331. // Purpose:
  1332. // Input : *in -
  1333. // numchars -
  1334. // *out -
  1335. // maxoutputbytes -
  1336. //-----------------------------------------------------------------------------
  1337. void V_hextobinary( const char *in, int numchars, byte *out, int maxoutputbytes )
  1338. {
  1339. int len = V_strlen( in );
  1340. numchars = MIN( len, numchars );
  1341. // Make sure it's even
  1342. numchars = ( numchars ) & ~0x1;
  1343. // Must be an even # of input characters (two chars per output byte)
  1344. Assert( numchars >= 2 );
  1345. memset( out, 0x00, maxoutputbytes );
  1346. byte *p;
  1347. int i;
  1348. p = out;
  1349. for ( i = 0;
  1350. ( i < numchars ) && ( ( p - out ) < maxoutputbytes );
  1351. i+=2, p++ )
  1352. {
  1353. *p = ( V_nibble( in[i] ) << 4 ) | V_nibble( in[i+1] );
  1354. }
  1355. }
  1356. //-----------------------------------------------------------------------------
  1357. // Purpose:
  1358. // Input : *in -
  1359. // inputbytes -
  1360. // *out -
  1361. // outsize -
  1362. //-----------------------------------------------------------------------------
  1363. void V_binarytohex( const byte *in, int inputbytes, char *out, int outsize )
  1364. {
  1365. Assert( outsize >= 1 );
  1366. char doublet[10];
  1367. int i;
  1368. out[0]=0;
  1369. for ( i = 0; i < inputbytes; i++ )
  1370. {
  1371. unsigned char c = in[i];
  1372. V_snprintf( doublet, sizeof( doublet ), "%02x", c );
  1373. V_strncat( out, doublet, outsize, COPY_ALL_CHARACTERS );
  1374. }
  1375. }
  1376. #define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
  1377. //-----------------------------------------------------------------------------
  1378. // Purpose: Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator)
  1379. // Input : *in -
  1380. // *out -
  1381. // maxlen -
  1382. //-----------------------------------------------------------------------------
  1383. void V_FileBase( const char *in, char *out, int maxlen )
  1384. {
  1385. Assert( maxlen >= 1 );
  1386. Assert( in );
  1387. Assert( out );
  1388. if ( !in || !in[ 0 ] )
  1389. {
  1390. *out = 0;
  1391. return;
  1392. }
  1393. int len, start, end;
  1394. len = V_strlen( in );
  1395. // scan backward for '.'
  1396. end = len - 1;
  1397. while ( end&& in[end] != '.' && !PATHSEPARATOR( in[end] ) )
  1398. {
  1399. end--;
  1400. }
  1401. if ( in[end] != '.' ) // no '.', copy to end
  1402. {
  1403. end = len-1;
  1404. }
  1405. else
  1406. {
  1407. end--; // Found ',', copy to left of '.'
  1408. }
  1409. // Scan backward for '/'
  1410. start = len-1;
  1411. while ( start >= 0 && !PATHSEPARATOR( in[start] ) )
  1412. {
  1413. start--;
  1414. }
  1415. if ( start < 0 || !PATHSEPARATOR( in[start] ) )
  1416. {
  1417. start = 0;
  1418. }
  1419. else
  1420. {
  1421. start++;
  1422. }
  1423. // Length of new sting
  1424. len = end - start + 1;
  1425. int maxcopy = MIN( len + 1, maxlen );
  1426. // Copy partial string
  1427. V_strncpy( out, &in[start], maxcopy );
  1428. }
  1429. //-----------------------------------------------------------------------------
  1430. // Purpose:
  1431. // Input : *ppath -
  1432. //-----------------------------------------------------------------------------
  1433. void V_StripTrailingSlash( char *ppath )
  1434. {
  1435. Assert( ppath );
  1436. int len = V_strlen( ppath );
  1437. if ( len > 0 )
  1438. {
  1439. if ( PATHSEPARATOR( ppath[ len - 1 ] ) )
  1440. {
  1441. ppath[ len - 1 ] = 0;
  1442. }
  1443. }
  1444. }
  1445. //-----------------------------------------------------------------------------
  1446. // Purpose:
  1447. // Input : *ppline -
  1448. //-----------------------------------------------------------------------------
  1449. void V_StripTrailingWhitespace( char *ppline )
  1450. {
  1451. Assert( ppline );
  1452. int len = V_strlen( ppline );
  1453. while ( len > 0 )
  1454. {
  1455. if ( !V_isspace( ppline[ len - 1 ] ) )
  1456. break;
  1457. ppline[ len - 1 ] = 0;
  1458. len--;
  1459. }
  1460. }
  1461. //-----------------------------------------------------------------------------
  1462. // Purpose:
  1463. // Input : *ppline -
  1464. //-----------------------------------------------------------------------------
  1465. void V_StripLeadingWhitespace( char *ppline )
  1466. {
  1467. Assert( ppline );
  1468. // Skip past initial whitespace
  1469. int skip = 0;
  1470. while( V_isspace( ppline[ skip ] ) )
  1471. skip++;
  1472. // Shuffle the rest of the string back (including the NULL-terminator)
  1473. if ( skip )
  1474. {
  1475. while( ( ppline[0] = ppline[skip] ) != 0 )
  1476. ppline++;
  1477. }
  1478. }
  1479. //-----------------------------------------------------------------------------
  1480. // Purpose:
  1481. // Input : *ppline -
  1482. //-----------------------------------------------------------------------------
  1483. void V_StripSurroundingQuotes( char *ppline )
  1484. {
  1485. Assert( ppline );
  1486. int len = V_strlen( ppline ) - 2;
  1487. if ( ( ppline[0] == '"' ) && ( len >= 0 ) && ( ppline[len+1] == '"' ) )
  1488. {
  1489. for ( int i = 0; i < len; i++ )
  1490. ppline[i] = ppline[i+1];
  1491. ppline[len] = 0;
  1492. }
  1493. }
  1494. //-----------------------------------------------------------------------------
  1495. // Purpose:
  1496. // Input : *in -
  1497. // *out -
  1498. // outSize -
  1499. //-----------------------------------------------------------------------------
  1500. void V_StripExtension( const char *in, char *out, int outSize )
  1501. {
  1502. // Find the last dot. If it's followed by a dot or a slash, then it's part of a
  1503. // directory specifier like ../../somedir/./blah.
  1504. // scan backward for '.'
  1505. int end = V_strlen( in ) - 1;
  1506. while ( end > 0 && in[end] != '.' && !PATHSEPARATOR( in[end] ) )
  1507. {
  1508. --end;
  1509. }
  1510. if (end > 0 && !PATHSEPARATOR( in[end] ) && end < outSize)
  1511. {
  1512. int nChars = MIN( end, outSize-1 );
  1513. if ( out != in )
  1514. {
  1515. memcpy( out, in, nChars );
  1516. }
  1517. out[nChars] = 0;
  1518. }
  1519. else
  1520. {
  1521. // nothing found
  1522. if ( out != in )
  1523. {
  1524. V_strncpy( out, in, outSize );
  1525. }
  1526. }
  1527. }
  1528. //-----------------------------------------------------------------------------
  1529. // Purpose:
  1530. // Input : *path -
  1531. // *extension -
  1532. // pathStringLength -
  1533. //-----------------------------------------------------------------------------
  1534. void V_DefaultExtension( char *path, const char *extension, int pathStringLength )
  1535. {
  1536. Assert( path );
  1537. Assert( pathStringLength >= 1 );
  1538. Assert( extension );
  1539. char *src;
  1540. // if path doesn't have a .EXT, append extension
  1541. // (extension should include the .)
  1542. src = path + V_strlen(path) - 1;
  1543. while ( !PATHSEPARATOR( *src ) && ( src > path ) )
  1544. {
  1545. if (*src == '.')
  1546. {
  1547. // it has an extension
  1548. return;
  1549. }
  1550. src--;
  1551. }
  1552. // Concatenate the desired extension
  1553. char pTemp[MAX_PATH];
  1554. if ( extension[0] != '.' )
  1555. {
  1556. pTemp[0] = '.';
  1557. V_strncpy( &pTemp[1], extension, sizeof(pTemp) - 1 );
  1558. extension = pTemp;
  1559. }
  1560. V_strncat( path, extension, pathStringLength, COPY_ALL_CHARACTERS );
  1561. }
  1562. //-----------------------------------------------------------------------------
  1563. // Purpose: Force extension...
  1564. // Input : *path -
  1565. // *extension -
  1566. // pathStringLength -
  1567. //-----------------------------------------------------------------------------
  1568. void V_SetExtension( char *path, const char *extension, int pathStringLength )
  1569. {
  1570. V_StripExtension( path, path, pathStringLength );
  1571. // This fails if the filename has multiple extensions (i.e. "filename.360.vtex_c").
  1572. //V_DefaultExtension( path, extension, pathStringLength );
  1573. // Concatenate the desired extension
  1574. char pTemp[MAX_PATH];
  1575. if ( extension[0] != '.' )
  1576. {
  1577. pTemp[0] = '.';
  1578. V_strncpy( &pTemp[1], extension, sizeof(pTemp) - 1 );
  1579. extension = pTemp;
  1580. }
  1581. V_strncat( path, extension, pathStringLength, COPY_ALL_CHARACTERS );
  1582. }
  1583. //-----------------------------------------------------------------------------
  1584. // Purpose: Remove final filename from string
  1585. // Input : *path -
  1586. // Output : void V_StripFilename
  1587. //-----------------------------------------------------------------------------
  1588. void V_StripFilename (char *path)
  1589. {
  1590. int length;
  1591. length = V_strlen( path )-1;
  1592. if ( length <= 0 )
  1593. return;
  1594. while ( length > 0 &&
  1595. !PATHSEPARATOR( path[length] ) )
  1596. {
  1597. length--;
  1598. }
  1599. path[ length ] = 0;
  1600. }
  1601. #ifdef _WIN32
  1602. #define CORRECT_PATH_SEPARATOR '\\'
  1603. #define INCORRECT_PATH_SEPARATOR '/'
  1604. #elif POSIX
  1605. #define CORRECT_PATH_SEPARATOR '/'
  1606. #define INCORRECT_PATH_SEPARATOR '\\'
  1607. #endif
  1608. //-----------------------------------------------------------------------------
  1609. // Purpose: Changes all '/' or '\' characters into separator
  1610. // Input : *pname -
  1611. // separator -
  1612. //-----------------------------------------------------------------------------
  1613. void V_FixSlashes( char *pname, char separator /* = CORRECT_PATH_SEPARATOR */ )
  1614. {
  1615. while ( *pname )
  1616. {
  1617. if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR )
  1618. {
  1619. *pname = separator;
  1620. }
  1621. pname++;
  1622. }
  1623. }
  1624. //-----------------------------------------------------------------------------
  1625. // Purpose: This function fixes cases of filenames like materials\\blah.vmt or somepath\otherpath\\ and removes the extra double slash.
  1626. //-----------------------------------------------------------------------------
  1627. void V_FixDoubleSlashes( char *pStr )
  1628. {
  1629. int len = V_strlen( pStr );
  1630. for ( int i=1; i < len-1; i++ )
  1631. {
  1632. if ( (pStr[i] == '/' || pStr[i] == '\\') && (pStr[i+1] == '/' || pStr[i+1] == '\\') )
  1633. {
  1634. // This means there's a double slash somewhere past the start of the filename. That
  1635. // can happen in Hammer if they use a material in the root directory. You'll get a filename
  1636. // that looks like 'materials\\blah.vmt'
  1637. V_memmove( &pStr[i], &pStr[i+1], len - i );
  1638. --len;
  1639. }
  1640. }
  1641. }
  1642. //-----------------------------------------------------------------------------
  1643. // Check if 2 paths are the same, works if slashes are different.
  1644. //-----------------------------------------------------------------------------
  1645. bool V_PathsMatch( const char *pPath1, const char *pPath2)
  1646. {
  1647. char pPath1Fixed[MAX_PATH];
  1648. V_strcpy_safe( pPath1Fixed, pPath1 );
  1649. char pPath2Fixed[MAX_PATH];
  1650. V_strcpy_safe( pPath2Fixed, pPath2 );
  1651. V_FixSlashes( pPath1Fixed, '/' );
  1652. V_FixSlashes( pPath2Fixed, '/' );
  1653. return ( V_stricmp( pPath1Fixed, pPath2Fixed ) == 0 );
  1654. }
  1655. //-----------------------------------------------------------------------------
  1656. // Purpose: Strip off the last directory from dirName
  1657. // Input : *dirName -
  1658. // maxlen -
  1659. // Output : Returns true on success, false on failure.
  1660. //-----------------------------------------------------------------------------
  1661. bool V_StripLastDir( char *dirName, int maxlen )
  1662. {
  1663. if( dirName[0] == 0 ||
  1664. !V_stricmp( dirName, "./" ) ||
  1665. !V_stricmp( dirName, ".\\" ) )
  1666. return false;
  1667. int len = V_strlen( dirName );
  1668. Assert( len < maxlen );
  1669. // skip trailing slash
  1670. if ( PATHSEPARATOR( dirName[len-1] ) )
  1671. {
  1672. len--;
  1673. }
  1674. bool bHitColon = false;
  1675. while ( len > 0 )
  1676. {
  1677. if ( PATHSEPARATOR( dirName[len-1] ) )
  1678. {
  1679. dirName[len] = 0;
  1680. V_FixSlashes( dirName, CORRECT_PATH_SEPARATOR );
  1681. return true;
  1682. }
  1683. else if ( dirName[len-1] == ':' )
  1684. {
  1685. bHitColon = true;
  1686. }
  1687. len--;
  1688. }
  1689. // If we hit a drive letter, then we're done.
  1690. // Ex: If they passed in c:\, then V_StripLastDir should return "" and false.
  1691. if ( bHitColon )
  1692. {
  1693. dirName[0] = 0;
  1694. return false;
  1695. }
  1696. // Allow it to return an empty string and true. This can happen if something like "tf2/" is passed in.
  1697. // The correct behavior is to strip off the last directory ("tf2") and return true.
  1698. if ( len == 0 && !bHitColon )
  1699. {
  1700. V_snprintf( dirName, maxlen, ".%c", CORRECT_PATH_SEPARATOR );
  1701. return true;
  1702. }
  1703. return true;
  1704. }
  1705. //-----------------------------------------------------------------------------
  1706. // Purpose: Returns a pointer to the beginning of the unqualified file name
  1707. // (no path information)
  1708. // Input: in - file name (may be unqualified, relative or absolute path)
  1709. // Output: pointer to unqualified file name
  1710. //-----------------------------------------------------------------------------
  1711. const char * V_UnqualifiedFileName( const char * in )
  1712. {
  1713. if ( !in || !in[0] )
  1714. return in;
  1715. // back up until the character after the first path separator we find,
  1716. // or the beginning of the string
  1717. const char * out = in + strlen( in ) - 1;
  1718. while ( ( out > in ) && ( !PATHSEPARATOR( *( out-1 ) ) ) )
  1719. out--;
  1720. return out;
  1721. }
  1722. //-----------------------------------------------------------------------------
  1723. // Purpose: Composes a path and filename together, inserting a path separator
  1724. // if need be
  1725. // Input: path - path to use
  1726. // filename - filename to use
  1727. // dest - buffer to compose result in
  1728. // destSize - size of destination buffer
  1729. //-----------------------------------------------------------------------------
  1730. void V_ComposeFileName( const char *path, const char *filename, char *dest, int destSize )
  1731. {
  1732. V_strncpy( dest, path, destSize );
  1733. V_FixSlashes( dest );
  1734. V_AppendSlash( dest, destSize );
  1735. V_strncat( dest, filename, destSize, COPY_ALL_CHARACTERS );
  1736. V_FixSlashes( dest );
  1737. }
  1738. //-----------------------------------------------------------------------------
  1739. // Purpose:
  1740. // Input : *path -
  1741. // *dest -
  1742. // destSize -
  1743. // Output : void V_ExtractFilePath
  1744. //-----------------------------------------------------------------------------
  1745. bool V_ExtractFilePath (const char *path, char *dest, int destSize )
  1746. {
  1747. Assert( destSize >= 1 );
  1748. if ( destSize < 1 )
  1749. {
  1750. return false;
  1751. }
  1752. // Last char
  1753. int len = V_strlen(path);
  1754. const char *src = path + (len ? len-1 : 0);
  1755. // back up until a \ or the start
  1756. while ( src != path && !PATHSEPARATOR( *(src-1) ) )
  1757. {
  1758. src--;
  1759. }
  1760. int copysize = MIN( src - path, destSize - 1 );
  1761. memcpy( dest, path, copysize );
  1762. dest[copysize] = 0;
  1763. return copysize != 0 ? true : false;
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. // Purpose:
  1767. // Input : *path -
  1768. // *dest -
  1769. // destSize -
  1770. // Output : void V_ExtractFileExtension
  1771. //-----------------------------------------------------------------------------
  1772. void V_ExtractFileExtension( const char *path, char *dest, int destSize )
  1773. {
  1774. *dest = 0;
  1775. const char * extension = V_GetFileExtension( path );
  1776. if ( NULL != extension )
  1777. V_strncpy( dest, extension, destSize );
  1778. }
  1779. //-----------------------------------------------------------------------------
  1780. // Purpose: Returns a pointer to the file extension within a file name string
  1781. // Input: in - file name
  1782. // Output: pointer to beginning of extension (after the "."), or ""
  1783. // if there is no extension
  1784. //-----------------------------------------------------------------------------
  1785. const char *V_GetFileExtensionSafe( const char *path )
  1786. {
  1787. const char *pExt = V_GetFileExtension( path );
  1788. if ( pExt == NULL )
  1789. return "";
  1790. else
  1791. return pExt;
  1792. }
  1793. //-----------------------------------------------------------------------------
  1794. // Purpose: Returns a pointer to the file extension within a file name string
  1795. // Input: in - file name
  1796. // Output: pointer to beginning of extension (after the "."), or NULL
  1797. // if there is no extension
  1798. //-----------------------------------------------------------------------------
  1799. const char *V_GetFileExtension( const char *path )
  1800. {
  1801. int len = V_strlen( path );
  1802. if ( len <= 1 )
  1803. return NULL;
  1804. const char *src = path + len - 1;
  1805. //
  1806. // back up until a . or the start
  1807. //
  1808. while (src != path && *(src-1) != '.' )
  1809. src--;
  1810. // check to see if the '.' is part of a pathname
  1811. if (src == path || PATHSEPARATOR( *src ) )
  1812. {
  1813. return NULL; // no extension
  1814. }
  1815. return src;
  1816. }
  1817. bool V_RemoveDotSlashes( char *pFilename, char separator )
  1818. {
  1819. // Remove '//' or '\\'
  1820. char *pIn = pFilename;
  1821. char *pOut = pFilename;
  1822. // (But skip a leading separator, for leading \\'s in network paths)
  1823. if ( *pIn && PATHSEPARATOR( *pIn ) )
  1824. {
  1825. *pOut = *pIn;
  1826. ++pIn;
  1827. ++pOut;
  1828. }
  1829. bool bPrevPathSep = false;
  1830. while ( *pIn )
  1831. {
  1832. bool bIsPathSep = PATHSEPARATOR( *pIn );
  1833. if ( !bIsPathSep || !bPrevPathSep )
  1834. {
  1835. *pOut++ = *pIn;
  1836. }
  1837. bPrevPathSep = bIsPathSep;
  1838. ++pIn;
  1839. }
  1840. *pOut = 0;
  1841. // Get rid of "./"'s
  1842. pIn = pFilename;
  1843. pOut = pFilename;
  1844. while ( *pIn )
  1845. {
  1846. // The logic on the second line is preventing it from screwing up "../"
  1847. if ( pIn[0] == '.' && PATHSEPARATOR( pIn[1] ) &&
  1848. (pIn == pFilename || pIn[-1] != '.') )
  1849. {
  1850. pIn += 2;
  1851. }
  1852. else
  1853. {
  1854. *pOut = *pIn;
  1855. ++pIn;
  1856. ++pOut;
  1857. }
  1858. }
  1859. *pOut = 0;
  1860. // Get rid of a trailing "/." (needless).
  1861. int len = V_strlen( pFilename );
  1862. if ( len > 2 && pFilename[len-1] == '.' && PATHSEPARATOR( pFilename[len-2] ) )
  1863. {
  1864. pFilename[len-2] = 0;
  1865. }
  1866. // Each time we encounter a "..", back up until we've read the previous directory name,
  1867. // then get rid of it.
  1868. pIn = pFilename;
  1869. while ( *pIn )
  1870. {
  1871. if ( pIn[0] == '.' &&
  1872. pIn[1] == '.' &&
  1873. (pIn == pFilename || PATHSEPARATOR(pIn[-1])) && // Preceding character must be a slash.
  1874. (pIn[2] == 0 || PATHSEPARATOR(pIn[2])) ) // Following character must be a slash or the end of the string.
  1875. {
  1876. char *pEndOfDots = pIn + 2;
  1877. char *pStart = pIn - 2;
  1878. // Ok, now scan back for the path separator that starts the preceding directory.
  1879. while ( 1 )
  1880. {
  1881. if ( pStart < pFilename )
  1882. return false;
  1883. if ( PATHSEPARATOR( *pStart ) )
  1884. break;
  1885. --pStart;
  1886. }
  1887. // Now slide the string down to get rid of the previous directory and the ".."
  1888. memmove( pStart, pEndOfDots, strlen( pEndOfDots ) + 1 );
  1889. // Start over.
  1890. pIn = pFilename;
  1891. }
  1892. else
  1893. {
  1894. ++pIn;
  1895. }
  1896. }
  1897. V_FixSlashes( pFilename, separator );
  1898. return true;
  1899. }
  1900. void V_AppendSlash( char *pStr, int strSize, char separator )
  1901. {
  1902. int len = V_strlen( pStr );
  1903. if ( len > 0 && !PATHSEPARATOR(pStr[len-1]) )
  1904. {
  1905. if ( len+1 >= strSize )
  1906. Plat_FatalError( "V_AppendSlash: ran out of space on %s.", pStr );
  1907. pStr[len] = separator;
  1908. pStr[len+1] = 0;
  1909. }
  1910. }
  1911. #if defined(_MSC_VER) && _MSC_VER >= 1900
  1912. bool
  1913. #else
  1914. void
  1915. #endif
  1916. V_MakeAbsolutePath( char *pOut, int outLen, const char *pPath, const char *pStartingDir )
  1917. {
  1918. if ( V_IsAbsolutePath( pPath ) )
  1919. {
  1920. // pPath is not relative.. just copy it.
  1921. V_strncpy( pOut, pPath, outLen );
  1922. }
  1923. else
  1924. {
  1925. // Make sure the starting directory is absolute..
  1926. if ( pStartingDir && V_IsAbsolutePath( pStartingDir ) )
  1927. {
  1928. V_strncpy( pOut, pStartingDir, outLen );
  1929. }
  1930. else
  1931. {
  1932. #ifdef _PS3
  1933. {
  1934. V_strncpy( pOut, g_pPS3PathInfo->GameImagePath(), outLen );
  1935. }
  1936. #else
  1937. {
  1938. if ( !_getcwd( pOut, outLen ) )
  1939. Plat_FatalError( "V_MakeAbsolutePath: _getcwd failed." );
  1940. }
  1941. #endif
  1942. if ( pStartingDir )
  1943. {
  1944. V_AppendSlash( pOut, outLen );
  1945. V_strncat( pOut, pStartingDir, outLen, COPY_ALL_CHARACTERS );
  1946. }
  1947. }
  1948. // Concatenate the paths.
  1949. V_AppendSlash( pOut, outLen );
  1950. V_strncat( pOut, pPath, outLen, COPY_ALL_CHARACTERS );
  1951. }
  1952. V_FixSlashes(pOut);
  1953. bool bRet = true;
  1954. if (!V_RemoveDotSlashes(pOut))
  1955. {
  1956. V_strncpy(pOut, pPath, outLen);
  1957. V_FixSlashes(pOut);
  1958. bRet = false;
  1959. }
  1960. #if defined(_MSC_VER) && _MSC_VER >= 1900
  1961. return bRet;
  1962. #endif
  1963. }
  1964. //-----------------------------------------------------------------------------
  1965. // Makes a relative path
  1966. //-----------------------------------------------------------------------------
  1967. bool V_MakeRelativePath( const char *pFullPath, const char *pDirectory, char *pRelativePath, int nBufLen )
  1968. {
  1969. pRelativePath[0] = 0;
  1970. const char *pPath = pFullPath;
  1971. const char *pDir = pDirectory;
  1972. // Strip out common parts of the path
  1973. const char *pLastCommonPath = NULL;
  1974. const char *pLastCommonDir = NULL;
  1975. while ( *pPath && ( tolower( *pPath ) == tolower( *pDir ) ||
  1976. ( PATHSEPARATOR( *pPath ) && ( PATHSEPARATOR( *pDir ) || (*pDir == 0) ) ) ) )
  1977. {
  1978. if ( PATHSEPARATOR( *pPath ) )
  1979. {
  1980. pLastCommonPath = pPath + 1;
  1981. pLastCommonDir = pDir + 1;
  1982. }
  1983. if ( *pDir == 0 )
  1984. {
  1985. --pLastCommonDir;
  1986. break;
  1987. }
  1988. ++pDir; ++pPath;
  1989. }
  1990. // Nothing in common
  1991. if ( !pLastCommonPath )
  1992. return false;
  1993. // For each path separator remaining in the dir, need a ../
  1994. int nOutLen = 0;
  1995. bool bLastCharWasSeparator = true;
  1996. for ( ; *pLastCommonDir; ++pLastCommonDir )
  1997. {
  1998. if ( PATHSEPARATOR( *pLastCommonDir ) )
  1999. {
  2000. pRelativePath[nOutLen++] = '.';
  2001. pRelativePath[nOutLen++] = '.';
  2002. pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR;
  2003. bLastCharWasSeparator = true;
  2004. }
  2005. else
  2006. {
  2007. bLastCharWasSeparator = false;
  2008. }
  2009. }
  2010. // Deal with relative paths not specified with a trailing slash
  2011. if ( !bLastCharWasSeparator )
  2012. {
  2013. pRelativePath[nOutLen++] = '.';
  2014. pRelativePath[nOutLen++] = '.';
  2015. pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR;
  2016. }
  2017. // Copy the remaining part of the relative path over, fixing the path separators
  2018. for ( ; *pLastCommonPath; ++pLastCommonPath )
  2019. {
  2020. if ( PATHSEPARATOR( *pLastCommonPath ) )
  2021. {
  2022. pRelativePath[nOutLen++] = CORRECT_PATH_SEPARATOR;
  2023. }
  2024. else
  2025. {
  2026. pRelativePath[nOutLen++] = *pLastCommonPath;
  2027. }
  2028. // Check for overflow
  2029. if ( nOutLen == nBufLen - 1 )
  2030. break;
  2031. }
  2032. pRelativePath[nOutLen] = 0;
  2033. return true;
  2034. }
  2035. int LengthOfMatchingPaths( char const *pFilenamePath, char const *pMatchPath )
  2036. {
  2037. char const *pStartPath = pFilenamePath;
  2038. char const *pLastSeparator = pFilenamePath - 1;
  2039. for(;;)
  2040. {
  2041. char c0 = pFilenamePath[0];
  2042. char c1 = pMatchPath[0];
  2043. c0 = ( c0 == INCORRECT_PATH_SEPARATOR ) ? CORRECT_PATH_SEPARATOR : FastASCIIToUpper( c0 );
  2044. c1 = ( c1 == INCORRECT_PATH_SEPARATOR ) ? CORRECT_PATH_SEPARATOR : FastASCIIToUpper( c1 );
  2045. if ( strchr( CHARACTERS_WHICH_SEPARATE_DIRECTORY_COMPONENTS_IN_PATHNAMES, c0 ) &&
  2046. ( ( c0 == c1 ) || ( c1 == 0 ) ) )
  2047. {
  2048. pLastSeparator = pFilenamePath;
  2049. }
  2050. if ( c0 != c1 )
  2051. return 1 + ( pLastSeparator - pStartPath );
  2052. if ( c0 == 0 )
  2053. {
  2054. return pFilenamePath - pStartPath; // whole string matched
  2055. }
  2056. ++pFilenamePath;
  2057. ++pMatchPath;
  2058. }
  2059. }
  2060. //-----------------------------------------------------------------------------
  2061. // small helper function shared by lots of modules
  2062. //-----------------------------------------------------------------------------
  2063. bool V_IsAbsolutePath( const char *pStr )
  2064. {
  2065. if ( !( pStr[0] && pStr[1] ) )
  2066. return false;
  2067. #if defined( PLATFORM_WINDOWS )
  2068. bool bIsAbsolute = ( pStr[0] && pStr[1] == ':' ) ||
  2069. ( ( pStr[0] == '/' || pStr[0] == '\\' ) && ( pStr[1] == '/' || pStr[1] == '\\' ) );
  2070. #else
  2071. bool bIsAbsolute = ( pStr[0] && pStr[1] == ':' ) || pStr[0] == '/' || pStr[0] == '\\';
  2072. #endif
  2073. if ( IsX360() && !bIsAbsolute )
  2074. {
  2075. bIsAbsolute = ( V_stristr( pStr, ":" ) != NULL );
  2076. }
  2077. return bIsAbsolute;
  2078. }
  2079. //-----------------------------------------------------------------------------
  2080. // Fixes up a file name, replacing ' ' with '_'
  2081. //-----------------------------------------------------------------------------
  2082. void V_FixupPathSpaceToUnderscore( char *pPath )
  2083. {
  2084. for ( ; *pPath; pPath++ )
  2085. {
  2086. if( *pPath == ' ' )
  2087. {
  2088. *pPath = '_';
  2089. }
  2090. }
  2091. }
  2092. //-----------------------------------------------------------------------------
  2093. // Fixes up a file name, removing dot slashes, fixing slashes, converting to lowercase, etc.
  2094. //-----------------------------------------------------------------------------
  2095. void V_FixupPathName( char *pOut, int nOutLen, const char *pPath )
  2096. {
  2097. V_strncpy( pOut, pPath, nOutLen );
  2098. V_FixSlashes( pOut );
  2099. V_RemoveDotSlashes( pOut );
  2100. V_FixDoubleSlashes( pOut );
  2101. V_strlower( pOut );
  2102. }
  2103. // Copies at most nCharsToCopy bytes from pIn into pOut.
  2104. // Returns false if it would have overflowed pOut's buffer.
  2105. static bool CopyToMaxChars( char *pOut, int outSize, const char *pIn, int nCharsToCopy )
  2106. {
  2107. if ( outSize == 0 )
  2108. return false;
  2109. int iOut = 0;
  2110. while ( *pIn && nCharsToCopy > 0 )
  2111. {
  2112. if ( iOut == (outSize-1) )
  2113. {
  2114. pOut[iOut] = 0;
  2115. return false;
  2116. }
  2117. pOut[iOut] = *pIn;
  2118. ++iOut;
  2119. ++pIn;
  2120. --nCharsToCopy;
  2121. }
  2122. pOut[iOut] = 0;
  2123. return true;
  2124. }
  2125. // Returns true if it completed successfully.
  2126. // If it would overflow pOut, it fills as much as it can and returns false.
  2127. bool V_StrSubst(
  2128. const char *pIn,
  2129. const char *pMatch,
  2130. const char *pReplaceWith,
  2131. char *pOut,
  2132. int outLen,
  2133. bool bCaseSensitive
  2134. )
  2135. {
  2136. int replaceFromLen = V_strlen( pMatch );
  2137. int replaceToLen = V_strlen( pReplaceWith );
  2138. const char *pInStart = pIn;
  2139. char *pOutPos = pOut;
  2140. pOutPos[0] = 0;
  2141. while ( 1 )
  2142. {
  2143. int nRemainingOut = outLen - (pOutPos - pOut);
  2144. const char *pTestPos = ( bCaseSensitive ? V_strstr( pInStart, pMatch ) : V_stristr( pInStart, pMatch ) );
  2145. if ( pTestPos )
  2146. {
  2147. // Found an occurence of pMatch. First, copy whatever leads up to the string.
  2148. int copyLen = pTestPos - pInStart;
  2149. if ( !CopyToMaxChars( pOutPos, nRemainingOut, pInStart, copyLen ) )
  2150. return false;
  2151. // Did we hit the end of the output string?
  2152. if ( copyLen > nRemainingOut-1 )
  2153. return false;
  2154. pOutPos += V_strlen( pOutPos );
  2155. nRemainingOut = outLen - (pOutPos - pOut);
  2156. // Now add the replacement string.
  2157. if ( !CopyToMaxChars( pOutPos, nRemainingOut, pReplaceWith, replaceToLen ) )
  2158. return false;
  2159. pInStart += copyLen + replaceFromLen;
  2160. pOutPos += replaceToLen;
  2161. }
  2162. else
  2163. {
  2164. // We're at the end of pIn. Copy whatever remains and get out.
  2165. int copyLen = V_strlen( pInStart );
  2166. V_strncpy( pOutPos, pInStart, nRemainingOut );
  2167. return ( copyLen <= nRemainingOut-1 );
  2168. }
  2169. }
  2170. }
  2171. char* AllocString( const char *pStr, int nMaxChars )
  2172. {
  2173. int allocLen;
  2174. if ( nMaxChars == -1 )
  2175. allocLen = V_strlen( pStr ) + 1;
  2176. else
  2177. allocLen = MIN( V_strlen(pStr), nMaxChars ) + 1;
  2178. char *pOut = new char[allocLen];
  2179. V_strncpy( pOut, pStr, allocLen );
  2180. return pOut;
  2181. }
  2182. void V_SplitString2( const char *pString, const char **pSeparators, int nSeparators, CUtlVector<char*> &outStrings )
  2183. {
  2184. // We must pass in an empty outStrings buffer or call outStrings.PurgeAndDeleteElements between
  2185. // calls.
  2186. Assert( outStrings.Count() == 0 );
  2187. // This will make outStrings empty but it will not free any memory that the elements were pointing to.
  2188. outStrings.Purge();
  2189. const char *pCurPos = pString;
  2190. while ( 1 )
  2191. {
  2192. int iFirstSeparator = -1;
  2193. const char *pFirstSeparator = 0;
  2194. for ( int i=0; i < nSeparators; i++ )
  2195. {
  2196. const char *pTest = V_stristr( pCurPos, pSeparators[i] );
  2197. if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) )
  2198. {
  2199. iFirstSeparator = i;
  2200. pFirstSeparator = pTest;
  2201. }
  2202. }
  2203. if ( pFirstSeparator )
  2204. {
  2205. // Split on this separator and continue on.
  2206. int separatorLen = V_strlen( pSeparators[iFirstSeparator] );
  2207. if ( pFirstSeparator > pCurPos )
  2208. {
  2209. outStrings.AddToTail( AllocString( pCurPos, pFirstSeparator-pCurPos ) );
  2210. }
  2211. pCurPos = pFirstSeparator + separatorLen;
  2212. }
  2213. else
  2214. {
  2215. // Copy the rest of the string
  2216. if ( V_strlen( pCurPos ) )
  2217. {
  2218. outStrings.AddToTail( AllocString( pCurPos, -1 ) );
  2219. }
  2220. return;
  2221. }
  2222. }
  2223. }
  2224. void V_SplitString( const char *pString, const char *pSeparator, CUtlVector<char*> &outStrings )
  2225. {
  2226. V_SplitString2( pString, &pSeparator, 1, outStrings );
  2227. }
  2228. void V_SplitString2(const char *pString, const char * const *pSeparators, int nSeparators, CUtlVector<CUtlString> &outStrings, bool bIncludeEmptyStrings)
  2229. {
  2230. outStrings.Purge();
  2231. const char *pCurPos = pString;
  2232. for (;;)
  2233. {
  2234. int iFirstSeparator = -1;
  2235. const char *pFirstSeparator = 0;
  2236. for (int i = 0; i < nSeparators; i++)
  2237. {
  2238. const char *pTest = V_stristr_fast(pCurPos, pSeparators[i]);
  2239. if (pTest && (!pFirstSeparator || pTest < pFirstSeparator))
  2240. {
  2241. iFirstSeparator = i;
  2242. pFirstSeparator = pTest;
  2243. }
  2244. }
  2245. if (pFirstSeparator)
  2246. {
  2247. // Split on this separator and continue on.
  2248. int separatorLen = (int)strlen(pSeparators[iFirstSeparator]);
  2249. if (pFirstSeparator > pCurPos || (pFirstSeparator == pCurPos && bIncludeEmptyStrings))
  2250. {
  2251. outStrings[outStrings.AddToTail()].SetDirect(pCurPos, (int)(pFirstSeparator - pCurPos));
  2252. }
  2253. pCurPos = pFirstSeparator + separatorLen;
  2254. }
  2255. else
  2256. {
  2257. // Copy the rest of the string, if there's anything there
  2258. if (pCurPos[0] != 0)
  2259. {
  2260. outStrings[outStrings.AddToTail()].Set(pCurPos);
  2261. }
  2262. return;
  2263. }
  2264. }
  2265. }
  2266. void V_SplitString(const char *pString, const char *pSeparator, CUtlVector<CUtlString> &outStrings, bool bIncludeEmptyStrings)
  2267. {
  2268. V_SplitString2(pString, &pSeparator, 1, outStrings, bIncludeEmptyStrings);
  2269. }
  2270. wchar_t* AllocWString( const wchar_t *pStr, int nMaxChars )
  2271. {
  2272. int allocLen;
  2273. if ( nMaxChars == -1 )
  2274. allocLen = V_wcslen( pStr ) + 1;
  2275. else
  2276. allocLen = MIN( (int)V_wcslen(pStr), nMaxChars ) + 1;
  2277. wchar_t *pOut = new wchar_t[allocLen];
  2278. V_wcsncpy( pOut, pStr, allocLen * sizeof(wchar_t) );
  2279. return pOut;
  2280. }
  2281. void V_SplitWString2( const wchar_t *pString, const wchar_t **pSeparators, int nSeparators, CUtlVector<wchar_t*> &outStrings )
  2282. {
  2283. outStrings.Purge();
  2284. const wchar_t *pCurPos = pString;
  2285. while ( 1 )
  2286. {
  2287. int iFirstSeparator = -1;
  2288. const wchar_t *pFirstSeparator = 0;
  2289. for ( int i=0; i < nSeparators; i++ )
  2290. {
  2291. const wchar_t *pTest = V_wcsistr( pCurPos, pSeparators[i] );
  2292. if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) )
  2293. {
  2294. iFirstSeparator = i;
  2295. pFirstSeparator = pTest;
  2296. }
  2297. }
  2298. if ( pFirstSeparator )
  2299. {
  2300. // Split on this separator and continue on.
  2301. int separatorLen = V_wcslen( pSeparators[iFirstSeparator] );
  2302. if ( pFirstSeparator > pCurPos )
  2303. {
  2304. outStrings.AddToTail( AllocWString( pCurPos, pFirstSeparator-pCurPos ) );
  2305. }
  2306. pCurPos = pFirstSeparator + separatorLen;
  2307. }
  2308. else
  2309. {
  2310. // Copy the rest of the string
  2311. if ( V_wcslen( pCurPos ) )
  2312. {
  2313. outStrings.AddToTail( AllocWString( pCurPos, -1 ) );
  2314. }
  2315. return;
  2316. }
  2317. }
  2318. }
  2319. void V_SplitWString( const wchar_t *pString, const wchar_t *pSeparator, CUtlVector<wchar_t*> &outStrings )
  2320. {
  2321. V_SplitWString2( pString, &pSeparator, 1, outStrings );
  2322. }
  2323. bool V_GetCurrentDirectory( char *pOut, int maxLen )
  2324. {
  2325. #if defined( _PS3 )
  2326. Assert( 0 );
  2327. return false; // not supported
  2328. #else // !_PS3
  2329. return _getcwd( pOut, maxLen ) == pOut;
  2330. #endif // _PS3
  2331. }
  2332. bool V_SetCurrentDirectory( const char *pDirName )
  2333. {
  2334. #if defined( _PS3 )
  2335. Assert( 0 );
  2336. return false; // not supported
  2337. #else // !_PS3
  2338. return _chdir( pDirName ) == 0;
  2339. #endif // _PS3
  2340. }
  2341. // This function takes a slice out of pStr and stores it in pOut.
  2342. // It follows the Python slice convention:
  2343. // Negative numbers wrap around the string (-1 references the last character).
  2344. // Numbers are clamped to the end of the string.
  2345. void V_StrSlice( const char *pStr, int firstChar, int lastCharNonInclusive, char *pOut, int outSize )
  2346. {
  2347. if ( outSize == 0 )
  2348. return;
  2349. int length = V_strlen( pStr );
  2350. // Fixup the string indices.
  2351. if ( firstChar < 0 )
  2352. {
  2353. firstChar = length - (-firstChar % length);
  2354. }
  2355. else if ( firstChar >= length )
  2356. {
  2357. pOut[0] = 0;
  2358. return;
  2359. }
  2360. if ( lastCharNonInclusive < 0 )
  2361. {
  2362. lastCharNonInclusive = length - (-lastCharNonInclusive % length);
  2363. }
  2364. else if ( lastCharNonInclusive > length )
  2365. {
  2366. lastCharNonInclusive %= length;
  2367. }
  2368. if ( lastCharNonInclusive <= firstChar )
  2369. {
  2370. pOut[0] = 0;
  2371. return;
  2372. }
  2373. int copyLen = lastCharNonInclusive - firstChar;
  2374. if ( copyLen <= (outSize-1) )
  2375. {
  2376. memcpy( pOut, &pStr[firstChar], copyLen );
  2377. pOut[copyLen] = 0;
  2378. }
  2379. else
  2380. {
  2381. memcpy( pOut, &pStr[firstChar], outSize-1 );
  2382. pOut[outSize-1] = 0;
  2383. }
  2384. }
  2385. void V_StrLeft( const char *pStr, int nChars, char *pOut, int outSize )
  2386. {
  2387. if ( nChars == 0 )
  2388. {
  2389. if ( outSize != 0 )
  2390. pOut[0] = 0;
  2391. return;
  2392. }
  2393. V_StrSlice( pStr, 0, nChars, pOut, outSize );
  2394. }
  2395. void V_StrRight( const char *pStr, int nChars, char *pOut, int outSize )
  2396. {
  2397. int len = V_strlen( pStr );
  2398. if ( nChars >= len )
  2399. {
  2400. V_strncpy( pOut, pStr, outSize );
  2401. }
  2402. else
  2403. {
  2404. V_StrSlice( pStr, -nChars, V_strlen( pStr ), pOut, outSize );
  2405. }
  2406. }
  2407. //-----------------------------------------------------------------------------
  2408. // Convert multibyte to wchar + back
  2409. //-----------------------------------------------------------------------------
  2410. void V_strtowcs( const char *pString, int nInSize, wchar_t *pWString, int nOutSizeInBytes )
  2411. {
  2412. Assert( nOutSizeInBytes >= sizeof(pWString[0]) );
  2413. #ifdef _WIN32
  2414. int nOutSizeInChars = nOutSizeInBytes / sizeof(pWString[0]);
  2415. int result = MultiByteToWideChar( CP_UTF8, 0, pString, nInSize, pWString, nOutSizeInChars );
  2416. // If the string completely fails to fit then MultiByteToWideChar will return 0.
  2417. // If the string exactly fits but with no room for a null-terminator then MultiByteToWideChar
  2418. // will happily fill the buffer and omit the null-terminator, returning nOutSizeInChars.
  2419. // Either way we need to return an empty string rather than a bogus and possibly not
  2420. // null-terminated result.
  2421. if ( result <= 0 || result >= nOutSizeInChars )
  2422. {
  2423. // If nInSize includes the null-terminator then a result of nOutSizeInChars is
  2424. // legal. We check this by seeing if the last character in the output buffer is
  2425. // a zero.
  2426. if ( result == nOutSizeInChars && pWString[ nOutSizeInChars - 1 ] == 0)
  2427. {
  2428. // We're okay! Do nothing.
  2429. }
  2430. else
  2431. {
  2432. // The string completely to fit. Null-terminate the buffer.
  2433. *pWString = L'\0';
  2434. }
  2435. }
  2436. else
  2437. {
  2438. // We have successfully converted our string. Now we need to null-terminate it, because
  2439. // MultiByteToWideChar will only do that if nInSize includes the source null-terminator!
  2440. pWString[ result ] = 0;
  2441. }
  2442. #elif POSIX
  2443. if ( mbstowcs( pWString, pString, nOutSizeInBytes / sizeof(pWString[0]) ) <= 0 )
  2444. {
  2445. *pWString = 0;
  2446. }
  2447. #endif
  2448. }
  2449. void V_wcstostr( const wchar_t *pWString, int nInSize, char *pString, int nOutSizeInChars )
  2450. {
  2451. #ifdef _WIN32
  2452. int result = WideCharToMultiByte( CP_UTF8, 0, pWString, nInSize, pString, nOutSizeInChars, NULL, NULL );
  2453. // If the string completely fails to fit then MultiByteToWideChar will return 0.
  2454. // If the string exactly fits but with no room for a null-terminator then MultiByteToWideChar
  2455. // will happily fill the buffer and omit the null-terminator, returning nOutSizeInChars.
  2456. // Either way we need to return an empty string rather than a bogus and possibly not
  2457. // null-terminated result.
  2458. if ( result <= 0 || result >= nOutSizeInChars )
  2459. {
  2460. // If nInSize includes the null-terminator then a result of nOutSizeInChars is
  2461. // legal. We check this by seeing if the last character in the output buffer is
  2462. // a zero.
  2463. if ( result == nOutSizeInChars && pWString[ nOutSizeInChars - 1 ] == 0)
  2464. {
  2465. // We're okay! Do nothing.
  2466. }
  2467. else
  2468. {
  2469. *pString = '\0';
  2470. }
  2471. }
  2472. else
  2473. {
  2474. // We have successfully converted our string. Now we need to null-terminate it, because
  2475. // MultiByteToWideChar will only do that if nInSize includes the source null-terminator!
  2476. pString[ result ] = '\0';
  2477. }
  2478. #elif POSIX
  2479. if ( wcstombs( pString, pWString, nOutSizeInChars ) <= 0 )
  2480. {
  2481. *pString = '\0';
  2482. }
  2483. #endif
  2484. }
  2485. //--------------------------------------------------------------------------------
  2486. // backslashification
  2487. //--------------------------------------------------------------------------------
  2488. static char s_BackSlashMap[]="\tt\nn\rr\"\"\\\\";
  2489. char *V_AddBackSlashesToSpecialChars( const char *pSrc )
  2490. {
  2491. // first, count how much space we are going to need
  2492. int nSpaceNeeded = 0;
  2493. for( const char *pScan = pSrc; *pScan; pScan++ )
  2494. {
  2495. nSpaceNeeded++;
  2496. for(const char *pCharSet=s_BackSlashMap; *pCharSet; pCharSet += 2 )
  2497. {
  2498. if ( *pCharSet == *pScan )
  2499. nSpaceNeeded++; // we need to store a bakslash
  2500. }
  2501. }
  2502. char *pRet = new char[ nSpaceNeeded + 1 ]; // +1 for null
  2503. char *pOut = pRet;
  2504. for( const char *pScan = pSrc; *pScan; pScan++ )
  2505. {
  2506. bool bIsSpecial = false;
  2507. for(const char *pCharSet=s_BackSlashMap; *pCharSet; pCharSet += 2 )
  2508. {
  2509. if ( *pCharSet == *pScan )
  2510. {
  2511. *( pOut++ ) = '\\';
  2512. *( pOut++ ) = pCharSet[1];
  2513. bIsSpecial = true;
  2514. break;
  2515. }
  2516. }
  2517. if (! bIsSpecial )
  2518. {
  2519. *( pOut++ ) = *pScan;
  2520. }
  2521. }
  2522. *( pOut++ ) = 0;
  2523. return pRet;
  2524. }
  2525. int V_StringToIntArray( int *pVector, int count, const char *pString )
  2526. {
  2527. char *pstr, *pfront, tempString[128];
  2528. int j;
  2529. V_strncpy( tempString, pString, sizeof(tempString) );
  2530. pstr = pfront = tempString;
  2531. for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
  2532. {
  2533. pVector[j] = atoi( pfront );
  2534. while ( *pstr && *pstr != ' ' )
  2535. pstr++;
  2536. if (!*pstr)
  2537. break;
  2538. pstr++;
  2539. pfront = pstr;
  2540. }
  2541. int nFound = j + 1;
  2542. for ( j++; j < count; j++ )
  2543. {
  2544. pVector[j] = 0;
  2545. }
  2546. return nFound;
  2547. }
  2548. int V_StringToFloatArray( float *pVector, int count, const char *pString )
  2549. {
  2550. char *pstr, *pfront, tempString[128];
  2551. int j;
  2552. V_strncpy( tempString, pString, sizeof(tempString) );
  2553. pstr = pfront = tempString;
  2554. for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
  2555. {
  2556. pVector[j] = atof( pfront );
  2557. // skip any leading whitespace
  2558. while ( *pstr && *pstr <= ' ' )
  2559. pstr++;
  2560. // skip to next whitespace
  2561. while ( *pstr && *pstr > ' ' )
  2562. pstr++;
  2563. if (!*pstr)
  2564. break;
  2565. pstr++;
  2566. pfront = pstr;
  2567. }
  2568. int nFound = j + 1;
  2569. for ( j++; j < count; j++ )
  2570. {
  2571. pVector[j] = 0;
  2572. }
  2573. return nFound;
  2574. }
  2575. void V_StringToVector( float *pVector, const char *pString )
  2576. {
  2577. V_StringToFloatArray( pVector, 3, pString );
  2578. }
  2579. void V_StringToColor32( color32 *color, const char *pString )
  2580. {
  2581. int tmp[4];
  2582. int nCount = V_StringToIntArray( tmp, 4, pString );
  2583. color->r = tmp[0];
  2584. color->g = tmp[1];
  2585. color->b = tmp[2];
  2586. color->a = ( nCount == 4 ) ? tmp[3] : 255;
  2587. }
  2588. // 3d memory copy
  2589. void CopyMemory3D( void *pDest, void const *pSrc,
  2590. int nNumCols, int nNumRows, int nNumSlices, // dimensions of copy
  2591. int nSrcBytesPerRow, int nSrcBytesPerSlice, // strides for source.
  2592. int nDestBytesPerRow, int nDestBytesPerSlice // strides for dest
  2593. )
  2594. {
  2595. if ( nNumSlices && nNumRows && nNumCols )
  2596. {
  2597. uint8 *pDestAdr = reinterpret_cast<uint8 *>( pDest );
  2598. uint8 const *pSrcAdr = reinterpret_cast<uint8 const *>( pSrc );
  2599. // first check for optimized cases
  2600. if ( ( nNumCols == nSrcBytesPerRow ) && ( nNumCols == nDestBytesPerRow ) ) // no row-to-row stride?
  2601. {
  2602. int n2DSize = nNumCols * nNumRows;
  2603. if ( nSrcBytesPerSlice == nDestBytesPerSlice ) // can we do one memcpy?
  2604. {
  2605. memcpy( pDestAdr, pSrcAdr, n2DSize * nNumSlices );
  2606. }
  2607. else
  2608. {
  2609. // there might be some slice-to-slice stride
  2610. do
  2611. {
  2612. memcpy( pDestAdr, pSrcAdr, n2DSize );
  2613. pDestAdr += nDestBytesPerSlice;
  2614. pSrcAdr += nSrcBytesPerSlice;
  2615. } while( nNumSlices-- );
  2616. }
  2617. }
  2618. else
  2619. {
  2620. // there is row-by-row stride - we have to do the full nested loop
  2621. do
  2622. {
  2623. int nRowCtr = nNumRows;
  2624. uint8 const *pSrcRow = pSrcAdr;
  2625. uint8 *pDestRow = pDestAdr;
  2626. do
  2627. {
  2628. memcpy( pDestRow, pSrcRow, nNumCols );
  2629. pDestRow += nDestBytesPerRow;
  2630. pSrcRow += nSrcBytesPerRow;
  2631. } while( --nRowCtr );
  2632. pSrcAdr += nSrcBytesPerSlice;
  2633. pDestAdr += nDestBytesPerSlice;
  2634. } while( --nNumSlices );
  2635. }
  2636. }
  2637. }
  2638. void V_TranslateLineFeedsToUnix( char *pStr )
  2639. {
  2640. char *pIn = pStr;
  2641. char *pOut = pStr;
  2642. while ( *pIn )
  2643. {
  2644. if ( pIn[0] == '\r' && pIn[1] == '\n' )
  2645. {
  2646. ++pIn;
  2647. }
  2648. *pOut++ = *pIn++;
  2649. }
  2650. *pOut = 0;
  2651. }
  2652. // Returns true if additional data is waiting to be processed on this line
  2653. bool V_TokenWaiting( const char *buffer )
  2654. {
  2655. const char *p = buffer;
  2656. while ( *p && *p != '\n' )
  2657. {
  2658. if ( !V_isspace( *p ) || V_isalnum( *p ) )
  2659. return true;
  2660. p++;
  2661. }
  2662. return false;
  2663. }
  2664. // If pBreakCharacters == NULL, then the tokenizer will split tokens at the following characters:
  2665. // { } ( ) ' :
  2666. const char *V_ParseToken( const char *pStrIn, char *pToken, int bufsize, bool *pbOverflowed /*= NULL*/, struct characterset_t *pTokenBreakCharacters /*= NULL*/ )
  2667. {
  2668. if ( pbOverflowed )
  2669. {
  2670. *pbOverflowed = false;
  2671. }
  2672. int maxpos = bufsize - 1;
  2673. unsigned char c;
  2674. int len;
  2675. characterset_t *breaks = pTokenBreakCharacters;
  2676. if ( !breaks )
  2677. {
  2678. static bool built = false;
  2679. static characterset_t s_BreakSetIncludingColons;
  2680. if ( !built )
  2681. {
  2682. built = true;
  2683. CharacterSetBuild( &s_BreakSetIncludingColons, "{}()':" );
  2684. }
  2685. breaks = &s_BreakSetIncludingColons;
  2686. }
  2687. len = 0;
  2688. pToken[0] = 0;
  2689. if (!pStrIn)
  2690. return NULL;
  2691. if ( maxpos <= 0 )
  2692. return pStrIn;
  2693. // skip whitespace
  2694. skipwhite:
  2695. while ( (c = *pStrIn) <= ' ')
  2696. {
  2697. if (c == 0)
  2698. return NULL; // end of file;
  2699. pStrIn++;
  2700. }
  2701. // skip // comments
  2702. if (c=='/' && pStrIn[1] == '/')
  2703. {
  2704. while (*pStrIn && *pStrIn != '\n')
  2705. pStrIn++;
  2706. goto skipwhite;
  2707. }
  2708. // handle quoted strings specially
  2709. if (c == '\"')
  2710. {
  2711. pStrIn++;
  2712. while ( 1 )
  2713. {
  2714. c = *pStrIn++;
  2715. if (c=='\"' || !c)
  2716. {
  2717. pToken[len] = 0;
  2718. return pStrIn;
  2719. }
  2720. pToken[len] = c;
  2721. len++;
  2722. // Got to last valid spot
  2723. if ( len >= maxpos )
  2724. {
  2725. if ( pbOverflowed )
  2726. {
  2727. *pbOverflowed = true;
  2728. }
  2729. pToken[ len ] = 0;
  2730. while ( 1 )
  2731. {
  2732. c = *pStrIn++;
  2733. if ( c == '\"' || !c )
  2734. break;
  2735. }
  2736. return pStrIn;
  2737. }
  2738. }
  2739. }
  2740. // parse single characters
  2741. if ( IN_CHARACTERSET( *breaks, c ) )
  2742. {
  2743. pToken[len] = c;
  2744. len++;
  2745. pToken[len] = 0;
  2746. return pStrIn+1;
  2747. }
  2748. // parse a regular word
  2749. do
  2750. {
  2751. pToken[len] = c;
  2752. pStrIn++;
  2753. len++;
  2754. c = *pStrIn;
  2755. if ( IN_CHARACTERSET( *breaks, c ) )
  2756. break;
  2757. if ( len >= maxpos )
  2758. {
  2759. if ( pbOverflowed )
  2760. {
  2761. *pbOverflowed = true;
  2762. }
  2763. break;
  2764. }
  2765. } while (c>32);
  2766. pToken[len] = 0;
  2767. return pStrIn;
  2768. }
  2769. // Parses a single line, does not trim any whitespace from start or end. Does not include the final '\n'.
  2770. // NOTE: This function has not been rigorously tested!!!
  2771. char const *V_ParseLine( char const *pStrIn, char *pToken, int bufsize, bool *pbOverflowed /*= NULL*/ )
  2772. {
  2773. if ( pbOverflowed )
  2774. {
  2775. *pbOverflowed = false;
  2776. }
  2777. int maxpos = bufsize - 1;
  2778. int len;
  2779. len = 0;
  2780. pToken[0] = 0;
  2781. if (!pStrIn)
  2782. return NULL;
  2783. if ( maxpos <= 0 )
  2784. return pStrIn;
  2785. while ( *pStrIn && *pStrIn != '\n')
  2786. {
  2787. pToken[ len++ ] = *pStrIn++;
  2788. if ( len >= maxpos )
  2789. {
  2790. if ( pbOverflowed )
  2791. {
  2792. *pbOverflowed = true;
  2793. }
  2794. return NULL;
  2795. }
  2796. }
  2797. pToken[len] = 0;
  2798. if ( *pStrIn == 0 )
  2799. return NULL;
  2800. return pStrIn + 1;
  2801. }
  2802. static char s_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
  2803. int HexToValue( char hex )
  2804. {
  2805. if( hex >= '0' && hex <= '9' )
  2806. {
  2807. return hex - '0';
  2808. }
  2809. if( hex >= 'A' && hex <= 'F' )
  2810. {
  2811. return hex - 'A' + 10;
  2812. }
  2813. if( hex >= 'a' && hex <= 'f' )
  2814. {
  2815. return hex - 'a' + 10;
  2816. }
  2817. // report error here
  2818. return -1;
  2819. }
  2820. bool V_StringToBin( const char*pString, void *pBin, uint nBinSize )
  2821. {
  2822. if ( (uint)V_strlen( pString ) != nBinSize * 2 )
  2823. {
  2824. return false;
  2825. }
  2826. for ( uint i = 0; i < nBinSize; ++i )
  2827. {
  2828. int high = HexToValue( pString[i*2+0] );
  2829. int low = HexToValue( pString[i*2+1] ) ;
  2830. if( high < 0 || low < 0 )
  2831. {
  2832. return false;
  2833. }
  2834. ( ( uint8* )pBin )[i] = uint8( ( high << 4 ) | low );
  2835. }
  2836. return true;
  2837. }
  2838. bool V_BinToString( char*pString, void *pBin, uint nBinSize )
  2839. {
  2840. for ( uint i = 0; i < nBinSize; ++i )
  2841. {
  2842. pString[i*2+0] = s_hex[( ( uint8* )pBin )[i] >> 4 ];
  2843. pString[i*2+1] = s_hex[( ( uint8* )pBin )[i] & 0xF];
  2844. }
  2845. pString[nBinSize*2] = '\0';
  2846. return true;
  2847. }
  2848. // The following characters are not allowed to begin a line for Asian language line-breaking
  2849. // purposes. They include the right parenthesis/bracket, space character, period, exclamation,
  2850. // question mark, and a number of language-specific characters for Chinese, Japanese, and Korean
  2851. static const wchar_t wszCantBeginLine[] =
  2852. {
  2853. 0x0020, 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b,
  2854. 0x003e, 0x003f, 0x005d, 0x007d, 0x00a2, 0x00a8, 0x00b0, 0x00b7,
  2855. 0x00bb, 0x02c7, 0x02c9, 0x2010, 0x2013, 0x2014, 0x2015, 0x2016,
  2856. 0x2019, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2025,
  2857. 0x2026, 0x2027, 0x203a, 0x203c, 0x2047, 0x2048, 0x2049, 0x2103,
  2858. 0x2236, 0x2574, 0x3001, 0x3002, 0x3003, 0x3005, 0x3006, 0x3009,
  2859. 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0x3019, 0x301b,
  2860. 0x301c, 0x301e, 0x301f, 0x303b, 0x3041, 0x3043, 0x3045, 0x3047,
  2861. 0x3049, 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x3095, 0x3096,
  2862. 0x30a0, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3,
  2863. 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fd, 0x30fe,
  2864. 0x30fc, 0x31f0, 0x31f1, 0x31f2, 0x31f3, 0x31f4, 0x31f5, 0x31f6,
  2865. 0x31f7, 0x31f8, 0x31f9, 0x31fa, 0x31fb, 0x31fc, 0x31fd, 0x31fe,
  2866. 0x31ff, 0xfe30, 0xfe31, 0xfe32, 0xfe33, 0xfe36, 0xfe38, 0xfe3a,
  2867. 0xfe3c, 0xfe3e, 0xfe40, 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51,
  2868. 0xfe52, 0xfe53, 0xfe54, 0xfe55, 0xfe56, 0xfe57, 0xfe58, 0xfe5a,
  2869. 0xfe5c, 0xfe5e, 0xff01, 0xff02, 0xff05, 0xff07, 0xff09, 0xff0c,
  2870. 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d, 0xff40, 0xff5c, 0xff5d,
  2871. 0xff5e, 0xff60, 0xff64
  2872. };
  2873. // The following characters are not allowed to end a line for Asian Language line-breaking
  2874. // purposes. They include left parenthesis/bracket, currency symbols, and an number
  2875. // of language-specific characters for Chinese, Japanese, and Korean
  2876. static const wchar_t wszCantEndLine[] =
  2877. {
  2878. 0x0024, 0x0028, 0x002a, 0x003c, 0x005b, 0x005c, 0x007b, 0x00a3,
  2879. 0x00a5, 0x00ab, 0x00ac, 0x00b7, 0x02c6, 0x2018, 0x201c, 0x201f,
  2880. 0x2035, 0x2039, 0x3005, 0x3007, 0x3008, 0x300a, 0x300c, 0x300e,
  2881. 0x3010, 0x3014, 0x3016, 0x3018, 0x301a, 0x301d, 0xfe34, 0xfe35,
  2882. 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59,
  2883. 0xfe5b, 0xfe5d, 0xff04, 0xff08, 0xff0e, 0xff3b, 0xff5b, 0xff5f,
  2884. 0xffe1, 0xffe5, 0xffe6
  2885. };
  2886. // Can't break between some repeated punctuation patterns ("--", "...", "<asian period repeated>")
  2887. static const wchar_t wszCantBreakRepeated[] =
  2888. {
  2889. 0x002d, 0x002e, 0x3002
  2890. };
  2891. bool AsianWordWrap::CanEndLine( wchar_t wcCandidate )
  2892. {
  2893. for( int i = 0; i < SIZE_OF_ARRAY( wszCantEndLine ); ++i )
  2894. {
  2895. if( wcCandidate == wszCantEndLine[i] )
  2896. return false;
  2897. }
  2898. return true;
  2899. }
  2900. bool AsianWordWrap::CanBeginLine( wchar_t wcCandidate )
  2901. {
  2902. for( int i = 0; i < SIZE_OF_ARRAY( wszCantBeginLine ); ++i )
  2903. {
  2904. if( wcCandidate == wszCantBeginLine[i] )
  2905. return false;
  2906. }
  2907. return true;
  2908. }
  2909. bool AsianWordWrap::CanBreakRepeated( wchar_t wcCandidate )
  2910. {
  2911. for( int i = 0; i < SIZE_OF_ARRAY( wszCantBreakRepeated ); ++i )
  2912. {
  2913. if( wcCandidate == wszCantBreakRepeated[i] )
  2914. return false;
  2915. }
  2916. return true;
  2917. }
  2918. #if defined( _PS3 ) || defined( LINUX )
  2919. inline int __cdecl iswascii(wchar_t c) { return ((unsigned)(c) < 0x80); } // not defined in wctype.h on the PS3
  2920. #endif
  2921. // Used to determine if we can break a line between the first two characters passed
  2922. bool AsianWordWrap::CanBreakAfter( const wchar_t* wsz )
  2923. {
  2924. if( wsz == NULL || wsz[0] == '\0' || wsz[1] == '\0' )
  2925. {
  2926. return false;
  2927. }
  2928. wchar_t first_char = wsz[0];
  2929. wchar_t second_char = wsz[1];
  2930. if( ( iswascii( first_char ) && iswascii( second_char ) ) // If not both CJK, return early
  2931. || ( iswalnum( first_char ) && iswalnum( second_char ) ) ) // both characters are alphanumeric - Don't split a number or a word!
  2932. {
  2933. return false;
  2934. }
  2935. if( !CanEndLine( first_char ) )
  2936. {
  2937. return false;
  2938. }
  2939. if( !CanBeginLine( second_char) )
  2940. {
  2941. return false;
  2942. }
  2943. // don't allow line wrapping in the middle of "--" or "..."
  2944. if( ( first_char == second_char ) && ( !CanBreakRepeated( first_char ) ) )
  2945. {
  2946. return false;
  2947. }
  2948. // If no rules would prevent us from breaking, assume it's safe to break here
  2949. return true;
  2950. }
  2951. // We use this function to determine where it is permissible to break lines
  2952. // of text while wrapping them. On some platforms, the native iswspace() function
  2953. // returns FALSE for the "non-breaking space" characters 0x00a0 and 0x202f, and so we don't
  2954. // break on them. On others (including the X360 and PC), iswspace returns TRUE for them.
  2955. // We get rid of the platform dependency by defining this wrapper which returns false
  2956. // for &nbsp; and calls through to the library function for everything else.
  2957. int isbreakablewspace( wchar_t ch )
  2958. {
  2959. // 0x00a0 and 0x202f are the wide and narrow non-breaking space UTF-16 values, respectively
  2960. return ch != 0x00a0 && ch != 0x202f && iswspace(ch);
  2961. }
  2962. bool V_StringMatchesPattern( const char* pszSource, const char* pszPattern, int nFlags /*= 0 */ )
  2963. {
  2964. bool bExact = true;
  2965. while( 1 )
  2966. {
  2967. if ( ( *pszPattern ) == 0 )
  2968. {
  2969. return ( (*pszSource ) == 0 );
  2970. }
  2971. if ( ( *pszPattern ) == '*' )
  2972. {
  2973. pszPattern++;
  2974. if ( ( *pszPattern ) == 0 )
  2975. {
  2976. return true;
  2977. }
  2978. bExact = false;
  2979. continue;
  2980. }
  2981. int nLength = 0;
  2982. while( ( *pszPattern ) != '*' && ( *pszPattern ) != 0 )
  2983. {
  2984. nLength++;
  2985. pszPattern++;
  2986. }
  2987. while( 1 )
  2988. {
  2989. const char *pszStartPattern = pszPattern - nLength;
  2990. const char *pszSearch = pszSource;
  2991. for( int i = 0; i < nLength; i++, pszSearch++, pszStartPattern++ )
  2992. {
  2993. if ( ( *pszSearch ) == 0 )
  2994. {
  2995. return false;
  2996. }
  2997. if ( ( *pszSearch ) != ( *pszStartPattern ) )
  2998. {
  2999. break;
  3000. }
  3001. }
  3002. if ( pszSearch - pszSource == nLength )
  3003. {
  3004. break;
  3005. }
  3006. if ( bExact == true )
  3007. {
  3008. return false;
  3009. }
  3010. if ( ( nFlags & PATTERN_DIRECTORY ) != 0 )
  3011. {
  3012. if ( ( *pszPattern ) != '/' && ( *pszSource ) == '/' )
  3013. {
  3014. return false;
  3015. }
  3016. }
  3017. pszSource++;
  3018. }
  3019. pszSource += nLength;
  3020. }
  3021. }
  3022. //-----------------------------------------------------------------------------
  3023. // Purpose: Helper for converting a numeric value to a hex digit, value should be 0-15.
  3024. //-----------------------------------------------------------------------------
  3025. char cIntToHexDigit( int nValue )
  3026. {
  3027. Assert( nValue >= 0 && nValue <= 15 );
  3028. return "0123456789ABCDEF"[ nValue & 15 ];
  3029. }
  3030. //-----------------------------------------------------------------------------
  3031. // Purpose: Helper for converting a hex char value to numeric, return -1 if the char
  3032. // is not a valid hex digit.
  3033. //-----------------------------------------------------------------------------
  3034. int iHexCharToInt( char cValue )
  3035. {
  3036. int32 iValue = cValue;
  3037. if ( (uint32)( iValue - '0' ) < 10 )
  3038. return iValue - '0';
  3039. iValue |= 0x20;
  3040. if ( (uint32)( iValue - 'a' ) < 6 )
  3041. return iValue - 'a' + 10;
  3042. return -1;
  3043. }
  3044. //-----------------------------------------------------------------------------
  3045. // Purpose: Internal implementation of encode, works in the strict RFC manner, or
  3046. // with spaces turned to + like HTML form encoding.
  3047. //-----------------------------------------------------------------------------
  3048. void Q_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen, bool bUsePlusForSpace )
  3049. {
  3050. if ( nDestLen < 3*nSourceLen )
  3051. {
  3052. pchDest[0] = '\0';
  3053. AssertMsg( false, "Target buffer for Q_URLEncode needs to be 3 times larger than source to guarantee enough space\n" );
  3054. return;
  3055. }
  3056. int iDestPos = 0;
  3057. for ( int i=0; i < nSourceLen; ++i )
  3058. {
  3059. // We allow only a-z, A-Z, 0-9, period, underscore, and hyphen to pass through unescaped.
  3060. // These are the characters allowed by both the original RFC 1738 and the latest RFC 3986.
  3061. // Current specs also allow '~', but that is forbidden under original RFC 1738.
  3062. if ( !( pchSource[i] >= 'a' && pchSource[i] <= 'z' ) && !( pchSource[i] >= 'A' && pchSource[i] <= 'Z' ) && !(pchSource[i] >= '0' && pchSource[i] <= '9' )
  3063. && pchSource[i] != '-' && pchSource[i] != '_' && pchSource[i] != '.'
  3064. )
  3065. {
  3066. if ( bUsePlusForSpace && pchSource[i] == ' ' )
  3067. {
  3068. pchDest[iDestPos++] = '+';
  3069. }
  3070. else
  3071. {
  3072. pchDest[iDestPos++] = '%';
  3073. uint8 iValue = pchSource[i];
  3074. if ( iValue == 0 )
  3075. {
  3076. pchDest[iDestPos++] = '0';
  3077. pchDest[iDestPos++] = '0';
  3078. }
  3079. else
  3080. {
  3081. char cHexDigit1 = cIntToHexDigit( iValue % 16 );
  3082. iValue /= 16;
  3083. char cHexDigit2 = cIntToHexDigit( iValue );
  3084. pchDest[iDestPos++] = cHexDigit2;
  3085. pchDest[iDestPos++] = cHexDigit1;
  3086. }
  3087. }
  3088. }
  3089. else
  3090. {
  3091. pchDest[iDestPos++] = pchSource[i];
  3092. }
  3093. }
  3094. // Null terminate
  3095. pchDest[iDestPos++] = 0;
  3096. }
  3097. //-----------------------------------------------------------------------------
  3098. // Purpose: Internal implementation of decode, works in the strict RFC manner, or
  3099. // with spaces turned to + like HTML form encoding.
  3100. //
  3101. // Returns the amount of space used in the output buffer.
  3102. //-----------------------------------------------------------------------------
  3103. size_t Q_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen, bool bUsePlusForSpace )
  3104. {
  3105. if ( nDecodeDestLen < nEncodedSourceLen )
  3106. {
  3107. AssertMsg( false, "Q_URLDecode needs a dest buffer at least as large as the source" );
  3108. return 0;
  3109. }
  3110. int iDestPos = 0;
  3111. for( int i=0; i < nEncodedSourceLen; ++i )
  3112. {
  3113. if ( bUsePlusForSpace && pchEncodedSource[i] == '+' )
  3114. {
  3115. pchDecodeDest[ iDestPos++ ] = ' ';
  3116. }
  3117. else if ( pchEncodedSource[i] == '%' )
  3118. {
  3119. // Percent signifies an encoded value, look ahead for the hex code, convert to numeric, and use that
  3120. // First make sure we have 2 more chars
  3121. if ( i < nEncodedSourceLen - 2 )
  3122. {
  3123. char cHexDigit1 = pchEncodedSource[i+1];
  3124. char cHexDigit2 = pchEncodedSource[i+2];
  3125. // Turn the chars into a hex value, if they are not valid, then we'll
  3126. // just place the % and the following two chars direct into the string,
  3127. // even though this really shouldn't happen, who knows what bad clients
  3128. // may do with encoding.
  3129. bool bValid = false;
  3130. int iValue = iHexCharToInt( cHexDigit1 );
  3131. if ( iValue != -1 )
  3132. {
  3133. iValue *= 16;
  3134. int iValue2 = iHexCharToInt( cHexDigit2 );
  3135. if ( iValue2 != -1 )
  3136. {
  3137. iValue += iValue2;
  3138. pchDecodeDest[ iDestPos++ ] = iValue;
  3139. bValid = true;
  3140. }
  3141. }
  3142. if ( !bValid )
  3143. {
  3144. pchDecodeDest[ iDestPos++ ] = '%';
  3145. pchDecodeDest[ iDestPos++ ] = cHexDigit1;
  3146. pchDecodeDest[ iDestPos++ ] = cHexDigit2;
  3147. }
  3148. }
  3149. // Skip ahead
  3150. i += 2;
  3151. }
  3152. else
  3153. {
  3154. pchDecodeDest[ iDestPos++ ] = pchEncodedSource[i];
  3155. }
  3156. }
  3157. // We may not have extra room to NULL terminate, since this can be used on raw data, but if we do
  3158. // go ahead and do it as this can avoid bugs.
  3159. if ( iDestPos < nDecodeDestLen )
  3160. {
  3161. pchDecodeDest[iDestPos] = 0;
  3162. }
  3163. return (size_t)iDestPos;
  3164. }
  3165. //-----------------------------------------------------------------------------
  3166. // Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
  3167. // This version of the call isn't a strict RFC implementation, but uses + for space as is
  3168. // the standard in HTML form encoding, despite it not being part of the RFC.
  3169. //
  3170. // Dest buffer should be at least as large as source buffer to guarantee room for decode.
  3171. //-----------------------------------------------------------------------------
  3172. void Q_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
  3173. {
  3174. return Q_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, true );
  3175. }
  3176. //-----------------------------------------------------------------------------
  3177. // Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
  3178. // This version of the call isn't a strict RFC implementation, but uses + for space as is
  3179. // the standard in HTML form encoding, despite it not being part of the RFC.
  3180. //
  3181. // Dest buffer should be at least as large as source buffer to guarantee room for decode.
  3182. // Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
  3183. //-----------------------------------------------------------------------------
  3184. size_t Q_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
  3185. {
  3186. return Q_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, true );
  3187. }
  3188. //-----------------------------------------------------------------------------
  3189. // Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
  3190. // This version will not encode space as + (which HTML form encoding uses despite not being part of the RFC)
  3191. //
  3192. // Dest buffer should be at least as large as source buffer to guarantee room for decode.
  3193. //-----------------------------------------------------------------------------
  3194. void Q_URLEncodeRaw( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
  3195. {
  3196. return Q_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false );
  3197. }
  3198. //-----------------------------------------------------------------------------
  3199. // Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
  3200. // This version will not recognize + as a space (which HTML form encoding uses despite not being part of the RFC)
  3201. //
  3202. // Dest buffer should be at least as large as source buffer to guarantee room for decode.
  3203. // Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
  3204. //-----------------------------------------------------------------------------
  3205. size_t Q_URLDecodeRaw( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
  3206. {
  3207. return Q_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, false );
  3208. }
  3209. #if defined( LINUX ) || defined( _PS3 )
  3210. extern "C" void qsort_s( void *base, size_t num, size_t width, int (*compare )(void *, const void *, const void *), void * context );
  3211. #endif
  3212. void V_qsort_s( void *base, size_t num, size_t width, int ( __cdecl *compare )(void *, const void *, const void *), void * context )
  3213. {
  3214. #if defined OSX
  3215. // the arguments are swapped 'round on the mac - awesome, huh?
  3216. return qsort_r( base, num, width, context, compare );
  3217. #elif defined LINUX
  3218. // FIXME: still not finding qsort_s, even though it's defined in qsort_s.cpp
  3219. // What's up with that?
  3220. return;
  3221. #else
  3222. return qsort_s( base, num, width, compare, context );
  3223. #endif
  3224. }
  3225. class CBoyerMooreSearch
  3226. {
  3227. public:
  3228. explicit CBoyerMooreSearch( const byte *pNeedle, int nNeedleSize );
  3229. int Search( const byte *pHayStack, int nHayStackLength );
  3230. private:
  3231. int m_JumpTable[256];
  3232. int m_nNeedleSize;
  3233. const byte *m_pNeedle;
  3234. };
  3235. CBoyerMooreSearch::CBoyerMooreSearch( const byte *pNeedle, int nNeedleSize )
  3236. {
  3237. m_pNeedle = pNeedle;
  3238. m_nNeedleSize = nNeedleSize;
  3239. int i = 0;
  3240. // All jumps by size of search string by default
  3241. for ( i = 0; i < 256; ++i )
  3242. {
  3243. m_JumpTable[ i ] = m_nNeedleSize;
  3244. }
  3245. // Now for each character in the needle, if it matches, we jump by less on failure
  3246. for ( i = 0; i < m_nNeedleSize - 1; ++i )
  3247. {
  3248. m_JumpTable[m_pNeedle[i]] = m_nNeedleSize - i - 1;
  3249. }
  3250. }
  3251. int CBoyerMooreSearch::Search( const byte *pHayStack, int nHayStackLength )
  3252. {
  3253. if ( m_nNeedleSize > nHayStackLength )
  3254. {
  3255. return -1;
  3256. }
  3257. int k = m_nNeedleSize - 1;
  3258. while ( k < nHayStackLength )
  3259. {
  3260. int j = m_nNeedleSize - 1;
  3261. int i = k;
  3262. while ( j >= 0 &&
  3263. pHayStack[i] == m_pNeedle[j] )
  3264. {
  3265. j--;
  3266. i--;
  3267. }
  3268. if (j == -1)
  3269. {
  3270. return i + 1;
  3271. }
  3272. k += m_JumpTable[ pHayStack[ k ] ];
  3273. }
  3274. return -1;
  3275. }
  3276. // Performs boyer moore text search, returns offset of first occurrence of needle in haystack, or -1 on failure. Note that haystack and the needle can be binary (non-text) data
  3277. int V_BoyerMooreSearch( const byte *pNeedle, int nNeedleLength, const byte *pHayStack, int nHayStackLength )
  3278. {
  3279. CBoyerMooreSearch search( pNeedle, nNeedleLength );
  3280. return search.Search( pHayStack, nHayStackLength );
  3281. }
  3282. CUtlString V_RandomString( int nLen )
  3283. {
  3284. CUtlString out;
  3285. for ( int i = 0; i < nLen; ++i )
  3286. {
  3287. char c = 0;
  3288. do
  3289. {
  3290. c = rand() & 0x7f;
  3291. } while ( !V_isalnum( c ) );
  3292. out += CFmtStr( "%c", c );
  3293. }
  3294. return out;
  3295. }
  3296. // Prints out a memory dump where stuff that's ascii is human readable, etc.
  3297. void V_LogMultiline( bool input, char const *label, const char *data, size_t len, CUtlString &output )
  3298. {
  3299. static const char HEX[] = "0123456789abcdef";
  3300. const char * direction = (input ? " << " : " >> ");
  3301. const size_t LINE_SIZE = 24;
  3302. char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
  3303. while (len > 0)
  3304. {
  3305. V_memset(asc_line, ' ', sizeof(asc_line));
  3306. V_memset(hex_line, ' ', sizeof(hex_line));
  3307. size_t line_len = MIN(len, LINE_SIZE);
  3308. for (size_t i=0; i<line_len; ++i) {
  3309. unsigned char ch = static_cast<unsigned char>(data[i]);
  3310. asc_line[i] = ( V_isprint(ch) && !V_iscntrl(ch) ) ? data[i] : '.';
  3311. hex_line[i*2 + i/4] = HEX[ch >> 4];
  3312. hex_line[i*2 + i/4 + 1] = HEX[ch & 0xf];
  3313. }
  3314. asc_line[sizeof(asc_line)-1] = 0;
  3315. hex_line[sizeof(hex_line)-1] = 0;
  3316. output += CFmtStr( "%s %s %s %s\n", label, direction, asc_line, hex_line );
  3317. data += line_len;
  3318. len -= line_len;
  3319. }
  3320. }
  3321. #ifdef WIN32
  3322. // Win32 CRT doesn't support the full range of UChar32, has no extended planes
  3323. inline int V_iswspace( int c ) { return ( c <= 0xFFFF ) ? iswspace( (wint_t)c ) : 0; }
  3324. #else
  3325. #define V_iswspace(x) iswspace(x)
  3326. #endif
  3327. //-----------------------------------------------------------------------------
  3328. // Purpose: Slightly modified strtok. Does not modify the input string. Does
  3329. // not skip over more than one separator at a time. This allows parsing
  3330. // strings where tokens between separators may or may not be present:
  3331. //
  3332. // Door01,,,0 would be parsed as "Door01" "" "" "0"
  3333. // Door01,Open,,0 would be parsed as "Door01" "Open" "" "0"
  3334. //
  3335. // Input : token - Returns with a token, or zero length if the token was missing.
  3336. // str - String to parse.
  3337. // sep - Character to use as separator. UNDONE: allow multiple separator chars
  3338. // Output : Returns a pointer to the next token to be parsed.
  3339. //-----------------------------------------------------------------------------
  3340. const char *nexttoken(char *token, const char *str, char sep)
  3341. {
  3342. if ((str == NULL) || (*str == '\0'))
  3343. {
  3344. *token = '\0';
  3345. return(NULL);
  3346. }
  3347. //
  3348. // Copy everything up to the first separator into the return buffer.
  3349. // Do not include separators in the return buffer.
  3350. //
  3351. while ((*str != sep) && (*str != '\0'))
  3352. {
  3353. *token++ = *str++;
  3354. }
  3355. *token = '\0';
  3356. //
  3357. // Advance the pointer unless we hit the end of the input string.
  3358. //
  3359. if (*str == '\0')
  3360. {
  3361. return(str);
  3362. }
  3363. return(++str);
  3364. }
  3365. int V_StrTrim( char *pStr )
  3366. {
  3367. char *pSource = pStr;
  3368. char *pDest = pStr;
  3369. // skip white space at the beginning
  3370. while ( *pSource != 0 && V_isspace( *pSource ) )
  3371. {
  3372. pSource++;
  3373. }
  3374. // copy everything else
  3375. char *pLastWhiteBlock = NULL;
  3376. char *pStart = pDest;
  3377. while ( *pSource != 0 )
  3378. {
  3379. *pDest = *pSource++;
  3380. if ( V_isspace( *pDest ) )
  3381. {
  3382. if ( pLastWhiteBlock == NULL )
  3383. pLastWhiteBlock = pDest;
  3384. }
  3385. else
  3386. {
  3387. pLastWhiteBlock = NULL;
  3388. }
  3389. pDest++;
  3390. }
  3391. *pDest = 0;
  3392. // did we end in a whitespace block?
  3393. if ( pLastWhiteBlock != NULL )
  3394. {
  3395. // yep; shorten the string
  3396. pDest = pLastWhiteBlock;
  3397. *pLastWhiteBlock = 0;
  3398. }
  3399. return pDest - pStart;
  3400. }
  3401. int64 V_strtoi64( const char *nptr, char **endptr, int base )
  3402. {
  3403. return _strtoi64( nptr, endptr, base );
  3404. }
  3405. uint64 V_strtoui64( const char *nptr, char **endptr, int base )
  3406. {
  3407. return _strtoui64( nptr, endptr, base );
  3408. }
  3409. struct HtmlEntity_t
  3410. {
  3411. unsigned short uCharCode;
  3412. const char *pchEntity;
  3413. int nEntityLength;
  3414. };
  3415. const static HtmlEntity_t g_BasicHTMLEntities[] = {
  3416. { '"', "&quot;", 6 },
  3417. { '\'', "&#039;", 6 },
  3418. { '<', "&lt;", 4 },
  3419. { '>', "&gt;", 4 },
  3420. { '&', "&amp;", 5 },
  3421. { 0, NULL, 0 } // sentinel for end of array
  3422. };
  3423. const static HtmlEntity_t g_WhitespaceEntities[] = {
  3424. { ' ', "&nbsp;", 6 },
  3425. { '\n', "<br>", 4 },
  3426. { 0, NULL, 0 } // sentinel for end of array
  3427. };
  3428. struct Tier1FullHTMLEntity_t
  3429. {
  3430. uchar32 uCharCode;
  3431. const char *pchEntity;
  3432. int nEntityLength;
  3433. };
  3434. #pragma warning( push )
  3435. #pragma warning( disable : 4428 ) // universal-character-name encountered in source
  3436. const Tier1FullHTMLEntity_t g_Tier1_FullHTMLEntities[] =
  3437. {
  3438. { L'"', "&quot;", 6 },
  3439. { L'\'', "&apos;", 6 },
  3440. { L'&', "&amp;", 5 },
  3441. { L'<', "&lt;", 4 },
  3442. { L'>', "&gt;", 4 },
  3443. { L' ', "&nbsp;", 6 },
  3444. { L'\u2122', "&trade;", 7 },
  3445. { L'\u00A9', "&copy;", 6 },
  3446. { L'\u00AE', "&reg;", 5 },
  3447. { L'\u2013', "&ndash;", 7 },
  3448. { L'\u2014', "&mdash;", 7 },
  3449. { L'\u20AC', "&euro;", 6 },
  3450. { L'\u00A1', "&iexcl;", 7 },
  3451. { L'\u00A2', "&cent;", 6 },
  3452. { L'\u00A3', "&pound;", 7 },
  3453. { L'\u00A4', "&curren;", 8 },
  3454. { L'\u00A5', "&yen;", 5 },
  3455. { L'\u00A6', "&brvbar;", 8 },
  3456. { L'\u00A7', "&sect;", 6 },
  3457. { L'\u00A8', "&uml;", 5 },
  3458. { L'\u00AA', "&ordf;", 6 },
  3459. { L'\u00AB', "&laquo;", 7 },
  3460. { L'\u00AC', "&not;", 8 },
  3461. { L'\u00AD', "&shy;", 5 },
  3462. { L'\u00AF', "&macr;", 6 },
  3463. { L'\u00B0', "&deg;", 5 },
  3464. { L'\u00B1', "&plusmn;", 8 },
  3465. { L'\u00B2', "&sup2;", 6 },
  3466. { L'\u00B3', "&sup3;", 6 },
  3467. { L'\u00B4', "&acute;", 7 },
  3468. { L'\u00B5', "&micro;", 7 },
  3469. { L'\u00B6', "&para;", 6 },
  3470. { L'\u00B7', "&middot;", 8 },
  3471. { L'\u00B8', "&cedil;", 7 },
  3472. { L'\u00B9', "&sup1;", 6 },
  3473. { L'\u00BA', "&ordm;", 6 },
  3474. { L'\u00BB', "&raquo;", 7 },
  3475. { L'\u00BC', "&frac14;", 8 },
  3476. { L'\u00BD', "&frac12;", 8 },
  3477. { L'\u00BE', "&frac34;", 8 },
  3478. { L'\u00BF', "&iquest;", 8 },
  3479. { L'\u00D7', "&times;", 7 },
  3480. { L'\u00F7', "&divide;", 8 },
  3481. { L'\u00C0', "&Agrave;", 8 },
  3482. { L'\u00C1', "&Aacute;", 8 },
  3483. { L'\u00C2', "&Acirc;", 7 },
  3484. { L'\u00C3', "&Atilde;", 8 },
  3485. { L'\u00C4', "&Auml;", 6 },
  3486. { L'\u00C5', "&Aring;", 7 },
  3487. { L'\u00C6', "&AElig;", 7 },
  3488. { L'\u00C7', "&Ccedil;", 8 },
  3489. { L'\u00C8', "&Egrave;", 8 },
  3490. { L'\u00C9', "&Eacute;", 8 },
  3491. { L'\u00CA', "&Ecirc;", 7 },
  3492. { L'\u00CB', "&Euml;", 6 },
  3493. { L'\u00CC', "&Igrave;", 8 },
  3494. { L'\u00CD', "&Iacute;", 8 },
  3495. { L'\u00CE', "&Icirc;", 7 },
  3496. { L'\u00CF', "&Iuml;", 6 },
  3497. { L'\u00D0', "&ETH;", 5 },
  3498. { L'\u00D1', "&Ntilde;", 8 },
  3499. { L'\u00D2', "&Ograve;", 8 },
  3500. { L'\u00D3', "&Oacute;", 8 },
  3501. { L'\u00D4', "&Ocirc;", 7 },
  3502. { L'\u00D5', "&Otilde;", 8 },
  3503. { L'\u00D6', "&Ouml;", 6 },
  3504. { L'\u00D8', "&Oslash;", 8 },
  3505. { L'\u00D9', "&Ugrave;", 8 },
  3506. { L'\u00DA', "&Uacute;", 8 },
  3507. { L'\u00DB', "&Ucirc;", 7 },
  3508. { L'\u00DC', "&Uuml;", 6 },
  3509. { L'\u00DD', "&Yacute;", 8 },
  3510. { L'\u00DE', "&THORN;", 7 },
  3511. { L'\u00DF', "&szlig;", 7 },
  3512. { L'\u00E0', "&agrave;", 8 },
  3513. { L'\u00E1', "&aacute;", 8 },
  3514. { L'\u00E2', "&acirc;", 7 },
  3515. { L'\u00E3', "&atilde;", 8 },
  3516. { L'\u00E4', "&auml;", 6 },
  3517. { L'\u00E5', "&aring;", 7 },
  3518. { L'\u00E6', "&aelig;", 7 },
  3519. { L'\u00E7', "&ccedil;", 8 },
  3520. { L'\u00E8', "&egrave;", 8 },
  3521. { L'\u00E9', "&eacute;", 8 },
  3522. { L'\u00EA', "&ecirc;", 7 },
  3523. { L'\u00EB', "&euml;", 6 },
  3524. { L'\u00EC', "&igrave;", 8 },
  3525. { L'\u00ED', "&iacute;", 8 },
  3526. { L'\u00EE', "&icirc;", 7 },
  3527. { L'\u00EF', "&iuml;", 6 },
  3528. { L'\u00F0', "&eth;", 5 },
  3529. { L'\u00F1', "&ntilde;", 8 },
  3530. { L'\u00F2', "&ograve;", 8 },
  3531. { L'\u00F3', "&oacute;", 8 },
  3532. { L'\u00F4', "&ocirc;", 7 },
  3533. { L'\u00F5', "&otilde;", 8 },
  3534. { L'\u00F6', "&ouml;", 6 },
  3535. { L'\u00F8', "&oslash;", 8 },
  3536. { L'\u00F9', "&ugrave;", 8 },
  3537. { L'\u00FA', "&uacute;", 8 },
  3538. { L'\u00FB', "&ucirc;", 7 },
  3539. { L'\u00FC', "&uuml;", 6 },
  3540. { L'\u00FD', "&yacute;", 8 },
  3541. { L'\u00FE', "&thorn;", 7 },
  3542. { L'\u00FF', "&yuml;", 6 },
  3543. { 0, NULL, 0 } // sentinel for end of array
  3544. };
  3545. #pragma warning( pop )
  3546. bool V_BasicHtmlEntityEncode( char *pDest, const int nDestSize, char const *pIn, const int nInSize, bool bPreserveWhitespace /*= false*/ )
  3547. {
  3548. Assert( nDestSize == 0 || pDest != NULL );
  3549. int iOutput = 0;
  3550. for ( int iInput = 0; iInput < nInSize; ++iInput )
  3551. {
  3552. bool bReplacementDone = false;
  3553. // See if the current char matches any of the basic entities
  3554. for ( int i = 0; g_BasicHTMLEntities[ i ].uCharCode != 0; ++i )
  3555. {
  3556. if ( pIn[ iInput ] == g_BasicHTMLEntities[ i ].uCharCode )
  3557. {
  3558. bReplacementDone = true;
  3559. for ( int j = 0; j < g_BasicHTMLEntities[ i ].nEntityLength; ++j )
  3560. {
  3561. if ( iOutput >= nDestSize - 1 )
  3562. {
  3563. pDest[ nDestSize - 1 ] = 0;
  3564. return false;
  3565. }
  3566. pDest[ iOutput++ ] = g_BasicHTMLEntities[ i ].pchEntity[ j ];
  3567. }
  3568. }
  3569. }
  3570. if ( bPreserveWhitespace && !bReplacementDone )
  3571. {
  3572. // See if the current char matches any of the basic entities
  3573. for ( int i = 0; g_WhitespaceEntities[ i ].uCharCode != 0; ++i )
  3574. {
  3575. if ( pIn[ iInput ] == g_WhitespaceEntities[ i ].uCharCode )
  3576. {
  3577. bReplacementDone = true;
  3578. for ( int j = 0; j < g_WhitespaceEntities[ i ].nEntityLength; ++j )
  3579. {
  3580. if ( iOutput >= nDestSize - 1 )
  3581. {
  3582. pDest[ nDestSize - 1 ] = 0;
  3583. return false;
  3584. }
  3585. pDest[ iOutput++ ] = g_WhitespaceEntities[ i ].pchEntity[ j ];
  3586. }
  3587. }
  3588. }
  3589. }
  3590. if ( !bReplacementDone )
  3591. {
  3592. pDest[ iOutput++ ] = pIn[ iInput ];
  3593. }
  3594. }
  3595. // Null terminate the output
  3596. pDest[ iOutput ] = 0;
  3597. return true;
  3598. }
  3599. bool V_HtmlEntityDecodeToUTF8( char *pDest, const int nDestSize, char const *pIn, const int nInSize )
  3600. {
  3601. Assert( nDestSize == 0 || pDest != NULL );
  3602. int iOutput = 0;
  3603. for ( int iInput = 0; iInput < nInSize && iOutput < nDestSize; ++iInput )
  3604. {
  3605. bool bReplacementDone = false;
  3606. if ( pIn[ iInput ] == '&' )
  3607. {
  3608. bReplacementDone = true;
  3609. uchar32 wrgchReplacement[ 2 ] = { 0, 0 };
  3610. char rgchReplacement[ 8 ];
  3611. rgchReplacement[ 0 ] = 0;
  3612. const char *pchEnd = Q_strstr( pIn + iInput + 1, ";" );
  3613. if ( pchEnd )
  3614. {
  3615. if ( iInput + 1 < nInSize && pIn[ iInput + 1 ] == '#' )
  3616. {
  3617. // Numeric
  3618. int iBase = 10;
  3619. int iOffset = 2;
  3620. if ( iInput + 3 < nInSize && pIn[ iInput + 2 ] == 'x' )
  3621. {
  3622. iBase = 16;
  3623. iOffset = 3;
  3624. }
  3625. wrgchReplacement[ 0 ] = (uchar32)V_strtoi64( pIn + iInput + iOffset, NULL, iBase );
  3626. if ( !Q_UTF32ToUTF8( wrgchReplacement, rgchReplacement, sizeof( rgchReplacement ) ) )
  3627. {
  3628. rgchReplacement[ 0 ] = 0;
  3629. }
  3630. }
  3631. else
  3632. {
  3633. // Lookup in map
  3634. const Tier1FullHTMLEntity_t *pFullEntities = g_Tier1_FullHTMLEntities;
  3635. for ( int i = 0; pFullEntities[ i ].uCharCode != 0; ++i )
  3636. {
  3637. if ( nInSize - iInput - 1 >= pFullEntities[ i ].nEntityLength )
  3638. {
  3639. if ( Q_memcmp( pIn + iInput, pFullEntities[ i ].pchEntity, pFullEntities[ i ].nEntityLength ) == 0 )
  3640. {
  3641. wrgchReplacement[ 0 ] = pFullEntities[ i ].uCharCode;
  3642. if ( !Q_UTF32ToUTF8( wrgchReplacement, rgchReplacement, sizeof( rgchReplacement ) ) )
  3643. {
  3644. rgchReplacement[ 0 ] = 0;
  3645. }
  3646. break;
  3647. }
  3648. }
  3649. }
  3650. }
  3651. // make sure we found a replacement. If not, skip
  3652. int cchReplacement = V_strlen( rgchReplacement );
  3653. if ( cchReplacement > 0 )
  3654. {
  3655. if ( (int)cchReplacement + iOutput < nDestSize )
  3656. {
  3657. for ( int i = 0; rgchReplacement[ i ] != 0; ++i )
  3658. {
  3659. pDest[ iOutput++ ] = rgchReplacement[ i ];
  3660. }
  3661. }
  3662. // Skip extra space that we passed
  3663. iInput += pchEnd - ( pIn + iInput );
  3664. }
  3665. else
  3666. {
  3667. bReplacementDone = false;
  3668. }
  3669. }
  3670. }
  3671. if ( !bReplacementDone )
  3672. {
  3673. pDest[ iOutput++ ] = pIn[ iInput ];
  3674. }
  3675. }
  3676. // Null terminate the output
  3677. if ( iOutput < nDestSize )
  3678. {
  3679. pDest[ iOutput ] = 0;
  3680. }
  3681. else
  3682. {
  3683. pDest[ nDestSize - 1 ] = 0;
  3684. }
  3685. return true;
  3686. }
  3687. static const char *g_pszSimpleBBCodeReplacements[] = {
  3688. "[b]", "<b>",
  3689. "[/b]", "</b>",
  3690. "[i]", "<i>",
  3691. "[/i]", "</i>",
  3692. "[u]", "<u>",
  3693. "[/u]", "</u>",
  3694. "[s]", "<s>",
  3695. "[/s]", "</s>",
  3696. "[code]", "<pre>",
  3697. "[/code]", "</pre>",
  3698. "[h1]", "<h1>",
  3699. "[/h1]", "</h1>",
  3700. "[list]", "<ul>",
  3701. "[/list]", "</ul>",
  3702. "[*]", "<li>",
  3703. "[/url]", "</a>",
  3704. "[img]", "<img src=\"",
  3705. "[/img]", "\"></img>",
  3706. };
  3707. // Converts BBCode tags to HTML tags
  3708. bool V_BBCodeToHTML( OUT_Z_CAP( nDestSize ) char *pDest, const int nDestSize, char const *pIn, const int nInSize )
  3709. {
  3710. Assert( nDestSize == 0 || pDest != NULL );
  3711. int iOutput = 0;
  3712. for ( int iInput = 0; iInput < nInSize && iOutput < nDestSize && pIn[ iInput ]; ++iInput )
  3713. {
  3714. if ( pIn[ iInput ] == '[' )
  3715. {
  3716. // check simple replacements
  3717. bool bFoundReplacement = false;
  3718. for ( int r = 0; r < ARRAYSIZE( g_pszSimpleBBCodeReplacements ); r += 2 )
  3719. {
  3720. int nBBCodeLength = V_strlen( g_pszSimpleBBCodeReplacements[ r ] );
  3721. if ( !V_strnicmp( &pIn[ iInput ], g_pszSimpleBBCodeReplacements[ r ], nBBCodeLength ) )
  3722. {
  3723. int nHTMLReplacementLength = V_strlen( g_pszSimpleBBCodeReplacements[ r + 1 ] );
  3724. for ( int c = 0; c < nHTMLReplacementLength && iOutput < nDestSize; c++ )
  3725. {
  3726. pDest[ iOutput ] = g_pszSimpleBBCodeReplacements[ r + 1 ][ c ];
  3727. iOutput++;
  3728. }
  3729. iInput += nBBCodeLength - 1;
  3730. bFoundReplacement = true;
  3731. break;
  3732. }
  3733. }
  3734. // check URL replacement
  3735. if ( !bFoundReplacement && !V_strnicmp( &pIn[ iInput ], "[url=", 5 ) && nDestSize - iOutput > 9 )
  3736. {
  3737. iInput += 5;
  3738. pDest[ iOutput++ ] = '<';
  3739. pDest[ iOutput++ ] = 'a';
  3740. pDest[ iOutput++ ] = ' ';
  3741. pDest[ iOutput++ ] = 'h';
  3742. pDest[ iOutput++ ] = 'r';
  3743. pDest[ iOutput++ ] = 'e';
  3744. pDest[ iOutput++ ] = 'f';
  3745. pDest[ iOutput++ ] = '=';
  3746. pDest[ iOutput++ ] = '\"';
  3747. // copy all characters up to the closing square bracket
  3748. while ( pIn[ iInput ] != ']' && iInput < nInSize && iOutput < nDestSize )
  3749. {
  3750. pDest[ iOutput++ ] = pIn[ iInput++ ];
  3751. }
  3752. if ( pIn[ iInput ] == ']' && nDestSize - iOutput > 2 )
  3753. {
  3754. pDest[ iOutput++ ] = '\"';
  3755. pDest[ iOutput++ ] = '>';
  3756. }
  3757. bFoundReplacement = true;
  3758. }
  3759. // otherwise, skip over everything up to the closing square bracket
  3760. if ( !bFoundReplacement )
  3761. {
  3762. while ( pIn[ iInput ] != ']' && iInput < nInSize )
  3763. {
  3764. iInput++;
  3765. }
  3766. }
  3767. }
  3768. else if ( pIn[ iInput ] == '\r' && pIn[ iInput + 1 ] == '\n' )
  3769. {
  3770. // convert carriage return and newline to a <br>
  3771. if ( nDestSize - iOutput > 4 )
  3772. {
  3773. pDest[ iOutput++ ] = '<';
  3774. pDest[ iOutput++ ] = 'b';
  3775. pDest[ iOutput++ ] = 'r';
  3776. pDest[ iOutput++ ] = '>';
  3777. }
  3778. iInput++;
  3779. }
  3780. else if ( pIn[ iInput ] == '\n' )
  3781. {
  3782. // convert newline to a <br>
  3783. if ( nDestSize - iOutput > 4 )
  3784. {
  3785. pDest[ iOutput++ ] = '<';
  3786. pDest[ iOutput++ ] = 'b';
  3787. pDest[ iOutput++ ] = 'r';
  3788. pDest[ iOutput++ ] = '>';
  3789. }
  3790. }
  3791. else
  3792. {
  3793. // copy character to destination
  3794. pDest[ iOutput++ ] = pIn[ iInput ];
  3795. }
  3796. }
  3797. // always terminate string
  3798. if ( iOutput >= nDestSize )
  3799. {
  3800. iOutput = nDestSize - 1;
  3801. }
  3802. pDest[ iOutput ] = 0;
  3803. return true;
  3804. }
  3805. //-----------------------------------------------------------------------------
  3806. // Purpose: returns true if a wide character is a "mean" space; that is,
  3807. // if it is technically a space or punctuation, but causes disruptive
  3808. // behavior when used in names, web pages, chat windows, etc.
  3809. //
  3810. // characters in this set are removed from the beginning and/or end of strings
  3811. // by Q_AggressiveStripPrecedingAndTrailingWhitespaceW()
  3812. //-----------------------------------------------------------------------------
  3813. bool V_IsMeanUnderscoreW( wchar_t wch )
  3814. {
  3815. bool bIsMean = false;
  3816. switch ( wch )
  3817. {
  3818. case L'\x005f': // low line (normal underscore)
  3819. case L'\xff3f': // fullwidth low line
  3820. case L'\x0332': // combining low line
  3821. bIsMean = true;
  3822. break;
  3823. default:
  3824. break;
  3825. }
  3826. return bIsMean;
  3827. }
  3828. //-----------------------------------------------------------------------------
  3829. // Purpose: returns true if a wide character is a "mean" space; that is,
  3830. // if it is technically a space or punctuation, but causes disruptive
  3831. // behavior when used in names, web pages, chat windows, etc.
  3832. //
  3833. // characters in this set are removed from the beginning and/or end of strings
  3834. // by Q_AggressiveStripPrecedingAndTrailingWhitespaceW()
  3835. //-----------------------------------------------------------------------------
  3836. bool V_IsMeanSpaceW( wchar_t wch )
  3837. {
  3838. bool bIsMean = false;
  3839. switch ( wch )
  3840. {
  3841. case L'\x0080': // PADDING CHARACTER
  3842. case L'\x0081': // HIGH OCTET PRESET
  3843. case L'\x0082': // BREAK PERMITTED HERE
  3844. case L'\x0083': // NO BREAK PERMITTED HERE
  3845. case L'\x0084': // INDEX
  3846. case L'\x0085': // NEXT LINE
  3847. case L'\x0086': // START OF SELECTED AREA
  3848. case L'\x0087': // END OF SELECTED AREA
  3849. case L'\x0088': // CHARACTER TABULATION SET
  3850. case L'\x0089': // CHARACTER TABULATION WITH JUSTIFICATION
  3851. case L'\x008A': // LINE TABULATION SET
  3852. case L'\x008B': // PARTIAL LINE FORWARD
  3853. case L'\x008C': // PARTIAL LINE BACKWARD
  3854. case L'\x008D': // REVERSE LINE FEED
  3855. case L'\x008E': // SINGLE SHIFT 2
  3856. case L'\x008F': // SINGLE SHIFT 3
  3857. case L'\x0090': // DEVICE CONTROL STRING
  3858. case L'\x0091': // PRIVATE USE
  3859. case L'\x0092': // PRIVATE USE
  3860. case L'\x0093': // SET TRANSMIT STATE
  3861. case L'\x0094': // CANCEL CHARACTER
  3862. case L'\x0095': // MESSAGE WAITING
  3863. case L'\x0096': // START OF PROTECTED AREA
  3864. case L'\x0097': // END OF PROTECED AREA
  3865. case L'\x0098': // START OF STRING
  3866. case L'\x0099': // SINGLE GRAPHIC CHARACTER INTRODUCER
  3867. case L'\x009A': // SINGLE CHARACTER INTRODUCER
  3868. case L'\x009B': // CONTROL SEQUENCE INTRODUCER
  3869. case L'\x009C': // STRING TERMINATOR
  3870. case L'\x009D': // OPERATING SYSTEM COMMAND
  3871. case L'\x009E': // PRIVACY MESSAGE
  3872. case L'\x009F': // APPLICATION PROGRAM COMMAND
  3873. case L'\x00A0': // NO-BREAK SPACE
  3874. case L'\x034F': // COMBINING GRAPHEME JOINER
  3875. case L'\x2000': // EN QUAD
  3876. case L'\x2001': // EM QUAD
  3877. case L'\x2002': // EN SPACE
  3878. case L'\x2003': // EM SPACE
  3879. case L'\x2004': // THICK SPACE
  3880. case L'\x2005': // MID SPACE
  3881. case L'\x2006': // SIX SPACE
  3882. case L'\x2007': // figure space
  3883. case L'\x2008': // PUNCTUATION SPACE
  3884. case L'\x2009': // THIN SPACE
  3885. case L'\x200A': // HAIR SPACE
  3886. case L'\x200B': // ZERO-WIDTH SPACE
  3887. case L'\x200C': // ZERO-WIDTH NON-JOINER
  3888. case L'\x200D': // ZERO WIDTH JOINER
  3889. case L'\x2028': // LINE SEPARATOR
  3890. case L'\x2029': // PARAGRAPH SEPARATOR
  3891. case L'\x202F': // NARROW NO-BREAK SPACE
  3892. case L'\x2060': // word joiner
  3893. case L'\xFEFF': // ZERO-WIDTH NO BREAK SPACE
  3894. case L'\xFFFC': // OBJECT REPLACEMENT CHARACTER
  3895. bIsMean = true;
  3896. break;
  3897. }
  3898. return bIsMean;
  3899. }
  3900. //-----------------------------------------------------------------------------
  3901. // Purpose: tell us if a Unicode character is deprecated
  3902. //
  3903. // See Unicode Technical Report #20: http://www.unicode.org/reports/tr20/
  3904. //
  3905. // Some characters are difficult or unreliably rendered. These characters eventually
  3906. // fell out of the Unicode standard, but are abusable by users. For example,
  3907. // setting "RIGHT-TO-LEFT OVERRIDE" without popping or undoing the action causes
  3908. // the layout instruction to bleed into following characters in HTML renderings,
  3909. // or upset layout calculations in vgui panels.
  3910. //
  3911. // Many games don't cope with these characters well, and end up providing opportunities
  3912. // for griefing others. For example, a user might join a game with a malformed player
  3913. // name and it turns out that player name can't be selected or typed into the admin
  3914. // console or UI to mute, kick, or ban the disruptive player.
  3915. //
  3916. // Ideally, we'd perfectly support these end-to-end but we never realistically will.
  3917. // The benefit of doing so far outweighs the cost, anyway.
  3918. //-----------------------------------------------------------------------------
  3919. bool V_IsDeprecatedW( wchar_t wch )
  3920. {
  3921. bool bIsDeprecated = false;
  3922. switch ( wch )
  3923. {
  3924. case L'\x202A': // LEFT-TO-RIGHT EMBEDDING
  3925. case L'\x202B': // RIGHT-TO-LEFT EMBEDDING
  3926. case L'\x202C': // POP DIRECTIONAL FORMATTING
  3927. case L'\x202D': // LEFT-TO-RIGHT OVERRIDE
  3928. case L'\x202E': // RIGHT-TO-LEFT OVERRIDE
  3929. case L'\x206A': // INHIBIT SYMMETRIC SWAPPING
  3930. case L'\x206B': // ACTIVATE SYMMETRIC SWAPPING
  3931. case L'\x206C': // INHIBIT ARABIC FORM SHAPING
  3932. case L'\x206D': // ACTIVATE ARABIC FORM SHAPING
  3933. case L'\x206E': // NATIONAL DIGIT SHAPES
  3934. case L'\x206F': // NOMINAL DIGIT SHAPES
  3935. bIsDeprecated = true;
  3936. }
  3937. return bIsDeprecated;
  3938. }
  3939. //-----------------------------------------------------------------------------
  3940. // returns true if the character is allowed in a DNS doman name, false otherwise
  3941. //-----------------------------------------------------------------------------
  3942. bool V_IsValidDomainNameCharacter( const char *pch, int *pAdvanceBytes )
  3943. {
  3944. if ( pAdvanceBytes )
  3945. *pAdvanceBytes = 0;
  3946. // We allow unicode in Domain Names without the an encoding unless it corresponds to
  3947. // a whitespace or control sequence or something we think is an underscore looking thing.
  3948. // If this character is the start of a UTF-8 sequence, try decoding it.
  3949. unsigned char ch = (unsigned char)*pch;
  3950. if ( ( ch & 0xC0 ) == 0xC0 )
  3951. {
  3952. uchar32 rgch32Buf;
  3953. bool bError = false;
  3954. int iAdvance = Q_UTF8ToUChar32( pch, rgch32Buf, bError );
  3955. if ( bError || iAdvance == 0 )
  3956. {
  3957. // Invalid UTF8 sequence, lets consider that invalid
  3958. return false;
  3959. }
  3960. if ( pAdvanceBytes )
  3961. *pAdvanceBytes = iAdvance;
  3962. if ( iAdvance )
  3963. {
  3964. // Ick. Want uchar32 versions of unicode character classification functions.
  3965. // Really would like Q_IsWhitespace32 and Q_IsNonPrintable32, but this is OK.
  3966. if ( rgch32Buf < 0x10000 && ( V_IsMeanSpaceW( (wchar_t)rgch32Buf ) || V_IsDeprecatedW( (wchar_t)rgch32Buf ) || V_IsMeanUnderscoreW( (wchar_t)rgch32Buf ) ) )
  3967. {
  3968. return false;
  3969. }
  3970. return true;
  3971. }
  3972. else
  3973. {
  3974. // Unreachable but would be invalid utf8
  3975. return false;
  3976. }
  3977. }
  3978. else
  3979. {
  3980. // Was not unicode
  3981. if ( pAdvanceBytes )
  3982. *pAdvanceBytes = 1;
  3983. // The only allowable non-unicode chars are a-z A-Z 0-9 and -
  3984. if ( ( ch >= 'a' && ch <= 'z' ) || ( ch >= 'A' && ch <= 'Z' ) || ( ch >= '0' && ch <= '9' ) || ch == '-' || ch == '.' )
  3985. return true;
  3986. return false;
  3987. }
  3988. }
  3989. //-----------------------------------------------------------------------------
  3990. // returns true if the character is allowed in a URL, false otherwise
  3991. //-----------------------------------------------------------------------------
  3992. bool V_IsValidURLCharacter( const char *pch, int *pAdvanceBytes )
  3993. {
  3994. if ( pAdvanceBytes )
  3995. *pAdvanceBytes = 0;
  3996. // We allow unicode in URLs unless it corresponds to a whitespace or control sequence.
  3997. // If this character is the start of a UTF-8 sequence, try decoding it.
  3998. unsigned char ch = (unsigned char)*pch;
  3999. if ( ( ch & 0xC0 ) == 0xC0 )
  4000. {
  4001. uchar32 rgch32Buf;
  4002. bool bError = false;
  4003. int iAdvance = Q_UTF8ToUChar32( pch, rgch32Buf, bError );
  4004. if ( bError || iAdvance == 0 )
  4005. {
  4006. // Invalid UTF8 sequence, lets consider that invalid
  4007. return false;
  4008. }
  4009. if ( pAdvanceBytes )
  4010. *pAdvanceBytes = iAdvance;
  4011. if ( iAdvance )
  4012. {
  4013. // Ick. Want uchar32 versions of unicode character classification functions.
  4014. // Really would like Q_IsWhitespace32 and Q_IsNonPrintable32, but this is OK.
  4015. if ( rgch32Buf < 0x10000 && ( V_IsMeanSpaceW( (wchar_t)rgch32Buf ) || V_IsDeprecatedW( (wchar_t)rgch32Buf ) ) )
  4016. {
  4017. return false;
  4018. }
  4019. return true;
  4020. }
  4021. else
  4022. {
  4023. // Unreachable but would be invalid utf8
  4024. return false;
  4025. }
  4026. }
  4027. else
  4028. {
  4029. // Was not unicode
  4030. if ( pAdvanceBytes )
  4031. *pAdvanceBytes = 1;
  4032. // Spaces, control characters, quotes, and angle brackets are not legal URL characters.
  4033. if ( ch <= 32 || ch == 127 || ch == '"' || ch == '<' || ch == '>' )
  4034. return false;
  4035. return true;
  4036. }
  4037. }
  4038. //-----------------------------------------------------------------------------
  4039. // Purpose: helper function to get a domain from a url
  4040. // Checks both standard url and steam://openurl/<url>
  4041. //-----------------------------------------------------------------------------
  4042. bool V_ExtractDomainFromURL( const char *pchURL, char *pchDomain, int cchDomain )
  4043. {
  4044. pchDomain[ 0 ] = 0;
  4045. static const char *k_pchSteamOpenUrl = "steam://openurl/";
  4046. static const char *k_pchSteamOpenUrlExt = "steam://openurl_external/";
  4047. const char *pchOpenUrlSuffix = StringAfterPrefix( pchURL, k_pchSteamOpenUrl );
  4048. if ( pchOpenUrlSuffix == NULL )
  4049. pchOpenUrlSuffix = StringAfterPrefix( pchURL, k_pchSteamOpenUrlExt );
  4050. if ( pchOpenUrlSuffix )
  4051. pchURL = pchOpenUrlSuffix;
  4052. if ( !pchURL || pchURL[ 0 ] == '\0' )
  4053. return false;
  4054. const char *pchDoubleSlash = strstr( pchURL, "//" );
  4055. // Put the domain and everything after into pchDomain.
  4056. // We'll find where to terminate it later.
  4057. if ( pchDoubleSlash )
  4058. {
  4059. // Skip the slashes
  4060. pchDoubleSlash += 2;
  4061. // If that's all there was, then there's no domain here. Bail.
  4062. if ( *pchDoubleSlash == '\0' )
  4063. {
  4064. return false;
  4065. }
  4066. // Skip any extra slashes
  4067. // ex: http:///steamcommunity.com/
  4068. while ( *pchDoubleSlash == '/' )
  4069. {
  4070. pchDoubleSlash++;
  4071. }
  4072. Q_strncpy( pchDomain, pchDoubleSlash, cchDomain );
  4073. }
  4074. else
  4075. {
  4076. // No double slash, so pchURL has no protocol.
  4077. Q_strncpy( pchDomain, pchURL, cchDomain );
  4078. }
  4079. // First character has to be valid
  4080. if ( *pchDomain == '?' || *pchDomain == '\0' )
  4081. {
  4082. return false;
  4083. }
  4084. // terminate the domain after the first non domain char
  4085. int iAdvance = 0;
  4086. int iStrLen = 0;
  4087. char cLast = 0;
  4088. while ( pchDomain[ iStrLen ] )
  4089. {
  4090. if ( !V_IsValidDomainNameCharacter( pchDomain + iStrLen, &iAdvance ) || ( pchDomain[ iStrLen ] == '.' && cLast == '.' ) )
  4091. {
  4092. pchDomain[ iStrLen ] = 0;
  4093. break;
  4094. }
  4095. cLast = pchDomain[ iStrLen ];
  4096. iStrLen += iAdvance;
  4097. }
  4098. return ( pchDomain[ 0 ] != 0 );
  4099. }
  4100. //-----------------------------------------------------------------------------
  4101. // Purpose: helper function to get a domain from a url
  4102. //-----------------------------------------------------------------------------
  4103. bool V_URLContainsDomain( const char *pchURL, const char *pchDomain )
  4104. {
  4105. char rgchExtractedDomain[ 2048 ];
  4106. if ( V_ExtractDomainFromURL( pchURL, rgchExtractedDomain, sizeof( rgchExtractedDomain ) ) )
  4107. {
  4108. // see if the last part of the domain matches what we extracted
  4109. int cchExtractedDomain = V_strlen( rgchExtractedDomain );
  4110. if ( pchDomain[ 0 ] == '.' )
  4111. {
  4112. ++pchDomain; // If the domain has a leading '.', skip it. The test below assumes there is none.
  4113. }
  4114. int cchDomain = V_strlen( pchDomain );
  4115. if ( cchDomain > cchExtractedDomain )
  4116. {
  4117. return false;
  4118. }
  4119. else if ( cchExtractedDomain >= cchDomain )
  4120. {
  4121. // If the actual domain is longer than what we're searching for, the character previous
  4122. // to the domain we're searching for must be a period
  4123. if ( cchExtractedDomain > cchDomain && rgchExtractedDomain[ cchExtractedDomain - cchDomain - 1 ] != '.' )
  4124. return false;
  4125. if ( 0 == V_stricmp( rgchExtractedDomain + cchExtractedDomain - cchDomain, pchDomain ) )
  4126. return true;
  4127. }
  4128. }
  4129. return false;
  4130. }
  4131. //-----------------------------------------------------------------------------
  4132. // Purpose: Strips all HTML tags not specified in rgszPreserveTags
  4133. // Does some additional formatting, like turning <li> into * when not preserving that tag,
  4134. // and auto-closing unclosed tags if they aren't specified in rgszNoCloseTags
  4135. //-----------------------------------------------------------------------------
  4136. void V_StripAndPreserveHTMLCore( CUtlBuffer *pbuffer, const char *pchHTML, const char **rgszPreserveTags, uint cPreserveTags, const char **rgszNoCloseTags, uint cNoCloseTags, uint cMaxResultSize )
  4137. {
  4138. uint cHTMLCur = 0;
  4139. bool bStripNewLines = true;
  4140. if ( cPreserveTags > 0 )
  4141. {
  4142. for ( uint i = 0; i < cPreserveTags; ++i )
  4143. {
  4144. if ( !Q_stricmp( rgszPreserveTags[ i ], "\n" ) )
  4145. bStripNewLines = false;
  4146. }
  4147. }
  4148. //state-
  4149. bool bInStrippedTag = false;
  4150. bool bInStrippedContentTag = false;
  4151. bool bInPreservedTag = false;
  4152. bool bInListItemTag = false;
  4153. bool bLastCharWasWhitespace = true; //set to true to strip leading whitespace
  4154. bool bInComment = false;
  4155. bool bInDoubleQuote = false;
  4156. bool bInSingleQuote = false;
  4157. int nPreTagDepth = 0;
  4158. CUtlVector< const char* > vecTagStack;
  4159. for ( int iContents = 0; pchHTML[ iContents ] != '\0' && cHTMLCur < cMaxResultSize; iContents++ )
  4160. {
  4161. char c = pchHTML[ iContents ];
  4162. // If we are entering a comment, flag as such and skip past the begin comment tag
  4163. const char *pchCur = &pchHTML[ iContents ];
  4164. if ( !Q_strnicmp( pchCur, "<!--", 4 ) )
  4165. {
  4166. bInComment = true;
  4167. iContents += 3;
  4168. continue;
  4169. }
  4170. // If we are in a comment, check if we are exiting
  4171. if ( bInComment )
  4172. {
  4173. if ( !Q_strnicmp( pchCur, "-->", 3 ) )
  4174. {
  4175. bInComment = false;
  4176. iContents += 2;
  4177. continue;
  4178. }
  4179. else
  4180. {
  4181. continue;
  4182. }
  4183. }
  4184. if ( bInStrippedTag || bInPreservedTag )
  4185. {
  4186. // we're inside a tag, keep stripping/preserving until we get to a >
  4187. if ( bInPreservedTag )
  4188. pbuffer->PutChar( c );
  4189. // While inside a tag, ignore ending > properties if they are inside a property value in "" or ''
  4190. if ( c == '"' )
  4191. {
  4192. if ( bInDoubleQuote )
  4193. bInDoubleQuote = false;
  4194. else
  4195. bInDoubleQuote = true;
  4196. }
  4197. if ( c == '\'' )
  4198. {
  4199. if ( bInSingleQuote )
  4200. bInSingleQuote = false;
  4201. else
  4202. bInSingleQuote = true;
  4203. }
  4204. if ( !bInDoubleQuote && !bInSingleQuote && c == '>' )
  4205. {
  4206. if ( bInPreservedTag )
  4207. bLastCharWasWhitespace = false;
  4208. bInPreservedTag = false;
  4209. bInStrippedTag = false;
  4210. }
  4211. }
  4212. else if ( bInStrippedContentTag )
  4213. {
  4214. if ( c == '<' && !Q_strnicmp( pchCur, "</script>", 9 ) )
  4215. {
  4216. bInStrippedContentTag = false;
  4217. iContents += 8;
  4218. continue;
  4219. }
  4220. else
  4221. {
  4222. continue;
  4223. }
  4224. }
  4225. else if ( c & 0x80 && !bInStrippedContentTag )
  4226. {
  4227. // start/continuation of a multibyte sequence, copy to output.
  4228. int nMultibyteRemaining = 0;
  4229. if ( ( c & 0xF8 ) == 0xF0 ) // first 5 bits are 11110
  4230. nMultibyteRemaining = 3;
  4231. else if ( ( c & 0xF0 ) == 0xE0 ) // first 4 bits are 1110
  4232. nMultibyteRemaining = 2;
  4233. else if ( ( c & 0xE0 ) == 0xC0 ) // first 3 bits are 110
  4234. nMultibyteRemaining = 1;
  4235. // cHTMLCur is in characters, so just +1
  4236. cHTMLCur++;
  4237. pbuffer->Put( pchCur, 1 + nMultibyteRemaining );
  4238. iContents += nMultibyteRemaining;
  4239. // Need to determine if we just added whitespace or not
  4240. wchar_t rgwch[ 3 ] = { 0 };
  4241. Q_UTF8CharsToWString( pchCur, 1, rgwch, sizeof( rgwch ) );
  4242. if ( !V_iswspace( rgwch[ 0 ] ) )
  4243. bLastCharWasWhitespace = false;
  4244. else
  4245. bLastCharWasWhitespace = true;
  4246. }
  4247. else
  4248. {
  4249. //not in a multibyte sequence- do our parsing/stripping
  4250. if ( c == '<' )
  4251. {
  4252. if ( !rgszPreserveTags || cPreserveTags == 0 )
  4253. {
  4254. //not preserving any tags, just strip it
  4255. bInStrippedTag = true;
  4256. }
  4257. else
  4258. {
  4259. //look ahead, is this our kind of tag?
  4260. bool bPreserve = false;
  4261. bool bEndTag = false;
  4262. const char *szTagStart = &pchHTML[ iContents + 1 ];
  4263. // if it's a close tag, skip the /
  4264. if ( *szTagStart == '/' )
  4265. {
  4266. bEndTag = true;
  4267. szTagStart++;
  4268. }
  4269. if ( Q_strnicmp( "script", szTagStart, 6 ) == 0 )
  4270. {
  4271. bInStrippedTag = true;
  4272. bInStrippedContentTag = true;
  4273. }
  4274. else
  4275. {
  4276. //see if this tag is one we want to preserve
  4277. for ( uint iTag = 0; iTag < cPreserveTags; iTag++ )
  4278. {
  4279. const char *szTag = rgszPreserveTags[ iTag ];
  4280. int cchTag = Q_strlen( szTag );
  4281. //make sure characters match, and are followed by some non-alnum char
  4282. // so "i" can match <i> or <i class=...>, but not <img>
  4283. if ( Q_strnicmp( szTag, szTagStart, cchTag ) == 0 && !V_isalnum( szTagStart[ cchTag ] ) )
  4284. {
  4285. bPreserve = true;
  4286. if ( bEndTag )
  4287. {
  4288. // ending a paragraph tag is optional. If we were expecting to find one, and didn't, skip
  4289. if ( Q_stricmp( szTag, "p" ) != 0 )
  4290. {
  4291. while ( vecTagStack.Count() > 0 && Q_stricmp( vecTagStack[ vecTagStack.Count() - 1 ], "p" ) == 0 )
  4292. {
  4293. vecTagStack.Remove( vecTagStack.Count() - 1 );
  4294. }
  4295. }
  4296. if ( vecTagStack.Count() > 0 && vecTagStack[ vecTagStack.Count() - 1 ] == szTag )
  4297. {
  4298. vecTagStack.Remove( vecTagStack.Count() - 1 );
  4299. if ( Q_stricmp( szTag, "pre" ) == 0 )
  4300. {
  4301. nPreTagDepth--;
  4302. if ( nPreTagDepth < 0 )
  4303. {
  4304. nPreTagDepth = 0;
  4305. }
  4306. }
  4307. }
  4308. else
  4309. {
  4310. // don't preserve this unbalanced tag. All open tags will be closed at the end of the blurb
  4311. bPreserve = false;
  4312. }
  4313. }
  4314. else
  4315. {
  4316. bool bNoCloseTag = false;
  4317. for ( uint iNoClose = 0; iNoClose < cNoCloseTags; iNoClose++ )
  4318. {
  4319. if ( Q_stricmp( szTag, rgszNoCloseTags[ iNoClose ] ) == 0 )
  4320. {
  4321. bNoCloseTag = true;
  4322. break;
  4323. }
  4324. }
  4325. if ( !bNoCloseTag )
  4326. {
  4327. vecTagStack.AddToTail( szTag );
  4328. if ( Q_stricmp( szTag, "pre" ) == 0 )
  4329. {
  4330. nPreTagDepth++;
  4331. }
  4332. }
  4333. }
  4334. break;
  4335. }
  4336. }
  4337. if ( !bPreserve )
  4338. {
  4339. bInStrippedTag = true;
  4340. }
  4341. else
  4342. {
  4343. bInPreservedTag = true;
  4344. pbuffer->PutChar( c );
  4345. }
  4346. }
  4347. }
  4348. if ( bInStrippedTag )
  4349. {
  4350. const char *szTagStart = &pchHTML[ iContents ];
  4351. if ( Q_strnicmp( szTagStart, "<li>", Q_strlen( "<li>" ) ) == 0 )
  4352. {
  4353. if ( bInListItemTag )
  4354. {
  4355. pbuffer->PutChar( ';' );
  4356. cHTMLCur++;
  4357. bInListItemTag = false;
  4358. }
  4359. if ( !bLastCharWasWhitespace )
  4360. {
  4361. pbuffer->PutChar( ' ' );
  4362. cHTMLCur++;
  4363. }
  4364. pbuffer->PutChar( '*' );
  4365. pbuffer->PutChar( ' ' );
  4366. cHTMLCur += 2;
  4367. bInListItemTag = true;
  4368. }
  4369. else if ( !bLastCharWasWhitespace )
  4370. {
  4371. if ( bInListItemTag )
  4372. {
  4373. char cLastChar = ' ';
  4374. if ( pbuffer->TellPut() > 0 )
  4375. {
  4376. cLastChar = ( ( (char*)pbuffer->Base() ) + pbuffer->TellPut() - 1 )[ 0 ];
  4377. }
  4378. if ( cLastChar != '.' && cLastChar != '?' && cLastChar != '!' )
  4379. {
  4380. pbuffer->PutChar( ';' );
  4381. cHTMLCur++;
  4382. }
  4383. bInListItemTag = false;
  4384. }
  4385. //we're decided to remove a tag, simulate a space in the original text
  4386. pbuffer->PutChar( ' ' );
  4387. cHTMLCur++;
  4388. }
  4389. bLastCharWasWhitespace = true;
  4390. }
  4391. }
  4392. else
  4393. {
  4394. //just a normal character, nothin' special.
  4395. if ( nPreTagDepth == 0 && V_isspace( c ) && ( bStripNewLines || c != '\n' ) )
  4396. {
  4397. if ( !bLastCharWasWhitespace )
  4398. {
  4399. //replace any block of whitespace with a single space
  4400. cHTMLCur++;
  4401. pbuffer->PutChar( ' ' );
  4402. bLastCharWasWhitespace = true;
  4403. }
  4404. // don't put anything for whitespace if the previous character was whitespace
  4405. // (effectively trimming all blocks of whitespace down to a single ' ')
  4406. }
  4407. else
  4408. {
  4409. cHTMLCur++;
  4410. pbuffer->PutChar( c );
  4411. bLastCharWasWhitespace = false;
  4412. }
  4413. }
  4414. }
  4415. }
  4416. if ( cHTMLCur >= cMaxResultSize )
  4417. {
  4418. // we terminated because the blurb was full. Add a '...' to the end
  4419. pbuffer->Put( "...", 3 );
  4420. }
  4421. //close any preserved tags that were open at the end.
  4422. FOR_EACH_VEC_BACK( vecTagStack, iTagStack )
  4423. {
  4424. pbuffer->PutChar( '<' );
  4425. pbuffer->PutChar( '/' );
  4426. pbuffer->Put( vecTagStack[ iTagStack ], Q_strlen( vecTagStack[ iTagStack ] ) );
  4427. pbuffer->PutChar( '>' );
  4428. }
  4429. // Null terminate
  4430. pbuffer->PutChar( '\0' );
  4431. }
  4432. //-----------------------------------------------------------------------------
  4433. // Purpose: Strips all HTML tags not specified in rgszPreserveTags
  4434. // Does some additional formatting, like turning <li> into * when not preserving that tag
  4435. //-----------------------------------------------------------------------------
  4436. void V_StripAndPreserveHTML( CUtlBuffer *pbuffer, const char *pchHTML, const char **rgszPreserveTags, uint cPreserveTags, uint cMaxResultSize )
  4437. {
  4438. const char *rgszNoCloseTags[] = { "br", "img" };
  4439. V_StripAndPreserveHTMLCore( pbuffer, pchHTML, rgszPreserveTags, cPreserveTags, rgszNoCloseTags, V_ARRAYSIZE( rgszNoCloseTags ), cMaxResultSize );
  4440. }