//========== Copyright (c) Valve Corporation, All rights reserved. ========
//
// Purpose:
//
//==========================================================================

//-------------------------------------
// Work around one of the headers below including crtdbg.h
#include "memdbgon.h"
#include "memdbgoff.h"
//-------------------------------------

#include <stdio.h>
#if defined( _PS3 ) || defined( POSIX )
#include <ctype.h>
#include <wctype.h>
#undef _STD_USING
#elif defined( _WIN32 )
#include <io.h>
#include <conio.h>
#include <direct.h>
#endif // _PS3

#ifdef _HAS_EXCEPTIONS
#undef _HAS_EXCEPTIONS
#endif

#define _HAS_EXCEPTIONS 0
#include <string>

#include "platform.h"

#include "tier1/utlmap.h"
#include "datamap.h"
#include "tier1/functors.h"
#include "tier1/utlvector.h"
#include "tier1/utlhash.h"
#include "tier1/utlbuffer.h"
#include "tier1/fmtstr.h"
#include "tier1/convar.h"
#include "mathlib/vector.h"
#include "vstdlib/random.h"

#pragma warning(push, 3)
#include "squirrel.h"
#include "sqstdaux.h"
#include "sqstdstring.h"
#include "sqstdmath.h"
#include "sqplus.h"
#include "sqrdbg.h"
#include "../squirrel/sqstate.h"
#include "../squirrel/sqvm.h"
#include "../squirrel/sqobject.h"
#include "../squirrel/sqstring.h"
#include "../squirrel/sqarray.h"
#include "../squirrel/sqtable.h"
#include "../squirrel/squserdata.h"
#include "../squirrel/sqfuncproto.h"
#include "../squirrel/sqclass.h"
#include "../squirrel/sqclosure.h"
#include "sqdbgserver.h"
#pragma warning(pop)

#include "vscript/ivscript.h"
#include "tier0/vprof.h"

#include "init_nut.h"

#include "memdbgon.h"

#ifdef VSQUIRREL_DEBUG_SERIALIZATION
static SQObjectType lastType;
#endif

#if defined( _PS3 ) || defined( POSIX )
inline int64 max( int64 a, int64 b)
{
	return a > b ? a : b;
}

#endif
//-----------------------------------------------------------------------------
// Stub out unwanted features
//-----------------------------------------------------------------------------
extern "C" 
{

SQRESULT sqstd_register_iolib(HSQUIRRELVM)
{
	return SQ_OK;
}

SQRESULT sqstd_loadfile(HSQUIRRELVM,const SQChar *,SQBool)
{
	return SQ_ERROR;
}

}

//-------------------------------------------------------------------------
// Helpers
//-------------------------------------------------------------------------

const char *FieldTypeToString( int type )
{
	switch( type )
	{
		case FIELD_VOID:		return "void";
		case FIELD_FLOAT:		return "float";
		case FIELD_CSTRING:		return "string";
		case FIELD_VECTOR:		return "Vector";
		case FIELD_INTEGER:		return "int";
		case FIELD_BOOLEAN:		return "bool";
		case FIELD_CHARACTER:	return "char";
		case FIELD_HSCRIPT:		return "handle";
		default:				return "<unknown>";
	}
}

static const char *SQTypeToString( SQObjectType sqType )
{
	switch( sqType )
	{
		case OT_FLOAT:			return "FLOAT";
		case OT_INTEGER:		return "INTEGER";
		case OT_BOOL:			return "BOOL";
		case OT_STRING:			return "STRING";
		case OT_NULL:			return "NULL";
		case OT_TABLE:			return "TABLE";
		case OT_ARRAY:			return "ARRAY";
		case OT_CLOSURE:		return "CLOSURE";
		case OT_NATIVECLOSURE:	return "NATIVECLOSURE";
		case OT_USERDATA:		return "USERDATA";
		case OT_GENERATOR:		return "GENERATOR";
		case OT_THREAD:			return "THREAD";
		case OT_USERPOINTER:	return "USERPOINTER";
		case OT_CLASS:			return "CLASS";
		case OT_INSTANCE:		return "INSTANCE";
		case OT_WEAKREF:		return "WEAKREF";
	}
	return "<unknown>";
}


//-------------------------------------------------------------------------
// Vector
//-------------------------------------------------------------------------
#define TYPETAG_VECTOR ((SQUserPointer)1)
SQInteger VectorRelease( SQUserPointer p, SQInteger size )
{
	delete (Vector *)p;
	return 0;
}

SQInteger VectorConstruct( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = new Vector;

	int i;
	for ( i = 0; i < 3 && i < sa.GetParamCount() - 1; i++ )
	{
		(*pVector)[i] = sa.GetFloat(i + 2);
	}

	for ( ; i < 3 ; i++ )
	{
		(*pVector)[i] = 0;
	}

	sq_setinstanceup(hVM, 1, pVector);
	sq_setreleasehook( hVM, 1, &VectorRelease );
	return 0;
}

SQInteger VectorGet( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);
	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	const char *pszKey = sa.GetString( 2 );
	if ( pszKey && *pszKey && !*(pszKey + 1) )
	{
		int index = *pszKey - 'x';
		if ( index >=0 && index <= 2)
		{
			sq_pushfloat( hVM, (*pVector)[index] );
			return 1;
		}
	}
	return SQ_ERROR;
}

SQInteger VectorSet( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);
	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	const char *pszKey = sa.GetString( 2 );
	if ( pszKey && *pszKey && !*(pszKey + 1) )
	{
		int index = *pszKey - 'x';
		if ( index >=0 && index <= 2)
		{
			(*pVector)[index] = sa.GetFloat(3);
			sq_pushfloat( hVM, (*pVector)[index] );
			return 0;
		}
	}
	return SQ_ERROR;
}

SQInteger VectorIterate( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	static const char *results[] =
	{
		"x", "y", "z"
	};
	const char *pszKey = (sa.GetType( 2 ) == OT_NULL ) ? "w" : sa.GetString( 2 );
	if ( pszKey && *pszKey && !*(pszKey + 1) )
	{
		int index = (*pszKey - 'x' ) + 1;
		if ( index >=0 && index <= 2)
		{
			sa.Return( results[index] );
			return 1;
		}
		sq_pushnull( hVM );
		return 1;
	}
	return SQ_ERROR;
}

SQInteger VectorToString( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);
	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	sa.Return( (static_cast<const char *>(CFmtStr("(vector : (%f, %f, %f))", pVector->x, pVector->y, pVector->z))) );
	return 1;
}

SQInteger VectorTypeOf( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	sa.Return( "Vector" );
	return 1;
}

SQInteger VectorToKeyValueString( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);
	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	sa.Return( (static_cast<const char *>(CFmtStr("%f %f %f))", pVector->x, pVector->y, pVector->z))) );
	return 1;
}

SQInteger VectorAdd( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVectorSrc = (Vector *)sa.GetInstanceUp(1,0);
	Vector *pVectorAdd = (Vector *)sa.GetInstanceUp(2,0);

	if ( !pVectorSrc || !pVectorAdd )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	Vector *pResult = new Vector;

	*pResult = *pVectorSrc + *pVectorAdd;

	sq_getclass( hVM, -1 );
	sq_createinstance( hVM, -1 );
	sq_setinstanceup( hVM, -1, (SQUserPointer)pResult );
	sq_setreleasehook( hVM, -1, &VectorRelease );
	sq_remove( hVM, -2 );

	return 1;
}

SQInteger VectorSubtract( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVectorSrc = (Vector *)sa.GetInstanceUp(1,0);
	Vector *pVectorAdd = (Vector *)sa.GetInstanceUp(2,0);

	if ( !pVectorSrc || !pVectorAdd )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	Vector *pResult = new Vector;

	*pResult = *pVectorSrc - *pVectorAdd;

	sq_getclass( hVM, -1 );
	sq_createinstance( hVM, -1 );
	sq_setinstanceup( hVM, -1, (SQUserPointer)pResult );
	sq_setreleasehook( hVM, -1, &VectorRelease );
	sq_remove( hVM, -2 );

	return 1;
}

SQInteger VectorScale( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVectorSrc = (Vector *)sa.GetInstanceUp(1,0);

	if ( !pVectorSrc )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float scale = sa.GetFloat( 2 );
	Vector *pResult = new Vector;

	*pResult = *pVectorSrc * scale;

	sq_getclass( hVM, -2 );
	sq_createinstance( hVM, -1 );
	sq_setinstanceup( hVM, -1, (SQUserPointer)pResult );
	sq_setreleasehook( hVM, -1, &VectorRelease );
	sq_remove( hVM, -2 );

	return 1;
}

SQInteger VectorLength( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);
	
	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float flLength = pVector->Length();
	sa.Return( flLength );
	
	return 1;
}

SQInteger VectorLengthSqr( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);

	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float flLength = pVector->LengthSqr();
	sa.Return( flLength );

	return 1;
}

SQInteger VectorLength2D( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);

	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float flLength = pVector->Length2D();
	sa.Return( flLength );

	return 1;
}

SQInteger VectorLength2DSqr( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);

	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float flLength = pVector->Length2DSqr();
	sa.Return( flLength );

	return 1;
}

SQInteger VectorCross( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVectorSrc = (Vector *)sa.GetInstanceUp(1,0);
	Vector *pVectorAdd = (Vector *)sa.GetInstanceUp(2,0);

	if ( !pVectorSrc || !pVectorAdd )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	Vector *pResult = new Vector;

	*pResult = (*pVectorSrc).Cross( *pVectorAdd );

	sq_getclass( hVM, -1 );
	sq_createinstance( hVM, -1 );
	sq_setinstanceup( hVM, -1, (SQUserPointer)pResult );
	sq_setreleasehook( hVM, -1, &VectorRelease );
	sq_remove( hVM, -2 );

	return 1;
}

SQInteger VectorDot( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVectorSrc = (Vector *)sa.GetInstanceUp(1,0);
	Vector *pVectorAdd = (Vector *)sa.GetInstanceUp(2,0);
	
	if ( !pVectorSrc || !pVectorAdd )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float flResult = (*pVectorSrc).Dot( *pVectorAdd );
	sa.Return( flResult );

	return 1;
}

SQInteger VectorNorm( HSQUIRRELVM hVM )
{
	StackHandler sa(hVM);
	Vector *pVector = (Vector *)sa.GetInstanceUp(1,0);

	if ( !pVector )
	{
		sq_throwerror( hVM, "null vector" );
		return SQ_ERROR;
	}

	float flLength = pVector->NormalizeInPlace();
	sa.Return( flLength );

	return 1;
}


SQRegFunction g_VectorFuncs[] =
{
	{ "constructor",	VectorConstruct,	0, NULL },
	{ "_get",			VectorGet,			2, ".." },
	{ "_set",			VectorSet,			3, "..n" },
	{ "_tostring",		VectorToString,		0, NULL },
	{ "_typeof",		VectorTypeOf,		0, NULL },
	{ "_nexti",			VectorIterate,		0, NULL },
	{ "_add",			VectorAdd,			2, NULL },
	{ "_sub",			VectorSubtract,		2, NULL },
	{ "_mul",			VectorScale,		2, NULL },
	{ "ToKVString",		VectorToKeyValueString, 0, NULL },
	{ "Length",			VectorLength,		0, NULL },
	{ "LengthSqr",		VectorLengthSqr,	0, NULL },
	{ "Length2D",		VectorLength2D,		0, NULL },
	{ "Length2DSqr",	VectorLength2DSqr,	0, NULL },
	{ "Length2DSqr",	VectorLength2DSqr,	0, NULL },
	{ "Dot",			VectorDot,			2, NULL },
	{ "Cross",			VectorCross,		2, NULL },
	{ "Norm",			VectorNorm,			0, NULL },
};


