code:
#include <string.h>
#include "scripthelper.h"
#include <assert.h>
#include <stdio.h>
#include <fstream>
#include <set>
#include <stdlib.h>
#include "../autowrapper/aswrappedcall.h"

using namespace std;

BEGIN_AS_NAMESPACE

int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result)
{
    // TODO: If a lot of script objects are going to be compared, e.g. when sorting an array,
    //       then the method id and context should be cached between calls.

	int retval = -1;
	asIScriptFunction *func = 0;

	asITypeInfo *ti = engine->GetTypeInfoById(typeId);
	if( ti )
	{
		// Check if the object type has a compatible opCmp method
		for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
		{
			asIScriptFunction *f = ti->GetMethodByIndex(n);
			asDWORD flags;
			if( strcmp(f->GetName(), "opCmp") == 0 &&
				f->GetReturnTypeId(&flags) == asTYPEID_INT32 &&
				flags == asTM_NONE &&
				f->GetParamCount() == 1 )
			{
				int paramTypeId;
				f->GetParam(0, &paramTypeId, &flags);

				// The parameter must be an input reference of the same type
				// If the reference is a inout reference, then it must also be read-only
				if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
					break;

				// Found the method
				func = f;
				break;
			}
		}
	}

	if( func )
	{
		// Call the method
		asIScriptContext *ctx = engine->CreateContext();
		ctx->Prepare(func);
		ctx->SetObject(lobj);
		ctx->SetArgAddress(0, robj);
		int r = ctx->Execute();
		if( r == asEXECUTION_FINISHED )
		{
			result = (int)ctx->GetReturnDWord();

			// The comparison was successful
			retval = 0;
		}
		ctx->Release();
	}

	return retval;
}

int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result)
{
    // TODO: If a lot of script objects are going to be compared, e.g. when searching for an
	//       entry in a set, then the method and context should be cached between calls.

	int retval = -1;
	asIScriptFunction *func = 0;

	asITypeInfo *ti = engine->GetTypeInfoById(typeId);
	if( ti )
	{
		// Check if the object type has a compatible opEquals method
		for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
		{
			asIScriptFunction *f = ti->GetMethodByIndex(n);
			asDWORD flags;
			if( strcmp(f->GetName(), "opEquals") == 0 &&
				f->GetReturnTypeId(&flags) == asTYPEID_BOOL &&
				flags == asTM_NONE &&
				f->GetParamCount() == 1 )
			{
				int paramTypeId;
				f->GetParam(0, &paramTypeId, &flags);

				// The parameter must be an input reference of the same type
				// If the reference is a inout reference, then it must also be read-only
				if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
					break;

				// Found the method
				func = f;
				break;
			}
		}
	}

	if( func )
	{
		// Call the method
		asIScriptContext *ctx = engine->CreateContext();
		ctx->Prepare(func);
		ctx->SetObject(lobj);
		ctx->SetArgAddress(0, robj);
		int r = ctx->Execute();
		if( r == asEXECUTION_FINISHED )
		{
			result = ctx->GetReturnByte() ? true : false;

			// The comparison was successful
			retval = 0;
		}
		ctx->Release();
	}
	else
	{
		// If the opEquals method doesn't exist, then we try with opCmp instead
		int relation;
		retval = CompareRelation(engine, lobj, robj, typeId, relation);
		if( retval >= 0 )
			result = relation == 0 ? true : false;
	}

	return retval;
}

int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod, asIScriptContext *ctx)
{
	return ExecuteString(engine, code, 0, asTYPEID_VOID, mod, ctx);
}

int ExecuteString(asIScriptEngine *engine, const char *code, void *ref, int refTypeId, asIScriptModule *mod, asIScriptContext *ctx)
{
	// Wrap the code in a function so that it can be compiled and executed
	string funcCode = " ExecuteString() {\n";
	funcCode += code;
	funcCode += "\n;}";

	// Determine the return type based on the type of the ref arg
	funcCode = engine->GetTypeDeclaration(refTypeId, true) + funcCode;

	// GetModule will free unused types, so to be on the safe side we'll hold on to a reference to the type
	asITypeInfo *type = 0;
	if( refTypeId & asTYPEID_MASK_OBJECT )
	{
		type = engine->GetTypeInfoById(refTypeId);
		if( type )
			type->AddRef();
	}

	// If no module was provided, get a dummy from the engine
	asIScriptModule *execMod = mod ? mod : engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE);

	// Now it's ok to release the type
	if( type )
		type->Release();

	// Compile the function that can be executed
	asIScriptFunction *func = 0;
	int r = execMod->CompileFunction("ExecuteString", funcCode.c_str(), -1, 0, &func);
	if( r < 0 )
		return r;

	// If no context was provided, request a new one from the engine
	asIScriptContext *execCtx = ctx ? ctx : engine->RequestContext();
	r = execCtx->Prepare(func);
	if (r >= 0)
	{
		// Execute the function
		r = execCtx->Execute();

		// Unless the provided type was void retrieve it's value
		if (ref != 0 && refTypeId != asTYPEID_VOID)
		{
			if (refTypeId & asTYPEID_OBJHANDLE)
			{
				// Expect the pointer to be null to start with
				assert(*reinterpret_cast<void**>(ref) == 0);
				*reinterpret_cast<void**>(ref) = *reinterpret_cast<void**>(execCtx->GetAddressOfReturnValue());
				engine->AddRefScriptObject(*reinterpret_cast<void**>(ref), engine->GetTypeInfoById(refTypeId));
			}
			else if (refTypeId & asTYPEID_MASK_OBJECT)
			{
				// Use the registered assignment operator to do a value assign.
				// This assumes that the ref is pointing to a valid object instance.
				engine->AssignScriptObject(ref, execCtx->GetAddressOfReturnValue(), engine->GetTypeInfoById(refTypeId));
			}
			else
			{
				// Copy the primitive value
				memcpy(ref, execCtx->GetAddressOfReturnValue(), engine->GetSizeOfPrimitiveType(refTypeId));
			}
		}
	}

	// Clean up
	func->Release();
	if( !ctx ) engine->ReturnContext(execCtx);

	return r;
}

int WriteConfigToFile(asIScriptEngine *engine, const char *filename)
{
	ofstream strm;
	strm.open(filename);

	return WriteConfigToStream(engine, strm);
}

