code:
#include "scriptsocket.h"
#include <assert.h>

BEGIN_AS_NAMESPACE

#ifdef _WIN32

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

// Manage the required calls to WSAStartup and WSACleanup
class WindowsSocketLib
{
public:
	WindowsSocketLib() : m_status(0)
	{
		WORD wVersionRequested = 0x0202;
		WSADATA wsadata;
		// WSAStartup can be called multiple times, so it is not a problem if another piece of code also called it
		m_status = WSAStartup(wVersionRequested, &wsadata);
		if (m_status != 0)
		{
			// No usable WINSOCK.DLL found
		}
		else if (wsadata.wVersion != wVersionRequested)
		{
			// WINSOCK.DLL does not support version 2.2
			WSACleanup();
			m_status = -1;
		}
	}

	~WindowsSocketLib()
	{
		// WSACleanup must be called for each successful call to WSAStartup
		if( m_status == 0 )
			WSACleanup();
	}

	int m_status;
} g_windowsSocketLib;
#endif

CScriptSocket::CScriptSocket() : m_refCount(1), m_socket(-1), m_isListening(false)
{
	// TODO: On Windows check if the Windows Socket was properly loaded, else raise a script exception
}

void CScriptSocket::AddRef() const
{
	asAtomicInc(m_refCount);;
}

void CScriptSocket::Release() const
{
	if (asAtomicDec(m_refCount) == 0)
		delete this;
}

CScriptSocket::~CScriptSocket()
{
	// Disconnect the socket if it is open
	Close();
}

int CScriptSocket::Listen(asWORD port)
{
	// If another socket is already used it must first be closed, and the working thread must be shutdown
	if (m_socket != -1)
		return -1;

	// Set up a listener socket
	// TODO: Allow script to define the protocol
	m_socket = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_socket == -1)
		return -1;

	sockaddr_in serverAddress = { 0 };
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_port = htons(port);
	serverAddress.sin_addr.s_addr = INADDR_ANY;

	// TODO: Need to be able to tell if the port is already occupied
	int r = bind(m_socket, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
	if (r == SOCKET_ERROR)
	{
		closesocket(m_socket);
		m_socket = -1;
		return -1;
	}
	m_isListening = true;

	// TODO: Allow script to define the max queue for incoming connections
	listen(m_socket, 5);

	return 0;
}

// Internal
// Returns 1 if there is something to read on the socket
// Returns 0 if there is nothing to read
// Returns a negative value if there is an error (see return codes for select())
int CScriptSocket::Select(asINT64 timeoutMicrosec)
{
	fd_set read = { 1, {(SOCKET)m_socket} };
	TIMEVAL timeout = { 0, 0 }; // Don't wait
	if (timeoutMicrosec > 0)
	{
		// Limit the wait time to the highest value that TIMEVAL can represent,
		// i.e. 2^31-1 seconds + 999,999 microseconds (approximately 68 years)
		const asINT64 maxTimeout = asINT64(0x7FFFFFFF) * 1000000 + 999999;
		if (timeoutMicrosec > maxTimeout) timeoutMicrosec = maxTimeout;
		timeout.tv_sec = asINT32(timeoutMicrosec / 1000000) & 0x7FFFFFFF;
		timeout.tv_usec = asINT32(timeoutMicrosec - asINT64(timeout.tv_sec) * 1000000);
	}

	int r = select(0, &read, 0, 0, timeoutMicrosec < 0 ? 0 : &timeout);
	return r;
}

CScriptSocket* CScriptSocket::Accept(asINT64 timeoutMicrosec)
{
	// Cannot accept a client on an ordinary socket or if the socket is not active
	if (!m_isListening || m_socket == -1)
		return 0;

	// First determine if there is anything to receive so that doesn't block
	int r = Select(timeoutMicrosec);
	if (r <= 0)
		return 0;

	// TODO: need to be able to check to what ip address and port the socket is connected it
	// Each incoming client connection a new CScriptSocket is created and put in the array so the script can retrieve them
	int clientSocket = (int)accept(m_socket, 0, 0);
	if (clientSocket != -1)
	{
		CScriptSocket* client = new CScriptSocket();
		client->m_socket = clientSocket;
		return client;
	}

	return 0;
}

int CScriptSocket::Close()
{
	// If the socket is open
	if (m_socket != -1)
		return -1;

	// Close the listener socket
	closesocket(m_socket);
	m_socket = -1;

	return 0;
}

