X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=blobdiff_plain;f=vg_console.c;fp=vg_console.c;h=ccc39f7b3c078aab83b43699e520e0806fb11833;hp=0000000000000000000000000000000000000000;hb=3b14f3dcd5bf9dd3c85144f2123d667bfa4bb63f;hpb=fce86711735b15bff37de0f70716808410fcf269 diff --git a/vg_console.c b/vg_console.c new file mode 100644 index 0000000..ccc39f7 --- /dev/null +++ b/vg_console.c @@ -0,0 +1,666 @@ +#include "vg_engine.h" +#include "vg_console.h" +#include "vg_imgui.h" +#include "vg_log.h" +#include "vg_string.h" +#include + +struct vg_console vg_console; + +void vg_console_reg_var( const char *alias, void *ptr, enum vg_var_dtype type, + u32 flags ) +{ + if( vg_thread_purpose() == k_thread_purpose_main ) + vg_fatal_error( "FIXME: Cannot register VAR from main thread" ); + + if( vg_console.var_count > vg_list_size(vg_console.vars) ) + vg_fatal_error( "Too many vars registered" ); + + vg_var *var = &vg_console.vars[ vg_console.var_count ++ ]; + var->name = alias; + var->data = ptr; + var->data_type = type; + var->flags = flags; + + vg_info( "Console variable '%s' registered\n", alias ); +} + +void vg_console_reg_cmd( const char *alias, + int (*function)(int argc, const char *argv[]), + void (*poll_suggest)(int argc, const char *argv[]) ) +{ + if( vg_thread_purpose() == k_thread_purpose_main ) + vg_fatal_error( "FIXME: Cannot register CMD from main thread" ); + + if( vg_console.function_count > vg_list_size(vg_console.functions) ) + vg_fatal_error( "Too many functions registered" ); + + vg_cmd *cmd = &vg_console.functions[ vg_console.function_count ++ ]; + + cmd->function = function; + cmd->poll_suggest = poll_suggest; + cmd->name = alias; + + vg_info( "Console function '%s' registered\n", alias ); +} + +static int _vg_console_list( int argc, char const *argv[] ) +{ + for( int i=0; iname ); + } + + for( int i=0; idata_type], + cv->name ); + } + + return 0; +} + +static void vg_console_write_persistent(void) +{ + FILE *fp = fopen( "cfg/auto.conf", "w" ); + + if( !fp ){ + vg_error( "Cannot open cfg/auto.conf\n" ); + return; + } + + for( int i=0; iflags & VG_VAR_PERSISTENT ){ + if( cv->data_type == k_var_dtype_i32 ){ + fprintf( fp, "%s %d\n", cv->name, *(i32 *)(cv->data) ); + } + else if( cv->data_type == k_var_dtype_u32 ){ + fprintf( fp, "%s %u\n", cv->name, *(u32 *)(cv->data) ); + } + else if( cv->data_type == k_var_dtype_f32 ){ + fprintf( fp, "%s %.5f\n", cv->name, *(float *)(cv->data ) ); + } + else if( cv->data_type == k_var_dtype_str ){ + vg_str *str = cv->data; + if( str->buffer && (str->i > 0) ) + fprintf( fp, "%s %s\n", cv->name, str->buffer ); + } + } + } + + fclose( fp ); +} + +static void _vg_console_free(void) +{ + vg_console_write_persistent(); +} + +/* + * splits src into tokens and fills out args as pointers to those tokens + * returns number of tokens + * dst must be as long as src + */ +static int vg_console_tokenize( const char *src, char *dst, + const char *args[8] ) +{ + int arg_count = 0, + in_token = 0; + + for( int i=0;; i ++ ){ + if( src[i] ){ + if( src[i] == ' ' || src[i] == '\t' ){ + if( in_token ) + dst[i] = '\0'; + + in_token = 0; + + if( arg_count == 8 ) + break; + } + else{ + dst[i] = src[i]; + + if( !in_token ){ + args[ arg_count ++ ] = &dst[i]; + in_token = 1; + } + } + } + else{ + dst[i] = '\0'; + break; + } + } + + return arg_count; +} + +vg_var *vg_console_match_var( const char *kw ) +{ + for( int i=0; iname, kw ) ){ + return cv; + } + } + + return NULL; +} + +vg_cmd *vg_console_match_cmd( const char *kw ) +{ + for( int i=0; iname, kw ) ){ + return cmd; + } + } + + return NULL; +} + +void vg_execute_console_input( const char *cmd, bool silent ) +{ + char temp[512]; + char const *args[8]; + int arg_count = vg_console_tokenize( cmd, temp, args ); + + if( arg_count == 0 ) + return; + + vg_var *cv = vg_console_match_var( args[0] ); + vg_cmd *fn = vg_console_match_cmd( args[0] ); + + if( cv ){ + /* Cvar Matched, try get value */ + if( arg_count >= 2 ){ + if( cv->flags & VG_VAR_CHEAT ){ + if( !vg_console.cheats && !silent ){ + vg_error( "variable is cheat protected\n" ); + return; + } + } + + if( (cv->data_type == k_var_dtype_u32) || + (cv->data_type == k_var_dtype_i32) ){ + int *ptr = cv->data; + *ptr = atoi( args[1] ); + } + else if( cv->data_type == k_var_dtype_f32 ){ + float *ptr = cv->data; + *ptr = atof( args[1] ); + } + else if( cv->data_type == k_var_dtype_str ){ + vg_str *str = cv->data; + vg_strfree( str ); + vg_strnull( str, NULL, -1 ); + + for( int i=1; idata_type == k_var_dtype_i32 ) + vg_info( "= %d\n", *((int *)cv->data) ); + else if( cv->data_type == k_var_dtype_u32 ) + vg_info( "= %u\n", *((u32 *)cv->data) ); + else if( cv->data_type == k_var_dtype_f32 ) + vg_info( "= %.4f\n", *((float *)cv->data) ); + else if( cv->data_type == k_var_dtype_str ){ + vg_str *str = cv->data; + vg_info( "= '%s'\n", str->buffer ); + } + } + + return; + } + else if( fn ){ + fn->function( arg_count-1, args+1 ); + return; + } + + if( !silent ) + vg_error( "No command/var named '%s'. Use 'list' to view all\n",args[0]); +} + +u32 str_lev_distance( const char *s1, const char *s2 ){ + u32 m = strlen( s1 ), + n = strlen( s2 ); + + if( m==0 ) return n; + if( n==0 ) return m; + + u32 costs[ 256 ]; + + for( u32 k=0; k<=n; k++ ) + costs[k] = k; + + u32 i = 0; + for( u32 i=0; i=0; j -- ) + if( score > vg_console.suggestions[j].lev_score ) + best_pos = j; + + /* insert if good score */ + if( best_pos < vg_list_size( vg_console.suggestions ) ){ + int start = VG_MIN( vg_console.suggestion_count, + vg_list_size( vg_console.suggestions )-1 ); + for( int j=start; j>best_pos; j -- ) + vg_console.suggestions[j] = vg_console.suggestions[j-1]; + + vg_console.suggestions[ best_pos ].str = str; + vg_console.suggestions[ best_pos ].len = strlen( str ); + vg_console.suggestions[ best_pos ].lev_score = score; + + if( vg_console.suggestion_count < + vg_list_size( vg_console.suggestions ) ) + vg_console.suggestion_count ++; + } +} + +static void console_update_suggestions(void) +{ + if( vg_ui.focused_control_type != k_ui_control_textbox || + vg_ui.textbuf != vg_console.input ) + return; + + vg_console.suggestion_count = 0; + vg_console.suggestion_select = -1; + vg_console.suggestion_maxlen = 0; + + /* + * - must be typing something + * - must be at the end + * - prev char must not be a whitespace + * - cursors should match + */ + + if( vg_ui.textbox.cursor_pos == 0 ) return; + if( vg_ui.textbox.cursor_pos != vg_ui.textbox.cursor_user ) return; + if( vg_console.input[ vg_ui.textbox.cursor_pos ] != '\0' ) return; + + if( (vg_console.input[ vg_ui.textbox.cursor_pos -1 ] == ' ') || + (vg_console.input[ vg_ui.textbox.cursor_pos -1 ] == '\t') ) + return; + + char temp[128]; + const char *args[8]; + + int token_count = vg_console_tokenize( vg_console.input, temp, args ); + if( !token_count ) return; + vg_console.suggestion_pastepos = args[token_count-1]-temp; + + /* Score all our commands and cvars */ + if( token_count == 1 ){ + for( int i=0; iname, args[0], 1 ); + } + + for( int i=0; iname, args[0], 1 ); + } + } + else{ + vg_cmd *cmd = vg_console_match_cmd( args[0] ); + vg_var *var = vg_console_match_var( args[0] ); + + if( cmd ) + if( cmd->poll_suggest ) + cmd->poll_suggest( token_count-1, &args[1] ); + } + + /* some post processing */ + for( int i=0; i= vg_console.suggestion_count ) + vg_console.suggestion_select = -1; + + _console_fetch_suggestion(); + } +} + +static void console_suggest_prev(void) +{ + if( vg_console.suggestion_count ){ + _console_suggest_store_normal(); + + vg_console.suggestion_select --; + + if( vg_console.suggestion_select < -1 ) + vg_console.suggestion_select = vg_console.suggestion_count-1; + + _console_fetch_suggestion(); + } +} + +static void _vg_console_on_update( char *buf, u32 len ) +{ + if( buf == vg_console.input ){ + console_update_suggestions(); + } +} + +static void console_history_get( char* buf, int entry_num ) +{ + if( !vg_console.history_count ) + return; + + int offset = VG_MIN( entry_num, vg_console.history_count -1 ), + pick = (vg_console.history_last - offset) % + vg_list_size( vg_console.history ); + strcpy( buf, vg_console.history[ pick ] ); +} + +static void _vg_console_on_up( char *buf, u32 len ) +{ + if( buf == vg_console.input ){ + vg_console.history_pos = + VG_MAX + ( + 0, + VG_MIN + ( + vg_console.history_pos+1, + VG_MIN + ( + vg_list_size( vg_console.history ), + vg_console.history_count - 1 + ) + ) + ); + + console_history_get( vg_console.input, vg_console.history_pos ); + _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, + &vg_ui.textbox.cursor_pos, + vg_list_size(vg_console.input)-1, 1); + } +} + +static void _vg_console_on_down( char *buf, u32 len ) +{ + if( buf == vg_console.input ){ + vg_console.history_pos = VG_MAX( 0, vg_console.history_pos-1 ); + console_history_get( vg_console.input, vg_console.history_pos ); + + _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, + &vg_ui.textbox.cursor_pos, + vg_list_size(vg_console.input)-1, 1 ); + } +} + +static void _vg_console_on_enter( char *buf, u32 len ) +{ + if( buf == vg_console.input ){ + if( !strlen( vg_console.input ) ) + return; + + vg_info( "%s\n", vg_console.input ); + + if( strcmp( vg_console.input, + vg_console.history[ vg_console.history_last ]) ) + { + vg_console.history_last = ( vg_console.history_last + 1) % + vg_list_size(vg_console.history ); + vg_console.history_count = + VG_MIN( vg_list_size( vg_console.history ), + vg_console.history_count + 1 ); + strcpy( vg_console.history[ vg_console.history_last ], + vg_console.input ); + } + + vg_console.history_pos = -1; + vg_execute_console_input( vg_console.input, 0 ); + _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, + &vg_ui.textbox.cursor_pos, -10000, 1 ); + + vg_console.input[0] = '\0'; + console_update_suggestions(); + } +} + +int vg_console_exec( int argc, const char *argv[] ) +{ + if( argc < 1 ) return 0; + + int silent=0; + if( argc == 2 ) silent=1; + + char path[256]; + strcpy( path, "cfg/" ); + strncat( path, argv[0], 250 ); + + FILE *fp = fopen( path, "r" ); + if( fp ){ + char line[256]; + + while( fgets( line, sizeof( line ), fp ) ){ + line[ strcspn( line, "\r\n#" ) ] = 0x00; + + if( line[0] != 0x00 ){ + vg_execute_console_input( line, silent ); + } + } + + fclose( fp ); + } + else{ + vg_error( "Could not open '%s'\n", path ); + } + + return 0; +} + + +void vg_console_init(void) +{ + vg_console_reg_cmd( "list", _vg_console_list, NULL ); + vg_console_reg_cmd( "exec", vg_console_exec, NULL ); + vg_console_reg_var( "cheats", &vg_console.cheats, k_var_dtype_i32, +#ifdef VG_DEVWINDOW + VG_VAR_PERSISTENT +#else + 0 +#endif + ); +} + +void vg_console_load_autos(void) +{ + vg_console_exec( 2, (const char *[]){ "auto.conf", "silent" } ); +} + +void vg_console_draw(void) +{ + if( !vg_console.enabled ) return; + + SDL_AtomicLock( &vg_log.print_sl ); + + int ptr = vg_log.log_line_current; + int const fh = vg_ui.font->line_height, log_lines = 32; + int console_lines = VG_MIN( log_lines, vg_log.log_line_count ); + + ui_rect rect_log = { 0, 0, vg.window_x, log_lines*fh }, + rect_input = { 0, log_lines*fh + 1, vg.window_x, fh*2 }, + rect_line = { 0, 0, vg.window_x, fh }; + + /* + * log + */ + u32 bg_colour = (ui_colour( k_ui_bg )&0x00ffffff)|0x9f000000; + + ui_fill( rect_log, bg_colour ); + rect_line[1] = rect_log[1]+rect_log[3]-fh; + + for( int i=0; ispacing*vg_console.suggestion_pastepos; + rect_suggest[1] += rect_input[3]; + rect_suggest[2] = vg_ui.font->spacing * vg_console.suggestion_maxlen; + rect_suggest[3] = vg_console.suggestion_count * fh; + + ui_fill( rect_suggest, bg_colour ); + + rect_suggest[3] = fh; + + for( int i=0; i