| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "platform/platformTLS.h"#include "platform/threads/thread.h"#include "console/console.h"#include "console/consoleInternal.h"#include "console/consoleObject.h"#include "console/consoleParser.h"#include "core/stream/fileStream.h"#include "console/ast.h"#include "core/tAlgorithm.h"#include "console/consoleTypes.h"#include "console/telnetDebugger.h"#include "console/simBase.h"#include "console/compiler.h"#include "console/stringStack.h"#include "console/ICallMethod.h"#include "console/engineAPI.h"#include <stdarg.h>#include "platform/threads/mutex.h"#include "core/util/journal/journal.h"extern StringStack STR;extern ConsoleValueStack CSTK;ConsoleDocFragment* ConsoleDocFragment::smFirst;ExprEvalState gEvalState;StmtNode *gStatementList;StmtNode *gAnonFunctionList;U32 gAnonFunctionID = 0;ConsoleConstructor *ConsoleConstructor::first = NULL;bool gWarnUndefinedScriptVariables;static char scratchBuffer[4096];CON_DECLARE_PARSER(CMD);static const char * prependDollar ( const char * name ){   if(name[0] != '$')   {      S32   len = dStrlen(name);      AssertFatal(len < sizeof(scratchBuffer)-2, "CONSOLE: name too long");      scratchBuffer[0] = '$';      dMemcpy(scratchBuffer + 1, name, len + 1);      name = scratchBuffer;   }   return name;}static const char * prependPercent ( const char * name ){   if(name[0] != '%')   {      S32   len = dStrlen(name);      AssertFatal(len < sizeof(scratchBuffer)-2, "CONSOLE: name too long");      scratchBuffer[0] = '%';      dMemcpy(scratchBuffer + 1, name, len + 1);      name = scratchBuffer;   }   return name;}//--------------------------------------void ConsoleConstructor::init( const char *cName, const char *fName, const char *usg, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   mina = minArgs;   maxa = maxArgs;   funcName = fName;   usage = usg;   className = cName;   sc = 0; fc = 0; vc = 0; bc = 0; ic = 0;   callback = group = false;   next = first;   ns = false;   first = this;   toolOnly = isToolOnly;   this->header = header;}void ConsoleConstructor::setup(){   for(ConsoleConstructor *walk = first; walk; walk = walk->next)   {#ifdef TORQUE_DEBUG      walk->validate();#endif      if( walk->sc )         Con::addCommand( walk->className, walk->funcName, walk->sc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header );      else if( walk->ic )         Con::addCommand( walk->className, walk->funcName, walk->ic, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header );      else if( walk->fc )         Con::addCommand( walk->className, walk->funcName, walk->fc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header );      else if( walk->vc )         Con::addCommand( walk->className, walk->funcName, walk->vc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header );      else if( walk->bc )         Con::addCommand( walk->className, walk->funcName, walk->bc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header );      else if( walk->group )         Con::markCommandGroup( walk->className, walk->funcName, walk->usage );      else if( walk->callback )         Con::noteScriptCallback( walk->className, walk->funcName, walk->usage, walk->header );      else if( walk->ns )      {         Namespace* ns = Namespace::find( StringTable->insert( walk->className ) );         if( ns )            ns->mUsage = walk->usage;      }      else      {         AssertISV( false, "Found a ConsoleConstructor with an indeterminate type!" );      }   }}ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, StringCallback sfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header );   sc = sfunc;}ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, IntCallback ifunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header );   ic = ifunc;}ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, FloatCallback ffunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header );   fc = ffunc;}ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, VoidCallback vfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header );   vc = vfunc;}ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, BoolCallback bfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header );   bc = bfunc;}ConsoleConstructor::ConsoleConstructor(const char* className, const char* groupName, const char* aUsage){   init(className, groupName, usage, -1, -2);   group = true;   // Somewhere, the entry list is getting flipped, partially.   // so we have to do tricks to deal with making sure usage   // is properly populated.   // This is probably redundant.   static char * lastUsage = NULL;   if(aUsage)      lastUsage = (char *)aUsage;   usage = lastUsage;}ConsoleConstructor::ConsoleConstructor(const char *className, const char *callbackName, const char *usage, ConsoleFunctionHeader* header ){   init( className, callbackName, usage, -2, -3, false, header );   callback = true;   ns = true;}void ConsoleConstructor::validate(){#ifdef TORQUE_DEBUG   // Don't do the following check if we're not a method/func.   if(this->group)      return;   // In debug, walk the list and make sure this isn't a duplicate.   for(ConsoleConstructor *walk = first; walk; walk = walk->next)   {      // Skip mismatching func/method names.      if(dStricmp(walk->funcName, this->funcName))         continue;      // Don't compare functions with methods or vice versa.      if(bool(this->className) != bool(walk->className))         continue;      // Skip mismatching classnames, if they're present.      if(this->className && walk->className && dStricmp(walk->className, this->className))         continue;      // If we encounter ourselves, stop searching; this prevents duplicate      // firing of the assert, instead only firing for the latter encountered      // entry.      if(this == walk)         break;      // Match!      if(this->className)      {         AssertISV(false, avar("ConsoleConstructor::setup - ConsoleMethod '%s::%s' collides with another of the same name.", this->className, this->funcName));      }      else      {         AssertISV(false, avar("ConsoleConstructor::setup - ConsoleFunction '%s' collides with another of the same name.", this->funcName));      }   }#endif}// We comment out the implementation of the Con namespace when doxygenizing because// otherwise Doxygen decides to ignore our docs in console.h#ifndef DOXYGENIZINGnamespace Con{static Vector<ConsumerCallback> gConsumers(__FILE__, __LINE__);static Vector< String > sInstantGroupStack( __FILE__, __LINE__ );static DataChunker consoleLogChunker;static Vector<ConsoleLogEntry> consoleLog(__FILE__, __LINE__);static bool consoleLogLocked;static bool logBufferEnabled=true;static S32 printLevel = 10;static FileStream consoleLogFile;static const char *defLogFileName = "console.log";static S32 consoleLogMode = 0;static bool active = false;static bool newLogFile;static const char *logFileName;static const S32 MaxCompletionBufferSize = 4096;static char completionBuffer[MaxCompletionBufferSize];static char tabBuffer[MaxCompletionBufferSize] = {0};static SimObjectPtr<SimObject> tabObject;static U32 completionBaseStart;static U32 completionBaseLen;String gInstantGroup;Con::ConsoleInputEvent smConsoleInput;/// Current script file name and root, these are registered as/// console variables./// @{///StringTableEntry gCurrentFile;StringTableEntry gCurrentRoot;/// @}S32 gObjectCopyFailures = -1;bool alwaysUseDebugOutput = true;bool useTimestamp = false;ConsoleFunctionGroupBegin( Clipboard, "Miscellaneous functions to control the clipboard and clear the console.");DefineConsoleFunction( cls, void, (), , "()"				"@brief Clears the console output.\n\n"				"@ingroup Console"){   if(consoleLogLocked)      return;   consoleLogChunker.freeBlocks();   consoleLog.setSize(0);};DefineConsoleFunction( getClipboard, const char*, (), , "()"				"@brief Get text from the clipboard.\n\n"				"@internal"){	return Platform::getClipboard();};DefineConsoleFunction( setClipboard, bool, (const char* text), , "(string text)"               "@brief Set the system clipboard.\n\n"			   "@internal"){	return Platform::setClipboard(text);};ConsoleFunctionGroupEnd( Clipboard );void postConsoleInput( RawData data );void init(){   AssertFatal(active == false, "Con::init should only be called once.");   // Set up general init values.   active                        = true;   logFileName                   = NULL;   newLogFile                    = true;   gWarnUndefinedScriptVariables = false;   // Initialize subsystems.   Namespace::init();   ConsoleConstructor::setup();   // Set up the parser(s)   CON_ADD_PARSER(CMD,   "cs",   true);   // TorqueScript   // Setup the console types.   ConsoleBaseType::initialize();   // And finally, the ACR...   AbstractClassRep::initialize();   // Variables   setVariable("Con::prompt", "% ");   addVariable("Con::logBufferEnabled", TypeBool, &logBufferEnabled, "If true, the log buffer will be enabled.\n"	   "@ingroup Console\n");   addVariable("Con::printLevel", TypeS32, &printLevel,       "@brief This is deprecated.\n\n"      "It is no longer in use and does nothing.\n"      	   "@ingroup Console\n");   addVariable("Con::warnUndefinedVariables", TypeBool, &gWarnUndefinedScriptVariables, "If true, a warning will be displayed in the console whenever a undefined variable is used in script.\n"	   "@ingroup Console\n");   addVariable( "instantGroup", TypeRealString, &gInstantGroup, "The group that objects will be added to when they are created.\n"	   "@ingroup Console\n");   addVariable("Con::objectCopyFailures", TypeS32, &gObjectCopyFailures, "If greater than zero then it counts the number of object creation "      "failures based on a missing copy object and does not report an error..\n"	   "@ingroup Console\n");      // Current script file name and root   addVariable( "Con::File", TypeString, &gCurrentFile, "The currently executing script file.\n"	   "@ingroup FileSystem\n");   addVariable( "Con::Root", TypeString, &gCurrentRoot, "The mod folder for the currently executing script file.\n"	   "@ingroup FileSystem\n" );   // alwaysUseDebugOutput determines whether to send output to the platform's    // "debug" system.  see winConsole for an example.     // in ship builds we don't expose this variable to script   // and we set it to false by default (don't want to provide more information   // to potential hackers).  platform code should also ifdef out the code that    // pays attention to this in ship builds (see winConsole.cpp)    // note that enabling this can slow down your game    // if you are running from the debugger and printing a lot of console messages.#ifndef TORQUE_SHIPPING   addVariable("Con::alwaysUseDebugOutput", TypeBool, &alwaysUseDebugOutput,       "@brief Determines whether to send output to the platform's \"debug\" system.\n\n"       "@note This is disabled in shipping builds.\n"	   "@ingroup Console");#else   alwaysUseDebugOutput = false;#endif   // controls whether a timestamp is prepended to every console message   addVariable("Con::useTimestamp", TypeBool, &useTimestamp, "If true a timestamp is prepended to every console message.\n"	   "@ingroup Console\n");   // Plug us into the journaled console input signal.   smConsoleInput.notify(postConsoleInput);}//--------------------------------------void shutdown(){   AssertFatal(active == true, "Con::shutdown should only be called once.");   active = false;   smConsoleInput.remove(postConsoleInput);   consoleLogFile.close();   Namespace::shutdown();   AbstractClassRep::shutdown();   Compiler::freeConsoleParserList();}bool isActive(){   return active;}bool isMainThread(){#ifdef TORQUE_MULTITHREAD   return ThreadManager::isMainThread();#else   // If we're single threaded we're always in the main thread.   return true;#endif}//--------------------------------------void getLockLog(ConsoleLogEntry *&log, U32 &size){   consoleLogLocked = true;   log = consoleLog.address();   size = consoleLog.size();}void unlockLog(){   consoleLogLocked = false;}U32 tabComplete(char* inputBuffer, U32 cursorPos, U32 maxResultLength, bool forwardTab){   // Check for null input.   if (!inputBuffer[0])    {      return cursorPos;   }   // Cap the max result length.   if (maxResultLength > MaxCompletionBufferSize)    {      maxResultLength = MaxCompletionBufferSize;   }   // See if this is the same partial text as last checked.   if (dStrcmp(tabBuffer, inputBuffer))    {      // If not...      // Save it for checking next time.      dStrcpy(tabBuffer, inputBuffer);      // Scan backward from the cursor position to find the base to complete from.      S32 p = cursorPos;      while ((p > 0) && (inputBuffer[p - 1] != ' ') && (inputBuffer[p - 1] != '.') && (inputBuffer[p - 1] != '('))      {         p--;      }      completionBaseStart = p;      completionBaseLen = cursorPos - p;      // Is this function being invoked on an object?      if (inputBuffer[p - 1] == '.')       {         // If so...         if (p <= 1)          {            // Bail if no object identifier.            return cursorPos;         }         // Find the object identifier.         S32 objLast = --p;         while ((p > 0) && (inputBuffer[p - 1] != ' ') && (inputBuffer[p - 1] != '('))          {            p--;         }         if (objLast == p)          {            // Bail if no object identifier.            return cursorPos;         }         // Look up the object identifier.         dStrncpy(completionBuffer, inputBuffer + p, objLast - p);         completionBuffer[objLast - p] = 0;         tabObject = Sim::findObject(completionBuffer);         if (tabObject == NULL)          {            // Bail if not found.            return cursorPos;         }      }      else       {         // Not invoked on an object; we'll use the global namespace.         tabObject = 0;      }   }   // Chop off the input text at the cursor position.   inputBuffer[cursorPos] = 0;   // Try to find a completion in the appropriate namespace.   const char *newText;   if (tabObject != 0)   {      newText = tabObject->tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab);   }   else    {      // In the global namespace, we can complete on global vars as well as functions.      if (inputBuffer[completionBaseStart] == '$')      {         newText = gEvalState.globalVars.tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab);      }      else       {         newText = Namespace::global()->tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab);      }   }   if (newText)    {      // If we got something, append it to the input text.      S32 len = dStrlen(newText);      if (len + completionBaseStart > maxResultLength)      {         len = maxResultLength - completionBaseStart;      }      dStrncpy(inputBuffer + completionBaseStart, newText, len);      inputBuffer[completionBaseStart + len] = 0;      // And set the cursor after it.      cursorPos = completionBaseStart + len;   }   // Save the modified input buffer for checking next time.   dStrcpy(tabBuffer, inputBuffer);   // Return the new (maybe) cursor position.   return cursorPos;}//------------------------------------------------------------------------------static void log(const char *string){   // Bail if we ain't logging.   if (!consoleLogMode)    {      return;   }   // In mode 1, we open, append, close on each log write.   if ((consoleLogMode & 0x3) == 1)    {      consoleLogFile.open(defLogFileName, Torque::FS::File::ReadWrite);   }   // Write to the log if its status is hunky-dory.   if ((consoleLogFile.getStatus() == Stream::Ok) || (consoleLogFile.getStatus() == Stream::EOS))    {      consoleLogFile.setPosition(consoleLogFile.getStreamSize());      // If this is the first write...      if (newLogFile)       {         // Make a header.         Platform::LocalTime lt;         Platform::getLocalTime(lt);         char buffer[128];         dSprintf(buffer, sizeof(buffer), "//-------------------------- %d/%d/%d -- %02d:%02d:%02d -----\r\n",               lt.month + 1,               lt.monthday,               lt.year + 1900,               lt.hour,               lt.min,               lt.sec);         consoleLogFile.write(dStrlen(buffer), buffer);         newLogFile = false;         if (consoleLogMode & 0x4)          {            // Dump anything that has been printed to the console so far.            consoleLogMode -= 0x4;            U32 size, line;            ConsoleLogEntry *log;            getLockLog(log, size);            for (line = 0; line < size; line++)             {               consoleLogFile.write(dStrlen(log[line].mString), log[line].mString);               consoleLogFile.write(2, "\r\n");            }            unlockLog();         }      }      // Now write what we came here to write.      consoleLogFile.write(dStrlen(string), string);      consoleLogFile.write(2, "\r\n");   }   if ((consoleLogMode & 0x3) == 1)    {      consoleLogFile.close();   }}//------------------------------------------------------------------------------static void _printf(ConsoleLogEntry::Level level, ConsoleLogEntry::Type type, const char* fmt, va_list argptr){   if (!active)	   return;   Con::active = false;    char buffer[8192];   U32 offset = 0;   if( gEvalState.traceOn && gEvalState.getStackDepth() > 0 )   {      offset = gEvalState.getStackDepth() * 3;      for(U32 i = 0; i < offset; i++)         buffer[i] = ' ';   }   if (useTimestamp)   {      static U32 startTime = Platform::getRealMilliseconds();      U32 curTime = Platform::getRealMilliseconds() - startTime;      offset += dSprintf(buffer + offset, sizeof(buffer) - offset, "[+%4d.%03d]", U32(curTime * 0.001), curTime % 1000);   }   dVsprintf(buffer + offset, sizeof(buffer) - offset, fmt, argptr);   for(S32 i = 0; i < gConsumers.size(); i++)      gConsumers[i](level, buffer);   if(logBufferEnabled || consoleLogMode)   {      char *pos = buffer;      while(*pos)      {         if(*pos == '\t')            *pos = '^';         pos++;      }      pos = buffer;      for(;;)      {         char *eofPos = dStrchr(pos, '\n');         if(eofPos)            *eofPos = 0;         log(pos);         if(logBufferEnabled && !consoleLogLocked)         {            ConsoleLogEntry entry;            entry.mLevel  = level;            entry.mType   = type;#ifndef TORQUE_SHIPPING // this is equivalent to a memory leak, turn it off in ship build                        entry.mString = (const char *)consoleLogChunker.alloc(dStrlen(pos) + 1);            dStrcpy(const_cast<char*>(entry.mString), pos);                        // This prevents infinite recursion if the console itself needs to            // re-allocate memory to accommodate the new console log entry, and             // LOG_PAGE_ALLOCS is defined. It is kind of a dirty hack, but the            // uses for LOG_PAGE_ALLOCS are limited, and it is not worth writing            // a lot of special case code to support this situation. -patw            const bool save = Con::active;            Con::active = false;            consoleLog.push_back(entry);            Con::active = save;#endif         }         if(!eofPos)            break;         pos = eofPos + 1;      }   }   Con::active = true;}//------------------------------------------------------------------------------void printf(const char* fmt,...){   va_list argptr;   va_start(argptr, fmt);   _printf(ConsoleLogEntry::Normal, ConsoleLogEntry::General, fmt, argptr);   va_end(argptr);}void warnf(ConsoleLogEntry::Type type, const char* fmt,...){   va_list argptr;   va_start(argptr, fmt);   _printf(ConsoleLogEntry::Warning, type, fmt, argptr);   va_end(argptr);}void errorf(ConsoleLogEntry::Type type, const char* fmt,...){   va_list argptr;   va_start(argptr, fmt);   _printf(ConsoleLogEntry::Error, type, fmt, argptr);   va_end(argptr);}void warnf(const char* fmt,...){   va_list argptr;   va_start(argptr, fmt);   _printf(ConsoleLogEntry::Warning, ConsoleLogEntry::General, fmt, argptr);   va_end(argptr);}void errorf(const char* fmt,...){   va_list argptr;   va_start(argptr, fmt);   _printf(ConsoleLogEntry::Error, ConsoleLogEntry::General, fmt, argptr);   va_end(argptr);}//---------------------------------------------------------------------------bool getVariableObjectField(const char *name, SimObject **object, const char **field){   // get the field info from the object..   const char *dot = dStrchr(name, '.');   if(name[0] != '$' && dot)   {      S32 len = dStrlen(name);      AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - name too long");      dMemcpy(scratchBuffer, name, len+1);      char * token = dStrtok(scratchBuffer, ".");      SimObject * obj = Sim::findObject(token);      if(!obj)         return false;      token = dStrtok(0, ".\0");      if(!token)         return false;      while(token != NULL)      {         const char * val = obj->getDataField(StringTable->insert(token), 0);         if(!val)            return false;         char *fieldToken = token;         token = dStrtok(0, ".\0");         if(token)         {            obj = Sim::findObject(token);            if(!obj)               return false;         }         else         {            *object = obj;            *field = fieldToken;            return true;         }      }   }   return false;}Dictionary::Entry *getLocalVariableEntry(const char *name){   name = prependPercent(name);   return gEvalState.getCurrentFrame().lookup(StringTable->insert(name));}Dictionary::Entry *getVariableEntry(const char *name){   name = prependDollar(name);   return gEvalState.globalVars.lookup(StringTable->insert(name));}Dictionary::Entry *addVariableEntry(const char *name){   name = prependDollar(name);   return gEvalState.globalVars.add(StringTable->insert(name));}Dictionary::Entry *getAddVariableEntry(const char *name){   name = prependDollar(name);   StringTableEntry stName = StringTable->insert(name);   Dictionary::Entry *entry = gEvalState.globalVars.lookup(stName);   if (!entry)	   entry = gEvalState.globalVars.add(stName);   return entry;}Dictionary::Entry *getAddLocalVariableEntry(const char *name){   name = prependPercent(name);   StringTableEntry stName = StringTable->insert(name);   Dictionary::Entry *entry = gEvalState.getCurrentFrame().lookup(stName);   if (!entry)	   entry = gEvalState.getCurrentFrame().add(stName);   return entry;}void setVariable(const char *name, const char *value){   SimObject *obj = NULL;   const char *objField = NULL;   if (getVariableObjectField(name, &obj, &objField))   {	   obj->setDataField(StringTable->insert(objField), 0, value);   }   else    {      name = prependDollar(name);      gEvalState.globalVars.setVariable(StringTable->insert(name), value);   }}void setLocalVariable(const char *name, const char *value){   name = prependPercent(name);   gEvalState.getCurrentFrame().setVariable(StringTable->insert(name), value);}void setBoolVariable(const char *varName, bool value){   SimObject *obj = NULL;   const char *objField = NULL;   if (getVariableObjectField(varName, &obj, &objField))   {	   obj->setDataField(StringTable->insert(objField), 0, value ? "1" : "0");   }   else   {      varName = prependDollar(varName);      Dictionary::Entry *entry = getAddVariableEntry(varName);	  entry->setStringValue(value ? "1" : "0");   }}void setIntVariable(const char *varName, S32 value){   SimObject *obj = NULL;   const char *objField = NULL;   if (getVariableObjectField(varName, &obj, &objField))   {	   char scratchBuffer[32];	   dSprintf(scratchBuffer, sizeof(scratchBuffer), "%d", value);	   obj->setDataField(StringTable->insert(objField), 0, scratchBuffer);   }   else   {      varName = prependDollar(varName);      Dictionary::Entry *entry = getAddVariableEntry(varName);      entry->setIntValue(value);   }}void setFloatVariable(const char *varName, F32 value){   SimObject *obj = NULL;   const char *objField = NULL;   if (getVariableObjectField(varName, &obj, &objField))   {	   char scratchBuffer[32];	   dSprintf(scratchBuffer, sizeof(scratchBuffer), "%g", value);	   obj->setDataField(StringTable->insert(objField), 0, scratchBuffer);   }   else   {      varName = prependDollar(varName);      Dictionary::Entry *entry = getAddVariableEntry(varName);	  entry->setFloatValue(value);   }}//---------------------------------------------------------------------------void addConsumer(ConsumerCallback consumer){   gConsumers.push_back(consumer);}// dhc - found this empty -- trying what I think is a reasonable impl.void removeConsumer(ConsumerCallback consumer){   for(S32 i = 0; i < gConsumers.size(); i++)   {      if (gConsumers[i] == consumer)      {         // remove it from the list.         gConsumers.erase(i);         break;      }   }}void stripColorChars(char* line){   char* c = line;   char cp = *c;   while (cp)    {      if (cp < 18)       {         // Could be a color control character; let's take a closer look.         if ((cp != 8) && (cp != 9) && (cp != 10) && (cp != 13))          {            // Yep... copy following chars forward over this.            char* cprime = c;            char cpp;            do             {               cpp = *++cprime;               *(cprime - 1) = cpp;            }             while (cpp);            // Back up 1 so we'll check this position again post-copy.            c--;         }      }      cp = *++c;   }}// const char *getObjectTokenField(const char *name){   const char *dot = dStrchr(name, '.');   if(name[0] != '$' && dot)   {      S32 len = dStrlen(name);      AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - object name too long");      dMemcpy(scratchBuffer, name, len+1);      char * token = dStrtok(scratchBuffer, ".");      SimObject * obj = Sim::findObject(token);      if(!obj)         return("");      token = dStrtok(0, ".\0");      if(!token)         return("");      while(token != NULL)      {         const char * val = obj->getDataField(StringTable->insert(token), 0);         if(!val)            return("");         token = dStrtok(0, ".\0");         if(token)         {            obj = Sim::findObject(token);            if(!obj)               return("");         }         else            return(val);      }   }   return NULL;}const char *getVariable(const char *name){   const char *objField = getObjectTokenField(name);   if (objField)   {      return objField;   }   else   {      Dictionary::Entry *entry = getVariableEntry(name);      return entry ? entry->getStringValue() : "";   }}const char *getLocalVariable(const char *name){   name = prependPercent(name);   return gEvalState.getCurrentFrame().getVariable(StringTable->insert(name));}bool getBoolVariable(const char *varName, bool def){   const char *objField = getObjectTokenField(varName);   if (objField)   {      return *objField ? dAtob(objField) : def;   }   else   {      Dictionary::Entry *entry = getVariableEntry(varName);      objField = entry ? entry->getStringValue() : "";      return *objField ? dAtob(objField) : def;   }}S32 getIntVariable(const char *varName, S32 def){   const char *objField = getObjectTokenField(varName);   if (objField)   {      return *objField ? dAtoi(objField) : def;   }   else   {      Dictionary::Entry *entry = getVariableEntry(varName);      return entry ? entry->getIntValue() : def;   }}F32 getFloatVariable(const char *varName, F32 def){   const char *objField = getObjectTokenField(varName);   if (objField)   {      return *objField ? dAtof(objField) : def;   }   else   {      Dictionary::Entry *entry = getVariableEntry(varName);	   return entry ? entry->getFloatValue() : def;   }}//---------------------------------------------------------------------------void addVariable(    const char *name,                      S32 type,                      void *dptr,                      const char* usage ){   gEvalState.globalVars.addVariable( name, type, dptr, usage );}void addConstant(    const char *name,                      S32 type,                      const void *dptr,                      const char* usage ){   Dictionary::Entry* entry = gEvalState.globalVars.addVariable( name, type, const_cast< void* >( dptr ), usage );   entry->mIsConstant = true;}bool removeVariable(const char *name){   name = StringTable->lookup(prependDollar(name));   return name!=0 && gEvalState.globalVars.removeVariable(name);}void addVariableNotify( const char *name, const NotifyDelegate &callback ){   gEvalState.globalVars.addVariableNotify( name, callback );}void removeVariableNotify( const char *name, const NotifyDelegate &callback ){   gEvalState.globalVars.removeVariableNotify( name, callback );}//---------------------------------------------------------------------------void addCommand( const char *nsName, const char *name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace *ns = lookupNamespace(nsName);   ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *nsName, const char *name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace *ns = lookupNamespace(nsName);   ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *nsName, const char *name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace *ns = lookupNamespace(nsName);   ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *nsName, const char *name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace *ns = lookupNamespace(nsName);   ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *nsName, const char *name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace *ns = lookupNamespace(nsName);   ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void noteScriptCallback( const char *className, const char *funcName, const char *usage, ConsoleFunctionHeader* header ){   Namespace *ns = lookupNamespace(className);   ns->addScriptCallback( StringTable->insert(funcName), usage, header );}void markCommandGroup(const char * nsName, const char *name, const char* usage){   Namespace *ns = lookupNamespace(nsName);   ns->markGroup(name,usage);}void beginCommandGroup(const char * nsName, const char *name, const char* usage){   markCommandGroup(nsName, name, usage);}void endCommandGroup(const char * nsName, const char *name){   markCommandGroup(nsName, name, NULL);}void addCommand( const char *name,StringCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *name,VoidCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *name,IntCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *name,FloatCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}void addCommand( const char *name,BoolCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ){   Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header );}bool executeFile(const char* fileName, bool noCalls, bool journalScript){   bool journal = false;   char scriptFilenameBuffer[1024];   U32 execDepth = 0;   U32 journalDepth = 1;   execDepth++;   if (journalDepth >= execDepth)      journalDepth = execDepth + 1;   else      journal = true;   bool ret = false;   if (journalScript && !journal)   {      journal = true;      journalDepth = execDepth;   }   // Determine the filename we actually want...   Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), fileName);   // since this function expects a script file reference, if it's a .dso   // lets terminate the string before the dso so it will act like a .cs   if (dStrEndsWith(scriptFilenameBuffer, ".dso"))   {      scriptFilenameBuffer[dStrlen(scriptFilenameBuffer) - dStrlen(".dso")] = '\0';   }   // Figure out where to put DSOs   StringTableEntry dsoPath = Con::getDSOPath(scriptFilenameBuffer);   const char *ext = dStrrchr(scriptFilenameBuffer, '.');   if (!ext)   {      // We need an extension!      Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", scriptFilenameBuffer);      execDepth--;      return false;   }   // Check Editor Extensions   bool isEditorScript = false;   // If the script file extension is '.ed.cs' then compile it to a different compiled extension   if (dStricmp(ext, ".cs") == 0)   {      const char* ext2 = ext - 3;      if (dStricmp(ext2, ".ed.cs") == 0)         isEditorScript = true;   }   else if (dStricmp(ext, ".gui") == 0)   {      const char* ext2 = ext - 3;      if (dStricmp(ext2, ".ed.gui") == 0)         isEditorScript = true;   }   StringTableEntry scriptFileName = StringTable->insert(scriptFilenameBuffer);#ifndef TORQUE_OS_XENON   // Is this a file we should compile? (anything in the prefs path should not be compiled)   StringTableEntry prefsPath = Platform::getPrefsPath();   bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs");   // [tom, 12/5/2006] stripBasePath() fucks up if the filename is not in the exe   // path, current directory or prefs path. Thus, getDSOFilename() will also screw   // up and so this allows the scripts to still load but without a DSO.   if (Platform::isFullPath(Platform::stripBasePath(scriptFilenameBuffer)))      compiled = false;   // [tom, 11/17/2006] It seems to make sense to not compile scripts that are in the   // prefs directory. However, getDSOPath() can handle this situation and will put   // the dso along with the script to avoid name clashes with tools/game dsos.   if ((dsoPath && *dsoPath == 0) || (prefsPath && prefsPath[0] && dStrnicmp(scriptFileName, prefsPath, dStrlen(prefsPath)) == 0))      compiled = false;#else   bool compiled = false;  // Don't try to compile things on the 360, ignore DSO's when debugging                           // because PC prefs will screw up stuff like SFX.#endif                           // If we're in a journaling mode, then we will read the script                           // from the journal file.   if (journal && Journal::IsPlaying())   {      char fileNameBuf[256];      bool fileRead = false;      U32 fileSize;      Journal::ReadString(fileNameBuf);      Journal::Read(&fileRead);      if (!fileRead)      {         Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf);         execDepth--;         return false;      }      Journal::Read(&fileSize);      char *script = new char[fileSize + 1];      Journal::Read(fileSize, script);      script[fileSize] = 0;      Con::printf("Executing (journal-read) %s.", scriptFileName);      CodeBlock *newCodeBlock = new CodeBlock();      newCodeBlock->compileExec(scriptFileName, script, noCalls, 0);      delete[] script;      execDepth--;      return true;   }   // Ok, we let's try to load and compile the script.   Torque::FS::FileNodeRef scriptFile = Torque::FS::GetFileNode(scriptFileName);   Torque::FS::FileNodeRef dsoFile;   //    ResourceObject *rScr = gResourceManager->find(scriptFileName);   //    ResourceObject *rCom = NULL;   char nameBuffer[512];   char* script = NULL;   U32 version;   Stream *compiledStream = NULL;   Torque::Time scriptModifiedTime, dsoModifiedTime;   // Check here for .edso   bool edso = false;   if (dStricmp(ext, ".edso") == 0 && scriptFile != NULL)   {      edso = true;      dsoFile = scriptFile;      scriptFile = NULL;      dsoModifiedTime = dsoFile->getModifiedTime();      dStrcpy(nameBuffer, scriptFileName);   }   // If we're supposed to be compiling this file, check to see if there's a DSO   if (compiled && !edso)   {      const char *filenameOnly = dStrrchr(scriptFileName, '/');      if (filenameOnly)         ++filenameOnly;      else         filenameOnly = scriptFileName;      char pathAndFilename[1024];      Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath);      if (isEditorScript)         dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL);      else         dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL);      dsoFile = Torque::FS::GetFileNode(nameBuffer);      if (scriptFile != NULL)         scriptModifiedTime = scriptFile->getModifiedTime();      if (dsoFile != NULL)         dsoModifiedTime = dsoFile->getModifiedTime();   }   // Let's do a sanity check to complain about DSOs in the future.   //   // MM:	This doesn't seem to be working correctly for now so let's just not issue   //		the warning until someone knows how to resolve it.   //   //if(compiled && rCom && rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) < 0)   //{   //Con::warnf("exec: Warning! Found a DSO from the future! (%s)", nameBuffer);   //}   // If we had a DSO, let's check to see if we should be reading from it.   //MGT: fixed bug with dsos not getting recompiled correctly   //Note: Using Nathan Martin's version from the forums since its easier to read and understand   if (compiled && dsoFile != NULL && (scriptFile == NULL || (dsoModifiedTime >= scriptModifiedTime)))   { //MGT: end      compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read);      if (compiledStream)      {         // Check the version!         compiledStream->read(&version);         if (version != Con::DSOVersion)         {            Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, Con::DSOVersion);            delete compiledStream;            compiledStream = NULL;         }      }   }   // If we're journalling, let's write some info out.   if (journal && Journal::IsRecording())      Journal::WriteString(scriptFileName);   if (scriptFile != NULL && !compiledStream)   {      // If we have source but no compiled version, then we need to compile      // (and journal as we do so, if that's required).      void *data;      U32 dataSize = 0;      Torque::FS::ReadFile(scriptFileName, data, dataSize, true);      if (journal && Journal::IsRecording())         Journal::Write(bool(data != NULL));      if (data == NULL)      {         Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName);         execDepth--;         return false;      }      else      {         if (!dataSize)         {            execDepth--;            return false;         }         script = (char *)data;         if (journal && Journal::IsRecording())         {            Journal::Write(dataSize);            Journal::Write(dataSize, data);         }      }#ifndef TORQUE_NO_DSO_GENERATION      if (compiled)      {         // compile this baddie.#ifdef TORQUE_DEBUG         Con::printf("Compiling %s...", scriptFileName);#endif            CodeBlock *code = new CodeBlock();         code->compile(nameBuffer, scriptFileName, script);         delete code;         code = NULL;         compiledStream = FileStream::createAndOpen(nameBuffer, Torque::FS::File::Read);         if (compiledStream)         {            compiledStream->read(&version);         }         else         {            // We have to exit out here, as otherwise we get double error reports.            delete[] script;            execDepth--;            return false;         }      }#endif   }   else   {      if (journal && Journal::IsRecording())         Journal::Write(bool(false));   }   if (compiledStream)   {      // Delete the script object first to limit memory used      // during recursive execs.      delete[] script;      script = 0;      // We're all compiled, so let's run it.#ifdef TORQUE_DEBUG      Con::printf("Loading compiled script %s.", scriptFileName);#endif         CodeBlock *code = new CodeBlock;      code->read(scriptFileName, *compiledStream);      delete compiledStream;      code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0);      ret = true;   }   else      if (scriptFile)      {         // No compiled script,  let's just try executing it         // directly... this is either a mission file, or maybe         // we're on a readonly volume.#ifdef TORQUE_DEBUG         Con::printf("Executing %s.", scriptFileName);#endif            CodeBlock *newCodeBlock = new CodeBlock();         StringTableEntry name = StringTable->insert(scriptFileName);         newCodeBlock->compileExec(name, script, noCalls, 0);         ret = true;      }      else      {         // Don't have anything.         Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", scriptFileName);         ret = false;      }   delete[] script;   execDepth--;   return ret;}ConsoleValueRef evaluate(const char* string, bool echo, const char *fileName){   ConsoleStackFrameSaver stackSaver;   stackSaver.save();   if (echo)   {      if (string[0] == '%')         Con::printf("%s", string);      else         Con::printf("%s%s", getVariable( "$Con::Prompt" ), string);   }   if(fileName)      fileName = StringTable->insert(fileName);   CodeBlock *newCodeBlock = new CodeBlock();   return newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0);}//------------------------------------------------------------------------------ConsoleValueRef evaluatef(const char* string, ...){   ConsoleStackFrameSaver stackSaver;   stackSaver.save();   char buffer[4096];   va_list args;   va_start(args, string);   dVsprintf(buffer, sizeof(buffer), string, args);   va_end(args);   CodeBlock *newCodeBlock = new CodeBlock();   return newCodeBlock->compileExec(NULL, buffer, false, 0);}//------------------------------------------------------------------------------// Internal execute for global function which does not save the stackConsoleValueRef _internalExecute(S32 argc, ConsoleValueRef argv[]){   Namespace::Entry *ent;   StringTableEntry funcName = StringTable->insert(argv[0]);   ent = Namespace::global()->lookup(funcName);   if(!ent)   {      warnf(ConsoleLogEntry::Script, "%s: Unknown command.", (const char*)argv[0]);      STR.clearFunctionOffset();      return ConsoleValueRef();   }   return ent->execute(argc, argv, &gEvalState);}ConsoleValueRef execute(S32 argc, ConsoleValueRef argv[]){#ifdef TORQUE_MULTITHREAD   if(isMainThread())   {#endif      ConsoleStackFrameSaver stackSaver;      stackSaver.save();	   return _internalExecute(argc, argv);#ifdef TORQUE_MULTITHREAD   }   else   {      SimConsoleThreadExecCallback cb;      SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(argc, argv, false, &cb);      Sim::postEvent(Sim::getRootGroup(), evt, Sim::getCurrentTime());            return cb.waitForResult();   }#endif}ConsoleValueRef execute(S32 argc, const char *argv[]){   ConsoleStackFrameSaver stackSaver;   stackSaver.save();   StringStackConsoleWrapper args(argc, argv);   return execute(args.count(), args);}//------------------------------------------------------------------------------// Internal execute for object method which does not save the stackConsoleValueRef _internalExecute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly){   if(argc < 2)   {      STR.clearFunctionOffset();      return ConsoleValueRef();   }   // [neo, 10/05/2007 - #3010]   // Make sure we don't get recursive calls, respect the flag!      // Should we be calling handlesMethod() first?   if( !thisCallOnly )   {      ICallMethod *com = dynamic_cast<ICallMethod *>(object);      if(com)      {         STR.pushFrame();         CSTK.pushFrame();         com->callMethodArgList(argc, argv, false);         STR.popFrame();         CSTK.popFrame();      }   }   if(object->getNamespace())   {      U32 ident = object->getId();      ConsoleValueRef oldIdent(argv[1]);      StringTableEntry funcName = StringTable->insert(argv[0]);      Namespace::Entry *ent = object->getNamespace()->lookup(funcName);      if(ent == NULL)      {         //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId());         STR.clearFunctionOffset();         return ConsoleValueRef();      }      // Twiddle %this argument      ConsoleValue func_ident;      func_ident.setIntValue((S32)ident);      argv[1] = ConsoleValueRef::fromValue(&func_ident);      SimObject *save = gEvalState.thisObject;      gEvalState.thisObject = object;      ConsoleValueRef ret = ent->execute(argc, argv, &gEvalState);      gEvalState.thisObject = save;      // Twiddle it back      argv[1] = oldIdent;      return ret;   }   warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]);   STR.clearFunctionOffset();   return ConsoleValueRef();}ConsoleValueRef execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly){   if(argc < 2)   {      STR.clearFunctionOffset();      return ConsoleValueRef();   }   ConsoleStackFrameSaver stackSaver;   stackSaver.save();   if (object->getNamespace() || !thisCallOnly)   {      if (isMainThread())      {         return _internalExecute(object, argc, argv, thisCallOnly);      }      else      {         SimConsoleThreadExecCallback cb;         SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(argc, argv, true, &cb);         Sim::postEvent(object, evt, Sim::getCurrentTime());      }   }   warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]);   STR.clearFunctionOffset();   return ConsoleValueRef();}ConsoleValueRef execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly){   ConsoleStackFrameSaver stackSaver;   stackSaver.save();   StringStackConsoleWrapper args(argc, argv);   return execute(object, args.count(), args, thisCallOnly);}inline ConsoleValueRef _executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValueRef *argv){   const U32 maxArg = 12;   AssertWarn(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)");   AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more.");   return execute(obj, argc, argv);}//------------------------------------------------------------------------------inline ConsoleValueRef _executef(S32 checkArgc, S32 argc, ConsoleValueRef *argv){   const U32 maxArg = 10;   AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef()");   AssertFatal(argc <= maxArg, "Too many args passed to Con::_executef(). Please update the function to handle more.");   return execute(argc, argv);}//------------------------------------------------------------------------------bool isFunction(const char *fn){   const char *string = StringTable->lookup(fn);   if(!string)      return false;   else      return Namespace::global()->lookup(string) != NULL;}//------------------------------------------------------------------------------void setLogMode(S32 newMode){   if ((newMode & 0x3) != (consoleLogMode & 0x3)) {      if (newMode && !consoleLogMode) {         // Enabling logging when it was previously disabled.         newLogFile = true;      }      if ((consoleLogMode & 0x3) == 2) {         // Changing away from mode 2, must close logfile.         consoleLogFile.close();      }      else if ((newMode & 0x3) == 2) {#ifdef _XBOX         // Xbox is not going to support logging to a file. Use the OutputDebugStr         // log consumer         Platform::debugBreak();#endif         // Starting mode 2, must open logfile.         consoleLogFile.open(defLogFileName, Torque::FS::File::Write);      }      consoleLogMode = newMode;   }}Namespace *lookupNamespace(const char *ns){   if(!ns)      return Namespace::global();   return Namespace::find(StringTable->insert(ns));}bool linkNamespaces(const char *parent, const char *child){   Namespace *pns = lookupNamespace(parent);   Namespace *cns = lookupNamespace(child);   if(pns && cns)      return cns->classLinkTo(pns);   return false;}bool unlinkNamespaces(const char *parent, const char *child){   Namespace *pns = lookupNamespace(parent);   Namespace *cns = lookupNamespace(child);   if(pns == cns)   {      Con::warnf("Con::unlinkNamespaces - trying to unlink '%s' from itself, aborting.", parent);      return false;   }   if(pns && cns)      return cns->unlinkClass(pns);   return false;}bool classLinkNamespaces(Namespace *parent, Namespace *child){   if(parent && child)      return child->classLinkTo(parent);   return false;}void setData(S32 type, void *dptr, S32 index, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag){   ConsoleBaseType *cbt = ConsoleBaseType::getType(type);   AssertFatal(cbt, "Con::setData - could not resolve type ID!");   cbt->setData((void *) (((const char *)dptr) + index * cbt->getTypeSize()),argc, argv, tbl, flag);}const char *getData(S32 type, void *dptr, S32 index, const EnumTable *tbl, BitSet32 flag){   ConsoleBaseType *cbt = ConsoleBaseType::getType(type);   AssertFatal(cbt, "Con::getData - could not resolve type ID!");   return cbt->getData((void *) (((const char *)dptr) + index * cbt->getTypeSize()), tbl, flag);}const char *getFormattedData(S32 type, const char *data, const EnumTable *tbl, BitSet32 flag){   ConsoleBaseType *cbt = ConsoleBaseType::getType( type );   AssertFatal(cbt, "Con::getData - could not resolve type ID!");   // Datablock types are just a datablock    // name and don't ever need formatting.   if ( cbt->isDatablock() )      return data;   bool currWarn = gWarnUndefinedScriptVariables;   gWarnUndefinedScriptVariables = false;   const char* globalValue = Con::getVariable(data);   gWarnUndefinedScriptVariables = currWarn;   if (dStrlen(globalValue) > 0)      return globalValue;   void* variable = cbt->getNativeVariable();   if (variable)   {      Con::setData(type, variable, 0, 1, &data, tbl, flag);      const char* formattedVal = Con::getData(type, variable, 0, tbl, flag);      static const U32 bufSize = 2048;      char* returnBuffer = Con::getReturnBuffer(bufSize);      dSprintf(returnBuffer, bufSize, "%s\0", formattedVal );      cbt->deleteNativeVariable(variable);      return returnBuffer;   }   else      return data;}//------------------------------------------------------------------------------bool isCurrentScriptToolScript(){   // With a player build we ALWAYS return false#ifndef TORQUE_TOOLS   return false;#else   const StringTableEntry cbFullPath = CodeBlock::getCurrentCodeBlockFullPath();   if(cbFullPath == NULL)      return false;   const StringTableEntry exePath = Platform::getMainDotCsDir();   return dStrnicmp(exePath, cbFullPath, dStrlen(exePath)) == 0;#endif}//------------------------------------------------------------------------------StringTableEntry getModNameFromPath(const char *path){   if(path == NULL || *path == 0)      return NULL;   char buf[1024];   buf[0] = 0;   if(path[0] == '/' || path[1] == ':')   {      // It's an absolute path      const StringTableEntry exePath = Platform::getMainDotCsDir();      U32 len = dStrlen(exePath);      if(dStrnicmp(exePath, path, len) == 0)      {         const char *ptr = path + len + 1;         const char *slash = dStrchr(ptr, '/');         if(slash)         {            dStrncpy(buf, ptr, slash - ptr);            buf[slash - ptr] = 0;         }         else            return NULL;      }      else         return NULL;   }   else   {      const char *slash = dStrchr(path, '/');      if(slash)      {         dStrncpy(buf, path, slash - path);         buf[slash - path] = 0;      }      else         return NULL;   }   return StringTable->insert(buf);}void postConsoleInput( RawData data ){   // Schedule this to happen at the next time event.   ConsoleValue values[2];   ConsoleValueRef argv[2];   values[0].init();   values[0].setStringValue("eval");   values[1].init();   values[1].setStringValue((const char*)data.data);   argv[0].value = &values[0];   argv[1].value = &values[1];   Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, argv, false));}//------------------------------------------------------------------------------void pushInstantGroup( String name ){   sInstantGroupStack.push_back( gInstantGroup );   gInstantGroup = name;}void popInstantGroup(){   if( sInstantGroupStack.empty() )      gInstantGroup = String::EmptyString;   else   {      gInstantGroup = sInstantGroupStack.last();      sInstantGroupStack.pop_back();   }}typedef HashMap<StringTableEntry, StringTableEntry> typePathExpandoMap;static typePathExpandoMap PathExpandos;//-----------------------------------------------------------------------------void addPathExpando(const char* pExpandoName, const char* pPath){   // Sanity!   AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL.");   AssertFatal(pPath != NULL, "Expando path cannot be NULL.");   // Fetch expando name.   StringTableEntry expandoName = StringTable->insert(pExpandoName);   // Fetch the length of the path.   S32 pathLength = dStrlen(pPath);   char pathBuffer[1024];   // Sanity!   if (pathLength == 0 || pathLength >= sizeof(pathBuffer))   {      Con::warnf("Cannot add path expando '%s' with path '%s' as the path is an invalid length.", pExpandoName, pPath);      return;   }   // Strip repeat slashes.   if (!Con::stripRepeatSlashes(pathBuffer, pPath, sizeof(pathBuffer)))   {      Con::warnf("Cannot add path expando '%s' with path '%s' as the path is an invalid length.", pExpandoName, pPath);      return;   }   // Fetch new path length.   pathLength = dStrlen(pathBuffer);   // Sanity!   if (pathLength == 0)   {      Con::warnf("Cannot add path expando '%s' with path '%s' as the path is an invalid length.", pExpandoName, pPath);      return;   }   // Remove any terminating slash.   if (pathBuffer[pathLength - 1] == '/')      pathBuffer[pathLength - 1] = 0;   // Fetch expanded path.   StringTableEntry expandedPath = StringTable->insert(pathBuffer);   // Info.#if defined(TORQUE_DEBUG)   Con::printf("Adding path expando of '%s' as '%s'.", expandoName, expandedPath);#endif   // Find any existing path expando.   typePathExpandoMap::iterator expandoItr = PathExpandos.find(pExpandoName);   // Does the expando exist?   if (expandoItr != PathExpandos.end())   {      // Yes, so modify the path.      expandoItr->value = expandedPath;      return;   }   // Insert expando.   PathExpandos.insert(expandoName, expandedPath);}//-----------------------------------------------------------------------------StringTableEntry getPathExpando(const char* pExpandoName){   // Sanity!   AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL.");   // Fetch expando name.   StringTableEntry expandoName = StringTable->insert(pExpandoName);   // Find any existing path expando.   typePathExpandoMap::iterator expandoItr = PathExpandos.find(expandoName);   // Does the expando exist?   if (expandoItr != PathExpandos.end())   {      // Yes, so return it.      return expandoItr->value;   }   // Not found.   return NULL;}//-----------------------------------------------------------------------------void removePathExpando(const char* pExpandoName){   // Sanity!   AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL.");   // Fetch expando name.   StringTableEntry expandoName = StringTable->insert(pExpandoName);   // Find any existing path expando.   typePathExpandoMap::iterator expandoItr = PathExpandos.find(expandoName);   // Does the expando exist?   if (expandoItr == PathExpandos.end())   {      // No, so warn.#if defined(TORQUE_DEBUG)      Con::warnf("Removing path expando of '%s' but it does not exist.", expandoName);#endif      return;   }   // Info.#if defined(TORQUE_DEBUG)   Con::printf("Removing path expando of '%s' as '%s'.", expandoName, expandoItr->value);#endif   // Remove expando.   PathExpandos.erase(expandoItr);}//-----------------------------------------------------------------------------bool isPathExpando(const char* pExpandoName){   // Sanity!   AssertFatal(pExpandoName != NULL, "Expando name cannot be NULL.");   // Fetch expando name.   StringTableEntry expandoName = StringTable->insert(pExpandoName);   return PathExpandos.contains(expandoName);}//-----------------------------------------------------------------------------U32 getPathExpandoCount(void){   return PathExpandos.size();}//-----------------------------------------------------------------------------StringTableEntry getPathExpandoKey(U32 expandoIndex){   // Finish if index is out of range.   if (expandoIndex >= PathExpandos.size())      return NULL;   // Find indexed iterator.   typePathExpandoMap::iterator expandoItr = PathExpandos.begin();   while (expandoIndex > 0) { ++expandoItr; --expandoIndex; }   return expandoItr->key;}//-----------------------------------------------------------------------------StringTableEntry getPathExpandoValue(U32 expandoIndex){   // Finish if index is out of range.   if (expandoIndex >= PathExpandos.size())      return NULL;   // Find indexed iterator.   typePathExpandoMap::iterator expandoItr = PathExpandos.begin();   while (expandoIndex > 0) { ++expandoItr; --expandoIndex; }   return expandoItr->value;}//-----------------------------------------------------------------------------bool expandPath(char* pDstPath, U32 size, const char* pSrcPath, const char* pWorkingDirectoryHint, const bool ensureTrailingSlash){   char pathBuffer[2048];   const char* pSrc = pSrcPath;   char* pSlash;   // Fetch leading character.   const char leadingToken = *pSrc;   // Fetch following token.   const char followingToken = leadingToken != 0 ? pSrc[1] : 0;   // Expando.   if (leadingToken == '^')   {      // Initial prefix search.      const char* pPrefixSrc = pSrc + 1;      char* pPrefixDst = pathBuffer;      // Search for end of expando.      while (*pPrefixSrc != '/' && *pPrefixSrc != 0)      {         // Copy prefix character.         *pPrefixDst++ = *pPrefixSrc++;      }      // Yes, so terminate the expando string.      *pPrefixDst = 0;      // Fetch the expando path.      StringTableEntry expandoPath = getPathExpando(pathBuffer);      // Does the expando exist?      if (expandoPath == NULL)      {         // No, so error.         Con::errorf("expandPath() : Could not find path expando '%s' for path '%s'.", pathBuffer, pSrcPath);         // Are we ensuring the trailing slash?         if (ensureTrailingSlash)         {            // Yes, so ensure it.            Con::ensureTrailingSlash(pDstPath, pSrcPath);         }         else         {            // No, so just use the source path.            dStrcpy(pDstPath, pSrcPath);         }         return false;      }      // Skip the expando and the following slash.      pSrc += dStrlen(pathBuffer) + 1;      // Format the output path.      dSprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", expandoPath, pSrc);      // Are we ensuring the trailing slash?      if (ensureTrailingSlash)      {         // Yes, so ensure it.         Con::ensureTrailingSlash(pathBuffer, pathBuffer);      }      // Strip repeat slashes.      Con::stripRepeatSlashes(pDstPath, pathBuffer, size);      return true;   }   // Script-Relative.   if (leadingToken == '.')   {      // Fetch the code-block file-path.      const StringTableEntry codeblockFullPath = CodeBlock::getCurrentCodeBlockFullPath();      // Do we have a code block full path?      if (codeblockFullPath == NULL)      {         // No, so error.         Con::errorf("expandPath() : Could not find relative path from code-block for path '%s'.", pSrcPath);         // Are we ensuring the trailing slash?         if (ensureTrailingSlash)         {            // Yes, so ensure it.            Con::ensureTrailingSlash(pDstPath, pSrcPath);         }         else         {            // No, so just use the source path.            dStrcpy(pDstPath, pSrcPath);         }         return false;      }      // Yes, so use it as the prefix.      dStrncpy(pathBuffer, codeblockFullPath, sizeof(pathBuffer) - 1);      // Find the final slash in the code-block.      pSlash = dStrrchr(pathBuffer, '/');      // Is this a parent directory token?      if (followingToken == '.')      {         // Yes, so terminate after the slash so we include it.         pSlash[1] = 0;      }      else      {         // No, it's a current directory token so terminate at the slash so we don't include it.         pSlash[0] = 0;         // Skip the current directory token.         pSrc++;      }      // Format the output path.      dStrncat(pathBuffer, "/", sizeof(pathBuffer) - 1 - strlen(pathBuffer));      dStrncat(pathBuffer, pSrc, sizeof(pathBuffer) - 1 - strlen(pathBuffer));      // Are we ensuring the trailing slash?      if (ensureTrailingSlash)      {         // Yes, so ensure it.         Con::ensureTrailingSlash(pathBuffer, pathBuffer);      }      // Strip repeat slashes.      Con::stripRepeatSlashes(pDstPath, pathBuffer, size);      return true;   }   // All else.   //Using a special case here because the code below barfs on trying to build a full path for apk reading#ifdef TORQUE_OS_ANDROID   if (leadingToken == '/' || strstr(pSrcPath, "/") == NULL)      Platform::makeFullPathName(pSrcPath, pathBuffer, sizeof(pathBuffer), pWorkingDirectoryHint);   else      dSprintf(pathBuffer, sizeof(pathBuffer), "/%s", pSrcPath);#else   Platform::makeFullPathName(pSrcPath, pathBuffer, sizeof(pathBuffer), pWorkingDirectoryHint);#endif   // Are we ensuring the trailing slash?   if (ensureTrailingSlash)   {      // Yes, so ensure it.      Con::ensureTrailingSlash(pathBuffer, pathBuffer);   }   // Strip repeat slashes.   Con::stripRepeatSlashes(pDstPath, pathBuffer, size);   return true;}//-----------------------------------------------------------------------------bool isBasePath(const char* SrcPath, const char* pBasePath){   char expandBuffer[1024];   Con::expandPath(expandBuffer, sizeof(expandBuffer), SrcPath);   return dStrnicmp(pBasePath, expandBuffer, dStrlen(pBasePath)) == 0;}//-----------------------------------------------------------------------------void collapsePath(char* pDstPath, U32 size, const char* pSrcPath, const char* pWorkingDirectoryHint){   // Check path against expandos.  If there are multiple matches, choose the   // expando that produces the shortest relative path.   char pathBuffer[2048];   // Fetch expando count.   const U32 expandoCount = getPathExpandoCount();   // Iterate expandos.   U32 expandoRelativePathLength = U32_MAX;   for (U32 expandoIndex = 0; expandoIndex < expandoCount; ++expandoIndex)   {      // Fetch expando value (path).      StringTableEntry expandoValue = getPathExpandoValue(expandoIndex);      // Skip if not the base path.      if (!isBasePath(pSrcPath, expandoValue))         continue;      // Fetch path relative to expando path.      StringTableEntry relativePath = Platform::makeRelativePathName(pSrcPath, expandoValue);      // If the relative path is simply a period      if (relativePath[0] == '.')         relativePath++;      if (dStrlen(relativePath) > expandoRelativePathLength)      {         // This expando covers less of the path than any previous one found.         // We will keep the previous one.         continue;      }      // Keep track of the relative path length      expandoRelativePathLength = dStrlen(relativePath);      // Fetch expando key (name).      StringTableEntry expandoName = getPathExpandoKey(expandoIndex);      // Format against expando.      dSprintf(pathBuffer, sizeof(pathBuffer), "^%s/%s", expandoName, relativePath);   }   // Check if we've found a suitable expando   if (expandoRelativePathLength != U32_MAX)   {      // Strip repeat slashes.      Con::stripRepeatSlashes(pDstPath, pathBuffer, size);      return;   }   // Fetch the working directory.   StringTableEntry workingDirectory = pWorkingDirectoryHint != NULL ? pWorkingDirectoryHint : Platform::getCurrentDirectory();   // Fetch path relative to current directory.   StringTableEntry relativePath = Platform::makeRelativePathName(pSrcPath, workingDirectory);   // If the relative path is simply a period   if (relativePath[0] == '.'  && relativePath[1] != '.')      relativePath++;   // Format against expando.   dSprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", workingDirectory, relativePath);   // Strip repeat slashes.   Con::stripRepeatSlashes(pDstPath, pathBuffer, size);}void ensureTrailingSlash(char* pDstPath, const char* pSrcPath){   // Copy to target.   dStrcpy(pDstPath, pSrcPath);   // Find trailing character index.   S32 trailIndex = dStrlen(pDstPath);   // Ignore if empty string.   if (trailIndex == 0)      return;   // Finish if the trailing slash already exists.   if (pDstPath[trailIndex - 1] == '/')      return;   // Add trailing slash.   pDstPath[trailIndex++] = '/';   pDstPath[trailIndex] = 0;}//-----------------------------------------------------------------------------StringTableEntry getDSOPath(const char *scriptPath){#ifndef TORQUE2D_TOOLS_FIXME   // [tom, 11/17/2006] Force old behavior for the player. May not want to do this.   const char *slash = dStrrchr(scriptPath, '/');   if (slash != NULL)      return StringTable->insertn(scriptPath, slash - scriptPath, true);   slash = dStrrchr(scriptPath, ':');   if (slash != NULL)      return StringTable->insertn(scriptPath, (slash - scriptPath) + 1, true);   return "";#else   char relPath[1024], dsoPath[1024];   bool isPrefs = false;   // [tom, 11/17/2006] Prefs are handled slightly differently to avoid dso name clashes   StringTableEntry prefsPath = Platform::getPrefsPath();   if (dStrnicmp(scriptPath, prefsPath, dStrlen(prefsPath)) == 0)   {      relPath[0] = 0;      isPrefs = true;   }   else   {      StringTableEntry strippedPath = Platform::stripBasePath(scriptPath);      dStrcpy(relPath, strippedPath);      char *slash = dStrrchr(relPath, '/');      if (slash)         *slash = 0;   }   const char *overridePath;   if (!isPrefs)      overridePath = Con::getVariable("$Scripts::OverrideDSOPath");   else      overridePath = prefsPath;   if (overridePath && *overridePath)      Platform::makeFullPathName(relPath, dsoPath, sizeof(dsoPath), overridePath);   else   {      char t[1024];      dSprintf(t, sizeof(t), "compiledScripts/%s", relPath);      Platform::makeFullPathName(t, dsoPath, sizeof(dsoPath), Platform::getPrefsPath());   }   return StringTable->insert(dsoPath);#endif}//-----------------------------------------------------------------------------bool stripRepeatSlashes(char* pDstPath, const char* pSrcPath, S32 dstSize){   // Note original destination.   char* pOriginalDst = pDstPath;   // Reset last source character.   char lastSrcChar = 0;   // Search source...   while (dstSize > 0)   {      // Fetch characters.      const char srcChar = *pSrcPath++;      // Do we have a repeat slash?      if (srcChar == '/' && lastSrcChar == '/')      {         // Yes, so skip it.         continue;      }      // No, so copy character.      *pDstPath++ = srcChar;      // Finish if end of source.      if (srcChar == 0)         return true;      // Reduce room left in destination.      dstSize--;      // Set last character.      lastSrcChar = srcChar;   }   // Terminate the destination string as we ran out of room.   *pOriginalDst = 0;   // Fail!   return false;}} // end of Console namespace#endif//=============================================================================//    API.//=============================================================================// MARK: ---- API ----//-----------------------------------------------------------------------------DefineEngineFunction( log, void, ( const char* message ),,   "@brief Logs a message to the console.\n\n"   "@param message The message text.\n"   "@note By default, messages will appear white in the console.\n"   "@ingroup Logging"){   Con::printf( "%s", message );}//-----------------------------------------------------------------------------DefineEngineFunction( logError, void, ( const char* message ),,   "@brief Logs an error message to the console.\n\n"   "@param message The message text.\n"   "@note By default, errors will appear red in the console.\n"   "@ingroup Logging"){   Con::errorf( "%s", message );}//-----------------------------------------------------------------------------DefineEngineFunction( logWarning, void, ( const char* message ),,   "@brief Logs a warning message to the console.\n\n"   "@param message The message text.\n\n"   "@note By default, warnings will appear turquoise in the console.\n"   "@ingroup Logging"){   Con::warnf( "%s", message );}//------------------------------------------------------------------------------extern ConsoleValueStack CSTK;ConsoleValueRef::ConsoleValueRef(const ConsoleValueRef &ref){   value = ref.value;}ConsoleValueRef& ConsoleValueRef::operator=(const ConsoleValueRef &newValue){   value = newValue.value;   return *this;}ConsoleValueRef& ConsoleValueRef::operator=(const char *newValue){   AssertFatal(value, "value should not be NULL");   value->setStringValue(newValue);   return *this;}ConsoleValueRef& ConsoleValueRef::operator=(S32 newValue){   AssertFatal(value, "value should not be NULL");   value->setIntValue(newValue);   return *this;}ConsoleValueRef& ConsoleValueRef::operator=(U32 newValue){   AssertFatal(value, "value should not be NULL");   value->setIntValue(newValue);   return *this;}ConsoleValueRef& ConsoleValueRef::operator=(F32 newValue){   AssertFatal(value, "value should not be NULL");   value->setFloatValue(newValue);   return *this;}ConsoleValueRef& ConsoleValueRef::operator=(F64 newValue){   AssertFatal(value, "value should not be NULL");   value->setFloatValue(newValue);   return *this;}//------------------------------------------------------------------------------StringStackWrapper::StringStackWrapper(int targc, ConsoleValueRef targv[]){   argv = new const char*[targc];   argc = targc;   for (int i=0; i<targc; i++)   {      argv[i] = dStrdup(targv[i]);   }}StringStackWrapper::~StringStackWrapper(){   for (int i=0; i<argc; i++)   {      dFree(argv[i]);   }   delete[] argv;}StringStackConsoleWrapper::StringStackConsoleWrapper(int targc, const char** targ){   argv = new ConsoleValueRef[targc];   argvValue = new ConsoleValue[targc];   argc = targc;   for (int i=0; i<targc; i++) {      argvValue[i].init();      argv[i].value = &argvValue[i];      argvValue[i].setStackStringValue(targ[i]);   }}StringStackConsoleWrapper::~StringStackConsoleWrapper(){   for (int i=0; i<argc; i++)   {      argv[i] = 0;   }   delete[] argv;   delete[] argvValue;}//------------------------------------------------------------------------------S32 ConsoleValue::getSignedIntValue(){   if(type <= TypeInternalString)      return (S32)fval;   else      return dAtoi(Con::getData(type, dataPtr, 0, enumTable));}U32 ConsoleValue::getIntValue(){   if(type <= TypeInternalString)      return ival;   else      return dAtoi(Con::getData(type, dataPtr, 0, enumTable));}F32 ConsoleValue::getFloatValue(){   if(type <= TypeInternalString)      return fval;   else      return dAtof(Con::getData(type, dataPtr, 0, enumTable));}const char *ConsoleValue::getStringValue(){   if(type == TypeInternalString || type == TypeInternalStackString)      return sval;   else if (type == TypeInternalStringStackPtr)      return STR.mBuffer + (uintptr_t)sval;   else   {      // We need a string representation, so lets create one      const char *internalValue = NULL;      if(type == TypeInternalFloat)         internalValue = Con::getData(TypeF32, &fval, 0);      else if(type == TypeInternalInt)         internalValue = Con::getData(TypeS32, &ival, 0);      else         return Con::getData(type, dataPtr, 0, enumTable); // We can't save sval here since it is the same as dataPtr      if (!internalValue)         return "";      U32 stringLen = dStrlen(internalValue);      U32 newLen = ((stringLen + 1) + 15) & ~15; // pad upto next cache line	         if (bufferLen == 0)         sval = (char *) dMalloc(newLen);      else if(newLen > bufferLen)         sval = (char *) dRealloc(sval, newLen);      dStrcpy(sval, internalValue);      bufferLen = newLen;      return sval;   }}StringStackPtr ConsoleValue::getStringStackPtr(){   if (type == TypeInternalStringStackPtr)      return (uintptr_t)sval;   else      return (uintptr_t)-1;}bool ConsoleValue::getBoolValue(){   if(type == TypeInternalString || type == TypeInternalStackString || type == TypeInternalStringStackPtr)      return dAtob(getStringValue());   if(type == TypeInternalFloat)      return fval > 0;   else if(type == TypeInternalInt)      return ival > 0;   else {      const char *value = Con::getData(type, dataPtr, 0, enumTable);      return dAtob(value);   }}void ConsoleValue::setIntValue(S32 val){   setFloatValue(val);}void ConsoleValue::setIntValue(U32 val){   if(type <= TypeInternalString)   {      fval = (F32)val;      ival = val;      if(bufferLen > 0)      {         dFree(sval);         bufferLen = 0;      }      sval = typeValueEmpty;      type = TypeInternalInt;   }   else   {      const char *dptr = Con::getData(TypeS32, &val, 0);      Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);   }}void ConsoleValue::setBoolValue(bool val){   return setIntValue(val ? 1 : 0);}void ConsoleValue::setFloatValue(F32 val){   if(type <= TypeInternalString)   {      fval = val;      ival = static_cast<U32>(val);      if(bufferLen > 0)      {         dFree(sval);         bufferLen = 0;      }      sval = typeValueEmpty;      type = TypeInternalFloat;   }   else   {      const char *dptr = Con::getData(TypeF32, &val, 0);      Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);   }}//------------------------------------------------------------------------------ConsoleValueRef _BaseEngineConsoleCallbackHelper::_exec(){   ConsoleValueRef returnValue;   if( mThis )   {      // Cannot invoke callback until object has been registered      if (mThis->isProperlyAdded()) {         returnValue = Con::_internalExecute( mThis, mArgc, mArgv, false );      } else {         STR.clearFunctionOffset();         returnValue = ConsoleValueRef();      }   }   else      returnValue = Con::_internalExecute( mArgc, mArgv );   mArgc = mInitialArgc; // reset args   return returnValue;}ConsoleValueRef _BaseEngineConsoleCallbackHelper::_execLater(SimConsoleThreadExecEvent *evt){   mArgc = mInitialArgc; // reset args   Sim::postEvent((SimObject*)Sim::getRootGroup(), evt, Sim::getCurrentTime());   return evt->getCB().waitForResult();}//------------------------------------------------------------------------------void ConsoleStackFrameSaver::save(){   CSTK.pushFrame();   STR.pushFrame();   mSaved = true;}void ConsoleStackFrameSaver::restore(){   if (mSaved)   {      CSTK.popFrame();      STR.popFrame();   }}
 |