DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

CmdSystem.cpp (20450B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition 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.
     13 
     14 Doom 3 BFG Edition 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.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 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.
     23 
     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.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 #ifdef ID_RETAIL
     33 idCVar net_allowCheats( "net_allowCheats", "0", CVAR_BOOL | CVAR_ROM, "Allow cheats in multiplayer" );
     34 #else
     35 idCVar net_allowCheats( "net_allowCheats", "0", CVAR_BOOL | CVAR_NOCHEAT, "Allow cheats in multiplayer" );
     36 #endif
     37 
     38 /*
     39 ===============================================================================
     40 
     41 	idCmdSystemLocal
     42 
     43 ===============================================================================
     44 */
     45 
     46 typedef struct commandDef_s {
     47 	struct commandDef_s *	next;
     48 	char *					name;
     49 	cmdFunction_t			function;
     50 	argCompletion_t			argCompletion;
     51 	int						flags;
     52 	char *					description;
     53 } commandDef_t;
     54 
     55 /*
     56 ================================================
     57 idCmdSystemLocal 
     58 ================================================
     59 */
     60 class idCmdSystemLocal : public idCmdSystem {
     61 public:
     62 	virtual void			Init();
     63 	virtual void			Shutdown();
     64 
     65 	virtual void			AddCommand( const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion = NULL );
     66 	virtual void			RemoveCommand( const char *cmdName );
     67 	virtual void			RemoveFlaggedCommands( int flags );
     68 
     69 	virtual void			CommandCompletion( void(*callback)( const char *s ) );
     70 	virtual void			ArgCompletion( const char *cmdString, void(*callback)( const char *s ) );
     71 	virtual void			ExecuteCommandText( const char * text );
     72 	virtual void			AppendCommandText( const char * text );
     73 
     74 	virtual void			BufferCommandText( cmdExecution_t exec, const char *text );
     75 	virtual void			ExecuteCommandBuffer();
     76 
     77 	virtual void			ArgCompletion_FolderExtension( const idCmdArgs &args, void(*callback)( const char *s ), const char *folder, bool stripFolder, ... );
     78 	virtual void			ArgCompletion_DeclName( const idCmdArgs &args, void(*callback)( const char *s ), int type );
     79 
     80 	virtual void			BufferCommandArgs( cmdExecution_t exec, const idCmdArgs &args );
     81 
     82 	virtual void			SetupReloadEngine( const idCmdArgs &args );
     83 	virtual bool			PostReloadEngine();
     84 
     85 	void					SetWait( int numFrames ) { wait = numFrames; }
     86 	commandDef_t *			GetCommands() const { return commands; }
     87 
     88 private:
     89 	static const int		MAX_CMD_BUFFER = 0x10000;
     90 
     91 	commandDef_t *			commands;
     92 
     93 	int						wait;
     94 	int						textLength;
     95 	byte					textBuf[MAX_CMD_BUFFER];
     96 
     97 	idStr					completionString;
     98 	idStrList				completionParms;
     99 
    100 	// piggybacks on the text buffer, avoids tokenize again and screwing it up
    101 	idList<idCmdArgs>		tokenizedCmds;
    102 
    103 	// a command stored to be executed after a reloadEngine and all associated commands have been processed
    104 	idCmdArgs				postReload;
    105 
    106 private:	
    107 	void					ExecuteTokenizedString( const idCmdArgs &args );
    108 	void					InsertCommandText( const char *text );
    109 
    110 	static void				ListByFlags( const idCmdArgs &args, cmdFlags_t flags );
    111 	static void				List_f( const idCmdArgs &args );
    112 	static void				SystemList_f( const idCmdArgs &args );
    113 	static void				RendererList_f( const idCmdArgs &args );
    114 	static void				SoundList_f( const idCmdArgs &args );
    115 	static void				GameList_f( const idCmdArgs &args );
    116 	static void				ToolList_f( const idCmdArgs &args );
    117 	static void				Exec_f( const idCmdArgs &args );
    118 	static void				Vstr_f( const idCmdArgs &args );
    119 	static void				Echo_f( const idCmdArgs &args );
    120 	static void				Parse_f( const idCmdArgs &args );
    121 	static void				Wait_f( const idCmdArgs &args );
    122 	static void				PrintMemInfo_f( const idCmdArgs &args );
    123 };
    124 
    125 idCmdSystemLocal			cmdSystemLocal;
    126 idCmdSystem *				cmdSystem = &cmdSystemLocal;
    127 
    128 /*
    129 ================================================
    130 idSort_CommandDef 
    131 ================================================
    132 */
    133 class idSort_CommandDef : public idSort_Quick< commandDef_t, idSort_CommandDef > {
    134 public:
    135 	int Compare( const commandDef_t & a, const commandDef_t & b ) const { return idStr::Icmp( a.name, b.name ); }
    136 };
    137 
    138 /*
    139 ============
    140 idCmdSystemLocal::ListByFlags
    141 ============
    142 */
    143 void idCmdSystemLocal::ListByFlags( const idCmdArgs &args, cmdFlags_t flags ) {
    144 	int i;
    145 	idStr match;
    146 	const commandDef_t *cmd;
    147 	idList<const commandDef_t *> cmdList;
    148 
    149 	if ( args.Argc() > 1 ) {
    150 		match = args.Args( 1, -1 );
    151 		match.Replace( " ", "" );
    152 	} else {
    153 		match = "";
    154 	}
    155 
    156 	for ( cmd = cmdSystemLocal.GetCommands(); cmd; cmd = cmd->next ) {
    157 		if ( !( cmd->flags & flags ) ) {
    158 			continue;
    159 		}
    160 		if ( match.Length() && idStr( cmd->name ).Filter( match, false ) == 0 ) {
    161 			continue;
    162 		}
    163 
    164 		cmdList.Append( cmd );
    165 	}
    166 
    167 	//cmdList.SortWithTemplate( idSort_CommandDef() );
    168 
    169 	for ( i = 0; i < cmdList.Num(); i++ ) {
    170 		cmd = cmdList[i];
    171 
    172 		common->Printf( "  %-21s %s\n", cmd->name, cmd->description );
    173 	}
    174 
    175 	common->Printf( "%i commands\n", cmdList.Num() );
    176 }
    177 
    178 /*
    179 ============
    180 idCmdSystemLocal::List_f
    181 ============
    182 */
    183 void idCmdSystemLocal::List_f( const idCmdArgs &args ) {
    184 	idCmdSystemLocal::ListByFlags( args, CMD_FL_ALL );
    185 }
    186 
    187 /*
    188 ============
    189 idCmdSystemLocal::SystemList_f
    190 ============
    191 */
    192 void idCmdSystemLocal::SystemList_f( const idCmdArgs &args ) {
    193 	idCmdSystemLocal::ListByFlags( args, CMD_FL_SYSTEM );
    194 }
    195 
    196 /*
    197 ============
    198 idCmdSystemLocal::RendererList_f
    199 ============
    200 */
    201 void idCmdSystemLocal::RendererList_f( const idCmdArgs &args ) {
    202 	idCmdSystemLocal::ListByFlags( args, CMD_FL_RENDERER );
    203 }
    204 
    205 /*
    206 ============
    207 idCmdSystemLocal::SoundList_f
    208 ============
    209 */
    210 void idCmdSystemLocal::SoundList_f( const idCmdArgs &args ) {
    211 	idCmdSystemLocal::ListByFlags( args, CMD_FL_SOUND );
    212 }
    213 
    214 /*
    215 ============
    216 idCmdSystemLocal::GameList_f
    217 ============
    218 */
    219 void idCmdSystemLocal::GameList_f( const idCmdArgs &args ) {
    220 	idCmdSystemLocal::ListByFlags( args, CMD_FL_GAME );
    221 }
    222 
    223 /*
    224 ============
    225 idCmdSystemLocal::ToolList_f
    226 ============
    227 */
    228 void idCmdSystemLocal::ToolList_f( const idCmdArgs &args ) {
    229 	idCmdSystemLocal::ListByFlags( args, CMD_FL_TOOL );
    230 }
    231 
    232 /*
    233 ===============
    234 idCmdSystemLocal::Exec_f
    235 ===============
    236 */
    237 void idCmdSystemLocal::Exec_f( const idCmdArgs &args ) {
    238 	char *	f;
    239 	int		len;
    240 	idStr	filename;
    241 
    242 	if ( args.Argc () != 2 ) {
    243 		common->Printf( "exec <filename> : execute a script file\n" );
    244 		return;
    245 	}
    246 
    247 	filename = args.Argv(1);
    248 	filename.DefaultFileExtension( ".cfg" );
    249 	len = fileSystem->ReadFile( filename, reinterpret_cast<void **>(&f), NULL );
    250 	if ( !f ) {
    251 		common->Printf( "couldn't exec %s\n", args.Argv(1) );
    252 		return;
    253 	}
    254 	common->Printf( "execing %s\n", args.Argv(1) );
    255 	
    256 	cmdSystemLocal.BufferCommandText( CMD_EXEC_INSERT, f );
    257 
    258 	fileSystem->FreeFile( f );
    259 }
    260 
    261 /*
    262 ===============
    263 idCmdSystemLocal::Vstr_f
    264 
    265 Inserts the current value of a cvar as command text
    266 ===============
    267 */
    268 void idCmdSystemLocal::Vstr_f( const idCmdArgs &args ) {
    269 	const char *v;
    270 
    271 	if ( args.Argc () != 2 ) {
    272 		common->Printf( "vstr <variablename> : execute a variable command\n" );
    273 		return;
    274 	}
    275 
    276 	v = cvarSystem->GetCVarString( args.Argv( 1 ) );
    277 
    278 	cmdSystemLocal.BufferCommandText( CMD_EXEC_APPEND, va( "%s\n", v ) );
    279 }
    280 
    281 /*
    282 ===============
    283 idCmdSystemLocal::Echo_f
    284 
    285 Just prints the rest of the line to the console
    286 ===============
    287 */
    288 void idCmdSystemLocal::Echo_f( const idCmdArgs &args ) {
    289 	int		i;
    290 	
    291 	for ( i = 1; i < args.Argc(); i++ ) {
    292 		common->Printf( "%s ", args.Argv( i ) );
    293 	}
    294 	common->Printf( "\n" );
    295 }
    296 
    297 /*
    298 ============
    299 idCmdSystemLocal::Wait_f
    300 
    301 Causes execution of the remainder of the command buffer to be delayed until next frame.
    302 ============
    303 */
    304 void idCmdSystemLocal::Wait_f( const idCmdArgs &args ) {
    305 	if ( args.Argc() == 2 ) {
    306 		cmdSystemLocal.SetWait( atoi( args.Argv( 1 ) ) );
    307 	} else {
    308 		cmdSystemLocal.SetWait( 1 );
    309 	}
    310 }
    311 
    312 /*
    313 ============
    314 idCmdSystemLocal::Parse_f
    315 
    316 This just prints out how the rest of the line was parsed, as a debugging tool.
    317 ============
    318 */
    319 void idCmdSystemLocal::Parse_f( const idCmdArgs &args ) {
    320 	int		i;
    321 
    322 	for ( i = 0; i < args.Argc(); i++ ) {
    323 		common->Printf( "%i: %s\n", i, args.Argv(i) );
    324 	}
    325 }
    326 
    327 /*
    328 ============
    329 idCmdSystemLocal::Init
    330 ============
    331 */
    332 void idCmdSystemLocal::Init() {
    333 
    334 	AddCommand( "listCmds", List_f, CMD_FL_SYSTEM, "lists commands" );
    335 	AddCommand( "listSystemCmds", SystemList_f, CMD_FL_SYSTEM, "lists system commands" );
    336 	AddCommand( "listRendererCmds", RendererList_f, CMD_FL_SYSTEM, "lists renderer commands" );
    337 	AddCommand( "listSoundCmds", SoundList_f, CMD_FL_SYSTEM, "lists sound commands" );
    338 	AddCommand( "listGameCmds", GameList_f, CMD_FL_SYSTEM, "lists game commands" );
    339 	AddCommand( "listToolCmds", ToolList_f, CMD_FL_SYSTEM, "lists tool commands" );
    340 	AddCommand( "exec", Exec_f, CMD_FL_SYSTEM, "executes a config file", ArgCompletion_ConfigName );
    341 	AddCommand( "vstr", Vstr_f, CMD_FL_SYSTEM, "inserts the current value of a cvar as command text" );
    342 	AddCommand( "echo", Echo_f, CMD_FL_SYSTEM, "prints text" );
    343 	AddCommand( "parse", Parse_f, CMD_FL_SYSTEM, "prints tokenized string" );
    344 	AddCommand( "wait", Wait_f, CMD_FL_SYSTEM, "delays remaining buffered commands one or more frames" );
    345 
    346 	// link in all the commands declared with static idCommandLink variables or CONSOLE_COMMAND macros
    347 	for ( idCommandLink * link = CommandLinks(); link != NULL; link = link->next ) {
    348 		AddCommand( link->cmdName_, link->function_, CMD_FL_SYSTEM, link->description_, link->argCompletion_ );
    349 	}
    350 
    351 	completionString = "*";
    352 
    353 	textLength = 0;
    354 }
    355 
    356 /*
    357 ============
    358 idCmdSystemLocal::Shutdown
    359 ============
    360 */
    361 void idCmdSystemLocal::Shutdown() {
    362 	commandDef_t *cmd;
    363 
    364 	for ( cmd = commands; cmd; cmd = commands ) {
    365 		commands = commands->next;
    366 		Mem_Free( cmd->name );
    367 		Mem_Free( cmd->description );
    368 		delete cmd;
    369 	}
    370 
    371 	completionString.Clear();
    372 	completionParms.Clear();
    373 	tokenizedCmds.Clear();
    374 	postReload.Clear();
    375 }
    376 
    377 /*
    378 ============
    379 idCmdSystemLocal::AddCommand
    380 ============
    381 */
    382 void idCmdSystemLocal::AddCommand( const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion ) {
    383 	commandDef_t *cmd;
    384 	
    385 	// fail if the command already exists
    386 	for ( cmd = commands; cmd; cmd = cmd->next ) {
    387 		if ( idStr::Cmp( cmdName, cmd->name ) == 0 ) {
    388 			if ( function != cmd->function ) {
    389 				common->Printf( "idCmdSystemLocal::AddCommand: %s already defined\n", cmdName );
    390 			}
    391 			return;
    392 		}
    393 	}
    394 
    395 	cmd = new (TAG_SYSTEM) commandDef_t;
    396 	cmd->name = Mem_CopyString( cmdName );
    397 	cmd->function = function;
    398 	cmd->argCompletion = argCompletion;
    399 	cmd->flags = flags;
    400 	cmd->description = Mem_CopyString( description );
    401 	cmd->next = commands;
    402 	commands = cmd;
    403 }
    404 
    405 /*
    406 ============
    407 idCmdSystemLocal::RemoveCommand
    408 ============
    409 */
    410 void idCmdSystemLocal::RemoveCommand( const char *cmdName ) {
    411 	commandDef_t *cmd, **last;
    412 
    413 	for ( last = &commands, cmd = *last; cmd; cmd = *last ) {
    414 		if ( idStr::Cmp( cmdName, cmd->name ) == 0 ) {
    415 			*last = cmd->next;
    416 			Mem_Free( cmd->name );
    417 			Mem_Free( cmd->description );
    418 			delete cmd;
    419 			return;
    420 		}
    421 		last = &cmd->next;
    422 	}
    423 }
    424 
    425 /*
    426 ============
    427 idCmdSystemLocal::RemoveFlaggedCommands
    428 ============
    429 */
    430 void idCmdSystemLocal::RemoveFlaggedCommands( int flags ) {
    431 	commandDef_t *cmd, **last;
    432 
    433 	for ( last = &commands, cmd = *last; cmd; cmd = *last ) {
    434 		if ( cmd->flags & flags ) {
    435 			*last = cmd->next;
    436 			Mem_Free( cmd->name );
    437 			Mem_Free( cmd->description );
    438 			delete cmd;
    439 			continue;
    440 		}
    441 		last = &cmd->next;
    442 	}
    443 }
    444 
    445 /*
    446 ============
    447 idCmdSystemLocal::CommandCompletion
    448 ============
    449 */
    450 void idCmdSystemLocal::CommandCompletion( void(*callback)( const char *s ) ) {
    451 	commandDef_t *cmd;
    452 	
    453 	for ( cmd = commands; cmd; cmd = cmd->next ) {
    454 		callback( cmd->name );
    455 	}
    456 }
    457 
    458 /*
    459 ============
    460 idCmdSystemLocal::ArgCompletion
    461 ============
    462 */
    463 void idCmdSystemLocal::ArgCompletion( const char *cmdString, void(*callback)( const char *s ) ) {
    464 	commandDef_t *cmd;
    465 	idCmdArgs args;
    466 
    467 	args.TokenizeString( cmdString, false );
    468 
    469 	for ( cmd = commands; cmd; cmd = cmd->next ) {
    470 		if ( !cmd->argCompletion ) {
    471 			continue;
    472 		}
    473 		if ( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) {
    474 			cmd->argCompletion( args, callback );
    475 			break;
    476 		}
    477 	}
    478 }
    479 
    480 /*
    481 ============
    482 idCmdSystemLocal::ExecuteTokenizedString
    483 ============
    484 */
    485 void idCmdSystemLocal::ExecuteTokenizedString( const idCmdArgs &args ) {	
    486 	commandDef_t *cmd, **prev;
    487 	
    488 	// execute the command line
    489 	if ( !args.Argc() ) {
    490 		return;		// no tokens
    491 	}
    492 
    493 	// check registered command functions	
    494 	for ( prev = &commands; *prev; prev = &cmd->next ) {
    495 		cmd = *prev;
    496 		if ( idStr::Icmp( args.Argv( 0 ), cmd->name ) == 0 ) {
    497 			// rearrange the links so that the command will be
    498 			// near the head of the list next time it is used
    499 			*prev = cmd->next;
    500 			cmd->next = commands;
    501 			commands = cmd;
    502 
    503 			if ( ( cmd->flags & (CMD_FL_CHEAT|CMD_FL_TOOL) ) && common->IsMultiplayer() && !net_allowCheats.GetBool() ) {
    504 				common->Printf( "Command '%s' not valid in multiplayer mode.\n", cmd->name );
    505 				return;
    506 			}
    507 			// perform the action
    508 			if ( !cmd->function ) {
    509 				break;
    510 			} else {
    511 				cmd->function( args );
    512 			}
    513 			return;
    514 		}
    515 	}
    516 	
    517 	// check cvars
    518 	if ( cvarSystem->Command( args ) ) {
    519 		return;
    520 	}
    521 
    522 	common->Printf( "Unknown command '%s'\n", args.Argv( 0 ) );
    523 }
    524 
    525 /*
    526 ============
    527 idCmdSystemLocal::ExecuteCommandText
    528 
    529 Tokenizes, then executes.
    530 ============
    531 */
    532 void idCmdSystemLocal::ExecuteCommandText( const char *text ) {	
    533 	ExecuteTokenizedString( idCmdArgs( text, false ) );
    534 }
    535 
    536 /*
    537 ============
    538 idCmdSystemLocal::InsertCommandText
    539 
    540 Adds command text immediately after the current command
    541 Adds a \n to the text
    542 ============
    543 */
    544 void idCmdSystemLocal::InsertCommandText( const char *text ) {
    545 	int		len;
    546 	int		i;
    547 
    548 	len = strlen( text ) + 1;
    549 	if ( len + textLength > (int)sizeof( textBuf ) ) {
    550 		common->Printf( "idCmdSystemLocal::InsertText: buffer overflow\n" );
    551 		return;
    552 	}
    553 
    554 	// move the existing command text
    555 	for ( i = textLength - 1; i >= 0; i-- ) {
    556 		textBuf[ i + len ] = textBuf[ i ];
    557 	}
    558 
    559 	// copy the new text in
    560 	memcpy( textBuf, text, len - 1 );
    561 
    562 	// add a \n
    563 	textBuf[ len - 1 ] = '\n';
    564 
    565 	textLength += len;
    566 }
    567 
    568 /*
    569 ============
    570 idCmdSystemLocal::AppendCommandText
    571 
    572 Adds command text at the end of the buffer, does NOT add a final \n
    573 ============
    574 */
    575 void idCmdSystemLocal::AppendCommandText( const char *text ) {
    576 	int l;
    577 	
    578 	l = strlen( text );
    579 
    580 	if ( textLength + l >= (int)sizeof( textBuf ) ) {
    581 		common->Printf( "idCmdSystemLocal::AppendText: buffer overflow\n" );
    582 		return;
    583 	}
    584 	memcpy( textBuf + textLength, text, l );
    585 	textLength += l;
    586 }
    587 
    588 /*
    589 ============
    590 idCmdSystemLocal::BufferCommandText
    591 ============
    592 */
    593 void idCmdSystemLocal::BufferCommandText( cmdExecution_t exec, const char *text ) {
    594 	switch( exec ) {
    595 		case CMD_EXEC_NOW: {
    596 			ExecuteCommandText( text );
    597 			break;
    598 		}
    599 		case CMD_EXEC_INSERT: {
    600 			InsertCommandText( text );
    601 			break;
    602 		}
    603 		case CMD_EXEC_APPEND: {
    604 			AppendCommandText( text );
    605 			break;
    606 		}
    607 		default: {
    608 			common->FatalError( "idCmdSystemLocal::BufferCommandText: bad exec type" );
    609 		}
    610 	}
    611 }
    612 
    613 /*
    614 ============
    615 idCmdSystemLocal::BufferCommandArgs
    616 ============
    617 */
    618 void idCmdSystemLocal::BufferCommandArgs( cmdExecution_t exec, const idCmdArgs &args ) {
    619 	switch ( exec ) {
    620 		case CMD_EXEC_NOW: {
    621 			ExecuteTokenizedString( args );
    622 			break;
    623 		}
    624 		case CMD_EXEC_APPEND: {
    625 			AppendCommandText( "_execTokenized\n" );
    626 			tokenizedCmds.Append( args );
    627 			break;
    628 		}
    629 		default: {
    630 			common->FatalError( "idCmdSystemLocal::BufferCommandArgs: bad exec type" );
    631 		}
    632 	}
    633 }
    634 
    635 /*
    636 ============
    637 idCmdSystemLocal::ExecuteCommandBuffer
    638 ============
    639 */
    640 void idCmdSystemLocal::ExecuteCommandBuffer() {
    641 	int			i;
    642 	char *		text;
    643 	int			quotes;
    644 	idCmdArgs	args;
    645 
    646 	while( textLength ) {
    647 
    648 		if ( wait )	{
    649 			// skip out while text still remains in buffer, leaving it for next frame
    650 			wait--;
    651 			break;
    652 		}
    653 
    654 		// find a \n or ; line break
    655 		text = (char *)textBuf;
    656 
    657 		quotes = 0;
    658 		for ( i = 0; i < textLength; i++ ) {
    659 			if ( text[i] == '"' ) {
    660 				quotes++;
    661 			}
    662 			if ( !( quotes & 1 ) &&  text[i] == ';' ) {
    663 				break;	// don't break if inside a quoted string
    664 			}
    665 			if ( text[i] == '\n' || text[i] == '\r' ) {
    666 				break;
    667 			}
    668 		}
    669 			
    670 		text[i] = 0;
    671 
    672 		if ( !idStr::Cmp( text, "_execTokenized" ) ) {
    673 			args = tokenizedCmds[ 0 ];
    674 			tokenizedCmds.RemoveIndex( 0 );
    675 		} else {
    676 			args.TokenizeString( text, false );
    677 		}
    678 
    679 		// delete the text from the command buffer and move remaining commands down
    680 		// this is necessary because commands (exec) can insert data at the
    681 		// beginning of the text buffer
    682 
    683 		if ( i == textLength ) {
    684 			textLength = 0;
    685 		} else {
    686 			i++;
    687 			textLength -= i;
    688 			memmove( text, text+i, textLength );
    689 		}
    690 
    691 		// execute the command line that we have already tokenized
    692 		ExecuteTokenizedString( args );
    693 	}
    694 }
    695 
    696 /*
    697 ============
    698 idCmdSystemLocal::ArgCompletion_FolderExtension
    699 ============
    700 */
    701 void idCmdSystemLocal::ArgCompletion_FolderExtension( const idCmdArgs &args, void(*callback)( const char *s ), const char *folder, bool stripFolder, ... ) {
    702 	int i;
    703 	idStr string;
    704 	const char *extension;
    705 	va_list argPtr;
    706 
    707 	string = args.Argv( 0 );
    708 	string += " ";
    709 	string += args.Argv( 1 );
    710 
    711 	if ( string.Icmp( completionString ) != 0 ) {
    712 		idStr parm, path;
    713 		idFileList *names;
    714 
    715 		completionString = string;
    716 		completionParms.Clear();
    717 
    718 		parm = args.Argv( 1 );
    719 		parm.ExtractFilePath( path );
    720 		if ( stripFolder || path.Length() == 0 ) {
    721 			path = folder + path;
    722 		}
    723 		path.StripTrailing( '/' );
    724 
    725 		// list folders
    726 		names = fileSystem->ListFiles( path, "/", true, true );
    727 		for ( i = 0; i < names->GetNumFiles(); i++ ) {
    728 			idStr name = names->GetFile( i );
    729 			if ( stripFolder ) {
    730 				name.Strip( folder );
    731 			} else {
    732 				name.Strip( "/" );
    733 			}
    734 			name = args.Argv( 0 ) + ( " " + name ) + "/";
    735 			completionParms.Append( name );
    736 		}
    737 		fileSystem->FreeFileList( names );
    738 
    739 		// list files
    740 		va_start( argPtr, stripFolder );
    741 		for ( extension = va_arg( argPtr, const char * ); extension; extension = va_arg( argPtr, const char * ) ) {
    742 			names = fileSystem->ListFiles( path, extension, true, true );
    743 			for ( i = 0; i < names->GetNumFiles(); i++ ) {
    744 				idStr name = names->GetFile( i );
    745 				if ( stripFolder ) {
    746 					name.Strip( folder );
    747 				} else {
    748 					name.Strip( "/" );
    749 				}
    750 				name = args.Argv( 0 ) + ( " " + name );
    751 				completionParms.Append( name );
    752 			}
    753 			fileSystem->FreeFileList( names );
    754 		}
    755 		va_end( argPtr );
    756 	}
    757 	for ( i = 0; i < completionParms.Num(); i++ ) {
    758 		callback( completionParms[i] );
    759 	}
    760 }
    761 
    762 /*
    763 ============
    764 idCmdSystemLocal::ArgCompletion_DeclName
    765 ============
    766 */
    767 void idCmdSystemLocal::ArgCompletion_DeclName( const idCmdArgs &args, void(*callback)( const char *s ), int type ) {
    768 	int i, num;
    769 
    770 	if ( declManager == NULL ) {
    771 		return;
    772 	}
    773 	num = declManager->GetNumDecls( (declType_t)type );
    774 	for ( i = 0; i < num; i++ ) {
    775 		callback( idStr( args.Argv( 0 ) ) + " " + declManager->DeclByIndex( (declType_t)type, i , false )->GetName() );
    776 	}
    777 }
    778 
    779 /*
    780 ============
    781 idCmdSystemLocal::SetupReloadEngine
    782 ============
    783 */
    784 void idCmdSystemLocal::SetupReloadEngine( const idCmdArgs &args ) {
    785 	BufferCommandText( CMD_EXEC_APPEND, "reloadEngine\n" );
    786 	postReload = args;
    787 }
    788 
    789 /*
    790 ============
    791 idCmdSystemLocal::PostReloadEngine
    792 ============
    793 */
    794 bool idCmdSystemLocal::PostReloadEngine() {
    795 	if ( !postReload.Argc() ) {
    796 		return false;
    797 	}
    798 	BufferCommandArgs( CMD_EXEC_APPEND, postReload );
    799 	postReload.Clear();
    800 	return true;
    801 }