int WriteConfigToStream(asIScriptEngine *engine, ostream &strm)
{
	// A helper function for escaping quotes in default arguments
	struct Escape
	{
		static string Quotes(const char *decl)
		{
			string str = decl;
			size_t pos = 0;
			for(;;)
			{
				// Find " characters
				pos = str.find("\"",pos);
				if( pos == string::npos )
					break;

				// Add a \ to escape them
				str.insert(pos, "\\");
				pos += 2;
			}

			return str;
		}
	};

	int c, n;

	asDWORD currAccessMask = 0;
	string currNamespace = "";
	engine->SetDefaultNamespace("");

	// Export the engine version, just for info
	strm << "// AngelScript " << asGetLibraryVersion() << "\n";
	strm << "// Lib options " <<  asGetLibraryOptions() << "\n";

	// Export the relevant engine properties
	strm << "// Engine properties\n";
	for( n = 0; n < asEP_LAST_PROPERTY; n++ )
		strm << "ep " << n << " " << engine->GetEngineProperty(asEEngineProp(n)) << "\n";

	// Make sure the default array type is expanded to the template form
	bool expandDefArrayToTempl = engine->GetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL) ? true : false;
	engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, true);

	// Write enum types and their values
	strm << "\n// Enums\n";
	c = engine->GetEnumCount();
	for( n = 0; n < c; n++ )
	{
		asITypeInfo *ti = engine->GetEnumByIndex(n);
		asDWORD accessMask = ti->GetAccessMask();
		if( accessMask != currAccessMask )
		{
			strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
			currAccessMask = accessMask;
		}
		const char *nameSpace = ti->GetNamespace();
		if( nameSpace != currNamespace )
		{
			strm << "namespace \"" << nameSpace << "\"\n";
			currNamespace = nameSpace;
			engine->SetDefaultNamespace(currNamespace.c_str());
		}
		const char *enumName = ti->GetName();
		strm << "enum " << enumName << "\n";
		for( asUINT m = 0; m < ti->GetEnumValueCount(); m++ )
		{
			const char *valName;
			int val;
			valName = ti->GetEnumValueByIndex(m, &val);
			strm << "enumval " << enumName << " " << valName << " " << val << "\n";
		}
	}

	// Enumerate all types
	strm << "\n// Types\n";

	// Keep a list of the template types, as the methods for these need to be exported first
	set<asITypeInfo*> templateTypes;

	c = engine->GetObjectTypeCount();
	for( n = 0; n < c; n++ )
	{
		asITypeInfo *type = engine->GetObjectTypeByIndex(n);
		asDWORD accessMask = type->GetAccessMask();
		if( accessMask != currAccessMask )
		{
			strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
			currAccessMask = accessMask;
		}
		const char *nameSpace = type->GetNamespace();
		if( nameSpace != currNamespace )
		{
			strm << "namespace \"" << nameSpace << "\"\n";
			currNamespace = nameSpace;
			engine->SetDefaultNamespace(currNamespace.c_str());
		}
		if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
		{
			// This should only be interfaces
			assert( type->GetSize() == 0 );

			strm << "intf " << type->GetName() << "\n";
		}
		else
		{
			// Only the type flags are necessary. The application flags are application
			// specific and doesn't matter to the offline compiler. The object size is also
			// unnecessary for the offline compiler
			strm << "objtype \"" << engine->GetTypeDeclaration(type->GetTypeId()) << "\" " << (unsigned int)(type->GetFlags() & asOBJ_MASK_VALID_FLAGS) << "\n";

			// Store the template types (but not template instances)
			if( (type->GetFlags() & asOBJ_TEMPLATE) && type->GetSubType() && (type->GetSubType()->GetFlags() & asOBJ_TEMPLATE_SUBTYPE) )
				templateTypes.insert(type);
		}
	}

	c = engine->GetTypedefCount();
	for( n = 0; n < c; n++ )
	{
		asITypeInfo *ti = engine->GetTypedefByIndex(n);
		const char *nameSpace = ti->GetNamespace();
		if( nameSpace != currNamespace )
		{
			strm << "namespace \"" << nameSpace << "\"\n";
			currNamespace = nameSpace;
			engine->SetDefaultNamespace(currNamespace.c_str());
		}
		asDWORD accessMask = ti->GetAccessMask();
		if( accessMask != currAccessMask )
		{
			strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
			currAccessMask = accessMask;
		}
		strm << "typedef " << ti->GetName() << " \"" << engine->GetTypeDeclaration(ti->GetTypedefTypeId()) << "\"\n";
	}

	c = engine->GetFuncdefCount();
	for( n = 0; n < c; n++ )
	{
		asITypeInfo *funcDef = engine->GetFuncdefByIndex(n);
		asDWORD accessMask = funcDef->GetAccessMask();
		const char *nameSpace = funcDef->GetNamespace();
		// Child funcdefs do not have any namespace, as they belong to the parent object
		if( nameSpace && nameSpace != currNamespace )
		{
			strm << "namespace \"" << nameSpace << "\"\n";
			currNamespace = nameSpace;
			engine->SetDefaultNamespace(currNamespace.c_str());
		}
		if( accessMask != currAccessMask )
		{
			strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
			currAccessMask = accessMask;
		}
		strm << "funcdef \"" << funcDef->GetFuncdefSignature()->GetDeclaration() << "\"\n";
	}

	// A helper for writing object type members
	struct TypeWriter
	{
		static void Write(asIScriptEngine *engine, ostream &strm, asITypeInfo *type, string &currNamespace, asDWORD &currAccessMask)
		{
			const char *nameSpace = type->GetNamespace();
			if( nameSpace != currNamespace )
			{
				strm << "namespace \"" << nameSpace << "\"\n";
				currNamespace = nameSpace;
				engine->SetDefaultNamespace(currNamespace.c_str());
			}
			string typeDecl = engine->GetTypeDeclaration(type->GetTypeId());
			if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
			{
				for( asUINT m = 0; m < type->GetMethodCount(); m++ )
				{
					asIScriptFunction *func = type->GetMethodByIndex(m);
					asDWORD accessMask = func->GetAccessMask();
					if( accessMask != currAccessMask )
					{
						strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
						currAccessMask = accessMask;
					}
					strm << "intfmthd " << typeDecl.c_str() << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
				}
			}
			else
			{
				asUINT m;
				for( m = 0; m < type->GetFactoryCount(); m++ )
				{
					asIScriptFunction *func = type->GetFactoryByIndex(m);
					asDWORD accessMask = func->GetAccessMask();
					if( accessMask != currAccessMask )
					{
						strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
						currAccessMask = accessMask;
					}
					strm << "objbeh \"" << typeDecl.c_str() << "\" " << asBEHAVE_FACTORY << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
				}
				for( m = 0; m < type->GetBehaviourCount(); m++ )
				{
					asEBehaviours beh;
					asIScriptFunction *func = type->GetBehaviourByIndex(m, &beh);

					if( beh == asBEHAVE_CONSTRUCT )
						// Prefix 'void'
						strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
					else if( beh == asBEHAVE_DESTRUCT )
						// Prefix 'void' and remove ~
						strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str()+1 << "\"\n";
					else
						strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
				}
				for( m = 0; m < type->GetMethodCount(); m++ )
				{
					asIScriptFunction *func = type->GetMethodByIndex(m);
					asDWORD accessMask = func->GetAccessMask();
					if( accessMask != currAccessMask )
					{
						strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
						currAccessMask = accessMask;
					}
					strm << "objmthd \"" << typeDecl.c_str() << "\" \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
				}
				for( m = 0; m < type->GetPropertyCount(); m++ )
				{
					asDWORD accessMask;
					type->GetProperty(m, 0, 0, 0, 0, 0, 0, &accessMask);
					if( accessMask != currAccessMask )
					{
						strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
						currAccessMask = accessMask;
					}
					strm << "objprop \"" << typeDecl.c_str() << "\" \"" << type->GetPropertyDeclaration(m) << "\"";

					// Save information about composite properties
					int compositeOffset;
					bool isCompositeIndirect;
					type->GetProperty(m, 0, 0, 0, 0, 0, 0, 0, &compositeOffset, &isCompositeIndirect);
					strm << " " << compositeOffset << " " << (isCompositeIndirect ? "1" : "0") << "\n";
				}
			}
		}
	};

	// Write the members of the template types, so they can be fully registered before any other type uses them
	// TODO: Order the template types based on dependency to avoid failure if one type uses instances of another
	strm << "\n// Template type members\n";
	for( set<asITypeInfo*>::iterator it = templateTypes.begin(); it != templateTypes.end(); ++it )
	{
		asITypeInfo *type = *it;
		TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
	}

	// Write the object types members
	strm << "\n// Type members\n";

	c = engine->GetObjectTypeCount();
	for( n = 0; n < c; n++ )
	{
		asITypeInfo *type = engine->GetObjectTypeByIndex(n);
		if( templateTypes.find(type) == templateTypes.end() )
			TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
	}

	// Write functions
	strm << "\n// Functions\n";

	c = engine->GetGlobalFunctionCount();
	for( n = 0; n < c; n++ )
	{
		asIScriptFunction *func = engine->GetGlobalFunctionByIndex(n);
		const char *nameSpace = func->GetNamespace();
		if( nameSpace != currNamespace )
		{
			strm << "namespace \"" << nameSpace << "\"\n";
			currNamespace = nameSpace;
			engine->SetDefaultNamespace(currNamespace.c_str());
		}
		asDWORD accessMask = func->GetAccessMask();
		if( accessMask != currAccessMask )
		{
			strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
			currAccessMask = accessMask;
		}
		strm << "func \"" << Escape::Quotes(func->GetDeclaration()).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
	}

	// Write global properties
	strm << "\n// Properties\n";

	c = engine->GetGlobalPropertyCount();
	for( n = 0; n < c; n++ )
	{
		const char *name;
		int typeId;
		bool isConst;
		asDWORD accessMask;
		const char *nameSpace;
		engine->GetGlobalPropertyByIndex(n, &name, &nameSpace, &typeId, &isConst, 0, 0, &accessMask);
		if( accessMask != currAccessMask )
		{
			strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
			currAccessMask = accessMask;
		}
		if( nameSpace != currNamespace )
		{
			strm << "namespace \"" << nameSpace << "\"\n";
			currNamespace = nameSpace;
			engine->SetDefaultNamespace(currNamespace.c_str());
		}
		strm << "prop \"" << (isConst ? "const " : "") << engine->GetTypeDeclaration(typeId) << " " << name << "\"\n";
	}

	// Write string factory
	strm << "\n// String factory\n";

	// Reset the namespace for the string factory and default array type
	if ("" != currNamespace)
	{
		strm << "namespace \"\"\n";
		currNamespace = "";
		engine->SetDefaultNamespace("");
	}

	asDWORD flags = 0;
	int typeId = engine->GetStringFactory(&flags);
	if( typeId > 0 )
		strm << "strfactory \"" << ((flags & asTM_CONST) ? "const " : "") << engine->GetTypeDeclaration(typeId) << ((flags & asTM_INOUTREF) ? "&" : "") << "\"\n";

	// Write default array type
	strm << "\n// Default array type\n";
	typeId = engine->GetDefaultArrayTypeId();
	if( typeId > 0 )
		strm << "defarray \"" << engine->GetTypeDeclaration(typeId) << "\"\n";

	// Restore original settings
	engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, expandDefArrayToTempl);

	return 0;
}

