Team Fortress 2 Source Code as on 22/4/2020
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.

620 lines
19 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <vstdlib/ikeyvaluessystem.h>
  8. #include <keyvalues.h>
  9. #include "tier1/mempool.h"
  10. #include "utlsymbol.h"
  11. #include "utlmap.h"
  12. #include "tier0/threadtools.h"
  13. #include "tier1/memstack.h"
  14. #include "tier1/convar.h"
  15. #ifdef _PS3
  16. #include "ps3/ps3_core.h"
  17. #endif
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include <tier0/memdbgon.h>
  20. #ifdef NO_SBH // no need to pool if using tier0 small block heap
  21. #define KEYVALUES_USE_POOL 1
  22. #endif
  23. //
  24. // Defines platform-endian-specific macros:
  25. // MEM_4BYTES_AS_0_AND_3BYTES : present a 4 byte uint32 as a memory
  26. // layout where first memory byte is zero
  27. // and the other 3 bytes represent value
  28. // MEM_4BYTES_FROM_0_AND_3BYTES: unpack from memory with first zero byte
  29. // and 3 value bytes the original uint32 value
  30. //
  31. // used for efficiently reading/writing storing 3 byte values into memory
  32. // region immediately following a null-byte-terminated string, essentially
  33. // sharing the null-byte-terminator with the first memory byte
  34. //
  35. #if defined( PLAT_LITTLE_ENDIAN )
  36. // Number in memory has lowest-byte in front, use shifts to make it zero
  37. #define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) << 8 )
  38. #define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) >> 8 )
  39. #endif
  40. #if defined( PLAT_BIG_ENDIAN )
  41. // Number in memory has highest-byte in front, use masking to make it zero
  42. #define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) & 0x00FFFFFF )
  43. #define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) & 0x00FFFFFF )
  44. #endif
  45. //-----------------------------------------------------------------------------
  46. // Purpose: Central storage point for KeyValues memory and symbols
  47. //-----------------------------------------------------------------------------
  48. class CKeyValuesSystem : public IKeyValuesSystem
  49. {
  50. public:
  51. CKeyValuesSystem();
  52. ~CKeyValuesSystem();
  53. // registers the size of the KeyValues in the specified instance
  54. // so it can build a properly sized memory pool for the KeyValues objects
  55. // the sizes will usually never differ but this is for versioning safety
  56. void RegisterSizeofKeyValues(int size);
  57. // allocates/frees a KeyValues object from the shared mempool
  58. void *AllocKeyValuesMemory(int size);
  59. void FreeKeyValuesMemory(void *pMem);
  60. // symbol table access (used for key names)
  61. HKeySymbol GetSymbolForString( const char *name, bool bCreate );
  62. const char *GetStringForSymbol(HKeySymbol symbol);
  63. // returns the wide version of ansi, also does the lookup on #'d strings
  64. void GetLocalizedFromANSI( const char *ansi, wchar_t *outBuf, int unicodeBufferSizeInBytes);
  65. void GetANSIFromLocalized( const wchar_t *wchar, char *outBuf, int ansiBufferSizeInBytes );
  66. // for debugging, adds KeyValues record into global list so we can track memory leaks
  67. virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name);
  68. virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem);
  69. // set/get a value for keyvalues resolution symbol
  70. // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
  71. virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue );
  72. virtual bool GetKeyValuesExpressionSymbol( const char *name );
  73. // symbol table access from code with case-preserving requirements (used for key names)
  74. virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true );
  75. private:
  76. #ifdef KEYVALUES_USE_POOL
  77. CUtlMemoryPool *m_pMemPool;
  78. #endif
  79. int m_iMaxKeyValuesSize;
  80. // string hash table
  81. /*
  82. Here's the way key values system data structures are laid out:
  83. hash table with 2047 hash buckets:
  84. [0] { hash_item_t }
  85. [1]
  86. [2]
  87. ...
  88. each hash_item_t's stringIndex is an offset in m_Strings memory
  89. at that offset we store the actual null-terminated string followed
  90. by another 3 bytes for an alternative capitalization.
  91. These 3 trailing bytes are set to 0 if no alternative capitalization
  92. variants are present in the dictionary.
  93. These trailing 3 bytes are interpreted as stringIndex into m_Strings
  94. memory for the next alternative capitalization
  95. Getting a string value by HKeySymbol : constant time access at the
  96. string memory represented by stringIndex
  97. Getting a symbol for a string value:
  98. 1) compute the hash
  99. 2) start walking the hash-bucket using special version of stricmp
  100. until a case insensitive match is found
  101. 3a) for case-insensitive lookup return the found stringIndex
  102. 3b) for case-sensitive lookup keep walking the list of alternative
  103. capitalizations using strcmp until exact case match is found
  104. */
  105. CMemoryStack m_Strings;
  106. struct hash_item_t
  107. {
  108. int stringIndex;
  109. hash_item_t *next;
  110. };
  111. CUtlMemoryPool m_HashItemMemPool;
  112. CUtlVector<hash_item_t> m_HashTable;
  113. int CaseInsensitiveHash(const char *string, int iBounds);
  114. struct MemoryLeakTracker_t
  115. {
  116. int nameIndex;
  117. void *pMem;
  118. };
  119. static bool MemoryLeakTrackerLessFunc( const MemoryLeakTracker_t &lhs, const MemoryLeakTracker_t &rhs )
  120. {
  121. return lhs.pMem < rhs.pMem;
  122. }
  123. CUtlRBTree<MemoryLeakTracker_t, int> m_KeyValuesTrackingList;
  124. CUtlMap< HKeySymbol, bool > m_KvConditionalSymbolTable;
  125. CThreadFastMutex m_mutex;
  126. };
  127. // EXPOSE_SINGLE_INTERFACE(CKeyValuesSystem, IKeyValuesSystem, KEYVALUES_INTERFACE_VERSION);
  128. //-----------------------------------------------------------------------------
  129. // Instance singleton and expose interface to rest of code
  130. //-----------------------------------------------------------------------------
  131. static CKeyValuesSystem g_KeyValuesSystem;
  132. IKeyValuesSystem *KeyValuesSystem()
  133. {
  134. return &g_KeyValuesSystem;
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Constructor
  138. //-----------------------------------------------------------------------------
  139. CKeyValuesSystem::CKeyValuesSystem() :
  140. m_HashItemMemPool(sizeof(hash_item_t), 64, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool"),
  141. m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc),
  142. m_KvConditionalSymbolTable( DefLessFunc( HKeySymbol ) )
  143. {
  144. MEM_ALLOC_CREDIT();
  145. // initialize hash table
  146. m_HashTable.AddMultipleToTail(2047);
  147. for (int i = 0; i < m_HashTable.Count(); i++)
  148. {
  149. m_HashTable[i].stringIndex = 0;
  150. m_HashTable[i].next = NULL;
  151. }
  152. m_Strings.Init( "CKeyValuesSystem::m_Strings", 4*1024*1024, 64*1024, 0, 4 );
  153. // Make 0 stringIndex to never be returned, by allocating
  154. // and wasting minimal number of alignment bytes now:
  155. char *pszEmpty = ((char *)m_Strings.Alloc(1));
  156. *pszEmpty = 0;
  157. #ifdef KEYVALUES_USE_POOL
  158. m_pMemPool = NULL;
  159. #endif
  160. m_iMaxKeyValuesSize = sizeof(KeyValues);
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose: Destructor
  164. //-----------------------------------------------------------------------------
  165. CKeyValuesSystem::~CKeyValuesSystem()
  166. {
  167. #ifdef KEYVALUES_USE_POOL
  168. #ifdef _DEBUG
  169. // display any memory leaks
  170. if (m_pMemPool && m_pMemPool->Count() > 0)
  171. {
  172. DevMsg("Leaked KeyValues blocks: %d\n", m_pMemPool->Count());
  173. }
  174. // iterate all the existing keyvalues displaying their names
  175. for (int i = 0; i < m_KeyValuesTrackingList.MaxElement(); i++)
  176. {
  177. if (m_KeyValuesTrackingList.IsValidIndex(i))
  178. {
  179. DevMsg("\tleaked KeyValues(%s)\n", &m_Strings[m_KeyValuesTrackingList[i].nameIndex]);
  180. }
  181. }
  182. #endif
  183. delete m_pMemPool;
  184. #endif
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: registers the size of the KeyValues in the specified instance
  188. // so it can build a properly sized memory pool for the KeyValues objects
  189. // the sizes will usually never differ but this is for versioning safety
  190. //-----------------------------------------------------------------------------
  191. void CKeyValuesSystem::RegisterSizeofKeyValues(int size)
  192. {
  193. if (size > m_iMaxKeyValuesSize)
  194. {
  195. m_iMaxKeyValuesSize = size;
  196. }
  197. }
  198. static void KVLeak( char const *fmt, ... )
  199. {
  200. va_list argptr;
  201. char data[1024];
  202. va_start(argptr, fmt);
  203. V_vsnprintf(data, sizeof( data ), fmt, argptr);
  204. va_end(argptr);
  205. Msg( data );
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: allocates a KeyValues object from the shared mempool
  209. //-----------------------------------------------------------------------------
  210. void *CKeyValuesSystem::AllocKeyValuesMemory(int size)
  211. {
  212. #ifdef KEYVALUES_USE_POOL
  213. // allocate, if we don't have one yet
  214. if (!m_pMemPool)
  215. {
  216. m_pMemPool = new CUtlMemoryPool(m_iMaxKeyValuesSize, 1024, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_pMemPool" );
  217. m_pMemPool->SetErrorReportFunc( KVLeak );
  218. }
  219. return m_pMemPool->Alloc(size);
  220. #else
  221. return malloc( size );
  222. #endif
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: frees a KeyValues object from the shared mempool
  226. //-----------------------------------------------------------------------------
  227. void CKeyValuesSystem::FreeKeyValuesMemory(void *pMem)
  228. {
  229. #ifdef KEYVALUES_USE_POOL
  230. m_pMemPool->Free(pMem);
  231. #else
  232. free( pMem );
  233. #endif
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose: symbol table access (used for key names)
  237. //-----------------------------------------------------------------------------
  238. HKeySymbol CKeyValuesSystem::GetSymbolForString( const char *name, bool bCreate )
  239. {
  240. if ( !name )
  241. {
  242. return (-1);
  243. }
  244. AUTO_LOCK( m_mutex );
  245. MEM_ALLOC_CREDIT();
  246. int hash = CaseInsensitiveHash(name, m_HashTable.Count());
  247. int i = 0;
  248. hash_item_t *item = &m_HashTable[hash];
  249. while (1)
  250. {
  251. if (!stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex ))
  252. {
  253. return (HKeySymbol)item->stringIndex;
  254. }
  255. i++;
  256. if (item->next == NULL)
  257. {
  258. if ( !bCreate )
  259. {
  260. // not found
  261. return -1;
  262. }
  263. // we're not in the table
  264. if (item->stringIndex != 0)
  265. {
  266. // first item is used, an new item
  267. item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t));
  268. item = item->next;
  269. }
  270. // build up the new item
  271. item->next = NULL;
  272. int numStringBytes = strlen(name);
  273. char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 );
  274. if ( !pString )
  275. {
  276. Error( "Out of keyvalue string space" );
  277. return -1;
  278. }
  279. item->stringIndex = pString - (char *)m_Strings.GetBase();
  280. V_memcpy( pString, name, numStringBytes );
  281. * reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes
  282. return (HKeySymbol)item->stringIndex;
  283. }
  284. item = item->next;
  285. }
  286. // shouldn't be able to get here
  287. Assert(0);
  288. return (-1);
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Purpose: symbol table access (used for key names)
  292. //-----------------------------------------------------------------------------
  293. HKeySymbol CKeyValuesSystem::GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate )
  294. {
  295. if ( !name )
  296. {
  297. return (-1);
  298. }
  299. AUTO_LOCK( m_mutex );
  300. MEM_ALLOC_CREDIT();
  301. int hash = CaseInsensitiveHash(name, m_HashTable.Count());
  302. int numNameStringBytes = -1;
  303. int i = 0;
  304. hash_item_t *item = &m_HashTable[hash];
  305. while (1)
  306. {
  307. char *pCompareString = (char *)m_Strings.GetBase() + item->stringIndex;
  308. int iResult = _V_stricmp_NegativeForUnequal( name, pCompareString );
  309. if ( iResult == 0 )
  310. {
  311. // strings are exactly equal matching every letter's case
  312. hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
  313. return (HKeySymbol)item->stringIndex;
  314. }
  315. else if ( iResult > 0 )
  316. {
  317. // strings are equal in a case-insensitive compare, but have different case for some letters
  318. // Need to walk the case-resolving chain
  319. numNameStringBytes = V_strlen( pCompareString );
  320. uint32 *pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes );
  321. hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
  322. while ( int nAlternativeStringIndex = MEM_4BYTES_FROM_0_AND_3BYTES( *pnCaseResolveIndex ) )
  323. {
  324. pCompareString = (char *)m_Strings.GetBase() + nAlternativeStringIndex;
  325. int iResult = strcmp( name, pCompareString );
  326. if ( !iResult )
  327. {
  328. // found an exact match
  329. return (HKeySymbol)nAlternativeStringIndex;
  330. }
  331. // Keep traversing alternative case-resolving chain
  332. pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes );
  333. }
  334. // Reached the end of alternative case-resolving chain, pnCaseResolveIndex is pointing at 0 bytes
  335. // indicating no further alternative stringIndex
  336. if ( !bCreate )
  337. {
  338. // If we aren't interested in creating the actual string index,
  339. // then return symbol with default capitalization
  340. // NOTE: this is not correct value, but it cannot be used to create a new value anyway,
  341. // only for locating a pre-existing value and lookups are case-insensitive
  342. return (HKeySymbol)item->stringIndex;
  343. }
  344. else
  345. {
  346. char *pString = (char *)m_Strings.Alloc( numNameStringBytes + 1 + 3 );
  347. if ( !pString )
  348. {
  349. Error( "Out of keyvalue string space" );
  350. return -1;
  351. }
  352. int nNewAlternativeStringIndex = pString - (char *)m_Strings.GetBase();
  353. V_memcpy( pString, name, numNameStringBytes );
  354. * reinterpret_cast< uint32 * >( pString + numNameStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes
  355. *pnCaseResolveIndex = MEM_4BYTES_AS_0_AND_3BYTES( nNewAlternativeStringIndex ); // link previous spelling entry to the new entry
  356. return (HKeySymbol)nNewAlternativeStringIndex;
  357. }
  358. }
  359. i++;
  360. if (item->next == NULL)
  361. {
  362. if ( !bCreate )
  363. {
  364. // not found
  365. return -1;
  366. }
  367. // we're not in the table
  368. if (item->stringIndex != 0)
  369. {
  370. // first item is used, an new item
  371. item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t));
  372. item = item->next;
  373. }
  374. // build up the new item
  375. item->next = NULL;
  376. int numStringBytes = strlen(name);
  377. char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 );
  378. if ( !pString )
  379. {
  380. Error( "Out of keyvalue string space" );
  381. return -1;
  382. }
  383. item->stringIndex = pString - (char *)m_Strings.GetBase();
  384. V_memcpy( pString, name, numStringBytes );
  385. * reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes
  386. hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
  387. return (HKeySymbol)item->stringIndex;
  388. }
  389. item = item->next;
  390. }
  391. // shouldn't be able to get here
  392. Assert(0);
  393. return (-1);
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose: symbol table access
  397. //-----------------------------------------------------------------------------
  398. const char *CKeyValuesSystem::GetStringForSymbol(HKeySymbol symbol)
  399. {
  400. if ( symbol == -1 )
  401. {
  402. return "";
  403. }
  404. return ((char *)m_Strings.GetBase() + (size_t)symbol);
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: adds KeyValues record into global list so we can track memory leaks
  408. //-----------------------------------------------------------------------------
  409. void CKeyValuesSystem::AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name)
  410. {
  411. #ifdef _DEBUG
  412. // only track the memory leaks in debug builds
  413. MemoryLeakTracker_t item = { name, pMem };
  414. m_KeyValuesTrackingList.Insert(item);
  415. #endif
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Purpose: used to track memory leaks
  419. //-----------------------------------------------------------------------------
  420. void CKeyValuesSystem::RemoveKeyValuesFromMemoryLeakList(void *pMem)
  421. {
  422. #ifdef _DEBUG
  423. // only track the memory leaks in debug builds
  424. MemoryLeakTracker_t item = { 0, pMem };
  425. int index = m_KeyValuesTrackingList.Find(item);
  426. m_KeyValuesTrackingList.RemoveAt(index);
  427. #endif
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose: generates a simple hash value for a string
  431. //-----------------------------------------------------------------------------
  432. int CKeyValuesSystem::CaseInsensitiveHash(const char *string, int iBounds)
  433. {
  434. unsigned int hash = 0;
  435. for ( ; *string != 0; string++ )
  436. {
  437. if (*string >= 'A' && *string <= 'Z')
  438. {
  439. hash = (hash << 1) + (*string - 'A' + 'a');
  440. }
  441. else
  442. {
  443. hash = (hash << 1) + *string;
  444. }
  445. }
  446. return hash % iBounds;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: set/get a value for keyvalues resolution symbol
  450. // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
  451. //-----------------------------------------------------------------------------
  452. void CKeyValuesSystem::SetKeyValuesExpressionSymbol( const char *name, bool bValue )
  453. {
  454. if ( !name )
  455. return;
  456. if ( name[0] == '$' )
  457. ++ name;
  458. HKeySymbol hSym = GetSymbolForString( name, true ); // find or create symbol
  459. {
  460. AUTO_LOCK( m_mutex );
  461. m_KvConditionalSymbolTable.InsertOrReplace( hSym, bValue );
  462. }
  463. }
  464. bool CKeyValuesSystem::GetKeyValuesExpressionSymbol( const char *name )
  465. {
  466. if ( !name )
  467. return false;
  468. if ( name[0] == '$' )
  469. ++ name;
  470. HKeySymbol hSym = GetSymbolForString( name, false ); // find or create symbol
  471. if ( hSym != -1 )
  472. {
  473. AUTO_LOCK( m_mutex );
  474. CUtlMap< HKeySymbol, bool >::IndexType_t idx = m_KvConditionalSymbolTable.Find( hSym );
  475. if ( idx != m_KvConditionalSymbolTable.InvalidIndex() )
  476. {
  477. // Found the symbol value in conditional symbol table
  478. return m_KvConditionalSymbolTable.Element( idx );
  479. }
  480. }
  481. //
  482. // Fallback conditionals
  483. //
  484. if ( !V_stricmp( name, "GAMECONSOLESPLITSCREEN" ) )
  485. {
  486. #if defined( _GAMECONSOLE )
  487. return ( XBX_GetNumGameUsers() > 1 );
  488. #else
  489. return false;
  490. #endif
  491. }
  492. if ( !V_stricmp( name, "GAMECONSOLEGUEST" ) )
  493. {
  494. #if defined( _GAMECONSOLE )
  495. return ( XBX_GetPrimaryUserIsGuest() != 0 );
  496. #else
  497. return false;
  498. #endif
  499. }
  500. if ( !V_stricmp( name, "ENGLISH" ) ||
  501. !V_stricmp( name, "JAPANESE" ) ||
  502. !V_stricmp( name, "GERMAN" ) ||
  503. !V_stricmp( name, "FRENCH" ) ||
  504. !V_stricmp( name, "SPANISH" ) ||
  505. !V_stricmp( name, "ITALIAN" ) ||
  506. !V_stricmp( name, "KOREAN" ) ||
  507. !V_stricmp( name, "TCHINESE" ) ||
  508. !V_stricmp( name, "PORTUGUESE" ) ||
  509. !V_stricmp( name, "SCHINESE" ) ||
  510. !V_stricmp( name, "POLISH" ) ||
  511. !V_stricmp( name, "RUSSIAN" ) ||
  512. !V_stricmp( name, "TURKISH" ) )
  513. {
  514. // the language symbols are true if we are in that language
  515. // english is assumed when no language is present
  516. const char *pLanguageString;
  517. #ifdef _GAMECONSOLE
  518. pLanguageString = XBX_GetLanguageString();
  519. #else
  520. static ConVarRef cl_language( "cl_language" );
  521. pLanguageString = cl_language.GetString();
  522. #endif
  523. if ( !pLanguageString || !pLanguageString[0] )
  524. {
  525. pLanguageString = "english";
  526. }
  527. if ( !V_stricmp( name, pLanguageString ) )
  528. {
  529. return true;
  530. }
  531. else
  532. {
  533. return false;
  534. }
  535. }
  536. // very expensive, back door for DLC updates
  537. if ( !V_strnicmp( name, "CVAR_", 5 ) )
  538. {
  539. ConVarRef cvRef( name + 5 );
  540. if ( cvRef.IsValid() )
  541. return cvRef.GetBool();
  542. }
  543. // purposely warn on these to prevent syntax errors
  544. // need to get these fixed asap, otherwise unintended false behavior
  545. Warning( "KV Conditional: Unknown symbol %s\n", name );
  546. return false;
  547. }