int CScriptSocket::Connect(asUINT ipv4Address, asWORD port)
{
	// If another socket is already used it must first be closed, and the working thread must be shutdown
	if (m_socket != -1)
		return -1;

	// Set up a client socket
	// TODO: Allow script to define the protocol
	m_socket = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	sockaddr_in serverAddress = { 0 };
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_port = htons(port);
	serverAddress.sin_addr.s_addr = htonl(ipv4Address);

	// TODO: Allow script to define a timeout
	int result = connect(m_socket, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
	if (result == SOCKET_ERROR)
	{
		closesocket(m_socket);
		m_socket = -1;
		return -1;
	}

	// TODO: Should the client socket already start receiving data? Or should it wait for the script to initiate that?

	return 0;
}

int CScriptSocket::Send(const std::string& data)
{
	// Cannot send on a listener socket or if the socket is not connected
	if (m_isListening || m_socket == -1)
		return -1;

	// Send a buffer of data over the socket
	// TODO: The sending of data should be done in a background thread so it can be done over a time rather than block the caller
	// TODO: How to determine the maximum size of data that can be sent in a single call?
	int r = send(m_socket, data.c_str(), (int)data.length(), 0);
	if (r == SOCKET_ERROR)
		return -1;

	// Return the number of bytes sent
	return r;
}

std::string CScriptSocket::Receive(asINT64 timeoutMicrosec)
{
	// Cannot receive on a listener socket or if the socket is not connected
	if (m_isListening || m_socket == -1)
		return "";

	// TODO: Need to be able to set the size of the internal buffer

	// First determine if there is anything to receive so that doesn't block
	int r = Select(timeoutMicrosec);
	if (r <= 0)
		return "";

	char buf[1024] = {};
	std::string msg;
	for (;;)
	{
		// Read the buffer
		r = recv(m_socket, buf, sizeof(buf), 0);
		if (r >= 0)
		{
			msg.append(buf, r);
			break;
		}
		else if (r == SOCKET_ERROR)
		{
			r = WSAGetLastError();
			if (r == WSAEMSGSIZE)
			{
				// Append what could be read, then read the rest
				msg.append(buf, sizeof(buf));
			}
			else
				break;
		}
	}

	return msg;
}

static CScriptSocket* CScriptSocket_Factory()
{
	return new CScriptSocket();
}

int RegisterScriptSocket(asIScriptEngine* engine)
{
	int r;

	// Check that the string type has been registered already
	r = engine->GetTypeIdByDecl("string"); assert(r >= 0);

	// Register the socket class with the script engine
	engine->RegisterObjectType("socket", 0, asOBJ_REF);
	r = engine->RegisterObjectBehaviour("socket", asBEHAVE_FACTORY, "socket @f()", asFUNCTION(CScriptSocket_Factory), asCALL_CDECL); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("socket", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptSocket, AddRef), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("socket", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptSocket, Release), asCALL_THISCALL); assert(r >= 0);

	r = engine->RegisterObjectMethod("socket", "int listen(uint16 port)", asMETHOD(CScriptSocket, Listen), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("socket", "int close()", asMETHOD(CScriptSocket, Close), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("socket", "socket @accept(int64 timeout = 0)", asMETHOD(CScriptSocket, Accept), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("socket", "int connect(uint ipv4address, uint16 port)", asMETHOD(CScriptSocket, Connect), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("socket", "int send(const string &in data)", asMETHOD(CScriptSocket, Send), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("socket", "string receive(int64 timeout = 0)", asMETHOD(CScriptSocket, Receive), asCALL_THISCALL); assert(r >= 0);

	return 0;
}

END_AS_NAMESPACE


code:
//
// CScriptSocket
//
// This class represents a socket. It can be used to set up listeners for
// connecting incoming clients, and also to connect to a server. The socket
// class will create threads in the background to handle the communication
// and update a send and a receive buffer so the script will not have to deal
// with that.
//

#ifndef SCRIPTSOCKET_H
#define SCRIPTSOCKET_H

#include <string>
#include <list>
#ifdef _WIN32
#include <winsock2.h>
#endif

#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif

BEGIN_AS_NAMESPACE

class CScriptSocket
{
public:
	CScriptSocket();

	void AddRef() const;
	void Release() const;

	int            Listen(asWORD port);
	int            Close();
	CScriptSocket* Accept(asINT64 timeoutMicrosec = 0);
	int            Connect(asUINT ipv4Address, asWORD port);
	int            Send(const std::string& data);
	std::string    Receive(asINT64 timeoutMicrosec = 0);

protected:
	~CScriptSocket();

	int Select(asINT64 timeoutMicrosec = 0);

	mutable int m_refCount;

	int m_socket;
	bool m_isListening;
};

int RegisterScriptSocket(asIScriptEngine* engine);

END_AS_NAMESPACE

#endif

repo_name: Antidote/openrpgeditor

license: mit


code:
#include "scriptstdstring.h"
#include <assert.h> // assert()
#include <sstream>  // std::stringstream
#include <string.h> // strstr()
#include <stdio.h>	// snprintf()
#include <stdlib.h> // strtod()
#ifndef __psp2__
	#include <locale.h> // setlocale()
#endif
#include <regex>

using namespace std;

// This macro is used to avoid warnings about unused variables.
// Usually where the variables are only used in debug mode.
#define UNUSED_VAR(x) (void)(x)

#ifdef AS_CAN_USE_CPP11
// The string factory doesn't need to keep a specific order in the
// cache, so the unordered_map is faster than the ordinary map
#include <unordered_map>  // std::unordered_map
BEGIN_AS_NAMESPACE
typedef unordered_map<string, int> map_t;
END_AS_NAMESPACE
#else
#include <map>      // std::map
BEGIN_AS_NAMESPACE
typedef map<string, int> map_t;
END_AS_NAMESPACE
#endif

BEGIN_AS_NAMESPACE
class CStdStringFactory : public asIStringFactory
{
public:
	CStdStringFactory() {}
	~CStdStringFactory()
	{
		// The script engine must release each string
		// constant that it has requested
		assert(stringCache.size() == 0);
	}

	const void *GetStringConstant(const char *data, asUINT length)
	{
		// The string factory might be modified from multiple
		// threads, so it is necessary to use a mutex.
		asAcquireExclusiveLock();

		string str(data, length);
		map_t::iterator it = stringCache.find(str);
		if (it != stringCache.end())
			it->second++;
		else
			it = stringCache.insert(map_t::value_type(str, 1)).first;

		asReleaseExclusiveLock();

		return reinterpret_cast<const void*>(&it->first);
	}

	int  ReleaseStringConstant(const void *str)
	{
		if (str == 0)
			return asERROR;

		int ret = asSUCCESS;

		// The string factory might be modified from multiple
		// threads, so it is necessary to use a mutex.
		asAcquireExclusiveLock();

		map_t::iterator it = stringCache.find(*reinterpret_cast<const string*>(str));
		if (it == stringCache.end())
			ret = asERROR;
		else
		{
			it->second--;
			if (it->second == 0)
				stringCache.erase(it);
		}

		asReleaseExclusiveLock();

		return ret;
	}

	int  GetRawStringData(const void *str, char *data, asUINT *length) const
	{
		if (str == 0)
			return asERROR;

		if (length)
			*length = (asUINT)reinterpret_cast<const string*>(str)->length();

		if (data)
			memcpy(data, reinterpret_cast<const string*>(str)->c_str(), reinterpret_cast<const string*>(str)->length());

		return asSUCCESS;
	}

	// THe access to the string cache is protected with the common mutex provided by AngelScript
	map_t stringCache;
};

static CStdStringFactory *stringFactory = 0;

// TODO: Make this public so the application can also use the string
//       factory and share the string constants if so desired, or to
//       monitor the size of the string factory cache.
CStdStringFactory *GetStdStringFactorySingleton()
{
	if( stringFactory == 0 )
	{
		// Make sure no other thread is creating the string factory at the same time
		asAcquireExclusiveLock();
		if (stringFactory == 0)
		{
			// The following instance will be destroyed by the global
			// CStdStringFactoryCleaner instance upon application shutdown
			stringFactory = new CStdStringFactory();
		}
		asReleaseExclusiveLock();
	}
	return stringFactory;
}

class CStdStringFactoryCleaner
{
public:
	~CStdStringFactoryCleaner()
	{
		if (stringFactory)
		{
			// Only delete the string factory if the stringCache is empty
			// If it is not empty, it means that someone might still attempt
			// to release string constants, so if we delete the string factory
			// the application might crash. Not deleting the cache would
			// lead to a memory leak, but since this is only happens when the
			// application is shutting down anyway, it is not important.
			if (stringFactory->stringCache.empty())
			{
				delete stringFactory;
				stringFactory = 0;
			}
		}
	}
};

static CStdStringFactoryCleaner cleaner;

static void ConstructString(string *thisPointer)
{
	new(thisPointer) string();
}

static void CopyConstructString(const string &other, string *thisPointer)
{
	new(thisPointer) string(other);
}

static void DestructString(string *thisPointer)
{
	thisPointer->~string();
}

static string &AddAssignStringToString(const string &str, string &dest)
{
	// We don't register the method directly because some compilers
	// and standard libraries inline the definition, resulting in the
	// linker being unable to find the declaration.
	// Example: CLang/LLVM with XCode 4.3 on OSX 10.7
	dest += str;
	return dest;
}

// bool string::isEmpty()
// bool string::empty() // if AS_USE_STLNAMES == 1
static bool StringIsEmpty(const string &str)
{
	// We don't register the method directly because some compilers
	// and standard libraries inline the definition, resulting in the
	// linker being unable to find the declaration
	// Example: CLang/LLVM with XCode 4.3 on OSX 10.7
	return str.empty();
}

#if AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE == 0
static string &AssignUInt64ToString(asQWORD i, string &dest)
{
	ostringstream stream;
	stream << i;
	dest = stream.str();
	return dest;
}

static string &AddAssignUInt64ToString(asQWORD i, string &dest)
{
	ostringstream stream;
	stream << i;
	dest += stream.str();
	return dest;
}

static string AddStringUInt64(const string &str, asQWORD i)
{
	ostringstream stream;
	stream << i;
	return str + stream.str();
}

static string AddInt64String(asINT64 i, const string &str)
{
	ostringstream stream;
	stream << i;
	return stream.str() + str;
}

static string &AssignInt64ToString(asINT64 i, string &dest)
{
	ostringstream stream;
	stream << i;
	dest = stream.str();
	return dest;
}

static string &AddAssignInt64ToString(asINT64 i, string &dest)
{
	ostringstream stream;
	stream << i;
	dest += stream.str();
	return dest;
}

static string AddStringInt64(const string &str, asINT64 i)
{
	ostringstream stream;
	stream << i;
	return str + stream.str();
}

static string AddUInt64String(asQWORD i, const string &str)
{
	ostringstream stream;
	stream << i;
	return stream.str() + str;
}

static string &AssignDoubleToString(double f, string &dest)
{
	ostringstream stream;
	stream << f;
	dest = stream.str();
	return dest;
}

static string &AddAssignDoubleToString(double f, string &dest)
{
	ostringstream stream;
	stream << f;
	dest += stream.str();
	return dest;
}

static string &AssignFloatToString(float f, string &dest)
{
	ostringstream stream;
	stream << f;
	dest = stream.str();
	return dest;
}

static string &AddAssignFloatToString(float f, string &dest)
{
	ostringstream stream;
	stream << f;
	dest += stream.str();
	return dest;
}

static string &AssignBoolToString(bool b, string &dest)
{
	ostringstream stream;
	stream << (b ? "true" : "false");
	dest = stream.str();
	return dest;
}

static string &AddAssignBoolToString(bool b, string &dest)
{
	ostringstream stream;
	stream << (b ? "true" : "false");
	dest += stream.str();
	return dest;
}

static string AddStringDouble(const string &str, double f)
{
	ostringstream stream;
	stream << f;
	return str + stream.str();
}

static string AddDoubleString(double f, const string &str)
{
	ostringstream stream;
	stream << f;
	return stream.str() + str;
}

static string AddStringFloat(const string &str, float f)
{
	ostringstream stream;
	stream << f;
	return str + stream.str();
}

static string AddFloatString(float f, const string &str)
{
	ostringstream stream;
	stream << f;
	return stream.str() + str;
}

static string AddStringBool(const string &str, bool b)
{
	ostringstream stream;
	stream << (b ? "true" : "false");
	return str + stream.str();
}

static string AddBoolString(bool b, const string &str)
{
	ostringstream stream;
	stream << (b ? "true" : "false");
	return stream.str() + str;
}
#endif

static char *StringCharAt(unsigned int i, string &str)
{
	if( i >= str.size() )
	{
		// Set a script exception
		asIScriptContext *ctx = asGetActiveContext();
		ctx->SetException("Out of range");

		// Return a null pointer
		return 0;
	}

	return &str[i];
}

// AngelScript signature:
// int string::opCmp(const string &in) const
static int StringCmp(const string &a, const string &b)
{
	int cmp = 0;
	if( a < b ) cmp = -1;
	else if( a > b ) cmp = 1;
	return cmp;
}

// This function returns the index of the first position where the substring
// exists in the input string. If the substring doesn't exist in the input
// string -1 is returned.
//
// AngelScript signature:
// int string::findFirst(const string &in sub, uint start = 0) const
static int StringFindFirst(const string &sub, asUINT start, const string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	return (int)str.find(sub, (size_t)(start < 0 ? string::npos : start));
}

// This function returns the index of the first position that matches the regular expression
//
// AngelScript signature:
// int string::regexFind(const string &in regex, uint start = 0, uint &out lengthOfMatch = void)
static int StringRegexFind(const string& rex, asUINT start, asUINT& outLengthOfMatch, const string& str)
{
	if (start >= str.length())
	{
		outLengthOfMatch = 0;
		return -1;
	}

	// TODO: If possible add support for matching utf8 characters
	// However on with MSVC it doesn't seem that std::regex works with utf8
	// This works with MSVC, but I don't want to have to convert the string to UTF-16 first because the position and length will not work
	// https://www.regular-expressions.info/stdregex.html
	//
	//  std::wregex pattern(L"[[:alpha:]]+");
	//  bool result = std::regex_match(std::wstring(L"abcd�fg"), pattern);
	//
	// The solution from stack overflow doesn't work with MSVC
	// https://stackoverflow.com/questions/11254232/do-c11-regular-expressions-work-with-utf-8-strings
	//
	//  std::locale old;
	//  std::locale::global(std::locale("en_US.UTF-8"));
	//  std::regex pattern("[[:alpha:]]+", std::regex_constants::extended);
	//  bool result = std::regex_match(std::string(u8"abcd�fg"), pattern);
	//
	// I've tried setting the manifest to use utf8 code page but it also doesn't work with MSVC
	// https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page

	std::regex pattern(rex, std::regex_constants::ECMAScript | std::regex_constants::collate);
	std::cmatch match;
	bool result = std::regex_search(str.c_str() + start, str.c_str()+str.length(), match, pattern);

	if (!result)
	{
		outLengthOfMatch = 0;
		return -1;
	}

	outLengthOfMatch = (asUINT)match[0].length();
	return (int)match.prefix().length();
}

// This function returns the index of the first position where the one of the bytes in substring
// exists in the input string. If the characters in the substring doesn't exist in the input
// string -1 is returned.
//
// AngelScript signature:
// int string::findFirstOf(const string &in sub, uint start = 0) const
static int StringFindFirstOf(const string &sub, asUINT start, const string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	return (int)str.find_first_of(sub, (size_t)(start < 0 ? string::npos : start));
}

// This function returns the index of the last position where the one of the bytes in substring
// exists in the input string. If the characters in the substring doesn't exist in the input
// string -1 is returned.
//
// AngelScript signature:
// int string::findLastOf(const string &in sub, uint start = -1) const
static int StringFindLastOf(const string &sub, asUINT start, const string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	return (int)str.find_last_of(sub, (size_t)(start < 0 ? string::npos : start));
}

// This function returns the index of the first position where a byte other than those in substring
// exists in the input string. If none is found -1 is returned.
//
// AngelScript signature:
// int string::findFirstNotOf(const string &in sub, uint start = 0) const
static int StringFindFirstNotOf(const string &sub, asUINT start, const string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	return (int)str.find_first_not_of(sub, (size_t)(start < 0 ? string::npos : start));
}

// This function returns the index of the last position where a byte other than those in substring
// exists in the input string. If none is found -1 is returned.
//
// AngelScript signature:
// int string::findLastNotOf(const string &in sub, uint start = -1) const
static int StringFindLastNotOf(const string &sub, asUINT start, const string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	return (int)str.find_last_not_of(sub, (size_t)(start < 0 ? string::npos : start));
}

// This function returns the index of the last position where the substring
// exists in the input string. If the substring doesn't exist in the input
// string -1 is returned.
//
// AngelScript signature:
// int string::findLast(const string &in sub, int start = -1) const
static int StringFindLast(const string &sub, int start, const string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	return (int)str.rfind(sub, (size_t)(start < 0 ? string::npos : start));
}

// AngelScript signature:
// void string::insert(uint pos, const string &in other)
static void StringInsert(unsigned int pos, const string &other, string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	str.insert(pos, other);
}

// AngelScript signature:
// void string::erase(uint pos, int count = -1)
static void StringErase(unsigned int pos, int count, string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	str.erase(pos, (size_t)(count < 0 ? string::npos : count));
}

// AngelScript signature:
// uint string::length() const
static asUINT StringLength(const string &str)
{
	// We don't register the method directly because the return type changes between 32bit and 64bit platforms
	return (asUINT)str.length();
}

// AngelScript signature:
// void string::resize(uint l)
static void StringResize(asUINT l, string &str)
{
	// We don't register the method directly because the argument types change between 32bit and 64bit platforms
	str.resize(l);
}

// AngelScript signature:
// string formatInt(int64 val, const string &in options, uint width)
static string formatInt(asINT64 value, const string &options, asUINT width)
{
	bool leftJustify = options.find("l") != string::npos;
	bool padWithZero = options.find("0") != string::npos;
	bool alwaysSign  = options.find("+") != string::npos;
	bool spaceOnSign = options.find(" ") != string::npos;
	bool hexSmall    = options.find("h") != string::npos;
	bool hexLarge    = options.find("H") != string::npos;

	string fmt = "%";
	if( leftJustify ) fmt += "-";
	if( alwaysSign ) fmt += "+";
	if( spaceOnSign ) fmt += " ";
	if( padWithZero ) fmt += "0";

#ifdef _WIN32
	fmt += "*I64";
#else
#ifdef _LP64
	fmt += "*l";
#else
	fmt += "*ll";
#endif
#endif

	if( hexSmall ) fmt += "x";
	else if( hexLarge ) fmt += "X";
	else fmt += "d";

	string buf;
	buf.resize(width+30);
#if _MSC_VER >= 1400 && !defined(__S3E__)
	// MSVC 8.0 / 2005 or newer
	sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, value);
#else
	snprintf(&buf[0], buf.size(), fmt.c_str(), width, value);
#endif
	buf.resize(strlen(&buf[0]));

	return buf;
}

// AngelScript signature:
// string formatUInt(uint64 val, const string &in options, uint width)
static string formatUInt(asQWORD value, const string &options, asUINT width)
{
	bool leftJustify = options.find("l") != string::npos;
	bool padWithZero = options.find("0") != string::npos;
	bool alwaysSign  = options.find("+") != string::npos;
	bool spaceOnSign = options.find(" ") != string::npos;
	bool hexSmall    = options.find("h") != string::npos;
	bool hexLarge    = options.find("H") != string::npos;

	string fmt = "%";
	if( leftJustify ) fmt += "-";
	if( alwaysSign ) fmt += "+";
	if( spaceOnSign ) fmt += " ";
	if( padWithZero ) fmt += "0";

#ifdef _WIN32
	fmt += "*I64";
#else
#ifdef _LP64
	fmt += "*l";
#else
	fmt += "*ll";
#endif
#endif

	if( hexSmall ) fmt += "x";
	else if( hexLarge ) fmt += "X";
	else fmt += "u";

	string buf;
	buf.resize(width+30);
#if _MSC_VER >= 1400 && !defined(__S3E__)
	// MSVC 8.0 / 2005 or newer
	sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, value);
#else
	snprintf(&buf[0], buf.size(), fmt.c_str(), width, value);
#endif
	buf.resize(strlen(&buf[0]));

	return buf;
}

// AngelScript signature:
// string formatFloat(double val, const string &in options, uint width, uint precision)
static string formatFloat(double value, const string &options, asUINT width, asUINT precision)
{
	bool leftJustify = options.find("l") != string::npos;
	bool padWithZero = options.find("0") != string::npos;
	bool alwaysSign  = options.find("+") != string::npos;
	bool spaceOnSign = options.find(" ") != string::npos;
	bool expSmall    = options.find("e") != string::npos;
	bool expLarge    = options.find("E") != string::npos;

	string fmt = "%";
	if( leftJustify ) fmt += "-";
	if( alwaysSign ) fmt += "+";
	if( spaceOnSign ) fmt += " ";
	if( padWithZero ) fmt += "0";

	fmt += "*.*";

	if( expSmall ) fmt += "e";
	else if( expLarge ) fmt += "E";
	else fmt += "f";

	string buf;
	buf.resize(width+precision+50);
#if _MSC_VER >= 1400 && !defined(__S3E__)
	// MSVC 8.0 / 2005 or newer
	sprintf_s(&buf[0], buf.size(), fmt.c_str(), width, precision, value);
#else
	snprintf(&buf[0], buf.size(), fmt.c_str(), width, precision, value);
#endif
	buf.resize(strlen(&buf[0]));

	return buf;
}

// TODO: variadic: review
static void StringFormat(asIScriptGeneric* gen)
{
	const string& fmt = *(string*)gen->GetArgAddress(0);
	string result;

	asUINT defaultArgIdx = 1; // Skip the first argument which is the fmt
	for (asUINT i = 0; i < fmt.size(); ++i)
	{
		char ch = fmt[i];
		if (ch == '{')
		{
			if (i + 1 >= (asUINT)fmt.size())
			{
				asGetActiveContext()->SetException("Invalid format string");
				return;
			}

			if (fmt[i + 1] == '{')
			{
				i += 1;
				result += '{';
			}
			else
			{
				// TODO: Parse optional argument index to support for relocating argument
				// e.g. "{1} {0}".format("there", "hello") == "hello there"
				asUINT argIdx = defaultArgIdx++;
				if (argIdx >= (asUINT)gen->GetArgCount())
				{
					asGetActiveContext()->SetException("Index out of range");
					return;
				}
				int typeId = gen->GetArgTypeId(argIdx);
				void* ref = gen->GetArgAddress(argIdx);

				switch (typeId)
				{
				case asTYPEID_BOOL:
					result += *(bool*)ref ? "true" : "false";
					break;

#define AS_STRING_FORMAT_IMPL(tid, type) \
	case tid: result += to_string(*(type*)ref); break

					AS_STRING_FORMAT_IMPL(asTYPEID_INT8, int8_t);
					AS_STRING_FORMAT_IMPL(asTYPEID_INT16, int16_t);
					AS_STRING_FORMAT_IMPL(asTYPEID_INT32, int32_t);
					AS_STRING_FORMAT_IMPL(asTYPEID_INT64, int64_t);

					AS_STRING_FORMAT_IMPL(asTYPEID_UINT8, uint8_t);
					AS_STRING_FORMAT_IMPL(asTYPEID_UINT16, uint16_t);
					AS_STRING_FORMAT_IMPL(asTYPEID_UINT32, uint32_t);
					AS_STRING_FORMAT_IMPL(asTYPEID_UINT64, uint64_t);

					AS_STRING_FORMAT_IMPL(asTYPEID_FLOAT, float);
					AS_STRING_FORMAT_IMPL(asTYPEID_DOUBLE, double);

				default:
					if (typeId & ~asTYPEID_MASK_SEQNBR)
					{
						asIScriptEngine* engine = gen->GetEngine();
						int stringTypeId = engine->GetStringFactory();
						if (typeId == stringTypeId)
						{
							result += *(string*)ref;
						}
						else
						{
							// TODO: Better explanation
							asGetActiveContext()->SetException("Unformattable");
							return;
						}
					}
					else // enums
					{
						// TODO: Format enum name
						result += to_string(*(int*)ref);
					}
				}
			}
		}
		else if (ch == '}')
		{
			if (i + 1 < (asUINT)fmt.size() && fmt[i + 1] == '}')
			{
				i += 1;
				result += '}';
			}
		}
		else
		{
			// Ordinary character
			result += ch;
		}
	}

	new(gen->GetAddressOfReturnLocation()) string(std::move(result));
}

// TODO: variadic: review
static void StringScan(asIScriptGeneric* gen)
{
	asIScriptEngine* engine = gen->GetEngine();

	stringstream ss(*(string*)gen->GetArgObject(0));
	asUINT scanned = 0;

	for (asUINT i = 1; i < (asUINT)gen->GetArgCount(); ++i)
	{
		int typeId = gen->GetArgTypeId(i);
		if (!(typeId & ~asTYPEID_MASK_SEQNBR))
		{
#define AS_STRING_SCAN_IMPL(tid, type) \
	case tid:\
	do {\
		type val;\
		ss >> val;\
		if(!ss) goto end_scan;\
		void* ref = gen->GetArgAddress(i); \
		*(type*)ref = val;\
	} while(0); \
	break

			switch (typeId)
			{
				AS_STRING_SCAN_IMPL(asTYPEID_BOOL, bool);

				AS_STRING_SCAN_IMPL(asTYPEID_INT16, int16_t);
			default: // enum
				AS_STRING_SCAN_IMPL(asTYPEID_INT32, int32_t);
				AS_STRING_SCAN_IMPL(asTYPEID_INT64, int64_t);

				AS_STRING_SCAN_IMPL(asTYPEID_UINT8, uint8_t);
				AS_STRING_SCAN_IMPL(asTYPEID_UINT16, uint16_t);
				AS_STRING_SCAN_IMPL(asTYPEID_UINT32, uint32_t);
				AS_STRING_SCAN_IMPL(asTYPEID_UINT64, uint64_t);

				AS_STRING_SCAN_IMPL(asTYPEID_FLOAT, float);
				AS_STRING_SCAN_IMPL(asTYPEID_DOUBLE, double);
			}
		}
		else if (typeId == engine->GetStringFactory())
		{
			string val;
			ss >> val;
			if (!ss) goto end_scan;

			void* ref = gen->GetArgAddress(i);
			*(string*)ref = std::move(val);
		}
		else // Invalid type
		{
			goto end_scan;
		}

		++scanned;
	}

end_scan:
	gen->SetReturnDWord(scanned);
}

// AngelScript signature:
// int64 parseInt(const string &in val, uint base = 10, uint &out byteCount = 0)
static asINT64 parseInt(const string &val, asUINT base, asUINT *byteCount)
{
	// Only accept base 10 and 16
	if( base != 10 && base != 16 )
	{
		if( byteCount ) *byteCount = 0;
		return 0;
	}

	const char *end = &val[0];

	// Determine the sign
	bool sign = false;
	if( *end == '-' )
	{
		sign = true;
		end++;
	}
	else if( *end == '+' )
		end++;

	asINT64 res = 0;
	if( base == 10 )
	{
		while( *end >= '0' && *end <= '9' )
		{
			res *= 10;
			res += *end++ - '0';
		}
	}
	else if( base == 16 )
	{
		while( (*end >= '0' && *end <= '9') ||
		       (*end >= 'a' && *end <= 'f') ||
		       (*end >= 'A' && *end <= 'F') )
		{
			res *= 16;
			if( *end >= '0' && *end <= '9' )
				res += *end++ - '0';
			else if( *end >= 'a' && *end <= 'f' )
				res += *end++ - 'a' + 10;
			else if( *end >= 'A' && *end <= 'F' )
				res += *end++ - 'A' + 10;
		}
	}

	if( byteCount )
		*byteCount = asUINT(size_t(end - val.c_str()));

	if( sign )
		res = -res;

	return res;
}

// AngelScript signature:
// uint64 parseUInt(const string &in val, uint base = 10, uint &out byteCount = 0)
static asQWORD parseUInt(const string &val, asUINT base, asUINT *byteCount)
{
	// Only accept base 10 and 16
	if (base != 10 && base != 16)
	{
		if (byteCount) *byteCount = 0;
		return 0;
	}

	const char *end = &val[0];

	asQWORD res = 0;
	if (base == 10)
	{
		while (*end >= '0' && *end <= '9')
		{
			res *= 10;
			res += *end++ - '0';
		}
	}
	else if (base == 16)
	{
		while ((*end >= '0' && *end <= '9') ||
			(*end >= 'a' && *end <= 'f') ||
			(*end >= 'A' && *end <= 'F'))
		{
			res *= 16;
			if (*end >= '0' && *end <= '9')
				res += *end++ - '0';
			else if (*end >= 'a' && *end <= 'f')
				res += *end++ - 'a' + 10;
			else if (*end >= 'A' && *end <= 'F')
				res += *end++ - 'A' + 10;
		}
	}

	if (byteCount)
		*byteCount = asUINT(size_t(end - val.c_str()));

	return res;
}

// AngelScript signature:
// double parseFloat(const string &in val, uint &out byteCount = 0)
double parseFloat(const string &val, asUINT *byteCount)
{
	char *end;

	// Set the locale to C so that we are guaranteed to parse the float value correctly
#if defined(_WIN32)
	// WinCE doesn't have setlocale. Some quick testing on my current platform
	// still manages to parse the numbers such as "3.14" even if the decimal for the
	// locale is ",".
#if !defined(_WIN32_WCE)
	// On Windows setlocale is made threadsafe by turning on thread local setlocale
	// ref: https://learn.microsoft.com/en-us/cpp/parallel/multithreading-and-locales?view=msvc-170&redirectedfrom=MSDN
	int oldConfig = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
	char* tmp = setlocale(LC_NUMERIC, 0);
	string orig = tmp ? tmp : "C";
	setlocale(LC_NUMERIC, "C");
#endif
#else
#if !defined(ANDROID) && !defined(__psp2__)
	// On Linux and other similar systems the threadsafe option is uselocale
	// ref: https://stackoverflow.com/questions/4057319/is-setlocale-thread-safe-function
	locale_t locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
	locale_t orig_locale = uselocale(locale);
#endif
#endif

	double res = strtod(val.c_str(), &end);

	// Restore the original locale
#if defined(_WIN32)
#if !defined(_WIN32_WCE)
	setlocale(LC_NUMERIC, orig.c_str());
	_configthreadlocale(oldConfig);
#endif
#else
#if !defined(ANDROID) && !defined(__psp2__)
#endif
	uselocale(orig_locale);
	freelocale(locale);
#endif

	if( byteCount )
		*byteCount = asUINT(size_t(end - val.c_str()));

	return res;
}

// This function returns a string containing the substring of the input string
// determined by the starting index and count of characters.
//
// AngelScript signature:
// string string::substr(uint start = 0, int count = -1) const
static string StringSubString(asUINT start, int count, const string &str)
{
	// Check for out-of-bounds
	string ret;
	if( start < str.length() && count != 0 )
		ret = str.substr(start, (size_t)(count < 0 ? string::npos : count));

	return ret;
}

// String equality comparison.
// Returns true iff lhs is equal to rhs.
//
// For some reason gcc 4.7 has difficulties resolving the
// asFUNCTIONPR(operator==, (const string &, const string &)
// makro, so this wrapper was introduced as work around.
static bool StringEquals(const std::string& lhs, const std::string& rhs)
{
	return lhs == rhs;
}

void RegisterStdString_Native(asIScriptEngine *engine)
{
	int r = 0;
	UNUSED_VAR(r);

	// Register the string type
#if AS_CAN_USE_CPP11
	// With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags to use
	r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asGetTypeTraits<string>()); assert( r >= 0 );
#else
	r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
#endif

	r = engine->RegisterStringFactory("string", GetStdStringFactorySingleton());

	// Register the object operator overloads
	r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT,  "void f()",                    asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT,  "void f(const string &in)",    asFUNCTION(CopyConstructString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT,   "void f()",                    asFUNCTION(DestructString),  asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asMETHODPR(string, operator =, (const string&), string&), asCALL_THISCALL); assert( r >= 0 );
	// Need to use a wrapper on Mac OS X 10.7/XCode 4.3 and CLang/LLVM, otherwise the linker fails
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignStringToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asMETHODPR(string, operator+=, (const string&), string&), asCALL_THISCALL); assert( r >= 0 );

	// Need to use a wrapper for operator== otherwise gcc 4.7 fails to compile
	r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTIONPR(StringEquals, (const string &, const string &), bool), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmp), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTIONPR(operator +, (const string &, const string &), string), asCALL_CDECL_OBJFIRST); assert( r >= 0 );

	// The string length can be accessed through methods or through virtual property
	// TODO: Register as size() for consistency with other types