int ConfigEngineFromStream(asIScriptEngine *engine, istream &strm, const char *configFile, asIStringFactory *stringFactory)
{
	int r;

	// Some helper functions for parsing the configuration
	struct in
	{
		static asETokenClass GetToken(asIScriptEngine *engine, string &token, const string &text, asUINT &pos)
		{
			asUINT len = 0;
			asETokenClass t = engine->ParseToken(&text[pos], text.length() - pos, &len);
			while( (t == asTC_WHITESPACE || t == asTC_COMMENT) && pos < text.length() )
			{
				pos += len;
				t = engine->ParseToken(&text[pos], text.length() - pos, &len);
			}

			token.assign(&text[pos], len);

			pos += len;

			return t;
		}

		static void ReplaceSlashQuote(string &str)
		{
			size_t pos = 0;
			for(;;)
			{
				// Search for \" in the string
				pos = str.find("\\\"", pos);
				if( pos == string::npos )
					break;

				// Remove the \ character
				str.erase(pos, 1);
			}
		}

		static asUINT GetLineNumber(const string &text, asUINT pos)
		{
			asUINT count = 1;
			for( asUINT n = 0; n < pos; n++ )
				if( text[n] == '\n' )
					count++;

			return count;
		}
	};

	// Since we are only going to compile the script and never actually execute it,
	// we turn off the initialization of global variables, so that the compiler can
	// just register dummy types and functions for the application interface.
	r = engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false); assert( r >= 0 );

	// Read the entire file
	char buffer[1000];
	string config;
	do {
		strm.getline(buffer, 1000);
		config += buffer;
		config += "\n";
	} while( !strm.eof() && strm.good() );

	// Process the configuration file and register each entity
	asUINT pos  = 0;
	while( pos < config.length() )
	{
		string token;
		// TODO: The position where the initial token is found should be stored for error messages
		in::GetToken(engine, token, config, pos);
		if( token == "ep" )
		{
			string tmp;
			in::GetToken(engine, tmp, config, pos);

			asEEngineProp ep = asEEngineProp(atol(tmp.c_str()));

			// Only set properties that affect the compiler
			if( ep != asEP_COPY_SCRIPT_SECTIONS &&
				ep != asEP_MAX_STACK_SIZE &&
				ep != asEP_INIT_GLOBAL_VARS_AFTER_BUILD &&
				ep != asEP_EXPAND_DEF_ARRAY_TO_TMPL &&
				ep != asEP_AUTO_GARBAGE_COLLECT )
			{
				// Get the value for the property
				in::GetToken(engine, tmp, config, pos);
				stringstream s(tmp);
				asPWORD value;

				s >> value;

				engine->SetEngineProperty(ep, value);
			}
		}
		else if( token == "namespace" )
		{
			string ns;
			in::GetToken(engine, ns, config, pos);
			ns = ns.substr(1, ns.length() - 2);

			r = engine->SetDefaultNamespace(ns.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to set namespace");
				return -1;
			}
		}
		else if( token == "access" )
		{
			string maskStr;
			in::GetToken(engine, maskStr, config, pos);
			asDWORD mask = strtoul(maskStr.c_str(), 0, 16);
			engine->SetDefaultAccessMask(mask);
		}
		else if( token == "objtype" )
		{
			string name, flags;
			in::GetToken(engine, name, config, pos);
			name = name.substr(1, name.length() - 2);
			in::GetToken(engine, flags, config, pos);

			// The size of the value type doesn't matter, because the
			// engine must adjust it anyway for different platforms
			r = engine->RegisterObjectType(name.c_str(), (atol(flags.c_str()) & asOBJ_VALUE) ? 1 : 0, atol(flags.c_str()));
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object type");
				return -1;
			}
		}
		else if( token == "objbeh" )
		{
			string name, behaviour, decl;
			in::GetToken(engine, name, config, pos);
			name = name.substr(1, name.length() - 2);
			in::GetToken(engine, behaviour, config, pos);
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);
			in::ReplaceSlashQuote(decl);

			// Remove the $ that the engine prefixes the behaviours with
			size_t n = decl.find("$");
			if( n != string::npos )
				decl[n] = ' ';

			asEBehaviours behave = static_cast<asEBehaviours>(atol(behaviour.c_str()));
			if( behave == asBEHAVE_TEMPLATE_CALLBACK )
			{
				// TODO: How can we let the compiler register this? Maybe through a plug-in system? Or maybe by implementing the callback as a script itself
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register template callback without the actual implementation");
			}
			else
			{
				r = engine->RegisterObjectBehaviour(name.c_str(), behave, decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
				if( r < 0 )
				{
					engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register behaviour");
					return -1;
				}
			}
		}
		else if( token == "objmthd" )
		{
			string name, decl;
			in::GetToken(engine, name, config, pos);
			name = name.substr(1, name.length() - 2);
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);
			in::ReplaceSlashQuote(decl);

			r = engine->RegisterObjectMethod(name.c_str(), decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object method");
				return -1;
			}
		}
		else if( token == "objprop" )
		{
			string name, decl, compositeOffset = "0", isCompositeIndirect = "0";
			in::GetToken(engine, name, config, pos);
			name = name.substr(1, name.length() - 2);
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);
			in::GetToken(engine, compositeOffset, config, pos);
			in::GetToken(engine, isCompositeIndirect, config, pos);
			if( !(isCompositeIndirect == "0" || isCompositeIndirect == "1") )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Wrong value for composite indirect. Is it an old config version?");
				return -1;
			}

			asITypeInfo *type = engine->GetTypeInfoById(engine->GetTypeIdByDecl(name.c_str()));
			if( type == 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Type doesn't exist for property registration");
				return -1;
			}

			// All properties must have different offsets in order to make them
			// distinct, so we simply register them with an incremental offset
			r = engine->RegisterObjectProperty(name.c_str(), decl.c_str(), type->GetPropertyCount(), compositeOffset != "0" ? type->GetPropertyCount() : 0, isCompositeIndirect != "0");
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object property");
				return -1;
			}
		}
		else if( token == "intf" )
		{
			string name, size, flags;
			in::GetToken(engine, name, config, pos);

			r = engine->RegisterInterface(name.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface");
				return -1;
			}
		}
		else if( token == "intfmthd" )
		{
			string name, decl;
			in::GetToken(engine, name, config, pos);
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);
			in::ReplaceSlashQuote(decl);

			r = engine->RegisterInterfaceMethod(name.c_str(), decl.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface method");
				return -1;
			}
		}
		else if( token == "func" )
		{
			string decl;
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);
			in::ReplaceSlashQuote(decl);

			r = engine->RegisterGlobalFunction(decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global function");
				return -1;
			}
		}
		else if( token == "prop" )
		{
			string decl;
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);

			// All properties must have different offsets in order to make them
			// distinct, so we simply register them with an incremental offset.
			// The pointer must also be non-null so we add 1 to have a value.
			r = engine->RegisterGlobalProperty(decl.c_str(), reinterpret_cast<void*>(asPWORD(engine->GetGlobalPropertyCount()+1)));
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global property");
				return -1;
			}
		}
		else if( token == "strfactory" )
		{
			string type;
			in::GetToken(engine, type, config, pos);
			type = type.substr(1, type.length() - 2);

			if (stringFactory == 0)
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register string factory without the actual implementation");
				return -1;
			}
			else
			{
				r = engine->RegisterStringFactory(type.c_str(), stringFactory);
				if (r < 0)
				{
					engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register string factory");
					return -1;
				}
			}
		}
		else if( token == "defarray" )
		{
			string type;
			in::GetToken(engine, type, config, pos);
			type = type.substr(1, type.length() - 2);

			r = engine->RegisterDefaultArrayType(type.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register the default array type");
				return -1;
			}
		}
		else if( token == "enum" )
		{
			string type;
			in::GetToken(engine, type, config, pos);

			r = engine->RegisterEnum(type.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum type");
				return -1;
			}
		}
		else if( token == "enumval" )
		{
			string type, name, value;
			in::GetToken(engine, type, config, pos);
			in::GetToken(engine, name, config, pos);
			in::GetToken(engine, value, config, pos);

			r = engine->RegisterEnumValue(type.c_str(), name.c_str(), atol(value.c_str()));
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum value");
				return -1;
			}
		}
		else if( token == "typedef" )
		{
			string type, decl;
			in::GetToken(engine, type, config, pos);
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);

			r = engine->RegisterTypedef(type.c_str(), decl.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register typedef");
				return -1;
			}
		}
		else if( token == "funcdef" )
		{
			string decl;
			in::GetToken(engine, decl, config, pos);
			decl = decl.substr(1, decl.length() - 2);

			r = engine->RegisterFuncdef(decl.c_str());
			if( r < 0 )
			{
				engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register funcdef");
				return -1;
			}
		}
	}

	return 0;
}

string GetExceptionInfo(asIScriptContext *ctx, bool showStack)
{
	if( ctx->GetState() != asEXECUTION_EXCEPTION ) return "";

	stringstream text;

	const asIScriptFunction *function = ctx->GetExceptionFunction();
	text << "func: " << function->GetDeclaration() << "\n";
	text << "modl: " << (function->GetModuleName() ? function->GetModuleName() : "") << "\n";
	text << "sect: " << (function->GetScriptSectionName() ? function->GetScriptSectionName() : "") << "\n";
	text << "line: " << ctx->GetExceptionLineNumber() << "\n";
	text << "desc: " << ctx->GetExceptionString() << "\n";

	if( showStack )
	{
		text << "--- call stack ---\n";
		for( asUINT n = 1; n < ctx->GetCallstackSize(); n++ )
		{
			function = ctx->GetFunction(n);
			if( function )
			{
				if( function->GetFuncType() == asFUNC_SCRIPT )
				{
					text << (function->GetScriptSectionName() ? function->GetScriptSectionName() : "") << " (" << ctx->GetLineNumber(n) << "): " << function->GetDeclaration() << "\n";
				}
				else
				{
					// The context is being reused by the application for a nested call
					text << "{...application...}: " << function->GetDeclaration() << "\n";
				}
			}
			else
			{
				// The context is being reused by the script engine for a nested call
				text << "{...script engine...}\n";
			}
		}
	}

	return text.str();
}

void ScriptThrow(const string &msg)
{
	asIScriptContext *ctx = asGetActiveContext();
	if (ctx)
		ctx->SetException(msg.c_str());
}

string ScriptGetExceptionInfo()
{
	asIScriptContext *ctx = asGetActiveContext();
	if (!ctx)
		return "";

	const char *msg = ctx->GetExceptionString();
	if (msg == 0)
		return "";

	return string(msg);
}

void RegisterExceptionRoutines(asIScriptEngine *engine)
{
	int r;

	// The string type must be available
	assert(engine->GetTypeInfoByDecl("string"));

	if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") == 0)
	{
		r = engine->RegisterGlobalFunction("void throw(const string &in)", asFUNCTION(ScriptThrow), asCALL_CDECL); assert(r >= 0);
		r = engine->RegisterGlobalFunction("string getExceptionInfo()", asFUNCTION(ScriptGetExceptionInfo), asCALL_CDECL); assert(r >= 0);
	}
	else
	{
		r = engine->RegisterGlobalFunction("void throw(const string &in)", WRAP_FN(ScriptThrow), asCALL_GENERIC); assert(r >= 0);
		r = engine->RegisterGlobalFunction("string getExceptionInfo()", WRAP_FN(ScriptGetExceptionInfo), asCALL_GENERIC); assert(r >= 0);
	}
}

END_AS_NAMESPACE


code:
#ifndef SCRIPTHELPER_H
#define SCRIPTHELPER_H

#include <sstream>
#include <string>

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

BEGIN_AS_NAMESPACE

// Compare relation between two objects of the same type
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result);

// Compare equality between two objects of the same type
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result);

// Compile and execute simple statements
// The module is optional. If given the statements can access the entities compiled in the module.
// The caller can optionally provide its own context, for example if a context should be reused.
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);

// Compile and execute simple statements with option of return value
// The module is optional. If given the statements can access the entities compiled in the module.
// The caller can optionally provide its own context, for example if a context should be reused.
int ExecuteString(asIScriptEngine *engine, const char *code, void *ret, int retTypeId, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);

// Write the registered application interface to a file for an offline compiler.
// The format is compatible with the offline compiler in /sdk/samples/asbuild/.
int WriteConfigToFile(asIScriptEngine *engine, const char *filename);

// Write the registered application interface to a text stream.
int WriteConfigToStream(asIScriptEngine *engine, std::ostream &strm);

// Loads an interface from a text stream and configures the engine with it. This will not
// set the correct function pointers, so it is not possible to use this engine to execute
// scripts, but it can be used to compile scripts and save the byte code.
int ConfigEngineFromStream(asIScriptEngine *engine, std::istream &strm, const char *nameOfStream = "config", asIStringFactory *stringFactory = 0);

// Format the details of the script exception into a human readable text
std::string GetExceptionInfo(asIScriptContext *ctx, bool showStack = false);

