/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "precompiled.h" #pragma hdrstop #ifdef ID_RETAIL idCVar net_allowCheats( "net_allowCheats", "0", CVAR_BOOL | CVAR_ROM, "Allow cheats in multiplayer" ); #else idCVar net_allowCheats( "net_allowCheats", "0", CVAR_BOOL | CVAR_NOCHEAT, "Allow cheats in multiplayer" ); #endif /* =============================================================================== idCmdSystemLocal =============================================================================== */ typedef struct commandDef_s { struct commandDef_s* next; char* name; cmdFunction_t function; argCompletion_t argCompletion; int flags; char* description; } commandDef_t; /* ================================================ idCmdSystemLocal ================================================ */ class idCmdSystemLocal : public idCmdSystem { public: virtual void Init(); virtual void Shutdown(); virtual void AddCommand( const char* cmdName, cmdFunction_t function, int flags, const char* description, argCompletion_t argCompletion = NULL ); virtual void RemoveCommand( const char* cmdName ); virtual void RemoveFlaggedCommands( int flags ); virtual void CommandCompletion( void( *callback )( const char* s ) ); virtual void ArgCompletion( const char* cmdString, void( *callback )( const char* s ) ); virtual void ExecuteCommandText( const char* text ); virtual void AppendCommandText( const char* text ); virtual void BufferCommandText( cmdExecution_t exec, const char* text ); virtual void ExecuteCommandBuffer(); virtual void ArgCompletion_FolderExtension( const idCmdArgs& args, void( *callback )( const char* s ), const char* folder, bool stripFolder, ... ); virtual void ArgCompletion_DeclName( const idCmdArgs& args, void( *callback )( const char* s ), int type ); virtual void BufferCommandArgs( cmdExecution_t exec, const idCmdArgs& args ); virtual void SetupReloadEngine( const idCmdArgs& args ); virtual bool PostReloadEngine(); void SetWait( int numFrames ) { wait = numFrames; } commandDef_t* GetCommands() const { return commands; } private: static const int MAX_CMD_BUFFER = 0x10000; commandDef_t* commands; int wait; int textLength; byte textBuf[MAX_CMD_BUFFER]; idStr completionString; idStrList completionParms; // piggybacks on the text buffer, avoids tokenize again and screwing it up idList tokenizedCmds; // a command stored to be executed after a reloadEngine and all associated commands have been processed idCmdArgs postReload; private: void ExecuteTokenizedString( const idCmdArgs& args ); void InsertCommandText( const char* text ); static void ListByFlags( const idCmdArgs& args, cmdFlags_t flags ); static void List_f( const idCmdArgs& args ); static void SystemList_f( const idCmdArgs& args ); static void RendererList_f( const idCmdArgs& args ); static void SoundList_f( const idCmdArgs& args ); static void GameList_f( const idCmdArgs& args ); static void ToolList_f( const idCmdArgs& args ); static void Exec_f( const idCmdArgs& args ); static void Vstr_f( const idCmdArgs& args ); static void Echo_f( const idCmdArgs& args ); static void Parse_f( const idCmdArgs& args ); static void Wait_f( const idCmdArgs& args ); static void PrintMemInfo_f( const idCmdArgs& args ); }; idCmdSystemLocal cmdSystemLocal; idCmdSystem* cmdSystem = &cmdSystemLocal; /* ================================================ idSort_CommandDef ================================================ */ class idSort_CommandDef : public idSort_Quick< commandDef_t, idSort_CommandDef > { public: int Compare( const commandDef_t& a, const commandDef_t& b ) const { return idStr::Icmp( a.name, b.name ); } }; /* ============ idCmdSystemLocal::ListByFlags ============ */ void idCmdSystemLocal::ListByFlags( const idCmdArgs& args, cmdFlags_t flags ) { int i; idStr match; const commandDef_t* cmd; idList cmdList; if( args.Argc() > 1 ) { match = args.Args( 1, -1 ); match.Replace( " ", "" ); } else { match = ""; } for( cmd = cmdSystemLocal.GetCommands(); cmd; cmd = cmd->next ) { if( !( cmd->flags & flags ) ) { continue; } if( match.Length() && idStr( cmd->name ).Filter( match, false ) == 0 ) { continue; } cmdList.Append( cmd ); } //cmdList.SortWithTemplate( idSort_CommandDef() ); for( i = 0; i < cmdList.Num(); i++ ) { cmd = cmdList[i]; common->Printf( " %-21s %s\n", cmd->name, cmd->description ); } common->Printf( "%i commands\n", cmdList.Num() ); } /* ============ idCmdSystemLocal::List_f ============ */ void idCmdSystemLocal::List_f( const idCmdArgs& args ) { idCmdSystemLocal::ListByFlags( args, CMD_FL_ALL ); } /* ============ idCmdSystemLocal::SystemList_f ============ */ void idCmdSystemLocal::SystemList_f( const idCmdArgs& args ) { idCmdSystemLocal::ListByFlags( args, CMD_FL_SYSTEM ); } /* ============ idCmdSystemLocal::RendererList_f ============ */ void idCmdSystemLocal::RendererList_f( const idCmdArgs& args ) { idCmdSystemLocal::ListByFlags( args, CMD_FL_RENDERER ); } /* ============ idCmdSystemLocal::SoundList_f ============ */ void idCmdSystemLocal::SoundList_f( const idCmdArgs& args ) { idCmdSystemLocal::ListByFlags( args, CMD_FL_SOUND ); } /* ============ idCmdSystemLocal::GameList_f ============ */ void idCmdSystemLocal::GameList_f( const idCmdArgs& args ) { idCmdSystemLocal::ListByFlags( args, CMD_FL_GAME ); } /* ============ idCmdSystemLocal::ToolList_f ============ */ void idCmdSystemLocal::ToolList_f( const idCmdArgs& args ) { idCmdSystemLocal::ListByFlags( args, CMD_FL_TOOL ); } /* =============== idCmdSystemLocal::Exec_f =============== */ void idCmdSystemLocal::Exec_f( const idCmdArgs& args ) { char* f; int len; idStr filename; if( args.Argc() != 2 ) { common->Printf( "exec : execute a script file\n" ); return; } filename = args.Argv( 1 ); filename.DefaultFileExtension( ".cfg" ); len = fileSystem->ReadFile( filename, reinterpret_cast( &f ), NULL ); if( !f ) { common->Printf( "couldn't exec %s\n", args.Argv( 1 ) ); return; } common->Printf( "execing %s\n", args.Argv( 1 ) ); cmdSystemLocal.BufferCommandText( CMD_EXEC_INSERT, f ); fileSystem->FreeFile( f ); } /* =============== idCmdSystemLocal::Vstr_f Inserts the current value of a cvar as command text =============== */ void idCmdSystemLocal::Vstr_f( const idCmdArgs& args ) { const char* v; if( args.Argc() != 2 ) { common->Printf( "vstr : execute a variable command\n" ); return; } v = cvarSystem->GetCVarString( args.Argv( 1 ) ); cmdSystemLocal.BufferCommandText( CMD_EXEC_APPEND, va( "%s\n", v ) ); } /* =============== idCmdSystemLocal::Echo_f Just prints the rest of the line to the console =============== */ void idCmdSystemLocal::Echo_f( const idCmdArgs& args ) { int i; for( i = 1; i < args.Argc(); i++ ) { common->Printf( "%s ", args.Argv( i ) ); } common->Printf( "\n" ); } /* ============ idCmdSystemLocal::Wait_f Causes execution of the remainder of the command buffer to be delayed until next frame. ============ */ void idCmdSystemLocal::Wait_f( const idCmdArgs& args ) { if( args.Argc() == 2 ) { cmdSystemLocal.SetWait( atoi( args.Argv( 1 ) ) ); } else { cmdSystemLocal.SetWait( 1 ); } } /* ============ idCmdSystemLocal::Parse_f This just prints out how the rest of the line was parsed, as a debugging tool. ============ */ void idCmdSystemLocal::Parse_f( const idCmdArgs& args ) { int i; for( i = 0; i < args.Argc(); i++ ) { common->Printf( "%i: %s\n", i, args.Argv( i ) ); } } /* ============ idCmdSystemLocal::Init ============ */ void idCmdSystemLocal::Init() { AddCommand( "listCmds", List_f, CMD_FL_SYSTEM, "lists commands" ); AddCommand( "listSystemCmds", SystemList_f, CMD_FL_SYSTEM, "lists system commands" ); AddCommand( "listRendererCmds", RendererList_f, CMD_FL_SYSTEM, "lists renderer commands" ); AddCommand( "listSoundCmds", SoundList_f, CMD_FL_SYSTEM, "lists sound commands" ); AddCommand( "listGameCmds", GameList_f, CMD_FL_SYSTEM, "lists game commands" ); AddCommand( "listToolCmds", ToolList_f, CMD_FL_SYSTEM, "lists tool commands" ); AddCommand( "exec", Exec_f, CMD_FL_SYSTEM, "executes a config file", ArgCompletion_ConfigName ); AddCommand( "vstr", Vstr_f, CMD_FL_SYSTEM, "inserts the current value of a cvar as command text" ); AddCommand( "echo", Echo_f, CMD_FL_SYSTEM, "prints text" ); AddCommand( "parse", Parse_f, CMD_FL_SYSTEM, "prints tokenized string" ); AddCommand( "wait", Wait_f, CMD_FL_SYSTEM, "delays remaining buffered commands one or more frames" ); // link in all the commands declared with static idCommandLink variables or CONSOLE_COMMAND macros for( idCommandLink* link = CommandLinks(); link != NULL; link = link->next ) { AddCommand( link->cmdName_, link->function_, CMD_FL_SYSTEM, link->description_, link->argCompletion_ ); } completionString = "*"; textLength = 0; } /* ============ idCmdSystemLocal::Shutdown ============ */ void idCmdSystemLocal::Shutdown() { commandDef_t* cmd; for( cmd = commands; cmd; cmd = commands ) { commands = commands->next; Mem_Free( cmd->name ); Mem_Free( cmd->description ); delete cmd; } completionString.Clear(); completionParms.Clear(); tokenizedCmds.Clear(); postReload.Clear(); } /* ============ idCmdSystemLocal::AddCommand ============ */ void idCmdSystemLocal::AddCommand( const char* cmdName, cmdFunction_t function, int flags, const char* description, argCompletion_t argCompletion ) { commandDef_t* cmd; // fail if the command already exists for( cmd = commands; cmd; cmd = cmd->next ) { if( idStr::Cmp( cmdName, cmd->name ) == 0 ) { if( function != cmd->function ) { common->Printf( "idCmdSystemLocal::AddCommand: %s already defined\n", cmdName ); } return; } } cmd = new( TAG_SYSTEM ) commandDef_t; cmd->name = Mem_CopyString( cmdName ); cmd->function = function; cmd->argCompletion = argCompletion; cmd->flags = flags; cmd->description = Mem_CopyString( description ); cmd->next = commands; commands = cmd; } /* ============ idCmdSystemLocal::RemoveCommand ============ */ void idCmdSystemLocal::RemoveCommand( const char* cmdName ) { commandDef_t* cmd, **last; for( last = &commands, cmd = *last; cmd; cmd = *last ) { if( idStr::Cmp( cmdName, cmd->name ) == 0 ) { *last = cmd->next; Mem_Free( cmd->name ); Mem_Free( cmd->description ); delete cmd; return; } last = &cmd->next; } } /* ============ idCmdSystemLocal::RemoveFlaggedCommands ============ */ void idCmdSystemLocal::RemoveFlaggedCommands( int flags ) { commandDef_t* cmd, **last; for( last = &commands, cmd = *last; cmd; cmd = *last ) { if( cmd->flags & flags ) { *last = cmd->next; Mem_Free( cmd->name ); Mem_Free( cmd->description ); delete cmd; continue; } last = &cmd->next; } } /* ============ idCmdSystemLocal::CommandCompletion ============ */ void idCmdSystemLocal::CommandCompletion( void( *callback )( const char* s ) ) { commandDef_t* cmd; for( cmd = commands; cmd; cmd = cmd->next ) { callback( cmd->name ); } } /* ============ idCmdSystemLocal::ArgCompletion ============ */ void idCmdSystemLocal::ArgCompletion( const char* cmdString, void( *callback )( const char* s ) ) { commandDef_t* cmd; idCmdArgs args; args.TokenizeString( cmdString, false ); for( cmd = commands; cmd; cmd = cmd->next ) { if( !cmd->argCompletion ) { continue; } if( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) { cmd->argCompletion( args, callback ); break; } } } /* ============ idCmdSystemLocal::ExecuteTokenizedString ============ */ void idCmdSystemLocal::ExecuteTokenizedString( const idCmdArgs& args ) { commandDef_t* cmd, **prev; // execute the command line if( !args.Argc() ) { return; // no tokens } // check registered command functions for( prev = &commands; *prev; prev = &cmd->next ) { cmd = *prev; if( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) { // rearrange the links so that the command will be // near the head of the list next time it is used *prev = cmd->next; cmd->next = commands; commands = cmd; if( ( cmd->flags & ( CMD_FL_CHEAT | CMD_FL_TOOL ) ) && common->IsMultiplayer() && !net_allowCheats.GetBool() ) { common->Printf( "Command '%s' not valid in multiplayer mode.\n", cmd->name ); return; } // perform the action if( !cmd->function ) { break; } else { cmd->function( args ); } return; } } // check cvars if( cvarSystem->Command( args ) ) { return; } common->Printf( "Unknown command '%s'\n", args.Argv( 0 ) ); } /* ============ idCmdSystemLocal::ExecuteCommandText Tokenizes, then executes. ============ */ void idCmdSystemLocal::ExecuteCommandText( const char* text ) { ExecuteTokenizedString( idCmdArgs( text, false ) ); } /* ============ idCmdSystemLocal::InsertCommandText Adds command text immediately after the current command Adds a \n to the text ============ */ void idCmdSystemLocal::InsertCommandText( const char* text ) { int len; int i; len = strlen( text ) + 1; if( len + textLength > ( int )sizeof( textBuf ) ) { common->Printf( "idCmdSystemLocal::InsertText: buffer overflow\n" ); return; } // move the existing command text for( i = textLength - 1; i >= 0; i-- ) { textBuf[ i + len ] = textBuf[ i ]; } // copy the new text in memcpy( textBuf, text, len - 1 ); // add a \n textBuf[ len - 1 ] = '\n'; textLength += len; } /* ============ idCmdSystemLocal::AppendCommandText Adds command text at the end of the buffer, does NOT add a final \n ============ */ void idCmdSystemLocal::AppendCommandText( const char* text ) { int l; l = strlen( text ); if( textLength + l >= ( int )sizeof( textBuf ) ) { common->Printf( "idCmdSystemLocal::AppendText: buffer overflow\n" ); return; } memcpy( textBuf + textLength, text, l ); textLength += l; } /* ============ idCmdSystemLocal::BufferCommandText ============ */ void idCmdSystemLocal::BufferCommandText( cmdExecution_t exec, const char* text ) { switch( exec ) { case CMD_EXEC_NOW: { ExecuteCommandText( text ); break; } case CMD_EXEC_INSERT: { InsertCommandText( text ); break; } case CMD_EXEC_APPEND: { AppendCommandText( text ); break; } default: { common->FatalError( "idCmdSystemLocal::BufferCommandText: bad exec type" ); } } } /* ============ idCmdSystemLocal::BufferCommandArgs ============ */ void idCmdSystemLocal::BufferCommandArgs( cmdExecution_t exec, const idCmdArgs& args ) { switch( exec ) { case CMD_EXEC_NOW: { ExecuteTokenizedString( args ); break; } case CMD_EXEC_APPEND: { AppendCommandText( "_execTokenized\n" ); tokenizedCmds.Append( args ); break; } default: { common->FatalError( "idCmdSystemLocal::BufferCommandArgs: bad exec type" ); } } } /* ============ idCmdSystemLocal::ExecuteCommandBuffer ============ */ void idCmdSystemLocal::ExecuteCommandBuffer() { int i; char* text; int quotes; idCmdArgs args; while( textLength ) { if( wait ) { // skip out while text still remains in buffer, leaving it for next frame wait--; break; } // find a \n or ; line break text = ( char* )textBuf; quotes = 0; for( i = 0; i < textLength; i++ ) { if( text[i] == '"' ) { quotes++; } if( !( quotes & 1 ) && text[i] == ';' ) { break; // don't break if inside a quoted string } if( text[i] == '\n' || text[i] == '\r' ) { break; } } text[i] = 0; if( !idStr::Cmp( text, "_execTokenized" ) ) { args = tokenizedCmds[ 0 ]; tokenizedCmds.RemoveIndex( 0 ); } else { args.TokenizeString( text, false ); } // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec) can insert data at the // beginning of the text buffer if( i == textLength ) { textLength = 0; } else { i++; textLength -= i; memmove( text, text + i, textLength ); } // execute the command line that we have already tokenized ExecuteTokenizedString( args ); } } /* ============ idCmdSystemLocal::ArgCompletion_FolderExtension ============ */ void idCmdSystemLocal::ArgCompletion_FolderExtension( const idCmdArgs& args, void( *callback )( const char* s ), const char* folder, bool stripFolder, ... ) { int i; idStr string; const char* extension; va_list argPtr; string = args.Argv( 0 ); string += " "; string += args.Argv( 1 ); if( string.Icmp( completionString ) != 0 ) { idStr parm, path; idFileList* names; completionString = string; completionParms.Clear(); parm = args.Argv( 1 ); parm.ExtractFilePath( path ); if( stripFolder || path.Length() == 0 ) { path = folder + path; } path.StripTrailing( '/' ); // list folders names = fileSystem->ListFiles( path, "/", true, true ); for( i = 0; i < names->GetNumFiles(); i++ ) { idStr name = names->GetFile( i ); if( stripFolder ) { name.Strip( folder ); } else { name.Strip( "/" ); } name = args.Argv( 0 ) + ( " " + name ) + "/"; completionParms.Append( name ); } fileSystem->FreeFileList( names ); // list files va_start( argPtr, stripFolder ); for( extension = va_arg( argPtr, const char* ); extension; extension = va_arg( argPtr, const char* ) ) { names = fileSystem->ListFiles( path, extension, true, true ); for( i = 0; i < names->GetNumFiles(); i++ ) { idStr name = names->GetFile( i ); if( stripFolder ) { name.Strip( folder ); } else { name.Strip( "/" ); } name = args.Argv( 0 ) + ( " " + name ); completionParms.Append( name ); } fileSystem->FreeFileList( names ); } va_end( argPtr ); } for( i = 0; i < completionParms.Num(); i++ ) { callback( completionParms[i] ); } } /* ============ idCmdSystemLocal::ArgCompletion_DeclName ============ */ void idCmdSystemLocal::ArgCompletion_DeclName( const idCmdArgs& args, void( *callback )( const char* s ), int type ) { int i, num; if( declManager == NULL ) { return; } num = declManager->GetNumDecls( ( declType_t )type ); for( i = 0; i < num; i++ ) { callback( idStr( args.Argv( 0 ) ) + " " + declManager->DeclByIndex( ( declType_t )type, i , false )->GetName() ); } } /* ============ idCmdSystemLocal::SetupReloadEngine ============ */ void idCmdSystemLocal::SetupReloadEngine( const idCmdArgs& args ) { BufferCommandText( CMD_EXEC_APPEND, "reloadEngine\n" ); postReload = args; } /* ============ idCmdSystemLocal::PostReloadEngine ============ */ bool idCmdSystemLocal::PostReloadEngine() { if( !postReload.Argc() ) { return false; } BufferCommandArgs( CMD_EXEC_APPEND, postReload ); postReload.Clear(); return true; }