openspades/Sources/AngelScript/addons/scriptbuilder.cpp
Tomoaki Kawada d2debd19fe Update AngelScript to 2.31.1
This (hopefully) fixes #416.
2016-11-10 22:21:19 +09:00

1028 lines
26 KiB
C++
Executable File

#include "scriptbuilder.h"
#include <vector>
#include <assert.h>
using namespace std;
#include <stdio.h>
#if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(__S3E__)
#include <direct.h>
#endif
#ifdef _WIN32_WCE
#include <windows.h> // For GetModuleFileName()
#endif
#if defined(__S3E__) || defined(__APPLE__) || defined(__GNUC__)
#include <unistd.h> // For getcwd()
#endif
BEGIN_AS_NAMESPACE
// Helper functions
static string GetCurrentDir();
static string GetAbsolutePath(const string &path);
CScriptBuilder::CScriptBuilder()
{
engine = 0;
module = 0;
includeCallback = 0;
callbackParam = 0;
}
void CScriptBuilder::SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam)
{
includeCallback = callback;
callbackParam = userParam;
}
int CScriptBuilder::StartNewModule(asIScriptEngine *inEngine, const char *moduleName)
{
if(inEngine == 0 ) return -1;
engine = inEngine;
module = inEngine->GetModule(moduleName, asGM_ALWAYS_CREATE);
if( module == 0 )
return -1;
ClearAll();
return 0;
}
asIScriptModule *CScriptBuilder::GetModule()
{
return module;
}
unsigned int CScriptBuilder::GetSectionCount() const
{
return (unsigned int)(includedScripts.size());
}
string CScriptBuilder::GetSectionName(unsigned int idx) const
{
if( idx >= includedScripts.size() ) return "";
#ifdef _WIN32
set<string, ci_less>::const_iterator it = includedScripts.begin();
#else
set<string>::const_iterator it = includedScripts.begin();
#endif
while( idx-- > 0 ) it++;
return *it;
}
// Returns 1 if the section was included
// Returns 0 if the section was not included because it had already been included before
// Returns <0 if there was an error
int CScriptBuilder::AddSectionFromFile(const char *filename)
{
// The file name stored in the set should be the fully resolved name because
// it is possible to name the same file in multiple ways using relative paths.
string fullpath = GetAbsolutePath(filename);
if( IncludeIfNotAlreadyIncluded(fullpath.c_str()) )
{
int r = LoadScriptSection(fullpath.c_str());
if( r < 0 )
return r;
else
return 1;
}
return 0;
}
// Returns 1 if the section was included
// Returns 0 if the section was not included because it had already been included before
// Returns <0 if there was an error
int CScriptBuilder::AddSectionFromMemory(const char *sectionName, const char *scriptCode, unsigned int scriptLength, int lineOffset)
{
if( IncludeIfNotAlreadyIncluded(sectionName) )
{
int r = ProcessScriptSection(scriptCode, scriptLength, sectionName, lineOffset);
if( r < 0 )
return r;
else
return 1;
}
return 0;
}
int CScriptBuilder::BuildModule()
{
return Build();
}
void CScriptBuilder::DefineWord(const char *word)
{
string sword = word;
if( definedWords.find(sword) == definedWords.end() )
{
definedWords.insert(sword);
}
}
void CScriptBuilder::ClearAll()
{
includedScripts.clear();
#if AS_PROCESS_METADATA == 1
currentClass = "";
currentNamespace = "";
foundDeclarations.clear();
typeMetadataMap.clear();
funcMetadataMap.clear();
varMetadataMap.clear();
#endif
}
bool CScriptBuilder::IncludeIfNotAlreadyIncluded(const char *filename)
{
string scriptFile = filename;
if( includedScripts.find(scriptFile) != includedScripts.end() )
{
// Already included
return false;
}
// Add the file to the set of included sections
includedScripts.insert(scriptFile);
return true;
}
int CScriptBuilder::LoadScriptSection(const char *filename)
{
// Open the script file
string scriptFile = filename;
#if _MSC_VER >= 1500 && !defined(__S3E__)
FILE *f = 0;
fopen_s(&f, scriptFile.c_str(), "rb");
#else
FILE *f = fopen(scriptFile.c_str(), "rb");
#endif
if( f == 0 )
{
// Write a message to the engine's message callback
string msg = "Failed to open script file '" + GetAbsolutePath(scriptFile) + "'";
engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str());
// TODO: Write the file where this one was included from
return -1;
}
// Determine size of the file
fseek(f, 0, SEEK_END);
int len = static_cast<int> (ftell(f));
fseek(f, 0, SEEK_SET);
// On Win32 it is possible to do the following instead
// int len = _filelength(_fileno(f));
// Read the entire file
string code;
size_t c = 0;
if( len > 0 )
{
code.resize(len);
c = fread(&code[0], len, 1, f);
}
fclose(f);
if( c == 0 && len > 0 )
{
// Write a message to the engine's message callback
string msg = "Failed to load script file '" + GetAbsolutePath(scriptFile) + "'";
engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str());
return -1;
}
// Process the script section even if it is zero length so that the name is registered
return ProcessScriptSection(code.c_str(), (unsigned int)(code.length()), filename, 0);
}
int CScriptBuilder::ProcessScriptSection(const char *script, unsigned int length, const char *sectionname, int lineOffset)
{
vector<string> includes;
// Perform a superficial parsing of the script first to store the metadata
if( length )
modifiedScript.assign(script, length);
else
modifiedScript = script;
// First perform the checks for #if directives to exclude code that shouldn't be compiled
unsigned int pos = 0;
int nested = 0;
while( pos < modifiedScript.size() )
{
asUINT len = 0;
asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_UNKNOWN && modifiedScript[pos] == '#' && (pos + 1 < modifiedScript.size()) )
{
int start = pos++;
// Is this an #if directive?
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
string token;
token.assign(&modifiedScript[pos], len);
pos += len;
if( token == "if" )
{
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_WHITESPACE )
{
pos += len;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
}
if( t == asTC_IDENTIFIER )
{
string word;
word.assign(&modifiedScript[pos], len);
// Overwrite the #if directive with space characters to avoid compiler error
pos += len;
OverwriteCode(start, pos-start);
// Has this identifier been defined by the application or not?
if( definedWords.find(word) == definedWords.end() )
{
// Exclude all the code until and including the #endif
pos = ExcludeCode(pos);
}
else
{
nested++;
}
}
}
else if( token == "endif" )
{
// Only remove the #endif if there was a matching #if
if( nested > 0 )
{
OverwriteCode(start, pos-start);
nested--;
}
}
}
else
pos += len;
}
#if AS_PROCESS_METADATA == 1
// Preallocate memory
string metadata, declaration;
metadata.reserve(500);
declaration.reserve(100);
#endif
// Then check for meta data and #include directives
pos = 0;
while( pos < modifiedScript.size() )
{
asUINT len = 0;
asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_COMMENT || t == asTC_WHITESPACE )
{
pos += len;
continue;
}
#if AS_PROCESS_METADATA == 1
// Check if class
if( currentClass == "" && modifiedScript.substr(pos,len) == "class" )
{
// Get the identifier after "class"
do
{
pos += len;
if( pos >= modifiedScript.size() )
{
t = asTC_UNKNOWN;
break;
}
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
} while(t == asTC_COMMENT || t == asTC_WHITESPACE);
if( t == asTC_IDENTIFIER )
{
currentClass = modifiedScript.substr(pos,len);
// Search until first { is encountered
while( pos < modifiedScript.length() )
{
engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
// If start of class section encountered stop
if( modifiedScript[pos] == '{' )
{
pos += len;
break;
}
// Check next symbol
pos += len;
}
}
continue;
}
// Check if end of class
if( currentClass != "" && modifiedScript[pos] == '}' )
{
currentClass = "";
pos += len;
continue;
}
// Check if namespace
if( modifiedScript.substr(pos,len) == "namespace" )
{
// Get the identifier after "namespace"
do
{
pos += len;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
} while(t == asTC_COMMENT || t == asTC_WHITESPACE);
if( currentNamespace != "" )
currentNamespace += "::";
currentNamespace += modifiedScript.substr(pos,len);
// Search until first { is encountered
while( pos < modifiedScript.length() )
{
engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
// If start of namespace section encountered stop
if( modifiedScript[pos] == '{' )
{
pos += len;
break;
}
// Check next symbol
pos += len;
}
continue;
}
// Check if end of namespace
if( currentNamespace != "" && modifiedScript[pos] == '}' )
{
size_t found = currentNamespace.rfind( "::" );
if( found != string::npos )
{
currentNamespace.erase( found );
}
else
{
currentNamespace = "";
}
pos += len;
continue;
}
// Is this the start of metadata?
if( modifiedScript[pos] == '[' )
{
// Get the metadata string
pos = ExtractMetadataString(pos, metadata);
// Determine what this metadata is for
int type;
ExtractDeclaration(pos, declaration, type);
// Store away the declaration in a map for lookup after the build has completed
if( type > 0 )
{
SMetadataDecl decl(metadata, declaration, type, currentClass, currentNamespace);
foundDeclarations.push_back(decl);
}
}
else
#endif
// Is this a preprocessor directive?
if( modifiedScript[pos] == '#' && (pos + 1 < modifiedScript.size()) )
{
int start = pos++;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_IDENTIFIER )
{
string token;
token.assign(&modifiedScript[pos], len);
if( token == "include" )
{
pos += len;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_WHITESPACE )
{
pos += len;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
}
if( t == asTC_VALUE && len > 2 && (modifiedScript[pos] == '"' || modifiedScript[pos] == '\'') )
{
// Get the include file
string includefile;
includefile.assign(&modifiedScript[pos+1], len-2);
pos += len;
// Store it for later processing
includes.push_back(includefile);
// Overwrite the include directive with space characters to avoid compiler error
OverwriteCode(start, pos-start);
}
}
}
}
// Don't search for metadata/includes within statement blocks or between tokens in statements
else
{
pos = SkipStatement(pos);
}
}
// Build the actual script
engine->SetEngineProperty(asEP_COPY_SCRIPT_SECTIONS, true);
module->AddScriptSection(sectionname, modifiedScript.c_str(), modifiedScript.size(), lineOffset);
if( includes.size() > 0 )
{
// If the callback has been set, then call it for each included file
if( includeCallback )
{
for( int n = 0; n < (int)includes.size(); n++ )
{
int r = includeCallback(includes[n].c_str(), sectionname, this, callbackParam);
if( r < 0 )
return r;
}
}
else
{
// By default we try to load the included file from the relative directory of the current file
// Determine the path of the current script so that we can resolve relative paths for includes
string path = sectionname;
size_t posOfSlash = path.find_last_of("/\\");
if( posOfSlash != string::npos )
path.resize(posOfSlash+1);
else
path = "";
// Load the included scripts
for( int n = 0; n < (int)includes.size(); n++ )
{
// If the include is a relative path, then prepend the path of the originating script
if( includes[n].find_first_of("/\\") != 0 &&
includes[n].find_first_of(":") == string::npos )
{
includes[n] = path + includes[n];
}
// Include the script section
int r = AddSectionFromFile(includes[n].c_str());
if( r < 0 )
return r;
}
}
}
return 0;
}
int CScriptBuilder::Build()
{
int r = module->Build();
if( r < 0 )
return r;
#if AS_PROCESS_METADATA == 1
// After the script has been built, the metadata strings should be
// stored for later lookup by function id, type id, and variable index
for( int n = 0; n < (int)foundDeclarations.size(); n++ )
{
SMetadataDecl *decl = &foundDeclarations[n];
module->SetDefaultNamespace(decl->nameSpace.c_str());
if( decl->type == 1 )
{
// Find the type id
int typeId = module->GetTypeIdByDecl(decl->declaration.c_str());
assert( typeId >= 0 );
if( typeId >= 0 )
typeMetadataMap.insert(map<int, string>::value_type(typeId, decl->metadata));
}
else if( decl->type == 2 )
{
if( decl->parentClass == "" )
{
// Find the function id
asIScriptFunction *func = module->GetFunctionByDecl(decl->declaration.c_str());
assert( func );
if( func )
funcMetadataMap.insert(map<int, string>::value_type(func->GetId(), decl->metadata));
}
else
{
// Find the method id
int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
assert( typeId > 0 );
map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
if( it == classMetadataMap.end() )
{
classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
it = classMetadataMap.find(typeId);
}
asITypeInfo *type = engine->GetTypeInfoById(typeId);
asIScriptFunction *func = type->GetMethodByDecl(decl->declaration.c_str());
assert( func );
if( func )
it->second.funcMetadataMap.insert(map<int, string>::value_type(func->GetId(), decl->metadata));
}
}
else if( decl->type == 4 )
{
if( decl->parentClass == "" )
{
// Find the global virtual property accessors
asIScriptFunction *func = module->GetFunctionByName(("get_" + decl->declaration).c_str());
if( func )
funcMetadataMap.insert(map<int, string>::value_type(func->GetId(), decl->metadata));
func = module->GetFunctionByName(("set_" + decl->declaration).c_str());
if( func )
funcMetadataMap.insert(map<int, string>::value_type(func->GetId(), decl->metadata));
}
else
{
// Find the method virtual property accessors
int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
assert( typeId > 0 );
map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
if( it == classMetadataMap.end() )
{
classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
it = classMetadataMap.find(typeId);
}
asITypeInfo *type = engine->GetTypeInfoById(typeId);
asIScriptFunction *func = type->GetMethodByName(("get_" + decl->declaration).c_str());
if( func )
it->second.funcMetadataMap.insert(map<int, string>::value_type(func->GetId(), decl->metadata));
func = type->GetMethodByName(("set_" + decl->declaration).c_str());
if( func )
it->second.funcMetadataMap.insert(map<int, string>::value_type(func->GetId(), decl->metadata));
}
}
else if( decl->type == 3 )
{
if( decl->parentClass == "" )
{
// Find the global variable index
int varIdx = module->GetGlobalVarIndexByName(decl->declaration.c_str());
assert( varIdx >= 0 );
if( varIdx >= 0 )
varMetadataMap.insert(map<int, string>::value_type(varIdx, decl->metadata));
}
else
{
int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
assert( typeId > 0 );
// Add the classes if needed
map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
if( it == classMetadataMap.end() )
{
classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
it = classMetadataMap.find(typeId);
}
// Add the variable to class
asITypeInfo *objectType = engine->GetTypeInfoById(typeId);
int idx = -1;
// Search through all properties to get proper declaration
for( asUINT i = 0; i < (asUINT)objectType->GetPropertyCount(); ++i )
{
const char *name;
objectType->GetProperty(i, &name);
if( decl->declaration == name )
{
idx = i;
break;
}
}
// If found, add it
assert( idx >= 0 );
if( idx >= 0 ) it->second.varMetadataMap.insert(map<int, string>::value_type(idx, decl->metadata));
}
}
}
module->SetDefaultNamespace("");
#endif
return 0;
}
int CScriptBuilder::SkipStatement(int pos)
{
asUINT len = 0;
// Skip until ; or { whichever comes first
while( pos < (int)modifiedScript.length() && modifiedScript[pos] != ';' && modifiedScript[pos] != '{' )
{
engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
pos += len;
}
// Skip entire statement block
if( pos < (int)modifiedScript.length() && modifiedScript[pos] == '{' )
{
pos += 1;
// Find the end of the statement block
int level = 1;
while( level > 0 && pos < (int)modifiedScript.size() )
{
asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_KEYWORD )
{
if( modifiedScript[pos] == '{' )
level++;
else if( modifiedScript[pos] == '}' )
level--;
}
pos += len;
}
}
else
pos += 1;
return pos;
}
// Overwrite all code with blanks until the matching #endif
int CScriptBuilder::ExcludeCode(int pos)
{
asUINT len = 0;
int nested = 0;
while( pos < (int)modifiedScript.size() )
{
engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( modifiedScript[pos] == '#' )
{
modifiedScript[pos] = ' ';
pos++;
// Is it an #if or #endif directive?
engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
string token;
token.assign(&modifiedScript[pos], len);
OverwriteCode(pos, len);
if( token == "if" )
{
nested++;
}
else if( token == "endif" )
{
if( nested-- == 0 )
{
pos += len;
break;
}
}
}
else if( modifiedScript[pos] != '\n' )
{
OverwriteCode(pos, len);
}
pos += len;
}
return pos;
}
// Overwrite all characters except line breaks with blanks
void CScriptBuilder::OverwriteCode(int start, int len)
{
char *code = &modifiedScript[start];
for( int n = 0; n < len; n++ )
{
if( *code != '\n' )
*code = ' ';
code++;
}
}
#if AS_PROCESS_METADATA == 1
int CScriptBuilder::ExtractMetadataString(int pos, string &metadata)
{
metadata = "";
// Overwrite the metadata with space characters to allow compilation
modifiedScript[pos] = ' ';
// Skip opening brackets
pos += 1;
int level = 1;
asUINT len = 0;
while( level > 0 && pos < (int)modifiedScript.size() )
{
asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_KEYWORD )
{
if( modifiedScript[pos] == '[' )
level++;
else if( modifiedScript[pos] == ']' )
level--;
}
// Copy the metadata to our buffer
if( level > 0 )
metadata.append(&modifiedScript[pos], len);
// Overwrite the metadata with space characters to allow compilation
if( t != asTC_WHITESPACE )
OverwriteCode(pos, len);
pos += len;
}
return pos;
}
int CScriptBuilder::ExtractDeclaration(int pos, string &declaration, int &type)
{
declaration = "";
type = 0;
int start = pos;
std::string token;
asUINT len = 0;
asETokenClass t = asTC_WHITESPACE;
// Skip white spaces and comments
do
{
pos += len;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
} while ( t == asTC_WHITESPACE || t == asTC_COMMENT );
// We're expecting, either a class, interface, function, or variable declaration
if( t == asTC_KEYWORD || t == asTC_IDENTIFIER )
{
token.assign(&modifiedScript[pos], len);
if( token == "interface" || token == "class" )
{
// Skip white spaces and comments
do
{
pos += len;
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
} while ( t == asTC_WHITESPACE || t == asTC_COMMENT );
if( t == asTC_IDENTIFIER )
{
type = 1;
declaration.assign(&modifiedScript[pos], len);
pos += len;
return pos;
}
}
else
{
// For function declarations, store everything up to the start of the statement block
// For variable declaration store just the name as there can only be one
// We'll only know if the declaration is a variable or function declaration when we see the statement block, or absense of a statement block.
bool hasParenthesis = false;
declaration.append(&modifiedScript[pos], len);
pos += len;
string name;
for(; pos < (int)modifiedScript.size();)
{
t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
if( t == asTC_KEYWORD )
{
token.assign(&modifiedScript[pos], len);
if( token == "{" )
{
if( hasParenthesis )
{
// We've found the end of a function signature
type = 2;
}
else
{
// We've found a virtual property. Just keep the name
declaration = name;
type = 4;
}
return pos;
}
if( token == "=" || token == ";" )
{
// Substitute the declaration with just the name
declaration = name;
type = 3;
return pos;
}
else if( token == "(" )
{
// This is the first parenthesis we encounter. If the parenthesis isn't followed
// by a statement block, then this is a variable declaration, in which case we
// should only store the type and name of the variable, not the initialization parameters.
hasParenthesis = true;
}
}
else if( t == asTC_IDENTIFIER )
{
name.assign(&modifiedScript[pos], len);
}
declaration.append(&modifiedScript[pos], len);
pos += len;
}
}
}
return start;
}
const char *CScriptBuilder::GetMetadataStringForType(int typeId)
{
map<int,string>::iterator it = typeMetadataMap.find(typeId);
if( it != typeMetadataMap.end() )
return it->second.c_str();
return "";
}
const char *CScriptBuilder::GetMetadataStringForFunc(asIScriptFunction *func)
{
if( func )
{
map<int,string>::iterator it = funcMetadataMap.find(func->GetId());
if( it != funcMetadataMap.end() )
return it->second.c_str();
}
return "";
}
const char *CScriptBuilder::GetMetadataStringForVar(int varIdx)
{
map<int,string>::iterator it = varMetadataMap.find(varIdx);
if( it != varMetadataMap.end() )
return it->second.c_str();
return "";
}
const char *CScriptBuilder::GetMetadataStringForTypeProperty(int typeId, int varIdx)
{
map<int, SClassMetadata>::iterator typeIt = classMetadataMap.find(typeId);
if(typeIt == classMetadataMap.end()) return "";
map<int, string>::iterator propIt = typeIt->second.varMetadataMap.find(varIdx);
if(propIt == typeIt->second.varMetadataMap.end()) return "";
return propIt->second.c_str();
}
const char *CScriptBuilder::GetMetadataStringForTypeMethod(int typeId, asIScriptFunction *method)
{
if( method )
{
map<int, SClassMetadata>::iterator typeIt = classMetadataMap.find(typeId);
if(typeIt == classMetadataMap.end()) return "";
map<int, string>::iterator methodIt = typeIt->second.funcMetadataMap.find(method->GetId());
if(methodIt == typeIt->second.funcMetadataMap.end()) return "";
return methodIt->second.c_str();
}
return "";
}
#endif
string GetAbsolutePath(const string &file)
{
string str = file;
// If this is a relative path, complement it with the current path
if( !((str.length() > 0 && (str[0] == '/' || str[0] == '\\')) ||
str.find(":") != string::npos) )
{
str = GetCurrentDir() + "/" + str;
}
// Replace backslashes for forward slashes
size_t pos = 0;
while( (pos = str.find("\\", pos)) != string::npos )
str[pos] = '/';
// Replace /./ with /
pos = 0;
while( (pos = str.find("/./", pos)) != string::npos )
str.erase(pos+1, 2);
// For each /../ remove the parent dir and the /../
pos = 0;
while( (pos = str.find("/../")) != string::npos )
{
size_t pos2 = str.rfind("/", pos-1);
if( pos2 != string::npos )
str.erase(pos2, pos+3-pos2);
else
{
// The path is invalid
break;
}
}
return str;
}
string GetCurrentDir()
{
char buffer[1024];
#if defined(_MSC_VER) || defined(_WIN32)
#ifdef _WIN32_WCE
static TCHAR apppath[MAX_PATH] = TEXT("");
if (!apppath[0])
{
GetModuleFileName(NULL, apppath, MAX_PATH);
int appLen = _tcslen(apppath);
// Look for the last backslash in the path, which would be the end
// of the path itself and the start of the filename. We only want
// the path part of the exe's full-path filename
// Safety is that we make sure not to walk off the front of the
// array (in case the path is nothing more than a filename)
while (appLen > 1)
{
if (apppath[appLen-1] == TEXT('\\'))
break;
appLen--;
}
// Terminate the string after the trailing backslash
apppath[appLen] = TEXT('\0');
}
#ifdef _UNICODE
wcstombs(buffer, apppath, min(1024, wcslen(apppath)*sizeof(wchar_t)));
#else
memcpy(buffer, apppath, min(1024, strlen(apppath)));
#endif
return buffer;
#elif defined(__S3E__)
// Marmalade uses its own portable C library
return getcwd(buffer, (int)1024);
#elif _XBOX_VER >= 200
// XBox 360 doesn't support the getcwd function, just use the root folder
return "game:/";
#elif defined(_M_ARM)
// TODO: How to determine current working dir on Windows Phone?
return "";
#else
return _getcwd(buffer, (int)1024);
#endif // _MSC_VER
#elif defined(__APPLE__) || defined(__linux__)
return getcwd(buffer, 1024);
#else
return "";
#endif
}
END_AS_NAMESPACE