#if AS_USE_ACCESSORS != 1
	r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 );
#endif
	r = engine->RegisterObjectMethod("string", "void resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); assert( r >= 0 );
#if AS_USE_STLNAMES != 1 && AS_USE_ACCESSORS == 1
	// Don't register these if STL names is used, as they conflict with the method size()
	r = engine->RegisterObjectMethod("string", "uint get_length() const property", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "void set_length(uint) property", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST); assert( r >= 0 );
#endif
	// Need to use a wrapper on Mac OS X 10.7/XCode 4.3 and CLang/LLVM, otherwise the linker fails
//	r = engine->RegisterObjectMethod("string", "bool isEmpty() const", asMETHOD(string, empty), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "bool isEmpty() const", asFUNCTION(StringIsEmpty), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	// Register the index operator, both as a mutator and as an inspector
	// Note that we don't register the operator[] directly, as it doesn't do bounds checking
	r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 );

#if AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE == 0
	// Automatic conversion from values
	r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddStringInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt64ToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddStringUInt64), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt64String), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBoolToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddStringBool), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBoolString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
#endif

	// Utilities
	r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "int findFirst(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "int findFirstOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstOf), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findFirstNotOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstNotOf), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findLast(const string &in, int start = -1) const", asFUNCTION(StringFindLast), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "int findLastOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastOf), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int regexFind(const string  &in regex, uint start = 0, uint &out lengthOfMatch = void) const", asFUNCTION(StringRegexFind), asCALL_CDECL_OBJLAST); assert(r >= 0);

	r = engine->RegisterGlobalFunction("uint scan(const string&in str, ?&out ...)", asFUNCTION(StringScan), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string format(const string&in fmt, const ?&in ...)", asFUNCTION(StringFormat), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt), asCALL_CDECL); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt), asCALL_CDECL); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat), asCALL_CDECL); assert(r >= 0);
	r = engine->RegisterGlobalFunction("int64 parseInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseInt), asCALL_CDECL); assert(r >= 0);
	r = engine->RegisterGlobalFunction("uint64 parseUInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseUInt), asCALL_CDECL); assert(r >= 0);
	r = engine->RegisterGlobalFunction("double parseFloat(const string &in, uint &out byteCount = 0)", asFUNCTION(parseFloat), asCALL_CDECL); assert(r >= 0);

#if AS_USE_STLNAMES == 1
	// Same as length
	r = engine->RegisterObjectMethod("string", "uint size() const", asFUNCTION(StringLength), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	// Same as isEmpty
	r = engine->RegisterObjectMethod("string", "bool empty() const", asFUNCTION(StringIsEmpty), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	// Same as findFirst
	r = engine->RegisterObjectMethod("string", "int find(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	// Same as findLast
	r = engine->RegisterObjectMethod("string", "int rfind(const string &in, int start = -1) const", asFUNCTION(StringFindLast), asCALL_CDECL_OBJLAST); assert( r >= 0 );
#endif

	// TODO: Implement the following
	// findAndReplace - replaces a text found in the string
	// replaceRange - replaces a range of bytes in the string
	// multiply/times/opMul/opMul_r - takes the string and multiplies it n times, e.g. "-".multiply(5) returns "-----"
}

static void ConstructStringGeneric(asIScriptGeneric * gen)
{
	new (gen->GetObject()) string();
}

static void CopyConstructStringGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetArgObject(0));
	new (gen->GetObject()) string(*a);
}

static void DestructStringGeneric(asIScriptGeneric * gen)
{
	string * ptr = static_cast<string *>(gen->GetObject());
	ptr->~string();
}

static void AssignStringGeneric(asIScriptGeneric *gen)
{
	string * a = static_cast<string *>(gen->GetArgObject(0));
	string * self = static_cast<string *>(gen->GetObject());
	*self = *a;
	gen->SetReturnAddress(self);
}

static void AddAssignStringGeneric(asIScriptGeneric *gen)
{
	string * a = static_cast<string *>(gen->GetArgObject(0));
	string * self = static_cast<string *>(gen->GetObject());
	*self += *a;
	gen->SetReturnAddress(self);
}

static void StringEqualsGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	string * b = static_cast<string *>(gen->GetArgAddress(0));
	*(bool*)gen->GetAddressOfReturnLocation() = (*a == *b);
}

static void StringCmpGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	string * b = static_cast<string *>(gen->GetArgAddress(0));

	int cmp = 0;
	if( *a < *b ) cmp = -1;
	else if( *a > *b ) cmp = 1;

	*(int*)gen->GetAddressOfReturnLocation() = cmp;
}

static void StringAddGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	string * b = static_cast<string *>(gen->GetArgAddress(0));
	string ret_val = *a + *b;
	gen->SetReturnObject(&ret_val);
}

