//=============================================================================// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "core/stdafx.h" #include "tier0/memstd.h" #include "tier1/strtools.h" #include "vpc/keyvalues.h" #include "vpc/kvleaktrace.h" #include "vstdlib/keyvaluessystem.h" #include "mathlib/color.h" #include "rtech/stryder/stryder.h" #include "engine/sys_dll2.h" //----------------------------------------------------------------------------- // Purpose: Constructor // Input : *pszSetName - //----------------------------------------------------------------------------- KeyValues::KeyValues(const char* pszSetName) { TRACK_KV_ADD(this, pszSetName); Init(); SetName(pszSetName); } //----------------------------------------------------------------------------- // Purpose: Constructor // Input : *pszSetName - // *pszFirstKey - // *pszFirstValue - //----------------------------------------------------------------------------- KeyValues::KeyValues(const char* pszSsetName, const char* pszFirstKey, const char* pszFirstValue) { TRACK_KV_ADD(this, pszSsetName); Init(); SetName(pszSsetName); SetString(pszFirstKey, pszFirstValue); } //----------------------------------------------------------------------------- // Purpose: Constructor // Input : *pszSetName - // *pszFirstKey - // *pwszFirstValue - //----------------------------------------------------------------------------- KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, const wchar_t* pwszFirstValue) { TRACK_KV_ADD(this, pszSetName); Init(); SetName(pszSetName); SetWString(pszFirstKey, pwszFirstValue); } //----------------------------------------------------------------------------- // Purpose: Constructor // Input : *pszSetName - // *pszFirstKey - // iFirstValue - //----------------------------------------------------------------------------- KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue) { TRACK_KV_ADD(this, pszSetName); Init(); SetName(pszSetName); SetInt(pszFirstKey, iFirstValue); } //----------------------------------------------------------------------------- // Purpose: Constructor // Input : *pszSetName - // *pszFirstKey - // *pszFirstValue - // *pszSecondKey - // *pszSecondValue - //----------------------------------------------------------------------------- KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue, const char* pszSecondKey, const char* pszSecondValue) { TRACK_KV_ADD(this, pszSetName); Init(); SetName(pszSetName); SetString(pszFirstKey, pszFirstValue); SetString(pszSecondKey, pszSecondValue); } //----------------------------------------------------------------------------- // Purpose: Constructor // Input : *pszSetName - // *pszFirstKey - // iFirstValue - // *pszSecondKey - // iSecondValue - //----------------------------------------------------------------------------- KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue, const char* pszSecondKey, int iSecondValue) { TRACK_KV_ADD(this, pszSetName); Init(); SetName(pszSetName); SetInt(pszFirstKey, iFirstValue); SetInt(pszSecondKey, iSecondValue); } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- KeyValues::~KeyValues(void) { TRACK_KV_REMOVE(this); RemoveEverything(); } //----------------------------------------------------------------------------- // Purpose: Initialize member variables //----------------------------------------------------------------------------- void KeyValues::Init(void) { m_iKeyName = 0; m_iKeyNameCaseSensitive1 = 0; m_iKeyNameCaseSensitive2 = 0; m_iDataType = TYPE_NONE; m_pSub = nullptr; m_pPeer = nullptr; m_pChain = nullptr; m_sValue = nullptr; m_wsValue = nullptr; m_pValue = nullptr; m_bHasEscapeSequences = 0; } //----------------------------------------------------------------------------- // Purpose: Clear out all subkeys, and the current value //----------------------------------------------------------------------------- void KeyValues::Clear(void) { MemAllocSingleton()->Free(m_pSub); m_pSub = nullptr; m_iDataType = TYPE_NONE; } //----------------------------------------------------------------------------- // for backwards compat - we used to need this to force the free to run from the same DLL // as the alloc //----------------------------------------------------------------------------- void KeyValues::DeleteThis(void) { MemAllocSingleton()->Free(this); } //----------------------------------------------------------------------------- // Purpose: remove everything //----------------------------------------------------------------------------- void KeyValues::RemoveEverything(void) { KeyValues* dat; KeyValues* datNext = nullptr; for (dat = m_pSub; dat != nullptr; dat = datNext) { datNext = dat->m_pPeer; dat->m_pPeer = nullptr; MemAllocSingleton()->Free(dat); } for (dat = m_pPeer; dat && dat != this; dat = datNext) { datNext = dat->m_pPeer; dat->m_pPeer = nullptr; MemAllocSingleton()->Free(dat); } MemAllocSingleton()->Free(m_sValue); m_sValue = nullptr; MemAllocSingleton()->Free(m_wsValue); m_wsValue = nullptr; } //----------------------------------------------------------------------------- // Purpose: Find a keyValue, create it if it is not found. // Set bCreate to true to create the key if it doesn't already exist // (which ensures a valid pointer will be returned) // Input : *pszKeyName - // bCreate - // Output : *KeyValues //----------------------------------------------------------------------------- KeyValues* KeyValues::FindKey(const char* pszKeyName, bool bCreate) { static auto func = reinterpret_cast(KeyValues_FindKey); return func(this, pszKeyName, bCreate); } //----------------------------------------------------------------------------- // Purpose: Locate last child. Returns NULL if we have no children // Output : *KeyValues //----------------------------------------------------------------------------- KeyValues* KeyValues::FindLastSubKey(void) const { // No children? if (m_pSub == nullptr) return nullptr; // Scan for the last one KeyValues* pLastChild = m_pSub; while (pLastChild->m_pPeer) pLastChild = pLastChild->m_pPeer; return pLastChild; } //----------------------------------------------------------------------------- // Purpose: Adds a subkey. Make sure the subkey isn't a child of some other keyvalues // Input : *pSubKey - //----------------------------------------------------------------------------- void KeyValues::AddSubKey(KeyValues* pSubkey) { // Make sure the subkey isn't a child of some other keyvalues Assert(pSubkey != nullptr); Assert(pSubkey->m_pPeer == nullptr); // add into subkey list if (m_pSub == nullptr) { m_pSub = pSubkey; } else { KeyValues* pTempDat = m_pSub; while (pTempDat->GetNextKey() != nullptr) { pTempDat = pTempDat->GetNextKey(); } pTempDat->SetNextKey(pSubkey); } } //----------------------------------------------------------------------------- // Purpose: Remove a subkey from the list // Input : *pSubKey - //----------------------------------------------------------------------------- void KeyValues::RemoveSubKey(KeyValues* pSubKey) { if (!pSubKey) return; // check the list pointer if (m_pSub == pSubKey) { m_pSub = pSubKey->m_pPeer; } else { // look through the list KeyValues* kv = m_pSub; while (kv->m_pPeer) { if (kv->m_pPeer == pSubKey) { kv->m_pPeer = pSubKey->m_pPeer; break; } kv = kv->m_pPeer; } } pSubKey->m_pPeer = nullptr; } //----------------------------------------------------------------------------- // Purpose: Insert a subkey at index // Input : nIndex - // *pSubKey - //----------------------------------------------------------------------------- void KeyValues::InsertSubKey(int nIndex, KeyValues* pSubKey) { // Sub key must be valid and not part of another chain Assert(pSubKey && pSubKey->m_pPeer == nullptr); if (nIndex == 0) { pSubKey->m_pPeer = m_pSub; m_pSub = pSubKey; return; } else { int nCurrentIndex = 0; for (KeyValues* pIter = GetFirstSubKey(); pIter != nullptr; pIter = pIter->GetNextKey()) { ++nCurrentIndex; if (nCurrentIndex == nIndex) { pSubKey->m_pPeer = pIter->m_pPeer; pIter->m_pPeer = pSubKey; return; } } // Index is out of range if we get here Assert(0); return; } } //----------------------------------------------------------------------------- // Purpose: Checks if key contains a subkey // Input : *pSubKey - // Output : true if contains, false otherwise //----------------------------------------------------------------------------- bool KeyValues::ContainsSubKey(KeyValues* pSubKey) { for (KeyValues* pIter = GetFirstSubKey(); pIter != nullptr; pIter = pIter->GetNextKey()) { if (pSubKey == pIter) { return true; } } return false; } //----------------------------------------------------------------------------- // Purpose: Swaps existing subkey with another // Input : *pExistingSubkey - // *pNewSubKey - //----------------------------------------------------------------------------- void KeyValues::SwapSubKey(KeyValues* pExistingSubkey, KeyValues* pNewSubKey) { Assert(pExistingSubkey != nullptr && pNewSubKey != nullptr); // Make sure the new sub key isn't a child of some other keyvalues Assert(pNewSubKey->m_pPeer == nullptr); // Check the list pointer if (m_pSub == pExistingSubkey) { pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; pExistingSubkey->m_pPeer = nullptr; m_pSub = pNewSubKey; } else { // Look through the list KeyValues* kv = m_pSub; while (kv->m_pPeer) { if (kv->m_pPeer == pExistingSubkey) { pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; pExistingSubkey->m_pPeer = nullptr; kv->m_pPeer = pNewSubKey; break; } kv = kv->m_pPeer; } // Existing sub key should always be found, otherwise it's a bug in the calling code. Assert(kv->m_pPeer != nullptr); } } //----------------------------------------------------------------------------- // Purpose: Elides subkey // Input : *pSubKey - //----------------------------------------------------------------------------- void KeyValues::ElideSubKey(KeyValues* pSubKey) { // This pointer's "next" pointer needs to be fixed up when we elide the key KeyValues** ppPointerToFix = &m_pSub; for (KeyValues* pKeyIter = m_pSub; pKeyIter != nullptr; ppPointerToFix = &pKeyIter->m_pPeer, pKeyIter = pKeyIter->GetNextKey()) { if (pKeyIter == pSubKey) { if (pSubKey->m_pSub == nullptr) { // No children, simply remove the key *ppPointerToFix = pSubKey->m_pPeer; MemAllocSingleton()->Free(pSubKey); } else { *ppPointerToFix = pSubKey->m_pSub; // Attach the remainder of this chain to the last child of pSubKey KeyValues* pChildIter = pSubKey->m_pSub; while (pChildIter->m_pPeer != nullptr) { pChildIter = pChildIter->m_pPeer; } // Now points to the last child of pSubKey pChildIter->m_pPeer = pSubKey->m_pPeer; // Detach the node to be elided pSubKey->m_pSub = nullptr; pSubKey->m_pPeer = nullptr; MemAllocSingleton()->Free(pSubKey); } return; } } // Key not found; that's caller error. Assert(0); } //----------------------------------------------------------------------------- // Purpose: Check if a keyName has no value assigned to it. // Input : *pszKeyName - // Output : true on success, false otherwise //----------------------------------------------------------------------------- bool KeyValues::IsEmpty(const char* pszKeyName) { KeyValues* pKey = FindKey(pszKeyName, false); if (!pKey) return true; if (pKey->m_iDataType == TYPE_NONE && pKey->m_pSub == nullptr) return true; return false; } //----------------------------------------------------------------------------- // Purpose: gets the first true sub key // Output : *KeyValues //----------------------------------------------------------------------------- KeyValues* KeyValues::GetFirstTrueSubKey(void) const { Assert(this, "Member function called on NULL KeyValues"); KeyValues* pRet = this ? m_pSub : nullptr; while (pRet && pRet->m_iDataType != TYPE_NONE) pRet = pRet->m_pPeer; return pRet; } //----------------------------------------------------------------------------- // Purpose: gets the next true sub key // Output : *KeyValues //----------------------------------------------------------------------------- KeyValues* KeyValues::GetNextTrueSubKey(void) const { Assert(this, "Member function called on NULL KeyValues"); KeyValues* pRet = this ? m_pPeer : nullptr; while (pRet && pRet->m_iDataType != TYPE_NONE) pRet = pRet->m_pPeer; return pRet; } //----------------------------------------------------------------------------- // Purpose: gets the first value // Output : *KeyValues //----------------------------------------------------------------------------- KeyValues* KeyValues::GetFirstValue(void) const { Assert(this, "Member function called on NULL KeyValues"); KeyValues* pRet = this ? m_pSub : nullptr; while (pRet && pRet->m_iDataType == TYPE_NONE) pRet = pRet->m_pPeer; return pRet; } //----------------------------------------------------------------------------- // Purpose: gets the next value // Output : *KeyValues //----------------------------------------------------------------------------- KeyValues* KeyValues::GetNextValue(void) const { Assert(this, "Member function called on NULL KeyValues"); KeyValues* pRet = this ? m_pPeer : nullptr; while (pRet && pRet->m_iDataType == TYPE_NONE) pRet = pRet->m_pPeer; return pRet; } //----------------------------------------------------------------------------- // Purpose: Return the first subkey in the list //----------------------------------------------------------------------------- KeyValues* KeyValues::GetFirstSubKey() const { Assert(this, "Member function called on NULL KeyValues"); return this ? m_pSub : nullptr; } //----------------------------------------------------------------------------- // Purpose: Return the next subkey //----------------------------------------------------------------------------- KeyValues* KeyValues::GetNextKey() const { Assert(this, "Member function called on NULL KeyValues"); return this ? m_pPeer : nullptr; } //----------------------------------------------------------------------------- // Purpose: Get the name of the current key section // Output : const char* //----------------------------------------------------------------------------- const char* KeyValues::GetName(void) const { return KeyValuesSystem()->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2)); } //----------------------------------------------------------------------------- // Purpose: Get the integer value of a keyName. Default value is returned // if the keyName can't be found. // Input : *pszKeyName - // nDefaultValue - // Output : int //----------------------------------------------------------------------------- int KeyValues::GetInt(const char* pszKeyName, int iDefaultValue) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { switch (pKey->m_iDataType) { case TYPE_STRING: return atoi(pKey->m_sValue); case TYPE_WSTRING: return _wtoi(pKey->m_wsValue); case TYPE_FLOAT: return static_cast(pKey->m_flValue); case TYPE_UINT64: // can't convert, since it would lose data Assert(0); return 0; case TYPE_INT: case TYPE_PTR: default: return pKey->m_iValue; }; } return iDefaultValue; } //----------------------------------------------------------------------------- // Purpose: Get the integer value of a keyName. Default value is returned // if the keyName can't be found. // Input : *pszKeyName - // nDefaultValue - // Output : uint64_t //----------------------------------------------------------------------------- uint64_t KeyValues::GetUint64(const char* pszKeyName, uint64_t nDefaultValue) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { switch (pKey->m_iDataType) { case TYPE_STRING: { uint64_t uiResult = 0ull; sscanf(pKey->m_sValue, "%lld", &uiResult); return uiResult; } case TYPE_WSTRING: { uint64_t uiResult = 0ull; swscanf(pKey->m_wsValue, L"%lld", &uiResult); return uiResult; } case TYPE_FLOAT: return static_cast(pKey->m_flValue); case TYPE_UINT64: return *reinterpret_cast(pKey->m_sValue); case TYPE_PTR: return static_cast(reinterpret_cast(pKey->m_pValue)); case TYPE_INT: default: return pKey->m_iValue; }; } return nDefaultValue; } //----------------------------------------------------------------------------- // Purpose: Get the pointer value of a keyName. Default value is returned // if the keyName can't be found. // Input : *pszKeyName - // pDefaultValue - // Output : void* //----------------------------------------------------------------------------- void* KeyValues::GetPtr(const char* pszKeyName, void* pDefaultValue) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { switch (pKey->m_iDataType) { case TYPE_PTR: return pKey->m_pValue; case TYPE_WSTRING: case TYPE_STRING: case TYPE_FLOAT: case TYPE_INT: case TYPE_UINT64: default: return nullptr; }; } return pDefaultValue; } //----------------------------------------------------------------------------- // Purpose: Get the float value of a keyName. Default value is returned // if the keyName can't be found. // Input : *pszKeyName - // flDefaultValue - // Output : float //----------------------------------------------------------------------------- float KeyValues::GetFloat(const char* pszKeyName, float flDefaultValue) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { switch (pKey->m_iDataType) { case TYPE_STRING: return static_cast(atof(pKey->m_sValue)); case TYPE_WSTRING: return static_cast(_wtof(pKey->m_wsValue)); // no wtof case TYPE_FLOAT: return pKey->m_flValue; case TYPE_INT: return static_cast(pKey->m_iValue); case TYPE_UINT64: return static_cast((*(reinterpret_cast(pKey->m_sValue)))); case TYPE_PTR: default: return 0.0f; }; } return flDefaultValue; } //----------------------------------------------------------------------------- // Purpose: Get the string pointer of a keyName. Default value is returned // if the keyName can't be found. // // Input : *pszKeyName - // pszDefaultValue - // Output : const char* //----------------------------------------------------------------------------- const char* KeyValues::GetString(const char* pszKeyName, const char* pszDefaultValue) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { // convert the data to string form then return it char buf[64]; switch (pKey->m_iDataType) { case TYPE_FLOAT: snprintf(buf, sizeof(buf), "%f", pKey->m_flValue); SetString(pszKeyName, buf); break; case TYPE_PTR: snprintf(buf, sizeof(buf), "%lld", CastPtrToInt64(pKey->m_pValue)); SetString(pszKeyName, buf); break; case TYPE_INT: snprintf(buf, sizeof(buf), "%d", pKey->m_iValue); SetString(pszKeyName, buf); break; case TYPE_UINT64: snprintf(buf, sizeof(buf), "%lld", *(reinterpret_cast(pKey->m_sValue))); SetString(pszKeyName, buf); break; case TYPE_COLOR: snprintf(buf, sizeof(buf), "%d %d %d %d", pKey->m_Color[0], pKey->m_Color[1], pKey->m_Color[2], pKey->m_Color[3]); SetString(pszKeyName, buf); break; case TYPE_WSTRING: { // convert the string to char *, set it for future use, and return it char wideBuf[512]; int result = V_UnicodeToUTF8(pKey->m_wsValue, wideBuf, 512); if (result) { // note: this will copy wideBuf SetString(pszKeyName, wideBuf); } else { return pszDefaultValue; } break; } case TYPE_STRING: break; default: return pszDefaultValue; }; return pKey->m_sValue; } return pszDefaultValue; } //----------------------------------------------------------------------------- // Purpose: Get the wide string pointer of a keyName. Default value is returned // if the keyName can't be found. // // Input : *pszKeyName - // pwszDefaultValue - // Output : const wchar_t* //----------------------------------------------------------------------------- const wchar_t* KeyValues::GetWString(const char* pszKeyName, const wchar_t* pwszDefaultValue) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { wchar_t wbuf[64]; switch (pKey->m_iDataType) { case TYPE_FLOAT: swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%f", pKey->m_flValue); SetWString(pszKeyName, wbuf); break; case TYPE_PTR: swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%lld", static_cast(reinterpret_cast(pKey->m_pValue))); SetWString(pszKeyName, wbuf); break; case TYPE_INT: swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%d", pKey->m_iValue); SetWString(pszKeyName, wbuf); break; case TYPE_UINT64: { swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%lld", *(reinterpret_cast(pKey->m_sValue))); SetWString(pszKeyName, wbuf); } break; case TYPE_COLOR: swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%d %d %d %d", pKey->m_Color[0], pKey->m_Color[1], pKey->m_Color[2], pKey->m_Color[3]); SetWString(pszKeyName, wbuf); break; case TYPE_WSTRING: break; case TYPE_STRING: { size_t bufSize = strlen(pKey->m_sValue) + 1; wchar_t* pWBuf = MemAllocSingleton()->Alloc(bufSize); int result = V_UTF8ToUnicode(pKey->m_sValue, pWBuf, static_cast(bufSize * sizeof(wchar_t))); if (result >= 0) // may be a zero length string { SetWString(pszKeyName, pWBuf); } else { MemAllocSingleton()->Free(pWBuf); return pwszDefaultValue; } MemAllocSingleton()->Free(pWBuf); break; } default: return pwszDefaultValue; }; return reinterpret_cast(pKey->m_wsValue); } return pwszDefaultValue; } //----------------------------------------------------------------------------- // Purpose: Gets a color // Input : *pszKeyName - // &defaultColor - // Output : Color //----------------------------------------------------------------------------- Color KeyValues::GetColor(const char* pszKeyName, const Color& defaultColor) { Color color = defaultColor; KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) { if (pKey->m_iDataType == TYPE_COLOR) { color[0] = pKey->m_Color[0]; color[1] = pKey->m_Color[1]; color[2] = pKey->m_Color[2]; color[3] = pKey->m_Color[3]; } else if (pKey->m_iDataType == TYPE_FLOAT) { color[0] = static_cast(pKey->m_flValue); } else if (pKey->m_iDataType == TYPE_INT) { color[0] = static_cast(pKey->m_iValue); } else if (pKey->m_iDataType == TYPE_STRING) { // parse the colors out of the string float a = 0, b = 0, c = 0, d = 0; sscanf(pKey->m_sValue, "%f %f %f %f", &a, &b, &c, &d); color[0] = static_cast(a); color[1] = static_cast(b); color[2] = static_cast(c); color[3] = static_cast(d); } } return color; } //----------------------------------------------------------------------------- // Purpose: Get the data type of the value stored in a keyName // Input : *pszKeyName - //----------------------------------------------------------------------------- KeyValuesTypes_t KeyValues::GetDataType(const char* pszKeyName) { KeyValues* pKey = FindKey(pszKeyName, false); if (pKey) return static_cast(pKey->m_iDataType); return TYPE_NONE; } //----------------------------------------------------------------------------- // Purpose: Get the data type of the value stored in this keyName //----------------------------------------------------------------------------- KeyValuesTypes_t KeyValues::GetDataType(void) const { return static_cast(m_iDataType); } //----------------------------------------------------------------------------- // Purpose: Set the integer value of a keyName. // Input : *pszKeyName - // iValue - //----------------------------------------------------------------------------- void KeyValues::SetInt(const char* pszKeyName, int iValue) { KeyValues* pKey = FindKey(pszKeyName, true); if (pKey) { pKey->m_iValue = iValue; pKey->m_iDataType = TYPE_INT; } } //----------------------------------------------------------------------------- // Purpose: Set the integer value of a keyName. //----------------------------------------------------------------------------- void KeyValues::SetUint64(const char* pszKeyName, uint64_t nValue) { KeyValues* pKey = FindKey(pszKeyName, true); if (pKey) { // delete the old value MemAllocSingleton()->Free(pKey->m_sValue); // make sure we're not storing the WSTRING - as we're converting over to STRING MemAllocSingleton()->Free(pKey->m_wsValue); pKey->m_wsValue = nullptr; pKey->m_sValue = MemAllocSingleton()->Alloc(sizeof(uint64_t)); *(reinterpret_cast(pKey->m_sValue)) = nValue; pKey->m_iDataType = TYPE_UINT64; } } //----------------------------------------------------------------------------- // Purpose: Set the float value of a keyName. // Input : *pszKeyName - // flValue - //----------------------------------------------------------------------------- void KeyValues::SetFloat(const char* pszKeyName, float flValue) { KeyValues* pKey = FindKey(pszKeyName, true); if (pKey) { pKey->m_flValue = flValue; pKey->m_iDataType = TYPE_FLOAT; } } //----------------------------------------------------------------------------- // Purpose: Set the name value of a keyName. // Input : *pszSetName - //----------------------------------------------------------------------------- void KeyValues::SetName(const char* pszSetName) { HKeySymbol hCaseSensitiveKeyName = INVALID_KEY_SYMBOL, hCaseInsensitiveKeyName = INVALID_KEY_SYMBOL; hCaseSensitiveKeyName = KeyValuesSystem()->GetSymbolForStringCaseSensitive(hCaseInsensitiveKeyName, pszSetName); m_iKeyName = hCaseInsensitiveKeyName; SPLIT_3_BYTES_INTO_1_AND_2(m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2, hCaseSensitiveKeyName); } //----------------------------------------------------------------------------- // Purpose: Set the pointer value of a keyName. // Input : *pszKeyName - // *pValue - //----------------------------------------------------------------------------- void KeyValues::SetPtr(const char* pszKeyName, void* pValue) { KeyValues* pKey = FindKey(pszKeyName, true); if (pKey) { pKey->m_pValue = pValue; pKey->m_iDataType = TYPE_PTR; } } //----------------------------------------------------------------------------- // Purpose: Set the string value (internal) // Input : *pszValue - //----------------------------------------------------------------------------- void KeyValues::SetStringValue(char const* pszValue) { // delete the old value MemAllocSingleton()->Free(m_sValue); // make sure we're not storing the WSTRING - as we're converting over to STRING MemAllocSingleton()->Free(m_wsValue); m_wsValue = nullptr; if (!pszValue) { // ensure a valid value pszValue = ""; } // allocate memory for the new value and copy it in size_t len = strlen(pszValue); m_sValue = MemAllocSingleton()->Alloc(len + 1); memcpy(m_sValue, pszValue, len + 1); m_iDataType = TYPE_STRING; } //----------------------------------------------------------------------------- // Purpose: Sets this key's peer to the KeyValues passed in // Input : *pDat - //----------------------------------------------------------------------------- void KeyValues::SetNextKey(KeyValues* pDat) { m_pPeer = pDat; } //----------------------------------------------------------------------------- // Purpose: Set the string value of a keyName. // Input : *pszKeyName - // *pszValue - //----------------------------------------------------------------------------- void KeyValues::SetString(const char* pszKeyName, const char* pszValue) { if (KeyValues* pKey = FindKey(pszKeyName, true)) { pKey->SetStringValue(pszValue); } } //----------------------------------------------------------------------------- // Purpose: Set the string value of a keyName. // Input : *pszKeyName - // *pwszValue - //----------------------------------------------------------------------------- void KeyValues::SetWString(const char* pszKeyName, const wchar_t* pwszValue) { KeyValues* pKey = FindKey(pszKeyName, true); if (pKey) { // delete the old value MemAllocSingleton()->Free(pKey->m_wsValue); // make sure we're not storing the STRING - as we're converting over to WSTRING MemAllocSingleton()->Free(pKey->m_sValue); pKey->m_sValue = nullptr; if (!pwszValue) { // ensure a valid value pwszValue = L""; } // allocate memory for the new value and copy it in size_t len = wcslen(pwszValue); pKey->m_wsValue = MemAllocSingleton()->Alloc(len + 1); memcpy(pKey->m_wsValue, pwszValue, (len + 1) * sizeof(wchar_t)); pKey->m_iDataType = TYPE_WSTRING; } } //----------------------------------------------------------------------------- // Purpose: Sets a color // Input : *pszKeyName - // color - //----------------------------------------------------------------------------- void KeyValues::SetColor(const char* pszKeyName, Color color) { KeyValues* pKey = FindKey(pszKeyName, true); if (pKey) { pKey->m_iDataType = TYPE_COLOR; pKey->m_Color[0] = color[0]; pKey->m_Color[1] = color[1]; pKey->m_Color[2] = color[2]; pKey->m_Color[3] = color[3]; } } //----------------------------------------------------------------------------- // Purpose: // Input : &src - //----------------------------------------------------------------------------- void KeyValues::RecursiveCopyKeyValues(KeyValues& src) { // garymcthack - need to check this code for possible buffer overruns. m_iKeyName = src.m_iKeyName; m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1; m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2; if (!src.m_pSub) { m_iDataType = src.m_iDataType; char buf[256]; switch (src.m_iDataType) { case TYPE_NONE: break; case TYPE_STRING: if (src.m_sValue) { size_t len = strlen(src.m_sValue) + 1; m_sValue = MemAllocSingleton()->Alloc(len); strncpy(m_sValue, src.m_sValue, len); } break; case TYPE_INT: { m_iValue = src.m_iValue; snprintf(buf, sizeof(buf), "%d", m_iValue); size_t len = strlen(buf) + 1; m_sValue = MemAllocSingleton()->Alloc(len); strncpy(m_sValue, buf, len); } break; case TYPE_FLOAT: { m_flValue = src.m_flValue; snprintf(buf, sizeof(buf), "%f", m_flValue); size_t len = strlen(buf) + 1; m_sValue = MemAllocSingleton()->Alloc(len); strncpy(m_sValue, buf, len); } break; case TYPE_PTR: { m_pValue = src.m_pValue; } break; case TYPE_UINT64: { m_sValue = MemAllocSingleton()->Alloc(sizeof(uint64_t)); memcpy(m_sValue, src.m_sValue, sizeof(uint64_t)); } break; case TYPE_COLOR: { m_Color[0] = src.m_Color[0]; m_Color[1] = src.m_Color[1]; m_Color[2] = src.m_Color[2]; m_Color[3] = src.m_Color[3]; } break; default: { // do nothing . .what the heck is this? Assert(0); } break; } } // Handle the immediate child if (src.m_pSub) { m_pSub = MemAllocSingleton()->Alloc(sizeof(KeyValues)); TRACK_KV_ADD(m_pSub, nullptr); m_pSub->Init(); m_pSub->SetName(nullptr); m_pSub->RecursiveCopyKeyValues(*src.m_pSub); } // Handle the immediate peer if (src.m_pPeer) { m_pPeer = MemAllocSingleton()->Alloc(sizeof(KeyValues)); TRACK_KV_ADD(m_pPeer, nullptr); m_pPeer->Init(); m_pPeer->SetName(nullptr); m_pPeer->RecursiveCopyKeyValues(*src.m_pPeer); } } //----------------------------------------------------------------------------- // Purpose: Make a new copy of all subkeys, add them all to the passed-in keyvalues // Input : *pParent - //----------------------------------------------------------------------------- void KeyValues::CopySubkeys(KeyValues* pParent) const { // recursively copy subkeys // Also maintain ordering.... KeyValues* pPrev = nullptr; for (KeyValues* pSub = m_pSub; pSub != nullptr; pSub = pSub->m_pPeer) { // take a copy of the subkey KeyValues* pKey = pSub->MakeCopy(); // add into subkey list if (pPrev) { pPrev->m_pPeer = pKey; } else { pParent->m_pSub = pKey; } pKey->m_pPeer = nullptr; pPrev = pKey; } } //----------------------------------------------------------------------------- // Purpose: Makes a copy of the whole key-value pair set // Output : KeyValues* //----------------------------------------------------------------------------- KeyValues* KeyValues::MakeCopy(void) const { KeyValues* pNewKeyValue = MemAllocSingleton()->Alloc(sizeof(KeyValues)); TRACK_KV_ADD(pNewKeyValue, GetName()); pNewKeyValue->Init(); pNewKeyValue->SetName(GetName()); // copy data pNewKeyValue->m_iDataType = m_iDataType; switch (m_iDataType) { case TYPE_STRING: { if (m_sValue) { size_t len = strlen(m_sValue); Assert(!pNewKeyValue->m_sValue); pNewKeyValue->m_sValue = MemAllocSingleton()->Alloc(len + 1); memcpy(pNewKeyValue->m_sValue, m_sValue, len + 1); } } break; case TYPE_WSTRING: { if (m_wsValue) { size_t len = wcslen(m_wsValue); pNewKeyValue->m_wsValue = MemAllocSingleton()->Alloc(len + 1); memcpy(pNewKeyValue->m_wsValue, m_wsValue, len + 1 * sizeof(wchar_t)); } } break; case TYPE_INT: pNewKeyValue->m_iValue = m_iValue; break; case TYPE_FLOAT: pNewKeyValue->m_flValue = m_flValue; break; case TYPE_PTR: pNewKeyValue->m_pValue = m_pValue; break; case TYPE_COLOR: pNewKeyValue->m_Color[0] = m_Color[0]; pNewKeyValue->m_Color[1] = m_Color[1]; pNewKeyValue->m_Color[2] = m_Color[2]; pNewKeyValue->m_Color[3] = m_Color[3]; break; case TYPE_UINT64: pNewKeyValue->m_sValue = MemAllocSingleton()->Alloc(sizeof(uint64_t)); memcpy(pNewKeyValue->m_sValue, m_sValue, sizeof(uint64_t)); break; }; // recursively copy subkeys CopySubkeys(pNewKeyValue); return pNewKeyValue; } //----------------------------------------------------------------------------- // Purpose: Initializes the playlist //----------------------------------------------------------------------------- void KeyValues::InitPlaylists(void) { if (*g_pPlaylistKeyValues) { KeyValues* pPlaylists = (*g_pPlaylistKeyValues)->FindKey("Playlists", false); if (pPlaylists) { g_vAllPlaylists.clear(); for (KeyValues* pSubKey = pPlaylists->GetFirstTrueSubKey(); pSubKey != nullptr; pSubKey = pSubKey->GetNextTrueSubKey()) { g_vAllPlaylists.push_back(pSubKey->GetName()); // Get all playlists. } } } } //----------------------------------------------------------------------------- // Purpose: Initializes the filesystem paths //----------------------------------------------------------------------------- void KeyValues::InitFileSystem(void) { KeyValues* pMainFile = KeyValues::ReadKeyValuesFile(FileSystem(), "GameInfo.txt"); if (pMainFile) { KeyValues* pFileSystemInfo = pMainFile->FindKey("FileSystem", false); if (pFileSystemInfo) { KeyValues* pSearchPaths = pFileSystemInfo->FindKey("SearchPaths", false); if (pSearchPaths) { g_vGameInfoPaths.clear(); for (KeyValues* pSubKey = pSearchPaths->GetFirstValue(); pSubKey != nullptr; pSubKey = pSubKey->GetNextValue()) { string svValue = pSubKey->GetString(); StringReplace(svValue, GAMEINFOPATH_TOKEN, ""); StringReplace(svValue, BASESOURCEPATHS_TOKEN, ""); g_vGameInfoPaths.push_back(svValue); // Get all SearchPaths } } } } } //----------------------------------------------------------------------------- // Purpose: loads the playlists // Input : *szPlaylist - // Output : true on success, false on failure //----------------------------------------------------------------------------- bool KeyValues::LoadPlaylists(const char* pszPlaylist) { bool bResults = KeyValues_LoadPlaylists(pszPlaylist); KeyValues::InitPlaylists(); return bResults; } //----------------------------------------------------------------------------- // Purpose: parses the playlists // Input : *szPlaylist - // Output : true on success, false on failure //----------------------------------------------------------------------------- bool KeyValues::ParsePlaylists(const char* pszPlaylist) { memset(g_pMapVPKCache, '\0', 0x40); // Clear VPK cache to prevent crash while loading playlist. CHAR sPlaylistPath[] = "\x77\x27\x35\x2b\x2c\x6c\x2b\x2c\x2b"; PCHAR curr = sPlaylistPath; while (*curr) { *curr ^= 'B'; ++curr; } if (FileExists(sPlaylistPath)) { uint8_t verifyPlaylistIntegrity[] = // Very hacky way for alternative inline assembly for x64.. { 0x48, 0x8B, 0x45, 0x58, // mov rcx, playlist 0xC7, 0x00, 0x00, 0x00, // test playlist, playlist 0x00, 0x00 }; void* verifyPlaylistIntegrityFn = nullptr; VirtualAlloc(verifyPlaylistIntegrity, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); memcpy(&verifyPlaylistIntegrityFn, reinterpret_cast(verifyPlaylistIntegrity), 9); reinterpret_cast(verifyPlaylistIntegrityFn)(); } return KeyValues_ParsePlaylists(pszPlaylist); // Parse playlist. } //----------------------------------------------------------------------------- // Purpose: reads a keyvalues file // Input : *pFileSystem - // * pFileName - // Output : pointer to KeyValues object //----------------------------------------------------------------------------- KeyValues* KeyValues::ReadKeyValuesFile(CFileSystem_Stdio* pFileSystem, const char* pFileName) { static bool bInitFileSystem{}; if (!bInitFileSystem) { bInitFileSystem = true; KeyValues::InitFileSystem(); } return KeyValues_ReadKeyValuesFile(pFileSystem, pFileName); } /////////////////////////////////////////////////////////////////////////////// void CKeyValueSystem_Attach() { DetourAttach((LPVOID*)&KeyValues_LoadPlaylists, &KeyValues::LoadPlaylists); DetourAttach((LPVOID*)&KeyValues_ParsePlaylists, &KeyValues::ParsePlaylists); DetourAttach((LPVOID*)&KeyValues_ReadKeyValuesFile, &KeyValues::ReadKeyValuesFile); } void CKeyValueSystem_Detach() { DetourDetach((LPVOID*)&KeyValues_LoadPlaylists, &KeyValues::LoadPlaylists); DetourDetach((LPVOID*)&KeyValues_ParsePlaylists, &KeyValues::ParsePlaylists); DetourDetach((LPVOID*)&KeyValues_ReadKeyValuesFile, &KeyValues::ReadKeyValuesFile); } /////////////////////////////////////////////////////////////////////////////// inline KeyValues** g_pPlaylistKeyValues = nullptr; // Get the KeyValue for the playlist file. vector g_vAllPlaylists = { "<>" }; vector g_vGameInfoPaths = { "/" };