bool RegisterVector( HSQUIRRELVM hVM )
{
	int top = sq_gettop( hVM );

	sq_pushroottable(hVM);
	sq_pushstring(hVM,"Vector",-1);

	if (SQ_FAILED(sq_newclass(hVM,0))) 
	{
		sq_settop(hVM,top);
		return false;
	}
	HSQOBJECT hClass;
	sq_getstackobj(hVM,-1, &hClass);
	sq_settypetag(hVM,-1,TYPETAG_VECTOR);
	sq_createslot(hVM,-3);

	sq_pushobject( hVM, hClass );
	for ( int i = 0; i < ARRAYSIZE(g_VectorFuncs); i++ )
	{
		sq_pushstring(hVM,g_VectorFuncs[i].name,-1);
		sq_newclosure(hVM,g_VectorFuncs[i].f,0);
		if ( g_VectorFuncs[i].nparamscheck )
			sq_setparamscheck(hVM,g_VectorFuncs[i].nparamscheck,g_VectorFuncs[i].typemask);
		sq_setnativeclosurename(hVM,-1,g_VectorFuncs[i].name);
		sq_createslot(hVM,-3);
	}

	sq_pop(hVM,1);

	sq_settop( hVM, top );
	return true;
}

//-----------------------------------------------------------------------------
// Bridge code, some cribbed from SqPlus
//-----------------------------------------------------------------------------

const HSQOBJECT INVALID_HSQOBJECT = { (SQObjectType)-1, (SQTable *)-1 };

inline bool operator==( const HSQOBJECT &lhs, const HSQOBJECT &rhs ) { COMPILE_TIME_ASSERT( sizeof(lhs._unVal) == sizeof(lhs._unVal.pTable) ); return ( lhs._type == rhs._type && lhs._unVal.pTable == rhs._unVal.pTable ); }
inline bool operator!=( const HSQOBJECT &lhs, const HSQOBJECT &rhs ) { return !operator==( lhs, rhs ); }

