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
113 #include "freespace.h"
118 #include "alphacolors.h"
121 #define MAX_COMMANDS 300
123 static int Num_debug_commands = 0;
124 static debug_command *Debug_command[MAX_COMMANDS];
127 debug_command::debug_command(const char *_name, const char *_help, void (*_func)() )
131 if ( Num_debug_commands >= MAX_COMMANDS ) {
132 Int3(); // Too many debug console commands!! Increase MAX_COMMANDS!!
136 for (i=0; i<Num_debug_commands; i++ ) {
137 int ret = SDL_strcasecmp( Debug_command[i]->name, _name );
140 Int3(); // This debug console command already exists!!!!
142 } else if ( ret > 0 ) {
143 break; // Insert it here
145 } else if ( ret < 0 ) {
150 if ( i < Num_debug_commands ) {
151 // Insert it at element i
153 for (j=Num_debug_commands; j>i; j-- ) {
154 Debug_command[j] = Debug_command[j-1];
156 Debug_command[i] = this;
157 Num_debug_commands++;
159 Debug_command[Num_debug_commands] = this;
160 Num_debug_commands++;
168 // some global variables
169 int Dc_command; // If this is set, then process the command
170 int Dc_help; // If this is set, then print out the help text in the form, "usage: ... \nLong description\n" );
171 int Dc_status; // If this is set, then print out the current status of the command.
172 char *Dc_arg; // The (lowercased) string value of the argument retrieved from dc_arg
173 char *Dc_arg_org; // Dc_arg before it got converted to lowercase
174 uint Dc_arg_type; // The type of dc_arg.
175 const char *Dc_command_line; // The rest of the command line, from the end of the last processed arg on.
176 int Dc_arg_int; // If Dc_arg_type & ARG_INT is set, then this is the value
177 float Dc_arg_float; // If Dc_arg_type & ARG_FLOAT is set, then this is the value
179 int scroll_times = 0; // incremented each time display scrolls
181 int debug_inited = 0;
186 int debug_x=0, debug_y=0;
187 char debug_text[DROWS][DCOLS];
190 static char command_line[1024];
191 static int command_line_pos = 0;
192 #define DEBUG_HISTORY 16
193 static char oldcommand_line[DEBUG_HISTORY][1024];
194 int last_oldcommand=-1;
195 int command_scroll = 0;
197 ///=========================== SCANNER =======================
199 LETTER, QUOTE, SPECIAL, EOF_CODE, DIGIT,
203 NO_TOKEN, IDENTIFIER, NUMBER, STRING,
207 #define MAX_TOKEN_STRING_LENGTH 128
210 TOKEN_CODE scanner_token;
212 char scanner_token_string[MAX_TOKEN_STRING_LENGTH];
213 char scanner_word_string[MAX_TOKEN_STRING_LENGTH];
214 const char * scanner_bufferp = "";
215 char * scanner_tokenp = scanner_token_string;
217 CHAR_CODE scanner_char_table[256];
219 #define scanner_char_code(x) scanner_char_table[x]
221 void scanner_get_char()
223 if ( *scanner_bufferp == '\0' ) {
227 scanner_ch = *scanner_bufferp++;
233 for (ch=0; ch<256; ++ch) scanner_char_table[ch] = SPECIAL;
234 for (ch='0'; ch<='9'; ++ch) scanner_char_table[ch] = DIGIT;
235 for (ch='A'; ch<='Z'; ++ch) scanner_char_table[ch] = LETTER;
236 for (ch='a'; ch<='z'; ++ch) scanner_char_table[ch] = LETTER;
238 scanner_char_table[(int)'.'] = DIGIT;
239 scanner_char_table[(int)'-'] = DIGIT;
240 scanner_char_table[(int)'+'] = DIGIT;
242 scanner_char_table[(int)'_'] = LETTER;
243 scanner_char_table[34] = QUOTE;
244 scanner_char_table[0] = EOF_CODE;
247 scanner_char_table[(int)':'] = LETTER;
248 scanner_char_table[(int)'\\'] = LETTER;
254 void scanner_skip_blanks()
256 while( (scanner_ch ==' ') || (scanner_ch =='\t') )
261 void scanner_downshift_word()
263 int offset = 'a' - 'A';
266 SDL_strlcpy( scanner_word_string, scanner_token_string, sizeof(scanner_word_string) );
268 tp = scanner_word_string;
270 *tp = (char)((*tp>='A') && (*tp <='Z') ? *tp + offset : *tp) ;
272 } while (*tp != '\0' );
276 void scanner_get_word()
278 while( (scanner_char_code((int)scanner_ch)==LETTER) || (scanner_char_code((int)scanner_ch)==DIGIT) ) {
279 *scanner_tokenp++ = scanner_ch;
282 *scanner_tokenp = '\0';
284 scanner_token = IDENTIFIER;
288 void scanner_get_string()
290 *scanner_tokenp++ = 34;
293 while(scanner_ch != 34 ) {
294 *scanner_tokenp++ = scanner_ch;
298 *scanner_tokenp++ = 34;
299 *scanner_tokenp = '\0';
300 scanner_token = STRING;
305 void scanner_get_token()
307 scanner_skip_blanks();
308 scanner_tokenp = scanner_token_string;
312 switch( scanner_char_code((int)scanner_ch) ) {
313 case QUOTE: scanner_get_string(); break;
314 case EOF_CODE: scanner_token = NO_TOKEN; break;
316 case LETTER: scanner_get_word(); break;
318 *scanner_tokenp++ = scanner_ch;
319 *scanner_tokenp = '\0';
321 scanner_token = IDENTIFIER;
325 scanner_downshift_word();
328 void scanner_start_command( const char * s )
338 void dc_get_arg(uint type)
342 Dc_command_line = scanner_bufferp;
343 Dc_arg_org = scanner_token_string;
344 Dc_arg = scanner_word_string;
347 dc_printf( "next arg is '%s', was originally '%s'\n", Dc_arg, Dc_arg_org );
348 dc_printf( "Rest of the command line is '%s'\n", Dc_command_line );
351 if ( scanner_token == NO_TOKEN ) {
352 Dc_arg_type = ARG_NONE;
353 } else if ( scanner_token == IDENTIFIER ) {
354 Dc_arg_type = ARG_STRING;
355 } else if ( scanner_token == STRING ) {
356 Dc_arg_type = ARG_QUOTE;
358 Dc_arg_type = ARG_STRING;
361 if ( Dc_arg_type & ARG_STRING ) {
362 int i, num_digits, len;
364 len = strlen(Dc_arg);
367 for (i=0; i<len; i++)
368 if ( scanner_char_table[(int)Dc_arg[i]] == DIGIT ) num_digits++;
370 if ( num_digits==len ) {
371 Dc_arg_type |= ARG_FLOAT;
372 Dc_arg_float = (float)atof(Dc_arg);
373 if ( !SDL_strchr( Dc_arg, '.' )) {
374 Dc_arg_type |= ARG_INT;
375 Dc_arg_int = atoi(Dc_arg);
378 if ( (Dc_arg[0] == '0') && (Dc_arg[1] == 'x') ) {
381 n = strtol(Dc_arg,&p,0);
383 Dc_arg_type |= ARG_INT|ARG_HEX;
390 if ( Dc_arg_type & ARG_FLOAT )
391 dc_printf( "Found float number! %f\n", Dc_arg_float );
393 if ( Dc_arg_type & ARG_INT )
394 dc_printf( "Found int number! %d\n", Dc_arg_int );
396 if ( Dc_arg_type & ARG_HEX )
397 dc_printf( "Found hex number! 0x%x\n", Dc_arg_int );
400 if ( !SDL_strcasecmp( Dc_arg, "on" ))
401 Dc_arg_type |= ARG_TRUE;
402 if ( !SDL_strcasecmp( Dc_arg, "true" ))
403 Dc_arg_type |= ARG_TRUE;
404 if ( !SDL_strcasecmp( Dc_arg, "off" ))
405 Dc_arg_type |= ARG_FALSE;
406 if ( !SDL_strcasecmp( Dc_arg, "false" ))
407 Dc_arg_type |= ARG_FALSE;
409 if ( !SDL_strcasecmp( Dc_arg, "+" ))
410 Dc_arg_type |= ARG_PLUS;
412 if ( !SDL_strcasecmp( Dc_arg, "-" ))
413 Dc_arg_type |= ARG_MINUS;
415 if ( !SDL_strcasecmp( Dc_arg, "," ))
416 Dc_arg_type |= ARG_COMMA;
419 if ( Dc_arg_type & ARG_INT) {
421 Dc_arg_type |= ARG_TRUE;
423 Dc_arg_type |= ARG_FALSE;
426 if ( !(Dc_arg_type&type) ) {
427 if ( (Dc_arg_type & ARG_NONE) && !(type & ARG_NONE))
428 dc_printf( "Error: Not enough parameters.\n" );
430 dc_printf( "Error: '%s' invalid type\n", Dc_arg );
431 longjmp(dc_bad_arg,1);
438 void debug_do_command(const char * command)
444 if ( strlen(command) < 1 ) return;
447 Dc_command_line = command;
448 scanner_start_command(command);
450 if (setjmp(dc_bad_arg) ) {
454 dc_get_arg( ARG_ANY );
456 if ( !strcmp( Dc_arg, "debug" ) ) {
458 dc_printf( "Command line: '%s'\n", Dc_command_line );
459 dc_get_arg( ARG_ANY );
462 if ( !strcmp( Dc_arg, "?" ) ) {
464 dc_get_arg( ARG_ANY );
466 if ( Dc_arg_type&ARG_NONE ) {
472 if ( !strcmp( Dc_arg, "help" ) || !strcmp( Dc_arg, "man" ) ) {
474 dc_get_arg( ARG_ANY );
475 if ( Dc_arg_type&ARG_NONE ) {
481 if ( strstr( Dc_command_line, "?" ) ) {
485 if ( !(Dc_arg_type&ARG_STRING) ) {
486 dc_printf( "Invalid keyword '%s'\n", Dc_arg );
492 dc_printf( "Searching for command '%s'\n", Dc_arg );
495 for (i=0; i<Num_debug_commands; i++ ) {
496 if ( !SDL_strcasecmp( Debug_command[i]->name, Dc_arg )) {
500 dc_printf( "Calling function '%s'\n", Dc_arg );
504 } else if (mode==1) {
506 dc_printf( "Checking status for '%s'\n", Dc_arg );
512 dc_printf( "Doing help for '%s'\n", Dc_arg );
518 (*Debug_command[i]->func)();
522 if (!(Dc_arg_type&ARG_NONE)) {
523 dc_printf( "Ignoring the unused command line tail '%s %s'\n", Dc_arg_org, Dc_command_line );
531 dc_printf( "Unknown command '%s'\n", Dc_arg );
540 gr_set_color_fast( &Color_bright );
541 gr_string( 0x8000, 3, "Debug Console" );
543 gr_set_color_fast( &Color_normal );
545 for (i=0; i<DROWS; i++ ) {
546 gr_string( 0, i*16+16, debug_text[i] );
549 int t = timer_get_fixed_seconds() / (F1_0/3);
554 c = debug_text[debug_y][command_line_pos+1];
555 debug_text[debug_y][command_line_pos+1] = 0;
557 gr_get_string_size( &w, &h, debug_text[debug_y] );
559 //gr_string( w, debug_y*16, "_" );
560 gr_rect(w+1,debug_y*16+1+16,2,14);
562 debug_text[debug_y][command_line_pos+1] = c;
569 void debug_output( char c )
572 int next_tab = ((debug_x/28)+1)*28;
574 if ( next_tab >= DCOLS-1 ) {
578 if ( debug_y >= DROWS ) {
580 for (i=1; i<DROWS; i++ )
581 SDL_strlcpy( debug_text[i-1], debug_text[i], DCOLS );
584 debug_text[debug_y][debug_x] = 0;
586 debug_text[debug_y][debug_x] = 0;
590 for ( ; debug_x < next_tab; )
591 debug_text[debug_y][debug_x++] = ' ';
592 debug_text[debug_y][debug_x] = 0;
596 if ( (c == '\n') || (debug_x >= DCOLS-1) ) {
600 if ( debug_y >= DROWS ) {
602 for (i=1; i<DROWS; i++ )
603 SDL_strlcpy( debug_text[i-1], debug_text[i], DCOLS );
606 debug_text[debug_y][debug_x] = 0;
608 debug_text[debug_y][debug_x] = 0;
609 if ( c == '\n' ) return;
612 debug_text[debug_y][debug_x++] = c;
613 debug_text[debug_y][debug_x] = 0;
616 void dc_printf(const char *format, ...)
621 va_start(args, format);
622 SDL_vsnprintf(tmp, sizeof(tmp), format, args);
637 if ( debug_inited ) return;
644 for (i=0; i<DROWS; i++ ) {
645 debug_text[i][0] = 0;
648 dc_printf("Debug console started.\n" );
652 void debug_console( void (*_func)() )
658 while( key_inkey() ){
662 if ( !debug_inited ) debug_init();
674 case KEY_SHIFTED+SDLK_RETURN:
679 if ( command_line_pos > 0 ) {
680 command_line[--command_line_pos] = 0;
685 if ( last_oldcommand > -1 ) {
686 SDL_strlcpy( command_line, oldcommand_line[last_oldcommand], sizeof(command_line) );
687 command_line_pos = strlen(command_line);
688 command_line[command_line_pos] = 0;
694 if (command_scroll<0)
695 command_scroll = last_oldcommand;
697 if ( command_scroll > -1 ) {
698 SDL_strlcpy( command_line, oldcommand_line[command_scroll], sizeof(command_line) );
699 command_line_pos = strlen(command_line);
700 command_line[command_line_pos] = 0;
706 if (command_scroll>last_oldcommand)
708 if (command_scroll>last_oldcommand)
710 if ( command_scroll > -1 ) {
711 SDL_strlcpy( command_line, oldcommand_line[command_scroll], sizeof(command_line) );
712 command_line_pos = strlen(command_line);
713 command_line[command_line_pos] = 0;
718 debug_output( '\n' );
721 debug_do_command(command_line);
724 for (i=0; i<=last_oldcommand; i++ ) {
725 if (!SDL_strcasecmp( oldcommand_line[i], command_line )) {
730 if ( last_oldcommand < DEBUG_HISTORY-1 ) {
732 SDL_strlcpy( oldcommand_line[last_oldcommand], command_line, sizeof(oldcommand_line[0]) );
735 for (i=0; i<last_oldcommand; i++ ) {
736 SDL_strlcpy( oldcommand_line[i], oldcommand_line[i+1], sizeof(oldcommand_line[0]) );
738 SDL_strlcpy( oldcommand_line[last_oldcommand], command_line, sizeof(oldcommand_line[0]) );
742 // for (i=0; i<=last_oldcommand; i++ ) {
743 // dc_printf( "OC %d. %s\n", i, oldcommand_line[i] );
746 debug_output( '\n' );
747 command_line_pos = 0;
748 command_line[command_line_pos] = 0;
755 int c = key_get_text_input();
756 if ( (c >= 0) && (c < 255) ) {
757 command_line[command_line_pos++] = (ubyte)c;
758 command_line[command_line_pos] = 0;
765 SDL_strlcpy( debug_text[debug_y], ">", DCOLS );
766 SDL_strlcat( debug_text[debug_y], command_line, DCOLS );
774 while( key_inkey() ){
783 dc_printf( "Available functions:\n\n" );
786 for (i=0; i<Num_debug_commands; i++ ) {
787 dc_printf( " %s - %s\n", Debug_command[i]->name, Debug_command[i]->help );
788 //mprintf(( "Scroll times %d\n", scroll_times - s ));
789 if ( scroll_times - s > DROWS - 3 ) {
791 dc_printf( " Press a key...B for back\n" );
805 dc_printf( "Typing '? function_name' will give the current status.\n" );
806 dc_printf( "Typing 'function_name ?' will give help on the function.\n" );
807 dc_printf( "Typing ? or help will give you help.\n");
808 dc_printf( "F3 selects last command line.\n" );