[C++] Simple command system (0.3.7R1)

y0mike

Well-Known Member
Joined
May 10, 2014
Messages
85
Likes
32
Points
68
10
#1
C++:
struct SCommandEntry
{
    SCommandEntry( const std::string& cmd, int nargs, const std::function< void( const std::vector< std::string >& ) > & cb ) :
        strCmd( cmd ), numArgs( nargs ), fnCallback( cb ) 
    {
    }

    std::string strCmd;
    int            numArgs;
    std::function< void( const std::vector< std::string >& ) > fnCallback;
};

static std::vector< SCommandEntry > s_cmdEntries;

void AddCommand( const char * szCmd, int numArgs, const std::function< void( const std::vector< std::string >& ) > &fnCallback )
{
    s_cmdEntries.emplace_back( szCmd, numArgs, fnCallback );
}

bool iequals( const std::string& a, const std::string& b )
{
    return std::equal( a.begin(), a.end(),
                       b.begin(), b.end(),
                       [ ]( char a, char b ) {
        return tolower( a ) == tolower( b );
    } );
}

bool __stdcall onCommandReceived( const char * strCommand )
{
//    g_pCons->Write( "Command: %s\n", strCommand );

    std::vector< std::string > vecArgs = SplitString( strCommand, " " );

    for ( const auto & entry : s_cmdEntries ) {     
        if ( !vecArgs.empty() && !entry.strCmd.empty() &&
             iequals( vecArgs[ 0 ].c_str(), entry.strCmd.c_str() ) )
        {
            // remove the first argument which is the command name (e.g /command)
            vecArgs.erase( vecArgs.begin() );

            // ensure that the num. of arguments matched the amount this command hsould have
            int numArgs = vecArgs.size();
            if ( entry.numArgs != numArgs )
                return false; // todo: print return message but im lazy

            // make sure the callback is valid
            if ( entry.fnCallback )
            {
                // call it
                entry.fnCallback( vecArgs );
                return true;
            }
        }
    }
    return false;
}

uintptr_t _onCommandReceived_jmpBack; 
uintptr_t _onCommandReceived_jmpRet;

__declspec( naked ) void __stdcall proxy_onCommandReceived() {
                       
    __asm
    {         
        push edi
        call onCommandReceived 
        cmp al, 1
        je NoSir

        mov eax, edi
        mov dword ptr [ esp + 0x128 ], 0

        jmp _onCommandReceived_jmpBack

    NoSir:  
        pop edi
        pop esi

        jmp _onCommandReceived_jmpRet
    }
}


void InitCmdCode()
{
    /*  
    .text:00065C9A 8B C7                                   mov     eax, edi
    .text:00065C9C C7 84 24 28 01 00 00 00+                mov     dword ptr [esp+128h], 0
    */                                                                                                       
    HookInstallJUMP( ( uintptr_t )GetModuleHandle( "samp.dll" ) + 0x65C9A, proxy_onCommandReceived, 11 );

    _onCommandReceived_jmpBack = ( uintptr_t )GetModuleHandle( "samp.dll" ) + 0x65CA7;
    _onCommandReceived_jmpRet  = ( uintptr_t )GetModuleHandle( "samp.dll" ) + 0x65D15;
}
Example usage
C++:
AddCommand( "/loadcfg", 1, [ ]( const std::vector< std::string >& strArgs )
{
    if ( !std::experimental::filesystem::exists( strArgs.front() ) )
        return;

    settings.load( strArgs.front() );
} );
 
Top