From d77b6c18c93d2aa2aefcf85cc8549737cfae72a4 Mon Sep 17 00:00:00 2001 From: nullifiedcat Date: Sun, 29 Jul 2018 18:50:08 +0300 Subject: [PATCH] settings impl + command autocomplete --- include/hack.hpp | 2 - src/CMakeLists.txt | 1 + src/MiscTemporary.cpp | 2 + src/core/cvwrapper.cpp | 2 +- src/hack.cpp | 10 - src/hacks/Misc.cpp | 2 +- src/sdk/CMakeLists.txt | 1 + src/sdk/UtlString.cpp | 767 +++++++++++++++++++++++++++++++ src/settings/CMakeLists.txt | 6 + src/settings/Manager.cpp | 44 ++ src/settings/Registered.cpp | 10 + src/settings/SettingCommands.cpp | 122 +++++ src/settings/Settings.cpp | 10 + src/settings/SettingsIO.cpp | 183 ++++++++ 14 files changed, 1148 insertions(+), 14 deletions(-) create mode 100644 src/sdk/UtlString.cpp create mode 100644 src/settings/CMakeLists.txt create mode 100644 src/settings/Manager.cpp create mode 100644 src/settings/Registered.cpp create mode 100644 src/settings/SettingCommands.cpp create mode 100644 src/settings/Settings.cpp create mode 100644 src/settings/SettingsIO.cpp diff --git a/include/hack.hpp b/include/hack.hpp index c5437817..ab5a3f25 100755 --- a/include/hack.hpp +++ b/include/hack.hpp @@ -34,6 +34,4 @@ void Initialize(); void Think(); void Shutdown(); -void CC_Cat(const CCommand &args); -extern ConCommand *c_Cat; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 291c9552..0e7ab9cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(hooks) add_subdirectory(reclasses) add_subdirectory(sdk) add_subdirectory(online) +add_subdirectory(settings) if(EnableVisuals) add_subdirectory(visual) diff --git a/src/MiscTemporary.cpp b/src/MiscTemporary.cpp index 03062a6a..1c45f2d5 100644 --- a/src/MiscTemporary.cpp +++ b/src/MiscTemporary.cpp @@ -14,6 +14,8 @@ Timer DelayTimer{}; float prevflow = 0.0f; int prevflowticks = 0; +bool *bSendPackets{ nullptr }; + settings::Bool crypt_chat{ "chat.crypto", "false" }; settings::Bool clean_screenshots{ "visual.clean-screenshots", "false" }; settings::Bool nolerp{ "misc.no-lerp", "false" }; diff --git a/src/core/cvwrapper.cpp b/src/core/cvwrapper.cpp index d6fc24ae..df38f95f 100644 --- a/src/core/cvwrapper.cpp +++ b/src/core/cvwrapper.cpp @@ -33,7 +33,7 @@ void CatCommand::Register() { char *name_c = new char[256]; char *help_c = new char[256]; - if (name.at(0) == '+' || name.at(0) == '-') + if (name.at(0) == '+' || name.at(0) == '-' || name == "cat") { strncpy(name_c, (name).c_str(), 255); } diff --git a/src/hack.cpp b/src/hack.cpp index e2b0d16b..b53a192c 100644 --- a/src/hack.cpp +++ b/src/hack.cpp @@ -108,15 +108,6 @@ void hack::ExecuteCommand(const std::string command) hack::command_stack().push(command); } -ConCommand *hack::c_Cat = 0; - -void hack::CC_Cat(const CCommand &args) -{ - g_ICvar->ConsoleColorPrintf(Color(255, 255, 255, 255), "cathook"); - g_ICvar->ConsoleColorPrintf(Color(0, 0, 255, 255), " by "); - g_ICvar->ConsoleColorPrintf(Color(255, 0, 0, 255), "nullifiedcat\n"); -} - /* This structure mirrors the one found in /usr/include/asm/ucontext.h */ typedef struct _sig_ucontext { @@ -244,7 +235,6 @@ free(logname);*/ InitClassTable(); BeginConVars(); - hack::c_Cat = CreateConCommand(CON_NAME, &hack::CC_Cat, "Info"); g_Settings.Init(); EndConVars(); diff --git a/src/hacks/Misc.cpp b/src/hacks/Misc.cpp index d0a16b5e..3b5e0616 100644 --- a/src/hacks/Misc.cpp +++ b/src/hacks/Misc.cpp @@ -32,7 +32,7 @@ static settings::Bool nopush_enabled{ "misc.no-push", "false" }; #if ENABLE_VISUALS static settings::Bool god_mode{ "misc.god-mode", "false" }; static settings::Bool debug_info{ "misc.debug-info", "false" }; -static settings::Bool no_homo{ "misc.no-homo", "false" }; +static settings::Bool no_homo{ "misc.no-homo", "true" }; static settings::Bool show_spectators{ "misc.show-spectators", "false" }; #endif diff --git a/src/sdk/CMakeLists.txt b/src/sdk/CMakeLists.txt index 427e3738..e986c91d 100644 --- a/src/sdk/CMakeLists.txt +++ b/src/sdk/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(cathook PRIVATE "${CMAKE_CURRENT_LIST_DIR}/MaterialSystemUtil.cpp" "${CMAKE_CURRENT_LIST_DIR}/tier1.cpp" "${CMAKE_CURRENT_LIST_DIR}/utlbuffer.cpp" + "${CMAKE_CURRENT_LIST_DIR}/UtlString.cpp" "${CMAKE_CURRENT_LIST_DIR}/netmessage.cpp") \ No newline at end of file diff --git a/src/sdk/UtlString.cpp b/src/sdk/UtlString.cpp new file mode 100644 index 00000000..0539387f --- /dev/null +++ b/src/sdk/UtlString.cpp @@ -0,0 +1,767 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#define __STDC_LIMIT_MACROS +#include + +#include +#include +#include + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Simple string class. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Either allocates or reallocates memory to the length +// +// Allocated space for length characters. It automatically adds space for the +// nul and the cached length at the start of the memory block. Will adjust +// m_pString and explicitly set the nul at the end before returning. +void *CUtlString::AllocMemory( uint32 length ) +{ + void *pMemoryBlock; + if ( m_pString ) + { + pMemoryBlock = realloc( m_pString, length + 1 ); + } + else + { + pMemoryBlock = malloc( length + 1 ); + } + m_pString = (char*)pMemoryBlock; + m_pString[ length ] = 0; + + return pMemoryBlock; +} + +//----------------------------------------------------------------------------- +void CUtlString::SetDirect( const char *pValue, int nChars ) +{ + if ( pValue && nChars > 0 ) + { + if ( pValue == m_pString ) + { + AssertMsg( nChars == Q_strlen(m_pString), "CUtlString::SetDirect does not support resizing strings in place." ); + return; // Do nothing. Realloc in AllocMemory might move pValue's location resulting in a bad memcpy. + } + + Assert( nChars <= Min( strnlen(pValue, nChars) + 1, nChars ) ); + AllocMemory( nChars ); + Q_memcpy( m_pString, pValue, nChars ); + } + else + { + Purge(); + } + +} + + +void CUtlString::Set( const char *pValue ) +{ + int length = pValue ? V_strlen( pValue ) : 0; + SetDirect( pValue, length ); +} + +// Sets the length (used to serialize into the buffer ) +void CUtlString::SetLength( int nLen ) +{ + if ( nLen > 0 ) + { +#ifdef _DEBUG + int prevLen = m_pString ? Length() : 0; +#endif + AllocMemory( nLen ); +#ifdef _DEBUG + if ( nLen > prevLen ) + { + V_memset( m_pString + prevLen, 0xEB, nLen - prevLen ); + } +#endif + } + else + { + Purge(); + } +} + +const char *CUtlString::Get( ) const +{ + if (!m_pString) + { + return ""; + } + return m_pString; +} + +char *CUtlString::GetForModify() +{ + if ( !m_pString ) + { + // In general, we optimise away small mallocs for empty strings + // but if you ask for the non-const bytes, they must be writable + // so we can't return "" here, like we do for the const version - jd + void *pMemoryBlock = malloc( 1 ); + m_pString = (char *)pMemoryBlock; + *m_pString = 0; + } + + return m_pString; +} + +char CUtlString::operator[]( int i ) const +{ + if ( !m_pString ) + return '\0'; + + if ( i >= Length() ) + { + return '\0'; + } + + return m_pString[i]; +} + +void CUtlString::Clear() +{ + Purge(); +} + +void CUtlString::Purge() +{ + free( m_pString ); + m_pString = NULL; +} + +bool CUtlString::IsEqual_CaseSensitive( const char *src ) const +{ + if ( !src ) + { + return (Length() == 0); + } + return ( V_strcmp( Get(), src ) == 0 ); +} + +bool CUtlString::IsEqual_CaseInsensitive( const char *src ) const +{ + if ( !src ) + { + return (Length() == 0); + } + return ( V_stricmp( Get(), src ) == 0 ); +} + + +void CUtlString::ToLower() +{ + if ( !m_pString ) + { + return; + } + + V_strlower( m_pString ); +} + +void CUtlString::ToUpper() +{ + if ( !m_pString ) + { + return; + } + + V_strupr( m_pString ); +} + +CUtlString &CUtlString::operator=( const CUtlString &src ) +{ + SetDirect( src.Get(), src.Length() ); + return *this; +} + +CUtlString &CUtlString::operator=( const char *src ) +{ + Set( src ); + return *this; +} + +bool CUtlString::operator==( const CUtlString &src ) const +{ + if ( IsEmpty() ) + { + if ( src.IsEmpty() ) + { + return true; + } + + return false; + } + else + { + if ( src.IsEmpty() ) + { + return false; + } + + return Q_strcmp( m_pString, src.m_pString ) == 0; + } +} + +CUtlString &CUtlString::operator+=( const CUtlString &rhs ) +{ + const int lhsLength( Length() ); + const int rhsLength( rhs.Length() ); + + if (!rhsLength) + { + return *this; + } + + const int requestedLength( lhsLength + rhsLength ); + + AllocMemory( requestedLength ); + Q_memcpy( m_pString + lhsLength, rhs.m_pString, rhsLength ); + + return *this; +} + +CUtlString &CUtlString::operator+=( const char *rhs ) +{ + const int lhsLength( Length() ); + const int rhsLength( V_strlen( rhs ) ); + const int requestedLength( lhsLength + rhsLength ); + + if (!requestedLength) + { + return *this; + } + + AllocMemory( requestedLength ); + Q_memcpy( m_pString + lhsLength, rhs, rhsLength ); + + return *this; +} + +CUtlString &CUtlString::operator+=( char c ) +{ + const int lhsLength( Length() ); + + AllocMemory( lhsLength + 1 ); + m_pString[ lhsLength ] = c; + + return *this; +} + +CUtlString &CUtlString::operator+=( int rhs ) +{ + Assert( sizeof( rhs ) == 4 ); + + char tmpBuf[ 12 ]; // Sufficient for a signed 32 bit integer [ -2147483648 to +2147483647 ] + V_snprintf( tmpBuf, sizeof( tmpBuf ), "%d", rhs ); + tmpBuf[ sizeof( tmpBuf ) - 1 ] = '\0'; + + return operator+=( tmpBuf ); +} + +CUtlString &CUtlString::operator+=( double rhs ) +{ + char tmpBuf[ 256 ]; // How big can doubles be??? Dunno. + V_snprintf( tmpBuf, sizeof( tmpBuf ), "%lg", rhs ); + tmpBuf[ sizeof( tmpBuf ) - 1 ] = '\0'; + + return operator+=( tmpBuf ); +} + +bool CUtlString::MatchesPattern( const CUtlString &Pattern, int nFlags ) const +{ + const char *pszSource = String(); + const char *pszPattern = Pattern.String(); + bool bExact = true; + + while( 1 ) + { + if ( ( *pszPattern ) == 0 ) + { + return ( (*pszSource ) == 0 ); + } + + if ( ( *pszPattern ) == '*' ) + { + pszPattern++; + + if ( ( *pszPattern ) == 0 ) + { + return true; + } + + bExact = false; + continue; + } + + int nLength = 0; + + while( ( *pszPattern ) != '*' && ( *pszPattern ) != 0 ) + { + nLength++; + pszPattern++; + } + + while( 1 ) + { + const char *pszStartPattern = pszPattern - nLength; + const char *pszSearch = pszSource; + + for( int i = 0; i < nLength; i++, pszSearch++, pszStartPattern++ ) + { + if ( ( *pszSearch ) == 0 ) + { + return false; + } + + if ( ( *pszSearch ) != ( *pszStartPattern ) ) + { + break; + } + } + + if ( pszSearch - pszSource == nLength ) + { + break; + } + + if ( bExact == true ) + { + return false; + } + + if ( ( nFlags & PATTERN_DIRECTORY ) != 0 ) + { + if ( ( *pszPattern ) != '/' && ( *pszSource ) == '/' ) + { + return false; + } + } + + pszSource++; + } + + pszSource += nLength; + } +} + + +int CUtlString::Format( const char *pFormat, ... ) +{ + va_list marker; + + va_start( marker, pFormat ); + int len = FormatV( pFormat, marker ); + va_end( marker ); + + return len; +} + +//-------------------------------------------------------------------------------------------------- +// This can be called from functions that take varargs. +//-------------------------------------------------------------------------------------------------- + +int CUtlString::FormatV( const char *pFormat, va_list marker ) +{ + char tmpBuf[ 4096 ]; //< Nice big 4k buffer, as much memory as my first computer had, a Radio Shack Color Computer + + //va_start( marker, pFormat ); + int len = V_vsprintf_safe( tmpBuf, pFormat, marker ); + //va_end( marker ); + Set( tmpBuf ); + return len; +} + +//----------------------------------------------------------------------------- +// Strips the trailing slash +//----------------------------------------------------------------------------- +void CUtlString::StripTrailingSlash() +{ + if ( IsEmpty() ) + return; + + int nLastChar = Length() - 1; + char c = m_pString[ nLastChar ]; + if ( c == '\\' || c == '/' ) + { + SetLength( nLastChar ); + } +} + +void CUtlString::FixSlashes( char cSeparator/*=CORRECT_PATH_SEPARATOR*/ ) +{ + if ( m_pString ) + { + V_FixSlashes( m_pString, cSeparator ); + } +} + +//----------------------------------------------------------------------------- +// Trim functions +//----------------------------------------------------------------------------- +void CUtlString::TrimLeft( char cTarget ) +{ + int nIndex = 0; + + if ( IsEmpty() ) + { + return; + } + + while( m_pString[nIndex] == cTarget ) + { + ++nIndex; + } + + // We have some whitespace to remove + if ( nIndex > 0 ) + { + memcpy( m_pString, &m_pString[nIndex], Length() - nIndex ); + SetLength( Length() - nIndex ); + } +} + + +void CUtlString::TrimLeft( const char *szTargets ) +{ + int i; + + if ( IsEmpty() ) + { + return; + } + + for( i = 0; m_pString[i] != 0; i++ ) + { + bool bWhitespace = false; + + for( int j = 0; szTargets[j] != 0; j++ ) + { + if ( m_pString[i] == szTargets[j] ) + { + bWhitespace = true; + break; + } + } + + if ( !bWhitespace ) + { + break; + } + } + + // We have some whitespace to remove + if ( i > 0 ) + { + memcpy( m_pString, &m_pString[i], Length() - i ); + SetLength( Length() - i ); + } +} + + +void CUtlString::TrimRight( char cTarget ) +{ + const int nLastCharIndex = Length() - 1; + int nIndex = nLastCharIndex; + + while ( nIndex >= 0 && m_pString[nIndex] == cTarget ) + { + --nIndex; + } + + // We have some whitespace to remove + if ( nIndex < nLastCharIndex ) + { + m_pString[nIndex + 1] = 0; + SetLength( nIndex + 2 ); + } +} + + +void CUtlString::TrimRight( const char *szTargets ) +{ + const int nLastCharIndex = Length() - 1; + int i; + + for( i = nLastCharIndex; i > 0; i-- ) + { + bool bWhitespace = false; + + for( int j = 0; szTargets[j] != 0; j++ ) + { + if ( m_pString[i] == szTargets[j] ) + { + bWhitespace = true; + break; + } + } + + if ( !bWhitespace ) + { + break; + } + } + + // We have some whitespace to remove + if ( i < nLastCharIndex ) + { + m_pString[i + 1] = 0; + SetLength( i + 2 ); + } +} + + +void CUtlString::Trim( char cTarget ) +{ + TrimLeft( cTarget ); + TrimRight( cTarget ); +} + + +void CUtlString::Trim( const char *szTargets ) +{ + TrimLeft( szTargets ); + TrimRight( szTargets ); +} + + +CUtlString CUtlString::Slice( int32 nStart, int32 nEnd ) const +{ + int length = Length(); + if ( length == 0 ) + { + return CUtlString(); + } + + if ( nStart < 0 ) + nStart = length - (-nStart % length); + else if ( nStart >= length ) + nStart = length; + + if ( nEnd == INT32_MAX ) + nEnd = length; + else if ( nEnd < 0 ) + nEnd = length - (-nEnd % length); + else if ( nEnd >= length ) + nEnd = length; + + if ( nStart >= nEnd ) + return CUtlString(); + + const char *pIn = String(); + + CUtlString ret; + ret.SetDirect( pIn + nStart, nEnd - nStart ); + return ret; +} + +// Grab a substring starting from the left or the right side. +CUtlString CUtlString::Left( int32 nChars ) const +{ + return Slice( 0, nChars ); +} + +CUtlString CUtlString::Right( int32 nChars ) const +{ + return Slice( -nChars ); +} + +CUtlString CUtlString::Replace( char cFrom, char cTo ) const +{ + if (!m_pString) + { + return CUtlString(); + } + + CUtlString ret = *this; + int len = ret.Length(); + for ( int i=0; i < len; i++ ) + { + if ( ret.m_pString[i] == cFrom ) + ret.m_pString[i] = cTo; + } + + return ret; +} + +CUtlString CUtlString::Replace( const char *pszFrom, const char *pszTo ) const +{ + Assert( pszTo ); // Can be 0 length, but not null + Assert( pszFrom && *pszFrom ); // Must be valid and have one character. + + + const char *pos = V_strstr( String(), pszFrom ); + if ( !pos ) + { + return *this; + } + + const char *pFirstFound = pos; + + // count number of search string + int nSearchCount = 0; + int nSearchLength = V_strlen( pszFrom ); + while ( pos ) + { + nSearchCount++; + int nSrcOffset = ( pos - String() ) + nSearchLength; + pos = V_strstr( String() + nSrcOffset, pszFrom ); + } + + // allocate the new string + int nReplaceLength = V_strlen( pszTo ); + int nAllocOffset = nSearchCount * ( nReplaceLength - nSearchLength ); + size_t srcLength = Length(); + CUtlString strDest; + size_t destLength = srcLength + nAllocOffset; + strDest.SetLength( destLength ); + + // find and replace the search string + pos = pFirstFound; + int nDestOffset = 0; + int nSrcOffset = 0; + while ( pos ) + { + // Found an instance + int nCurrentSearchOffset = pos - String(); + int nCopyLength = nCurrentSearchOffset - nSrcOffset; + V_strncpy( strDest.GetForModify() + nDestOffset, String() + nSrcOffset, nCopyLength + 1 ); + nDestOffset += nCopyLength; + V_strncpy( strDest.GetForModify() + nDestOffset, pszTo, nReplaceLength + 1 ); + nDestOffset += nReplaceLength; + + nSrcOffset = nCurrentSearchOffset + nSearchLength; + pos = V_strstr( String() + nSrcOffset, pszFrom ); + } + + // making sure that the left over string from the source is the same size as the left over dest buffer + Assert( destLength - nDestOffset == srcLength - nSrcOffset ); + if ( destLength - nDestOffset > 0 ) + { + V_strncpy( strDest.GetForModify() + nDestOffset, String() + nSrcOffset, destLength - nDestOffset + 1 ); + } + + return strDest; +} + +CUtlString CUtlString::AbsPath( const char *pStartingDir ) const +{ + char szNew[MAX_PATH]; + V_MakeAbsolutePath( szNew, sizeof( szNew ), this->String(), pStartingDir ); + return CUtlString( szNew ); +} + +CUtlString CUtlString::UnqualifiedFilename() const +{ + const char *pFilename = V_UnqualifiedFileName( this->String() ); + return CUtlString( pFilename ); +} + +CUtlString CUtlString::DirName() const +{ + CUtlString ret( this->String() ); + V_StripLastDir( (char*)ret.Get(), ret.Length() + 1 ); + V_StripTrailingSlash( (char*)ret.Get() ); + return ret; +} + +CUtlString CUtlString::StripExtension() const +{ + char szTemp[MAX_PATH]; + V_StripExtension( String(), szTemp, sizeof( szTemp ) ); + return CUtlString( szTemp ); +} + +CUtlString CUtlString::StripFilename() const +{ + const char *pFilename = V_UnqualifiedFileName( Get() ); // NOTE: returns 'Get()' on failure, never NULL + int nCharsToCopy = pFilename - Get(); + CUtlString result; + result.SetDirect( Get(), nCharsToCopy ); + result.StripTrailingSlash(); + return result; +} + +CUtlString CUtlString::GetBaseFilename() const +{ + char szTemp[MAX_PATH]; + V_FileBase( String(), szTemp, sizeof( szTemp ) ); + return CUtlString( szTemp ); +} + +CUtlString CUtlString::GetExtension() const +{ + char szTemp[MAX_PATH]; + V_ExtractFileExtension( String(), szTemp, sizeof( szTemp ) ); + return CUtlString( szTemp ); +} + + +CUtlString CUtlString::PathJoin( const char *pStr1, const char *pStr2 ) +{ + char szPath[MAX_PATH]; + V_ComposeFileName( pStr1, pStr2, szPath, sizeof( szPath ) ); + return CUtlString( szPath ); +} + +CUtlString CUtlString::operator+( const char *pOther ) const +{ + CUtlString s = *this; + s += pOther; + return s; +} + +CUtlString CUtlString::operator+( const CUtlString &other ) const +{ + CUtlString s = *this; + s += other; + return s; +} + +CUtlString CUtlString::operator+( int rhs ) const +{ + CUtlString ret = *this; + ret += rhs; + return ret; +} + +//----------------------------------------------------------------------------- +// Purpose: concatenate the provided string to our current content +//----------------------------------------------------------------------------- +void CUtlString::Append( const char *pchAddition ) +{ + (*this) += pchAddition; +} + +void CUtlString::Append( const char *pchAddition, int nChars ) +{ + nChars = Min( nChars, V_strlen( pchAddition ) ); + + const int lhsLength( Length() ); + const int rhsLength( nChars ); + const int requestedLength( lhsLength + rhsLength ); + + AllocMemory( requestedLength ); + const int allocatedLength( requestedLength ); + const int copyLength( allocatedLength - lhsLength < rhsLength ? allocatedLength - lhsLength : rhsLength ); + memcpy( GetForModify() + lhsLength, pchAddition, copyLength ); + m_pString[ allocatedLength ] = '\0'; +} + +// Shared static empty string. +const CUtlString &CUtlString::GetEmptyString() +{ + static const CUtlString s_emptyString; + + return s_emptyString; +} \ No newline at end of file diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt new file mode 100644 index 00000000..6a5966b0 --- /dev/null +++ b/src/settings/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(cathook PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/Manager.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Registered.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Settings.cpp" + "${CMAKE_CURRENT_LIST_DIR}/SettingCommands.cpp" + "${CMAKE_CURRENT_LIST_DIR}/SettingsIO.cpp") \ No newline at end of file diff --git a/src/settings/Manager.cpp b/src/settings/Manager.cpp new file mode 100644 index 00000000..d3292f51 --- /dev/null +++ b/src/settings/Manager.cpp @@ -0,0 +1,44 @@ +/* + Created on 02.07.18. +*/ + +#include + +namespace settings +{ + +Manager& Manager::instance() +{ + static Manager object{}; + return object; +} + +void Manager::add(IVariable &me, std::string name) +{ + if (registered.find(name) != registered.end()) + { + throw std::runtime_error("Double registering variable: " + name); + } + registered.emplace(name, me); +} + +IVariable *Manager::lookup(const std::string &string) +{ + auto it = registered.find(string); + if (it != registered.end()) + return &it->second.variable; + return nullptr; +} + +Manager::VariableDescriptor::VariableDescriptor(IVariable& variable): variable(variable) +{ + type = variable.getType(); + defaults = variable.toString(); +} + +bool Manager::VariableDescriptor::isChanged() +{ + return variable.toString() != defaults; +} + +} \ No newline at end of file diff --git a/src/settings/Registered.cpp b/src/settings/Registered.cpp new file mode 100644 index 00000000..cc320909 --- /dev/null +++ b/src/settings/Registered.cpp @@ -0,0 +1,10 @@ +/* + Created on 02.07.18. +*/ + +#include + +void settings::registerVariable(IVariable &variable, std::string name) +{ + Manager::instance().add(variable, name); +} diff --git a/src/settings/SettingCommands.cpp b/src/settings/SettingCommands.cpp new file mode 100644 index 00000000..b761c77e --- /dev/null +++ b/src/settings/SettingCommands.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +/* + Created on 29.07.18. +*/ + +static CatCommand cat("cat", "", [](const CCommand& args) { + if (args.ArgC() < 3) + { + g_ICvar->ConsolePrintf("Usage: cat [value]\n"); + return; + } + + auto variable = settings::Manager::instance().lookup(args.Arg(2)); + if (variable == nullptr) + { + g_ICvar->ConsolePrintf("Variable not found: %s\n", args.Arg(2)); + return; + } + + if (!strcmp(args.Arg(1), "set")) + { + if (args.ArgC() < 4) + { + g_ICvar->ConsolePrintf("Usage: cat \n"); + return; + } + variable->fromString(args.Arg(3)); + g_ICvar->ConsolePrintf("%s = \"%s\"\n", args.Arg(2), variable->toString().c_str()); + return; + } + else if (!strcmp(args.Arg(1), "get")) + { + g_ICvar->ConsolePrintf("%s = \"%s\"\n", args.Arg(2), variable->toString().c_str()); + return; + } + else + { + g_ICvar->ConsolePrintf("Usage: cat \n"); + return; + } +}); + +static std::vector sorted{}; + +static void getAndSortAllVariables() +{ + for (auto& v: settings::Manager::instance().registered) + { + sorted.push_back(v.first); + } + + std::sort(sorted.begin(), sorted.end()); + + logging::Info("Sorted %u variables\n", sorted.size()); +} + +static int completionCallback(const char *c_partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ]) +{ + std::string partial = c_partial; + std::string parts[2] {}; + auto j = 0u; + auto f = false; + int count = 0; + + for (auto i = 0u; i < partial.size() && j < 3; ++i) + { + auto space = (bool)isspace(partial[i]); + if (!space) + { + if (j) + parts[j - 1].push_back(partial[i]); + f = true; + } + + if (i == partial.size() - 1 || (f && space)) + { + if (space) + ++j; + f = false; + } + } + + // "" -> cat [get, set] + // "g" -> cat get + // "get " -> cat get + + logging::Info("%s|%s", parts[0].c_str(), parts[1].c_str()); + + if (parts[0].empty() || parts[1].empty() && (!parts[0].empty() && partial.back() != ' ')) + { + if (std::string("get").find(parts[0]) != std::string::npos) + snprintf(commands[count++], COMMAND_COMPLETION_ITEM_LENGTH, "cat get "); + if (std::string("set").find(parts[0]) != std::string::npos) + snprintf(commands[count++], COMMAND_COMPLETION_ITEM_LENGTH, "cat set "); + return count; + } + + for (const auto& s: sorted) + { + if (s.find(parts[1]) == 0) + { + auto variable = settings::Manager::instance().lookup(s); + if (variable) + { + snprintf(commands[count++], COMMAND_COMPLETION_ITEM_LENGTH - 1, "cat %s %s %s", parts[0].c_str(), s.c_str(), variable->toString().c_str()); + if (count == COMMAND_COMPLETION_MAXITEMS) + break; + } + } + } + return count; +} + +static InitRoutine init([]() { + getAndSortAllVariables(); + cat.cmd->m_bHasCompletionCallback = true; + cat.cmd->m_fnCompletionCallback = completionCallback; +}); \ No newline at end of file diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp new file mode 100644 index 00000000..10702403 --- /dev/null +++ b/src/settings/Settings.cpp @@ -0,0 +1,10 @@ +/* + Created on 01.07.18. +*/ + +#include + +namespace settings +{ + +} \ No newline at end of file diff --git a/src/settings/SettingsIO.cpp b/src/settings/SettingsIO.cpp new file mode 100644 index 00000000..5f439d61 --- /dev/null +++ b/src/settings/SettingsIO.cpp @@ -0,0 +1,183 @@ +/* + Created on 27.07.18. +*/ + +#include +#include +#include + +settings::SettingsWriter::SettingsWriter(settings::Manager &manager): manager(manager) +{ + +} + +bool settings::SettingsWriter::saveTo(std::string path, bool only_changed) +{ + this->only_changed = only_changed; + + stream.open(path, std::ios::out); + + if (stream.bad()) + return false; + + using pair_type = std::pair; + std::vector all_registered{}; + for (auto& v: settings::Manager::instance().registered) + { + if (!only_changed || v.second.isChanged()) + all_registered.emplace_back(std::make_pair(v.first, &v.second.variable)); + } + + std::sort(all_registered.begin(), all_registered.end(), [](const pair_type& a, const pair_type& b) -> bool { + return a.first.compare(b.first) < 0; + }); + + for (auto& v: all_registered) + { + write(v.first, v.second); + } + + return true; +} + +void settings::SettingsWriter::write(std::string name, IVariable *variable) +{ + writeEscaped(name); + stream << "="; + writeEscaped(variable->toString()); + stream << '\n'; +} + +void settings::SettingsWriter::writeEscaped(std::string str) +{ + for (auto c: str) + { + switch (c) + { + case '#': + case '\n': + case '=': + stream << '\\'; + break; + default: + break; + } + stream << c; + } +} + +settings::SettingsReader::SettingsReader(settings::Manager &manager): manager(manager) +{ + +} + +bool settings::SettingsReader::loadFrom(std::string path) +{ + stream.open(path, std::ios::in | std::ios::binary); + + if (stream.bad()) + return false; + + while (stream) + { + char c; + stream.read(&c, 1); + if (stream.eof()) + break; + pushChar(c); + } + finishString(true); + + return true; +} + +void settings::SettingsReader::pushChar(char c) +{ + if (comment) + { + if (c == '\n') + comment = false; + return; + } + + if (isspace(c)) + { + if (c != '\n' && !was_non_space) + return; + } + else + { + + was_non_space = true; + } + + if (!escape) + { + if (c == '\\') + escape = true; + else if (c == '#' && !quote) + { + finishString(true); + comment = true; + } + else if (c == '"') + quote = !quote; + else if (c == '=' && !quote && reading_key) + finishString(false); + else if (c == '\n') + finishString(true); + else if (isspace(c) && !quote) + temporary_spaces.push_back(c); + else + { + for (auto x: temporary_spaces) + oss.push_back(x); + temporary_spaces.clear(); + oss.push_back(c); + } + } + else + { + // Escaped character can be anything but null + if (c != 0) + oss.push_back(c); + escape = false; + } +} + +void settings::SettingsReader::finishString(bool complete) +{ + if (complete && reading_key) + { + oss.clear(); + return; + } + + std::string str = oss; + oss.clear(); + if (reading_key) + { + stored_key = std::move(str); + } + else + { + onReadKeyValue(stored_key, str); + } + reading_key = !reading_key; + was_non_space = false; + temporary_spaces.clear(); +} + +void +settings::SettingsReader::onReadKeyValue(std::string key, std::string value) +{ + printf("Read: '%s' = '%s'\n", key.c_str(), value.c_str()); + auto v = manager.lookup(key); + if (v == nullptr) + { + printf("Could not find variable %s\n", key.c_str()); + return; + } + v->fromString(value); + printf("Set: '%s' = '%s'\n", key.c_str(), v->toString().c_str()); +}