2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "platform/platformTLS.h"
# include "platform/threads/thread.h"
# include "console/console.h"
# include "console/consoleInternal.h"
# include "console/consoleObject.h"
# include "console/consoleParser.h"
# include "core/stream/fileStream.h"
# include "core/tAlgorithm.h"
# include "console/consoleTypes.h"
# include "console/telnetDebugger.h"
# include "console/simBase.h"
# include "console/stringStack.h"
# include "console/ICallMethod.h"
# include "console/engineAPI.h"
# include <stdarg.h>
2023-04-23 08:39:54 +00:00
# include "returnBuffer.h"
2012-09-19 15:15:01 +00:00
# include "platform/threads/mutex.h"
2016-04-19 06:35:52 +00:00
# include "core/util/journal/journal.h"
2021-03-31 03:58:07 +00:00
# include "console/consoleValueStack.h"
2012-09-19 15:15:01 +00:00
extern StringStack STR ;
2021-03-31 03:58:07 +00:00
extern ConsoleValueStack < 4096 > gCallStack ;
2022-05-22 01:55:44 +00:00
DataChunker ConsoleValue : : sConversionAllocator ;
2021-03-31 03:58:07 +00:00
void ConsoleValue : : init ( )
{
2022-05-22 01:55:44 +00:00
sConversionAllocator . setChunkSize ( 8092 ) ;
2021-09-05 01:25:11 +00:00
}
void ConsoleValue : : resetConversionBuffer ( )
{
2022-05-22 01:55:44 +00:00
sConversionAllocator . freeBlocks ( ) ;
2021-03-31 03:58:07 +00:00
}
char * ConsoleValue : : convertToBuffer ( ) const
{
2022-05-22 01:55:44 +00:00
char * buffer = static_cast < char * > ( sConversionAllocator . alloc ( 32 ) ) ;
2021-09-05 01:25:11 +00:00
2021-03-31 03:58:07 +00:00
if ( type = = ConsoleValueType : : cvFloat )
2022-05-22 01:55:44 +00:00
dSprintf ( buffer , 32 , " %.9g " , f ) ;
2021-03-31 03:58:07 +00:00
else
2022-05-22 01:55:44 +00:00
dSprintf ( buffer , 32 , " %lld " , i ) ;
2021-03-31 03:58:07 +00:00
2022-05-22 01:55:44 +00:00
return buffer ;
2021-03-31 03:58:07 +00:00
}
2012-09-19 15:15:01 +00:00
2021-04-01 02:10:55 +00:00
const char * ConsoleValue : : getConsoleData ( ) const
{
2025-05-11 21:57:56 +00:00
return Con : : getData ( type , dataPtr , 0 , enumTable ) ;
2021-04-01 02:10:55 +00:00
}
2012-09-19 15:15:01 +00:00
ConsoleDocFragment * ConsoleDocFragment : : smFirst ;
2014-12-29 10:49:52 +00:00
U32 gAnonFunctionID = 0 ;
2018-03-17 01:05:47 +00:00
ConsoleConstructor * ConsoleConstructor : : mFirst = NULL ;
2012-09-19 15:15:01 +00:00
bool gWarnUndefinedScriptVariables ;
static char scratchBuffer [ 4096 ] ;
CON_DECLARE_PARSER ( CMD ) ;
static const char * prependDollar ( const char * name )
{
if ( name [ 0 ] ! = ' $ ' )
{
2023-05-01 15:13:12 +00:00
U64 len = dStrlen ( name ) ;
2012-09-19 15:15:01 +00:00
AssertFatal ( len < sizeof ( scratchBuffer ) - 2 , " CONSOLE: name too long " ) ;
scratchBuffer [ 0 ] = ' $ ' ;
dMemcpy ( scratchBuffer + 1 , name , len + 1 ) ;
name = scratchBuffer ;
}
return name ;
}
static const char * prependPercent ( const char * name )
{
if ( name [ 0 ] ! = ' % ' )
{
2023-05-01 15:13:12 +00:00
U64 len = dStrlen ( name ) ;
2012-09-19 15:15:01 +00:00
AssertFatal ( len < sizeof ( scratchBuffer ) - 2 , " CONSOLE: name too long " ) ;
scratchBuffer [ 0 ] = ' % ' ;
dMemcpy ( scratchBuffer + 1 , name , len + 1 ) ;
name = scratchBuffer ;
}
return name ;
}
//--------------------------------------
void ConsoleConstructor : : init ( const char * cName , const char * fName , const char * usg , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
2018-03-17 01:05:47 +00:00
mMina = minArgs ;
mMaxa = maxArgs ;
mFuncName = fName ;
mUsage = usg ;
mClassName = cName ;
mSC = 0 ; mFC = 0 ; mVC = 0 ; mBC = 0 ; mIC = 0 ;
mCallback = mGroup = false ;
mNext = mFirst ;
mNS = false ;
mFirst = this ;
mToolOnly = isToolOnly ;
mHeader = header ;
2012-09-19 15:15:01 +00:00
}
void ConsoleConstructor : : setup ( )
{
2018-03-17 01:05:47 +00:00
for ( ConsoleConstructor * walk = mFirst ; walk ; walk = walk - > mNext )
2012-09-19 15:15:01 +00:00
{
# ifdef TORQUE_DEBUG
walk - > validate ( ) ;
# endif
2018-03-17 01:05:47 +00:00
if ( walk - > mSC )
Con : : addCommand ( walk - > mClassName , walk - > mFuncName , walk - > mSC , walk - > mUsage , walk - > mMina , walk - > mMaxa , walk - > mToolOnly , walk - > mHeader ) ;
else if ( walk - > mIC )
Con : : addCommand ( walk - > mClassName , walk - > mFuncName , walk - > mIC , walk - > mUsage , walk - > mMina , walk - > mMaxa , walk - > mToolOnly , walk - > mHeader ) ;
else if ( walk - > mFC )
Con : : addCommand ( walk - > mClassName , walk - > mFuncName , walk - > mFC , walk - > mUsage , walk - > mMina , walk - > mMaxa , walk - > mToolOnly , walk - > mHeader ) ;
else if ( walk - > mVC )
Con : : addCommand ( walk - > mClassName , walk - > mFuncName , walk - > mVC , walk - > mUsage , walk - > mMina , walk - > mMaxa , walk - > mToolOnly , walk - > mHeader ) ;
else if ( walk - > mBC )
Con : : addCommand ( walk - > mClassName , walk - > mFuncName , walk - > mBC , walk - > mUsage , walk - > mMina , walk - > mMaxa , walk - > mToolOnly , walk - > mHeader ) ;
else if ( walk - > mGroup )
Con : : markCommandGroup ( walk - > mClassName , walk - > mFuncName , walk - > mUsage ) ;
else if ( walk - > mClassName )
Con : : noteScriptCallback ( walk - > mClassName , walk - > mFuncName , walk - > mUsage , walk - > mHeader ) ;
else if ( walk - > mNS )
2012-09-19 15:15:01 +00:00
{
2018-03-17 01:05:47 +00:00
Namespace * ns = Namespace : : find ( StringTable - > insert ( walk - > mClassName ) ) ;
2012-09-19 15:15:01 +00:00
if ( ns )
2018-03-17 01:05:47 +00:00
ns - > mUsage = walk - > mUsage ;
2012-09-19 15:15:01 +00:00
}
else
{
AssertISV ( false , " Found a ConsoleConstructor with an indeterminate type! " ) ;
}
}
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * funcName , StringCallback sfunc , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
init ( className , funcName , usage , minArgs , maxArgs , isToolOnly , header ) ;
2018-03-17 01:05:47 +00:00
mSC = sfunc ;
2012-09-19 15:15:01 +00:00
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * funcName , IntCallback ifunc , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
init ( className , funcName , usage , minArgs , maxArgs , isToolOnly , header ) ;
2018-03-17 01:05:47 +00:00
mIC = ifunc ;
2012-09-19 15:15:01 +00:00
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * funcName , FloatCallback ffunc , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
init ( className , funcName , usage , minArgs , maxArgs , isToolOnly , header ) ;
2018-03-17 01:05:47 +00:00
mFC = ffunc ;
2012-09-19 15:15:01 +00:00
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * funcName , VoidCallback vfunc , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
init ( className , funcName , usage , minArgs , maxArgs , isToolOnly , header ) ;
2018-03-17 01:05:47 +00:00
mVC = vfunc ;
2012-09-19 15:15:01 +00:00
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * funcName , BoolCallback bfunc , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
init ( className , funcName , usage , minArgs , maxArgs , isToolOnly , header ) ;
2018-03-17 01:05:47 +00:00
mBC = bfunc ;
2012-09-19 15:15:01 +00:00
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * groupName , const char * aUsage )
{
2018-03-17 01:05:47 +00:00
init ( className , groupName , mUsage , - 1 , - 2 ) ;
2012-09-19 15:15:01 +00:00
2018-03-17 01:05:47 +00:00
mGroup = true ;
2012-09-19 15:15:01 +00:00
// Somewhere, the entry list is getting flipped, partially.
// so we have to do tricks to deal with making sure usage
// is properly populated.
// This is probably redundant.
static char * lastUsage = NULL ;
if ( aUsage )
lastUsage = ( char * ) aUsage ;
2018-03-17 01:05:47 +00:00
mUsage = lastUsage ;
2012-09-19 15:15:01 +00:00
}
ConsoleConstructor : : ConsoleConstructor ( const char * className , const char * callbackName , const char * usage , ConsoleFunctionHeader * header )
{
init ( className , callbackName , usage , - 2 , - 3 , false , header ) ;
2018-03-17 01:05:47 +00:00
mCallback = true ;
mNS = true ;
2012-09-19 15:15:01 +00:00
}
void ConsoleConstructor : : validate ( )
{
# ifdef TORQUE_DEBUG
// Don't do the following check if we're not a method/func.
2018-03-17 01:05:47 +00:00
if ( mGroup )
2012-09-19 15:15:01 +00:00
return ;
// In debug, walk the list and make sure this isn't a duplicate.
2018-03-17 01:05:47 +00:00
for ( ConsoleConstructor * walk = mFirst ; walk ; walk = walk - > mNext )
2012-09-19 15:15:01 +00:00
{
// Skip mismatching func/method names.
2018-03-17 01:05:47 +00:00
if ( dStricmp ( walk - > mFuncName , mFuncName ) )
2012-09-19 15:15:01 +00:00
continue ;
// Don't compare functions with methods or vice versa.
2018-03-17 01:05:47 +00:00
if ( bool ( mClassName ) ! = bool ( walk - > mClassName ) )
2012-09-19 15:15:01 +00:00
continue ;
// Skip mismatching classnames, if they're present.
2018-03-17 01:05:47 +00:00
if ( mClassName & & walk - > mClassName & & dStricmp ( walk - > mClassName , mClassName ) )
2012-09-19 15:15:01 +00:00
continue ;
// If we encounter ourselves, stop searching; this prevents duplicate
// firing of the assert, instead only firing for the latter encountered
// entry.
if ( this = = walk )
break ;
// Match!
2018-03-17 01:05:47 +00:00
if ( mClassName )
2012-09-19 15:15:01 +00:00
{
2018-03-17 01:05:47 +00:00
AssertISV ( false , avar ( " ConsoleConstructor::setup - ConsoleMethod '%s::%s' collides with another of the same name. " , mClassName , mFuncName ) ) ;
2012-09-19 15:15:01 +00:00
}
else
{
2018-03-17 01:05:47 +00:00
AssertISV ( false , avar ( " ConsoleConstructor::setup - ConsoleFunction '%s' collides with another of the same name. " , mFuncName ) ) ;
2012-09-19 15:15:01 +00:00
}
}
# endif
}
// We comment out the implementation of the Con namespace when doxygenizing because
// otherwise Doxygen decides to ignore our docs in console.h
# ifndef DOXYGENIZING
namespace Con
{
static Vector < ConsumerCallback > gConsumers ( __FILE__ , __LINE__ ) ;
static Vector < String > sInstantGroupStack ( __FILE__ , __LINE__ ) ;
static DataChunker consoleLogChunker ;
static Vector < ConsoleLogEntry > consoleLog ( __FILE__ , __LINE__ ) ;
static bool consoleLogLocked ;
2021-11-17 04:56:52 +00:00
bool scriptWarningsAsAsserts = true ;
2012-09-19 15:15:01 +00:00
static bool logBufferEnabled = true ;
static S32 printLevel = 10 ;
static FileStream consoleLogFile ;
static const char * defLogFileName = " console.log " ;
static S32 consoleLogMode = 0 ;
static bool active = false ;
static bool newLogFile ;
static const char * logFileName ;
2013-08-04 21:26:01 +00:00
static const S32 MaxCompletionBufferSize = 4096 ;
2012-09-19 15:15:01 +00:00
static char completionBuffer [ MaxCompletionBufferSize ] ;
static char tabBuffer [ MaxCompletionBufferSize ] = { 0 } ;
static SimObjectPtr < SimObject > tabObject ;
static U32 completionBaseStart ;
static U32 completionBaseLen ;
String gInstantGroup ;
Con : : ConsoleInputEvent smConsoleInput ;
/// Current script file name and root, these are registered as
/// console variables.
/// @{
///
StringTableEntry gCurrentFile ;
StringTableEntry gCurrentRoot ;
2021-09-13 23:46:21 +00:00
S32 gObjectCopyFailures = - 1 ;
2012-09-19 15:15:01 +00:00
/// @}
bool alwaysUseDebugOutput = true ;
bool useTimestamp = false ;
2020-10-24 09:41:18 +00:00
bool useRealTimestamp = false ;
2012-09-19 15:15:01 +00:00
2023-09-05 03:28:49 +00:00
static U32 initTime = Platform : : getRealMilliseconds ( ) ;
2023-09-08 01:54:22 +00:00
static U32 startTime = initTime ;
2023-09-05 03:28:49 +00:00
2012-09-19 15:15:01 +00:00
ConsoleFunctionGroupBegin ( Clipboard , " Miscellaneous functions to control the clipboard and clear the console. " ) ;
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( cls , void , ( ) , , " () "
2017-01-06 23:04:28 +00:00
" @brief Clears the console output. \n \n "
" @ingroup Console " )
2012-09-19 15:15:01 +00:00
{
if ( consoleLogLocked )
return ;
consoleLogChunker . freeBlocks ( ) ;
consoleLog . setSize ( 0 ) ;
} ;
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( getClipboard , const char * , ( ) , , " () "
2017-01-06 23:04:28 +00:00
" @brief Get text from the clipboard. \n \n "
" @internal " )
2012-09-19 15:15:01 +00:00
{
2017-01-06 23:04:28 +00:00
return Platform : : getClipboard ( ) ;
2012-09-19 15:15:01 +00:00
} ;
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( setClipboard , bool , ( const char * text ) , , " (string text) "
2023-09-05 03:28:49 +00:00
" @brief Set the system clipboard. \n \n "
2017-01-06 23:04:28 +00:00
" @internal " )
2012-09-19 15:15:01 +00:00
{
2017-01-06 23:04:28 +00:00
return Platform : : setClipboard ( text ) ;
2012-09-19 15:15:01 +00:00
} ;
ConsoleFunctionGroupEnd ( Clipboard ) ;
2023-09-05 03:28:49 +00:00
DefineEngineFunction ( resetTimeStamp , void , ( ) , , " () "
" @brief Reset the timestamp to 0 ms. \n \n "
" @ingroup Console " )
{
startTime = Platform : : getRealMilliseconds ( ) ;
} ;
DefineEngineFunction ( getInitTime , int , ( ) , , " () "
" @brief Get the initialization time in miliseconds. \n \n "
" @internal " )
{
return initTime ;
} ;
2012-09-19 15:15:01 +00:00
void postConsoleInput ( RawData data ) ;
void init ( )
{
AssertFatal ( active = = false , " Con::init should only be called once. " ) ;
// Set up general init values.
active = true ;
logFileName = NULL ;
newLogFile = true ;
gWarnUndefinedScriptVariables = false ;
// Initialize subsystems.
Namespace : : init ( ) ;
ConsoleConstructor : : setup ( ) ;
// Set up the parser(s)
2021-11-17 04:56:52 +00:00
CON_ADD_PARSER ( CMD , ( char * ) TORQUE_SCRIPT_EXTENSION , true ) ; // TorqueScript
2012-09-19 15:15:01 +00:00
// Setup the console types.
ConsoleBaseType : : initialize ( ) ;
2021-03-30 23:33:19 +00:00
ConsoleValue : : init ( ) ;
2012-09-19 15:15:01 +00:00
// And finally, the ACR...
AbstractClassRep : : initialize ( ) ;
// Variables
setVariable ( " Con::prompt " , " % " ) ;
addVariable ( " Con::logBufferEnabled " , TypeBool , & logBufferEnabled , " If true, the log buffer will be enabled. \n "
2017-01-06 23:04:28 +00:00
" @ingroup Console \n " ) ;
2012-09-19 15:15:01 +00:00
addVariable ( " Con::printLevel " , TypeS32 , & printLevel ,
" @brief This is deprecated. \n \n "
" It is no longer in use and does nothing. \n "
2017-01-06 23:04:28 +00:00
" @ingroup Console \n " ) ;
2012-09-19 15:15:01 +00:00
addVariable ( " Con::warnUndefinedVariables " , TypeBool , & gWarnUndefinedScriptVariables , " If true, a warning will be displayed in the console whenever a undefined variable is used in script. \n "
2017-01-06 23:04:28 +00:00
" @ingroup Console \n " ) ;
2012-09-19 15:15:01 +00:00
addVariable ( " instantGroup " , TypeRealString , & gInstantGroup , " The group that objects will be added to when they are created. \n "
2017-01-06 23:04:28 +00:00
" @ingroup Console \n " ) ;
2021-09-13 23:46:21 +00:00
addVariable ( " Con::objectCopyFailures " , TypeS32 , & gObjectCopyFailures , " If greater than zero then it counts the number of object creation "
" failures based on a missing copy object and does not report an error.. \n "
" @ingroup Console \n " ) ;
2021-11-17 04:56:52 +00:00
addVariable ( " Con::scriptWarningsAsAsserts " , TypeBool , & scriptWarningsAsAsserts , " If true, script warnings (outside of syntax errors) will be treated as fatal asserts. " ) ;
2012-09-19 15:15:01 +00:00
// Current script file name and root
addVariable ( " Con::File " , TypeString , & gCurrentFile , " The currently executing script file. \n "
2017-01-06 23:04:28 +00:00
" @ingroup FileSystem \n " ) ;
2012-09-19 15:15:01 +00:00
addVariable ( " Con::Root " , TypeString , & gCurrentRoot , " The mod folder for the currently executing script file. \n "
2017-01-06 23:04:28 +00:00
" @ingroup FileSystem \n " ) ;
2012-09-19 15:15:01 +00:00
// alwaysUseDebugOutput determines whether to send output to the platform's
// "debug" system. see winConsole for an example.
// in ship builds we don't expose this variable to script
// and we set it to false by default (don't want to provide more information
// to potential hackers). platform code should also ifdef out the code that
// pays attention to this in ship builds (see winConsole.cpp)
// note that enabling this can slow down your game
// if you are running from the debugger and printing a lot of console messages.
# ifndef TORQUE_SHIPPING
addVariable ( " Con::alwaysUseDebugOutput " , TypeBool , & alwaysUseDebugOutput ,
" @brief Determines whether to send output to the platform's \" debug \" system. \n \n "
" @note This is disabled in shipping builds. \n "
2017-01-06 23:04:28 +00:00
" @ingroup Console " ) ;
2012-09-19 15:15:01 +00:00
# else
alwaysUseDebugOutput = false ;
# endif
// controls whether a timestamp is prepended to every console message
addVariable ( " Con::useTimestamp " , TypeBool , & useTimestamp , " If true a timestamp is prepended to every console message. \n "
2017-01-06 23:04:28 +00:00
" @ingroup Console \n " ) ;
2012-09-19 15:15:01 +00:00
2020-10-24 09:41:18 +00:00
// controls whether a real date and time is prepended to every console message
addVariable ( " Con::useRealTimestamp " , TypeBool , & useRealTimestamp , " If true a date and time will be prepended to every console message. \n "
" @ingroup Console \n " ) ;
2012-09-19 15:15:01 +00:00
// Plug us into the journaled console input signal.
smConsoleInput . notify ( postConsoleInput ) ;
}
bool isActive ( )
{
return active ;
}
bool isMainThread ( )
{
# ifdef TORQUE_MULTITHREAD
return ThreadManager : : isMainThread ( ) ;
# else
// If we're single threaded we're always in the main thread.
return true ;
# endif
}
//--------------------------------------
void getLockLog ( ConsoleLogEntry * & log , U32 & size )
{
consoleLogLocked = true ;
log = consoleLog . address ( ) ;
size = consoleLog . size ( ) ;
}
void unlockLog ( )
{
consoleLogLocked = false ;
}
U32 tabComplete ( char * inputBuffer , U32 cursorPos , U32 maxResultLength , bool forwardTab )
{
// Check for null input.
if ( ! inputBuffer [ 0 ] )
{
return cursorPos ;
}
// Cap the max result length.
if ( maxResultLength > MaxCompletionBufferSize )
{
maxResultLength = MaxCompletionBufferSize ;
}
// See if this is the same partial text as last checked.
2020-10-03 12:37:55 +00:00
if ( String : : compare ( tabBuffer , inputBuffer ) )
2012-09-19 15:15:01 +00:00
{
// If not...
// Save it for checking next time.
2018-03-06 06:59:05 +00:00
dStrcpy ( tabBuffer , inputBuffer , MaxCompletionBufferSize ) ;
2012-09-19 15:15:01 +00:00
// Scan backward from the cursor position to find the base to complete from.
S32 p = cursorPos ;
while ( ( p > 0 ) & & ( inputBuffer [ p - 1 ] ! = ' ' ) & & ( inputBuffer [ p - 1 ] ! = ' . ' ) & & ( inputBuffer [ p - 1 ] ! = ' ( ' ) )
{
p - - ;
}
completionBaseStart = p ;
completionBaseLen = cursorPos - p ;
2021-11-02 17:55:57 +00:00
// Bail if we end up at start of string
if ( p = = 0 )
{
return cursorPos ;
}
2012-09-19 15:15:01 +00:00
// Is this function being invoked on an object?
if ( inputBuffer [ p - 1 ] = = ' . ' )
{
// If so...
if ( p < = 1 )
{
// Bail if no object identifier.
return cursorPos ;
}
// Find the object identifier.
2023-05-01 15:13:12 +00:00
U64 objLast = - - p ;
2012-09-19 15:15:01 +00:00
while ( ( p > 0 ) & & ( inputBuffer [ p - 1 ] ! = ' ' ) & & ( inputBuffer [ p - 1 ] ! = ' ( ' ) )
{
p - - ;
}
if ( objLast = = p )
{
// Bail if no object identifier.
return cursorPos ;
}
// Look up the object identifier.
dStrncpy ( completionBuffer , inputBuffer + p , objLast - p ) ;
completionBuffer [ objLast - p ] = 0 ;
tabObject = Sim : : findObject ( completionBuffer ) ;
if ( tabObject = = NULL )
{
// Bail if not found.
return cursorPos ;
}
}
else
{
// Not invoked on an object; we'll use the global namespace.
tabObject = 0 ;
}
}
// Chop off the input text at the cursor position.
inputBuffer [ cursorPos ] = 0 ;
// Try to find a completion in the appropriate namespace.
const char * newText ;
if ( tabObject ! = 0 )
{
newText = tabObject - > tabComplete ( inputBuffer + completionBaseStart , completionBaseLen , forwardTab ) ;
}
else
{
// In the global namespace, we can complete on global vars as well as functions.
if ( inputBuffer [ completionBaseStart ] = = ' $ ' )
{
2023-04-23 08:39:54 +00:00
newText = gGlobalVars . tabComplete ( inputBuffer + completionBaseStart , completionBaseLen , forwardTab ) ;
2012-09-19 15:15:01 +00:00
}
else
{
newText = Namespace : : global ( ) - > tabComplete ( inputBuffer + completionBaseStart , completionBaseLen , forwardTab ) ;
}
}
if ( newText )
{
// If we got something, append it to the input text.
S32 len = dStrlen ( newText ) ;
if ( len + completionBaseStart > maxResultLength )
{
len = maxResultLength - completionBaseStart ;
}
dStrncpy ( inputBuffer + completionBaseStart , newText , len ) ;
inputBuffer [ completionBaseStart + len ] = 0 ;
// And set the cursor after it.
cursorPos = completionBaseStart + len ;
}
// Save the modified input buffer for checking next time.
2018-03-06 06:59:05 +00:00
dStrcpy ( tabBuffer , inputBuffer , MaxCompletionBufferSize ) ;
2012-09-19 15:15:01 +00:00
// Return the new (maybe) cursor position.
return cursorPos ;
}
//------------------------------------------------------------------------------
static void log ( const char * string )
{
// Bail if we ain't logging.
if ( ! consoleLogMode )
{
return ;
}
// In mode 1, we open, append, close on each log write.
if ( ( consoleLogMode & 0x3 ) = = 1 )
{
consoleLogFile . open ( defLogFileName , Torque : : FS : : File : : ReadWrite ) ;
}
// Write to the log if its status is hunky-dory.
if ( ( consoleLogFile . getStatus ( ) = = Stream : : Ok ) | | ( consoleLogFile . getStatus ( ) = = Stream : : EOS ) )
{
consoleLogFile . setPosition ( consoleLogFile . getStreamSize ( ) ) ;
// If this is the first write...
if ( newLogFile )
{
// Make a header.
Platform : : LocalTime lt ;
Platform : : getLocalTime ( lt ) ;
char buffer [ 128 ] ;
dSprintf ( buffer , sizeof ( buffer ) , " //-------------------------- %d/%d/%d -- %02d:%02d:%02d ----- \r \n " ,
lt . month + 1 ,
lt . monthday ,
lt . year + 1900 ,
lt . hour ,
lt . min ,
lt . sec ) ;
consoleLogFile . write ( dStrlen ( buffer ) , buffer ) ;
newLogFile = false ;
if ( consoleLogMode & 0x4 )
{
// Dump anything that has been printed to the console so far.
consoleLogMode - = 0x4 ;
U32 size , line ;
ConsoleLogEntry * log ;
getLockLog ( log , size ) ;
for ( line = 0 ; line < size ; line + + )
{
consoleLogFile . write ( dStrlen ( log [ line ] . mString ) , log [ line ] . mString ) ;
consoleLogFile . write ( 2 , " \r \n " ) ;
}
unlockLog ( ) ;
}
}
// Now write what we came here to write.
consoleLogFile . write ( dStrlen ( string ) , string ) ;
consoleLogFile . write ( 2 , " \r \n " ) ;
}
if ( ( consoleLogMode & 0x3 ) = = 1 )
{
consoleLogFile . close ( ) ;
}
}
//------------------------------------------------------------------------------
static void _printf ( ConsoleLogEntry : : Level level , ConsoleLogEntry : : Type type , const char * fmt , va_list argptr )
{
if ( ! active )
2017-01-06 23:04:28 +00:00
return ;
2023-09-05 03:28:49 +00:00
Con : : active = false ;
2012-09-19 15:15:01 +00:00
2023-05-01 15:13:12 +00:00
char buffer [ 8192 ] = { } ;
2012-09-19 15:15:01 +00:00
U32 offset = 0 ;
2023-04-23 08:39:54 +00:00
if ( gTraceOn & & ! getFrameStack ( ) . empty ( ) )
2012-09-19 15:15:01 +00:00
{
2023-04-23 08:39:54 +00:00
offset = getFrameStack ( ) . size ( ) * 3 ;
2012-09-19 15:15:01 +00:00
for ( U32 i = 0 ; i < offset ; i + + )
buffer [ i ] = ' ' ;
}
2020-10-24 09:41:18 +00:00
if ( useRealTimestamp )
{
Platform : : LocalTime lt ;
Platform : : getLocalTime ( lt ) ;
2020-10-25 00:39:27 +00:00
offset + = dSprintf ( buffer + offset , sizeof ( buffer ) - offset , " [%d-%d-%d %02d:%02d:%02d] " , lt . year + 1900 , lt . month + 1 , lt . monthday , lt . hour , lt . min , lt . sec ) ;
2020-10-24 09:41:18 +00:00
}
2012-09-19 15:15:01 +00:00
if ( useTimestamp )
{
U32 curTime = Platform : : getRealMilliseconds ( ) - startTime ;
2023-09-05 05:09:44 +00:00
offset + = dSprintf ( buffer + offset , sizeof ( buffer ) - offset , " [+%4d.%03d] " , U32 ( curTime * 0.001 ) , curTime % 1000 ) ;
2012-09-19 15:15:01 +00:00
}
2020-10-25 00:39:27 +00:00
2023-09-05 03:28:49 +00:00
if ( useTimestamp | | useRealTimestamp )
{
2020-10-25 00:39:27 +00:00
offset + = dSprintf ( buffer + offset , sizeof ( buffer ) - offset , " " ) ;
}
2012-09-19 15:15:01 +00:00
dVsprintf ( buffer + offset , sizeof ( buffer ) - offset , fmt , argptr ) ;
for ( S32 i = 0 ; i < gConsumers . size ( ) ; i + + )
gConsumers [ i ] ( level , buffer ) ;
if ( logBufferEnabled | | consoleLogMode )
{
char * pos = buffer ;
while ( * pos )
{
if ( * pos = = ' \t ' )
* pos = ' ^ ' ;
pos + + ;
}
pos = buffer ;
for ( ; ; )
{
char * eofPos = dStrchr ( pos , ' \n ' ) ;
if ( eofPos )
* eofPos = 0 ;
log ( pos ) ;
if ( logBufferEnabled & & ! consoleLogLocked )
{
ConsoleLogEntry entry ;
entry . mLevel = level ;
entry . mType = type ;
# ifndef TORQUE_SHIPPING // this is equivalent to a memory leak, turn it off in ship build
2023-05-01 15:13:12 +00:00
U64 logStringLen = dStrlen ( pos ) + 1 ;
2018-03-09 01:59:40 +00:00
entry . mString = ( const char * ) consoleLogChunker . alloc ( logStringLen ) ;
dStrcpy ( const_cast < char * > ( entry . mString ) , pos , logStringLen ) ;
2012-09-19 15:15:01 +00:00
// This prevents infinite recursion if the console itself needs to
// re-allocate memory to accommodate the new console log entry, and
// LOG_PAGE_ALLOCS is defined. It is kind of a dirty hack, but the
// uses for LOG_PAGE_ALLOCS are limited, and it is not worth writing
// a lot of special case code to support this situation. -patw
const bool save = Con : : active ;
Con : : active = false ;
consoleLog . push_back ( entry ) ;
Con : : active = save ;
# endif
}
if ( ! eofPos )
break ;
pos = eofPos + 1 ;
}
}
Con : : active = true ;
}
//------------------------------------------------------------------------------
void printf ( const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
_printf ( ConsoleLogEntry : : Normal , ConsoleLogEntry : : General , fmt , argptr ) ;
va_end ( argptr ) ;
}
void warnf ( ConsoleLogEntry : : Type type , const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
_printf ( ConsoleLogEntry : : Warning , type , fmt , argptr ) ;
va_end ( argptr ) ;
}
void errorf ( ConsoleLogEntry : : Type type , const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
_printf ( ConsoleLogEntry : : Error , type , fmt , argptr ) ;
va_end ( argptr ) ;
}
void warnf ( const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
_printf ( ConsoleLogEntry : : Warning , ConsoleLogEntry : : General , fmt , argptr ) ;
va_end ( argptr ) ;
}
void errorf ( const char * fmt , . . . )
{
va_list argptr ;
va_start ( argptr , fmt ) ;
_printf ( ConsoleLogEntry : : Error , ConsoleLogEntry : : General , fmt , argptr ) ;
va_end ( argptr ) ;
}
//---------------------------------------------------------------------------
2012-09-23 08:59:48 +00:00
bool getVariableObjectField ( const char * name , SimObject * * object , const char * * field )
2012-09-19 15:15:01 +00:00
{
// get the field info from the object..
2012-09-23 08:59:48 +00:00
const char * dot = dStrchr ( name , ' . ' ) ;
if ( name [ 0 ] ! = ' $ ' & & dot )
2012-09-19 15:15:01 +00:00
{
2023-05-01 15:13:12 +00:00
U64 len = dStrlen ( name ) ;
2012-09-19 15:15:01 +00:00
AssertFatal ( len < sizeof ( scratchBuffer ) - 1 , " Sim::getVariable - name too long " ) ;
dMemcpy ( scratchBuffer , name , len + 1 ) ;
char * token = dStrtok ( scratchBuffer , " . " ) ;
SimObject * obj = Sim : : findObject ( token ) ;
if ( ! obj )
2012-09-23 08:59:48 +00:00
return false ;
2012-09-19 15:15:01 +00:00
token = dStrtok ( 0 , " . \0 " ) ;
if ( ! token )
2012-09-23 08:59:48 +00:00
return false ;
2012-09-19 15:15:01 +00:00
while ( token ! = NULL )
{
const char * val = obj - > getDataField ( StringTable - > insert ( token ) , 0 ) ;
if ( ! val )
2012-09-23 08:59:48 +00:00
return false ;
2012-09-19 15:15:01 +00:00
char * fieldToken = token ;
token = dStrtok ( 0 , " . \0 " ) ;
if ( token )
{
obj = Sim : : findObject ( token ) ;
if ( ! obj )
2012-09-23 08:59:48 +00:00
return false ;
2012-09-19 15:15:01 +00:00
}
else
{
2012-10-11 20:29:39 +00:00
* object = obj ;
* field = fieldToken ;
return true ;
2012-09-19 15:15:01 +00:00
}
}
}
2012-09-23 08:59:48 +00:00
return false ;
}
Dictionary : : Entry * getVariableEntry ( const char * name )
{
name = prependDollar ( name ) ;
2023-04-23 08:39:54 +00:00
return gGlobalVars . lookup ( StringTable - > insert ( name ) ) ;
2012-09-23 08:59:48 +00:00
}
Dictionary : : Entry * addVariableEntry ( const char * name )
{
name = prependDollar ( name ) ;
2023-04-23 08:39:54 +00:00
return gGlobalVars . add ( StringTable - > insert ( name ) ) ;
2012-09-23 08:59:48 +00:00
}
Dictionary : : Entry * getAddVariableEntry ( const char * name )
{
2012-09-19 15:15:01 +00:00
name = prependDollar ( name ) ;
2012-09-23 08:59:48 +00:00
StringTableEntry stName = StringTable - > insert ( name ) ;
2023-04-23 08:39:54 +00:00
Dictionary : : Entry * entry = gGlobalVars . lookup ( stName ) ;
2012-10-11 20:29:39 +00:00
if ( ! entry )
2023-04-23 08:39:54 +00:00
entry = gGlobalVars . add ( stName ) ;
2012-09-23 08:59:48 +00:00
return entry ;
}
void setVariable ( const char * name , const char * value )
{
SimObject * obj = NULL ;
const char * objField = NULL ;
2012-10-11 20:29:39 +00:00
if ( getVariableObjectField ( name , & obj , & objField ) )
{
2017-01-06 23:04:28 +00:00
obj - > setDataField ( StringTable - > insert ( objField ) , 0 , value ) ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
name = prependDollar ( name ) ;
2023-04-23 08:39:54 +00:00
gGlobalVars . setVariable ( StringTable - > insert ( name ) , value ) ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
}
void setBoolVariable ( const char * varName , bool value )
{
2012-09-23 08:59:48 +00:00
SimObject * obj = NULL ;
const char * objField = NULL ;
2012-10-11 20:29:39 +00:00
if ( getVariableObjectField ( varName , & obj , & objField ) )
{
2017-01-06 23:04:28 +00:00
obj - > setDataField ( StringTable - > insert ( objField ) , 0 , value ? " 1 " : " 0 " ) ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
varName = prependDollar ( varName ) ;
Dictionary : : Entry * entry = getAddVariableEntry ( varName ) ;
2017-01-06 23:04:28 +00:00
entry - > setStringValue ( value ? " 1 " : " 0 " ) ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
}
void setIntVariable ( const char * varName , S32 value )
{
2012-09-23 08:59:48 +00:00
SimObject * obj = NULL ;
const char * objField = NULL ;
2012-10-11 20:29:39 +00:00
if ( getVariableObjectField ( varName , & obj , & objField ) )
{
2018-03-15 20:36:38 +00:00
char varBuffer [ 32 ] ;
dSprintf ( varBuffer , sizeof ( varBuffer ) , " %d " , value ) ;
obj - > setDataField ( StringTable - > insert ( objField ) , 0 , varBuffer ) ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
varName = prependDollar ( varName ) ;
Dictionary : : Entry * entry = getAddVariableEntry ( varName ) ;
entry - > setIntValue ( value ) ;
}
2012-09-19 15:15:01 +00:00
}
void setFloatVariable ( const char * varName , F32 value )
{
2012-09-23 08:59:48 +00:00
SimObject * obj = NULL ;
const char * objField = NULL ;
2012-10-11 20:29:39 +00:00
if ( getVariableObjectField ( varName , & obj , & objField ) )
{
2018-03-15 20:36:38 +00:00
char varBuffer [ 32 ] ;
dSprintf ( varBuffer , sizeof ( varBuffer ) , " %g " , value ) ;
obj - > setDataField ( StringTable - > insert ( objField ) , 0 , varBuffer ) ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
varName = prependDollar ( varName ) ;
Dictionary : : Entry * entry = getAddVariableEntry ( varName ) ;
2017-01-06 23:04:28 +00:00
entry - > setFloatValue ( value ) ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
}
//---------------------------------------------------------------------------
void addConsumer ( ConsumerCallback consumer )
{
gConsumers . push_back ( consumer ) ;
}
// dhc - found this empty -- trying what I think is a reasonable impl.
void removeConsumer ( ConsumerCallback consumer )
{
for ( S32 i = 0 ; i < gConsumers . size ( ) ; i + + )
{
if ( gConsumers [ i ] = = consumer )
{
// remove it from the list.
gConsumers . erase ( i ) ;
break ;
}
}
}
void stripColorChars ( char * line )
{
char * c = line ;
char cp = * c ;
while ( cp )
{
if ( cp < 18 )
{
// Could be a color control character; let's take a closer look.
if ( ( cp ! = 8 ) & & ( cp ! = 9 ) & & ( cp ! = 10 ) & & ( cp ! = 13 ) )
{
// Yep... copy following chars forward over this.
char * cprime = c ;
char cpp ;
do
{
cpp = * + + cprime ;
* ( cprime - 1 ) = cpp ;
}
while ( cpp ) ;
// Back up 1 so we'll check this position again post-copy.
c - - ;
}
}
cp = * + + c ;
}
}
2023-04-23 08:39:54 +00:00
//
2012-09-23 08:59:48 +00:00
const char * getObjectTokenField ( const char * name )
2012-09-19 15:15:01 +00:00
{
2012-09-23 08:59:48 +00:00
const char * dot = dStrchr ( name , ' . ' ) ;
if ( name [ 0 ] ! = ' $ ' & & dot )
2012-09-19 15:15:01 +00:00
{
2023-05-01 15:13:12 +00:00
U64 len = dStrlen ( name ) ;
2012-09-23 08:59:48 +00:00
AssertFatal ( len < sizeof ( scratchBuffer ) - 1 , " Sim::getVariable - object name too long " ) ;
2012-09-19 15:15:01 +00:00
dMemcpy ( scratchBuffer , name , len + 1 ) ;
char * token = dStrtok ( scratchBuffer , " . " ) ;
SimObject * obj = Sim : : findObject ( token ) ;
if ( ! obj )
return ( " " ) ;
token = dStrtok ( 0 , " . \0 " ) ;
if ( ! token )
return ( " " ) ;
while ( token ! = NULL )
{
const char * val = obj - > getDataField ( StringTable - > insert ( token ) , 0 ) ;
if ( ! val )
return ( " " ) ;
token = dStrtok ( 0 , " . \0 " ) ;
if ( token )
{
obj = Sim : : findObject ( token ) ;
if ( ! obj )
return ( " " ) ;
}
else
return ( val ) ;
}
}
2012-09-23 08:59:48 +00:00
return NULL ;
}
2017-07-24 05:40:27 +00:00
const char * getVariable ( const char * name , const char * def )
2012-09-23 08:59:48 +00:00
{
const char * objField = getObjectTokenField ( name ) ;
2012-10-11 20:29:39 +00:00
if ( objField )
{
2012-09-23 08:59:48 +00:00
return objField ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
Dictionary : : Entry * entry = getVariableEntry ( name ) ;
2017-07-24 05:40:27 +00:00
return entry ? entry - > getStringValue ( ) : def ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
}
const char * getLocalVariable ( const char * name )
{
name = prependPercent ( name ) ;
2023-04-23 08:39:54 +00:00
return Con : : getCurrentStackFrame ( ) - > getVariable ( StringTable - > insert ( name ) ) ;
2012-09-19 15:15:01 +00:00
}
bool getBoolVariable ( const char * varName , bool def )
{
2012-09-23 08:59:48 +00:00
const char * objField = getObjectTokenField ( varName ) ;
2012-10-11 20:29:39 +00:00
if ( objField )
{
2012-09-23 08:59:48 +00:00
return * objField ? dAtob ( objField ) : def ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
Dictionary : : Entry * entry = getVariableEntry ( varName ) ;
2012-10-11 20:29:39 +00:00
objField = entry ? entry - > getStringValue ( ) : " " ;
2012-09-23 08:59:48 +00:00
return * objField ? dAtob ( objField ) : def ;
}
2012-09-19 15:15:01 +00:00
}
S32 getIntVariable ( const char * varName , S32 def )
{
2012-09-23 08:59:48 +00:00
const char * objField = getObjectTokenField ( varName ) ;
2012-10-11 20:29:39 +00:00
if ( objField )
{
2012-09-23 08:59:48 +00:00
return * objField ? dAtoi ( objField ) : def ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
Dictionary : : Entry * entry = getVariableEntry ( varName ) ;
2012-10-11 20:29:39 +00:00
return entry ? entry - > getIntValue ( ) : def ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
}
F32 getFloatVariable ( const char * varName , F32 def )
{
2012-09-23 08:59:48 +00:00
const char * objField = getObjectTokenField ( varName ) ;
2012-10-11 20:29:39 +00:00
if ( objField )
{
2012-09-23 08:59:48 +00:00
return * objField ? dAtof ( objField ) : def ;
2012-10-11 20:29:39 +00:00
}
else
{
2012-09-23 08:59:48 +00:00
Dictionary : : Entry * entry = getVariableEntry ( varName ) ;
2017-01-06 23:04:28 +00:00
return entry ? entry - > getFloatValue ( ) : def ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
}
//---------------------------------------------------------------------------
void addVariable ( const char * name ,
S32 type ,
void * dptr ,
const char * usage )
{
2023-04-23 08:39:54 +00:00
gGlobalVars . addVariable ( name , type , dptr , usage ) ;
2012-09-19 15:15:01 +00:00
}
void addConstant ( const char * name ,
S32 type ,
const void * dptr ,
const char * usage )
{
2023-04-23 08:39:54 +00:00
Dictionary : : Entry * entry = gGlobalVars . addVariable ( name , type , const_cast < void * > ( dptr ) , usage ) ;
2012-09-19 15:15:01 +00:00
entry - > mIsConstant = true ;
}
bool removeVariable ( const char * name )
{
name = StringTable - > lookup ( prependDollar ( name ) ) ;
2023-04-23 08:39:54 +00:00
return name ! = 0 & & gGlobalVars . removeVariable ( name ) ;
2012-09-19 15:15:01 +00:00
}
void addVariableNotify ( const char * name , const NotifyDelegate & callback )
{
2023-04-23 08:39:54 +00:00
gGlobalVars . addVariableNotify ( name , callback ) ;
2012-09-19 15:15:01 +00:00
}
void removeVariableNotify ( const char * name , const NotifyDelegate & callback )
{
2023-04-23 08:39:54 +00:00
gGlobalVars . removeVariableNotify ( name , callback ) ;
2012-09-19 15:15:01 +00:00
}
//---------------------------------------------------------------------------
void addCommand ( const char * nsName , const char * name , StringCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace * ns = lookupNamespace ( nsName ) ;
ns - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * nsName , const char * name , VoidCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace * ns = lookupNamespace ( nsName ) ;
ns - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * nsName , const char * name , IntCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace * ns = lookupNamespace ( nsName ) ;
ns - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * nsName , const char * name , FloatCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace * ns = lookupNamespace ( nsName ) ;
ns - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * nsName , const char * name , BoolCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace * ns = lookupNamespace ( nsName ) ;
ns - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void noteScriptCallback ( const char * className , const char * funcName , const char * usage , ConsoleFunctionHeader * header )
{
Namespace * ns = lookupNamespace ( className ) ;
ns - > addScriptCallback ( StringTable - > insert ( funcName ) , usage , header ) ;
}
void markCommandGroup ( const char * nsName , const char * name , const char * usage )
{
Namespace * ns = lookupNamespace ( nsName ) ;
ns - > markGroup ( name , usage ) ;
}
void beginCommandGroup ( const char * nsName , const char * name , const char * usage )
{
markCommandGroup ( nsName , name , usage ) ;
}
void endCommandGroup ( const char * nsName , const char * name )
{
markCommandGroup ( nsName , name , NULL ) ;
}
void addCommand ( const char * name , StringCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace : : global ( ) - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * name , VoidCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace : : global ( ) - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * name , IntCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace : : global ( ) - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * name , FloatCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace : : global ( ) - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
void addCommand ( const char * name , BoolCallback cb , const char * usage , S32 minArgs , S32 maxArgs , bool isToolOnly , ConsoleFunctionHeader * header )
{
Namespace : : global ( ) - > addCommand ( StringTable - > insert ( name ) , cb , usage , minArgs , maxArgs , isToolOnly , header ) ;
}
2015-02-07 22:41:54 +00:00
//------------------------------------------------------------------------------
// Internal execute for global function which does not save the stack
2021-03-31 03:58:07 +00:00
ConsoleValue _internalExecute ( S32 argc , ConsoleValue argv [ ] )
2015-02-07 22:41:54 +00:00
{
2021-03-31 03:58:07 +00:00
StringTableEntry funcName = StringTable - > insert ( argv [ 0 ] . getString ( ) ) ;
2015-02-07 22:41:54 +00:00
Namespace : : Entry * ent ;
2021-03-31 03:58:07 +00:00
2015-02-07 22:41:54 +00:00
ent = Namespace : : global ( ) - > lookup ( funcName ) ;
if ( ! ent )
{
2021-03-31 03:58:07 +00:00
warnf ( ConsoleLogEntry : : Script , " %s: Unknown command. " , funcName ) ;
2015-02-07 22:41:54 +00:00
STR . clearFunctionOffset ( ) ;
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2015-02-07 22:41:54 +00:00
}
2021-03-31 03:58:07 +00:00
2025-05-07 16:04:55 +00:00
return ( ent - > execute ( argc , argv , NULL ) ) ;
2015-02-07 22:41:54 +00:00
}
2021-03-31 03:58:07 +00:00
ConsoleValue execute ( S32 argc , ConsoleValue argv [ ] )
2012-09-19 15:15:01 +00:00
{
# ifdef TORQUE_MULTITHREAD
if ( isMainThread ( ) )
{
# endif
2015-02-07 22:41:54 +00:00
ConsoleStackFrameSaver stackSaver ;
stackSaver . save ( ) ;
2017-01-06 23:04:28 +00:00
return _internalExecute ( argc , argv ) ;
2012-09-19 15:15:01 +00:00
# ifdef TORQUE_MULTITHREAD
}
else
{
SimConsoleThreadExecCallback cb ;
SimConsoleThreadExecEvent * evt = new SimConsoleThreadExecEvent ( argc , argv , false , & cb ) ;
Sim : : postEvent ( Sim : : getRootGroup ( ) , evt , Sim : : getCurrentTime ( ) ) ;
return cb . waitForResult ( ) ;
}
# endif
}
2021-03-31 03:58:07 +00:00
ConsoleValue execute ( S32 argc , const char * argv [ ] )
2012-09-23 08:59:48 +00:00
{
2015-02-07 22:41:54 +00:00
ConsoleStackFrameSaver stackSaver ;
stackSaver . save ( ) ;
2021-03-31 03:58:07 +00:00
StringArrayToConsoleValueWrapper args ( argc , argv ) ;
2025-05-07 16:04:55 +00:00
return ( execute ( args . count ( ) , args ) ) ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
//------------------------------------------------------------------------------
2015-02-07 22:41:54 +00:00
// Internal execute for object method which does not save the stack
2021-03-31 03:58:07 +00:00
static ConsoleValue _internalExecute ( SimObject * object , S32 argc , ConsoleValue argv [ ] , bool thisCallOnly )
2012-09-19 15:15:01 +00:00
{
2023-05-01 15:13:12 +00:00
if ( object = = NULL )
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2023-05-01 15:13:12 +00:00
2012-09-19 15:15:01 +00:00
if ( argc < 2 )
2015-02-07 22:41:54 +00:00
{
STR . clearFunctionOffset ( ) ;
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2015-02-07 22:41:54 +00:00
}
2012-09-19 15:15:01 +00:00
// [neo, 10/05/2007 - #3010]
// Make sure we don't get recursive calls, respect the flag!
// Should we be calling handlesMethod() first?
if ( ! thisCallOnly )
{
ICallMethod * com = dynamic_cast < ICallMethod * > ( object ) ;
2012-10-11 20:29:39 +00:00
if ( com )
{
2021-11-21 00:07:48 +00:00
ConsoleStackFrameSaver saver ;
saver . save ( ) ;
2012-09-19 15:15:01 +00:00
com - > callMethodArgList ( argc , argv , false ) ;
2012-10-11 20:29:39 +00:00
}
2012-09-19 15:15:01 +00:00
}
2021-03-31 03:58:07 +00:00
StringTableEntry funcName = StringTable - > insert ( argv [ 0 ] . getString ( ) ) ;
2018-04-21 08:24:41 +00:00
2012-09-19 15:15:01 +00:00
if ( object - > getNamespace ( ) )
{
2015-02-07 22:41:54 +00:00
U32 ident = object - > getId ( ) ;
2012-09-19 15:15:01 +00:00
Namespace : : Entry * ent = object - > getNamespace ( ) - > lookup ( funcName ) ;
if ( ent = = NULL )
{
2021-04-13 01:26:26 +00:00
//warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId());
2012-09-19 15:15:01 +00:00
STR . clearFunctionOffset ( ) ;
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2012-09-19 15:15:01 +00:00
}
2023-10-15 18:39:23 +00:00
const char * oldIdent = dStrdup ( argv [ 1 ] . getString ( ) ) ;
2012-09-19 15:15:01 +00:00
// Twiddle %this argument
2021-03-31 03:58:07 +00:00
argv [ 1 ] . setInt ( ident ) ;
2012-09-19 15:15:01 +00:00
2025-05-07 16:04:55 +00:00
ConsoleValue ret = ( ent - > execute ( argc , argv , object ) ) ;
2012-09-19 15:15:01 +00:00
// Twiddle it back
2021-04-02 04:57:49 +00:00
argv [ 1 ] . setString ( oldIdent ) ;
2021-09-09 23:30:32 +00:00
dFree ( oldIdent ) ;
2012-09-19 15:15:01 +00:00
2021-09-09 23:30:32 +00:00
return ret ;
2012-09-19 15:15:01 +00:00
}
2015-02-07 22:41:54 +00:00
2021-03-31 03:58:07 +00:00
warnf ( ConsoleLogEntry : : Script , " Con::execute - %d has no namespace: %s " , object - > getId ( ) , funcName ) ;
2015-02-07 22:41:54 +00:00
STR . clearFunctionOffset ( ) ;
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2015-02-07 22:41:54 +00:00
}
2021-03-31 03:58:07 +00:00
ConsoleValue execute ( SimObject * object , S32 argc , ConsoleValue argv [ ] , bool thisCallOnly )
2015-02-07 22:41:54 +00:00
{
if ( argc < 2 )
{
STR . clearFunctionOffset ( ) ;
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2015-02-07 22:41:54 +00:00
}
ConsoleStackFrameSaver stackSaver ;
stackSaver . save ( ) ;
if ( object - > getNamespace ( ) | | ! thisCallOnly )
{
if ( isMainThread ( ) )
{
2025-05-07 16:04:55 +00:00
return ( _internalExecute ( object , argc , argv , thisCallOnly ) ) ;
2015-02-07 22:41:54 +00:00
}
else
{
SimConsoleThreadExecCallback cb ;
SimConsoleThreadExecEvent * evt = new SimConsoleThreadExecEvent ( argc , argv , true , & cb ) ;
Sim : : postEvent ( object , evt , Sim : : getCurrentTime ( ) ) ;
}
}
2021-03-31 03:58:07 +00:00
warnf ( ConsoleLogEntry : : Script , " Con::execute - %d has no namespace: %s " , object - > getId ( ) , argv [ 0 ] . getString ( ) ) ;
2015-02-07 22:41:54 +00:00
STR . clearFunctionOffset ( ) ;
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2012-09-19 15:15:01 +00:00
}
2021-03-31 03:58:07 +00:00
ConsoleValue execute ( SimObject * object , S32 argc , const char * argv [ ] , bool thisCallOnly )
2012-09-23 08:59:48 +00:00
{
2015-02-07 22:41:54 +00:00
ConsoleStackFrameSaver stackSaver ;
stackSaver . save ( ) ;
2021-03-31 03:58:07 +00:00
StringArrayToConsoleValueWrapper args ( argc , argv ) ;
2025-05-07 16:04:55 +00:00
return ( execute ( object , args . count ( ) , args , thisCallOnly ) ) ;
2012-09-23 08:59:48 +00:00
}
2021-03-31 03:58:07 +00:00
inline ConsoleValue _executef ( SimObject * obj , S32 checkArgc , S32 argc , ConsoleValue * argv )
2012-09-19 15:15:01 +00:00
{
const U32 maxArg = 12 ;
2021-03-31 03:58:07 +00:00
AssertFatal ( checkArgc = = argc , " Incorrect arg count passed to Con::executef(SimObject*) " ) ;
2012-09-19 15:15:01 +00:00
AssertFatal ( argc < = maxArg - 1 , " Too many args passed to Con::_executef(SimObject*). Please update the function to handle more. " ) ;
2025-05-07 16:04:55 +00:00
return ( execute ( obj , argc , argv ) ) ;
2012-09-23 08:59:48 +00:00
}
2012-09-19 15:15:01 +00:00
//------------------------------------------------------------------------------
2021-03-31 03:58:07 +00:00
inline ConsoleValue _executef ( S32 checkArgc , S32 argc , ConsoleValue * argv )
2012-09-19 15:15:01 +00:00
{
const U32 maxArg = 10 ;
AssertFatal ( checkArgc = = argc , " Incorrect arg count passed to Con::executef() " ) ;
AssertFatal ( argc < = maxArg , " Too many args passed to Con::_executef(). Please update the function to handle more. " ) ;
2025-05-07 16:04:55 +00:00
return ( execute ( argc , argv ) ) ;
2012-09-19 15:15:01 +00:00
}
//------------------------------------------------------------------------------
bool isFunction ( const char * fn )
{
const char * string = StringTable - > lookup ( fn ) ;
if ( ! string )
return false ;
else
return Namespace : : global ( ) - > lookup ( string ) ! = NULL ;
}
//------------------------------------------------------------------------------
void setLogMode ( S32 newMode )
{
if ( ( newMode & 0x3 ) ! = ( consoleLogMode & 0x3 ) ) {
if ( newMode & & ! consoleLogMode ) {
// Enabling logging when it was previously disabled.
newLogFile = true ;
}
if ( ( consoleLogMode & 0x3 ) = = 2 ) {
// Changing away from mode 2, must close logfile.
consoleLogFile . close ( ) ;
}
else if ( ( newMode & 0x3 ) = = 2 ) {
# ifdef _XBOX
// Xbox is not going to support logging to a file. Use the OutputDebugStr
// log consumer
Platform : : debugBreak ( ) ;
# endif
// Starting mode 2, must open logfile.
consoleLogFile . open ( defLogFileName , Torque : : FS : : File : : Write ) ;
}
consoleLogMode = newMode ;
}
}
2023-04-23 08:39:54 +00:00
//------------------------------------------------------------------------------
ReturnBuffer retBuffer ;
char * getReturnBuffer ( U32 bufferSize )
{
return retBuffer . getBuffer ( bufferSize ) ;
}
char * getReturnBuffer ( const char * stringToCopy )
{
U32 len = dStrlen ( stringToCopy ) + 1 ;
char * ret = retBuffer . getBuffer ( len ) ;
dMemcpy ( ret , stringToCopy , len ) ;
return ret ;
}
char * getReturnBuffer ( const String & str )
{
const U32 size = str . size ( ) ;
char * ret = retBuffer . getBuffer ( size ) ;
dMemcpy ( ret , str . c_str ( ) , size ) ;
return ret ;
}
char * getReturnBuffer ( const StringBuilder & str )
{
char * buffer = Con : : getReturnBuffer ( str . length ( ) + 1 ) ;
str . copy ( buffer ) ;
buffer [ str . length ( ) ] = ' \0 ' ;
return buffer ;
}
char * getArgBuffer ( U32 bufferSize )
{
return STR . getArgBuffer ( bufferSize ) ;
}
char * getFloatArg ( F64 arg )
{
char * ret = STR . getArgBuffer ( 32 ) ;
dSprintf ( ret , 32 , " %g " , arg ) ;
return ret ;
}
char * getIntArg ( S32 arg )
{
char * ret = STR . getArgBuffer ( 32 ) ;
dSprintf ( ret , 32 , " %d " , arg ) ;
return ret ;
}
char * getBoolArg ( bool arg )
{
char * ret = STR . getArgBuffer ( 32 ) ;
dSprintf ( ret , 32 , " %d " , arg ) ;
return ret ;
}
char * getStringArg ( const char * arg )
{
U32 len = dStrlen ( arg ) + 1 ;
char * ret = STR . getArgBuffer ( len ) ;
dMemcpy ( ret , arg , len ) ;
return ret ;
}
char * getStringArg ( const String & arg )
{
const U32 size = arg . size ( ) ;
char * ret = STR . getArgBuffer ( size ) ;
dMemcpy ( ret , arg . c_str ( ) , size ) ;
return ret ;
}
//------------------------------------------------------------------------------
2012-09-19 15:15:01 +00:00
Namespace * lookupNamespace ( const char * ns )
{
if ( ! ns )
return Namespace : : global ( ) ;
return Namespace : : find ( StringTable - > insert ( ns ) ) ;
}
bool linkNamespaces ( const char * parent , const char * child )
{
Namespace * pns = lookupNamespace ( parent ) ;
Namespace * cns = lookupNamespace ( child ) ;
if ( pns & & cns )
return cns - > classLinkTo ( pns ) ;
return false ;
}
bool unlinkNamespaces ( const char * parent , const char * child )
{
Namespace * pns = lookupNamespace ( parent ) ;
Namespace * cns = lookupNamespace ( child ) ;
if ( pns = = cns )
{
Con : : warnf ( " Con::unlinkNamespaces - trying to unlink '%s' from itself, aborting. " , parent ) ;
return false ;
}
if ( pns & & cns )
return cns - > unlinkClass ( pns ) ;
return false ;
}
bool classLinkNamespaces ( Namespace * parent , Namespace * child )
{
if ( parent & & child )
return child - > classLinkTo ( parent ) ;
return false ;
}
void setData ( S32 type , void * dptr , S32 index , S32 argc , const char * * argv , const EnumTable * tbl , BitSet32 flag )
{
ConsoleBaseType * cbt = ConsoleBaseType : : getType ( type ) ;
AssertFatal ( cbt , " Con::setData - could not resolve type ID! " ) ;
cbt - > setData ( ( void * ) ( ( ( const char * ) dptr ) + index * cbt - > getTypeSize ( ) ) , argc , argv , tbl , flag ) ;
}
const char * getData ( S32 type , void * dptr , S32 index , const EnumTable * tbl , BitSet32 flag )
{
ConsoleBaseType * cbt = ConsoleBaseType : : getType ( type ) ;
AssertFatal ( cbt , " Con::getData - could not resolve type ID! " ) ;
return cbt - > getData ( ( void * ) ( ( ( const char * ) dptr ) + index * cbt - > getTypeSize ( ) ) , tbl , flag ) ;
}
const char * getFormattedData ( S32 type , const char * data , const EnumTable * tbl , BitSet32 flag )
{
ConsoleBaseType * cbt = ConsoleBaseType : : getType ( type ) ;
AssertFatal ( cbt , " Con::getData - could not resolve type ID! " ) ;
// Datablock types are just a datablock
// name and don't ever need formatting.
if ( cbt - > isDatablock ( ) )
return data ;
bool currWarn = gWarnUndefinedScriptVariables ;
gWarnUndefinedScriptVariables = false ;
const char * globalValue = Con : : getVariable ( data ) ;
gWarnUndefinedScriptVariables = currWarn ;
if ( dStrlen ( globalValue ) > 0 )
return globalValue ;
void * variable = cbt - > getNativeVariable ( ) ;
if ( variable )
{
Con : : setData ( type , variable , 0 , 1 , & data , tbl , flag ) ;
const char * formattedVal = Con : : getData ( type , variable , 0 , tbl , flag ) ;
2014-05-15 07:12:43 +00:00
static const U32 bufSize = 2048 ;
char * returnBuffer = Con : : getReturnBuffer ( bufSize ) ;
dSprintf ( returnBuffer , bufSize , " %s \0 " , formattedVal ) ;
2012-09-19 15:15:01 +00:00
cbt - > deleteNativeVariable ( variable ) ;
return returnBuffer ;
}
else
return data ;
}
//------------------------------------------------------------------------------
bool isCurrentScriptToolScript ( )
{
// With a player build we ALWAYS return false
# ifndef TORQUE_TOOLS
return false ;
# else
2023-04-23 08:39:54 +00:00
const StringTableEntry cbFullPath = getCurrentScriptModulePath ( ) ;
2012-09-19 15:15:01 +00:00
if ( cbFullPath = = NULL )
return false ;
const StringTableEntry exePath = Platform : : getMainDotCsDir ( ) ;
return dStrnicmp ( exePath , cbFullPath , dStrlen ( exePath ) ) = = 0 ;
# endif
}
//------------------------------------------------------------------------------
2023-04-23 08:39:54 +00:00
bool isScriptFile ( const char * pFilePath )
{
return ( Torque : : FS : : IsFile ( pFilePath )
| | Torque : : FS : : IsFile ( pFilePath + String ( " .dso " ) )
| | Torque : : FS : : IsFile ( pFilePath + String ( " .mis " ) )
| | Torque : : FS : : IsFile ( pFilePath + String ( " .mis.dso " ) )
| | Torque : : FS : : IsFile ( pFilePath + String ( " .gui " ) )
| | Torque : : FS : : IsFile ( pFilePath + String ( " .gui.dso " ) )
| | Torque : : FS : : IsFile ( pFilePath + String ( " . " TORQUE_SCRIPT_EXTENSION ) )
| | Torque : : FS : : IsFile ( pFilePath + String ( " . " TORQUE_SCRIPT_EXTENSION ) + String ( " .dso " ) ) ) ;
}
//------------------------------------------------------------------------------
2012-09-19 15:15:01 +00:00
StringTableEntry getModNameFromPath ( const char * path )
{
if ( path = = NULL | | * path = = 0 )
return NULL ;
2023-05-01 15:13:12 +00:00
char buf [ 1024 ] = { } ;
2012-09-19 15:15:01 +00:00
buf [ 0 ] = 0 ;
if ( path [ 0 ] = = ' / ' | | path [ 1 ] = = ' : ' )
{
// It's an absolute path
const StringTableEntry exePath = Platform : : getMainDotCsDir ( ) ;
U32 len = dStrlen ( exePath ) ;
if ( dStrnicmp ( exePath , path , len ) = = 0 )
{
const char * ptr = path + len + 1 ;
const char * slash = dStrchr ( ptr , ' / ' ) ;
if ( slash )
{
dStrncpy ( buf , ptr , slash - ptr ) ;
buf [ slash - ptr ] = 0 ;
}
else
return NULL ;
}
else
return NULL ;
}
else
{
const char * slash = dStrchr ( path , ' / ' ) ;
if ( slash )
{
dStrncpy ( buf , path , slash - path ) ;
buf [ slash - path ] = 0 ;
}
else
return NULL ;
}
return StringTable - > insert ( buf ) ;
}
void postConsoleInput ( RawData data )
{
// Schedule this to happen at the next time event.
2021-08-20 02:25:11 +00:00
ConsoleValue argv [ 2 ] ;
2021-04-02 04:57:49 +00:00
argv [ 0 ] . setString ( " eval " ) ;
argv [ 1 ] . setString ( reinterpret_cast < const char * > ( data . data ) ) ;
2015-05-06 22:28:46 +00:00
2012-09-23 08:59:48 +00:00
Sim : : postCurrentEvent ( Sim : : getRootGroup ( ) , new SimConsoleEvent ( 2 , argv , false ) ) ;
2012-09-19 15:15:01 +00:00
}
//------------------------------------------------------------------------------
void pushInstantGroup ( String name )
{
sInstantGroupStack . push_back ( gInstantGroup ) ;
gInstantGroup = name ;
}
void popInstantGroup ( )
{
if ( sInstantGroupStack . empty ( ) )
gInstantGroup = String : : EmptyString ;
else
{
gInstantGroup = sInstantGroupStack . last ( ) ;
sInstantGroupStack . pop_back ( ) ;
}
}
2015-10-13 20:19:36 +00:00
typedef HashMap < StringTableEntry , StringTableEntry > typePathExpandoMap ;
static typePathExpandoMap PathExpandos ;
//-----------------------------------------------------------------------------
void addPathExpando ( const char * pExpandoName , const char * pPath )
{
// Sanity!
AssertFatal ( pExpandoName ! = NULL , " Expando name cannot be NULL. " ) ;
AssertFatal ( pPath ! = NULL , " Expando path cannot be NULL. " ) ;
// Fetch expando name.
StringTableEntry expandoName = StringTable - > insert ( pExpandoName ) ;
// Fetch the length of the path.
S32 pathLength = dStrlen ( pPath ) ;
char pathBuffer [ 1024 ] ;
// Sanity!
if ( pathLength = = 0 | | pathLength > = sizeof ( pathBuffer ) )
{
Con : : warnf ( " Cannot add path expando '%s' with path '%s' as the path is an invalid length. " , pExpandoName , pPath ) ;
return ;
}
// Strip repeat slashes.
if ( ! Con : : stripRepeatSlashes ( pathBuffer , pPath , sizeof ( pathBuffer ) ) )
{
Con : : warnf ( " Cannot add path expando '%s' with path '%s' as the path is an invalid length. " , pExpandoName , pPath ) ;
return ;
}
// Fetch new path length.
pathLength = dStrlen ( pathBuffer ) ;
// Sanity!
if ( pathLength = = 0 )
{
Con : : warnf ( " Cannot add path expando '%s' with path '%s' as the path is an invalid length. " , pExpandoName , pPath ) ;
return ;
}
// Remove any terminating slash.
if ( pathBuffer [ pathLength - 1 ] = = ' / ' )
pathBuffer [ pathLength - 1 ] = 0 ;
// Fetch expanded path.
StringTableEntry expandedPath = StringTable - > insert ( pathBuffer ) ;
// Info.
# if defined(TORQUE_DEBUG)
Con : : printf ( " Adding path expando of '%s' as '%s'. " , expandoName , expandedPath ) ;
# endif
// Find any existing path expando.
typePathExpandoMap : : iterator expandoItr = PathExpandos . find ( pExpandoName ) ;
// Does the expando exist?
if ( expandoItr ! = PathExpandos . end ( ) )
{
// Yes, so modify the path.
expandoItr - > value = expandedPath ;
return ;
}
// Insert expando.
PathExpandos . insert ( expandoName , expandedPath ) ;
}
//-----------------------------------------------------------------------------
StringTableEntry getPathExpando ( const char * pExpandoName )
{
// Sanity!
AssertFatal ( pExpandoName ! = NULL , " Expando name cannot be NULL. " ) ;
// Fetch expando name.
StringTableEntry expandoName = StringTable - > insert ( pExpandoName ) ;
// Find any existing path expando.
typePathExpandoMap : : iterator expandoItr = PathExpandos . find ( expandoName ) ;
// Does the expando exist?
if ( expandoItr ! = PathExpandos . end ( ) )
{
// Yes, so return it.
return expandoItr - > value ;
}
// Not found.
return NULL ;
}
//-----------------------------------------------------------------------------
void removePathExpando ( const char * pExpandoName )
{
// Sanity!
AssertFatal ( pExpandoName ! = NULL , " Expando name cannot be NULL. " ) ;
// Fetch expando name.
StringTableEntry expandoName = StringTable - > insert ( pExpandoName ) ;
// Find any existing path expando.
typePathExpandoMap : : iterator expandoItr = PathExpandos . find ( expandoName ) ;
// Does the expando exist?
if ( expandoItr = = PathExpandos . end ( ) )
{
// No, so warn.
# if defined(TORQUE_DEBUG)
Con : : warnf ( " Removing path expando of '%s' but it does not exist. " , expandoName ) ;
# endif
return ;
}
// Info.
# if defined(TORQUE_DEBUG)
Con : : printf ( " Removing path expando of '%s' as '%s'. " , expandoName , expandoItr - > value ) ;
# endif
// Remove expando.
PathExpandos . erase ( expandoItr ) ;
}
//-----------------------------------------------------------------------------
bool isPathExpando ( const char * pExpandoName )
{
// Sanity!
AssertFatal ( pExpandoName ! = NULL , " Expando name cannot be NULL. " ) ;
// Fetch expando name.
StringTableEntry expandoName = StringTable - > insert ( pExpandoName ) ;
return PathExpandos . contains ( expandoName ) ;
}
//-----------------------------------------------------------------------------
U32 getPathExpandoCount ( void )
{
return PathExpandos . size ( ) ;
}
//-----------------------------------------------------------------------------
StringTableEntry getPathExpandoKey ( U32 expandoIndex )
{
// Finish if index is out of range.
if ( expandoIndex > = PathExpandos . size ( ) )
return NULL ;
// Find indexed iterator.
typePathExpandoMap : : iterator expandoItr = PathExpandos . begin ( ) ;
while ( expandoIndex > 0 ) { + + expandoItr ; - - expandoIndex ; }
return expandoItr - > key ;
}
//-----------------------------------------------------------------------------
StringTableEntry getPathExpandoValue ( U32 expandoIndex )
{
// Finish if index is out of range.
if ( expandoIndex > = PathExpandos . size ( ) )
return NULL ;
// Find indexed iterator.
typePathExpandoMap : : iterator expandoItr = PathExpandos . begin ( ) ;
while ( expandoIndex > 0 ) { + + expandoItr ; - - expandoIndex ; }
return expandoItr - > value ;
}
//-----------------------------------------------------------------------------
bool expandPath ( char * pDstPath , U32 size , const char * pSrcPath , const char * pWorkingDirectoryHint , const bool ensureTrailingSlash )
{
2023-05-01 15:13:12 +00:00
char pathBuffer [ 2048 ] = { } ;
2015-10-13 20:19:36 +00:00
const char * pSrc = pSrcPath ;
char * pSlash ;
// Fetch leading character.
const char leadingToken = * pSrc ;
// Fetch following token.
const char followingToken = leadingToken ! = 0 ? pSrc [ 1 ] : 0 ;
// Expando.
if ( leadingToken = = ' ^ ' )
{
// Initial prefix search.
const char * pPrefixSrc = pSrc + 1 ;
char * pPrefixDst = pathBuffer ;
// Search for end of expando.
while ( * pPrefixSrc ! = ' / ' & & * pPrefixSrc ! = 0 )
{
// Copy prefix character.
* pPrefixDst + + = * pPrefixSrc + + ;
}
// Yes, so terminate the expando string.
* pPrefixDst = 0 ;
// Fetch the expando path.
StringTableEntry expandoPath = getPathExpando ( pathBuffer ) ;
// Does the expando exist?
if ( expandoPath = = NULL )
{
// No, so error.
Con : : errorf ( " expandPath() : Could not find path expando '%s' for path '%s'. " , pathBuffer , pSrcPath ) ;
// Are we ensuring the trailing slash?
if ( ensureTrailingSlash )
{
// Yes, so ensure it.
2018-03-06 06:59:05 +00:00
Con : : ensureTrailingSlash ( pDstPath , pSrcPath , size ) ;
2015-10-13 20:19:36 +00:00
}
else
{
// No, so just use the source path.
2018-03-06 06:59:05 +00:00
dStrcpy ( pDstPath , pSrcPath , size ) ;
2015-10-13 20:19:36 +00:00
}
return false ;
}
// Skip the expando and the following slash.
pSrc + = dStrlen ( pathBuffer ) + 1 ;
// Format the output path.
dSprintf ( pathBuffer , sizeof ( pathBuffer ) , " %s/%s " , expandoPath , pSrc ) ;
// Are we ensuring the trailing slash?
if ( ensureTrailingSlash )
{
// Yes, so ensure it.
2018-03-06 06:59:05 +00:00
Con : : ensureTrailingSlash ( pathBuffer , pathBuffer , size ) ;
2015-10-13 20:19:36 +00:00
}
// Strip repeat slashes.
Con : : stripRepeatSlashes ( pDstPath , pathBuffer , size ) ;
return true ;
}
// Script-Relative.
if ( leadingToken = = ' . ' )
{
// Fetch the code-block file-path.
2023-04-23 08:39:54 +00:00
const StringTableEntry codeblockFullPath = getCurrentScriptModulePath ( ) ;
2015-10-13 20:19:36 +00:00
// Do we have a code block full path?
if ( codeblockFullPath = = NULL )
{
// No, so error.
Con : : errorf ( " expandPath() : Could not find relative path from code-block for path '%s'. " , pSrcPath ) ;
// Are we ensuring the trailing slash?
if ( ensureTrailingSlash )
{
// Yes, so ensure it.
2018-03-06 06:59:05 +00:00
Con : : ensureTrailingSlash ( pDstPath , pSrcPath , size ) ;
2015-10-13 20:19:36 +00:00
}
else
{
// No, so just use the source path.
2018-03-06 06:59:05 +00:00
dStrcpy ( pDstPath , pSrcPath , size ) ;
2015-10-13 20:19:36 +00:00
}
return false ;
}
// Yes, so use it as the prefix.
dStrncpy ( pathBuffer , codeblockFullPath , sizeof ( pathBuffer ) - 1 ) ;
// Find the final slash in the code-block.
pSlash = dStrrchr ( pathBuffer , ' / ' ) ;
// Is this a parent directory token?
if ( followingToken = = ' . ' )
{
// Yes, so terminate after the slash so we include it.
pSlash [ 1 ] = 0 ;
}
else
{
// No, it's a current directory token so terminate at the slash so we don't include it.
pSlash [ 0 ] = 0 ;
// Skip the current directory token.
pSrc + + ;
}
// Format the output path.
2018-03-07 05:44:28 +00:00
dStrncat ( pathBuffer , " / " , sizeof ( pathBuffer ) - 1 - strlen ( pathBuffer ) ) ;
dStrncat ( pathBuffer , pSrc , sizeof ( pathBuffer ) - 1 - strlen ( pathBuffer ) ) ;
2015-10-13 20:19:36 +00:00
// Are we ensuring the trailing slash?
if ( ensureTrailingSlash )
{
// Yes, so ensure it.
2018-03-06 06:59:05 +00:00
Con : : ensureTrailingSlash ( pathBuffer , pathBuffer , size ) ;
2015-10-13 20:19:36 +00:00
}
// Strip repeat slashes.
Con : : stripRepeatSlashes ( pDstPath , pathBuffer , size ) ;
return true ;
}
// All else.
//Using a special case here because the code below barfs on trying to build a full path for apk reading
# ifdef TORQUE_OS_ANDROID
if ( leadingToken = = ' / ' | | strstr ( pSrcPath , " / " ) = = NULL )
Platform : : makeFullPathName ( pSrcPath , pathBuffer , sizeof ( pathBuffer ) , pWorkingDirectoryHint ) ;
else
dSprintf ( pathBuffer , sizeof ( pathBuffer ) , " /%s " , pSrcPath ) ;
# else
Platform : : makeFullPathName ( pSrcPath , pathBuffer , sizeof ( pathBuffer ) , pWorkingDirectoryHint ) ;
# endif
// Are we ensuring the trailing slash?
if ( ensureTrailingSlash )
{
// Yes, so ensure it.
2018-03-06 06:59:05 +00:00
Con : : ensureTrailingSlash ( pathBuffer , pathBuffer , size ) ;
2015-10-13 20:19:36 +00:00
}
// Strip repeat slashes.
Con : : stripRepeatSlashes ( pDstPath , pathBuffer , size ) ;
return true ;
}
//-----------------------------------------------------------------------------
bool isBasePath ( const char * SrcPath , const char * pBasePath )
{
2021-01-11 09:54:41 +00:00
char expandBuffer [ 1024 ] , expandBaseBuffer [ 1024 ] ;
2015-10-13 20:19:36 +00:00
Con : : expandPath ( expandBuffer , sizeof ( expandBuffer ) , SrcPath ) ;
2021-01-11 09:54:41 +00:00
Con : : expandPath ( expandBaseBuffer , sizeof ( expandBaseBuffer ) , pBasePath ) ;
return dStrnicmp ( expandBaseBuffer , expandBuffer , dStrlen ( expandBaseBuffer ) ) = = 0 ;
2015-10-13 20:19:36 +00:00
}
//-----------------------------------------------------------------------------
void collapsePath ( char * pDstPath , U32 size , const char * pSrcPath , const char * pWorkingDirectoryHint )
{
// Check path against expandos. If there are multiple matches, choose the
// expando that produces the shortest relative path.
char pathBuffer [ 2048 ] ;
// Fetch expando count.
const U32 expandoCount = getPathExpandoCount ( ) ;
// Iterate expandos.
U32 expandoRelativePathLength = U32_MAX ;
for ( U32 expandoIndex = 0 ; expandoIndex < expandoCount ; + + expandoIndex )
{
// Fetch expando value (path).
StringTableEntry expandoValue = getPathExpandoValue ( expandoIndex ) ;
// Skip if not the base path.
if ( ! isBasePath ( pSrcPath , expandoValue ) )
continue ;
// Fetch path relative to expando path.
StringTableEntry relativePath = Platform : : makeRelativePathName ( pSrcPath , expandoValue ) ;
// If the relative path is simply a period
if ( relativePath [ 0 ] = = ' . ' )
relativePath + + ;
if ( dStrlen ( relativePath ) > expandoRelativePathLength )
{
// This expando covers less of the path than any previous one found.
// We will keep the previous one.
continue ;
}
// Keep track of the relative path length
expandoRelativePathLength = dStrlen ( relativePath ) ;
// Fetch expando key (name).
StringTableEntry expandoName = getPathExpandoKey ( expandoIndex ) ;
// Format against expando.
dSprintf ( pathBuffer , sizeof ( pathBuffer ) , " ^%s/%s " , expandoName , relativePath ) ;
}
// Check if we've found a suitable expando
if ( expandoRelativePathLength ! = U32_MAX )
{
// Strip repeat slashes.
Con : : stripRepeatSlashes ( pDstPath , pathBuffer , size ) ;
return ;
}
// Fetch the working directory.
StringTableEntry workingDirectory = pWorkingDirectoryHint ! = NULL ? pWorkingDirectoryHint : Platform : : getCurrentDirectory ( ) ;
// Fetch path relative to current directory.
StringTableEntry relativePath = Platform : : makeRelativePathName ( pSrcPath , workingDirectory ) ;
// If the relative path is simply a period
if ( relativePath [ 0 ] = = ' . ' & & relativePath [ 1 ] ! = ' . ' )
relativePath + + ;
// Format against expando.
dSprintf ( pathBuffer , sizeof ( pathBuffer ) , " %s/%s " , workingDirectory , relativePath ) ;
// Strip repeat slashes.
Con : : stripRepeatSlashes ( pDstPath , pathBuffer , size ) ;
}
2018-03-06 06:59:05 +00:00
void ensureTrailingSlash ( char * pDstPath , const char * pSrcPath , S32 dstSize )
2015-10-13 20:19:36 +00:00
{
// Copy to target.
2018-03-06 06:59:05 +00:00
dStrcpy ( pDstPath , pSrcPath , dstSize ) ;
2015-10-13 20:19:36 +00:00
// Find trailing character index.
S32 trailIndex = dStrlen ( pDstPath ) ;
// Ignore if empty string.
if ( trailIndex = = 0 )
return ;
// Finish if the trailing slash already exists.
if ( pDstPath [ trailIndex - 1 ] = = ' / ' )
return ;
// Add trailing slash.
pDstPath [ trailIndex + + ] = ' / ' ;
pDstPath [ trailIndex ] = 0 ;
}
2025-05-05 19:14:03 +00:00
//--------------------------------------
void shutdown ( )
{
AssertFatal ( active = = true , " Con::shutdown should only be called once. " ) ;
active = false ;
smConsoleInput . remove ( postConsoleInput ) ;
consoleLogFile . close ( ) ;
Namespace : : shutdown ( ) ;
AbstractClassRep : : shutdown ( ) ;
Compiler : : freeConsoleParserList ( ) ;
gGlobalVars . reset ( ) ;
PathExpandos . clear ( ) ;
}
2015-10-13 20:19:36 +00:00
//-----------------------------------------------------------------------------
2016-04-19 06:35:52 +00:00
StringTableEntry getDSOPath ( const char * scriptPath )
{
# ifndef TORQUE2D_TOOLS_FIXME
// [tom, 11/17/2006] Force old behavior for the player. May not want to do this.
const char * slash = dStrrchr ( scriptPath , ' / ' ) ;
if ( slash ! = NULL )
return StringTable - > insertn ( scriptPath , slash - scriptPath , true ) ;
slash = dStrrchr ( scriptPath , ' : ' ) ;
if ( slash ! = NULL )
return StringTable - > insertn ( scriptPath , ( slash - scriptPath ) + 1 , true ) ;
return " " ;
# else
char relPath [ 1024 ] , dsoPath [ 1024 ] ;
bool isPrefs = false ;
// [tom, 11/17/2006] Prefs are handled slightly differently to avoid dso name clashes
StringTableEntry prefsPath = Platform : : getPrefsPath ( ) ;
if ( dStrnicmp ( scriptPath , prefsPath , dStrlen ( prefsPath ) ) = = 0 )
{
relPath [ 0 ] = 0 ;
isPrefs = true ;
}
else
{
StringTableEntry strippedPath = Platform : : stripBasePath ( scriptPath ) ;
2018-03-06 06:59:05 +00:00
dStrcpy ( relPath , strippedPath , 1024 ) ;
2016-04-19 06:35:52 +00:00
char * slash = dStrrchr ( relPath , ' / ' ) ;
if ( slash )
* slash = 0 ;
}
const char * overridePath ;
if ( ! isPrefs )
overridePath = Con : : getVariable ( " $Scripts::OverrideDSOPath " ) ;
else
overridePath = prefsPath ;
if ( overridePath & & * overridePath )
Platform : : makeFullPathName ( relPath , dsoPath , sizeof ( dsoPath ) , overridePath ) ;
else
{
char t [ 1024 ] ;
dSprintf ( t , sizeof ( t ) , " compiledScripts/%s " , relPath ) ;
Platform : : makeFullPathName ( t , dsoPath , sizeof ( dsoPath ) , Platform : : getPrefsPath ( ) ) ;
}
return StringTable - > insert ( dsoPath ) ;
# endif
}
//-----------------------------------------------------------------------------
2015-10-13 20:19:36 +00:00
bool stripRepeatSlashes ( char * pDstPath , const char * pSrcPath , S32 dstSize )
{
// Note original destination.
char * pOriginalDst = pDstPath ;
// Reset last source character.
char lastSrcChar = 0 ;
// Search source...
while ( dstSize > 0 )
{
// Fetch characters.
const char srcChar = * pSrcPath + + ;
// Do we have a repeat slash?
if ( srcChar = = ' / ' & & lastSrcChar = = ' / ' )
{
// Yes, so skip it.
continue ;
}
// No, so copy character.
* pDstPath + + = srcChar ;
// Finish if end of source.
if ( srcChar = = 0 )
return true ;
// Reduce room left in destination.
dstSize - - ;
// Set last character.
lastSrcChar = srcChar ;
}
// Terminate the destination string as we ran out of room.
* pOriginalDst = 0 ;
// Fail!
return false ;
}
2012-09-19 15:15:01 +00:00
} // end of Console namespace
# endif
//=============================================================================
// API.
//=============================================================================
// MARK: ---- API ----
//-----------------------------------------------------------------------------
DefineEngineFunction ( log , void , ( const char * message ) , ,
" @brief Logs a message to the console. \n \n "
" @param message The message text. \n "
" @note By default, messages will appear white in the console. \n "
" @ingroup Logging " )
{
Con : : printf ( " %s " , message ) ;
}
//-----------------------------------------------------------------------------
DefineEngineFunction ( logError , void , ( const char * message ) , ,
" @brief Logs an error message to the console. \n \n "
" @param message The message text. \n "
" @note By default, errors will appear red in the console. \n "
" @ingroup Logging " )
{
Con : : errorf ( " %s " , message ) ;
}
//-----------------------------------------------------------------------------
DefineEngineFunction ( logWarning , void , ( const char * message ) , ,
" @brief Logs a warning message to the console. \n \n "
" @param message The message text. \n \n "
" @note By default, warnings will appear turquoise in the console. \n "
" @ingroup Logging " )
{
Con : : warnf ( " %s " , message ) ;
}
2012-09-23 08:59:48 +00:00
2015-02-07 22:41:54 +00:00
//------------------------------------------------------------------------------
2021-03-30 23:33:19 +00:00
ConsoleValueToStringArrayWrapper : : ConsoleValueToStringArrayWrapper ( int targc , ConsoleValue * targv )
2012-09-23 08:59:48 +00:00
{
argv = new const char * [ targc ] ;
argc = targc ;
2021-03-30 23:33:19 +00:00
for ( S32 i = 0 ; i < targc ; i + + )
2012-10-11 20:29:39 +00:00
{
2021-03-30 23:33:19 +00:00
argv [ i ] = dStrdup ( targv [ i ] . getString ( ) ) ;
2012-09-23 08:59:48 +00:00
}
}
2021-03-30 23:33:19 +00:00
ConsoleValueToStringArrayWrapper : : ~ ConsoleValueToStringArrayWrapper ( )
2012-09-23 08:59:48 +00:00
{
2021-03-30 23:33:19 +00:00
for ( S32 i = 0 ; i < argc ; i + + )
2012-10-11 20:29:39 +00:00
{
2012-09-23 08:59:48 +00:00
dFree ( argv [ i ] ) ;
}
delete [ ] argv ;
}
2021-03-30 23:33:19 +00:00
StringArrayToConsoleValueWrapper : : StringArrayToConsoleValueWrapper ( int targc , const char * * targv )
2012-09-23 08:59:48 +00:00
{
2021-08-20 02:05:43 +00:00
argv = new ConsoleValue [ targc ] ( ) ;
2012-09-23 08:59:48 +00:00
argc = targc ;
2021-03-30 23:33:19 +00:00
for ( int i = 0 ; i < targc ; i + + )
2012-09-23 08:59:48 +00:00
{
2021-04-02 04:57:49 +00:00
argv [ i ] . setString ( targv [ i ] ) ;
2012-09-23 08:59:48 +00:00
}
2012-10-11 20:29:39 +00:00
}
2021-03-30 23:33:19 +00:00
StringArrayToConsoleValueWrapper : : ~ StringArrayToConsoleValueWrapper ( )
2012-11-09 23:00:46 +00:00
{
2021-03-30 23:33:19 +00:00
delete [ ] argv ;
2012-10-11 20:29:39 +00:00
}
2012-09-23 08:59:48 +00:00
2015-02-07 22:41:54 +00:00
//------------------------------------------------------------------------------
2012-09-23 08:59:48 +00:00
2021-03-31 03:58:07 +00:00
ConsoleValue _BaseEngineConsoleCallbackHelper : : _exec ( )
2012-10-11 20:29:39 +00:00
{
2015-02-07 22:41:54 +00:00
if ( mThis )
2012-09-23 08:59:48 +00:00
{
2015-02-07 22:41:54 +00:00
// Cannot invoke callback until object has been registered
2021-03-31 03:58:07 +00:00
if ( mThis - > isProperlyAdded ( ) )
{
2021-04-17 03:21:39 +00:00
ConsoleValue returnValue = Con : : _internalExecute ( mThis , mArgc , mArgv , false ) ;
2021-03-31 03:58:07 +00:00
mArgc = mInitialArgc ; // reset
2023-05-01 15:13:12 +00:00
return returnValue ;
2015-02-07 22:41:54 +00:00
}
2021-03-31 03:58:07 +00:00
STR . clearFunctionOffset ( ) ;
mArgc = mInitialArgc ; // reset
2025-05-07 16:04:55 +00:00
return ( ConsoleValue ( ) ) ;
2012-09-23 08:59:48 +00:00
}
2025-05-07 16:04:55 +00:00
ConsoleValue returnValue = ( Con : : _internalExecute ( mArgc , mArgv ) ) ;
2015-02-07 22:41:54 +00:00
mArgc = mInitialArgc ; // reset args
2023-05-01 15:13:12 +00:00
return returnValue ;
2012-11-09 23:00:46 +00:00
}
2021-03-31 03:58:07 +00:00
ConsoleValue _BaseEngineConsoleCallbackHelper : : _execLater ( SimConsoleThreadExecEvent * evt )
2012-09-23 08:59:48 +00:00
{
2015-02-07 22:41:54 +00:00
mArgc = mInitialArgc ; // reset args
Sim : : postEvent ( ( SimObject * ) Sim : : getRootGroup ( ) , evt , Sim : : getCurrentTime ( ) ) ;
2025-05-07 16:04:55 +00:00
return ( evt - > getCB ( ) . waitForResult ( ) ) ;
2012-09-23 08:59:48 +00:00
}
2015-02-07 22:41:54 +00:00
//------------------------------------------------------------------------------
2012-09-23 08:59:48 +00:00
2015-02-07 22:41:54 +00:00
void ConsoleStackFrameSaver : : save ( )
2012-09-23 08:59:48 +00:00
{
2021-03-31 03:58:07 +00:00
gCallStack . pushFrame ( 0 ) ;
2015-02-07 22:41:54 +00:00
mSaved = true ;
2012-09-23 08:59:48 +00:00
}
2015-02-07 22:41:54 +00:00
void ConsoleStackFrameSaver : : restore ( )
2012-09-23 08:59:48 +00:00
{
2015-02-07 22:41:54 +00:00
if ( mSaved )
{
2021-03-31 03:58:07 +00:00
gCallStack . popFrame ( ) ;
2015-02-07 22:41:54 +00:00
}
2012-10-11 20:29:39 +00:00
}
2023-09-05 03:28:49 +00:00
//------------------------------------------------------------------------------