2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/DebugConsole/Console.cpp $
15 * Routines for managing the debug console window.
18 * Revision 1.4 2004/07/04 11:31:43 taylor
19 * amd64 support, compiler warning fixes, don't use software rendering
21 * Revision 1.3 2002/06/09 04:41:16 relnev
22 * added copyright header
24 * Revision 1.2 2002/05/07 03:16:43 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:08 root
31 * 4 6/04/99 10:35a Dave
33 * 3 10/13/98 9:28a Dave
34 * Started neatening up freespace.h. Many variables renamed and
35 * reorganized. Added AlphaColors.[h,cpp]
37 * 2 10/07/98 10:52a Dave
40 * 1 10/07/98 10:48a Dave
42 * 17 2/05/98 11:43a Allender
43 * enhcanced network statistic collection. Made callback in debug console
44 * to do networking if player is in the console
46 * 16 1/22/98 6:42p John
47 * Move game_flush out of debug console into freespace. Made object
48 * pair code throw out some weapons that turn. Added stats for how many
49 * object pair are actually checked.
51 * 15 1/16/98 11:56a Allender
52 * ability to scroll back in debug console, and call game_flush() when
55 * 14 1/10/98 1:14p John
56 * Added explanation to debug console commands
58 * 13 12/21/97 4:39p John
59 * fixed bug in name compare
61 * 12 12/21/97 4:33p John
62 * Made debug console functions a class that registers itself
63 * automatically, so you don't need to add the function to
66 * 11 9/13/97 9:31a Lawrance
67 * if playing a demo, clear key filter then reset it once done with debug
70 * 10 9/09/97 3:39p Sandeep
71 * warning level 4 bugs
73 * 9 6/13/97 3:50p John
74 * sped up debug console printf
76 * 8 6/13/97 3:27p John
77 * made debug console pretty
79 * 7 6/09/97 9:24a John
80 * Changed the way fonts are set.
82 * 6 5/29/97 3:09p John
83 * Took out debug menu.
84 * Made software scaler draw larger bitmaps.
85 * Optimized Direct3D some.
87 * 5 5/13/97 4:07p John
88 * made numbers also be marked as strings.
90 * 4 4/28/97 5:24p John
91 * made so : and \ don't get parsed out in debug console.
93 * 3 4/24/97 11:49a John
94 * added new lighting commands to console.
96 * 2 4/24/97 10:36a John
97 * moved the debug console stuff into it's own lib... made it compile with
100 * 1 4/24/97 10:16a John
112 #include "freespace.h"
117 #include "alphacolors.h"
120 #define MAX_COMMANDS 300
122 static int Num_debug_commands = 0;
123 static debug_command *Debug_command[MAX_COMMANDS];
126 debug_command::debug_command(const char *_name, const char *_help, void (*_func)() )
130 if ( Num_debug_commands >= MAX_COMMANDS ) {
131 Int3(); // Too many debug console commands!! Increase MAX_COMMANDS!!
135 for (i=0; i<Num_debug_commands; i++ ) {
136 int ret = SDL_strcasecmp( Debug_command[i]->name, _name );
139 Int3(); // This debug console command already exists!!!!
141 } else if ( ret > 0 ) {
142 break; // Insert it here
144 } else if ( ret < 0 ) {
149 if ( i < Num_debug_commands ) {
150 // Insert it at element i
152 for (j=Num_debug_commands; j>i; j-- ) {
153 Debug_command[j] = Debug_command[j-1];
155 Debug_command[i] = this;
156 Num_debug_commands++;
158 Debug_command[Num_debug_commands] = this;
159 Num_debug_commands++;
167 // some global variables
168 int Dc_command; // If this is set, then process the command
169 int Dc_help; // If this is set, then print out the help text in the form, "usage: ... \nLong description\n" );
170 int Dc_status; // If this is set, then print out the current status of the command.
171 char *Dc_arg; // The (lowercased) string value of the argument retrieved from dc_arg
172 char *Dc_arg_org; // Dc_arg before it got converted to lowercase
173 uint Dc_arg_type; // The type of dc_arg.
174 const char *Dc_command_line; // The rest of the command line, from the end of the last processed arg on.
175 int Dc_arg_int; // If Dc_arg_type & ARG_INT is set, then this is the value
176 float Dc_arg_float; // If Dc_arg_type & ARG_FLOAT is set, then this is the value
178 int scroll_times = 0; // incremented each time display scrolls
180 int debug_inited = 0;
185 int debug_x=0, debug_y=0;
186 char debug_text[DROWS][DCOLS];
189 static char command_line[1024];
190 static int command_line_pos = 0;
191 #define DEBUG_HISTORY 16
192 static char oldcommand_line[DEBUG_HISTORY][1024];
193 int last_oldcommand=-1;
194 int command_scroll = 0;
196 ///=========================== SCANNER =======================
198 LETTER, QUOTE, SPECIAL, EOF_CODE, DIGIT,
202 NO_TOKEN, IDENTIFIER, NUMBER, STRING,
206 #define MAX_TOKEN_STRING_LENGTH 128
209 TOKEN_CODE scanner_token;
211 char scanner_token_string[MAX_TOKEN_STRING_LENGTH];
212 char scanner_word_string[MAX_TOKEN_STRING_LENGTH];
213 const char * scanner_bufferp = "";
214 char * scanner_tokenp = scanner_token_string;
216 CHAR_CODE scanner_char_table[256];
218 #define scanner_char_code(x) scanner_char_table[x]
220 void scanner_get_char()
222 if ( *scanner_bufferp == '\0' ) {
226 scanner_ch = *scanner_bufferp++;
232 for (ch=0; ch<256; ++ch) scanner_char_table[ch] = SPECIAL;
233 for (ch='0'; ch<='9'; ++ch) scanner_char_table[ch] = DIGIT;
234 for (ch='A'; ch<='Z'; ++ch) scanner_char_table[ch] = LETTER;
235 for (ch='a'; ch<='z'; ++ch) scanner_char_table[ch] = LETTER;
237 scanner_char_table[(int)'.'] = DIGIT;
238 scanner_char_table[(int)'-'] = DIGIT;
239 scanner_char_table[(int)'+'] = DIGIT;
241 scanner_char_table[(int)'_'] = LETTER;
242 scanner_char_table[34] = QUOTE;
243 scanner_char_table[0] = EOF_CODE;
246 scanner_char_table[(int)':'] = LETTER;
247 scanner_char_table[(int)'\\'] = LETTER;
253 void scanner_skip_blanks()
255 while( (scanner_ch ==' ') || (scanner_ch =='\t') )
260 void scanner_downshift_word()
262 int offset = 'a' - 'A';
265 SDL_strlcpy( scanner_word_string, scanner_token_string, SDL_arraysize(scanner_word_string) );
267 tp = scanner_word_string;
269 *tp = (char)((*tp>='A') && (*tp <='Z') ? *tp + offset : *tp) ;
271 } while (*tp != '\0' );
275 void scanner_get_word()
277 while( (scanner_char_code((int)scanner_ch)==LETTER) || (scanner_char_code((int)scanner_ch)==DIGIT) ) {
278 *scanner_tokenp++ = scanner_ch;
281 *scanner_tokenp = '\0';
283 scanner_token = IDENTIFIER;
287 void scanner_get_string()
289 *scanner_tokenp++ = 34;
292 while(scanner_ch != 34 ) {
293 *scanner_tokenp++ = scanner_ch;
297 *scanner_tokenp++ = 34;
298 *scanner_tokenp = '\0';
299 scanner_token = STRING;
304 void scanner_get_token()
306 scanner_skip_blanks();
307 scanner_tokenp = scanner_token_string;
311 switch( scanner_char_code((int)scanner_ch) ) {
312 case QUOTE: scanner_get_string(); break;
313 case EOF_CODE: scanner_token = NO_TOKEN; break;
315 case LETTER: scanner_get_word(); break;
317 *scanner_tokenp++ = scanner_ch;
318 *scanner_tokenp = '\0';
320 scanner_token = IDENTIFIER;
324 scanner_downshift_word();
327 void scanner_start_command( const char * s )
336 void dc_get_arg(uint type)
340 Dc_command_line = scanner_bufferp;
341 Dc_arg_org = scanner_token_string;
342 Dc_arg = scanner_word_string;
345 dc_printf( "next arg is '%s', was originally '%s'\n", Dc_arg, Dc_arg_org );
346 dc_printf( "Rest of the command line is '%s'\n", Dc_command_line );
349 if ( scanner_token == NO_TOKEN ) {
350 Dc_arg_type = ARG_NONE;
351 } else if ( scanner_token == IDENTIFIER ) {
352 Dc_arg_type = ARG_STRING;
353 } else if ( scanner_token == STRING ) {
354 Dc_arg_type = ARG_QUOTE;
356 Dc_arg_type = ARG_STRING;
359 if ( Dc_arg_type & ARG_STRING ) {
360 int i, num_digits, len;
362 len = strlen(Dc_arg);
365 for (i=0; i<len; i++)
366 if ( scanner_char_table[(int)Dc_arg[i]] == DIGIT ) num_digits++;
368 if ( num_digits==len ) {
369 Dc_arg_type |= ARG_FLOAT;
370 Dc_arg_float = (float)atof(Dc_arg);
371 if ( !SDL_strchr( Dc_arg, '.' )) {
372 Dc_arg_type |= ARG_INT;
373 Dc_arg_int = atoi(Dc_arg);
376 if ( (Dc_arg[0] == '0') && (Dc_arg[1] == 'x') ) {
379 n = strtol(Dc_arg,&p,0);
381 Dc_arg_type |= ARG_INT|ARG_HEX;
388 if ( Dc_arg_type & ARG_FLOAT )
389 dc_printf( "Found float number! %f\n", Dc_arg_float );
391 if ( Dc_arg_type & ARG_INT )
392 dc_printf( "Found int number! %d\n", Dc_arg_int );
394 if ( Dc_arg_type & ARG_HEX )
395 dc_printf( "Found hex number! 0x%x\n", Dc_arg_int );
398 if ( !SDL_strcasecmp( Dc_arg, "on" ))
399 Dc_arg_type |= ARG_TRUE;
400 if ( !SDL_strcasecmp( Dc_arg, "true" ))
401 Dc_arg_type |= ARG_TRUE;
402 if ( !SDL_strcasecmp( Dc_arg, "off" ))
403 Dc_arg_type |= ARG_FALSE;
404 if ( !SDL_strcasecmp( Dc_arg, "false" ))
405 Dc_arg_type |= ARG_FALSE;
407 if ( !SDL_strcasecmp( Dc_arg, "+" ))
408 Dc_arg_type |= ARG_PLUS;
410 if ( !SDL_strcasecmp( Dc_arg, "-" ))
411 Dc_arg_type |= ARG_MINUS;
413 if ( !SDL_strcasecmp( Dc_arg, "," ))
414 Dc_arg_type |= ARG_COMMA;
417 if ( Dc_arg_type & ARG_INT) {
419 Dc_arg_type |= ARG_TRUE;
421 Dc_arg_type |= ARG_FALSE;
424 if ( !(Dc_arg_type&type) ) {
425 if ( (Dc_arg_type & ARG_NONE) && !(type & ARG_NONE))
426 dc_printf( "Error: Not enough parameters.\n" );
428 dc_printf( "Error: '%s' invalid type\n", Dc_arg );
437 void debug_do_command(const char * command)
443 if ( strlen(command) < 1 ) return;
446 Dc_command_line = command;
447 scanner_start_command(command);
450 dc_get_arg( ARG_ANY );
452 if ( !strcmp( Dc_arg, "debug" ) ) {
454 dc_printf( "Command line: '%s'\n", Dc_command_line );
455 dc_get_arg( ARG_ANY );
458 if ( !strcmp( Dc_arg, "?" ) ) {
460 dc_get_arg( ARG_ANY );
462 if ( Dc_arg_type&ARG_NONE ) {
468 if ( !strcmp( Dc_arg, "help" ) || !strcmp( Dc_arg, "man" ) ) {
470 dc_get_arg( ARG_ANY );
471 if ( Dc_arg_type&ARG_NONE ) {
477 if ( strstr( Dc_command_line, "?" ) ) {
481 if ( !(Dc_arg_type&ARG_STRING) ) {
482 dc_printf( "Invalid keyword '%s'\n", Dc_arg );
488 dc_printf( "Searching for command '%s'\n", Dc_arg );
491 for (i=0; i<Num_debug_commands; i++ ) {
492 if ( !SDL_strcasecmp( Debug_command[i]->name, Dc_arg )) {
496 dc_printf( "Calling function '%s'\n", Dc_arg );
500 } else if (mode==1) {
502 dc_printf( "Checking status for '%s'\n", Dc_arg );
508 dc_printf( "Doing help for '%s'\n", Dc_arg );
514 (*Debug_command[i]->func)();
518 if (!(Dc_arg_type&ARG_NONE)) {
519 dc_printf( "Ignoring the unused command line tail '%s %s'\n", Dc_arg_org, Dc_command_line );
530 dc_printf( "Unknown command '%s'\n", Dc_arg );
539 gr_set_color_fast( &Color_bright );
540 gr_string( 0x8000, 3, "Debug Console" );
542 gr_set_color_fast( &Color_normal );
544 for (i=0; i<DROWS; i++ ) {
545 gr_string( 0, i*16+16, debug_text[i] );
548 int t = timer_get_fixed_seconds() / (F1_0/3);
553 c = debug_text[debug_y][command_line_pos+1];
554 debug_text[debug_y][command_line_pos+1] = 0;
556 gr_get_string_size( &w, &h, debug_text[debug_y] );
558 //gr_string( w, debug_y*16, "_" );
559 gr_rect(w+1,debug_y*16+1+16,2,14);
561 debug_text[debug_y][command_line_pos+1] = c;
568 void debug_output( char c )
571 int next_tab = ((debug_x/28)+1)*28;
573 if ( next_tab >= DCOLS-1 ) {
577 if ( debug_y >= DROWS ) {
579 for (i=1; i<DROWS; i++ )
580 SDL_strlcpy( debug_text[i-1], debug_text[i], DCOLS );
583 debug_text[debug_y][debug_x] = 0;
585 debug_text[debug_y][debug_x] = 0;
589 for ( ; debug_x < next_tab; )
590 debug_text[debug_y][debug_x++] = ' ';
591 debug_text[debug_y][debug_x] = 0;
595 if ( (c == '\n') || (debug_x >= DCOLS-1) ) {
599 if ( debug_y >= DROWS ) {
601 for (i=1; i<DROWS; i++ )
602 SDL_strlcpy( debug_text[i-1], debug_text[i], DCOLS );
605 debug_text[debug_y][debug_x] = 0;
607 debug_text[debug_y][debug_x] = 0;
608 if ( c == '\n' ) return;
611 debug_text[debug_y][debug_x++] = c;
612 debug_text[debug_y][debug_x] = 0;
615 void dc_printf(const char *format, ...)
620 va_start(args, format);
621 SDL_vsnprintf(tmp, SDL_arraysize(tmp), format, args);
636 if ( debug_inited ) return;
643 for (i=0; i<DROWS; i++ ) {
644 debug_text[i][0] = 0;
647 dc_printf("Debug console started.\n" );
651 void debug_console( void (*_func)() )
657 while( key_inkey() ){
661 if ( !debug_inited ) debug_init();
673 case KEY_SHIFTED+SDLK_RETURN:
678 if ( command_line_pos > 0 ) {
679 command_line[--command_line_pos] = 0;
684 if ( last_oldcommand > -1 ) {
685 SDL_strlcpy( command_line, oldcommand_line[last_oldcommand], SDL_arraysize(command_line) );
686 command_line_pos = strlen(command_line);
687 command_line[command_line_pos] = 0;
693 if (command_scroll<0)
694 command_scroll = last_oldcommand;
696 if ( command_scroll > -1 ) {
697 SDL_strlcpy( command_line, oldcommand_line[command_scroll], SDL_arraysize(command_line) );
698 command_line_pos = strlen(command_line);
699 command_line[command_line_pos] = 0;
705 if (command_scroll>last_oldcommand)
707 if (command_scroll>last_oldcommand)
709 if ( command_scroll > -1 ) {
710 SDL_strlcpy( command_line, oldcommand_line[command_scroll], SDL_arraysize(command_line) );
711 command_line_pos = strlen(command_line);
712 command_line[command_line_pos] = 0;
717 debug_output( '\n' );
720 debug_do_command(command_line);
723 for (i=0; i<=last_oldcommand; i++ ) {
724 if (!SDL_strcasecmp( oldcommand_line[i], command_line )) {
729 if ( last_oldcommand < DEBUG_HISTORY-1 ) {
731 SDL_strlcpy( oldcommand_line[last_oldcommand], command_line, SDL_arraysize(oldcommand_line[0]) );
733 for (i=0; i<last_oldcommand; i++ ) {
734 SDL_strlcpy( oldcommand_line[i], oldcommand_line[i+1], SDL_arraysize(oldcommand_line[0]) );
736 SDL_strlcpy( oldcommand_line[last_oldcommand], command_line, SDL_arraysize(oldcommand_line[0]) );
740 // for (i=0; i<=last_oldcommand; i++ ) {
741 // dc_printf( "OC %d. %s\n", i, oldcommand_line[i] );
744 debug_output( '\n' );
745 command_line_pos = 0;
746 command_line[command_line_pos] = 0;
753 int c = key_get_text_input();
754 if ( (c >= 0) && (c < 255) ) {
755 command_line[command_line_pos++] = (ubyte)c;
756 command_line[command_line_pos] = 0;
763 SDL_strlcpy( debug_text[debug_y], ">", DCOLS );
764 SDL_strlcat( debug_text[debug_y], command_line, DCOLS );
772 while( key_inkey() ){
781 dc_printf( "Available functions:\n\n" );
784 for (i=0; i<Num_debug_commands; i++ ) {
785 dc_printf( " %s - %s\n", Debug_command[i]->name, Debug_command[i]->help );
786 //mprintf(( "Scroll times %d\n", scroll_times - s ));
787 if ( scroll_times - s > DROWS - 3 ) {
789 dc_printf( " Press a key...B for back\n" );
803 dc_printf( "Typing '? function_name' will give the current status.\n" );
804 dc_printf( "Typing 'function_name ?' will give help on the function.\n" );
805 dc_printf( "Typing ? or help will give you help.\n");
806 dc_printf( "F3 selects last command line.\n" );