2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
24 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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
33 ===============================================================================
37 ===============================================================================
40 typedef struct commandDef_s {
41 struct commandDef_s * next;
43 cmdFunction_t function;
44 argCompletion_t argCompletion;
50 class idCmdSystemLocal : public idCmdSystem {
52 virtual void Init( void );
53 virtual void Shutdown( void );
55 virtual void AddCommand( const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion = NULL );
56 virtual void RemoveCommand( const char *cmdName );
57 virtual void RemoveFlaggedCommands( int flags );
59 virtual void CommandCompletion( void(*callback)( const char *s ) );
60 virtual void ArgCompletion( const char *cmdString, void(*callback)( const char *s ) );
62 virtual void BufferCommandText( cmdExecution_t exec, const char *text );
63 virtual void ExecuteCommandBuffer( void );
65 virtual void ArgCompletion_FolderExtension( const idCmdArgs &args, void(*callback)( const char *s ), const char *folder, bool stripFolder, ... );
66 virtual void ArgCompletion_DeclName( const idCmdArgs &args, void(*callback)( const char *s ), int type );
68 virtual void BufferCommandArgs( cmdExecution_t exec, const idCmdArgs &args );
70 virtual void SetupReloadEngine( const idCmdArgs &args );
71 virtual bool PostReloadEngine( void );
73 void SetWait( int numFrames ) { wait = numFrames; }
74 commandDef_t * GetCommands( void ) const { return commands; }
77 static const int MAX_CMD_BUFFER = 0x10000;
79 commandDef_t * commands;
83 byte textBuf[MAX_CMD_BUFFER];
85 idStr completionString;
86 idStrList completionParms;
88 // piggybacks on the text buffer, avoids tokenize again and screwing it up
89 idList<idCmdArgs> tokenizedCmds;
91 // a command stored to be executed after a reloadEngine and all associated commands have been processed
95 void ExecuteTokenizedString( const idCmdArgs &args );
96 void ExecuteCommandText( const char *text );
97 void InsertCommandText( const char *text );
98 void AppendCommandText( const char *text );
100 static void ListByFlags( const idCmdArgs &args, cmdFlags_t flags );
101 static void List_f( const idCmdArgs &args );
102 static void SystemList_f( const idCmdArgs &args );
103 static void RendererList_f( const idCmdArgs &args );
104 static void SoundList_f( const idCmdArgs &args );
105 static void GameList_f( const idCmdArgs &args );
106 static void ToolList_f( const idCmdArgs &args );
107 static void Exec_f( const idCmdArgs &args );
108 static void Vstr_f( const idCmdArgs &args );
109 static void Echo_f( const idCmdArgs &args );
110 static void Parse_f( const idCmdArgs &args );
111 static void Wait_f( const idCmdArgs &args );
112 static void PrintMemInfo_f( const idCmdArgs &args );
115 idCmdSystemLocal cmdSystemLocal;
116 idCmdSystem * cmdSystem = &cmdSystemLocal;
121 idCmdSystemLocal::ListByFlags
124 // NOTE: the const wonkyness is required to make msvc happy
126 ID_INLINE int idListSortCompare( const commandDef_t * const *a, const commandDef_t * const *b ) {
127 return idStr::Icmp( (*a)->name, (*b)->name );
130 void idCmdSystemLocal::ListByFlags( const idCmdArgs &args, cmdFlags_t flags ) {
133 const commandDef_t *cmd;
134 idList<const commandDef_t *> cmdList;
136 if ( args.Argc() > 1 ) {
137 match = args.Args( 1, -1 );
138 match.Replace( " ", "" );
143 for ( cmd = cmdSystemLocal.GetCommands(); cmd; cmd = cmd->next ) {
144 if ( !( cmd->flags & flags ) ) {
147 if ( match.Length() && idStr( cmd->name ).Filter( match, false ) == 0 ) {
151 cmdList.Append( cmd );
156 for ( i = 0; i < cmdList.Num(); i++ ) {
159 common->Printf( " %-21s %s\n", cmd->name, cmd->description );
162 common->Printf( "%i commands\n", cmdList.Num() );
167 idCmdSystemLocal::List_f
170 void idCmdSystemLocal::List_f( const idCmdArgs &args ) {
171 idCmdSystemLocal::ListByFlags( args, CMD_FL_ALL );
176 idCmdSystemLocal::SystemList_f
179 void idCmdSystemLocal::SystemList_f( const idCmdArgs &args ) {
180 idCmdSystemLocal::ListByFlags( args, CMD_FL_SYSTEM );
185 idCmdSystemLocal::RendererList_f
188 void idCmdSystemLocal::RendererList_f( const idCmdArgs &args ) {
189 idCmdSystemLocal::ListByFlags( args, CMD_FL_RENDERER );
194 idCmdSystemLocal::SoundList_f
197 void idCmdSystemLocal::SoundList_f( const idCmdArgs &args ) {
198 idCmdSystemLocal::ListByFlags( args, CMD_FL_SOUND );
203 idCmdSystemLocal::GameList_f
206 void idCmdSystemLocal::GameList_f( const idCmdArgs &args ) {
207 idCmdSystemLocal::ListByFlags( args, CMD_FL_GAME );
212 idCmdSystemLocal::ToolList_f
215 void idCmdSystemLocal::ToolList_f( const idCmdArgs &args ) {
216 idCmdSystemLocal::ListByFlags( args, CMD_FL_TOOL );
221 idCmdSystemLocal::Exec_f
224 void idCmdSystemLocal::Exec_f( const idCmdArgs &args ) {
229 if ( args.Argc () != 2 ) {
230 common->Printf( "exec <filename> : execute a script file\n" );
234 filename = args.Argv(1);
235 filename.DefaultFileExtension( ".cfg" );
236 len = fileSystem->ReadFile( filename, reinterpret_cast<void **>(&f), NULL );
238 common->Printf( "couldn't exec %s\n", args.Argv(1) );
241 common->Printf( "execing %s\n", args.Argv(1) );
243 cmdSystemLocal.BufferCommandText( CMD_EXEC_INSERT, f );
245 fileSystem->FreeFile( f );
250 idCmdSystemLocal::Vstr_f
252 Inserts the current value of a cvar as command text
255 void idCmdSystemLocal::Vstr_f( const idCmdArgs &args ) {
258 if ( args.Argc () != 2 ) {
259 common->Printf( "vstr <variablename> : execute a variable command\n" );
263 v = cvarSystem->GetCVarString( args.Argv( 1 ) );
265 cmdSystemLocal.BufferCommandText( CMD_EXEC_APPEND, va( "%s\n", v ) );
270 idCmdSystemLocal::Echo_f
272 Just prints the rest of the line to the console
275 void idCmdSystemLocal::Echo_f( const idCmdArgs &args ) {
278 for ( i = 1; i < args.Argc(); i++ ) {
279 common->Printf( "%s ", args.Argv( i ) );
281 common->Printf( "\n" );
286 idCmdSystemLocal::Wait_f
288 Causes execution of the remainder of the command buffer to be delayed until next frame.
291 void idCmdSystemLocal::Wait_f( const idCmdArgs &args ) {
292 if ( args.Argc() == 2 ) {
293 cmdSystemLocal.SetWait( atoi( args.Argv( 1 ) ) );
295 cmdSystemLocal.SetWait( 1 );
301 idCmdSystemLocal::Parse_f
303 This just prints out how the rest of the line was parsed, as a debugging tool.
306 void idCmdSystemLocal::Parse_f( const idCmdArgs &args ) {
309 for ( i = 0; i < args.Argc(); i++ ) {
310 common->Printf( "%i: %s\n", i, args.Argv(i) );
316 idCmdSystemLocal::Init
319 void idCmdSystemLocal::Init( void ) {
321 AddCommand( "listCmds", List_f, CMD_FL_SYSTEM, "lists commands" );
322 AddCommand( "listSystemCmds", SystemList_f, CMD_FL_SYSTEM, "lists system commands" );
323 AddCommand( "listRendererCmds", RendererList_f, CMD_FL_SYSTEM, "lists renderer commands" );
324 AddCommand( "listSoundCmds", SoundList_f, CMD_FL_SYSTEM, "lists sound commands" );
325 AddCommand( "listGameCmds", GameList_f, CMD_FL_SYSTEM, "lists game commands" );
326 AddCommand( "listToolCmds", ToolList_f, CMD_FL_SYSTEM, "lists tool commands" );
327 AddCommand( "exec", Exec_f, CMD_FL_SYSTEM, "executes a config file", ArgCompletion_ConfigName );
328 AddCommand( "vstr", Vstr_f, CMD_FL_SYSTEM, "inserts the current value of a cvar as command text" );
329 AddCommand( "echo", Echo_f, CMD_FL_SYSTEM, "prints text" );
330 AddCommand( "parse", Parse_f, CMD_FL_SYSTEM, "prints tokenized string" );
331 AddCommand( "wait", Wait_f, CMD_FL_SYSTEM, "delays remaining buffered commands one or more frames" );
333 completionString = "*";
340 idCmdSystemLocal::Shutdown
343 void idCmdSystemLocal::Shutdown( void ) {
346 for ( cmd = commands; cmd; cmd = commands ) {
347 commands = commands->next;
348 Mem_Free( cmd->name );
349 Mem_Free( cmd->description );
353 completionString.Clear();
354 completionParms.Clear();
355 tokenizedCmds.Clear();
361 idCmdSystemLocal::AddCommand
364 void idCmdSystemLocal::AddCommand( const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion ) {
367 // fail if the command already exists
368 for ( cmd = commands; cmd; cmd = cmd->next ) {
369 if ( idStr::Cmp( cmdName, cmd->name ) == 0 ) {
370 if ( function != cmd->function ) {
371 common->Printf( "idCmdSystemLocal::AddCommand: %s already defined\n", cmdName );
377 cmd = new commandDef_t;
378 cmd->name = Mem_CopyString( cmdName );
379 cmd->function = function;
380 cmd->argCompletion = argCompletion;
382 cmd->description = Mem_CopyString( description );
383 cmd->next = commands;
389 idCmdSystemLocal::RemoveCommand
392 void idCmdSystemLocal::RemoveCommand( const char *cmdName ) {
393 commandDef_t *cmd, **last;
395 for ( last = &commands, cmd = *last; cmd; cmd = *last ) {
396 if ( idStr::Cmp( cmdName, cmd->name ) == 0 ) {
398 Mem_Free( cmd->name );
399 Mem_Free( cmd->description );
409 idCmdSystemLocal::RemoveFlaggedCommands
412 void idCmdSystemLocal::RemoveFlaggedCommands( int flags ) {
413 commandDef_t *cmd, **last;
415 for ( last = &commands, cmd = *last; cmd; cmd = *last ) {
416 if ( cmd->flags & flags ) {
418 Mem_Free( cmd->name );
419 Mem_Free( cmd->description );
429 idCmdSystemLocal::CommandCompletion
432 void idCmdSystemLocal::CommandCompletion( void(*callback)( const char *s ) ) {
435 for ( cmd = commands; cmd; cmd = cmd->next ) {
436 callback( cmd->name );
442 idCmdSystemLocal::ArgCompletion
445 void idCmdSystemLocal::ArgCompletion( const char *cmdString, void(*callback)( const char *s ) ) {
449 args.TokenizeString( cmdString, false );
451 for ( cmd = commands; cmd; cmd = cmd->next ) {
452 if ( !cmd->argCompletion ) {
455 if ( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) {
456 cmd->argCompletion( args, callback );
464 idCmdSystemLocal::ExecuteTokenizedString
467 void idCmdSystemLocal::ExecuteTokenizedString( const idCmdArgs &args ) {
468 commandDef_t *cmd, **prev;
470 // execute the command line
471 if ( !args.Argc() ) {
475 // check registered command functions
476 for ( prev = &commands; *prev; prev = &cmd->next ) {
478 if ( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) {
479 // rearrange the links so that the command will be
480 // near the head of the list next time it is used
482 cmd->next = commands;
485 if ( ( cmd->flags & (CMD_FL_CHEAT|CMD_FL_TOOL) ) && session && session->IsMultiplayer() && !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
486 common->Printf( "Command '%s' not valid in multiplayer mode.\n", cmd->name );
489 // perform the action
490 if ( !cmd->function ) {
493 cmd->function( args );
500 if ( cvarSystem->Command( args ) ) {
504 common->Printf( "Unknown command '%s'\n", args.Argv( 0 ) );
509 idCmdSystemLocal::ExecuteCommandText
511 Tokenizes, then executes.
514 void idCmdSystemLocal::ExecuteCommandText( const char *text ) {
515 ExecuteTokenizedString( idCmdArgs( text, false ) );
520 idCmdSystemLocal::InsertCommandText
522 Adds command text immediately after the current command
523 Adds a \n to the text
526 void idCmdSystemLocal::InsertCommandText( const char *text ) {
530 len = strlen( text ) + 1;
531 if ( len + textLength > (int)sizeof( textBuf ) ) {
532 common->Printf( "idCmdSystemLocal::InsertText: buffer overflow\n" );
536 // move the existing command text
537 for ( i = textLength - 1; i >= 0; i-- ) {
538 textBuf[ i + len ] = textBuf[ i ];
541 // copy the new text in
542 memcpy( textBuf, text, len - 1 );
545 textBuf[ len - 1 ] = '\n';
552 idCmdSystemLocal::AppendCommandText
554 Adds command text at the end of the buffer, does NOT add a final \n
557 void idCmdSystemLocal::AppendCommandText( const char *text ) {
562 if ( textLength + l >= (int)sizeof( textBuf ) ) {
563 common->Printf( "idCmdSystemLocal::AppendText: buffer overflow\n" );
566 memcpy( textBuf + textLength, text, l );
572 idCmdSystemLocal::BufferCommandText
575 void idCmdSystemLocal::BufferCommandText( cmdExecution_t exec, const char *text ) {
578 ExecuteCommandText( text );
581 case CMD_EXEC_INSERT: {
582 InsertCommandText( text );
585 case CMD_EXEC_APPEND: {
586 AppendCommandText( text );
590 common->FatalError( "idCmdSystemLocal::BufferCommandText: bad exec type" );
597 idCmdSystemLocal::BufferCommandArgs
600 void idCmdSystemLocal::BufferCommandArgs( cmdExecution_t exec, const idCmdArgs &args ) {
603 ExecuteTokenizedString( args );
606 case CMD_EXEC_APPEND: {
607 AppendCommandText( "_execTokenized\n" );
608 tokenizedCmds.Append( args );
612 common->FatalError( "idCmdSystemLocal::BufferCommandArgs: bad exec type" );
619 idCmdSystemLocal::ExecuteCommandBuffer
622 void idCmdSystemLocal::ExecuteCommandBuffer( void ) {
628 while( textLength ) {
631 // skip out while text still remains in buffer, leaving it for next frame
636 // find a \n or ; line break
637 text = (char *)textBuf;
640 for ( i = 0; i < textLength; i++ ) {
641 if ( text[i] == '"' ) {
644 if ( !( quotes & 1 ) && text[i] == ';' ) {
645 break; // don't break if inside a quoted string
647 if ( text[i] == '\n' || text[i] == '\r' ) {
654 if ( !idStr::Cmp( text, "_execTokenized" ) ) {
655 args = tokenizedCmds[ 0 ];
656 tokenizedCmds.RemoveIndex( 0 );
658 args.TokenizeString( text, false );
661 // delete the text from the command buffer and move remaining commands down
662 // this is necessary because commands (exec) can insert data at the
663 // beginning of the text buffer
665 if ( i == textLength ) {
670 memmove( text, text+i, textLength );
673 // execute the command line that we have already tokenized
674 ExecuteTokenizedString( args );
680 idCmdSystemLocal::ArgCompletion_FolderExtension
683 void idCmdSystemLocal::ArgCompletion_FolderExtension( const idCmdArgs &args, void(*callback)( const char *s ), const char *folder, bool stripFolder, ... ) {
686 const char *extension;
689 string = args.Argv( 0 );
691 string += args.Argv( 1 );
693 if ( string.Icmp( completionString ) != 0 ) {
697 completionString = string;
698 completionParms.Clear();
700 parm = args.Argv( 1 );
701 parm.ExtractFilePath( path );
702 if ( stripFolder || path.Length() == 0 ) {
703 path = folder + path;
705 path.StripTrailing( '/' );
708 names = fileSystem->ListFiles( path, "/", true, true );
709 for ( i = 0; i < names->GetNumFiles(); i++ ) {
710 idStr name = names->GetFile( i );
712 name.Strip( folder );
716 name = args.Argv( 0 ) + ( " " + name ) + "/";
717 completionParms.Append( name );
719 fileSystem->FreeFileList( names );
722 va_start( argPtr, stripFolder );
723 for ( extension = va_arg( argPtr, const char * ); extension; extension = va_arg( argPtr, const char * ) ) {
724 names = fileSystem->ListFiles( path, extension, true, true );
725 for ( i = 0; i < names->GetNumFiles(); i++ ) {
726 idStr name = names->GetFile( i );
728 name.Strip( folder );
732 name = args.Argv( 0 ) + ( " " + name );
733 completionParms.Append( name );
735 fileSystem->FreeFileList( names );
739 for ( i = 0; i < completionParms.Num(); i++ ) {
740 callback( completionParms[i] );
746 idCmdSystemLocal::ArgCompletion_DeclName
749 void idCmdSystemLocal::ArgCompletion_DeclName( const idCmdArgs &args, void(*callback)( const char *s ), int type ) {
752 if ( declManager == NULL ) {
755 num = declManager->GetNumDecls( (declType_t)type );
756 for ( i = 0; i < num; i++ ) {
757 callback( idStr( args.Argv( 0 ) ) + " " + declManager->DeclByIndex( (declType_t)type, i , false )->GetName() );
763 idCmdSystemLocal::SetupReloadEngine
766 void idCmdSystemLocal::SetupReloadEngine( const idCmdArgs &args ) {
767 BufferCommandText( CMD_EXEC_APPEND, "reloadEngine\n" );
773 idCmdSystemLocal::PostReloadEngine
776 bool idCmdSystemLocal::PostReloadEngine( void ) {
777 if ( !postReload.Argc() ) {
780 BufferCommandArgs( CMD_EXEC_APPEND, postReload );