static void StringLengthGeneric(asIScriptGeneric * gen)
{
	string * self = static_cast<string *>(gen->GetObject());
	*static_cast<asUINT *>(gen->GetAddressOfReturnLocation()) = (asUINT)self->length();
}

static void StringIsEmptyGeneric(asIScriptGeneric * gen)
{
	string * self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<bool *>(gen->GetAddressOfReturnLocation()) = StringIsEmpty(*self);
}

static void StringResizeGeneric(asIScriptGeneric * gen)
{
	string * self = static_cast<string *>(gen->GetObject());
	self->resize(*static_cast<asUINT *>(gen->GetAddressOfArg(0)));
}

static void StringInsert_Generic(asIScriptGeneric *gen)
{
	string * self = static_cast<string *>(gen->GetObject());
	asUINT pos = gen->GetArgDWord(0);
	string *other = reinterpret_cast<string*>(gen->GetArgAddress(1));
	StringInsert(pos, *other, *self);
}

static void StringErase_Generic(asIScriptGeneric *gen)
{
	string * self = static_cast<string *>(gen->GetObject());
	asUINT pos = gen->GetArgDWord(0);
	int count = int(gen->GetArgDWord(1));
	StringErase(pos, count, *self);
}

static void StringFindFirst_Generic(asIScriptGeneric * gen)
{
	string *find = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT start = gen->GetArgDWord(1);
	string *self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<int *>(gen->GetAddressOfReturnLocation()) = StringFindFirst(*find, start, *self);
}