// Register the exception routines
//  'void throw(const string &msg)'
//  'string getExceptionInfo()'
void RegisterExceptionRoutines(asIScriptEngine *engine);

END_AS_NAMESPACE

#endif

repo_name: Antidote/openrpgeditor

license: mit


code:
#include "scriptjson.h"

#include <assert.h> // assert()
#include <fstream>

#include "../scriptarray/scriptarray.h"
#include "../scriptjson/scriptjson.h"

#include <iostream>

BEGIN_AS_NAMESPACE

// TODO: This needs some rework, some of the Generic calls are completely busted, those have been disabled for the time being
// TODO: Array subscripting is incomplete, fix
// TODO: Add ability to create json objects with known value up front

CScriptJson* CScriptJson::Create(asIScriptEngine* engine) {
  // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  CScriptJson* obj = (CScriptJson*)asAllocMem(sizeof(CScriptJson));
  new (obj) CScriptJson(engine);
  return obj;
}

CScriptJson* CScriptJson::Create(asIScriptEngine* engine, bool v) {
  // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  CScriptJson* obj = (CScriptJson*)asAllocMem(sizeof(CScriptJson));
  new (obj) CScriptJson(engine);
  *obj->js_info = v;
  return obj;
}

CScriptJson* CScriptJson::Create(asIScriptEngine* engine, asINT64 v) {
  // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  CScriptJson* obj = (CScriptJson*)asAllocMem(sizeof(CScriptJson));
  new (obj) CScriptJson(engine);
  *obj->js_info = v;
  return obj;
}

CScriptJson* CScriptJson::Create(asIScriptEngine* engine, double v) {
  // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  CScriptJson* obj = (CScriptJson*)asAllocMem(sizeof(CScriptJson));
  new (obj) CScriptJson(engine);
  *obj->js_info = v;
  return obj;
}

CScriptJson* CScriptJson::Create(asIScriptEngine* engine, const std::string& v) {
  // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  CScriptJson* obj = (CScriptJson*)asAllocMem(sizeof(CScriptJson));
  new (obj) CScriptJson(engine);
  *obj->js_info = v;
  return obj;
}

CScriptJson* CScriptJson::Create(asIScriptEngine* engine, nlohmann::ordered_json js) {
  // Use the custom memory routine from AngelScript to allow application to better control how much memory is used
  CScriptJson* obj = (CScriptJson*)asAllocMem(sizeof(CScriptJson));
  new (obj) CScriptJson(engine);
  *(obj->js_info) = js;
  return obj;
}

void CScriptJson::AddRef() const { asAtomicInc(refCount); }

void CScriptJson::Release() const {
  if (asAtomicDec(refCount) == 0) {
    this->~CScriptJson();
    asFreeMem(const_cast<CScriptJson*>(this));
  }
}

CScriptJson& CScriptJson::operator=(bool other) {
  // Clear everything we had before
  js_info->clear();

  *js_info = other;

  return *this;
}

CScriptJson& CScriptJson::operator=(asINT64 other) {
  // Clear everything we had before
  js_info->clear();

  *js_info = other;

  return *this;
}

CScriptJson& CScriptJson::operator=(double other) {
  // Clear everything we had before
  js_info->clear();

  *js_info = other;

  return *this;
}

CScriptJson& CScriptJson::operator=(const std::string& other) {
  // Clear everything we had before
  js_info->clear();

  *js_info = other;

  return *this;
}

CScriptJson& CScriptJson::operator=(const CScriptArray& other) {
  nlohmann::ordered_json js_temp = nlohmann::ordered_json::array({});
  for (asUINT i = 0; i < other.GetSize(); i++) {
    CScriptJson** node = (CScriptJson**)other.At(i);
    if (node && *node) {
      js_temp += *(*node)->js_info;
    }
  }

  // Clear everything we had before
  js_info->clear();

  *js_info = js_temp;

  return *this;
}

CScriptJson& CScriptJson::operator=(const CScriptJson& other) {
  // Clear everything we had before
  js_info->clear();

  *js_info = *other.js_info;

  return *this;
}

void CScriptJson::Set(const jsonKey_t& key, const bool& value) { (*js_info)[key] = value; }

void CScriptJson::Set(const jsonKey_t& key, const asINT64& value) { (*js_info)[key] = value; }

void CScriptJson::Set(const jsonKey_t& key, const double& value) { (*js_info)[key] = value; }

void CScriptJson::Set(const jsonKey_t& key, const std::string& value) { (*js_info)[key] = value; }

void CScriptJson::Set(const jsonKey_t& key, const CScriptArray& value) {
  nlohmann::ordered_json js_temp = nlohmann::ordered_json::array({});
  for (asUINT i = 0; i < value.GetSize(); i++) {
    CScriptJson** node = (CScriptJson**)value.At(i);
    if (node && *node) {
      js_temp += *(*node)->js_info;
    }
  }
  (*js_info)[key] = js_temp;
}

bool CScriptJson::Get(const jsonKey_t& key, bool& value) const {
  if (js_info->contains(key)) {
    if (js_info->is_boolean()) {
      value = static_cast<bool>((*js_info)[key]);
      return true;
    }
  }
  return false;
}

bool CScriptJson::Get(const jsonKey_t& key, asINT64& value) const {
  if (js_info->contains(key)) {
    if (js_info->is_number()) {
      value = static_cast<asINT64>((*js_info)[key]);
      return true;
    }
  }
  return false;
}

bool CScriptJson::Get(const jsonKey_t& key, double& value) const {
  if (js_info->contains(key)) {
    if (js_info->is_number()) {
      value = static_cast<double>((*js_info)[key]);
      return true;
    }
  }
  return false;
}

bool CScriptJson::Get(const jsonKey_t& key, std::string& value) const {
  if (js_info->contains(key)) {
    if (js_info->is_string()) {
      value = static_cast<std::string>((*js_info)[key]);
      return true;
    }
  }
  return false;
}

bool CScriptJson::Get(const jsonKey_t& key, CScriptArray& value) const {
  if (!js_info->contains(key) || !(*js_info)[key].is_array())
    return false;

  nlohmann::ordered_json js_temp = (*js_info)[key];
  value.Resize(js_temp.size());

  for (asUINT i = 0; i < js_temp.size(); ++i) {
    CScriptJson* childNode = Create(engine);
    *(childNode->js_info) = js_temp[i];
    value.SetValue(i, &childNode);
    childNode->Release();
  }
  return true;
}

bool CScriptJson::GetBool() { return static_cast<bool>(*js_info); }

int CScriptJson::GetNumber() { return static_cast<int>(*js_info); }

double CScriptJson::GetReal() { return static_cast<double>(*js_info); }

std::string CScriptJson::GetString() { return static_cast<std::string>(*js_info); }

std::string CScriptJson::Dump(int indent) { return js_info->dump(indent); }

CScriptArray* CScriptJson::GetArray() {
  CScriptArray* retVal = CScriptArray::Create(engine->GetTypeInfoByDecl("array<JsonValue@>"));
  for (nlohmann::ordered_json::iterator it = js_info->begin(); it != js_info->end(); ++it) {
    CScriptJson* childNode = CScriptJson::Create(engine, *it);

    retVal->InsertLast(childNode);
    childNode->Release();
  }
  return retVal;
}

CScriptJson* CScriptJson::operator[](const jsonKey_t& key) {
  CScriptJson* retVal = Create(engine);
  retVal->js_info = &(*js_info)[key];
  // Return the existing value if it exists, else insert an empty value
  return retVal;
}

const CScriptJson* CScriptJson::operator[](const jsonKey_t& key) const {
  if (js_info->contains(key)) {
    CScriptJson* retVal = Create(engine);
    *(retVal->js_info) = (*js_info)[key];
    return retVal;
  }

  return 0;
}

CScriptJson* CScriptJson::operator[](size_t idx) {
  if (!js_info->is_array()) {
    return 0;
  }
  if (idx < js_info->size()) {
    CScriptJson* retVal = Create(engine);
    retVal->js_info = &(*js_info)[idx];
    // Return the existing value if it exists, else insert an empty value
    return retVal;
  }

  // Need to do this the slow way because we don't have `resize` >.<
  while (js_info->size() < idx) {
    js_info->emplace_back();
  }

  CScriptJson* retVal = Create(engine);
  retVal->js_info = &(*js_info)[idx];
  // Return the existing value if it exists, else insert an empty value
  return retVal;
}

const CScriptJson* CScriptJson::operator[](size_t idx) const {
  if (!js_info->is_array()) {
    return 0;
  }
  if (idx < js_info->size()) {
    CScriptJson* retVal = Create(engine);
    *(retVal->js_info) = (*js_info)[idx];
    return retVal;
  }

  return 0;
}

void CScriptJson::push_back(CScriptJson* value) {
  if (!js_info->is_null() && !js_info->is_array()) {
    asIScriptContext* ctx = asGetActiveContext();
    if (ctx)
      ctx->SetException("Invalid access to non-array json");
    return;
  }

  js_info->push_back(*value->js_info);
}

bool CScriptJson::Exists(const jsonKey_t& key) const { return js_info->contains(key); }

bool CScriptJson::IsEmpty() const { return js_info->empty(); }

asUINT CScriptJson::GetSize() const { return js_info->size(); }

void CScriptJson::Clear() { js_info->clear(); }

CScriptJsonType CScriptJson::Type() {
  switch (js_info->type()) {
  case nlohmann::ordered_json::value_t::object:
    return OBJECT_VALUE;
  case nlohmann::ordered_json::value_t::array:
    return ARRAY_VALUE;
  case nlohmann::ordered_json::value_t::string:
    return STRING_VALUE;
  case nlohmann::ordered_json::value_t::boolean:
    return BOOLEAN_VALUE;
  case nlohmann::ordered_json::value_t::number_integer:
  case nlohmann::ordered_json::value_t::number_unsigned:
    return NUMBER_VALUE;
  case nlohmann::ordered_json::value_t::number_float:
    return REAL_VALUE;
  default:
    return NULL_VALUE;
  }
}

int CScriptJson::GetRefCount() { return refCount; }

CScriptJson::CScriptJson(asIScriptEngine* e) {
  js_info = new nlohmann::ordered_json();
  // We start with one reference
  refCount = 1;

  engine = e;
}

