//----------------------------------------------------------------------------- // Copyright (c) 2013 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 "console/console.h" #include "console/consoleInternal.h" #include "console/ast.h" #include "io/resource/resourceManager.h" #include "io/fileStream.h" #include "console/compiler.h" #if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_OSX) #include #include #endif // Buffer for expanding script filenames. static char pathBuffer[1024]; static U32 execDepth = 0; extern S32 QSORT_CALLBACK ACRCompare(const void *aptr, const void *bptr); ConsoleFunctionGroupBegin(MetaScripting, "Functions that let you manipulate the scripting engine programmatically."); /*! @defgroup MetaScriptingFunctions Meta-Scripting @ingroup TorqueScriptFunctions @{ */ /*! Use the call function to dynamically build and call a function. @param funcName A string containing the unadorned name of a function to be executed. @param args .. - Any arguments that should be passed to the function. @return Returns a string containing the results from the function that is built and called. @sa eval */ ConsoleFunctionWithDocs(call, ConsoleString, 2, 0, ( funcName, [args ... ]?)) { return Con::execute(argc - 1, argv + 1); } static StringTableEntry getDSOPath(const char *scriptPath) { const char *slash = dStrrchr(scriptPath, '/'); return StringTable->insertn(scriptPath, (U32)(slash - scriptPath), true); } /*! Returns the DSO path of the given filename @param scriptFileName A string contains the file to search for @return Returns either the requested pathname as a string, or an empty string if not found. */ ConsoleFunctionWithDocs(getDSOPath, ConsoleString, 2, 2, (scriptFileName)) { Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]); const char *filename = getDSOPath(pathBuffer); if(filename == NULL || *filename == 0) return ""; return filename; } /*! Use the compile function to pre-compile a script file without executing the contents. @param fileName A path to the script to compile. @return Returns 1 if the script compiled without errors and 0 if the file did not compile correctly or if the path is wrong. Also, ff the path is invalid, an error will print to the console. @sa exec */ ConsoleFunctionWithDocs(compile, ConsoleBool, 2, 2, ( fileName )) { TORQUE_UNUSED( argc ); char nameBuffer[512]; char* script = NULL; U32 scriptSize = 0; FileTime comModifyTime, scrModifyTime; Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]); // Figure out where to put DSOs StringTableEntry dsoPath = getDSOPath(pathBuffer); // If the script file extention is '.ed.cs' then compile it to a different compiled extention bool isEditorScript = false; const char *ext = dStrrchr( pathBuffer, '.' ); if( ext && ( dStricmp( ext, ".cs" ) == 0 ) ) { const char* ext2 = ext - 3; if( dStricmp( ext2, ".ed.cs" ) == 0 ) isEditorScript = true; } else if( ext && ( dStricmp( ext, ".gui" ) == 0 ) ) { const char* ext2 = ext - 3; if( dStricmp( ext2, ".ed.gui" ) == 0 ) isEditorScript = true; } const char *filenameOnly = dStrrchr(pathBuffer, '/'); if(filenameOnly) ++filenameOnly; else filenameOnly = pathBuffer; if( isEditorScript ) dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".edso", NULL); else dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".dso", NULL); ResourceObject *rScr = ResourceManager->find(pathBuffer); ResourceObject *rCom = ResourceManager->find(nameBuffer); if(rCom) rCom->getFileTimes(NULL, &comModifyTime); if(rScr) rScr->getFileTimes(NULL, &scrModifyTime); Stream *s = ResourceManager->openStream(pathBuffer); if(s) { scriptSize = ResourceManager->getSize(pathBuffer); script = new char [scriptSize+1]; s->read(scriptSize, script); ResourceManager->closeStream(s); script[scriptSize] = 0; } if (!scriptSize || !script) { delete [] script; Con::errorf(ConsoleLogEntry::Script, "compile: invalid script file %s.", pathBuffer); return false; } // compile this baddie. // -Mat reducing console noise #if defined(TORQUE_DEBUG) Con::printf("Compiling %s...", pathBuffer); #endif CodeBlock *code = new CodeBlock(); code->compile(nameBuffer, pathBuffer, script); delete code; code = NULL; delete[] script; return true; } /*! */ ConsoleFunctionWithDocs(compilePath, ConsoleString, 2, 2, ( path )) { if ( !Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]) ) return "-1 0"; const char *compileArgs[2] = { "compile", NULL }; S32 failedScripts = 0; S32 totalScripts = 0; ResourceObject *match = NULL; while ( (match = ResourceManager->findMatch( pathBuffer, &compileArgs[1], match )) ) { if ( !ccompile( NULL, 1, compileArgs ) ) failedScripts++; totalScripts++; } char* result = Con::getReturnBuffer(32); dSprintf( result, 32, "%d %d", failedScripts, totalScripts ); return result; } static bool scriptExecutionEcho = false; /*! Whether to echo script file execution or not. */ ConsoleFunctionWithDocs(setScriptExecEcho, ConsoleVoid, 2, 2, (echo?)) { scriptExecutionEcho = dAtob(argv[1]); } /*! Use the exec function to compile and execute a normal script, or a special journal script. If $Pref::ignoreDSOs is set to true, the system will use .cs before a .dso file if both are found. @param fileName A string containing a path to the script to be compiled and executed. @param nocalls A boolean value. If this value is set to true, then all function calls encountered while executing the script file will be skipped and not called. This allows us to re-define function definitions found in a script file, without re-executing other worker scripts in the same file. @param journalScript A boolean value. If this value is set tot true, and if a journal is being played, the engine will attempt to read this script from the journal stream. If no journal is playing, this field is ignored. @return Returns true if the file compiled and executed w/o errors, false otherwise. @sa compile */ ConsoleFunctionWithDocs(exec, ConsoleBool, 2, 4, ( fileName, [nocalls]?, [journalScript ]?)) { execDepth++; #ifdef TORQUE_ALLOW_JOURNALING bool journal = false; if(journalDepth >= execDepth) journalDepth = execDepth + 1; else journal = true; #endif //TORQUE_ALLOW_JOURNALING bool noCalls = false; bool ret = false; if(argc >= 3 && dAtoi(argv[2])) noCalls = true; #ifdef TORQUE_ALLOW_JOURNALING if(argc >= 4 && dAtoi(argv[3]) && !journal) { journal = true; journalDepth = execDepth; } #endif //TORQUE_ALLOW_JOURNALING // Determine the filename we actually want... Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]); // Figure out where to put DSOs StringTableEntry dsoPath = getDSOPath(pathBuffer); const char *ext = dStrrchr(pathBuffer, '.'); if(!ext) { // We need an extension! Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", pathBuffer); execDepth--; return false; } // Check Editor Extensions bool isEditorScript = false; #ifdef TORQUE_ALLOW_DSO_GENERATION // 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; } #endif //TORQUE_ALLOW_DSO_GENERATION // rdbhack: if we can't find the script file in the game directory, look for it // in the Application Data directory. This makes it possible to keep the user // ignorant of where the files are actually saving to, thus eliminating the need // for the script functions: execPrefs, getUserDataDirectory, etc. // // This works because we know that script files located in the prefs path will // not have compiled versions (it checks for this further down). Otherwise this // would be a big problem! StringTableEntry scriptFileName = StringTable->EmptyString; //Luma : This is redundant, we wont be building dso's on the device - //plus saving dso to the user directory when attempting build for the //release tests on iPhone is irrelevant. #ifdef TORQUE_ALLOW_DSO_GENERATION if(!ResourceManager->find(pathBuffer)) { // NOTE: this code is pretty much a duplication of code much further down in this // function... // our work just got a little harder.. if we couldn't find the .cs, then we need to // also look for the .dso BEFORE we can try the prefs path.. UGH const char *filenameOnly = dStrrchr(pathBuffer, '/'); if(filenameOnly) ++filenameOnly; else filenameOnly = pathBuffer; // we could skip this step and rid ourselves of a bunch of nonsense but we can't be // certain the dso path is the same as the path given to use in scriptFileNameBuffer char pathAndFilename[1024]; Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath); char nameBuffer[1024]; if( isEditorScript ) // this should never be the case since we are a PLAYER not a TOOL, but you never know dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL); else dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL); if(!ResourceManager->find(nameBuffer)) scriptFileName = Platform::getPrefsPath(Platform::stripBasePath(pathBuffer)); else scriptFileName = StringTable->insert(pathBuffer); } else scriptFileName = StringTable->insert(pathBuffer); #else //TORQUE_ALLOW_DSO_GENERATION //Luma : Just insert the file name. scriptFileName = StringTable->insert(pathBuffer); #endif //TORQUE_ALLOW_DSO_GENERATION //Luma : Something screwed up so get out early if(scriptFileName == NULL || *scriptFileName == 0) { execDepth--; return false; } #ifdef TORQUE_ALLOW_JOURNALING bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs"); #else bool compiled = dStricmp(ext, ".mis") && !Con::getBoolVariable("Scripts::ignoreDSOs"); #endif //TORQUE_ALLOW_JOURNALING // [tom, 12/5/2006] stripBasePath() messes up if the filename is not in the exe // path, current directory or prefs path. Thus, getDSOFilename() will also mess // up and so this allows the scripts to still load but without a DSO. if(Platform::isFullPath(Platform::stripBasePath(pathBuffer))) 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. #ifdef TORQUE_ALLOW_DSO_GENERATION // Is this a file we should compile? (anything in the prefs path should not be compiled) StringTableEntry prefsPath = Platform::getPrefsPath(); if( dStrlen(prefsPath) > 0 && dStrnicmp(scriptFileName, prefsPath, dStrlen(prefsPath)) == 0) compiled = false; #endif //TORQUE_ALLOW_DSO_GENERATION // If we're in a journaling mode, then we will read the script // from the journal file. #ifdef TORQUE_ALLOW_JOURNALING if(journal && Game->isJournalReading()) { char fileNameBuf[256]; bool fileRead; U32 fileSize; Game->getJournalStream()->readString(fileNameBuf); Game->getJournalStream()->read(&fileRead); if(!fileRead) { Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf); execDepth--; return false; } Game->journalRead(&fileSize); char *script = new char[fileSize + 1]; Game->journalRead(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; } #endif //TORQUE_ALLOW_JOURNALING // Ok, we let's try to load and compile the script. ResourceObject *rScr = ResourceManager->find(scriptFileName); ResourceObject *rCom = NULL; char nameBuffer[512]; char* script = NULL; U32 scriptSize = 0; U32 version; Stream *compiledStream = NULL; FileTime comModifyTime, scrModifyTime; // Check here for .edso //bool edso = false; //if( dStricmp( ext, ".edso" ) == 0 && rScr ) //{ // edso = true; // rCom = rScr; // rScr = NULL; // rCom->getFileTimes( NULL, &comModifyTime ); // 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; //remove the / at the front 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); rCom = ResourceManager->find(nameBuffer); if(rCom) rCom->getFileTimes(NULL, &comModifyTime); if(rScr) rScr->getFileTimes(NULL, &scrModifyTime); } // 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. if((compiled && rCom) && (!rScr || Platform::compareFileTimes(comModifyTime, scrModifyTime) >= 0)) { compiledStream = ResourceManager->openStream(nameBuffer); if (compiledStream) { // Check the version! compiledStream->read(&version); if(version != DSO_VERSION) { Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, DSO_VERSION); ResourceManager->closeStream(compiledStream); compiledStream = NULL; } } } #ifdef TORQUE_ALLOW_JOURNALING // If we're journalling, let's write some info out. if(journal && Game->isJournalWriting()) Game->getJournalStream()->writeString(scriptFileName); #endif //TORQUE_ALLOW_JOURNALING if(rScr && !compiledStream) { // If we have source but no compiled version, then we need to compile // (and journal as we do so, if that's required). //Con::errorf( "No DSO found! : %s", scriptFileName ); Stream *s = ResourceManager->openStream(scriptFileName); #ifdef TORQUE_ALLOW_JOURNALING if(journal && Game->isJournalWriting()) Game->getJournalStream()->write(bool(s != NULL)); #endif //TORQUE_ALLOW_JOURNALING if(s) { scriptSize = ResourceManager->getSize(scriptFileName); script = new char [scriptSize+1]; s->read(scriptSize, script); #ifdef TORQUE_ALLOW_JOURNALING if(journal && Game->isJournalWriting()) { Game->journalWrite(scriptSize); Game->journalWrite(scriptSize, script); } #endif //TORQUE_ALLOW_JOURNALING ResourceManager->closeStream(s); script[scriptSize] = 0; } if (!scriptSize || !script) { delete [] script; Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName); execDepth--; return false; } //Luma: Sven - // no dsos in the editor, seems to fail with so many console changes and version crap. Leaving it to // work with cs files as is, as they are included with the source either way. //Also, no DSO generation on iPhone #if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) if(false) #else if(compiled) #endif { // compile this baddie. #if defined(TORQUE_DEBUG) Con::printf("Compiling %s...", scriptFileName); #endif CodeBlock *code = new CodeBlock(); code->compile(nameBuffer, scriptFileName, script); delete code; code = NULL; compiledStream = ResourceManager->openStream(nameBuffer); if(compiledStream) { compiledStream->read(&version); } else { // We have to exit out here, as otherwise we get double error reports. delete [] script; execDepth--; return false; } } } else { #ifdef TORQUE_ALLOW_JOURNALING if(journal && Game->isJournalWriting()) Game->getJournalStream()->write(bool(false)); #endif //TORQUE_ALLOW_JOURNALING } //Luma : Load compiled script here 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. //Luma: Profile script executions F32 st1 = (F32)Platform::getRealMilliseconds(); CodeBlock *code = new CodeBlock; code->read(scriptFileName, *compiledStream); ResourceManager->closeStream(compiledStream); code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0); F32 et1 = (F32)Platform::getRealMilliseconds(); F32 etf = et1 - st1; if ( scriptExecutionEcho ) Con::printf("Loaded compiled script %s. Took %.0f ms", scriptFileName, etf); ret = true; } else if(rScr) //Luma : Load normal cs file here. { // No compiled script, let's just try executing it // directly... this is either a mission file, or maybe // we're on a readonly volume. CodeBlock *newCodeBlock = new CodeBlock(); StringTableEntry name = StringTable->insert(scriptFileName); //Luma: Profile script executions F32 st1 = (F32)Platform::getRealMilliseconds(); newCodeBlock->compileExec(name, script, noCalls, 0); F32 et1 = (F32)Platform::getRealMilliseconds(); F32 etf = et1 - st1; if ( scriptExecutionEcho ) Con::printf("Executed %s. Took %.0f ms", scriptFileName, etf); ret = true; } else { // Don't have anything. Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", pathBuffer); ret = false; } delete [] script; execDepth--; return ret; } /*! Use the eval function to execute any valid script statement. If you choose to eval a multi-line statement, be sure that there are no comments or comment blocks embedded in the script string. @param script A string containing a valid script statement. This may be a single line statement or multiple lines concatenated together with new-line characters. @return Returns the result of executing the script statement. @sa call */ ConsoleFunctionWithDocs(eval, ConsoleString, 2, 2, ( script )) { TORQUE_UNUSED( argc ); return Con::evaluate(argv[1], false, NULL); } /*! Grabs the relevant data for the variable represented by the given string @param varName A String representing the variable to check @return Returns a string containing component data for the requested variable or an empty string if not found. */ ConsoleFunctionWithDocs(getVariable, ConsoleString, 2, 2, (string varName)) { return Con::getVariable(argv[1]); } /*! Checks whether given name represents a current valid function. @param funcName The name of the function to check. @return Returns either true if the string represents a valid function or false if not. */ ConsoleFunctionWithDocs(isFunction, ConsoleBool, 2, 2, (string funcName)) { return Con::isFunction(argv[1]); } /*! Checks whether the given method name represents a valid method within the given namespace. @param namespace A string representing the namespace in which the method should reside. @param method The name of the method in question. @return Returns a boolean value which is true if the given mathod name represents a valid method in the namespace and false otherwise. */ ConsoleFunctionWithDocs(isMethod, ConsoleBool, 3, 3, (string namespace, string method)) { Namespace* ns = Namespace::find( StringTable->insert( argv[1] ) ); Namespace::Entry* nse = ns->lookup( StringTable->insert( argv[2] ) ); if( !nse ) return false; return true; } /*! Attempts to extract a mod directory from path. Returns empty string on failure. */ ConsoleFunctionWithDocs(getModNameFromPath, ConsoleString, 2, 2, (string path)) { StringTableEntry modPath = Con::getModNameFromPath(argv[1]); return modPath ? modPath : ""; } //---------------------------------------------------------------- /*! */ ConsoleFunctionWithDocs(getPrefsPath, ConsoleString, 1, 2, ([fileName]?)) { const char *filename = Platform::getPrefsPath(argc > 1 ? argv[1] : NULL); if(filename == NULL || *filename == 0) return ""; return filename; } /*! */ ConsoleFunctionWithDocs(execPrefs, ConsoleBool, 2, 4, (fileName, [nocalls]?, [journalScript]?)) { const char *filename = Platform::getPrefsPath(argv[1]); if (filename == NULL || *filename == 0) return false; if ( !Platform::isFile(filename) ) return false; argv[0] = "exec"; argv[1] = filename; return dAtob(Con::execute(argc, argv)); } /*! Use the export function to save all global variables matching the specified name pattern in wildCard to a file, either appending to that file or over-writing it. @param wildCard A string identifying what variable(s) to export. All characters used to create a global are allowed and the special symbol \*\, meaning 0 or more instances of any character. @param fileName A string containing a path to a file in which to save the globals and their definitions. @param append A boolean value. If this value is true, the file will be appended to if it exists, otherwise it will be created/over-written. @return No return value */ ConsoleFunctionWithDocs(export, ConsoleVoid, 2, 4, ( wildCard, [fileName]?, [append]?)) { // Fetch the wildcard. const char* pWildcard = argv[1]; // Fetch the filename. const char* pFilename = NULL; if ( argc >= 3 ) { Con::expandPath( pathBuffer, sizeof(pathBuffer), argv[2] ); pFilename = pathBuffer; } // Fetch append flag. const bool append = argc >= 4 ? dAtob(argv[3] ) : false; // Export the variables. gEvalState.globalVars.exportVariables( pWildcard, pFilename, append ); } /*! Use the deleteVariables function to delete any global variable matching the wildCard statement. @param wildCard A string identifying what variable(s) to delete. All characters used to create a global are allowed and the special symbol \*\, meaning 0 or more instances of any character. @return No return value */ ConsoleFunctionWithDocs(deleteVariables, ConsoleVoid, 2, 2, ( wildCard )) { TORQUE_UNUSED( argc ); gEvalState.globalVars.deleteVariables(argv[1]); } //---------------------------------------------------------------- /*! Use the trace function to enable (or disable) function call tracing. If enabled, tracing will print a message every time a function is entered, showing what arguments it received, and it will print a message every time a function is exited, showing the return value (or last value of last statement) for that function. @param enable A boolean value. If set to true, tracing is enabled, otherwise it is disabled. @return No return value */ ConsoleFunctionWithDocs(trace, ConsoleVoid, 2, 2, ( enable )) { TORQUE_UNUSED( argc ); gEvalState.traceOn = dAtob(argv[1]); Con::printf("Console trace is %s", gEvalState.traceOn ? "on." : "off."); } //---------------------------------------------------------------- #if defined(TORQUE_DEBUG) || defined(INTERNAL_RELEASE) /*! Use the debug function to cause the engine to issue a debug break and to break into an active debugger. For this to work, the engine must have been compiled with either TORQUE_DEBUG, or INTERNAL_RELEASE defined @return No return value. */ ConsoleFunctionWithDocs(debug, ConsoleVoid, 1, 1, ()) { TORQUE_UNUSED( argc ); TORQUE_UNUSED( argv ); Platform::debugBreak(); } #endif #if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_OSX) //Some code for allowing torsion to connect, this code returns the ipads local ip address //code was obtained from http://blog.zachwaugh.com/post/309927273/programmatically-retrieving-ip-address-of-iphone //adapted to iT2D by me /*! Gets the Apple hardware local IP on wifi. Should work on OS X and iOS */ ConsoleFunctionWithDocs(getAppleDeviceIPAddress, ConsoleString, 1, 1, ()) { char *address = Con::getReturnBuffer(32); dStrcpy(address, "error"); struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; // retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { // Loop through linked list of interfaces temp_addr = interfaces; while(temp_addr != NULL) { if(temp_addr->ifa_addr->sa_family == AF_INET) { // Check if interface is en0 which is the wifi connection on the iPhone // Note: Could be different on MacOSX and simulator and may need modifying if(dStrcmp(temp_addr->ifa_name, "en0") == 0) { dStrcpy(address, inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)); } } temp_addr = temp_addr->ifa_next; } } // Free memory freeifaddrs(interfaces); return address; } #endif //---------------------------------------------------------------- /*! */ ConsoleFunctionWithDocs( enumerateConsoleClasses, ConsoleString, 1, 2, ([baseClass]?)) { AbstractClassRep *base = NULL; if(argc > 1) { base = AbstractClassRep::findClassRep(argv[1]); if(!base) return ""; } Vector classes; U32 bufSize = 0; for(AbstractClassRep *rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass()) { if( !base || rep->isClass(base)) { classes.push_back(rep); bufSize += dStrlen(rep->getClassName()) + 1; } } if(!classes.size()) return ""; dQsort(classes.address(), classes.size(), sizeof(AbstractClassRep*), ACRCompare); char* ret = Con::getReturnBuffer(bufSize); dStrcpy( ret, classes[0]->getClassName()); for( U32 i=0; i< (U32)classes.size(); i++) { dStrcat( ret, "\t" ); dStrcat( ret, classes[i]->getClassName() ); } return ret; } /*! @} */ // group MetaScriptFunctions