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.

230 lines
7.1 KiB

  1. //====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose: Implementation of China Government Censorship enforced on all user-generated strings
  4. //
  5. //=============================================================================
  6. //
  7. // Pre-compiled header (cbase.h in the game branch; none in Steam Client branch)
  8. //
  9. #include "cbase.h"
  10. //
  11. // Custom implementations for sharing code verbatim between Steam Client and the game branch
  12. //
  13. #ifdef CSTRIKE15
  14. static bool BannedWords_LoadFileIntoBuffer( char const *szFilename, CUtlBuffer &buf )
  15. {
  16. return g_pFullFileSystem->ReadFile( szFilename, "MOD", buf );
  17. }
  18. #else
  19. #include "utlbuffer.h"
  20. #include "utlmap.h"
  21. #include "filesystem.h"
  22. #include "filesystem_helpers.h"
  23. #include "tier1/fileio.h"
  24. static bool BannedWords_LoadFileIntoBuffer( char const *szFilename, CUtlBuffer &buf )
  25. {
  26. return LoadFileIntoBuffer( szFilename, buf, false );
  27. }
  28. #endif
  29. #include "bannedwords.h"
  30. #include "utlmemory.h"
  31. #include "utlbuffer.h"
  32. // NOTE: This has to be the last file included!
  33. #include "tier0/memdbgon.h"
  34. //////////////////////////////////////////////////////////////////////////
  35. //
  36. // Banned words dictionary
  37. //
  38. //////////////////////////////////////////////////////////////////////////
  39. class CBannedWordsDictionary
  40. {
  41. public:
  42. ~CBannedWordsDictionary() { m_mapExternalStrings.PurgeAndDeleteElements(); }
  43. bool InitFromFile( char const *szFilename );
  44. int CensorBannedWordsInplace( wchar_t *wsz ) const;
  45. char const * CensorExternalString( uint64 ullKey, char const *szExternalString );
  46. public:
  47. CUtlBuffer m_buf;
  48. struct ExternalStringCache_t
  49. {
  50. char m_chExternalString[256];
  51. char m_chCensoredString[256];
  52. };
  53. typedef CUtlMap< uint64, ExternalStringCache_t *, int, CDefLess< uint64 > > KeyStringMap_t;
  54. KeyStringMap_t m_mapExternalStrings;
  55. };
  56. bool CBannedWordsDictionary::InitFromFile( char const *szFilename )
  57. {
  58. if ( !BannedWords_LoadFileIntoBuffer( szFilename, m_buf ) )
  59. return false;
  60. if ( m_buf.TellPut() % 2 )
  61. return false;
  62. if ( m_buf.TellPut() <= 0x10000 )
  63. return false;
  64. if ( V_memcmp( m_buf.Base(), "BDR1", 4 ) )
  65. return false;
  66. return true;
  67. }
  68. int CBannedWordsDictionary::CensorBannedWordsInplace( wchar_t *wsz ) const
  69. {
  70. wchar_t const * const wszStartOfInput = wsz;
  71. int numReplaced = 0;
  72. for ( ; *wsz; ++ wsz )
  73. {
  74. wchar_t wchThisLetter = *wsz;
  75. if ( ( wchThisLetter >= 'A' ) && ( wchThisLetter <= 'Z' ) )
  76. wchThisLetter += 'a' - 'A'; // ensure input is also lowercase
  77. int32 nOffset = reinterpret_cast< int32 const * >( m_buf.Base() )[ wchThisLetter ];
  78. if ( !nOffset ) continue; // no banned words start with this char
  79. for ( wchar_t const *pwchBan = ( wchar_t const * ) ( ( byte const * )( m_buf.Base() ) + nOffset );
  80. pwchBan[1] == wchThisLetter; pwchBan += 1 + *pwchBan + 1 ) // (len wchar) + (actual number of wchars) + wnull-terminator
  81. {
  82. bool bWordBanned = true;
  83. bool bWordIsAllAlpha = true;
  84. { // if ( wcsncmp( wsz, pwchBan + 1, *pwchBan ) ) continue;
  85. wchar_t const *x = wsz;
  86. wchar_t const *y = pwchBan + 1; // dictionary word, compiled as lowercase
  87. for ( wchar_t numChecksRemaining = *pwchBan;
  88. numChecksRemaining -- > 0;
  89. ++ x, ++ y )
  90. {
  91. wchar_t wchx = *x;
  92. if ( ( wchx >= 'A' ) && ( wchx <= 'Z' ) )
  93. wchx += 'a' - 'A'; // ensure input is also lowercase
  94. if ( wchx != *y )
  95. {
  96. bWordBanned = false;
  97. break;
  98. }
  99. if ( !( ( wchx >= 'a' ) && ( wchx <= 'z' ) ) )
  100. bWordIsAllAlpha = false;
  101. }
  102. }
  103. if ( bWordBanned && bWordIsAllAlpha )
  104. {
  105. bool bBannedSequenceStartsWord = ( ( wsz <= wszStartOfInput ) || ( wsz[ -1 ] >= 0xFF ) || !V_isalpha( wsz[ -1 ] ) );
  106. bool bBannedSequenceEndsWord = ( !wsz[ *pwchBan ] || ( wsz[ *pwchBan ] >= 0xFF ) || !V_isalpha( wsz[ *pwchBan ] ) );
  107. // Must match the full word, not substring in English word (otherwise banned words like
  108. // "ri", "mb", "sm" cause censoring all around)
  109. if ( *pwchBan < 4 )
  110. bWordBanned = bBannedSequenceStartsWord && bBannedSequenceEndsWord;
  111. // Otherwise require that the banned word appears at the start or end of a word
  112. // so that it censored words like "bullshit" or "shitshow", but didn't censor
  113. // pro player name "pashaBiceps" containing "shabi"
  114. else
  115. bWordBanned = bBannedSequenceStartsWord || bBannedSequenceEndsWord;
  116. }
  117. if ( !bWordBanned )
  118. continue;
  119. // BANNED WORD!
  120. for ( int kk = *pwchBan; kk -- > 0; ++ wsz )
  121. {
  122. *wsz = L'*';
  123. ++ numReplaced;
  124. }
  125. -- wsz; // already advanced by number of asterisks inserted (-1 because the loop will ++)
  126. break;
  127. }
  128. }
  129. return numReplaced;
  130. }
  131. char const * CBannedWordsDictionary::CensorExternalString( uint64 ullKey, char const *szExternalString )
  132. {
  133. KeyStringMap_t::IndexType_t idx = m_mapExternalStrings.Find( ullKey );
  134. if ( idx == m_mapExternalStrings.InvalidIndex() )
  135. {
  136. ExternalStringCache_t *pNewEntry = new ExternalStringCache_t;
  137. V_memset( pNewEntry, 0, sizeof( ExternalStringCache_t ) );
  138. idx = m_mapExternalStrings.InsertOrReplace( ullKey, pNewEntry );
  139. }
  140. ExternalStringCache_t *pEntry = m_mapExternalStrings.Element( idx );
  141. if ( V_strcmp( pEntry->m_chExternalString, szExternalString ) )
  142. {
  143. V_strcpy_safe( pEntry->m_chExternalString, szExternalString );
  144. wchar_t *wch = ( wchar_t * ) stackalloc( sizeof(pEntry->m_chExternalString)*sizeof( wchar_t ) );
  145. V_UTF8ToUnicode( pEntry->m_chExternalString, wch, sizeof(pEntry->m_chExternalString)*sizeof( wchar_t ) );
  146. if ( CensorBannedWordsInplace( wch ) )
  147. V_UnicodeToUTF8( wch, pEntry->m_chCensoredString, sizeof( pEntry->m_chCensoredString ) );
  148. else
  149. V_strcpy_safe( pEntry->m_chCensoredString, pEntry->m_chExternalString );
  150. }
  151. return pEntry->m_chCensoredString;
  152. }
  153. //////////////////////////////////////////////////////////////////////////
  154. //
  155. // Banned words interface exposed to clients
  156. //
  157. //////////////////////////////////////////////////////////////////////////
  158. CBannedWords::~CBannedWords()
  159. {
  160. delete m_pDictionary;
  161. m_pDictionary = NULL;
  162. }
  163. bool CBannedWords::InitFromFile( char const *szFilename )
  164. {
  165. CBannedWordsDictionary *pDictionary = new CBannedWordsDictionary;
  166. if ( pDictionary->InitFromFile( szFilename ) )
  167. {
  168. delete m_pDictionary;
  169. m_pDictionary = pDictionary;
  170. return true;
  171. }
  172. else
  173. {
  174. delete pDictionary;
  175. return false;
  176. }
  177. }
  178. int CBannedWords::CensorBannedWordsInplace( wchar_t *wsz ) const
  179. {
  180. return ( m_pDictionary && wsz && *wsz ) ? m_pDictionary->CensorBannedWordsInplace( wsz ) : 0;
  181. }
  182. int CBannedWords::CensorBannedWordsInplace( char *sz ) const
  183. {
  184. if ( !m_pDictionary )
  185. return 0;
  186. if ( !sz || !*sz )
  187. return 0;
  188. int nLen = V_strlen( sz );
  189. wchar_t *wch = ( wchar_t * ) stackalloc( ( 1 + nLen )*sizeof( wchar_t ) );
  190. V_UTF8ToUnicode( sz, wch, ( 1 + nLen )*sizeof( wchar_t ) );
  191. int numCensored = m_pDictionary->CensorBannedWordsInplace( wch );
  192. if ( !numCensored )
  193. return 0;
  194. V_UnicodeToUTF8( wch, sz, nLen + 1 );
  195. return numCensored;
  196. }
  197. char const * CBannedWords::CensorExternalString( uint64 ullKey, char const *szExternalString ) const
  198. {
  199. return ( m_pDictionary && szExternalString && *szExternalString ) ? m_pDictionary->CensorExternalString( ullKey, szExternalString ) : szExternalString;
  200. }
  201. CBannedWords g_BannedWords;