From c068f22329d5cc722622a2183bbb22eef2093df7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 24 Aug 2026 11:16:07 +0200 Subject: initial import --- src/parsec/con_com.cpp | 1156 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1156 insertions(+) create mode 100644 src/parsec/con_com.cpp (limited to 'src/parsec/con_com.cpp') diff --git a/src/parsec/con_com.cpp b/src/parsec/con_com.cpp new file mode 100644 index 0000000..af671e7 --- /dev/null +++ b/src/parsec/con_com.cpp @@ -0,0 +1,1156 @@ +/* + * PARSEC - Common Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:34 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger 1996-1999 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include +#include +#include +#include +#include + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" +#include "od_props.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "aud_defs.h" +#include "inp_defs.h" +#include "net_defs.h" +#include "vid_defs.h" + +// local module header +#include "con_com.h" + +// proprietary module headers +#include "con_act.h" +#include "con_arg.h" +#include "con_aux.h" +#include "con_cani.h" +#include "con_conf.h" +#include "con_ext.h" +#include "con_info.h" +#include "con_int.h" +#include "con_list.h" +#include "con_load.h" +#include "con_main.h" +#include "con_say.h" +#include "con_shad.h" +#include "con_std.h" +#include "e_demo.h" +#include "e_supp.h" +#include "g_supp.h" +#include "h_supp.h" +#include "m_main.h" +#include "net_conn.h" +#include "obj_ctrl.h" +#include "sys_date.h" +#include "sys_path.h" +#include "vid_init.h" +#include "vid_plug.h" + + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + + +// string constants ----------------------------------------------------------- +// +static char base_str[] = "base is set to dec."; +#define BASE_INSERT_POS 15 +static char base_dec[] = "dec."; +static char base_hex[] = "hex."; +static char base_oct[] = "oct."; +static char base_set_to_dec[] = "base set to dec."; +static char base_set_to_hex[] = "base set to hex."; +static char base_set_to_oct[] = "base set to oct."; +static char info_sep_line[] = "-----------------------------------------------"; +PUBLIC char dont_use_params[] = "this command takes no parameters."; +static char server_name_missing[] = "server must be supplied."; +static char mode_number_invalid[] = "invalid mode number."; +static char mode_spec_invalid[] = "invalid mode specifier. use something like \"640x480x32\"."; +static char mode_spec_unavail[] = "this mode is not available in the current video subsystem."; +static char mode_modifier_invalid[] = "invalid mode modifier. use \"fullscreen\" or \"windowed\"."; +static char only_alpha_chars[] = "name must not contain non-alphabetic characters."; +static char name_change_preempted[] = "name change preempted. please try again."; + + +// remove all objects (command "objectclear") --------------------------------- +// +PRIVATE +void Cmd_OBJECTCLEAR() +{ + // free objects and particles + KillAllObjects(); +} + + +// print object counts for all object lists (command "objectcount") ----------- +// +PRIVATE +void Cmd_OBJECTCOUNT() +{ + GenObject *walklist; + int listlength; + + listlength = 0; + for ( walklist = FetchFirstShip(); walklist; walklist = walklist->NextObj ) + listlength++; + sprintf( paste_str, "ships :%3d", listlength ); + CON_AddLine( paste_str ); + + listlength = 0; + for ( walklist = FetchFirstLaser(); walklist; walklist = walklist->NextObj ) + listlength++; + sprintf( paste_str, "lasers :%3d", listlength ); + CON_AddLine( paste_str ); + + listlength = 0; + for ( walklist = FetchFirstMissile(); walklist; walklist = walklist->NextObj ) + listlength++; + sprintf( paste_str, "missiles:%3d", listlength ); + CON_AddLine( paste_str ); + + listlength = 0; + for ( walklist = FetchFirstExtra(); walklist; walklist = walklist->NextObj ) + listlength++; + sprintf( paste_str, "extras :%3d (obj, counted %d) and %d particle", + listlength, CurrentNumExtras, CurrentNumPrtExtras ); + CON_AddLine( paste_str ); + + listlength = 0; + for ( walklist = FetchFirstCustom(); walklist; walklist = walklist->NextObj ) + listlength++; + sprintf( paste_str, "customs :%3d", listlength ); + CON_AddLine( paste_str ); +} + + +// display heap info (command "heap") ----------------------------------------- +// +#ifdef PARSEC_DEBUG +PRIVATE +int Cmd_HEAP( char *params ) +{ + ASSERT( params != NULL ); + USERCOMMAND_NOPARAM( params ); + + extern unsigned int Dyn_Mem_Size; + extern unsigned int Num_Allocs; + extern unsigned int Num_Frees; + + sprintf( paste_str, "heapsize: %.2fmb :%d", (float) Dyn_Mem_Size / ( 1024 * 1024 ), Dyn_Mem_Size ); + CON_AddLine( paste_str ); + sprintf( paste_str, "memobjs : %d", Num_Allocs - Num_Frees ); + CON_AddLine( paste_str ); + + return TRUE; +} +#endif // PARSEC_DEBUG + + +// show position of local ship (command "showposition") ----------------------- +// +PRIVATE +void Cmd_SHOWPOSITION() +{ + sprintf( paste_str, "x position: %13f", GEOMV_TO_FLOAT( MyShip->ObjPosition[ 0 ][ 3 ] ) ); + CON_AddLine( paste_str ); + + sprintf( paste_str, "y position: %13f", GEOMV_TO_FLOAT( MyShip->ObjPosition[ 1 ][ 3 ] ) ); + CON_AddLine( paste_str ); + + sprintf( paste_str, "z position: %13f", GEOMV_TO_FLOAT( MyShip->ObjPosition[ 2 ][ 3 ] ) ); + CON_AddLine( paste_str ); +} + + +// player name has been changed -> change remote name accordingly ------------- +// +void CheckPlayerName( const char *name ) +{ + ASSERT( name != NULL ); + +/* + for ( char *scan = name; *scan; scan++ ) { + + // allow only alphabetic chars + if ( !isalpha( *scan ) ) { + CON_AddLine( only_alpha_chars ); + return; + } + + // convert to lowercase + *scan = tolower( *scan ); + } +*/ + // exit if name not altered + if ( strcmp( LocalPlayerName, name ) == 0 ) { + + //NOTE: + // this happens either if the user tried to set + // the same name again, or supplied no argument + // and therefore only queried the current name. + + return; + } + + // ensure everybody knows about new name + if ( NET_SetPlayerName( name ) ) { + sprintf( paste_str, "name set to %s.", LocalPlayerName ); + CON_AddLine( paste_str ); + } else { + CON_AddLine( name_change_preempted ); + } +} + + +// open output script for writing (don't overwrite existing file) ------------- +// +PRIVATE +int Cmd_Create( char *command ) +{ + return OpenOutputBatch( command, CM_CREATE ); +} + + +// open output script for writing (overwrite existing file) ------------------- +// +PRIVATE +int Cmd_Open( char *command ) +{ + return OpenOutputBatch( command, CM_OPEN ); +} + + +// append to output script ---------------------------------------------------- +// +PRIVATE +int Cmd_Append( char *command ) +{ + return OpenOutputBatch( command, CM_APPEND ); +} + + +// record demo script after saving state -------------------------------------- +// +PRIVATE +int Cmd_Recdem( char *command ) +{ + return StartRecording( command, TRUE ); +} + + +// record demo script without saving state first ------------------------------ +// +PRIVATE +int Cmd_Record( char *command ) +{ + return StartRecording( command, FALSE ); +} + + +// print parsec build info (command "build") ---------------------------------- +// +PRIVATE +int Cmd_BuildInfo( char *cstr ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( cstr ); + + // split off first token (detail) + char *dstr = strtok( cstr, " " ); + if ( dstr == NULL ) { + // display basic build info + CON_AddLine( build_text ); + CON_AddLine( build_date ); + CON_AddLine( build_time ); + return TRUE; + } + + // check if more than one parameter + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( too_many_args ); + return TRUE; + } + + // check for detail spec + if ( strcmp( dstr, "x" ) == 0 ) { + + CON_AddLine( info_sep_line ); + CON_AddLine( build_text ); + CON_AddLine( info_sep_line ); + CON_AddLine( build_date ); + CON_AddLine( build_time ); + CON_AddLine( info_sep_line ); + DisplayCurrentNetSubsys(); + CON_AddLine( info_sep_line ); + CON_AddLine( build_comp ); + CON_AddLine( build_bind ); + CON_AddLine( build_endn ); + CON_AddLine( info_sep_line ); + + } else { + + CON_AddLine( invalid_arg ); + } + + return TRUE; +} + + +// exec display command (display arbitrary text in message area) -------------- +// +PRIVATE +int Cmd_DisplayText( char *cstr ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN( cstr ); + + const char *scan = GetStringBehindCommand( cstr, FALSE ); + + if ( ( scan != NULL ) && ( strlen( scan ) <= MAX_MESSAGELEN ) ) { + ShowMessage( scan ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// exec write command (display arbitrary text in console) --------------------- +// +PRIVATE +int Cmd_WriteText( char *cstr ) +{ + //NOTE: + // this command normally only makes sense if + // used from within a console script. + + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN( cstr ); + + const char *scan = GetStringBehindCommand( cstr, TRUE ); + + // output string in console (new-line possible) + if ( scan != NULL ) { + if ( TextModeActive && AUX_CMD_WRITE_ACTIVE_IN_TEXTMODE ) + MSGPUT( "\n::%s", scan ); + if ( !AUX_CMD_WRITE_DISABLE_OUTPUT ) + CON_AddLine( scan ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set current working directory (general use) -------------------------------- +// +PRIVATE +int Cmd_SetPathWorkingDirectory( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + if ( SetSingleStringCommand( command, CurWorkDir, PATH_MAX ) ) { + + // process file name + char *path = SYSs_ProcessPathString( CurWorkDir ); + if ( path != CurWorkDir ) { + strncpy( CurWorkDir, path, PATH_MAX ); + CurWorkDir[ PATH_MAX ] = 0; + } + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for intro animation ------------------------------------------ +// +PRIVATE +int Cmd_SetPathAnimIntro( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + /* + extern char demo_movie_name[]; + if ( SetSingleStringCommand( command, demo_movie_name, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( demo_movie_name, SubstCurWorkDir( demo_movie_name ) ); + } + */ + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for all audio streams ---------------------------------------- +// +PRIVATE +int Cmd_SetPathAudioStreams( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + if ( SetSingleStringCommand( command, console_audio_stream_path, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( console_audio_stream_path, SubstCurWorkDir( console_audio_stream_path ) ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for intro audio stream --------------------------------------- +// +PRIVATE +int Cmd_SetPathAudioStreamIntro( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + if ( SetSingleStringCommand( command, intro_stream_name, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( intro_stream_name, SubstCurWorkDir( intro_stream_name ) ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for menu audio stream ---------------------------------------- +// +PRIVATE +int Cmd_SetPathAudioStreamMenu( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + if ( SetSingleStringCommand( command, menu_stream_name, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( menu_stream_name, SubstCurWorkDir( menu_stream_name ) ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// flash screen white --------------------------------------------------------- +// +PRIVATE +void Cmd_FLASHWHITE() +{ + SetScreenWhite = LASER2_INTENSITY * 16; +} + + +// flash screen blue ---------------------------------------------------------- +// +PRIVATE +void Cmd_FLASHBLUE() +{ + SetScreenBlue = FLASHTIME_LASER_THIEF; +} + + +// init console talk mode ----------------------------------------------------- +// +PRIVATE +void InitTalkMode() +{ + ASSERT( con_in_talk_mode ); + + // clear screen, set cursor to screen-bottom line + ClearConScreen(); + + // create separator line + CreateSeparatorLine( con_bottom ); + + // save number of line right below separator line + CON_AddLineFeed(); + CON_DisableLineFeed(); + con_talk_line = con_bottom; +} + + +// command "cls" -------------------------------------------------------------- +// +PRIVATE +void Cmd_CLS() +{ + if ( con_in_talk_mode ) { + + // reactivate talkmode + InitTalkMode(); + + } else { + + // ensure automatic linefeed will not be eaten + CON_EnableLineFeed(); + + // ensure that after successive linefeed + // ( con_bottom == 0 ) && ( con_content == 1 ) + con_bottom = -1; + con_content = 0; + + // reset back-viewing + con_back_view = 0; + } +} + + +// command "talk" (toggle talk mode on and off) ------------------------------- +// +PRIVATE +void Cmd_TALK() +{ + // toggle talk mode + con_in_talk_mode = !con_in_talk_mode; + + if ( con_in_talk_mode ) { + // talkmode init clears screen + InitTalkMode(); + } else { + // clear screen + Cmd_CLS(); + } +} + + +// command "hide" ------------------------------------------------------------- +// +PRIVATE +void Cmd_HIDE() +{ + //NOTE: + // simulate depression of the console + // toggle key. + + DepressedKeys->key_ToggleConsole = 1; +} + + +// command "quit" ------------------------------------------------------------- +// +PRIVATE +void Cmd_QUIT() +{ + //NOTE: + // simulate depression of the key. + // (or whatever is currently mapped to + // gamefunction escape) + + DepressedKeys->key_Escape = 1; +} + + +// command "exitgame" ------------------------------------------------------------- +// +PRIVATE +void Cmd_EXITGAME() +{ + ExitGameLoop = 3; +} + + +// command "enable" ----------------------------------------------------------- +// +PRIVATE +void Cmd_ENABLE() +{ + //NOTE: + // enable typing in console. keyboard input + // will be routed to the console instead of to + // the game. the console cursor will be blinking + // to signify this. + + KeybFlags->ConActive = 0xff; +} + + +// command "disable" ---------------------------------------------------------- +// +PRIVATE +void Cmd_DISABLE() +{ + //NOTE: + // disable typing in console. keyboard input + // will be routed to the game instead of to + // the console, even if the console is enabled. + // the console cursor will not be blinking + // to signify this. + + KeybFlags->ConActive = 0x00; +} + + +// command "hex" -------------------------------------------------------------- +// +PRIVATE +void Cmd_HEX() +{ + int_print_base = "%x"; + int_calc_base = 16; + CON_AddLine( base_set_to_hex ); + strcpy( base_str + BASE_INSERT_POS, base_hex ); +} + + +// command "dec" -------------------------------------------------------------- +// +PRIVATE +void Cmd_DEC() +{ + int_print_base = "%d"; + int_calc_base = 10; + CON_AddLine( base_set_to_dec ); + strcpy( base_str + BASE_INSERT_POS, base_dec ); +} + + +// command "oct" -------------------------------------------------------------- +// +PRIVATE +void Cmd_OCT() +{ + int_print_base = "%o"; + int_calc_base = 8; + CON_AddLine( base_set_to_oct ); + strcpy( base_str + BASE_INSERT_POS, base_oct ); +} + + +// command "base" ------------------------------------------------------------- +// +PRIVATE +void Cmd_BASE() +{ + CON_AddLine( base_str ); +} + + +// set video mode to specified mode number ------------------------------------ +// +PRIVATE +int Cmd_SetVidMode( char *modestr ) +{ + ASSERT( modestr != NULL ); + HANDLE_COMMAND_DOMAIN( modestr ); + + // determine whether specifier should be parsed instead of mode number + // (specifiers like "640x480x32" and all valid modifiers contain at + // least one char not allowed in a hexadecimal mode number) + int parsespec = FALSE; + char *scan = NULL; + for ( scan = modestr; *scan != 0; scan++ ) { + if ( *scan == ' ' ) + continue; + if ( ( *scan >= '0' ) && ( *scan <= '9' ) ) + continue; + if ( ( *scan >= 'a' ) && ( *scan <= 'f' ) ) + continue; + if ( ( *scan >= 'A' ) && ( *scan <= 'F' ) ) + continue; + parsespec = TRUE; + break; + } + + int xres = 0, yres = 0; + bool fullscreen = false; + int bpp = 32; + + if ( parsespec ) { + + // read mode specifier + char *modespec = (char *) GetStringBehindCommand( modestr, FALSE ); + if ( modespec != NULL ) { + + // a modifier for explicit fullscreen/windowed modes may + // be appended to the specifier after a delimiting space + char *modifier = (char *) modespec; + while ( ( *modifier != ' ' ) && ( *modifier != 0 ) ) + modifier++; + + // if a mode is already active it is possible to just use a + // modifier to switch between windowed and fullscreen mode + int modifieronly = FALSE; + if ( !TextModeActive ) { + for ( scan = modespec; scan < modifier; scan++ ) { + if ( *scan == 'x' ) + break; + } + if ( scan == modifier ) { + modifieronly = TRUE; + modifier = modespec; + } + } + + // parse the (optional) modifier + int indxmodifier = -1; + if ( *modifier != 0 ) { + + if ( !modifieronly ) { + // sever spec and modifier + *modifier++ = 0; + // strip intermediate spaces + while ( *modifier == ' ' ) + modifier++; + } + + // modifier may be "fullscreen" or "windowed" + if ( strcmp( modifier, "fullscreen" ) == 0 ) { + indxmodifier = 1; + } else if ( strcmp( modifier, "windowed" ) == 0 ) { + indxmodifier = 0; + } else { + CON_AddLine( mode_modifier_invalid ); + return TRUE; + } + } + + if (modifieronly) { + xres = GameScreenRes.width; + yres = GameScreenRes.height; + bpp = GameScreenBPP; + } else { + int modearray[] = {0, 0, 0}; + + VID_MapSpecifierToMode(modespec, modearray); + + xres = modearray[0]; + yres = modearray[1]; + bpp = modearray[2]; + } + + if ( indxmodifier != -1 ) { + fullscreen = indxmodifier == 1 ? true : false; + } else { + fullscreen = !Op_WindowedMode ? true : false; + } + } + + } else { + + MSGOUT("Setting video mode using hex number no longer works!\n"); + } + + // switch to desired mode if mode number found. this doesn't mean that + // the mode is actually valid for the current video subsystem, if it + // was specified directly. (specifiers have been checked already.) + if ( xres != 0 && yres != 0 && bpp != 0 ) { + if ( !VID_HotChangeMode(xres, yres, fullscreen, bpp)) { + CON_AddLine( mode_number_invalid ); + } + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// connect to server ("connect") ---------------------------------------------- +// +PRIVATE +int Cmd_Connect( char *server ) +{ + ASSERT( server != NULL ); + HANDLE_COMMAND_DOMAIN( server ); + + // eat whitespace behind command + const char *scan = GetStringBehindCommand( server, FALSE ); + + if ( scan != NULL ) { + if ( NET_CommandConnect( scan ) ) { + MenuNotifyConnect(); + } + } else { + CON_AddLine( server_name_missing ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// close network connection ("disconnect") ------------------------------------ +// +PRIVATE +void Cmd_DISCONNECT() +{ + if ( NET_CommandDisconnect() ) { + MenuNotifyDisconnect(); + } +} + + +// commands corresponding to functions with no parameters --------------------- +// +struct cmd_noparam_s { + + int commandid; + void (*commandfunc)(void); +}; + +cmd_noparam_s cmd_noparam[] = { + + { CM_REPINFO, Cmd_QueryReplayInfo }, + { CM_REPSTOP, Cmd_StopBatchReplay }, + { CM_CLS, Cmd_CLS }, + { CM_CLEAR, Cmd_CLS }, + { CM_HIDE, Cmd_HIDE }, + { CM_QUIT, Cmd_QUIT }, + { CM_EXIT, Cmd_QUIT }, + { CM_EXITGAME, Cmd_EXITGAME }, + { CM_ENABLE, Cmd_ENABLE }, + { CM_DISABLE, Cmd_DISABLE }, + { CM_HEX, Cmd_HEX }, + { CM_DEC, Cmd_DEC }, + { CM_OCT, Cmd_OCT }, + { CM_BASE, Cmd_BASE }, + { CM_FLASHWHITE, Cmd_FLASHWHITE }, + { CM_FLASHBLUE, Cmd_FLASHBLUE }, + { CM_OBJECTCLEAR, Cmd_OBJECTCLEAR }, + { CM_OBJECTCOUNT, Cmd_OBJECTCOUNT }, + { CM_SHIPS, Cmd_ShipInfo }, + { CM_LISTCLASSES, Cmd_ListClasses }, + { CM_LISTTYPES, Cmd_ListTypes }, + { CM_LISTTEXTMACS, Cmd_ListSayMacros }, + { CM_LISTBINDINGS, Cmd_ListKeyBindings }, + { CM_SETORIGIN, Cmd_SetShipOrigin }, + { CM_SHOWPOSITION, Cmd_SHOWPOSITION }, + { CM_CLOSE, Cmd_CloseOutputBatch }, + { CM_LISTGAMEFUNCKEYS, Cmd_ListKeyFunctions }, + { CM_VID_LISTMODES, Cmd_ListVidModes }, + { CM_DISCONNECT, Cmd_DISCONNECT }, + { CM_TALKMODE, Cmd_TALK }, + { CM_RESCAN, Cmd_RescanExternalCommands }, + { CM_LISTDEMOS, Cmd_ListBinaryDemos }, +}; + +#define NUM_CMDS_NOPARAM CALC_NUM_ARRAY_ENTRIES( cmd_noparam ) + + +// commands corresponding to functions with no parameters --------------------- +// +int CheckCmdsNoParam( const char *command ) +{ + //NOTE: + // this function returns TRUE if a valid command + // has been detected and executed. it returns + // FALSE if no valid command has been detected + // and the command string therefore needs + // further processing by the main dispatcher. + + ASSERT( command != NULL ); + + // check for commands in "no param" table + for ( unsigned int tid = 0; tid < NUM_CMDS_NOPARAM; tid++ ) { + + const char *cmdstr = CMSTR( cmd_noparam[ tid ].commandid ); + int cmdlen = CMLEN( cmd_noparam[ tid ].commandid ); + + if ( strncmp( command, cmdstr, cmdlen ) == 0 ) { + + // eat trailing whitespace + const char *whsp = command + cmdlen; + for ( ; *whsp; whsp++ ) + if ( *whsp != ' ' ) + break; + if ( *whsp == 0 ) { + // only whitespace -> command valid + (*cmd_noparam[ tid ].commandfunc)(); + return TRUE; + } else if ( command[ cmdlen ] == ' ' ) { + // excess parameters + CON_AddLine( dont_use_params ); + return TRUE; + } + } + } + + return FALSE; +} + + +// commands corresponding to functions with an arbitrary string parameter ----- +// +struct cmd_customstring_s { + + int commandid; + int (*commandfunc)(char*); +}; + +cmd_customstring_s cmd_customstring[] = { + + { CM_BUILD, Cmd_BuildInfo }, + { CM_PATH_ANIM_INTRO, Cmd_SetPathAnimIntro }, + { CM_PATH_ASTREAM_INTRO, Cmd_SetPathAudioStreamIntro }, + { CM_PATH_ASTREAM_MENU, Cmd_SetPathAudioStreamMenu }, + { CM_PATH_ASTREAM, Cmd_SetPathAudioStreams }, + { CM_WORKINGDIRECTORY, Cmd_SetPathWorkingDirectory }, + { CM_LISTINTCOMMANDS, Cmd_ListIntCommands }, + { CM_LISTSTDCOMMANDS, Cmd_ListStdCommands }, + { CM_LISTEXTCOMMS, Cmd_ListExternalCommands }, + { CM_DISPLAY, Cmd_DisplayText }, + { CM_CAT, Cmd_CatExternalCommands }, + { CM_GAMELOOPBATCH, Cmd_SetGameLoopBatchName }, + { CM_SAY, Cmd_SayText }, + { CM_SET_MACRO, Cmd_SetTextMacro }, + { CM_LISTDATA, Cmd_ListDataTable }, + { CM_VID_SETMODE, Cmd_SetVidMode }, + { CM_CONNECT, Cmd_Connect }, + { CM_WRITESTRING, Cmd_WriteText }, + { CM_CREATE, Cmd_Create }, + { CM_OPEN, Cmd_Open }, + { CM_RECDEM, Cmd_Recdem }, + { CM_RECORD, Cmd_Record }, + { CM_APPEND, Cmd_Append }, + { CM_PLAYDEMO, Cmd_PlayBinaryDemoFile }, + { CM_LOAD, Cmd_ExecLoadCommand }, + { CM_DEFSHADER, Cmd_DefShader }, + { CM_SETSHADER, Cmd_SetShader }, + { CM_COLANIM, Cmd_DefColAnim }, + { CM_FACEINFO, Cmd_FaceInfo }, +}; + +#define NUM_CMDS_CUSTOMSTRING CALC_NUM_ARRAY_ENTRIES( cmd_customstring ) + + +// commands corresponding to functions taking an arbitrary string parameter --- +// +int CheckCmdsCustomString( const char *command ) +{ + ASSERT( command != NULL ); + + //NOTE: + // this function returns TRUE if a valid command + // has been detected and executed. it returns + // FALSE if no valid command has been detected + // and the command string therefore needs + // further processing by the main dispatcher. + + //NOTE: + // a space as delimiter behind a command is not + // enforced by this function. that is, the supplied + // parsing functions can grab an entire domain + // (set of commands starting with the same characters). + // but this also means they have to ensure the delimiter + // is indeed there if there should be only one command. + + //NOTE: + // due to the semantics of this function and most parsing + // functions it calls, most "custom string" commands reserve + // an entire domain. that is, after this function no + // commands starting with the same characters as a + // "custom string" command will be recognized. + // this behavior also depends on HANDLE_COMMAND_DOMAIN(). + + // check for commands in "custom string" table + for ( unsigned int tid = 0; tid < NUM_CMDS_CUSTOMSTRING; tid++ ) { + + const char *cmdstr = CMSTR( cmd_customstring[ tid ].commandid ); + int cmdlen = CMLEN( cmd_customstring[ tid ].commandid ); + + if ( strncmp( command, cmdstr, cmdlen ) == 0 ) { + + ASSERT( cmd_customstring[ tid ].commandfunc != NULL ); + return (*cmd_customstring[ tid ].commandfunc)( (char *) command + cmdlen ); + } + } + + return FALSE; +} + + +// user command handling ------------------------------------------------------ +// +PRIVATE user_command_s user_commands_default[ 32 ]; +PUBLIC user_command_s* user_commands = user_commands_default; +PUBLIC int num_user_commands = 0; +PRIVATE int max_user_commands = 32; + + +// check user commands -------------------------------------------------------- +// +int CheckCmdsUserDefined( const char *command ) +{ + ASSERT( command != NULL ); + + //NOTE: + // the semantics of this function is basically + // the same as for CheckCmdsCustomString(). + + //NOTE: + // this function will be called very early in + // the sequence of command comparsions. thus, + // it can override most other commands. the + // callback functions have to be very careful + // not to block entire domains without any reason. + + // check for commands in user commands table + for ( int cid = 0; cid < num_user_commands; cid++ ) { + + const char *cmdstr = user_commands[ cid ].command; + int cmdlen = user_commands[ cid ].commlen; + + if ( strncmp( command, cmdstr, cmdlen ) == 0 ) { + + ASSERT( user_commands[ cid ].execute != NULL ); + return (*user_commands[ cid ].execute)( (char *) command + cmdlen ); + } + } + + return FALSE; +} + + +// register a new user-defined command ---------------------------------------- +// +int CON_RegisterUserCommand( user_command_s *regcom ) +{ + //NOTE: + //CAVEAT: + // the supplied command string is not copied + // by this function. thus, the caller MUST ENSURE + // that this string is available indefinitely. + // (e.g., allocated statically.) + + //NOTE: + // the commlen field need not be valid, it will be + // set by this function. (in the original struct!) + + ASSERT( regcom != NULL ); + ASSERT( regcom->command != NULL ); + ASSERT( regcom->execute != NULL ); + ASSERT( regcom->numparams >= 0 ); + ASSERT( num_user_commands <= max_user_commands ); + + // expand table memory if already used up + if ( num_user_commands == max_user_commands ) { + + // expand exponentially + int newtabsize = max_user_commands * 2; + + // alloc new table + user_command_s *newlist = (user_command_s *) ALLOCMEM( sizeof( user_command_s ) * newtabsize ); + if ( newlist == NULL ) { + ASSERT( 0 ); + return FALSE; + } + + // set new size + max_user_commands = newtabsize; + + // move old table + memcpy( newlist, user_commands, sizeof( user_command_s ) * num_user_commands ); + if ( user_commands != user_commands_default ) + FREEMEM( user_commands ); + user_commands = newlist; + } + + // append new command + regcom->commlen = strlen( regcom->command ); + ASSERT( num_user_commands < max_user_commands ); + user_commands[ num_user_commands++ ] = *regcom; + + return TRUE; +} + +// replace the entry for an already registered user-defined command ----------- +// +int CON_ReplaceUserCommand( user_command_s* repcom ) +{ + //NOTE: + //CAVEAT: + // the supplied command string is not copied + // by this function. thus, the caller MUST ENSURE + // that this string is available indefinitely. + // (e.g., allocated statically.) + + //NOTE: + // the commlen field need not be valid, it will be + // set by this function. (in the original struct!) + + ASSERT( repcom != NULL ); + ASSERT( repcom->command != NULL ); + ASSERT( repcom->execute != NULL ); + ASSERT( repcom->numparams >= 0 ); + + for( int user_command_index = 0; user_command_index < num_user_commands; user_command_index++ ) { + + // check for equal command string + if( stricmp( user_commands[ user_command_index ].command , repcom->command ) == 0 ) { + repcom->commlen = strlen( repcom->command ); + user_commands[ user_command_index ] = *repcom; + + return TRUE; + } + } + + return FALSE; +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( CON_COM ) +{ + +#ifdef PARSEC_DEBUG + + // register "heap" command + user_command_s regcom; + regcom.command = "heap"; + regcom.numparams = 0; + regcom.execute = Cmd_HEAP; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + +#endif // PARSEC_DEBUG + +} + + + -- cgit v1.2.3