static void StringFindLast_Generic(asIScriptGeneric * gen)
{
	string *find = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT start = gen->GetArgDWord(1);
	string *self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<int *>(gen->GetAddressOfReturnLocation()) = StringFindLast(*find, start, *self);
}

static void StringFindFirstOf_Generic(asIScriptGeneric * gen)
{
	string *find = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT start = gen->GetArgDWord(1);
	string *self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<int *>(gen->GetAddressOfReturnLocation()) = StringFindFirstOf(*find, start, *self);
}

static void StringFindLastOf_Generic(asIScriptGeneric * gen)
{
	string *find = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT start = gen->GetArgDWord(1);
	string *self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<int *>(gen->GetAddressOfReturnLocation()) = StringFindLastOf(*find, start, *self);
}

static void StringFindFirstNotOf_Generic(asIScriptGeneric * gen)
{
	string *find = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT start = gen->GetArgDWord(1);
	string *self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<int *>(gen->GetAddressOfReturnLocation()) = StringFindFirstNotOf(*find, start, *self);
}

static void StringFindLastNotOf_Generic(asIScriptGeneric * gen)
{
	string *find = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT start = gen->GetArgDWord(1);
	string *self = reinterpret_cast<string *>(gen->GetObject());
	*reinterpret_cast<int *>(gen->GetAddressOfReturnLocation()) = StringFindLastNotOf(*find, start, *self);
}

static void formatInt_Generic(asIScriptGeneric * gen)
{
	asINT64 val = gen->GetArgQWord(0);
	string *options = reinterpret_cast<string*>(gen->GetArgAddress(1));
	asUINT width = gen->GetArgDWord(2);
	new(gen->GetAddressOfReturnLocation()) string(formatInt(val, *options, width));
}

static void formatUInt_Generic(asIScriptGeneric * gen)
{
	asQWORD val = gen->GetArgQWord(0);
	string *options = reinterpret_cast<string*>(gen->GetArgAddress(1));
	asUINT width = gen->GetArgDWord(2);
	new(gen->GetAddressOfReturnLocation()) string(formatUInt(val, *options, width));
}

static void formatFloat_Generic(asIScriptGeneric *gen)
{
	double val = gen->GetArgDouble(0);
	string *options = reinterpret_cast<string*>(gen->GetArgAddress(1));
	asUINT width = gen->GetArgDWord(2);
	asUINT precision = gen->GetArgDWord(3);
	new(gen->GetAddressOfReturnLocation()) string(formatFloat(val, *options, width, precision));
}

static void parseInt_Generic(asIScriptGeneric *gen)
{
	string *str = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT base = gen->GetArgDWord(1);
	asUINT *byteCount = reinterpret_cast<asUINT*>(gen->GetArgAddress(2));
	gen->SetReturnQWord(parseInt(*str,base,byteCount));
}

static void parseUInt_Generic(asIScriptGeneric *gen)
{
	string *str = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT base = gen->GetArgDWord(1);
	asUINT *byteCount = reinterpret_cast<asUINT*>(gen->GetArgAddress(2));
	gen->SetReturnQWord(parseUInt(*str, base, byteCount));
}

static void parseFloat_Generic(asIScriptGeneric *gen)
{
	string *str = reinterpret_cast<string*>(gen->GetArgAddress(0));
	asUINT *byteCount = reinterpret_cast<asUINT*>(gen->GetArgAddress(1));
	gen->SetReturnDouble(parseFloat(*str,byteCount));
}

static void StringCharAtGeneric(asIScriptGeneric * gen)
{
	unsigned int index = gen->GetArgDWord(0);
	string * self = static_cast<string *>(gen->GetObject());

	if (index >= self->size())
	{
		// Set a script exception
		asIScriptContext *ctx = asGetActiveContext();
		ctx->SetException("Out of range");

		gen->SetReturnAddress(0);
	}
	else
	{
		gen->SetReturnAddress(&(self->operator [](index)));
	}
}

#if AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE == 0
static void AssignInt2StringGeneric(asIScriptGeneric *gen)
{
	asINT64 *a = static_cast<asINT64*>(gen->GetAddressOfArg(0));
	string *self = static_cast<string*>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self = sstr.str();
	gen->SetReturnAddress(self);
}

static void AssignUInt2StringGeneric(asIScriptGeneric *gen)
{
	asQWORD *a = static_cast<asQWORD*>(gen->GetAddressOfArg(0));
	string *self = static_cast<string*>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self = sstr.str();
	gen->SetReturnAddress(self);
}

static void AssignDouble2StringGeneric(asIScriptGeneric *gen)
{
	double *a = static_cast<double*>(gen->GetAddressOfArg(0));
	string *self = static_cast<string*>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self = sstr.str();
	gen->SetReturnAddress(self);
}

static void AssignFloat2StringGeneric(asIScriptGeneric *gen)
{
	float *a = static_cast<float*>(gen->GetAddressOfArg(0));
	string *self = static_cast<string*>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self = sstr.str();
	gen->SetReturnAddress(self);
}

static void AssignBool2StringGeneric(asIScriptGeneric *gen)
{
	bool *a = static_cast<bool*>(gen->GetAddressOfArg(0));
	string *self = static_cast<string*>(gen->GetObject());
	std::stringstream sstr;
	sstr << (*a ? "true" : "false");
	*self = sstr.str();
	gen->SetReturnAddress(self);
}

static void AddAssignDouble2StringGeneric(asIScriptGeneric * gen)
{
	double * a = static_cast<double *>(gen->GetAddressOfArg(0));
	string * self = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self += sstr.str();
	gen->SetReturnAddress(self);
}

static void AddAssignFloat2StringGeneric(asIScriptGeneric * gen)
{
	float * a = static_cast<float *>(gen->GetAddressOfArg(0));
	string * self = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self += sstr.str();
	gen->SetReturnAddress(self);
}

static void AddAssignInt2StringGeneric(asIScriptGeneric * gen)
{
	asINT64 * a = static_cast<asINT64 *>(gen->GetAddressOfArg(0));
	string * self = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self += sstr.str();
	gen->SetReturnAddress(self);
}

static void AddAssignUInt2StringGeneric(asIScriptGeneric * gen)
{
	asQWORD * a = static_cast<asQWORD *>(gen->GetAddressOfArg(0));
	string * self = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a;
	*self += sstr.str();
	gen->SetReturnAddress(self);
}

static void AddAssignBool2StringGeneric(asIScriptGeneric * gen)
{
	bool * a = static_cast<bool *>(gen->GetAddressOfArg(0));
	string * self = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << (*a ? "true" : "false");
	*self += sstr.str();
	gen->SetReturnAddress(self);
}

