console.cpp
Engine/source/console/console.cpp
Namespaces:
namespace
This namespace contains the core of the console functionality.
Public Variables
bool
Indicates that warnings about undefined script variables should be displayed.
char
scratchBuffer [4096]
Public Functions
CON_DECLARE_PARSER(CMD )
DefineEngineFunction(log , void , (const char *message) , "@brief Logs <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> message <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param message The message <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n</a>" "@note By default, messages will appear white in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" " @ingroup Logging" )
DefineEngineFunction(logError , void , (const char *message) , "@brief Logs an error message <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param message The message <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n</a>" "@note By default, errors will appear red in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" " @ingroup Logging" )
DefineEngineFunction(logWarning , void , (const char *message) , "@brief Logs <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> warning message <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param message The message <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "@note By default, warnings will appear turquoise in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" " @ingroup Logging" )
const char *
prependDollar(const char * name)
const char *
prependPercent(const char * name)
Detailed Description
Public Variables
ConsoleValueStack CSTK
U32 gAnonFunctionID
StmtNode * gAnonFunctionList
ExprEvalState gEvalState
StmtNode * gStatementList
bool gWarnUndefinedScriptVariables
Indicates that warnings about undefined script variables should be displayed.
note:This is set and controlled by script.
char scratchBuffer [4096]
StringStack STR
Public Functions
CON_DECLARE_PARSER(CMD )
DefineEngineFunction(log , void , (const char *message) , "@brief Logs <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> message <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param message The message <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n</a>" "@note By default, messages will appear white in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" " @ingroup Logging" )
DefineEngineFunction(logError , void , (const char *message) , "@brief Logs an error message <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param message The message <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n</a>" "@note By default, errors will appear red in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" " @ingroup Logging" )
DefineEngineFunction(logWarning , void , (const char *message) , "@brief Logs <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> warning message <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n\n</a>" "@param message The message <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text.\n\n</a>" "@note By default, warnings will appear turquoise in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">console.\n</a>" " @ingroup Logging" )
prependDollar(const char * name)
prependPercent(const char * name)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "platform/platformTLS.h" 26#include "platform/threads/thread.h" 27#include "console/console.h" 28#include "console/consoleInternal.h" 29#include "console/consoleObject.h" 30#include "console/consoleParser.h" 31#include "core/stream/fileStream.h" 32#include "console/ast.h" 33#include "core/tAlgorithm.h" 34#include "console/consoleTypes.h" 35#include "console/telnetDebugger.h" 36#include "console/simBase.h" 37#include "console/compiler.h" 38#include "console/stringStack.h" 39#include "console/ICallMethod.h" 40#include "console/engineAPI.h" 41#include <stdarg.h> 42#include "platform/threads/mutex.h" 43#include "core/util/journal/journal.h" 44#include "cinterface/cinterface.h" 45 46extern StringStack STR; 47extern ConsoleValueStack CSTK; 48 49ConsoleDocFragment* ConsoleDocFragment::smFirst; 50ExprEvalState gEvalState; 51StmtNode *gStatementList; 52StmtNode *gAnonFunctionList; 53U32 gAnonFunctionID = 0; 54ConsoleConstructor *ConsoleConstructor::mFirst = NULL; 55bool gWarnUndefinedScriptVariables; 56 57static char scratchBuffer[4096]; 58 59CON_DECLARE_PARSER(CMD); 60 61static const char * prependDollar ( const char * name ) 62{ 63 if(name[0] != '$') 64 { 65 S32 len = dStrlen(name); 66 AssertFatal(len < sizeof(scratchBuffer)-2, "CONSOLE: name too long"); 67 scratchBuffer[0] = '$'; 68 dMemcpy(scratchBuffer + 1, name, len + 1); 69 name = scratchBuffer; 70 } 71 return name; 72} 73 74static const char * prependPercent ( const char * name ) 75{ 76 if(name[0] != '%') 77 { 78 S32 len = dStrlen(name); 79 AssertFatal(len < sizeof(scratchBuffer)-2, "CONSOLE: name too long"); 80 scratchBuffer[0] = '%'; 81 dMemcpy(scratchBuffer + 1, name, len + 1); 82 name = scratchBuffer; 83 } 84 return name; 85} 86 87//-------------------------------------- 88void ConsoleConstructor::init( const char *cName, const char *fName, const char *usg, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 89{ 90 mMina = minArgs; 91 mMaxa = maxArgs; 92 mFuncName = fName; 93 mUsage = usg; 94 mClassName = cName; 95 mSC = 0; mFC = 0; mVC = 0; mBC = 0; mIC = 0; 96 mCallback = mGroup = false; 97 mNext = mFirst; 98 mNS = false; 99 mFirst = this; 100 mToolOnly = isToolOnly; 101 mHeader = header; 102} 103 104void ConsoleConstructor::setup() 105{ 106 for(ConsoleConstructor *walk = mFirst; walk; walk = walk->mNext) 107 { 108#ifdef TORQUE_DEBUG 109 walk->validate(); 110#endif 111 112 if( walk->mSC ) 113 Con::addCommand( walk->mClassName, walk->mFuncName, walk->mSC, walk->mUsage, walk->mMina, walk->mMaxa, walk->mToolOnly, walk->mHeader); 114 else if( walk->mIC ) 115 Con::addCommand( walk->mClassName, walk->mFuncName, walk->mIC, walk->mUsage, walk->mMina, walk->mMaxa, walk->mToolOnly, walk->mHeader); 116 else if( walk->mFC ) 117 Con::addCommand( walk->mClassName, walk->mFuncName, walk->mFC, walk->mUsage, walk->mMina, walk->mMaxa, walk->mToolOnly, walk->mHeader); 118 else if( walk->mVC ) 119 Con::addCommand( walk->mClassName, walk->mFuncName, walk->mVC, walk->mUsage, walk->mMina, walk->mMaxa, walk->mToolOnly, walk->mHeader); 120 else if( walk->mBC ) 121 Con::addCommand( walk->mClassName, walk->mFuncName, walk->mBC, walk->mUsage, walk->mMina, walk->mMaxa, walk->mToolOnly, walk->mHeader); 122 else if( walk->mGroup ) 123 Con::markCommandGroup( walk->mClassName, walk->mFuncName, walk->mUsage); 124 else if( walk->mClassName) 125 Con::noteScriptCallback( walk->mClassName, walk->mFuncName, walk->mUsage, walk->mHeader); 126 else if( walk->mNS ) 127 { 128 Namespace* ns = Namespace::find( StringTable->insert( walk->mClassName) ); 129 if( ns ) 130 ns->mUsage = walk->mUsage; 131 } 132 else 133 { 134 AssertISV( false, "Found a ConsoleConstructor with an indeterminate type!" ); 135 } 136 } 137} 138 139ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, StringCallback sfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 140{ 141 init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); 142 mSC = sfunc; 143} 144 145ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, IntCallback ifunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 146{ 147 init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); 148 mIC = ifunc; 149} 150 151ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, FloatCallback ffunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 152{ 153 init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); 154 mFC = ffunc; 155} 156 157ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, VoidCallback vfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 158{ 159 init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); 160 mVC = vfunc; 161} 162 163ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, BoolCallback bfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 164{ 165 init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); 166 mBC = bfunc; 167} 168 169ConsoleConstructor::ConsoleConstructor(const char* className, const char* groupName, const char* aUsage) 170{ 171 init(className, groupName, mUsage, -1, -2); 172 173 mGroup = true; 174 175 // Somewhere, the entry list is getting flipped, partially. 176 // so we have to do tricks to deal with making sure usage 177 // is properly populated. 178 179 // This is probably redundant. 180 static char * lastUsage = NULL; 181 if(aUsage) 182 lastUsage = (char *)aUsage; 183 184 mUsage = lastUsage; 185} 186 187ConsoleConstructor::ConsoleConstructor(const char *className, const char *callbackName, const char *usage, ConsoleFunctionHeader* header ) 188{ 189 init( className, callbackName, usage, -2, -3, false, header ); 190 mCallback = true; 191 mNS = true; 192} 193 194void ConsoleConstructor::validate() 195{ 196#ifdef TORQUE_DEBUG 197 // Don't do the following check if we're not a method/func. 198 if(mGroup) 199 return; 200 201 // In debug, walk the list and make sure this isn't a duplicate. 202 for(ConsoleConstructor *walk = mFirst; walk; walk = walk->mNext) 203 { 204 // Skip mismatching func/method names. 205 if(dStricmp(walk->mFuncName, mFuncName)) 206 continue; 207 208 // Don't compare functions with methods or vice versa. 209 if(bool(mClassName) != bool(walk->mClassName)) 210 continue; 211 212 // Skip mismatching classnames, if they're present. 213 if(mClassName && walk->mClassName && dStricmp(walk->mClassName, mClassName)) 214 continue; 215 216 // If we encounter ourselves, stop searching; this prevents duplicate 217 // firing of the assert, instead only firing for the latter encountered 218 // entry. 219 if(this == walk) 220 break; 221 222 // Match! 223 if(mClassName) 224 { 225 AssertISV(false, avar("ConsoleConstructor::setup - ConsoleMethod '%s::%s' collides with another of the same name.", mClassName, mFuncName)); 226 } 227 else 228 { 229 AssertISV(false, avar("ConsoleConstructor::setup - ConsoleFunction '%s' collides with another of the same name.", mFuncName)); 230 } 231 } 232#endif 233} 234 235// We comment out the implementation of the Con namespace when doxygenizing because 236// otherwise Doxygen decides to ignore our docs in console.h 237#ifndef DOXYGENIZING 238 239namespace Con 240{ 241 242static Vector<ConsumerCallback> gConsumers(__FILE__, __LINE__); 243static Vector< String> sInstantGroupStack( __FILE__, __LINE__ ); 244static DataChunker consoleLogChunker; 245static Vector<ConsoleLogEntry> consoleLog(__FILE__, __LINE__); 246static bool consoleLogLocked; 247static bool logBufferEnabled=true; 248static S32 printLevel = 10; 249static FileStream consoleLogFile; 250static const char *defLogFileName = "console.log"; 251static S32 consoleLogMode = 0; 252static bool active = false; 253static bool newLogFile; 254static const char *logFileName; 255 256static const S32 MaxCompletionBufferSize = 4096; 257static char completionBuffer[MaxCompletionBufferSize]; 258static char tabBuffer[MaxCompletionBufferSize] = {0}; 259static SimObjectPtr<SimObject> tabObject; 260static U32 completionBaseStart; 261static U32 completionBaseLen; 262 263String gInstantGroup; 264Con::ConsoleInputEvent smConsoleInput; 265 266/// Current script file name and root, these are registered as 267/// console variables. 268/// @{ 269 270/// 271StringTableEntry gCurrentFile; 272StringTableEntry gCurrentRoot; 273/// @} 274 275S32 gObjectCopyFailures = -1; 276 277bool alwaysUseDebugOutput = true; 278bool useTimestamp = false; 279bool useRealTimestamp = false; 280 281ConsoleFunctionGroupBegin( Clipboard, "Miscellaneous functions to control the clipboard and clear the console."); 282 283DefineEngineFunction( cls, void, (), , "()" 284 "@brief Clears the console output.\n\n" 285 "@ingroup Console") 286{ 287 if(consoleLogLocked) 288 return; 289 consoleLogChunker.freeBlocks(); 290 consoleLog.setSize(0); 291}; 292 293DefineEngineFunction( getClipboard, const char*, (), , "()" 294 "@brief Get text from the clipboard.\n\n" 295 "@internal") 296{ 297 return Platform::getClipboard(); 298}; 299 300DefineEngineFunction( setClipboard, bool, (const char* text), , "(string text)" 301 "@brief Set the system clipboard.\n\n" 302 "@internal") 303{ 304 return Platform::setClipboard(text); 305}; 306 307ConsoleFunctionGroupEnd( Clipboard ); 308 309 310void postConsoleInput( RawData data ); 311 312void init() 313{ 314 AssertFatal(active == false, "Con::init should only be called once."); 315 316 // Set up general init values. 317 active = true; 318 logFileName = NULL; 319 newLogFile = true; 320 gWarnUndefinedScriptVariables = false; 321 322 // Initialize subsystems. 323 Namespace::init(); 324 ConsoleConstructor::setup(); 325 326 // Set up the parser(s) 327 CON_ADD_PARSER(CMD, TORQUE_SCRIPT_EXTENSION, true); // TorqueScript 328 329 // Setup the console types. 330 ConsoleBaseType::initialize(); 331 332 // And finally, the ACR... 333 AbstractClassRep::initialize(); 334 335 // Variables 336 setVariable("Con::prompt", "% "); 337 addVariable("Con::logBufferEnabled", TypeBool, &logBufferEnabled, "If true, the log buffer will be enabled.\n" 338 "@ingroup Console\n"); 339 addVariable("Con::printLevel", TypeS32, &printLevel, 340 "@brief This is deprecated.\n\n" 341 "It is no longer in use and does nothing.\n" 342 "@ingroup Console\n"); 343 addVariable("Con::warnUndefinedVariables", TypeBool, &gWarnUndefinedScriptVariables, "If true, a warning will be displayed in the console whenever a undefined variable is used in script.\n" 344 "@ingroup Console\n"); 345 addVariable( "instantGroup", TypeRealString, &gInstantGroup, "The group that objects will be added to when they are created.\n" 346 "@ingroup Console\n"); 347 348 addVariable("Con::objectCopyFailures", TypeS32, &gObjectCopyFailures, "If greater than zero then it counts the number of object creation " 349 "failures based on a missing copy object and does not report an error..\n" 350 "@ingroup Console\n"); 351 352 // Current script file name and root 353 addVariable( "Con::File", TypeString, &gCurrentFile, "The currently executing script file.\n" 354 "@ingroup FileSystem\n"); 355 addVariable( "Con::Root", TypeString, &gCurrentRoot, "The mod folder for the currently executing script file.\n" 356 "@ingroup FileSystem\n" ); 357 358 // alwaysUseDebugOutput determines whether to send output to the platform's 359 // "debug" system. see winConsole for an example. 360 // in ship builds we don't expose this variable to script 361 // and we set it to false by default (don't want to provide more information 362 // to potential hackers). platform code should also ifdef out the code that 363 // pays attention to this in ship builds (see winConsole.cpp) 364 // note that enabling this can slow down your game 365 // if you are running from the debugger and printing a lot of console messages. 366#ifndef TORQUE_SHIPPING 367 addVariable("Con::alwaysUseDebugOutput", TypeBool, &alwaysUseDebugOutput, 368 "@brief Determines whether to send output to the platform's \"debug\" system.\n\n" 369 "@note This is disabled in shipping builds.\n" 370 "@ingroup Console"); 371#else 372 alwaysUseDebugOutput = false; 373#endif 374 375 // controls whether a timestamp is prepended to every console message 376 addVariable("Con::useTimestamp", TypeBool, &useTimestamp, "If true a timestamp is prepended to every console message.\n" 377 "@ingroup Console\n"); 378 379 // controls whether a real date and time is prepended to every console message 380 addVariable("Con::useRealTimestamp", TypeBool, &useRealTimestamp, "If true a date and time will be prepended to every console message.\n" 381 "@ingroup Console\n"); 382 383 // Plug us into the journaled console input signal. 384 smConsoleInput.notify(postConsoleInput); 385} 386 387//-------------------------------------- 388 389void shutdown() 390{ 391 AssertFatal(active == true, "Con::shutdown should only be called once."); 392 active = false; 393 394 smConsoleInput.remove(postConsoleInput); 395 396 consoleLogFile.close(); 397 Namespace::shutdown(); 398 AbstractClassRep::shutdown(); 399 Compiler::freeConsoleParserList(); 400} 401 402bool isActive() 403{ 404 return active; 405} 406 407bool isMainThread() 408{ 409#ifdef TORQUE_MULTITHREAD 410 return ThreadManager::isMainThread(); 411#else 412 // If we're single threaded we're always in the main thread. 413 return true; 414#endif 415} 416 417//-------------------------------------- 418 419void getLockLog(ConsoleLogEntry *&log, U32 &size) 420{ 421 consoleLogLocked = true; 422 log = consoleLog.address(); 423 size = consoleLog.size(); 424} 425 426void unlockLog() 427{ 428 consoleLogLocked = false; 429} 430 431U32 tabComplete(char* inputBuffer, U32 cursorPos, U32 maxResultLength, bool forwardTab) 432{ 433 // Check for null input. 434 if (!inputBuffer[0]) 435 { 436 return cursorPos; 437 } 438 439 // Cap the max result length. 440 if (maxResultLength > MaxCompletionBufferSize) 441 { 442 maxResultLength = MaxCompletionBufferSize; 443 } 444 445 // See if this is the same partial text as last checked. 446 if (String::compare(tabBuffer, inputBuffer)) 447 { 448 // If not... 449 // Save it for checking next time. 450 dStrcpy(tabBuffer, inputBuffer, MaxCompletionBufferSize); 451 // Scan backward from the cursor position to find the base to complete from. 452 S32 p = cursorPos; 453 while ((p > 0) && (inputBuffer[p - 1] != ' ') && (inputBuffer[p - 1] != '.') && (inputBuffer[p - 1] != '(')) 454 { 455 p--; 456 } 457 completionBaseStart = p; 458 completionBaseLen = cursorPos - p; 459 // Is this function being invoked on an object? 460 if (inputBuffer[p - 1] == '.') 461 { 462 // If so... 463 if (p <= 1) 464 { 465 // Bail if no object identifier. 466 return cursorPos; 467 } 468 469 // Find the object identifier. 470 S32 objLast = --p; 471 while ((p > 0) && (inputBuffer[p - 1] != ' ') && (inputBuffer[p - 1] != '(')) 472 { 473 p--; 474 } 475 476 if (objLast == p) 477 { 478 // Bail if no object identifier. 479 return cursorPos; 480 } 481 482 // Look up the object identifier. 483 dStrncpy(completionBuffer, inputBuffer + p, objLast - p); 484 completionBuffer[objLast - p] = 0; 485 tabObject = Sim::findObject(completionBuffer); 486 if (tabObject == NULL) 487 { 488 // Bail if not found. 489 return cursorPos; 490 } 491 } 492 else 493 { 494 // Not invoked on an object; we'll use the global namespace. 495 tabObject = 0; 496 } 497 } 498 499 // Chop off the input text at the cursor position. 500 inputBuffer[cursorPos] = 0; 501 502 // Try to find a completion in the appropriate namespace. 503 const char *newText; 504 505 if (tabObject != 0) 506 { 507 newText = tabObject->tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab); 508 } 509 else 510 { 511 // In the global namespace, we can complete on global vars as well as functions. 512 if (inputBuffer[completionBaseStart] == '$') 513 { 514 newText = gEvalState.globalVars.tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab); 515 } 516 else 517 { 518 newText = Namespace::global()->tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab); 519 } 520 } 521 522 if (newText) 523 { 524 // If we got something, append it to the input text. 525 S32 len = dStrlen(newText); 526 if (len + completionBaseStart > maxResultLength) 527 { 528 len = maxResultLength - completionBaseStart; 529 } 530 dStrncpy(inputBuffer + completionBaseStart, newText, len); 531 inputBuffer[completionBaseStart + len] = 0; 532 // And set the cursor after it. 533 cursorPos = completionBaseStart + len; 534 } 535 536 // Save the modified input buffer for checking next time. 537 dStrcpy(tabBuffer, inputBuffer, MaxCompletionBufferSize); 538 539 // Return the new (maybe) cursor position. 540 return cursorPos; 541} 542 543//------------------------------------------------------------------------------ 544static void log(const char *string) 545{ 546 // Bail if we ain't logging. 547 if (!consoleLogMode) 548 { 549 return; 550 } 551 552 // In mode 1, we open, append, close on each log write. 553 if ((consoleLogMode & 0x3) == 1) 554 { 555 consoleLogFile.open(defLogFileName, Torque::FS::File::ReadWrite); 556 } 557 558 // Write to the log if its status is hunky-dory. 559 if ((consoleLogFile.getStatus() == Stream::Ok) || (consoleLogFile.getStatus() == Stream::EOS)) 560 { 561 consoleLogFile.setPosition(consoleLogFile.getStreamSize()); 562 // If this is the first write... 563 if (newLogFile) 564 { 565 // Make a header. 566 Platform::LocalTime lt; 567 Platform::getLocalTime(lt); 568 char buffer[128]; 569 dSprintf(buffer, sizeof(buffer), "//-------------------------- %d/%d/%d -- %02d:%02d:%02d -----\r\n", 570 lt.month + 1, 571 lt.monthday, 572 lt.year + 1900, 573 lt.hour, 574 lt.min, 575 lt.sec); 576 consoleLogFile.write(dStrlen(buffer), buffer); 577 newLogFile = false; 578 if (consoleLogMode & 0x4) 579 { 580 // Dump anything that has been printed to the console so far. 581 consoleLogMode -= 0x4; 582 U32 size, line; 583 ConsoleLogEntry *log; 584 getLockLog(log, size); 585 for (line = 0; line < size; line++) 586 { 587 consoleLogFile.write(dStrlen(log[line].mString), log[line].mString); 588 consoleLogFile.write(2, "\r\n"); 589 } 590 unlockLog(); 591 } 592 } 593 // Now write what we came here to write. 594 consoleLogFile.write(dStrlen(string), string); 595 consoleLogFile.write(2, "\r\n"); 596 } 597 598 if ((consoleLogMode & 0x3) == 1) 599 { 600 consoleLogFile.close(); 601 } 602} 603 604//------------------------------------------------------------------------------ 605 606static void _printf(ConsoleLogEntry::Level level, ConsoleLogEntry::Type type, const char* fmt, va_list argptr) 607{ 608 if (!active) 609 return; 610 Con::active = false; 611 612 char buffer[8192]; 613 U32 offset = 0; 614 if( gEvalState.traceOn && gEvalState.getStackDepth() > 0 ) 615 { 616 offset = gEvalState.getStackDepth() * 3; 617 for(U32 i = 0; i < offset; i++) 618 buffer[i] = ' '; 619 } 620 621 if (useRealTimestamp) 622 { 623 Platform::LocalTime lt; 624 Platform::getLocalTime(lt); 625 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); 626 } 627 628 if (useTimestamp) 629 { 630 static U32 startTime = Platform::getRealMilliseconds(); 631 U32 curTime = Platform::getRealMilliseconds() - startTime; 632 offset += dSprintf(buffer + offset, sizeof(buffer) - offset, "[+%4d.%03d]", U32(curTime * 0.001), curTime % 1000); 633 } 634 635 if (useTimestamp || useRealTimestamp) { 636 offset += dSprintf(buffer + offset, sizeof(buffer) - offset, " "); 637 } 638 639 640 dVsprintf(buffer + offset, sizeof(buffer) - offset, fmt, argptr); 641 642 for(S32 i = 0; i < gConsumers.size(); i++) 643 gConsumers[i](level, buffer); 644 645 if(logBufferEnabled || consoleLogMode) 646 { 647 char *pos = buffer; 648 while(*pos) 649 { 650 if(*pos == '\t') 651 *pos = '^'; 652 pos++; 653 } 654 pos = buffer; 655 656 for(;;) 657 { 658 char *eofPos = dStrchr(pos, '\n'); 659 if(eofPos) 660 *eofPos = 0; 661 662 log(pos); 663 if(logBufferEnabled && !consoleLogLocked) 664 { 665 ConsoleLogEntry entry; 666 entry.mLevel = level; 667 entry.mType = type; 668#ifndef TORQUE_SHIPPING // this is equivalent to a memory leak, turn it off in ship build 669 dsize_t logStringLen = dStrlen(pos) + 1; 670 entry.mString = (const char *)consoleLogChunker.alloc(logStringLen); 671 dStrcpy(const_cast<char*>(entry.mString), pos, logStringLen); 672 673 // This prevents infinite recursion if the console itself needs to 674 // re-allocate memory to accommodate the new console log entry, and 675 // LOG_PAGE_ALLOCS is defined. It is kind of a dirty hack, but the 676 // uses for LOG_PAGE_ALLOCS are limited, and it is not worth writing 677 // a lot of special case code to support this situation. -patw 678 const bool save = Con::active; 679 Con::active = false; 680 consoleLog.push_back(entry); 681 Con::active = save; 682#endif 683 } 684 if(!eofPos) 685 break; 686 pos = eofPos + 1; 687 } 688 } 689 690 Con::active = true; 691} 692 693//------------------------------------------------------------------------------ 694void printf(const char* fmt,...) 695{ 696 va_list argptr; 697 va_start(argptr, fmt); 698 _printf(ConsoleLogEntry::Normal, ConsoleLogEntry::General, fmt, argptr); 699 va_end(argptr); 700} 701 702void warnf(ConsoleLogEntry::Type type, const char* fmt,...) 703{ 704 va_list argptr; 705 va_start(argptr, fmt); 706 _printf(ConsoleLogEntry::Warning, type, fmt, argptr); 707 va_end(argptr); 708} 709 710void errorf(ConsoleLogEntry::Type type, const char* fmt,...) 711{ 712 va_list argptr; 713 va_start(argptr, fmt); 714 _printf(ConsoleLogEntry::Error, type, fmt, argptr); 715 va_end(argptr); 716} 717 718void warnf(const char* fmt,...) 719{ 720 va_list argptr; 721 va_start(argptr, fmt); 722 _printf(ConsoleLogEntry::Warning, ConsoleLogEntry::General, fmt, argptr); 723 va_end(argptr); 724} 725 726void errorf(const char* fmt,...) 727{ 728 va_list argptr; 729 va_start(argptr, fmt); 730 _printf(ConsoleLogEntry::Error, ConsoleLogEntry::General, fmt, argptr); 731 va_end(argptr); 732} 733 734//--------------------------------------------------------------------------- 735 736bool getVariableObjectField(const char *name, SimObject **object, const char **field) 737{ 738 // get the field info from the object.. 739 const char *dot = dStrchr(name, '.'); 740 if(name[0] != '$' && dot) 741 { 742 S32 len = dStrlen(name); 743 AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - name too long"); 744 dMemcpy(scratchBuffer, name, len+1); 745 746 char * token = dStrtok(scratchBuffer, "."); 747 SimObject * obj = Sim::findObject(token); 748 if(!obj) 749 return false; 750 751 token = dStrtok(0, ".\0"); 752 if(!token) 753 return false; 754 755 while(token != NULL) 756 { 757 const char * val = obj->getDataField(StringTable->insert(token), 0); 758 if(!val) 759 return false; 760 761 char *fieldToken = token; 762 token = dStrtok(0, ".\0"); 763 if(token) 764 { 765 obj = Sim::findObject(token); 766 if(!obj) 767 return false; 768 } 769 else 770 { 771 *object = obj; 772 *field = fieldToken; 773 return true; 774 } 775 } 776 } 777 778 return false; 779} 780 781Dictionary::Entry *getLocalVariableEntry(const char *name) 782{ 783 name = prependPercent(name); 784 return gEvalState.getCurrentFrame().lookup(StringTable->insert(name)); 785} 786 787Dictionary::Entry *getVariableEntry(const char *name) 788{ 789 name = prependDollar(name); 790 return gEvalState.globalVars.lookup(StringTable->insert(name)); 791} 792 793Dictionary::Entry *addVariableEntry(const char *name) 794{ 795 name = prependDollar(name); 796 return gEvalState.globalVars.add(StringTable->insert(name)); 797} 798 799Dictionary::Entry *getAddVariableEntry(const char *name) 800{ 801 name = prependDollar(name); 802 StringTableEntry stName = StringTable->insert(name); 803 Dictionary::Entry *entry = gEvalState.globalVars.lookup(stName); 804 if (!entry) 805 entry = gEvalState.globalVars.add(stName); 806 return entry; 807} 808 809Dictionary::Entry *getAddLocalVariableEntry(const char *name) 810{ 811 name = prependPercent(name); 812 StringTableEntry stName = StringTable->insert(name); 813 Dictionary::Entry *entry = gEvalState.getCurrentFrame().lookup(stName); 814 if (!entry) 815 entry = gEvalState.getCurrentFrame().add(stName); 816 return entry; 817} 818 819void setVariable(const char *name, const char *value) 820{ 821 SimObject *obj = NULL; 822 const char *objField = NULL; 823 824 if (getVariableObjectField(name, &obj, &objField)) 825 { 826 obj->setDataField(StringTable->insert(objField), 0, value); 827 } 828 else 829 { 830 name = prependDollar(name); 831 gEvalState.globalVars.setVariable(StringTable->insert(name), value); 832 } 833} 834 835void setLocalVariable(const char *name, const char *value) 836{ 837 name = prependPercent(name); 838 gEvalState.getCurrentFrame().setVariable(StringTable->insert(name), value); 839} 840 841void setBoolVariable(const char *varName, bool value) 842{ 843 SimObject *obj = NULL; 844 const char *objField = NULL; 845 846 if (getVariableObjectField(varName, &obj, &objField)) 847 { 848 obj->setDataField(StringTable->insert(objField), 0, value ? "1" : "0"); 849 } 850 else 851 { 852 varName = prependDollar(varName); 853 Dictionary::Entry *entry = getAddVariableEntry(varName); 854 entry->setStringValue(value ? "1" : "0"); 855 } 856} 857 858void setIntVariable(const char *varName, S32 value) 859{ 860 SimObject *obj = NULL; 861 const char *objField = NULL; 862 863 if (getVariableObjectField(varName, &obj, &objField)) 864 { 865 char varBuffer[32]; 866 dSprintf(varBuffer, sizeof(varBuffer), "%d", value); 867 obj->setDataField(StringTable->insert(objField), 0, varBuffer); 868 } 869 else 870 { 871 varName = prependDollar(varName); 872 Dictionary::Entry *entry = getAddVariableEntry(varName); 873 entry->setIntValue(value); 874 } 875} 876 877void setFloatVariable(const char *varName, F32 value) 878{ 879 SimObject *obj = NULL; 880 const char *objField = NULL; 881 882 if (getVariableObjectField(varName, &obj, &objField)) 883 { 884 char varBuffer[32]; 885 dSprintf(varBuffer, sizeof(varBuffer), "%g", value); 886 obj->setDataField(StringTable->insert(objField), 0, varBuffer); 887 } 888 else 889 { 890 varName = prependDollar(varName); 891 Dictionary::Entry *entry = getAddVariableEntry(varName); 892 entry->setFloatValue(value); 893 } 894} 895 896//--------------------------------------------------------------------------- 897void addConsumer(ConsumerCallback consumer) 898{ 899 gConsumers.push_back(consumer); 900} 901 902// dhc - found this empty -- trying what I think is a reasonable impl. 903void removeConsumer(ConsumerCallback consumer) 904{ 905 for(S32 i = 0; i < gConsumers.size(); i++) 906 { 907 if (gConsumers[i] == consumer) 908 { 909 // remove it from the list. 910 gConsumers.erase(i); 911 break; 912 } 913 } 914} 915 916void stripColorChars(char* line) 917{ 918 char* c = line; 919 char cp = *c; 920 while (cp) 921 { 922 if (cp < 18) 923 { 924 // Could be a color control character; let's take a closer look. 925 if ((cp != 8) && (cp != 9) && (cp != 10) && (cp != 13)) 926 { 927 // Yep... copy following chars forward over this. 928 char* cprime = c; 929 char cpp; 930 do 931 { 932 cpp = *++cprime; 933 *(cprime - 1) = cpp; 934 } 935 while (cpp); 936 // Back up 1 so we'll check this position again post-copy. 937 c--; 938 } 939 } 940 cp = *++c; 941 } 942} 943 944// 945const char *getObjectTokenField(const char *name) 946{ 947 const char *dot = dStrchr(name, '.'); 948 if(name[0] != '$' && dot) 949 { 950 S32 len = dStrlen(name); 951 AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - object name too long"); 952 dMemcpy(scratchBuffer, name, len+1); 953 954 char * token = dStrtok(scratchBuffer, "."); 955 SimObject * obj = Sim::findObject(token); 956 if(!obj) 957 return(""); 958 959 token = dStrtok(0, ".\0"); 960 if(!token) 961 return(""); 962 963 while(token != NULL) 964 { 965 const char * val = obj->getDataField(StringTable->insert(token), 0); 966 if(!val) 967 return(""); 968 969 token = dStrtok(0, ".\0"); 970 if(token) 971 { 972 obj = Sim::findObject(token); 973 if(!obj) 974 return(""); 975 } 976 else 977 return(val); 978 } 979 } 980 981 return NULL; 982} 983 984const char *getVariable(const char *name, const char* def) 985{ 986 const char *objField = getObjectTokenField(name); 987 if (objField) 988 { 989 return objField; 990 } 991 else 992 { 993 Dictionary::Entry *entry = getVariableEntry(name); 994 return entry ? entry->getStringValue() : def; 995 } 996} 997 998const char *getLocalVariable(const char *name) 999{ 1000 name = prependPercent(name); 1001 1002 return gEvalState.getCurrentFrame().getVariable(StringTable->insert(name)); 1003} 1004 1005bool getBoolVariable(const char *varName, bool def) 1006{ 1007 const char *objField = getObjectTokenField(varName); 1008 if (objField) 1009 { 1010 return *objField ? dAtob(objField) : def; 1011 } 1012 else 1013 { 1014 Dictionary::Entry *entry = getVariableEntry(varName); 1015 objField = entry ? entry->getStringValue() : ""; 1016 return *objField ? dAtob(objField) : def; 1017 } 1018} 1019 1020S32 getIntVariable(const char *varName, S32 def) 1021{ 1022 const char *objField = getObjectTokenField(varName); 1023 if (objField) 1024 { 1025 return *objField ? dAtoi(objField) : def; 1026 } 1027 else 1028 { 1029 Dictionary::Entry *entry = getVariableEntry(varName); 1030 return entry ? entry->getIntValue() : def; 1031 } 1032} 1033 1034F32 getFloatVariable(const char *varName, F32 def) 1035{ 1036 const char *objField = getObjectTokenField(varName); 1037 if (objField) 1038 { 1039 return *objField ? dAtof(objField) : def; 1040 } 1041 else 1042 { 1043 Dictionary::Entry *entry = getVariableEntry(varName); 1044 return entry ? entry->getFloatValue() : def; 1045 } 1046} 1047 1048//--------------------------------------------------------------------------- 1049 1050void addVariable( const char *name, 1051 S32 type, 1052 void *dptr, 1053 const char* usage ) 1054{ 1055 gEvalState.globalVars.addVariable( name, type, dptr, usage ); 1056} 1057 1058void addConstant( const char *name, 1059 S32 type, 1060 const void *dptr, 1061 const char* usage ) 1062{ 1063 Dictionary::Entry* entry = gEvalState.globalVars.addVariable( name, type, const_cast< void* >( dptr ), usage ); 1064 entry->mIsConstant = true; 1065} 1066 1067bool removeVariable(const char *name) 1068{ 1069 name = StringTable->lookup(prependDollar(name)); 1070 return name!=0 && gEvalState.globalVars.removeVariable(name); 1071} 1072 1073void addVariableNotify( const char *name, const NotifyDelegate &callback ) 1074{ 1075 gEvalState.globalVars.addVariableNotify( name, callback ); 1076} 1077 1078void removeVariableNotify( const char *name, const NotifyDelegate &callback ) 1079{ 1080 gEvalState.globalVars.removeVariableNotify( name, callback ); 1081} 1082 1083//--------------------------------------------------------------------------- 1084 1085void addCommand( const char *nsName, const char *name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1086{ 1087 Namespace *ns = lookupNamespace(nsName); 1088 ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1089} 1090 1091void addCommand( const char *nsName, const char *name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1092{ 1093 Namespace *ns = lookupNamespace(nsName); 1094 ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1095} 1096 1097void addCommand( const char *nsName, const char *name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1098{ 1099 Namespace *ns = lookupNamespace(nsName); 1100 ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1101} 1102 1103void addCommand( const char *nsName, const char *name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1104{ 1105 Namespace *ns = lookupNamespace(nsName); 1106 ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1107} 1108 1109void addCommand( const char *nsName, const char *name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1110{ 1111 Namespace *ns = lookupNamespace(nsName); 1112 ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1113} 1114 1115void noteScriptCallback( const char *className, const char *funcName, const char *usage, ConsoleFunctionHeader* header ) 1116{ 1117 Namespace *ns = lookupNamespace(className); 1118 ns->addScriptCallback( StringTable->insert(funcName), usage, header ); 1119} 1120 1121void markCommandGroup(const char * nsName, const char *name, const char* usage) 1122{ 1123 Namespace *ns = lookupNamespace(nsName); 1124 ns->markGroup(name,usage); 1125} 1126 1127void beginCommandGroup(const char * nsName, const char *name, const char* usage) 1128{ 1129 markCommandGroup(nsName, name, usage); 1130} 1131 1132void endCommandGroup(const char * nsName, const char *name) 1133{ 1134 markCommandGroup(nsName, name, NULL); 1135} 1136 1137void addCommand( const char *name,StringCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1138{ 1139 Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1140} 1141 1142void addCommand( const char *name,VoidCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1143{ 1144 Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1145} 1146 1147void addCommand( const char *name,IntCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1148{ 1149 Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1150} 1151 1152void addCommand( const char *name,FloatCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1153{ 1154 Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1155} 1156 1157void addCommand( const char *name,BoolCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) 1158{ 1159 Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); 1160} 1161 1162bool executeFile(const char* fileName, bool noCalls, bool journalScript) 1163{ 1164 bool journal = false; 1165 1166 char scriptFilenameBuffer[1024]; 1167 U32 execDepth = 0; 1168 U32 journalDepth = 1; 1169 1170 execDepth++; 1171 if (journalDepth >= execDepth) 1172 journalDepth = execDepth + 1; 1173 else 1174 journal = true; 1175 1176 bool ret = false; 1177 1178 if (journalScript && !journal) 1179 { 1180 journal = true; 1181 journalDepth = execDepth; 1182 } 1183 1184 // Determine the filename we actually want... 1185 Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), fileName); 1186 1187 // since this function expects a script file reference, if it's a .dso 1188 // lets terminate the string before the dso so it will act like a .tscript 1189 if (dStrEndsWith(scriptFilenameBuffer, ".dso")) 1190 { 1191 scriptFilenameBuffer[dStrlen(scriptFilenameBuffer) - dStrlen(".dso")] = '\0'; 1192 } 1193 1194 // Figure out where to put DSOs 1195 StringTableEntry dsoPath = Con::getDSOPath(scriptFilenameBuffer); 1196 1197 const char *ext = dStrrchr(scriptFilenameBuffer, '.'); 1198 1199 if (!ext) 1200 { 1201 // Try appending the default script extension and see if that succeeds 1202 1203 if (executeFile(fileName + String("." TORQUE_SCRIPT_EXTENSION), noCalls, journalScript)) 1204 { 1205 return true; 1206 } 1207 1208 // We need an extension! 1209 Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", scriptFilenameBuffer); 1210 execDepth--; 1211 return false; 1212 } 1213 1214 // Check Editor Extensions 1215 bool isEditorScript = false; 1216 1217 // If the script file extension is '.ed.tscript' then compile it to a different compiled extension 1218 if (dStricmp(ext, "." TORQUE_SCRIPT_EXTENSION) == 0) 1219 { 1220 const char* ext2 = ext - 3; 1221 if (dStricmp(ext2, ".ed." TORQUE_SCRIPT_EXTENSION) == 0) 1222 isEditorScript = true; 1223 } 1224 else if (dStricmp(ext, ".gui") == 0) 1225 { 1226 const char* ext2 = ext - 3; 1227 if (dStricmp(ext2, ".ed.gui") == 0) 1228 isEditorScript = true; 1229 } 1230 1231 StringTableEntry scriptFileName = StringTable->insert(scriptFilenameBuffer); 1232 1233 // Is this a file we should compile? (anything in the prefs path should not be compiled) 1234 StringTableEntry prefsPath = Platform::getPrefsPath(); 1235 bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs"); 1236 1237 // [tom, 12/5/2006] stripBasePath() fucks up if the filename is not in the exe 1238 // path, current directory or prefs path. Thus, getDSOFilename() will also screw 1239 // up and so this allows the scripts to still load but without a DSO. 1240 if (Platform::isFullPath(Platform::stripBasePath(scriptFilenameBuffer))) 1241 compiled = false; 1242 1243 // [tom, 11/17/2006] It seems to make sense to not compile scripts that are in the 1244 // prefs directory. However, getDSOPath() can handle this situation and will put 1245 // the dso along with the script to avoid name clashes with tools/game dsos. 1246 if ((dsoPath && *dsoPath == 0) || (prefsPath && prefsPath[0] && dStrnicmp(scriptFileName, prefsPath, dStrlen(prefsPath)) == 0)) 1247 compiled = false; 1248 1249 // If we're in a journaling mode, then we will read the script 1250 // from the journal file. 1251 if (journal && Journal::IsPlaying()) 1252 { 1253 char fileNameBuf[256]; 1254 bool fileRead = false; 1255 U32 fileSize; 1256 1257 Journal::ReadString(fileNameBuf); 1258 Journal::Read(&fileRead); 1259 1260 if (!fileRead) 1261 { 1262 Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf); 1263 execDepth--; 1264 return false; 1265 } 1266 Journal::Read(&fileSize); 1267 char *script = new char[fileSize + 1]; 1268 Journal::Read(fileSize, script); 1269 script[fileSize] = 0; 1270 Con::printf("Executing (journal-read) %s.", scriptFileName); 1271 CodeBlock *newCodeBlock = new CodeBlock(); 1272 newCodeBlock->compileExec(scriptFileName, script, noCalls, 0); 1273 delete[] script; 1274 1275 execDepth--; 1276 return true; 1277 } 1278 1279 // Ok, we let's try to load and compile the script. 1280 Torque::FS::FileNodeRef scriptFile = Torque::FS::GetFileNode(scriptFileName); 1281 Torque::FS::FileNodeRef dsoFile; 1282 1283 // ResourceObject *rScr = gResourceManager->find(scriptFileName); 1284 // ResourceObject *rCom = NULL; 1285 1286 char nameBuffer[512]; 1287 char* script = NULL; 1288 U32 version; 1289 1290 Stream *compiledStream = NULL; 1291 Torque::Time scriptModifiedTime, dsoModifiedTime; 1292 1293 // Check here for .edso 1294 bool edso = false; 1295 if (dStricmp(ext, ".edso") == 0 && scriptFile != NULL) 1296 { 1297 edso = true; 1298 dsoFile = scriptFile; 1299 scriptFile = NULL; 1300 1301 dsoModifiedTime = dsoFile->getModifiedTime(); 1302 dStrcpy(nameBuffer, scriptFileName, 512); 1303 } 1304 1305 // If we're supposed to be compiling this file, check to see if there's a DSO 1306 if (compiled && !edso) 1307 { 1308 const char *filenameOnly = dStrrchr(scriptFileName, '/'); 1309 if (filenameOnly) 1310 ++filenameOnly; 1311 else 1312 filenameOnly = scriptFileName; 1313 1314 char pathAndFilename[1024]; 1315 Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath); 1316 1317 if (isEditorScript) 1318 dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL); 1319 else 1320 dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL); 1321 1322 dsoFile = Torque::FS::GetFileNode(nameBuffer); 1323 1324 if (scriptFile != NULL) 1325 scriptModifiedTime = scriptFile->getModifiedTime(); 1326 1327 if (dsoFile != NULL) 1328 dsoModifiedTime = dsoFile->getModifiedTime(); 1329 } 1330 1331 // Let's do a sanity check to complain about DSOs in the future. 1332 // 1333 // MM: This doesn't seem to be working correctly for now so let's just not issue 1334 // the warning until someone knows how to resolve it. 1335 // 1336 //if(compiled && rCom && rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) < 0) 1337 //{ 1338 //Con::warnf("exec: Warning! Found a DSO from the future! (%s)", nameBuffer); 1339 //} 1340 1341 // If we had a DSO, let's check to see if we should be reading from it. 1342 //MGT: fixed bug with dsos not getting recompiled correctly 1343 //Note: Using Nathan Martin's version from the forums since its easier to read and understand 1344 if (compiled && dsoFile != NULL && (scriptFile == NULL || (dsoModifiedTime >= scriptModifiedTime))) 1345 { //MGT: end 1346 compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read); 1347 if (compiledStream) 1348 { 1349 // Check the version! 1350 compiledStream->read(&version); 1351 if (version != Con::DSOVersion) 1352 { 1353 Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, Con::DSOVersion); 1354 delete compiledStream; 1355 compiledStream = NULL; 1356 } 1357 } 1358 } 1359 1360 // If we're journalling, let's write some info out. 1361 if (journal && Journal::IsRecording()) 1362 Journal::WriteString(scriptFileName); 1363 1364 if (scriptFile != NULL && !compiledStream) 1365 { 1366 // If we have source but no compiled version, then we need to compile 1367 // (and journal as we do so, if that's required). 1368 1369 void *data; 1370 U32 dataSize = 0; 1371 Torque::FS::ReadFile(scriptFileName, data, dataSize, true); 1372 1373 if (journal && Journal::IsRecording()) 1374 Journal::Write(bool(data != NULL)); 1375 1376 if (data == NULL) 1377 { 1378 Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName); 1379 execDepth--; 1380 return false; 1381 } 1382 else 1383 { 1384 if (!dataSize) 1385 { 1386 execDepth--; 1387 return false; 1388 } 1389 1390 script = (char *)data; 1391 1392 if (journal && Journal::IsRecording()) 1393 { 1394 Journal::Write(dataSize); 1395 Journal::Write(dataSize, data); 1396 } 1397 } 1398 1399#ifndef TORQUE_NO_DSO_GENERATION 1400 if (compiled) 1401 { 1402 // compile this baddie. 1403#ifdef TORQUE_DEBUG 1404 Con::printf("Compiling %s...", scriptFileName); 1405#endif 1406 1407 CodeBlock *code = new CodeBlock(); 1408 code->compile(nameBuffer, scriptFileName, script); 1409 delete code; 1410 code = NULL; 1411 1412 compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read); 1413 if (compiledStream) 1414 { 1415 compiledStream->read(&version); 1416 } 1417 else 1418 { 1419 // We have to exit out here, as otherwise we get double error reports. 1420 delete[] script; 1421 execDepth--; 1422 return false; 1423 } 1424 } 1425#endif 1426 } 1427 else 1428 { 1429 if (journal && Journal::IsRecording()) 1430 Journal::Write(bool(false)); 1431 } 1432 1433 if (compiledStream) 1434 { 1435 // Delete the script object first to limit memory used 1436 // during recursive execs. 1437 delete[] script; 1438 script = 0; 1439 1440 // We're all compiled, so let's run it. 1441#ifdef TORQUE_DEBUG 1442 Con::printf("Loading compiled script %s.", scriptFileName); 1443#endif 1444 CodeBlock *code = new CodeBlock; 1445 code->read(scriptFileName, *compiledStream); 1446 delete compiledStream; 1447 code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0); 1448 ret = true; 1449 } 1450 else 1451 if (scriptFile) 1452 { 1453 // No compiled script, let's just try executing it 1454 // directly... this is either a mission file, or maybe 1455 // we're on a readonly volume. 1456#ifdef TORQUE_DEBUG 1457 Con::printf("Executing %s.", scriptFileName); 1458#endif 1459 1460 CodeBlock *newCodeBlock = new CodeBlock(); 1461 StringTableEntry name = StringTable->insert(scriptFileName); 1462 1463 newCodeBlock->compileExec(name, script, noCalls, 0); 1464 ret = true; 1465 } 1466 else 1467 { 1468 // Don't have anything. 1469 Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", scriptFileName); 1470 ret = false; 1471 } 1472 1473 delete[] script; 1474 execDepth--; 1475 return ret; 1476} 1477 1478ConsoleValueRef evaluate(const char* string, bool echo, const char *fileName) 1479{ 1480 ConsoleStackFrameSaver stackSaver; 1481 stackSaver.save(); 1482 1483 if (echo) 1484 { 1485 if (string[0] == '%') 1486 Con::printf("%s", string); 1487 else 1488 Con::printf("%s%s", getVariable( "$Con::Prompt" ), string); 1489 } 1490 1491 if(fileName) 1492 fileName = StringTable->insert(fileName); 1493 1494 CodeBlock *newCodeBlock = new CodeBlock(); 1495 return newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0); 1496} 1497 1498//------------------------------------------------------------------------------ 1499ConsoleValueRef evaluatef(const char* string, ...) 1500{ 1501 ConsoleStackFrameSaver stackSaver; 1502 stackSaver.save(); 1503 1504 char buffer[4096]; 1505 va_list args; 1506 va_start(args, string); 1507 dVsprintf(buffer, sizeof(buffer), string, args); 1508 va_end(args); 1509 CodeBlock *newCodeBlock = new CodeBlock(); 1510 return newCodeBlock->compileExec(NULL, buffer, false, 0); 1511} 1512 1513//------------------------------------------------------------------------------ 1514 1515// Internal execute for global function which does not save the stack 1516ConsoleValueRef _internalExecute(S32 argc, ConsoleValueRef argv[]) 1517{ 1518 const char** argv_str = static_cast<const char**>(malloc((argc - 1) * sizeof(char *))); 1519 for (int i = 0; i < argc - 1; i++) 1520 { 1521 argv_str[i] = argv[i + 1]; 1522 } 1523 bool result; 1524 const char* methodRes = CInterface::CallFunction(NULL, argv[0], argv_str, argc - 1, &result); 1525 free(argv_str); 1526 if (result) 1527 { 1528 return ConsoleValueRef::fromValue(CSTK.pushString(methodRes)); 1529 } 1530 1531 Namespace::Entry *ent; 1532 StringTableEntry funcName = StringTable->insert(argv[0]); 1533 ent = Namespace::global()->lookup(funcName); 1534 1535 if(!ent) 1536 { 1537 warnf(ConsoleLogEntry::Script, "%s: Unknown command.", (const char*)argv[0]); 1538 1539 STR.clearFunctionOffset(); 1540 return ConsoleValueRef(); 1541 } 1542 return ent->execute(argc, argv, &gEvalState); 1543} 1544 1545ConsoleValueRef execute(S32 argc, ConsoleValueRef argv[]) 1546{ 1547#ifdef TORQUE_MULTITHREAD 1548 if(isMainThread()) 1549 { 1550#endif 1551 ConsoleStackFrameSaver stackSaver; 1552 stackSaver.save(); 1553 return _internalExecute(argc, argv); 1554#ifdef TORQUE_MULTITHREAD 1555 } 1556 else 1557 { 1558 SimConsoleThreadExecCallback cb; 1559 SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(argc, argv, false, &cb); 1560 Sim::postEvent(Sim::getRootGroup(), evt, Sim::getCurrentTime()); 1561 1562 return cb.waitForResult(); 1563 } 1564#endif 1565} 1566 1567ConsoleValueRef execute(S32 argc, const char *argv[]) 1568{ 1569 ConsoleStackFrameSaver stackSaver; 1570 stackSaver.save(); 1571 StringStackConsoleWrapper args(argc, argv); 1572 return execute(args.count(), args); 1573} 1574 1575//------------------------------------------------------------------------------ 1576 1577// Internal execute for object method which does not save the stack 1578ConsoleValueRef _internalExecute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly) 1579{ 1580 if(argc < 2) 1581 { 1582 STR.clearFunctionOffset(); 1583 return ConsoleValueRef(); 1584 } 1585 1586 // [neo, 10/05/2007 - #3010] 1587 // Make sure we don't get recursive calls, respect the flag! 1588 // Should we be calling handlesMethod() first? 1589 if( !thisCallOnly ) 1590 { 1591 ICallMethod *com = dynamic_cast<ICallMethod *>(object); 1592 if(com) 1593 { 1594 STR.pushFrame(); 1595 CSTK.pushFrame(); 1596 com->callMethodArgList(argc, argv, false); 1597 STR.popFrame(); 1598 CSTK.popFrame(); 1599 } 1600 } 1601 1602 const char** argv_str = static_cast<const char**>(malloc((argc - 2) * sizeof(char *))); 1603 for (int i = 0; i < argc - 2; i++) 1604 { 1605 argv_str[i] = argv[i + 2]; 1606 } 1607 bool result; 1608 const char* methodRes = CInterface::CallMethod(object, argv[0], argv_str, argc - 2, &result); 1609 1610 free(argv_str); 1611 1612 if (result) 1613 { 1614 return ConsoleValueRef::fromValue(CSTK.pushString(methodRes)); 1615 } 1616 1617 if(object->getNamespace()) 1618 { 1619 U32 ident = object->getId(); 1620 ConsoleValueRef oldIdent(argv[1]); 1621 1622 StringTableEntry funcName = StringTable->insert(argv[0]); 1623 Namespace::Entry *ent = object->getNamespace()->lookup(funcName); 1624 1625 if(ent == NULL) 1626 { 1627 //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId()); 1628 1629 STR.clearFunctionOffset(); 1630 return ConsoleValueRef(); 1631 } 1632 1633 // Twiddle %this argument 1634 ConsoleValue func_ident; 1635 func_ident.setIntValue((S32)ident); 1636 argv[1] = ConsoleValueRef::fromValue(&func_ident); 1637 1638 SimObject *save = gEvalState.thisObject; 1639 gEvalState.thisObject = object; 1640 ConsoleValueRef ret = ent->execute(argc, argv, &gEvalState); 1641 gEvalState.thisObject = save; 1642 1643 // Twiddle it back 1644 argv[1] = oldIdent; 1645 1646 return ret; 1647 } 1648 1649 warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]); 1650 STR.clearFunctionOffset(); 1651 return ConsoleValueRef(); 1652} 1653 1654 1655ConsoleValueRef execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly) 1656{ 1657 if(argc < 2) 1658 { 1659 STR.clearFunctionOffset(); 1660 return ConsoleValueRef(); 1661 } 1662 1663 ConsoleStackFrameSaver stackSaver; 1664 stackSaver.save(); 1665 1666 if (object->getNamespace() || !thisCallOnly) 1667 { 1668 if (isMainThread()) 1669 { 1670 return _internalExecute(object, argc, argv, thisCallOnly); 1671 } 1672 else 1673 { 1674 SimConsoleThreadExecCallback cb; 1675 SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(argc, argv, true, &cb); 1676 Sim::postEvent(object, evt, Sim::getCurrentTime()); 1677 } 1678 } 1679 1680 warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]); 1681 STR.clearFunctionOffset(); 1682 return ConsoleValueRef(); 1683} 1684 1685ConsoleValueRef execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly) 1686{ 1687 ConsoleStackFrameSaver stackSaver; 1688 stackSaver.save(); 1689 StringStackConsoleWrapper args(argc, argv); 1690 return execute(object, args.count(), args, thisCallOnly); 1691} 1692 1693inline ConsoleValueRef _executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValueRef *argv) 1694{ 1695 const U32 maxArg = 12; 1696 AssertWarn(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)"); 1697 AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more."); 1698 return execute(obj, argc, argv); 1699} 1700 1701//------------------------------------------------------------------------------ 1702inline ConsoleValueRef _executef(S32 checkArgc, S32 argc, ConsoleValueRef *argv) 1703{ 1704 const U32 maxArg = 10; 1705 AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef()"); 1706 AssertFatal(argc <= maxArg, "Too many args passed to Con::_executef(). Please update the function to handle more."); 1707 return execute(argc, argv); 1708} 1709 1710//------------------------------------------------------------------------------ 1711bool isFunction(const char *fn) 1712{ 1713 if (CInterface::isMethod(NULL, fn)) return true; 1714 const char *string = StringTable->lookup(fn); 1715 if(!string) 1716 return false; 1717 else 1718 return Namespace::global()->lookup(string) != NULL; 1719} 1720 1721//------------------------------------------------------------------------------ 1722 1723void setLogMode(S32 newMode) 1724{ 1725 if ((newMode & 0x3) != (consoleLogMode & 0x3)) { 1726 if (newMode && !consoleLogMode) { 1727 // Enabling logging when it was previously disabled. 1728 newLogFile = true; 1729 } 1730 if ((consoleLogMode & 0x3) == 2) { 1731 // Changing away from mode 2, must close logfile. 1732 consoleLogFile.close(); 1733 } 1734 else if ((newMode & 0x3) == 2) { 1735#ifdef _XBOX 1736 // Xbox is not going to support logging to a file. Use the OutputDebugStr 1737 // log consumer 1738 Platform::debugBreak(); 1739#endif 1740 // Starting mode 2, must open logfile. 1741 consoleLogFile.open(defLogFileName, Torque::FS::File::Write); 1742 } 1743 consoleLogMode = newMode; 1744 } 1745} 1746 1747Namespace *lookupNamespace(const char *ns) 1748{ 1749 if(!ns) 1750 return Namespace::global(); 1751 return Namespace::find(StringTable->insert(ns)); 1752} 1753 1754bool linkNamespaces(const char *parent, const char *child) 1755{ 1756 Namespace *pns = lookupNamespace(parent); 1757 Namespace *cns = lookupNamespace(child); 1758 if(pns && cns) 1759 return cns->classLinkTo(pns); 1760 return false; 1761} 1762 1763bool unlinkNamespaces(const char *parent, const char *child) 1764{ 1765 Namespace *pns = lookupNamespace(parent); 1766 Namespace *cns = lookupNamespace(child); 1767 1768 if(pns == cns) 1769 { 1770 Con::warnf("Con::unlinkNamespaces - trying to unlink '%s' from itself, aborting.", parent); 1771 return false; 1772 } 1773 1774 if(pns && cns) 1775 return cns->unlinkClass(pns); 1776 1777 return false; 1778} 1779 1780bool classLinkNamespaces(Namespace *parent, Namespace *child) 1781{ 1782 if(parent && child) 1783 return child->classLinkTo(parent); 1784 return false; 1785} 1786 1787void setData(S32 type, void *dptr, S32 index, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag) 1788{ 1789 ConsoleBaseType *cbt = ConsoleBaseType::getType(type); 1790 AssertFatal(cbt, "Con::setData - could not resolve type ID!"); 1791 cbt->setData((void *) (((const char *)dptr) + index * cbt->getTypeSize()),argc, argv, tbl, flag); 1792} 1793 1794const char *getData(S32 type, void *dptr, S32 index, const EnumTable *tbl, BitSet32 flag) 1795{ 1796 ConsoleBaseType *cbt = ConsoleBaseType::getType(type); 1797 AssertFatal(cbt, "Con::getData - could not resolve type ID!"); 1798 return cbt->getData((void *) (((const char *)dptr) + index * cbt->getTypeSize()), tbl, flag); 1799} 1800 1801const char *getFormattedData(S32 type, const char *data, const EnumTable *tbl, BitSet32 flag) 1802{ 1803 ConsoleBaseType *cbt = ConsoleBaseType::getType( type ); 1804 AssertFatal(cbt, "Con::getData - could not resolve type ID!"); 1805 1806 // Datablock types are just a datablock 1807 // name and don't ever need formatting. 1808 if ( cbt->isDatablock() ) 1809 return data; 1810 1811 bool currWarn = gWarnUndefinedScriptVariables; 1812 gWarnUndefinedScriptVariables = false; 1813 1814 const char* globalValue = Con::getVariable(data); 1815 1816 gWarnUndefinedScriptVariables = currWarn; 1817 1818 if (dStrlen(globalValue) > 0) 1819 return globalValue; 1820 1821 void* variable = cbt->getNativeVariable(); 1822 1823 if (variable) 1824 { 1825 Con::setData(type, variable, 0, 1, &data, tbl, flag); 1826 const char* formattedVal = Con::getData(type, variable, 0, tbl, flag); 1827 1828 static const U32 bufSize = 2048; 1829 char* returnBuffer = Con::getReturnBuffer(bufSize); 1830 dSprintf(returnBuffer, bufSize, "%s\0", formattedVal ); 1831 1832 cbt->deleteNativeVariable(variable); 1833 1834 return returnBuffer; 1835 } 1836 else 1837 return data; 1838} 1839 1840//------------------------------------------------------------------------------ 1841 1842bool isCurrentScriptToolScript() 1843{ 1844 // With a player build we ALWAYS return false 1845#ifndef TORQUE_TOOLS 1846 return false; 1847#else 1848 const StringTableEntry cbFullPath = CodeBlock::getCurrentCodeBlockFullPath(); 1849 if(cbFullPath == NULL) 1850 return false; 1851 const StringTableEntry exePath = Platform::getMainDotCsDir(); 1852 1853 return dStrnicmp(exePath, cbFullPath, dStrlen(exePath)) == 0; 1854#endif 1855} 1856 1857//------------------------------------------------------------------------------ 1858 1859StringTableEntry getModNameFromPath(const char *path) 1860{ 1861 if(path == NULL || *path == 0) 1862 return NULL; 1863 1864 char buf[1024]; 1865 buf[0] = 0; 1866 1867 if(path[0] == '/' || path[1] == ':') 1868 { 1869 // It's an absolute path 1870 const StringTableEntry exePath = Platform::getMainDotCsDir(); 1871 U32 len = dStrlen(exePath); 1872 if(dStrnicmp(exePath, path, len) == 0) 1873 { 1874 const char *ptr = path + len + 1; 1875 const char *slash = dStrchr(ptr, '/'); 1876 if(slash) 1877 { 1878 dStrncpy(buf, ptr, slash - ptr); 1879 buf[slash - ptr] = 0; 1880 } 1881 else 1882 return NULL; 1883 } 1884 else 1885 return NULL; 1886 } 1887 else 1888 { 1889 const char *slash = dStrchr(path, '/'); 1890 if(slash) 1891 { 1892 dStrncpy(buf, path, slash - path); 1893 buf[slash - path] = 0; 1894 } 1895 else 1896 return NULL; 1897 } 1898 1899 return StringTable->insert(buf); 1900} 1901 1902void postConsoleInput( RawData data ) 1903{ 1904 // Schedule this to happen at the next time event. 1905 ConsoleValue values[2]; 1906 ConsoleValueRef argv[2]; 1907 1908 values[0].init(); 1909 values[0].setStringValue("eval"); 1910 values[1].init(); 1911 values[1].setStringValue((const char*)data.data); 1912 argv[0].value = &values[0]; 1913 argv[1].value = &values[1]; 1914 1915 Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, argv, false)); 1916} 1917 1918//------------------------------------------------------------------------------ 1919 1920void pushInstantGroup( String name ) 1921{ 1922 sInstantGroupStack.push_back( gInstantGroup ); 1923 gInstantGroup = name; 1924} 1925 1926void popInstantGroup() 1927{ 1928 if( sInstantGroupStack.empty() ) 1929 gInstantGroup = String::EmptyString; 1930 else 1931 { 1932 gInstantGroup = sInstantGroupStack.last(); 1933 sInstantGroupStack.pop_back(); 1934 } 1935} 1936 1937 1938typedef HashMap<StringTableEntry, StringTableEntry> typePathExpandoMap; 1939static typePathExpandoMap PathExpandos; 1940 1941//----------------------------------------------------------------------------- 1942 1943void addPathExpando(const char* pExpandoName, const char* pPath) 1944{ 1945 // Sanity! 1946 AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL."); 1947 AssertFatal(pPath != NULL, "Expando path cannot be NULL."); 1948 1949 // Fetch expando name. 1950 StringTableEntry expandoName = StringTable->insert(pExpandoName); 1951 1952 // Fetch the length of the path. 1953 S32 pathLength = dStrlen(pPath); 1954 1955 char pathBuffer[1024]; 1956 1957 // Sanity! 1958 if (pathLength == 0 || pathLength >= sizeof(pathBuffer)) 1959 { 1960 Con::warnf("Cannot add path expando '%s' with path '%s' as the path is an invalid length.", pExpandoName, pPath); 1961 return; 1962 } 1963 1964 // Strip repeat slashes. 1965 if (!Con::stripRepeatSlashes(pathBuffer, pPath, sizeof(pathBuffer))) 1966 { 1967 Con::warnf("Cannot add path expando '%s' with path '%s' as the path is an invalid length.", pExpandoName, pPath); 1968 return; 1969 } 1970 1971 // Fetch new path length. 1972 pathLength = dStrlen(pathBuffer); 1973 1974 // Sanity! 1975 if (pathLength == 0) 1976 { 1977 Con::warnf("Cannot add path expando '%s' with path '%s' as the path is an invalid length.", pExpandoName, pPath); 1978 return; 1979 } 1980 1981 // Remove any terminating slash. 1982 if (pathBuffer[pathLength - 1] == '/') 1983 pathBuffer[pathLength - 1] = 0; 1984 1985 // Fetch expanded path. 1986 StringTableEntry expandedPath = StringTable->insert(pathBuffer); 1987 1988 // Info. 1989#if defined(TORQUE_DEBUG) 1990 Con::printf("Adding path expando of '%s' as '%s'.", expandoName, expandedPath); 1991#endif 1992 1993 // Find any existing path expando. 1994 typePathExpandoMap::iterator expandoItr = PathExpandos.find(pExpandoName); 1995 1996 // Does the expando exist? 1997 if (expandoItr != PathExpandos.end()) 1998 { 1999 // Yes, so modify the path. 2000 expandoItr->value = expandedPath; 2001 return; 2002 } 2003 2004 // Insert expando. 2005 PathExpandos.insert(expandoName, expandedPath); 2006} 2007 2008//----------------------------------------------------------------------------- 2009 2010StringTableEntry getPathExpando(const char* pExpandoName) 2011{ 2012 // Sanity! 2013 AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL."); 2014 2015 // Fetch expando name. 2016 StringTableEntry expandoName = StringTable->insert(pExpandoName); 2017 2018 // Find any existing path expando. 2019 typePathExpandoMap::iterator expandoItr = PathExpandos.find(expandoName); 2020 2021 // Does the expando exist? 2022 if (expandoItr != PathExpandos.end()) 2023 { 2024 // Yes, so return it. 2025 return expandoItr->value; 2026 } 2027 2028 // Not found. 2029 return NULL; 2030} 2031 2032//----------------------------------------------------------------------------- 2033 2034void removePathExpando(const char* pExpandoName) 2035{ 2036 // Sanity! 2037 AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL."); 2038 2039 // Fetch expando name. 2040 StringTableEntry expandoName = StringTable->insert(pExpandoName); 2041 2042 // Find any existing path expando. 2043 typePathExpandoMap::iterator expandoItr = PathExpandos.find(expandoName); 2044 2045 // Does the expando exist? 2046 if (expandoItr == PathExpandos.end()) 2047 { 2048 // No, so warn. 2049#if defined(TORQUE_DEBUG) 2050 Con::warnf("Removing path expando of '%s' but it does not exist.", expandoName); 2051#endif 2052 return; 2053 } 2054 2055 // Info. 2056#if defined(TORQUE_DEBUG) 2057 Con::printf("Removing path expando of '%s' as '%s'.", expandoName, expandoItr->value); 2058#endif 2059 // Remove expando. 2060 PathExpandos.erase(expandoItr); 2061} 2062 2063//----------------------------------------------------------------------------- 2064 2065bool isPathExpando(const char* pExpandoName) 2066{ 2067 // Sanity! 2068 AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL."); 2069 2070 // Fetch expando name. 2071 StringTableEntry expandoName = StringTable->insert(pExpandoName); 2072 2073 return PathExpandos.contains(expandoName); 2074} 2075 2076//----------------------------------------------------------------------------- 2077 2078U32 getPathExpandoCount(void) 2079{ 2080 return PathExpandos.size(); 2081} 2082 2083//----------------------------------------------------------------------------- 2084 2085StringTableEntry getPathExpandoKey(U32 expandoIndex) 2086{ 2087 // Finish if index is out of range. 2088 if (expandoIndex >= PathExpandos.size()) 2089 return NULL; 2090 2091 // Find indexed iterator. 2092 typePathExpandoMap::iterator expandoItr = PathExpandos.begin(); 2093 while (expandoIndex > 0) { ++expandoItr; --expandoIndex; } 2094 2095 return expandoItr->key; 2096} 2097 2098//----------------------------------------------------------------------------- 2099 2100StringTableEntry getPathExpandoValue(U32 expandoIndex) 2101{ 2102 // Finish if index is out of range. 2103 if (expandoIndex >= PathExpandos.size()) 2104 return NULL; 2105 2106 // Find indexed iterator. 2107 typePathExpandoMap::iterator expandoItr = PathExpandos.begin(); 2108 while (expandoIndex > 0) { ++expandoItr; --expandoIndex; } 2109 2110 return expandoItr->value; 2111} 2112 2113//----------------------------------------------------------------------------- 2114 2115bool expandPath(char* pDstPath, U32 size, const char* pSrcPath, const char* pWorkingDirectoryHint, const bool ensureTrailingSlash) 2116{ 2117 char pathBuffer[2048]; 2118 const char* pSrc = pSrcPath; 2119 char* pSlash; 2120 2121 // Fetch leading character. 2122 const char leadingToken = *pSrc; 2123 2124 // Fetch following token. 2125 const char followingToken = leadingToken != 0 ? pSrc[1] : 0; 2126 2127 // Expando. 2128 if (leadingToken == '^') 2129 { 2130 // Initial prefix search. 2131 const char* pPrefixSrc = pSrc + 1; 2132 char* pPrefixDst = pathBuffer; 2133 2134 // Search for end of expando. 2135 while (*pPrefixSrc != '/' && *pPrefixSrc != 0) 2136 { 2137 // Copy prefix character. 2138 *pPrefixDst++ = *pPrefixSrc++; 2139 } 2140 2141 // Yes, so terminate the expando string. 2142 *pPrefixDst = 0; 2143 2144 // Fetch the expando path. 2145 StringTableEntry expandoPath = getPathExpando(pathBuffer); 2146 2147 // Does the expando exist? 2148 if (expandoPath == NULL) 2149 { 2150 // No, so error. 2151 Con::errorf("expandPath() : Could not find path expando '%s' for path '%s'.", pathBuffer, pSrcPath); 2152 2153 // Are we ensuring the trailing slash? 2154 if (ensureTrailingSlash) 2155 { 2156 // Yes, so ensure it. 2157 Con::ensureTrailingSlash(pDstPath, pSrcPath, size); 2158 } 2159 else 2160 { 2161 // No, so just use the source path. 2162 dStrcpy(pDstPath, pSrcPath, size); 2163 } 2164 2165 return false; 2166 } 2167 2168 // Skip the expando and the following slash. 2169 pSrc += dStrlen(pathBuffer) + 1; 2170 2171 // Format the output path. 2172 dSprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", expandoPath, pSrc); 2173 2174 // Are we ensuring the trailing slash? 2175 if (ensureTrailingSlash) 2176 { 2177 // Yes, so ensure it. 2178 Con::ensureTrailingSlash(pathBuffer, pathBuffer, size); 2179 } 2180 2181 // Strip repeat slashes. 2182 Con::stripRepeatSlashes(pDstPath, pathBuffer, size); 2183 2184 return true; 2185 } 2186 2187 // Script-Relative. 2188 if (leadingToken == '.') 2189 { 2190 // Fetch the code-block file-path. 2191 const StringTableEntry codeblockFullPath = CodeBlock::getCurrentCodeBlockFullPath(); 2192 2193 // Do we have a code block full path? 2194 if (codeblockFullPath == NULL) 2195 { 2196 // No, so error. 2197 Con::errorf("expandPath() : Could not find relative path from code-block for path '%s'.", pSrcPath); 2198 2199 // Are we ensuring the trailing slash? 2200 if (ensureTrailingSlash) 2201 { 2202 // Yes, so ensure it. 2203 Con::ensureTrailingSlash(pDstPath, pSrcPath, size); 2204 } 2205 else 2206 { 2207 // No, so just use the source path. 2208 dStrcpy(pDstPath, pSrcPath, size); 2209 } 2210 2211 return false; 2212 } 2213 2214 // Yes, so use it as the prefix. 2215 dStrncpy(pathBuffer, codeblockFullPath, sizeof(pathBuffer) - 1); 2216 2217 // Find the final slash in the code-block. 2218 pSlash = dStrrchr(pathBuffer, '/'); 2219 2220 // Is this a parent directory token? 2221 if (followingToken == '.') 2222 { 2223 // Yes, so terminate after the slash so we include it. 2224 pSlash[1] = 0; 2225 } 2226 else 2227 { 2228 // No, it's a current directory token so terminate at the slash so we don't include it. 2229 pSlash[0] = 0; 2230 2231 // Skip the current directory token. 2232 pSrc++; 2233 } 2234 2235 // Format the output path. 2236 dStrncat(pathBuffer, "/", sizeof(pathBuffer) - 1 - strlen(pathBuffer)); 2237 dStrncat(pathBuffer, pSrc, sizeof(pathBuffer) - 1 - strlen(pathBuffer)); 2238 2239 // Are we ensuring the trailing slash? 2240 if (ensureTrailingSlash) 2241 { 2242 // Yes, so ensure it. 2243 Con::ensureTrailingSlash(pathBuffer, pathBuffer, size); 2244 } 2245 2246 // Strip repeat slashes. 2247 Con::stripRepeatSlashes(pDstPath, pathBuffer, size); 2248 2249 return true; 2250 } 2251 2252 // All else. 2253 2254 //Using a special case here because the code below barfs on trying to build a full path for apk reading 2255#ifdef TORQUE_OS_ANDROID 2256 if (leadingToken == '/' || strstr(pSrcPath, "/") == NULL) 2257 Platform::makeFullPathName(pSrcPath, pathBuffer, sizeof(pathBuffer), pWorkingDirectoryHint); 2258 else 2259 dSprintf(pathBuffer, sizeof(pathBuffer), "/%s", pSrcPath); 2260#else 2261 Platform::makeFullPathName(pSrcPath, pathBuffer, sizeof(pathBuffer), pWorkingDirectoryHint); 2262#endif 2263 2264 // Are we ensuring the trailing slash? 2265 if (ensureTrailingSlash) 2266 { 2267 // Yes, so ensure it. 2268 Con::ensureTrailingSlash(pathBuffer, pathBuffer, size); 2269 } 2270 2271 // Strip repeat slashes. 2272 Con::stripRepeatSlashes(pDstPath, pathBuffer, size); 2273 2274 return true; 2275} 2276 2277//----------------------------------------------------------------------------- 2278 2279bool isBasePath(const char* SrcPath, const char* pBasePath) 2280{ 2281 char expandBuffer[1024]; 2282 Con::expandPath(expandBuffer, sizeof(expandBuffer), SrcPath); 2283 return dStrnicmp(pBasePath, expandBuffer, dStrlen(pBasePath)) == 0; 2284} 2285 2286//----------------------------------------------------------------------------- 2287 2288void collapsePath(char* pDstPath, U32 size, const char* pSrcPath, const char* pWorkingDirectoryHint) 2289{ 2290 // Check path against expandos. If there are multiple matches, choose the 2291 // expando that produces the shortest relative path. 2292 2293 char pathBuffer[2048]; 2294 2295 // Fetch expando count. 2296 const U32 expandoCount = getPathExpandoCount(); 2297 2298 // Iterate expandos. 2299 U32 expandoRelativePathLength = U32_MAX; 2300 for (U32 expandoIndex = 0; expandoIndex < expandoCount; ++expandoIndex) 2301 { 2302 // Fetch expando value (path). 2303 StringTableEntry expandoValue = getPathExpandoValue(expandoIndex); 2304 2305 // Skip if not the base path. 2306 if (!isBasePath(pSrcPath, expandoValue)) 2307 continue; 2308 2309 // Fetch path relative to expando path. 2310 StringTableEntry relativePath = Platform::makeRelativePathName(pSrcPath, expandoValue); 2311 2312 // If the relative path is simply a period 2313 if (relativePath[0] == '.') 2314 relativePath++; 2315 2316 if (dStrlen(relativePath) > expandoRelativePathLength) 2317 { 2318 // This expando covers less of the path than any previous one found. 2319 // We will keep the previous one. 2320 continue; 2321 } 2322 2323 // Keep track of the relative path length 2324 expandoRelativePathLength = dStrlen(relativePath); 2325 2326 // Fetch expando key (name). 2327 StringTableEntry expandoName = getPathExpandoKey(expandoIndex); 2328 2329 // Format against expando. 2330 dSprintf(pathBuffer, sizeof(pathBuffer), "^%s/%s", expandoName, relativePath); 2331 } 2332 2333 // Check if we've found a suitable expando 2334 if (expandoRelativePathLength != U32_MAX) 2335 { 2336 // Strip repeat slashes. 2337 Con::stripRepeatSlashes(pDstPath, pathBuffer, size); 2338 2339 return; 2340 } 2341 2342 // Fetch the working directory. 2343 StringTableEntry workingDirectory = pWorkingDirectoryHint != NULL ? pWorkingDirectoryHint : Platform::getCurrentDirectory(); 2344 2345 // Fetch path relative to current directory. 2346 StringTableEntry relativePath = Platform::makeRelativePathName(pSrcPath, workingDirectory); 2347 2348 // If the relative path is simply a period 2349 if (relativePath[0] == '.' && relativePath[1] != '.') 2350 relativePath++; 2351 2352 // Format against expando. 2353 dSprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", workingDirectory, relativePath); 2354 2355 // Strip repeat slashes. 2356 Con::stripRepeatSlashes(pDstPath, pathBuffer, size); 2357} 2358 2359 2360void ensureTrailingSlash(char* pDstPath, const char* pSrcPath, S32 dstSize) 2361{ 2362 // Copy to target. 2363 dStrcpy(pDstPath, pSrcPath, dstSize); 2364 2365 // Find trailing character index. 2366 S32 trailIndex = dStrlen(pDstPath); 2367 2368 // Ignore if empty string. 2369 if (trailIndex == 0) 2370 return; 2371 2372 // Finish if the trailing slash already exists. 2373 if (pDstPath[trailIndex - 1] == '/') 2374 return; 2375 2376 // Add trailing slash. 2377 pDstPath[trailIndex++] = '/'; 2378 pDstPath[trailIndex] = 0; 2379} 2380 2381//----------------------------------------------------------------------------- 2382 2383StringTableEntry getDSOPath(const char *scriptPath) 2384{ 2385#ifndef TORQUE2D_TOOLS_FIXME 2386 2387 // [tom, 11/17/2006] Force old behavior for the player. May not want to do this. 2388 const char *slash = dStrrchr(scriptPath, '/'); 2389 if (slash != NULL) 2390 return StringTable->insertn(scriptPath, slash - scriptPath, true); 2391 2392 slash = dStrrchr(scriptPath, ':'); 2393 if (slash != NULL) 2394 return StringTable->insertn(scriptPath, (slash - scriptPath) + 1, true); 2395 2396 return ""; 2397 2398#else 2399 2400 char relPath[1024], dsoPath[1024]; 2401 bool isPrefs = false; 2402 2403 // [tom, 11/17/2006] Prefs are handled slightly differently to avoid dso name clashes 2404 StringTableEntry prefsPath = Platform::getPrefsPath(); 2405 if (dStrnicmp(scriptPath, prefsPath, dStrlen(prefsPath)) == 0) 2406 { 2407 relPath[0] = 0; 2408 isPrefs = true; 2409 } 2410 else 2411 { 2412 StringTableEntry strippedPath = Platform::stripBasePath(scriptPath); 2413 dStrcpy(relPath, strippedPath, 1024); 2414 2415 char *slash = dStrrchr(relPath, '/'); 2416 if (slash) 2417 *slash = 0; 2418 } 2419 2420 const char *overridePath; 2421 if (!isPrefs) 2422 overridePath = Con::getVariable("$Scripts::OverrideDSOPath"); 2423 else 2424 overridePath = prefsPath; 2425 2426 if (overridePath && *overridePath) 2427 Platform::makeFullPathName(relPath, dsoPath, sizeof(dsoPath), overridePath); 2428 else 2429 { 2430 char t[1024]; 2431 dSprintf(t, sizeof(t), "compiledScripts/%s", relPath); 2432 Platform::makeFullPathName(t, dsoPath, sizeof(dsoPath), Platform::getPrefsPath()); 2433 } 2434 2435 return StringTable->insert(dsoPath); 2436 2437#endif 2438} 2439 2440//----------------------------------------------------------------------------- 2441bool stripRepeatSlashes(char* pDstPath, const char* pSrcPath, S32 dstSize) 2442{ 2443 // Note original destination. 2444 char* pOriginalDst = pDstPath; 2445 2446 // Reset last source character. 2447 char lastSrcChar = 0; 2448 2449 // Search source... 2450 while (dstSize > 0) 2451 { 2452 // Fetch characters. 2453 const char srcChar = *pSrcPath++; 2454 2455 // Do we have a repeat slash? 2456 if (srcChar == '/' && lastSrcChar == '/') 2457 { 2458 // Yes, so skip it. 2459 continue; 2460 } 2461 2462 // No, so copy character. 2463 *pDstPath++ = srcChar; 2464 2465 // Finish if end of source. 2466 if (srcChar == 0) 2467 return true; 2468 2469 // Reduce room left in destination. 2470 dstSize--; 2471 2472 // Set last character. 2473 lastSrcChar = srcChar; 2474 } 2475 2476 // Terminate the destination string as we ran out of room. 2477 *pOriginalDst = 0; 2478 2479 // Fail! 2480 return false; 2481} 2482 2483} // end of Console namespace 2484 2485#endif 2486 2487//============================================================================= 2488// API. 2489//============================================================================= 2490// MARK: ---- API ---- 2491 2492//----------------------------------------------------------------------------- 2493 2494DefineEngineFunction( log, void, ( const char* message ),, 2495 "@brief Logs a message to the console.\n\n" 2496 "@param message The message text.\n" 2497 "@note By default, messages will appear white in the console.\n" 2498 "@ingroup Logging") 2499{ 2500 Con::printf( "%s", message ); 2501} 2502 2503//----------------------------------------------------------------------------- 2504 2505DefineEngineFunction( logError, void, ( const char* message ),, 2506 "@brief Logs an error message to the console.\n\n" 2507 "@param message The message text.\n" 2508 "@note By default, errors will appear red in the console.\n" 2509 "@ingroup Logging") 2510{ 2511 Con::errorf( "%s", message ); 2512} 2513 2514//----------------------------------------------------------------------------- 2515 2516DefineEngineFunction( logWarning, void, ( const char* message ),, 2517 "@brief Logs a warning message to the console.\n\n" 2518 "@param message The message text.\n\n" 2519 "@note By default, warnings will appear turquoise in the console.\n" 2520 "@ingroup Logging") 2521{ 2522 Con::warnf( "%s", message ); 2523} 2524 2525//------------------------------------------------------------------------------ 2526 2527extern ConsoleValueStack CSTK; 2528 2529ConsoleValueRef::ConsoleValueRef(const ConsoleValueRef &ref) 2530{ 2531 value = ref.value; 2532} 2533 2534ConsoleValueRef& ConsoleValueRef::operator=(const ConsoleValueRef &newValue) 2535{ 2536 value = newValue.value; 2537 return *this; 2538} 2539 2540ConsoleValueRef& ConsoleValueRef::operator=(const char *newValue) 2541{ 2542 AssertFatal(value, "value should not be NULL"); 2543 value->setStringValue(newValue); 2544 return *this; 2545} 2546 2547ConsoleValueRef& ConsoleValueRef::operator=(S32 newValue) 2548{ 2549 AssertFatal(value, "value should not be NULL"); 2550 value->setIntValue(newValue); 2551 return *this; 2552} 2553 2554ConsoleValueRef& ConsoleValueRef::operator=(U32 newValue) 2555{ 2556 AssertFatal(value, "value should not be NULL"); 2557 value->setIntValue(newValue); 2558 return *this; 2559} 2560 2561ConsoleValueRef& ConsoleValueRef::operator=(F32 newValue) 2562{ 2563 AssertFatal(value, "value should not be NULL"); 2564 value->setFloatValue(newValue); 2565 return *this; 2566} 2567 2568ConsoleValueRef& ConsoleValueRef::operator=(F64 newValue) 2569{ 2570 AssertFatal(value, "value should not be NULL"); 2571 value->setFloatValue(newValue); 2572 return *this; 2573} 2574 2575//------------------------------------------------------------------------------ 2576 2577StringStackWrapper::StringStackWrapper(int targc, ConsoleValueRef targv[]) 2578{ 2579 argv = new const char*[targc]; 2580 argc = targc; 2581 2582 for (int i=0; i<targc; i++) 2583 { 2584 argv[i] = dStrdup(targv[i]); 2585 } 2586} 2587 2588StringStackWrapper::~StringStackWrapper() 2589{ 2590 for (int i=0; i<argc; i++) 2591 { 2592 dFree(argv[i]); 2593 } 2594 delete[] argv; 2595} 2596 2597 2598StringStackConsoleWrapper::StringStackConsoleWrapper(int targc, const char** targ) 2599{ 2600 argv = new ConsoleValueRef[targc]; 2601 argvValue = new ConsoleValue[targc]; 2602 argc = targc; 2603 2604 for (int i=0; i<targc; i++) { 2605 argvValue[i].init(); 2606 argv[i].value = &argvValue[i]; 2607 argvValue[i].setStackStringValue(targ[i]); 2608 } 2609} 2610 2611StringStackConsoleWrapper::~StringStackConsoleWrapper() 2612{ 2613 for (int i=0; i<argc; i++) 2614 { 2615 argv[i] = 0; 2616 } 2617 delete[] argv; 2618 delete[] argvValue; 2619} 2620 2621//------------------------------------------------------------------------------ 2622 2623S32 ConsoleValue::getSignedIntValue() 2624{ 2625 if(type <= TypeInternalString) 2626 return (S32)fval; 2627 else 2628 return dAtoi(Con::getData(type, dataPtr, 0, enumTable)); 2629} 2630 2631U32 ConsoleValue::getIntValue() 2632{ 2633 if(type <= TypeInternalString) 2634 return ival; 2635 else 2636 return dAtoi(Con::getData(type, dataPtr, 0, enumTable)); 2637} 2638 2639F32 ConsoleValue::getFloatValue() 2640{ 2641 if(type <= TypeInternalString) 2642 return fval; 2643 else 2644 return dAtof(Con::getData(type, dataPtr, 0, enumTable)); 2645} 2646 2647const char *ConsoleValue::getStringValue() 2648{ 2649 if(type == TypeInternalString || type == TypeInternalStackString) 2650 return sval; 2651 else if (type == TypeInternalStringStackPtr) 2652 return STR.mBuffer + (uintptr_t)sval; 2653 else 2654 { 2655 // We need a string representation, so lets create one 2656 const char *internalValue = NULL; 2657 2658 if(type == TypeInternalFloat) 2659 internalValue = Con::getData(TypeF32, &fval, 0); 2660 else if(type == TypeInternalInt) 2661 internalValue = Con::getData(TypeS32, &ival, 0); 2662 else 2663 return Con::getData(type, dataPtr, 0, enumTable); // We can't save sval here since it is the same as dataPtr 2664 2665 if (!internalValue) 2666 return ""; 2667 2668 U32 stringLen = dStrlen(internalValue); 2669 U32 newLen = ((stringLen + 1) + 15) & ~15; // pad upto next cache line 2670 2671 if (bufferLen == 0) 2672 sval = (char *) dMalloc(newLen); 2673 else if(newLen > bufferLen) 2674 sval = (char *) dRealloc(sval, newLen); 2675 2676 dStrcpy(sval, internalValue, newLen); 2677 bufferLen = newLen; 2678 2679 return sval; 2680 } 2681} 2682 2683StringStackPtr ConsoleValue::getStringStackPtr() 2684{ 2685 if (type == TypeInternalStringStackPtr) 2686 return (uintptr_t)sval; 2687 else 2688 return (uintptr_t)-1; 2689} 2690 2691bool ConsoleValue::getBoolValue() 2692{ 2693 if(type == TypeInternalString || type == TypeInternalStackString || type == TypeInternalStringStackPtr) 2694 return dAtob(getStringValue()); 2695 if(type == TypeInternalFloat) 2696 return fval > 0; 2697 else if(type == TypeInternalInt) 2698 return ival > 0; 2699 else { 2700 const char *value = Con::getData(type, dataPtr, 0, enumTable); 2701 return dAtob(value); 2702 } 2703} 2704 2705void ConsoleValue::setIntValue(S32 val) 2706{ 2707 setFloatValue(val); 2708} 2709 2710void ConsoleValue::setIntValue(U32 val) 2711{ 2712 if(type <= TypeInternalString) 2713 { 2714 fval = (F32)val; 2715 ival = val; 2716 if(bufferLen > 0) 2717 { 2718 dFree(sval); 2719 bufferLen = 0; 2720 } 2721 2722 sval = typeValueEmpty; 2723 type = TypeInternalInt; 2724 } 2725 else 2726 { 2727 const char *dptr = Con::getData(TypeS32, &val, 0); 2728 Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); 2729 } 2730} 2731 2732void ConsoleValue::setBoolValue(bool val) 2733{ 2734 return setIntValue(val ? 1 : 0); 2735} 2736 2737void ConsoleValue::setFloatValue(F32 val) 2738{ 2739 if(type <= TypeInternalString) 2740 { 2741 fval = val; 2742 ival = static_cast<U32>(val); 2743 if(bufferLen > 0) 2744 { 2745 dFree(sval); 2746 bufferLen = 0; 2747 } 2748 sval = typeValueEmpty; 2749 type = TypeInternalFloat; 2750 } 2751 else 2752 { 2753 const char *dptr = Con::getData(TypeF32, &val, 0); 2754 Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); 2755 } 2756} 2757 2758//------------------------------------------------------------------------------ 2759 2760ConsoleValueRef _BaseEngineConsoleCallbackHelper::_exec() 2761{ 2762 ConsoleValueRef returnValue; 2763 if( mThis ) 2764 { 2765 // Cannot invoke callback until object has been registered 2766 if (mThis->isProperlyAdded()) { 2767 returnValue = Con::_internalExecute( mThis, mArgc, mArgv, false ); 2768 } else { 2769 STR.clearFunctionOffset(); 2770 returnValue = ConsoleValueRef(); 2771 } 2772 } 2773 else 2774 returnValue = Con::_internalExecute( mArgc, mArgv ); 2775 2776 mArgc = mInitialArgc; // reset args 2777 return returnValue; 2778} 2779 2780ConsoleValueRef _BaseEngineConsoleCallbackHelper::_execLater(SimConsoleThreadExecEvent *evt) 2781{ 2782 mArgc = mInitialArgc; // reset args 2783 Sim::postEvent((SimObject*)Sim::getRootGroup(), evt, Sim::getCurrentTime()); 2784 return evt->getCB().waitForResult(); 2785} 2786 2787//------------------------------------------------------------------------------ 2788 2789void ConsoleStackFrameSaver::save() 2790{ 2791 CSTK.pushFrame(); 2792 STR.pushFrame(); 2793 mSaved = true; 2794} 2795 2796void ConsoleStackFrameSaver::restore() 2797{ 2798 if (mSaved) 2799 { 2800 CSTK.popFrame(); 2801 STR.popFrame(); 2802 } 2803} 2804