CScriptJson::~CScriptJson() {
  Clear();
  delete js_info;
}

void ScriptJsonFactory_Generic(asIScriptGeneric* gen) { *(CScriptJson**)gen->GetAddressOfReturnLocation() = CScriptJson::Create(gen->GetEngine()); }
void ScriptJsonFactory_Generic_Bool(asIScriptGeneric* gen) { *(CScriptJson**)gen->GetAddressOfReturnLocation() = CScriptJson::Create(gen->GetEngine(), (bool)gen->GetArgByte(0)); }
void ScriptJsonFactory_Generic_Int64(asIScriptGeneric* gen) {
  //
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = CScriptJson::Create(gen->GetEngine(), (asINT64)gen->GetArgQWord(0));
}
void ScriptJsonFactory_Generic_Double(asIScriptGeneric* gen) { *(CScriptJson**)gen->GetAddressOfReturnLocation() = CScriptJson::Create(gen->GetEngine(), (double)gen->GetArgDouble(0)); }
void ScriptJsonFactory_Generic_String(asIScriptGeneric* gen) { *(CScriptJson**)gen->GetAddressOfReturnLocation() = CScriptJson::Create(gen->GetEngine(), *(std::string*)(gen->GetArgAddress(0))); }

void ScriptJsonAddRef_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  json->AddRef();
}

void ScriptJsonRelease_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  json->Release();
}

void ScriptJsonAssignBool_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  *json = (bool)gen->GetArgByte(0);
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = json;
}

void ScriptJsonAssignInt_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  *json = (asINT64)gen->GetArgQWord(0);
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = json;
}

void ScriptJsonAssignFlt_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  *json = gen->GetArgDouble(0);
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = json;
}

void ScriptJsonAssignStr_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  std::string* other = *(std::string**)gen->GetAddressOfArg(0);
  *json = *other;
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = json;
}

void ScriptJsonAssignArr_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  CScriptArray* other = *(CScriptArray**)gen->GetAddressOfArg(0);
  *json = *other;
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = json;
}

void ScriptJsonAssign_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  CScriptJson* other = *(CScriptJson**)gen->GetAddressOfArg(0);
  *json = *other;
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = json;
}

void ScriptJsonSetBool_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  json->Set(*key, *(bool*)ref);
}

void ScriptJsonSetInt_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  json->Set(*key, *(asINT64*)ref);
}

void ScriptJsonSetFlt_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  json->Set(*key, *(double*)ref);
}

void ScriptJsonSetStr_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  json->Set(*key, *(std::string*)ref);
}

void ScriptJsonSetArr_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  json->Set(*key, *(CScriptArray*)ref);
}

void ScriptJsonGetBool_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  *(bool*)gen->GetAddressOfReturnLocation() = json->Get(*key, *(bool*)ref);
}

void ScriptJsonGetInt_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  *(bool*)gen->GetAddressOfReturnLocation() = json->Get(*key, *(asINT64*)ref);
}

void ScriptJsonGetFlt_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  *(bool*)gen->GetAddressOfReturnLocation() = json->Get(*key, *(double*)ref);
}

void ScriptJsonGetStr_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  *(bool*)gen->GetAddressOfReturnLocation() = json->Get(*key, *(std::string*)ref);
}

void ScriptJsonGetArr_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  void* ref = *(void**)gen->GetAddressOfArg(1);
  *(bool*)gen->GetAddressOfReturnLocation() = json->Get(*key, *(CScriptArray*)ref);
}

void ScriptJsonExists_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  bool ret = json->Exists(*key);
  *(bool*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptJsonIsEmpty_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  bool ret = json->IsEmpty();
  *(bool*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptJsonGetSize_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  asUINT ret = json->GetSize();
  *(asUINT*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptJsonClear_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  json->Clear();
}

void ScriptJsonGetType_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetObject();
  CScriptJsonType ret = json->Type();
  *(CScriptJsonType*)gen->GetAddressOfReturnLocation() = ret;
}

static void CScriptJson_opIndex_Generic(asIScriptGeneric* gen) {
  CScriptJson* self = (CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = self->operator[](*key);
}

static void CScriptJson_opIndex_const_Generic(asIScriptGeneric* gen) {
  const CScriptJson* self = (const CScriptJson*)gen->GetObject();
  jsonKey_t* key = *(jsonKey_t**)gen->GetAddressOfArg(0);
  *(const CScriptJson**)gen->GetAddressOfReturnLocation() = self->operator[](*key);
}

static CScriptJson* JsonParseFile(const std::string& file) {
  std::ifstream inputFile(file.c_str());
  if (!inputFile.is_open()) {
    return nullptr;
  }

  asIScriptContext* currentContext = asGetActiveContext();
  if (currentContext) {
    asIScriptEngine* engine = currentContext->GetEngine();
    if (engine) {
      CScriptJson* newNode = CScriptJson::Create(engine);
      *(newNode->js_info) = nlohmann::ordered_json::parse(inputFile);
      return newNode;
    }
  }
  return NULL;
}

static CScriptJson* JsonParse(const std::string& str) {
  asIScriptContext* currentContext = asGetActiveContext();
  if (currentContext) {
    asIScriptEngine* engine = currentContext->GetEngine();
    if (engine) {
      CScriptJson* newNode = CScriptJson::Create(engine);
      *(newNode->js_info) = nlohmann::ordered_json::parse(str.c_str());
      return newNode;
    }
  }
  return NULL;
}

// Json to text
static bool JsonWriteFile(const CScriptJson& node, const std::string& file) {
  FILE* outputFile = NULL;
  if ((outputFile = fopen(file.c_str(), "w")) == NULL) {
    return false;
  }
  char* data_str;
  std::string dump_str = node.js_info->dump(4);
  data_str = (char*)malloc(dump_str.length() + 1);
  strcpy(data_str, dump_str.c_str());

  fwrite(data_str, strlen(data_str), 1, outputFile);
  fclose(outputFile);
  free(data_str);
  return true;
}

static bool JsonWrite(const CScriptJson& node, std::string& content) {
  content = node.js_info->dump(1, '\t');
  return true;
}

static void ScriptJson_ParseFile_Generic(asIScriptGeneric* gen) {
  std::string* file = (std::string*)gen->GetArgAddress(0);

  CScriptJson* ret = JsonParseFile(*file);
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = ret;
}

static void ScriptJson_Parse_Generic(asIScriptGeneric* gen) {
  std::string* file = (std::string*)gen->GetArgAddress(0);

  CScriptJson* ret = JsonParse(*file);
  *(CScriptJson**)gen->GetAddressOfReturnLocation() = ret;
}

static void ScriptJson_WriteFile_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetArgAddress(0);
  std::string* file = (std::string*)gen->GetArgAddress(1);

  bool ret = JsonWriteFile(*json, *file);
  *(bool*)gen->GetAddressOfReturnLocation() = ret;
}

static void ScriptJson_Write_Generic(asIScriptGeneric* gen) {
  CScriptJson* json = (CScriptJson*)gen->GetArgAddress(0);
  std::string* content = (std::string*)gen->GetArgAddress(1);

  bool ret = JsonWrite(*json, *content);
  *(bool*)gen->GetAddressOfReturnLocation() = ret;
}

//--------------------------------------------------------------------------
// Register the type

void RegisterScriptJson_Native(asIScriptEngine* engine) {
  int r;

  // The array<string> type must be available
  assert(engine->GetTypeInfoByDecl("array<string>"));

  r = engine->RegisterEnum("jsonType");
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "OBJECT_VALUE", OBJECT_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "ARRAY_VALUE", ARRAY_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "BOOLEAN_VALUE", BOOLEAN_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "STRING_VALUE", STRING_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "NUMBER_VALUE", NUMBER_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "REAL_VALUE", REAL_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "NULL_VALUE", NULL_VALUE);
  assert(r >= 0);

  r = engine->RegisterObjectType("json", sizeof(CScriptJson), asOBJ_REF);
  assert(r >= 0);
  // Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved the engine pointer from the active context
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_FACTORY, "json@ f()", asFUNCTION(ScriptJsonFactory_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_FACTORY, "json@ f(bool v)", asFUNCTION(ScriptJsonFactory_Generic_Bool), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_FACTORY, "json@ f(int64 v)", asFUNCTION(ScriptJsonFactory_Generic_Int64), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_FACTORY, "json@ f(double v)", asFUNCTION(ScriptJsonFactory_Generic_Double), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_FACTORY, "json@ f(const string& in)", asFUNCTION(ScriptJsonFactory_Generic_String), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptJson, AddRef), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptJson, Release), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "json &opAssign(bool)", asMETHODPR(CScriptJson, operator=, (bool), CScriptJson&), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(int64)", asMETHODPR(CScriptJson, operator=, (asINT64), CScriptJson&), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(double)", asMETHODPR(CScriptJson, operator=, (double), CScriptJson&), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(const string& in)", asMETHODPR(CScriptJson, operator=, (const std::string&), CScriptJson&), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(const array<json@> &in)", asMETHODPR(CScriptJson, operator=, (const CScriptArray&), CScriptJson&), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(const json &in)", asMETHODPR(CScriptJson, operator=, (const CScriptJson&), CScriptJson&), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const bool&in)", asMETHODPR(CScriptJson, Set, (const std::string&, const bool&), void), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, bool &out) const", asMETHODPR(CScriptJson, Get, (const std::string&, bool&) const, bool), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const int64&in)", asMETHODPR(CScriptJson, Set, (const std::string&, const asINT64&), void), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, int64 &out) const", asMETHODPR(CScriptJson, Get, (const std::string&, asINT64&) const, bool), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const double&in)", asMETHODPR(CScriptJson, Set, (const std::string&, const double&), void), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, double &out) const", asMETHODPR(CScriptJson, Get, (const std::string&, double&) const, bool), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const string&in)", asMETHODPR(CScriptJson, Set, (const std::string&, const std::string&), void), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, string &out) const", asMETHODPR(CScriptJson, Get, (const std::string&, std::string&) const, bool), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const array<json@>&in)", asMETHODPR(CScriptJson, Set, (const std::string&, const CScriptArray&), void), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, array<json@> &out) const", asMETHODPR(CScriptJson, Get, (const std::string&, CScriptArray&) const, bool), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "bool exists(const string &in) const", asMETHOD(CScriptJson, Exists), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool isEmpty() const", asMETHOD(CScriptJson, IsEmpty), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "uint getSize() const", asMETHOD(CScriptJson, GetSize), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "void clear()", asMETHOD(CScriptJson, Clear), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "jsonType getType()", asMETHOD(CScriptJson, Type), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "json &opIndex(const string &in)", asMETHODPR(CScriptJson, operator[], (const jsonKey_t&), CScriptJson*), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "const json &opIndex(const string &in) const", asMETHODPR(CScriptJson, operator[], (const jsonKey_t&) const, const CScriptJson*), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "json &opIndex(uint)", asMETHODPR(CScriptJson, operator[], (size_t), CScriptJson*), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "const json &opIndex(uint) const", asMETHODPR(CScriptJson, operator[], (size_t) const, const CScriptJson*), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void push_back(json& in) const", asMETHODPR(CScriptJson, push_back, (CScriptJson*), void), asCALL_THISCALL);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "bool opConv()", asMETHOD(CScriptJson, GetBool), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "string opConv()", asMETHOD(CScriptJson, GetString), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "int opConv()", asMETHOD(CScriptJson, GetNumber), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "double opConv()", asMETHOD(CScriptJson, GetReal), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "array<json@>& opConv()", asMETHOD(CScriptJson, GetArray), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "string dump(int depth=-1)", asMETHOD(CScriptJson, Dump), asCALL_THISCALL);
  assert(r >= 0);

  // Json functions
  r = engine->RegisterGlobalFunction("json@ jsonParseFile(const string& in)", asFUNCTIONPR(JsonParseFile, (const std::string&), CScriptJson*), asCALL_CDECL);
  assert(r >= 0);
  r = engine->RegisterGlobalFunction("json@ jsonParse(const string& in)", asFUNCTIONPR(JsonParse, (const std::string&), CScriptJson*), asCALL_CDECL);
  assert(r >= 0);
  r = engine->RegisterGlobalFunction("bool jsonWriteFile(const json& in json,const string& in)", asFUNCTIONPR(JsonWriteFile, (const CScriptJson& node, const std::string&), bool), asCALL_CDECL);
  assert(r >= 0);
  r = engine->RegisterGlobalFunction("bool jsonWrite(const json& in json,string& out)", asFUNCTIONPR(JsonWrite, (const CScriptJson& node, std::string&), bool), asCALL_CDECL);
  assert(r >= 0);
}

