From 9556b0520975d0846f0f97f8772bd64680e25db3 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 29 Jul 2022 17:30:05 +0200 Subject: [PATCH] CCommand improvements Add constructor and work-in-progress tokenizer. --- r5dev/tier1/characterset.cpp | 40 ++++++ r5dev/tier1/characterset.h | 43 ++++++ r5dev/tier1/cmd.cpp | 181 +++++++++++++++++++++++++- r5dev/tier1/cmd.h | 8 +- r5dev/vproj/clientsdk.vcxproj | 2 + r5dev/vproj/clientsdk.vcxproj.filters | 6 + r5dev/vproj/dedicated.vcxproj | 2 + r5dev/vproj/dedicated.vcxproj.filters | 6 + r5dev/vproj/gamesdk.vcxproj | 2 + r5dev/vproj/gamesdk.vcxproj.filters | 6 + 10 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 r5dev/tier1/characterset.cpp create mode 100644 r5dev/tier1/characterset.h diff --git a/r5dev/tier1/characterset.cpp b/r5dev/tier1/characterset.cpp new file mode 100644 index 00000000..62588144 --- /dev/null +++ b/r5dev/tier1/characterset.cpp @@ -0,0 +1,40 @@ +//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//============================================================================= + +#include +#include "characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +//#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: builds a simple lookup table of a group of important characters +// Input : *pParseGroup - pointer to the buffer for the group +// *pGroupString - null terminated list of characters to flag +//----------------------------------------------------------------------------- +void CharacterSetBuild(characterset_t* pSetBuffer, const char* pszSetString) +{ + int i = 0; + + // Test our pointers + if (!pSetBuffer || !pszSetString) + return; + + memset(pSetBuffer->set, 0, sizeof(pSetBuffer->set)); + + while (pszSetString[i]) + { + pSetBuffer->set[pszSetString[i]] = 1; + i++; + } +} diff --git a/r5dev/tier1/characterset.h b/r5dev/tier1/characterset.h new file mode 100644 index 00000000..81e54f66 --- /dev/null +++ b/r5dev/tier1/characterset.h @@ -0,0 +1,43 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Shared code for parsing / searching for characters in a string +// using lookup tables +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef CHARACTERSET_H +#define CHARACTERSET_H + +#ifdef _WIN32 +#pragma once +#endif + + +struct characterset_t +{ + char set[256]; +}; + + +// This is essentially a strpbrk() using a precalculated lookup table +//----------------------------------------------------------------------------- +// Purpose: builds a simple lookup table of a group of important characters +// Input : *pSetBuffer - pointer to the buffer for the group +// *pSetString - list of characters to flag +//----------------------------------------------------------------------------- +extern void CharacterSetBuild(characterset_t* pSetBuffer, const char* pSetString); + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pSetBuffer - pre-build group buffer +// character - character to lookup +// Output : int - 1 if the character was in the set +//----------------------------------------------------------------------------- +#define IN_CHARACTERSET( SetBuffer, character ) ((SetBuffer).set[(character)]) + + +#endif // CHARACTERSET_H diff --git a/r5dev/tier1/cmd.cpp b/r5dev/tier1/cmd.cpp index 5a332ac0..3eb19048 100644 --- a/r5dev/tier1/cmd.cpp +++ b/r5dev/tier1/cmd.cpp @@ -9,14 +9,169 @@ #include "tier0/memstd.h" #include "tier1/cmd.h" #include "tier1/cvar.h" +#include "tier1/characterset.h" #include "vstdlib/callback.h" //----------------------------------------------------------------------------- -// Purpose: returns max command lenght +// Global methods //----------------------------------------------------------------------------- -int CCommand::MaxCommandLength(void) +static characterset_t s_BreakSet; +static bool s_bBuiltBreakSet = false; + + +//----------------------------------------------------------------------------- +// Tokenizer class +//----------------------------------------------------------------------------- +CCommand::CCommand() { - return COMMAND_MAX_LENGTH - 1; + if (!s_bBuiltBreakSet) + { + s_bBuiltBreakSet = true; + CharacterSetBuild(&s_BreakSet, "{}()':"); + } + + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +// Input : nArgC - +// **ppArgV - +// source - +//----------------------------------------------------------------------------- +CCommand::CCommand(int nArgC, const char** ppArgV, cmd_source_t source) +{ + Assert(nArgC > 0); + + if (!s_bBuiltBreakSet) + { + s_bBuiltBreakSet = true; + CharacterSetBuild(&s_BreakSet, "{}()':"); + } + + Reset(); + + char* pBuf = m_pArgvBuffer; + char* pSBuf = m_pArgSBuffer; + m_nArgc = nArgC; + for (int i = 0; i < nArgC; ++i) + { + m_ppArgv[i] = pBuf; + int nLen = strlen(ppArgV[i]); + memcpy(pBuf, ppArgV[i], nLen + 1); + if (i == 0) + { + m_nArgv0Size = nLen; + } + pBuf += nLen + 1; + + bool bContainsSpace = strchr(ppArgV[i], ' ') != NULL; + if (bContainsSpace) + { + *pSBuf++ = '\"'; + } + memcpy(pSBuf, ppArgV[i], nLen); + pSBuf += nLen; + if (bContainsSpace) + { + *pSBuf++ = '\"'; + } + + if (i != nArgC - 1) + { + *pSBuf++ = ' '; + } + } + + m_nQueuedVal = source; +} + +//----------------------------------------------------------------------------- +// Purpose: tokenizer +// Input : *pCommand - +// source - +// *pBreakSet - +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool CCommand::Tokenize(const char* pCommand, cmd_source_t source, characterset_t* pBreakSet) +{ + /* !TODO (CUtlBuffer). + Reset(); + m_nQueuedVal = source; + + if (!pCommand) + return false; + + // Use default break set + if (!pBreakSet) + { + pBreakSet = &s_BreakSet; + } + + // Copy the current command into a temp buffer + // NOTE: This is here to avoid the pointers returned by DequeueNextCommand + // to become invalid by calling AddText. Is there a way we can avoid the memcpy? + int nLen = Q_strlen(pCommand); + if (nLen >= COMMAND_MAX_LENGTH - 1) + { + Warning(eDLL_T::ENGINE, "%s: Encountered command which overflows the tokenizer buffer.. Skipping!\n", __FUNCTION__); + return false; + } + + memcpy(m_pArgSBuffer, pCommand, nLen + 1); + + // Parse the current command into the current command buffer + CUtlBuffer bufParse(m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY); + int nArgvBufferSize = 0; + while (bufParse.IsValid() && (m_nArgc < COMMAND_MAX_ARGC)) + { + char* pArgvBuf = &m_pArgvBuffer[nArgvBufferSize]; + int nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize; + int nStartGet = bufParse.TellGet(); + int nSize = bufParse.ParseToken(pBreakSet, pArgvBuf, nMaxLen); + if (nSize < 0) + break; + + // Check for overflow condition + if (nMaxLen == nSize) + { + Reset(); + return false; + } + + if (m_nArgc == 1) + { + // Deal with the case where the arguments were quoted + m_nArgv0Size = bufParse.TellGet(); + bool bFoundEndQuote = m_pArgSBuffer[m_nArgv0Size - 1] == '\"'; + if (bFoundEndQuote) + { + --m_nArgv0Size; + } + m_nArgv0Size -= nSize; + Assert(m_nArgv0Size != 0); + + // The StartGet check is to handle this case: "foo"bar + // which will parse into 2 different args. ArgS should point to bar. + bool bFoundStartQuote = (m_nArgv0Size > nStartGet) && (m_pArgSBuffer[m_nArgv0Size - 1] == '\"'); + Assert(bFoundEndQuote == bFoundStartQuote); + if (bFoundStartQuote) + { + --m_nArgv0Size; + } + } + + m_ppArgv[m_nArgc++] = pArgvBuf; + if (m_nArgc >= COMMAND_MAX_ARGC) + { + Warning(eDLL_T::ENGINE, "%s: Encountered command which overflows the argument buffer.. Clamped!\n", __FUNCTION__); + } + + nArgvBufferSize += nSize + 1; + Assert(nArgvBufferSize <= COMMAND_MAX_LENGTH); + }*/ + + return true; } //----------------------------------------------------------------------------- @@ -76,6 +231,15 @@ const char* CCommand::operator[](int nIndex) const return Arg(nIndex); } +//----------------------------------------------------------------------------- +// Purpose: returns max command lenght +//----------------------------------------------------------------------------- +int CCommand::MaxCommandLength(void) const +{ + return COMMAND_MAX_LENGTH - 1; +} + + //----------------------------------------------------------------------------- // Purpose: return boolean depending on if the string only has digits in it // Input : svString - @@ -93,6 +257,17 @@ bool CCommand::HasOnlyDigits(int nIndex) const return true; } +//----------------------------------------------------------------------------- +// Purpose: reset +//----------------------------------------------------------------------------- +void CCommand::Reset() +{ + m_nArgc = 0; + m_nArgv0Size = 0; + m_pArgSBuffer[0] = 0; + m_nQueuedVal = cmd_source_t::kCommandSrcInvalid; +} + //----------------------------------------------------------------------------- // Purpose: construct/allocate //----------------------------------------------------------------------------- diff --git a/r5dev/tier1/cmd.h b/r5dev/tier1/cmd.h index 16e15131..cbd99a1e 100644 --- a/r5dev/tier1/cmd.h +++ b/r5dev/tier1/cmd.h @@ -1,4 +1,5 @@ #pragma once +#include "tier1/characterset.h" #include "public/include/iconcommand.h" //----------------------------------------------------------------------------- @@ -58,8 +59,9 @@ private: public: CCommand() = delete; + CCommand(int nArgC, const char** ppArgV, cmd_source_t source); + bool Tokenize(const char* pCommand, cmd_source_t source, characterset_t* pBreakSet); - int MaxCommandLength(); int64_t ArgC(void) const; const char** ArgV(void) const; const char* ArgS(void) const; @@ -67,10 +69,12 @@ public: const char* Arg(int nIndex) const; const char* operator[](int nIndex) const; + void Reset(); + int MaxCommandLength(void) const; bool HasOnlyDigits(int nIndex) const; private: - int m_nQueuedVal; + cmd_source_t m_nQueuedVal; int m_nArgc; int64_t m_nArgv0Size; char m_pArgSBuffer[COMMAND_MAX_LENGTH]; diff --git a/r5dev/vproj/clientsdk.vcxproj b/r5dev/vproj/clientsdk.vcxproj index f8dab16a..88fbc354 100644 --- a/r5dev/vproj/clientsdk.vcxproj +++ b/r5dev/vproj/clientsdk.vcxproj @@ -108,6 +108,7 @@ + @@ -468,6 +469,7 @@ + diff --git a/r5dev/vproj/clientsdk.vcxproj.filters b/r5dev/vproj/clientsdk.vcxproj.filters index 57a8dd5f..cbb1591a 100644 --- a/r5dev/vproj/clientsdk.vcxproj.filters +++ b/r5dev/vproj/clientsdk.vcxproj.filters @@ -561,6 +561,9 @@ sdk\game\client + + sdk\tier1 + @@ -1652,6 +1655,9 @@ sdk\public\include + + sdk\tier1 + diff --git a/r5dev/vproj/dedicated.vcxproj b/r5dev/vproj/dedicated.vcxproj index 6afe7a8b..087b6919 100644 --- a/r5dev/vproj/dedicated.vcxproj +++ b/r5dev/vproj/dedicated.vcxproj @@ -431,6 +431,7 @@ + @@ -545,6 +546,7 @@ + diff --git a/r5dev/vproj/dedicated.vcxproj.filters b/r5dev/vproj/dedicated.vcxproj.filters index 4c0f3069..f44177e0 100644 --- a/r5dev/vproj/dedicated.vcxproj.filters +++ b/r5dev/vproj/dedicated.vcxproj.filters @@ -1185,6 +1185,9 @@ sdk\public\include + + sdk\tier1 + @@ -1484,6 +1487,9 @@ sdk\server + + sdk\tier1 + diff --git a/r5dev/vproj/gamesdk.vcxproj b/r5dev/vproj/gamesdk.vcxproj index 092778df..7a7424ff 100644 --- a/r5dev/vproj/gamesdk.vcxproj +++ b/r5dev/vproj/gamesdk.vcxproj @@ -118,6 +118,7 @@ + @@ -496,6 +497,7 @@ + diff --git a/r5dev/vproj/gamesdk.vcxproj.filters b/r5dev/vproj/gamesdk.vcxproj.filters index eab4b2a9..2f5f468a 100644 --- a/r5dev/vproj/gamesdk.vcxproj.filters +++ b/r5dev/vproj/gamesdk.vcxproj.filters @@ -600,6 +600,9 @@ sdk\game\shared + + sdk\tier1 + @@ -1745,6 +1748,9 @@ sdk\public\include + + sdk\tier1 +