static void AddString2DoubleGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	double * b = static_cast<double *>(gen->GetAddressOfArg(0));
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddString2FloatGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	float * b = static_cast<float *>(gen->GetAddressOfArg(0));
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddString2IntGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	asINT64 * b = static_cast<asINT64 *>(gen->GetAddressOfArg(0));
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddString2UIntGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	asQWORD * b = static_cast<asQWORD *>(gen->GetAddressOfArg(0));
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddString2BoolGeneric(asIScriptGeneric * gen)
{
	string * a = static_cast<string *>(gen->GetObject());
	bool * b = static_cast<bool *>(gen->GetAddressOfArg(0));
	std::stringstream sstr;
	sstr << *a << (*b ? "true" : "false");
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddDouble2StringGeneric(asIScriptGeneric * gen)
{
	double* a = static_cast<double *>(gen->GetAddressOfArg(0));
	string * b = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddFloat2StringGeneric(asIScriptGeneric * gen)
{
	float* a = static_cast<float *>(gen->GetAddressOfArg(0));
	string * b = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddInt2StringGeneric(asIScriptGeneric * gen)
{
	asINT64* a = static_cast<asINT64 *>(gen->GetAddressOfArg(0));
	string * b = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddUInt2StringGeneric(asIScriptGeneric * gen)
{
	asQWORD* a = static_cast<asQWORD *>(gen->GetAddressOfArg(0));
	string * b = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << *a << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}

static void AddBool2StringGeneric(asIScriptGeneric * gen)
{
	bool* a = static_cast<bool *>(gen->GetAddressOfArg(0));
	string * b = static_cast<string *>(gen->GetObject());
	std::stringstream sstr;
	sstr << (*a ? "true" : "false") << *b;
	std::string ret_val = sstr.str();
	gen->SetReturnObject(&ret_val);
}
#endif

static void StringSubString_Generic(asIScriptGeneric *gen)
{
	// Get the arguments
	string *str   = (string*)gen->GetObject();
	asUINT  start = *(int*)gen->GetAddressOfArg(0);
	int     count = *(int*)gen->GetAddressOfArg(1);

	// Return the substring
	new(gen->GetAddressOfReturnLocation()) string(StringSubString(start, count, *str));
}

void RegisterStdString_Generic(asIScriptEngine *engine)
{
	int r = 0;
	UNUSED_VAR(r);

	// Register the string type
	r = engine->RegisterObjectType("string", sizeof(string), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );

	r = engine->RegisterStringFactory("string", GetStdStringFactorySingleton());

	// Register the object operator overloads
	r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT,  "void f()",                    asFUNCTION(ConstructStringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("string", asBEHAVE_CONSTRUCT,  "void f(const string &in)",    asFUNCTION(CopyConstructStringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("string", asBEHAVE_DESTRUCT,   "void f()",                    asFUNCTION(DestructStringGeneric),  asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAssign(const string &in)", asFUNCTION(AssignStringGeneric),    asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(const string &in)", asFUNCTION(AddAssignStringGeneric), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "bool opEquals(const string &in) const", asFUNCTION(StringEqualsGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "int opCmp(const string &in) const", asFUNCTION(StringCmpGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(const string &in) const", asFUNCTION(StringAddGeneric), asCALL_GENERIC); assert( r >= 0 );

	// Register the object methods
#if AS_USE_ACCESSORS != 1
	r = engine->RegisterObjectMethod("string", "uint length() const", asFUNCTION(StringLengthGeneric), asCALL_GENERIC); assert( r >= 0 );
#endif
	r = engine->RegisterObjectMethod("string", "void resize(uint)",   asFUNCTION(StringResizeGeneric), asCALL_GENERIC); assert( r >= 0 );
#if AS_USE_STLNAMES != 1 && AS_USE_ACCESSORS == 1
	r = engine->RegisterObjectMethod("string", "uint get_length() const property", asFUNCTION(StringLengthGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "void set_length(uint) property", asFUNCTION(StringResizeGeneric), asCALL_GENERIC); assert( r >= 0 );
#endif
	r = engine->RegisterObjectMethod("string", "bool isEmpty() const", asFUNCTION(StringIsEmptyGeneric), asCALL_GENERIC); assert( r >= 0 );

	// Register the index operator, both as a mutator and as an inspector
	r = engine->RegisterObjectMethod("string", "uint8 &opIndex(uint)", asFUNCTION(StringCharAtGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAtGeneric), asCALL_GENERIC); assert( r >= 0 );

#if AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE == 0
	// Automatic conversion from values
	r = engine->RegisterObjectMethod("string", "string &opAssign(double)", asFUNCTION(AssignDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(double)", asFUNCTION(AddAssignDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(double) const", asFUNCTION(AddString2DoubleGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(double) const", asFUNCTION(AddDouble2StringGeneric), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(float)", asFUNCTION(AssignFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(float)", asFUNCTION(AddAssignFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(float) const", asFUNCTION(AddString2FloatGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(float) const", asFUNCTION(AddFloat2StringGeneric), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(int64)", asFUNCTION(AssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(int64)", asFUNCTION(AddAssignInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(int64) const", asFUNCTION(AddString2IntGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(int64) const", asFUNCTION(AddInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(uint64)", asFUNCTION(AssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(uint64)", asFUNCTION(AddAssignUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(uint64) const", asFUNCTION(AddString2UIntGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(uint64) const", asFUNCTION(AddUInt2StringGeneric), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("string", "string &opAssign(bool)", asFUNCTION(AssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string &opAddAssign(bool)", asFUNCTION(AddAssignBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd(bool) const", asFUNCTION(AddString2BoolGeneric), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("string", "string opAdd_r(bool) const", asFUNCTION(AddBool2StringGeneric), asCALL_GENERIC); assert( r >= 0 );
#endif

	r = engine->RegisterObjectMethod("string", "string substr(uint start = 0, int count = -1) const", asFUNCTION(StringSubString_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findFirst(const string &in, uint start = 0) const", asFUNCTION(StringFindFirst_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findFirstOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstOf_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findFirstNotOf(const string &in, uint start = 0) const", asFUNCTION(StringFindFirstNotOf_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findLast(const string &in, int start = -1) const", asFUNCTION(StringFindLast_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findLastOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastOf_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase_Generic), asCALL_GENERIC); assert(r >= 0);

	r = engine->RegisterGlobalFunction("uint scan(const string&in str, ?&out ...)", asFUNCTION(StringScan), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string format(const string&in fmt, const ?&in ...)", asFUNCTION(StringFormat), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("int64 parseInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseInt_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("uint64 parseUInt(const string &in, uint base = 10, uint &out byteCount = 0)", asFUNCTION(parseUInt_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterGlobalFunction("double parseFloat(const string &in, uint &out byteCount = 0)", asFUNCTION(parseFloat_Generic), asCALL_GENERIC); assert(r >= 0);
}

void RegisterStdString(asIScriptEngine * engine)
{
	if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY"))
		RegisterStdString_Generic(engine);
	else
		RegisterStdString_Native(engine);
}

END_AS_NAMESPACE


code:
//
// Script std::string
//
// This function registers the std::string type with AngelScript to be used as the default string type.
//
// The string type is registered as a value type, thus may have performance issues if a lot of
// string operations are performed in the script. However, for relatively few operations, this should
// not cause any problem for most applications.
//

#ifndef SCRIPTSTDSTRING_H
#define SCRIPTSTDSTRING_H

#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif

#include <string>

//---------------------------
// Compilation settings
//

// Sometimes it may be desired to use the same method names as used by C++ STL.
// This may for example reduce time when converting code from script to C++ or
// back.
//
//  0 = off
//  1 = on
#ifndef AS_USE_STLNAMES
#define AS_USE_STLNAMES 0
#endif

// Some prefer to use property accessors to get/set the length of the string
// This option registers the accessors instead of the method length()
#ifndef AS_USE_ACCESSORS
#define AS_USE_ACCESSORS 0
#endif

// This option disables the implicit operators with primitives
#ifndef AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE
#define AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE 0
#endif

BEGIN_AS_NAMESPACE

void RegisterStdString(asIScriptEngine *engine);
void RegisterStdStringUtils(asIScriptEngine *engine);

END_AS_NAMESPACE

#endif


code:
#include <assert.h>
#include "scriptstdstring.h"
#include "../scriptarray/scriptarray.h"
#include <stdio.h>
#include <string.h>

using namespace std;

BEGIN_AS_NAMESPACE

// This function takes an input string and splits it into parts by looking
// for a specified delimiter. Example:
//
// string str = "A|B||D";
// array<string>@ arr = str.split("|");
//
// The resulting array has the following elements:
//
// {"A", "B", "", "D"}
//
// AngelScript signature:
// array<string>@ string::split(const string &in delim) const
static CScriptArray *StringSplit(const string &delim, const string &str)
{
	// Obtain a pointer to the engine
	asIScriptContext *ctx = asGetActiveContext();
	asIScriptEngine *engine = ctx->GetEngine();

	// TODO: This should only be done once
	// TODO: This assumes that CScriptArray was already registered
	asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");

	// Create the array object
	CScriptArray *array = CScriptArray::Create(arrayType);

	// Find the existence of the delimiter in the input string
	size_t pos = 0, prev = 0;
	asUINT count = 0;
	while( (pos = str.find(delim, prev)) != string::npos )
	{
		// Add the part to the array
		array->Resize(array->GetSize()+1);
		((string*)array->At(count))->assign(&str[prev], pos-prev);

		// Find the next part
		count++;
		prev = pos + delim.length();
	}

	// Add the remaining part
	array->Resize(array->GetSize()+1);
	((string*)array->At(count))->assign(&str[prev]);

	return array;
}

static void StringSplit_Generic(asIScriptGeneric *gen)
{
	// Get the arguments
	string *str   = (string*)gen->GetObject();
	string *delim = *(string**)gen->GetAddressOfArg(0);

	// Return the array by handle
	*(CScriptArray**)gen->GetAddressOfReturnLocation() = StringSplit(*delim, *str);
}

// This function takes as input an array of string handles as well as a
// delimiter and concatenates the array elements into one delimited string.
// Example:
//
// array<string> arr = {"A", "B", "", "D"};
// string str = join(arr, "|");
//
// The resulting string is:
//
// "A|B||D"
//
// AngelScript signature:
// string join(const array<string> &in arr, const string &in delim)
static string StringJoin(const CScriptArray &array, const string &delim)
{
	// Create the new string
	string str = "";
	if( array.GetSize() )
	{
		int n;
		for( n = 0; n < (int)array.GetSize() - 1; n++ )
		{
			str += *(const string*)array.At(n);
			str += delim;
		}

		// Add the last part
		str += *(const string*)array.At(n);
	}

	return str;
}

static void StringJoin_Generic(asIScriptGeneric *gen)
{
	// Get the arguments
	CScriptArray  *array = *(CScriptArray**)gen->GetAddressOfArg(0);
	string *delim = *(string**)gen->GetAddressOfArg(1);

	// Return the string
	new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim));
}

// This is where the utility functions are registered.
// The string type must have been registered first.
void RegisterStdStringUtils(asIScriptEngine *engine)
{
	int r;

	if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
	{
		r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0);
		r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0);
	}
	else
	{
		r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0);
		r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0);
	}
}

END_AS_NAMESPACE


code:
//
// CSerializer
//
// This code was based on the CScriptReloader written by FDsagizi
// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
//

#include <assert.h>
#include <string.h> // strstr
#include <stdio.h>  // sprintf
#include "serializer.h"

using namespace std;

BEGIN_AS_NAMESPACE

///////////////////////////////////////////////////////////////////////////////////

CSerializer::CSerializer()
{
	m_engine = 0;
}

CSerializer::~CSerializer()
{
	Clear();

	// Delete the user types
	std::map<std::string, CUserType*>::iterator it;
	for (it = m_userTypes.begin(); it != m_userTypes.end(); it++)
		delete it->second;
}

void CSerializer::Clear()
{
	// Extra objects need to be released, since they are not stored in
	// the module and we cannot rely on the application releasing them
	for (size_t i = 0; i < m_extraObjects.size(); i++)
	{
		SExtraObject& o = m_extraObjects[i];
		for (size_t i2 = 0; i2 < m_root.m_children.size(); i2++)
		{
			if (m_root.m_children[i2]->m_originalPtr == o.originalObject && m_root.m_children[i2]->m_restorePtr)
				reinterpret_cast<asIScriptObject*>(m_root.m_children[i2]->m_restorePtr)->Release();
		}
	}
	m_extraObjects.resize(0);

	// Clean the serialized values before we remove the user types
	m_root.Uninit();

	if (m_engine)
		m_engine->Release();
	m_engine = 0;
}

void CSerializer::AddUserType(CUserType *ref, const std::string &name)
{
	m_userTypes[name] = ref;
}

int CSerializer::Store(asIScriptModule *mod)
{
	m_mod = mod;

	// The engine must not be destroyed before we're completed, so we'll hold on to a reference
	mod->GetEngine()->AddRef();
	if( m_engine ) m_engine->Release();
	m_engine = mod->GetEngine();

	m_root.m_serializer = this;

	// First store global variables
	asUINT i;
	for( i = 0; i < mod->GetGlobalVarCount(); i++ )
	{
		const char *name, *nameSpace;
		int typeId;
		mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
		m_root.m_children.push_back(new CSerializedValue(&m_root, name, nameSpace, mod->GetAddressOfGlobalVar(i), typeId));
	}

	// Second store extra objects
	for( i = 0; i < m_extraObjects.size(); i++ )
		m_root.m_children.push_back(new CSerializedValue(&m_root, "", "", m_extraObjects[i].originalObject, m_extraObjects[i].originalTypeId));

	// For the handles that were stored, we need to substitute the stored pointer
	// that is still pointing to the original object to an internal reference so
	// it can be restored later on.
	m_root.ReplaceHandles();

	return 0;
}

// Retrieve all global variables after reload script.
int CSerializer::Restore(asIScriptModule *mod)
{
	m_mod = mod;

	// The engine must not be destroyed before we're completed, so we'll hold on to a reference
	mod->GetEngine()->AddRef();
	if( m_engine ) m_engine->Release();
	m_engine = mod->GetEngine();

	// First restore extra objects, i.e. the ones that are not directly seen from the module's global variables
	asUINT i;
	for( i = 0; i < m_extraObjects.size(); i++ )
	{
		SExtraObject &o = m_extraObjects[i];
		asITypeInfo *type = m_mod->GetTypeInfoByName( o.originalClassName.c_str() );
		if( type )
		{
			for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
			{
				if( m_root.m_children[i2]->m_originalPtr == o.originalObject )
				{
					// Create a new script object, but don't call its constructor as we will initialize the members.
					// Calling the constructor may have unwanted side effects if for example the constructor changes
					// any outside entities, such as setting global variables to point to new objects, etc.
					void *newPtr = m_engine->CreateUninitializedScriptObject( type );
					m_root.m_children[i2]->Restore( newPtr, type->GetTypeId() );
				}
			}
		}
	}

	// Second restore the global variables
	asUINT varCount = mod->GetGlobalVarCount();
	for( i = 0; i < varCount; i++ )
	{
		const char *name, *nameSpace;
		int typeId;
		mod->GetGlobalVar(i, &name, &nameSpace, &typeId);

		CSerializedValue *v = m_root.FindByName(name, nameSpace);
		if( v )
			v->Restore(mod->GetAddressOfGlobalVar(i), typeId);
	}

	// The handles that were restored needs to be
	// updated to point to their final objects.
	m_root.RestoreHandles();

	return 0;
}

void *CSerializer::GetPointerToRestoredObject(void *ptr)
{
	return m_root.GetPointerToRestoredObject( ptr );
}

void CSerializer::AddExtraObjectToStore( asIScriptObject *object )
{
	if( !object )
		return;

	// Check if the object hasn't been included already
	for( size_t i=0; i < m_extraObjects.size(); i++ )
		if( m_extraObjects[i].originalObject == object )
			return;

	SExtraObject o;
	o.originalObject    = object;
	o.originalClassName = object->GetObjectType()->GetName();
	o.originalTypeId    = object->GetTypeId();

	m_extraObjects.push_back( o );
}

///////////////////////////////////////////////////////////////////////////////////

CSerializedValue::CSerializedValue()
{
	Init();
}

CSerializedValue::CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId)
{
	Init();

	m_name       = name;
	m_nameSpace  = nameSpace;
	m_serializer = parent->m_serializer;
	Store(ref, typeId);
}

void CSerializedValue::Init()
{
	m_handlePtr   = 0;
	m_restorePtr  = 0;
	m_typeId      = 0;
	m_isInit      = false;
	m_serializer  = 0;
	m_userData    = 0;
	m_originalPtr = 0;
}

void CSerializedValue::Uninit()
{
	m_isInit = false;

	ClearChildren();

	if( m_userData )
	{
		CUserType *type = m_serializer->m_userTypes[m_typeName];
		if( type )
			type->CleanupUserData(this);
		m_userData = 0;
	}
}

void CSerializedValue::ClearChildren()
{
	// If this value is for an object handle that created an object during the restore
	// then it is necessary to release the handle here, so we won't get a memory leak
	if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 && m_children[0]->m_restorePtr )
	{
		m_serializer->m_engine->ReleaseScriptObject(m_children[0]->m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_children[0]->m_typeId));
	}

	for( size_t n = 0; n < m_children.size(); n++ )
		delete m_children[n];
	m_children.clear();
}

CSerializedValue::~CSerializedValue()
{
	Uninit();
}

CSerializedValue *CSerializedValue::FindByName(const std::string &name, const std::string &nameSpace)
{
	for( size_t i = 0; i < m_children.size(); i++ )
		if( m_children[i]->m_name      == name &&
			m_children[i]->m_nameSpace == nameSpace )
			return m_children[i];

	return 0;
}

void  CSerializedValue::GetAllPointersOfChildren(std::vector<void*> *ptrs)
{
	ptrs->push_back(m_originalPtr);

	for( size_t i = 0; i < m_children.size(); ++i )
		m_children[i]->GetAllPointersOfChildren(ptrs);
}

CSerializedValue *CSerializedValue::FindByPtr(void *ptr)
{
	if( m_originalPtr == ptr )
		return this;

	for( size_t i = 0; i < m_children.size(); i++ )
	{
		CSerializedValue *find = m_children[i]->FindByPtr(ptr);
		if( find )
			return find;
	}

	return 0;
}

void *CSerializedValue::GetPointerToRestoredObject(void *ptr)
{
	if( m_originalPtr == ptr )
		return m_restorePtr;

	for( size_t i = 0; i < m_children.size(); ++i )
	{
		void *ret = m_children[i]->GetPointerToRestoredObject(ptr);
		if( ret )
			return ret;
	}

	return 0;
}

// find variable by ptr but looking only at those in the references, which will create a new object
CSerializedValue *CSerializedValue::FindByPtrInHandles(void *ptr)
{
	// if this handle created object
	if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 )
	{
		if( m_children[0]->m_originalPtr == ptr )
			return this;
	}

	if( !(m_typeId & asTYPEID_OBJHANDLE) )
	{
		for( size_t i = 0; i < m_children.size(); i++ )
		{
			CSerializedValue *find = m_children[i]->FindByPtrInHandles(ptr);
			if( find )
				return find;
		}
	}

	return 0;
}

void CSerializedValue::Store(void *ref, int typeId)
{
	m_isInit = true;
	SetType(typeId);
	m_originalPtr = ref;

	if( m_typeId & asTYPEID_OBJHANDLE )
	{
		m_handlePtr = *(void**)ref;
	}
	else if( m_typeId & asTYPEID_SCRIPTOBJECT )
	{
		asIScriptObject *obj = (asIScriptObject *)ref;
		asITypeInfo *type = obj->GetObjectType();
		SetType(type->GetTypeId());

		// Store children
		for( asUINT i = 0; i < type->GetPropertyCount(); i++ )
		{
			int childId;
			const char *childName;
			type->GetProperty(i, &childName, &childId);

			m_children.push_back(new CSerializedValue(this, childName, "", obj->GetAddressOfProperty(i), childId));
		}
	}
	else
	{
		int size = m_serializer->m_engine->GetSizeOfPrimitiveType(m_typeId);

		if( size == 0 )
		{
			// if it is user type( string, array, etc ... )
			if( m_serializer->m_userTypes[m_typeName] )
				m_serializer->m_userTypes[m_typeName]->Store(this, m_originalPtr);
			else
			{
				// POD-types can be stored without need for user type
				asITypeInfo *type = GetType();
				if( type && (type->GetFlags() & asOBJ_POD) )
					size = GetType()->GetSize();

				// It is not necessary to report an error here if it is not a POD-type as that will be done when restoring
			}
		}

		if( size )
		{
			m_mem.resize(size);
			memcpy(&m_mem[0], ref, size);
		}
	}
}

void CSerializedValue::Restore(void *ref, int typeId)
{
	if( !m_isInit || !ref )
		return;

	// Verify that the stored type matched the new type of the value being restored
	if( typeId <= asTYPEID_DOUBLE && typeId != m_typeId ) return; // TODO: We may try to do a type conversion for primitives
	if( (typeId & ~asTYPEID_MASK_SEQNBR) ^ (m_typeId & ~asTYPEID_MASK_SEQNBR) ) return;
	asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
	if( type && m_typeName != type->GetName() ) return;

	// Set the new pointer and type
	m_restorePtr = ref;
	SetType(typeId);

	// Restore the value
	if( m_typeId & asTYPEID_OBJHANDLE )
	{
		// if need create objects
		if( m_children.size() == 1 )
		{
			asITypeInfo *ctype = m_children[0]->GetType();

			if( ctype->GetFactoryCount() == 0 )
			{
				// There are no factories, so assume the same pointer is going to be used
				m_children[0]->m_restorePtr = m_handlePtr;

				// Increase the refCount for the object as it will be released upon clean-up
				m_serializer->m_engine->AddRefScriptObject(m_handlePtr, ctype);
			}
			else
			{
				// Create a new script object, but don't call its constructor as we will initialize the members.
				// Calling the constructor may have unwanted side effects if for example the constructor changes
				// any outside entities, such as setting global variables to point to new objects, etc.
				void* newObject = 0;
				if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
					newObject = m_serializer->m_engine->CreateUninitializedScriptObject(ctype);
				// Allocate an unitialized object for the custom type
				else if (m_serializer->m_userTypes[m_typeName])
				{
					newObject = m_serializer->m_userTypes[m_typeName]->AllocateUnitializedMemory(this);
				}
				else
				{
					std::string str = "Cannot allocate memory for type '";
					str += type->GetName();
					str += "'";
					m_serializer->m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.c_str());
				}

				m_children[0]->Restore(newObject, ctype->GetTypeId());
			}
		}
	}
	else if( m_typeId & asTYPEID_SCRIPTOBJECT )
	{
		asIScriptObject *obj = (asIScriptObject *)ref;

		// Retrieve children
		for( asUINT i = 0; i < type->GetPropertyCount() ; i++ )
		{
			const char *nameProperty;
			int ptypeId;
			type->GetProperty(i, &nameProperty, &ptypeId);

			CSerializedValue *var = FindByName(nameProperty, "");
			if( var )
				var->Restore(obj->GetAddressOfProperty(i), ptypeId);
		}
	}
	else
	{
		if( m_mem.size() )
		{
			// POD values can be restored with direct copy
			memcpy(ref, &m_mem[0], m_mem.size());
		}
		else if( m_serializer->m_userTypes[m_typeName] )
		{
			// user type restore
			m_serializer->m_userTypes[m_typeName]->Restore(this, m_restorePtr);
		}
		else
		{
			std::string str = "Cannot restore type '";
			str += type->GetName();
			str += "'";
			m_serializer->m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.c_str());
		}
	}
}

void CSerializedValue::CancelDuplicates(CSerializedValue *from)
{
	std::vector<void*> ptrs;
	from->GetAllPointersOfChildren(&ptrs);

	for( size_t i = 0; i < ptrs.size(); ++i )
	{
		CSerializedValue *find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);

		while( find )
		{
			// cancel create object
			find->ClearChildren();

			// Find next link to this ptr
			find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
		}
	}
}

void CSerializedValue::ReplaceHandles()
{
	if( m_handlePtr )
	{
		// Find the object that the handle is referring to
		CSerializedValue *handle_to = m_serializer->m_root.FindByPtr(m_handlePtr);

		// If the object hasn't been stored yet...
		if( handle_to == 0 )
		{
			// Store the object now
			asITypeInfo *type = GetType();
			CSerializedValue *need_create = new CSerializedValue(this, m_name, m_nameSpace, m_handlePtr, type->GetTypeId());

			// Make sure all other handles that point to the same object
			// are updated, so we don't end up creating duplicates
			CancelDuplicates(need_create);

			m_children.push_back(need_create);
		}
	}

	// Replace the handles in the children too
	for( size_t i = 0; i < m_children.size(); ++i )
		m_children[i]->ReplaceHandles();
}

void CSerializedValue::RestoreHandles()
{
	if( m_typeId & asTYPEID_OBJHANDLE )
	{
		if( m_handlePtr )
		{
			// Find the object the handle is supposed to point to
			CSerializedValue *handleTo = m_serializer->m_root.FindByPtr(m_handlePtr);

			if( m_restorePtr && handleTo && handleTo->m_restorePtr )
			{
				asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(m_typeId);

				// If the handle is already pointing to something it must be released first
				if( *(void**)m_restorePtr )
					m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, type);

				// Update the internal pointer
				*(void**)m_restorePtr = handleTo->m_restorePtr;

				// Increase the reference
				m_serializer->m_engine->AddRefScriptObject(handleTo->m_restorePtr, type);
			}
		}
		else
		{
			// If the handle is pointing to something, we must release it to restore the null pointer
			if( m_restorePtr && *(void**)m_restorePtr )
			{
				m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_typeId));
				*(void**)m_restorePtr = 0;
			}
		}
	}

	// Do the same for the children
	for( size_t i = 0; i < m_children.size(); ++i )
		m_children[i]->RestoreHandles();
}

void CSerializedValue::SetType(int typeId)
{
	m_typeId = typeId;

	asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);

	if (type)
	{
		m_typeName = type->GetName();
		m_typeDecl = m_serializer->m_engine->GetTypeDeclaration(typeId, true); // For templates we need the full declaration, not just the name
	}
}

asITypeInfo *CSerializedValue::GetType()
{
	if( !m_typeName.empty() )
	{
		int newTypeId = m_serializer->m_mod->GetTypeIdByDecl(m_typeDecl.c_str());
		return m_serializer->m_engine->GetTypeInfoById(newTypeId);
	}

	return 0;
}

void CSerializedValue::SetUserData(void *data)
{
	m_userData = data;
}

void *CSerializedValue::GetUserData()
{
	return m_userData;
}

END_AS_NAMESPACE


code:
//
// CSerializer
//
// This code was based on the CScriptReloader written by FDsagizi
// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
//

#ifndef SERIALIZER_H
#define SERIALIZER_H

#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif

#include <vector>
#include <string>
#include <map>

BEGIN_AS_NAMESPACE

class CSerializer;
class CSerializedValue;

// Need for register user types objects
// string, any, array... for all object
// user ref type.
struct CUserType
{
	virtual ~CUserType() {};

	virtual void *AllocateUnitializedMemory(CSerializedValue* val) = 0;
	virtual void  Store(CSerializedValue *val, void *ptr) = 0;
	virtual void  Restore(CSerializedValue *val, void *ptr) = 0;
	virtual void  CleanupUserData(CSerializedValue * /*val*/) {}
};

class CSerializedValue
{
public:
	CSerializedValue();
	CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId);
	~CSerializedValue();

	// Save the object and its children
	void Store(void *ref, int refTypeId);

	// Restore the object and its children
	void Restore(void *ref, int refTypeId);

	// Set type of this var
	void SetType(int typeId);

	// Returns the object type for non-primitives
	asITypeInfo *GetType();

	// Get child by name variable
	CSerializedValue *FindByName(const std::string &name, const std::string &nameSpace);

	// Find variable by ptr
	CSerializedValue *FindByPtr(void *ptr);

	// User data
	void *GetUserData();
	void  SetUserData(void *data);

	// Children, e.g. properties of a script class, or elements
	// of an array, or object pointed to by a handle unless it
	// is already a variable)
	std::vector<CSerializedValue*> m_children;

protected:
	friend class CSerializer;

	void Init();
	void Uninit();

	// you first need to save all the objects before you can save references to objects
	void ReplaceHandles();

	// After the objects has been restored, the handles needs to
	// be updated to point to the right objects
	void RestoreHandles();

	// Recursively get all ptrs of the children
	void  GetAllPointersOfChildren(std::vector<void*> *ptrs);

	// may be that the two references refer to the same variable.
	// But this variable is not available in the global list.
	// According to this reference will be restores it.
	// And so two links are not created 2 variables,
	// it is necessary to cancel the creation of one of them.
	void CancelDuplicates(CSerializedValue *from);

	// Find variable by ptr but looking only at those in the references, which will create a new object
	CSerializedValue *FindByPtrInHandles(void *ptr);

	// ptr  - is a handle to class
	void *GetPointerToRestoredObject(void *ptr);

	// Cleanup children
	void ClearChildren();

	// The serializer object
	CSerializer *m_serializer;

	// The user data can be used by CUserType to store extra information
	void *m_userData;

	// The type id of the stored value
	int m_typeId;

	// For non-primitives the typeId may change if the module is reloaded so
	// it is necessary to store the type name to determine the new type id
	std::string m_typeName;
	std::string m_typeDecl;

	// Name of variable or property
	std::string m_name;
	std::string m_nameSpace;

	// Is initialized
	bool m_isInit;

	// 'this' pointer to variable.
	// While storing, this points to the actual variable that was stored.
	// While restoring, it is just a unique identifier.
	void *m_originalPtr;

	// where handle references
	// While storing, this points to the actual object.
	// While restoring, it is just a unique identifier.
	void *m_handlePtr;

	// new address object, ie address the restoration
	// While storing this isn't used.
	// While restoring it will point to the actual variable/object that is restored.
	void *m_restorePtr;

	// Serialized data for primitives
	std::vector<char> m_mem;
};

// This class keeps a list of variables, then restores them after the script is rebuilt.
// But you have to be careful with the change of signature in classes, or
// changing the types of objects. You can remove or add variables, functions,
// methods, but you can not (yet) change the type of variables.
//
// You also need to understand that after a rebuild you should get
// new functions and typeids from the module.
class CSerializer
{
public:
	CSerializer();
	~CSerializer();

	// Clear the serializer to free references held internally
	void Clear();

	// Add implementation for serializing user types
	void AddUserType(CUserType *ref, const std::string &name);

	// Store all global variables in the module
	int Store(asIScriptModule *mod);

	// Restore all global variables after reloading script
	int Restore(asIScriptModule *mod);

	// Store extra objects that are not seen from the module's global variables
	void AddExtraObjectToStore(asIScriptObject *object);

	// Return new pointer to restored object
	void *GetPointerToRestoredObject(void *originalObject);

protected:
	friend class CSerializedValue;

	CSerializedValue  m_root;
	asIScriptEngine  *m_engine;
	asIScriptModule  *m_mod;

	std::map<std::string, CUserType*> m_userTypes;

	struct SExtraObject
	{
		asIScriptObject *originalObject;
		std::string      originalClassName;
		int              originalTypeId;
	};

	std::vector<SExtraObject> m_extraObjects;
};

END_AS_NAMESPACE

#endif