You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3548 lines
88 KiB
3548 lines
88 KiB
//========== 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
|