class CSquirrelVM : public IScriptVM
{
public:
	CSquirrelVM( HSQUIRRELVM hVM = NULL )
	  :	m_hVM( hVM ), m_hDbg( NULL ), m_PtrMap( DefLessFunc(void *) ), m_iUniqueIdSerialNumber( 0 )
#ifndef VSQUIRREL_TEST
	    , developer( "developer" )
#else
	    , developer( "developer", "1" )
#endif
	{
		m_hOnCreateScopeFunc = _null_;
		m_hOnReleaseScopeFunc = _null_;
		m_hClassVector = _null_;
		m_ErrorString = _null_;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual bool Init()
	{
		m_hVM = sq_open(1024);

		m_hVM->_sharedstate->m_pOwnerData = this;
		m_hVM->SetQuerySuspendFn( &QueryContinue );
		// Need to make this conditional on convar or commandline [2/11/2008 tom]
		//m_hDbg = sq_rdbg_init( m_hVM, 1234, SQTrue);
		sq_setprintfunc(m_hVM, &PrintFunc);
		sq_pushroottable(m_hVM);
		sqstd_register_mathlib(m_hVM);
		sqstd_register_stringlib(m_hVM);
		sqstd_seterrorhandlers(m_hVM);
		sq_pop(m_hVM,1);
		if ( IsDebug() || developer.GetInt() > 0 )
		{
			sq_enabledebuginfo( m_hVM, SQTrue );
		}
		sq_pushroottable( m_hVM);

		sq_pushstring( m_hVM, "developer", -1 );
		sq_newclosure( m_hVM, &GetDeveloper, 0 );
		sq_setnativeclosurename(m_hVM, -1, "developer" );
		sq_createslot( m_hVM, -3 );

		sq_pushstring( m_hVM, "GetFunctionSignature", -1 );
		sq_newclosure( m_hVM, &GetFunctionSignature, 0 );
		sq_setnativeclosurename(m_hVM, -1, "GetFunctionSignature" );
		sq_createslot( m_hVM, -3 );

		sq_pop( m_hVM, 1 );

		m_TypeMap.Init( 256 );

		RegisterVector( m_hVM );

		sq_pushroottable(m_hVM );
		sq_pushstring( m_hVM, "Vector", -1 );
		sq_get(m_hVM,-2); //get the function from the root table
		sq_getstackobj(m_hVM,-1,&m_hClassVector);
		sq_addref(m_hVM,&m_hClassVector);
		sq_pop(m_hVM, 2);

		// Initialization scripts & hookup
		Run( (const char *)g_Script_init, "init.nut" );

		m_hOnCreateScopeFunc = LookupObject( "VSquirrel_OnCreateScope" );
		m_hOnReleaseScopeFunc = LookupObject( "VSquirrel_OnReleaseScope" );

		return true;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool Frame( float simTime )
	{
		//
		// <sergiy> removed garbage collection that was called at least 2 times a frame (60 fps server tick / 30fps game = 2 calls a frame)
		//          and took 1 ms on PS3 PPU. It's not necessary because our scripts are supposed to never create circular references
		//          and everything else is handled with ref counting. For the case of bugs creating circular references, the plan is to add
		//          diagnostics that detects such loops and warns the developer.
		//
		if ( m_hDbg )
		{
			sq_rdbg_update( m_hDbg );
			if ( !m_hDbg->IsConnected() )
				DisconnectDebugger();
		}
		return false;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void Shutdown()
	{
		if ( m_hVM ) 
		{
			sq_collectgarbage( m_hVM );
			sq_pushnull(m_hVM);
			sq_setroottable(m_hVM);
			DisconnectDebugger();
			sq_close( m_hVM );
			m_hVM = NULL;
		}
		m_TypeMap.Purge();
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	ScriptLanguage_t GetLanguage()
	{
		return SL_SQUIRREL;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual const char *GetLanguageName()
	{
		return "Squirrel";
	}

	virtual void AddSearchPath( const char *pszSearchPath )
	{

	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSQUIRRELVM GetVM()
	{
		return m_hVM;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool ConnectDebugger()
	{
		if ( developer.GetInt() > 0 )
		{
			if ( !m_hDbg )
			{
				m_hDbg = sq_rdbg_init( m_hVM, 1234, SQTrue);
			}

			if ( !m_hDbg )
			{
				return false;
			}
			//!! SUSPENDS THE APP UNTIL THE DEBUGGER CLIENT CONNECTS
			return SQ_SUCCEEDED(sq_rdbg_waitforconnections(m_hDbg));
		}
		return false;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void DisconnectDebugger()
	{
		if ( m_hDbg )
		{
			sq_rdbg_shutdown( m_hDbg );
			m_hDbg = NULL;
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	ScriptStatus_t Run( const char *pszScript, bool bWait = true )
	{
		Assert( bWait );
		if(SQ_SUCCEEDED(sq_compilebuffer(m_hVM,pszScript,(int)V_strlen(pszScript)*sizeof(SQChar),"unnamed",1))) 
		{
			HSQOBJECT hScript;
			sq_getstackobj(m_hVM,-1, &hScript);
			sq_addref(m_hVM, &hScript );
			sq_pop(m_hVM,1);

			ScriptStatus_t result = CSquirrelVM::ExecuteFunction( (HSCRIPT)(&hScript), NULL, 0, NULL, NULL, bWait );

			sq_release( m_hVM, &hScript );

			return result;
		}
		return SCRIPT_ERROR;

	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL )
	{
		if ( !pszScript || !*pszScript )
		{
			return NULL;
		}

		if(SQ_SUCCEEDED(sq_compilebuffer(m_hVM,pszScript,(int)V_strlen(pszScript)*sizeof(SQChar),(pszId) ? pszId : "unnamed",1))) 
		{
			HSQOBJECT *pRet = new HSQOBJECT;
			sq_getstackobj(m_hVM,-1,pRet);
			sq_addref(m_hVM, pRet);
			sq_pop(m_hVM,1);
			return (HSCRIPT)pRet;
		}
		return NULL;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void ReleaseScript( HSCRIPT hScript )
	{
		ReleaseScriptObject( hScript );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true )
	{
		return CSquirrelVM::ExecuteFunction( hScript, NULL, 0, NULL, hScope, bWait );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	ScriptStatus_t Run( HSCRIPT hScript, bool bWait )
	{
		Assert( bWait );
		return CSquirrelVM::Run( hScript, (HSCRIPT)NULL, bWait );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL )
	{
		if ( !hParent )
		{
			hParent = (HSCRIPT)&m_hVM->_roottable;
		}

		HSQOBJECT result;
		sq_pushobject( m_hVM, m_hOnCreateScopeFunc );
		sq_pushroottable( m_hVM );
		sq_pushstring( m_hVM, pszScope, -1 );
		sq_pushobject( m_hVM, *((HSQOBJECT *)hParent) );
		if ( sq_call( m_hVM, 3, true, SQ_CALL_RAISE_ERROR ) == SQ_OK )
		{
			sq_getstackobj(m_hVM,-1,&result);
			sq_pop(m_hVM,2);
		}
		else
		{
			result = _null_;
			sq_pop(m_hVM,1);
		}

		if ( sq_isnull( result ) )
		{
			return NULL;
		}

		sq_addref(m_hVM, &result);
		HSQOBJECT *pRet = new HSQOBJECT;
		*pRet = result;
		return (HSCRIPT)pRet;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void ReleaseScope( HSCRIPT hScript )
	{
		HSQOBJECT &o = *((HSQOBJECT *)hScript);
		sq_pushobject( m_hVM, m_hOnReleaseScopeFunc );
		sq_pushroottable( m_hVM );
		sq_pushobject(m_hVM, o );
		sq_call( m_hVM, 2, false, SQ_CALL_RAISE_ERROR );
		sq_pop(m_hVM,1);

		ReleaseScriptObject( hScript );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSQOBJECT LookupObject( const char *pszObject, HSCRIPT hScope = NULL, bool bAddRef = true )
	{
		HSQOBJECT result = { OT_NULL, NULL };
		if ( !hScope )
		{
			sq_pushroottable( m_hVM );
		}
		else
		{
			if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
			{
				return _null_;
			}
			sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
		}
		sq_pushstring( m_hVM, pszObject, -1 );
		if ( sq_get( m_hVM, -2 ) == SQ_OK )
		{
			sq_getstackobj(m_hVM,-1,&result);
			if ( bAddRef )
				sq_addref(m_hVM, &result);
			sq_pop(m_hVM,1);
		}
		sq_pop(m_hVM,1);
		return result;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL )
	{
		HSQOBJECT result = LookupObject( pszFunction, hScope );
		if ( !sq_isnull( result ) )
		{
			if ( sq_isclosure(result) )
			{
				HSQOBJECT *pResult = new HSQOBJECT;
				*pResult = result;
				return (HSCRIPT)pResult;
			}
			sq_release( m_hVM, &result );
		}
		return NULL;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void ReleaseFunction( HSCRIPT hScript )
	{
		ReleaseScriptObject( hScript );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope = NULL, bool bWait = true )
	{
		if ( hScope == INVALID_HSCRIPT )
		{
			DevWarning( "Invalid scope handed to script VM\n" );
			return SCRIPT_ERROR;
		}
		if ( m_hDbg )
		{
			extern bool g_bSqDbgTerminateScript;
			if ( g_bSqDbgTerminateScript )
			{
				DisconnectDebugger();
				g_bSqDbgTerminateScript = false;
			}
		}

		Assert( bWait );
		if ( hFunction )
		{
			SQInteger initialTop = m_hVM->_top;
			HSQOBJECT &o = *((HSQOBJECT *)hFunction);
			Assert( bWait );

			sq_pushobject( m_hVM, o);
			if ( hScope ) 
			{
				if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
				{
					sq_pop(m_hVM,1);
					return SCRIPT_ERROR;
				}
				sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
			}
			else
			{
				sq_pushroottable( m_hVM );
			}

			for ( int i = 0; i < nArgs; i++ )
			{
				PushVariant( pArgs[i], true );
			}

			m_TimeStartExecute = Plat_FloatTime();
			if (SQ_SUCCEEDED(sq_call(m_hVM,1+nArgs, ( pReturn != NULL ),SQ_CALL_RAISE_ERROR))) 
			{
				m_TimeStartExecute = 0.0f;
				if ( pReturn )
				{
					HSQOBJECT ret;
					sq_getstackobj(m_hVM,-1,&ret);
					if ( !ConvertToVariant( ret, pReturn ) )
					{
						DevMsg( "Script function returned unsupported type\n" );
					}

					sq_pop(m_hVM,2);
				}
				else
				{
					sq_pop(m_hVM,1);
				}
				if ( m_hVM->_top != initialTop )
				{
					Warning( "Callstack mismatch in VScript/Squirrel!\n" );
					Assert( m_hVM->_top == initialTop );
				}
				if ( !sq_isnull( m_ErrorString ) )
				{
					if ( sq_isstring( m_ErrorString ) )
					{
						sq_throwerror( m_hVM, m_ErrorString._unVal.pString->_val );
					}
					else
					{
						sq_throwerror( m_hVM, "Internal error" );
					}
					m_ErrorString = _null_;
					return SCRIPT_ERROR;
				}
				return SCRIPT_DONE;
			}
			m_TimeStartExecute = 0.0f;
			sq_pop(m_hVM,1);
		}

		if ( pReturn )
		{
			pReturn->m_type = FIELD_VOID;
		}
		return SCRIPT_ERROR;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction )
	{
		sq_pushroottable( m_hVM );
		RegisterFunctionGuts( pScriptFunction );
		sq_pop( m_hVM, 1 );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc )
	{
		COMPILE_TIME_ASSERT( sizeof(pClassDesc) == sizeof(intptr_t) );
		if ( m_TypeMap.Find( (intptr_t)pClassDesc ) != m_TypeMap.InvalidHandle() )
		{
			return true;
		}

		sq_pushroottable( m_hVM );
		sq_pushstring( m_hVM, pClassDesc->m_pszScriptName, -1 );
		if ( sq_get( m_hVM, -2 ) == SQ_OK )
		{
			sq_pop( m_hVM, 2 );
			return false;
		}
		sq_pop( m_hVM, 1 );

		if ( pClassDesc->m_pBaseDesc )
		{
			CSquirrelVM::RegisterClass( pClassDesc->m_pBaseDesc );
		}

		int top = sq_gettop(m_hVM);

		HSQOBJECT newClass;
		newClass = CreateClass( pClassDesc );
		if ( newClass != INVALID_HSQOBJECT )
		{
			sq_pushobject( m_hVM, newClass );
			if ( pClassDesc->m_pfnConstruct )
			{
				sq_pushstring( m_hVM, "constructor", -1 );
				void **pUserData = (void **)sq_newuserdata(m_hVM, sizeof(void *));
				*pUserData = pClassDesc;
				sq_newclosure( m_hVM, &CallConstructor, 1 );
				sq_createslot( m_hVM, -3 );
			}

 			sq_pushstring( m_hVM, "_tostring", -1 );
 			sq_newclosure( m_hVM, &InstanceToString, 0 );
 			sq_createslot( m_hVM, -3 );

			sq_pushstring( m_hVM, "IsValid", -1 );
			sq_newclosure( m_hVM, &InstanceIsValid, 0 );
			sq_createslot( m_hVM, -3 );

			for ( int i = 0; i < pClassDesc->m_FunctionBindings.Count(); i++ )
			{
				RegisterFunctionGuts( &pClassDesc->m_FunctionBindings[i], pClassDesc );
			}
			sq_pop( m_hVM, 1 );
			// more setup required for inheritance?
		}
		sq_settop(m_hVM, top);
		m_TypeMap.Insert( (intptr_t)pClassDesc, newClass._unVal.pClass );
		return true;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool CreateNativeInstance( ScriptClassDesc_t *pDesc, SQUserPointer ud,SQRELEASEHOOK hook )
	{
		sq_pushobject( m_hVM, SQObjectPtr(m_TypeMap[m_TypeMap.Find((intptr_t)pDesc)]) );
		if(SQ_FAILED(sq_createinstance(m_hVM,-1))) 
		{
			sq_pop( m_hVM, 1 );
			return false;
		}
		sq_remove(m_hVM,-2); //removes the class
		if(SQ_FAILED(sq_setinstanceup(m_hVM,-1,ud))) 
		{
			return false;
		}
		sq_setreleasehook(m_hVM,-1,hook);
		return TRUE;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance )
	{
		if ( !CSquirrelVM::RegisterClass( pDesc ) )
		{
			return NULL;
		}

		InstanceContext_t *pInstanceContext = new InstanceContext_t;
		pInstanceContext->pInstance = pInstance;
		pInstanceContext->pClassDesc = pDesc;
		pInstanceContext->name = _null_;

		if ( !CreateNativeInstance( pDesc, pInstanceContext, &ExternalInstanceReleaseHook ) )
		{
			delete pInstanceContext;
			return NULL;
		}

		HSQOBJECT hObject;
		sq_getstackobj(m_hVM,-1,&hObject);
		sq_addref( m_hVM, &hObject );
		sq_pop( m_hVM, 1 );

		HSQOBJECT *pResult = new HSQOBJECT;
		*pResult = hObject;
		return (HSCRIPT)pResult;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId )
	{
		if ( !hInstance )
		{
			ExecuteOnce( DevMsg( "NULL instance passed to vscript!\n" ) );
			return;
		}
		HSQOBJECT *pInstance = (HSQOBJECT *)hInstance;
		Assert( pInstance->_type == OT_INSTANCE );
		if ( pInstance->_type == OT_INSTANCE )
			((InstanceContext_t *)(pInstance->_unVal.pInstance->_userpointer))->name = SQString::Create( _ss(m_hVM), pszId );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void RemoveInstance( HSCRIPT hInstance )
	{
		if ( !hInstance )
		{
			ExecuteOnce( DevMsg( "NULL instance passed to vscript!\n" ) );
			return;
		}
		HSQOBJECT *pInstance = (HSQOBJECT *)hInstance;
		Assert( pInstance->_type == OT_INSTANCE );
		if ( pInstance->_type == OT_INSTANCE )
			((InstanceContext_t *)(pInstance->_unVal.pInstance->_userpointer))->pInstance = NULL;
		ReleaseScriptObject( hInstance );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType )
	{
		if ( !hInstance )
		{
			ExecuteOnce( DevMsg( "NULL instance passed to vscript!\n" ) );
			return NULL;
		}
		HSQOBJECT *pInstance = (HSQOBJECT *)hInstance;
		if ( pInstance->_type == OT_INSTANCE && pInstance->_unVal.pInstance->_userpointer )
		{
			InstanceContext_t *pContext = ((InstanceContext_t *)(pInstance->_unVal.pInstance->_userpointer));
			if ( !pExpectedType || pContext->pClassDesc == pExpectedType || IsClassDerivedFrom( pContext->pClassDesc, pExpectedType ) )
				return pContext->pInstance;
		}
		return NULL;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool IsClassDerivedFrom( const ScriptClassDesc_t *pDerivedClass, const ScriptClassDesc_t *pBaseClass )
	{
		const ScriptClassDesc_t* pType = pDerivedClass->m_pBaseDesc;
		while ( pType )
		{
			if ( pType == pBaseClass )
				return true;

			pType = pType->m_pBaseDesc;
		}
		
		return false;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize )
	{
		Assert( V_strlen(pszRoot) + 40 + 1 <= nBufSize );
		if ( V_strlen(pszRoot) + 40 + 1 <= nBufSize )
		{
			Q_snprintf( pBuf, nBufSize, "%x%x%llx_%s", RandomInt(0, 0xfff), Plat_MSTime(), m_iUniqueIdSerialNumber++, pszRoot ); // random to limit key compare when serial number gets large
			return true;
		}
		Error( "GenerateUniqueKey: buffer too small" );
		if ( nBufSize )
		{
			*pBuf = 0;
		}
		return false;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual bool ValueExists(  HSCRIPT hScope, const char *pszKey )
	{
		return !sq_isnull( LookupObject( pszKey, hScope, false ) );
	}


	bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue )
	{
		if ( !hScope )
		{
			sq_pushroottable( m_hVM );
		}
		else
		{
			if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
			{
				return false;
			}
			sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
		}

		sq_pushstring( m_hVM, pszKey, -1 );
		sq_pushstring( m_hVM, pszValue, -1 );

		sq_createslot( m_hVM, -3 );
		sq_pop( m_hVM, 1 );

		return true;
	}


	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value )
	{
		if ( !hScope )
		{
			sq_pushroottable( m_hVM );
		}
		else
		{
			if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
			{
				return false;
			}
			sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
		}

		sq_pushstring( m_hVM, pszKey, -1 );
		if ( value.m_type == FIELD_HSCRIPT && value.m_hScript )
		{
			HSQOBJECT hObject = *((HSQOBJECT *)value.m_hScript);
			if ( sq_isinstance( hObject ) )
			{
				SQInstance *pInstance = hObject._unVal.pInstance;
				if ( pInstance->_class->_typetag && pInstance->_class->_typetag != TYPETAG_VECTOR )
				{
					InstanceContext_t *pContext = (InstanceContext_t *)pInstance->_userpointer;
					if ( sq_isnull( pContext->name ) )
					{
						pContext->name = m_hVM->_stack[m_hVM->_top - 1];
					}
				}
			}
		}
		PushVariant( value, true );
		sq_createslot( m_hVM, -3 );
		sq_pop(m_hVM,1);

		return true;
	}


	void CreateTable( ScriptVariant_t &Table )
	{
		HSQOBJECT hObject;

		sq_newtable( m_hVM );
		sq_getstackobj(m_hVM, -1, &hObject );
		sq_addref( m_hVM, &hObject );

		ConvertToVariant( hObject, &Table );
		sq_pop( m_hVM, 1 ); 
	}


	//------------------------------------------------------------------------------
	// Purpose: returns the number of elements in the table
	// Input  : hScope - the table
	// Output : returns the number of elements in the table
	//------------------------------------------------------------------------------
	int	GetNumTableEntries( HSCRIPT hScope )
	{
		if ( !hScope )
		{
			sq_pushroottable( m_hVM );
		}
		else
		{
			if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
			{
				return 0;
			}
			sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
		}

		int nCount = sq_getsize( m_hVM, -1 );

		sq_pop( m_hVM , 1 );

		return nCount;
	}


	//------------------------------------------------------------------------------
	// Purpose: Gets a key / value pair from the table
	// Input  : hScope - the table
	//			nInterator - the current location inside of the table.  NOTE this is nota linear representation
	// Output : returns the next iterator spot, otherwise -1 if error or end of table
	//			pKey - the key entry
	//			pValue - the value entry
	//------------------------------------------------------------------------------
	virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue )
	{
		HSQOBJECT KeyResult = { OT_NULL, NULL };
		HSQOBJECT ValueResult = { OT_NULL, NULL };

		if ( !hScope )
		{
			sq_pushroottable( m_hVM );
		}
		else
		{
			if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
			{
				return -1;
			}
			sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
		}

		intp nReturnValue;

		sq_pushinteger(m_hVM, nIterator);

		if ( SQ_SUCCEEDED(sq_next(m_hVM,-2) ) )
		{
			sq_getstackobj(m_hVM,-2, &KeyResult );
			sq_getstackobj(m_hVM,-1, &ValueResult );
			sq_addref( m_hVM,&KeyResult );
			sq_addref( m_hVM,&ValueResult );

			ConvertToVariant( KeyResult, pKey );
			ConvertToVariant( ValueResult, pValue );

			sq_pop(m_hVM,2); //pops key and val before the nex iteration

			sq_getinteger(m_hVM, -1, &nReturnValue);
		}
		else
		{
			nReturnValue = -1;
		}
		sq_pop( m_hVM, 1 ); //pops the null iterator
		sq_pop( m_hVM, 1 );
			
		return nReturnValue;
	}


	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue )
	{
		HSQOBJECT result = LookupObject( pszKey, hScope );
		if ( ConvertToVariant( result, pValue ) && !sq_isnull( result ) )
		{
			return true;
		}
		__Release( result._type, result._unVal );
		return false;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool ClearValue( HSCRIPT hScope, const char *pszKey )
	{
		if ( !hScope )
		{
			sq_pushroottable( m_hVM );
		}
		else
		{
			if ( hScope == INVALID_HSCRIPT || *((HSQOBJECT *)hScope) == INVALID_HSQOBJECT || !sq_istable( *((HSQOBJECT *)hScope) ) )
			{
				return false;
			}
			sq_pushobject( m_hVM, *((HSQOBJECT *)hScope) );
		}
		sq_pushstring( m_hVM, pszKey, -1 );
		sq_deleteslot( m_hVM, -2, false );
		sq_pop(m_hVM,1);
		return false;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void ReleaseValue( ScriptVariant_t &value )
	{
		if ( value.m_type == FIELD_HSCRIPT )
		{
			sq_release( m_hVM, (HSQOBJECT *)value.m_hScript );
			delete ((HSQOBJECT *)value.m_hScript);
		}
		else
		{
			value.Free();
		}
		value.m_type = FIELD_VOID;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual bool RaiseException( const char *pszExceptionText )
	{
		m_ErrorString = SQString::Create( m_hVM->_sharedstate, pszExceptionText );
		return true;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void DumpState()
	{
		struct CIterator : public CSQStateIterator
		{
			CIterator( HSQUIRRELVM hVM )
			{
				indent = 0;
				m_hVM = hVM;
				m_bKey = false;
			}

			void Indent()
			{
				for ( int i = 0; i < indent; i++)
				{
					Msg( "  " );
				}
			}

			virtual void PsuedoKey( const char *pszPsuedoKey )
			{
				Indent();
				Msg( "%s: ", pszPsuedoKey );
				m_bKey = true;
			}

			virtual void Key( SQObjectPtr &key )
			{
				Indent();
				SQObjectPtr res;
				m_hVM->ToString( key, res );
				Msg( "%s: ", res._unVal.pString->_val );
				m_bKey = true;
			}

			virtual void Value( SQObjectPtr &value )
			{
				if ( !m_bKey )
				{
					Indent();
				}
				m_bKey = false;
				SQObjectPtr res;
				m_hVM->ToString( value, res );
				if ( ISREFCOUNTED(value._type) )
					Msg( "%s [%d]\n", res._unVal.pString->_val, value._unVal.pRefCounted->_uiRef );
				else
					Msg( "%s\n", res._unVal.pString->_val );
			}

			virtual bool BeginContained()
			{
				if ( m_bKey )
				{
					Msg( "\n" );
				}
				m_bKey = false;
				Indent();
				Msg( "{\n" );
				indent++;
				return true;
			}

			virtual void EndContained()
			{
				indent--;
				Indent();
				Msg( "}\n" );
			}

			int indent;
			HSQUIRRELVM m_hVM;
			bool m_bKey;
		};

		CIterator iter( m_hVM );
		m_hVM->_sharedstate->Iterate( m_hVM, &iter );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void WriteState( CUtlBuffer *pBuffer)
	{
#ifdef VSQUIRREL_DEBUG_SERIALIZATION
		Msg( "BEGIN WRITE\n" );
#endif
		m_pBuffer = pBuffer;
		sq_collectgarbage( m_hVM );

		m_pBuffer->PutInt( SAVEVERSION );
		m_pBuffer->PutInt64( (int64)m_iUniqueIdSerialNumber );
		WriteVM( m_hVM );

		m_pBuffer = NULL;

		SQCollectable *t = m_hVM->_sharedstate->_gc_chain;
		while(t) 
		{
			t->UnMark();
			t = t->_next;
		}

		m_PtrMap.Purge();
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void ReadState( CUtlBuffer *pBuffer )
	{
#ifdef VSQUIRREL_DEBUG_SERIALIZATION
#ifdef VSQUIRREL_DEBUG_SERIALIZATION_HEAPCHK
		g_pMemAlloc->CrtCheckMemory();
		int flags = g_pMemAlloc->CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
		g_pMemAlloc->CrtSetDbgFlag( flags | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_CHECK_CRT_DF );
#endif
		Msg( "BEGIN READ\n" );
#endif

		if ( pBuffer->GetInt() != SAVEVERSION )
		{
			DevMsg( "Incompatible script version\n" );
			return;
		}
		sq_collectgarbage( m_hVM );
		m_hVM->_sharedstate->_gc_disableDepth++;
		m_pBuffer = pBuffer;
		uint64 uniqueIdSerialNumber = (uint64)m_pBuffer->GetInt64();
		m_iUniqueIdSerialNumber = max( m_iUniqueIdSerialNumber, uniqueIdSerialNumber );
		Verify( pBuffer->GetInt() == OT_THREAD );
		m_PtrMap.Insert( pBuffer->GetPtr(), m_hVM );
		ReadVM( m_hVM );
		m_pBuffer = NULL;
		m_PtrMap.Purge();
		m_hVM->_sharedstate->_gc_disableDepth--;
		sq_collectgarbage( m_hVM );

#ifdef VSQUIRREL_DEBUG_SERIALIZATION_HEAPCHK
		g_pMemAlloc->CrtSetDbgFlag( flags );
#endif
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void RemoveOrphanInstances()
	{

	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void SetOutputCallback( ScriptOutputFunc_t pFunc )
	{

	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	virtual void SetErrorCallback( ScriptErrorFunc_t pFunc )
	{

	}

private:
	struct InstanceContext_t
	{
		void *pInstance;
		ScriptClassDesc_t *pClassDesc;
		SQObjectPtr name;
	};

	//---------------------------------------------------------
	// Callbacks
	//---------------------------------------------------------
	static void PrintFunc(HSQUIRRELVM m_hVM,const SQChar* s,...)
	{
		char string[2048];
		va_list		argptr;
		va_start (argptr,s);
		Q_vsnprintf (string,sizeof(string),s,argptr);
		va_end (argptr);
		
		Msg( "%s", string );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger ReleaseHook( SQUserPointer p, SQInteger size )
	{
		InstanceContext_t *pInstanceContext = (InstanceContext_t *)p;
		pInstanceContext->pClassDesc->m_pfnDestruct( pInstanceContext->pInstance );
		delete pInstanceContext;
		return 0;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger ExternalInstanceReleaseHook( SQUserPointer p, SQInteger size )
	{
		InstanceContext_t *pInstanceContext = (InstanceContext_t *)p;
		delete pInstanceContext;
		return 0;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger GetFunctionSignature( HSQUIRRELVM hVM )
	{
		StackHandler sa(hVM);
		if ( sa.GetParamCount() != 3 )
		{
			return 0;
		}

		HSQOBJECT hFunction = sa.GetObjectHandle( 2 );
		if ( !sq_isclosure( hFunction ) )
		{
			return 0;
		}

		char result[ 512 ] = {0};
		const char *pszName = sa.GetString( 3 );
		SQClosure *pClosure = hFunction._unVal.pClosure;
		SQFunctionProto *pProto = pClosure->_function._unVal.pFunctionProto;

		V_strcat_safe( result,
			"function " );
		if ( pszName && *pszName )
		{
			V_strcat_safe( result,
				pszName );
		}
		else if ( sq_isstring( pProto->_name ) )
		{
			V_strcat_safe( result,
				pProto->_name._unVal.pString->_val );
		}
		else
		{
			V_strcat_safe( result,
				"<unnamed>" );
		}
		V_strcat_safe( result,
			"(" );

		for ( int i = 1; i < pProto->_nparameters; i++ )
		{
			if ( i != 1 )
			{
				V_strcat_safe( result,
					", " );
			}
			if ( sq_isstring( pProto->_parameters[i] ) )
			{
				V_strcat_safe( result,
					pProto->_parameters[i]._unVal.pString->_val );
			}
			else
			{
				V_strcat_safe( result,
					"arg" );
			}
		}
		V_strcat_safe( result,
			")" );

		result[ sizeof( result ) - 1 ] = 0;
		sa.Return( result );

		return 1;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger GetDeveloper( HSQUIRRELVM hVM )
	{
		StackHandler sa(hVM);
		sa.Return( ((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData)->developer.GetInt() );
		return 1;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger CallConstructor( HSQUIRRELVM hVM )
	{
		StackHandler sa(hVM);
		int nActualParams = sa.GetParamCount();
		ScriptClassDesc_t *pClassDesc = *((ScriptClassDesc_t **)sa.GetUserData( nActualParams ));
		InstanceContext_t *pInstanceContext = new InstanceContext_t;
		pInstanceContext->pInstance = pClassDesc->m_pfnConstruct();
		pInstanceContext->pClassDesc = pClassDesc;
		sq_setinstanceup(hVM, 1, pInstanceContext);
		sq_setreleasehook( hVM, 1, &ReleaseHook );
		return 0;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger TranslateCall( HSQUIRRELVM hVM )
	{
		StackHandler sa(hVM);
		int nActualParams = sa.GetParamCount();
		ScriptFunctionBinding_t *pVMScriptFunction = *((ScriptFunctionBinding_t **)sa.GetUserData( nActualParams ));
		int nFormalParams = pVMScriptFunction->m_desc.m_Parameters.Count();
		CUtlVectorFixed<ScriptVariant_t, 14> params;
		ScriptVariant_t returnValue;
		bool bCallFree = false;

		params.SetSize( nFormalParams );

		int i = 0;

		if ( nActualParams )
		{
			int iLimit = MIN( nActualParams, nFormalParams );
			ScriptDataType_t *pCurParamType = pVMScriptFunction->m_desc.m_Parameters.Base();
			for ( i = 0; i < iLimit; i++, pCurParamType++ )
			{
				switch ( *pCurParamType )
				{
				case FIELD_FLOAT:		params[i] = sa.GetFloat( i + 2 ); break;
				case FIELD_CSTRING:		params[i] = sa.GetString( i + 2 ); break;
				case FIELD_VECTOR:		
					{
						Vector *pVector = (Vector *)sa.GetInstanceUp( i + 2, TYPETAG_VECTOR );
						if ( pVector )
						{
							params[i] = pVector;
							break;
						}
						else
						{
							sq_throwerror( hVM, "Vector argument expected" );
							return SQ_ERROR;
						}
					}
				case FIELD_INTEGER:		params[i] = sa.GetInt( i + 2 ); break;
				case FIELD_BOOLEAN:		params[i] = sa.GetBool( i + 2 ); break;
				case FIELD_CHARACTER:	params[i] = sa.GetString( i + 2 )[0]; break;
				case FIELD_HSCRIPT:
					{
						HSQOBJECT object = sa.GetObjectHandle( i+2 );
						if ( object._type == OT_NULL)
						{
							params[i] = (HSCRIPT)NULL;
						}
						else
						{
							HSQOBJECT *pObject = new HSQOBJECT;
							*pObject = object;
							params[i] = (HSCRIPT)pObject;
							params[i].m_flags |= SV_FREE;
							bCallFree = true;
						}
						break;
					}
				default:				break;
				}
			}
		}

#ifdef _DEBUG
		for ( ; i < nFormalParams; i++ )
		{
			Assert( params[i].IsNull() );
		}
#endif

		InstanceContext_t *pContext;
		void *pObject;

		if ( pVMScriptFunction->m_flags & SF_MEMBER_FUNC )
		{
			pContext = (InstanceContext_t *)sa.GetInstanceUp(1,0);

			if ( !pContext )
			{
				sq_throwerror( hVM, "Accessed null instance" );
				return SQ_ERROR;
			}

			pObject = pContext->pInstance;

			if ( !pObject )
			{
				sq_throwerror( hVM, "Accessed null instance" );
				return SQ_ERROR;
			}

			if ( pContext->pClassDesc->pHelper )
			{
				pObject = pContext->pClassDesc->pHelper->GetProxied( pObject );
			}

			if ( !pObject )
			{
				sq_throwerror( hVM, "Accessed null instance" );
				return SQ_ERROR;
			}
		}
		else
		{
			pObject = NULL;
		}

		(*pVMScriptFunction->m_pfnBinding)( pVMScriptFunction->m_pFunction, pObject, params.Base(), params.Count(), ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID ) ? &returnValue : NULL );

		if ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID )
		{
			switch ( returnValue.m_type )
			{
			case FIELD_FLOAT:		sa.Return( (float)returnValue ); break;
			case FIELD_CSTRING:		sa.Return( (const char *)returnValue ); break;
			case FIELD_VECTOR:		
				{
					sq_pushobject( hVM, ((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData)->m_hClassVector );
					sq_createinstance( hVM, -1 );
					sq_setinstanceup( hVM, -1, (SQUserPointer)returnValue.m_pVector );
					sq_setreleasehook( hVM, -1, &VectorRelease );
					sq_remove( hVM, -2 );
					break;
				}
			case FIELD_INTEGER:		sa.Return( (int)returnValue ); break;
			case FIELD_BOOLEAN:		sa.Return( (bool)returnValue ); break;
			case FIELD_CHARACTER:	Assert( 0 ); sq_pushnull( hVM ); break;
			case FIELD_HSCRIPT:
				{
					if ( returnValue.m_hScript )
					{
						sq_pushobject( hVM, *((HSQOBJECT *)returnValue.m_hScript) );
					}
					else
					{
						sq_pushnull( hVM );
					}
					break;
				}
			default:				sq_pushnull( hVM ); break;
			}
		}

		if ( bCallFree )
		{
			for ( i = 0; i < params.Count(); i++ )
			{
				params[i].Free();
			}
		}

		if ( !sq_isnull( ((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData)->m_ErrorString ) )
		{
			if ( sq_isstring( ((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData)->m_ErrorString ) )
			{
				sq_throwerror( hVM, ((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData)->m_ErrorString._unVal.pString->_val );
			}
			else
			{
				sq_throwerror( hVM, "Internal error" );
			}
			((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData)->m_ErrorString = _null_;
			return SQ_ERROR;
		}

		return ( pVMScriptFunction->m_desc.m_ReturnType != FIELD_VOID );
	}

	//-------------------------------------------------------------

	static int QueryContinue( HSQUIRRELVM hVM )
	{
		CSquirrelVM *pVM = ((CSquirrelVM *)hVM->_sharedstate->m_pOwnerData);
		if ( !pVM->m_hDbg )
		{
			if ( pVM->m_TimeStartExecute != 0.0f && Plat_FloatTime() - pVM->m_TimeStartExecute > 0.03f )
			{
				DevMsg( "Script running too long, terminating\n" );
				// @TODO: Mark the offending closure so that it won't be executed again [5/13/2008 tom]
				return SQ_QUERY_BREAK;
			}
		}
		return SQ_QUERY_CONTINUE;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger InstanceToString( HSQUIRRELVM hVM )
	{
		StackHandler sa(hVM);
		InstanceContext_t *pContext = (InstanceContext_t *)sa.GetInstanceUp(1,0);
		char szBuf[64];

		if ( pContext && pContext->pInstance && pContext->pClassDesc->pHelper && pContext->pClassDesc->pHelper->ToString( pContext->pInstance, szBuf, ARRAYSIZE(szBuf) ) )
		{
			sa.Return( szBuf );
		}
		else
		{
			HSQOBJECT hInstance = sa.GetObjectHandle( 1 );
			sq_pushstring( hVM, CFmtStr( "(instance : 0x%p)", (void*)_rawval(hInstance) ), -1 );
		}
		return 1;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger InstanceIsValid( HSQUIRRELVM hVM )
	{
		StackHandler sa(hVM);
		InstanceContext_t *pContext = (InstanceContext_t *)sa.GetInstanceUp(1,0);
		sq_pushbool( hVM, ( pContext && pContext->pInstance ) );
		return 1;
	}



	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	HSQOBJECT CreateClass( ScriptClassDesc_t *pDesc )
	{
		int oldtop = sq_gettop(m_hVM);
		sq_pushroottable(m_hVM);
		sq_pushstring(m_hVM,pDesc->m_pszScriptName,-1);
		if (pDesc->m_pBaseDesc)
		{
			sq_pushstring(m_hVM,pDesc->m_pBaseDesc->m_pszScriptName,-1);
			if (SQ_FAILED(sq_get(m_hVM,-3))) 
			{ // Make sure the base exists if specified by baseName.
				sq_settop(m_hVM,oldtop);
				return INVALID_HSQOBJECT;
			}
		}

		if (SQ_FAILED(sq_newclass(m_hVM,pDesc->m_pBaseDesc ? 1 : 0))) 
		{ // Will inherit from base class on stack from sq_get() above.
			sq_settop(m_hVM,oldtop);
			return INVALID_HSQOBJECT;
		}
		HSQOBJECT hObject;
		sq_getstackobj(m_hVM,-1, &hObject);
		sq_addref(m_hVM, &hObject);
		sq_settypetag(m_hVM,-1,pDesc);
		sq_createslot(m_hVM,-3);
		sq_pop(m_hVM,1);
		return hObject;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void RegisterFunctionGuts( ScriptFunctionBinding_t *pScriptFunction, ScriptClassDesc_t *pClassDesc = NULL )
	{
		char szTypeMask[64];

		if ( pScriptFunction->m_desc.m_Parameters.Count() > ARRAYSIZE(szTypeMask) - 1 )
		{
			AssertMsg1( 0, "Too many agruments for script function %s\n", pScriptFunction->m_desc.m_pszFunction );
			return;
		}

		szTypeMask[0] = '.';
		char *pCurrent = &szTypeMask[1];
		for ( int i = 0; i < pScriptFunction->m_desc.m_Parameters.Count(); i++, pCurrent++ )
		{
			switch ( pScriptFunction->m_desc.m_Parameters[i] )
			{
			case FIELD_CSTRING:	
				*pCurrent = 's';
				break;
			case FIELD_FLOAT:
			case FIELD_INTEGER:
				*pCurrent = 'n';
				break;
			case FIELD_BOOLEAN:
				*pCurrent = 'b';
				break;

			case FIELD_VECTOR:
				*pCurrent = 'x';
				break;

			case FIELD_HSCRIPT:
				*pCurrent = '.';
				break;

			case FIELD_CHARACTER:
			default:
				*pCurrent = FIELD_VOID;
				AssertMsg( 0 , "Not supported" );
				break;
			}
		}
		Assert( pCurrent - szTypeMask < ARRAYSIZE(szTypeMask) - 1 );
		*pCurrent = 0;
		sq_pushstring( m_hVM, pScriptFunction->m_desc.m_pszScriptName, -1 );
		ScriptFunctionBinding_t **pVMScriptFunction = (ScriptFunctionBinding_t **)sq_newuserdata(m_hVM, sizeof(ScriptFunctionBinding_t *));
		*pVMScriptFunction = pScriptFunction;
		sq_newclosure( m_hVM, &TranslateCall, 1 );
		HSQOBJECT hFunction;
		sq_getstackobj( m_hVM, -1, &hFunction );
		sq_setnativeclosurename(m_hVM, -1, pScriptFunction->m_desc.m_pszScriptName );
		sq_setparamscheck( m_hVM, pScriptFunction->m_desc.m_Parameters.Count() + 1, szTypeMask );
		sq_createslot( m_hVM, -3 );

		if ( developer.GetInt() )
		{
			const char *pszHide = SCRIPT_HIDE;
			if ( !pScriptFunction->m_desc.m_pszDescription || *pScriptFunction->m_desc.m_pszDescription != *pszHide )
			{
				char name[512] = {0};
				char signature[512] = {0};

				if ( pClassDesc )
				{
					V_strcat_safe( name,
						pClassDesc->m_pszScriptName );
					V_strcat_safe( name,
						"::" );
				}

				V_strcat_safe( name,
					pScriptFunction->m_desc.m_pszScriptName );

				V_strcat_safe( signature,
					FieldTypeToString( pScriptFunction->m_desc.m_ReturnType ) );
				V_strcat_safe( signature,
					" " );
				V_strcat_safe( signature,
					name );
				V_strcat_safe( signature,
					"(" );
				for ( int i = 0; i < pScriptFunction->m_desc.m_Parameters.Count(); i++ )
				{
					if ( i != 0 )
					{
						V_strcat_safe( signature,
							", " );
					}

					V_strcat_safe( signature,
						FieldTypeToString( pScriptFunction->m_desc.m_Parameters[i] ) );
				}
				V_strcat_safe( signature,
					")" );

				sq_pushobject( m_hVM, LookupObject( "RegisterFunctionDocumentation", NULL, false ) );
				sq_pushroottable( m_hVM );
				sq_pushobject( m_hVM, hFunction );
				sq_pushstring( m_hVM, name, -1 );
				sq_pushstring( m_hVM, signature, -1 );
				sq_pushstring( m_hVM, pScriptFunction->m_desc.m_pszDescription, -1 );
				sq_call( m_hVM, 5, false, /*false*/ true );
				sq_pop( m_hVM, 1 );
			}
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void ReleaseScriptObject( HSCRIPT hScript )
	{
		if ( hScript )
		{
			HSQOBJECT *pScript = (HSQOBJECT *)hScript;
			sq_release( m_hVM, pScript );
			delete pScript;
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void PushVariant( const ScriptVariant_t &value, bool bCopy = false )
	{
		switch ( value.m_type )
		{
		case FIELD_VOID:		sq_pushnull( m_hVM ); break;
		case FIELD_FLOAT:		sq_pushfloat( m_hVM, value ); break;
		case FIELD_CSTRING:		sq_pushstring( m_hVM, value, strlen( value.m_pszString ) ); break;
		case FIELD_VECTOR:
			{
				// @TODO: should make a pool of these and reuse [4/22/2008 tom]
				sq_pushobject( m_hVM, m_hClassVector );
				sq_createinstance( m_hVM, -1 );
				if ( !bCopy )
				{
					sq_setinstanceup( m_hVM, -1, (SQUserPointer)value.m_pVector );
				}
				else
				{
					sq_setinstanceup( m_hVM, -1, (SQUserPointer)new Vector( *value.m_pVector ) );
					sq_setreleasehook( m_hVM, -1, &VectorRelease );
				}
				sq_remove( m_hVM, -2 );
				break;
			}
		case FIELD_INTEGER:		sq_pushinteger( m_hVM, value ); break;
		case FIELD_BOOLEAN:		sq_pushbool( m_hVM, value.m_bool ); break;
		case FIELD_CHARACTER:	{ char sz[2]; sz[0] = value.m_char; sz[1] = 0; sq_pushstring( m_hVM, sz, 1 ); break; }
		case FIELD_HSCRIPT:		if ( value.m_hScript ) sq_pushobject( m_hVM, *((HSQOBJECT *)value.m_hScript) ); else sq_pushnull( m_hVM ); break;
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool ConvertToVariant( HSQOBJECT object, ScriptVariant_t *pReturn )
	{
		switch ( object._type )
		{
		case OT_NULL:		pReturn->m_type = FIELD_VOID; break;
		case OT_INTEGER:	*pReturn = object._unVal.nInteger; break;
		case OT_FLOAT:		*pReturn = object._unVal.fFloat; break;
		case OT_BOOL:		*pReturn = (object._unVal.nInteger != 0); break;
		case OT_STRING:		
			{ 
				int size = object._unVal.pString->_len + 1; 
				pReturn->m_type = FIELD_CSTRING;
				pReturn->m_pszString = new char[size]; 
				memcpy( (void *)pReturn->m_pszString, object._unVal.pString->_val, size ); 
				pReturn->m_flags |= SV_FREE;
			}
			break;
		case OT_INSTANCE:
			{
				SQUserPointer pVector;
				sq_pushobject( m_hVM, object );
				SQRESULT getResult = sq_getinstanceup( m_hVM, -1, &pVector, TYPETAG_VECTOR );
				sq_poptop( m_hVM );
				if ( getResult == SQ_OK )
				{
					pReturn->m_type = FIELD_VECTOR;
					pReturn->m_pVector = new Vector( *((Vector *)pVector) ); 
					pReturn->m_flags |= SV_FREE;
					break;
				}
			}
			// fall through
		default:
			{
				pReturn->m_type = FIELD_HSCRIPT;
				HSQOBJECT *pObject = new HSQOBJECT;
				*pObject = object;
				pReturn->m_hScript = (HSCRIPT)pObject;
			}
		}
		return true;
	}

	//-------------------------------------------------------------------------
	// Serialization
	//-------------------------------------------------------------------------
	enum
	{
		SAVEVERSION = 2
	};

	void WriteObject( const SQObjectPtr &object )
	{
		switch ( object._type )
		{
			case OT_NULL:
				m_pBuffer->PutInt( OT_NULL ); 
				break;
			case OT_INTEGER:
				m_pBuffer->PutInt( OT_INTEGER ); 
				m_pBuffer->PutInt( object._unVal.nInteger );
				break;
			case OT_FLOAT:
				m_pBuffer->PutInt( OT_FLOAT ); 
				m_pBuffer->PutFloat( object._unVal.fFloat);
				break;
			case OT_BOOL:			
				m_pBuffer->PutInt( OT_BOOL ); 
				m_pBuffer->PutInt( object._unVal.nInteger );
				break;
			case OT_STRING:			
				m_pBuffer->PutInt( OT_STRING ); 
				m_pBuffer->PutInt( object._unVal.pString->_len );
				m_pBuffer->PutString( object._unVal.pString->_val );	
				break;
			case OT_TABLE:			WriteTable( object._unVal.pTable );					break;
			case OT_ARRAY:			WriteArray( object._unVal.pArray );					break;
			case OT_USERDATA:		WriteUserData( object._unVal.pUserData );			break;
			case OT_CLOSURE:		WriteClosure( object._unVal.pClosure );				break;
			case OT_NATIVECLOSURE:	WriteNativeClosure( object._unVal.pNativeClosure );	break;
			case OT_GENERATOR:		WriteGenerator( object._unVal.pGenerator );			break;
			case OT_USERPOINTER:	WriteUserPointer( object._unVal.pUserPointer );		break;
			case OT_THREAD:			WriteVM( object._unVal.pThread );					break;
			case OT_FUNCPROTO:		WriteFuncProto( object._unVal.pFunctionProto );		break;
			case OT_CLASS:			WriteClass( object._unVal.pClass );					break;
			case OT_INSTANCE:		WriteInstance( object._unVal.pInstance );			break;
			case OT_WEAKREF:		WriteWeakRef( object._unVal.pWeakRef );				break;
			default:				Assert( 0 ); break;
		}

#ifdef VSQUIRREL_DEBUG_SERIALIZATION
		SQObjectPtr res;
		m_hVM->ToString( object, res );
		Msg( "%d: %s\n", m_pBuffer->TellPut(),  res._unVal.pString->_val );
#endif
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteVM( SQVM *pVM )
	{
		unsigned i;

		m_pBuffer->PutInt( OT_THREAD );
		m_pBuffer->PutPtr( pVM );

		if ( pVM->_uiRef & MARK_FLAG )
			return;
		pVM->_uiRef |= MARK_FLAG;

		WriteObject( pVM->_roottable );
		m_pBuffer->PutInt( pVM->_top );
		m_pBuffer->PutInt( pVM->_stackbase );
		m_pBuffer->PutUnsignedInt( pVM->_stack.size() );
		for( i = 0; i < pVM->_stack.size(); i++ ) 
		{
			WriteObject( pVM->_stack[i] );
		}
		m_pBuffer->PutUnsignedInt( pVM->_vargsstack.size() );
		for( i = 0; i < pVM->_vargsstack.size(); i++ ) 
		{
			WriteObject( pVM->_vargsstack[i] );
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteArray( SQArray *pArray )
	{
		m_pBuffer->PutInt( OT_ARRAY );
		m_pBuffer->PutPtr( pArray );

		if ( pArray->_uiRef & MARK_FLAG )
			return;
		pArray->_uiRef |= MARK_FLAG;

		int len = pArray->_values.size();
		m_pBuffer->PutInt( len );
		for ( int i = 0; i < len; i++ )
			WriteObject( pArray->_values[i] );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteTable( SQTable *pTable )
	{
		m_pBuffer->PutInt( OT_TABLE );
		m_pBuffer->PutPtr( pTable );

		if ( pTable->_uiRef & MARK_FLAG )
			return;
		pTable->_uiRef |= MARK_FLAG;

		m_pBuffer->PutInt( pTable->_delegate != NULL );
		if ( pTable->_delegate )
		{
			WriteObject( pTable->_delegate );
		}

		int len = pTable->_numofnodes;
		m_pBuffer->PutInt( len );
		for(int i = 0; i < len; i++)
		{
			WriteObject( pTable->_nodes[i].key );
			WriteObject( pTable->_nodes[i].val );
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteClass( SQClass *pClass )
	{
		m_pBuffer->PutInt( OT_CLASS );
		m_pBuffer->PutPtr( pClass );

		if ( !pClass || ( pClass->_uiRef & MARK_FLAG ) )
			return;
		pClass->_uiRef |= MARK_FLAG;

		bool bIsNative = ( pClass->_typetag != NULL );
		unsigned i;
		if ( !bIsNative )
		{
			for( i = 0; i < pClass->_methods.size(); i++) 
			{
				if ( sq_isnativeclosure( pClass->_methods[i].val ) )
				{
					bIsNative = true;
					break;
				}
			}
		}
		m_pBuffer->PutInt( bIsNative );
		if ( !bIsNative )
		{
			m_pBuffer->PutInt( pClass->_base != NULL );
			if ( pClass->_base )
			{
				WriteObject( pClass->_base );
			}

			WriteObject( pClass->_members );
			WriteObject( pClass->_attributes );
			m_pBuffer->PutInt( pClass->_defaultvalues.size() );
			for( i = 0; i< pClass->_defaultvalues.size(); i++) 
			{
				WriteObject(pClass->_defaultvalues[i].val);
				WriteObject(pClass->_defaultvalues[i].attrs);
			}
			m_pBuffer->PutInt( pClass->_methods.size() );
			for( i = 0; i < pClass->_methods.size(); i++) 
			{
				WriteObject(pClass->_methods[i].val);
				WriteObject(pClass->_methods[i].attrs);
			}
			m_pBuffer->PutInt( pClass->_metamethods.size() );
			for( i = 0; i < pClass->_metamethods.size(); i++) 
			{
				WriteObject(pClass->_metamethods[i]);
			}
		}
		else
		{
			if ( pClass->_typetag )
			{
				if ( pClass->_typetag == TYPETAG_VECTOR )
				{
					m_pBuffer->PutString( "Vector" );
				}
				else
				{
					ScriptClassDesc_t *pDesc = (ScriptClassDesc_t *)pClass->_typetag;
					m_pBuffer->PutString( pDesc->m_pszScriptName );
				}
			}
			else
			{
				// Have to grovel for the name
				SQObjectPtr key;
				if ( FindKeyForObject( m_hVM->_roottable, pClass, key ) )
				{
					m_pBuffer->PutString( key._unVal.pString->_val );
				}
				else
				{
					Assert( 0 );
					m_pBuffer->PutString( "" );
				}
			}
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteInstance( SQInstance *pInstance )
	{
		m_pBuffer->PutInt( OT_INSTANCE );
		m_pBuffer->PutPtr( pInstance );

		if ( pInstance->_uiRef & MARK_FLAG )
			return;
		pInstance->_uiRef |= MARK_FLAG;

		WriteObject( pInstance->_class );

		unsigned nvalues = pInstance->_class->_defaultvalues.size();
		m_pBuffer->PutInt( nvalues );
		for ( unsigned i =0; i< nvalues; i++ ) 
		{
			WriteObject( pInstance->_values[i] );
		}

		m_pBuffer->PutPtr( pInstance->_class->_typetag );

		if ( pInstance->_class->_typetag )
		{
			if ( pInstance->_class->_typetag == TYPETAG_VECTOR )
			{
				Vector *pVector = (Vector *)pInstance->_userpointer;
				m_pBuffer->PutFloat( pVector->x );
				m_pBuffer->PutFloat( pVector->y );
				m_pBuffer->PutFloat( pVector->z );
			}
			else
			{
				InstanceContext_t *pContext = ((InstanceContext_t *)pInstance->_userpointer);
				WriteObject( pContext->name );
				m_pBuffer->PutPtr( pContext->pInstance );
			}
		}
		else
		{
			WriteUserPointer( NULL );
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteGenerator( SQGenerator *pGenerator )
	{
		ExecuteOnce( Msg( "Save load of generators not well tested. caveat emptor\n" ) );
		WriteObject(pGenerator->_closure);

		m_pBuffer->PutInt( OT_GENERATOR );
		m_pBuffer->PutPtr( pGenerator );

		if ( pGenerator->_uiRef & MARK_FLAG )
			return;
		pGenerator->_uiRef |= MARK_FLAG;
		
		WriteObject( pGenerator->_closure );
		m_pBuffer->PutInt( pGenerator->_stack.size() );
		for(SQUnsignedInteger i = 0; i < pGenerator->_stack.size(); i++) WriteObject(pGenerator->_stack[i]);
		m_pBuffer->PutInt( pGenerator->_vargsstack.size() );
		for(SQUnsignedInteger j = 0; j < pGenerator->_vargsstack.size(); j++) WriteObject(pGenerator->_vargsstack[j]);
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteClosure( SQClosure *pClosure )
	{
		m_pBuffer->PutInt( OT_CLOSURE );
		m_pBuffer->PutPtr( pClosure );
		if ( pClosure->_uiRef & MARK_FLAG )
			return;
		pClosure->_uiRef |= MARK_FLAG;

		WriteObject( pClosure->_function );
		WriteObject( pClosure->_env );

		m_pBuffer->PutInt( pClosure->_outervalues.size() );
		for(SQUnsignedInteger i = 0; i < pClosure->_outervalues.size(); i++) WriteObject(pClosure->_outervalues[i]);
		m_pBuffer->PutInt( pClosure->_defaultparams.size() );
		for(SQUnsignedInteger i = 0; i < pClosure->_defaultparams.size(); i++) WriteObject(pClosure->_defaultparams[i]);
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteNativeClosure( SQNativeClosure *pNativeClosure )
	{
		m_pBuffer->PutInt( OT_NATIVECLOSURE );
		m_pBuffer->PutPtr( pNativeClosure );

		if ( pNativeClosure->_uiRef & MARK_FLAG )
			return;
		pNativeClosure->_uiRef |= MARK_FLAG;

		WriteObject( pNativeClosure->_name );
		return;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteUserData( SQUserData *pUserData )
	{
		m_pBuffer->PutInt( OT_USERDATA );
		m_pBuffer->PutPtr( pUserData );

		if ( pUserData->_uiRef & MARK_FLAG )
			return;
		pUserData->_uiRef |= MARK_FLAG;
		
		// Need to call back or something. Unsure, TBD. [4/3/2008 tom]
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteUserPointer( SQUserPointer pUserPointer )
	{
		m_pBuffer->PutInt( OT_USERPOINTER );
		// Need to call back or something. Unsure, TBD. [4/3/2008 tom]
		m_pBuffer->PutPtr( pUserPointer );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger SqWriteFunc(SQUserPointer up,SQUserPointer data, SQInteger size)
	{
		CSquirrelVM *pThis = (CSquirrelVM *)up;
		pThis->m_pBuffer->Put( data, size );
		return size;
	}

	void WriteFuncProto( SQFunctionProto *pFuncProto )
	{
		m_pBuffer->PutInt( OT_FUNCPROTO );
		m_pBuffer->PutPtr( pFuncProto );

		// Using the map to track these as they're not collectables
		if ( m_PtrMap.Find( pFuncProto ) != m_PtrMap.InvalidIndex() )
		{
			return;
		}
		m_PtrMap.Insert( pFuncProto, pFuncProto );

		pFuncProto->Save( m_hVM, this, &SqWriteFunc );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void WriteWeakRef( SQWeakRef *pWeakRef )
	{
		m_pBuffer->PutInt( OT_WEAKREF );
		WriteObject( pWeakRef->_obj );
	}

	//--------------------------------------------------------
	template <typename T>
	bool BeginRead( T **ppOld, T **ppNew )
	{
		*ppOld = (T *)m_pBuffer->GetPtr();
		if ( *ppOld )
		{
			int iNew = m_PtrMap.Find( *ppOld );
			if ( iNew != m_PtrMap.InvalidIndex() )
			{
				*ppNew = (T*)m_PtrMap[iNew];
				return false;
			}
		}
		*ppNew = NULL;
		return true;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void MapPtr( void *pOld, void *pNew )
	{
		Assert( m_PtrMap.Find( pOld ) == m_PtrMap.InvalidIndex() );
		m_PtrMap.Insert( pOld, pNew );
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool ReadObject( SQObjectPtr &objectOut, const char *pszName = NULL )
	{
		SQObject object;
		bool bResult = true;
		object._type = (SQObjectType)m_pBuffer->GetInt();
		if ( _RAW_TYPE(object._type) < _RT_TABLE )
		{
			switch ( object._type )
			{
			case OT_NULL:
				object._unVal.pUserPointer = 0;
				break;
			case OT_INTEGER:
				object._unVal.nInteger = m_pBuffer->GetInt();
				break;
			case OT_FLOAT:
				object._unVal.fFloat = m_pBuffer->GetFloat();
				break;
			case OT_BOOL:			
				object._unVal.nInteger = m_pBuffer->GetInt();
				break;
			case OT_STRING:
				{
					int len = m_pBuffer->GetInt();
					char *pString = (char *)stackalloc( len + 1 );
					m_pBuffer->GetString( pString, len + 1 );
					pString[len] = 0;
					object._unVal.pString = SQString::Create( m_hVM->_sharedstate, pString, len );
					break;
				}
			default:
				Assert( 0 );
				break;
			}
		}
		else
		{
			switch ( object._type )
			{
			case OT_TABLE:
				{
					object._unVal.pTable = ReadTable();
					break;
				}
			case OT_ARRAY:			
				{
					object._unVal.pArray = ReadArray();
					break;
				}
			case OT_USERDATA:
				{
					object._unVal.pUserData = ReadUserData();			
					break;
				}
			case OT_CLOSURE:
				{
					object._unVal.pClosure = ReadClosure();
					break;
				}
			case OT_NATIVECLOSURE:	
				{
					object._unVal.pNativeClosure = ReadNativeClosure();
					break;
				}
			case OT_GENERATOR:
				{
					object._unVal.pGenerator = ReadGenerator();
					break;
				}
			case OT_USERPOINTER:
				{
					object._unVal.pUserPointer = ReadUserPointer();
					break;
				}
			case OT_THREAD:			
				{
					object._unVal.pThread = ReadVM();
					break;
				}
			case OT_FUNCPROTO:
				{
					object._unVal.pFunctionProto = ReadFuncProto();
					break;
				}
			case OT_CLASS:			
				{
					object._unVal.pClass = ReadClass();			
					break;
				}
			case OT_INSTANCE:
				{
					object._unVal.pInstance = ReadInstance();
					if ( !object._unVal.pInstance )
					{
						// Look for a match in the current root table
						HSQOBJECT hExistingObject = LookupObject( pszName, NULL, false );
						if ( sq_isinstance( hExistingObject ) )
						{
							object._unVal.pInstance = hExistingObject._unVal.pInstance;	
						}
					}
					break;
				}
			case OT_WEAKREF:		
				{
					object._unVal.pWeakRef = ReadWeakRef();
					break;
				}
			default:				
				{
					object._unVal.pUserPointer = NULL;
					Assert( 0 );
				}
			}
			if ( !object._unVal.pUserPointer )
			{
				DevMsg( "Failed to restore a Squirrel object of type %s\n", SQTypeToString( object._type ) );
				object._type = OT_NULL;
				bResult = false;
			}
		}

#ifdef VSQUIRREL_DEBUG_SERIALIZATION
		lastType = object._type;
		SQObjectPtr res;
		if ( ISREFCOUNTED(object._type) )
		{
			SQ_VALIDATE_REF_COUNT( object._unVal.pRefCounted );
			object._unVal.pRefCounted->_uiRef++;
		}
		m_hVM->ToString( object, res );
		if ( ISREFCOUNTED(object._type) )
		{
			object._unVal.pRefCounted->_uiRef--;
			SQ_VALIDATE_REF_COUNT( object._unVal.pRefCounted );
		}
		Msg( "%d: %s [%d]\n", m_pBuffer->TellGet(),  res._unVal.pString->_val, ( ISREFCOUNTED(object._type) ) ? object._unVal.pRefCounted->_uiRef : -1 );
#ifdef VSQUIRREL_DEBUG_SERIALIZATION_HEAPCHK
		_heapchk();
#endif
#endif
		objectOut = object;
		return bResult;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQVM *ReadVM()
	{
		SQVM *pVM = sq_newthread( m_hVM, MIN_STACK_OVERHEAD + 2 );
		m_hVM->Pop();
		return pVM;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	void ReadVM( SQVM *pVM )
	{
		unsigned i;

		ReadObject( pVM->_roottable );
		pVM->_top = m_pBuffer->GetInt();
		pVM->_stackbase  =  m_pBuffer->GetInt();
		unsigned stackSize = m_pBuffer->GetUnsignedInt();
		pVM->_stack.resize( stackSize );
		for( i = 0; i < pVM->_stack.size(); i++ ) 
		{
			ReadObject( pVM->_stack[i] );
		}
		stackSize = m_pBuffer->GetUnsignedInt();
		for( i = 0; i < pVM->_vargsstack.size(); i++ ) 
		{
			ReadObject( pVM->_vargsstack[i] );
		}
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQTable *ReadTable()
	{
		SQTable *pOld;
		SQTable *pTable;

		if ( !BeginRead( &pOld, &pTable ) )
		{
			return pTable;
		}

		pTable = SQTable::Create(_ss(m_hVM), 0);

		MapPtr( pOld, pTable );

		if ( m_pBuffer->GetInt() )
		{
			SQObjectPtr delegate;
			ReadObject( delegate );
			pTable->SetDelegate( delegate._unVal.pTable );
		}
		else
		{
			pTable->_delegate = NULL;
		}
		int n = m_pBuffer->GetInt();
		while ( n-- )
		{
			SQObjectPtr key, value;
			ReadObject( key );
			if ( !ReadObject( value, ( key._type == OT_STRING ) ? key._unVal.pString->_val : NULL ) )
			{
				DevMsg( "Failed to read Squirrel table entry %s\n", ( key._type == OT_STRING ) ? key._unVal.pString->_val : SQTypeToString( key._type ) );
			}
			if ( key._type != OT_NULL )
			{
				pTable->NewSlot( key, value );
			}
		}
		return pTable;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQArray *ReadArray()
	{
		SQArray *pOld;
		SQArray *pArray;
		if ( !BeginRead( &pOld, &pArray ) )
		{
			return pArray;
		}

		pArray = SQArray::Create(_ss(m_hVM), 0);

		MapPtr( pOld, pArray );

		int n = m_pBuffer->GetInt();
		pArray->Reserve( n );

		while ( n-- )
		{
			SQObjectPtr value;
			ReadObject( value );
			pArray->Append( value );
		}
		return pArray;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQClass *ReadClass()
	{
		SQClass *pOld;
		SQClass *pClass;
		if ( !BeginRead( &pOld, &pClass ) )
		{
			return pClass;
		}

		SQClass *pBase = NULL;
 		bool bIsNative = !!m_pBuffer->GetInt();
		// If it's not a C++ defined type...
		if ( !bIsNative )
		{
			if ( m_pBuffer->GetInt() )
			{
				SQObjectPtr base;
				ReadObject( base );
				pBase = base._unVal.pClass;
			}

			SQClass *pClass = SQClass::Create( _ss(m_hVM), pBase );
			MapPtr( pOld, pClass );

			SQObjectPtr members;
			ReadObject( members );
			pClass->_members->Release();
			pClass->_members = members._unVal.pTable;
			__ObjAddRef( members._unVal.pTable );

			ReadObject( pClass->_attributes );
			unsigned i, n;

			n = m_pBuffer->GetUnsignedInt();
			pClass->_defaultvalues.resize( n );
			for ( i = 0; i < n; i++ ) 
			{
				ReadObject(pClass->_defaultvalues[i].val);
				ReadObject(pClass->_defaultvalues[i].attrs);
			}

			n = m_pBuffer->GetUnsignedInt();
			pClass->_methods.resize( n );
			for ( i = 0; i < n; i++ ) 
			{
				ReadObject(pClass->_methods[i].val);
				ReadObject(pClass->_methods[i].attrs);
			}

			n = m_pBuffer->GetUnsignedInt();
			pClass->_metamethods.resize( n );
			for ( i = 0; i < n; i++ ) 
			{
				ReadObject(pClass->_metamethods[i]);
			}
			return pClass;
		}
		else
		{
			char *pszName = (char *)stackalloc( 1024 );
			m_pBuffer->GetString( pszName, 1024 );
			pszName[1023] = 0;

			SQObjectPtr value;
			if ( m_hVM->_roottable._unVal.pTable->Get( SQString::Create( _ss(m_hVM ), pszName ), value ) && sq_isclass( value ) )
			{
				MapPtr( pOld, value._unVal.pClass );
				return value._unVal.pClass;
			}
			MapPtr( pOld, NULL );
		}
		return NULL;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQInstance *ReadInstance()
	{
		SQInstance *pOld;
		SQInstance *pInstance;
		if ( !BeginRead( &pOld, &pInstance ) )
		{
			return pInstance;
		}

		SQObjectPtr pClass;
		ReadObject( pClass );

		unsigned i, n;
		if ( pClass._unVal.pClass )
		{
			pInstance = SQInstance::Create( _ss(m_hVM), pClass._unVal.pClass );

			n = m_pBuffer->GetUnsignedInt();
			for ( i = 0; i < n; i++ ) 
			{
				ReadObject(pInstance->_values[i]);
			}
			m_pBuffer->GetPtr(); // ignored in this path
			if ( pInstance->_class->_typetag )
			{
				if ( pInstance->_class->_typetag == TYPETAG_VECTOR )
				{
					Vector *pValue = new Vector;
					pValue->x = m_pBuffer->GetFloat();
					pValue->y = m_pBuffer->GetFloat();
					pValue->z = m_pBuffer->GetFloat();
					pInstance->_userpointer = pValue;
				}
				else
				{
					InstanceContext_t *pContext = new InstanceContext_t;
					pContext->pInstance = NULL;
					ReadObject( pContext->name );
					pContext->pClassDesc = (ScriptClassDesc_t *)( pInstance->_class->_typetag );
					void *pOldInstance = m_pBuffer->GetPtr();
					if ( sq_isstring(pContext->name) )
					{
						char *pszName = pContext->name._unVal.pString->_val;
						if ( pContext->pClassDesc->pHelper )
						{
							HSQOBJECT *pInstanceHandle = new HSQOBJECT;
							pInstanceHandle->_type = OT_INSTANCE;
							pInstanceHandle->_unVal.pInstance = pInstance;
							pContext->pInstance = pContext->pClassDesc->pHelper->BindOnRead( (HSCRIPT)pInstanceHandle, pOldInstance, pszName );
							if ( pContext->pInstance )
							{
								SQ_VALIDATE_REF_COUNT( pInstance );
								pInstance->_uiRef++;
								sq_addref( m_hVM, pInstanceHandle );
								pInstance->_uiRef--;
								SQ_VALIDATE_REF_COUNT( pInstance );
							}
							else
							{
								delete pInstanceHandle;
							}
						}

						if ( !pContext->pInstance )
						{
							// Look for a match in the current root table
							HSQOBJECT hExistingObject = LookupObject( pszName, NULL, false );
							if ( sq_isinstance(hExistingObject) && hExistingObject._unVal.pInstance->_class == pInstance->_class )
							{
								delete pInstance;
								return hExistingObject._unVal.pInstance;	
							}

							pContext->pInstance = NULL;
						}
					}
					pInstance->_userpointer = pContext;
				}
			}
			else 
			{
				Verify( m_pBuffer->GetInt() == OT_USERPOINTER );
				pInstance->_userpointer = ReadUserPointer();
				Assert( pInstance->_userpointer == NULL );
			}

			MapPtr( pOld, pInstance );
		}
		else
		{
			MapPtr( pOld, NULL );
			n = m_pBuffer->GetUnsignedInt();
			for ( i = 0; i < n; i++ ) 
			{
				SQObjectPtr ignored;
				ReadObject(ignored);
			}
			void *pOldTypeTag = m_pBuffer->GetPtr(); // ignored in this path

			if ( pOldTypeTag )
			{
				if ( pOldTypeTag == TYPETAG_VECTOR )
				{
					m_pBuffer->GetFloat();
					m_pBuffer->GetFloat();
					m_pBuffer->GetFloat();
				}
				else
				{
					SQObjectPtr ignored;
					ReadObject( ignored );
					m_pBuffer->GetPtr();
				}
			}
			else 
			{
				Verify( m_pBuffer->GetInt() == OT_USERPOINTER );
				ReadUserPointer();
			}
			pInstance = NULL;
		}
		return pInstance;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQGenerator *ReadGenerator()
	{
		SQGenerator *pOld;
		SQGenerator *pGenerator;
		if ( !BeginRead( &pOld, &pGenerator ) )
		{
			return pGenerator;
		}

		SQObjectPtr closure;
		ReadObject( closure );

		pGenerator = SQGenerator::Create( _ss(m_hVM), closure._unVal.pClosure );
		MapPtr( pOld, pGenerator );

		unsigned i, n;
		n = m_pBuffer->GetUnsignedInt();
		pGenerator->_stack.resize( n );
		for ( i = 0; i < n; i++ ) 
		{
			ReadObject(pGenerator->_stack[i]);
		}
		n = m_pBuffer->GetUnsignedInt();
		pGenerator->_vargsstack.resize( n );
		for ( i = 0; i < n; i++ ) 
		{
			ReadObject(pGenerator->_vargsstack[i]);
		}
		return pGenerator;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQClosure *ReadClosure()
	{
		SQClosure *pOld;
		SQClosure *pClosure;
		if ( !BeginRead( &pOld, &pClosure ) )
		{
			return pClosure;
		}

		SQObjectPtr proto;
		ReadObject( proto );
		pClosure = SQClosure::Create( _ss(m_hVM), proto._unVal.pFunctionProto );
		MapPtr( pOld, pClosure );

		ReadObject( pClosure->_env );

		unsigned i, n;
		n = m_pBuffer->GetUnsignedInt();
		pClosure->_outervalues.resize( n );
		for ( i = 0; i < n; i++ ) 
		{
			ReadObject(pClosure->_outervalues[i]);
		}

		n = m_pBuffer->GetUnsignedInt();
		pClosure->_defaultparams.resize( n );
		for ( i = 0; i < n; i++ ) 
		{
			ReadObject(pClosure->_defaultparams[i]);
		}

		return pClosure;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQNativeClosure *ReadNativeClosure()
	{
		SQNativeClosure *pOld;
		SQNativeClosure *pClosure;
		if ( !BeginRead( &pOld, &pClosure ) )
		{
			return pClosure;
		}

		SQObjectPtr name;
		ReadObject( name );
		SQObjectPtr value;
		if ( m_hVM->_roottable._unVal.pTable->Get( name, value ) && sq_isnativeclosure(value) )
		{
			MapPtr( pOld, value._unVal.pNativeClosure );
			return value._unVal.pNativeClosure;
		}
		MapPtr( pOld, NULL );
		return NULL; // @TBD [4/15/2008 tom]
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQUserData *ReadUserData()
	{
		m_pBuffer->GetPtr();
		return NULL; // @TBD [4/15/2008 tom]
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQUserPointer *ReadUserPointer()
	{
		m_pBuffer->GetPtr();
		return NULL; // @TBD [4/15/2008 tom]
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	static SQInteger SqReadFunc(SQUserPointer up,SQUserPointer data, SQInteger size)
 	{
 		CSquirrelVM *pThis = (CSquirrelVM *)up;
 		pThis->m_pBuffer->Get( data, size );
 		return size;
 	}
 
	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQFunctionProto *ReadFuncProto()
	{
		SQFunctionProto *pOld;
		SQFunctionProto *pResult;
		if ( !BeginRead( &pOld, &pResult ) )
		{
			return pResult;
		}

		SQObjectPtr result;
		SQFunctionProto::Load( m_hVM, this, &SqReadFunc, result );
		pResult = result._unVal.pFunctionProto;
		SQ_VALIDATE_REF_COUNT( pResult );
		pResult->_uiRef++;
		result.Null();
		pResult->_uiRef--;
		SQ_VALIDATE_REF_COUNT( pResult );
		MapPtr( pOld, pResult );
		return pResult;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	SQWeakRef *ReadWeakRef( )
	{
		SQObjectPtr obj;
		ReadObject( obj );
		if ( !obj._unVal.pRefCounted )
		{
			return NULL;
		}

		// Need to up ref count if read order has weak ref loading first
		Assert( ISREFCOUNTED(obj._type) );
		SQRefCounted *pRefCounted = obj._unVal.pRefCounted;
		SQ_VALIDATE_REF_COUNT( pRefCounted );
		pRefCounted->_uiRef++;

		SQWeakRef *pResult = obj._unVal.pRefCounted->GetWeakRef( obj._type );

		obj.Null();
		pRefCounted->_uiRef--;
		SQ_VALIDATE_REF_COUNT( pRefCounted );

		return pResult;
	}

	//-------------------------------------------------------------
	//
	//-------------------------------------------------------------
	bool FindKeyForObject( const SQObjectPtr &table, void *p, SQObjectPtr &key )
	{
		SQTable *pTable = table._unVal.pTable;
		int len = pTable->_numofnodes;
		for(int i = 0; i < len; i++)
		{
			if ( pTable->_nodes[i].val._unVal.pUserPointer == p )
			{
				key = pTable->_nodes[i].key;
				return true;
			}
			if ( sq_istable( pTable->_nodes[i].val ) )
			{
				if ( FindKeyForObject( pTable->_nodes[i].val, p, key ) )
				{
					return true;
				}
			}
		}
		return false;
	}

	//-------------------------------------------------------------------------
	// 
	//-------------------------------------------------------------------------

	HSQUIRRELVM		m_hVM;
	HSQREMOTEDBG	m_hDbg;
	HSQOBJECT		m_hOnCreateScopeFunc;
	HSQOBJECT		m_hOnReleaseScopeFunc;
	HSQOBJECT		m_hClassVector;
	SQObjectPtr		m_ErrorString;
	uint64			m_iUniqueIdSerialNumber;
	float			m_TimeStartExecute;
#ifdef VSQUIRREL_TEST
	ConVar			developer;
#else
	ConVarRef		developer;
#endif

	CUtlHashFast<SQClass *, CUtlHashFastGenericHash> m_TypeMap;
	friend class CVSquirrelSerializer;

	// Serialization support
	CUtlBuffer *m_pBuffer;
	CUtlMap<void *, void *> m_PtrMap;
};

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------

IScriptVM *ScriptCreateSquirrelVM()
{
	return new CSquirrelVM;
}

void ScriptDestroySquirrelVM( IScriptVM *pVM )
{
	CSquirrelVM *pSquirrelVM = assert_cast<CSquirrelVM *>( pVM );
	delete pSquirrelVM;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

#ifdef VSQUIRREL_TEST

#include "fasttimer.h"

CSquirrelVM g_SquirrelVM;
IScriptVM *g_pScriptVM = &g_SquirrelVM;


//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------

#include <time.h>
#include "fasttimer.h"

static void FromScript_AddBehavior( const char *pBehaviorName, HSCRIPT hTable )
{
	ScriptVariant_t		KeyVariant, ValueVariant;

	Msg( "Behavior: %s\n", pBehaviorName );

	int nInterator = 0;
	int index =	g_pScriptVM->GetNumTableEntries( hTable );
	for( int i = 0; i < index; i++ )
	{
		nInterator = g_pScriptVM->GetKeyValue( hTable, nInterator, &KeyVariant, &ValueVariant );

		Msg( "   %d: %s / %s\n", i, KeyVariant.m_pszString, ValueVariant.m_pszString );

		g_pScriptVM->ReleaseValue( KeyVariant );
		g_pScriptVM->ReleaseValue( ValueVariant );
	}
}

static ScriptVariant_t TestReturn( )
{
	return ScriptVariant_t("test");
}

static Vector MyVectorAdd( Vector A, Vector B )
{
	return A + B;
}

void TestOutput( const char *pszText )
{
	Msg( "%s\n", pszText );
}

bool TestError( const char *pszText )
{
	Msg( "%s\n", pszText );

	return true;
}


class CMyClass
{
public:
	bool Foo( int );
	void Bar( HSCRIPT TableA, HSCRIPT TableB );
	float FooBar( int, const char * );
	float OverlyTechnicalName( bool );
};

bool CMyClass::Foo( int test  )
{
	return true;
}

void CMyClass::Bar( HSCRIPT TableA, HSCRIPT TableB )
{
	ScriptVariant_t MyValue;

	//	g_pScriptVM->CreateTable( MyTable );

	MyValue = 10;
	g_pScriptVM->SetValue( TableA, "1", MyValue );
	MyValue = 20;
	g_pScriptVM->SetValue( TableA, "2", MyValue );
	MyValue = 30;
	g_pScriptVM->SetValue( TableA, "3", MyValue );

	MyValue = 100;
	g_pScriptVM->SetValue( TableB, "1", MyValue );
	MyValue = 200;
	g_pScriptVM->SetValue( TableB, "2", MyValue );
	MyValue = 300;
	g_pScriptVM->SetValue( TableB, "3", MyValue );

	//	return MyTable;
}

float CMyClass::FooBar( int test1, const char *test2 )
{
	return 2.34f;
}

float CMyClass::OverlyTechnicalName( bool test )
{
	return 4.56f;
}

BEGIN_SCRIPTDESC_ROOT_NAMED( CMyClass , "CMyClass", SCRIPT_SINGLETON "" )
DEFINE_SCRIPTFUNC( Foo, "" )
DEFINE_SCRIPTFUNC( Bar, "" )
DEFINE_SCRIPTFUNC( FooBar, "" )
DEFINE_SCRIPTFUNC_NAMED( OverlyTechnicalName, "SimpleMemberName", "" )
END_SCRIPTDESC();

class CMyDerivedClass : public CMyClass
{
public:
	float DerivedFunc() const;
};

BEGIN_SCRIPTDESC( CMyDerivedClass, CMyClass, SCRIPT_SINGLETON "" )
DEFINE_SCRIPTFUNC( DerivedFunc, "" )
END_SCRIPTDESC();

float CMyDerivedClass::DerivedFunc() const
{
	return 8.91f;
}

CMyDerivedClass derivedInstance;

void AnotherFunction()
{
	// Manual class exposure
	g_pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) );

	// Auto registration by instance
	g_pScriptVM->RegisterInstance( &derivedInstance, "theInstance" );
}

int main( int argc, const char **argv)
{
	if ( argc < 2 )
	{
		printf( "No script specified" );
		return 1;
	}
	g_pScriptVM->Init();

	g_pScriptVM->SetOutputCallback( TestOutput );

	AnotherFunction();

	CCycleCount count;
	count.Sample();
	RandomSeed( time( NULL ) ^ count.GetMicroseconds() );
	ScriptRegisterFunction( g_pScriptVM, RandomFloat, "" );
	ScriptRegisterFunction( g_pScriptVM, RandomInt, "" );

	ScriptRegisterFunction( g_pScriptVM, FromScript_AddBehavior, "" );
	ScriptRegisterFunction( g_pScriptVM, MyVectorAdd, "" );
	ScriptRegisterFunction( g_pScriptVM, TestReturn, "" );

	if ( argc == 3 && *argv[2] == 'd' )
	{
		g_pScriptVM->ConnectDebugger();
	}

	int key;
	CScriptScope scope;
	scope.Init( "TestScope" );
	do 
	{
		const char *pszScript = argv[1];
		FILE *hFile = fopen( pszScript, "rb" );
		if ( !hFile )
		{
			printf( "\"%s\" not found.\n", pszScript );
			return 1;
		}

		int nFileLen = _filelength( _fileno( hFile ) );
		char *pBuf = new char[nFileLen + 1];
		fread( pBuf, 1, nFileLen, hFile );
		pBuf[nFileLen] = 0;
		fclose( hFile );

		if (1)
		{
			printf( "Executing script \"%s\"\n----------------------------------------\n", pszScript );
			HSCRIPT hScript = g_pScriptVM->CompileScript( pBuf, ( strrchr( pszScript, '\\' ) ? strrchr( pszScript, '\\' ) + 1 : pszScript ) );
			if ( hScript )
			{
				ScriptVariant_t	Table;

				if ( scope.Run( hScript ) != SCRIPT_ERROR )
				{
					printf( "----------------------------------------\n" );
					printf("Script complete.  Press q to exit, r to reset the scope, m to dump memory usage, enter to run again.\n");
				}
				else
				{
					printf( "----------------------------------------\n" );
					printf("Script execution error.  Press q to exit, r to reset the scope, m to dump memory usage, enter to run again.\n");
				}
				g_pScriptVM->ReleaseScript( hScript );
			}
			else
			{
				printf( "----------------------------------------\n" );
				printf("Script failed to compile.  Press q to exit, r to reset the scope, m to dump memory usage, enter to run again.\n");
			}
		}
		key = _getch(); // Keypress before exit
		if ( key == 'm' )
		{
			Msg( "%d\n", g_pMemAlloc->GetSize( NULL ) );
		}
		if ( key == 'r' )
		{
			scope.Term();
			scope.Init( "TestScope" );
		}
		delete pBuf;
	} while ( key != 'q' );

	scope.Term();
	g_pScriptVM->DisconnectDebugger();

	g_pScriptVM->Shutdown();
	return 0;
}

#endif