console.cpp

Engine/source/console/console.cpp

More...

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

Public Functions

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