void RegisterScriptJson_Generic(asIScriptEngine* engine) {
  int r;

  r = engine->RegisterEnum("jsonType");
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "OBJECT_VALUE", OBJECT_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "ARRAY_VALUE", ARRAY_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "BOOLEAN_VALUE", BOOLEAN_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "STRING_VALUE", STRING_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "NUMBER_VALUE", NUMBER_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "REAL_VALUE", REAL_VALUE);
  assert(r >= 0);
  r = engine->RegisterEnumValue("jsonType", "NULL_VALUE", NULL_VALUE);
  assert(r >= 0);

  r = engine->RegisterObjectType("json", sizeof(CScriptJson), asOBJ_REF);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_FACTORY, "json@ f()", asFUNCTIONPR(ScriptJsonFactory_Generic, (asIScriptGeneric*), void), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptJsonAddRef_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectBehaviour("json", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptJsonRelease_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "json &opAssign(bool)", asFUNCTION(ScriptJsonAssignBool_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(int64)", asFUNCTION(ScriptJsonAssignInt_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(double)", asFUNCTION(ScriptJsonAssignFlt_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(const string &in)", asFUNCTION(ScriptJsonAssignStr_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(const array<json@> &in)", asFUNCTION(ScriptJsonAssignArr_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "json &opAssign(const json &in)", asFUNCTION(ScriptJsonAssign_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const bool&in)", asFUNCTION(ScriptJsonSetBool_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, bool&out) const", asFUNCTION(ScriptJsonGetBool_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const int64&in)", asFUNCTION(ScriptJsonSetInt_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, int64&out) const", asFUNCTION(ScriptJsonGetInt_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const double&in)", asFUNCTION(ScriptJsonSetFlt_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, double&out) const", asFUNCTION(ScriptJsonGetFlt_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const string&in)", asFUNCTION(ScriptJsonSetStr_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, string&out) const", asFUNCTION(ScriptJsonGetStr_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "void set(const string &in, const array<json@>&in)", asFUNCTION(ScriptJsonSetArr_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool get(const string &in, array<json@>&out) const", asFUNCTION(ScriptJsonGetArr_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "bool exists(const string &in) const", asFUNCTION(ScriptJsonExists_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "bool isEmpty() const", asFUNCTION(ScriptJsonIsEmpty_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "uint getSize() const", asFUNCTION(ScriptJsonGetSize_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "void clear()", asFUNCTION(ScriptJsonClear_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "void jsonType getType()", asFUNCTION(ScriptJsonGetType_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "json &opIndex(const string &in)", asFUNCTION(CScriptJson_opIndex_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "const json &opIndex(const string &in) const", asFUNCTION(CScriptJson_opIndex_const_Generic), asCALL_GENERIC);
  assert(r >= 0);

  r = engine->RegisterObjectMethod("json", "bool opConv()", asFUNCTION(ScriptJsonGetBool_Generic), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "string opConv()", asFUNCTION(ScriptJsonGetStr_Generic), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "int opConv()", asFUNCTION(ScriptJsonGetInt_Generic), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "double opConv()", asFUNCTION(ScriptJsonGetFlt_Generic), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "array<json@>& opConv()", asFUNCTION(ScriptJsonGetArr_Generic), asCALL_THISCALL);
  assert(r >= 0);
  r = engine->RegisterObjectMethod("json", "string dump(int depth=-1)", asMETHOD(CScriptJson, Dump), asCALL_THISCALL);
  assert(r >= 0);
  // Json functions
  r = engine->RegisterGlobalFunction("json@ jsonParseFile(const string& file)", asFUNCTION(ScriptJson_ParseFile_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterGlobalFunction("json@ jsonParse(const string& str)", asFUNCTION(ScriptJson_Parse_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterGlobalFunction("bool jsonWriteFile(const json& in json,const string& file)", asFUNCTION(ScriptJson_WriteFile_Generic), asCALL_GENERIC);
  assert(r >= 0);
  r = engine->RegisterGlobalFunction("bool jsonWrite(const json& in json,string& out str)", asFUNCTION(ScriptJson_Write_Generic), asCALL_GENERIC);
  assert(r >= 0);
}

void RegisterScriptJson(asIScriptEngine* engine) {
  //    if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
  //        RegisterScriptJson_Generic(engine);
  //    else
  RegisterScriptJson_Native(engine);
}

END_AS_NAMESPACE


code:
//
// Script JSON.
//
#ifndef _ScriptJSON_h_
#define _ScriptJSON_h_

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

#include <string>
typedef std::string jsonKey_t;

#include <nlohmann/json.hpp>

BEGIN_AS_NAMESPACE

class CScriptArray;

enum CScriptJsonType {
  OBJECT_VALUE,
  ARRAY_VALUE,
  BOOLEAN_VALUE,
  STRING_VALUE,
  NUMBER_VALUE,
  REAL_VALUE,
  NULL_VALUE,
};

class CScriptJson {
public:
  // Factory functions
  static CScriptJson* Create(asIScriptEngine* engine);
  static CScriptJson* Create(asIScriptEngine* engine, bool v);
  static CScriptJson* Create(asIScriptEngine* engine, asINT64 v);
  static CScriptJson* Create(asIScriptEngine* engine, double v);
  static CScriptJson* Create(asIScriptEngine* engine, const std::string& v);
  static CScriptJson* Create(asIScriptEngine* engine, nlohmann::ordered_json js);

  // Reference counting
  void AddRef() const;
  void Release() const;

  // Reassign the json
  CScriptJson& operator=(bool other);
  CScriptJson& operator=(asINT64 other);
  CScriptJson& operator=(double other);
  CScriptJson& operator=(const std::string& other);
  CScriptJson& operator=(const CScriptArray& other);
  CScriptJson& operator=(const CScriptJson& other);

  // Sets a key/value pair
  void Set(const jsonKey_t& key, const bool& value);
  void Set(const jsonKey_t& key, const asINT64& value);
  void Set(const jsonKey_t& key, const double& value);
  void Set(const jsonKey_t& key, const std::string& value);
  void Set(const jsonKey_t& key, const CScriptArray& value);

  // Gets the stored value. Returns false if the value isn't compatible
  bool Get(const jsonKey_t& key, bool& value) const;
  bool Get(const jsonKey_t& key, asINT64& value) const;
  bool Get(const jsonKey_t& key, double& value) const;
  bool Get(const jsonKey_t& key, std::string& value) const;
  bool Get(const jsonKey_t& key, CScriptArray& value) const;

  bool GetBool();
  std::string GetString();
  int GetNumber();
  double GetReal();
  CScriptArray* GetArray();

  std::string Dump(int depth = -1);

  // Index accessors. If the json is not const it inserts the value if it doesn't already exist
  // If the json is const then a script exception is set if it doesn't exist and a null pointer is returned
  CScriptJson* operator[](const jsonKey_t& key);
  const CScriptJson* operator[](const jsonKey_t& key) const;

  CScriptJson* operator[](size_t idx);
  const CScriptJson* operator[](const size_t idx) const;

  void push_back(CScriptJson* value);

  // Returns true if the key is set
  bool Exists(const jsonKey_t& key) const;

  // Returns true if there are no key/value pairs in the json
  bool IsEmpty() const;

  // Returns the number of key/value pairs in the json
  asUINT GetSize() const;

  // Deletes all keys
  void Clear();

  // Get Value type
  CScriptJsonType Type();

  int GetRefCount();

  nlohmann::ordered_json* js_info = NULL;

private:
  // Since the dictionary uses the asAllocMem and asFreeMem functions to allocate memory
  // the constructors are made protected so that the application cannot allocate it
  // manually in a different way
  CScriptJson(asIScriptEngine* engine);

  // We don't want anyone to call the destructor directly, it should be called through the Release method
  ~CScriptJson();

  // Our properties
  asIScriptEngine* engine;
  mutable int refCount;
};

// This function will determine the configuration of the engine
// and use one of the two functions below to register the dictionary object
void RegisterScriptJson(asIScriptEngine* engine);

END_AS_NAMESPACE

#endif


code:
#include <assert.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include "scriptmath.h"

#ifdef __BORLANDC__
#include <cmath>

// The C++Builder RTL doesn't pull the *f functions into the global namespace per default.
using namespace std;

#if __BORLANDC__ < 0x580
// C++Builder 6 and earlier don't come with any *f variants of the math functions at all.
inline float cosf (float arg) { return std::cos (arg); }
inline float sinf (float arg) { return std::sin (arg); }
inline float tanf (float arg) { return std::tan (arg); }
inline float atan2f (float y, float x) { return std::atan2 (y, x); }
inline float logf (float arg) { return std::log (arg); }
inline float powf (float x, float y) { return std::pow (x, y); }
inline float sqrtf (float arg) { return std::sqrt (arg); }
#endif

// C++Builder doesn't define most of the non-standard float-specific math functions with
// "*f" suffix; instead it provides overloads for the standard math functions which take
// "float" arguments.
inline float acosf (float arg) { return std::acos (arg); }
inline float asinf (float arg) { return std::asin (arg); }
inline float atanf (float arg) { return std::atan (arg); }
inline float coshf (float arg) { return std::cosh (arg); }
inline float sinhf (float arg) { return std::sinh (arg); }
inline float tanhf (float arg) { return std::tanh (arg); }
inline float log10f (float arg) { return std::log10 (arg); }
inline float ceilf (float arg) { return std::ceil (arg); }
inline float fabsf (float arg) { return std::fabs (arg); }
inline float floorf (float arg) { return std::floor (arg); }

// C++Builder doesn't define a non-standard "modff" function but rather an overload of "modf"
// for float arguments. However, BCC's float overload of fmod() is broken (QC #74816; fixed
// in C++Builder 2010).
inline float modff (float x, float *y)
{
	double d;
	float f = (float) modf((double) x, &d);
	*y = (float) d;
	return f;
}
#endif

BEGIN_AS_NAMESPACE

// Determine whether the float version should be registered, or the double version
#ifndef AS_USE_FLOAT
#if !defined(_WIN32_WCE) // WinCE doesn't have the float versions of the math functions
#define AS_USE_FLOAT 1
#endif
#endif

// The modf function doesn't seem very intuitive, so I'm writing this
// function that simply returns the fractional part of the float value
#if AS_USE_FLOAT
float fractionf(float v)
{
	float intPart;
	return modff(v, &intPart);
}
#else
double fraction(double v)
{
	double intPart;
	return modf(v, &intPart);
}
#endif

// As AngelScript doesn't allow bitwise manipulation of float types we'll provide a couple of
// functions for converting float values to IEEE 754 formatted values etc. This also allow us to
// provide a platform agnostic representation to the script so the scripts don't have to worry
// about whether the CPU uses IEEE 754 floats or some other representation
float fpFromIEEE(asUINT raw)
{
	// TODO: Identify CPU family to provide proper conversion
	//        if the CPU doesn't natively use IEEE style floats
	union conv
	{
		asUINT raw;
		float fp;
	} value;
	value.raw = raw;
	return value.fp;
}
asUINT fpToIEEE(float fp)
{
	union conv
	{
		asUINT raw;
		float fp;
	} value;
	value.fp = fp;
	return value.raw;
}
double fpFromIEEE(asQWORD raw)
{
	union conv
	{
		asQWORD raw;
		double fp;
	} value;
	value.raw = raw;
	return value.fp;
}
asQWORD fpToIEEE(double fp)
{
	union conv
	{
		asQWORD raw;
		double fp;
	} value;
	value.fp = fp;
	return value.raw;
}

// closeTo() is used to determine if the binary representation of two numbers are
// relatively close to each other. Numerical errors due to rounding errors build
// up over many operations, so it is almost impossible to get exact numbers and
// this is where closeTo() comes in.
//
// It shouldn't be used to determine if two numbers are mathematically close to
// each other.
//
// ref: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
// ref: http://www.gamedev.net/topic/653449-scriptmath-and-closeto/
bool closeTo(float a, float b, float epsilon)
{
	// Equal numbers and infinity will return immediately
	if( a == b ) return true;

	// When very close to 0, we can use the absolute comparison
	float diff = fabsf(a - b);
	if( (a == 0 || b == 0) && (diff < epsilon) )
		return true;

	// Otherwise we need to use relative comparison to account for precision
	return diff / (fabs(a) + fabs(b)) < epsilon;
}

bool closeTo(double a, double b, double epsilon)
{
	if( a == b ) return true;

	double diff = fabs(a - b);
	if( (a == 0 || b == 0) && (diff < epsilon) )
		return true;

	return diff / (fabs(a) + fabs(b)) < epsilon;
}

void RegisterScriptMath_Native(asIScriptEngine *engine)
{
	int r;

	// Conversion between floating point and IEEE bits representations
	r = engine->RegisterGlobalFunction("float fpFromIEEE(uint)", asFUNCTIONPR(fpFromIEEE, (asUINT), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("uint fpToIEEE(float)", asFUNCTIONPR(fpToIEEE, (float), asUINT), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double fpFromIEEE(uint64)", asFUNCTIONPR(fpFromIEEE, (asQWORD), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("uint64 fpToIEEE(double)", asFUNCTIONPR(fpToIEEE, (double), asQWORD), asCALL_CDECL); assert( r >= 0 );

	// Close to comparison with epsilon
	r = engine->RegisterGlobalFunction("bool closeTo(float, float, float = 0.00001f)", asFUNCTIONPR(closeTo, (float, float, float), bool), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("bool closeTo(double, double, double = 0.0000000001)", asFUNCTIONPR(closeTo, (double, double, double), bool), asCALL_CDECL); assert( r >= 0 );

#if AS_USE_FLOAT
	// Trigonometric functions
	r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTIONPR(cosf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTIONPR(sinf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTIONPR(tanf, (float), float), asCALL_CDECL); assert( r >= 0 );

	r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTIONPR(acosf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTIONPR(asinf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTIONPR(atanf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTIONPR(atan2f, (float, float), float), asCALL_CDECL); assert( r >= 0 );

	// Hyberbolic functions
	r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTIONPR(coshf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTIONPR(sinhf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTIONPR(tanhf, (float), float), asCALL_CDECL); assert( r >= 0 );

	// Exponential and logarithmic functions
	r = engine->RegisterGlobalFunction("float log(float)", asFUNCTIONPR(logf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTIONPR(log10f, (float), float), asCALL_CDECL); assert( r >= 0 );

	// Power functions
	r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTIONPR(powf, (float, float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTIONPR(sqrtf, (float), float), asCALL_CDECL); assert( r >= 0 );

	// Nearest integer, absolute value, and remainder functions
	r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTIONPR(ceilf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTIONPR(fabsf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTIONPR(floorf, (float), float), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTIONPR(fractionf, (float), float), asCALL_CDECL); assert( r >= 0 );

	// Don't register modf because AngelScript already supports the % operator
#else
	// double versions of the same
	r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTIONPR(cos, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTIONPR(sin, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTIONPR(tan, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTIONPR(acos, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTIONPR(asin, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTIONPR(atan, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTIONPR(atan2, (double, double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTIONPR(cosh, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTIONPR(sinh, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTIONPR(tanh, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double log(double)", asFUNCTIONPR(log, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTIONPR(log10, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTIONPR(pow, (double, double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTIONPR(sqrt, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTIONPR(ceil, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTIONPR(fabs, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTIONPR(floor, (double), double), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTIONPR(fraction, (double), double), asCALL_CDECL); assert( r >= 0 );
#endif
}

#if AS_USE_FLOAT
// This macro creates simple generic wrappers for functions of type 'float func(float)'
#define GENERICff(x) \
void x##_generic(asIScriptGeneric *gen) \
{ \
	float f = *(float*)gen->GetAddressOfArg(0); \
	*(float*)gen->GetAddressOfReturnLocation() = x(f); \
}

GENERICff(cosf)
GENERICff(sinf)
GENERICff(tanf)
GENERICff(acosf)
GENERICff(asinf)
GENERICff(atanf)
GENERICff(coshf)
GENERICff(sinhf)
GENERICff(tanhf)
GENERICff(logf)
GENERICff(log10f)
GENERICff(sqrtf)
GENERICff(ceilf)
GENERICff(fabsf)
GENERICff(floorf)
GENERICff(fractionf)

void powf_generic(asIScriptGeneric *gen)
{
	float f1 = *(float*)gen->GetAddressOfArg(0);
	float f2 = *(float*)gen->GetAddressOfArg(1);
	*(float*)gen->GetAddressOfReturnLocation() = powf(f1, f2);
}
void atan2f_generic(asIScriptGeneric *gen)
{
	float f1 = *(float*)gen->GetAddressOfArg(0);
	float f2 = *(float*)gen->GetAddressOfArg(1);
	*(float*)gen->GetAddressOfReturnLocation() = atan2f(f1, f2);
}

#else
// This macro creates simple generic wrappers for functions of type 'double func(double)'
#define GENERICdd(x) \
void x##_generic(asIScriptGeneric *gen) \
{ \
	double f = *(double*)gen->GetAddressOfArg(0); \
	*(double*)gen->GetAddressOfReturnLocation() = x(f); \
}

GENERICdd(cos)
GENERICdd(sin)
GENERICdd(tan)
GENERICdd(acos)
GENERICdd(asin)
GENERICdd(atan)
GENERICdd(cosh)
GENERICdd(sinh)
GENERICdd(tanh)
GENERICdd(log)
GENERICdd(log10)
GENERICdd(sqrt)
GENERICdd(ceil)
GENERICdd(fabs)
GENERICdd(floor)
GENERICdd(fraction)

void pow_generic(asIScriptGeneric *gen)
{
	double f1 = *(double*)gen->GetAddressOfArg(0);
	double f2 = *(double*)gen->GetAddressOfArg(1);
	*(double*)gen->GetAddressOfReturnLocation() = pow(f1, f2);
}
void atan2_generic(asIScriptGeneric *gen)
{
	double f1 = *(double*)gen->GetAddressOfArg(0);
	double f2 = *(double*)gen->GetAddressOfArg(1);
	*(double*)gen->GetAddressOfReturnLocation() = atan2(f1, f2);
}
#endif
void RegisterScriptMath_Generic(asIScriptEngine *engine)
{
	int r;

#if AS_USE_FLOAT
	// Trigonometric functions
	r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTION(cosf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTION(sinf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTION(tanf_generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTION(acosf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTION(asinf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTION(atanf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTION(atan2f_generic), asCALL_GENERIC); assert( r >= 0 );

	// Hyberbolic functions
	r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTION(coshf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTION(sinhf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTION(tanhf_generic), asCALL_GENERIC); assert( r >= 0 );

	// Exponential and logarithmic functions
	r = engine->RegisterGlobalFunction("float log(float)", asFUNCTION(logf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTION(log10f_generic), asCALL_GENERIC); assert( r >= 0 );

	// Power functions
	r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTION(powf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTION(sqrtf_generic), asCALL_GENERIC); assert( r >= 0 );

	// Nearest integer, absolute value, and remainder functions
	r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTION(ceilf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTION(fabsf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTION(floorf_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTION(fractionf_generic), asCALL_GENERIC); assert( r >= 0 );

	// Don't register modf because AngelScript already supports the % operator
#else
	// double versions of the same
	r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTION(cos_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTION(sin_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTION(tan_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTION(acos_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTION(asin_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTION(atan_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTION(atan2_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTION(cosh_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTION(sinh_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTION(tanh_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double log(double)", asFUNCTION(log_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTION(log10_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTION(pow_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTION(sqrt_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTION(ceil_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTION(fabs_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTION(floor_generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTION(fraction_generic), asCALL_GENERIC); assert( r >= 0 );
#endif
}

void RegisterScriptMath(asIScriptEngine *engine)
{
	if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
		RegisterScriptMath_Generic(engine);
	else
		RegisterScriptMath_Native(engine);
}

END_AS_NAMESPACE


code:
#ifndef SCRIPTMATH_H
#define SCRIPTMATH_H

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

BEGIN_AS_NAMESPACE

// This function will determine the configuration of the engine
// and use one of the two functions below to register the math functions
void RegisterScriptMath(asIScriptEngine *engine);

// Call this function to register the math functions
// using native calling conventions
void RegisterScriptMath_Native(asIScriptEngine *engine);

// Use this one instead if native calling conventions
// are not supported on the target platform
void RegisterScriptMath_Generic(asIScriptEngine *engine);

END_AS_NAMESPACE

#endif


code:
#include <assert.h>
#include <string.h> // strstr
#include <new> // new()
#include <math.h>
#include "scriptmathcomplex.h"

#ifdef __BORLANDC__
// C++Builder doesn't define a non-standard "sqrtf" function but rather an overload of "sqrt"
// for float arguments.
inline float sqrtf (float x) { return sqrt (x); }
#endif

BEGIN_AS_NAMESPACE

Complex::Complex()
{
	r = 0;
	i = 0;
}

Complex::Complex(const Complex &other)
{
	r = other.r;
	i = other.i;
}

Complex::Complex(float _r, float _i)
{
	r = _r;
	i = _i;
}

bool Complex::operator==(const Complex &o) const
{
	return (r == o.r) && (i == o.i);
}

bool Complex::operator!=(const Complex &o) const
{
	return !(*this == o);
}

Complex &Complex::operator=(const Complex &other)
{
	r = other.r;
	i = other.i;
	return *this;
}

Complex &Complex::operator+=(const Complex &other)
{
	r += other.r;
	i += other.i;
	return *this;
}

Complex &Complex::operator-=(const Complex &other)
{
	r -= other.r;
	i -= other.i;
	return *this;
}

Complex &Complex::operator*=(const Complex &other)
{
	*this = *this * other;
	return *this;
}

Complex &Complex::operator/=(const Complex &other)
{
	*this = *this / other;
	return *this;
}

float Complex::squaredLength() const
{
	return r*r + i*i;
}

float Complex::length() const
{
	return sqrtf(squaredLength());
}

Complex Complex::operator+(const Complex &other) const
{
	return Complex(r + other.r, i + other.i);
}

Complex Complex::operator-(const Complex &other) const
{
	return Complex(r - other.r, i + other.i);
}

Complex Complex::operator*(const Complex &other) const
{
	return Complex(r*other.r - i*other.i, r*other.i + i*other.r);
}

Complex Complex::operator/(const Complex &other) const
{
	float squaredLen = other.squaredLength();
	if( squaredLen == 0 ) return Complex(0,0);

	return Complex((r*other.r + i*other.i)/squaredLen, (i*other.r - r*other.i)/squaredLen);
}

//-----------------------
// Swizzle operators
//-----------------------

Complex Complex::get_ri() const
{
	return *this;
}
Complex Complex::get_ir() const
{
	return Complex(r,i);
}
void Complex::set_ri(const Complex &o)
{
	*this = o;
}
void Complex::set_ir(const Complex &o)
{
	r = o.i;
	i = o.r;
}

//-----------------------
// AngelScript functions
//-----------------------

static void ComplexDefaultConstructor(Complex *self)
{
	new(self) Complex();
}

static void ComplexCopyConstructor(const Complex &other, Complex *self)
{
	new(self) Complex(other);
}

static void ComplexConvConstructor(float r, Complex *self)
{
	new(self) Complex(r);
}

static void ComplexInitConstructor(float r, float i, Complex *self)
{
	new(self) Complex(r,i);
}

static void ComplexListConstructor(float *list, Complex *self)
{
	new(self) Complex(list[0], list[1]);
}

//--------------------------------
// Registration
//-------------------------------------

static void RegisterScriptMathComplex_Native(asIScriptEngine *engine)
{
	int r;

	// Register the type
#if AS_CAN_USE_CPP11
	// With C++11 it is possible to use asGetTypeTraits to determine the correct flags to represent the C++ class, except for the asOBJ_APP_CLASS_ALLFLOATS
	r = engine->RegisterObjectType("complex", sizeof(Complex), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<Complex>() | asOBJ_APP_CLASS_ALLFLOATS); assert( r >= 0 );
#else
	r = engine->RegisterObjectType("complex", sizeof(Complex), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK | asOBJ_APP_CLASS_ALLFLOATS); assert( r >= 0 );
#endif

	// Register the object properties
	r = engine->RegisterObjectProperty("complex", "float r", asOFFSET(Complex, r)); assert( r >= 0 );
	r = engine->RegisterObjectProperty("complex", "float i", asOFFSET(Complex, i)); assert( r >= 0 );

	// Register the constructors
	r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT,      "void f()",                             asFUNCTION(ComplexDefaultConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT,      "void f(const complex &in)",            asFUNCTION(ComplexCopyConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT,      "void f(float)",                        asFUNCTION(ComplexConvConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT,      "void f(float, float)",                 asFUNCTION(ComplexInitConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("complex", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float}", asFUNCTION(ComplexListConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	// Register the operator overloads
	r = engine->RegisterObjectMethod("complex", "complex &opAddAssign(const complex &in)", asMETHODPR(Complex, operator+=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex &opSubAssign(const complex &in)", asMETHODPR(Complex, operator-=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex &opMulAssign(const complex &in)", asMETHODPR(Complex, operator*=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex &opDivAssign(const complex &in)", asMETHODPR(Complex, operator/=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "bool opEquals(const complex &in) const", asMETHODPR(Complex, operator==, (const Complex &) const, bool), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex opAdd(const complex &in) const", asMETHODPR(Complex, operator+, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex opSub(const complex &in) const", asMETHODPR(Complex, operator-, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex opMul(const complex &in) const", asMETHODPR(Complex, operator*, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex opDiv(const complex &in) const", asMETHODPR(Complex, operator/, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );

	// Register the object methods
	r = engine->RegisterObjectMethod("complex", "float abs() const", asMETHOD(Complex,length), asCALL_THISCALL); assert( r >= 0 );

	// Register the swizzle operators
	r = engine->RegisterObjectMethod("complex", "complex get_ri() const property", asMETHOD(Complex, get_ri), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "complex get_ir() const property", asMETHOD(Complex, get_ir), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "void set_ri(const complex &in) property", asMETHOD(Complex, set_ri), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("complex", "void set_ir(const complex &in) property", asMETHOD(Complex, set_ir), asCALL_THISCALL); assert( r >= 0 );
}

void RegisterScriptMathComplex(asIScriptEngine *engine)
{
	if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
	{
		assert( false );
		// TODO: implement support for generic calling convention
		// RegisterScriptMathComplex_Generic(engine);
	}
	else
		RegisterScriptMathComplex_Native(engine);
}

END_AS_NAMESPACE


code:
#ifndef SCRIPTMATHCOMPLEX_H
#define SCRIPTMATHCOMPLEX_H

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

BEGIN_AS_NAMESPACE

// This class implements complex numbers and the common
// operations that can be done with it.
//
// Ref: http://mathworld.wolfram.com/ComplexNumber.html

struct Complex
{
	Complex();
	Complex(const Complex &other);
	Complex(float r, float i = 0);

	// Assignment operator
	Complex &operator=(const Complex &other);

	// Compound assigment operators
	Complex &operator+=(const Complex &other);
	Complex &operator-=(const Complex &other);
	Complex &operator*=(const Complex &other);
	Complex &operator/=(const Complex &other);

	float length() const;
	float squaredLength() const;

	// Swizzle operators
	Complex get_ri() const;
	void    set_ri(const Complex &in);
	Complex get_ir() const;
	void    set_ir(const Complex &in);

	// Comparison
	bool operator==(const Complex &other) const;
	bool operator!=(const Complex &other) const;

	// Math operators
	Complex operator+(const Complex &other) const;
	Complex operator-(const Complex &other) const;
	Complex operator*(const Complex &other) const;
	Complex operator/(const Complex &other) const;

	float r;
	float i;
};

// This function will determine the configuration of the engine
// and use one of the two functions below to register the string type
void RegisterScriptMathComplex(asIScriptEngine *engine);

END_AS_NAMESPACE

#endif