diff --git a/README b/README index 284ab51d14..ff26851139 100644 --- a/README +++ b/README @@ -8,6 +8,7 @@ The Alien Swarm-based radial fog and rope code as well as the multiple skybox su The dynamic RTT shadow angles code is from Saul Rennison on the VDC. (https://developer.valvesoftware.com/wiki/Dynamic_RTT_shadow_angles_in_Source_2007) The vortigaunt LOS fix is from Half-Life 2: Community Edition (dky.tehkingd.u in particular). (https://gitlab.com/RaraCerberus/HL2CE) The parallax corrected cubemap code was originally created by Brian Charles. (https://developer.valvesoftware.com/wiki/Parallax_Corrected_Cubemaps) +The custom VScript library was created by reductor for Mapbase. (https://github.com/mapbase-source/source-sdk-2013/pull/5) Various other code and contributions were based off of pull requests in the Source 2013 SDK (https://github.com/ValveSoftware/source-sdk-2013/pulls) and snippets on the Valve Developer Community (http://developer.valvesoftware.com/). All of the work mentioned above was open source when it was borrowed. diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index 22dde7bad0..4f09a112f5 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -281,6 +281,11 @@ BEGIN_DATADESC( C_ClientRagdoll ) END_DATADESC() +BEGIN_ENT_SCRIPTDESC( C_BaseAnimating, C_BaseEntity, "Animating models client-side" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetPoseParameter, "SetPoseParameter", "Set the specified pose parameter to the specified value" ) + DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) +END_SCRIPTDESC(); + C_ClientRagdoll::C_ClientRagdoll( bool bRestoring ) { m_iCurrentFriction = 0; @@ -1403,6 +1408,15 @@ float C_BaseAnimating::ClampCycle( float flCycle, bool isLooping ) return flCycle; } +void C_BaseAnimating::ScriptSetPoseParameter(const char* szName, float fValue) +{ + CStudioHdr* pHdr = GetModelPtr(); + if (pHdr == NULL) + return; + + int iPoseParam = LookupPoseParameter(pHdr, szName); + SetPoseParameter(pHdr, iPoseParam, fValue); +} void C_BaseAnimating::GetCachedBoneMatrix( int boneIndex, matrix3x4_t &out ) { diff --git a/sp/src/game/client/c_baseanimating.h b/sp/src/game/client/c_baseanimating.h index f91745ef1b..1a460b85c8 100644 --- a/sp/src/game/client/c_baseanimating.h +++ b/sp/src/game/client/c_baseanimating.h @@ -95,6 +95,7 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); DECLARE_INTERPOLATION(); + DECLARE_ENT_SCRIPTDESC(); enum { @@ -445,6 +446,7 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback virtual bool IsViewModel() const; + void ScriptSetPoseParameter(const char* szName, float fValue); protected: // View models scale their attachment positions to account for FOV. To get the unmodified // attachment position (like if you're rendering something else during the view model's DrawModel call), diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 5d1cfd7364..eceec4d87f 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -44,6 +44,8 @@ #include "viewrender.h" #endif +#include "gamestringpool.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -423,6 +425,42 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_AnimTimeMustBeFirst ) RecvPropInt( RECVINFO(m_flAnimTime), 0, RecvProxy_AnimTime ), END_RECV_TABLE() +BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeft, "GetLeftVector", "Get the left vector of the entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) + DEFINE_SCRIPTFUNC( GetTeamNumber, "Gets this entity's team" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEmitSound, "EmitSound", "Plays a sound from this entity." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptPrecacheScriptSound, "PrecacheSoundScript", "Precache a sound for later playing." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSoundDuration, "GetSoundDuration", "Returns float duration of the sound. Takes soundname and optional actormodelname." ) + + DEFINE_SCRIPTFUNC( GetClassname, "" ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityName, "GetName", "" ) + + DEFINE_SCRIPTFUNC_NAMED( WorldSpaceCenter, "GetCenter", "Get vector to center of object - absolute coords" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEyePosition, "EyePosition", "Get vector to eye position - absolute coords" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMins, "GetBoundingMins", "Get a vector containing min bounds, centered on object" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMaxs, "GetBoundingMaxs", "Get a vector containing max bounds, centered on object" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNextMovePeer, "NextMovePeer", "" ) + + DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) +#endif +END_SCRIPTDESC(); #ifndef NO_ENTITY_PREDICTION BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_PredictableId ) @@ -466,6 +504,8 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) RecvPropInt( RECVINFO_NAME(m_hNetworkMoveParent, moveparent), 0, RecvProxy_IntToMoveParent ), RecvPropInt( RECVINFO( m_iParentAttachment ) ), + RecvPropString(RECVINFO(m_iName)), + RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ), RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ), RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ), @@ -1095,6 +1135,8 @@ bool C_BaseEntity::Init( int entnum, int iSerialNum ) m_nCreationTick = gpGlobals->tickcount; + m_hScriptInstance = NULL; + return true; } @@ -1165,6 +1207,7 @@ void C_BaseEntity::Term() g_Predictables.RemoveFromPredictablesList( GetClientHandle() ); } + // If it's play simulated, remove from simulation list if the player still exists... if ( IsPlayerSimulated() && C_BasePlayer::GetLocalPlayer() ) { @@ -1201,6 +1244,12 @@ void C_BaseEntity::Term() RemoveFromLeafSystem(); RemoveFromAimEntsList(); + + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } } @@ -6442,6 +6491,55 @@ int C_BaseEntity::GetCreationTick() const return m_nCreationTick; } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetMoveParent( void ) +{ + return ToHScript( GetMoveParent() ); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript( GetRootMoveParent() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptFirstMoveChild( void ) +{ + return ToHScript( FirstMoveChild() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT C_BaseEntity::ScriptNextMovePeer( void ) +{ + return ToHScript( NextMovePeer() ); +} +#endif + //------------------------------------------------------------------------------ void CC_CL_Find_Ent( const CCommand& args ) { diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index c68e3868b1..cb8eae7a0f 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -36,6 +36,9 @@ #include "toolframework/itoolentity.h" #include "tier0/threadtools.h" +#include "vscript/ivscript.h" +#include "vscript_shared.h" + class C_Team; class IPhysicsObject; class IClientVehicle; @@ -183,6 +186,8 @@ class C_BaseEntity : public IClientEntity DECLARE_DATADESC(); DECLARE_CLIENTCLASS(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); C_BaseEntity(); virtual ~C_BaseEntity(); @@ -256,6 +261,11 @@ class C_BaseEntity : public IClientEntity string_t m_iClassname; + HSCRIPT GetScriptInstance(); + + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; + // IClientUnknown overrides. public: @@ -1119,6 +1129,30 @@ class C_BaseEntity : public IClientEntity virtual int GetBody() { return 0; } virtual int GetSkin() { return 0; } + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } + const Vector& ScriptGetLeft(void) { static Vector vecLeft; GetVectors(NULL, &vecLeft, NULL); return vecLeft; } + const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } + +#ifdef MAPBASE_VSCRIPT + const char* ScriptGetModelName( void ) const { return STRING(GetModelName()); } + + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + void VScriptPrecacheScriptSound(const char* soundname); + + const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } + const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } + + const Vector& ScriptGetBoundingMins( void ) { return m_Collision.OBBMins(); } + const Vector& ScriptGetBoundingMaxs( void ) { return m_Collision.OBBMaxs(); } + + HSCRIPT ScriptGetMoveParent( void ); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild( void ); + HSCRIPT ScriptNextMovePeer( void ); +#endif + // Stubs on client void NetworkStateManualMode( bool activate ) { } void NetworkStateChanged() { } @@ -1266,6 +1300,7 @@ class C_BaseEntity : public IClientEntity void SetRenderMode( RenderMode_t nRenderMode, bool bForceUpdate = false ); RenderMode_t GetRenderMode() const; + const char* GetEntityName(); public: // Determine what entity this corresponds to @@ -1648,6 +1683,8 @@ class C_BaseEntity : public IClientEntity // The owner! EHANDLE m_hOwnerEntity; EHANDLE m_hEffectEntity; + + char m_iName[MAX_PATH]; // This is a random seed used by the networking code to allow client - side prediction code // randon number generators to spit out the same random numbers on both sides for a particular @@ -2203,6 +2240,12 @@ inline bool C_BaseEntity::ShouldRecordInTools() const #endif } +inline const char *C_BaseEntity::GetEntityName() +{ + return m_iName; +} + + C_BaseEntity *CreateEntityByName( const char *className ); #endif // C_BASEENTITY_H diff --git a/sp/src/game/client/c_baseflex.cpp b/sp/src/game/client/c_baseflex.cpp index 606ba0099c..6d0193cce7 100644 --- a/sp/src/game/client/c_baseflex.cpp +++ b/sp/src/game/client/c_baseflex.cpp @@ -1480,7 +1480,7 @@ bool C_BaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool can // expression - // duration - //----------------------------------------------------------------------------- -void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide ) +void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget, bool bClientSide, C_SceneEntity* pSceneEntity) { if ( !scene || !event ) { @@ -1505,6 +1505,7 @@ void C_BaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseE info.m_hTarget = pTarget; info.m_bStarted = false; info.m_bClientSide = bClientSide; + info.m_hSceneEntity = pSceneEntity; if (StartSceneEvent( &info, scene, event, actor, pTarget )) { diff --git a/sp/src/game/client/c_baseflex.h b/sp/src/game/client/c_baseflex.h index 71cee3d81e..6d9f1b54de 100644 --- a/sp/src/game/client/c_baseflex.h +++ b/sp/src/game/client/c_baseflex.h @@ -214,7 +214,7 @@ class C_BaseFlex : public C_BaseAnimatingOverlay, public IHasLocalToGlobalFlexSe virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); // Add the event to the queue for this actor - void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false ); + void AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, C_BaseEntity *pTarget = NULL, bool bClientSide = false, C_SceneEntity* pSceneEntity = NULL); // Remove the event from the queue for this actor void RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill ); diff --git a/sp/src/game/client/c_baseplayer.cpp b/sp/src/game/client/c_baseplayer.cpp index acbd1ade7e..fee1fdc308 100644 --- a/sp/src/game/client/c_baseplayer.cpp +++ b/sp/src/game/client/c_baseplayer.cpp @@ -480,6 +480,13 @@ C_BasePlayer::~C_BasePlayer() s_pLocalPlayer = NULL; } +#ifdef MAPBASE_VSCRIPT + if ( IsLocalPlayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } +#endif + delete m_pFlashlight; } @@ -974,6 +981,16 @@ void C_BasePlayer::OnRestore() input->ClearInputButton( IN_ATTACK | IN_ATTACK2 ); // GetButtonBits() has to be called for the above to take effect input->GetButtonBits( 0 ); + +#ifdef MAPBASE_VSCRIPT + // HACK: (03/25/09) Then the player goes across a transition it doesn't spawn and register + // it's instance. We're hacking around this for now, but this will go away when we get around to + // having entities cross transitions and keep their script state. + if ( g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } +#endif } // For ammo history icons to current value so they don't flash on level transtions diff --git a/sp/src/game/client/c_sceneentity.cpp b/sp/src/game/client/c_sceneentity.cpp index 99a412d24a..1b15faf479 100644 --- a/sp/src/game/client/c_sceneentity.cpp +++ b/sp/src/game/client/c_sceneentity.cpp @@ -674,7 +674,7 @@ void C_SceneEntity::DispatchStartSpeak( CChoreoScene *scene, C_BaseFlex *actor, es.m_pSoundName = event->GetParameters(); EmitSound( filter, actor->entindex(), es ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); // Close captioning only on master token no matter what... if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) @@ -964,7 +964,7 @@ void C_SceneEntity::UnloadScene( void ) //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -984,7 +984,7 @@ void C_SceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, C_BaseFlex *a //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartExpression( CChoreoScene *scene, C_BaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1008,7 +1008,7 @@ void C_SceneEntity::DispatchStartGesture( CChoreoScene *scene, C_BaseFlex *actor if ( !Q_stricmp( event->GetName(), "NULL" ) ) return; - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1023,7 +1023,7 @@ void C_SceneEntity::DispatchProcessGesture( CChoreoScene *scene, C_BaseFlex *act return; actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1046,7 +1046,7 @@ void C_SceneEntity::DispatchEndGesture( CChoreoScene *scene, C_BaseFlex *actor, //----------------------------------------------------------------------------- void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- @@ -1056,7 +1056,7 @@ void C_SceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor void C_SceneEntity::DispatchProcessSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { actor->RemoveSceneEvent( scene, event, false ); - actor->AddSceneEvent( scene, event, NULL, IsClientOnly() ); + actor->AddSceneEvent( scene, event, NULL, IsClientOnly(), this ); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/c_world.cpp b/sp/src/game/client/c_world.cpp index 44a59723d5..238f4cb3a6 100644 --- a/sp/src/game/client/c_world.cpp +++ b/sp/src/game/client/c_world.cpp @@ -62,6 +62,9 @@ BEGIN_RECV_TABLE( C_World, DT_World ) #ifdef MAPBASE RecvPropString(RECVINFO(m_iszChapterTitle)), #endif +#ifdef MAPBASE_VSCRIPT + RecvPropInt(RECVINFO(m_iScriptLanguage)), +#endif END_RECV_TABLE() diff --git a/sp/src/game/client/c_world.h b/sp/src/game/client/c_world.h index 5f1538f9ec..aa1ce2269c 100644 --- a/sp/src/game/client/c_world.h +++ b/sp/src/game/client/c_world.h @@ -41,6 +41,10 @@ class C_World : public C_BaseEntity float GetWaveHeight() const; const char *GetDetailSpriteMaterial() const; +#ifdef MAPBASE_VSCRIPT + ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)m_iScriptLanguage; } +#endif + public: enum { @@ -59,6 +63,9 @@ class C_World : public C_BaseEntity #ifdef MAPBASE char m_iszChapterTitle[64]; #endif +#ifdef MAPBASE_VSCRIPT + int m_iScriptLanguage; +#endif private: void RegisterSharedActivities( void ); diff --git a/sp/src/game/client/cdll_client_int.cpp b/sp/src/game/client/cdll_client_int.cpp index c12cb2c78a..6fa98d682c 100644 --- a/sp/src/game/client/cdll_client_int.cpp +++ b/sp/src/game/client/cdll_client_int.cpp @@ -223,6 +223,8 @@ IReplaySystem *g_pReplay = NULL; IVEngineServer *serverengine = NULL; #endif +IScriptManager *scriptmanager = NULL; + IHaptics* haptics = NULL;// NVNT haptics system interface singleton //============================================================================= @@ -964,6 +966,16 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physi if (!g_pMatSystemSurface) return false; + if ( !CommandLine()->CheckParm( "-noscripting") ) + { + scriptmanager = (IScriptManager *)appSystemFactory( VSCRIPT_INTERFACE_VERSION, NULL ); + + if (scriptmanager == nullptr) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + } + } + #ifdef WORKSHOP_IMPORT_ENABLED if ( !ConnectDataModel( appSystemFactory ) ) return false; diff --git a/sp/src/game/client/client_base.vpc b/sp/src/game/client/client_base.vpc index e15c2a66c4..29e5acc675 100644 --- a/sp/src/game/client/client_base.vpc +++ b/sp/src/game/client/client_base.vpc @@ -493,6 +493,11 @@ $Project $File "viewrender.cpp" $File "$SRCDIR\game\shared\voice_banmgr.cpp" $File "$SRCDIR\game\shared\voice_status.cpp" + $File "vscript_client.cpp" + $File "vscript_client.h" + $File "vscript_client.nut" + $File "$SRCDIR\game\shared\vscript_shared.cpp" + $File "$SRCDIR\game\shared\vscript_shared.h" $File "warp_overlay.cpp" $File "WaterLODMaterialProxy.cpp" $File "$SRCDIR\game\shared\weapon_parse.cpp" diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 7efb1522dd..f245e32236 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -31,6 +31,9 @@ $Project $File "$SRCDIR\game\shared\mapbase\MapEdit.h" $File "$SRCDIR\game\shared\mapbase\matchers.cpp" $File "$SRCDIR\game\shared\mapbase\matchers.h" + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_math.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\c_func_clientclip.cpp" } @@ -51,4 +54,9 @@ $Project } } } + + $Folder "Link Libraries" + { + $Lib "vscript" [$MAPBASE_VSCRIPT] + } } diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp new file mode 100644 index 0000000000..29b998f81f --- /dev/null +++ b/sp/src/game/client/vscript_client.cpp @@ -0,0 +1,592 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_client.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "characterset.h" +#include "isaverestore.h" +#include "gamerules.h" +#include "vscript_client.nut" +#ifdef MAPBASE_VSCRIPT +#include "mapbase/matchers.h" +#include "c_world.h" +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#endif + +extern IScriptManager *scriptmanager; +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: A clientside variant of CScriptEntityIterator. +//----------------------------------------------------------------------------- +class CScriptClientEntityIterator +{ +public: + HSCRIPT First() { return Next(NULL); } + + HSCRIPT Next( HSCRIPT hStartEntity ) + { + return ToHScript( ClientEntityList().NextBaseEntity( ToEnt( hStartEntity ) ) ); + } + + HSCRIPT CreateByClassname( const char *className ) + { + return ToHScript( CreateEntityByName( className ) ); + } + + HSCRIPT FindByClassname( HSCRIPT hStartEntity, const char *szName ) + { + const CEntInfo *pInfo = hStartEntity ? ClientEntityList().GetEntInfoPtr( ToEnt( hStartEntity )->GetRefEHandle() )->m_pNext : ClientEntityList().FirstEntInfo(); + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + C_BaseEntity *ent = (C_BaseEntity *)pInfo->m_pEntity; + if ( !ent ) + continue; + + if ( Matcher_Match( szName, ent->GetClassname() ) ) + return ToHScript( ent ); + } + + return NULL; + } + + HSCRIPT FindByName( HSCRIPT hStartEntity, const char *szName ) + { + const CEntInfo *pInfo = hStartEntity ? ClientEntityList().GetEntInfoPtr( ToEnt( hStartEntity )->GetRefEHandle() )->m_pNext : ClientEntityList().FirstEntInfo(); + for ( ;pInfo; pInfo = pInfo->m_pNext ) + { + C_BaseEntity *ent = (C_BaseEntity *)pInfo->m_pEntity; + if ( !ent ) + continue; + + if ( Matcher_Match( szName, ent->GetEntityName() ) ) + return ToHScript( ent ); + } + + return NULL; + } + +private: +} g_ScriptEntityIterator; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptClientEntityIterator, "CEntities", SCRIPT_SINGLETON "The global list of entities" ) + DEFINE_SCRIPTFUNC( First, "Begin an iteration over the list of entities" ) + DEFINE_SCRIPTFUNC( Next, "Continue an iteration over the list of entities, providing reference to a previously found entity" ) + DEFINE_SCRIPTFUNC( CreateByClassname, "Creates an entity by classname" ) + DEFINE_SCRIPTFUNC( FindByClassname, "Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByName, "Find entities by name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +// Purpose: A base class for VScript-utilizing clientside classes which can persist +// across levels, requiring their scripts to be shut down manually. +//----------------------------------------------------------------------------- +abstract_class IClientScriptPersistable +{ +public: + virtual void TermScript() = 0; +}; + +CUtlVector g_ScriptPersistableList; + +#define SCRIPT_MAT_PROXY_MAX_VARS 8 + +//----------------------------------------------------------------------------- +// Purpose: A material proxy which runs a VScript and allows it to read/write +// to material variables. +//----------------------------------------------------------------------------- +class CScriptMaterialProxy : public IMaterialProxy, public IClientScriptPersistable +{ +public: + CScriptMaterialProxy(); + virtual ~CScriptMaterialProxy(); + + virtual void Release( void ); + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + virtual void OnBind( void *pRenderable ); + virtual IMaterial *GetMaterial() { return NULL; } + + // Proxies can persist across levels and aren't bound to a loaded map. + // The VM, however, is bound to the loaded map, so the proxy's script variables persisting + // causes problems when they're used in a new level with a new VM. + // As a result, we call InitScript() and TermScript() during OnBind and when the level is unloaded respectively. + bool InitScript(); + void TermScript(); + + bool ValidateIndex(int i) + { + if (i > SCRIPT_MAT_PROXY_MAX_VARS || i < 0) + { + Warning("VScriptProxy: %i out of range", i); + return false; + } + + return true; + } + + const char *GetVarString( int i ); + int GetVarInt( int i ); + float GetVarFloat( int i ); + const Vector& GetVarVector( int i ); + + void SetVarString( int i, const char *value ); + void SetVarInt( int i, int value ); + void SetVarFloat( int i, float value ); + void SetVarVector( int i, const Vector &value ); + +private: + IMaterialVar *m_MaterialVars[SCRIPT_MAT_PROXY_MAX_VARS]; + + // Save the keyvalue string for InitScript() + char m_szFilePath[MAX_PATH]; + + CScriptScope m_ScriptScope; + HSCRIPT m_hScriptInstance; + HSCRIPT m_hFuncOnBind; +}; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" ) + DEFINE_SCRIPTFUNC( GetVarString, "Gets a material var's string value" ) + DEFINE_SCRIPTFUNC( GetVarInt, "Gets a material var's int value" ) + DEFINE_SCRIPTFUNC( GetVarFloat, "Gets a material var's float value" ) + DEFINE_SCRIPTFUNC( GetVarVector, "Gets a material var's vector value" ) + DEFINE_SCRIPTFUNC( SetVarString, "Sets a material var's string value" ) + DEFINE_SCRIPTFUNC( SetVarInt, "Sets a material var's int value" ) + DEFINE_SCRIPTFUNC( SetVarFloat, "Sets a material var's float value" ) + DEFINE_SCRIPTFUNC( SetVarVector, "Sets a material var's vector value" ) +END_SCRIPTDESC(); + +CScriptMaterialProxy::CScriptMaterialProxy() +{ + m_hScriptInstance = NULL; + m_hFuncOnBind = NULL; +} + +CScriptMaterialProxy::~CScriptMaterialProxy() +{ +} + + +//----------------------------------------------------------------------------- +// Cleanup +//----------------------------------------------------------------------------- +void CScriptMaterialProxy::Release( void ) +{ + if ( m_hScriptInstance && g_pScriptVM ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } + + delete this; +} + +bool CScriptMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + for (KeyValues *pKey = pKeyValues->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey()) + { + // Get each variable we're looking for + if (Q_strnicmp( pKey->GetName(), "var", 3 ) == 0) + { + int index = atoi(pKey->GetName() + 3); + if (index > SCRIPT_MAT_PROXY_MAX_VARS) + { + Warning("VScript material proxy only supports 8 vars (not %i)\n", index); + continue; + } + + bool foundVar; + m_MaterialVars[index] = pMaterial->FindVar( pKey->GetString(), &foundVar ); + + // Don't init if we didn't find the var + if (!foundVar) + return false; + } + else if (FStrEq( pKey->GetName(), "scriptfile" )) + { + Q_strncpy( m_szFilePath, pKey->GetString(), sizeof( m_szFilePath ) ); + } + } + + return true; +} + +bool CScriptMaterialProxy::InitScript() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + char* iszScriptId = (char*)stackalloc( 1024 ); + g_pScriptVM->GenerateUniqueKey("VScriptProxy", iszScriptId, 1024); + + m_hScriptInstance = g_pScriptVM->RegisterInstance( GetScriptDescForClass( CScriptMaterialProxy ), this ); + g_pScriptVM->SetInstanceUniqeId( m_hScriptInstance, iszScriptId ); + + bool bResult = m_ScriptScope.Init( iszScriptId ); + + if (!bResult) + { + DevMsg("VScriptProxy couldn't create ScriptScope!\n"); + return false; + } + + g_pScriptVM->SetValue( m_ScriptScope, "self", m_hScriptInstance ); + } + + // Don't init if we can't run the script + if (!VScriptRunScript( m_szFilePath, m_ScriptScope, true )) + return false; + + m_hFuncOnBind = m_ScriptScope.LookupFunction( "OnBind" ); + + if (!m_hFuncOnBind) + { + // Don't init if we can't find our func + Warning("VScript material proxy can't find OnBind function\n"); + return false; + } + + g_ScriptPersistableList.AddToTail( this ); + return true; +} + +void CScriptMaterialProxy::TermScript() +{ + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } + + m_hFuncOnBind = NULL; + + m_ScriptScope.Term(); +} + +void CScriptMaterialProxy::OnBind( void *pRenderable ) +{ + if( !pRenderable ) + return; + + if (m_hFuncOnBind != NULL) + { + IClientRenderable *pRend = ( IClientRenderable* )pRenderable; + C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity(); + if ( pEnt ) + { + g_pScriptVM->SetValue( m_ScriptScope, "entity", pEnt->GetScriptInstance() ); + } + else + { + // Needs to register as a null value so the script doesn't break if it looks for an entity + g_pScriptVM->SetValue( m_ScriptScope, "entity", SCRIPT_VARIANT_NULL ); + } + + m_ScriptScope.Call( m_hFuncOnBind, NULL ); + + g_pScriptVM->ClearValue( m_ScriptScope, "entity" ); + } + else + { + // The VM might not exist if we do it from Init(), so we have to do it here. + // TODO: We have no handling for if this fails, how do you cancel a proxy? + if (InitScript()) + OnBind( pRenderable ); + } +} + +const char *CScriptMaterialProxy::GetVarString( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return NULL; + + return m_MaterialVars[i]->GetStringValue(); +} + +int CScriptMaterialProxy::GetVarInt( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return 0; + + return m_MaterialVars[i]->GetIntValue(); +} + +float CScriptMaterialProxy::GetVarFloat( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return 0.0f; + + return m_MaterialVars[i]->GetFloatValue(); +} + +const Vector& CScriptMaterialProxy::GetVarVector( int i ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return vec3_origin; + + if (m_MaterialVars[i]->GetType() != MATERIAL_VAR_TYPE_VECTOR) + return vec3_origin; + + // This is really bad. Too bad! + return *(reinterpret_cast(m_MaterialVars[i]->GetVecValue())); +} + +void CScriptMaterialProxy::SetVarString( int i, const char *value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetStringValue( value ); +} + +void CScriptMaterialProxy::SetVarInt( int i, int value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetIntValue( value ); +} + +void CScriptMaterialProxy::SetVarFloat( int i, float value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetFloatValue( value ); +} + +void CScriptMaterialProxy::SetVarVector( int i, const Vector &value ) +{ + if (!ValidateIndex( i ) || !m_MaterialVars[i]) + return; + + return m_MaterialVars[i]->SetVecValue( value.Base(), 3 ); +} + +EXPOSE_INTERFACE( CScriptMaterialProxy, IMaterialProxy, "VScriptProxy" IMATERIAL_PROXY_INTERFACE_VERSION ); +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static float Time() +{ + return gpGlobals->curtime; +} + +static const char *GetMapName() +{ + return engine->GetLevelName(); +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +bool VScriptClientInit() +{ + VMPROF_START + + if( scriptmanager != NULL ) + { + ScriptLanguage_t scriptLanguage = SL_DEFAULT; + + char const *pszScriptLanguage; +#ifdef MAPBASE_VSCRIPT + if (GetClientWorldEntity()->GetScriptLanguage() != SL_NONE) + { + // Allow world entity to override script language + scriptLanguage = GetClientWorldEntity()->GetScriptLanguage(); + } + else +#endif + if ( CommandLine()->CheckParm( "-scriptlang", &pszScriptLanguage ) ) + { + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + scriptLanguage = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + scriptLanguage = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + scriptLanguage = SL_PYTHON; + } + else + { + DevWarning("-scriptlang does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + scriptLanguage = SL_NONE; + } + + } + if( scriptLanguage != SL_NONE ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( scriptLanguage ); + + if( g_pScriptVM ) + { + Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); + ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + + if ( GameRules() ) + { + GameRules()->RegisterScriptFunctions(); + } + +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); + + IGameSystem::RegisterVScriptAllSystems(); + + RegisterSharedScriptFunctions(); +#else + //g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); +#endif + + if (scriptLanguage == SL_SQUIRREL) + { + g_pScriptVM->Run( g_Script_vscript_client ); + } + + VScriptRunScript( "mapspawn", false ); + + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); + + return true; + } + else + { + DevWarning("VM Did not start!\n"); + } + } + } + else + { + Log( "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptClientTerm() +{ + if( g_pScriptVM != NULL ) + { +#ifdef MAPBASE_VSCRIPT + // Things like proxies can persist across levels, so we have to shut down their scripts manually + for (int i = g_ScriptPersistableList.Count()-1; i >= 0; i--) + { + if (g_ScriptPersistableList[i]) + { + g_ScriptPersistableList[i]->TermScript(); + g_ScriptPersistableList.FastRemove( i ); + } + } +#endif + + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} + + +class CVScriptGameSystem : public CAutoGameSystemPerFrame +{ +public: + // Inherited from IAutoServerSystem + virtual void LevelInitPreEntity( void ) + { + m_bAllowEntityCreationInScripts = true; + VScriptClientInit(); + } + + virtual void LevelInitPostEntity( void ) + { + m_bAllowEntityCreationInScripts = false; +#ifdef MAPBASE_VSCRIPT + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if (pPlayer) + { + g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); + } +#endif + } + + virtual void LevelShutdownPostEntity( void ) + { + VScriptClientTerm(); + } + + virtual void FrameUpdatePostEntityThink() + { + if ( g_pScriptVM ) + g_pScriptVM->Frame( gpGlobals->frametime ); + } + + bool m_bAllowEntityCreationInScripts; +}; + +CVScriptGameSystem g_VScriptGameSystem; + +bool IsEntityCreationAllowedInScripts( void ) +{ + return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; +} + + diff --git a/sp/src/game/client/vscript_client.h b/sp/src/game/client/vscript_client.h new file mode 100644 index 0000000000..b2aa3aae8b --- /dev/null +++ b/sp/src/game/client/vscript_client.h @@ -0,0 +1,22 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SERVER_H +#define VSCRIPT_SERVER_H + +#include "vscript/ivscript.h" +#include "vscript_shared.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM * g_pScriptVM; + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#endif // VSCRIPT_SERVER_H diff --git a/sp/src/game/client/vscript_client.nut b/sp/src/game/client/vscript_client.nut new file mode 100644 index 0000000000..5fce6ff5ec --- /dev/null +++ b/sp/src/game/client/vscript_client.nut @@ -0,0 +1,22 @@ +static char g_Script_vscript_client[] = R"vscript( +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +function UniqueString( string = "" ) +{ + return DoUniqueString( string.tostring() ); +} + +function IncludeScript( name, scope = null ) +{ + if ( scope == null ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +)vscript"; \ No newline at end of file diff --git a/sp/src/game/server/ai_baseactor.cpp b/sp/src/game/server/ai_baseactor.cpp index 224cf42d00..2ddc60f722 100644 --- a/sp/src/game/server/ai_baseactor.cpp +++ b/sp/src/game/server/ai_baseactor.cpp @@ -98,6 +98,15 @@ BEGIN_DATADESC( CAI_BaseActor ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_BaseActor, CAI_BaseNPC, "The base class for NPCs which act in complex choreo scenes." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTarget, "AddLookTarget", "Add a potential look target for this actor." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddLookTargetPos, "AddLookTargetPos", "Add a potential look target position for this actor." ) + +END_SCRIPTDESC(); +#endif + BEGIN_SIMPLE_DATADESC( CAI_InterestTarget_t ) DEFINE_FIELD( m_eType, FIELD_INTEGER ), diff --git a/sp/src/game/server/ai_baseactor.h b/sp/src/game/server/ai_baseactor.h index b2c8c5ca9f..c51391f396 100644 --- a/sp/src/game/server/ai_baseactor.h +++ b/sp/src/game/server/ai_baseactor.h @@ -170,6 +170,15 @@ class CAI_BaseActor : public CAI_ExpresserHost void ClearExpression(); const char * GetExpression(); +#ifdef MAPBASE_VSCRIPT + //--------------------------------- + + void ScriptAddLookTarget( HSCRIPT pTarget, float flImportance, float flDuration, float flRamp = 0.0 ) { AddLookTarget(ToEnt(pTarget), flImportance, flDuration, flRamp); } + void ScriptAddLookTargetPos( const Vector &vecPosition, float flImportance, float flDuration, float flRamp = 0.0 ) { AddLookTarget(vecPosition, flImportance, flDuration, flRamp); } + + //--------------------------------- +#endif + enum { SCENE_AI_BLINK = 1, @@ -190,6 +199,9 @@ class CAI_BaseActor : public CAI_ExpresserHost DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif private: enum { diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 1a5f36b73c..74e14f875d 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -689,6 +689,131 @@ void CAI_BaseNPC::InputSetFriendlyFire( inputdata_t &inputdata ) } #endif +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptGetEnemy() +{ + return ToHScript( GetEnemy() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::VScriptSetEnemy( HSCRIPT pEnemy ) +{ + SetEnemy( ToEnt( pEnemy ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Vector CAI_BaseNPC::VScriptGetEnemyLKP() +{ + return GetEnemyLKP(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptFindEnemyMemory( HSCRIPT pEnemy ) +{ + HSCRIPT hScript = NULL; + AI_EnemyInfo_t *info = GetEnemies()->Find( ToEnt(pEnemy) ); + if (info) + { + hScript = g_pScriptVM->RegisterInstance( info ); + } + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CAI_BaseNPC::VScriptGetState() +{ + return (int)GetState(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptGetHintNode() +{ + return ToHScript( GetHintNode() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CAI_BaseNPC::VScriptGetSchedule() +{ + const char *pName = NULL; + pName = GetCurSchedule()->GetName(); + if (!pName) + pName = "Unknown"; + + return pName; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CAI_BaseNPC::VScriptGetScheduleID() +{ + int iSched = GetCurSchedule()->GetId(); + + // Local IDs are needed to correspond with user-friendly enums + if ( AI_IdIsGlobal( iSched ) ) + { + iSched = GetClassScheduleIdSpace()->ScheduleGlobalToLocal(iSched); + } + + return iSched; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_BaseNPC::VScriptSetSchedule( const char *szSchedule ) +{ + SetSchedule( GetScheduleID( szSchedule ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CAI_BaseNPC::VScriptGetTask() +{ + const Task_t *pTask = GetTask(); + const char *pName = NULL; + if (pTask) + pName = TaskName( pTask->iTask ); + else + pName = "None"; + + return pName; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CAI_BaseNPC::VScriptGetTaskID() +{ + const Task_t *pTask = GetTask(); + int iID = -1; + if (pTask) + iID = GetTaskID( TaskName( pTask->iTask ) ); + + return iID; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CAI_BaseNPC::VScriptGetExpresser() +{ + HSCRIPT hScript = NULL; + CAI_Expresser *pExpresser = GetExpresser(); + if (pExpresser) + { + hScript = g_pScriptVM->RegisterInstance( pExpresser ); + } + + return hScript; +} +#endif + bool CAI_BaseNPC::PassesDamageFilter( const CTakeDamageInfo &info ) { if ( ai_block_damage.GetBool() ) @@ -6403,6 +6528,34 @@ Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) break; } } + +#ifdef MAPBASE_VSCRIPT + if ( m_ScriptScope.IsInitialized() ) + { + g_pScriptVM->SetValue( "activity", GetActivityName(eNewActivity) ); + g_pScriptVM->SetValue( "activity_id", (int)eNewActivity ); + + ScriptVariant_t functionReturn; + if( CallScriptFunction( "NPC_TranslateActivity", &functionReturn ) ) + { + if (functionReturn.m_type == FIELD_INTEGER) + { + Activity activity = (Activity)functionReturn.m_int; + if (activity != ACT_INVALID) + eNewActivity = (Activity)functionReturn.m_int; + } + else + { + Activity activity = (Activity)GetActivityID( functionReturn.m_pszString ); + if (activity != ACT_INVALID) + eNewActivity = activity; + } + } + + g_pScriptVM->ClearValue( "activity" ); + g_pScriptVM->ClearValue( "activity_id" ); + } +#endif #else Assert( eNewActivity != ACT_INVALID ); @@ -11758,6 +11911,56 @@ BEGIN_DATADESC( CAI_BaseNPC ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPCs derive from." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetEnemy, "GetEnemy", "Get the NPC's current enemy." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetEnemy, "SetEnemy", "Set the NPC's current enemy." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetEnemyLKP, "GetEnemyLKP", "Get the last known position of the NPC's current enemy." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptFindEnemyMemory, "FindEnemyMemory", "Get information about the NPC's current enemy." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetState, "GetNPCState", "Get the NPC's current state." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetHintGroup, "GetHintGroup", "Get the name of the NPC's hint group." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetHintNode, "GetHintNode", "Get the NPC's current AI hint." ) + + DEFINE_SCRIPTFUNC( CapabilitiesGet, "Get the capabilities the NPC currently possesses." ) + DEFINE_SCRIPTFUNC( CapabilitiesAdd, "Add capabilities to the NPC." ) + DEFINE_SCRIPTFUNC( CapabilitiesRemove, "Remove capabilities from the NPC." ) + DEFINE_SCRIPTFUNC( CapabilitiesClear, "Clear capabilities for the NPC." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActivity, "GetActivity", "Get the NPC's current activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActivityID, "GetActivityID", "Get the NPC's current activity ID." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivity, "SetActivity", "Set the NPC's current activity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivityID, "SetActivityID", "Set the NPC's current activity ID." ) + DEFINE_SCRIPTFUNC( ResetActivity, "Reset the NPC's current activity." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSchedule, "GetSchedule", "Get the NPC's current schedule." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetScheduleID, "GetScheduleID", "Get the NPC's current schedule ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetSchedule, "SetSchedule", "Set the NPC's current schedule." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetScheduleID, "SetScheduleID", "Set the NPC's current schedule ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetTask, "GetTask", "Get the NPC's current task." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetTaskID, "GetTaskID", "Get the NPC's current task ID." ) + DEFINE_SCRIPTFUNC( ClearSchedule, "Clear the NPC's current schedule for the specified reason." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptHasCondition, "HasCondition", "Get whether the NPC has a condition." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptHasConditionID, "HasConditionID", "Get whether the NPC has a condition ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptSetCondition, "SetCondition", "Set a condition on the NPC." ) + DEFINE_SCRIPTFUNC_NAMED( SetCondition, "SetConditionID", "Set a condition on the NPC by ID." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptClearCondition, "ClearCondition", "Clear a condition on the NPC." ) + DEFINE_SCRIPTFUNC_NAMED( ClearCondition, "ClearConditionID", "Clear a condition on the NPC by ID." ) + + DEFINE_SCRIPTFUNC( IsMoving, "Check if the NPC is moving." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetExpresser, "GetExpresser", "Get a handle for this NPC's expresser." ) + + DEFINE_SCRIPTFUNC( IsCommandable, "Check if the NPC is commandable." ) + DEFINE_SCRIPTFUNC( IsInPlayerSquad, "Check if the NPC is in the player's squad." ) + +END_SCRIPTDESC(); +#endif + BEGIN_SIMPLE_DATADESC( AIScheduleState_t ) DEFINE_FIELD( iCurTask, FIELD_INTEGER ), DEFINE_FIELD( fTaskStatus, FIELD_INTEGER ), diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index d05b4a2fc6..07a06acdbe 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -547,6 +547,9 @@ class CAI_BaseNPC : public CBaseCombatCharacter, DECLARE_DATADESC(); DECLARE_SERVERCLASS(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif virtual int Save( ISave &save ); virtual int Restore( IRestore &restore ); @@ -1207,6 +1210,40 @@ class CAI_BaseNPC : public CBaseCombatCharacter, virtual void AddGrenades( int inc, CBaseEntity *pLastGrenade = NULL ) { ; } #endif +#ifdef MAPBASE_VSCRIPT + // VScript stuff uses "VScript" instead of just "Script" to avoid + // confusion with NPC_STATE_SCRIPT or StartScripting + HSCRIPT VScriptGetEnemy(); + void VScriptSetEnemy( HSCRIPT pEnemy ); + Vector VScriptGetEnemyLKP(); + + HSCRIPT VScriptFindEnemyMemory( HSCRIPT pEnemy ); + + int VScriptGetState(); + + const char* VScriptGetHintGroup() { return STRING( GetHintGroup() ); } + HSCRIPT VScriptGetHintNode(); + + const char* ScriptGetActivity() { return GetActivityName( GetActivity() ); } + int ScriptGetActivityID() { return GetActivity(); } + void ScriptSetActivity( const char *szActivity ) { SetActivity( (Activity)GetActivityID( szActivity ) ); } + void ScriptSetActivityID( int iActivity ) { SetActivity((Activity)iActivity); } + + const char* VScriptGetSchedule(); + int VScriptGetScheduleID(); + void VScriptSetSchedule( const char *szSchedule ); + void VScriptSetScheduleID( int iSched ) { SetSchedule( iSched ); } + const char* VScriptGetTask(); + int VScriptGetTaskID(); + + bool VScriptHasCondition( const char *szCondition ) { return HasCondition( GetConditionID( szCondition ) ); } + bool VScriptHasConditionID( int iCondition ) { return HasCondition( iCondition ); } + void VScriptSetCondition( const char *szCondition ) { SetCondition( GetConditionID( szCondition ) ); } + void VScriptClearCondition( const char *szCondition ) { ClearCondition( GetConditionID( szCondition ) ); } + + HSCRIPT VScriptGetExpresser(); +#endif + //----------------------------------------------------- // Dynamic scripted NPC interactions //----------------------------------------------------- diff --git a/sp/src/game/server/ai_default.cpp b/sp/src/game/server/ai_default.cpp index e80817ad51..879fdd1765 100644 --- a/sp/src/game/server/ai_default.cpp +++ b/sp/src/game/server/ai_default.cpp @@ -386,6 +386,44 @@ CAI_Schedule *CAI_BaseNPC::GetScheduleOfType( int scheduleType ) scheduleType = TranslateSchedule( scheduleType ); AI_PROFILE_SCOPE_END(); +#ifdef MAPBASE_VSCRIPT + if ( m_ScriptScope.IsInitialized() ) + { + // Some of this code should know if there's a function first, so look + // up the function beforehand instead of using CallScriptFunction() + HSCRIPT hFunc = m_ScriptScope.LookupFunction( "NPC_TranslateSchedule" ); + if (hFunc) + { + int newSchedule = scheduleType; + if ( AI_IdIsLocal( newSchedule ) ) + { + newSchedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(newSchedule); + } + + g_pScriptVM->SetValue( "schedule", GetSchedulingSymbols()->ScheduleIdToSymbol( newSchedule ) ); + g_pScriptVM->SetValue( "schedule_id", scheduleType ); // Use the local ID + + ScriptVariant_t functionReturn; + m_ScriptScope.Call( hFunc, &functionReturn ); + + if (functionReturn.m_type == FIELD_INTEGER) + { + newSchedule = functionReturn.m_int; + } + else + { + newSchedule = GetScheduleID( functionReturn.m_pszString ); + } + + if (newSchedule != scheduleType && newSchedule > -1) + scheduleType = newSchedule; + + g_pScriptVM->ClearValue( "schedule" ); + g_pScriptVM->ClearValue( "schedule_id" ); + } + } +#endif + // Get a pointer to that schedule CAI_Schedule *schedule = GetSchedule(scheduleType); diff --git a/sp/src/game/server/ai_goalentity.cpp b/sp/src/game/server/ai_goalentity.cpp index 3198109714..916d9210fb 100644 --- a/sp/src/game/server/ai_goalentity.cpp +++ b/sp/src/game/server/ai_goalentity.cpp @@ -38,6 +38,15 @@ BEGIN_DATADESC( CAI_GoalEntity ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_GoalEntity, CBaseEntity, "The base class for goal entities used to control NPC behavior." ) + + DEFINE_SCRIPTFUNC( IsActive, "Check if the goal entity is active." ) + DEFINE_SCRIPTFUNC( NumActors, "Get the number of actors using this goal entity." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------- diff --git a/sp/src/game/server/ai_goalentity.h b/sp/src/game/server/ai_goalentity.h index 2a6805f46c..56252d2506 100644 --- a/sp/src/game/server/ai_goalentity.h +++ b/sp/src/game/server/ai_goalentity.h @@ -27,6 +27,9 @@ class CAI_GoalEntity : public CBaseEntity, public IEntityListener { DECLARE_CLASS( CAI_GoalEntity, CBaseEntity ); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: CAI_GoalEntity() : m_iszActor(NULL_STRING), diff --git a/sp/src/game/server/ai_hint.cpp b/sp/src/game/server/ai_hint.cpp index 644e054f5f..1f8ec4d142 100644 --- a/sp/src/game/server/ai_hint.cpp +++ b/sp/src/game/server/ai_hint.cpp @@ -900,6 +900,23 @@ BEGIN_DATADESC( CAI_Hint ) END_DATADESC( ); +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_Hint, CBaseEntity, "An entity which gives contextual pointers for NPCs." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHintType, "GetHintType", "Get the hint's type ID." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUser, "GetUser", "Get the hint's current user." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHintGroup, "GetHintGroup", "Get the name of the hint's group." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHintActivity, "GetHintActivity", "Get the name of the hint activity." ) + + DEFINE_SCRIPTFUNC( IsDisabled, "Check if the hint is disabled." ) + DEFINE_SCRIPTFUNC( IsLocked, "Check if the hint is locked." ) + DEFINE_SCRIPTFUNC( GetNodeId, "Get the hint's node ID." ) + DEFINE_SCRIPTFUNC( Yaw, "Get the hint's yaw." ) + DEFINE_SCRIPTFUNC( GetDirection, "Get the hint's direction." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------------------------------------------------ // Purpose : //------------------------------------------------------------------------------ diff --git a/sp/src/game/server/ai_hint.h b/sp/src/game/server/ai_hint.h index e88d802152..25fd4fdb2c 100644 --- a/sp/src/game/server/ai_hint.h +++ b/sp/src/game/server/ai_hint.h @@ -312,6 +312,13 @@ class CAI_Hint : public CServerOnlyEntity void NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ); #endif +#ifdef MAPBASE_VSCRIPT + int ScriptGetHintType() { return (int)HintType(); } + HSCRIPT ScriptGetUser() { return ToHScript( User() ); } + const char* ScriptGetHintGroup() { return STRING( GetGroup() ); } + const char* ScriptGetHintActivity() { return STRING( HintActivityName() ); } +#endif + private: void Spawn( void ); virtual void Activate(); @@ -343,6 +350,9 @@ class CAI_Hint : public CServerOnlyEntity friend class CAI_HintManager; DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif }; #define SF_ALLOW_JUMP_UP 65536 diff --git a/sp/src/game/server/ai_memory.cpp b/sp/src/game/server/ai_memory.cpp index 7ac69311a2..46cd09f8eb 100644 --- a/sp/src/game/server/ai_memory.cpp +++ b/sp/src/game/server/ai_memory.cpp @@ -146,6 +146,29 @@ BEGIN_SIMPLE_DATADESC( AI_EnemyInfo_t ) // NOT SAVED nextEMemory END_DATADESC() +#ifdef MAPBASE_VSCRIPT +#define DEFINE_ENEMY_INFO_SCRIPTFUNCS(name, desc) \ + DEFINE_SCRIPTFUNC_NAMED( Get##name, #name, "Get " desc ) \ + DEFINE_SCRIPTFUNC( Set##name, "Set " desc ) + +BEGIN_SCRIPTDESC_ROOT( AI_EnemyInfo_t, "Accessor for information about an enemy." ) + DEFINE_SCRIPTFUNC( Enemy, "Get the enemy." ) + DEFINE_SCRIPTFUNC( SetEnemy, "Set the enemy." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastKnownLocation, "the enemy's last known location." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( LastSeenLocation, "the enemy's last seen location." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastSeen, "the last time the enemy was seen." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeFirstSeen, "the first time the enemy was seen." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReacquired, "the last time the enemy was reaquired." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeValidEnemy, "the time at which the enemy can be selected (reaction delay)." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeLastReceivedDamageFrom, "the last time damage was received from this enemy." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( TimeAtFirstHand, "the time at which the enemy was seen firsthand." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( DangerMemory, "the memory of danger position w/o enemy pointer." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( EludedMe, "whether the enemy is not at the last known location." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( Unforgettable, "whether the enemy is unfortgettable." ) + DEFINE_ENEMY_INFO_SCRIPTFUNCS( MobbedMe, "whether the enemy was part of a mob at some point." ) +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- CAI_Enemies::CAI_Enemies(void) diff --git a/sp/src/game/server/ai_memory.h b/sp/src/game/server/ai_memory.h index d348c53e02..faa482a267 100644 --- a/sp/src/game/server/ai_memory.h +++ b/sp/src/game/server/ai_memory.h @@ -45,6 +45,29 @@ struct AI_EnemyInfo_t bool bUnforgettable; bool bMobbedMe; // True if enemy was part of a mob at some point +#ifdef MAPBASE_VSCRIPT + // Script functions. + #define ENEMY_INFO_SCRIPT_FUNCS(type, name, var) \ + type Get##name() { return var; } \ + void Set##name( type v ) { var = v; } + + HSCRIPT Enemy() { return ToHScript(hEnemy); } + void SetEnemy( HSCRIPT ent ) { hEnemy = ToEnt(ent); } + + ENEMY_INFO_SCRIPT_FUNCS( Vector, LastKnownLocation, vLastKnownLocation ); + ENEMY_INFO_SCRIPT_FUNCS( Vector, LastSeenLocation, vLastSeenLocation ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastSeen, timeLastSeen ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeFirstSeen, timeFirstSeen ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastReacquired, timeLastReacquired ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeValidEnemy, timeValidEnemy ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeLastReceivedDamageFrom, timeLastReceivedDamageFrom ); + ENEMY_INFO_SCRIPT_FUNCS( float, TimeAtFirstHand, timeAtFirstHand ); + ENEMY_INFO_SCRIPT_FUNCS( bool, DangerMemory, bDangerMemory ); + ENEMY_INFO_SCRIPT_FUNCS( bool, EludedMe, bEludedMe ); + ENEMY_INFO_SCRIPT_FUNCS( bool, Unforgettable, bUnforgettable ); + ENEMY_INFO_SCRIPT_FUNCS( bool, MobbedMe, bMobbedMe ); +#endif + DECLARE_SIMPLE_DATADESC(); }; diff --git a/sp/src/game/server/ai_network.cpp b/sp/src/game/server/ai_network.cpp index 78b7a53f6e..16d8a7fa58 100644 --- a/sp/src/game/server/ai_network.cpp +++ b/sp/src/game/server/ai_network.cpp @@ -16,6 +16,9 @@ #include "ai_navigator.h" #include "world.h" #include "ai_moveprobe.h" +#ifdef MAPBASE_VSCRIPT +#include "ai_hint.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -31,6 +34,53 @@ extern float MOVE_HEIGHT_EPSILON; // later point we will probabaly have multiple AINetworkds per level CAI_Network* g_pBigAINet; +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAI_Network, SCRIPT_SINGLETON "The global list of AI nodes." ) + DEFINE_SCRIPTFUNC( NumNodes, "Number of nodes in the level" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodePosition, "GetNodePosition", "Get position of node using a generic human hull" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodePositionWithHull, "GetNodePositionWithHull", "Get position of node using the specified hull" ) + DEFINE_SCRIPTFUNC( GetNodeYaw, "Get yaw of node" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptNearestNodeToPoint, "NearestNodeToPoint", "Get ID of nearest node" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNearestNodeToPointWithNPC, "NearestNodeToPointForNPC", "Get ID of nearest node using the specified NPC's properties" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodeType, "GetNodeType", "Get a node's type" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNodeHint, "GetNodeHint", "Get a node's hint" ) +END_SCRIPTDESC(); + +HSCRIPT CAI_Network::ScriptGetNodeHint( int nodeID ) +{ + CAI_Node *pNode = GetNode( nodeID ); + if (!pNode) + return NULL; + + return ToHScript( pNode->GetHint() ); +} + +int CAI_Network::ScriptGetNodeType( int nodeID ) +{ + CAI_Node *pNode = GetNode( nodeID ); + if (!pNode) + return NULL; + + return (int)pNode->GetType(); +} + +int CAI_Network::ScriptNearestNodeToPointWithNPC( HSCRIPT hNPC, const Vector &vecPosition, bool bCheckVisibility ) +{ + CBaseEntity *pEnt = ToEnt( hNPC ); + if (!pEnt || !pEnt->MyNPCPointer()) + { + Warning("vscript: NearestNodeToPointWithNPC - Invalid NPC\n"); + return NO_NODE; + } + + return NearestNodeToPoint( pEnt->MyNPCPointer(), vecPosition, bCheckVisibility ); +} +#endif + //----------------------------------------------------------------------------- abstract_class INodeListFilter diff --git a/sp/src/game/server/ai_network.h b/sp/src/game/server/ai_network.h index 8cebd16a21..a86d89b889 100644 --- a/sp/src/game/server/ai_network.h +++ b/sp/src/game/server/ai_network.h @@ -127,6 +127,17 @@ class CAI_Network : public IPartitionEnumerator } CAI_Node** AccessNodes() const { return m_pAInode; } + +#ifdef MAPBASE_VSCRIPT + Vector ScriptGetNodePosition( int nodeID ) { return GetNodePosition( HULL_HUMAN, nodeID ); } + Vector ScriptGetNodePositionWithHull( int nodeID, int hull ) { return GetNodePosition( (Hull_t)hull, nodeID ); } + + int ScriptNearestNodeToPoint( const Vector &vecPosition, bool bCheckVisibility = true ) { return NearestNodeToPoint( NULL, vecPosition, bCheckVisibility ); } + int ScriptNearestNodeToPointWithNPC( HSCRIPT hNPC, const Vector &vecPosition, bool bCheckVisibility = true ); + + HSCRIPT ScriptGetNodeHint( int nodeID ); + int ScriptGetNodeType( int nodeID ); +#endif private: friend class CAI_NetworkManager; diff --git a/sp/src/game/server/ai_networkmanager.cpp b/sp/src/game/server/ai_networkmanager.cpp index d5bc187c7e..8f470a47fb 100644 --- a/sp/src/game/server/ai_networkmanager.cpp +++ b/sp/src/game/server/ai_networkmanager.cpp @@ -948,6 +948,13 @@ void CAI_NetworkManager::InitializeAINetworks() } } +#ifdef MAPBASE_VSCRIPT + if (g_pScriptVM) + { + g_pScriptVM->RegisterInstance( g_pBigAINet, "AINetwork" ); + } +#endif + // Reset node counter used during load CNodeEnt::m_nNodeCount = 0; diff --git a/sp/src/game/server/ai_speech.cpp b/sp/src/game/server/ai_speech.cpp index 6d47b961a4..c364b704d5 100644 --- a/sp/src/game/server/ai_speech.cpp +++ b/sp/src/game/server/ai_speech.cpp @@ -184,6 +184,20 @@ BEGIN_SIMPLE_DATADESC( CAI_Expresser ) DEFINE_FIELD( m_flLastTimeAcceptedSpeak, FIELD_TIME ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CAI_Expresser, "Expresser class for complex speech." ) + + DEFINE_SCRIPTFUNC( IsSpeaking, "Check if the actor is speaking." ) + DEFINE_SCRIPTFUNC( CanSpeak, "Check if the actor can speak." ) + DEFINE_SCRIPTFUNC( BlockSpeechUntil, "Block speech for a certain amount of time. This is stored in curtime." ) + DEFINE_SCRIPTFUNC( ForceNotSpeaking, "If the actor is speaking, force the system to recognize them as not speaking." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSpeakRawScene, "SpeakRawScene", "Speak a raw, instanced VCD scene as though it were played through the Response System. Return whether the scene successfully plays." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSpeakAutoGeneratedScene, "SpeakAutoGeneratedScene", "Speak an automatically generated, instanced VCD scene for this sound as though it were played through the Response System. Return whether the scene successfully plays." ) + +END_SCRIPTDESC(); +#endif + //------------------------------------- bool CAI_Expresser::SemaphoreIsAvailable( CBaseEntity *pTalker ) diff --git a/sp/src/game/server/ai_speech.h b/sp/src/game/server/ai_speech.h index 519df95535..2abb87f667 100644 --- a/sp/src/game/server/ai_speech.h +++ b/sp/src/game/server/ai_speech.h @@ -203,6 +203,12 @@ class CAI_Expresser : public IResponseFilter // Force the NPC to release the semaphore & clear next speech time void ForceNotSpeaking( void ); +#ifdef MAPBASE_VSCRIPT + bool ScriptSpeakRawScene( char const *soundname, float delay ) { return SpeakRawScene( soundname, delay, NULL ); } + bool ScriptSpeakAutoGeneratedScene( char const *soundname, float delay ) { return SpeakAutoGeneratedScene( soundname, delay ); } + bool ScriptSpeak( AIConcept_t concept, const char *modifiers ) { return Speak( concept, modifiers ); } +#endif + protected: CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc ); diff --git a/sp/src/game/server/baseanimating.cpp b/sp/src/game/server/baseanimating.cpp index 5fb0ff2fbf..2b89e5cc8f 100644 --- a/sp/src/game/server/baseanimating.cpp +++ b/sp/src/game/server/baseanimating.cpp @@ -282,6 +282,17 @@ IMPLEMENT_SERVERCLASS_ST(CBaseAnimating, DT_BaseAnimating) END_SEND_TABLE() +BEGIN_ENT_SCRIPTDESC( CBaseAnimating, CBaseEntity, "Animating models" ) + + DEFINE_SCRIPTFUNC( LookupAttachment, "Get the named attachement id" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentOrigin, "GetAttachmentOrigin", "Get the attachement id's origin vector" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentAngles, "GetAttachmentAngles", "Get the attachement id's angles as a p,y,r vector" ) +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttachmentMatrix, "GetAttachmentMatrix", "Get the attachement id's matrix transform" ) +#endif + DEFINE_SCRIPTFUNC( IsSequenceFinished, "Ask whether the main sequence is done playing" ) + DEFINE_SCRIPTFUNC( SetBodygroup, "Sets a bodygroup") +END_SCRIPTDESC(); CBaseAnimating::CBaseAnimating() { @@ -2117,6 +2128,45 @@ bool CBaseAnimating::GetAttachment( int iAttachment, Vector &absOrigin, Vector * return bRet; } +//----------------------------------------------------------------------------- +// Purpose: Returns the world location and world angles of an attachment to vscript caller +// Input : attachment name +// Output : location and angles +//----------------------------------------------------------------------------- +const Vector& CBaseAnimating::ScriptGetAttachmentOrigin( int iAttachment ) +{ + + static Vector absOrigin; + static QAngle qa; + + CBaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + + return absOrigin; +} + +const Vector& CBaseAnimating::ScriptGetAttachmentAngles( int iAttachment ) +{ + + static Vector absOrigin; + static Vector absAngles; + static QAngle qa; + + CBaseAnimating::GetAttachment( iAttachment, absOrigin, qa ); + absAngles.x = qa.x; + absAngles.y = qa.y; + absAngles.z = qa.z; + return absAngles; +} + +#ifdef MAPBASE_VSCRIPT +HSCRIPT CBaseAnimating::ScriptGetAttachmentMatrix( int iAttachment ) +{ + static matrix3x4_t matrix; + + CBaseAnimating::GetAttachment( iAttachment, matrix ); + return ScriptCreateMatrixInstance( matrix ); +} +#endif //----------------------------------------------------------------------------- // Returns the attachment in local space diff --git a/sp/src/game/server/baseanimating.h b/sp/src/game/server/baseanimating.h index 9ad963bc30..c89a1ebee8 100644 --- a/sp/src/game/server/baseanimating.h +++ b/sp/src/game/server/baseanimating.h @@ -44,6 +44,7 @@ class CBaseAnimating : public CBaseEntity DECLARE_DATADESC(); DECLARE_SERVERCLASS(); + DECLARE_ENT_SCRIPTDESC(); virtual void SetModel( const char *szModelName ); virtual void Activate(); @@ -186,6 +187,11 @@ class CBaseAnimating : public CBaseEntity bool GetAttachment( int iAttachment, Vector &absOrigin, QAngle &absAngles ); int GetAttachmentBone( int iAttachment ); virtual bool GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld ); + const Vector& ScriptGetAttachmentOrigin(int iAttachment); + const Vector& ScriptGetAttachmentAngles(int iAttachment); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetAttachmentMatrix(int iAttachment); +#endif // These return the attachment in the space of the entity bool GetAttachmentLocal( const char *szName, Vector &origin, QAngle &angles ); diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index d96b934573..c5fb9e567f 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -150,6 +150,44 @@ BEGIN_DATADESC( CBaseCombatCharacter ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by players and NPCs." ) + + DEFINE_SCRIPTFUNC_NAMED( GetScriptActiveWeapon, "GetActiveWeapon", "Get the character's active weapon entity." ) + DEFINE_SCRIPTFUNC( WeaponCount, "Get the number of weapons a character possesses." ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptWeaponIndex, "GetWeapon", "Get a specific weapon in the character's inventory." ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptWeaponByType, "FindWeapon", "Find a specific weapon in the character's inventory by its classname." ) + + DEFINE_SCRIPTFUNC_NAMED( Weapon_ShootPosition, "ShootPosition", "Get the character's shoot position." ) + DEFINE_SCRIPTFUNC_NAMED( Weapon_DropAll, "DropAllWeapons", "Make the character drop all of its weapons." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEquipWeapon, "EquipWeapon", "Make the character equip the specified weapon entity. If they don't already own the weapon, they will acquire it instantly." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAmmoCount, "GetAmmoCount", "Get the ammo count of the specified ammo type." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAmmoCount, "SetAmmoCount", "Set the ammo count of the specified ammo type." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptRelationType, "GetRelationship", "Get a character's relationship to a specific entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptRelationPriority, "GetRelationPriority", "Get a character's relationship priority for a specific entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetRelationship, "SetRelationship", "Set a character's relationship with a specific entity." ) + + DEFINE_SCRIPTFUNC_NAMED( GetScriptVehicleEntity, "GetVehicleEntity", "Get the entity for a character's current vehicle if they're in one." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptInViewCone, "InViewCone", "Check if the specified position is in the character's viewcone." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEntInViewCone, "EntInViewCone", "Check if the specified entity is in the character's viewcone." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptInAimCone, "InAimCone", "Check if the specified position is in the character's aim cone." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptEntInViewCone, "EntInAimCone", "Check if the specified entity is in the character's aim cone." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptBodyAngles, "BodyAngles", "Get the body's angles." ) + DEFINE_SCRIPTFUNC( BodyDirection2D, "Get the body's 2D direction." ) + DEFINE_SCRIPTFUNC( BodyDirection3D, "Get the body's 3D direction." ) + DEFINE_SCRIPTFUNC( HeadDirection2D, "Get the head's 2D direction." ) + DEFINE_SCRIPTFUNC( HeadDirection3D, "Get the head's 3D direction." ) + DEFINE_SCRIPTFUNC( EyeDirection2D, "Get the eyes' 2D direction." ) + DEFINE_SCRIPTFUNC( EyeDirection3D, "Get the eyes' 3D direction." ) + +END_SCRIPTDESC(); +#endif + BEGIN_SIMPLE_DATADESC( Relationship_t ) DEFINE_FIELD( entity, FIELD_EHANDLE ), @@ -4317,6 +4355,114 @@ void CBaseCombatCharacter::DoMuzzleFlash() } } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptActiveWeapon() +{ + return ToHScript( GetActiveWeapon() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptWeaponIndex( int i ) +{ + return ToHScript( GetWeapon( i ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptWeaponByType( const char *pszWeapon, int iSubType ) +{ + return ToHScript( Weapon_OwnsThisType( pszWeapon, iSubType ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptEquipWeapon( HSCRIPT hWeapon ) +{ + CBaseEntity *pEntity = ToEnt( hWeapon ); + CBaseCombatWeapon *pWeapon = pEntity->MyCombatWeaponPointer(); + if (!pEntity || !pWeapon) + return; + + if (pWeapon->GetOwner() == this) + { + // Switch to this weapon + Weapon_Switch( pWeapon ); + } + else + { + if (CBaseCombatWeapon *pExistingWeapon = Weapon_OwnsThisType( pWeapon->GetClassname() )) + { + // Drop our existing weapon then! + Weapon_Drop( pExistingWeapon ); + } + + if (IsNPC()) + { + Weapon_Equip( pWeapon ); + MyNPCPointer()->OnGivenWeapon( pWeapon ); + } + else + { + Weapon_Equip( pWeapon ); + } + + pWeapon->OnPickedUp( this ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::ScriptGetAmmoCount( const char *szName ) const +{ + return GetAmmoCount( GetAmmoDef()->Index(szName) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptSetAmmoCount( const char *szName, int iCount ) +{ + int iType = GetAmmoDef()->Index( szName ); + if (iType == -1) + { + Warning("\"%s\" is not a valid ammo type\n", szName); + return; + } + + return SetAmmoCount( iCount, iType ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::ScriptRelationType( HSCRIPT pTarget ) +{ + return (int)IRelationType( ToEnt( pTarget ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseCombatCharacter::ScriptRelationPriority( HSCRIPT pTarget ) +{ + return IRelationPriority( ToEnt( pTarget ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseCombatCharacter::ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority ) +{ + AddEntityRelationship( ToEnt( pTarget ), (Disposition_t)disposition, priority ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseCombatCharacter::GetScriptVehicleEntity() +{ + return ToHScript( GetVehicleEntity() ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: return true if given target cant be seen because of fog //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index a9a5c8152f..5d5c30df60 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -119,6 +119,9 @@ class CBaseCombatCharacter : public CBaseFlex DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: @@ -407,6 +410,31 @@ class CBaseCombatCharacter : public CBaseFlex virtual float GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ); virtual void DoMuzzleFlash(); +#ifdef MAPBASE_VSCRIPT // DO NOT COMMIT; WAIT UNTIL FULL MERGE (5/15/2020) + HSCRIPT GetScriptActiveWeapon(); + HSCRIPT GetScriptWeaponIndex( int i ); + HSCRIPT GetScriptWeaponByType( const char *pszWeapon, int iSubType = 0 ); + + void ScriptEquipWeapon( HSCRIPT hWeapon ); + + int ScriptGetAmmoCount( const char *szName ) const; + void ScriptSetAmmoCount( const char *szName, int iCount ); + + int ScriptRelationType( HSCRIPT pTarget ); + int ScriptRelationPriority( HSCRIPT pTarget ); + void ScriptSetRelationship( HSCRIPT pTarget, int disposition, int priority ); + + HSCRIPT GetScriptVehicleEntity(); + + bool ScriptInViewCone( const Vector &vecSpot ) { return FInViewCone( vecSpot ); } + bool ScriptEntInViewCone( HSCRIPT pEntity ) { return FInViewCone( ToEnt( pEntity ) ); } + + bool ScriptInAimCone( const Vector &vecSpot ) { return FInAimCone( vecSpot ); } + bool ScriptEntInAimCone( HSCRIPT pEntity ) { return FInAimCone( ToEnt( pEntity ) ); } + + const Vector& ScriptBodyAngles( void ) { static Vector vec; QAngle qa = BodyAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } +#endif + // Interactions static void InitInteractionSystem(); diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 6bc1b6ea1a..8eac20342a 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -101,6 +101,9 @@ bool CBaseEntity::s_bAbsQueriesValid = true; ConVar sv_netvisdist( "sv_netvisdist", "10000", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Test networking visibility distance" ); +ConVar sv_script_think_interval("sv_script_think_interval", "0.1"); + + // This table encodes edict data. void SendProxy_AnimTime( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ) { @@ -291,6 +294,8 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) SendPropEHandle (SENDINFO_NAME(m_hMoveParent, moveparent)), SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED), + SendPropStringT( SENDINFO( m_iName ) ), + SendPropInt (SENDINFO_NAME( m_MoveType, movetype ), MOVETYPE_MAX_BITS, SPROP_UNSIGNED ), SendPropInt (SENDINFO_NAME( m_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ), #if PREDICTION_ERROR_CHECK_LEVEL > 1 @@ -1313,6 +1318,19 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa if ( pszOutput == NULL ) return; + CBaseEntityOutput *pOutput = FindNamedOutput( pszOutput ); + if ( pOutput ) + { + pOutput->FireOutput( variant, pActivator, pCaller, flDelay ); + return; + } +} + +CBaseEntityOutput *CBaseEntity::FindNamedOutput( const char *pszOutput ) +{ + if ( pszOutput == NULL ) + return NULL; + datamap_t *dmap = GetDataDescMap(); while ( dmap ) { @@ -1325,14 +1343,13 @@ void CBaseEntity::FireNamedOutput( const char *pszOutput, variant_t variant, CBa CBaseEntityOutput *pOutput = ( CBaseEntityOutput * )( ( int )this + ( int )dataDesc->fieldOffset[0] ); if ( !Q_stricmp( dataDesc->externalName, pszOutput ) ) { - pOutput->FireOutput( variant, pActivator, pCaller, flDelay ); - return; + return pOutput; } } } - dmap = dmap->baseMap; } + return NULL; } void CBaseEntity::Activate( void ) @@ -1822,6 +1839,12 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FIELD( m_flSimulationTime, FIELD_TIME ), DEFINE_FIELD( m_nLastThinkTick, FIELD_TICK ), + DEFINE_FIELD(m_iszScriptId, FIELD_STRING), + // m_ScriptScope; + // m_hScriptInstance; + + DEFINE_KEYFIELD(m_iszVScripts, FIELD_STRING, "vscripts"), + DEFINE_KEYFIELD(m_iszScriptThinkFunction, FIELD_STRING, "thinkfunction"), DEFINE_KEYFIELD( m_nNextThinkTick, FIELD_TICK, "nextthink" ), DEFINE_KEYFIELD( m_fEffects, FIELD_INTEGER, "effects" ), DEFINE_KEYFIELD( m_clrRender, FIELD_COLOR32, "rendercolor" ), @@ -2004,6 +2027,14 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_INPUTFUNC( FIELD_STRING, "FireUser4", InputFireUser4 ), #endif + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptFile", InputRunScriptFile), + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptCode", InputRunScript), + DEFINE_INPUTFUNC(FIELD_STRING, "CallScriptFunction", InputCallScriptFunction), +#ifdef MAPBASE_VSCRIPT + DEFINE_INPUTFUNC(FIELD_STRING, "RunScriptCodeQuotable", InputRunScriptQuotable), + DEFINE_INPUTFUNC(FIELD_VOID, "ClearScriptScope", InputClearScriptScope), +#endif + #ifdef MAPBASE DEFINE_OUTPUT( m_OutUser1, "OutUser1" ), DEFINE_OUTPUT( m_OutUser2, "OutUser2" ), @@ -2081,6 +2112,7 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) DEFINE_FUNCTION( SUB_Vanish ), DEFINE_FUNCTION( SUB_CallUseToggle ), DEFINE_THINKFUNC( ShadowCastDistThink ), + DEFINE_THINKFUNC( ScriptThink ), #ifdef MAPBASE DEFINE_FUNCTION( SUB_RemoveWhenNotVisible ), @@ -2097,6 +2129,122 @@ BEGIN_DATADESC_NO_BASE( CBaseEntity ) END_DATADESC() + +BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" ) + DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) + DEFINE_SCRIPTFUNC_NAMED( ConnectOutputToScript, "ConnectOutput", "Adds an I/O connection that will call the named function when the specified output fires" ) + DEFINE_SCRIPTFUNC_NAMED( DisconnectOutputFromScript, "DisconnectOutput", "Removes a connected script function from an I/O event." ) + + DEFINE_SCRIPTFUNC( GetHealth, "" ) + DEFINE_SCRIPTFUNC( SetHealth, "" ) + DEFINE_SCRIPTFUNC( GetMaxHealth, "" ) + DEFINE_SCRIPTFUNC( SetMaxHealth, "" ) + + DEFINE_SCRIPTFUNC( SetModel, "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelName, "GetModelName", "Returns the name of the model" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptEmitSound, "EmitSound", "Plays a sound from this entity." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptPrecacheScriptSound, "PrecacheSoundScript", "Precache a sound for later playing." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSoundDuration, "GetSoundDuration", "Returns float duration of the sound. Takes soundname and optional actormodelname.") + + + DEFINE_SCRIPTFUNC( GetClassname, "" ) + DEFINE_SCRIPTFUNC_NAMED( GetEntityNameAsCStr, "GetName", "" ) + DEFINE_SCRIPTFUNC( GetPreTemplateName, "Get the entity name stripped of template unique decoration" ) + + DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) + DEFINE_SCRIPTFUNC( SetAbsOrigin, "SetAbsOrigin" ) + + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOrigin, "SetOrigin", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLeft, "GetLeftVector", "Get the left vector of the entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUp, "GetUpVector", "Get the up vector of the entity" ) + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) +#endif + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetForward, "SetForwardVector", "Set the orientation of the entity to have this forward vector" ) + DEFINE_SCRIPTFUNC_NAMED( GetAbsVelocity, "GetVelocity", "" ) + DEFINE_SCRIPTFUNC_NAMED( SetAbsVelocity, "SetVelocity", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetLocalAngularVelocity, "SetAngularVelocity", "Set the local angular velocity - takes float pitch,yaw,roll velocities" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLocalAngularVelocity, "GetAngularVelocity", "Get the local angular velocity - returns a vector of pitch,yaw,roll" ) + + + DEFINE_SCRIPTFUNC_NAMED( WorldSpaceCenter, "GetCenter", "Get vector to center of object - absolute coords") + DEFINE_SCRIPTFUNC_NAMED( ScriptEyePosition, "EyePosition", "Get vector to eye position - absolute coords") +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptEyeAngles, "EyeAngles", "Get eye pitch, yaw, roll as a vector" ) +#endif + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAngles, "SetAngles", "Set entity pitch, yaw, roll") + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAngles, "GetAngles", "Get entity pitch, yaw, roll as a vector") + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetSize, "SetSize", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMins, "GetBoundingMins", "Get a vector containing min bounds, centered on object") + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBoundingMaxs, "GetBoundingMaxs", "Get a vector containing max bounds, centered on object") + + DEFINE_SCRIPTFUNC_NAMED( ScriptUtilRemove, "Destroy", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetOwner, "SetOwner", "" ) + DEFINE_SCRIPTFUNC_NAMED( GetTeamNumber, "GetTeam", "" ) + DEFINE_SCRIPTFUNC_NAMED( ChangeTeam, "SetTeam", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMoveParent, "GetMoveParent", "If in hierarchy, retrieves the entity's parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetRootMoveParent, "GetRootMoveParent", "If in hierarchy, walks up the hierarchy to find the root parent" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFirstMoveChild, "FirstMoveChild", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptNextMovePeer, "NextMovePeer", "" ) + + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromString, "__KeyValueFromString", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromFloat, "__KeyValueFromFloat", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromInt, "__KeyValueFromInt", SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC_NAMED( KeyValueFromVector, "__KeyValueFromVector", SCRIPT_HIDE ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetModelKeyValues, "GetModelKeyValues", "Get a KeyValue class instance on this entity's model") + +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptIsVisible, "IsVisible", "Check if the specified position can be visible to this entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsEntVisible, "IsEntVisible", "Check if the specified entity can be visible to this entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsVisibleWithMask, "IsVisibleWithMask", "Check if the specified position can be visible to this entity with a specific trace mask." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptTakeDamage, "TakeDamage", "Apply damage to this entity with a given info handle" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFireBullets, "FireBullets", "Fire bullets from entity with a given info handle" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetContext, "GetContext", "Get a response context value" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddContext, "AddContext", "Add a response context value" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptClassify, "Classify", "Get Class_T class ID" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValue, "GetKeyValue", "Get a keyvalue" ) + + DEFINE_SCRIPTFUNC( GetSpawnFlags, "Get spawnflags" ) + DEFINE_SCRIPTFUNC( AddSpawnFlags, "Add spawnflag(s)" ) + DEFINE_SCRIPTFUNC( RemoveSpawnFlags, "Remove spawnflag(s)" ) + DEFINE_SCRIPTFUNC( ClearSpawnFlags, "Clear spawnflag(s)" ) + DEFINE_SCRIPTFUNC( HasSpawnFlags, "Check if the entity has specific spawnflag(s) ticked" ) + + DEFINE_SCRIPTFUNC( GetEffects, "Get effects" ) + DEFINE_SCRIPTFUNC( AddEffects, "Add effect(s)" ) + DEFINE_SCRIPTFUNC( RemoveEffects, "Remove effect(s)" ) + DEFINE_SCRIPTFUNC( ClearEffects, "Clear effect(s)" ) + DEFINE_SCRIPTFUNC( SetEffects, "Set effect(s)" ) + DEFINE_SCRIPTFUNC( IsEffectActive, "Check if an effect is active" ) + + DEFINE_SCRIPTFUNC( IsPlayer, "Returns true if this entity is a player." ) + DEFINE_SCRIPTFUNC( IsNPC, "Returns true if this entity is a NPC." ) + DEFINE_SCRIPTFUNC( IsCombatCharacter, "Returns true if this entity is a combat character (player or NPC)." ) + DEFINE_SCRIPTFUNC_NAMED( IsBaseCombatWeapon, "IsWeapon", "Returns true if this entity is a weapon." ) +#endif + + DEFINE_SCRIPTFUNC( ValidateScriptScope, "Ensure that an entity's script scope has been created" ) + DEFINE_SCRIPTFUNC( GetScriptScope, "Retrieve the script-side data associated with an entity" ) + DEFINE_SCRIPTFUNC( GetScriptId, "Retrieve the unique identifier used to refer to the entity within the scripting system" ) + DEFINE_SCRIPTFUNC_NAMED( GetScriptOwnerEntity, "GetOwner", "Gets this entity's owner" ) + DEFINE_SCRIPTFUNC_NAMED( SetScriptOwnerEntity, "SetOwner", "Sets this entity's owner" ) + DEFINE_SCRIPTFUNC( entindex, "" ) +END_SCRIPTDESC(); + + // For code error checking extern bool g_bReceivedChainedUpdateOnRemove; @@ -2196,6 +2344,12 @@ void CBaseEntity::UpdateOnRemove( void ) modelinfo->ReleaseDynamicModel( m_nModelIndex ); // no-op if not dynamic m_nModelIndex = -1; } + + if ( m_hScriptInstance ) + { + g_pScriptVM->RemoveInstance( m_hScriptInstance ); + m_hScriptInstance = NULL; + } } //----------------------------------------------------------------------------- @@ -3925,9 +4079,9 @@ const char *CBaseEntity::GetDebugName(void) if ( this == NULL ) return "<>"; - if ( m_iName != NULL_STRING ) + if ( m_iName.Get() != NULL_STRING ) { - return STRING(m_iName); + return STRING(m_iName.Get()); } else { @@ -4108,7 +4262,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, // mapper debug message if (pCaller != NULL) { - Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, STRING(pCaller->m_iName), GetDebugName(), szInputName, Value.String() ); + Q_snprintf( szBuffer, sizeof(szBuffer), "(%0.2f) input %s: %s.%s(%s)\n", gpGlobals->curtime, STRING(pCaller->m_iName.Get()), GetDebugName(), szInputName, Value.String() ); } else { @@ -4155,7 +4309,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, Warning( "!! ERROR: bad input/output link:\n!! Unable to convert value \"%s\" from %s (%s) to field type %i\n!! Target Entity: %s (%s), Input: %s\n", Value.GetDebug(), ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "", - ( pCaller != NULL ) ? STRING(pCaller->m_iName) : "", + ( pCaller != NULL ) ? STRING(pCaller->m_iName.Get()) : "", dmap->dataDesc[i].fieldType, STRING(m_iClassname), GetDebugName(), szInputName ); return false; @@ -4168,7 +4322,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, Warning( "!! ERROR: bad input/output link:\n!! %s(%s,%s) doesn't match type from %s(%s)\n", STRING(m_iClassname), GetDebugName(), szInputName, ( pCaller != NULL ) ? STRING(pCaller->m_iClassname) : "", - ( pCaller != NULL ) ? STRING(pCaller->m_iName) : "" ); + ( pCaller != NULL ) ? STRING(pCaller->m_iName.Get()) : "" ); return false; } #endif @@ -4187,7 +4341,44 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, data.value = Value; data.nOutputID = outputID; - (this->*pfnInput)( data ); + + // Now, see if there's a function named Input in this entity's script file. + // If so, execute it and let it decide whether to allow the default behavior to also execute. + bool bCallInputFunc = true; // Always assume default behavior (do call the input function) + ScriptVariant_t functionReturn; + + if ( m_ScriptScope.IsInitialized() ) + { + char szScriptFunctionName[255]; + Q_strcpy( szScriptFunctionName, "Input" ); + Q_strcat( szScriptFunctionName, szInputName, 255 ); + + g_pScriptVM->SetValue( "activator", ( pActivator ) ? ScriptVariant_t( pActivator->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + g_pScriptVM->SetValue( "caller", ( pCaller ) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); +#ifdef MAPBASE_VSCRIPT + Value.SetScriptVariant( functionReturn ); + g_pScriptVM->SetValue( "parameter", functionReturn ); +#endif + + if( CallScriptFunction( szScriptFunctionName, &functionReturn ) ) + { + bCallInputFunc = functionReturn.m_bool; + } + } + + if( bCallInputFunc ) + { + (this->*pfnInput)( data ); + } + + if ( m_ScriptScope.IsInitialized() ) + { + g_pScriptVM->ClearValue( "activator" ); + g_pScriptVM->ClearValue( "caller" ); +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->ClearValue( "parameter" ); +#endif + } } else if ( dmap->dataDesc[i].flags & FTYPEDESC_KEY ) { @@ -4210,7 +4401,7 @@ bool CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, } } - DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName)*/ ); + DevMsg( 2, "unhandled input: (%s) -> (%s,%s)\n", szInputName, STRING(m_iClassname), GetDebugName()/*,", from (%s,%s)" STRING(pCaller->m_iClassname), STRING(pCaller->m_iName.Get())*/ ); return false; } @@ -5300,6 +5491,36 @@ void CC_Ent_Name( const CCommand& args ) } static ConCommand ent_name("ent_name", CC_Ent_Name, 0, FCVAR_CHEAT); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void DumpScriptScope(CBasePlayer* pPlayer, const char* name) +{ + CBaseEntity* pEntity = NULL; + while ((pEntity = GetNextCommandEntity(pPlayer, name, pEntity)) != NULL) + { + if (pEntity->m_ScriptScope.IsInitialized()) + { + Msg("----Script Dump for entity %s\n", pEntity->GetDebugName()); + HSCRIPT hDumpScopeFunc = g_pScriptVM->LookupFunction("__DumpScope"); + g_pScriptVM->Call(hDumpScopeFunc, NULL, true, NULL, 1, (HSCRIPT)pEntity->m_ScriptScope); + Msg("----End Script Dump\n"); + } + else + { + DevWarning("ent_script_dump: Entity %s has no script scope!\n", pEntity->GetDebugName()); + } + } +} + +//------------------------------------------------------------------------------ +void CC_Ent_Script_Dump( const CCommand& args ) +{ + DumpScriptScope(UTIL_GetCommandClient(),args[1]); +} +static ConCommand ent_script_dump("ent_script_dump", CC_Ent_Script_Dump, "Dumps the names and values of this entity's script scope to the console\n\tArguments: {entity_name} / {class_name} / no argument picks what player is looking at ", FCVAR_CHEAT); + + //------------------------------------------------------------------------------ #ifdef MAPBASE class CEntTextAutoCompletionFunctor : public ICommandCallback, public ICommandCompletionCallback @@ -6978,6 +7199,7 @@ const char *CBaseEntity::GetContextValue( int index ) const } return m_ResponseContexts[ index ].m_iszValue.ToCStr(); + } //----------------------------------------------------------------------------- @@ -7223,7 +7445,6 @@ void CBaseEntity::InputPassRandomUser( inputdata_t& inputdata ) } #endif - #ifdef MAPBASE //----------------------------------------------------------------------------- // Purpose: Sets the entity's targetname. @@ -7820,6 +8041,282 @@ void CBaseEntity::InputSetThinkNull( inputdata_t& inputdata ) #endif +//--------------------------------------------------------- +// Use the string as the filename of a script file +// that should be loaded from disk, compiled, and run. +//--------------------------------------------------------- +void CBaseEntity::InputRunScriptFile(inputdata_t& inputdata) +{ + RunScriptFile(inputdata.value.String()); +} + +//--------------------------------------------------------- +// Send the string to the VM as source code and execute it +//--------------------------------------------------------- +void CBaseEntity::InputRunScript(inputdata_t& inputdata) +{ + RunScript(inputdata.value.String(), "InputRunScript"); +} + +//--------------------------------------------------------- +// Make an explicit function call. +//--------------------------------------------------------- +void CBaseEntity::InputCallScriptFunction(inputdata_t& inputdata) +{ + CallScriptFunction(inputdata.value.String(), NULL); +} + +#ifdef MAPBASE_VSCRIPT +//--------------------------------------------------------- +// Send the string to the VM as source code and execute it +//--------------------------------------------------------- +void CBaseEntity::InputRunScriptQuotable(inputdata_t& inputdata) +{ + char szQuotableCode[1024]; + if (V_StrSubst( inputdata.value.String(), "''", "\"", szQuotableCode, sizeof( szQuotableCode ), false )) + { + RunScript( szQuotableCode, "InputRunScriptQuotable" ); + } + else + { + RunScript( inputdata.value.String(), "InputRunScriptQuotable" ); + } +} + +//--------------------------------------------------------- +// Clear this entity's script scope +//--------------------------------------------------------- +void CBaseEntity::InputClearScriptScope(inputdata_t& inputdata) +{ + m_ScriptScope.Term(); +} +#endif + +// #define VMPROFILE // define to profile vscript calls + +#ifdef VMPROFILE +float g_debugCumulativeTime = 0.0; +float g_debugCounter = 0; + +#define START_VMPROFILE float debugStartTime = Plat_FloatTime(); +#define UPDATE_VMPROFILE \ + g_debugCumulativeTime += Plat_FloatTime() - debugStartTime; \ + g_debugCounter++; \ + if ( g_debugCounter >= 500 ) \ + { \ + DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", "500 vscript function calls", "", g_debugCumulativeTime*1000.0 ); \ + g_debugCounter = 0; \ + g_debugCumulativeTime = 0.0; \ + } \ + +#else + +#define START_VMPROFILE +#define UPDATE_VMPROFILE + +#endif // VMPROFILE + +//----------------------------------------------------------------------------- +// Returns true if the function was located and called. false otherwise. +// NOTE: Assumes the function takes no parameters at the moment. +//----------------------------------------------------------------------------- +bool CBaseEntity::CallScriptFunction(const char* pFunctionName, ScriptVariant_t* pFunctionReturn) +{ + START_VMPROFILE + + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + + HSCRIPT hFunc = m_ScriptScope.LookupFunction(pFunctionName); + + if (hFunc) + { + m_ScriptScope.Call(hFunc, pFunctionReturn); + m_ScriptScope.ReleaseFunction(hFunc); + + UPDATE_VMPROFILE + + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ConnectOutputToScript(const char* pszOutput, const char* pszScriptFunc) +{ + CBaseEntityOutput* pOutput = FindNamedOutput(pszOutput); + if (!pOutput) + { + DevMsg(2, "Script failed to find output \"%s\"\n", pszOutput); + return; + } + + string_t iszSelf = AllocPooledString("!self"); // @TODO: cache this [4/25/2008 tom] + CEventAction* pAction = pOutput->GetActionList(); + while (pAction) + { + if (pAction->m_iTarget == iszSelf && + pAction->m_flDelay == 0 && + pAction->m_nTimesToFire == EVENT_FIRE_ALWAYS && + V_strcmp(STRING(pAction->m_iTargetInput), "CallScriptFunction") == 0 && + V_strcmp(STRING(pAction->m_iParameter), pszScriptFunc) == 0) + { + return; + } + pAction = pAction->m_pNext; + } + + pAction = new CEventAction(NULL); + pAction->m_iTarget = iszSelf; + pAction->m_iTargetInput = AllocPooledString("CallScriptFunction"); + pAction->m_iParameter = AllocPooledString(pszScriptFunc); + pOutput->AddEventAction(pAction); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::DisconnectOutputFromScript(const char* pszOutput, const char* pszScriptFunc) +{ + CBaseEntityOutput* pOutput = FindNamedOutput(pszOutput); + if (!pOutput) + { + DevMsg(2, "Script failed to find output \"%s\"\n", pszOutput); + return; + } + + string_t iszSelf = AllocPooledString("!self"); // @TODO: cache this [4/25/2008 tom] + CEventAction* pAction = pOutput->GetActionList(); + while (pAction) + { + if (pAction->m_iTarget == iszSelf && + pAction->m_flDelay == 0 && + pAction->m_nTimesToFire == EVENT_FIRE_ALWAYS && + V_strcmp(STRING(pAction->m_iTargetInput), "CallScriptFunction") == 0 && + V_strcmp(STRING(pAction->m_iParameter), pszScriptFunc) == 0) + { + pOutput->RemoveEventAction(pAction); + delete pAction; + return; + } + pAction = pAction->m_pNext; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptThink(void) +{ + ScriptVariant_t varThinkRetVal; + if (CallScriptFunction(m_iszScriptThinkFunction.ToCStr(), &varThinkRetVal)) + { + float flThinkFrequency = 0.0f; + if (!varThinkRetVal.AssignTo(&flThinkFrequency)) + { + // use default think interval if script think function doesn't provide one + flThinkFrequency = sv_script_think_interval.GetFloat(); + } + SetContextThink(&CBaseEntity::ScriptThink, + gpGlobals->curtime + flThinkFrequency, "ScriptThink"); + } + else + { + DevWarning("%s FAILED to call script think function %s!\n", GetDebugName(), STRING(m_iszScriptThinkFunction)); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char* CBaseEntity::GetScriptId() +{ + return STRING(m_iszScriptThinkFunction); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::GetScriptScope() +{ + return m_ScriptScope; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetMoveParent(void) +{ + return ToHScript(GetMoveParent()); +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetRootMoveParent() +{ + return ToHScript(GetRootMoveParent()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptFirstMoveChild(void) +{ + return ToHScript(FirstMoveChild()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptNextMovePeer(void) +{ + return ToHScript(NextMovePeer()); +} + +//----------------------------------------------------------------------------- +// Purpose: Load, compile, and run a script file from disk. +// Input : *pScriptFile - The filename of the script file. +// bUseRootScope - If true, runs this script in the root scope, not +// in this entity's private scope. +//----------------------------------------------------------------------------- +bool CBaseEntity::RunScriptFile(const char* pScriptFile, bool bUseRootScope) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (bUseRootScope) + { + return VScriptRunScript(pScriptFile); + } + else + { + return VScriptRunScript(pScriptFile, m_ScriptScope, true); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Compile and execute a discrete string of script source code +// Input : *pScriptText - A string containing script code to compile and run +//----------------------------------------------------------------------------- +bool CBaseEntity::RunScript(const char* pScriptText, const char* pDebugFilename) +{ + if (!ValidateScriptScope()) + { + DevMsg("\n***\nFAILED to create private ScriptScope. ABORTING script\n***\n"); + return false; + } + + if (m_ScriptScope.Run(pScriptText, pDebugFilename) == SCRIPT_ERROR) + { + DevWarning(" Entity %s encountered an error in RunScript()\n", GetDebugName()); + } + + return true; +} + //----------------------------------------------------------------------------- // Purpose: // Input : *contextName - @@ -8796,6 +9293,329 @@ void CBaseEntity::SetCollisionBoundsFromModel() } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::GetScriptInstance() +{ + if (!m_hScriptInstance) + { + if (m_iszScriptId == NULL_STRING) + { + char* szName = (char*)stackalloc(1024); + g_pScriptVM->GenerateUniqueKey((m_iName.Get() != NULL_STRING) ? STRING(GetEntityName()) : GetClassname(), szName, 1024); + m_iszScriptId = AllocPooledString(szName); + } + + m_hScriptInstance = g_pScriptVM->RegisterInstance(GetScriptDesc(), this); + g_pScriptVM->SetInstanceUniqeId(m_hScriptInstance, STRING(m_iszScriptId)); + } + return m_hScriptInstance; +} + +//----------------------------------------------------------------------------- +// Using my edict, cook up a unique VScript scope that's private to me, and +// persistent. +//----------------------------------------------------------------------------- +bool CBaseEntity::ValidateScriptScope() +{ + if (!m_ScriptScope.IsInitialized()) + { + if (scriptmanager == NULL) + { + ExecuteOnce(DevMsg("Cannot execute script because scripting is disabled (-scripting)\n")); + return false; + } + + if (g_pScriptVM == NULL) + { + ExecuteOnce(DevMsg(" Cannot execute script because there is no available VM\n")); + return false; + } + + // Force instance creation + GetScriptInstance(); + + EHANDLE hThis; + hThis.Set(this); + + bool bResult = m_ScriptScope.Init(STRING(m_iszScriptId)); + + if (!bResult) + { + DevMsg("%s couldn't create ScriptScope!\n", GetDebugName()); + return false; + } + g_pScriptVM->SetValue(m_ScriptScope, "self", GetScriptInstance()); + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Run all of the vscript files that are set in this entity's VSCRIPTS +// field in Hammer. The list is space-delimited. +//----------------------------------------------------------------------------- +void CBaseEntity::RunVScripts() +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + + ValidateScriptScope(); + + // All functions we want to have call chained instead of overwritten + // by other scripts in this entities list. + static const char* sCallChainFunctions[] = + { + "OnPostSpawn", + "Precache" + }; + + ScriptLanguage_t language = g_pScriptVM->GetLanguage(); + + // Make a call chainer for each in this entities scope + for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j) + { + + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter + HSCRIPT hCreateChainScript = g_pScriptVM->CompileScript(CFmtStr("%sCallChain <- CSimpleCallChainer(\"%s\", self.GetScriptScope(), true)", sCallChainFunctions[j], sCallChainFunctions[j])); + g_pScriptVM->Run(hCreateChainScript, (HSCRIPT)m_ScriptScope); + } + } + + char szScriptsList[255]; + Q_strcpy(szScriptsList, STRING(m_iszVScripts)); + CUtlStringList szScripts; + + V_SplitString(szScriptsList, " ", szScripts); + + for (int i = 0; i < szScripts.Count(); i++) + { + Log( "%s executing script: %s\n", GetDebugName(), szScripts[i]); + + RunScriptFile(szScripts[i], IsWorld()); + + for (int j = 0; j < ARRAYSIZE(sCallChainFunctions); ++j) + { + if (language == SL_PYTHON) + { + // UNDONE - handle call chaining in python + ; + } + else if (language == SL_SQUIRREL) + { + //TODO: For perf, this should be precompiled and the %s should be passed as a parameter. + HSCRIPT hRunPostScriptExecute = g_pScriptVM->CompileScript(CFmtStr("%sCallChain.PostScriptExecute()", sCallChainFunctions[j])); + g_pScriptVM->Run(hRunPostScriptExecute, (HSCRIPT)m_ScriptScope); + } + } + } + + if (m_iszScriptThinkFunction != NULL_STRING) + { + SetContextThink(&CBaseEntity::ScriptThink, gpGlobals->curtime + sv_script_think_interval.GetFloat(), "ScriptThink"); + } +} + + +//-------------------------------------------------------------------------------------------------- +// This is called during entity spawning and after restore to allow scripts to precache any +// resources they need. +//-------------------------------------------------------------------------------------------------- +void CBaseEntity::RunPrecacheScripts(void) +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + + HSCRIPT hScriptPrecache = m_ScriptScope.LookupFunction("DispatchPrecache"); + if (hScriptPrecache) + { + g_pScriptVM->Call(hScriptPrecache, m_ScriptScope); + m_ScriptScope.ReleaseFunction(hScriptPrecache); + } +} + +void CBaseEntity::RunOnPostSpawnScripts(void) +{ + if (m_iszVScripts == NULL_STRING) + { + return; + } + + HSCRIPT hFuncConnect = g_pScriptVM->LookupFunction("ConnectOutputs"); + if (hFuncConnect) + { + g_pScriptVM->Call(hFuncConnect, NULL, true, NULL, (HSCRIPT)m_ScriptScope); + g_pScriptVM->ReleaseFunction(hFuncConnect); + } + + HSCRIPT hFuncDisp = m_ScriptScope.LookupFunction("DispatchOnPostSpawn"); + if (hFuncDisp) + { + variant_t variant; + variant.SetString(MAKE_STRING("DispatchOnPostSpawn")); + g_EventQueue.AddEvent(this, "CallScriptFunction", variant, 0, this, this); + m_ScriptScope.ReleaseFunction(hFuncDisp); + + } +} + +HSCRIPT CBaseEntity::GetScriptOwnerEntity() +{ + return ToHScript(GetOwnerEntity()); +} + +void CBaseEntity::SetScriptOwnerEntity(HSCRIPT pOwner) +{ + SetOwnerEntity(ToEnt(pOwner)); +} + +//----------------------------------------------------------------------------- +// VScript access to model's key values +// for iteration and value access, use: +// ScriptFindKey, ScriptGetFirstSubKey, ScriptGetString, +// ScriptGetInt, ScriptGetFloat, ScriptGetNextKey +//----------------------------------------------------------------------------- +HSCRIPT CBaseEntity::ScriptGetModelKeyValues( void ) +{ + KeyValues *pModelKeyValues = new KeyValues(""); + HSCRIPT hScript = NULL; + const char *pszModelName = modelinfo->GetModelName( GetModel() ); + const char *pBuffer = modelinfo->GetModelKeyValueText( GetModel() ) ; + + if ( pModelKeyValues->LoadFromBuffer( pszModelName, pBuffer ) ) + { + // UNDONE: how does destructor get called on this + m_pScriptModelKeyValues = new CScriptKeyValues( pModelKeyValues ); + + // UNDONE: who calls ReleaseInstance on this??? Does name need to be unique??? + + hScript = g_pScriptVM->RegisterInstance( m_pScriptModelKeyValues ); + + /* + KeyValues *pParticleEffects = pModelKeyValues->FindKey("Particles"); + if ( pParticleEffects ) + { + // Start grabbing the sounds and slotting them in + for ( KeyValues *pSingleEffect = pParticleEffects->GetFirstSubKey(); pSingleEffect; pSingleEffect = pSingleEffect->GetNextKey() ) + { + const char *pParticleEffectName = pSingleEffect->GetString( "name", "" ); + PrecacheParticleSystem( pParticleEffectName ); + } + } + */ + } + + return hScript; +} + +void CBaseEntity::ScriptSetLocalAngularVelocity(float pitchVel, float yawVel, float rollVel) +{ + QAngle qa; + qa.Init(pitchVel, yawVel, rollVel); + SetLocalAngularVelocity(qa); +} + +const Vector& CBaseEntity::ScriptGetLocalAngularVelocity(void) +{ + QAngle qa = GetLocalAngularVelocity(); + static Vector v; + v.x = qa.x; + v.y = qa.y; + v.z = qa.z; + return v; +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the min collision bounds, centered on object +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetBoundingMins(void) +{ + return m_Collision.OBBMins(); +} + +//----------------------------------------------------------------------------- +// Vscript: Gets the max collision bounds, centered on object +//----------------------------------------------------------------------------- +const Vector& CBaseEntity::ScriptGetBoundingMaxs(void) +{ + return m_Collision.OBBMaxs(); +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseEntity::ScriptTakeDamage( HSCRIPT pInfo ) +{ + if (pInfo) + { + CTakeDamageInfo *info = HScriptToClass( pInfo ); //ToDamageInfo( pInfo ); + if (info) + { + return OnTakeDamage( *info ); + } + } + + return 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptFireBullets( HSCRIPT pInfo ) +{ + if (pInfo) + { + extern FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ); + FireBulletsInfo_t *info = GetFireBulletsInfoFromInfo( pInfo ); + if (info) + { + FireBullets( *info ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptAddContext( const char *name, const char *value, float duration ) +{ + AddContext( name, value, duration ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CBaseEntity::ScriptGetContext( const char *name ) +{ + return GetContextValue( name ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CBaseEntity::ScriptClassify( void ) +{ + return (int)Classify(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CBaseEntity::ScriptGetKeyValue( const char *pszKeyName ) +{ + static char szValue[128]; + GetKeyValue( pszKeyName, szValue, sizeof(szValue) ); + return szValue; +} +#endif + + #ifdef MAPBASE extern int EntityFactory_AutoComplete( const char *cmdname, CUtlVector< CUtlString > &commands, CUtlRBTree< CUtlString > &symbols, char *substring, int checklen = 0 ); diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 8bccccbccd..c8e571c631 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -21,6 +21,9 @@ #include "shareddefs.h" #include "engine/ivmodelinfo.h" +#include "vscript/ivscript.h" +#include "vscript_server.h" + class CDamageModifier; class CDmgAccumulator; @@ -311,11 +314,13 @@ a list of all CBaseEntitys is kept in gEntList CBaseEntity *CreateEntityByName( const char *className, int iForceEdictIndex = -1 ); CBaseNetworkable *CreateNetworkableByName( const char *className ); +CBaseEntity* ToEnt(HSCRIPT hScript); + // creates an entity and calls all the necessary spawn functions extern void SpawnEntityByName( const char *className, CEntityMapData *mapData = NULL ); // calls the spawn functions for an entity -extern int DispatchSpawn( CBaseEntity *pEntity ); +extern int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts = true); inline CBaseEntity *GetContainingEntity( edict_t *pent ); @@ -379,6 +384,8 @@ class CBaseEntity : public IServerEntity DECLARE_SERVERCLASS(); // data description DECLARE_DATADESC(); + // script description + DECLARE_ENT_SCRIPTDESC(); // memory handling void *operator new( size_t stAllocateBlock ); @@ -493,6 +500,8 @@ class CBaseEntity : public IServerEntity virtual void SetOwnerEntity( CBaseEntity* pOwner ); void SetEffectEntity( CBaseEntity *pEffectEnt ); CBaseEntity *GetEffectEntity() const; + HSCRIPT GetScriptOwnerEntity(); + virtual void SetScriptOwnerEntity(HSCRIPT pOwner); // Only CBaseEntity implements these. CheckTransmit calls the virtual ShouldTransmit to see if the // entity wants to be sent. If so, it calls SetTransmit, which will mark any dependents for transmission too. @@ -563,9 +572,14 @@ class CBaseEntity : public IServerEntity virtual bool KeyValue( const char *szKeyName, float flValue ); virtual bool KeyValue( const char *szKeyName, const Vector &vecValue ); virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); + bool KeyValueFromString( const char *szKeyName, const char *szValue ) { return KeyValue( szKeyName, szValue ); } + bool KeyValueFromFloat( const char *szKeyName, float flValue ) { return KeyValue( szKeyName, flValue ); } + bool KeyValueFromInt( const char *szKeyName, int nValue ) { return KeyValue( szKeyName, nValue ); } + bool KeyValueFromVector( const char *szKeyName, const Vector &vecValue ) { return KeyValue( szKeyName, vecValue ); } void ValidateEntityConnections(); void FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay = 0.0f ); + CBaseEntityOutput *FindNamedOutput( const char *pszOutput ); // Activate - called for each entity after each load game and level load virtual void Activate( void ); @@ -588,6 +602,8 @@ class CBaseEntity : public IServerEntity int GetParentAttachment(); string_t GetEntityName(); + const char* GetEntityNameAsCStr(); // This method is temporary for VSCRIPT functionality until we figure out what to do with string_t (sjb) + const char* GetPreTemplateName(); // Not threadsafe. Get the name stripped of template unique decoration bool NameMatches( const char *pszNameOrWildcard ); bool ClassMatches( const char *pszClassOrWildcard ); @@ -735,6 +751,18 @@ class CBaseEntity : public IServerEntity COutputEvent m_OnKilled; #endif + void InputRunScript(inputdata_t& inputdata); + void InputRunScriptFile(inputdata_t& inputdata); + void InputCallScriptFunction(inputdata_t& inputdata); +#ifdef MAPBASE_VSCRIPT + void InputRunScriptQuotable(inputdata_t& inputdata); + void InputClearScriptScope(inputdata_t& inputdata); +#endif + + bool RunScriptFile(const char* pScriptFile, bool bUseRootScope = false); + bool RunScript(const char* pScriptText, const char* pDebugFilename = "CBaseEntity::RunScript"); + + // Returns the origin at which to play an inputted dispatcheffect virtual void GetInputDispatchEffectPosition( const char *sInputString, Vector &pOrigin, QAngle &pAngles ); @@ -1715,7 +1743,7 @@ class CBaseEntity : public IServerEntity // was pev->flags CNetworkVarForDerived( int, m_fFlags ); - string_t m_iName; // name used to identify this entity + CNetworkVar( string_t, m_iName ); // name used to identify this entity // Damage modifiers friend class CDamageModifier; @@ -1920,6 +1948,85 @@ class CBaseEntity : public IServerEntity { return s_bAbsQueriesValid; } + + + // VSCRIPT + HSCRIPT GetScriptInstance(); + bool ValidateScriptScope(); + virtual void RunVScripts(); + bool CallScriptFunction(const char* pFunctionName, ScriptVariant_t* pFunctionReturn); + void ConnectOutputToScript(const char* pszOutput, const char* pszScriptFunc); + void DisconnectOutputFromScript(const char* pszOutput, const char* pszScriptFunc); + void ScriptThink(); + const char* GetScriptId(); + HSCRIPT GetScriptScope(); + void RunPrecacheScripts(void); + void RunOnPostSpawnScripts(void); + + HSCRIPT ScriptGetMoveParent(void); + HSCRIPT ScriptGetRootMoveParent(); + HSCRIPT ScriptFirstMoveChild(void); + HSCRIPT ScriptNextMovePeer(void); + + const Vector& ScriptEyePosition(void) { static Vector vec; vec = EyePosition(); return vec; } +#ifdef MAPBASE_VSCRIPT + const QAngle& ScriptEyeAngles(void) { static QAngle ang; ang = EyeAngles(); return ang; } + void ScriptSetAngles(const QAngle angles) { Teleport(NULL, &angles, NULL); } + const QAngle& ScriptGetAngles(void) { return GetAbsAngles(); } +#else + void ScriptSetAngles(float fPitch, float fYaw, float fRoll) { QAngle angles(fPitch, fYaw, fRoll); Teleport(NULL, &angles, NULL); } + const Vector& ScriptGetAngles(void) { static Vector vec; QAngle qa = GetAbsAngles(); vec.x = qa.x; vec.y = qa.y; vec.z = qa.z; return vec; } +#endif + + void ScriptSetSize(const Vector& mins, const Vector& maxs) { UTIL_SetSize(this, mins, maxs); } + void ScriptUtilRemove(void) { UTIL_Remove(this); } + void ScriptSetOwner(HSCRIPT hEntity) { SetOwnerEntity(ToEnt(hEntity)); } + void ScriptSetOrigin(const Vector& v) { Teleport(&v, NULL, NULL); } + void ScriptSetForward(const Vector& v) { QAngle angles; VectorAngles(v, angles); Teleport(NULL, &angles, NULL); } + const Vector& ScriptGetForward(void) { static Vector vecForward; GetVectors(&vecForward, NULL, NULL); return vecForward; } + const Vector& ScriptGetLeft(void) { static Vector vecLeft; GetVectors(NULL, &vecLeft, NULL); return vecLeft; } + const Vector& ScriptGetUp(void) { static Vector vecUp; GetVectors(NULL, NULL, &vecUp); return vecUp; } + +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptEntityToWorldTransform(void) { return ScriptCreateMatrixInstance( EntityToWorldTransform() ); } +#endif + + const char* ScriptGetModelName(void) const; + HSCRIPT ScriptGetModelKeyValues(void); + + void ScriptEmitSound(const char* soundname); + float ScriptSoundDuration(const char* soundname, const char* actormodel); + + void VScriptPrecacheScriptSound(const char* soundname); + + const Vector& ScriptGetLocalAngularVelocity( void ); + void ScriptSetLocalAngularVelocity( float pitchVel, float yawVel, float rollVel ); + + const Vector& ScriptGetBoundingMins(void); + const Vector& ScriptGetBoundingMaxs(void); + +#ifdef MAPBASE_VSCRIPT + bool ScriptIsVisible( const Vector &vecSpot ) { return FVisible( vecSpot ); } + bool ScriptIsEntVisible( HSCRIPT pEntity ) { return FVisible( ToEnt( pEntity ) ); } + bool ScriptIsVisibleWithMask( const Vector &vecSpot, int traceMask ) { return FVisible( vecSpot, traceMask ); } + + int ScriptTakeDamage( HSCRIPT pInfo ); + void ScriptFireBullets( HSCRIPT pInfo ); + + void ScriptAddContext( const char *name, const char *value, float duration = 0.0f ); + const char *ScriptGetContext( const char *name ); + + int ScriptClassify(void); + + const char *ScriptGetKeyValue( const char *pszKeyName ); +#endif + + string_t m_iszVScripts; + string_t m_iszScriptThinkFunction; + CScriptScope m_ScriptScope; + HSCRIPT m_hScriptInstance; + string_t m_iszScriptId; + CScriptKeyValues* m_pScriptModelKeyValues; }; // Send tables exposed in this module. @@ -2048,6 +2155,21 @@ inline string_t CBaseEntity::GetEntityName() return m_iName; } +inline const char *CBaseEntity::GetEntityNameAsCStr() +{ + return STRING(m_iName.Get()); +} + +inline const char *CBaseEntity::GetPreTemplateName() +{ + const char *pszDelimiter = V_strrchr( STRING(m_iName.Get()), '&' ); + if ( !pszDelimiter ) + return STRING( m_iName.Get() ); + static char szStrippedName[128]; + V_strncpy( szStrippedName, STRING( m_iName.Get() ), MIN( ARRAYSIZE(szStrippedName), pszDelimiter - STRING( m_iName.Get() ) + 1 ) ); + return szStrippedName; +} + inline void CBaseEntity::SetName( string_t newName ) { m_iName = newName; @@ -2726,6 +2848,14 @@ inline void CBaseEntity::FireBullets( int cShots, const Vector &vecSrc, FireBullets( info ); } +//----------------------------------------------------------------------------- +// VScript +//----------------------------------------------------------------------------- +inline const char* CBaseEntity::ScriptGetModelName(void) const +{ + return STRING(m_ModelName); +} + // Ugly technique to override base member functions // Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a // member function of a base class. static_cast is a sleezy way around that problem. diff --git a/sp/src/game/server/baseflex.cpp b/sp/src/game/server/baseflex.cpp index e76ce1dd4d..006b85e84e 100644 --- a/sp/src/game/server/baseflex.cpp +++ b/sp/src/game/server/baseflex.cpp @@ -95,6 +95,10 @@ BEGIN_DATADESC( CBaseFlex ) END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimating, "Animated characters who have vertex flex capability." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetOldestScene, "GetCurrentScene", "Returns the instance of the oldest active scene entity (if any)." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSceneByIndex, "GetSceneByIndex", "Returns the instance of the scene entity at the specified index." ) +END_SCRIPTDESC(); LINK_ENTITY_TO_CLASS( funCBaseFlex, CBaseFlex ); // meaningless independant class!! @@ -421,7 +425,8 @@ void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEn info.m_pEvent = event; info.m_pScene = scene; info.m_hTarget = pTarget; - info.m_bStarted = false; + info.m_bStarted = false; + info.m_hSceneEntity = pSceneEnt; #ifdef MAPBASE if (StartSceneEvent( &info, scene, event, actor, pTarget, pSceneEnt )) @@ -2029,6 +2034,37 @@ float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname ) #endif +//-------------------------------------------------------------------------------------------------- +// Returns the script instance of the scene entity associated with our oldest ("top level") scene event +//-------------------------------------------------------------------------------------------------- +HSCRIPT CBaseFlex::ScriptGetOldestScene( void ) +{ + if ( m_SceneEvents.Count() > 0 ) + { + CSceneEventInfo curScene = m_SceneEvents.Head(); + return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) ); + } + else + { + return NULL; + } +} + +//-------------------------------------------------------------------------------------------------- +// Returns the script instance of the scene at the specified index, or null if index >= count +//-------------------------------------------------------------------------------------------------- +HSCRIPT CBaseFlex::ScriptGetSceneByIndex( int index ) +{ + if ( m_SceneEvents.IsValidIndex( index ) ) + { + CSceneEventInfo curScene = m_SceneEvents.Element( index ); + return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) ); + } + else + { + return NULL; + } +} // FIXME: move to CBaseActor diff --git a/sp/src/game/server/baseflex.h b/sp/src/game/server/baseflex.h index aaa03c9cad..838ba89ebf 100644 --- a/sp/src/game/server/baseflex.h +++ b/sp/src/game/server/baseflex.h @@ -46,6 +46,8 @@ class CBaseFlex : public CBaseAnimatingOverlay DECLARE_SERVERCLASS(); DECLARE_DATADESC(); DECLARE_PREDICTABLE(); + // script description + DECLARE_ENT_SCRIPTDESC(); // Construction CBaseFlex( void ); @@ -130,6 +132,10 @@ class CBaseFlex : public CBaseAnimatingOverlay virtual float PlayAutoGeneratedSoundScene( const char *soundname ); #endif + // Returns the script instance of the scene entity associated with our oldest ("top level") scene event + virtual HSCRIPT ScriptGetOldestScene( void ); + virtual HSCRIPT ScriptGetSceneByIndex( int index ); + virtual int GetSpecialDSP( void ) { return 0; } protected: diff --git a/sp/src/game/server/cbase.cpp b/sp/src/game/server/cbase.cpp index 57c3f2cd47..d51de2db2b 100644 --- a/sp/src/game/server/cbase.cpp +++ b/sp/src/game/server/cbase.cpp @@ -396,6 +396,28 @@ void CBaseEntityOutput::AddEventAction( CEventAction *pEventAction ) m_ActionList = pEventAction; } +void CBaseEntityOutput::RemoveEventAction( CEventAction *pEventAction ) +{ + CEventAction *pAction = GetActionList(); + CEventAction *pPrevAction = NULL; + while ( pAction ) + { + if ( pAction == pEventAction ) + { + if ( !pPrevAction ) + { + m_ActionList = NULL; + } + else + { + pPrevAction->m_pNext = pAction->m_pNext; + } + return; + } + pAction = pAction->m_pNext; + } +} + // save data description for the event queue BEGIN_SIMPLE_DATADESC( CBaseEntityOutput ) diff --git a/sp/src/game/server/enginecallback.h b/sp/src/game/server/enginecallback.h index cce0786643..45993be904 100644 --- a/sp/src/game/server/enginecallback.h +++ b/sp/src/game/server/enginecallback.h @@ -26,6 +26,7 @@ class IDataCache; class IMDLCache; class IServerEngineTools; class IXboxSystem; +class IScriptManager; class CSteamAPIContext; class CSteamGameServerAPIContext; @@ -43,6 +44,7 @@ extern IDataCache *datacache; extern IMDLCache *mdlcache; extern IServerEngineTools *serverenginetools; extern IXboxSystem *xboxsystem; // 360 only +extern IScriptManager *scriptmanager; extern CSteamAPIContext *steamapicontext; // available on game clients extern CSteamGameServerAPIContext *steamgameserverapicontext; //available on game servers diff --git a/sp/src/game/server/entitylist.cpp b/sp/src/game/server/entitylist.cpp index 7413539111..cbe944ab21 100644 --- a/sp/src/game/server/entitylist.cpp +++ b/sp/src/game/server/entitylist.cpp @@ -667,7 +667,7 @@ CBaseEntity *CGlobalEntityList::FindEntityByName( CBaseEntity *pStartEntity, con continue; } - if ( !ent->m_iName ) + if ( !ent->m_iName.Get() ) continue; if ( ent->NameMatches( szName ) ) diff --git a/sp/src/game/server/entityoutput.h b/sp/src/game/server/entityoutput.h index 792ac6da2d..424ae761fe 100644 --- a/sp/src/game/server/entityoutput.h +++ b/sp/src/game/server/entityoutput.h @@ -63,6 +63,7 @@ class CBaseEntityOutput void ParseEventAction( const char *EventData ); void AddEventAction( CEventAction *pEventAction ); + void RemoveEventAction( CEventAction *pEventAction ); int Save( ISave &save ); int Restore( IRestore &restore, int elementCount ); diff --git a/sp/src/game/server/env_entity_maker.cpp b/sp/src/game/server/env_entity_maker.cpp index cbbf1b0abc..19d8ddf601 100644 --- a/sp/src/game/server/env_entity_maker.cpp +++ b/sp/src/game/server/env_entity_maker.cpp @@ -30,6 +30,7 @@ class CEnvEntityMaker : public CPointEntity DECLARE_CLASS( CEnvEntityMaker, CPointEntity ); public: DECLARE_DATADESC(); + DECLARE_ENT_SCRIPTDESC(); virtual void Spawn( void ); virtual void Activate( void ); @@ -43,6 +44,10 @@ class CEnvEntityMaker : public CPointEntity void InputForceSpawnAtPosition( inputdata_t &inputdata ); #endif + void SpawnEntityFromScript(); + void SpawnEntityAtEntityOriginFromScript(HSCRIPT hEntity); + void SpawnEntityAtNamedEntityOriginFromScript(const char* pszName); + void SpawnEntityAtLocationFromScript(const Vector& vecAlternateOrigin, const Vector& vecAlternateAngles); private: CPointTemplate *FindTemplate(); @@ -102,6 +107,13 @@ BEGIN_DATADESC( CEnvEntityMaker ) DEFINE_THINKFUNC( CheckSpawnThink ), END_DATADESC() +BEGIN_ENT_SCRIPTDESC( CEnvEntityMaker, CBaseEntity, "env_entity_maker" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityFromScript, "SpawnEntity", "Create an entity at the location of the maker" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtEntityOriginFromScript, "SpawnEntityAtEntityOrigin", "Create an entity at the location of a specified entity instance" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtNamedEntityOriginFromScript, "SpawnEntityAtNamedEntityOrigin", "Create an entity at the location of a named entity" ) + DEFINE_SCRIPTFUNC_NAMED( SpawnEntityAtLocationFromScript, "SpawnEntityAtLocation", "Create an entity at a specified location and orientaton, orientation is Euler angle in degrees (pitch, yaw, roll)" ) +END_SCRIPTDESC() + LINK_ENTITY_TO_CLASS( env_entity_maker, CEnvEntityMaker ); @@ -266,8 +278,50 @@ void CEnvEntityMaker::SpawnEntity( Vector vecAlternateOrigin, QAngle vecAlternat } } #endif + + pTemplate->CreationComplete( hNewEntities ); +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityFromScript() +{ + SpawnEntity(); +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtEntityOriginFromScript( HSCRIPT hEntity ) +{ + CBaseEntity *pTargetEntity = ToEnt( hEntity ); + if ( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } } +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtNamedEntityOriginFromScript( const char *pszName ) +{ + CBaseEntity *pTargetEntity = gEntList.FindEntityByName( NULL, pszName, this, NULL, NULL ); + + if( pTargetEntity ) + { + SpawnEntity( pTargetEntity->GetAbsOrigin(), pTargetEntity->GetAbsAngles() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Spawn an instance of the entity +//----------------------------------------------------------------------------- +void CEnvEntityMaker::SpawnEntityAtLocationFromScript( const Vector &vecAlternateOrigin, const Vector &vecAlternateAngles ) +{ + SpawnEntity( vecAlternateOrigin, *((QAngle *)&vecAlternateAngles) ); +} //----------------------------------------------------------------------------- // Purpose: Returns whether or not the template entities can fit if spawned. diff --git a/sp/src/game/server/filters.cpp b/sp/src/game/server/filters.cpp index f9380dc717..c1746ad1b6 100644 --- a/sp/src/game/server/filters.cpp +++ b/sp/src/game/server/filters.cpp @@ -44,6 +44,18 @@ BEGIN_DATADESC( CBaseFilter ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseFilter, CBaseEntity, "All entities which could be used as filters." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesFilter, "PassesFilter", "Check if the given caller and entity pass the filter." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesDamageFilter, "PassesDamageFilter", "Check if the given caller and damage info pass the damage filter." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPassesFinalDamageFilter, "PassesFinalDamageFilter", "Used by filter_damage_redirect to distinguish between standalone filter calls and actually damaging an entity. Returns true if there's no unique behavior." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptBloodAllowed, "BloodAllowed", "Check if the given caller and damage info allow for the production of blood." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptDamageMod, "DamageMod", "Mods the damage info with the given caller." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- bool CBaseFilter::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) @@ -142,6 +154,14 @@ void CBaseFilter::InputSetField( inputdata_t& inputdata ) } #endif +#ifdef MAPBASE_VSCRIPT +bool CBaseFilter::ScriptPassesFilter( HSCRIPT pCaller, HSCRIPT pEntity ) { return PassesFilter( ToEnt(pCaller), ToEnt(pEntity) ); } +bool CBaseFilter::ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } +bool CBaseFilter::ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? PassesFinalDamageFilter( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } +bool CBaseFilter::ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? BloodAllowed( ToEnt( pCaller ), *const_cast(HScriptToClass( pInfo )) ) : NULL; } +bool CBaseFilter::ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ) { return (pInfo) ? DamageMod( ToEnt( pCaller ), *HScriptToClass( pInfo ) ) : NULL; } +#endif + // ################################################################### // > FilterMultiple @@ -223,6 +243,13 @@ void CFilterMultiple::Activate( void ) Warning("filter_multi: Tried to add entity (%s) which is not a filter entity!\n", STRING( m_iFilterName[i] ) ); continue; } +#ifdef MAPBASE + else if ( pFilter == this ) + { + Warning("filter_multi: Tried to add itself!\n"); + continue; + } +#endif // Take this entity and increment out array pointer m_hFilter[nNextFilter] = pFilter; @@ -2109,3 +2136,152 @@ BEGIN_DATADESC( CFilterDamageLogic ) END_DATADESC() #endif + +#ifdef MAPBASE_VSCRIPT +// ################################################################### +// > CFilterScript +// ################################################################### +class CFilterScript : public CBaseFilter +{ + DECLARE_CLASS( CFilterScript, CBaseFilter ); + DECLARE_DATADESC(); + +public: + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if (m_ScriptScope.IsInitialized()) + { + g_pScriptVM->SetValue( "caller", (pCaller) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + g_pScriptVM->SetValue( "activator", (pEntity) ? ScriptVariant_t( pEntity->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + + ScriptVariant_t functionReturn; + if (!CallScriptFunction( "PassesFilter", &functionReturn )) + { + Warning("%s: No PassesFilter function\n", GetDebugName()); + } + + g_pScriptVM->ClearValue( "caller" ); + g_pScriptVM->ClearValue( "activator" ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool PassesDamageFilterImpl( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + g_pScriptVM->SetValue( "info", pInfo ); + g_pScriptVM->SetValue( "caller", (pCaller) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + + ScriptVariant_t functionReturn; + if (!CallScriptFunction( "PassesDamageFilter", &functionReturn )) + { + // Fall back to main filter function + return PassesFilterImpl( pCaller, info.GetAttacker() ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + g_pScriptVM->ClearValue( "info" ); + g_pScriptVM->ClearValue( "caller" ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool PassesFinalDamageFilter( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + g_pScriptVM->SetValue( "info", pInfo ); + g_pScriptVM->SetValue( "caller", (pCaller) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + + ScriptVariant_t functionReturn; + if (!CallScriptFunction( "PassesFinalDamageFilter", &functionReturn )) + { + return BaseClass::PassesFinalDamageFilter( pCaller, info ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + g_pScriptVM->ClearValue( "info" ); + g_pScriptVM->ClearValue( "caller" ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool BloodAllowed( CBaseEntity *pCaller, const CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( const_cast(&info) ); + + g_pScriptVM->SetValue( "info", pInfo ); + g_pScriptVM->SetValue( "caller", (pCaller) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + + ScriptVariant_t functionReturn; + if (!CallScriptFunction( "BloodAllowed", &functionReturn )) + { + return BaseClass::BloodAllowed( pCaller, info ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + g_pScriptVM->ClearValue( "info" ); + g_pScriptVM->ClearValue( "caller" ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } + + bool DamageMod( CBaseEntity *pCaller, CTakeDamageInfo &info ) + { + if (m_ScriptScope.IsInitialized()) + { + HSCRIPT pInfo = g_pScriptVM->RegisterInstance( &info ); + + g_pScriptVM->SetValue( "info", pInfo ); + g_pScriptVM->SetValue( "caller", (pCaller) ? ScriptVariant_t( pCaller->GetScriptInstance() ) : SCRIPT_VARIANT_NULL ); + + ScriptVariant_t functionReturn; + if (!CallScriptFunction( "DamageMod", &functionReturn )) + { + return BaseClass::DamageMod( pCaller, info ); + } + + g_pScriptVM->RemoveInstance( pInfo ); + + g_pScriptVM->ClearValue( "info" ); + g_pScriptVM->ClearValue( "caller" ); + + return functionReturn.m_bool; + } + + Warning("%s: No script scope, cannot filter\n", GetDebugName()); + return false; + } +}; + +LINK_ENTITY_TO_CLASS( filter_script, CFilterScript ); + +BEGIN_DATADESC( CFilterScript ) +END_DATADESC() +#endif diff --git a/sp/src/game/server/filters.h b/sp/src/game/server/filters.h index 46345f51de..faaa20fd83 100644 --- a/sp/src/game/server/filters.h +++ b/sp/src/game/server/filters.h @@ -38,6 +38,9 @@ class CBaseFilter : public CLogicalEntity public: DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif bool PassesFilter( CBaseEntity *pCaller, CBaseEntity *pEntity ); #ifdef MAPBASE @@ -56,6 +59,14 @@ class CBaseFilter : public CLogicalEntity bool PassesDamageFilter( const CTakeDamageInfo &info ); #endif +#ifdef MAPBASE_VSCRIPT + bool ScriptPassesFilter( HSCRIPT pCaller, HSCRIPT pEntity ); + bool ScriptPassesDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ); + bool ScriptPassesFinalDamageFilter( HSCRIPT pCaller, HSCRIPT pInfo ); + bool ScriptBloodAllowed( HSCRIPT pCaller, HSCRIPT pInfo ); + bool ScriptDamageMod( HSCRIPT pCaller, HSCRIPT pInfo ); +#endif + bool m_bNegated; // Inputs diff --git a/sp/src/game/server/fourwheelvehiclephysics.cpp b/sp/src/game/server/fourwheelvehiclephysics.cpp index 3a70d02d79..ba4a827a18 100644 --- a/sp/src/game/server/fourwheelvehiclephysics.cpp +++ b/sp/src/game/server/fourwheelvehiclephysics.cpp @@ -148,6 +148,39 @@ BEGIN_DATADESC_NO_BASE( CFourWheelVehiclePhysics ) DEFINE_FIELD( m_bLastSkid, FIELD_BOOLEAN ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CFourWheelVehiclePhysics, "Handler for four-wheel vehicle physics." ) + + DEFINE_SCRIPTFUNC( SetThrottle, "Sets the throttle." ) + DEFINE_SCRIPTFUNC( SetMaxThrottle, "Sets the max throttle." ) + DEFINE_SCRIPTFUNC( SetMaxReverseThrottle, "Sets the max reverse throttle." ) + DEFINE_SCRIPTFUNC( SetSteering, "Sets the steering." ) + DEFINE_SCRIPTFUNC( SetSteeringDegrees, "Sets the degrees of steering." ) + DEFINE_SCRIPTFUNC( SetAction, "Sets the action." ) + DEFINE_SCRIPTFUNC( SetHandbrake, "Sets the handbrake." ) + DEFINE_SCRIPTFUNC( SetBoost, "Sets the boost." ) + DEFINE_SCRIPTFUNC( SetHasBrakePedal, "Sets whether a handbrake pedal exists." ) + + DEFINE_SCRIPTFUNC( SetDisableEngine, "Sets whether the engine is disabled." ) + DEFINE_SCRIPTFUNC( IsEngineDisabled, "Checks whether the engine is disabled." ) + + DEFINE_SCRIPTFUNC( EnableMotion, "Enables vehicle motion." ) + DEFINE_SCRIPTFUNC( DisableMotion, "Disables vehicle motion." ) + + DEFINE_SCRIPTFUNC( GetSpeed, "Gets the speed." ) + DEFINE_SCRIPTFUNC( GetMaxSpeed, "Gets the max speed." ) + DEFINE_SCRIPTFUNC( GetRPM, "Gets the RPM." ) + DEFINE_SCRIPTFUNC( GetThrottle, "Gets the throttle." ) + DEFINE_SCRIPTFUNC( HasBoost, "Checks if the vehicle has the ability to boost." ) + DEFINE_SCRIPTFUNC( BoostTimeLeft, "Gets how much time is left in any current boost." ) + DEFINE_SCRIPTFUNC( IsBoosting, "Checks if the vehicle is boosting." ) + DEFINE_SCRIPTFUNC( GetHLSpeed, "Gets HL speed." ) + DEFINE_SCRIPTFUNC( GetSteering, "Gets the steeering." ) + DEFINE_SCRIPTFUNC( GetSteeringDegrees, "Gets the degrees of steeering." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Constructor diff --git a/sp/src/game/server/gameinterface.cpp b/sp/src/game/server/gameinterface.cpp index 0a91d34e14..6014f684e4 100644 --- a/sp/src/game/server/gameinterface.cpp +++ b/sp/src/game/server/gameinterface.cpp @@ -93,6 +93,9 @@ #include "world.h" #endif +#include "vscript/ivscript.h" +#include "vscript_server.h" + #ifdef TF_DLL #include "gc_clientsystem.h" @@ -185,6 +188,7 @@ IServerEngineTools *serverenginetools = NULL; ISceneFileCache *scenefilecache = NULL; IXboxSystem *xboxsystem = NULL; // Xbox 360 only IMatchmaking *matchmaking = NULL; // Xbox 360 only +IScriptManager *scriptmanager = NULL; #if defined( REPLAY_ENABLED ) IReplaySystem *g_pReplay = NULL; IServerReplayContext *g_pReplayServerContext = NULL; @@ -627,6 +631,16 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, if ( IsX360() && (matchmaking = (IMatchmaking *)appSystemFactory( VENGINE_MATCHMAKING_VERSION, NULL )) == NULL ) return false; + if (!CommandLine()->CheckParm("-noscripting")) + { + scriptmanager = (IScriptManager*)appSystemFactory(VSCRIPT_INTERFACE_VERSION, NULL); + + if (scriptmanager == nullptr) + { + scriptmanager = (IScriptManager*)Sys_GetFactoryThis()(VSCRIPT_INTERFACE_VERSION, NULL); + } + } + // If not running dedicated, grab the engine vgui interface if ( !engine->IsDedicatedServer() ) { @@ -684,6 +698,7 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, g_pGameSaveRestoreBlockSet->AddBlockHandler( GetCommentarySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->AddBlockHandler( GetAchievementSaveRestoreBlockHandler() ); + g_pGameSaveRestoreBlockSet->AddBlockHandler( GetVScriptSaveRestoreBlockHandler() ); // The string system must init first + shutdown last IGameSystem::Add( GameStringSystem() ); @@ -762,6 +777,7 @@ void CServerGameDLL::DLLShutdown( void ) // Due to dependencies, these are not autogamesystems ModelSoundsCacheShutdown(); + g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetVScriptSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetAchievementSaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetCommentarySaveRestoreBlockHandler() ); g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEventQueueSaveRestoreBlockHandler() ); diff --git a/sp/src/game/server/globalstate.cpp b/sp/src/game/server/globalstate.cpp index 7d50d5ce63..7ade776277 100644 --- a/sp/src/game/server/globalstate.cpp +++ b/sp/src/game/server/globalstate.cpp @@ -143,6 +143,28 @@ class CGlobalState : public CAutoGameSystem return m_list.Count(); } +#ifdef MAPBASE_VSCRIPT + virtual void RegisterVScript() + { + g_pScriptVM->RegisterInstance( this, "Globals" ); + } + + int ScriptAddEntity( const char *pGlobalname, const char *pMapName, int state ) + { + return AddEntity( pGlobalname, pMapName, (GLOBALESTATE)state ); + } + + void ScriptSetState( int globalIndex, int state ) + { + SetState( globalIndex, (GLOBALESTATE)state ); + } + + int ScriptGetState( int globalIndex ) + { + return (int)GetState( globalIndex ); + } +#endif + void Reset( void ); int Save( ISave &save ); int Restore( IRestore &restore ); @@ -323,3 +345,15 @@ CON_COMMAND(server_game_time, "Gives the game time in seconds (server's curtime) ShowServerGameTime(); } + +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CGlobalState, SCRIPT_SINGLETON "Global state system." ) + DEFINE_SCRIPTFUNC( GetIndex, "Gets the index of the specified global name. Returns -1 if it does not exist." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddEntity, "AddGlobal", "Adds a new global with a specific map name and state. Returns its index." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetState, "GetState", "Gets the state of the specified global." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetState, "SetState", "Sets the state of the specified global." ) + DEFINE_SCRIPTFUNC( GetCounter, "Gets the counter of the specified global." ) + DEFINE_SCRIPTFUNC( SetCounter, "Sets the counter of the specified global." ) + DEFINE_SCRIPTFUNC( AddToCounter, "Adds to the counter of the specified global." ) +END_SCRIPTDESC(); +#endif diff --git a/sp/src/game/server/hl2/ai_behavior_actbusy.cpp b/sp/src/game/server/hl2/ai_behavior_actbusy.cpp index 83aaae5bfd..9a4f09e6d9 100644 --- a/sp/src/game/server/hl2/ai_behavior_actbusy.cpp +++ b/sp/src/game/server/hl2/ai_behavior_actbusy.cpp @@ -2457,6 +2457,16 @@ BEGIN_DATADESC( CAI_ActBusyGoal ) DEFINE_OUTPUT( m_OnNPCSeeEnemy, "OnNPCSeeEnemy" ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CAI_ActBusyGoal, CAI_GoalEntity, "A goal entity which makes NPCs act busy." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptForceBusy, "ForceBusy", "Force a NPC to act busy." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptForceBusyComplex, "ForceBusyComplex", "Force a NPC to act busy with additional parameters." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptStopBusy, "StopBusy", "Force a NPC to stop busying." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2824,6 +2834,46 @@ interval_t &CAI_ActBusyGoal::NextBusySearchInterval() } #endif +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::ScriptForceBusy( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( ToEnt( hNPC ), "ForceBusy (vscript)" ); + if ( !pBehavior ) + return; + + // Tell the NPC to immediately act busy + pBehavior->SetBusySearchRange( m_flBusySearchRange ); + pBehavior->ForceActBusy( this, dynamic_cast(ToEnt( hHint )), NO_MAX_TIME, false, bTeleportOnly ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::ScriptForceBusyComplex( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly, bool bVisibleOnly, bool bUseNearestBusy, float flMaxTime, int activity, HSCRIPT pSeeEntity ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( ToEnt( hNPC ), "ForceBusyComplex (vscript)" ); + if ( !pBehavior ) + return; + + // Tell the NPC to immediately act busy + pBehavior->SetBusySearchRange( m_flBusySearchRange ); + pBehavior->ForceActBusy( this, dynamic_cast(ToEnt( hHint )), flMaxTime, bVisibleOnly, bTeleportOnly, bUseNearestBusy, ToEnt( pSeeEntity ), (Activity)activity ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CAI_ActBusyGoal::ScriptStopBusy( HSCRIPT hNPC ) +{ + CAI_ActBusyBehavior *pBehavior = GetBusyBehaviorForNPC( ToEnt( hNPC ), "StopBusy (vscript)" ); + if ( !pBehavior ) + return; + + // Just stop busying + pBehavior->StopBusying(); +} +#endif + //========================================================================================================== // ACT BUSY QUEUE //========================================================================================================== diff --git a/sp/src/game/server/hl2/ai_behavior_actbusy.h b/sp/src/game/server/hl2/ai_behavior_actbusy.h index 3389def5f5..a33dd438ff 100644 --- a/sp/src/game/server/hl2/ai_behavior_actbusy.h +++ b/sp/src/game/server/hl2/ai_behavior_actbusy.h @@ -239,6 +239,12 @@ class CAI_ActBusyGoal : public CAI_GoalEntity interval_t &NextBusySearchInterval(); #endif +#ifdef MAPBASE_VSCRIPT + void ScriptForceBusy( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly ); + void ScriptForceBusyComplex( HSCRIPT hNPC, HSCRIPT hHint, bool bTeleportOnly, bool bVisibleOnly, bool bUseNearestBusy, float flMaxTime, int activity, HSCRIPT pSeeEntity ); + void ScriptStopBusy( HSCRIPT hNPC ); +#endif + protected: CAI_ActBusyBehavior *GetBusyBehaviorForNPC( const char *pszActorName, CBaseEntity *pActivator, CBaseEntity *pCaller, const char *sInputName ); CAI_ActBusyBehavior *GetBusyBehaviorForNPC( CBaseEntity *pEntity, const char *sInputName ); @@ -257,6 +263,9 @@ class CAI_ActBusyGoal : public CAI_GoalEntity #endif DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif protected: float m_flBusySearchRange; diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index 5bedb5b52a..0d1df86d0d 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -593,6 +593,18 @@ BEGIN_DATADESC( CHL2_Player ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CHL2_Player, CBasePlayer, "The HL2 player entity." ) + + DEFINE_SCRIPTFUNC_NAMED( SuitPower_Drain, "RemoveAuxPower", "Removes from the player's available aux power." ) + DEFINE_SCRIPTFUNC_NAMED( SuitPower_Charge, "AddAuxPower", "Adds to the player's available aux power." ) + DEFINE_SCRIPTFUNC_NAMED( SuitPower_SetCharge, "SetAuxPower", "Sets the player's available aux power." ) + DEFINE_SCRIPTFUNC_NAMED( SuitPower_GetCurrentPercentage, "GetAuxPower", "Gets the player's available aux power." ) + DEFINE_SCRIPTFUNC( GetFlashlightBattery, "Gets the energy available in the player's flashlight. If the legacy (aux power-based) flashlight is enabled, this returns the aux power." ) + +END_SCRIPTDESC(); +#endif + CHL2_Player::CHL2_Player() { m_nNumMissPositions = 0; @@ -4678,7 +4690,7 @@ void CLogicPlayerProxy::InputRequestPlayerFlashBattery( inputdata_t &inputdata ) // If it's the EP2 flashlight, it returns the flashlight battery. If it's the legacy flashlight, it returns the aux power. // Note that this is on CHL2_Player, not CLogicPlayerProxy. -inline float CHL2_Player::GetFlashlightBattery() +float CHL2_Player::GetFlashlightBattery() { #ifdef HL2_EPISODIC return Flashlight_UseLegacyVersion() ? SuitPower_GetCurrentPercentage() : m_HL2Local.m_flFlashBattery; diff --git a/sp/src/game/server/hl2/hl2_player.h b/sp/src/game/server/hl2/hl2_player.h index 3f65b17ebd..d7ab459895 100644 --- a/sp/src/game/server/hl2/hl2_player.h +++ b/sp/src/game/server/hl2/hl2_player.h @@ -79,6 +79,9 @@ class CHL2_Player : public CBasePlayer { public: DECLARE_CLASS( CHL2_Player, CBasePlayer ); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif CHL2_Player(); ~CHL2_Player( void ); diff --git a/sp/src/game/server/logicentities.cpp b/sp/src/game/server/logicentities.cpp index 0f5244641a..2d9b48cef8 100644 --- a/sp/src/game/server/logicentities.cpp +++ b/sp/src/game/server/logicentities.cpp @@ -27,6 +27,126 @@ extern CServerGameDLL g_ServerGameDLL; +//----------------------------------------------------------------------------- +// Purpose: An entity that acts as a container for game scripts. +//----------------------------------------------------------------------------- + +#define MAX_SCRIPT_GROUP 16 + +class CLogicScript : public CPointEntity +{ +public: + DECLARE_CLASS( CLogicScript, CPointEntity ); + DECLARE_DATADESC(); + + void RunVScripts() + { + /* + EntityGroup <- []; + function __AppendToScriptGroup( name ) + { + if ( name.len() == 0 ) + { + EntityGroup.append( null ); + } + else + { + local ent = Entities.FindByName( null, name ); + EntityGroup.append( ent ); + if ( ent != null ) + { + ent.ValidateScriptScope(); + ent.GetScriptScope().EntityGroup <- EntityGroup; + } + } + } + */ + + static const char szAddCode[] = + { + 0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x20,0x3c,0x2d,0x20,0x5b,0x5d,0x3b,0x0d,0x0a, + 0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x5f,0x5f,0x41,0x70,0x70,0x65,0x6e,0x64,0x54,0x6f,0x53, + 0x63,0x72,0x69,0x70,0x74,0x47,0x72,0x6f,0x75,0x70,0x28,0x20,0x6e,0x61,0x6d,0x65,0x20,0x29,0x20,0x0d, + 0x0a,0x7b,0x0d,0x0a,0x09,0x69,0x66,0x20,0x28,0x20,0x6e,0x61,0x6d,0x65,0x2e,0x6c,0x65,0x6e,0x28,0x29, + 0x20,0x3d,0x3d,0x20,0x30,0x20,0x29,0x20,0x0d,0x0a,0x09,0x7b,0x20,0x0d,0x0a,0x09,0x09,0x45,0x6e,0x74, + 0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x2e,0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x20,0x6e,0x75,0x6c, + 0x6c,0x20,0x29,0x3b,0x20,0x0d,0x0a,0x09,0x7d,0x20,0x0d,0x0a,0x09,0x65,0x6c,0x73,0x65,0x0d,0x0a,0x09, + 0x7b,0x20,0x0d,0x0a,0x09,0x09,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x65,0x6e,0x74,0x20,0x3d,0x20,0x45,0x6e, + 0x74,0x69,0x74,0x69,0x65,0x73,0x2e,0x46,0x69,0x6e,0x64,0x42,0x79,0x4e,0x61,0x6d,0x65,0x28,0x20,0x6e, + 0x75,0x6c,0x6c,0x2c,0x20,0x6e,0x61,0x6d,0x65,0x20,0x29,0x3b,0x0d,0x0a,0x09,0x09,0x45,0x6e,0x74,0x69, + 0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x2e,0x61,0x70,0x70,0x65,0x6e,0x64,0x28,0x20,0x65,0x6e,0x74,0x20, + 0x29,0x3b,0x0d,0x0a,0x09,0x09,0x69,0x66,0x20,0x28,0x20,0x65,0x6e,0x74,0x20,0x21,0x3d,0x20,0x6e,0x75, + 0x6c,0x6c,0x20,0x29,0x0d,0x0a,0x09,0x09,0x7b,0x0d,0x0a,0x09,0x09,0x09,0x65,0x6e,0x74,0x2e,0x56,0x61, + 0x6c,0x69,0x64,0x61,0x74,0x65,0x53,0x63,0x72,0x69,0x70,0x74,0x53,0x63,0x6f,0x70,0x65,0x28,0x29,0x3b, + 0x0d,0x0a,0x09,0x09,0x09,0x65,0x6e,0x74,0x2e,0x47,0x65,0x74,0x53,0x63,0x72,0x69,0x70,0x74,0x53,0x63, + 0x6f,0x70,0x65,0x28,0x29,0x2e,0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x20,0x3c,0x2d, + 0x20,0x45,0x6e,0x74,0x69,0x74,0x79,0x47,0x72,0x6f,0x75,0x70,0x3b,0x0d,0x0a,0x09,0x09,0x7d,0x0d,0x0a, + 0x09,0x7d,0x0d,0x0a,0x7d,0x0d,0x0a,0x00 + }; + + int iLastMember; + for ( iLastMember = MAX_SCRIPT_GROUP - 1; iLastMember >= 0; iLastMember-- ) + { + if ( m_iszGroupMembers[iLastMember] != NULL_STRING ) + { + break; + } + } + + if ( iLastMember >= 0 ) + { + HSCRIPT hAddScript = g_pScriptVM->CompileScript( szAddCode ); + if ( hAddScript ) + { + ValidateScriptScope(); + m_ScriptScope.Run( hAddScript ); + HSCRIPT hAddFunc = m_ScriptScope.LookupFunction( "__AppendToScriptGroup" ); + if ( hAddFunc ) + { + for ( int i = 0; i <= iLastMember; i++ ) + { + m_ScriptScope.Call( hAddFunc, NULL, STRING(m_iszGroupMembers[i]) ); + } + g_pScriptVM->ReleaseFunction( hAddFunc ); + m_ScriptScope.ClearValue( "__AppendToScriptGroup" ); + } + + g_pScriptVM->ReleaseScript( hAddScript ); + } + } + BaseClass::RunVScripts(); + } + + string_t m_iszGroupMembers[MAX_SCRIPT_GROUP]; + +}; + +LINK_ENTITY_TO_CLASS( logic_script, CLogicScript ); + +BEGIN_DATADESC( CLogicScript ) + // Silence, Classcheck! + // DEFINE_ARRAY( m_iszGroupMembers, FIELD_STRING, MAX_NUM_TEMPLATES ), + + DEFINE_KEYFIELD( m_iszGroupMembers[0], FIELD_STRING, "Group00"), + DEFINE_KEYFIELD( m_iszGroupMembers[1], FIELD_STRING, "Group01"), + DEFINE_KEYFIELD( m_iszGroupMembers[2], FIELD_STRING, "Group02"), + DEFINE_KEYFIELD( m_iszGroupMembers[3], FIELD_STRING, "Group03"), + DEFINE_KEYFIELD( m_iszGroupMembers[4], FIELD_STRING, "Group04"), + DEFINE_KEYFIELD( m_iszGroupMembers[5], FIELD_STRING, "Group05"), + DEFINE_KEYFIELD( m_iszGroupMembers[6], FIELD_STRING, "Group06"), + DEFINE_KEYFIELD( m_iszGroupMembers[7], FIELD_STRING, "Group07"), + DEFINE_KEYFIELD( m_iszGroupMembers[8], FIELD_STRING, "Group08"), + DEFINE_KEYFIELD( m_iszGroupMembers[9], FIELD_STRING, "Group09"), + DEFINE_KEYFIELD( m_iszGroupMembers[10], FIELD_STRING, "Group10"), + DEFINE_KEYFIELD( m_iszGroupMembers[11], FIELD_STRING, "Group11"), + DEFINE_KEYFIELD( m_iszGroupMembers[12], FIELD_STRING, "Group12"), + DEFINE_KEYFIELD( m_iszGroupMembers[13], FIELD_STRING, "Group13"), + DEFINE_KEYFIELD( m_iszGroupMembers[14], FIELD_STRING, "Group14"), + DEFINE_KEYFIELD( m_iszGroupMembers[15], FIELD_STRING, "Group15"), + +END_DATADESC() + + //----------------------------------------------------------------------------- // Purpose: Compares a set of integer inputs to the one main input @@ -3679,7 +3799,8 @@ BEGIN_DATADESC( CLogicConsole ) END_DATADESC() -ConVar sv_allow_logic_convar("sv_allow_logic_convar", "1"); +ConVar sv_allow_logic_convar( "sv_allow_logic_convar", "1", FCVAR_NOT_CONNECTED ); + //----------------------------------------------------------------------------- // Purpose: Gets console variables for the evil mapper. //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/mapbase/SystemConvarMod.cpp b/sp/src/game/server/mapbase/SystemConvarMod.cpp index 9bd72188cd..fd2197f5a7 100644 --- a/sp/src/game/server/mapbase/SystemConvarMod.cpp +++ b/sp/src/game/server/mapbase/SystemConvarMod.cpp @@ -91,8 +91,9 @@ void CV_InitMod() void CVEnt_Precache(CMapbaseCVarModEntity *modent) { - if (Q_strstr(STRING(modent->m_target), "sv_allow_logic_convar")) - return; + // Now protected by FCVAR_NOT_CONNECTED + //if (Q_strstr(STRING(modent->m_target), "sv_allow_logic_convar")) + // return; #ifdef MAPBASE_MP if (gpGlobals->maxClients > 1 && !modent->m_bUseServer) diff --git a/sp/src/game/server/mapbase/logic_externaldata.cpp b/sp/src/game/server/mapbase/logic_externaldata.cpp index ce4f4d564a..89500b6515 100644 --- a/sp/src/game/server/mapbase/logic_externaldata.cpp +++ b/sp/src/game/server/mapbase/logic_externaldata.cpp @@ -16,6 +16,9 @@ class CLogicExternalData : public CLogicalEntity { DECLARE_CLASS( CLogicExternalData, CLogicalEntity ); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: ~CLogicExternalData(); @@ -34,6 +37,16 @@ class CLogicExternalData : public CLogicalEntity void InputSave( inputdata_t &inputdata ); void InputReload( inputdata_t &inputdata ); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetKeyValues( void ); + HSCRIPT ScriptGetKeyValueBlock( void ); + + void ScriptSetKeyValues( HSCRIPT hKV ); + void ScriptSetKeyValueBlock( HSCRIPT hKV ); + + void ScriptSetBlock( const char *szNewBlock, HSCRIPT hActivator = NULL, HSCRIPT hCaller = NULL ); +#endif + char m_iszFile[MAX_PATH]; // Root file @@ -76,6 +89,21 @@ BEGIN_DATADESC( CLogicExternalData ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CLogicExternalData, CBaseEntity, "An entity which loads keyvalues from an external data file." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValues, "GetKeyValues", "Gets the external data expressed in CScriptKeyValues." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBlock, "GetKeyValueBlock", "Gets the current external data block expressed in CScriptKeyValues." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValues, "SetKeyValues", "Sets the external data from a CScriptKeyValues object." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueBlock, "SetKeyValues", "Sets the current external data block from a CScriptKeyValues object." ) + + DEFINE_SCRIPTFUNC( LoadFile, "Loads external data from the external file." ) + DEFINE_SCRIPTFUNC( SaveFile, "Saves the external data to the external file." ) + +END_SCRIPTDESC(); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -243,3 +271,95 @@ void CLogicExternalData::InputReload( inputdata_t &inputdata ) { LoadFile(); } + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CLogicExternalData::ScriptGetKeyValues( void ) +{ + if (m_bReloadBeforeEachAction) + LoadFile(); + + HSCRIPT hScript = NULL; + if (m_pRoot) + { + // Does this need to be destructed or freed? m_pScriptModelKeyValues apparently doesn't. + CScriptKeyValues *pKV = new CScriptKeyValues( m_pRoot ); + hScript = g_pScriptVM->RegisterInstance( pKV ); + } + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CLogicExternalData::ScriptGetKeyValueBlock( void ) +{ + if (m_bReloadBeforeEachAction) + LoadFile(); + + HSCRIPT hScript = NULL; + if (m_pBlock) + { + // Does this need to be destructed or freed? m_pScriptModelKeyValues apparently doesn't. + CScriptKeyValues *pKV = new CScriptKeyValues( m_pBlock ); + hScript = g_pScriptVM->RegisterInstance( pKV ); + } + + return hScript; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CLogicExternalData::ScriptSetKeyValues( HSCRIPT hKV ) +{ + if (m_pRoot) + { + m_pRoot->deleteThis(); + m_pRoot = NULL; + } + + CScriptKeyValues *pKV = HScriptToClass( hKV ); + if (pKV) + { + m_pRoot = pKV->m_pKeyValues; + } +} + +void CLogicExternalData::ScriptSetKeyValueBlock( HSCRIPT hKV ) +{ + if (m_pBlock) + { + m_pBlock->deleteThis(); + m_pBlock = NULL; + } + + CScriptKeyValues *pKV = HScriptToClass( hKV ); + if (pKV) + { + m_pBlock = pKV->m_pKeyValues; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CLogicExternalData::ScriptSetBlock( const char *szNewBlock, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + CBaseEntity *pActivator = ToEnt( hActivator ); + CBaseEntity *pCaller = ToEnt( hCaller ); + string_t iszNewTarget = AllocPooledString(szNewBlock); + if (STRING(iszNewTarget)[0] == '!') + { + if (FStrEq(STRING(iszNewTarget), "!self")) + iszNewTarget = GetEntityName(); + else if (pActivator && FStrEq(STRING(iszNewTarget), "!activator")) + iszNewTarget = pActivator->GetEntityName(); + else if (pCaller && FStrEq(STRING(iszNewTarget), "!caller")) + iszNewTarget = pCaller->GetEntityName(); + } + + m_target = iszNewTarget; + LoadFile(); +} +#endif diff --git a/sp/src/game/server/monstermaker.cpp b/sp/src/game/server/monstermaker.cpp index 98f1e02f3f..5d56eabf30 100644 --- a/sp/src/game/server/monstermaker.cpp +++ b/sp/src/game/server/monstermaker.cpp @@ -18,6 +18,8 @@ #include "IEffects.h" #include "props.h" +#include "point_template.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -114,6 +116,8 @@ END_DATADESC() //----------------------------------------------------------------------------- void CBaseNPCMaker::Spawn( void ) { + ScriptInstallPreSpawnHook(); + SetSolid( SOLID_NONE ); m_nLiveChildren = 0; Precache(); @@ -830,6 +834,12 @@ void CTemplateNPCMaker::MakeNPC( void ) pent->SetAbsAngles( angles ); } + if ( !ScriptPreInstanceSpawn( &m_ScriptScope, pEntity, m_iszTemplateData ) ) + { + UTIL_RemoveImmediate( pEntity ); + return; + } + m_OnSpawnNPC.Set( pEntity, pEntity, this ); if ( m_spawnflags & SF_NPCMAKER_FADE ) @@ -867,6 +877,8 @@ void CTemplateNPCMaker::MakeNPC( void ) SetUse( NULL ); } } + + ScriptPostSpawn( &m_ScriptScope, &pEntity, 1 ); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index af5ef0cb4a..fcc7488e5e 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -478,6 +478,33 @@ BEGIN_DATADESC( CBasePlayer ) // DEFINE_UTLVECTOR( m_vecPlayerSimInfo ), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptIsPlayerNoclipping, "IsNoclipping", "Returns true if the player is in noclip mode." ) + + DEFINE_SCRIPTFUNC_NAMED( VScriptGetExpresser, "GetExpresser", "Gets a handle for this player's expresser." ) + + DEFINE_SCRIPTFUNC( FragCount, "Gets the number of frags (kills) this player has in a multiplayer game." ) + DEFINE_SCRIPTFUNC( DeathCount, "Gets the number of deaths this player has had in a multiplayer game." ) + DEFINE_SCRIPTFUNC( IsConnected, "Returns true if this player is connected." ) + DEFINE_SCRIPTFUNC( IsDisconnecting, "Returns true if this player is disconnecting." ) + DEFINE_SCRIPTFUNC( IsSuitEquipped, "Returns true if this player had the HEV suit equipped." ) + + DEFINE_SCRIPTFUNC_NAMED( ArmorValue, "GetArmor", "Gets the player's armor." ) + DEFINE_SCRIPTFUNC_NAMED( SetArmorValue, "SetArmor", "Sets the player's armor." ) + + DEFINE_SCRIPTFUNC( FlashlightIsOn, "Returns true if the flashlight is on." ) + DEFINE_SCRIPTFUNC( FlashlightTurnOn, "Turns on the flashlight." ) + DEFINE_SCRIPTFUNC( FlashlightTurnOff, "Turns off the flashlight." ) + +END_SCRIPTDESC(); +#else +BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseAnimating, "The player entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsPlayerNoclipping, "IsNoclipping", "Returns true if the player is in noclip mode." ) +END_SCRIPTDESC(); +#endif + int giPrecacheGrunt = 0; edict_t *CBasePlayer::s_PlayerEdict = NULL; @@ -690,6 +717,11 @@ CBasePlayer::~CBasePlayer( ) //----------------------------------------------------------------------------- void CBasePlayer::UpdateOnRemove( void ) { + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", SCRIPT_VARIANT_NULL ); + } + VPhysicsDestroyObject(); // Remove him from his current team @@ -5110,6 +5142,11 @@ void CBasePlayer::Spawn( void ) UpdateLastKnownArea(); m_weaponFiredTimer.Invalidate(); + + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } } void CBasePlayer::Activate( void ) @@ -5300,6 +5337,14 @@ void CBasePlayer::OnRestore( void ) m_nVehicleViewSavedFrame = 0; m_nBodyPitchPoseParam = LookupPoseParameter( "body_pitch" ); + + // HACK: (03/25/09) Then the player goes across a transition it doesn't spawn and register + // it's instance. We're hacking around this for now, but this will go away when we get around to + // having entities cross transitions and keep their script state. + if ( !g_pGameRules->IsMultiplayer() && g_pScriptVM && (gpGlobals->eLoadType == MapLoad_Transition) ) + { + g_pScriptVM->SetValue( "player", GetScriptInstance() ); + } } /* void CBasePlayer::SetTeamName( const char *pTeamName ) @@ -6864,6 +6909,30 @@ void CBasePlayer::ShowCrosshair( bool bShow ) } } +//----------------------------------------------------------------------------- +// Used by vscript to determine if the player is noclipping +//----------------------------------------------------------------------------- +bool CBasePlayer::ScriptIsPlayerNoclipping(void) +{ + return (GetMoveType() == MOVETYPE_NOCLIP); +} + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CBasePlayer::VScriptGetExpresser() +{ + HSCRIPT hScript = NULL; + CAI_Expresser *pExpresser = GetExpresser(); + if (pExpresser) + { + hScript = g_pScriptVM->RegisterInstance( pExpresser ); + } + + return hScript; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index 1e547fea29..35b19e842d 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -244,6 +244,8 @@ class CBasePlayer : public CBaseCombatCharacter public: DECLARE_DATADESC(); DECLARE_SERVERCLASS(); + // script description + DECLARE_ENT_SCRIPTDESC(); CBasePlayer(); ~CBasePlayer(); @@ -386,6 +388,12 @@ class CBasePlayer : public CBaseCombatCharacter void ShowViewModel( bool bShow ); void ShowCrosshair( bool bShow ); + bool ScriptIsPlayerNoclipping(void); + +#ifdef MAPBASE_VSCRIPT + HSCRIPT VScriptGetExpresser(); +#endif + // View model prediction setup void CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ); diff --git a/sp/src/game/server/point_devshot_camera.cpp b/sp/src/game/server/point_devshot_camera.cpp index fdcefa257b..4d476664ce 100644 --- a/sp/src/game/server/point_devshot_camera.cpp +++ b/sp/src/game/server/point_devshot_camera.cpp @@ -53,24 +53,6 @@ END_DATADESC() LINK_ENTITY_TO_CLASS( point_devshot_camera, CPointDevShotCamera ); -//----------------------------------------------------------------------------- -// Purpose: Convenience function so we don't have to make this check all over -//----------------------------------------------------------------------------- -static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) -{ - if ( gpGlobals->maxClients > 1 ) - { - if ( engine->IsDedicatedServer() ) - { - return NULL; - } - - return UTIL_GetListenServerHost(); - } - - return UTIL_GetLocalPlayer(); -} - //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/point_template.cpp b/sp/src/game/server/point_template.cpp index a1af651b9d..c6a09ea6a2 100644 --- a/sp/src/game/server/point_template.cpp +++ b/sp/src/game/server/point_template.cpp @@ -133,6 +133,8 @@ void PrecachePointTemplates() void CPointTemplate::Spawn( void ) { Precache(); + ScriptInstallPreSpawnHook(); + ValidateScriptScope(); } void CPointTemplate::Precache() @@ -345,7 +347,7 @@ bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecA // Some templates have Entity I/O connecting the entities within the template. // Unique versions of these templates need to be created whenever they're instanced. - if ( AllowNameFixup() && Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) ) + if ( AllowNameFixup() && ( Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) || m_ScriptScope.IsInitialized() ) ) { // This template requires instancing. // Create a new mapdata block and ask the template system to fill it in with @@ -381,7 +383,15 @@ bool CPointTemplate::CreateInstance( const Vector &vecOrigin, const QAngle &vecA pEntity->SetAbsOrigin( vecNewOrigin ); pEntity->SetAbsAngles( vecNewAngles ); - pSpawnList[i].m_pEntity = pEntity; + if (ScriptPreInstanceSpawn(&m_ScriptScope, pEntity, Templates_FindByIndex(iTemplateIndex))) + { + pSpawnList[i].m_pEntity = pEntity; + } + else + { + pSpawnList[i].m_pEntity = NULL; + UTIL_RemoveImmediate(pEntity); + } pSpawnList[i].m_nDepth = 0; pSpawnList[i].m_pDeferredParent = NULL; } @@ -426,7 +436,7 @@ bool CPointTemplate::CreateSpecificInstance( int iTemplate, const Vector &vecOri // Some templates have Entity I/O connecting the entities within the template. // Unique versions of these templates need to be created whenever they're instanced. - if ( AllowNameFixup() && Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) ) + if ( AllowNameFixup() && ( Templates_IndexRequiresEntityIOFixup( iTemplateIndex ) || m_ScriptScope.IsInitialized() ) ) { // This template requires instancing. // Create a new mapdata block and ask the template system to fill it in with @@ -474,6 +484,17 @@ bool CPointTemplate::CreateSpecificInstance( int iTemplate, const Vector &vecOri } #endif +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CPointTemplate::CreationComplete( const CUtlVector &entities ) +{ + if ( !entities.Count() ) + return; + + ScriptPostSpawn( &m_ScriptScope, (CBaseEntity **)entities.Base(), entities.Count() ); +} + //----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - @@ -514,3 +535,72 @@ void CPointTemplate::InputForceSpawnRandomTemplate( inputdata_t &inputdata ) m_pOutputOutEntity.Set(pEntity, pEntity, this); } #endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void ScriptInstallPreSpawnHook() +{ +#ifdef IS_WINDOWS_PC + if ( !g_pScriptVM->ValueExists( "__ExecutePreSpawn " ) ) + { + //g_pScriptVM->Run( g_Script_spawn_helper ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: This function is called after a spawner creates its child entity +// but before the keyvalues are injected. This gives us an +// opportunity to change any keyvalues before the entity is +// configured and spawned. In this case, we see if there is a VScript +// that wants to change anything about this entity. +//----------------------------------------------------------------------------- +bool ScriptPreInstanceSpawn( CScriptScope *pScriptScope, CBaseEntity *pChild, string_t iszKeyValueData ) +{ + if ( !pScriptScope->IsInitialized() ) + return true; + + ScriptVariant_t result; + if ( pScriptScope->Call( "__ExecutePreSpawn", &result, ToHScript( pChild ) ) != SCRIPT_DONE ) + return true; + + if ( ( result.m_type == FIELD_BOOLEAN && !result.m_bool ) || ( result.m_type == FIELD_INTEGER && !result.m_int ) ) + return false; + + return true; + +} + +void ScriptPostSpawn( CScriptScope *pScriptScope, CBaseEntity **ppEntities, int nEntities ) +{ + if ( !pScriptScope->IsInitialized() ) + return; + + HSCRIPT hPostSpawnFunc = pScriptScope->LookupFunction( "PostSpawn" ); + + if ( !hPostSpawnFunc ) + return; + + ScriptVariant_t varEntityMakerResultTable; + if ( !g_pScriptVM->GetValue( *pScriptScope, "__EntityMakerResult", &varEntityMakerResultTable ) ) + return; + + if ( varEntityMakerResultTable.m_type != FIELD_HSCRIPT ) + return; + + HSCRIPT hEntityMakerResultTable = varEntityMakerResultTable.m_hScript; + char szEntName[256]; + for ( int i = 0; i < nEntities; i++ ) + { + V_strncpy( szEntName, ppEntities[i]->GetEntityNameAsCStr(), ARRAYSIZE(szEntName) ); + char *pAmpersand = V_strrchr( szEntName, '&' ); + if ( pAmpersand ) + *pAmpersand = 0; + g_pScriptVM->SetValue( hEntityMakerResultTable, szEntName, ToHScript( ppEntities[i] ) ); + } + pScriptScope->Call( hPostSpawnFunc, NULL, hEntityMakerResultTable ); + pScriptScope->Call( "__FinishSpawn" ); + g_pScriptVM->ReleaseValue( varEntityMakerResultTable ); + g_pScriptVM->ReleaseFunction( hPostSpawnFunc ); +} diff --git a/sp/src/game/server/point_template.h b/sp/src/game/server/point_template.h index dc60d0785b..b5c34d472d 100644 --- a/sp/src/game/server/point_template.h +++ b/sp/src/game/server/point_template.h @@ -20,6 +20,10 @@ struct template_t DECLARE_SIMPLE_DATADESC(); }; +void ScriptInstallPreSpawnHook(); +bool ScriptPreInstanceSpawn( CScriptScope *pScriptScope, CBaseEntity *pChild, string_t iszKeyValueData ); +void ScriptPostSpawn( CScriptScope *pScriptScope, CBaseEntity **ppEntities, int nEntities ); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -52,6 +56,7 @@ class CPointTemplate : public CLogicalEntity #ifdef MAPBASE bool CreateSpecificInstance( int iTemplate, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity **pOutEntity ); #endif + void CreationComplete(const CUtlVector& entities); // Inputs void InputForceSpawn( inputdata_t &inputdata ); diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp index e43cdf331e..eb926a4281 100644 --- a/sp/src/game/server/sceneentity.cpp +++ b/sp/src/game/server/sceneentity.cpp @@ -33,6 +33,8 @@ #include "SceneCache.h" #include "scripted.h" #include "env_debughistory.h" +#include "team.h" +#include "triggers.h" #ifdef HL2_EPISODIC #include "npc_alyx_episodic.h" @@ -323,6 +325,8 @@ class CSceneEntity : public CPointEntity, public IChoreoEventCallback DECLARE_CLASS( CSceneEntity, CPointEntity ); DECLARE_SERVERCLASS(); + // script description + DECLARE_ENT_SCRIPTDESC(); CSceneEntity( void ); ~CSceneEntity( void ); @@ -470,6 +474,8 @@ class CSceneEntity : public CPointEntity, public IChoreoEventCallback void InputScriptPlayerDeath( inputdata_t &inputdata ); + void AddBroadcastTeamTarget( int nTeamIndex ); + void RemoveBroadcastTeamTarget( int nTeamIndex ); // Data public: string_t m_iszSceneFile; @@ -543,6 +549,9 @@ class CSceneEntity : public CPointEntity, public IChoreoEventCallback virtual CBaseEntity *FindNamedEntity( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false ); CBaseEntity *FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly = false ); virtual CBaseEntity *FindNamedEntityClosest( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false, const char *pszSecondary = NULL ); + HSCRIPT ScriptFindNamedEntity( const char *name ); + bool ScriptLoadSceneFromString( const char * pszFilename, const char *pszData ); + private: @@ -764,6 +773,17 @@ BEGIN_DATADESC( CSceneEntity ) DEFINE_OUTPUT( m_OnTrigger16, "OnTrigger16"), END_DATADESC() + +BEGIN_ENT_SCRIPTDESC( CSceneEntity, CBaseEntity, "Choreographed scene which controls animation and/or dialog on one or more actors." ) + DEFINE_SCRIPTFUNC( EstimateLength, "Returns length of this scene in seconds." ) + DEFINE_SCRIPTFUNC( IsPlayingBack, "If this scene is currently playing." ) + DEFINE_SCRIPTFUNC( IsPaused, "If this scene is currently paused." ) + DEFINE_SCRIPTFUNC( AddBroadcastTeamTarget, "Adds a team (by index) to the broadcast list" ) + DEFINE_SCRIPTFUNC( RemoveBroadcastTeamTarget, "Removes a team (by index) from the broadcast list" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptFindNamedEntity, "FindNamedEntity", "given an entity reference, such as !target, get actual entity from scene object" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptLoadSceneFromString, "LoadSceneFromString", "given a dummy scene name and a vcd string, load the scene" ) +END_SCRIPTDESC(); + const ConVar *CSceneEntity::m_pcvSndMixahead = NULL; //----------------------------------------------------------------------------- @@ -3066,7 +3086,7 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv CBaseFlex *pActor = NULL; CChoreoActor *actor = event->GetActor(); - if ( actor ) + if ( actor && (event->GetType() != CChoreoEvent::SCRIPT) && (event->GetType() != CChoreoEvent::CAMERA) ) { pActor = FindNamedActor( actor ); if (pActor == NULL) @@ -3229,6 +3249,80 @@ void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEv } } break; + + case CChoreoEvent::CAMERA: + { + // begin the camera shot + const char *pszShotType = event->GetParameters(); + + CBaseEntity *pActor1 = FindNamedEntity( event->GetParameters2( ), pActor ); + CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters3( ), pActor ); + float duration = event->GetDuration(); + + // grab any camera we find in the map + // TODO: find camera that is nearest this scene entity? + CTriggerCamera *pCamera = (CTriggerCamera *)gEntList.FindEntityByClassname( NULL, "point_viewcontrol" ); + + if ( !pCamera ) + { + Warning( "CSceneEntity %s unable to find a camera (point_viewcontrol) in this map!\n", STRING(GetEntityName()) ); + } + else + { + pCamera->StartCameraShot( pszShotType, this, pActor1, pActor2, duration ); + } + } + break; + + case CChoreoEvent::SCRIPT: + { + // NOTE: this is only used by auto-generated vcds to embed script commands to map entities. + + // vscript call - param1 is entity name, param2 is function name, param3 is function parameter string + // calls a vscript function defined on the scope of the named CBaseEntity object/actor. + // script call is of the format FunctionName(pActor, pThisSceneEntity, pszScriptParameters, duration) + const char *pszActorName = event->GetParameters(); + const char *pszFunctionName = event->GetParameters2(); + const char *pszScriptParameters = event->GetParameters3(); + + float duration = event->GetDuration(); + + // TODO: should be new method CBaseEntity::CallScriptFunctionParams() + CBaseEntity *pEntity = (CBaseEntity *)gEntList.FindEntityByName( NULL, pszActorName ); + + //START_VMPROFILE + if ( !pEntity ) + { + Warning( "CSceneEntity::SCRIPT event - unable to find entity named '%s' in this map!\n", pszActorName ); + } + else + { + + if( !pEntity->ValidateScriptScope() ) + { + DevMsg("\n***\nCChoreoEvent::SCRIPT - FAILED to create private ScriptScope. ABORTING script call\n***\n"); + break; + } + + HSCRIPT hFunc = pEntity->m_ScriptScope.LookupFunction( pszFunctionName ); + + if( hFunc ) + { + pEntity->m_ScriptScope.Call( hFunc, NULL, ToHScript(this), pszScriptParameters, duration ); + pEntity->m_ScriptScope.ReleaseFunction( hFunc ); + + //UPDATE_VMPROFILE + } + else + { + Warning("CSceneEntity::SCRIPT event - '%s' entity has no script function '%s' defined!\n", pszActorName,pszFunctionName); + } + } + + } + break; + + case CChoreoEvent::FIRETRIGGER: { if ( IsMultiplayer() ) @@ -3437,6 +3531,19 @@ void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEven } } break; + + case CChoreoEvent::CAMERA: + { + // call the end of camera or call a dispatch function + } + break; + + case CChoreoEvent::SCRIPT: + { + // call the end of script or call a dispatch function + } + break; + case CChoreoEvent::SEQUENCE: { if ( pActor ) @@ -4268,6 +4375,53 @@ CBaseEntity *CSceneEntity::FindNamedEntityClosest( const char *name, CBaseEntity } +HSCRIPT CSceneEntity::ScriptFindNamedEntity(const char* name) +{ + return ToHScript(FindNamedEntity(name, NULL, false, false)); +} + +//----------------------------------------------------------------------------- +// Purpose: vscript - create a scene directly from a buffer containing +// a vcd description, and load it into the scene entity. +//----------------------------------------------------------------------------- +bool CSceneEntity::ScriptLoadSceneFromString(const char* pszFilename, const char* pszData) +{ + CChoreoScene* pScene = new CChoreoScene(NULL); + + // CSceneTokenProcessor SceneTokenProcessor; + // SceneTokenProcessor.SetBuffer( pszData ); + g_TokenProcessor.SetBuffer((char*)pszData); + + if (!pScene->ParseFromBuffer(pszFilename, &g_TokenProcessor)) //&SceneTokenProcessor ) ) + { + Warning("CSceneEntity::LoadSceneFromString: Unable to parse scene data '%s'\n", pszFilename); + delete pScene; + pScene = NULL; + } + else + { + pScene->SetPrintFunc(LocalScene_Printf); + pScene->SetEventCallbackInterface(this); + + + // precache all sounds for the newly constructed scene + PrecacheScene(pScene); + } + + if (pScene != NULL) + { + // release prior scene if present + UnloadScene(); + m_pScene = pScene; + return true; + } + else + { + return false; + } +} + + //----------------------------------------------------------------------------- // Purpose: Remove all "scene" expressions from all actors in this scene //----------------------------------------------------------------------------- @@ -4691,6 +4845,44 @@ void CSceneEntity::SetRecipientFilter( IRecipientFilter *filter ) } } +//----------------------------------------------------------------------------- +// Purpose: Adds a player (by index) to the recipient filter +//----------------------------------------------------------------------------- +void CSceneEntity::AddBroadcastTeamTarget(int nTeamIndex) +{ + if (m_pRecipientFilter == NULL) + { + CRecipientFilter filter; + SetRecipientFilter(&filter); + } + + CTeam* pTeam = GetGlobalTeam(nTeamIndex); + Assert(pTeam); + if (pTeam == NULL) + return; + + m_pRecipientFilter->AddRecipientsByTeam(pTeam); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes a player (by index) from the recipient filter +//----------------------------------------------------------------------------- +void CSceneEntity::RemoveBroadcastTeamTarget(int nTeamIndex) +{ + if (m_pRecipientFilter == NULL) + { + CRecipientFilter filter; + SetRecipientFilter(&filter); + } + + CTeam* pTeam = GetGlobalTeam(nTeamIndex); + Assert(pTeam); + if (pTeam == NULL) + return; + + m_pRecipientFilter->RemoveRecipientsByTeam(pTeam); +} + //----------------------------------------------------------------------------- // Purpose: @@ -4986,6 +5178,26 @@ int GetSceneSpeechCount( char const *pszScene ) return 0; } +HSCRIPT ScriptCreateSceneEntity( const char* pszScene ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create a scene entity mid-game. Entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + g_pScriptVM->RegisterClass( GetScriptDescForClass( CSceneEntity ) ); + CSceneEntity *pScene = (CSceneEntity *)CBaseEntity::CreateNoSpawn( "logic_choreographed_scene", vec3_origin, vec3_angle ); + + if ( pScene ) + { + pScene->m_iszSceneFile = AllocPooledString( pszScene ); + DispatchSpawn( pScene ); + } + + return ToHScript( pScene ); +} + #ifdef MAPBASE CON_COMMAND(mapbase_scene_precache, "Just work") { diff --git a/sp/src/game/server/sceneentity.h b/sp/src/game/server/sceneentity.h index 9bc5b4b0f6..6e005a60bf 100644 --- a/sp/src/game/server/sceneentity.h +++ b/sp/src/game/server/sceneentity.h @@ -48,6 +48,7 @@ int GetSceneSpeechCount( char const *pszScene ); bool IsInInterruptableScenes( CBaseFlex *pActor ); void PrecacheInstancedScene( char const *pszScene ); +HSCRIPT ScriptCreateSceneEntity( char const *pszScene ); char const *GetSceneFilename( CBaseEntity *ent ); void ReloadSceneFromDisk( CBaseEntity *ent ); diff --git a/sp/src/game/server/server_base.vpc b/sp/src/game/server/server_base.vpc index 6f3abc5e65..df35da1e6d 100644 --- a/sp/src/game/server/server_base.vpc +++ b/sp/src/game/server/server_base.vpc @@ -648,6 +648,11 @@ $Project $File "$SRCDIR\game\shared\voice_common.h" $File "$SRCDIR\game\shared\voice_gamemgr.cpp" $File "$SRCDIR\game\shared\voice_gamemgr.h" + $File "vscript_server.cpp" + $File "vscript_server.h" + $File "vscript_server.nut" + $File "$SRCDIR\game\shared\vscript_shared.cpp" + $File "$SRCDIR\game\shared\vscript_shared.h" $File "waterbullet.cpp" $File "waterbullet.h" $File "WaterLODControl.cpp" diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 0a138607e8..7d30a51184 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -31,6 +31,9 @@ $Project $File "$SRCDIR\game\shared\mapbase\MapEdit.h" $File "$SRCDIR\game\shared\mapbase\matchers.cpp" $File "$SRCDIR\game\shared\mapbase\matchers.h" + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_math.cpp" [$MAPBASE_VSCRIPT] + $File "$SRCDIR\game\shared\mapbase\vscript_funcs_hl2.cpp" [$MAPBASE_VSCRIPT] $File "mapbase\ai_grenade.cpp" $File "mapbase\ai_grenade.h" @@ -81,4 +84,9 @@ $Project } } } + + $Folder "Link Libraries" + { + $Lib "vscript" [$MAPBASE_VSCRIPT] + } } diff --git a/sp/src/game/server/triggers.cpp b/sp/src/game/server/triggers.cpp index c67223840b..1cc438a37b 100644 --- a/sp/src/game/server/triggers.cpp +++ b/sp/src/game/server/triggers.cpp @@ -2978,94 +2978,6 @@ void CAI_ChangeHintGroup::InputActivate( inputdata_t &inputdata ) #endif -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -class CTriggerCamera : public CBaseEntity -{ -public: - DECLARE_CLASS( CTriggerCamera, CBaseEntity ); - -#ifdef MAPBASE - CTriggerCamera(); - - void UpdateOnRemove(); -#endif - - void Spawn( void ); - bool KeyValue( const char *szKeyName, const char *szValue ); - void Enable( void ); - void Disable( void ); - void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); - void FollowTarget( void ); -#ifdef MAPBASE - void MoveThink( void ); -#endif - void Move(void); - - // Always transmit to clients so they know where to move the view to - virtual int UpdateTransmitState(); - - DECLARE_DATADESC(); - - // Input handlers - void InputEnable( inputdata_t &inputdata ); - void InputDisable( inputdata_t &inputdata ); - -#ifdef MAPBASE - void InputSetFOV( inputdata_t &inputdata ); - void InputSetFOVRate( inputdata_t &inputdata ); -#endif - -private: - EHANDLE m_hPlayer; - EHANDLE m_hTarget; - - // used for moving the camera along a path (rail rides) - CBaseEntity *m_pPath; - string_t m_sPath; - float m_flWait; - float m_flReturnTime; - float m_flStopTime; - float m_moveDistance; - float m_targetSpeed; - float m_initialSpeed; - float m_acceleration; - float m_deceleration; - int m_state; - Vector m_vecMoveDir; - -#ifdef MAPBASE - float m_fov; - float m_fovSpeed; - - bool m_bDontSetPlayerView; -#endif - - string_t m_iszTargetAttachment; - int m_iAttachmentIndex; - bool m_bSnapToGoal; - -#if HL2_EPISODIC - bool m_bInterpolatePosition; - - // these are interpolation vars used for interpolating the camera over time - Vector m_vStartPos, m_vEndPos; - float m_flInterpStartTime; - - const static float kflPosInterpTime; // seconds -#endif - - int m_nPlayerButtons; - int m_nOldTakeDamage; - -private: - COutputEvent m_OnEndFollow; -#ifdef MAPBASE - COutputEvent m_OnStartFollow; -#endif -}; - #if HL2_EPISODIC const float CTriggerCamera::kflPosInterpTime = 2.0f; #endif @@ -3127,6 +3039,12 @@ BEGIN_DATADESC( CTriggerCamera ) END_DATADESC() +// VScript: publish class and select members to script language +BEGIN_ENT_SCRIPTDESC( CTriggerCamera, CBaseEntity, "Server-side camera entity" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFov, "GetFov", "get camera's current fov setting as integer" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFov, "SetFov", "set camera's current fov in integer degrees and fov change rate as float" ) +END_SCRIPTDESC(); + #ifdef MAPBASE //----------------------------------------------------------------------------- // Purpose: @@ -3641,6 +3559,59 @@ void CTriggerCamera::FollowTarget( ) Move(); } +void CTriggerCamera::StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration ) +{ + // called from SceneEntity in response to a CChoreoEvent::CAMERA sent from a VCD. + // talk to vscript, start a camera move + + HSCRIPT hStartCameraShot = NULL; + + // switch to this camera + // Enable(); + + // get script module associated with this ent, lookup function in module + if( m_iszVScripts != NULL_STRING ) + { + hStartCameraShot = m_ScriptScope.LookupFunction( "ScriptStartCameraShot" ); + } + + // call the script function to begin the camera move + if ( hStartCameraShot ) + { + g_pScriptVM->Call( hStartCameraShot, m_ScriptScope, true, NULL, pszShotType, ToHScript(pSceneEntity), ToHScript(pActor1), ToHScript(pActor2), duration ); + g_pScriptVM->ReleaseFunction( hStartCameraShot ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: vscript callback to get the player's fov +//----------------------------------------------------------------------------- +int CTriggerCamera::ScriptGetFov(void) +{ + if (m_hPlayer) + { + CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + int iFOV = pBasePlayer->GetFOV(); + return iFOV; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: vscript callback to slam the player's fov +//----------------------------------------------------------------------------- +void CTriggerCamera::ScriptSetFov(int iFOV, float fovSpeed) +{ + if (m_hPlayer) + { + m_fov = iFOV; + m_fovSpeed = fovSpeed; + + CBasePlayer* pBasePlayer = (CBasePlayer*)m_hPlayer.Get(); + pBasePlayer->SetFOV(this, iFOV, fovSpeed); + } +} + #ifdef MAPBASE void CTriggerCamera::MoveThink() { diff --git a/sp/src/game/server/triggers.h b/sp/src/game/server/triggers.h index 9d7c182974..a1a2cdb71b 100644 --- a/sp/src/game/server/triggers.h +++ b/sp/src/game/server/triggers.h @@ -244,4 +244,98 @@ class CTriggerHurt : public CBaseTrigger CUtlVector m_hurtEntities; }; +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTriggerCamera : public CBaseEntity +{ +public: + DECLARE_CLASS( CTriggerCamera, CBaseEntity ); + // script description + DECLARE_ENT_SCRIPTDESC(); + +#ifdef MAPBASE + CTriggerCamera(); + + void UpdateOnRemove(); +#endif + + void Spawn( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void Enable( void ); + void Disable( void ); + + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + void FollowTarget( void ); + void StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration ); + int ScriptGetFov(void); + void ScriptSetFov(int iFOV, float rate); +#ifdef MAPBASE + void MoveThink( void ); +#endif + void Move(void); + + // Always transmit to clients so they know where to move the view to + virtual int UpdateTransmitState(); + + DECLARE_DATADESC(); + + // Input handlers + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); + +#ifdef MAPBASE + void InputSetFOV( inputdata_t &inputdata ); + void InputSetFOVRate( inputdata_t &inputdata ); +#endif + +private: + EHANDLE m_hPlayer; + EHANDLE m_hTarget; + + // used for moving the camera along a path (rail rides) + CBaseEntity *m_pPath; + string_t m_sPath; + float m_flWait; + float m_flReturnTime; + float m_flStopTime; + float m_moveDistance; + float m_targetSpeed; + float m_initialSpeed; + float m_acceleration; + float m_deceleration; + int m_state; + Vector m_vecMoveDir; + +#ifdef MAPBASE + float m_fov; + float m_fovSpeed; + + bool m_bDontSetPlayerView; +#endif + + string_t m_iszTargetAttachment; + int m_iAttachmentIndex; + bool m_bSnapToGoal; + +#if HL2_EPISODIC + bool m_bInterpolatePosition; + + // these are interpolation vars used for interpolating the camera over time + Vector m_vStartPos, m_vEndPos; + float m_flInterpStartTime; + + const static float kflPosInterpTime; // seconds +#endif + + int m_nPlayerButtons; + int m_nOldTakeDamage; + +private: + COutputEvent m_OnEndFollow; +#ifdef MAPBASE + COutputEvent m_OnStartFollow; +#endif +}; + #endif // TRIGGERS_H diff --git a/sp/src/game/server/util.cpp b/sp/src/game/server/util.cpp index c0e8912d53..0a1467002c 100644 --- a/sp/src/game/server/util.cpp +++ b/sp/src/game/server/util.cpp @@ -1959,7 +1959,7 @@ extern "C" void Sys_Error( char *error, ... ) // *mapData - pointer a block of entity map data // Output : -1 if the entity was not successfully created; 0 on success //----------------------------------------------------------------------------- -int DispatchSpawn( CBaseEntity *pEntity ) +int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts ) { if ( pEntity ) { @@ -1974,6 +1974,12 @@ int DispatchSpawn( CBaseEntity *pEntity ) //pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) ); //pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) ); + if (bRunVScripts) + { + pEntity->RunVScripts(); + pEntity->RunPrecacheScripts(); + } + #if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG) const char *pszClassname = NULL; int iClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.Find( pEntity->GetClassname() ); @@ -2040,6 +2046,11 @@ int DispatchSpawn( CBaseEntity *pEntity ) } gEntList.NotifySpawn( pEntity ); + + if( bRunVScripts ) + { + pEntity->RunOnPostSpawnScripts(); + } } return 0; diff --git a/sp/src/game/server/util.h b/sp/src/game/server/util.h index d13cb3a711..9f35689186 100644 --- a/sp/src/game/server/util.h +++ b/sp/src/game/server/util.h @@ -237,6 +237,24 @@ CBasePlayer* UTIL_GetLocalPlayer( void ); // get the local player on a listen server CBasePlayer *UTIL_GetListenServerHost( void ); +//----------------------------------------------------------------------------- +// Purpose: Convenience function so we don't have to make this check all over +//----------------------------------------------------------------------------- +static CBasePlayer * UTIL_GetLocalPlayerOrListenServerHost( void ) +{ + if ( gpGlobals->maxClients > 1 ) + { + if ( engine->IsDedicatedServer() ) + { + return NULL; + } + + return UTIL_GetListenServerHost(); + } + + return UTIL_GetLocalPlayer(); +} + CBasePlayer* UTIL_PlayerByUserId( int userID ); CBasePlayer* UTIL_PlayerByName( const char *name ); // not case sensitive diff --git a/sp/src/game/server/variant_t.cpp b/sp/src/game/server/variant_t.cpp index 9d38368e45..04d96f79ab 100644 --- a/sp/src/game/server/variant_t.cpp +++ b/sp/src/game/server/variant_t.cpp @@ -65,6 +65,22 @@ const char *variant_t::GetDebug() return UTIL_VarArgs("%s (%s)", String(), fieldtype); } +#ifdef MAPBASE_VSCRIPT +void variant_t::SetScriptVariant( ScriptVariant_t &var ) +{ + switch (FieldType()) + { + case FIELD_INTEGER: var = Int(); break; + case FIELD_FLOAT: var = Float(); break; + case FIELD_STRING: var = String(); break; + case FIELD_POSITION_VECTOR: + case FIELD_VECTOR: var = reinterpret_cast(&flVal); break; // HACKHACK + case FIELD_BOOLEAN: var = Bool(); break; + case FIELD_EHANDLE: var = ToHScript( Entity() ); break; + } +} +#endif + // cmp1 = val1 float // cmp2 = val2 float #define VariantToFloat(val1, val2, lenallowed) \ diff --git a/sp/src/game/server/variant_t.h b/sp/src/game/server/variant_t.h index ffd6914410..fc460bfec2 100644 --- a/sp/src/game/server/variant_t.h +++ b/sp/src/game/server/variant_t.h @@ -17,6 +17,10 @@ class CBaseEntity; +#ifdef MAPBASE_VSCRIPT +struct ScriptVariant_t; +#endif + // // A variant class for passing data in entity input/output connections. @@ -80,6 +84,10 @@ class variant_t const char *GetDebug(); #endif +#ifdef MAPBASE_VSCRIPT + void SetScriptVariant( ScriptVariant_t &var ); +#endif + static typedescription_t m_SaveBool[]; static typedescription_t m_SaveInt[]; static typedescription_t m_SaveFloat[]; diff --git a/sp/src/game/server/vehicle_base.cpp b/sp/src/game/server/vehicle_base.cpp index d4194d9b53..5e348ee9cf 100644 --- a/sp/src/game/server/vehicle_base.cpp +++ b/sp/src/game/server/vehicle_base.cpp @@ -66,6 +66,15 @@ BEGIN_DATADESC( CPropVehicle ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CPropVehicle, CBaseAnimating, "The base class for four-wheel physics vehicles." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetVehicleType, "GetVehicleType", "Get a vehicle's type." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysics, "GetPhysics", "Get a vehicle's physics." ) + +END_SCRIPTDESC(); +#endif + LINK_ENTITY_TO_CLASS( prop_vehicle, CPropVehicle ); //----------------------------------------------------------------------------- @@ -226,6 +235,23 @@ void CPropVehicle::InputHandBrakeOff( inputdata_t &inputdata ) m_VehiclePhysics.ReleaseHandbrake(); } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +HSCRIPT CPropVehicle::ScriptGetPhysics() +{ + HSCRIPT hScript = NULL; + CFourWheelVehiclePhysics *pPhysics = GetPhysics(); + if (pPhysics) + { + hScript = g_pScriptVM->RegisterInstance( pPhysics ); + } + + return hScript; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -379,6 +405,19 @@ BEGIN_DATADESC( CPropVehicleDriveable ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CPropVehicleDriveable, CPropVehicle, "The base class for driveable vehicles." ) + + DEFINE_SCRIPTFUNC( IsOverturned, "Check if the vehicle is overturned." ) + DEFINE_SCRIPTFUNC( IsVehicleBodyInWater, "Check if the vehicle's body is submerged in water." ) + DEFINE_SCRIPTFUNC( StartEngine, "Start the engine." ) + DEFINE_SCRIPTFUNC( StopEngine, "Stop the engine." ) + DEFINE_SCRIPTFUNC( IsEngineOn, "Check if the engine is on." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDriver, "GetDriver", "Get a vehicle's driver, which could be either a player or a npc_vehicledriver." ) + +END_SCRIPTDESC(); +#endif + LINK_ENTITY_TO_CLASS( prop_vehicle_driveable, CPropVehicleDriveable ); diff --git a/sp/src/game/server/vehicle_base.h b/sp/src/game/server/vehicle_base.h index 1f4b57107b..831ea6b436 100644 --- a/sp/src/game/server/vehicle_base.h +++ b/sp/src/game/server/vehicle_base.h @@ -109,6 +109,12 @@ class CPropVehicle : public CBaseProp, public CDefaultPlayerPickupVPhysics void InputHandBrakeOff( inputdata_t &inputdata ); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); + + HSCRIPT ScriptGetPhysics(); + int ScriptGetVehicleType() { return GetVehicleType(); } +#endif #ifdef HL2_EPISODIC void AddPhysicsChild( CBaseEntity *pChild ); @@ -166,6 +172,9 @@ class CPropVehicleDriveable : public CPropVehicle, public IDrivableVehicle, publ DECLARE_CLASS( CPropVehicleDriveable, CPropVehicle ); DECLARE_SERVERCLASS(); DECLARE_DATADESC(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif public: CPropVehicleDriveable( void ); ~CPropVehicleDriveable( void ); @@ -238,6 +247,10 @@ class CPropVehicleDriveable : public CPropVehicle, public IDrivableVehicle, publ // If this is a vehicle, returns the vehicle interface virtual IServerVehicle *GetServerVehicle() { return m_pServerVehicle; } +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetDriver() { return ToHScript( GetDriver() ); } +#endif + protected: virtual bool ShouldThink() { return ( GetDriver() != NULL ); } diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp new file mode 100644 index 0000000000..a0c87f9343 --- /dev/null +++ b/sp/src/game/server/vscript_server.cpp @@ -0,0 +1,932 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_server.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "eventqueue.h" +#include "characterset.h" +#include "sceneentity.h" // for exposing scene precache function +#include "isaverestore.h" +#include "gamerules.h" +#include "vscript_server.nut" +#ifdef MAPBASE_VSCRIPT +#include "world.h" +#endif + +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CScriptEntityIterator +{ +public: + HSCRIPT First() { return Next(NULL); } + + HSCRIPT Next( HSCRIPT hStartEntity ) + { + return ToHScript( gEntList.NextEnt( ToEnt( hStartEntity ) ) ); + } + + HSCRIPT CreateByClassname( const char *className ) + { + return ToHScript( CreateEntityByName( className ) ); + } + + HSCRIPT FindByClassname( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByClassname( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindByName( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByName( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindInSphere( HSCRIPT hStartEntity, const Vector &vecCenter, float flRadius ) + { + return ToHScript( gEntList.FindEntityInSphere( ToEnt( hStartEntity ), vecCenter, flRadius ) ); + } + + HSCRIPT FindByTarget( HSCRIPT hStartEntity, const char *szName ) + { + return ToHScript( gEntList.FindEntityByTarget( ToEnt( hStartEntity ), szName ) ); + } + + HSCRIPT FindByModel( HSCRIPT hStartEntity, const char *szModelName ) + { + return ToHScript( gEntList.FindEntityByModel( ToEnt( hStartEntity ), szModelName ) ); + } + + HSCRIPT FindByNameNearest( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByNameNearest( szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByNameWithin( HSCRIPT hStartEntity, const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByNameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByClassnameNearest( const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameNearest( szName, vecSrc, flRadius ) ); + } + + HSCRIPT FindByClassnameWithin( HSCRIPT hStartEntity , const char *szName, const Vector &vecSrc, float flRadius ) + { + return ToHScript( gEntList.FindEntityByClassnameWithin( ToEnt( hStartEntity ), szName, vecSrc, flRadius ) ); + } + +private: +} g_ScriptEntityIterator; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptEntityIterator, "CEntities", SCRIPT_SINGLETON "The global list of entities" ) + DEFINE_SCRIPTFUNC( First, "Begin an iteration over the list of entities" ) + DEFINE_SCRIPTFUNC( Next, "Continue an iteration over the list of entities, providing reference to a previously found entity" ) + DEFINE_SCRIPTFUNC( CreateByClassname, "Creates an entity by classname" ) + DEFINE_SCRIPTFUNC( FindByClassname, "Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByName, "Find entities by name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindInSphere, "Find entities within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByTarget, "Find entities by targetname. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByModel, "Find entities by model name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByNameNearest, "Find entities by name nearest to a point." ) + DEFINE_SCRIPTFUNC( FindByNameWithin, "Find entities by name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) + DEFINE_SCRIPTFUNC( FindByClassnameNearest, "Find entities by class name nearest to a point." ) + DEFINE_SCRIPTFUNC( FindByClassnameWithin, "Find entities by class name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search" ) +END_SCRIPTDESC(); + +// ---------------------------------------------------------------------------- +// KeyValues access - CBaseEntity::ScriptGetKeyFromModel returns root KeyValues +// ---------------------------------------------------------------------------- + +BEGIN_SCRIPTDESC_ROOT( CScriptKeyValues, "Wrapper class over KeyValues instance" ) + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC_NAMED( ScriptFindKey, "FindKey", "Given a KeyValues object and a key name, find a KeyValues object associated with the key name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFirstSubKey, "GetFirstSubKey", "Given a KeyValues object, return the first sub key object" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetNextKey, "GetNextKey", "Given a KeyValues object, return the next key object in a sub key group" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueInt, "GetKeyInt", "Given a KeyValues object and a key name, return associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueFloat, "GetKeyFloat", "Given a KeyValues object and a key name, return associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueBool, "GetKeyBool", "Given a KeyValues object and a key name, return associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetKeyValueString, "GetKeyString", "Given a KeyValues object and a key name, return associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptIsKeyValueEmpty, "IsKeyEmpty", "Given a KeyValues object and a key name, return true if key name has no value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptReleaseKeyValues, "ReleaseKeyValues", "Given a root KeyValues object, release its contents" ); +#ifdef MAPBASE_VSCRIPT + DEFINE_SCRIPTFUNC_NAMED( ScriptGetName, "GetName", "Given a KeyValues object, return its name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetInt, "GetInt", "Given a KeyValues object, return its own associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFloat, "GetFloat", "Given a KeyValues object, return its own associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetString, "GetString", "Given a KeyValues object, return its own associated string value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptGetBool, "GetBool", "Given a KeyValues object, return its own associated bool value" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueInt, "SetKeyInt", "Given a KeyValues object and a key name, set associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueFloat, "SetKeyFloat", "Given a KeyValues object and a key name, set associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueBool, "SetKeyBool", "Given a KeyValues object and a key name, set associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetKeyValueString, "SetKeyString", "Given a KeyValues object and a key name, set associated string value" ); + + DEFINE_SCRIPTFUNC_NAMED( ScriptSetName, "SetName", "Given a KeyValues object, set its name" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetInt, "SetInt", "Given a KeyValues object, set its own associated integer value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetFloat, "SetFloat", "Given a KeyValues object, set its own associated float value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetBool, "SetBool", "Given a KeyValues object, set its own associated bool value" ); + DEFINE_SCRIPTFUNC_NAMED( ScriptSetString, "SetString", "Given a KeyValues object, set its own associated string value" ); +#endif +END_SCRIPTDESC(); + +HSCRIPT CScriptKeyValues::ScriptFindKey( const char *pszName ) +{ + KeyValues *pKeyValues = m_pKeyValues->FindKey(pszName); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetFirstSubKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetFirstSubKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +HSCRIPT CScriptKeyValues::ScriptGetNextKey( void ) +{ + KeyValues *pKeyValues = m_pKeyValues->GetNextKey(); + if ( pKeyValues == NULL ) + return NULL; + + CScriptKeyValues *pScriptKey = new CScriptKeyValues( pKeyValues ); + + // UNDONE: who calls ReleaseInstance on this?? + HSCRIPT hScriptInstance = g_pScriptVM->RegisterInstance( pScriptKey ); + return hScriptInstance; +} + +int CScriptKeyValues::ScriptGetKeyValueInt( const char *pszName ) +{ + int i = m_pKeyValues->GetInt( pszName ); + return i; +} + +float CScriptKeyValues::ScriptGetKeyValueFloat( const char *pszName ) +{ + float f = m_pKeyValues->GetFloat( pszName ); + return f; +} + +const char *CScriptKeyValues::ScriptGetKeyValueString( const char *pszName ) +{ + const char *psz = m_pKeyValues->GetString( pszName ); + return psz; +} + +bool CScriptKeyValues::ScriptIsKeyValueEmpty( const char *pszName ) +{ + bool b = m_pKeyValues->IsEmpty( pszName ); + return b; +} + +bool CScriptKeyValues::ScriptGetKeyValueBool( const char *pszName ) +{ + bool b = m_pKeyValues->GetBool( pszName ); + return b; +} + +void CScriptKeyValues::ScriptReleaseKeyValues( ) +{ + m_pKeyValues->deleteThis(); + m_pKeyValues = NULL; +} + +#ifdef MAPBASE_VSCRIPT +const char *CScriptKeyValues::ScriptGetName() +{ + const char *psz = m_pKeyValues->GetName(); + return psz; +} + +int CScriptKeyValues::ScriptGetInt() +{ + int i = m_pKeyValues->GetInt(); + return i; +} + +float CScriptKeyValues::ScriptGetFloat() +{ + float f = m_pKeyValues->GetFloat(); + return f; +} + +const char *CScriptKeyValues::ScriptGetString() +{ + const char *psz = m_pKeyValues->GetString(); + return psz; +} + +bool CScriptKeyValues::ScriptGetBool() +{ + bool b = m_pKeyValues->GetBool(); + return b; +} + + +void CScriptKeyValues::ScriptSetKeyValueInt( const char *pszName, int iValue ) +{ + m_pKeyValues->SetInt( pszName, iValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueFloat( const char *pszName, float flValue ) +{ + m_pKeyValues->SetFloat( pszName, flValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueString( const char *pszName, const char *pszValue ) +{ + m_pKeyValues->SetString( pszName, pszValue ); +} + +void CScriptKeyValues::ScriptSetKeyValueBool( const char *pszName, bool bValue ) +{ + m_pKeyValues->SetBool( pszName, bValue ); +} + +void CScriptKeyValues::ScriptSetName( const char *pszValue ) +{ + m_pKeyValues->SetName( pszValue ); +} + +void CScriptKeyValues::ScriptSetInt( int iValue ) +{ + m_pKeyValues->SetInt( NULL, iValue ); +} + +void CScriptKeyValues::ScriptSetFloat( float flValue ) +{ + m_pKeyValues->SetFloat( NULL, flValue ); +} + +void CScriptKeyValues::ScriptSetString( const char *pszValue ) +{ + m_pKeyValues->SetString( NULL, pszValue ); +} + +void CScriptKeyValues::ScriptSetBool( bool bValue ) +{ + m_pKeyValues->SetBool( NULL, bValue ); +} +#endif + + +// constructors +CScriptKeyValues::CScriptKeyValues( KeyValues *pKeyValues = NULL ) +{ + m_pKeyValues = pKeyValues; +} + +// destructor +CScriptKeyValues::~CScriptKeyValues( ) +{ + if (m_pKeyValues) + { + m_pKeyValues->deleteThis(); + } + m_pKeyValues = NULL; +} + + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static float Time() +{ + return gpGlobals->curtime; +} + +static float FrameTime() +{ + return gpGlobals->frametime; +} + +#ifdef MAPBASE_VSCRIPT +static int MaxPlayers() +{ + return gpGlobals->maxClients; +} + +static float IntervalPerTick() +{ + return gpGlobals->interval_per_tick; +} +#endif + +static void SendToConsole( const char *pszCommand ) +{ + CBasePlayer *pPlayer = UTIL_GetLocalPlayerOrListenServerHost(); + if ( !pPlayer ) + { + DevMsg ("Cannot execute \"%s\", no player\n", pszCommand ); + return; + } + + engine->ClientCommand( pPlayer->edict(), pszCommand ); +} + +static const char *GetMapName() +{ + return STRING( gpGlobals->mapname ); +} + +static const char *DoUniqueString( const char *pszBase ) +{ + static char szBuf[512]; + g_pScriptVM->GenerateUniqueKey( pszBase, szBuf, ARRAYSIZE(szBuf) ); + return szBuf; +} + +static void DoEntFire( const char *pszTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + const char *target = "", *action = "Use"; + variant_t value; + + target = STRING( AllocPooledString( pszTarget ) ); + + // Don't allow them to run anything on a point_servercommand unless they're the host player. Otherwise they can ent_fire + // and run any command on the server. Admittedly, they can only do the ent_fire if sv_cheats is on, but + // people complained about users resetting the rcon password if the server briefly turned on cheats like this: + // give point_servercommand + // ent_fire point_servercommand command "rcon_password mynewpassword" + if ( gpGlobals->maxClients > 1 && V_stricmp( target, "point_servercommand" ) == 0 ) + { + return; + } + + if ( *pszAction ) + { + action = STRING( AllocPooledString( pszAction ) ); + } + if ( *pszValue ) + { + value.SetString( AllocPooledString( pszValue ) ); + } + if ( delay < 0 ) + { + delay = 0; + } + + g_EventQueue.AddEvent( target, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); +} + + +bool DoIncludeScript( const char *pszScript, HSCRIPT hScope ) +{ + if ( !VScriptRunScript( pszScript, hScope, true ) ) + { + g_pScriptVM->RaiseException( CFmtStr( "Failed to include script \"%s\"", ( pszScript ) ? pszScript : "unknown" ) ); + return false; + } + return true; +} + +HSCRIPT CreateProp( const char *pszEntityName, const Vector &vOrigin, const char *pszModelName, int iAnim ) +{ + CBaseAnimating *pBaseEntity = (CBaseAnimating *)CreateEntityByName( pszEntityName ); + pBaseEntity->SetAbsOrigin( vOrigin ); + pBaseEntity->SetModel( pszModelName ); + pBaseEntity->SetPlaybackRate( 1.0f ); + + int iSequence = pBaseEntity->SelectWeightedSequence( (Activity)iAnim ); + + if ( iSequence != -1 ) + { + pBaseEntity->SetSequence( iSequence ); + } + + return ToHScript( pBaseEntity ); +} + +//-------------------------------------------------------------------------------------------------- +// Use an entity's script instance to add an entity IO event (used for firing events on unnamed entities from vscript) +//-------------------------------------------------------------------------------------------------- +static void DoEntFireByInstanceHandle( HSCRIPT hTarget, const char *pszAction, const char *pszValue, float delay, HSCRIPT hActivator, HSCRIPT hCaller ) +{ + const char *action = "Use"; + variant_t value; + + if ( *pszAction ) + { + action = STRING( AllocPooledString( pszAction ) ); + } + if ( *pszValue ) + { + value.SetString( AllocPooledString( pszValue ) ); + } + if ( delay < 0 ) + { + delay = 0; + } + + CBaseEntity* pTarget = ToEnt(hTarget); + + if ( !pTarget ) + { + Warning( "VScript error: DoEntFire was passed an invalid entity instance.\n" ); + return; + } + + g_EventQueue.AddEvent( pTarget, action, value, delay, ToEnt(hActivator), ToEnt(hCaller) ); +} + +static float ScriptTraceLine( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore ) +{ + // UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_BLOCKLOS, pLooker, COLLISION_GROUP_NONE, ptr ); + trace_t tr; + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceLine( vecStart, vecEnd, MASK_NPCWORLDSTATIC, pLooker, COLLISION_GROUP_NONE, &tr); + if (tr.fractionleftsolid && tr.startsolid) + { + return 1.0 - tr.fractionleftsolid; + } + else + { + return tr.fraction; + } +} + +bool VScriptServerInit() +{ + VMPROF_START + + if( scriptmanager != NULL ) + { + ScriptLanguage_t scriptLanguage = SL_DEFAULT; + + char const *pszScriptLanguage; +#ifdef MAPBASE_VSCRIPT + if (GetWorldEntity()->GetScriptLanguage() != SL_NONE) + { + // Allow world entity to override script language + scriptLanguage = GetWorldEntity()->GetScriptLanguage(); + } + else +#endif + if ( CommandLine()->CheckParm( "-scriptlang", &pszScriptLanguage ) ) + { + if( !Q_stricmp(pszScriptLanguage, "gamemonkey") ) + { + scriptLanguage = SL_GAMEMONKEY; + } + else if( !Q_stricmp(pszScriptLanguage, "squirrel") ) + { + scriptLanguage = SL_SQUIRREL; + } + else if( !Q_stricmp(pszScriptLanguage, "python") ) + { + scriptLanguage = SL_PYTHON; + } +#ifdef MAPBASE_VSCRIPT + else if( !Q_stricmp(pszScriptLanguage, "lua") ) + { + scriptLanguage = SL_LUA; + } +#endif + else + { + DevWarning("-server_script does not recognize a language named '%s'. virtual machine did NOT start.\n", pszScriptLanguage ); + scriptLanguage = SL_NONE; + } + + } + if( scriptLanguage != SL_NONE ) + { + if ( g_pScriptVM == NULL ) + g_pScriptVM = scriptmanager->CreateVM( scriptLanguage ); + + if( g_pScriptVM ) + { + Log( "VSCRIPT: Started VScript virtual machine using script language '%s'\n", g_pScriptVM->GetLanguageName() ); + ScriptRegisterFunctionNamed( g_pScriptVM, UTIL_ShowMessageAll, "ShowMessage", "Print a hud message on all clients" ); + + ScriptRegisterFunction( g_pScriptVM, SendToConsole, "Send a string to the console as a command" ); + ScriptRegisterFunction( g_pScriptVM, GetMapName, "Get the name of the map."); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLine, "TraceLine", "given 2 points & ent to ignore, return fraction along line that hits world or models" ); + + ScriptRegisterFunction( g_pScriptVM, Time, "Get the current server time" ); + ScriptRegisterFunction( g_pScriptVM, FrameTime, "Get the time spent on the server in the last frame" ); +#ifdef MAPBASE_VSCRIPT + ScriptRegisterFunction( g_pScriptVM, MaxPlayers, "Get the maximum number of players allowed on this server" ); + ScriptRegisterFunction( g_pScriptVM, IntervalPerTick, "Get the interval used between each tick" ); + ScriptRegisterFunction( g_pScriptVM, DoEntFire, SCRIPT_ALIAS( "EntFire", "Generate an entity i/o event" ) ); + ScriptRegisterFunction( g_pScriptVM, DoEntFireByInstanceHandle, SCRIPT_ALIAS( "EntFireByHandle", "Generate an entity i/o event. First parameter is an entity instance." ) ); +#else + ScriptRegisterFunction( g_pScriptVM, DoEntFire, SCRIPT_ALIAS( "EntFire", "Generate and entity i/o event" ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, DoEntFireByInstanceHandle, "EntFireByHandle", "Generate and entity i/o event. First parameter is an entity instance." ); +#endif + ScriptRegisterFunction( g_pScriptVM, DoUniqueString, SCRIPT_ALIAS( "UniqueString", "Generate a string guaranteed to be unique across the life of the script VM, with an optional root string. Useful for adding data to tables when not sure what keys are already in use in that table." ) ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCreateSceneEntity, "CreateSceneEntity", "Create a scene entity to play the specified scene." ); + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Box, "DebugDrawBox", "Draw a debug overlay box" ); + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Line, "DebugDrawLine", "Draw a debug overlay box" ); + ScriptRegisterFunction( g_pScriptVM, DoIncludeScript, "Execute a script (internal)" ); + ScriptRegisterFunction( g_pScriptVM, CreateProp, "Create a physics prop" ); + + + if ( GameRules() ) + { + GameRules()->RegisterScriptFunctions(); + } + + g_pScriptVM->RegisterInstance( &g_ScriptEntityIterator, "Entities" ); + +#ifdef MAPBASE_VSCRIPT + IGameSystem::RegisterVScriptAllSystems(); + + RegisterSharedScriptFunctions(); +#endif + + if (scriptLanguage == SL_SQUIRREL) + { + g_pScriptVM->Run( g_Script_vscript_server ); + } + + VScriptRunScript( "mapspawn", false ); + + VMPROF_SHOW( pszScriptLanguage, "virtual machine startup" ); + + return true; + } + else + { + DevWarning("VM Did not start!\n"); + } + } + } + else + { + Log( "\nVSCRIPT: Scripting is disabled.\n" ); + } + g_pScriptVM = NULL; + return false; +} + +void VScriptServerTerm() +{ + if( g_pScriptVM != NULL ) + { + if( g_pScriptVM ) + { + scriptmanager->DestroyVM( g_pScriptVM ); + g_pScriptVM = NULL; + } + } +} + + +bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + HSCRIPT hReplaceClosuresFunc = g_pScriptVM->LookupFunction( "__ReplaceClosures" ); + if ( !hReplaceClosuresFunc ) + { + return false; + } + HSCRIPT hNewScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + if ( !hNewScript ) + { + return false; + } + + g_pScriptVM->Call( hReplaceClosuresFunc, NULL, true, NULL, hNewScript, hScope ); + return true; +} + +CON_COMMAND( script_reload_code, "Execute a vscript file, replacing existing functions with the functions in the run script" ) +{ + if ( !*args[1] ) + { + Warning( "No script specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + VScriptServerReplaceClosures( args[1], NULL, true ); +} + +CON_COMMAND( script_reload_entity_code, "Execute all of this entity's VScripts, replacing existing functions with the functions in the run scripts" ) +{ + extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ); + + const char *pszTarget = ""; + if ( *args[1] ) + { + pszTarget = args[1]; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CBaseEntity *pEntity = NULL; + while ( (pEntity = GetNextCommandEntity( pPlayer, pszTarget, pEntity )) != NULL ) + { + if ( pEntity->m_ScriptScope.IsInitialized() && pEntity->m_iszVScripts != NULL_STRING ) + { + char szScriptsList[255]; + Q_strcpy( szScriptsList, STRING(pEntity->m_iszVScripts) ); + CUtlStringList szScripts; + V_SplitString( szScriptsList, " ", szScripts); + + for( int i = 0 ; i < szScripts.Count() ; i++ ) + { + VScriptServerReplaceClosures( szScripts[i], pEntity->m_ScriptScope, true ); + } + } + } +} + +CON_COMMAND( script_reload_think, "Execute an activation script, replacing existing functions with the functions in the run script" ) +{ + extern CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ); + + const char *pszTarget = ""; + if ( *args[1] ) + { + pszTarget = args[1]; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + CBasePlayer *pPlayer = UTIL_GetCommandClient(); + if ( !pPlayer ) + return; + + CBaseEntity *pEntity = NULL; + while ( (pEntity = GetNextCommandEntity( pPlayer, pszTarget, pEntity )) != NULL ) + { + if ( pEntity->m_ScriptScope.IsInitialized() && pEntity->m_iszScriptThinkFunction != NULL_STRING ) + { + VScriptServerReplaceClosures( STRING(pEntity->m_iszScriptThinkFunction), pEntity->m_ScriptScope, true ); + } + } +} + +class CVScriptGameSystem : public CAutoGameSystemPerFrame +{ +public: + // Inherited from IAutoServerSystem + virtual void LevelInitPreEntity( void ) + { + m_bAllowEntityCreationInScripts = true; + VScriptServerInit(); + } + + virtual void LevelInitPostEntity( void ) + { + m_bAllowEntityCreationInScripts = false; + } + + virtual void LevelShutdownPostEntity( void ) + { + VScriptServerTerm(); + } + + virtual void FrameUpdatePostEntityThink() + { + if ( g_pScriptVM ) + g_pScriptVM->Frame( gpGlobals->frametime ); + } + + bool m_bAllowEntityCreationInScripts; +}; + +CVScriptGameSystem g_VScriptGameSystem; + +#ifdef MAPBASE_VSCRIPT +ConVar script_allow_entity_creation_midgame( "script_allow_entity_creation_midgame", "1", FCVAR_NOT_CONNECTED, "Allows VScript files to create entities mid-game, as opposed to only creating entities on startup." ); +#endif + +bool IsEntityCreationAllowedInScripts( void ) +{ +#ifdef MAPBASE_VSCRIPT + if (script_allow_entity_creation_midgame.GetBool()) + return true; +#endif + + return g_VScriptGameSystem.m_bAllowEntityCreationInScripts; +} + +static short VSCRIPT_SERVER_SAVE_RESTORE_VERSION = 2; + + +//----------------------------------------------------------------------------- + +class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler +{ +public: + CVScriptSaveRestoreBlockHandler() : + m_InstanceMap( DefLessFunc(const char *) ) + { + } + const char *GetBlockName() + { + return "VScriptServer"; + } + + //--------------------------------- + + void Save( ISave *pSave ) + { + pSave->StartBlock(); + + int temp = g_pScriptVM != NULL; + pSave->WriteInt( &temp ); + if ( g_pScriptVM ) + { + temp = g_pScriptVM->GetLanguage(); + pSave->WriteInt( &temp ); + CUtlBuffer buffer; + g_pScriptVM->WriteState( &buffer ); + temp = buffer.TellPut(); + pSave->WriteInt( &temp ); + if ( temp > 0 ) + { + pSave->WriteData( (const char *)buffer.Base(), temp ); + } + } + + pSave->EndBlock(); + } + + //--------------------------------- + + void WriteSaveHeaders( ISave *pSave ) + { + pSave->WriteShort( &VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void ReadRestoreHeaders( IRestore *pRestore ) + { + // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so. + short version; + pRestore->ReadShort( &version ); + m_fDoLoad = ( version == VSCRIPT_SERVER_SAVE_RESTORE_VERSION ); + } + + //--------------------------------- + + void Restore( IRestore *pRestore, bool createPlayers ) + { + if ( !m_fDoLoad && g_pScriptVM ) + { + return; + } + CBaseEntity *pEnt = gEntList.FirstEnt(); + while ( pEnt ) + { + if ( pEnt->m_iszScriptId != NULL_STRING ) + { + g_pScriptVM->RegisterClass( pEnt->GetScriptDesc() ); + m_InstanceMap.Insert( STRING( pEnt->m_iszScriptId ), pEnt ); + } + pEnt = gEntList.NextEnt( pEnt ); + } + + pRestore->StartBlock(); + if ( pRestore->ReadInt() && pRestore->ReadInt() == g_pScriptVM->GetLanguage() ) + { + int nBytes = pRestore->ReadInt(); + if ( nBytes > 0 ) + { + CUtlBuffer buffer; + buffer.EnsureCapacity( nBytes ); + pRestore->ReadData( (char *)buffer.AccessForDirectRead( nBytes ), nBytes, 0 ); + g_pScriptVM->ReadState( &buffer ); + } + } + pRestore->EndBlock(); + } + + void PostRestore( void ) + { + for ( int i = m_InstanceMap.FirstInorder(); i != m_InstanceMap.InvalidIndex(); i = m_InstanceMap.NextInorder( i ) ) + { + CBaseEntity *pEnt = m_InstanceMap[i]; + if ( pEnt->m_hScriptInstance ) + { + ScriptVariant_t variant; + if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) + { + pEnt->m_ScriptScope.Init( variant.m_hScript, false ); + pEnt->RunPrecacheScripts(); + } + } + else + { + // Script system probably has no internal references + pEnt->m_iszScriptId = NULL_STRING; + } + } + m_InstanceMap.Purge(); + } + + + CUtlMap m_InstanceMap; + +private: + bool m_fDoLoad; +}; + +//----------------------------------------------------------------------------- + +CVScriptSaveRestoreBlockHandler g_VScriptSaveRestoreBlockHandler; + +//------------------------------------- + +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler() +{ + return &g_VScriptSaveRestoreBlockHandler; +} + +//----------------------------------------------------------------------------- + +bool CBaseEntityScriptInstanceHelper::ToString( void *p, char *pBuf, int bufSize ) +{ + CBaseEntity *pEntity = (CBaseEntity *)p; + if ( pEntity->GetEntityName() != NULL_STRING ) + { + V_snprintf( pBuf, bufSize, "([%d] %s: %s)", pEntity->entindex(), STRING(pEntity->m_iClassname), STRING( pEntity->GetEntityName() ) ); + } + else + { + V_snprintf( pBuf, bufSize, "([%d] %s)", pEntity->entindex(), STRING(pEntity->m_iClassname) ); + } + return true; +} + +void *CBaseEntityScriptInstanceHelper::BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) +{ + int iEntity = g_VScriptSaveRestoreBlockHandler.m_InstanceMap.Find( pszId ); + if ( iEntity != g_VScriptSaveRestoreBlockHandler.m_InstanceMap.InvalidIndex() ) + { + CBaseEntity *pEnt = g_VScriptSaveRestoreBlockHandler.m_InstanceMap[iEntity]; + pEnt->m_hScriptInstance = hInstance; + return pEnt; + } + return NULL; +} + + +CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + + diff --git a/sp/src/game/server/vscript_server.h b/sp/src/game/server/vscript_server.h new file mode 100644 index 0000000000..f2cd0ab310 --- /dev/null +++ b/sp/src/game/server/vscript_server.h @@ -0,0 +1,74 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SERVER_H +#define VSCRIPT_SERVER_H + +#include "vscript/ivscript.h" +#include "tier1/KeyValues.h" +#include "vscript_shared.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +class ISaveRestoreBlockHandler; + +bool VScriptServerReplaceClosures( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +ISaveRestoreBlockHandler *GetVScriptSaveRestoreBlockHandler(); + + +class CBaseEntityScriptInstanceHelper : public IScriptInstanceHelper +{ + bool ToString( void *p, char *pBuf, int bufSize ); + void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ); +}; + +extern CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +// ---------------------------------------------------------------------------- +// KeyValues access +// ---------------------------------------------------------------------------- +class CScriptKeyValues +{ +public: + CScriptKeyValues( KeyValues *pKeyValues ); + ~CScriptKeyValues( ); + + HSCRIPT ScriptFindKey( const char *pszName ); + HSCRIPT ScriptGetFirstSubKey( void ); + HSCRIPT ScriptGetNextKey( void ); + int ScriptGetKeyValueInt( const char *pszName ); + float ScriptGetKeyValueFloat( const char *pszName ); + const char *ScriptGetKeyValueString( const char *pszName ); + bool ScriptIsKeyValueEmpty( const char *pszName ); + bool ScriptGetKeyValueBool( const char *pszName ); + void ScriptReleaseKeyValues( ); +#ifdef MAPBASE_VSCRIPT + const char *ScriptGetName(); + int ScriptGetInt(); + float ScriptGetFloat(); + const char *ScriptGetString(); + bool ScriptGetBool(); + + void ScriptSetKeyValueInt( const char *pszName, int iValue ); + void ScriptSetKeyValueFloat( const char *pszName, float flValue ); + void ScriptSetKeyValueString( const char *pszName, const char *pszValue ); + void ScriptSetKeyValueBool( const char *pszName, bool bValue ); + void ScriptSetName( const char *pszValue ); + void ScriptSetInt( int iValue ); + void ScriptSetFloat( float flValue ); + void ScriptSetString( const char *pszValue ); + void ScriptSetBool( bool bValue ); +#endif + + KeyValues *m_pKeyValues; // actual KeyValue entity +}; + +#endif // VSCRIPT_SERVER_H diff --git a/sp/src/game/server/vscript_server.nut b/sp/src/game/server/vscript_server.nut new file mode 100644 index 0000000000..b4e5804b7a --- /dev/null +++ b/sp/src/game/server/vscript_server.nut @@ -0,0 +1,155 @@ +static char g_Script_vscript_server[] = R"vscript( +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +function UniqueString( string = "" ) +{ + return DoUniqueString( string.tostring() ); +} + +function EntFire( target, action, value = null, delay = 0.0, activator = null, caller = null ) +{ + if ( !value ) + { + value = ""; + } + + if ( "self" in this ) + { + if ( !caller ) + { + caller = self; + } + + if ( !activator ) + { + activator = self; + } + } + + DoEntFire( target.tostring(), action.tostring(), value.tostring(), delay, activator, caller ); +} + +function EntFireByHandle( target, action, value = null, delay = 0.0, activator = null, caller = null ) +{ + if ( !value ) + { + value = ""; + } + + if ( "self" in this ) + { + if ( !caller ) + { + caller = self; + } + + if ( !activator ) + { + activator = self; + } + } + + DoEntFireByInstanceHandle( target, action.tostring(), value.tostring(), delay, activator, caller ); +} + +function __ReplaceClosures( script, scope ) +{ + if ( !scope ) + { + scope = getroottable(); + } + + local tempParent = { getroottable = function() { return null; } }; + local temp = { runscript = script }; + temp.set_delegate(tempParent); + + temp.runscript() + foreach( key,val in temp ) + { + if ( typeof(val) == "function" && key != "runscript" ) + { + printl( " Replacing " + key ); + scope[key] <- val; + } + } +} + +__OutputsPattern <- regexp("^On.*Output$"); + +function ConnectOutputs( table ) +{ + const nCharsToStrip = 6; + foreach( key, val in table ) + { + if ( typeof( val ) == "function" && __OutputsPattern.match( key ) ) + { + //printl(key.slice( 0, nCharsToStrip ) ); + table.self.ConnectOutput( key.slice( 0, key.len() - nCharsToStrip ), key ); + } + } +} + +function IncludeScript( name, scope = null ) +{ + if ( scope == null ) + { + scope = this; + } + return ::DoIncludeScript( name, scope ); +} + +//--------------------------------------------------------- +// Text dump this scope's contents to the console. +//--------------------------------------------------------- +function __DumpScope( depth, table ) +{ + local indent=function( count ) + { + local i; + for( i = 0 ; i < count ; i++ ) + { + print(" "); + } + } + + foreach(key, value in table) + { + indent(depth); + print( key ); + switch (type(value)) + { + case "table": + print("(TABLE)\n"); + indent(depth); + print("{\n"); + __DumpScope( depth + 1, value); + indent(depth); + print("}"); + break; + case "array": + print("(ARRAY)\n"); + indent(depth); + print("[\n") + __DumpScope( depth + 1, value); + indent(depth); + print("]"); + break; + case "string": + print(" = \""); + print(value); + print("\""); + break; + default: + print(" = "); + print(value); + break; + } + print("\n"); + } +} + +)vscript"; \ No newline at end of file diff --git a/sp/src/game/server/world.cpp b/sp/src/game/server/world.cpp index 460809cd45..211e2bdac2 100644 --- a/sp/src/game/server/world.cpp +++ b/sp/src/game/server/world.cpp @@ -393,6 +393,9 @@ BEGIN_DATADESC( CWorld ) DEFINE_KEYFIELD( m_flMaxPropScreenSpaceWidth, FIELD_FLOAT, "maxpropscreenwidth" ), DEFINE_KEYFIELD( m_flMinPropScreenSpaceWidth, FIELD_FLOAT, "minpropscreenwidth" ), DEFINE_KEYFIELD( m_iszDetailSpriteMaterial, FIELD_STRING, "detailmaterial" ), +#ifdef MAPBASE_VSCRIPT + DEFINE_KEYFIELD( m_iScriptLanguage, FIELD_INTEGER, "vscriptlanguage" ), +#endif DEFINE_KEYFIELD( m_bColdWorld, FIELD_BOOLEAN, "coldworld" ), #ifdef MAPBASE @@ -417,6 +420,9 @@ IMPLEMENT_SERVERCLASS_ST(CWorld, DT_WORLD) #ifdef MAPBASE SendPropStringT (SENDINFO(m_iszChapterTitle) ), #endif +#ifdef MAPBASE_VSCRIPT + SendPropInt (SENDINFO(m_iScriptLanguage), 2, SPROP_UNSIGNED ), +#endif END_SEND_TABLE() // @@ -476,6 +482,10 @@ CWorld::CWorld( ) SetSolid( SOLID_BSP ); SetMoveType( MOVETYPE_NONE ); +#ifdef MAPBASE_VSCRIPT + m_iScriptLanguage = SL_NONE; +#endif + m_bColdWorld = false; } diff --git a/sp/src/game/server/world.h b/sp/src/game/server/world.h index 3737eb4935..4fd82d2e4b 100644 --- a/sp/src/game/server/world.h +++ b/sp/src/game/server/world.h @@ -61,6 +61,10 @@ class CWorld : public CBaseEntity void InputSetChapterTitle( inputdata_t &inputdata ); #endif +#ifdef MAPBASE_VSCRIPT + ScriptLanguage_t GetScriptLanguage() { return (ScriptLanguage_t)(m_iScriptLanguage.Get()); } +#endif + private: DECLARE_DATADESC(); @@ -84,6 +88,10 @@ class CWorld : public CBaseEntity CNetworkVar( float, m_flMaxPropScreenSpaceWidth ); CNetworkVar( string_t, m_iszDetailSpriteMaterial ); +#ifdef MAPBASE_VSCRIPT + CNetworkVar( int, m_iScriptLanguage ); +#endif + // start flags CNetworkVar( bool, m_bStartDark ); CNetworkVar( bool, m_bColdWorld ); diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 09a568f8a5..19b37f113e 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -1217,6 +1217,19 @@ void CBaseEntity::EmitSound( const char *soundname, HSOUNDSCRIPTHANDLE& handle, EmitSound( filter, entindex(), params, handle ); } +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) +void CBaseEntity::ScriptEmitSound( const char *soundname ) +{ + EmitSound( soundname ); +} + +float CBaseEntity::ScriptSoundDuration( const char *soundname, const char *actormodel ) +{ + float duration = CBaseEntity::GetSoundDuration( soundname, actormodel ); + return duration; +} +#endif // !CLIENT + //----------------------------------------------------------------------------- // Purpose: // Input : filter - @@ -1486,6 +1499,14 @@ HSOUNDSCRIPTHANDLE CBaseEntity::PrecacheScriptSound( const char *soundname ) #endif } +#if !defined ( CLIENT_DLL ) || defined( MAPBASE_VSCRIPT ) +// Same as server version of above, but signiture changed so it can be deduced by the macros +void CBaseEntity::VScriptPrecacheScriptSound(const char* soundname) +{ + g_SoundEmitterSystem.PrecacheScriptSound(soundname); +} +#endif // !CLIENT_DLL + void CBaseEntity::PrefetchScriptSound( const char *soundname ) { g_SoundEmitterSystem.PrefetchScriptSound( soundname ); diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 8287cde0fe..a5e4c4d630 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -1801,6 +1801,22 @@ void CBaseCombatWeapon::InputHideWeapon( inputdata_t &inputdata ) SetWeaponVisible( false ); } } + +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CBaseCombatWeapon::ScriptGetPrimaryAmmoType() +{ + return GetPrimaryAmmoType() <= GetAmmoDef()->m_nAmmoIndex ? GetAmmoDef()->m_AmmoType[GetPrimaryAmmoType()].pName : NULL; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CBaseCombatWeapon::ScriptGetSecondaryAmmoType() +{ + return GetSecondaryAmmoType() <= GetAmmoDef()->m_nAmmoIndex ? GetAmmoDef()->m_AmmoType[GetSecondaryAmmoType()].pName : NULL; +} +#endif #endif //----------------------------------------------------------------------------- @@ -2871,6 +2887,48 @@ END_PREDICTION_DATA() // Special hack since we're aliasing the name C_BaseCombatWeapon with a macro on the client IMPLEMENT_NETWORKCLASS_ALIASED( BaseCombatWeapon, DT_BaseCombatWeapon ) +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, CBaseAnimating, "The base class for all equippable weapons." ) + + DEFINE_SCRIPTFUNC( Clip1, "Get the weapon's current primary ammo." ) + DEFINE_SCRIPTFUNC( Clip2, "Get the weapon's current secondary ammo." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetClip1, "SetClip1", "Set the weapon's current primary ammo." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetClip2, "SetClip2", "Set the weapon's current secondary ammo." ) + DEFINE_SCRIPTFUNC( GetMaxClip1, "Get the weapon's maximum primary ammo." ) + DEFINE_SCRIPTFUNC( GetMaxClip2, "Get the weapon's maximum secondary ammo." ) + DEFINE_SCRIPTFUNC( GetDefaultClip1, "Get the weapon's default primary ammo." ) + DEFINE_SCRIPTFUNC( GetDefaultClip2, "Get the weapon's default secondary ammo." ) + + DEFINE_SCRIPTFUNC( HasAnyAmmo, "Check if the weapon currently has ammo or doesn't need ammo." ) + DEFINE_SCRIPTFUNC( HasPrimaryAmmo, "Check if the weapon currently has ammo or doesn't need primary ammo." ) + DEFINE_SCRIPTFUNC( HasSecondaryAmmo, "Check if the weapon currently has ammo or doesn't need secondary ammo." ) + DEFINE_SCRIPTFUNC( UsesPrimaryAmmo, "Check if the weapon uses primary ammo." ) + DEFINE_SCRIPTFUNC( UsesSecondaryAmmo, "Check if the weapon uses secondary ammo." ) + DEFINE_SCRIPTFUNC( GiveDefaultAmmo, "Fill the weapon back up to default ammo." ) + + DEFINE_SCRIPTFUNC( UsesClipsForAmmo1, "Check if the weapon uses clips for primary ammo." ) + DEFINE_SCRIPTFUNC( UsesClipsForAmmo2, "Check if the weapon uses clips for secondary ammo." ) + +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC_NAMED( ScriptGetPrimaryAmmoType, "GetPrimaryAmmoType", "Get the weapon's primary ammo type." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetSecondaryAmmoType, "GetSecondaryAmmoType", "Get the weapon's secondary ammo type." ) +#endif + + DEFINE_SCRIPTFUNC( GetSubType, "Get the weapon's subtype." ) + DEFINE_SCRIPTFUNC( SetSubType, "Set the weapon's subtype." ) + + DEFINE_SCRIPTFUNC( GetFireRate, "Get the weapon's firing rate." ) + + DEFINE_SCRIPTFUNC( GetWorldModel, "Get the weapon's world model." ) + DEFINE_SCRIPTFUNC( GetViewModel, "Get the weapon's view model." ) + + DEFINE_SCRIPTFUNC( GetWeight, "Get the weapon's weight." ) + + DEFINE_SCRIPTFUNC( CanBePickedUpByNPCs, "Check if the weapon can be picked up by NPCs." ) + +END_SCRIPTDESC(); +#endif + #if !defined( CLIENT_DLL ) //----------------------------------------------------------------------------- // Purpose: Save Data for Base Weapon object diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index af5f37faac..c5f4e863ec 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -196,6 +196,9 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM DECLARE_CLASS( CBaseCombatWeapon, BASECOMBATWEAPON_DERIVED_FROM ); DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif CBaseCombatWeapon(); virtual ~CBaseCombatWeapon(); @@ -446,6 +449,12 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM virtual void Activate( void ); virtual bool ShouldUseLargeViewModelVROverride() { return false; } + +#ifdef MAPBASE_VSCRIPT + void ScriptSetClip1( int ammo ) { m_iClip1 = ammo; } + void ScriptSetClip2( int ammo ) { m_iClip2 = ammo; } +#endif + public: // Server Only Methods #if !defined( CLIENT_DLL ) @@ -510,6 +519,11 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM virtual CDmgAccumulator *GetDmgAccumulator( void ) { return NULL; } +#ifdef MAPBASE_VSCRIPT + const char* ScriptGetPrimaryAmmoType(); + const char* ScriptGetSecondaryAmmoType(); +#endif + // Client only methods #else diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index de825fe1dd..643b650ae1 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -1599,6 +1599,27 @@ typedef CTraceFilterSimpleList CBulletsTraceFilter; void CBaseEntity::FireBullets( const FireBulletsInfo_t &info ) { +#if defined(MAPBASE_VSCRIPT) && defined(GAME_DLL) + if (m_ScriptScope.IsInitialized()) + { + CFireBulletsInfoAccessor pInfo( const_cast(&info) ); + HSCRIPT hInfo = g_pScriptVM->RegisterInstance( &pInfo ); + + g_pScriptVM->SetValue( "info", hInfo ); + + ScriptVariant_t functionReturn; + if ( CallScriptFunction( "FireBullets", &functionReturn ) ) + { + if (!functionReturn.m_bool) + return; + } + + g_pScriptVM->RemoveInstance( hInfo ); + + g_pScriptVM->ClearValue( "info" ); + } +#endif + static int tracerCount; trace_t tr; CAmmoDef* pAmmoDef = GetAmmoDef(); diff --git a/sp/src/game/shared/baseentity_shared.h b/sp/src/game/shared/baseentity_shared.h index b5c95ba660..85a0ffd89a 100644 --- a/sp/src/game/shared/baseentity_shared.h +++ b/sp/src/game/shared/baseentity_shared.h @@ -68,6 +68,9 @@ enum InvalidatePhysicsBits_t #endif +#include "vscript/ivscript.h" +#include "vscript_shared.h" + #if !defined( NO_ENTITY_PREDICTION ) // CBaseEntity inlines inline bool CBaseEntity::IsPlayerSimulated( void ) const @@ -247,6 +250,17 @@ inline bool CBaseEntity::IsEffectActive( int nEffects ) const return (m_fEffects & nEffects) != 0; } +inline HSCRIPT ToHScript(CBaseEntity* pEnt) +{ + return (pEnt) ? pEnt->GetScriptInstance() : NULL; +} + +template <> ScriptClassDesc_t* GetScriptDesc(CBaseEntity*); +inline CBaseEntity* ToEnt(HSCRIPT hScript) +{ + return (hScript) ? (CBaseEntity*)g_pScriptVM->GetInstanceValue(hScript, GetScriptDescForClass(CBaseEntity)) : NULL; +} + // Shared EntityMessage between game and client .dlls #define BASEENTITY_MSG_REMOVE_DECALS 1 diff --git a/sp/src/game/shared/choreoevent.cpp b/sp/src/game/shared/choreoevent.cpp index 50755f048e..1623a37d42 100644 --- a/sp/src/game/shared/choreoevent.cpp +++ b/sp/src/game/shared/choreoevent.cpp @@ -2064,6 +2064,8 @@ static EventNameMap_t g_NameMap[] = { CChoreoEvent::STOPPOINT, "stoppoint" }, { CChoreoEvent::PERMIT_RESPONSES, "permitresponses" }, { CChoreoEvent::GENERIC, "generic" }, + { CChoreoEvent::CAMERA, "camera" }, + { CChoreoEvent::SCRIPT, "script" }, }; //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/choreoevent.h b/sp/src/game/shared/choreoevent.h index ad4828d3c0..64db52afd8 100644 --- a/sp/src/game/shared/choreoevent.h +++ b/sp/src/game/shared/choreoevent.h @@ -307,6 +307,12 @@ class CChoreoEvent : public ICurveDataAccessor // A string passed to the game code for interpretation GENERIC, + // Camera control + CAMERA, + + // Script function call + SCRIPT, + // THIS MUST BE LAST!!! NUM_TYPES, } EVENTTYPE; diff --git a/sp/src/game/shared/gamerules.h b/sp/src/game/shared/gamerules.h index 58c2907755..384d5b4ec1 100644 --- a/sp/src/game/shared/gamerules.h +++ b/sp/src/game/shared/gamerules.h @@ -173,6 +173,8 @@ abstract_class CGameRules : public CAutoGameSystemPerFrame virtual bool InRoundRestart( void ) { return false; } + virtual void RegisterScriptFunctions( void ){ }; + //Allow thirdperson camera. virtual bool AllowThirdPersonCamera( void ) { return false; } diff --git a/sp/src/game/shared/hl2/hl2_gamerules.h b/sp/src/game/shared/hl2/hl2_gamerules.h index 1c9975b942..26d628080a 100644 --- a/sp/src/game/shared/hl2/hl2_gamerules.h +++ b/sp/src/game/shared/hl2/hl2_gamerules.h @@ -79,6 +79,10 @@ class CHalfLife2 : public CSingleplayRules virtual void LevelInitPreEntity(); #endif +#ifdef MAPBASE_VSCRIPT + virtual void RegisterScriptFunctions( void ); +#endif + private: // Rules change for the mega physgun CNetworkVar( bool, m_bMegaPhysgun ); diff --git a/sp/src/game/shared/igamesystem.cpp b/sp/src/game/shared/igamesystem.cpp index 6a4d7f4f27..19cbc78cbc 100644 --- a/sp/src/game/shared/igamesystem.cpp +++ b/sp/src/game/shared/igamesystem.cpp @@ -344,6 +344,15 @@ void IGameSystem::PreClientUpdateAllSystems() #endif +#ifdef MAPBASE_VSCRIPT + +void IGameSystem::RegisterVScriptAllSystems() +{ + InvokeMethod( &IGameSystem::RegisterVScript ); +} + +#endif + //----------------------------------------------------------------------------- // Invokes a method on all installed game systems in proper order diff --git a/sp/src/game/shared/igamesystem.h b/sp/src/game/shared/igamesystem.h index 6dc9835084..85621924c3 100644 --- a/sp/src/game/shared/igamesystem.h +++ b/sp/src/game/shared/igamesystem.h @@ -98,6 +98,13 @@ abstract_class IGameSystem static CBasePlayer *RunCommandPlayer(); static CUserCmd *RunCommandUserCmd(); #endif + +#ifdef MAPBASE_VSCRIPT + // This should be abstract, but there's a lot of systems which derive from + // this interface that would need to have this declared + virtual void RegisterVScript() { ; } + static void RegisterVScriptAllSystems(); +#endif }; class IGameSystemPerFrame : public IGameSystem diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 31523c1b25..dcf1296245 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -244,7 +244,7 @@ class CMapbaseSystem : public CAutoGameSystem pKV->deleteThis(); } - void AddManifestFile( const char *file, bool bDontStore = false ) + void AddManifestFile( const char *file ) { KeyValues *pKV = new KeyValues(file); if ( !pKV->LoadFromFile( filesystem, file ) ) @@ -364,6 +364,24 @@ class CMapbaseSystem : public CAutoGameSystem } } } + +#ifdef MAPBASE_VSCRIPT + void ScriptAddManifestFile( const char *szScript ) { AddManifestFile( szScript ); } + + void LoadSoundscriptFile( const char *szScript ) { LoadFromValue(szScript, MANIFEST_SOUNDSCRIPTS, false); } +#ifndef CLIENT_DLL + void LoadTalkerFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_TALKER, false ); } + void LoadActbusyFile( const char *szScript ) { LoadFromValue( szScript, MANIFEST_ACTBUSY, false ); } +#endif + + const char *GetModName() { return g_iszGameName; } + bool IsCoreMapbase() { return g_bMapbaseCore; } + + virtual void RegisterVScript() + { + g_pScriptVM->RegisterInstance( this, "Mapbase" ); + } +#endif }; CMapbaseSystem g_MapbaseSystem; @@ -374,6 +392,19 @@ BEGIN_DATADESC_NO_BASE( CMapbaseSystem ) END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CMapbaseSystem, SCRIPT_SINGLETON "All-purpose Mapbase system primarily used for map-specific files." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptAddManifestFile, "AddManifestFile", "Loads a manifest file." ) + DEFINE_SCRIPTFUNC( LoadSoundscriptFile, "Loads a custom soundscript file." ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( LoadTalkerFile, "Loads a custom talker file." ) + DEFINE_SCRIPTFUNC( LoadActbusyFile, "Loads a custom actbusy file." ) +#endif + DEFINE_SCRIPTFUNC( GetModName, "Gets the name of the mod. This is the name which shows up on Steam, RPC, etc." ) + DEFINE_SCRIPTFUNC( IsCoreMapbase, "Indicates whether this is one of the original Mapbase mods or just a separate mod using its code." ) +END_SCRIPTDESC(); +#endif + #ifdef GAME_DLL static CUtlVector g_MapbaseChapterMaps; static CUtlVector g_MapbaseChapterList; @@ -437,7 +468,7 @@ ThreeState_t Flashlight_GetLegacyVersionKey() static void CC_Mapbase_LoadManifestFile( const CCommand& args ) { - g_MapbaseSystem.AddManifestFile(args[1], args[2]); + g_MapbaseSystem.AddManifestFile(args[1]); } static ConCommand mapbase_loadmanifestfile("mapbase_loadmanifestfile", CC_Mapbase_LoadManifestFile, "Loads a Mapbase manifest file. If you don't want this to be saved and found when reloaded, type a '1' after the file path." ); diff --git a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp new file mode 100644 index 0000000000..92aa86aee8 --- /dev/null +++ b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp @@ -0,0 +1,68 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: VScript functions for Half-Life 2. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +#include "hl2_gamerules.h" +#ifndef CLIENT_DLL +#include "eventqueue.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +#ifndef CLIENT_DLL +extern CBaseEntity *CreatePlayerLoadSave( Vector vOrigin, float flDuration, float flHoldTime, float flLoadTime ); + +HSCRIPT ScriptGameOver( const char *pszMessage, float flDelay, float flFadeTime, float flLoadTime, int r, int g, int b ) +{ + CBaseEntity *pPlayer = AI_GetSinglePlayer(); + if (pPlayer) + { + UTIL_ShowMessage( pszMessage, ToBasePlayer( pPlayer ) ); + ToBasePlayer( pPlayer )->NotifySinglePlayerGameEnding(); + } + else + { + // TODO: How should MP handle this? + return NULL; + } + + CBaseEntity *pReload = CreatePlayerLoadSave( vec3_origin, flFadeTime, flLoadTime + 1.0f, flLoadTime ); + if (pReload) + { + pReload->SetRenderColor( r, g, b, 255 ); + g_EventQueue.AddEvent( pReload, "Reload", flDelay, pReload, pReload ); + } + + return ToHScript( pReload ); +} + +bool ScriptMegaPhyscannonActive() +{ + return HL2GameRules()->MegaPhyscannonActive(); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Returns how much damage the given ammo type should do to the victim +// when fired by the attacker. +// Input : pAttacker - Dude what shot the gun. +// pVictim - Dude what done got shot. +// nAmmoType - What been shot out. +// Output : How much hurt to put on dude what done got shot (pVictim). +//----------------------------------------------------------------------------- +void CHalfLife2::RegisterScriptFunctions( void ) +{ + BaseClass::RegisterScriptFunctions(); + +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGameOver, "GameOver", "Ends the game and reloads the last save." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMegaPhyscannonActive, "MegaPhyscannonActive", "Checks if supercharged gravity gun mode is enabled." ); +#endif +} diff --git a/sp/src/game/shared/mapbase/vscript_funcs_math.cpp b/sp/src/game/shared/mapbase/vscript_funcs_math.cpp new file mode 100644 index 0000000000..fc306be3db --- /dev/null +++ b/sp/src/game/shared/mapbase/vscript_funcs_math.cpp @@ -0,0 +1,308 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Shared VScript math functions. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//============================================================================= +// +// matrix3x4_t +// +//============================================================================= + +// +// Exposes matrix3x4_t to VScript +// +class CScriptMatrix3x4 +{ +public: + CScriptMatrix3x4() + { + matrix = new matrix3x4_t(); + m_bFree = true; + } + + ~CScriptMatrix3x4() + { + if (m_bFree == true) + delete matrix; + } + + CScriptMatrix3x4( matrix3x4_t &inmatrix ) { matrix = &inmatrix; } + + matrix3x4_t *GetMatrix() { return matrix; } + void SetMatrix( matrix3x4_t &inmatrix ) { matrix = &inmatrix; } + + void Init( const Vector& xAxis, const Vector& yAxis, const Vector& zAxis, const Vector &vecOrigin ) + { + matrix->Init( xAxis, yAxis, zAxis, vecOrigin ); + } + +private: + matrix3x4_t *matrix; + bool m_bFree = false; +}; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMatrix3x4, "matrix3x4_t", "A 3x4 matrix transform." ) + + DEFINE_SCRIPT_CONSTRUCTOR() + DEFINE_SCRIPTFUNC( Init, "Creates a matrix where the X axis = forward, the Y axis = left, and the Z axis = up." ) + +END_SCRIPTDESC(); + +inline matrix3x4_t *ToMatrix3x4( HSCRIPT hMat ) { return HScriptToClass( hMat )->GetMatrix(); } + +HSCRIPT ScriptCreateMatrixInstance( matrix3x4_t &matrix ) +{ + CScriptMatrix3x4 *smatrix = new CScriptMatrix3x4( matrix ); + + return g_pScriptVM->RegisterInstance( smatrix ); +} + +void ScriptFreeMatrixInstance( HSCRIPT hMat ) +{ + CScriptMatrix3x4 *smatrix = HScriptToClass( hMat ); + if (smatrix) + { + g_pScriptVM->RemoveInstance( hMat ); + delete smatrix; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void ScriptConcatTransforms( HSCRIPT hMat1, HSCRIPT hMat2, HSCRIPT hOut ) +{ + if (!hMat1 || !hMat2 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + ConcatTransforms( *pMat1, *pMat2, *pOut ); +} + +void ScriptMatrixCopy( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixCopy( *pMat1, *pOut ); +} + +void ScriptMatrixInvert( HSCRIPT hMat1, HSCRIPT hOut ) +{ + if (!hMat1 || !hOut) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pOut = ToMatrix3x4( hOut ); + + MatrixInvert( *pMat1, *pOut ); +} + +void ScriptMatricesAreEqual( HSCRIPT hMat1, HSCRIPT hMat2 ) +{ + if (!hMat1 || !hMat2) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + matrix3x4_t *pMat2 = ToMatrix3x4( hMat2 ); + + MatricesAreEqual( *pMat1, *pMat2 ); +} + +const Vector& ScriptMatrixGetColumn( HSCRIPT hMat1, int column ) +{ + static Vector outvec; + outvec.Zero(); + if (!hMat1) + return outvec; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixGetColumn( *pMat1, column, outvec ); + return outvec; +} + +void ScriptMatrixSetColumn( const Vector& vecset, int column, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + static Vector outvec; + MatrixSetColumn( vecset, column, *pMat1 ); +} + +void ScriptMatrixAngles( HSCRIPT hMat1, const QAngle& angset, const Vector& vecset ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + MatrixAngles( *pMat1, *const_cast(&angset), *const_cast(&vecset) ); +} + +void ScriptAngleMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleMatrix( angset, vecset, *pMat1 ); +} + +void ScriptAngleIMatrix( const QAngle& angset, const Vector& vecset, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + AngleIMatrix( angset, vecset, *pMat1 ); +} + +void ScriptSetIdentityMatrix( HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetIdentityMatrix( *pMat1 ); +} + +void ScriptSetScaleMatrix( float x, float y, float z, HSCRIPT hMat1 ) +{ + if (!hMat1) + return; + + matrix3x4_t *pMat1 = ToMatrix3x4( hMat1 ); + + SetScaleMatrix( x, y, z, *pMat1 ); +} + +//============================================================================= +// +// Misc. Vector/QAngle functions +// +//============================================================================= + +const Vector& ScriptAngleVectors( const QAngle &angles ) +{ + static Vector forward; + AngleVectors( angles, &forward ); + return forward; +} + +const QAngle& ScriptVectorAngles( const Vector &forward ) +{ + static QAngle angles; + VectorAngles( forward, angles ); + return angles; +} + +const Vector& ScriptCalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point ) +{ + static Vector outvec; + CalcClosestPointOnAABB( mins, maxs, point, outvec ); + return outvec; +} + +const Vector& ScriptCalcClosestPointOnLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLine( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLine( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLine( point, vLineA, vLineB ); +} + +const Vector& ScriptCalcClosestPointOnLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + static Vector outvec; + CalcClosestPointOnLineSegment( point, vLineA, vLineB, outvec ); + return outvec; +} + +float ScriptCalcDistanceToLineSegment( const Vector &point, const Vector &vLineA, const Vector &vLineB ) +{ + return CalcDistanceToLineSegment( point, vLineA, vLineB ); +} + +#ifndef CLIENT_DLL +const Vector& ScriptPredictedPosition( HSCRIPT hTarget, float flTimeDelta ) +{ + static Vector predicted; + UTIL_PredictedPosition( ToEnt(hTarget), flTimeDelta, &predicted ); + return predicted; +} +#endif + +void RegisterMathScriptFunctions() +{ + ScriptRegisterFunction( g_pScriptVM, RandomFloat, "Generate a random floating point number within a range, inclusive." ); + ScriptRegisterFunction( g_pScriptVM, RandomInt, "Generate a random integer within a range, inclusive." ); + + ScriptRegisterFunction( g_pScriptVM, Approach, "Returns a value which approaches the target value from the input value with the specified speed." ); + ScriptRegisterFunction( g_pScriptVM, ApproachAngle, "Returns an angle which approaches the target angle from the input angle with the specified speed." ); + ScriptRegisterFunction( g_pScriptVM, AngleDiff, "Returns the degrees difference between two yaw angles." ); + ScriptRegisterFunction( g_pScriptVM, AngleDistance, "Returns the distance between two angles." ); + ScriptRegisterFunction( g_pScriptVM, AngleNormalize, "Clamps an angle to be in between -360 and 360." ); + ScriptRegisterFunction( g_pScriptVM, AngleNormalizePositive, "Clamps an angle to be in between 0 and 360." ); + ScriptRegisterFunction( g_pScriptVM, AnglesAreEqual, "Checks if two angles are equal based on a given tolerance value." ); + + // + // matrix3x4_t + // + g_pScriptVM->RegisterClass( GetScriptDescForClass( CScriptMatrix3x4 ) ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptFreeMatrixInstance, "FreeMatrixInstance", "Frees an allocated matrix instance." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptConcatTransforms, "ConcatTransforms", "Concatenates two transformation matrices into another matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixCopy, "MatrixCopy", "Copies a matrix to another matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixInvert, "MatrixInvert", "Inverts a matrix and copies the result to another matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatricesAreEqual, "MatricesAreEqual", "Checks if two matrices are equal." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixGetColumn, "MatrixGetColumn", "Gets the column of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixSetColumn, "MatrixSetColumn", "Sets the column of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatrixAngles, "MatrixAngles", "Gets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleMatrix, "AngleMatrix", "Sets the angles and position of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleIMatrix, "AngleIMatrix", "Sets the inverted angles and position of a matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetIdentityMatrix, "SetIdentityMatrix", "Turns a matrix into an identity matrix." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptSetScaleMatrix, "SetScaleMatrix", "Scales a matrix." ); + + // + // Misc. Vector/QAngle functions + // + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptAngleVectors, "AngleVectors", "Turns an angle into a direction vector." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptVectorAngles, "VectorAngles", "Turns a direction vector into an angle." ); + + ScriptRegisterFunction( g_pScriptVM, CalcSqrDistanceToAABB, "Returns the squared distance to a bounding box." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnAABB, "CalcClosestPointOnAABB", "Returns the closest point on a bounding box." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcDistanceToLine, "CalcDistanceToLine", "Returns the distance to a line." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnLine, "CalcClosestPointOnLine", "Returns the closest point on a line." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcDistanceToLineSegment, "CalcDistanceToLineSegment", "Returns the distance to a line segment." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalcClosestPointOnLineSegment, "CalcClosestPointOnLineSegment", "Returns the closest point on a line segment." ); + +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPredictedPosition, "PredictedPosition", "Predicts what an entity's position will be in a given amount of time." ); +#endif +} diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp new file mode 100644 index 0000000000..c16b3eb774 --- /dev/null +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -0,0 +1,857 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Due to this being a custom integration of VScript based on the Alien Swarm SDK, we don't have access to +// some of the code normally available in games like L4D2 or Valve's original VScript DLL. +// Instead, that code is recreated here, shared between server and client. +// +// It also contains other functions unique to Mapbase. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "matchers.h" +#include "takedamageinfo.h" + +#ifndef CLIENT_DLL +#include "globalstate.h" +#include "vscript_server.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CScriptNetPropManager +{ +public: + +#ifdef CLIENT_DLL + RecvProp *RecurseTable( RecvTable *pTable, const char *pszPropName ) +#else + SendProp *RecurseTable( SendTable *pTable, const char *pszPropName ) +#endif + { +#ifdef CLIENT_DLL + RecvProp *pProp = NULL; +#else + SendProp *pProp = NULL; +#endif + for (int i = 0; i < pTable->GetNumProps(); i++) + { + pProp = pTable->GetProp( i ); + if (pProp->GetType() == DPT_DataTable) + { + pProp = RecurseTable(pProp->GetDataTable(), pszPropName); + if (pProp) + return pProp; + } + else + { + if (FStrEq( pProp->GetName(), pszPropName )) + return pProp; + } + } + + return NULL; + } + +#ifdef CLIENT_DLL + RecvProp *RecurseNetworkClass( ClientClass *pClass, const char *pszPropName ) +#else + SendProp *RecurseNetworkClass( ServerClass *pClass, const char *pszPropName ) +#endif + { +#ifdef CLIENT_DLL + RecvProp *pProp = RecurseTable( pClass->m_pRecvTable, pszPropName ); +#else + SendProp *pProp = RecurseTable( pClass->m_pTable, pszPropName ); +#endif + if (pProp) + return pProp; + + if (pClass->m_pNext) + return RecurseNetworkClass( pClass->m_pNext, pszPropName ); + else + return NULL; + } + +#ifdef CLIENT_DLL + RecvProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) + { + if (pEnt) + { + return RecurseNetworkClass( pEnt->GetClientClass(), pszPropName ); + } + + return NULL; + } +#else + SendProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) + { + if (pEnt) + { + return RecurseNetworkClass( pEnt->GetServerClass(), pszPropName ); + } + + return NULL; + } +#endif + + int GetPropArraySize( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp) + { + // TODO: Is this what this function wants? + return pProp->GetNumElements(); + } + + return -1; + } + + #define GetPropFunc( name, varType, propType, defaultval ) \ + varType name( HSCRIPT hEnt, const char *pszPropName ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + return *(varType*)((char *)pEnt + pProp->GetOffset()); \ + } \ + return defaultval; \ + } \ + + #define GetPropFuncArray( name, varType, propType, defaultval ) \ + varType name( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + return ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; \ + } \ + return defaultval; \ + } \ + + GetPropFunc( GetPropFloat, float, DPT_Float, -1 ); + GetPropFuncArray( GetPropFloatArray, float, DPT_Float, -1 ); + GetPropFunc( GetPropInt, int, DPT_Int, -1 ); + GetPropFuncArray( GetPropIntArray, int, DPT_Int, -1 ); + GetPropFunc( GetPropVector, Vector, DPT_Vector, vec3_invalid ); + GetPropFuncArray( GetPropVectorArray, Vector, DPT_Vector, vec3_invalid ); + + HSCRIPT GetPropEntity( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return ToHScript( *(CHandle*)((char *)pEnt + pProp->GetOffset()) ); + } + + return NULL; + } + + HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return ToHScript( ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] ); + } + + return NULL; + } + + const char *GetPropString( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return (const char*)((char *)pEnt + pProp->GetOffset()); + } + + return NULL; + } + + const char *GetPropStringArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + return ((const char**)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; + } + + return NULL; + } + + const char *GetPropType( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp) + { + switch (pProp->GetType()) + { + case DPT_Int: return "integer"; + case DPT_Float: return "float"; + case DPT_Vector: return "vector"; + case DPT_VectorXY: return "vector2d"; + case DPT_String: return "string"; + case DPT_Array: return "array"; + case DPT_DataTable: return "datatable"; + } + } + + return NULL; + } + + bool HasProp( HSCRIPT hEnt, const char *pszPropName ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + return GetPropByName( pEnt, pszPropName ) != NULL; + } + + #define SetPropFunc( name, varType, propType ) \ + void name( HSCRIPT hEnt, const char *pszPropName, varType value ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + *(varType*)((char *)pEnt + pProp->GetOffset()) = value; \ + } \ + } \ + + #define SetPropFuncArray( name, varType, propType ) \ + void name( HSCRIPT hEnt, const char *pszPropName, varType value, int iArrayElement ) \ + { \ + CBaseEntity *pEnt = ToEnt( hEnt ); \ + auto *pProp = GetPropByName( pEnt, pszPropName ); \ + if (pProp && pProp->GetType() == propType) \ + { \ + ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = value; \ + } \ + } \ + + SetPropFunc( SetPropFloat, float, DPT_Float ); + SetPropFuncArray( SetPropFloatArray, float, DPT_Float ); + SetPropFunc( SetPropInt, int, DPT_Int ); + SetPropFuncArray( SetPropIntArray, int, DPT_Int ); + SetPropFunc( SetPropVector, Vector, DPT_Vector ); + SetPropFuncArray( SetPropVectorArray, Vector, DPT_Vector ); + SetPropFunc( SetPropString, const char*, DPT_String ); + SetPropFuncArray( SetPropStringArray, const char*, DPT_String ); + + void SetPropEntity( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + *((CHandle*)((char *)pEnt + pProp->GetOffset())) = ToEnt(value); + } + } + + HSCRIPT SetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value, int iArrayElement ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + auto *pProp = GetPropByName( pEnt, pszPropName ); + if (pProp && pProp->GetType() == DPT_Int) + { + ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = ToEnt(value); + } + + return NULL; + } + +private: +} g_ScriptNetPropManager; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties of an entity." ) + DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an netprop array, or -1." ) + DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an EHANDLE valued netprop (21 bit integer). Returns the script handle of the entity." ) + DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an EHANDLE valued netprop (21 bit integer) from an array. Returns the script handle of the entity." ) + DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropString, "Reads a string valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector valued netprop." ) + DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector valued netprop from an array." ) + DEFINE_SCRIPTFUNC( GetPropType, "Returns the name of the netprop type as a string." ) + DEFINE_SCRIPTFUNC( HasProp, "Checks if a netprop exists." ) + DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an EHANDLE valued netprop (21 bit integer) to reference the specified entity." ) + DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an EHANDLE valued netprop (21 bit integer) from an array to reference the specified entity." ) + DEFINE_SCRIPTFUNC( SetPropFloat, "Sets a netprop to the specified float." ) + DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a netprop from an array to the specified float." ) + DEFINE_SCRIPTFUNC( SetPropInt, "Sets a netprop to the specified integer." ) + DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets a netprop from an array to the specified integer." ) + DEFINE_SCRIPTFUNC( SetPropString, "Sets a netprop to the specified string." ) + DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a netprop from an array to the specified string." ) + DEFINE_SCRIPTFUNC( SetPropVector, "Sets a netprop to the specified vector." ) + DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a netprop from an array to the specified vector." ) +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CScriptConvarLookup +{ +public: + +#ifndef CLIENT_DLL + const char *GetClientConvarValue( const char *pszConVar, int entindex ) + { + return engine->GetClientConVarValue( entindex, pszConVar ); + } +#endif + + const char *GetStr( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetString(); + } + + float GetFloat( const char *pszConVar ) + { + ConVarRef cvar( pszConVar ); + return cvar.GetFloat(); + } + + void SetValue( const char *pszConVar, const char *pszValue ) + { + ConVarRef cvar( pszConVar ); + if (!cvar.IsValid()) + return; + + // FCVAR_NOT_CONNECTED can be used to protect specific convars from nefarious interference + if (cvar.IsFlagSet(FCVAR_NOT_CONNECTED)) + return; + + cvar.SetValue( pszValue ); + } + +private: +} g_ScriptConvarLookup; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarLookup, "CConvars", SCRIPT_SINGLETON "Provides an interface for getting and setting convars." ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( GetClientConvarValue, "Returns the convar value for the entindex as a string. Only works with client convars with the FCVAR_USERINFO flag." ) +#endif + DEFINE_SCRIPTFUNC( GetStr, "Returns the convar as a string. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( GetFloat, "Returns the convar as a float. May return null if no such convar." ) + DEFINE_SCRIPTFUNC( SetValue, "Sets the value of the convar. Supported types are bool, int, float, string." ) +END_SCRIPTDESC(); + +#ifndef CLIENT_DLL +void AddThinkToEnt( HSCRIPT entity, const char *pszFuncName ) +{ + CBaseEntity *pEntity = ToEnt( entity ); + if (!pEntity) + return; + + if (pszFuncName == NULL || pszFuncName[0] == '\0') + pEntity->m_iszScriptThinkFunction = NULL_STRING; + else + pEntity->m_iszScriptThinkFunction = AllocPooledString(pszFuncName); + + pEntity->SetContextThink( &CBaseEntity::ScriptThink, gpGlobals->curtime, "ScriptThink" ); +} + +HSCRIPT EntIndexToHScript( int index ) +{ + return ToHScript( UTIL_EntityByIndex( index ) ); +} + +void ParseScriptTableKeyValues( CBaseEntity *pEntity, HSCRIPT hKV ) +{ + int nIterator = -1; + ScriptVariant_t varKey, varValue; + while ((nIterator = g_pScriptVM->GetKeyValue( hKV, nIterator, &varKey, &varValue )) != -1) + { + switch (varValue.m_type) + { + case FIELD_CSTRING: pEntity->KeyValue( varKey.m_pszString, varValue.m_pszString ); break; + case FIELD_FLOAT: pEntity->KeyValue( varKey.m_pszString, varValue.m_float ); break; + case FIELD_VECTOR: pEntity->KeyValue( varKey.m_pszString, *varValue.m_pVector ); break; + } + + g_pScriptVM->ReleaseValue( varKey ); + g_pScriptVM->ReleaseValue( varValue ); + } +} + +void PrecacheEntityFromTable( const char *pszClassname, HSCRIPT hKV ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create an entity mid-game. Due to the server's settings, entity creation from scripts is only allowed during map init.\n" ); + return; + } + + // This is similar to UTIL_PrecacheOther(), but we can't check if we can only precache it once. + // Probably for the best anyway, as similar classes can still have different precachable properties. + CBaseEntity *pEntity = CreateEntityByName( pszClassname ); + if (!pEntity) + { + Assert( !"PrecacheEntityFromTable: only works for CBaseEntities" ); + return; + } + + ParseScriptTableKeyValues( pEntity, hKV ); + + pEntity->Precache(); + + UTIL_RemoveImmediate( pEntity ); +} + +HSCRIPT SpawnEntityFromTable( const char *pszClassname, HSCRIPT hKV ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create an entity mid-game. Due to the server's settings, entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + CBaseEntity *pEntity = CreateEntityByName( pszClassname ); + if ( !pEntity ) + { + Assert( !"SpawnEntityFromTable: only works for CBaseEntities" ); + return NULL; + } + + gEntList.NotifyCreateEntity( pEntity ); + + ParseScriptTableKeyValues( pEntity, hKV ); + + DispatchSpawn( pEntity ); + pEntity->Activate(); + + return ToHScript( pEntity ); +} +#endif + +//----------------------------------------------------------------------------- +// Mapbase-specific functions start here +//----------------------------------------------------------------------------- + +static void ScriptColorPrint( int r, int g, int b, const char *pszMsg ) +{ + const Color clr(r, g, b, 255); + ConColorMsg( clr, "%s", pszMsg ); +} + +static void ScriptColorPrintL( int r, int g, int b, const char *pszMsg ) +{ + const Color clr(r, g, b, 255); + ConColorMsg( clr, "%s\n", pszMsg ); +} + +#ifndef CLIENT_DLL +HSCRIPT SpawnEntityFromKeyValues( const char *pszClassname, HSCRIPT hKV ) +{ + if ( IsEntityCreationAllowedInScripts() == false ) + { + Warning( "VScript error: A script attempted to create an entity mid-game. Due to the server's settings, entity creation from scripts is only allowed during map init.\n" ); + return NULL; + } + + CBaseEntity *pEntity = CreateEntityByName( pszClassname ); + if ( !pEntity ) + { + Assert( !"SpawnEntityFromKeyValues: only works for CBaseEntities" ); + return NULL; + } + + gEntList.NotifyCreateEntity( pEntity ); + + CScriptKeyValues *pScriptKV = hKV ? HScriptToClass( hKV ) : NULL; + if (pScriptKV) + { + KeyValues *pKV = pScriptKV->m_pKeyValues; + for (pKV = pKV->GetFirstSubKey(); pKV != NULL; pKV = pKV->GetNextKey()) + { + pEntity->KeyValue( pKV->GetName(), pKV->GetString() ); + } + } + + DispatchSpawn( pEntity ); + pEntity->Activate(); + + return ToHScript( pEntity ); +} + +void ScriptDispatchSpawn( HSCRIPT hEntity ) +{ + CBaseEntity *pEntity = ToEnt( hEntity ); + if (pEntity) + { + DispatchSpawn( pEntity ); + } +} +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static HSCRIPT CreateDamageInfo( HSCRIPT hInflictor, HSCRIPT hAttacker, const Vector &vecForce, const Vector &vecDamagePos, float flDamage, int iDamageType ) +{ + // The script is responsible for deleting this via DestroyDamageInfo(). + CTakeDamageInfo *damageInfo = new CTakeDamageInfo(ToEnt(hInflictor), ToEnt(hAttacker), flDamage, iDamageType); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( damageInfo ); + + damageInfo->SetDamagePosition( vecDamagePos ); + damageInfo->SetDamageForce( vecForce ); + + return hScript; +} + +static void DestroyDamageInfo( HSCRIPT hDamageInfo ) +{ + if (hDamageInfo) + { + CTakeDamageInfo *pInfo = (CTakeDamageInfo*)g_pScriptVM->GetInstanceValue( hDamageInfo, GetScriptDescForClass( CTakeDamageInfo ) ); + if (pInfo) + { + g_pScriptVM->RemoveInstance( hDamageInfo ); + delete pInfo; + } + } +} + +void ScriptCalculateExplosiveDamageForce( HSCRIPT info, const Vector &vecDir, const Vector &vecForceOrigin, float flScale ) { CalculateExplosiveDamageForce( HScriptToClass(info), vecDir, vecForceOrigin, flScale ); } +void ScriptCalculateBulletDamageForce( HSCRIPT info, int iBulletType, const Vector &vecBulletDir, const Vector &vecForceOrigin, float flScale ) { CalculateBulletDamageForce( HScriptToClass(info), iBulletType, vecBulletDir, vecForceOrigin, flScale ); } +void ScriptCalculateMeleeDamageForce( HSCRIPT info, const Vector &vecMeleeDir, const Vector &vecForceOrigin, float flScale ) { CalculateMeleeDamageForce( HScriptToClass( info ), vecMeleeDir, vecForceOrigin, flScale ); } +void ScriptGuessDamageForce( HSCRIPT info, const Vector &vecForceDir, const Vector &vecForceOrigin, float flScale ) { GuessDamageForce( HScriptToClass( info ), vecForceDir, vecForceOrigin, flScale ); } + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CTraceInfoAccessor +{ +public: + + // CGrameTrace stuff + bool DidHitWorld() const { return m_tr.DidHitWorld(); } + bool DidHitNonWorldEntity() const { return m_tr.DidHitNonWorldEntity(); } + int GetEntityIndex() const { return m_tr.GetEntityIndex(); } + bool DidHit() const { return m_tr.DidHit(); } + + float FractionLeftSolid() const { return m_tr.fractionleftsolid; } + int HitGroup() const { return m_tr.hitgroup; } + int PhysicsBone() const { return m_tr.physicsbone; } + + HSCRIPT Entity() const { return ToHScript(m_tr.m_pEnt); } + + int HitBox() const { return m_tr.hitbox; } + + // CBaseTrace stuff + bool IsDispSurface() { return m_tr.IsDispSurface(); } + bool IsDispSurfaceWalkable() { return m_tr.IsDispSurfaceWalkable(); } + bool IsDispSurfaceBuildable() { return m_tr.IsDispSurfaceBuildable(); } + bool IsDispSurfaceProp1() { return m_tr.IsDispSurfaceProp1(); } + bool IsDispSurfaceProp2() { return m_tr.IsDispSurfaceProp2(); } + + Vector StartPos() const { return m_tr.startpos; } + Vector EndPos() const { return m_tr.endpos; } + + float Fraction() const { return m_tr.fraction; } + + int Contents() const { return m_tr.contents; } + int DispFlags() const { return m_tr.dispFlags; } + + bool AllSolid() const { return m_tr.allsolid; } + bool StartSolid() const { return m_tr.startsolid; } + + trace_t &GetTrace() { return m_tr; } + void Destroy() { delete this; } + +private: + trace_t m_tr; + +}; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CTraceInfoAccessor, "CGameTrace", "Handle for accessing trace_t info." ) + DEFINE_SCRIPTFUNC( DidHitWorld, "Returns whether the trace hit the world entity or not." ) + DEFINE_SCRIPTFUNC( DidHitNonWorldEntity, "Returns whether the trace hit something other than the world entity." ) + DEFINE_SCRIPTFUNC( GetEntityIndex, "Returns the index of whatever entity this trace hit." ) + DEFINE_SCRIPTFUNC( DidHit, "Returns whether the trace hit anything." ) + + DEFINE_SCRIPTFUNC( FractionLeftSolid, "If this trace started within a solid, this is the point in the trace's fraction at which it left that solid." ) + DEFINE_SCRIPTFUNC( HitGroup, "Returns the specific hit group this trace hit if it hit an entity." ) + DEFINE_SCRIPTFUNC( PhysicsBone, "Returns the physics bone this trace hit if it hit an entity." ) + DEFINE_SCRIPTFUNC( Entity, "Returns the entity this trace has hit." ) + DEFINE_SCRIPTFUNC( HitBox, "Returns the hitbox of the entity this trace has hit. If it hit the world entity, this returns the static prop index." ) + + DEFINE_SCRIPTFUNC( IsDispSurface, "Returns whether this trace hit a displacement." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceWalkable, "Returns whether DISPSURF_FLAG_WALKABLE is ticked on the displacement this trace hit." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceBuildable, "Returns whether DISPSURF_FLAG_BUILDABLE is ticked on the displacement this trace hit." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceProp1, "Returns whether DISPSURF_FLAG_SURFPROP1 is ticked on the displacement this trace hit." ) + DEFINE_SCRIPTFUNC( IsDispSurfaceProp2, "Returns whether DISPSURF_FLAG_SURFPROP2 is ticked on the displacement this trace hit." ) + + DEFINE_SCRIPTFUNC( StartPos, "Gets the trace's start position." ) + DEFINE_SCRIPTFUNC( EndPos, "Gets the trace's end position." ) + + DEFINE_SCRIPTFUNC( Fraction, "Gets the fraction of the trace completed. For example, if the trace stopped exactly halfway to the end position, this would be 0.5." ) + DEFINE_SCRIPTFUNC( Contents, "Gets the contents of the surface the trace has hit." ) + DEFINE_SCRIPTFUNC( DispFlags, "Gets the displacement flags of the surface the trace has hit." ) + + DEFINE_SCRIPTFUNC( AllSolid, "Returns whether the trace is completely within a solid." ) + DEFINE_SCRIPTFUNC( StartSolid, "Returns whether the trace started within a solid." ) + + DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) +END_SCRIPTDESC(); + +static HSCRIPT ScriptTraceLineComplex( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore, int iMask, int iCollisionGroup ) +{ + // The script is responsible for deleting this via Destroy(). + CTraceInfoAccessor *traceInfo = new CTraceInfoAccessor(); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo ); + + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceLine( vecStart, vecEnd, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + return hScript; +} + +static HSCRIPT ScriptTraceHullComplex( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin, const Vector &hullMax, + HSCRIPT entIgnore, int iMask, int iCollisionGroup ) +{ + // The script is responsible for deleting this via Destroy(). + CTraceInfoAccessor *traceInfo = new CTraceInfoAccessor(); + HSCRIPT hScript = g_pScriptVM->RegisterInstance( traceInfo ); + + CBaseEntity *pLooker = ToEnt(entIgnore); + UTIL_TraceHull( vecStart, vecEnd, hullMin, hullMax, iMask, pLooker, iCollisionGroup, &traceInfo->GetTrace()); + return hScript; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BEGIN_SCRIPTDESC_ROOT_NAMED( CFireBulletsInfoAccessor, "FireBulletsInfo_t", "Handle for accessing FireBulletsInfo_t info." ) + DEFINE_SCRIPTFUNC( GetShots, "Gets the number of shots which should be fired." ) + DEFINE_SCRIPTFUNC( SetShots, "Sets the number of shots which should be fired." ) + + DEFINE_SCRIPTFUNC( GetSource, "Gets the source of the bullets." ) + DEFINE_SCRIPTFUNC( SetSource, "Sets the source of the bullets." ) + DEFINE_SCRIPTFUNC( GetDirShooting, "Gets the direction of the bullets." ) + DEFINE_SCRIPTFUNC( SetDirShooting, "Sets the direction of the bullets." ) + DEFINE_SCRIPTFUNC( GetSpread, "Gets the spread of the bullets." ) + DEFINE_SCRIPTFUNC( SetSpread, "Sets the spread of the bullets." ) + + DEFINE_SCRIPTFUNC( GetDistance, "Gets the distance the bullets should travel." ) + DEFINE_SCRIPTFUNC( SetDistance, "Sets the distance the bullets should travel." ) + + DEFINE_SCRIPTFUNC( GetAmmoType, "Gets the ammo type the bullets should use." ) + DEFINE_SCRIPTFUNC( SetAmmoType, "Sets the ammo type the bullets should use." ) + + DEFINE_SCRIPTFUNC( GetTracerFreq, "Gets the tracer frequency." ) + DEFINE_SCRIPTFUNC( SetTracerFreq, "Sets the tracer frequency." ) + + DEFINE_SCRIPTFUNC( GetDamage, "Gets the damage the bullets should deal. 0 = use ammo type" ) + DEFINE_SCRIPTFUNC( SetDamage, "Sets the damage the bullets should deal. 0 = use ammo type" ) + DEFINE_SCRIPTFUNC( GetPlayerDamage, "Gets the damage the bullets should deal when hitting the player. 0 = use regular damage" ) + DEFINE_SCRIPTFUNC( SetPlayerDamage, "Sets the damage the bullets should deal when hitting the player. 0 = use regular damage" ) + + DEFINE_SCRIPTFUNC( GetFlags, "Gets the flags the bullets should use." ) + DEFINE_SCRIPTFUNC( SetFlags, "Sets the flags the bullets should use." ) + + DEFINE_SCRIPTFUNC( GetDamageForceScale, "Gets the scale of the damage force applied by the bullets." ) + DEFINE_SCRIPTFUNC( SetDamageForceScale, "Sets the scale of the damage force applied by the bullets." ) + + DEFINE_SCRIPTFUNC( GetAttacker, "Gets the entity considered to be the one who fired the bullets." ) + DEFINE_SCRIPTFUNC( SetAttacker, "Sets the entity considered to be the one who fired the bullets." ) + DEFINE_SCRIPTFUNC( GetAdditionalIgnoreEnt, "Gets the optional entity which the bullets should ignore." ) + DEFINE_SCRIPTFUNC( SetAdditionalIgnoreEnt, "Sets the optional entity which the bullets should ignore." ) + + DEFINE_SCRIPTFUNC( GetPrimaryAttack, "Gets whether the bullets came from a primary attack." ) + DEFINE_SCRIPTFUNC( SetPrimaryAttack, "Sets whether the bullets came from a primary attack." ) + + //DEFINE_SCRIPTFUNC( Destroy, "Deletes this instance. Important for preventing memory leaks." ) +END_SCRIPTDESC(); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +HSCRIPT CFireBulletsInfoAccessor::GetAttacker() +{ + return ToHScript( m_info->m_pAttacker ); +} + +void CFireBulletsInfoAccessor::SetAttacker( HSCRIPT value ) +{ + m_info->m_pAttacker = ToEnt( value ); +} + +HSCRIPT CFireBulletsInfoAccessor::GetAdditionalIgnoreEnt() +{ + return ToHScript( m_info->m_pAdditionalIgnoreEnt ); +} + +void CFireBulletsInfoAccessor::SetAdditionalIgnoreEnt( HSCRIPT value ) +{ + m_info->m_pAdditionalIgnoreEnt = ToEnt( value ); +} + +static HSCRIPT CreateFireBulletsInfo( int cShots, const Vector &vecSrc, const Vector &vecDirShooting, + const Vector &vecSpread, float iDamage, HSCRIPT pAttacker ) +{ + // The script is responsible for deleting this via DestroyFireBulletsInfo(). + FireBulletsInfo_t *info = new FireBulletsInfo_t(); + CFireBulletsInfoAccessor *bulletsInfo = new CFireBulletsInfoAccessor( info ); + + HSCRIPT hScript = g_pScriptVM->RegisterInstance( bulletsInfo ); + + bulletsInfo->SetShots( cShots ); + bulletsInfo->SetSource( vecSrc ); + bulletsInfo->SetDirShooting( vecDirShooting ); + bulletsInfo->SetSpread( vecSpread ); + bulletsInfo->SetDamage( iDamage ); + bulletsInfo->SetAttacker( pAttacker ); + + return hScript; +} + +static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo ) +{ + if (hBulletsInfo) + { + CFireBulletsInfoAccessor *pInfo = (CFireBulletsInfoAccessor*)g_pScriptVM->GetInstanceValue( hBulletsInfo, GetScriptDescForClass( CFireBulletsInfoAccessor ) ); + if (pInfo) + { + g_pScriptVM->RemoveInstance( hBulletsInfo ); + pInfo->Destroy(); + } + } +} + +// For the function in baseentity.cpp +FireBulletsInfo_t *GetFireBulletsInfoFromInfo( HSCRIPT hBulletsInfo ) +{ + if (hBulletsInfo) + { + CFireBulletsInfoAccessor *pInfo = (CFireBulletsInfoAccessor*)g_pScriptVM->GetInstanceValue( hBulletsInfo, GetScriptDescForClass( CFireBulletsInfoAccessor ) ); + if (pInfo) + { + return pInfo->GetInfo(); + } + } + + return NULL; +} + +//============================================================================= +//============================================================================= + +static void ScriptEntitiesInBox( HSCRIPT hTable, int listMax, const Vector &hullMin, const Vector &hullMax, int iMask ) +{ + CBaseEntity *list[1024]; + int count = UTIL_EntitiesInBox( list, listMax, hullMin, hullMax, iMask ); + + for ( int i = 0; i < count; i++ ) + { + g_pScriptVM->SetValue( hTable, STRING(list[i]->GetEntityName()), ToHScript( list[i] ) ); + } +} + +static void ScriptEntitiesAtPoint( HSCRIPT hTable, int listMax, const Vector &point, int iMask ) +{ + CBaseEntity *list[1024]; + int count = UTIL_EntitiesAtPoint( list, listMax, point, iMask ); + + for ( int i = 0; i < count; i++ ) + { + g_pScriptVM->SetValue( hTable, STRING(list[i]->GetEntityName()), ToHScript( list[i] ) ); + } +} + +static void ScriptEntitiesInSphere( HSCRIPT hTable, int listMax, const Vector ¢er, float radius, int iMask ) +{ + CBaseEntity *list[1024]; + int count = UTIL_EntitiesInSphere( list, listMax, center, radius, iMask ); + + for ( int i = 0; i < count; i++ ) + { + g_pScriptVM->SetValue( hTable, STRING(list[i]->GetEntityName()), ToHScript( list[i] ) ); + } +} + +//============================================================================= +//============================================================================= + +bool ScriptMatcherMatch( const char *pszQuery, const char *szValue ) { return Matcher_Match( pszQuery, szValue ); } + +//============================================================================= +//============================================================================= + +extern void RegisterMathScriptFunctions(); + +void RegisterSharedScriptFunctions() +{ + // + // Due to this being a custom integration of VScript based on the Alien Swarm SDK, we don't have access to + // some of the code normally available in games like L4D2 or Valve's original VScript DLL. + // Instead, that code is recreated here, shared between server and client. + // + +#ifndef CLIENT_DLL + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::BoxDirection, "DebugDrawBoxDirection", "Draw a debug forward box" ); + ScriptRegisterFunctionNamed( g_pScriptVM, NDebugOverlay::Text, "DebugDrawText", "Draw a debug overlay text" ); + + ScriptRegisterFunction( g_pScriptVM, AddThinkToEnt, "This will put a think function onto an entity, or pass null to remove it. This is NOT chained, so be careful." ); + ScriptRegisterFunction( g_pScriptVM, EntIndexToHScript, "Returns the script handle for the given entity index." ); + ScriptRegisterFunction( g_pScriptVM, PrecacheEntityFromTable, "Precache an entity from KeyValues in a table." ); + ScriptRegisterFunction( g_pScriptVM, SpawnEntityFromTable, "Native function for entity spawning." ); +#endif + + g_pScriptVM->RegisterInstance( &g_ScriptConvarLookup, "Convars" ); + g_pScriptVM->RegisterInstance( &g_ScriptNetPropManager, "NetProps" ); + + // Functions unique to Mapbase + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptColorPrint, "printc", "Version of print() which takes a color before the message." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptColorPrintL, "printcl", "Version of printl() which takes a color before the message." ); + +#ifndef CLIENT_DLL + ScriptRegisterFunction( g_pScriptVM, SpawnEntityFromKeyValues, "Spawns an entity with the keyvalues in a CScriptKeyValues handle." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptDispatchSpawn, "DispatchSpawn", "Spawns an unspawned entity." ); +#endif + + ScriptRegisterFunction( g_pScriptVM, CreateDamageInfo, "Creates damage info." ); + ScriptRegisterFunction( g_pScriptVM, DestroyDamageInfo, "Destroys damage info." ); + ScriptRegisterFunction( g_pScriptVM, ImpulseScale, "Returns an impulse scale required to push an object." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateExplosiveDamageForce, "CalculateExplosiveDamageForce", "Fill out a damage info handle with a damage force for an explosive." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateBulletDamageForce, "CalculateBulletDamageForce", "Fill out a damage info handle with a damage force for a bullet impact." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptCalculateMeleeDamageForce, "CalculateMeleeDamageForce", "Fill out a damage info handle with a damage force for a melee impact." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGuessDamageForce, "GuessDamageForce", "Try and guess the physics force to use." ); + + ScriptRegisterFunction( g_pScriptVM, CreateFireBulletsInfo, "Creates FireBullets info." ); + ScriptRegisterFunction( g_pScriptVM, DestroyFireBulletsInfo, "Destroys FireBullets info." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceLineComplex, "TraceLineComplex", "Complex version of TraceLine which takes 2 points, an ent to ignore, a trace mask, and a collision group. Returns a handle which can access all trace info." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptTraceHullComplex, "TraceHullComplex", "Takes 2 points, min/max hull bounds, an ent to ignore, a trace mask, and a collision group to trace to a point using a hull. Returns a handle which can access all trace info." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesInBox, "EntitiesInBox", "Gets all entities which are within a worldspace box." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesAtPoint, "EntitiesAtPoint", "Gets all entities which are intersecting a point in space." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptEntitiesInSphere, "EntitiesInSphere", "Gets all entities which are within a sphere." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMatcherMatch, "Matcher_Match", "Compares a string to a query using Mapbase's matcher system, supporting wildcards, RS matchers, etc." ); + ScriptRegisterFunction( g_pScriptVM, Matcher_NamesMatch, "Compares a string to a query using Mapbase's matcher system using wildcards only." ); + ScriptRegisterFunction( g_pScriptVM, AppearsToBeANumber, "Checks if the given string appears to be a number." ); + + RegisterMathScriptFunctions(); + +#ifdef CLIENT_DLL + VScriptRunScript( "vscript_client", true ); +#else + VScriptRunScript( "vscript_server", true ); +#endif +} diff --git a/sp/src/game/shared/sceneentity_shared.h b/sp/src/game/shared/sceneentity_shared.h index 6293e23ba9..a190e53b4f 100644 --- a/sp/src/game/shared/sceneentity_shared.h +++ b/sp/src/game/shared/sceneentity_shared.h @@ -36,6 +36,7 @@ class CSceneEventInfo m_pEvent( 0 ), m_pScene( 0 ), m_pActor( 0 ), + m_hSceneEntity(0), m_bStarted( false ), m_iLayer( -1 ), m_iPriority( 0 ), @@ -63,6 +64,8 @@ class CSceneEventInfo // Current actor CChoreoActor *m_pActor; + CHandle< CSceneEntity > m_hSceneEntity; + // Set after the first time the event has been configured ( allows // bumping markov index only at start of event playback, not every frame ) bool m_bStarted; diff --git a/sp/src/game/shared/takedamageinfo.cpp b/sp/src/game/shared/takedamageinfo.cpp index e9062d80c6..269f804348 100644 --- a/sp/src/game/shared/takedamageinfo.cpp +++ b/sp/src/game/shared/takedamageinfo.cpp @@ -31,6 +31,60 @@ BEGIN_SIMPLE_DATADESC( CTakeDamageInfo ) DEFINE_FIELD( m_iDamagedOtherPlayers, FIELD_INTEGER), END_DATADESC() +#ifdef MAPBASE_VSCRIPT +BEGIN_SCRIPTDESC_ROOT( CTakeDamageInfo, "Damage information handler." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetInflictor, "GetInflictor", "Gets the inflictor." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetInflictor, "SetInflictor", "Sets the inflictor." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetWeapon, "GetWeapon", "Gets the weapon." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetWeapon, "SetWeapon", "Sets the weapon." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetAttacker, "GetAttacker", "Gets the attacker." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptSetAttacker, "SetAttacker", "Sets the attacker." ) + + DEFINE_SCRIPTFUNC( GetDamage, "Gets the damage." ) + DEFINE_SCRIPTFUNC( SetDamage, "Sets the damage." ) + DEFINE_SCRIPTFUNC( GetMaxDamage, "Gets the max damage." ) + DEFINE_SCRIPTFUNC( SetMaxDamage, "Sets the max damage." ) + DEFINE_SCRIPTFUNC( ScaleDamage, "Scales the damage." ) + DEFINE_SCRIPTFUNC( AddDamage, "Adds to the damage." ) + DEFINE_SCRIPTFUNC( SubtractDamage, "Removes from the damage." ) + DEFINE_SCRIPTFUNC( GetDamageBonus, "Gets the damage bonus." ) + DEFINE_SCRIPTFUNC( SetDamageBonus, "Sets the damage bonus." ) + + DEFINE_SCRIPTFUNC( GetBaseDamage, "Gets the base damage." ) + DEFINE_SCRIPTFUNC( BaseDamageIsValid, "Checks if the base damage is valid." ) + + DEFINE_SCRIPTFUNC( GetDamageForce, "Gets the damage force." ) + DEFINE_SCRIPTFUNC( SetDamageForce, "Sets the damage force." ) + DEFINE_SCRIPTFUNC( ScaleDamageForce, "Scales the damage force." ) + + DEFINE_SCRIPTFUNC( GetDamagePosition, "Gets the damage position." ) + DEFINE_SCRIPTFUNC( SetDamagePosition, "Sets the damage position." ) + + DEFINE_SCRIPTFUNC( GetReportedPosition, "Gets the reported damage position." ) + DEFINE_SCRIPTFUNC( SetReportedPosition, "Sets the reported damage position." ) + + DEFINE_SCRIPTFUNC( GetDamageType, "Gets the damage type." ) + DEFINE_SCRIPTFUNC( SetDamageType, "Sets the damage type." ) + DEFINE_SCRIPTFUNC( AddDamageType, "Adds to the damage type." ) + DEFINE_SCRIPTFUNC( GetDamageCustom, "Gets the damage custom." ) + DEFINE_SCRIPTFUNC( SetDamageCustom, "Sets the damage custom." ) + DEFINE_SCRIPTFUNC( GetDamageStats, "Gets the damage stats." ) + DEFINE_SCRIPTFUNC( SetDamageStats, "Sets the damage stats." ) + DEFINE_SCRIPTFUNC( IsForceFriendlyFire, "Gets force friendly fire." ) + DEFINE_SCRIPTFUNC( SetForceFriendlyFire, "Sets force friendly fire." ) + + DEFINE_SCRIPTFUNC( GetAmmoType, "Gets the ammo type." ) + DEFINE_SCRIPTFUNC( SetAmmoType, "Sets the ammo type." ) + DEFINE_SCRIPTFUNC( GetAmmoName, "Gets the ammo type name." ) + + DEFINE_SCRIPTFUNC( GetPlayerPenetrationCount, "Gets the player penetration count." ) + DEFINE_SCRIPTFUNC( SetPlayerPenetrationCount, "Sets the player penetration count." ) + + DEFINE_SCRIPTFUNC( GetDamagedOtherPlayers, "Gets whether other players have been damaged." ) + DEFINE_SCRIPTFUNC( SetDamagedOtherPlayers, "Sets whether other players have been damaged." ) +END_SCRIPTDESC(); +#endif + void CTakeDamageInfo::Init( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iCustomDamage ) { m_hInflictor = pInflictor; @@ -166,6 +220,50 @@ const char *CTakeDamageInfo::GetAmmoName() const return pszAmmoType; } +#ifdef MAPBASE_VSCRIPT +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetInflictor() const +{ + return ToHScript( GetInflictor() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetInflictor( HSCRIPT pInflictor ) +{ + SetInflictor( ToEnt( pInflictor ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetWeapon() const +{ + return ToHScript( GetWeapon() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetWeapon( HSCRIPT pWeapon ) +{ + SetWeapon( ToEnt( pWeapon ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +HSCRIPT CTakeDamageInfo::ScriptGetAttacker() const +{ + return ToHScript( GetAttacker() ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::ScriptSetAttacker( HSCRIPT pAttacker ) +{ + SetAttacker( ToEnt( pAttacker ) ); +} +#endif + // -------------------------------------------------------------------------------------------------- // // MultiDamage // Collects multiple small damages into a single damage diff --git a/sp/src/game/shared/takedamageinfo.h b/sp/src/game/shared/takedamageinfo.h index 702e93228f..840432b1c8 100644 --- a/sp/src/game/shared/takedamageinfo.h +++ b/sp/src/game/shared/takedamageinfo.h @@ -14,6 +14,10 @@ #include "networkvar.h" // todo: change this when DECLARE_CLASS is moved into a better location. +#ifdef MAPBASE_VSCRIPT +#include "vscript/ivscript.h" +#endif + // Used to initialize m_flBaseDamage to something that we know pretty much for sure // hasn't been modified by a user. #define BASEDAMAGE_NOT_SPECIFIED FLT_MAX @@ -102,6 +106,15 @@ class CTakeDamageInfo // For designer debug output. static void DebugGetDamageTypeString(unsigned int DamageType, char *outbuf, int outbuflength ); +#ifdef MAPBASE_VSCRIPT + HSCRIPT ScriptGetInflictor() const; + void ScriptSetInflictor( HSCRIPT pInflictor ); + HSCRIPT ScriptGetWeapon() const; + void ScriptSetWeapon( HSCRIPT pWeapon ); + HSCRIPT ScriptGetAttacker() const; + void ScriptSetAttacker( HSCRIPT pAttacker ); +#endif + //private: void CopyDamageToBaseDamage(); diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp new file mode 100644 index 0000000000..987359fdd3 --- /dev/null +++ b/sp/src/game/shared/vscript_shared.cpp @@ -0,0 +1,276 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "vscript_shared.h" +#include "icommandline.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" +#include "characterset.h" +#include "isaverestore.h" +#include "gamerules.h" + +IScriptVM * g_pScriptVM; +extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); + +// #define VMPROFILE 1 + +#ifdef VMPROFILE + +#define VMPROF_START float debugStartTime = Plat_FloatTime(); +#define VMPROF_SHOW( funcname, funcdesc ) DevMsg("***VSCRIPT PROFILE***: %s %s: %6.4f milliseconds\n", (##funcname), (##funcdesc), (Plat_FloatTime() - debugStartTime)*1000.0 ); + +#else // !VMPROFILE + +#define VMPROF_START +#define VMPROF_SHOW + +#endif // VMPROFILE + +#ifdef MAPBASE_VSCRIPT +// This is to ensure a dependency exists between the vscript library and the game DLLs +extern int vscript_token; +int vscript_token_hack = vscript_token; +#endif + + + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return NULL; + } + + static const char *pszExtensions[] = + { + "", // SL_NONE + ".gm", // SL_GAMEMONKEY + ".nut", // SL_SQUIRREL + ".lua", // SL_LUA + ".py", // SL_PYTHON + }; + + const char *pszVMExtension = pszExtensions[g_pScriptVM->GetLanguage()]; + const char *pszIncomingExtension = V_strrchr( pszScriptName , '.' ); + if ( pszIncomingExtension && V_strcmp( pszIncomingExtension, pszVMExtension ) != 0 ) + { + Warning( "Script file type does not match VM type\n" ); + return NULL; + } + + CFmtStr scriptPath; + if ( pszIncomingExtension ) + { + scriptPath.sprintf( "scripts/vscripts/%s", pszScriptName ); + } + else + { + scriptPath.sprintf( "scripts/vscripts/%s%s", pszScriptName, pszVMExtension ); + } + + const char *pBase; + CUtlBuffer bufferScript; + + if ( g_pScriptVM->GetLanguage() == SL_PYTHON ) + { + // python auto-loads raw or precompiled modules - don't load data here + pBase = NULL; + } + else + { + bool bResult = filesystem->ReadFile( scriptPath, "GAME", bufferScript ); + +#ifdef MAPBASE_VSCRIPT + if ( !bResult && bWarnMissing ) +#else + if( !bResult ) +#endif + { + Warning( "Script not found (%s) \n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + + pBase = (const char *) bufferScript.Base(); + + if ( !pBase || !*pBase ) + { + return NULL; + } + } + + + const char *pszFilename = V_strrchr( scriptPath, '/' ); + pszFilename++; + HSCRIPT hScript = g_pScriptVM->CompileScript( pBase, pszFilename ); + if ( !hScript ) + { + Warning( "FAILED to compile and execute script file named %s\n", scriptPath.operator const char *() ); + Assert( "Error running script" ); + } + return hScript; +} + +static int g_ScriptServerRunScriptDepth; + +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ) +{ + if ( !g_pScriptVM ) + { + return false; + } + + if ( !pszScriptName || !*pszScriptName ) + { + Warning( "Cannot run script: NULL script name\n" ); + return false; + } + + // Prevent infinite recursion in VM + if ( g_ScriptServerRunScriptDepth > 16 ) + { + Warning( "IncludeScript stack overflow\n" ); + return false; + } + + g_ScriptServerRunScriptDepth++; + HSCRIPT hScript = VScriptCompileScript( pszScriptName, bWarnMissing ); + bool bSuccess = false; + if ( hScript ) + { +#ifdef GAME_DLL + if ( gpGlobals->maxClients == 1 ) + { + CBaseEntity *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer ) + { + g_pScriptVM->SetValue( "player", pPlayer->GetScriptInstance() ); + } + } +#endif + bSuccess = ( g_pScriptVM->Run( hScript, hScope ) != SCRIPT_ERROR ); + if ( !bSuccess ) + { + Warning( "Error running script named %s\n", pszScriptName ); + Assert( "Error running script" ); + } + } + g_ScriptServerRunScriptDepth--; + return bSuccess; +} + +#ifdef CLIENT_DLL +CON_COMMAND( script_client, "Run the text as a script" ) +#else +CON_COMMAND( script, "Run the text as a script" ) +#endif +{ + if ( !*args[1] ) + { + Warning( "No function name specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + const char *pszScript = args.GetCommandString(); + +#ifdef CLIENT_DLL + pszScript += 13; +#else + pszScript += 6; +#endif + + while ( *pszScript == ' ' ) + { + pszScript++; + } + + if ( !*pszScript ) + { + return; + } + + if ( *pszScript != '\"' ) + { + g_pScriptVM->Run( pszScript ); + } + else + { + pszScript++; + const char *pszEndQuote = pszScript; + while ( *pszEndQuote != '\"' ) + { + pszEndQuote++; + } + if ( !*pszEndQuote ) + { + return; + } + *((char *)pszEndQuote) = 0; + g_pScriptVM->Run( pszScript ); + *((char *)pszEndQuote) = '\"'; + } +} + + +CON_COMMAND_SHARED( script_execute, "Run a vscript file" ) +{ + if ( !*args[1] ) + { + Warning( "No script specified\n" ); + return; + } + + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + + VScriptRunScript( args[1], true ); +} + +CON_COMMAND_SHARED( script_debug, "Connect the vscript VM to the script debugger" ) +{ + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + g_pScriptVM->ConnectDebugger(); +} + +CON_COMMAND_SHARED( script_help, "Output help for script functions, optionally with a search string" ) +{ + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + const char *pszArg1 = "*"; + if ( *args[1] ) + { + pszArg1 = args[1]; + } + + g_pScriptVM->Run( CFmtStr( "PrintHelp( \"%s\" );", pszArg1 ) ); +} + +CON_COMMAND_SHARED( script_dump_all, "Dump the state of the VM to the console" ) +{ + if ( !g_pScriptVM ) + { + Warning( "Scripting disabled or no server running\n" ); + return; + } + g_pScriptVM->DumpState(); +} diff --git a/sp/src/game/shared/vscript_shared.h b/sp/src/game/shared/vscript_shared.h new file mode 100644 index 0000000000..ba27eaae9e --- /dev/null +++ b/sp/src/game/shared/vscript_shared.h @@ -0,0 +1,94 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_SHARED_H +#define VSCRIPT_SHARED_H + +#include "vscript/ivscript.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +extern IScriptVM * g_pScriptVM; + +HSCRIPT VScriptCompileScript( const char *pszScriptName, bool bWarnMissing = false ); +bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing = false ); +inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = false ) { return VScriptRunScript( pszScriptName, NULL, bWarnMissing ); } + +#define DECLARE_ENT_SCRIPTDESC() ALLOW_SCRIPT_ACCESS(); virtual ScriptClassDesc_t *GetScriptDesc() + +#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description ) +#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) + +#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc( className * ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); } + +// Only allow scripts to create entities during map initialization +bool IsEntityCreationAllowedInScripts( void ); + +#ifdef MAPBASE_VSCRIPT +void RegisterSharedScriptFunctions(); + +// +// Exposes FireBulletsInfo_t to VScript +// +class CFireBulletsInfoAccessor +{ +public: + CFireBulletsInfoAccessor( FireBulletsInfo_t *info ) { m_info = info; } + + int GetShots() { return m_info->m_iShots; } + void SetShots( int value ) { m_info->m_iShots = value; } + + Vector GetSource() { return m_info->m_vecSrc; } + void SetSource( Vector value ) { m_info->m_vecSrc = value; } + Vector GetDirShooting() { return m_info->m_vecDirShooting; } + void SetDirShooting( Vector value ) { m_info->m_vecDirShooting = value; } + Vector GetSpread() { return m_info->m_vecSpread; } + void SetSpread( Vector value ) { m_info->m_vecSpread = value; } + + float GetDistance() { return m_info->m_flDistance; } + void SetDistance( float value ) { m_info->m_flDistance = value; } + + int GetAmmoType() { return m_info->m_iAmmoType; } + void SetAmmoType( int value ) { m_info->m_iAmmoType = value; } + + int GetTracerFreq() { return m_info->m_iTracerFreq; } + void SetTracerFreq( int value ) { m_info->m_iTracerFreq = value; } + + float GetDamage() { return m_info->m_flDamage; } + void SetDamage( float value ) { m_info->m_flDamage = value; } + int GetPlayerDamage() { return m_info->m_iPlayerDamage; } + void SetPlayerDamage( float value ) { m_info->m_iPlayerDamage = value; } + + int GetFlags() { return m_info->m_nFlags; } + void SetFlags( float value ) { m_info->m_nFlags = value; } + + float GetDamageForceScale() { return m_info->m_flDamageForceScale; } + void SetDamageForceScale( float value ) { m_info->m_flDamageForceScale = value; } + + HSCRIPT GetAttacker(); + void SetAttacker( HSCRIPT value ); + HSCRIPT GetAdditionalIgnoreEnt(); + void SetAdditionalIgnoreEnt( HSCRIPT value ); + + bool GetPrimaryAttack() { return m_info->m_bPrimaryAttack; } + void SetPrimaryAttack( bool value ) { m_info->m_bPrimaryAttack = value; } + + FireBulletsInfo_t *GetInfo() { return m_info; } + + void Destroy() { delete m_info; delete this; } + +private: + FireBulletsInfo_t *m_info; +}; + +HSCRIPT ScriptCreateMatrixInstance( matrix3x4_t &matrix ); +#endif + +#endif // VSCRIPT_SHARED_H diff --git a/sp/src/public/tier1/convar.h b/sp/src/public/tier1/convar.h index 4bff787e79..e83c17c843 100644 --- a/sp/src/public/tier1/convar.h +++ b/sp/src/public/tier1/convar.h @@ -632,6 +632,18 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu static ConCommand name##_command( #name, name, description ); \ static void name( const CCommand &args ) +#ifdef CLIENT_DLL + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description ); \ + static void name( const CCommand &args ) +#endif + #define CON_COMMAND_F( name, description, flags ) \ static void name( const CCommand &args ); \ static ConCommand name##_command( #name, name, description, flags ); \ diff --git a/sp/src/public/tier1/utlbuffer.h b/sp/src/public/tier1/utlbuffer.h index 8ce115a79d..033407b4c4 100644 --- a/sp/src/public/tier1/utlbuffer.h +++ b/sp/src/public/tier1/utlbuffer.h @@ -142,6 +142,9 @@ class CUtlBuffer // Makes sure we've got at least this much memory void EnsureCapacity( int num ); + + // Access for direct read into buffer + void * AccessForDirectRead( int nBytes ); // Attaches the buffer to external memory.... void SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags = 0 ); @@ -1096,5 +1099,13 @@ inline void CUtlBuffer::CopyBuffer( const void *pubData, int cubData ) } } +inline void *CUtlBuffer::AccessForDirectRead( int nBytes ) +{ + Assert( m_Get == 0 && m_Put == 0 && m_nMaxPut == 0 ); + EnsureCapacity( nBytes ); + m_nMaxPut = nBytes; + return Base(); +} + #endif // UTLBUFFER_H diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h new file mode 100644 index 0000000000..3acba8b7bb --- /dev/null +++ b/sp/src/public/vscript/ivscript.h @@ -0,0 +1,1443 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: VScript +// +// Overview +// -------- +// VScript is an abstract binding layer that allows code to expose itself to +// multiple scripting languages in a uniform format. Code can expose +// functions, classes, and data to the scripting languages, and can also +// call functions that reside in scripts. +// +// Initializing +// ------------ +// +// To create a script virtual machine (VM), grab the global instance of +// IScriptManager, call CreateVM, then call Init on the returned VM. Right +// now you can have multiple VMs, but only VMs for a specific language. +// +// Exposing functions and classes +// ------------------------------ +// +// To expose a C++ function to the scripting system, you just need to fill out a +// description block. Using templates, the system will automatically deduce +// all of the binding requirements (parameters and return values). Functions +// are limited as to what the types of the parameters can be. See ScriptVariant_t. +// +// extern IScriptVM *pScriptVM; +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// +// void RegisterFuncs() +// { +// ScriptRegisterFunction( pScriptVM, Foo ); +// ScriptRegisterFunction( pScriptVM, Bar ); +// ScriptRegisterFunction( pScriptVM, FooBar ); +// ScriptRegisterFunctionNamed( pScriptVM, OverlyTechnicalName, "SimpleName" ); +// } +// +// class CMyClass +// { +// public: +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// }; +// +// BEGIN_SCRIPTDESC_ROOT( CMyClass ) +// 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 ) +// DEFINE_SCRIPTFUNC( DerivedFunc ) +// END_SCRIPTDESC(); +// +// CMyDerivedClass derivedInstance; +// +// void AnotherFunction() +// { +// // Manual class exposure +// pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) ); +// +// // Auto registration by instance +// pScriptVM->RegisterInstance( &derivedInstance, "theInstance" ); +// } +// +// Classes with "DEFINE_SCRIPT_CONSTRUCTOR()" in their description can be instanced within scripts +// +// Scopes +// ------ +// Scripts can either be run at the global scope, or in a user defined scope. In the latter case, +// all "globals" within the script are actually in the scope. This can be used to bind private +// data spaces with C++ objects. +// +// Calling a function on a script +// ------------------------------ +// Generally, use the "Call" functions. This example is the equivalent of DoIt("Har", 6.0, 99). +// +// hFunction = pScriptVM->LookupFunction( "DoIt", hScope ); +// pScriptVM->Call( hFunction, hScope, true, NULL, "Har", 6.0, 99 ); +// +//============================================================================= + +#ifndef IVSCRIPT_H +#define IVSCRIPT_H + +#include "platform.h" +#include "datamap.h" +#include "appframework/IAppSystem.h" +#include "tier1/functors.h" +#include "tier0/memdbgon.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifdef VSCRIPT_DLL_EXPORT +#define VSCRIPT_INTERFACE DLL_EXPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_EXPORT +#define VSCRIPT_CLASS DLL_CLASS_EXPORT +#else +#define VSCRIPT_INTERFACE DLL_IMPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_IMPORT +#define VSCRIPT_CLASS DLL_CLASS_IMPORT +#endif + +class CUtlBuffer; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define VSCRIPT_INTERFACE_VERSION "VScriptManager009" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class IScriptVM; + +enum ScriptLanguage_t +{ + SL_NONE, + SL_GAMEMONKEY, + SL_SQUIRREL, + SL_LUA, + SL_PYTHON, + + SL_DEFAULT = SL_SQUIRREL +}; + +class IScriptManager : public IAppSystem +{ +public: + virtual IScriptVM *CreateVM( ScriptLanguage_t language = SL_DEFAULT ) = 0; + virtual void DestroyVM( IScriptVM * ) = 0; +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) + +#ifdef MAPBASE_VSCRIPT +template T *HScriptToClass( HSCRIPT hObj ) +{ + return (hObj) ? (T*)g_pScriptVM->GetInstanceValue( hObj, GetScriptDesc( (T*)NULL ) ) : NULL; +} +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ExtendedFieldType +{ + FIELD_TYPEUNKNOWN = FIELD_TYPECOUNT, + FIELD_CSTRING, + FIELD_HSCRIPT, + FIELD_VARIANT, +}; + +typedef int ScriptDataType_t; +struct ScriptVariant_t; + +template struct ScriptDeducer { /*enum { FIELD_TYPE = FIELD_TYPEUNKNOWN };*/ }; +#define DECLARE_DEDUCE_FIELDTYPE( fieldType, type ) template<> struct ScriptDeducer { enum { FIELD_TYPE = fieldType }; }; + +DECLARE_DEDUCE_FIELDTYPE( FIELD_VOID, void ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_FLOAT, float ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, const char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, Vector ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const Vector &); +DECLARE_DEDUCE_FIELDTYPE( FIELD_INTEGER, int ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_BOOLEAN, bool ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CHARACTER, char ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_HSCRIPT, HSCRIPT ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VARIANT, ScriptVariant_t ); +#ifdef MAPBASE_VSCRIPT +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, QAngle ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const QAngle& ); +#endif + +#define ScriptDeduceType( T ) ScriptDeducer::FIELD_TYPE + +template +inline const char * ScriptFieldTypeName() +{ + T::using_unknown_script_type(); +} + +#define DECLARE_NAMED_FIELDTYPE( fieldType, strName ) template <> inline const char * ScriptFieldTypeName() { return strName; } +DECLARE_NAMED_FIELDTYPE( void, "void" ); +DECLARE_NAMED_FIELDTYPE( float, "float" ); +DECLARE_NAMED_FIELDTYPE( const char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( Vector, "vector" ); +DECLARE_NAMED_FIELDTYPE( const Vector&, "vector" ); +DECLARE_NAMED_FIELDTYPE( int, "integer" ); +DECLARE_NAMED_FIELDTYPE( bool, "boolean" ); +DECLARE_NAMED_FIELDTYPE( char, "character" ); +DECLARE_NAMED_FIELDTYPE( HSCRIPT, "hscript" ); +DECLARE_NAMED_FIELDTYPE( ScriptVariant_t, "variant" ); +#ifdef MAPBASE_VSCRIPT +DECLARE_NAMED_FIELDTYPE( QAngle, "vector" ); +DECLARE_NAMED_FIELDTYPE( const QAngle&, "vector" ); +#endif + +inline const char * ScriptFieldTypeName( int16 eType) +{ + switch( eType ) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "cstring"; + case FIELD_VECTOR: return "vector"; + case FIELD_INTEGER: return "integer"; + case FIELD_BOOLEAN: return "boolean"; + case FIELD_CHARACTER: return "character"; + case FIELD_HSCRIPT: return "hscript"; + case FIELD_VARIANT: return "variant"; + default: return "unknown_script_type"; + } +} + +//--------------------------------------------------------- + +struct ScriptFuncDescriptor_t +{ + ScriptFuncDescriptor_t() + { + m_pszFunction = NULL; + m_ReturnType = FIELD_TYPEUNKNOWN; + m_pszDescription = NULL; + } + + const char *m_pszScriptName; + const char *m_pszFunction; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; + CUtlVector m_Parameters; +}; + + +//--------------------------------------------------------- + +// Prefix a script description with this in order to not show the function or class in help +#define SCRIPT_HIDE "@" + +// Prefix a script description of a class to indicate it is a singleton and the single instance should be in the help +#define SCRIPT_SINGLETON "!" + +// Prefix a script description with this to indicate it should be represented using an alternate name +#define SCRIPT_ALIAS( alias, description ) "#" alias ":" description + +//--------------------------------------------------------- + +enum ScriptFuncBindingFlags_t +{ + SF_MEMBER_FUNC = 0x01, +}; + +typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ); + +struct ScriptFunctionBinding_t +{ + ScriptFuncDescriptor_t m_desc; + ScriptBindingFunc_t m_pfnBinding; + void * m_pFunction; + unsigned m_flags; +}; + +//--------------------------------------------------------- +class IScriptInstanceHelper +{ +public: + virtual void *GetProxied( void *p ) { return p; } + virtual bool ToString( void *p, char *pBuf, int bufSize ) { return false; } + virtual void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) { return NULL; } +}; + +//--------------------------------------------------------- + +struct ScriptClassDesc_t +{ + ScriptClassDesc_t() : m_pszScriptName( 0 ), m_pszClassname( 0 ), m_pszDescription( 0 ), m_pBaseDesc( 0 ), m_pfnConstruct( 0 ), m_pfnDestruct( 0 ), pHelper(NULL) {} + + const char * m_pszScriptName; + const char * m_pszClassname; + const char * m_pszDescription; + ScriptClassDesc_t * m_pBaseDesc; + CUtlVector m_FunctionBindings; + + void *(*m_pfnConstruct)(); + void (*m_pfnDestruct)( void *); + IScriptInstanceHelper * pHelper; // optional helper +}; + +//--------------------------------------------------------- +// A simple variant type. Intentionally not full featured (no implicit conversion, no memory management) +//--------------------------------------------------------- + +enum SVFlags_t +{ + SV_FREE = 0x01, +}; + +#pragma warning(push) +#pragma warning(disable:4800) +struct ScriptVariant_t +{ + ScriptVariant_t() : m_flags( 0 ), m_type( FIELD_VOID ) { m_pVector = 0; } + ScriptVariant_t( int val ) : m_flags( 0 ), m_type( FIELD_INTEGER ) { m_int = val;} + ScriptVariant_t( float val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = val; } + ScriptVariant_t( double val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = (float)val; } + ScriptVariant_t( char val ) : m_flags( 0 ), m_type( FIELD_CHARACTER ) { m_char = val; } + ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; } + ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; } + + ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = &val; } else { m_pVector = new Vector( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const Vector *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = val; } else { m_pVector = new Vector( *val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { if ( !bCopy ) { m_pszString = val; } else { m_pszString = strdup( val ); m_flags |= SV_FREE; } } + +#ifdef MAPBASE_VSCRIPT + ScriptVariant_t( const QAngle &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pAngle = &val; } else { m_pAngle = new QAngle( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const QAngle *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pAngle = val; } else { m_pAngle = new QAngle( *val ); m_flags |= SV_FREE; } } +#endif + + bool IsNull() const { return (m_type == FIELD_VOID ); } + + operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; } + operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; } + operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; } + operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; } + operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; } + operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; } + operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; } +#ifdef MAPBASE_VSCRIPT + operator const QAngle &() const { Assert( m_type == FIELD_VECTOR ); static QAngle vecNull(0, 0, 0); return (m_pAngle) ? *m_pAngle : vecNull; } +#endif + + void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; } + void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; } + void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; } + void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; } + void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; } + void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; } + void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; } + void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; } + void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; } +#ifdef MAPBASE_VSCRIPT + void operator=( const QAngle &vec ) { m_type = FIELD_VECTOR; m_pAngle = &vec; } + void operator=( const QAngle *vec ) { m_type = FIELD_VECTOR; m_pAngle = vec; } +#endif + + void Free() { if ( ( m_flags & SV_FREE ) && ( m_type == FIELD_HSCRIPT || m_type == FIELD_VECTOR || m_type == FIELD_CSTRING ) ) delete m_pszString; } // Generally only needed for return results + + template + T Get() + { + T value; + AssignTo( &value ); + return value; + } + + template + bool AssignTo( T *pDest ) + { + ScriptDataType_t destType = ScriptDeduceType( T ); + if ( destType == FIELD_TYPEUNKNOWN ) + { + DevWarning( "Unable to convert script variant to unknown type\n" ); + } + if ( destType == m_type ) + { + *pDest = *this; + return true; + } + + if ( m_type != FIELD_VECTOR && m_type != FIELD_CSTRING && destType != FIELD_VECTOR && destType != FIELD_CSTRING ) + { + switch ( m_type ) + { + case FIELD_VOID: *pDest = 0; break; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_CHARACTER: *pDest = m_char; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + case FIELD_HSCRIPT: *pDest = m_hScript; return true; + } + } + else + { + DevWarning( "No free conversion of %s script variant to %s right now\n", + ScriptFieldTypeName( m_type ), ScriptFieldTypeName() ); + if ( destType != FIELD_VECTOR ) + { + *pDest = 0; + } + } + return false; + } + + bool AssignTo( float *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to float now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( int *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to int now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( bool *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to bool now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( char **pDest ) + { + DevWarning( "No free conversion of string or vector script variant right now\n" ); + // If want to support this, probably need to malloc string and require free on other side [3/24/2008 tom] + *pDest = ""; + return false; + } + + bool AssignTo( ScriptVariant_t *pDest ) + { + pDest->m_type = m_type; + if ( m_type == FIELD_VECTOR ) + { + pDest->m_pVector = new Vector; + ((Vector *)(pDest->m_pVector))->Init( m_pVector->x, m_pVector->y, m_pVector->z ); + pDest->m_flags |= SV_FREE; + } + else if ( m_type == FIELD_CSTRING ) + { + pDest->m_pszString = strdup( m_pszString ); + pDest->m_flags |= SV_FREE; + } + else + { + pDest->m_int = m_int; + } + return false; + } + + union + { + int m_int; + float m_float; + const char * m_pszString; + const Vector * m_pVector; + char m_char; + bool m_bool; + HSCRIPT m_hScript; +#ifdef MAPBASE_VSCRIPT + // This uses FIELD_VECTOR, so it's considered a Vector in the VM (just like save/restore) + const QAngle * m_pAngle; +#endif + }; + + int16 m_type; + int16 m_flags; + +private: +}; + +#define SCRIPT_VARIANT_NULL ScriptVariant_t() + +#pragma warning(pop) + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#include "vscript_templates.h" + +// Lower level macro primitives +#define ScriptInitFunctionBinding( pScriptFunction, func ) ScriptInitFunctionBindingNamed( pScriptFunction, func, #func ) +#define ScriptInitFunctionBindingNamed( pScriptFunction, func, scriptName ) do { ScriptInitFuncDescriptorNamed( (&(pScriptFunction)->m_desc), func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( &func ); (pScriptFunction)->m_pFunction = (void *)&func; } while (0) + +#define ScriptInitMemberFunctionBinding( pScriptFunction, class, func ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, #func ) +#define ScriptInitMemberFunctionBindingNamed( pScriptFunction, class, func, scriptName ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) +#define ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) do { ScriptInitMemberFuncDescriptor_( (&(pScriptFunction)->m_desc), class, func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( ((class *)0), &class::func ); (pScriptFunction)->m_pFunction = ScriptConvertFuncPtrToVoid( &class::func ); (pScriptFunction)->m_flags = SF_MEMBER_FUNC; } while (0) + +#define ScriptInitClassDesc( pClassDesc, class, pBaseClassDesc ) ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, #class ) +#define ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) +#define ScriptInitClassDescNoBase( pClassDesc, class ) ScriptInitClassDescNoBaseNamed( pClassDesc, class, #class ) +#define ScriptInitClassDescNoBaseNamed( pClassDesc, class, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, NULL, scriptName ) +#define ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) do { (pClassDesc)->m_pszScriptName = scriptName; (pClassDesc)->m_pszClassname = #class; (pClassDesc)->m_pBaseDesc = pBaseClassDesc; } while ( 0 ) + +#define ScriptAddFunctionToClassDesc( pClassDesc, class, func, description ) ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, #func, description ) +#define ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, scriptName, description ) do { ScriptFunctionBinding_t *pBinding = &((pClassDesc)->m_FunctionBindings[(pClassDesc)->m_FunctionBindings.AddToTail()]); pBinding->m_desc.m_pszDescription = description; ScriptInitMemberFunctionBindingNamed( pBinding, class, func, scriptName ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterFunction( pVM, func, description ) ScriptRegisterFunctionNamed( pVM, func, #func, description ) +#define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) do { static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); pVM->RegisterFunction( &binding ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *); + +#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_NAMED( className, baseClass, #className, description ) +#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_NAMED( className, #className, description ) + +#ifdef MSVC + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + ScriptClassDesc_t * GetScriptDesc( className * ) +#else + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + template <> ScriptClassDesc_t * GetScriptDesc( baseClass *); \ + template <> ScriptClassDesc_t * GetScriptDesc( className *) +#endif + +#define BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) \ + ScriptClassDesc_t g_##className##_ScriptDesc; \ + DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + { \ + return &g_##className##_ScriptDesc; \ + } \ + \ + bInitialized = true; \ + \ + typedef className _className; \ + ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ + pDesc->m_pszDescription = description; \ + ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ + ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ + while ( pInstanceHelperBase ) \ + { \ + if ( pInstanceHelperBase->pHelper ) \ + { \ + pDesc->pHelper = pInstanceHelperBase->pHelper; \ + break; \ + } \ + pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \ + } + + +#define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \ + BEGIN_SCRIPTDESC_NAMED( className, ScriptNoBase_t, scriptName, description ) + +#define END_SCRIPTDESC() \ + return pDesc; \ + } + +#define DEFINE_SCRIPTFUNC( func, description ) DEFINE_SCRIPTFUNC_NAMED( func, #func, description ) +#define DEFINE_SCRIPTFUNC_NAMED( func, scriptName, description ) ScriptAddFunctionToClassDescNamed( pDesc, _className, func, scriptName, description ); +#define DEFINE_SCRIPT_CONSTRUCTOR() ScriptAddConstructorToClassDesc( pDesc, _className ); +#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) pDesc->pHelper = (p); + +template ScriptClassDesc_t *GetScriptDesc(T *); + +struct ScriptNoBase_t; +template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *) { return NULL; } + +#define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +class CScriptConstructor +{ +public: + static void *Construct() { return new T; } + static void Destruct( void *p ) { delete (T *)p; } +}; + +#define ScriptAddConstructorToClassDesc( pClassDesc, class ) do { (pClassDesc)->m_pfnConstruct = &CScriptConstructor::Construct; (pClassDesc)->m_pfnDestruct = &CScriptConstructor::Destruct; } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ScriptErrorLevel_t +{ + SCRIPT_LEVEL_WARNING = 0, + SCRIPT_LEVEL_ERROR, +}; + +typedef void ( *ScriptOutputFunc_t )( const char *pszText ); +typedef bool ( *ScriptErrorFunc_t )( ScriptErrorLevel_t eLevel, const char *pszText ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef RegisterClass +#undef RegisterClass +#endif + +enum ScriptStatus_t +{ + SCRIPT_ERROR = -1, + SCRIPT_DONE, + SCRIPT_RUNNING, +}; + +class IScriptVM +{ +public: + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual bool ConnectDebugger() = 0; + virtual void DisconnectDebugger() = 0; + + virtual ScriptLanguage_t GetLanguage() = 0; + virtual const char *GetLanguageName() = 0; + + virtual void AddSearchPath( const char *pszSearchPath ) = 0; + + //-------------------------------------------------------- + + virtual bool Frame( float simTime ) = 0; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run( const char *pszScript, bool bWait = true ) = 0; + inline ScriptStatus_t Run( const unsigned char *pszScript, bool bWait = true ) { return Run( (char *)pszScript, bWait ); } + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) = 0; + inline HSCRIPT CompileScript( const unsigned char *pszScript, const char *pszId = NULL ) { return CompileScript( (char *)pszScript, pszId ); } + virtual void ReleaseScript( HSCRIPT ) = 0; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) = 0; + virtual ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) = 0; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) = 0; + virtual void ReleaseScope( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) = 0; + virtual void ReleaseFunction( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) = 0; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) = 0; + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } + virtual void RemoveInstance( HSCRIPT ) = 0; + void RemoveInstance( HSCRIPT hInstance, const char *pszInstance, HSCRIPT hScope = NULL ) { ClearValue( hScope, pszInstance ); RemoveInstance( hInstance ); } + void RemoveInstance( const char *pszInstance, HSCRIPT hScope = NULL ) { ScriptVariant_t val; if ( GetValue( hScope, pszInstance, &val ) ) { if ( val.m_type == FIELD_HSCRIPT ) { RemoveInstance( val, pszInstance, hScope ); } ReleaseValue( val ); } } + + virtual void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType = NULL ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) = 0; + bool ValueExists( const char *pszKey ) { return ValueExists( NULL, pszKey ); } + + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) = 0; + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) = 0; + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return SetValue(NULL, pszKey, value ); } + + virtual void CreateTable( ScriptVariant_t &Table ) = 0; + virtual int GetNumTableEntries( HSCRIPT hScope ) = 0; + virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) = 0; + + virtual bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) = 0; + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetValue(NULL, pszKey, pValue ); } + virtual void ReleaseValue( ScriptVariant_t &value ) = 0; + + virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) = 0; + bool ClearValue( const char *pszKey) { return ClearValue( NULL, pszKey ); } + + //---------------------------------------------------------------------------- + + virtual void WriteState( CUtlBuffer *pBuffer ) = 0; + virtual void ReadState( CUtlBuffer *pBuffer ) = 0; + virtual void RemoveOrphanInstances() = 0; + + virtual void DumpState() = 0; + + virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) = 0; + virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException( const char *pszExceptionText ) = 0; + + //---------------------------------------------------------------------------- + // Call API + // + // Note for string and vector return types, the caller must delete the pointed to memory + //---------------------------------------------------------------------------- + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope = NULL, bool bWait = true, ScriptVariant_t *pReturn = NULL ) + { + return ExecuteFunction( hFunction, NULL, 0, pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + +}; + + +//----------------------------------------------------------------------------- +// Script scope helper class +//----------------------------------------------------------------------------- + +class CDefScriptScopeBase +{ +public: + static IScriptVM *GetVM() + { + extern IScriptVM *g_pScriptVM; + return g_pScriptVM; + } +}; + +template +class CScriptScopeT : public CDefScriptScopeBase +{ +public: + CScriptScopeT() : + m_hScope( INVALID_HSCRIPT ), + m_flags( 0 ) + { + } + + ~CScriptScopeT() + { + Term(); + } + + bool IsInitialized() + { + return m_hScope != INVALID_HSCRIPT; + } + + bool Init( const char *pszName ) + { + m_hScope = GetVM()->CreateScope( pszName ); + return ( m_hScope != NULL ); + } + + bool Init( HSCRIPT hScope, bool bExternal = true ) + { + if ( bExternal ) + { + m_flags |= EXTERNAL; + } + m_hScope = hScope; + return ( m_hScope != NULL ); + } + + bool InitGlobal() + { + Assert( 0 ); // todo [3/24/2008 tom] + m_hScope = GetVM()->CreateScope( "" ); + return ( m_hScope != NULL ); + } + + void Term() + { + if ( m_hScope != INVALID_HSCRIPT ) + { + IScriptVM *pVM = GetVM(); + if ( pVM ) + { + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + pVM->ReleaseFunction( *m_FuncHandles[i] ); + } + } + m_FuncHandles.Purge(); + if ( m_hScope && pVM && !(m_flags & EXTERNAL) ) + { + pVM->ReleaseScope( m_hScope ); + } + m_hScope = INVALID_HSCRIPT; + } + m_flags = 0; + } + + void InvalidateCachedValues() + { + IScriptVM *pVM = GetVM(); + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + if ( *m_FuncHandles[i] ) + pVM->ReleaseFunction( *m_FuncHandles[i] ); + *m_FuncHandles[i] = INVALID_HSCRIPT; + } + m_FuncHandles.RemoveAll(); + } + + operator HSCRIPT() + { + return ( m_hScope != INVALID_HSCRIPT ) ? m_hScope : NULL; + } + + bool ValueExists( const char *pszKey ) { return GetVM()->ValueExists( m_hScope, pszKey ); } + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return GetVM()->SetValue(m_hScope, pszKey, value ); } + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetVM()->GetValue(m_hScope, pszKey, pValue ); } + void ReleaseValue( ScriptVariant_t &value ) { GetVM()->ReleaseValue( value ); } + bool ClearValue( const char *pszKey) { return GetVM()->ClearValue( m_hScope, pszKey ); } + + ScriptStatus_t Run( HSCRIPT hScript ) + { + InvalidateCachedValues(); + return GetVM()->Run( hScript, m_hScope ); + } + + ScriptStatus_t Run( const char *pszScriptText, const char *pszScriptName = NULL ) + { + InvalidateCachedValues(); + HSCRIPT hScript = GetVM()->CompileScript( pszScriptText, pszScriptName ); + if ( hScript ) + { + ScriptStatus_t result = GetVM()->Run( hScript, m_hScope ); + GetVM()->ReleaseScript( hScript ); + return result; + } + return SCRIPT_ERROR; + } + + ScriptStatus_t Run( const unsigned char *pszScriptText, const char *pszScriptName = NULL ) + { + return Run( (const char *)pszScriptText, pszScriptName); + } + + HSCRIPT LookupFunction( const char *pszFunction ) + { + return GetVM()->LookupFunction( pszFunction, m_hScope ); + } + + void ReleaseFunction( HSCRIPT hScript ) + { + GetVM()->ReleaseFunction( hScript ); + } + + bool FunctionExists( const char *pszFunction ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + GetVM()->ReleaseFunction( hFunction ); + return ( hFunction != NULL ) ; + } + + //----------------------------------------------------- + + enum Flags_t + { + EXTERNAL = 0x01, + }; + + //----------------------------------------------------- + + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn = NULL ) + { + return GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn = NULL ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + +protected: + HSCRIPT m_hScope; + int m_flags; + CUtlVectorConservative m_FuncHandles; +}; + +typedef CScriptScopeT<> CScriptScope; + +#define VScriptAddEnumToScope_( scope, enumVal, scriptName ) (scope).SetValue( scriptName, (int)enumVal ) +#define VScriptAddEnumToScope( scope, enumVal ) VScriptAddEnumToScope_( scope, enumVal, #enumVal ) + +#define VScriptAddEnumToRoot( enumVal ) g_pScriptVM->SetValue( #enumVal, (int)enumVal ) + +//----------------------------------------------------------------------------- +// Script function proxy support +//----------------------------------------------------------------------------- + +class CScriptFuncHolder +{ +public: + CScriptFuncHolder() : hFunction( INVALID_HSCRIPT ) {} + bool IsValid() { return ( hFunction != INVALID_HSCRIPT ); } + bool IsNull() { return ( !hFunction ); } + HSCRIPT hFunction; +}; + +#define DEFINE_SCRIPT_PROXY_GUTS( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < typename RET_TYPE FUNC_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( RET_TYPE *pRetVal FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptVariant_t returnVal; \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, &returnVal, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + returnVal.AssignTo( pRetVal ); \ + returnVal.Free(); \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0V( FuncName ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + bool FuncName() \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 0 ) +#define DEFINE_SCRIPT_PROXY_1( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 14 ) + +#define DEFINE_SCRIPT_PROXY_1V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 14 ) + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // IVSCRIPT_H diff --git a/sp/src/public/vscript/vscript_templates.h b/sp/src/public/vscript/vscript_templates.h new file mode 100644 index 0000000000..e23a9fe9d5 --- /dev/null +++ b/sp/src/public/vscript/vscript_templates.h @@ -0,0 +1,414 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_TEMPLATES_H +#define VSCRIPT_TEMPLATES_H + +#include "tier0/basetypes.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#define FUNC_APPEND_PARAMS_0 +#define FUNC_APPEND_PARAMS_1 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 1 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); +#define FUNC_APPEND_PARAMS_2 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 2 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); +#define FUNC_APPEND_PARAMS_3 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 3 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); +#define FUNC_APPEND_PARAMS_4 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 4 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); +#define FUNC_APPEND_PARAMS_5 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 5 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); +#define FUNC_APPEND_PARAMS_6 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 6 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); +#define FUNC_APPEND_PARAMS_7 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 7 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); +#define FUNC_APPEND_PARAMS_8 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 8 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); +#define FUNC_APPEND_PARAMS_9 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 9 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); +#define FUNC_APPEND_PARAMS_10 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 10 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); +#define FUNC_APPEND_PARAMS_11 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 11 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); +#define FUNC_APPEND_PARAMS_12 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 12 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); +#define FUNC_APPEND_PARAMS_13 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 13 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); +#define FUNC_APPEND_PARAMS_14 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 14 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_14 ) ); + +#define DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER ); + +#define DEFINE_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); + +#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } + +#define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } +#define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) +#define ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, scriptName ) ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) +#define ScriptInitMemberFuncDescriptor( pDesc, class, func ) ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, #func ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +inline void *ScriptConvertFuncPtrToVoid( FUNCPTR_TYPE pFunc ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.pFunc = pFunc; + return convert.p; + } +#if defined( _MSC_VER ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } +#elif defined( GNUC ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + AssertMsg( 0, "Note: This path has not been verified yet. See comments below in #else case." ); + + struct GnuMFP + { + union + { + void *funcadr; // If vtable_index_2 is even, then this is the function pointer. + int vtable_index_2; // If vtable_index_2 is odd, then this = vindex*2+1. + }; + int delta; + }; + + GnuMFP *p = (GnuMFP*)&pFunc; + if ( p->vtable_index_2 & 1 ) + { + char **delta = (char**)p->delta; + char *pCur = *delta + (p->vtable_index_2+1)/2; + return (void*)( pCur + 4 ); + } + else + { + return p->funcadr; + } + } +#else +#error "Need to implement code to crack non-offset member function pointer case" + // For gcc, see: http://www.codeproject.com/KB/cpp/FastDelegate.aspx + // + // Current versions of the GNU compiler use a strange and tricky + // optimization. It observes that, for virtual inheritance, you have to look + // up the vtable in order to get the voffset required to calculate the this + // pointer. While you're doing that, you might as well store the function + // pointer in the vtable. By doing this, they combine the m_func_address and + // m_vtable_index fields into one, and they distinguish between them by + // ensuring that function pointers always point to even addresses but vtable + // indices are always odd: + // + // // GNU g++ uses a tricky space optimisation, also adopted by IBM's VisualAge and XLC. + // struct GnuMFP { + // union { + // CODEPTR funcadr; // always even + // int vtable_index_2; // = vindex*2+1, always odd + // }; + // int delta; + // }; + // adjustedthis = this + delta + // if (funcadr & 1) CALL (* ( *delta + (vindex+1)/2) + 4) + // else CALL funcadr + // + // The G++ method is well documented, so it has been adopted by many other + // vendors, including IBM's VisualAge and XLC compilers, recent versions of + // Open64, Pathscale EKO, and Metrowerks' 64-bit compilers. A simpler scheme + // used by earlier versions of GCC is also very common. SGI's now + // discontinued MIPSPro and Pro64 compilers, and Apple's ancient MrCpp + // compiler used this method. (Note that the Pro64 compiler has become the + // open source Open64 compiler). + +#endif + else + AssertMsg( 0, "Member function pointer not supported. Why on earth are you using virtual inheritance!?" ); + return NULL; +} + +template +inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.p = p; + return convert.pFunc; + } + +#if defined( _MSC_VER ) + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } +#elif defined( POSIX ) + AssertMsg( 0, "Note: This path has not been implemented yet." ); +#else +#error "Need to implement code to crack non-offset member function pointer case" +#endif + Assert( 0 ); + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_0 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_1 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_1 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_2 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_2 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_3 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_3 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_4 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_4 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_5 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_5 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_6 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_6 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_7 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_7 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_8 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_8 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_9 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_9 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_10 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_10 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_11 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_11 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_12 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_12 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_13 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_13 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_14 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_14 + +#define SCRIPT_BINDING_ARGS_0 +#define SCRIPT_BINDING_ARGS_1 pArguments[0] +#define SCRIPT_BINDING_ARGS_2 pArguments[0], pArguments[1] +#define SCRIPT_BINDING_ARGS_3 pArguments[0], pArguments[1], pArguments[2] +#define SCRIPT_BINDING_ARGS_4 pArguments[0], pArguments[1], pArguments[2], pArguments[3] +#define SCRIPT_BINDING_ARGS_5 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4] +#define SCRIPT_BINDING_ARGS_6 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5] +#define SCRIPT_BINDING_ARGS_7 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6] +#define SCRIPT_BINDING_ARGS_8 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7] +#define SCRIPT_BINDING_ARGS_9 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8] +#define SCRIPT_BINDING_ARGS_10 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9] +#define SCRIPT_BINDING_ARGS_11 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10] +#define SCRIPT_BINDING_ARGS_12 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11] +#define SCRIPT_BINDING_ARGS_13 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12] +#define SCRIPT_BINDING_ARGS_14 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12], pArguments[13] + + +#define DEFINE_SCRIPT_BINDINGS(N) \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || !pReturn || pContext ) \ + { \ + return false; \ + } \ + *pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || pReturn || pContext ) \ + { \ + return false; \ + } \ + ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || !pReturn || !pContext ) \ + { \ + return false; \ + } \ + *pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || pReturn || !pContext ) \ + { \ + return false; \ + } \ + (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CNonMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } + +FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#endif // VSCRIPT_TEMPLATES_H diff --git a/sp/src/vpc_scripts/groups.vgc b/sp/src/vpc_scripts/groups.vgc index 47585605fc..7354a28ef3 100644 --- a/sp/src/vpc_scripts/groups.vgc +++ b/sp/src/vpc_scripts/groups.vgc @@ -22,6 +22,7 @@ $Group "game" "server" "tier1" "vgui_controls" + "vscript" } $Group "shaders" @@ -51,6 +52,7 @@ $Group "everything" "vice" "vrad_dll" "vrad_launcher" + "vscript" "vtf2tga" "vtfdiff" "vvis_dll" diff --git a/sp/src/vpc_scripts/projects.vgc b/sp/src/vpc_scripts/projects.vgc index 0602afb4b1..17f0440e78 100644 --- a/sp/src/vpc_scripts/projects.vgc +++ b/sp/src/vpc_scripts/projects.vgc @@ -111,6 +111,11 @@ $Project "vrad_launcher" "utils\vrad_launcher\vrad_launcher.vpc" [$WIN32] } +$Project "vscript" +{ + "vscript\vscript.vpc" +} + $Project "vtf2tga" { "utils\vtf2tga\vtf2tga.vpc" [$WIN32] diff --git a/sp/src/vpc_scripts/source_base.vpc b/sp/src/vpc_scripts/source_base.vpc index 7f7b24d887..372a257d9c 100644 --- a/sp/src/vpc_scripts/source_base.vpc +++ b/sp/src/vpc_scripts/source_base.vpc @@ -21,6 +21,9 @@ $Conditional MAPBASE "1" // Toggles Mapbase's RPC implementation $Conditional MAPBASE_RPC "1" + +// Toggles VScript implementation (note: interfaces still exist, just the provided implementation is not present) +$Conditional MAPBASE_VSCRIPT "1" //----------------------------------------------------------------------------- $Configuration "Debug" @@ -38,6 +41,7 @@ $Configuration "Debug" // Mapbase base definitions $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } @@ -56,5 +60,6 @@ $Configuration "Release" // Mapbase base definitions $PreprocessorDefinitions "$BASE;MAPBASE" [$MAPBASE] + $PreprocessorDefinitions "$BASE;MAPBASE_VSCRIPT" [$MAPBASE_VSCRIPT] } } diff --git a/sp/src/vscript/squirrel/.gitignore b/sp/src/vscript/squirrel/.gitignore new file mode 100644 index 0000000000..6e97beb729 --- /dev/null +++ b/sp/src/vscript/squirrel/.gitignore @@ -0,0 +1,6 @@ +# Folders created at compilation +bin/ +lib/ + +# Folders created at documentation generation +doc/build/ diff --git a/sp/src/vscript/squirrel/.travis.yml b/sp/src/vscript/squirrel/.travis.yml new file mode 100644 index 0000000000..1e31c1d6ca --- /dev/null +++ b/sp/src/vscript/squirrel/.travis.yml @@ -0,0 +1,17 @@ +language: cpp +compiler: + - gcc + - clang + +# Travis VMs are 64-bit but we compile both for 32 and 64 bit. To enable the +# 32-bit builds to work, we need gcc-multilib. +addons: + apt: + packages: + - gcc-multilib + - g++-multilib + +# Enable container-based builds. +sudo: false + +script: mkdir build && cd build && cmake .. && make -j2 diff --git a/sp/src/vscript/squirrel/CMakeLists.txt b/sp/src/vscript/squirrel/CMakeLists.txt new file mode 100644 index 0000000000..dc35b6f4ea --- /dev/null +++ b/sp/src/vscript/squirrel/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.4) +project(squirrel VERSION 3.1 LANGUAGES C CXX) + +option(DISABLE_STATIC "Avoid building/installing static libraries.") +option(LONG_OUTPUT_NAMES "Use longer names for binaries and libraries: squirrel3 (not sq).") + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif () + +include(GNUInstallDirs) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") +set(CMAKE_CXX_STANDARD 11) + +if(CMAKE_COMPILER_IS_GNUCXX) + add_compile_options( + "$<$:-fno-rtti;-fno-exceptions>" + -fno-strict-aliasing + -Wall + -Wextra + -pedantic + -Wcast-qual + "$<$:-O3>" + "$<$:-O3;-g>" + "$<$:-Os>" + "$<$:-pg;-pie;-gstabs;-g3;-Og>" + ) +elseif(MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +add_subdirectory(squirrel) +add_subdirectory(sqstdlib) +add_subdirectory(sq) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(tgts) + if(NOT DISABLE_DYNAMIC) + list(APPEND tgts squirrel sqstdlib sq) + endif() + if(NOT DISABLE_STATIC) + list(APPEND tgts squirrel_static sqstdlib_static sq_static) + endif() + foreach(t ${tgts}) + target_compile_definitions(${t} PUBLIC -D_SQ64) + endforeach() +endif() + +if(NOT DISABLE_DYNAMIC) + set_target_properties(squirrel sqstdlib PROPERTIES SOVERSION 0 VERSION 0.0.0) +endif() + +if(NOT SQ_DISABLE_INSTALLER AND NOT SQ_DISABLE_HEADER_INSTALLER) + install(FILES + include/sqconfig.h + include/squirrel.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT Development + ) + install(FILES + include/sqstdaux.h + include/sqstdblob.h + include/sqstdio.h + include/sqstdmath.h + include/sqstdstring.h + include/sqstdsystem.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT Development + ) +endif() + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config-version.cmake" + VERSION "${squirrel_VERSION}" + COMPATIBILITY AnyNewerVersion + ) + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/squirrel-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" + ) + +export(EXPORT squirrel + NAMESPACE squirrel:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-targets.cmake" + ) + +if(NOT SQ_DISABLE_INSTALLER AND NOT SQ_DISABLE_CMAKE_INSTALLER) + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config-version.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" + COMPONENT Development + ) + + install(EXPORT squirrel + NAMESPACE squirrel:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" + FILE "squirrel-targets.cmake" + COMPONENT Development + ) +endif() diff --git a/sp/src/vscript/squirrel/COMPILE b/sp/src/vscript/squirrel/COMPILE new file mode 100644 index 0000000000..375a92096f --- /dev/null +++ b/sp/src/vscript/squirrel/COMPILE @@ -0,0 +1,86 @@ +Squirrel 3.1 stable +-------------------------------------------------------- +What is in this distribution? + +squirrel + static library implementing the compiler and interpreter of the language + +sqstdlib + the standard utility libraries + +sq + stand alone interpreter + +doc + The manual + +etc + a minimalistic embedding sample + +samples + samples programs + + +HOW TO COMPILE +--------------------------------------------------------- +CMAKE USERS +......................................................... +If you want to build the shared libraries under Windows using Visual +Studio, you will have to use CMake version 3.4 or newer. If not, an +earlier version will suffice. For a traditional out-of-source build +under Linux, type something like + + $ mkdir build # Create temporary build directory + $ cd build + $ cmake .. # CMake will determine all the necessary information, + # including the platform (32- vs. 64-bit) + $ make + $ make install + $ cd ..; rm -r build + +The default installation directory will be /usr/local on Unix platforms, +and C:/Program Files/squirrel on Windows. The binaries will go into bin/ +and the libraries into lib/. You can change this behavior by calling CMake like +this: + + $ cmake .. -DCMAKE_INSTALL_PREFIX=/some/path/on/your/system + +With the CMAKE_INSTALL_BINDIR and CMAKE_INSTALL_LIBDIR options, the directories +the binaries & libraries will go in (relative to CMAKE_INSTALL_PREFIX) +can be specified. For instance, + + $ cmake .. -DCMAKE_INSTALL_LIBDIR=lib64 + +will install the libraries into a 'lib64' subdirectory in the top +source directory. The public header files will be installed into the directory +the value of CMAKE_INSTALL_INCLUDEDIR points to. If you want only the +binaries and no headers, just set -DSQ_DISABLE_HEADER_INSTALLER=ON, and no +header files will be installed. + +Under Windows, it is probably easiest to use the CMake GUI interface, +although invoking CMake from the command line as explained above +should work as well. + +GCC USERS +......................................................... +There is a very simple makefile that compiles all libraries and exes +from the root of the project run 'make' + +for 32 bits systems + + $ make + +for 64 bits systems + + $ make sq64 + +VISUAL C++ USERS +......................................................... +Open squirrel.dsw from the root project directory and build(dho!) + +DOCUMENTATION GENERATION +......................................................... +To be able to compile the documentation, make sure that you have Python +installed and the packages sphinx and sphinx_rtd_theme. Browse into doc/ +and use either the Makefile for GCC-based platforms or make.bat for +Windows platforms. diff --git a/sp/src/vscript/squirrel/COPYRIGHT b/sp/src/vscript/squirrel/COPYRIGHT new file mode 100644 index 0000000000..17d13ac161 --- /dev/null +++ b/sp/src/vscript/squirrel/COPYRIGHT @@ -0,0 +1,21 @@ +Copyright (c) 2003-2017 Alberto Demichelis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------- +END OF COPYRIGHT diff --git a/sp/src/vscript/squirrel/HISTORY b/sp/src/vscript/squirrel/HISTORY new file mode 100644 index 0000000000..f8c29e6bbb --- /dev/null +++ b/sp/src/vscript/squirrel/HISTORY @@ -0,0 +1,533 @@ +***version 3.2 stable*** +-added sq_tailcall +-added rawcall keyword +-added post call initializer syntax +-added table.keys() and table.values() +-added table.filter() +-additional parameters in array.map() and array.apply() +-additional optional initializer in array.reduce() +-closure.call() is now a "native tailcall" and the invoked function can now be suspended +-fixed sq_newmember and sq_rawnewmember properly pop parameters +-fixed capturing free variable on for loop counter before a break statement +-fixed \u in lexer +-various bugfixes + +***version 3.1.1 stable*** +-sq_gettypetag doesn't set last error(it's treated as SQBool function but keeps a SQRESULT for backward compatibility) +-fixed _set method in userdata delegates +-fixed some warnings + +***version 3.1 stable*** +-added slice range for tolower and toupper +-added startswith() and endswith() in string lib +-added SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS to exclude default mem fuction from compilation +-added sq_getreleasehook +-added thread.wakeupthrow() +-added sq_pushthread +-added \u and \U escape sequence for UTF8,UTF16 or UCS4 characters +-added CMake scripts(thx Fabian Wolff) +-the escape character \x is based on sizeof(SQChar) +-fixed several warnings(thx Markus Oberhumer) +-fixed optimizer bug in compound arith oprators(+=,-= etc...) +-fixed sq_getrefvmcount() (thx Gerrit) +-fixed sq_getrefcount() when no references were added with sq_addref() (thx Gerrit) +-fixed bug in string.tointeger() (thx Domingo) +-fixed weakref comparison in 32bit builds using doubles(thx Domingo) +-fixed compiler bug(thx Peter) +-fixed some error in the documentation(thx Alexander) +-fixed some error reporting in compiler(thx Alexander) +-fixed incorrect optional semicolon after "if block"(thx Alexander) +-fixed crash bug in compiler related to compound arith operators(+=,-= etc...) (thx Jeff1) + +***2015-01-10 *** +***version 3.1 RC 1*** +-added new header sqconfig.h for all optional type declarations(unicode, 64bits etc..) +-added sq_setsharedforeignptr sq_getsharedforeignptr +-added sq_setsharedreleasehook sq_getsharedreleasehook +-added escape() in sqstd string library +-added __LINE__ and __FILE__ (thx mingodad) +-widechar support on gcc builds +-now boolean can be used in constants +-reduced dependencies on C runtime library +-newthread and sq_newthread() no longer reinitialize the root table on friend VMs(thx Lucas Cardellini) +-exceptions in the _inherited metamethod are propagated(thx Lucas Cardellini) +-'in' operator performance improvement(thx unagipai and mingodad) +-fixes crash in compiler when trying to write 'base' +-fixed bug in switch statement when using locals as case values (thx mingodad) +-fixed bug in print()(thx Lucas Cardellini) + +***2013-08-30 *** +***version 3.1 beta 1*** +-added new scoping rule(root attached to closures) +-added closure.setroot() closure.getroot() +-added sq_setclosureroot() and sq_getclosureroot() +-added sq_setvmreleasehook() and sq_getvmreleasehook() +-added documentaion for sq_getbase() +-now string.tointeger() accepts an optional parameter 'base' +-now format accepts zeroes in the format string (thx mingodad) +-fixed bug in sqstd_createfile() (thx mingodad) +-minor buxfixes + +***2012-11-10 *** +***version 3.0.4 stable*** +-sq_deleteslot slot now pops the key in case of failure +-fixed bug when _get metamethod throws null +-fixed a bug in rstrip +-added some error handling +-minor bugfixes + +***2012-06-19 *** +***version 3.1.0 alpha 1*** +-changed in and instanceof operator precendence +-root object in closures +-added closure.setroot closure.getroot +-added sq_setclosureroot and sq_getclosureroot + +***version 3.0.3 stable*** +-improved error messages for _cmp(when a non integer value is returned) (thx Yexo) +-added class.newmember() built in method (thx Nam) +-added class.rawnewmember() built in method (thx Nam) +-added sq_rawnewmember() (thx Nam) +-added sq_getversion() +-added sq_typeof() +-added sq_getclosurename() +-added file.close() in stdlib +-documented closure.getinfos() built-in method +-fixed string iteration doesn't return negative numbers for characters > 127 +-fixed bug in tofloat() when converting a string with scientific notation without a decimal point (thx wr2) +-fixed potential infinite loop in array.sort() when the _cmp function is inconsistent (thx Yexo) +-fixed obscure bug in the compiler(thx yishin) +-fixed some minor bug + +***2011-11-28 *** +***version 3.0.2 stable*** +-added sq_gethash API +-now array.sort() is implemented with heapsort +-now floats in scientific notation also accept numbers with no '.' (eg. 1e+6 or 1e6) +-fixed some warning +-fixed some documentation +-fixed bug in GC + +***2011-09-08 *** +***version 3.0.1 stable*** +-added # as alternative symbol for "line comment"(mostly useful for shell scripts) +-added sq_throwobject() to throw an arbitrary object from the C API +-added alignement flag for userdata types, SQ_ALIGNMENT (thx Shigemasa) +-added rawset() and rawget() to class and instance default delegate +-changed bytecode format now ensures matching integer size and float size +-now inherited classes also inherit userdatasize +-added SQUIRREL_VERSION_NUMBER in squirrel.h and _versionnumber_ global symbol +-fixed sq_getmemberhandle +-fixed sq_getrefcount +-refactored some sqstdio code +-refactored some clone code +-refactored some stuff in the string lib +-added -s and -fno-exceptions in GCC makefile(better performance when using GCC) + +***2011-03-13 *** +***version 3.0 stable*** +-added sq_getcallee() +-sq_getfreevariable() also works for native closures +-minior optimizations +-removed several warning when compiling with GCC 4.x +-fixed some errors in the documentation +-fixed bug when using SQUSEDOUBLE and 32bits intengers +-fixed bug when invoking generators with closure.call() (thx huntercool) + +***2010-12-19 *** +***version 3.0 release candidate 1(RC 1)*** +-improved metamethods error handling +-added parameter 'isstatic' to _newmember metamethod(thx G.Meyer) +-added sq_getrefcount() to return number of refences from C++(thx G.Meyer) + +***2010-11-07 *** +***version 3.0 beta 3*** +-license changed to "MIT license" +-added sq_resurrectunreachable() and resurrectunreachable() +-added callee() built in function, returns the current running closure +-added thread.getstackinfos() +-added sq_objtouserpointer() +-added sq_newtableex() +-various refactoring and optimizations +-fixed several 64bits issues regarding integer to string conversions +-fixed some bugs when SQUSEDOUBLE is used in 32bits systems + +***2010-08-18 *** +***version 3.0 beta 2.1*** +-fixed bug in class constructor +-fixed bug in compound arith + +***2010-08-12 *** +***version 3.0 beta 2*** +-class methods can be added or replaced after the class as been instantiated +-JSON compliant table syntax, this is currently an experimental feature (thx atai) +-sq_getsize() now returns userdatasize for classes and instances +-now setroottable() and setconsttable() return the previous value of the respective table +-fixed bug in compound arith operators when used on a free variable (thx ellon) +-fixed some x64 minor bugs +-fixed minor bug in the compiler +-refactored some VM internals +-documented sq_getmemberhandle, sq_getbyhandle, sq_setbyhandle to set and get value from classes + +***2009-11-15 *** +***version 3.0 beta 1*** +-various refactoring and optimizations +-fixed bug in free variables (thx mokehehe) +-fixed bug in functions with default parameters (thx ara & Yexo) +-fixed bug in exception handling +-improved error propagation in _set and _get metamethods ( and 'throw null' for clean failure) +-added sq_getmemberhandle, sq_getbyhandle, sq_setbyhandle to set and get value from classes + +***2009-06-30 *** +***version 3.0 alpha 2*** +-added real free variables(thx Paul Ruizendaal) +-added refactored function call implementation and compiler(thx Paul Ruizendaal) +-added sq_getfunctioninfo +-added compile time flag SQUSEDOUBLE to use double precision floats +-added global slot _floatsize_ int the base lib to recognize single precision and double precision builds +-sq_wakeupvm can now resume the vm with an exception +-added sqstd_format +-now blobs can be cloned +-generators can now be instantiated by calling sq_call() or closure.call() +-fixed debughook bug +-fixed cooroutine error propagation + +***2008-07-23 *** +***version 3.0 alpha 1*** +-first branch from 2.x source tree +-added 'base' keyword +-removed 'delegate' keyword +-now compiled scripts are vararg functions +-added setdelegate() and getdelegate() table builtin methods +-added <=> 3 ways compare operator +-added lambda expression @(a,b) a + b +-added local function statement +-added array built-in map(),reduce(),apply(),filter() and find() +-generators hold only a weak reference of the enviroment object +-removed 'vargv' and 'vargc' keywords +-now var args are passed as an array called vargv(as a paramter) +-removed 'parent' keyword +-added class getbase() built in method +-instanceof doesn't throw an exception if the left expression is not a class +-lexical scoping for free variables(free variables are no longer in the second parameter list) +-sq_setprintfunc accept error func +-sq_geterrorfunc() +-added sq_arrayremove() and sq_arrayinsert() +-error() built in function(works like print but prints using the errorfunc) +-added native debug hook + +***2008-02-17 *** +***version 2.2 stable*** +-added _newslot metamethod in classes +-added enums added constants +-added sq_pushconsttable, sq_setconsttable +-added default param +-added octal literals(thx Dinosaur) +-fixed debug hook, 'calls' and 'returns' are properly notified in the same number. +-fixed a coroutine bug + +***2007-07-29 *** +***version 2.1.2 stable*** +-new behaviour for generators iteration using foreach +now when a generator is iterated by foreach the value returned by a 'return val' statement +will terminate the iteration but will not be returned as foreach iteration +-added sq_setclassudsize() +-added sq_clear() +-added table.clear(), array.clear() +-fixed sq_cmp() (thx jyuill) +-fixed minor bugs + +***2006-08-21 *** +***version 2.1.1 stable*** +-vm refactoring +-optimized internal function memory layout +-new global symbol _version_ (is the version string) +-code size optimization for float literals(on 32bits float builts) +-now the raw ref API(sq_addref etc...) is fully reentrant. +-fixed a bug in sq_getdelegate() now pushes null if the object doesn't have a delegate(thx MatzeB) +-improved C reference performances in NO_GARBAGE_COLLECTOR builds +-sq_getlocal() now enumerates also outer values. +-fixed regexp library for GCC users. + +***2006-03-19 *** +***version 2.1 stable*** +-added static class fields, new keyword static +-added 64bits architecture support +-added global slot _intsize_ int the base lib to recognize 32bits and 64bits builds +-added functions with fixed environment, closure.bindenv() built-in function +-all types except userdata and null implement the tostring() method +-string concatenation now invokes metamethod _tostring +-new metamethods for class objects _newmember and _inherited +-sq_call() sq_resume() sq_wakeupvm() have a new signature +-new C referencing implementation(scales more with the amount of references) +-refactored hash table +-new api functions sq_newslot(),sq_tobool(),sq_getbase(), sq_instanceof(), sq_bindenv() +-the api func sq_createslot was deprecated but still supported in form of C macro on top of sq_newslot +-sq_setreleasehook() now also works for classes +-stream.readstr() and stream.writestr() have been deprecated(this affects file and blob) +-fixed squirrel.h undeclared api calls +-fixed few minor bugs +-SQChar is now defined as wchar_t +-removed warning when building with -Wall -pedantic for GCC users +-added new std io function writeclosuretofile() +-added new std string functions strip(),rstrip(),lstrip() and split() +-regular expressions operators (+,*) now have more POSIX greedyness behaviour +-class constructors are now invoked as normal functions + +***2005-10-02 *** +***version 2.0.5 stable*** +-fixed some 64bits incompatibilities (thx sarge) +-fixed minor bug in the stdlib format() function (thx Rick) +-fixed a bug in dofile() that was preventing to compile empty files +-added new API sq_poptop() & sq_getfreevariable() +-some performance improvements + +***2005-08-14 *** +***version 2.0.4 stable*** +-weak references and related API calls +-added sq_objtobool() +-class instances memory policies improved(1 mem allocation for the whole instance) +-typetags are now declared as SQUserPointer instead of unsigned int +-first pass for 64bits compatibility +-fixed minor bug in the stdio stream +-fixed a bug in format() +-fixed bug in string.tointeger() and string.tofloat() + +***2005-06-24 *** +***version 2.0.3 stable*** +-dofile() and loadfile() in the iolib now can decode ASCII, UTF8 files UCS2 big-endian and little-endian +-sq_setparamscheck() : now typemesk can check for null +-added string escape sequence \xhhhh +-fixed some C++ standard incompatibilities + +***2005-05-15 *** +***version 2.0.2 stable*** +-performances improvements (expecially for GCC users) +-removed all dependencies from C++ exception handling +-various bugfixes + +***2005-04-12 *** +***version 2.0.1 stable*** +-various bugfixes +-sq_setparamscheck() now allows spaces in the typemask + +***2005-04-03 *** +***version 2.0 stable*** +-added API sq_gettypetag() +-added built-in function to the bool type(tointeger, tostring etc...) + +***2005-02-27 *** +***version 2.0 release candidate 1(RC 1)*** +-added API sq_reseterror() +-modified sq_release() +-now class instances can be cloned +-various bufixes + +***2005-01-26 *** +***version 2.0 beta 1*** +-added bool type +-class properties can be redefined in a derived class +-added ops *= /= and %= +-new syntax for class attributes declaration instead of ( and ) +-increased the max number of literals per function from 65535 to 16777215 +-now free variables have proper lexical scoping +-added API sq_createinstance(), sq_pushbool(), sq_getbool() +-added built-in function type() +-added built-in function obj.rawin(key) in table,class and instance +-sq_rawget() and sq_rawset() now work also on classes and instances +-the VM no longer uses C++ exception handling (more suitable for embedded devices) +-various bufixes + +***2004-12-21 *** +***version 2.0 alpha 2*** +-globals scoping changed, now if :: is omitted the VM automatically falls back on the root table +-various bufixes +-added class level attributes + +***2004-12-12 *** +***version 2.0 alpha 1*** +-codebase branch from version 1.x +-added classes +-added functions with variable number of parameters(vargc & vargv and the ...) +-0 and 0.0 are now considered 'false' by all conditional statements(if,while,for,?,do-while) +-added new api functions sq_newclass() sq_setinstanceup() sq_getinstanceup() sq_getattributes() sq_setattributes() +-modified api sq_settypetag() + +***2004-11-01 *** +***version 1.0 stable*** +-fixed some minor bug +-improved operator 'delete' performances +-added scientific notation for float numbers( eg. 2.e16 or 2.e-2) + +***2004-08-30 *** +***version 1.0 release candidate 2(RC 2)*** +-fixed bug in the vm(thx Pierre Renaux) +-fixed bug in the optimizer(thx Pierre Renaux) +-fixed some bug in the documentation(thx JD) +-added new api functions for raw object handling +-removed nested multiline comments +-reduced memory footprint in C references + +***2004-08-23 *** +***version 1.0 release candidate 1(RC 1)*** +-fixed division by zero +-the 'in' operator and obj.rawget() do not query the default delegate anymore +-added function sq_getprintfunc() +-added new standard library 'auxlib'(implements default error handlers) + +***2004-07-12 *** +***version 1.0 beta 4*** +-fixed a bug in the integer.tochar() built-in method +-fixed unary minus operator +-fixed bug in dofile() +-fixed inconsistency between != and == operators(on float/integer comparison) +-added javascript style unsigned right shift operator '>>>' +-added array(size) constructor built-in function +-array.resize(size,[fill]) built-in function accepts an optional 'fill' value +-improved debug API, added sq_getclosureinfo() and sq_setnativeclosurename() + +***2004-05-23 *** +***version 1.0 beta 3*** +-minor vm bug fixes +-string allocation is now faster +-tables and array memory usage is now less conservative(they shrink) +-added regular expression routines in the standard library +-The 'c' expression now accepts only 1 character(thx irbrian) +-multiline strings <[ ]> have been substituted with C# style verbatim strings (eg. @"string") +-added new keyword 'parent' for accessing the delegate of tables and unserdata +-The metamethod '_clone' has been renamed '_cloned' +-the _delslot metamethod's behaviour and prototype have been changed +-new default function in the integer and float object 'tochar()' +-the built-in function chcode2string has been removed +-the default method [table].getdelegate() has been removed +-new api sq_rawdeleteslot() +-new table built-in method rawdelete(key) +-the dynamic mudule loading has been removed from the standard distribution +-some optimizations in the VM + +***2004-04-21 *** +***version 1.0 beta 2*** +-minor compiler/parser bug fixes +-sq_newclosure has a different prototype, the "paramscheck" of paramter has been moved to the new function sq_setparamscheck() +-sq_setparamscheck allows to add automatic parameters type checking in native closures +-sq_compile() lost the lineinfo parameter +-new api sq_enabledebuginfo() globally sets compiler's debug info generation +-added consistency check on bytecode serialization +-fixed += operator, now works on strings like + +-added global slot in the base lib _charsize_ to recognize unicode builds from ascii builds runtime +-added registry table +-new api call sq_pushregistrytable() +-added type tag to the userdata type sq_settypetag() +-sq_getuserdata now queries the userdata typetag +-the built in function collect_garbage() as been renamed collectgarbage() for consistency reasons +-new standard libraries(sqlibs are now obsolete) + +***2004-02-20 *** +***version 1.0 beta 1*** +-fixed a bug in the compiler (thanks Martin Kofler) +-fixed bug in the switch case statement +-fixed the _unm metamethod +-fixed minor bugs in the API +-fixed automatic stack resizing +-first beta version + first pass code clean up in the VM and base lib + first pass code coverege test has been done on VM and built-in lib +-new VM creation API sq_open() sq_close() (sq_newvm and sq_releasevm are now obsolete) +-new api allows to specifiy a "print" function to output text(sq_printfunc) +-added some small optimizations +-new cooperative multi-threading capabilities in the base library(coroutines), VMs are now a built in type("thread") +-new built in functions have been added for manipulating the new "thread" type +-friend virtual machines share the same root table, error handler and debug hook by default +-new compile time options + +***2004-01-19 *** +***version 0.9 alpha*** +-fixed a garbage collection bug +-fixed some API bugs(thanks to Joshua Jensen) +-fixed tail calls (in the version 0.8 the tail call optimization was erroneously disabled) +-new function parameters semantic, now passing a wrong number of parameters generates an exception +-native closures have now a built in parameter number checking +-sq_rawget and sq_rawset now work also on arrays +-sq_getsize now woks also on userdata +-the userdata release hook prototype is changed(now passes the size of the userdata) +-the lexer reader function now returns an integer instead of a char that allows better error checking on the input(thx Joshua Jensen) +-faster compiler +-try/catch blocks do not cause any runtime memory allocation anymore + +***2003-12-06 *** +***version 0.8 alpha*** +-fixed a bug that was preventing to have callable userdata throught the metamethod _call +-fixed a garbage collection bug +-fixed == operator now can compare correctly different types +-new built in method getstackinfos(level) +-improved line informations precision for the debug hook +-new api call sq_compilebuffer() +-new built-in api function compilestring() +-new syntactic sugar for function declarations inside tables +-the debug API has been finalized + +***2003-11-17 *** +***version 0.7 alpha*** +-fixed critical bug SQInteger the tail call system +-fixed bug in the continue statement code generation +-fixed func call param issue(thanks to Rewoonenco Andrew) +-added _delslot metamethod(thanks to Rewoonenco Andrew) +-new multiline string expression ( delimited by <[ and ]> ) +-normal strings ("") do not allow embedded new line anymore +-reduced vm memory footprint(C refs are shared between friend VMs) +-new api method sq_deleteslot() +-new debug hook event 'r' is triggered when a function returns + +***2003-11-04 *** +***version 0.6 alpha*** +-fixed switch statement(was executing the default case after a break) +-sq_call() doesn't pop the closure (just the params) +-the vm execution can be suspended from the C API anytime (micro-threads) +-new api calls sq_suspendvm() sq_wakeupvm() sq_getvmstate() and sq_reservestack() + +***2003-10-13 *** +***version 0.5 alpha*** +-fixed some minor bug +-tested with non ASCII identifiers in unicode mode(I've tried chinese chars) +-added built-in function string.find() +-the built-in function array.sort() optionally accepts a cmp(a,b) function +-the debug hook function now has a new prototype debug_hook(event_type,sourcefile,line,functionname) +-fixed some debug info imprecision + +***2003-10-01 *** +***version 0.4 alpha*** +-faster VM +-sq_call will pop arguments and closure also in case of failure +-fixed a bug in sq_remove +-now the VM detects delegation cycles(and throws an exception) +-new operators ++ and -- +-new operator ',' comma operator +-fixed some expression precedence issue +-fixed bug in sq_arraypop + +***2003-09-15 *** +***version 0.3 alpha*** +-fixed a bug in array::insert() +-optional Unicode core(define SQUNICODE or _UNICODE on Win32) +-sq_compiler uses a new reader function SQLEXREADFUNC +-the debug hook passes 'l' instead of 'line' for line callbacks + and 'c' instead of 'call' for call callbacks +-new array.extend() bulit-in function +-new API sq_clone() + +***2003-09-10 *** +***version 0.2 pre-alpha*** +-new completely reentrant VM (sq_open and sq_close are now obsolete) +-sq_newvm() has a new prototype +-allocators are now global and linked in the VM +-_newslot meta method added +-rawset creates a slot if doesn't exists +-the compiler error callback pass the vm handle(thanks Pierre Renaux) +-sq_setforeignptr() sq_getforeingptr() are now public +-sq_resume() now is possible to resume generators from C +-sq_getlasterror() retrieve the last thrown error +-improved docs + +***2003-09-06 *** +***version 0.1 pre-alpha*** +first release diff --git a/sp/src/vscript/squirrel/Makefile b/sp/src/vscript/squirrel/Makefile new file mode 100644 index 0000000000..2ed97e26a8 --- /dev/null +++ b/sp/src/vscript/squirrel/Makefile @@ -0,0 +1,22 @@ + +SQUIRREL=. +MAKE=make + +sq32: folders + cd squirrel; $(MAKE) + cd sqstdlib; $(MAKE) + cd sq; $(MAKE) + +sqprof: folders + cd squirrel; $(MAKE) sqprof + cd sqstdlib; $(MAKE) sqprof + cd sq; $(MAKE) sqprof + +sq64: folders + cd squirrel; $(MAKE) sq64 + cd sqstdlib; $(MAKE) sq64 + cd sq; $(MAKE) sq64 + +folders: + mkdir -p lib + mkdir -p bin diff --git a/sp/src/vscript/squirrel/README b/sp/src/vscript/squirrel/README new file mode 100644 index 0000000000..298aec75ae --- /dev/null +++ b/sp/src/vscript/squirrel/README @@ -0,0 +1,33 @@ +The programming language SQUIRREL 3.1 stable + +-------------------------------------------------- +This project has successfully been compiled and run on + * Windows (x86 and amd64) + * Linux (x86, amd64 and ARM) + * Illumos (x86 and amd64) + * FreeBSD (x86 and ARM) + +The following compilers have been confirmed to be working: + MS Visual C++ 6.0 (all on x86 and amd64) + 7.0 | + 7.1 v + 8.0 + 9.0 + 10.0 + 12.0 --- + MinGW gcc 3.2 (mingw special 20020817-1) + Cygnus gcc 3.2 + Linux gcc 3.2.3 + 4.0.0 (x86 and amd64) + 5.3.1 (amd64) + Illumos gcc 4.0.0 (x86 and amd64) + ARM Linux gcc 4.6.3 (Raspberry Pi Model B) + + +Feedback and suggestions are appreciated +project page - http://www.squirrel-lang.org +community forums - http://forum.squirrel-lang.org +wiki - http://wiki.squirrel-lang.org +author - alberto@demichelis.net + +END OF README diff --git a/sp/src/vscript/squirrel/appveyor.yml b/sp/src/vscript/squirrel/appveyor.yml new file mode 100644 index 0000000000..4da9b37bdd --- /dev/null +++ b/sp/src/vscript/squirrel/appveyor.yml @@ -0,0 +1,28 @@ +version: 0.0.{build} + +platform: + - x86 + - x64 + +configuration: + - Debug + - Release + +clone_folder: c:\sq + +before_build: + - mkdir build + - cd build + - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %platform% + - echo %platform% + - if %platform%==X64 (cmake .. -G "Visual Studio 14 2015 Win64") + - if %platform%==x86 (cmake .. -G "Visual Studio 14 2015") + +build_script: + - cmake --build . --config %configuration% -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + +artifacts: + - path: build\*\%configuration%\*.exe + - path: build\*\%configuration%\*.dll + +test: off diff --git a/sp/src/vscript/squirrel/doc/Makefile b/sp/src/vscript/squirrel/doc/Makefile new file mode 100644 index 0000000000..b01e98db52 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/Makefile @@ -0,0 +1,216 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/testy_sphinxy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/testy_sphinxy.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/testy_sphinxy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/testy_sphinxy" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/sp/src/vscript/squirrel/doc/make.bat b/sp/src/vscript/squirrel/doc/make.bat new file mode 100644 index 0000000000..a32fa10a43 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\testy_sphinxy.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\testy_sphinxy.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/sp/src/vscript/squirrel/doc/source/_static/nut.ico b/sp/src/vscript/squirrel/doc/source/_static/nut.ico new file mode 100644 index 0000000000..c28977b020 Binary files /dev/null and b/sp/src/vscript/squirrel/doc/source/_static/nut.ico differ diff --git a/sp/src/vscript/squirrel/doc/source/_static/simple_nut.png b/sp/src/vscript/squirrel/doc/source/_static/simple_nut.png new file mode 100644 index 0000000000..4c0e8a20f4 Binary files /dev/null and b/sp/src/vscript/squirrel/doc/source/_static/simple_nut.png differ diff --git a/sp/src/vscript/squirrel/doc/source/conf.py b/sp/src/vscript/squirrel/doc/source/conf.py new file mode 100644 index 0000000000..996eafefdf --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/conf.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# +# Squirrel documentation build configuration file, created by +# sphinx-quickstart on Sun Jan 31 00:26:52 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import time + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Squirrel documentation' +copyright = '2003-%s, Alberto Demichelis' % time.strftime('%Y') +author = u'Alberto Demichelis' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'3.1' +# The full version, including alpha/beta/rc tags. +release = u'3.1 stable' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'simple_nut.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'nut.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'squirrel_doc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +_stdauthor = r'Alberto Demichelis' +latex_documents = [ + ('reference/index', 'reference.tex', + 'Squirrel Reference Manual', _stdauthor, 'manual'), + ('stdlib/index', 'stdlib.tex', + 'Squirrel Standard Library', _stdauthor, 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'Squirrel', u'Squirrel Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Squirrel', u'Squirrel Documentation', + author, 'Squirrel', 'The Programming Language.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/sp/src/vscript/squirrel/doc/source/index.rst b/sp/src/vscript/squirrel/doc/source/index.rst new file mode 100644 index 0000000000..0cc1bb4d9f --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/index.rst @@ -0,0 +1,24 @@ +.. Squirrel documentation master file, created by + sphinx-quickstart on Sun Jan 31 00:26:52 2016. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Squirrel's documentation +========================================= + +Contents: + +.. toctree:: + :maxdepth: 1 + + reference/index.rst + stdlib/index.rst + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + + diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst b/sp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst new file mode 100644 index 0000000000..0e1f586a47 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/bytecode_serialization.rst @@ -0,0 +1,32 @@ +.. _api_ref_bytecode_serialization: + +====================== +Bytecode serialization +====================== + +.. _sq_readclosure: + +.. c:function:: SQRESULT sq_readclosure(HSQUIRRELVM v, SQREADFUNC readf, SQUserPointer up) + + :param HSQUIRRELVM v: the target VM + :param SQREADFUNC readf: pointer to a read function that will be invoked by the vm during the serialization. + :param SQUserPointer up: pointer that will be passed to each call to the read function + :returns: a SQRESULT + +serialize (read) a closure and pushes it on top of the stack, the source is user defined through a read callback. + + + + + +.. _sq_writeclosure: + +.. c:function:: SQRESULT sq_writeclosure(HSQUIRRELVM v, SQWRITEFUNC writef, SQUserPointer up) + + :param HSQUIRRELVM v: the target VM + :param SQWRITEFUNC writef: pointer to a write function that will be invoked by the vm during the serialization. + :param SQUserPointer up: pointer that will be passed to each call to the write function + :returns: a SQRESULT + :remarks: closures with free variables cannot be serialized + +serializes(writes) the closure on top of the stack, the destination is user defined through a write callback. diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/calls.rst b/sp/src/vscript/squirrel/doc/source/reference/api/calls.rst new file mode 100644 index 0000000000..7ba43fb62c --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/calls.rst @@ -0,0 +1,130 @@ +.. _api_ref_calls: + +===== +Calls +===== + +.. _sq_call: + +.. c:function:: SQRESULT sq_call(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param SQInteger params: number of parameters of the function + :param SQBool retval: if true the function will push the return value in the stack + :param SQBool raiseerror: if true, if a runtime error occurs during the execution of the call, the vm will invoke the error handler. + :returns: a SQRESULT + +calls a closure or a native closure. The function pops all the parameters and leave the closure in the stack; if retval is true the return value of the closure is pushed. If the execution of the function is suspended through sq_suspendvm(), the closure and the arguments will not be automatically popped from the stack. + +When using to create an instance, push a dummy parameter to be filled with the newly-created instance for the constructor's 'this' parameter. + + + +.. _sq_getcallee: + +.. c:function:: SQRESULT sq_getcallee(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: a SQRESULT + +push in the stack the currently running closure. + + + + + +.. _sq_getlasterror: + +.. c:function:: SQRESULT sq_getlasterror(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: a SQRESULT + :remarks: the pushed error descriptor can be any valid squirrel type. + +pushes the last error in the stack. + + + + + +.. _sq_getlocal: + +.. c:function:: const SQChar * sq_getlocal(HSQUIRRELVM v, SQUnsignedInteger level, SQUnsignedInteger nseq) + + :param HSQUIRRELVM v: the target VM + :param SQUnsignedInteger level: the function index in the calls stack, 0 is the current function + :param SQUnsignedInteger nseq: the index of the local variable in the stack frame (0 is 'this') + :returns: the name of the local variable if a variable exists at the given level/seq otherwise NULL. + +Returns the name of a local variable given stackframe and sequence in the stack and pushes is current value. Free variables are treated as local variables, by sq_getlocal(), and will be returned as they would be at the base of the stack, just before the real local variables. + + + + + +.. _sq_reseterror: + +.. c:function:: void sq_reseterror(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + +reset the last error in the virtual machine to null + + + + + +.. _sq_resume: + +.. c:function:: SQRESULT sq_resume(HSQUIRRELVM v, SQBool retval, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param SQBool retval: if true the function will push the return value in the stack + :param SQBool raiseerror: if true, if a runtime error occurs during the execution of the call, the vm will invoke the error handler. + :returns: a SQRESULT + :remarks: if retval != 0 the return value of the generator is pushed. + +resumes the generator at the top position of the stack. + + +.. _sq_tailcall: + +.. c:function:: SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) + + :param HSQUIRRELVM v: the target VM + :param SQInteger params: number of parameters of the function + + Calls a closure and removes the caller function from the call stack. + This function must be invoke from a native closure and + he return value of sq_tailcall must be returned by the caller function(see example). + +*.eg* + +:: + + SQInteger tailcall_something_example(HSQUIRRELVM v) + { + //push closure and parameters here + ... + return sq_tailcall(v,2); + } + +.. _sq_throwerror: + +.. c:function:: SQRESULT sq_throwerror(HSQUIRRELVM v, const SQChar * err) + + :param HSQUIRRELVM v: the target VM + :param const SQChar * err: the description of the error that has to be thrown + :returns: the value that has to be returned by a native closure in order to throw an exception in the virtual machine. + +sets the last error in the virtual machine and returns the value that has to be returned by a native closure in order to trigger an exception in the virtual machine. + + +.. _sq_throwobject: + +.. c:function:: SQRESULT sq_throwobject(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: the value that has to be returned by a native closure in order to throw an exception in the virtual machine. + +pops a value from the stack sets it as the last error in the virtual machine. Returns the value that has to be returned by a native closure in order to trigger an exception in the virtual machine (aka SQ_ERROR). diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/compiler.rst b/sp/src/vscript/squirrel/doc/source/reference/api/compiler.rst new file mode 100644 index 0000000000..7fcb933d35 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/compiler.rst @@ -0,0 +1,79 @@ +.. _api_ref_compiler: + +======== +Compiler +======== + +.. _sq_compile: + +.. c:function:: SQRESULT sq_compile(HSQUIRRELVM v, HSQLEXREADFUNC read, SQUserPointer p, const SQChar * sourcename, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param HSQLEXREADFUNC read: a pointer to a read function that will feed the compiler with the program. + :param SQUserPointer p: a user defined pointer that will be passed by the compiler to the read function at each invocation. + :param const SQChar * sourcename: the symbolic name of the program (used only for more meaningful runtime errors) + :param SQBool raiseerror: if this value is true the compiler error handler will be called in case of an error + :returns: a SQRESULT. If the sq_compile fails nothing is pushed in the stack. + :remarks: in case of an error the function will call the function set by sq_setcompilererrorhandler(). + +compiles a squirrel program; if it succeeds, push the compiled script as function in the stack. + + + + + +.. _sq_compilebuffer: + +.. c:function:: SQRESULT sq_compilebuffer(HSQUIRRELVM v, const SQChar* s, SQInteger size, const SQChar * sourcename, SQBool raiseerror) + + :param HSQUIRRELVM v: the target VM + :param const SQChar* s: a pointer to the buffer that has to be compiled. + :param SQInteger size: size in characters of the buffer passed in the parameter 's'. + :param const SQChar * sourcename: the symbolic name of the program (used only for more meaningful runtime errors) + :param SQBool raiseerror: if this value true the compiler error handler will be called in case of an error + :returns: a SQRESULT. If the sq_compilebuffer fails nothing is pushed in the stack. + :remarks: in case of an error the function will call the function set by sq_setcompilererrorhandler(). + +compiles a squirrel program from a memory buffer; if it succeeds, push the compiled script as function in the stack. + + + + + +.. _sq_enabledebuginfo: + +.. c:function:: void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable) + + :param HSQUIRRELVM v: the target VM + :param SQBool enable: if true enables the debug info generation, if == 0 disables it. + :remarks: The function affects all threads as well. + +enable/disable the debug line information generation at compile time. + + + + + +.. _sq_notifyallexceptions: + +.. c:function:: void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) + + :param HSQUIRRELVM v: the target VM + :param SQBool enable: if true enables the error callback notification of handled exceptions. + :remarks: By default the VM will invoke the error callback only if an exception is not handled (no try/catch traps are present in the call stack). If notifyallexceptions is enabled, the VM will call the error callback for any exception even if between try/catch blocks. This feature is useful for implementing debuggers. + +enable/disable the error callback notification of handled exceptions. + + + + + +.. _sq_setcompilererrorhandler: + +.. c:function:: void sq_setcompilererrorhandler(HSQUIRRELVM v, SQCOMPILERERROR f) + + :param HSQUIRRELVM v: the target VM + :param SQCOMPILERERROR f: A pointer to the error handler function + :remarks: if the parameter f is NULL no function will be called when a compiler error occurs. The compiler error handler is shared between friend VMs. + +sets the compiler error handler function diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst b/sp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst new file mode 100644 index 0000000000..ac929e143d --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/debug_interface.rst @@ -0,0 +1,72 @@ +.. _api_ref_debug_interface: + +=============== +Debug interface +=============== + +.. _sq_getfunctioninfo: + +.. c:function:: SQRESULT sq_getfunctioninfo(HSQUIRRELVM v, SQInteger level, SQFunctionInfo * fi) + + :param HSQUIRRELVM v: the target VM + :param SQInteger level: calls stack level + :param SQFunctionInfo * fi: pointer to the SQFunctionInfo structure that will store the closure informations + :returns: a SQRESULT. + :remarks: the member 'funcid' of the returned SQFunctionInfo structure is a unique identifier of the function; this can be useful to identify a specific piece of squirrel code in an application like for instance a profiler. this method will fail if the closure in the stack is a native C closure. + + + +*.eg* + +:: + + + typedef struct tagSQFunctionInfo { + SQUserPointer funcid; //unique idetifier for a function (all it's closures will share the same funcid) + const SQChar *name; //function name + const SQChar *source; //function source file name + }SQFunctionInfo; + + + + + + + +.. _sq_setdebughook: + +.. c:function:: void sq_setdebughook(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :remarks: In order to receive a 'per line' callback, is necessary to compile the scripts with the line informations. Without line informations activated, only the 'call/return' callbacks will be invoked. + +pops a closure from the stack an sets it as debug hook. When a debug hook is set it overrides any previously set native or non native hooks. if the hook is null the debug hook will be disabled. + + + + + +.. _sq_setnativedebughook: + +.. c:function:: void sq_setnativedebughook(HSQUIRRELVM v, SQDEBUGHOOK hook) + + :param HSQUIRRELVM v: the target VM + :param SQDEBUGHOOK hook: the native hook function + :remarks: In order to receive a 'per line' callback, is necessary to compile the scripts with the line informations. Without line informations activated, only the 'call/return' callbacks will be invoked. + +sets the native debug hook. When a native hook is set it overrides any previously set native or non native hooks. if the hook is NULL the debug hook will be disabled. + + + + + +.. _sq_stackinfos: + +.. c:function:: SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos * si) + + :param HSQUIRRELVM v: the target VM + :param SQInteger level: calls stack level + :param SQStackInfos * si: pointer to the SQStackInfos structure that will store the stack informations + :returns: a SQRESULT. + +retrieve the calls stack informations of a ceratain level in the calls stack. diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst b/sp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst new file mode 100644 index 0000000000..cfe03e68e0 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/garbage_collector.rst @@ -0,0 +1,27 @@ +.. _api_ref_garbage_collector: + +================= +Garbage Collector +================= + +.. _sq_collectgarbage: + +.. c:function:: SQInteger sq_collectgarbage(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :remarks: this api only works with garbage collector builds (NO_GARBAGE_COLLECTOR is not defined) + +runs the garbage collector and returns the number of reference cycles found (and deleted) + + + + + +.. _sq_resurrectunreachable: + +.. c:function:: SQRESULT sq_resurrectunreachable(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :remarks: this api only works with garbage collector builds (NO_GARBAGE_COLLECTOR is not defined) + +runs the garbage collector and pushes an array in the stack containing all unreachable object found. If no unreachable object is found, null is pushed instead. This function is meant to help debug reference cycles. diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst b/sp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst new file mode 100644 index 0000000000..5345c05c63 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/object_creation_and_handling.rst @@ -0,0 +1,695 @@ +.. _api_ref_object_creation_and_handling: + +============================ +Object creation and handling +============================ + +.. _sq_bindenv: + +.. c:function:: SQRESULT sq_bindenv(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: a SQRESULT + :remarks: the cloned closure holds the environment object as weak reference + +pops an object from the stack (must be a table, instance, or class); clones the closure at position idx in the stack and sets the popped object as environment of the cloned closure. Then pushes the new cloned closure on top of the stack. + + + + + +.. _sq_createinstance: + +.. c:function:: SQRESULT sq_createinstance(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class + :returns: a SQRESULT + :remarks: the function doesn't invoke the instance contructor. To create an instance and automatically invoke its contructor, sq_call must be used instead. + +creates an instance of the class at 'idx' position in the stack. The new class instance is pushed on top of the stack. + + + + + +.. _sq_getbool: + +.. c:function:: SQRESULT sq_getbool(HSQUIRRELVM v, SQInteger idx, SQBool * b) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQBool * b: A pointer to the bool that will store the value + :returns: a SQRESULT + +gets the value of the bool at the idx position in the stack. + + + + + +.. _sq_getbyhandle: + +.. c:function:: SQRESULT sq_getbyhandle(HSQUIRRELVM v, SQInteger idx, HSQMEMBERHANDLE* handle) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class or instance + :param HSQMEMBERHANDLE* handle: a pointer to the member handle + :returns: a SQRESULT + +pushes the value of a class or instance member using a member handle (see sq_getmemberhandle) + + + + + +.. _sq_getclosureinfo: + +.. c:function:: SQRESULT sq_getclosureinfo(HSQUIRRELVM v, SQInteger idx, SQInteger * nparams, SQInteger * nfreevars) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :param SQInteger * nparams: a pointer to an integer that will store the number of parameters + :param SQInteger * nfreevars: a pointer to an integer that will store the number of free variables + :returns: an SQRESULT + +retrieves number of parameters and number of freevariables from a squirrel closure. + + + + + +.. _sq_getclosurename: + +.. c:function:: SQRESULT sq_getclosurename(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: an SQRESULT + +pushes the name of the closure at position idx in the stack. Note that the name can be a string or null if the closure is anonymous or a native closure with no name assigned to it. + + + + + +.. _sq_getclosureroot: + +.. c:function:: SQRESULT sq_getclosureroot(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: an SQRESULT + +pushes the root table of the closure at position idx in the stack + + + + + +.. _sq_getfloat: + +.. c:function:: SQRESULT sq_getfloat(HSQUIRRELVM v, SQInteger idx, SQFloat * f) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQFloat * f: A pointer to the float that will store the value + :returns: a SQRESULT + +gets the value of the float at the idx position in the stack. + + + + + +.. _sq_gethash: + +.. c:function:: SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: the hash key of the value at the position idx in the stack + :remarks: the hash value function is the same used by the VM. + +returns the hash key of a value at the idx position in the stack. + + + + + +.. _sq_getinstanceup: + +.. c:function:: SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer * up, SQUSerPointer typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * up: a pointer to the userpointer that will store the result + :param SQUSerPointer typetag: the typetag that has to be checked, if this value is set to 0 the typetag is ignored. + :returns: a SQRESULT + +gets the userpointer of the class instance at position idx in the stack. if the parameter 'typetag' is different than 0, the function checks that the class or a base class of the instance is tagged with the specified tag; if not the function fails. If 'typetag' is 0 the function will ignore the tag check. + + + + + +.. _sq_getinteger: + +.. c:function:: SQRESULT sq_getinteger(HSQUIRRELVM v, SQInteger idx, SQInteger * i) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQInteger * i: A pointer to the integer that will store the value + :returns: a SQRESULT + +gets the value of the integer at the idx position in the stack. + + + + + +.. _sq_getmemberhandle: + +.. c:function:: SQRESULT sq_getmemberhandle(HSQUIRRELVM v, SQInteger idx, HSQMEMBERHANDLE* handle) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class + :param HSQMEMBERHANDLE* handle: a pointer to the variable that will store the handle + :returns: a SQRESULT + :remarks: This method works only with classes. A handle retrieved through a class can be later used to set or get values from one of the class instances. Handles retrieved from base classes are still valid in derived classes and respect inheritance rules. + +pops a value from the stack and uses it as index to fetch the handle of a class member. The handle can be later used to set or get the member value using sq_getbyhandle(), sq_setbyhandle(). + + + + + +.. _sq_getreleasehook: + +.. c:function:: SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :remarks: if the object that position idx is not an userdata, class instance or class the function returns NULL. + +gets the release hook of the userdata, class instance or class at position idx in the stack. + + + + + +.. _sq_getscratchpad: + +.. c:function:: SQChar * sq_getscratchpad(HSQUIRRELVM v, SQInteger minsize) + + :param HSQUIRRELVM v: the target VM + :param SQInteger minsize: the requested size for the scratchpad buffer + :remarks: the buffer is valid until the next call to sq_getscratchpad + +returns a pointer to a memory buffer that is at least as big as minsize. + + + + + +.. _sq_getsize: + +.. c:function:: SQObjectType sq_getsize(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: the size of the value at the position idx in the stack + :remarks: this function only works with strings, arrays, tables, classes, instances, and userdata if the value is not a valid type, the function will return -1. + +returns the size of a value at the idx position in the stack. If the value is a class or a class instance the size returned is the size of the userdata buffer (see sq_setclassudsize). + + + + + +.. _sq_getstring: + +.. c:function:: SQRESULT sq_getstring(HSQUIRRELVM v, SQInteger idx, const SQChar ** c) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param const SQChar ** c: a pointer to the pointer that will point to the string + :returns: a SQRESULT + +gets a pointer to the string at the idx position in the stack. + + + + + +.. _sq_getstringandsize: + +.. c:function:: SQRESULT sq_getstringandsize(HSQUIRRELVM v, SQInteger idx, const SQChar ** c, SQInteger* size) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param const SQChar ** c: a pointer to the pointer that will point to the string + :param SQInteger * size: a pointer to a SQInteger which will receive the size of the string + :returns: a SQRESULT + +gets a pointer to the string at the idx position in the stack; additionally retrieves its size. + + + + +.. _sq_getthread: + +.. c:function:: SQRESULT sq_getthread(HSQUIRRELVM v, SQInteger idx, HSQUIRRELVM* v) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param HSQUIRRELVM* v: A pointer to the variable that will store the thread pointer + :returns: a SQRESULT + +gets a pointer to the thread the idx position in the stack. + + + + + +.. _sq_gettype: + +.. c:function:: SQObjectType sq_gettype(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: the type of the value at the position idx in the stack + +returns the type of the value at the position idx in the stack + + + + + +.. _sq_gettypetag: + +.. c:function:: SQRESULT sq_gettypetag(HSQUIRRELVM v, SQInteger idx, SQUserPointer * typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * typetag: a pointer to the variable that will store the tag + :returns: a SQRESULT + :remarks: the function works also with instances. if the taget object is an instance, the typetag of it's base class is fetched. + +gets the typetag of the object (userdata or class) at position idx in the stack. + + + + + +.. _sq_getuserdata: + +.. c:function:: SQRESULT sq_getuserdata(HSQUIRRELVM v, SQInteger idx, SQUserPointer * p, SQUserPointer * typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * p: A pointer to the userpointer that will point to the userdata's payload + :param SQUserPointer * typetag: A pointer to a SQUserPointer that will store the userdata tag(see sq_settypetag). The parameter can be NULL. + :returns: a SQRESULT + +gets a pointer to the value of the userdata at the idx position in the stack. + + + + + +.. _sq_getuserpointer: + +.. c:function:: SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer * p) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer * p: A pointer to the userpointer that will store the value + :returns: a SQRESULT + +gets the value of the userpointer at the idx position in the stack. + + + + + +.. _sq_newarray: + +.. c:function:: void sq_newarray(HSQUIRRELVM v, SQInteger size) + + :param HSQUIRRELVM v: the target VM + :param SQInteger size: the size of the array that as to be created + +creates a new array and pushes it in the stack + + + + + +.. _sq_newclass: + +.. c:function:: SQRESULT sq_newclass(HSQUIRRELVM v, SQBool hasbase) + + :param HSQUIRRELVM v: the target VM + :param SQBool hasbase: if the parameter is true the function expects a base class on top of the stack. + :returns: a SQRESULT + +creates a new class object. If the parameter 'hasbase' is different than 0, the function pops a class from the stack and inherits the new created class from it. The new class is pushed in the stack. + + + + + +.. _sq_newclosure: + +.. c:function:: void sq_newclosure(HSQUIRRELVM v, HSQFUNCTION func, SQInteger nfreevars) + + :param HSQUIRRELVM v: the target VM + :param HSQFUNCTION func: a pointer to a native-function + :param SQInteger nfreevars: number of free variables(can be 0) + +create a new native closure, pops n values set those as free variables of the new closure, and push the new closure in the stack. + + + + + +.. _sq_newtable: + +.. c:function:: void sq_newtable(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + +creates a new table and pushes it in the stack + + + + + +.. _sq_newtableex: + +.. c:function:: void sq_newtableex(HSQUIRRELVM v, SQInteger initialcapacity) + + :param HSQUIRRELVM v: the target VM + :param SQInteger initialcapacity: number of key/value pairs to preallocate + +creates a new table and pushes it in the stack. This function allows you to specify the initial capacity of the table to prevent unnecessary rehashing when the number of slots required is known at creation-time. + + + + + +.. _sq_newuserdata: + +.. c:function:: SQUserPointer sq_newuserdata(HSQUIRRELVM v, SQUnsignedInteger size) + + :param HSQUIRRELVM v: the target VM + :param SQUnsignedInteger size: the size of the userdata that as to be created in bytes + +creates a new userdata and pushes it in the stack + + + + + +.. _sq_pushbool: + +.. c:function:: void sq_pushbool(HSQUIRRELVM v, SQBool b) + + :param HSQUIRRELVM v: the target VM + :param SQBool b: the bool that has to be pushed(SQTrue or SQFalse) + +pushes a bool into the stack + + + + + +.. _sq_pushfloat: + +.. c:function:: void sq_pushfloat(HSQUIRRELVM v, SQFloat f) + + :param HSQUIRRELVM v: the target VM + :param SQFloat f: the float that has to be pushed + +pushes a float into the stack + + + + + +.. _sq_pushinteger: + +.. c:function:: void sq_pushinteger(HSQUIRRELVM v, SQInteger n) + + :param HSQUIRRELVM v: the target VM + :param SQInteger n: the integer that has to be pushed + +pushes an integer into the stack + + + + + +.. _sq_pushnull: + +.. c:function:: void sq_pushnull(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + +pushes a null value into the stack + + + + + +.. _sq_pushstring: + +.. c:function:: void sq_pushstring(HSQUIRRELVM v, const SQChar * s, SQInteger len) + + :param HSQUIRRELVM v: the target VM + :param const SQChar * s: pointer to the string that has to be pushed + :param SQInteger len: length of the string pointed by s + :remarks: if the parameter len is less than 0 the VM will calculate the length using strlen(s) + +pushes a string in the stack + + + + + +.. _sq_pushuserpointer: + +.. c:function:: void sq_pushuserpointer(HSQUIRRELVM v, SQUserPointer p) + + :param HSQUIRRELVM v: the target VM + :param SQUserPointer p: the pointer that as to be pushed + +pushes a userpointer into the stack + + + + + +.. _sq_setbyhandle: + +.. c:function:: SQRESULT sq_setbyhandle(HSQUIRRELVM v, SQInteger idx, HSQMEMBERHANDLE* handle) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class + :param HSQMEMBERHANDLE* handle: a pointer the member handle + :returns: a SQRESULT + +pops a value from the stack and sets it to a class or instance member using a member handle (see sq_getmemberhandle) + + + + + +.. _sq_setclassudsize: + +.. c:function:: SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack pointing to the class + :param SQInteger udsize: size in bytes reserved for user data + :returns: a SQRESULT + +Sets the user data size of a class. If a class 'user data size' is greater than 0. When an instance of the class is created additional space will be reserved at the end of the memory chunk where the instance is stored. The userpointer of the instance will also be automatically set to this memory area. This allows you to minimize allocations in applications that have to carry data along with the class instance. + + + + + +.. _sq_setclosureroot: + +.. c:function:: SQRESULT sq_setclosureroot(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target closure + :returns: an SQRESULT + +pops a table from the stack and sets it as root of the closure at position idx in the stack + + + + + +.. _sq_setinstanceup: + +.. c:function:: SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer up) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer up: an arbitrary user pointer + :returns: a SQRESULT + +sets the userpointer of the class instance at position idx in the stack. + + + + + +.. _sq_setnativeclosurename: + +.. c:function:: SQRESULT sq_setnativeclosurename(HSQUIRRELVM v, SQInteger idx, const SQChar * name) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target native closure + :param const SQChar * name: the name that has to be set + :returns: an SQRESULT + +sets the name of the native closure at the position idx in the stack. The name of a native closure is purely for debug purposes. The name is retrieved through the function sq_stackinfos() while the closure is in the call stack. + + + + + +.. _sq_setparamscheck: + +.. c:function:: SQRESULT sq_setparamscheck(HSQUIRRELVM v, SQInteger nparamscheck, const SQChar * typemask) + + :param HSQUIRRELVM v: the target VM + :param SQInteger nparamscheck: defines the parameters number check policy (0 disables the param checking). If nparamscheck is greater than 0, the VM ensures that the number of parameters is exactly the number specified in nparamscheck (eg. if nparamscheck == 3 the function can only be called with 3 parameters). If nparamscheck is less than 0 the VM ensures that the closure is called with at least the absolute value of the number specified in nparamcheck (eg. nparamscheck == -3 will check that the function is called with at least 3 parameters). The hidden parameter 'this' is included in this number; free variables aren't. If SQ_MATCHTYPEMASKSTRING is passed instead of the number of parameters, the function will automatically infer the number of parameters to check from the typemask (eg. if the typemask is ".sn", it is like passing 3). + :param const SQChar * typemask: defines a mask to validate the parametes types passed to the function. If the parameter is NULL, no typechecking is applied (default). + :remarks: The typemask consists in a zero terminated string that represent the expected parameter type. The types are expressed as follows: 'o' null, 'i' integer, 'f' float, 'n' integer or float, 's' string, 't' table, 'a' array, 'u' userdata, 'c' closure and nativeclosure, 'g' generator, 'p' userpointer, 'v' thread, 'x' instance(class instance), 'y' class, 'b' bool. and '.' any type. The symbol '|' can be used as 'or' to accept multiple types on the same parameter. There isn't any limit on the number of 'or' that can be used. Spaces are ignored so can be inserted between types to increase readability. For instance to check a function that expect a table as 'this' a string as first parameter and a number or a userpointer as second parameter, the string would be "tsn|p" (table,string,number or userpointer). If the parameters mask is contains fewer parameters than 'nparamscheck', the remaining parameters will not be typechecked. + +Sets the parameter validation scheme for the native closure at the top position in the stack. Allows you to validate the number of parameters accepted by the function and optionally their types. If the function call does not comply with the parameter schema set by sq_setparamscheck, an exception is thrown. + +*.eg* + +:: + + //example + SQInteger testy(HSQUIRRELVM v) + { + SQUserPointer p; + const SQChar *s; + SQInteger i; + //no type checking, if the call complies with the mask + //surely the functions will succeed. + sq_getuserdata(v,1,&p,NULL); + sq_getstring(v,2,&s); + sq_getinteger(v,3,&i); + //... do something + return 0; + } + + //the reg code + + //....stuff + sq_newclosure(v,testy,0); + //expects exactly 3 parameters(userdata,string,number) + sq_setparamscheck(v,3,_SC("usn")); + //....stuff + + + + + + +.. _sq_setreleasehook: + +.. c:function:: void sq_setreleasehook(HSQUIRRELVM v, SQInteger idx, SQRELEASEHOOK hook) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQRELEASEHOOK hook: a function pointer to the hook(see sample below) + :remarks: the function hook is called by the VM before the userdata memory is deleted. + +sets the release hook of the userdata, class instance, or class at position idx in the stack. + +*.eg* + +:: + + + /* tyedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size); */ + + SQInteger my_release_hook(SQUserPointer p,SQInteger size) + { + /* do something here */ + return 1; + } + + + + + + +.. _sq_settypetag: + +.. c:function:: SQRESULT sq_settypetag(HSQUIRRELVM v, SQInteger idx, SQUserPointer typetag) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQUserPointer typetag: an arbitrary SQUserPointer + :returns: a SQRESULT + +sets the typetag of the object (userdata or class) at position idx in the stack. + + + + + +.. _sq_tobool: + +.. c:function:: void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool * b) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :param SQBool * b: A pointer to the bool that will store the value + :remarks: if the object is not a bool the function converts the value to bool according to squirrel's rules. For instance the number 1 will result in true, and the number 0 in false. + +gets the value at position idx in the stack as bool. + + + + + +.. _sq_tostring: + +.. c:function:: void sq_tostring(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + +converts the object at position idx in the stack to string and pushes the resulting string in the stack. + + + + + +.. _sq_typeof: + +.. c:function:: SQObjectType sq_typeof(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: an index in the stack + :returns: a SQRESULT + +pushes the type name of the value at the position idx in the stack. It also invokes the _typeof metamethod for tables and class instances that implement it; in that case the pushed object could be something other than a string (is up to the _typeof implementation). + + + diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst b/sp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst new file mode 100644 index 0000000000..d5fe533647 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/object_manipulation.rst @@ -0,0 +1,451 @@ +.. _api_ref_object_manipulation: + +==================== +Object manipulation +==================== + +.. _sq_arrayappend: + +.. c:function:: SQRESULT sq_arrayappend(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :returns: a SQRESULT + :remarks: Only works on arrays. + +pops a value from the stack and pushes it in the back of the array at the position idx in the stack. + + + + + +.. _sq_arrayinsert: + +.. c:function:: SQRESULT sq_arrayinsert(HSQUIRRELVM v, SQInteger idx, SQInteger destpos) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :param SQInteger destpos: the position in the array where the item has to be inserted + :returns: a SQRESULT + :remarks: Only works on arrays. + +pops a value from the stack and inserts it in an array at the specified position + + + + + +.. _sq_arraypop: + +.. c:function:: SQRESULT sq_arraypop(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :returns: a SQRESULT + :remarks: Only works on arrays. + +pops a value from the back of the array at the position idx in the stack. + + + + + +.. _sq_arrayremove: + +.. c:function:: SQRESULT sq_arrayremove(HSQUIRRELVM v, SQInteger idx, SQInteger itemidx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :param SQInteger itemidx: the index of the item in the array that has to be removed + :returns: a SQRESULT + :remarks: Only works on arrays. + +removes an item from an array + + + + + +.. _sq_arrayresize: + +.. c:function:: SQRESULT sq_arrayresize(HSQUIRRELVM v, SQInteger idx, SQInteger newsize) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :param SQInteger newsize: requested size of the array + :returns: a SQRESULT + :remarks: Only works on arrays. If newsize if greater than the current size the new array slots will be filled with nulls. + +resizes the array at the position idx in the stack. + + + + + +.. _sq_arrayreverse: + +.. c:function:: SQRESULT sq_arrayreverse(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target array in the stack + :returns: a SQRESULT + :remarks: Only works on arrays. + +reverses an array in place. + + + + + +.. _sq_clear: + +.. c:function:: SQRESULT sq_clear(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: Only works on tables and arrays. + +clears all the elements of the table/array at position idx in the stack. + + + + + +.. _sq_clone: + +.. c:function:: SQRESULT sq_clone(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + +pushes a clone of the table, array, or class instance at the position idx. + + + + + +.. _sq_createslot: + +.. c:function:: SQRESULT sq_createslot(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :returns: a SQRESULT + :remarks: invoke the _newslot metamethod in the table delegate. it only works on tables. [this function is deperecated since version 2.0.5 use sq_newslot() instead] + +pops a key and a value from the stack and performs a set operation on the table or class that is at position idx in the stack; if the slot does not exist, it will be created. + + + + + +.. _sq_deleteslot: + +.. c:function:: SQRESULT sq_deleteslot(HSQUIRRELVM v, SQInteger idx, SQBool pushval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool pushval: if this param is true the function will push the value of the deleted slot. + :returns: a SQRESULT + :remarks: invoke the _delslot metamethod in the table delegate. it only works on tables. + +pops a key from the stack and delete the slot indexed by it from the table at position idx in the stack; if the slot does not exist, nothing happens. + + + + + +.. _sq_get: + +.. c:function:: SQRESULT sq_get(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: this call will invokes the delegation system like a normal dereference it only works on tables, arrays, classes, instances and userdata; if the function fails, nothing will be pushed in the stack. + +pops a key from the stack and performs a get operation on the object at the position idx in the stack; and pushes the result in the stack. + + + + + +.. _sq_getattributes: + +.. c:function:: SQRESULT sq_getattributes(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class in the stack + :returns: a SQRESULT + +Gets the attribute of a class member. The function pops a key from the stack and pushes the attribute of the class member indexed by they key from a class at position idx in the stack. If key is null the function gets the class level attribute. + + + + + +.. _sq_getbase: + +.. c:function:: SQRESULT sq_getbase(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class in the stack + :returns: a SQRESULT + +pushes the base class of the 'class' at stored position idx in the stack. + + + + + +.. _sq_getclass: + +.. c:function:: SQRESULT sq_getclass(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class instance in the stack + :returns: a SQRESULT + +pushes the class of the 'class instance' at stored position idx in the stack. + + + + + +.. _sq_getdelegate: + +.. c:function:: SQRESULT sq_getdelegate(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + +pushes the current delegate of the object at the position idx in the stack. + + + + + +.. _sq_getfreevariable: + +.. c:function:: const SQChar * sq_getfreevariable(HSQUIRRELVM v, SQInteger idx, SQInteger nval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack(closure) + :param SQInteger nval: 0 based index of the free variable(relative to the closure). + :returns: the name of the free variable for pure squirrel closures. NULL in case of error or if the index of the variable is out of range. In case the target closure is a native closure, the return name is always "@NATIVE". + :remarks: The function works for both squirrel closure and native closure. + +gets the value of the free variable of the closure at the position idx in the stack. + + + + + +.. _sq_getweakrefval: + +.. c:function:: SQRESULT sq_getweakrefval(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target weak reference + :returns: a SQRESULT + :remarks: if the function fails, nothing is pushed in the stack. + +pushes the object pointed by the weak reference at position idx in the stack. + + + + + +.. _sq_instanceof: + +.. c:function:: SQBool sq_instanceof(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: SQTrue if the instance at position -2 in the stack is an instance of the class object at position -1 in the stack. + :remarks: The function doesn't pop any object from the stack. + +Determines if an object is an instance of a certain class. Expects an instance and a class in the stack. + + + + + +.. _sq_newmember: + +.. c:function:: SQRESULT sq_newmember(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool bstatic: if SQTrue creates a static member. + :returns: a SQRESULT + :remarks: Invokes the _newmember metamethod in the class. it only works on classes. + +pops a key, a value and an object (which will be set as attribute of the member) from the stack and performs a new slot operation on the class that is at position idx in the stack; if the slot does not exist, it will be created. + + + + + +.. _sq_newslot: + +.. c:function:: SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool bstatic: if SQTrue creates a static member. This parameter is only used if the target object is a class. + :returns: a SQRESULT + :remarks: Invokes the _newslot metamethod in the table delegate. it only works on tables and classes. + +pops a key and a value from the stack and performs a set operation on the table or class that is at position idx in the stack, if the slot does not exist it will be created. + + + + + +.. _sq_next: + +.. c:function:: SQRESULT sq_next(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + +Pushes in the stack the next key and value of an array, table, or class slot. To start the iteration this function expects a null value on top of the stack; at every call the function will substitute the null value with an iterator and push key and value of the container slot. Every iteration the application has to pop the previous key and value but leave the iterator(that is used as reference point for the next iteration). The function will fail when all slots have been iterated(see Tables and arrays manipulation). + + + + + +.. _sq_rawdeleteslot: + +.. c:function:: SQRESULT sq_rawdeleteslot(HSQUIRRELVM v, SQInteger idx, SQBool pushval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool pushval: if this param is true the function will push the value of the deleted slot. + :returns: a SQRESULT + +Deletes a slot from a table without employing the _delslot metamethod. Pops a key from the stack and delete the slot indexed by it from the table at position idx in the stack; if the slot does not exist nothing happens. + + + + + +.. _sq_rawget: + +.. c:function:: SQRESULT sq_rawget(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: Only works on tables and arrays. + +pops a key from the stack and performs a get operation on the object at position idx in the stack, without employing delegation or metamethods. + + + + + +.. _sq_rawnewmember: + +.. c:function:: SQRESULT sq_rawnewmember(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target table in the stack + :param SQBool bstatic: if SQTrue creates a static member. + :returns: a SQRESULT + :remarks: it only works on classes. + +pops a key, a value and an object(that will be set as attribute of the member) from the stack and performs a new slot operation on the class that is at position idx in the stack; if the slot does not exist it will be created. + + + + + +.. _sq_rawset: + +.. c:function:: SQRESULT sq_rawset(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: it only works on tables and arrays. if the function fails nothing will be pushed in the stack. + +pops a key and a value from the stack and performs a set operation on the object at position idx in the stack, without employing delegation or metamethods. + + + + + +.. _sq_set: + +.. c:function:: SQRESULT sq_set(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: this call will invoke the delegation system like a normal assignment, it only works on tables, arrays and userdata. + +pops a key and a value from the stack and performs a set operation on the object at position idx in the stack. + + + + + +.. _sq_setattributes: + +.. c:function:: SQRESULT sq_setattributes(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target class in the stack. + :returns: a SQRESULT + +Sets the attribute of a class member. The function pops a key and a value from the stack and sets the attribute (indexed by the key) on the class at position idx in the stack. If key is null the function sets the class level attribute. If the function succeed, the old attribute value is pushed in the stack. + + + + + +.. _sq_setdelegate: + +.. c:function:: SQRESULT sq_setdelegate(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :returns: a SQRESULT + :remarks: to remove the delegate from an object, set a null value. + +pops a table from the stack and sets it as the delegate of the object at the position idx in the stack. + + + + + +.. _sq_setfreevariable: + +.. c:function:: SQRESULT sq_setfreevariable(HSQUIRRELVM v, SQInteger idx, SQInteger nval) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :param SQInteger nval: 0 based index of the free variable(relative to the closure). + :returns: a SQRESULT + +pops a value from the stack and sets it as a free variable of the closure at the position idx in the stack. + + + + + +.. _sq_weakref: + +.. c:function:: void sq_weakref(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index to the target object in the stack + :returns: a SQRESULT + :remarks: if the object at idx position is one of (integer, float, bool, null), the object itself is pushed instead of a weak ref. + +pushes a weak reference to the object at position idx in the stack. diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst b/sp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst new file mode 100644 index 0000000000..f655c345be --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/raw_object_handling.rst @@ -0,0 +1,163 @@ +.. _api_ref_raw_object_handling: + +=================== +Raw object handling +=================== + +.. _sq_addref: + +.. c:function:: void sq_addref(HSQUIRRELVM v, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT* po: pointer to an object handler + +adds a reference to an object handler. + + + + + +.. _sq_getobjtypetag: + +.. c:function:: SQRESULT sq_getobjtypetag(HSQOBJECT* o, SQUserPointer* typetag) + + :param HSQOBJECT* o: pointer to an object handler + :param SQUserPointer* typetag: a pointer to the variable that will store the tag + :returns: a SQRESULT + :remarks: the function works also with instances. if the target object is an instance, the typetag of it's base class is fetched. + +gets the typetag of a raw object reference(userdata or class). + + + + + +.. _sq_getrefcount: + +.. c:function:: SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT* po: object handler + +returns the number of references of a given object. + + + + + +.. _sq_getstackobj: + +.. c:function:: SQRESULT sq_getstackobj(HSQUIRRELVM v, SQInteger idx, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: index of the target object in the stack + :param HSQOBJECT* po: pointer to an object handler + :returns: a SQRESULT + +gets an object from the stack and stores it in a object handler. + + + + + +.. _sq_objtobool: + +.. c:function:: SQBool sq_objtobool(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object is not a bool will always return false. + +return the bool value of a raw object reference. + + + + + +.. _sq_objtofloat: + +.. c:function:: SQFloat sq_objtofloat(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object is an integer will convert it to float. If the object is not a number will always return 0. + +return the float value of a raw object reference. + + + + + +.. _sq_objtointeger: + +.. c:function:: SQInteger sq_objtointeger(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object is a float will convert it to integer. If the object is not a number will always return 0. + +return the integer value of a raw object reference. + + + + + +.. _sq_objtostring: + +.. c:function:: const SQChar* sq_objtostring(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object doesn't reference a string it returns NULL. + +return the string value of a raw object reference. + + + + + +.. _sq_objtouserpointer: + +.. c:function:: SQUserPointer sq_objtouserpointer(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: If the object doesn't reference a userpointer it returns NULL. + +return the userpointer value of a raw object reference. + + + + + +.. _sq_pushobject: + +.. c:function:: void sq_pushobject(HSQUIRRELVM v, HSQOBJECT obj) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT obj: object handler + +push an object referenced by an object handler into the stack. + + + + + +.. _sq_release: + +.. c:function:: SQBool sq_release(HSQUIRRELVM v, HSQOBJECT* po) + + :param HSQUIRRELVM v: the target VM + :param HSQOBJECT* po: pointer to an object handler + :returns: SQTrue if the object handler released has lost all is references(the ones added with sq_addref). SQFalse otherwise. + :remarks: the function will reset the object handler to null when it loses all references. + +remove a reference from an object handler. + + + + + +.. _sq_resetobject: + +.. c:function:: void sq_resetobject(HSQOBJECT* po) + + :param HSQOBJECT* po: pointer to an object handler + :remarks: Every object handler has to be initialized with this function. + +resets(initialize) an object handler. diff --git a/sp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst b/sp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst new file mode 100644 index 0000000000..bcd0e6c260 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/api/stack_operations.rst @@ -0,0 +1,107 @@ +.. _api_ref_stack_operations: + +================ +Stack Operations +================ + +.. _sq_cmp: + +.. c:function:: SQInteger sq_cmp(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: > 0 if obj1>obj2 + :returns: == 0 if obj1==obj2 + :returns: < 0 if obj1 0. :: + + sq_pushroottable(v); + sq_pushstring(v,"foo",-1); + sq_get(v,-2); //get the function from the root table + sq_pushroottable(v); //'this' (function environment object) + sq_pushinteger(v,1); + sq_pushfloat(v,2.0); + sq_pushstring(v,"three",-1); + sq_call(v,4,SQFalse,SQFalse); + sq_pop(v,2); //pops the roottable and the function + +this is equivalent to the following Squirrel code:: + + foo(1,2.0,"three"); + +If a runtime error occurs (or a exception is thrown) during the squirrel code execution +the sq_call will fail. diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst new file mode 100644 index 0000000000..88c15c86c0 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/compiling_a_script.rst @@ -0,0 +1,58 @@ +.. embedding_compiling_a_script: + +================== +Compiling a script +================== + +You can compile a Squirrel script with the function *sq_compile*.:: + + typedef SQInteger (*SQLEXREADFUNC)(SQUserPointer userdata); + + SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p, + const SQChar *sourcename,SQBool raiseerror); + +In order to compile a script is necessary for the host application to implement a reader +function (SQLEXREADFUNC); this function is used to feed the compiler with the script +data. +The function is called every time the compiler needs a character; It has to return a +character code if succeed or 0 if the source is finished. + +If sq_compile succeeds, the compiled script will be pushed as Squirrel function in the +stack. + +.. :note:: + In order to execute the script, the function generated by *sq_compile()* has + to be called through *sq_call()* + +Here an example of a 'read' function that read from a file: :: + + SQInteger file_lexfeedASCII(SQUserPointer file) + { + int ret; + char c; + if( ( ret=fread(&c,sizeof(c),1,(FILE *)file )>0) ) + return c; + return 0; + } + + int compile_file(HSQUIRRELVM v,const char *filename) + { + FILE *f=fopen(filename,"rb"); + if(f) + { + sq_compile(v,file_lexfeedASCII,f,filename,1); + fclose(f); + return 1; + } + return 0; + } + +When the compiler fails for a syntax error it will try to call the 'compiler error handler'; +this function must be declared as follow: :: + + typedef void (*SQCOMPILERERROR)(HSQUIRRELVM /*v*/,const SQChar * /*desc*/,const SQChar * /*source*/, + SQInteger /*line*/,SQInteger /*column*/); + +and can be set with the following API call:: + + void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f); diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst new file mode 100644 index 0000000000..43772f3823 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/creating_a_c_function.rst @@ -0,0 +1,106 @@ +.. _embedding_creating_a_c_function: + +=================== +Create a C function +=================== + +A native C function must have the following prototype: :: + + typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM); + +The parameters is an handle to the calling VM and the return value is an integer +respecting the following rules: + +* 1 if the function returns a value +* 0 if the function does not return a value +* SQ_ERROR runtime error is thrown + +In order to obtain a new callable squirrel function from a C function pointer, is necessary +to call sq_newclosure() passing the C function to it; the new Squirrel function will be +pushed in the stack. + +When the function is called, the stackbase is the first parameter of the function and the +top is the last. In order to return a value the function has to push it in the stack and +return 1. + +Function parameters are in the stack from position 1 ('this') to *n*. +*sq_gettop()* can be used to determinate the number of parameters. + +If the function has free variables, those will be in the stack after the explicit parameters +an can be handled as normal parameters. Note also that the value returned by *sq_gettop()* will be +affected by free variables. *sq_gettop()* will return the number of parameters plus +number of free variables. + +Here an example, the following function print the value of each argument and return the +number of arguments. :: + + SQInteger print_args(HSQUIRRELVM v) + { + SQInteger nargs = sq_gettop(v); //number of arguments + for(SQInteger n=1;n<=nargs;n++) + { + printf("arg %d is ",n); + switch(sq_gettype(v,n)) + { + case OT_NULL: + printf("null"); + break; + case OT_INTEGER: + printf("integer"); + break; + case OT_FLOAT: + printf("float"); + break; + case OT_STRING: + printf("string"); + break; + case OT_TABLE: + printf("table"); + break; + case OT_ARRAY: + printf("array"); + break; + case OT_USERDATA: + printf("userdata"); + break; + case OT_CLOSURE: + printf("closure(function)"); + break; + case OT_NATIVECLOSURE: + printf("native closure(C function)"); + break; + case OT_GENERATOR: + printf("generator"); + break; + case OT_USERPOINTER: + printf("userpointer"); + break; + case OT_CLASS: + printf("class"); + break; + case OT_INSTANCE: + printf("instance"); + break; + case OT_WEAKREF: + printf("weak reference"); + break; + default: + return sq_throwerror(v,"invalid param"); //throws an exception + } + } + printf("\n"); + sq_pushinteger(v,nargs); //push the number of arguments as return value + return 1; //1 because 1 value is returned + } + +Here an example of how to register a function:: + + SQInteger register_global_func(HSQUIRRELVM v,SQFUNCTION f,const char *fname) + { + sq_pushroottable(v); + sq_pushstring(v,fname,-1); + sq_newclosure(v,f,0); //create a new function + sq_newslot(v,-3,SQFalse); + sq_pop(v,1); //pops the root table + return 0; + } diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst new file mode 100644 index 0000000000..3d38bf2719 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/debug_interface.rst @@ -0,0 +1,58 @@ +.. _embedding_debug_interface: + +=============== +Debug Interface +=============== + +The squirrel VM exposes a very simple debug interface that allows to easily built a full +featured debugger. +Through the functions sq_setdebughook and sq_setnativedebughook is possible in fact to set a callback function that +will be called every time the VM executes an new line of a script or if a function get +called/returns. The callback will pass as argument the current line the current source and the +current function name (if any).:: + + SQUIRREL_API void sq_setdebughook(HSQUIRRELVM v); + +or :: + + SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook); + +The following code shows how a debug hook could look like(obviously is possible to +implement this function in C as well). :: + + function debughook(event_type,sourcefile,line,funcname) + { + local fname=funcname?funcname:"unknown"; + local srcfile=sourcefile?sourcefile:"unknown" + switch (event_type) { + case 'l': //called every line(that contains some code) + ::print("LINE line [" + line + "] func [" + fname + "]"); + ::print("file [" + srcfile + "]\n"); + break; + case 'c': //called when a function has been called + ::print("LINE line [" + line + "] func [" + fname + "]"); + ::print("file [" + srcfile + "]\n"); + break; + case 'r': //called when a function returns + ::print("LINE line [" + line + "] func [" + fname + "]"); + ::print("file [" + srcfile + "]\n"); + break; + } + } + +The parameter *event_type* can be 'l' ,'c' or 'r' ; a hook with a 'l' event is called for each line that +gets executed, 'c' every time a function gets called and 'r' every time a function returns. + +A full-featured debugger always allows displaying local variables and calls stack. +The call stack information are retrieved through sq_getstackinfos():: + + SQInteger sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si); + +While the local variables info through sq_getlocal():: + + SQInteger sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger nseq); + +In order to receive line callbacks the scripts have to be compiled with debug infos enabled +this is done through sq_enabledebuginfo(); :: + + void sq_enabledebuginfo(HSQUIRRELVM v, SQInteger debuginfo); diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst new file mode 100644 index 0000000000..f86e7416b7 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/error_conventions.rst @@ -0,0 +1,16 @@ +.. _embedding_error_convetions: + + +======================== +Error Conventions +======================== + +.. index:: + single: Error Conventions + +Most of the functions in the API return a SQRESULT value; SQRESULT indicates if a +function completed successfully or not. +The macros SQ_SUCCEEDED() and SQ_FAILED() are used to test the result of a function.:: + + if(SQ_FAILED(sq_getstring(v,-1,&s))) + printf("getstring failed"); diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst new file mode 100644 index 0000000000..ad0fb08223 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/memory_management.rst @@ -0,0 +1,28 @@ +.. _embedding_memory_management: + +======================== +Memory Management +======================== + +.. index:: single: Memory Management + +Squirrel uses reference counting (RC) as primary system for memory management; +however, the virtual machine (VM) has an auxiliary +mark and sweep garbage collector that can be invoked on demand. + +There are 2 possible compile time options: + + * The default configuration consists in RC plus a mark and sweep garbage collector. + The host program can call the function sq_collectgarbage() and perform a garbage collection cycle + during the program execution. The garbage collector isn't invoked by the VM and has to + be explicitly called by the host program. + + * The second a situation consists in RC only(define NO_GARBAGE_COLLECTOR); in this case is impossible for + the VM to detect reference cycles, so is the programmer that has to solve them explicitly in order to + avoid memory leaks. + +The only advantage introduced by the second option is that saves 2 additional +pointers that have to be stored for each object in the default configuration with +garbage collector(8 bytes for 32 bits systems). +The types involved are: tables, arrays, functions, threads, userdata and generators; all other +types are untouched. These options do not affect execution speed. diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst new file mode 100644 index 0000000000..ba84042c65 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/references_from_c.rst @@ -0,0 +1,21 @@ +.. embedding_references_from_c: + +======================================================== +Mantaining references to Squirrel values from the C API +======================================================== + +Squirrel allows to reference values through the C API; the function sq_getstackobj() gets +a handle to a squirrel object(any type). The object handle can be used to control the lifetime +of an object by adding or removing references to it( see sq_addref() and sq_release()). +The object can be also re-pushed in the VM stack using sq_pushobject().:: + + HSQOBJECT obj; + + sq_resetobject(&obj); //initialize the handle + sq_getstackobj(v,-2,&obj); //retrieve an object handle from the pos -2 + sq_addref(v,&obj); //adds a reference to the object + + ... //do stuff + + sq_pushobject(v,obj); //push the object in the stack + sq_release(v,&obj); //relese the object diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst new file mode 100644 index 0000000000..b956e4be3f --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/runtime_error_handling.rst @@ -0,0 +1,17 @@ +.. _embedding_runtime_error_handling: + +====================== +Runtime error handling +====================== + +When an exception is not handled by Squirrel code with a try/catch statement, a runtime +error is raised and the execution of the current program is interrupted. It is possible to +set a call back function to intercept the runtime error from the host program; this is +useful to show meaningful errors to the script writer and for implementing visual +debuggers. +The following API call pops a Squirrel function from the stack and sets it as error handler.:: + + SQUIRREL_API void sq_seterrorhandler(HSQUIRRELVM v); + +The error handler is called with 2 parameters, an environment object (this) and a object. +The object can be any squirrel type. diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst new file mode 100644 index 0000000000..8378a102ab --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/tables_and_arrays_manipulation.rst @@ -0,0 +1,70 @@ +.. _embedding_tables_and_arrays_manipulation: + +============================== +Tables and arrays manipulation +============================== + +A new table is created calling sq_newtable, this function pushes a new table in the stack.:: + + void sq_newtable(HSQUIRRELVM v); + +To create a new slot:: + + SQRESULT sq_newslot(HSQUIRRELVM v,SQInteger idx,SQBool bstatic); + +To set or get the table delegate:: + + SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx); + SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx); + + +A new array is created calling sq_newarray, the function pushes a new array in the +stack; if the parameters size is bigger than 0 the elements are initialized to null.:: + + void sq_newarray (HSQUIRRELVM v,SQInteger size); + +To append a value to the back of the array:: + + SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx); + +To remove a value from the back of the array:: + + SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQInteger pushval); + +To resize the array:: + + SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize); + +To retrieve the size of a table or an array you must use sq_getsize():: + + SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx); + +To set a value in an array or table:: + + SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx); + +To get a value from an array or table:: + + SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx); + +To get or set a value from a table without employing delegation:: + + SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx); + SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx); + +To iterate a table or an array:: + + SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx); + +Here an example of how to perform an iteration: :: + + //push your table/array here + sq_pushnull(v) //null iterator + while(SQ_SUCCEEDED(sq_next(v,-2))) + { + //here -1 is the value and -2 is the key + + sq_pop(v,2); //pops key and val before the nex iteration + } + + sq_pop(v,1); //pops the null iterator diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst new file mode 100644 index 0000000000..4aa5f78227 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/the_registry_table.rst @@ -0,0 +1,14 @@ +.. _embedding_the_registry_table: + +================== +The registry table +================== + +The registry table is an hidden table shared between vm and all his thread(friend vms). +This table is accessible only through the C API and is meant to be an utility structure +for native C library implementation. +For instance the sqstdlib(squirrel standard library)uses it to store configuration and shared objects +delegates. +The registry is accessible through the API call *sq_pushregistrytable()*.:: + + void sq_pushregistrytable(HSQUIRRELVM v); diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst new file mode 100644 index 0000000000..9c5f9aef55 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/the_stack.rst @@ -0,0 +1,104 @@ +.. _embedding_the_stack: + + +========== +The Stack +========== + +Squirrel exchanges values with the virtual machine through a stack. This mechanism has +been inherited from the language Lua. +For instance to call a Squirrel function from C it is necessary to push the function and the +arguments in the stack and then invoke the function; also when Squirrel calls a C +function the parameters will be in the stack as well. + +------------- +Stack indexes +------------- + +Many API functions can arbitrarily refer to any element in the stack through an index. +The stack indexes follow those conventions: + +* 1 is the stack base +* Negative indexes are considered an offset from top of the stack. For instance -1 isthe top of the stack. +* 0 is an invalid index + +Here an example (let's pretend that this table is the VM stack) + ++------------+--------------------+--------------------+ +| **STACK** | **positive index** | **negative index** | ++============+====================+====================+ +| "test" | 4 | -1(top) | ++------------+--------------------+--------------------+ +| 1 | 3 | -2 | ++------------+--------------------+--------------------+ +| 0.5 | 2 | -3 | ++------------+--------------------+--------------------+ +| "foo" | 1(base) | -4 | ++------------+--------------------+--------------------+ + +In this case, the function *sq_gettop* would return 4; + +------------------ +Stack manipulation +------------------ + +The API offers several functions to push and retrieve data from the Squirrel stack. + +To push a value that is already present in the stack in the top position:: + + void sq_push(HSQUIRRELVM v,SQInteger idx); + +To pop an arbitrary number of elements:: + + void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop); + +To remove an element from the stack:: + + void sq_remove(HSQUIRRELVM v,SQInteger idx); + +To retrieve the top index (and size) of the current +virtual stack you must call *sq_gettop* :: + + SQInteger sq_gettop(HSQUIRRELVM v); + +To force the stack to a certain size you can call *sq_settop* :: + + void sq_settop(HSQUIRRELVM v,SQInteger newtop); + +If the newtop is bigger than the previous one, the new positions in the stack will be +filled with null values. + +The following function pushes a C value into the stack:: + + void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len); + void sq_pushfloat(HSQUIRRELVM v,SQFloat f); + void sq_pushinteger(HSQUIRRELVM v,SQInteger n); + void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p); + void sq_pushbool(HSQUIRRELVM v,SQBool b); + +this function pushes a null into the stack:: + + void sq_pushnull(HSQUIRRELVM v); + +returns the type of the value in a arbitrary position in the stack:: + + SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx); + +the result can be one of the following values: :: + + OT_NULL,OT_INTEGER,OT_FLOAT,OT_STRING,OT_TABLE,OT_ARRAY,OT_USERDATA, + OT_CLOSURE,OT_NATIVECLOSURE,OT_GENERATOR,OT_USERPOINTER,OT_BOOL,OT_INSTANCE,OT_CLASS,OT_WEAKREF + +The following functions convert a squirrel value in the stack to a C value:: + + SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c); + SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger size); + SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i); + SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f); + SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p); + SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag); + SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *p); + +The function sq_cmp compares 2 values from the stack and returns their relation (like strcmp() in ANSI C).:: + + SQInteger sq_cmp(HSQUIRRELVM v); diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst new file mode 100644 index 0000000000..81e14f0352 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/userdata_and_userpointers.rst @@ -0,0 +1,33 @@ +.. _embedding_userdata_and_userpointers: + +========================= +Userdata and UserPointers +========================= + +Squirrel allows the host application put arbitrary data chunks into a Squirrel value, this is +possible through the data type userdata.:: + + SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size); + +When the function *sq_newuserdata* is called, Squirrel allocates a new userdata with the +specified size, returns a pointer to his payload buffer and push the object in the stack; at +this point the application can do whatever it want with this memory chunk, the VM will +automatically take cake of the memory deallocation like for every other built-in type. +A userdata can be passed to a function or stored in a table slot. By default Squirrel +cannot manipulate directly userdata; however is possible to assign a delegate to it and +define a behavior like it would be a table. +Because the application would want to do something with the data stored in a userdata +object when it get deleted, is possible to assign a callback that will be called by the VM +just before deleting a certain userdata. +This is done through the API call *sq_setreleasehook*.:: + + typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size); + + void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook); + +Another kind of userdata is the userpointer; this type is not a memory chunk like the +normal userdata, but just a 'void*' pointer. It cannot have a delegate and is passed by +value, so pushing a userpointer doesn't cause any memory allocation.:: + + void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p); + diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst new file mode 100644 index 0000000000..03fba06ce2 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding/vm_initialization.rst @@ -0,0 +1,21 @@ +.. _embedding_vm_initialization: + +============================== +Virtual Machine Initialization +============================== + +The first thing that a host application has to do, is create a virtual machine. +The host application can create any number of virtual machines through the function +*sq_open()*. +Every single VM that was created using *sq_open()* has to be released with the function *sq_close()* when it is no +longer needed.:: + + int main(int argc, char* argv[]) + { + HSQUIRRELVM v; + v = sq_open(1024); //creates a VM with initial stack size 1024 + + //do some stuff with squirrel here + + sq_close(v); + } diff --git a/sp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst b/sp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst new file mode 100644 index 0000000000..dc8d7fb14d --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/embedding_squirrel.rst @@ -0,0 +1,29 @@ +.. _embedding_squirrel: + +*************************** + Embedding Squirrel +*************************** + +*This section describes how to embed Squirrel in a host application, +C language knowledge is required to understand this part of the manual.* + +Because of his nature of extension language, Squirrel's compiler and virtual machine +are implemented as C library. The library exposes a set of functions to compile scripts, +call functions, manipulate data and extend the virtual machine. +All declarations needed for embedding the language in an application are in the header file 'squirrel.h'. + +.. toctree:: + embedding/memory_management.rst + embedding/build_configuration.rst + embedding/error_conventions.rst + embedding/vm_initialization.rst + embedding/the_stack.rst + embedding/runtime_error_handling.rst + embedding/compiling_a_script.rst + embedding/calling_a_function.rst + embedding/creating_a_c_function.rst + embedding/tables_and_arrays_manipulation.rst + embedding/userdata_and_userpointers.rst + embedding/the_registry_table.rst + embedding/references_from_c.rst + embedding/debug_interface.rst diff --git a/sp/src/vscript/squirrel/doc/source/reference/index.rst b/sp/src/vscript/squirrel/doc/source/reference/index.rst new file mode 100644 index 0000000000..e2d3818444 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/index.rst @@ -0,0 +1,34 @@ +.. _reference: + +################################# + Squirrel 3.1 Reference Manual +################################# + +Copyright (c) 2003-2016 Alberto Demichelis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +.. toctree:: + :maxdepth: 3 + :numbered: + + introduction.rst + language.rst + embedding_squirrel.rst + api_reference.rst diff --git a/sp/src/vscript/squirrel/doc/source/reference/introduction.rst b/sp/src/vscript/squirrel/doc/source/reference/introduction.rst new file mode 100644 index 0000000000..bc83447f6e --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/introduction.rst @@ -0,0 +1,16 @@ +.. _introduction: + +************ +Introduction +************ + +.. index:: + single: introduction + +Squirrel is a high-level, imperative-OO programming language, designed to be a powerful +scripting tool that fits within the size, memory bandwidth, and real-time requirements of +applications like games. +Squirrel offers a wide range of features like dynamic typing, delegation, higher +order functions, generators, tail recursion, exception handling, automatic memory +management while fitting both compiler and virtual machine into about 6k lines of C++ +code. diff --git a/sp/src/vscript/squirrel/doc/source/reference/language.rst b/sp/src/vscript/squirrel/doc/source/reference/language.rst new file mode 100644 index 0000000000..356211968d --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language.rst @@ -0,0 +1,23 @@ +.. _thelanguage: + +*************************** + The language +*************************** + +.. toctree:: + language/lexical_structure.rst + language/datatypes.rst + language/execution_context.rst + language/statements.rst + language/expressions.rst + language/tables.rst + language/arrays.rst + language/functions.rst + language/classes.rst + language/generators.rst + language/constants_and_enumerations.rst + language/threads.rst + language/weak_references.rst + language/delegation.rst + language/metamethods.rst + language/builtin_functions.rst diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/arrays.rst b/sp/src/vscript/squirrel/doc/source/reference/language/arrays.rst new file mode 100644 index 0000000000..ddf5114fe5 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/arrays.rst @@ -0,0 +1,19 @@ +.. _arrays: + + +================= +Arrays +================= + +.. index:: + single: Arrays + +An array is a sequence of values indexed by a integer number from 0 to the size of the +array minus 1. Arrays elements can be obtained through their index.:: + + local a=["I'm a string", 123] + print(typeof a[0]) //prints "string" + print(typeof a[1]) //prints "integer" + +Resizing, insertion, deletion of arrays and arrays elements is done through a set of +standard functions (see :ref:`built-in functions `). diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst b/sp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst new file mode 100644 index 0000000000..2f64105b27 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/builtin_functions.rst @@ -0,0 +1,693 @@ +.. _builtin_functions: + + +================== +Built-in Functions +================== + +.. index:: + single: Built-in Functions + pair: Global Symbols; Built-in Functions + + +^^^^^^^^^^^^^^ +Global Symbols +^^^^^^^^^^^^^^ + +.. js:function:: array(size,[fill]) + +creates and returns array of a specified size. If the optional parameter fill is specified its value will be used to fill the new array's slots. If the fill parameter is omitted, null is used instead. + +.. js:function:: seterrorhandler(func) + + +sets the runtime error handler + +.. js:function:: callee() + + +returns the currently running closure + +.. js:function:: setdebughook(hook_func) + + +sets the debug hook + +.. js:function:: enabledebuginfo(enable) + +enable/disable the debug line information generation at compile time. enable != null enables. enable == null disables. + +.. js:function:: getroottable() + +returns the root table of the VM. + +.. js:function:: setroottable(table) + +sets the root table of the VM. And returns the previous root table. + +.. js:function:: getconsttable() + +returns the const table of the VM. + +.. js:function:: setconsttable(table) + +sets the const table of the VM; returns the previous const table. + +.. js:function:: assert(exp, [message]) + +throws an exception if exp is null or false. Throws "assertion failed" string by default, or message if specified. + +.. js:function:: print(x) + +prints x to the standard output + +.. js:function:: error(x) + +prints x in the standard error output + +.. js:function:: compilestring(string,[buffername]) + +compiles a string containing a squirrel script into a function and returns it:: + + local compiledscript=compilestring("::print(\"ciao\")"); + //run the script + compiledscript(); + +.. js:function:: collectgarbage() + + Runs the garbage collector and returns the number of reference cycles found (and deleted). This function only works on garbage collector builds. + +.. js:function:: resurrectunreachable() + +Runs the garbage collector and returns an array containing all unreachable object found. If no unreachable object is found, null is returned instead. This function is meant to help debugging reference cycles. This function only works on garbage collector builds. + +.. js:function:: type(obj) + +return the 'raw' type of an object without invoking the metamethod '_typeof'. + +.. js:function:: getstackinfos(level) + +returns the stack informations of a given call stack level. returns a table formatted as follow: :: + + { + func="DoStuff", //function name + + src="test.nut", //source file + + line=10, //line number + + locals = { //a table containing the local variables + + a=10, + + testy="I'm a string" + } + } + +level = 0 is getstackinfos() itself! level = 1 is the current function, level = 2 is the caller of the current function, and so on. If the stack level doesn't exist the function returns null. + +.. js:function:: newthread(threadfunc) + +creates a new cooperative thread object(coroutine) and returns it + +.. js:data:: _versionnumber_ + +integer values describing the version of VM and compiler. e.g. for Squirrel 3.0.1 this value will be 301 + +.. js:data:: _version_ + +string values describing the version of VM and compiler. + +.. js:data:: _charsize_ + +size in bytes of the internal VM representation for characters(1 for ASCII builds 2 for UNICODE builds). + +.. js:data:: _intsize_ + +size in bytes of the internal VM representation for integers(4 for 32bits builds 8 for 64bits builds). + +.. js:data:: _floatsize_ + +size in bytes of the internal VM representation for floats(4 for single precision builds 8 for double precision builds). + +----------------- +Default delegates +----------------- + +Except null and userdata every squirrel object has a default delegate containing a set of functions to manipulate and retrieve information from the object itself. + +^^^^^^^^ +Integer +^^^^^^^^ + +.. js:function:: integer.tofloat() + +convert the number to float and returns it + + +.. js:function:: integer.tostring() + +converts the number to string and returns it + + +.. js:function:: integer.tointeger() + +dummy function; returns the value of the integer. + + +.. js:function:: integer.tochar() + +returns a string containing a single character represented by the integer. + + +.. js:function:: integer.weakref() + +dummy function; returns the integer itself. + +^^^^^ +Float +^^^^^ + +.. js:function:: float.tofloat() + +returns the value of the float(dummy function) + + +.. js:function:: float.tointeger() + +converts the number to integer and returns it + + +.. js:function:: float.tostring() + +converts the number to string and returns it + + +.. js:function:: float.tochar() + +returns a string containing a single character represented by the integer part of the float. + + +.. js:function:: float.weakref() + +dummy function; returns the float itself. + +^^^^ +Bool +^^^^ + +.. js:function:: bool.tofloat() + +returns 1.0 for true 0.0 for false + + +.. js:function:: bool.tointeger() + +returns 1 for true 0 for false + + +.. js:function:: bool.tostring() + +returns "true" for true and "false" for false + + +.. js:function:: bool.weakref() + +dummy function; returns the bool itself. + +^^^^^^ +String +^^^^^^ + +.. js:function:: string.len() + +returns the string length + + +.. js:function:: string.tointeger([base]) + +Converts the string to integer and returns it. An optional parameter base can be specified--if a base is not specified, it defaults to base 10. + + +.. js:function:: string.tofloat() + +converts the string to float and returns it + + +.. js:function:: string.tostring() + +returns the string (really, a dummy function) + + +.. js:function:: string.slice(start,[end]) + +returns a section of the string as new string. Copies from start to the end (not included). If start is negative the index is calculated as length + start, if end is negative the index is calculated as length + end. If end is omitted end is equal to the string length. + + +.. js:function:: string.find(substr,[startidx]) + +Searches a sub string (substr) starting from the index startidx and returns the index of its first occurrence. If startidx is omitted the search operation starts from the beginning of the string. The function returns null if substr is not found. + + +.. js:function:: string.tolower() + +returns a lowercase copy of the string. + + +.. js:function:: string.toupper() + +returns a uppercase copy of the string. + + +.. js:function:: string.weakref() + +returns a weak reference to the object. + +^^^^^ +Table +^^^^^ + +.. js:function:: table.len() + +returns the number of slots contained in a table + + +.. js:function:: table.rawget(key) + +tries to get a value from the slot 'key' without employing delegation + + +.. js:function:: table.rawset(key,val) + +Sets the slot 'key' with the value 'val' without employing delegation. If the slot does not exists, it will be created. Returns table itself. + + +.. js:function:: table.rawdelete() + +Deletes the slot key without employing delegation and returns its value. If the slot does not exists, returns null. + + +.. js:function:: table.rawin(key) + +returns true if the slot 'key' exists. the function has the same effect as the operator 'in' but does not employ delegation. + + +.. js:function:: table.weakref() + +returns a weak reference to the object. + + +.. js:function:: table.tostring() + +Tries to invoke the _tostring metamethod. If that fails, it returns "(table : pointer)". + + +.. js:function:: table.clear() + +removes all the slots from the table. Returns table itself. + + +.. js:function:: table.setdelegate(table) + +Sets the delegate of the table. To remove a delegate, 'null' must be passed to the function. The function returns the table itself (e.g. a.setdelegate(b) -- in this case 'a' is the return value). + + +.. js:function:: table.getdelegate() + +returns the table's delegate or null if no delegate was set. + + +.. js:function:: table.filter(func(key,val)) + +Creates a new table with all values that pass the test implemented by the provided function. In detail, it creates a new table, invokes the specified function for each key-value pair in the original table; if the function returns 'true', then the value is added to the newly created table at the same key. + +.. js:function:: table.keys() + +returns an array containing all the keys of the table slots. + +.. js:function:: table.values() + +returns an array containing all the values of the table slots. + +^^^^^^ +Array +^^^^^^ + +.. js:function:: array.len() + +returns the length of the array + + +.. js:function:: array.append(val) + +appends the value 'val' at the end of the array. Returns array itself. + + +.. js:function:: array.push(val) + +appends the value 'val' at the end of the array. Returns array itself. + + +.. js:function:: array.extend(array) + +Extends the array by appending all the items in the given array. Returns array itself. + + +.. js:function:: array.pop() + +removes a value from the back of the array and returns it. + + +.. js:function:: array.top() + +returns the value of the array with the higher index + + +.. js:function:: array.insert(idx,val) + +inserts the value 'val' at the position 'idx' in the array. Returns array itself. + + +.. js:function:: array.remove(idx) + +removes the value at the position 'idx' in the array and returns its value. + + +.. js:function:: array.resize(size,[fill]) + +Resizes the array. If the optional parameter 'fill' is specified, its value will be used to fill the new array's slots when the size specified is bigger than the previous size. If the fill parameter is omitted, null is used instead. Returns array itself. + + +.. js:function:: array.sort([compare_func]) + +Sorts the array in-place. A custom compare function can be optionally passed. The function prototype as to be the following.:: + + function custom_compare(a,b) + { + if(a>b) return 1 + else if(a :: + + arr.sort(@(a,b) a <=> b); + +Returns array itself. + +.. js:function:: array.reverse() + +reverse the elements of the array in place. Returns array itself. + + +.. js:function:: array.slice(start,[end]) + +Returns a section of the array as new array. Copies from start to the end (not included). If start is negative the index is calculated as length + start, if end is negative the index is calculated as length + end. If end is omitted end is equal to the array length. + + +.. js:function:: array.weakref() + +returns a weak reference to the object. + + +.. js:function:: array.tostring() + +returns the string "(array : pointer)". + + +.. js:function:: array.clear() + +removes all the items from the array + + +.. js:function:: array.map(func(item_value, [item_index], [array_ref])) + +Creates a new array of the same size. For each element in the original array invokes the function 'func' and assigns the return value of the function to the corresponding element of the newly created array. +Provided func can accept up to 3 arguments: array item value (required), array item index (optional), reference to array itself (optional). + + +.. js:function:: array.apply(func([item_value, [item_index], [array_ref])) + +for each element in the array invokes the function 'func' and replace the original value of the element with the return value of the function. + + +.. js:function:: array.reduce(func(prevval,curval), [initializer]) + +Reduces an array to a single value. For each element in the array invokes the function 'func' passing +the initial value (or value from the previous callback call) and the value of the current element. +The return value of the function is then used as 'prevval' for the next element. +If the optional initializer is present, it is placed before the items of the array in the calculation, +and serves as a default when the sequence is empty. +If initializer is not given then for sequence contains only one item, reduce() returns the first item, +and for empty sequence returns null. + +Given an sequence with 2 or more elements (including initializer) calls the function with the first two elements as the parameters, +gets that result, then calls the function with that result and the third element, gets that result, +calls the function with that result and the fourth parameter and so on until all element have been processed. +Finally, returns the return value of the last invocation of func. + + +.. js:function:: array.filter(func(index,val)) + +Creates a new array with all elements that pass the test implemented by the provided function. In detail, it creates a new array, for each element in the original array invokes the specified function passing the index of the element and it's value; if the function returns 'true', then the value of the corresponding element is added on the newly created array. + + +.. js:function:: array.find(value) + +Performs a linear search for the value in the array. Returns the index of the value if it was found null otherwise. + +^^^^^^^^ +Function +^^^^^^^^ + +.. js:function:: function.call(_this,args...) + +calls the function with the specified environment object('this') and parameters + + +.. js:function:: function.pcall(_this,args...) + +calls the function with the specified environment object('this') and parameters, this function will not invoke the error callback in case of failure(pcall stays for 'protected call') + + +.. js:function:: function.acall(array_args) + +calls the function with the specified environment object('this') and parameters. The function accepts an array containing the parameters that will be passed to the called function.Where array_args has to contain the required 'this' object at the [0] position. + + +.. js:function:: function.pacall(array_args) + +calls the function with the specified environment object('this') and parameters. The function accepts an array containing the parameters that will be passed to the called function.Where array_args has to contain the required 'this' object at the [0] position. This function will not invoke the error callback in case of failure(pacall stays for 'protected array call') + + +.. js:function:: function.weakref() + +returns a weak reference to the object. + + +.. js:function:: function.tostring() + +returns the string "(closure : pointer)". + + +.. js:function:: function.setroot(table) + +sets the root table of a closure + + +.. js:function:: function.getroot() + +returns the root table of the closure + + +.. js:function:: function.bindenv(env) + +clones the function(aka closure) and bind the environment object to it(table,class or instance). the this parameter of the newly create function will always be set to env. Note that the created function holds a weak reference to its environment object so cannot be used to control its lifetime. + + +.. js:function:: function.getinfos() + +returns a table containing informations about the function, like parameters, name and source name; :: + + //the data is returned as a table is in form + //pure squirrel function + { + native = false + name = "zefuncname" + src = "/somthing/something.nut" + parameters = ["a","b","c"] + defparams = [1,"def"] + varargs = 2 + } + //native C function + { + native = true + name = "zefuncname" + paramscheck = 2 + typecheck = [83886082,83886384] //this is the typemask (see C defines OT_INTEGER,OT_FLOAT etc...) + } + + + +^^^^^ +Class +^^^^^ + +.. js:function:: class.instance() + +returns a new instance of the class. this function does not invoke the instance constructor. The constructor must be explicitly called (eg. class_inst.constructor(class_inst) ). + + +.. js:function:: class.getattributes(membername) + +returns the attributes of the specified member. if the parameter member is null the function returns the class level attributes. + + +.. js:function:: class.setattributes(membername,attr) + +sets the attribute of the specified member and returns the previous attribute value. if the parameter member is null the function sets the class level attributes. + + +.. js:function:: class.rawin(key) + +returns true if the slot 'key' exists. the function has the same effect as the operator 'in' but does not employ delegation. + + +.. js:function:: class.weakref() + +returns a weak reference to the object. + + +.. js:function:: class.tostring() + +returns the string "(class : pointer)". + + +.. js:function:: class.rawget(key) + +tries to get a value from the slot 'key' without employing delegation + + +.. js:function:: class.rawset(key,val) + +sets the slot 'key' with the value 'val' without employing delegation. If the slot does not exists, it will be created. + + +.. js:function:: class.newmember(key,val,[attrs],[bstatic]) + +sets/adds the slot 'key' with the value 'val' and attributes 'attrs' and if present invokes the _newmember metamethod. If bstatic is true the slot will be added as static. If the slot does not exists , it will be created. + + +.. js:function:: class.rawnewmember(key,val,[attrs],[bstatic]) + +sets/adds the slot 'key' with the value 'val' and attributes 'attrs'. If bstatic is true the slot will be added as static. If the slot does not exist, it will be created. It doesn't invoke any metamethod. + +^^^^^^^^^^^^^^ +Class Instance +^^^^^^^^^^^^^^ + +.. js:function:: instance.getclass() + +returns the class that created the instance. + + +.. js:function:: instance.rawin(key) + + :param key: ze key + +returns true if the slot 'key' exists. the function has the same effect as the operator 'in' but does not employ delegation. + + +.. js:function:: instance.weakref() + +returns a weak reference to the object. + + +.. js:function:: instance.tostring() + +tries to invoke the _tostring metamethod, if failed. returns "(instance : pointer)". + + +.. js:function:: instance.rawget(key) + +tries to get a value from the slot 'key' without employing delegation + + +.. js:function:: instance.rawset(key,val) + +sets the slot 'key' with the value 'val' without employing delegation. If the slot does not exists, it will be created. + +^^^^^^^^^^^^^^ +Generator +^^^^^^^^^^^^^^ + + +.. js:function:: generator.getstatus() + +returns the status of the generator as string : "running", "dead" or "suspended". + + +.. js:function:: generator.weakref() + +returns a weak reference to the object. + + +.. js:function:: generator.tostring() + +returns the string "(generator : pointer)". + +^^^^^^^^^^^^^^ +Thread +^^^^^^^^^^^^^^ + +.. js:function:: thread.call(...) + +starts the thread with the specified parameters + + +.. js:function:: thread.wakeup([wakeupval]) + +wakes up a suspended thread, accepts a optional parameter that will be used as return value for the function that suspended the thread(usually suspend()) + + +.. js:function:: thread.wakeupthrow(objtothrow,[propagateerror = true]) + +wakes up a suspended thread, throwing an exception in the awaken thread, throwing the object 'objtothrow'. + + +.. js:function:: thread.getstatus() + +returns the status of the thread ("idle","running","suspended") + + +.. js:function:: thread.weakref() + +returns a weak reference to the object. + + +.. js:function:: thread.tostring() + +returns the string "(thread : pointer)". + + +.. js:function:: thread.getstackinfos(stacklevel) + +returns the stack frame informations at the given stack level (0 is the current function 1 is the caller and so on). + +^^^^^^^^^^^^^^ +Weak Reference +^^^^^^^^^^^^^^ + +.. js:function:: weakreference.ref() + +returns the object that the weak reference is pointing at; null if the object that was point at was destroyed. + + +.. js:function:: weakreference.weakref() + +returns a weak reference to the object. + + +.. js:function:: weakreference.tostring() + +returns the string "(weakref : pointer)". diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/classes.rst b/sp/src/vscript/squirrel/doc/source/reference/language/classes.rst new file mode 100644 index 0000000000..81ee4c418d --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/classes.rst @@ -0,0 +1,429 @@ +.. _classes: + + +================= +Classes +================= + +.. index:: + single: Classes + +Squirrel implements a class mechanism similar to languages like Java/C++/etc... +however because of its dynamic nature it differs in several aspects. +Classes are first class objects like integer or strings and can be stored in +table slots local variables, arrays and passed as function parameters. + +----------------- +Class Declaration +----------------- + +.. index:: + pair: declaration; Class + single: Class Declaration + +A class object is created through the keyword 'class' . The class object follows +the same declaration syntax of a table(see :ref:`Tables `) with the only difference +of using ';' as optional separator rather than ','. + +For instance: :: + + class Foo { + //constructor + constructor(a) + { + testy = ["stuff",1,2,3,a]; + } + //member function + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //property + testy = null; + + } + +the previous code example is a syntactic sugar for: :: + + Foo <- class { + //constructor + constructor(a) + { + testy = ["stuff",1,2,3,a]; + } + //member function + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //property + testy = null; + + } + +in order to emulate namespaces, it is also possible to declare something like this:: + + //just 2 regular nested tables + FakeNamespace <- { + Utils = {} + } + + class FakeNamespace.Utils.SuperClass { + constructor() + { + ::print("FakeNamespace.Utils.SuperClass") + } + function DoSomething() + { + ::print("DoSomething()") + } + } + + function FakeNamespace::Utils::SuperClass::DoSomethingElse() + { + ::print("FakeNamespace::Utils::SuperClass::DoSomethingElse()") + } + + local testy = FakeNamespace.Utils.SuperClass(); + testy.DoSomething(); + testy.DoSomethingElse(); + +After its declaration, methods or properties can be added or modified by following +the same rules that apply to a table(operator ``<-``).:: + + //adds a new property + Foo.stuff <- 10; + + //modifies the default value of an existing property + Foo.testy <- "I'm a string"; + + //adds a new method + function Foo::DoSomething(a,b) + { + return a+b; + } + +After a class is instantiated is no longer possible to add new properties however is possible to add or replace methods. + +^^^^^^^^^^^^^^^^ +Static variables +^^^^^^^^^^^^^^^^ + +.. index:: + pair: static variables; Class + single: Static variables + +Squirrel's classes support static member variables. A static variable shares its value +between all instances of the class. Statics are declared by prefixing the variable declaration +with the keyword ``static``; the declaration must be in the class body. + +.. note:: Statics are read-only. + +:: + + class Foo { + constructor() + { + //..stuff + } + name = "normal variable"; + //static variable + static classname = "The class name is foo"; + }; + +^^^^^^^^^^^^^^^^ +Class Attributes +^^^^^^^^^^^^^^^^ + +.. index:: + pair: attributes; Class + single: Class Attributes + +Classes allow to associate attributes to it's members. Attributes are a form of metadata +that can be used to store application specific informations, like documentations +strings, properties for IDEs, code generators etc... +Class attributes are declared in the class body by preceding the member declaration and +are delimited by the symbol ````. +Here an example: :: + + class Foo { + //attributes of PrintTesty + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //attributes of testy + testy = null; + } + +Attributes are, matter of fact, a table. Squirrel uses ```` syntax +instead of curly brackets ``{}`` for the attribute declaration to increase readability. + +This means that all rules that apply to tables apply to attributes. + +Attributes can be retrieved through the built-in function ``classobj.getattributes(membername)`` (see built-in functions). +and can be modified/added through the built-in function ``classobj.setattributes(membername,val)``. + +the following code iterates through the attributes of all Foo members.:: + + foreach(member,val in Foo) + { + ::print(member+"\n"); + local attr; + if((attr = Foo.getattributes(member)) != null) { + foreach(i,v in attr) + { + ::print("\t"+i+" = "+(typeof v)+"\n"); + } + } + else { + ::print("\t\n") + } + } + +----------------- +Class Instances +----------------- + +.. index:: + pair: instances; Class + single: Class Instances + +The class objects inherits several of the table's feature with the difference that multiple instances of the +same class can be created. +A class instance is an object that share the same structure of the table that created it but +holds is own values. +Class *instantiation* uses function notation. +A class instance is created by calling a class object. Can be useful to imagine a class like a function +that returns a class instance.:: + + //creates a new instance of Foo + local inst = Foo(); + +When a class instance is created its member are initialized *with the same value* specified in the +class declaration. The values are copied verbatim, *no cloning is performed* even if the value is a container or a class instances. + +.. note:: FOR C# and Java programmers: + + Squirrel doesn't clone member's default values nor executes the member declaration for each instance(as C# or java). + + So consider this example: :: + + class Foo { + myarray = [1,2,3] + mytable = {} + } + + local a = Foo(); + local b = Foo(); + + In the snippet above both instances will refer to the same array and same table.To achieve what a C# or Java programmer would + expect, the following approach should be taken. :: + + class Foo { + myarray = null + mytable = null + constructor() + { + myarray = [1,2,3] + mytable = {} + } + } + + local a = Foo(); + local b = Foo(); + +When a class defines a method called 'constructor', the class instantiation operation will +automatically invoke it for the newly created instance. +The constructor method can have parameters, this will impact on the number of parameters +that the *instantiation operation* will require. +Constructors, as normal functions, can have variable number of parameters (using the parameter ``...``).:: + + class Rect { + constructor(w,h) + { + width = w; + height = h; + } + x = 0; + y = 0; + width = null; + height = null; + } + + //Rect's constructor has 2 parameters so the class has to be 'called' + //with 2 parameters + local rc = Rect(100,100); + +After an instance is created, its properties can be set or fetched following the +same rules that apply to tables. Methods cannot be set. + +Instance members cannot be removed. + +The class object that created a certain instance can be retrieved through the built-in function +``instance.getclass()`` (see :ref:`built-in functions `) + +The operator ``instanceof`` tests if a class instance is an instance of a certain class.:: + + local rc = Rect(100,100); + if(rc instanceof ::Rect) { + ::print("It's a rect"); + } + else { + ::print("It isn't a rect"); + } + +.. note:: Since Squirrel 3.x instanceof doesn't throw an exception if the left expression is not a class, it simply fails + +-------------- +Inheritance +-------------- + +.. index:: + pair: inheritance; Class + single: Inheritance + +Squirrel's classes support single inheritance by adding the keyword ``extends``, followed +by an expression, in the class declaration. +The syntax for a derived class is the following: :: + + class SuperFoo extends Foo { + function DoSomething() { + ::print("I'm doing something"); + } + } + +When a derived class is declared, Squirrel first copies all base's members in the +new class then proceeds with evaluating the rest of the declaration. + +A derived class inherit all members and properties of it's base, if the derived class +overrides a base function the base implementation is shadowed. +It's possible to access a overridden method of the base class by fetching the method from +through the 'base' keyword. + +Here an example: :: + + class Foo { + function DoSomething() { + ::print("I'm the base"); + } + }; + + class SuperFoo extends Foo { + //overridden method + function DoSomething() { + //calls the base method + base.DoSomething(); + ::print("I'm doing something"); + } + } + +Same rule apply to the constructor. The constructor is a regular function (apart from being automatically invoked on construction).:: + + class BaseClass { + constructor() + { + ::print("Base constructor\n"); + } + } + + class ChildClass extends BaseClass { + constructor() + { + base.constructor(); + ::print("Child constructor\n"); + } + } + + local test = ChildClass(); + +The base class of a derived class can be retrieved through the built-in method ``getbase()``.:: + + local thebaseclass = SuperFoo.getbase(); + +Note that because methods do not have special protection policies when calling methods of the same +objects, a method of a base class that calls a method of the same class can end up calling a overridden method of the derived class. + +A method of a base class can be explicitly invoked by a method of a derived class though the keyword ``base`` (as in base.MyMethod() ).:: + + class Foo { + function DoSomething() { + ::print("I'm the base"); + } + function DoIt() + { + DoSomething(); + } + }; + + class SuperFoo extends Foo { + //overridden method + function DoSomething() { + ::print("I'm the derived"); + + } + function DoIt() { + base.DoIt(); + } + } + + //creates a new instance of SuperFoo + local inst = SuperFoo(); + + //prints "I'm the derived" + inst.DoIt(); + +---------------------- +Metamethods +---------------------- + +.. index:: + pair: metamethods; Class + single: Class metamethods + +Class instances allow the customization of certain aspects of the +their semantics through metamethods(see see :ref:`Metamethods `). +For C++ programmers: "metamethods behave roughly like overloaded operators". +The metamethods supported by classes are ``_add, _sub, _mul, _div, _unm, _modulo, +_set, _get, _typeof, _nexti, _cmp, _call, _delslot, _tostring`` + +Class objects instead support only 2 metamethods : ``_newmember`` and ``_inherited`` + +the following example show how to create a class that implements the metamethod ``_add``.:: + + class Vector3 { + constructor(...) + { + if(vargv.len() >= 3) { + x = vargv[0]; + y = vargv[1]; + z = vargv[2]; + } + } + function _add(other) + { + return ::Vector3(x+other.x,y+other.y,z+other.z); + } + + x = 0; + y = 0; + z = 0; + } + + local v0 = Vector3(1,2,3) + local v1 = Vector3(11,12,13) + local v2 = v0 + v1; + ::print(v2.x+","+v2.y+","+v2.z+"\n"); + +Since version 2.1, classes support 2 metamethods ``_inherited`` and ``_newmember``. +``_inherited`` is invoked when a class inherits from the one that implements ``_inherited``. +``_newmember`` is invoked for each member that is added to the class(at declaration time). diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst b/sp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst new file mode 100644 index 0000000000..77fd9bc289 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/constants_and_enumerations.rst @@ -0,0 +1,97 @@ +.. _constants_and_enumerations: + + +======================== +Constants & Enumerations +======================== + +.. index:: + single: Constants & Enumerations + + + +Squirrel allows to bind constant values to an identifier that will be evaluated compile-time. +This is achieved though constants and Enumerations. + +--------------- +Constants +--------------- + +.. index:: + single: Constants + +Constants bind a specific value to an identifier. Constants are similar to +global values, except that they are evaluated compile time and their value cannot be changed. + +constants values can only be integers, floats or string literals. No expression are allowed. +are declared with the following syntax.:: + + const foobar = 100; + const floatbar = 1.2; + const stringbar = "I'm a constant string"; + +constants are always globally scoped, from the moment they are declared, any following code +can reference them. +Constants will shadow any global slot with the same name( the global slot will remain visible by using the ``::`` syntax).:: + + local x = foobar * 2; + +--------------- +Enumerations +--------------- + +.. index:: + single: Enumerations + +As Constants, Enumerations bind a specific value to a name. Enumerations are also evaluated at compile time +and their value cannot be changed. + +An enum declaration introduces a new enumeration into the program. +Enumeration values can only be integers, floats or string literals. No expression are allowed.:: + + enum Stuff { + first, //this will be 0 + second, //this will be 1 + third //this will be 2 + } + +or:: + + enum Stuff { + first = 10 + second = "string" + third = 1.2 + } + +An enum value is accessed in a manner that's similar to accessing a static class member. +The name of the member must be qualified with the name of the enumeration, for example ``Stuff.second`` +Enumerations will shadow any global slot with the same name( the global slot will remain visible by using the ``::`` syntax).:: + + local x = Stuff.first * 2; + +-------------------- +Implementation notes +-------------------- + +Enumerations and Constants are a compile-time feature. Only integers, string and floats can be declared as const/enum; +No expressions are allowed(because they would have to be evaluated compile time). +When a const or an enum is declared, it is added compile time to the ``consttable``. This table is stored in the VM shared state +and is shared by the VM and all its threads. +The ``consttable`` is a regular squirrel table; In the same way as the ``roottable`` +it can be modified runtime. +You can access the ``consttable`` through the built-in function ``getconsttable()`` +and also change it through the built-in function ``setconsttable()`` + +here some example: :: + + //creates a constant + getconsttable()["something"] <- 10" + //creates an enumeration + getconsttable()["somethingelse"] <- { a = "10", c = "20", d = "200"}; + //deletes the constant + delete getconsttable()["something"] + //deletes the enumeration + delete getconsttable()["somethingelse"] + +This system allows to procedurally declare constants and enumerations, it is also possible to assign any squirrel type +to a constant/enumeration(function,classes etc...). However this will make serialization of a code chunk impossible. diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst b/sp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst new file mode 100644 index 0000000000..843ed8b3d0 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/datatypes.rst @@ -0,0 +1,162 @@ +.. _datatypes_and_values: + +===================== +Values and Data types +===================== + +While Squirrel is a dynamically typed language and variables do not +have a type, different operations may interpret the variable as +containing a type. Squirrel's basic types are integer, float, string, +null, table, array, function, generator, class, instance, bool, thread +and userdata. + +.. _userdata-index: + +-------- +Integer +-------- + +An Integer represents a 32 bit (or better) signed number.:: + + local a = 123 //decimal + local b = 0x0012 //hexadecimal + local c = 075 //octal + local d = 'w' //char code + +-------- +Float +-------- + +A float represents a 32 bit (or better) floating point number.:: + + local a=1.0 + local b=0.234 + +-------- +String +-------- + +Strings are an immutable sequence of characters. In order to modify a +string is it necessary create a new one. + +Squirrel's strings are similar to strings in C or C++. They are +delimited by quotation marks(``"``) and can contain escape +sequences (``\t``, ``\a``, ``\b``, ``\n``, ``\r``, ``\v``, ``\f``, +``\\``, ``\"``, ``\'``, ``\0``, ``\x``, ``\u`` and +``\U``). + +Verbatim string literals do not interpret escape sequences. They begin +with ``@"`` and end with the matching quote. Verbatim string literals +also can extend over a line break. If they do, they include any white +space characters between the quotes: :: + + local a = "I'm a wonderful string\n" + // has a newline at the end of the string + local x = @"I'm a verbatim string\n" + // the \n is literal, similar to "\\n" in a regular string. + +However, a doubled quotation mark within a verbatim string is replaced +by a single quotation mark: :: + + local multiline = @" + this is a multiline string + it will ""embed"" all the new line + characters + " + +-------- +Null +-------- + +The null value is a primitive value that represents the null, empty, or non-existent +reference. The type Null has exactly one value, called null.:: + + local a = null + +-------- +Bool +-------- + +Bool is a double-valued (Boolean) data type. Its literals are ``true`` +and ``false``. A bool value expresses the validity of a condition +(tells whether the condition is true or false).:: + + local a = true; + +-------- +Table +-------- + +Tables are associative containers implemented as a set of key/value pairs +called slots.:: + + local t={} + local test= + { + a=10 + b=function(a) { return a+1; } + } + +-------- +Array +-------- + +Arrays are simple sequence of objects. Their size is dynamic and their index always starts from 0.:: + + local a = ["I'm","an","array"] + local b = [null] + b[0] = a[2]; + +-------- +Function +-------- + +Functions are similar to those in other C-like languages with a few key differences (see below). + +-------- +Class +-------- + +Classes are associative containers implemented as sets of key/value +pairs. Classes are created through a 'class expression' or a 'class +statement'. class members can be inherited from another class object +at creation time. After creation, members can be added until an +instance of the class is created. + +-------------- +Class Instance +-------------- + +Class instances are created by calling a *class object*. Instances, as +tables, are implemented as sets of key/value pairs. Instance members +cannot be dynamically added or removed; however the value of the +members can be changed. + +--------- +Generator +--------- + +Generators are functions that can be suspended with the statement +'yield' and resumed later (see :ref:`Generators `). + +--------- +Userdata +--------- + +Userdata objects are blobs of memory or pointers defined by the host +application but stored within Squirrel variables (See :ref:`Userdata +and UserPointers `). + +--------- +Thread +--------- + +Threads are objects representing a cooperative thread of execution, +also known as coroutines. + +-------------- +Weak Reference +-------------- + +Weak References are objects that point to another (non-scalar) object but do not own a strong reference to it. +(See :ref:`Weak References `). diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/delegation.rst b/sp/src/vscript/squirrel/doc/source/reference/language/delegation.rst new file mode 100644 index 0000000000..63607d5cde --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/delegation.rst @@ -0,0 +1,35 @@ +.. _delegation: + + +======================== +Delegation +======================== + +.. index:: + single: Delegation + +Squirrel supports implicit delegation. Every table or userdata can have a parent table +(delegate). A parent table is a normal table that allows the definition of special behaviors +for his child. +When a table (or userdata) is indexed with a key that doesn't correspond to one of its +slots, the interpreter automatically delegates the get (or set) operation to its parent.:: + + Entity <- { + } + + function Entity::DoStuff() + { + ::print(_name); + } + + local newentity = { + _name="I'm the new entity" + } + newentity.setdelegate(Entity) + + newentity.DoStuff(); //prints "I'm the new entity" + +The delegate of a table can be retreived through built-in method ``table.getdelegate()``.:: + + local thedelegate = newentity.getdelegate(); + diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst b/sp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst new file mode 100644 index 0000000000..47ef320984 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/execution_context.rst @@ -0,0 +1,95 @@ +.. _executioncontext: + +======================= +Execution Context +======================= + +.. index:: + single: execution context + +The execution context is the union of the function stack frame and the function +environment object(this) and the function root(root table). +The stack frame is the portion of stack where the local variables declared in its body are +stored. +The environment object is an implicit parameter that is automatically passed by the +function caller (see see :ref:`functions `). +The root table is a table associated to the function during its creation. +The root table value of a function is the root table of the VM at the function creation. +The root table of function can also be changed after creation with closure.setroot(). +During the execution, the body of a function can only transparently refer to his execution +context. +This mean that a single identifier can refer to a local variable, to an environment object slot +or to the slot of the closure root table; +The environment object can be explicitly accessed by the keyword ``this``. +The closure root table can be explicitly accessed through the operator ``::`` (see :ref:`Variables `). + +.. _variables: + +----------------- +Variables +----------------- + +There are two types of variables in Squirrel, local variables and tables/arrays slots. +Because global variables(variables stored in the root of a closure) are stored in a table, they are table slots. + +A single identifier refers to a local variable or a slot in the environment object.:: + + derefexp := id; + +:: + + _table["foo"] + _array[10] + +with tables we can also use the '.' syntax:: + + derefexp := exp '.' id + +:: + + _table.foo + +Squirrel first checks if an identifier is a local variable (function arguments are local +variables) if not looks up the environment object (this) and finally looks up +to the closure root. + +For instance::: + + function testy(arg) + { + local a=10; + print(a); + return arg; + } + +in this case 'foo' will be equivalent to 'this.foo' or this["foo"]. + +Global variables are stored in a table called the root table. Usually in the global scope the +environment object is the root table, but to explicitly access the closure root of the function from +another scope, the slot name must be prefixed with ``'::'`` (``::foo``). + +For instance::: + + function testy(arg) + { + local a=10; + return arg+::foo; + } + +accesses the variable 'foo' in the closure root table. + +Since Squirrel 3.1 each function has a weak reference to a specific root table, this can differ from the current VM root table.:: + + function test() { + foo = 10; + } + +is equivalent to write:: + + function test() { + if("foo" in this) { + this.foo = 10; + }else { + ::foo = 10; + } + } diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/expressions.rst b/sp/src/vscript/squirrel/doc/source/reference/language/expressions.rst new file mode 100644 index 0000000000..9e75c7e7a3 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/expressions.rst @@ -0,0 +1,374 @@ +.. _expressions: + + +================= +Expressions +================= + +.. index:: + single: Expressions + +---------------- +Assignment +---------------- + +.. index:: + single: assignment(=) + single: new slot(<-) + +:: + + exp := derefexp '=' exp + exp:= derefexp '<-' exp + +squirrel implements 2 kind of assignment: the normal assignment(=):: + + a = 10; + +and the "new slot" assignment.:: + + a <- 10; + +The new slot expression allows to add a new slot into a table(see :ref:`Tables `). If the slot +already exists in the table it behaves like a normal assignment. + +---------------- +Operators +---------------- + +.. index:: + single: Operators + +^^^^^^^^^^^^^ +?: Operator +^^^^^^^^^^^^^ + +.. index:: + pair: ?: Operator; Operators + +:: + + exp := exp_cond '?' exp1 ':' exp2 + +conditionally evaluate an expression depending on the result of an expression. + +^^^^^^^^^^^^^ +Arithmetic +^^^^^^^^^^^^^ + +.. index:: + pair: Arithmetic Operators; Operators + +:: + + exp:= 'exp' op 'exp' + +Squirrel supports the standard arithmetic operators ``+, -, *, / and %``. +Other than that is also supports compact operators (``+=,-=,*=,/=,%=``) and +increment and decrement operators(++ and --);:: + + a += 2; + //is the same as writing + a = a + 2; + x++ + //is the same as writing + x = x + 1 + +All operators work normally with integers and floats; if one operand is an integer and one +is a float the result of the expression will be float. +The + operator has a special behavior with strings; if one of the operands is a string the +operator + will try to convert the other operand to string as well and concatenate both +together. For instances and tables, ``_tostring`` is invoked. + +^^^^^^^^^^^^^ +Relational +^^^^^^^^^^^^^ + +.. index:: + pair: Relational Operators; Operators + +:: + + exp:= 'exp' op 'exp' + +Relational operators in Squirrel are : ``==, <, <=, <, <=, !=`` + +These operators return true if the expression is false and a value different than true if the +expression is true. Internally the VM uses the integer 1 as true but this could change in +the future. + +^^^^^^^^^^^^^^ +3 ways compare +^^^^^^^^^^^^^^ + +.. index:: + pair: 3 ways compare operator; Operators + +:: + + exp:= 'exp' <=> 'exp' + +the 3 ways compare operator <=> compares 2 values A and B and returns an integer less than 0 +if A < B, 0 if A == B and an integer greater than 0 if A > B. + +^^^^^^^^^^^^^^ +Logical +^^^^^^^^^^^^^^ + +.. index:: + pair: Logical operators; Operators + +:: + + exp := exp op exp + exp := '!' exp + +Logical operators in Squirrel are : ``&&, ||, !`` + +The operator ``&&`` (logical and) returns null if its first argument is null, otherwise returns +its second argument. +The operator ``||`` (logical or) returns its first argument if is different than null, otherwise +returns the second argument. + +The '!' operator will return null if the given value to negate was different than null, or a +value different than null if the given value was null. + +^^^^^^^^^^^^^^^ +in operator +^^^^^^^^^^^^^^^ + +.. index:: + pair: in operator; Operators + +:: + + exp:= keyexp 'in' tableexp + +Tests the existence of a slot in a table. +Returns true if *keyexp* is a valid key in *tableexp* :: + + local t= + { + foo="I'm foo", + [123]="I'm not foo" + } + + if("foo" in t) dostuff("yep"); + if(123 in t) dostuff(); + +^^^^^^^^^^^^^^^^^^^ +instanceof operator +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: instanceof operator; Operators + +:: + + exp:= instanceexp 'instanceof' classexp + +Tests if a class instance is an instance of a certain class. +Returns true if *instanceexp* is an instance of *classexp*. + +^^^^^^^^^^^^^^^^^^^ +typeof operator +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: typeof operator; Operators + +:: + + exp:= 'typeof' exp + +returns the type name of a value as string.:: + + local a={},b="squirrel" + print(typeof a); //will print "table" + print(typeof b); //will print "string" + +^^^^^^^^^^^^^^^^^^^ +Comma operator +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: Comma operator; Operators + +:: + + exp:= exp ',' exp + +The comma operator evaluates two expression left to right, the result of the operator is +the result of the expression on the right; the result of the left expression is discarded.:: + + local j=0,k=0; + for(local i=0; i<10; i++ , j++) + { + k = i + j; + } + local a,k; + a = (k=1,k+2); //a becomes 3 + +^^^^^^^^^^^^^^^^^^^ +Bitwise Operators +^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: Bitwise Operators; Operators + +:: + + exp:= 'exp' op 'exp' + exp := '~' exp + +Squirrel supports the standard C-like bitwise operators ``&, |, ^, ~, <<, >>`` plus the unsigned +right shift operator ``>>>``. The unsigned right shift works exactly like the normal right shift operator(``>>``) +except for treating the left operand as an unsigned integer, so is not affected by the sign. Those operators +only work on integer values; passing of any other operand type to these operators will +cause an exception. + +^^^^^^^^^^^^^^^^^^^^^ +Operators precedence +^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + pair: Operators precedence; Operators + ++---------------------------------------+-----------+ +| ``-, ~, !, typeof , ++, --`` | highest | ++---------------------------------------+-----------+ +| ``/, *, %`` | ... | ++---------------------------------------+-----------+ +| ``+, -`` | | ++---------------------------------------+-----------+ +| ``<<, >>, >>>`` | | ++---------------------------------------+-----------+ +| ``<, <=, >, >=, instanceof`` | | ++---------------------------------------+-----------+ +| ``==, !=, <=>`` | | ++---------------------------------------+-----------+ +| ``&`` | | ++---------------------------------------+-----------+ +| ``^`` | | ++---------------------------------------+-----------+ +| ``&&, in`` | | ++---------------------------------------+-----------+ +| ``+=, =, -=, /=, *=, %=`` | ... | ++---------------------------------------+-----------+ +| ``, (comma operator)`` | lowest | ++---------------------------------------+-----------+ + +.. _table_contructor: + +----------------- +Table Constructor +----------------- + +.. index:: + single: Table Contructor + +:: + + tslots := ( 'id' '=' exp | '[' exp ']' '=' exp ) [','] + exp := '{' [tslots] '}' + +Creates a new table.:: + + local a = {} //create an empty table + +A table constructor can also contain slots declaration; With the syntax: :: + + local a = { + slot1 = "I'm the slot value" + } + +An alternative syntax can be:: + + '[' exp1 ']' = exp2 [','] + +A new slot with exp1 as key and exp2 as value is created:: + + local a= + { + [1]="I'm the value" + } + +Both syntaxes can be mixed:: + + local table= + { + a=10, + b="string", + [10]={}, + function bau(a,b) + { + return a+b; + } + } + +The comma between slots is optional. + +^^^^^^^^^^^^^^^^^^^^^^ +Table with JSON syntax +^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: Table with JSON syntax + +Since Squirrel 3.0 is possible to declare a table using JSON syntax(see http://www.wikipedia.org/wiki/JSON). + +the following JSON snippet: :: + + local x = { + "id": 1, + "name": "Foo", + "price": 123, + "tags": ["Bar","Eek"] + } + +is equivalent to the following squirrel code: :: + + local x = { + id = 1, + name = "Foo", + price = 123, + tags = ["Bar","Eek"] + } + +----------------- +clone +----------------- + +.. index:: + single: clone + +:: + + exp:= 'clone' exp + +Clone performs shallow copy of a table, array or class instance (copies all slots in the new object without +recursion). If the source table has a delegate, the same delegate will be assigned as +delegate (not copied) to the new table (see :ref:`Delegation `). + +After the new object is ready the "_cloned" meta method is called (see :ref:`Metamethods `). + +When a class instance is cloned the constructor is not invoked(initializations must rely on ```_cloned``` instead + +----------------- +Array contructor +----------------- + +.. index:: + single: Array constructor + +:: + + exp := '[' [explist] ']' + +Creates a new array.:: + + a <- [] //creates an empty array + +Arrays can be initialized with values during the construction:: + + a <- [1,"string!",[],{}] //creates an array with 4 elements diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/functions.rst b/sp/src/vscript/squirrel/doc/source/reference/language/functions.rst new file mode 100644 index 0000000000..f3f98c0bd6 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/functions.rst @@ -0,0 +1,272 @@ +.. _functions: + + +================= +Functions +================= + +.. index:: + single: Functions + +Functions are first class values like integer or strings and can be stored in table slots, +local variables, arrays and passed as function parameters. +Functions can be implemented in Squirrel or in a native language with calling conventions +compatible with ANSI C. + +-------------------- +Function declaration +-------------------- + +.. index:: + single: Function Declaration + +Functions are declared through the function expression:: + + local a = function(a, b, c) { return a + b - c; } + +or with the syntactic sugar:: + + function ciao(a,b,c) + { + return a+b-c; + } + +that is equivalent to:: + + this.ciao <- function(a,b,c) + { + return a+b-c; + } + +a local function can be declared with this syntactic sugar:: + + local function tuna(a,b,c) + { + return a+b-c; + } + +that is equivalent to:: + + local tuna = function(a,b,c) + { + return a+b-c; + } + +is also possible to declare something like:: + + T <- {} + function T::ciao(a,b,c) + { + return a+b-c; + } + + //that is equivalent to write + + T.ciao <- function(a,b,c) + { + return a+b-c; + } + + //or + + T <- { + function ciao(a,b,c) + { + return a+b-c; + } + } + +^^^^^^^^^^^^^^^^^^ +Default Paramaters +^^^^^^^^^^^^^^^^^^ + +.. index:: + single: Function Default Paramaters + +Squirrel's functions can have default parameters. + +A function with default parameters is declared as follows: :: + + function test(a,b,c = 10, d = 20) + { + .... + } + +when the function *test* is invoked and the parameter c or d are not specified, +the VM autometically assigns the default value to the unspecified parameter. A default parameter can be +any valid squirrel expression. The expression is evaluated at runtime. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Function with variable number of paramaters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: + single: Function with variable number of paramaters + +Squirrel's functions can have variable number of parameters(varargs functions). + +A vararg function is declared by adding three dots (`...`) at the end of its parameter list. + +When the function is called all the extra parameters will be accessible through the *array* +called ``vargv``, that is passed as implicit parameter. + +``vargv`` is a regular squirrel array and can be used accordingly.:: + + function test(a,b,...) + { + for(local i = 0; i< vargv.len(); i++) + { + ::print("varparam "+i+" = "+vargv[i]+"\n"); + } + foreach(i,val in vargv) + { + ::print("varparam "+i+" = "+val+"\n"); + } + } + + test("goes in a","goes in b",0,1,2,3,4,5,6,7,8); + +--------------- +Function calls +--------------- + +.. index:: + single: Function calls + +:: + + exp:= derefexp '(' explist ')' + +The expression is evaluated in this order: derefexp after the explist (arguments) and at +the end the call. + +A function call in Squirrel passes the current environment object *this* as a hidden parameter. +But when the function was immediately indexed from an object, *this* shall be the object +which was indexed, instead. + +If we call a function with the syntax:: + + mytable.foo(x,y) + +the environment object passed to 'foo' as *this* will be 'mytable' (since 'foo' was immediately indexed from 'mytable') + +Whereas with the syntax:: + + foo(x,y) // implicitly equivalent to this.foo(x,y) + +the environment object will be the current *this* (that is, propagated from the caller's *this*). + +It may help to remember the rules in the following way: + + foo(x,y) ---> this.foo(x,y) + table.foo(x,y) ---> call foo with (table,x,y) + +It may also help to consider why it works this way: it's designed to assist with object-oriented style. +When calling 'foo(x,y)' it's assumed you're calling another member of the object (or of the file) and +so should operate on the same object. +When calling 'mytable.foo(x,y)' it's written plainly that you're calling a member of a different object. + +--------------------------------------------- +Binding an environment to a function +--------------------------------------------- + +.. index:: + single: Binding an environment to a function + +while by default a squirrel function call passes as environment object 'this', the object +where the function was indexed from. However, is also possible to statically bind an evironment to a +closure using the built-in method ``closure.bindenv(env_obj)``. +The method bindenv() returns a new instance of a closure with the environment bound to it. +When an environment object is bound to a function, every time the function is invoked, its +'this' parameter will always be the previously bound environent. +This mechanism is useful to implement callbacks systems similar to C# delegates. + +.. note:: The closure keeps a weak reference to the bound environmet object, because of this if + the object is deleted, the next call to the closure will result in a ``null`` + environment object. + +--------------------------------------------- +Lambda Expressions +--------------------------------------------- + +.. index:: + single: Lambda Expressions + +:: + + exp := '@' '(' paramlist ')' exp + +Lambda expressions are a syntactic sugar to quickly define a function that consists of a single expression. +This feature comes handy when functional programming patterns are applied, like map/reduce or passing a compare method to +array.sort(). + +here a lambda expression:: + + local myexp = @(a,b) a + b + +that is equivalent to:: + + local myexp = function(a,b) { return a + b; } + +a more useful usage could be:: + + local arr = [2,3,5,8,3,5,1,2,6]; + arr.sort(@(a,b) a <=> b); + arr.sort(@(a,b) -(a <=> b)); + +that could have been written as:: + + local arr = [2,3,5,8,3,5,1,2,6]; + arr.sort(function(a,b) { return a <=> b; } ); + arr.sort(function(a,b) { return -(a <=> b); } ); + +other than being limited to a single expression lambdas support all features of regular functions. +in fact are implemented as a compile time feature. + +--------------------------------------------- +Free Variables +--------------------------------------------- + +.. index:: + single: Free Variables + +A free variable is a variable external from the function scope as is not a local variable +or parameter of the function. +Free variables reference a local variable from a outer scope. +In the following example the variables 'testy', 'x' and 'y' are bound to the function 'foo'.:: + + local x=10,y=20 + local testy="I'm testy" + + function foo(a,b) + { + ::print(testy); + return a+b+x+y; + } + +A program can read or write a free variable. + +--------------------------------------------- +Tail Recursion +--------------------------------------------- + +.. index:: + single: Tail Recursion + +Tail recursion is a method for partially transforming a recursion in a program into an +iteration: it applies when the recursive calls in a function are the last executed +statements in that function (just before the return). +If this happenes the squirrel interpreter collapses the caller stack frame before the +recursive call; because of that very deep recursions are possible without risk of a stack +overflow.:: + + function loopy(n) + { + if(n>0){ + ::print("n="+n+"\n"); + return loopy(n-1); + } + } + + loopy(1000); + diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/generators.rst b/sp/src/vscript/squirrel/doc/source/reference/language/generators.rst new file mode 100644 index 0000000000..6c03de9a9b --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/generators.rst @@ -0,0 +1,51 @@ +.. _generators: + + +================= +Generators +================= + +.. index:: + single: Generators + +A function that contains a ``yield`` statement is called *'generator function'* . +When a generator function is called, it does not execute the function body, instead it +returns a new suspended generator. +The returned generator can be resumed through the resume statement while it is alive. +The yield keyword, suspends the execution of a generator and optionally returns the +result of an expression to the function that resumed the generator. +The generator dies when it returns, this can happen through an explicit return +statement or by exiting the function body; If an unhandled exception (or runtime error) +occurs while a generator is running, the generator will automatically die. A dead +generator cannot be resumed anymore.:: + + function geny(n) + { + for(local i=1;i<=n;i+=1) + yield i; + return null; + } + + local gtor=geny(10); + local x; + while(x=resume gtor) print(x+"\n"); + +the output of this program will be:: + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + +generators can also be iterated using the foreach statement. When a generator is evaluated +by ``foreach``, the generator will be resumed for each iteration until it returns. The value +returned by the ``return`` statement will be ignored. + +.. note:: A suspended generator will hold a strong reference to all the values stored in it's local variables except the ``this`` + object that is only a weak reference. A running generator hold a strong reference also to the ``this`` object. diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst b/sp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst new file mode 100644 index 0000000000..72e23f5ca1 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/lexical_structure.rst @@ -0,0 +1,154 @@ +.. _lexical_structure: + + +================= +Lexical Structure +================= + +.. index:: single: lexical structure + +----------- +Identifiers +----------- + +.. index:: single: identifiers + +Identifiers start with an alphabetic character or the symbol '_' followed by any number +of alphabetic characters, '_' or digits ([0-9]). Squirrel is a case sensitive language +meaning that the lowercase and uppercase representation of the same alphabetic +character are considered different characters. For instance, "foo", "Foo" and "fOo" are +treated as 3 distinct identifiers. + +----------- +Keywords +----------- + +.. index:: single: keywords + +The following words are reserved and cannot be used as identifiers: + ++------------+------------+-----------+------------+------------+-------------+ +| base | break | case | catch | class | clone | ++------------+------------+-----------+------------+------------+-------------+ +| continue | const | default | delete | else | enum | ++------------+------------+-----------+------------+------------+-------------+ +| extends | for | foreach | function | if | in | ++------------+------------+-----------+------------+------------+-------------+ +| local | null | resume | return | switch | this | ++------------+------------+-----------+------------+------------+-------------+ +| throw | try | typeof | while | yield | constructor | ++------------+------------+-----------+------------+------------+-------------+ +| instanceof | true | false | static | __LINE__ | __FILE__ | ++------------+------------+-----------+------------+------------+-------------+ + +Keywords are covered in detail later in this document. + +----------- +Operators +----------- + +.. index:: single: operators + +Squirrel recognizes the following operators: + ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``!`` | ``!=`` | ``||`` | ``==`` | ``&&`` | ``>=`` | ``<=`` | ``>`` | ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``<=>`` | ``+`` | ``+=`` | ``-`` | ``-=`` | ``/`` | ``/=`` | ``*`` | ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``*=`` | ``%`` | ``%=`` | ``++`` | ``--`` | ``<-`` | ``=`` | ``&`` | ++----------+----------+----------+----------+----------+----------+----------+----------+ +| ``^`` | ``|`` | ``~`` | ``>>`` | ``<<`` | ``>>>`` | | | ++----------+----------+----------+----------+----------+----------+----------+----------+ + +------------ +Other tokens +------------ + +.. index:: + single: delimiters + single: other tokens + +Other significant tokens are: + ++----------+----------+----------+----------+----------+----------+ +| ``{`` | ``}`` | ``[`` | ``]`` | ``.`` | ``:`` | ++----------+----------+----------+----------+----------+----------+ +| ``::`` | ``'`` | ``;`` | ``"`` | ``@"`` | | ++----------+----------+----------+----------+----------+----------+ + +----------- +Literals +----------- + +.. index:: + single: literals + single: string literals + single: numeric literals + +Squirrel accepts integer numbers, floating point numbers and string literals. + ++-------------------------------+------------------------------------------+ +| ``34`` | Integer number(base 10) | ++-------------------------------+------------------------------------------+ +| ``0xFF00A120`` | Integer number(base 16) | ++-------------------------------+------------------------------------------+ +| ``0753`` | Integer number(base 8) | ++-------------------------------+------------------------------------------+ +| ``'a'`` | Integer number | ++-------------------------------+------------------------------------------+ +| ``1.52`` | Floating point number | ++-------------------------------+------------------------------------------+ +| ``1.e2`` | Floating point number | ++-------------------------------+------------------------------------------+ +| ``1.e-2`` | Floating point number | ++-------------------------------+------------------------------------------+ +| ``"I'm a string"`` | String | ++-------------------------------+------------------------------------------+ +| ``@"I'm a verbatim string"`` | String | ++-------------------------------+------------------------------------------+ +| ``@" I'm a`` | | +| ``multiline verbatim string`` | | +| ``"`` | String | ++-------------------------------+------------------------------------------+ + +Pesudo BNF + +.. productionlist:: + IntegerLiteral : [1-9][0-9]* | '0x' [0-9A-Fa-f]+ | ''' [.]+ ''' | 0[0-7]+ + FloatLiteral : [0-9]+ '.' [0-9]+ + FloatLiteral : [0-9]+ '.' 'e'|'E' '+'|'-' [0-9]+ + StringLiteral: '"'[.]* '"' + VerbatimStringLiteral: '@''"'[.]* '"' + +----------- +Comments +----------- + +.. index:: single: comments + +A comment is text that the compiler ignores but that is useful for programmers. +Comments are normally used to embed annotations in the code. The compiler +treats them as white space. + +A comment can be ``/*`` (slash, asterisk) characters, followed by any +sequence of characters (including new lines), +followed by the ``*/`` characters. This syntax is the same as ANSI C.:: + + /* + this is + a multiline comment. + this lines will be ignored by the compiler + */ + +A comment can also be ``//`` (two slashes) characters, followed by any sequence of +characters. A new line not immediately preceded by a backslash terminates this form of +comment. It is commonly called a *"single-line comment."*:: + + //this is a single line comment. this line will be ignored by the compiler + +The character ``#`` is an alternative syntax for single line comment.:: + + # this is also a single line comment. + +This to facilitate the use of squirrel in UNIX-style shell scripts. diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst b/sp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst new file mode 100644 index 0000000000..260b0308ab --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/metamethods.rst @@ -0,0 +1,270 @@ +.. _metamethods: + +----------- +Metamethods +----------- + +Metamethods are a mechanism that allows the customization of certain aspects of the +language semantics. Those methods are normal functions placed in a table +parent(delegate) or class declaration; It is possible to change many aspects of a table/class instance behavior by just defining +a metamethod. Class objects (not instances) support only 2 metamethods ``_newmember, _inherited`` . + +For example when we use relational operators other than '==' on 2 tables, the VM will +check if the table has a method in his parent called '_cmp'; if so it will call it to determine +the relation between the tables.:: + + local comparable={ + _cmp = function (other) + { + if(nameother.name)return 1; + return 0; + } + } + + local a={ name="Alberto" }.setdelegate(comparable); + local b={ name="Wouter" }.setdelegate(comparable); + + if(a>b) + print("a>b") + else + print("b<=a"); + +for classes the previous code become: :: + + class Comparable { + constructor(n) + { + name = n; + } + function _cmp(other) + { + if(nameother.name) return 1; + return 0; + } + name = null; + } + + local a = Comparable("Alberto"); + local b = Comparable("Wouter"); + + if(a>b) + print("a>b") + else + print("b<=a"); + +^^^^^ +_set +^^^^^ + +:: + + _set(idx,val) + +invoked when the index idx is not present in the object or in its delegate chain. +``_set`` must 'throw null' to notify that a key wasn't found but the there were not runtime errors (clean failure). +This allows the program to differentiate between a runtime error and a 'index not found'. + +^^^^^ +_get +^^^^^ + +:: + + _get(idx) + +invoked when the index idx is not present in the object or in its delegate chain. +_get must 'throw null' to notify that a key wasn't found but the there were not runtime errors (clean failure). +This allows the program to differentiate between a runtime error and a 'index not found'. + +^^^^^^^^^ +_newslot +^^^^^^^^^ + +:: + + _newslot(key,value) + +invoked when a script tries to add a new slot in a table. + +if the slot already exists in the target table the method will not be invoked also if the +"new slot" operator is used. + +^^^^^^^^^ +_delslot +^^^^^^^^^ + +:: + + _delslot(key) + +invoked when a script deletes a slot from a table. +if the method is invoked squirrel will not try to delete the slot himself + +^^^^^^^^ +_add +^^^^^^^^ + +:: + + _add(other) + +the + operator + +returns this + other + +^^^^^^^^^^^^^^^^^^^^^^^^ +_sub +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _sub(other) + +the - operator (like _add) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_mul +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _mul(other) + +the ``*`` operator (like _add) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_div +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _div(other) + +the ``/`` operator (like _add) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_modulo +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _modulo(other) + +the ``%`` operator (like _add) + +^^^^^^^^^ +_unm +^^^^^^^^^ + +:: + + _unm() + +the unary minus operator + +^^^^^^^^^^^^^^^^^^^^^^^^ +_typeof +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _typeof() + +invoked by the typeof operator on tables, userdata, and class instances. + +Returns the type of ``this`` as string + +^^^^^^^^^^^^^^^^^^^^^^^^ +_cmp +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _cmp(other) + +invoked to emulate the ``< > <= >=`` and ``<=>`` operators + +returns an integer as follow: + ++-----------+----------------------------+ +| returns | relationship | ++===========+============================+ +| > 0 | if ``this`` > ``other`` | ++-----------+----------------------------+ +| 0 | if ``this`` == ``other`` | ++-----------+----------------------------+ +| < 0 | if ``this`` < ``other`` | ++-----------+----------------------------+ + +^^^^^^^^^^^^^^^^^^^^^^^^ +_call +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _call(other) + +invoked when a table, userdata, or class instance is called + +^^^^^^^^^^^^^^^^^^^^^^^^ +_cloned +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _cloned(original) + +invoked when a table or class instance is cloned(in the cloned table) + +^^^^^^^^^^^^^^^^^^^^^^^^ +_nexti +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _nexti(previdx) + +invoked when a userdata or class instance is iterated by a foreach loop. + +If previdx==null it means that it is the first iteration. +The function has to return the index of the 'next' value. + +^^^^^^^^^^^^^^^^^^^^^^^^ +_tostring +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _tostring() + +Invoked when during string concatenation or when the ``print`` function prints a table, instance, or userdata. +The method is also invoked by the sq_tostring() API. + +Must return a string representation of the object. + +^^^^^^^^^^^^^^^^^^^^^^^^ +_inherited +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _inherited(attributes) + +invoked when a class object inherits from the class implementing ``_inherited``. +The ``this`` contains the new class. + +Return value is ignored. + +^^^^^^^^^^^^^^^^^^^^^^^^ +_newmember +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + _newmember(index,value,attributes,isstatic) + +invoked for each member declared in a class body (at declaration time). + +If the function is implemented, members will not be added to the class. diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/statements.rst b/sp/src/vscript/squirrel/doc/source/reference/language/statements.rst new file mode 100644 index 0000000000..febde52670 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/statements.rst @@ -0,0 +1,386 @@ +.. _statements: + + +================= +Statements +================= + +.. index:: + single: statements + +A squirrel program is a simple sequence of statements.:: + + stats := stat [';'|'\n'] stats + +Statements in squirrel are comparable to the C-Family languages (C/C++, Java, C# +etc...): assignment, function calls, program flow control structures etc.. plus some +custom statement like yield, table and array constructors (All those will be covered in detail +later in this document). +Statements can be separated with a new line or ';' (or with the keywords case or default if +inside a switch/case statement), both symbols are not required if the statement is +followed by '}'. + +------ +Block +------ + +.. index:: + pair: block; statement + +:: + + stat := '{' stats '}' + +A sequence of statements delimited by curly brackets ({ }) is called block; +a block is a statement itself. + +----------------------- +Control Flow Statements +----------------------- + +.. index:: + single: control flow statements + +squirrel implements the most common control flow statements: ``if, while, do-while, switch-case, for, foreach`` + +^^^^^^^^^^^^^^ +true and false +^^^^^^^^^^^^^^ + +.. index:: + single: true and false + single: true + single: false + +Squirrel has a boolean type (bool) however like C++ it considers null, 0(integer) and 0.0(float) +as *false*, any other value is considered *true*. + +^^^^^^^^^^^^^^^^^ +if/else statement +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: if/else; statement + pair: if; statement + pair: else; statement + +:: + + stat:= 'if' '(' exp ')' stat ['else' stat] + +Conditionally execute a statement depending on the result of an expression.:: + + if(a>b) + a=b; + else + b=a; + //// + if(a==10) + { + b=a+b; + return a; + } + +^^^^^^^^^^^^^^^^^ +while statement +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: while; statement + +:: + + stat:= 'while' '(' exp ')' stat + +Executes a statement while the condition is true.:: + + function testy(n) + { + local a=0; + while(a100) + +^^^^^^^^^^^^^^^^^ +switch statement +^^^^^^^^^^^^^^^^^ + +.. index:: + pair: switch; statement + +:: + + stat := 'switch' ''( exp ')' '{' + 'case' case_exp ':' + stats + ['default' ':' + stats] + '}' + +Switch is a control statement allows multiple selections of code by passing control to one of the +case statements within its body. +The control is transferred to the case label whose case_exp matches with exp if none of +the case match will jump to the default label (if present). +A switch statement can contain any number if case instances, if 2 case have the same +expression result the first one will be taken in account first. The default label is only +allowed once and must be the last one. +A break statement will jump outside the switch block. + +----- +Loops +----- + +.. index:: + single: Loops + +^^^^^^^^ +for +^^^^^^^^ + +.. index:: + pair: for; statement + +:: + + stat:= 'for' '(' [initexp] ';' [condexp] ';' [incexp] ')' statement + +Executes a statement as long as a condition is different than false.:: + + for(local a=0;a<10;a+=1) + print(a+"\n"); + //or + glob <- null + for(glob=0;glob<10;glob+=1){ + print(glob+"\n"); + } + //or + for(;;){ + print(loops forever+"\n"); + } + +^^^^^^^^ +foreach +^^^^^^^^ + +.. index:: + pair: foreach; statement + +:: + + 'foreach' '(' [index_id','] value_id 'in' exp ')' stat + +Executes a statement for every element contained in an array, table, class, string or generator. +If exp is a generator it will be resumed every iteration as long as it is alive; the value will +be the result of 'resume' and the index the sequence number of the iteration starting +from 0.:: + + local a=[10,23,33,41,589,56] + foreach(idx,val in a) + print("index="+idx+" value="+val+"\n"); + //or + foreach(val in a) + print("value="+val+"\n"); + +------- +break +------- + +.. index:: + pair: break; statement + +:: + + stat := 'break' + +The break statement terminates the execution of a loop (for, foreach, while or do/while) +or jumps out of switch statement; + +--------- +continue +--------- + +.. index:: + pair: continue; statement + +:: + + stat := 'continue' + +The continue operator jumps to the next iteration of the loop skipping the execution of +the following statements. + +--------- +return +--------- + +.. index:: + pair: return; statement + +:: + + stat:= return [exp] + +The return statement terminates the execution of the current function/generator and +optionally returns the result of an expression. If the expression is omitted the function +will return null. If the return statement is used inside a generator, the generator will not +be resumable anymore. + +--------- +yield +--------- + +.. index:: + pair: yield; statement + +:: + + stat := yield [exp] + +(see :ref:`Generators `). + + +--------------------------- +Local variables declaration +--------------------------- + +.. index:: + pair: Local variables declaration; statement + +:: + + initz := id [= exp][',' initz] + stat := 'local' initz + +Local variables can be declared at any point in the program; they exist between their +declaration to the end of the block where they have been declared. +*EXCEPTION:* a local declaration statement is allowed as first expression in a for loop.:: + + for(local a=0;a<10;a+=1) + print(a); + +-------------------- +Function declaration +-------------------- + +.. index:: + pair: Function declaration; statement + +:: + + funcname := id ['::' id] + stat:= 'function' id ['::' id]+ '(' args ')' stat + +creates a new function. + +----------------- +Class declaration +----------------- + +.. index:: + pair: Class declaration; statement + +:: + + memberdecl := id '=' exp [';'] | '[' exp ']' '=' exp [';'] | functionstat | 'constructor' functionexp + stat:= 'class' derefexp ['extends' derefexp] '{' + [memberdecl] + '}' + +creates a new class. + +----------- +try/catch +----------- + +.. index:: + pair: try/catch; statement + +:: + + stat:= 'try' stat 'catch' '(' id ')' stat + +The try statement encloses a block of code in which an exceptional condition can occur, +such as a runtime error or a throw statement. The catch clause provides the exception-handling +code. When a catch clause catches an exception, its id is bound to that +exception. + +----------- +throw +----------- + +.. index:: + pair: throw; statement + +:: + + stat:= 'throw' exp + +Throws an exception. Any value can be thrown. + +-------------- +const +-------------- + +.. index:: + pair: const; statement + +:: + + stat:= 'const' id '=' 'Integer | Float | StringLiteral + +Declares a constant (see :ref:`Constants & Enumerations `). + +-------------- +enum +-------------- + +.. index:: + pair: enum; statement + +:: + + enumerations := ( 'id' '=' Integer | Float | StringLiteral ) [','] + stat:= 'enum' id '{' enumerations '}' + +Declares an enumeration (see :ref:`Constants & Enumerations `). + +-------------------- +Expression statement +-------------------- + +.. index:: + pair: Expression statement; statement + +:: + + stat := exp + +In Squirrel every expression is also allowed as statement, if so, the result of the +expression is thrown away. + diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/tables.rst b/sp/src/vscript/squirrel/doc/source/reference/language/tables.rst new file mode 100644 index 0000000000..eb80a6884c --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/tables.rst @@ -0,0 +1,71 @@ +.. _tables: + + +================= +Tables +================= + +.. index:: + single: Tables + +Tables are associative containers implemented as pairs of key/value (called slot); values +can be any possible type and keys any type except 'null'. +Tables are squirrel's skeleton, delegation and many other features are all implemented +through this type; even the environment, where "global" variables are stored, is a table +(known as root table). + +------------------ +Construction +------------------ + +Tables are created through the table constructor (see :ref:`Table constructor `) + +------------------ +Slot creation +------------------ + +.. index:: + single: Slot Creation(table) + +Adding a new slot in a existing table is done through the "new slot" operator ``<-``; this +operator behaves like a normal assignment except that if the slot does not exists it will +be created.:: + + local a = {} + +The following line will cause an exception because the slot named 'newslot' does not exist +in the table 'a':: + + a.newslot = 1234 + +this will succeed: :: + + a.newslot <- 1234; + +or:: + + a[1] <- "I'm the value of the new slot"; + +----------------- +Slot deletion +----------------- + +.. index:: + single: Slot Deletion(table) + + +:: + + exp:= delete derefexp + +Deletion of a slot is done through the keyword delete; the result of this expression will be +the value of the deleted slot.:: + + a <- { + test1=1234 + deleteme="now" + } + + delete a.test1 + print(delete a.deleteme); //this will print the string "now" + diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/threads.rst b/sp/src/vscript/squirrel/doc/source/reference/language/threads.rst new file mode 100644 index 0000000000..efe114ab7a --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/threads.rst @@ -0,0 +1,106 @@ +.. _threads: + + +======================== +Threads +======================== + +.. index:: + single: Threads + +Squirrel supports cooperative threads(also known as coroutines). +A cooperative thread is a subroutine that can suspended in mid-execution and provide a value to the +caller without returning program flow, then its execution can be resumed later from the same +point where it was suspended. +At first look a Squirrel thread can be confused with a generator, in fact their behaviour is quite similar. +However while a generator runs in the caller stack and can suspend only the local routine stack a thread +has its own execution stack, global table and error handler; This allows a thread to suspend nested calls and +have it's own error policies. + +------------------ +Using threads +------------------ + +.. index:: + single: Using Threads + +Threads are created through the built-in function 'newthread(func)'; this function +gets as parameter a squirrel function and bind it to the new thread objects (will be the thread body). +The returned thread object is initially in 'idle' state. the thread can be started with the function +'threadobj.call()'; the parameters passed to 'call' are passed to the thread function. + +A thread can be be suspended calling the function suspend(), when this happens the function +that wokeup(or started) the thread returns (If a parameter is passed to suspend() it will +be the return value of the wakeup function , if no parameter is passed the return value will be null). +A suspended thread can be resumed calling the function 'threadobj.wakeup', when this happens +the function that suspended the thread will return(if a parameter is passed to wakeup it will +be the return value of the suspend function, if no parameter is passed the return value will be null). + +A thread terminates when its main function returns or when an unhandled exception occurs during its execution.:: + + function coroutine_test(a,b) + { + ::print(a+" "+b+"\n"); + local ret = ::suspend("suspend 1"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 2"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 3"); + ::print("the coroutine says "+ret+"\n"); + return "I'm done" + } + + local coro = ::newthread(coroutine_test); + + local susparam = coro.call("test","coroutine"); //starts the coroutine + + local i = 1; + do + { + ::print("suspend passed ("+susparam+")\n") + susparam = coro.wakeup("ciao "+i); + ++i; + }while(coro.getstatus()=="suspended") + + ::print("return passed ("+susparam+")\n") + +the result of this program will be:: + + test coroutine + suspend passed (suspend 1) + the coroutine says ciao 1 + suspend passed (suspend 2) + the coroutine says ciao 2 + suspend passed (suspend 3) + the coroutine says ciao 3 + return passed (I'm done). + + +the following is an interesting example of how threads and tail recursion +can be combined.:: + + function state1() + { + ::suspend("state1"); + return state2(); //tail call + } + + function state2() + { + ::suspend("state2"); + return state3(); //tail call + } + + function state3() + { + ::suspend("state3"); + return state1(); //tail call + } + + local statethread = ::newthread(state1) + + ::print(statethread.call()+"\n"); + + for(local i = 0; i < 10000; i++) + ::print(statethread.wakeup()+"\n"); + diff --git a/sp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst b/sp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst new file mode 100644 index 0000000000..5f87bc826b --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/reference/language/weak_references.rst @@ -0,0 +1,61 @@ +.. _weak_references: + + +======================== +Weak References +======================== + +.. index:: + single: Weak References + + +The weak references allows the programmers to create references to objects without +influencing the lifetime of the object itself. +In squirrel Weak references are first-class objects created through the built-in method obj.weakref(). +All types except null implement the weakref() method; however in bools, integers, and floats the method +simply returns the object itself(this because this types are always passed by value). +When a weak references is assigned to a container (table slot,array,class or +instance) is treated differently than other objects; When a container slot that hold a weak +reference is fetched, it always returns the value pointed by the weak reference instead of the weak +reference object. This allow the programmer to ignore the fact that the value handled is weak. +When the object pointed by weak reference is destroyed, the weak reference is automatically set to null.:: + + local t = {} + local a = ["first","second","third"] + //creates a weakref to the array and assigns it to a table slot + t.thearray <- a.weakref(); + +The table slot 'thearray' contains a weak reference to an array. +The following line prints "first", because tables(and all other containers) always return +the object pointed by a weak ref:: + + print(t.thearray[0]); + +the only strong reference to the array is owned by the local variable 'a', so +because the following line assigns a integer to 'a' the array is destroyed.:: + + a = 123; + +When an object pointed by a weak ref is destroyed the weak ref is automatically set to null, +so the following line will print "null".:: + + ::print(typeof(t.thearray)) + +----------------------------------- +Handling weak references explicitly +----------------------------------- + +If a weak reference is assigned to a local variable, then is treated as any other value.:: + + local t = {} + local weakobj = t.weakref(); + +the following line prints "weakref".:: + + ::print(typeof(weakobj)) + +the object pointed by the weakref can be obtained through the built-in method weakref.ref(). + +The following line prints "table".:: + + ::print(typeof(weakobj.ref())) diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/index.rst b/sp/src/vscript/squirrel/doc/source/stdlib/index.rst new file mode 100644 index 0000000000..7137bdb5c5 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/index.rst @@ -0,0 +1,39 @@ +.. _stdlib: + +################################# + Squirrel Standard Library 3.1 +################################# + +Copyright (c) 2003-2016 Alberto Demichelis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +.. toctree:: + :maxdepth: 1 + :numbered: + + introduction.rst + stdiolib.rst + stdbloblib.rst + stdmathlib.rst + stdsystemlib.rst + stdstringlib.rst + stdauxlib.rst + diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/introduction.rst b/sp/src/vscript/squirrel/doc/source/stdlib/introduction.rst new file mode 100644 index 0000000000..171e2425c4 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/introduction.rst @@ -0,0 +1,22 @@ +.. _stdlib_introduction: + +============ +Introduction +============ + +The squirrel standard libraries consist in a set of modules implemented in C++. +While are not essential for the language, they provide a set of useful services that are +commonly used by a wide range of applications(file I/O, regular expressions, etc...), +plus they offer a foundation for developing additional libraries. + +All libraries are implemented through the squirrel API and the ANSI C runtime library. +The modules are organized in the following way: + +* I/O : input and output +* blob : binary buffers manipilation +* math : basic mathematical routines +* system : system access function +* string : string formatting and manipulation +* aux : auxiliary functions + +The libraries can be registered independently,except for the IO library that depends from the bloblib. diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst b/sp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst new file mode 100644 index 0000000000..b800c100c4 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/stdauxlib.rst @@ -0,0 +1,31 @@ +.. _stdlib_stdauxlib: + +=============== +The Aux library +=============== + +The aux library implements default handlers for compiler and runtime errors and a stack dumping. + ++++++++++++ +C API ++++++++++++ + +.. _sqstd_seterrorhandlers: + +.. c:function:: void sqstd_seterrorhandlers(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + + initialize compiler and runtime error handlers, the handlers + use the print function set through(:ref:`sq_setprintfunc `) to output + the error. + +.. _sqstd_printcallstack: + +.. c:function:: void sqstd_printcallstack(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + + prints the call stack and stack contents. the function + uses the print function set through(:ref:`sq_setprintfunc `) to output + the stack dump. diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst b/sp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst new file mode 100644 index 0000000000..83d3426407 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/stdbloblib.rst @@ -0,0 +1,213 @@ +.. _stdlib_stdbloblib: + +================== +The Blob library +================== +The blob library implements binary data manipulations routines. The library is +based on `blob objects` that represent a buffer of arbitrary +binary data. + +--------------- +Squirrel API +--------------- + ++++++++++++++++ +Global symbols ++++++++++++++++ + +.. js:function:: castf2i(f) + + casts a float to a int + +.. js:function:: casti2f(n) + + casts a int to a float + +.. js:function:: swap2(n) + + swap the byte order of a number (like it would be a 16bits integer) + +.. js:function:: swap4(n) + + swap the byte order of an integer + +.. js:function:: swapfloat(n) + + swaps the byteorder of a float + +++++++++++++++++++ +The blob class +++++++++++++++++++ + +The blob object is a buffer of arbitrary binary data. The object behaves like +a file stream, it has a read/write pointer and it automatically grows if data +is written out of his boundary. +A blob can also be accessed byte by byte through the `[]` operator. + +.. js:class:: blob(size) + + :param int size: initial size of the blob + + returns a new instance of a blob class of the specified size in bytes + +.. js:function:: blob.eos() + + returns a non null value if the read/write pointer is at the end of the stream. + +.. js:function:: blob.flush() + + flushes the stream.return a value != null if succeded, otherwise returns null + +.. js:function:: blob.len() + + returns the length of the stream + +.. js:function:: blob.readblob(size) + + :param int size: number of bytes to read + + read n bytes from the stream and returns them as blob + +.. js:function:: blob.readn(type) + + :param int type: type of the number to read + + reads a number from the stream according to the type parameter. + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+----------------------+ +| parameter | return description | return type | ++==============+================================================================================+======================+ +| 'l' | processor dependent, 32bits on 32bits processors, 64bits on 64bits processors | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'i' | 32bits number | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 's' | 16bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'w' | 16bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'c' | 8bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'b' | 8bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'f' | 32bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'd' | 64bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ + +.. js:function:: blob.resize(size) + + :param int size: the new size of the blob in bytes + + resizes the blob to the specified `size` + +.. js:function:: blob.seek(offset [,origin]) + + :param int offset: indicates the number of bytes from `origin`. + :param int origin: origin of the seek + + +--------------+-------------------------------------------+ + | 'b' | beginning of the stream | + +--------------+-------------------------------------------+ + | 'c' | current location | + +--------------+-------------------------------------------+ + | 'e' | end of the stream | + +--------------+-------------------------------------------+ + + Moves the read/write pointer to a specified location. + +.. note:: If origin is omitted the parameter is defaulted as 'b'(beginning of the stream). + +.. js:function:: blob.swap2() + + swaps the byte order of the blob content as it would be an array of `16bits integers` + +.. js:function:: blob.swap4() + + swaps the byte order of the blob content as it would be an array of `32bits integers` + +.. js:function:: blob.tell() + + returns the read/write pointer absolute position + +.. js:function:: blob.writeblob(src) + + :param blob src: the source blob containing the data to be written + + writes a blob in the stream + +.. js:function:: blob.writen(n, type) + + :param number n: the value to be written + :param int type: type of the number to write + + writes a number in the stream formatted according to the `type` parameter + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+ +| parameter | return description | ++==============+================================================================================+ +| 'i' | 32bits number | ++--------------+--------------------------------------------------------------------------------+ +| 's' | 16bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'w' | 16bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'c' | 8bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'b' | 8bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'f' | 32bits float | ++--------------+--------------------------------------------------------------------------------+ +| 'd' | 64bits float | ++--------------+--------------------------------------------------------------------------------+ + + +------ +C API +------ + +.. _sqstd_register_bloblib: + +.. c:function:: SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initializes and registers the blob library in the given VM. + +.. _sqstd_getblob: + +.. c:function:: SQRESULT sqstd_getblob(HSQUIRRELVM v, SQInteger idx, SQUserPointer* ptr) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: and index in the stack + :param SQUserPointer* ptr: A pointer to the userpointer that will point to the blob's payload + :returns: an SQRESULT + + retrieve the pointer of a blob's payload from an arbitrary + position in the stack. + +.. _sqstd_getblobsize: + +.. c:function:: SQInteger sqstd_getblobsize(HSQUIRRELVM v, SQInteger idx) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: and index in the stack + :returns: the size of the blob at `idx` position + + retrieves the size of a blob's payload from an arbitrary + position in the stack. + +.. _sqstd_createblob: + +.. c:function:: SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) + + :param HSQUIRRELVM v: the target VM + :param SQInteger size: the size of the blob payload that has to be created + :returns: a pointer to the newly created blob payload + + creates a blob with the given payload size and pushes it in the stack. diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst b/sp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst new file mode 100644 index 0000000000..c7de168e90 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/stdiolib.rst @@ -0,0 +1,264 @@ +.. _stdlib_stdiolib: + +======================== +The Input/Output library +======================== + +the i/o library implements basic input/output routines. + +-------------- +Squirrel API +-------------- + +++++++++++++++ +Global Symbols +++++++++++++++ + + +.. js:function:: dofile(path, [raiseerror]) + + compiles a squirrel script or loads a precompiled one and executes it. + returns the value returned by the script or null if no value is returned. + if the optional parameter 'raiseerror' is true, the compiler error handler is invoked + in case of a syntax error. If raiseerror is omitted or set to false, the compiler + error handler is not invoked. + When squirrel is compiled in Unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed UTF8 encoding is used as default. + +.. js:function:: loadfile(path, [raiseerror]) + + compiles a squirrel script or loads a precompiled one an returns it as as function. + if the optional parameter 'raiseerror' is true, the compiler error handler is invoked + in case of a syntax error. If raiseerror is omitted or set to false, the compiler + error handler is not invoked. + When squirrel is compiled in Unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed UTF8 encoding is used as default. + +.. js:function:: writeclosuretofile(destpath, closure) + + serializes a closure to a bytecode file (destpath). The serialized file can be loaded + using loadfile() and dofile(). + + +.. js:data:: stderr + + File object bound on the os *standard error* stream + +.. js:data:: stdin + + File object bound on the os *standard input* stream + +.. js:data:: stdout + + File object bound on the os *standard output* stream + + +++++++++++++++ +The file class +++++++++++++++ + + The file object implements a stream on a operating system file. + +.. js:class:: file(path, patten) + + It's constructor imitates the behaviour of the C runtime function fopen for eg. :: + + local myfile = file("test.xxx","wb+"); + + creates a file with read/write access in the current directory. + +.. js:function:: file.close() + + closes the file. + +.. js:function:: file.eos() + + returns a non null value if the read/write pointer is at the end of the stream. + +.. js:function:: file.flush() + + flushes the stream.return a value != null if succeeded, otherwise returns null + +.. js:function:: file.len() + + returns the length of the stream + +.. js:function:: file.readblob(size) + + :param int size: number of bytes to read + + read n bytes from the stream and returns them as blob + +.. js:function:: file.readn(type) + + :param int type: type of the number to read + + reads a number from the stream according to the type parameter. + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+----------------------+ +| parameter | return description | return type | ++==============+================================================================================+======================+ +| 'l' | processor dependent, 32bits on 32bits processors, 64bits on 64bits processors | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'i' | 32bits number | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 's' | 16bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'w' | 16bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'c' | 8bits signed integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'b' | 8bits unsigned integer | integer | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'f' | 32bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ +| 'd' | 64bits float | float | ++--------------+--------------------------------------------------------------------------------+----------------------+ + +.. js:function:: file.resize(size) + + :param int size: the new size of the blob in bytes + + resizes the blob to the specified `size` + +.. js:function:: file.seek(offset [,origin]) + + :param int offset: indicates the number of bytes from `origin`. + :param int origin: origin of the seek + + +--------------+-------------------------------------------+ + | 'b' | beginning of the stream | + +--------------+-------------------------------------------+ + | 'c' | current location | + +--------------+-------------------------------------------+ + | 'e' | end of the stream | + +--------------+-------------------------------------------+ + + Moves the read/write pointer to a specified location. + +.. note:: If origin is omitted the parameter is defaulted as 'b'(beginning of the stream). + +.. js:function:: file.tell() + + returns the read/write pointer absolute position + +.. js:function:: file.writeblob(src) + + :param blob src: the source blob containing the data to be written + + writes a blob in the stream + +.. js:function:: file.writen(n, type) + + :param number n: the value to be written + :param int type: type of the number to write + + writes a number in the stream formatted according to the `type` pamraeter + + `type` can have the following values: + ++--------------+--------------------------------------------------------------------------------+ +| parameter | return description | ++==============+================================================================================+ +| 'i' | 32bits number | ++--------------+--------------------------------------------------------------------------------+ +| 's' | 16bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'w' | 16bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'c' | 8bits signed integer | ++--------------+--------------------------------------------------------------------------------+ +| 'b' | 8bits unsigned integer | ++--------------+--------------------------------------------------------------------------------+ +| 'f' | 32bits float | ++--------------+--------------------------------------------------------------------------------+ +| 'd' | 64bits float | ++--------------+--------------------------------------------------------------------------------+ + + +-------------- +C API +-------------- + +.. _sqstd_register_iolib: + +.. c:function:: SQRESULT sqstd_register_iolib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initialize and register the io library in the given VM. + +++++++++++++++ +File Object +++++++++++++++ + +.. c:function:: SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file, SQBool owns) + + :param HSQUIRRELVM v: the target VM + :param SQFILE file: the stream that will be rapresented by the file object + :param SQBool owns: if different true the stream will be automatically closed when the newly create file object is destroyed. + :returns: an SQRESULT + + creates a file object bound to the SQFILE passed as parameter + and pushes it in the stack + +.. c:function:: SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE* file) + + :param HSQUIRRELVM v: the target VM + :param SQInteger idx: and index in the stack + :param SQFILE* file: A pointer to a SQFILE handle that will store the result + :returns: an SQRESULT + + retrieve the pointer of a stream handle from an arbitrary + position in the stack. + +++++++++++++++++++++++++++++++++ +Script loading and serialization +++++++++++++++++++++++++++++++++ + +.. c:function:: SQRESULT sqstd_loadfile(HSQUIRRELVM v, const SQChar* filename, SQBool printerror) + + :param HSQUIRRELVM v: the target VM + :param SQChar* filename: path of the script that has to be loaded + :param SQBool printerror: if true the compiler error handler will be called if a error occurs + :returns: an SQRESULT + + Compiles a squirrel script or loads a precompiled one an pushes it as closure in the stack. + When squirrel is compiled in Unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed UTF8 encoding is used as default. + +.. c:function:: SQRESULT sqstd_dofile(HSQUIRRELVM v, const SQChar* filename, SQBool retval, SQBool printerror) + + :param HSQUIRRELVM v: the target VM + :param SQChar* filename: path of the script that has to be loaded + :param SQBool retval: if true the function will push the return value of the executed script in the stack. + :param SQBool printerror: if true the compiler error handler will be called if a error occurs + :returns: an SQRESULT + :remarks: the function expects a table on top of the stack that will be used as 'this' for the execution of the script. The 'this' parameter is left untouched in the stack. + + Compiles a squirrel script or loads a precompiled one and executes it. + Optionally pushes the return value of the executed script in the stack. + When squirrel is compiled in unicode mode the function can handle different character encodings, + UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). + If the source stream is not prefixed, UTF8 encoding is used as default. :: + + sq_pushroottable(v); //push the root table(were the globals of the script will are stored) + sqstd_dofile(v, _SC("test.nut"), SQFalse, SQTrue);// also prints syntax errors if any + +.. c:function:: SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v, const SQChar* filename) + + :param HSQUIRRELVM v: the target VM + :param SQChar* filename: destination path of serialized closure + :returns: an SQRESULT + + serializes the closure at the top position in the stack as bytecode in + the file specified by the parameter filename. If a file with the + same name already exists, it will be overwritten. + diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst b/sp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst new file mode 100644 index 0000000000..5f0b8b4932 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/stdmathlib.rst @@ -0,0 +1,111 @@ +.. _stdlib_stdmathlib: + +================ +The Math library +================ + +the math lib provides basic mathematic routines. The library mimics the +C runtime library implementation. + +------------ +Squirrel API +------------ + ++++++++++++++++ +Global Symbols ++++++++++++++++ + +.. js:function:: abs(x) + + returns the absolute value of `x` as an integer + +.. js:function:: acos(x) + + returns the arccosine of `x` + +.. js:function:: asin(x) + + returns the arcsine of `x` + +.. js:function:: atan(x) + + returns the arctangent of `x` + +.. js:function:: atan2(x,y) + + returns the arctangent of `x/y` + +.. js:function:: ceil(x) + + returns a float value representing the smallest integer that is greater than or equal to `x` + +.. js:function:: cos(x) + + returns the cosine of `x` + +.. js:function:: exp(x) + + returns the exponential value of the float parameter `x` + +.. js:function:: fabs(x) + + returns the absolute value of `x` as a float + +.. js:function:: floor(x) + + returns a float value representing the largest integer that is less than or equal to `x` + +.. js:function:: log(x) + + returns the natural logarithm of `x` + +.. js:function:: log10(x) + + returns the logarithm base-10 of `x` + +.. js:function:: pow(x,y) + + returns `x` raised to the power of `y` + +.. js:function:: rand() + + returns a pseudorandom integer in the range 0 to `RAND_MAX` + +.. js:function:: sin(x) + + rreturns the sine of `x` + +.. js:function:: sqrt(x) + + returns the square root of `x` + +.. js:function:: srand(seed) + + sets the starting point for generating a series of pseudorandom integers + +.. js:function:: tan(x) + + returns the tangent of `x` + +.. js:data:: RAND_MAX + + the maximum value that can be returned by the `rand()` function + +.. js:data:: PI + + The numeric constant pi (3.141592) is the ratio of the circumference of a circle to its diameter + +------------ +C API +------------ + +.. _sqstd_register_mathlib: + +.. c:function:: SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initializes and register the math library in the given VM. + diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst b/sp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst new file mode 100644 index 0000000000..19c18d7a93 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/stdstringlib.rst @@ -0,0 +1,319 @@ +.. _stdlib_stdstringlib: + +================== +The String library +================== + +the string lib implements string formatting and regular expression matching routines. + +-------------- +Squirrel API +-------------- + +++++++++++++++ +Global Symbols +++++++++++++++ + +.. js:function:: endswith(str, cmp) + + returns `true` if the end of the string `str` matches a the string `cmp` otherwise returns `false` + +.. js:function:: escape(str) + + Returns a string with backslashes before characters that need to be escaped(`\",\a,\b,\t,\n,\v,\f,\r,\\,\",\',\0,\xnn`). + +.. js:function:: format(formatstr, ...) + + Returns a string formatted according `formatstr` and the optional parameters following it. + The format string follows the same rules as the `printf` family of + standard C functions( the "*" is not supported). :: + + e.g. + sq> print(format("%s %d 0x%02X\n","this is a test :",123,10)); + this is a test : 123 0x0A + +.. js:function:: printf(formatstr, ...) + + Just like calling `print(format(formatstr` as in the example above, but is more convenient AND more efficient. :: + + e.g. + sq> printf("%s %d 0x%02X\n","this is a test :",123,10); + this is a test : 123 0x0A + +.. js:function:: lstrip(str) + + Strips white-space-only characters that might appear at the beginning of the given string + and returns the new stripped string. + +.. js:function:: rstrip(str) + + Strips white-space-only characters that might appear at the end of the given string + and returns the new stripped string. + +.. js:function:: split(str, separators) + + returns an array of strings split at each point where a separator character occurs in `str`. + The separator is not returned as part of any array element. + The parameter `separators` is a string that specifies the characters as to be used for the splitting. + + :: + + eg. + local a = split("1.2-3;4/5",".-/;"); + // the result will be [1,2,3,4,5] + + +.. js:function:: startswith(str, cmp) + + returns `true` if the beginning of the string `str` matches the string `cmp`; otherwise returns `false` + +.. js:function:: strip(str) + + Strips white-space-only characters that might appear at the beginning or end of the given string and returns the new stripped string. + +++++++++++++++++++ +The regexp class +++++++++++++++++++ + +.. js:class:: regexp(pattern) + + The regexp object represents a precompiled regular expression pattern. The object is created + through `regexp(pattern)`. + + ++---------------------+--------------------------------------+ +| `\\` | Quote the next metacharacter | ++---------------------+--------------------------------------+ +| `^` | Match the beginning of the string | ++---------------------+--------------------------------------+ +| `.` | Match any character | ++---------------------+--------------------------------------+ +| `$` | Match the end of the string | ++---------------------+--------------------------------------+ +| `|` | Alternation | ++---------------------+--------------------------------------+ +| `(subexp)` | Grouping (creates a capture) | ++---------------------+--------------------------------------+ +| `(?:subexp)` | No Capture Grouping (no capture) | ++---------------------+--------------------------------------+ +| `[]` | Character class | ++---------------------+--------------------------------------+ + +**GREEDY CLOSURES** + ++---------------------+---------------------------------------------+ +| `*` | Match 0 or more times | ++---------------------+---------------------------------------------+ +| `+` | Match 1 or more times | ++---------------------+---------------------------------------------+ +| `?` | Match 1 or 0 times | ++---------------------+---------------------------------------------+ +| `{n}` | Match exactly n times | ++---------------------+---------------------------------------------+ +| `{n,}` | Match at least n times | ++---------------------+---------------------------------------------+ +| `{n,m}` | Match at least n but not more than m times | ++---------------------+---------------------------------------------+ + +**ESCAPE CHARACTERS** + ++---------------------+--------------------------------------+ +| `\\t` | tab (HT, TAB) | ++---------------------+--------------------------------------+ +| `\\n` | newline (LF, NL) | ++---------------------+--------------------------------------+ +| `\\r` | return (CR) | ++---------------------+--------------------------------------+ +| `\\f` | form feed (FF) | ++---------------------+--------------------------------------+ + +**PREDEFINED CLASSES** + ++---------------------+--------------------------------------+ +| `\\l` | lowercase next char | ++---------------------+--------------------------------------+ +| `\\u` | uppercase next char | ++---------------------+--------------------------------------+ +| `\\a` | letters | ++---------------------+--------------------------------------+ +| `\\A` | non letters | ++---------------------+--------------------------------------+ +| `\\w` | alphanumeric `[_0-9a-zA-Z]` | ++---------------------+--------------------------------------+ +| `\\W` | non alphanumeric `[^_0-9a-zA-Z]` | ++---------------------+--------------------------------------+ +| `\\s` | space | ++---------------------+--------------------------------------+ +| `\\S` | non space | ++---------------------+--------------------------------------+ +| `\\d` | digits | ++---------------------+--------------------------------------+ +| `\\D` | non digits | ++---------------------+--------------------------------------+ +| `\\x` | hexadecimal digits | ++---------------------+--------------------------------------+ +| `\\X` | non hexadecimal digits | ++---------------------+--------------------------------------+ +| `\\c` | control characters | ++---------------------+--------------------------------------+ +| `\\C` | non control characters | ++---------------------+--------------------------------------+ +| `\\p` | punctuation | ++---------------------+--------------------------------------+ +| `\\P` | non punctuation | ++---------------------+--------------------------------------+ +| `\\b` | word boundary | ++---------------------+--------------------------------------+ +| `\\B` | non word boundary | ++---------------------+--------------------------------------+ + + +.. js:function:: regexp.capture(str [, start]) + + returns an array of tables containing two indexes ("begin" and "end") of + the first match of the regular expression in the string `str`. + An array entry is created for each captured sub expressions. If no match occurs returns null. + The search starts from the index `start` + of the string; if `start` is omitted the search starts from the beginning of the string. + + The first element of the returned array(index 0) always contains the complete match. + + :: + + local ex = regexp(@"(\d+) ([a-zA-Z]+)(\p)"); + local string = "stuff 123 Test;"; + local res = ex.capture(string); + foreach(i,val in res) + { + print(format("match number[%02d] %s\n", + i,string.slice(val.begin,val.end))); //prints "Test" + } + + ... + will print + match number[00] 123 Test; + match number[01] 123 + match number[02] Test + match number[03] ; + +.. js:function:: regexp.match(str) + + returns a true if the regular expression matches the string + `str`, otherwise returns false. + +.. js:function:: regexp.search(str [, start]) + + returns a table containing two indexes ("begin" and "end") of the first match of the regular expression in + the string `str`, otherwise if no match occurs returns null. The search starts from the index `start` + of the string; if `start` is omitted the search starts from the beginning of the string. + + :: + + local ex = regexp("[a-zA-Z]+"); + local string = "123 Test;"; + local res = ex.search(string); + print(string.slice(res.begin,res.end)); //prints "Test" + +------------- +C API +------------- + +.. _sqstd_register_stringlib: + +.. c:function:: SQRESULT sqstd_register_stringlib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initialize and register the string library in the given VM. + ++++++++++++++ +Formatting ++++++++++++++ + +.. c:function:: SQRESULT sqstd_format(HSQUIRRELVM v, SQInteger nformatstringidx, SQInteger* outlen, SQChar** output) + + :param HSQUIRRELVM v: the target VM + :param SQInteger nformatstringidx: index in the stack of the format string + :param SQInteger* outlen: a pointer to an integer that will be filled with the length of the newly created string + :param SQChar** output: a pointer to a string pointer that will receive the newly created string + :returns: an SQRESULT + :remarks: the newly created string is allocated in the scratchpad memory. + + + creates a new string formatted according to the object at position `nformatstringidx` and the optional parameters following it. + The format string follows the same rules as the `printf` family of + standard C functions( the "*" is not supported). + +++++++++++++++++++ +Regular Expessions +++++++++++++++++++ + +.. c:function:: SQRex* sqstd_rex_compile(const SQChar *pattern, const SQChar ** error) + + :param SQChar* pattern: a pointer to a zero terminated string containing the pattern that has to be compiled. + :param SQChar** error: a pointer to a string pointer that will be set with an error string in case of failure. + :returns: a pointer to the compiled pattern + + compiles an expression and returns a pointer to the compiled version. + in case of failure returns NULL.The returned object has to be deleted + through the function sqstd_rex_free(). + +.. c:function:: void sqstd_rex_free(SQRex * exp) + + :param SQRex* exp: the expression structure that has to be deleted. + + deletes a expression structure created with sqstd_rex_compile() + +.. c:function:: SQBool sqstd_rex_match(SQRex * exp,const SQChar * text) + + :param SQRex* exp: a compiled expression + :param SQChar* text: the string that has to be tested + :returns: SQTrue if successful otherwise SQFalse + + returns SQTrue if the string specified in the parameter text is an + exact match of the expression, otherwise returns SQFalse. + +.. c:function:: SQBool sqstd_rex_search(SQRex * exp, const SQChar * text, const SQChar ** out_begin, const SQChar ** out_end) + + :param SQRex* exp: a compiled expression + :param SQChar* text: the string that has to be tested + :param SQChar** out_begin: a pointer to a string pointer that will be set with the beginning of the match + :param SQChar** out_end: a pointer to a string pointer that will be set with the end of the match + :returns: SQTrue if successful otherwise SQFalse + + searches the first match of the expression in the string specified in the parameter text. + if the match is found returns SQTrue and the sets out_begin to the beginning of the + match and out_end at the end of the match; otherwise returns SQFalse. + +.. c:function:: SQBool sqstd_rex_searchrange(SQRex * exp, const SQChar * text_begin, const SQChar * text_end, const SQChar ** out_begin, const SQChar ** out_end) + + :param SQRex* exp: a compiled expression + :param SQChar* text_begin: a pointer to the beginnning of the string that has to be tested + :param SQChar* text_end: a pointer to the end of the string that has to be tested + :param SQChar** out_begin: a pointer to a string pointer that will be set with the beginning of the match + :param SQChar** out_end: a pointer to a string pointer that will be set with the end of the match + :returns: SQTrue if successful otherwise SQFalse + + searches the first match of the expression in the string delimited + by the parameter text_begin and text_end. + if the match is found returns SQTrue and sets out_begin to the beginning of the + match and out_end at the end of the match; otherwise returns SQFalse. + +.. c:function:: SQInteger sqstd_rex_getsubexpcount(SQRex * exp) + + :param SQRex* exp: a compiled expression + :returns: the number of sub expressions matched by the expression + + returns the number of sub expressions matched by the expression + +.. c:function:: SQBool sqstd_rex_getsubexp(SQRex * exp, SQInteger n, SQRexMatch *subexp) + + :param SQRex* exp: a compiled expression + :param SQInteger n: the index of the submatch(0 is the complete match) + :param SQRexMatch* a: pointer to structure that will store the result + :returns: the function returns SQTrue if n is a valid index; otherwise SQFalse. + + retrieve the begin and and pointer to the length of the sub expression indexed + by n. The result is passed through the struct SQRexMatch. diff --git a/sp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst b/sp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst new file mode 100644 index 0000000000..5c565fd641 --- /dev/null +++ b/sp/src/vscript/squirrel/doc/source/stdlib/stdsystemlib.rst @@ -0,0 +1,82 @@ +.. _stdlib_stdsystemlib: + +================== +The System library +================== + +The system library exposes operating system facilities like environment variables, +date time manipulation etc.. + +-------------- +Squirrel API +-------------- + +++++++++++++++ +Global Symbols +++++++++++++++ + +.. js:function:: clock() + + returns a float representing the number of seconds elapsed since the start of the process + +.. js:function:: date([time [, format]]) + + returns a table containing a date/time split into the slots: + ++-------------+----------------------------------------+ +| sec | Seconds after minute (0 - 59). | ++-------------+----------------------------------------+ +| min | Minutes after hour (0 - 59). | ++-------------+----------------------------------------+ +| hour | Hours since midnight (0 - 23). | ++-------------+----------------------------------------+ +| day | Day of month (1 - 31). | ++-------------+----------------------------------------+ +| month | Month (0 - 11; January = 0). | ++-------------+----------------------------------------+ +| year | Year (current year). | ++-------------+----------------------------------------+ +| wday | Day of week (0 - 6; Sunday = 0). | ++-------------+----------------------------------------+ +| yday | Day of year (0 - 365; January 1 = 0). | ++-------------+----------------------------------------+ + +if `time` is omitted the current time is used. + +if `format` can be 'l' local time or 'u' UTC time, if omitted is defaulted as 'l'(local time). + +.. js:function:: getenv(varaname) + + Returns a string containing the value of the environment variable `varname` + +.. js:function:: remove(path) + + deletes the file specified by `path` + +.. js:function:: rename(oldname, newname) + + renames the file or directory specified by `oldname` to the name given by `newname` + +.. js:function:: system(cmd) + + xecutes the string `cmd` through the os command interpreter. + +.. js:function:: time() + + returns the number of seconds elapsed since midnight 00:00:00, January 1, 1970. + + the result of this function can be formatted through the function `date()` + +-------------- +C API +-------------- + +.. _sqstd_register_systemlib: + +.. c:function:: SQRESULT sqstd_register_systemlib(HSQUIRRELVM v) + + :param HSQUIRRELVM v: the target VM + :returns: an SQRESULT + :remarks: The function aspects a table on top of the stack where to register the global library functions. + + initialize and register the system library in the given VM. diff --git a/sp/src/vscript/squirrel/etc/minimal.c b/sp/src/vscript/squirrel/etc/minimal.c new file mode 100644 index 0000000000..0695768b86 --- /dev/null +++ b/sp/src/vscript/squirrel/etc/minimal.c @@ -0,0 +1,78 @@ +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma comment (lib ,"squirrel.lib") +#pragma comment (lib ,"sqstdlib.lib") +#endif + +#ifdef SQUNICODE + +#define scvprintf vfwprintf +#else + +#define scvprintf vfprintf +#endif + +void printfunc(HSQUIRRELVM v,const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stdout, s, vl); + va_end(vl); +} + +void errorfunc(HSQUIRRELVM v,const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stderr, s, vl); + va_end(vl); +} + +void call_foo(HSQUIRRELVM v, int n,float f,const SQChar *s) +{ + SQInteger top = sq_gettop(v); //saves the stack size before the call + sq_pushroottable(v); //pushes the global table + sq_pushstring(v,_SC("foo"),-1); + if(SQ_SUCCEEDED(sq_get(v,-2))) { //gets the field 'foo' from the global table + sq_pushroottable(v); //push the 'this' (in this case is the global table) + sq_pushinteger(v,n); + sq_pushfloat(v,f); + sq_pushstring(v,s,-1); + sq_call(v,4,SQFalse,SQTrue); //calls the function + } + sq_settop(v,top); //restores the original stack size +} + +int main(int argc, char* argv[]) +{ + HSQUIRRELVM v; + v = sq_open(1024); // creates a VM with initial stack size 1024 + + //REGISTRATION OF STDLIB + //sq_pushroottable(v); //push the root table where the std function will be registered + //sqstd_register_iolib(v); //registers a library + // ... call here other stdlibs string,math etc... + //sq_pop(v,1); //pops the root table + //END REGISTRATION OF STDLIB + + sqstd_seterrorhandlers(v); //registers the default error handlers + + sq_setprintfunc(v, printfunc,errorfunc); //sets the print function + + sq_pushroottable(v); //push the root table(were the globals of the script will be stored) + if(SQ_SUCCEEDED(sqstd_dofile(v, _SC("test.nut"), SQFalse, SQTrue))) // also prints syntax errors if any + { + call_foo(v,1,2.5,_SC("teststring")); + } + + sq_pop(v,1); //pops the root table + sq_close(v); + + return 0; +} diff --git a/sp/src/vscript/squirrel/etc/test.nut b/sp/src/vscript/squirrel/etc/test.nut new file mode 100644 index 0000000000..125df32cd8 --- /dev/null +++ b/sp/src/vscript/squirrel/etc/test.nut @@ -0,0 +1,4 @@ +function foo(i, f, s) +{ + print("Called foo(), i="+i+", f="+f+", s='"+s+"'\n"); +} diff --git a/sp/src/vscript/squirrel/include/sqconfig.h b/sp/src/vscript/squirrel/include/sqconfig.h new file mode 100644 index 0000000000..58bc9793a3 --- /dev/null +++ b/sp/src/vscript/squirrel/include/sqconfig.h @@ -0,0 +1,146 @@ + +#ifdef _SQ64 + +#ifdef _MSC_VER +typedef __int64 SQInteger; +typedef unsigned __int64 SQUnsignedInteger; +typedef unsigned __int64 SQHash; /*should be the same size of a pointer*/ +#else +typedef long long SQInteger; +typedef unsigned long long SQUnsignedInteger; +typedef unsigned long long SQHash; /*should be the same size of a pointer*/ +#endif +typedef int SQInt32; +typedef unsigned int SQUnsignedInteger32; +#else +typedef int SQInteger; +typedef int SQInt32; /*must be 32 bits(also on 64bits processors)*/ +typedef unsigned int SQUnsignedInteger32; /*must be 32 bits(also on 64bits processors)*/ +typedef unsigned int SQUnsignedInteger; +typedef unsigned int SQHash; /*should be the same size of a pointer*/ +#endif + + +#ifdef SQUSEDOUBLE +typedef double SQFloat; +#else +typedef float SQFloat; +#endif + +#if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) +#ifdef _MSC_VER +typedef __int64 SQRawObjectVal; //must be 64bits +#else +typedef long long SQRawObjectVal; //must be 64bits +#endif +#define SQ_OBJECT_RAWINIT() { _unVal.raw = 0; } +#else +typedef SQUnsignedInteger SQRawObjectVal; //is 32 bits on 32 bits builds and 64 bits otherwise +#define SQ_OBJECT_RAWINIT() +#endif + +#ifndef SQ_ALIGNMENT // SQ_ALIGNMENT shall be less than or equal to SQ_MALLOC alignments, and its value shall be power of 2. +#if defined(SQUSEDOUBLE) || defined(_SQ64) +#define SQ_ALIGNMENT 8 +#else +#define SQ_ALIGNMENT 4 +#endif +#endif + +typedef void* SQUserPointer; +typedef SQUnsignedInteger SQBool; +typedef SQInteger SQRESULT; + +#ifdef SQUNICODE +#include +#include + + +typedef wchar_t SQChar; + + +#define scstrcmp wcscmp +#ifdef _WIN32 +#define scsprintf _snwprintf +#else +#define scsprintf swprintf +#endif +#define scstrlen wcslen +#define scstrtod wcstod +#ifdef _SQ64 +#define scstrtol wcstoll +#else +#define scstrtol wcstol +#endif +#define scstrtoul wcstoul +#define scvsprintf vswprintf +#define scstrstr wcsstr +#define scprintf wprintf + +#ifdef _WIN32 +#define WCHAR_SIZE 2 +#define WCHAR_SHIFT_MUL 1 +#define MAX_CHAR 0xFFFF +#else +#define WCHAR_SIZE 4 +#define WCHAR_SHIFT_MUL 2 +#define MAX_CHAR 0xFFFFFFFF +#endif + +#define _SC(a) L##a + + +#define scisspace iswspace +#define scisdigit iswdigit +#define scisprint iswprint +#define scisxdigit iswxdigit +#define scisalpha iswalpha +#define sciscntrl iswcntrl +#define scisalnum iswalnum + + +#define sq_rsl(l) ((l)<=0) + +#ifdef __GNUC__ +# define SQ_UNUSED_ARG(x) x __attribute__((__unused__)) +#else +# define SQ_UNUSED_ARG(x) x +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*_SQUIRREL_H_*/ diff --git a/sp/src/vscript/squirrel/samples/ackermann.nut b/sp/src/vscript/squirrel/samples/ackermann.nut new file mode 100644 index 0000000000..8b18ec214a --- /dev/null +++ b/sp/src/vscript/squirrel/samples/ackermann.nut @@ -0,0 +1,23 @@ +/* +* +* Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) +* +*/ + +function Ack(M, N) { + if (M == 0) return( N + 1 ); + if (N == 0) return( Ack(M - 1, 1) ); + return( Ack(M - 1, Ack(M, (N - 1))) ); +} + +local n; + +if(vargv.len()!=0) { + n = vargv[0].tointeger(); + if(n < 1) n = 1; +} else { + n = 1; +} +print("n="+n+"\n"); +print("Ack(3,"+ n+ "):"+ Ack(3, n)); + diff --git a/sp/src/vscript/squirrel/samples/array.nut b/sp/src/vscript/squirrel/samples/array.nut new file mode 100644 index 0000000000..0102d62f18 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/array.nut @@ -0,0 +1,29 @@ +/* +* +* Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) +* +*/ +local n, i, k; + +if(vargv.len()!=0) { + n = vargv[0].tointeger(); + if(n < 1) n = 1; +} else { + n = 1; +} + +local x = []; x.resize(n); +local y = []; y.resize(n); + +for (i = 0; i < n; i+=1) { + x[i] = i + 1; + y[i] = 0; +} + +for (k = 0 ; k < n; k+=1) { + for (i = n-1; i >= 0; i-=1) { + y[i] = y[i]+ x[i]; + } +} +print(y[0].tostring()+" "+y[n-1]); + diff --git a/sp/src/vscript/squirrel/samples/class.nut b/sp/src/vscript/squirrel/samples/class.nut new file mode 100644 index 0000000000..2d9246023f --- /dev/null +++ b/sp/src/vscript/squirrel/samples/class.nut @@ -0,0 +1,49 @@ +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +class BaseVector { + constructor(...) + { + if(vargv.len() >= 3) { + x = vargv[0]; + y = vargv[1]; + z = vargv[2]; + } + } + + + x = 0; + y = 0; + z = 0; +} + +class Vector3 extends BaseVector { + function _add(other) + { + if(other instanceof this.getclass()) + return ::Vector3(x+other.x,y+other.y,z+other.z); + else + throw "wrong parameter"; + } + function Print() + { + ::print(x+","+y+","+z+"\n"); + } +} + +local v0 = Vector3(1,2,3) +local v1 = Vector3(11,12,13) +local v2 = v0 + v1; +v2.Print(); + +FakeNamespace <- { + Utils = {} +} + +class FakeNamespace.Utils.SuperClass { + constructor() + { + ::print("FakeNamespace.Utils.SuperClass") + } +} + +local testy = FakeNamespace.Utils.SuperClass(); diff --git a/sp/src/vscript/squirrel/samples/classattributes.nut b/sp/src/vscript/squirrel/samples/classattributes.nut new file mode 100644 index 0000000000..4d2d078dda --- /dev/null +++ b/sp/src/vscript/squirrel/samples/classattributes.nut @@ -0,0 +1,35 @@ +class Foo { + //constructor + constructor(a) + { + testy = ["stuff",1,2,3]; + } + //attributes of PrintTesty + + function PrintTesty() + { + foreach(i,val in testy) + { + ::print("idx = "+i+" = "+val+" \n"); + } + } + //attributes of testy + + testy = null; + +} + +foreach(member,val in Foo) +{ + ::print(member+"\n"); + local attr; + if((attr = Foo.getattributes(member)) != null) { + foreach(i,v in attr) + { + ::print("\t"+i+" = "+(typeof v)+"\n"); + } + } + else { + ::print("\t\n") + } +} diff --git a/sp/src/vscript/squirrel/samples/coroutines.nut b/sp/src/vscript/squirrel/samples/coroutines.nut new file mode 100644 index 0000000000..0cc1992011 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/coroutines.nut @@ -0,0 +1,25 @@ +function coroutine_test(a,b) +{ + ::print(a+" "+b+"\n"); + local ret = ::suspend("suspend 1"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 2"); + ::print("the coroutine says "+ret+"\n"); + ret = ::suspend("suspend 3"); + ::print("the coroutine says "+ret+"\n"); + return "I'm done" +} + +local coro = ::newthread(coroutine_test); + +local susparam = coro.call("test","coroutine"); //starts the coroutine + +local i = 1; +do +{ + ::print("suspend passed ["+susparam+"]\n") + susparam = coro.wakeup("ciao "+i); + ++i; +}while(coro.getstatus()=="suspended") + +::print("return passed ["+susparam+"]\n") diff --git a/sp/src/vscript/squirrel/samples/delegation.nut b/sp/src/vscript/squirrel/samples/delegation.nut new file mode 100644 index 0000000000..9f208dc407 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/delegation.nut @@ -0,0 +1,54 @@ + +PEntity <- { + name="noname" + pos={x=0,y=0,z=0} + type="entity" + //methamethod + _typeof=function() + { + return type; + } +} + +function PEntity::PrintPos() +{ + ::print("x="+pos.x+" y="+pos.y+" z="+pos.z+"\n"); +} + +function PEntity::new(name,pos) +{ + local newentity=clone ::PEntity; + if(name) + newentity.name=name; + if(pos) + newentity.pos=pos; + return newentity; +} + +PPlayer <- { + model="warrior.mdl" + weapon="fist" + health=100 + armor=0 + //overrides the parent type + type="player" +} + +function PPlayer::new(name,pos) +{ + local p = clone ::PPlayer; + local newplayer = ::PEntity.new(name,pos); + newplayer.setdelegate(p); + return newplayer; +} + +local player=PPlayer.new("godzilla",{x=10,y=20,z=30}); + +::print("PLAYER NAME"+player.name+"\n"); +::print("ENTITY TYPE"+typeof player+"\n"); + +player.PrintPos(); + +player.pos.x=123; + +player.PrintPos(); diff --git a/sp/src/vscript/squirrel/samples/fibonacci.nut b/sp/src/vscript/squirrel/samples/fibonacci.nut new file mode 100644 index 0000000000..c722d1a1ab --- /dev/null +++ b/sp/src/vscript/squirrel/samples/fibonacci.nut @@ -0,0 +1,15 @@ +/* +* +* Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) +* +*/ + +function fib(n) +{ + if (n < 2) return 1 + return fib(n-2) + fib(n-1) +} + +local n = vargv.len()!=0?vargv[0].tointeger():1 + +print(fib(n)+"\n") diff --git a/sp/src/vscript/squirrel/samples/flow.nut b/sp/src/vscript/squirrel/samples/flow.nut new file mode 100644 index 0000000000..635bc89f91 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/flow.nut @@ -0,0 +1,33 @@ +function min(x,y) + return xy?x:y; + +if(min(100,200)>max(50,20)) + print("I'm useless statement just to show up the if/else\n"); +else + print("squirrel!!\n"); + +print("\n") + +function typy(obj) +{ + switch(typeof obj) + { + case "integer": + case "float": + return "is a number"; + case "table": + case "array": + return "is a container"; + default: + return "is other stuff" + } +} + +local a=1,b={},c=function(a,b){return a+b;} + +print("a "+typy(a)+"\n"); +print("b "+typy(b)+"\n"); +print("c "+typy(c)+"\n"); diff --git a/sp/src/vscript/squirrel/samples/generators.nut b/sp/src/vscript/squirrel/samples/generators.nut new file mode 100644 index 0000000000..eb5eef6b05 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/generators.nut @@ -0,0 +1,42 @@ +/* +*Random number function from The Great Computer Language shootout +*converted to a generator func +*/ + +function gen_random(max) { + local last=42 + local IM = 139968; + local IA = 3877; + local IC = 29573; + for(;;){ //loops forever + yield (max * (last = (last * IA + IC) % IM) / IM); + } +} + +local randtor=gen_random(100); + +print("RAND NUMBERS \n") + +for(local i=0;i<10;i+=1) + print(">"+resume randtor+"\n"); + +print("FIBONACCI \n") +function fiboz(n) +{ + local prev=0; + local curr=1; + yield 1; + + for(local i=0;i"+val+"\n"); +} diff --git a/sp/src/vscript/squirrel/samples/hello.nut b/sp/src/vscript/squirrel/samples/hello.nut new file mode 100644 index 0000000000..f301245e24 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/hello.nut @@ -0,0 +1 @@ +print("Hello World!") diff --git a/sp/src/vscript/squirrel/samples/list.nut b/sp/src/vscript/squirrel/samples/list.nut new file mode 100644 index 0000000000..e7cc2218a9 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/list.nut @@ -0,0 +1,40 @@ +/*translation of the list test from The Great Computer Language Shootout +*/ + +function compare_arr(a1,a2) +{ + foreach(i,val in a1) + if(val!=a2[i])return null; + return 1; +} + +function test() +{ + local size=10000 + local l1=[]; l1.resize(size); + for(local i=0;i0) + l3.append(l2.pop()); + while(l3.len()>0) + l2.append(l3.pop()); + l1.reverse(); + + if(compare_arr(l1,l2)) + return l1.len(); + return null; +} + +local n = vargv.len()!=0?vargv[0].tointeger():1 +for(local i=0;i\n"); +else + print("\n"); diff --git a/sp/src/vscript/squirrel/samples/methcall.nut b/sp/src/vscript/squirrel/samples/methcall.nut new file mode 100644 index 0000000000..73bb2e841e --- /dev/null +++ b/sp/src/vscript/squirrel/samples/methcall.nut @@ -0,0 +1,68 @@ +/*translation of the methcall test from The Great Computer Language Shootout +*/ + +class Toggle { + bool=null +} + +function Toggle::constructor(startstate) { + bool = startstate +} + +function Toggle::value() { + return bool; +} + +function Toggle::activate() { + bool = !bool; + return this; +} + +class NthToggle extends Toggle { + count_max=null + count=0 +} + +function NthToggle::constructor(start_state,max_counter) +{ + base.constructor(start_state); + count_max = max_counter +} + +function NthToggle::activate () +{ + ++count; + if (count >= count_max ) { + base.activate(); + count = 0; + } + return this; +} + + +function main() { + local n = vargv.len()!=0?vargv[0].tointeger():1 + + + + local val = 1; + local toggle = Toggle(val); + local i = n; + while(i--) { + val = toggle.activate().value(); + + } + print(toggle.value() ? "true\n" : "false\n"); + + val = 1; + local ntoggle = NthToggle(val, 3); + i = n; + while(i--) { + val = ntoggle.activate().value(); + } + print(ntoggle.value() ? "true\n" : "false\n"); + +} +local start=clock(); +main(); +print("TIME="+(clock()-start)+"\n"); diff --git a/sp/src/vscript/squirrel/samples/regex.nut b/sp/src/vscript/squirrel/samples/regex.nut new file mode 100644 index 0000000000..fcd8e59e59 --- /dev/null +++ b/sp/src/vscript/squirrel/samples/regex.nut @@ -0,0 +1,10 @@ +local ex = regexp("[a-zA-Z]+"); +local string = "123 Test; strlen(str);"; +local res = ex.search(string); +print(string.slice(res.begin,res.end)); //prints "Test" +print("\n"); +ex = regexp(@"\m()"); +string = "123 Test; doSomething(str, getTemp(), (a+(b/c)));"; +res = ex.search(string); +print(string.slice(res.begin,res.end)); //prints "(...)" +print("\n"); diff --git a/sp/src/vscript/squirrel/samples/tailstate.nut b/sp/src/vscript/squirrel/samples/tailstate.nut new file mode 100644 index 0000000000..7ea06c695f --- /dev/null +++ b/sp/src/vscript/squirrel/samples/tailstate.nut @@ -0,0 +1,24 @@ +function state1() +{ + ::suspend("state1"); + return state2(); +} + +function state2() +{ + ::suspend("state2"); + return state3(); +} + +function state3() +{ + ::suspend("state3"); + return state1(); +} + +local statethread = ::newthread(state1) + +::print(statethread.call()+"\n"); + +for(local i = 0; i < 10000; i++) + ::print(statethread.wakeup()+"\n"); diff --git a/sp/src/vscript/squirrel/sq/CMakeLists.txt b/sp/src/vscript/squirrel/sq/CMakeLists.txt new file mode 100644 index 0000000000..bdea07d0b8 --- /dev/null +++ b/sp/src/vscript/squirrel/sq/CMakeLists.txt @@ -0,0 +1,38 @@ +set(CMAKE_C_STANDARD 99) +if(NOT DISABLE_DYNAMIC) + add_executable(sq sq.c) + add_executable(squirrel::interpreter ALIAS sq) + set_target_properties(sq PROPERTIES LINKER_LANGUAGE C EXPORT_NAME interpreter) + target_link_libraries(sq squirrel sqstdlib) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sq EXPORT squirrel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) + endif() + target_include_directories(sq PUBLIC + "$" + "$" + ) +endif() + +if(NOT DISABLE_STATIC) + add_executable(sq_static sq.c) + add_executable(squirrel::interpreter_static ALIAS sq) + set_target_properties(sq_static PROPERTIES LINKER_LANGUAGE C EXPORT_NAME interpreter_static) + target_link_libraries(sq_static squirrel_static sqstdlib_static) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sq_static EXPORT squirrel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) + endif() + target_include_directories(sq_static PUBLIC + "$" + "$" + ) +endif() + +if(LONG_OUTPUT_NAMES) + if(NOT DISABLE_DYNAMIC) + set_target_properties(sq PROPERTIES OUTPUT_NAME squirrel3) + endif() + + if(NOT DISABLE_STATIC) + set_target_properties(sq_static PROPERTIES OUTPUT_NAME squirrel3_static) + endif() +endif() diff --git a/sp/src/vscript/squirrel/sq/Makefile b/sp/src/vscript/squirrel/sq/Makefile new file mode 100644 index 0000000000..948fd1ea10 --- /dev/null +++ b/sp/src/vscript/squirrel/sq/Makefile @@ -0,0 +1,21 @@ +SQUIRREL= .. + + +OUT= $(SQUIRREL)/bin/sq +INCZ= -I$(SQUIRREL)/include -I. -I$(SQUIRREL)/sqlibs +LIBZ= -L$(SQUIRREL)/lib +LIB= -lsquirrel -lsqstdlib + +OBJS= sq.o + +SRCS= sq.c + + +sq32: + g++ -O2 -fno-exceptions -fno-rtti -o $(OUT) $(SRCS) $(INCZ) $(LIBZ) $(LIB) + +sqprof: + g++ -O2 -pg -fno-exceptions -fno-rtti -pie -gstabs -g3 -o $(OUT) $(SRCS) $(INCZ) $(LIBZ) $(LIB) + +sq64: + g++ -O2 -m64 -fno-exceptions -fno-rtti -D_SQ64 -o $(OUT) $(SRCS) $(INCZ) $(LIBZ) $(LIB) diff --git a/sp/src/vscript/squirrel/sq/sq.c b/sp/src/vscript/squirrel/sq/sq.c new file mode 100644 index 0000000000..ee5eabbbc6 --- /dev/null +++ b/sp/src/vscript/squirrel/sq/sq.c @@ -0,0 +1,349 @@ +/* see copyright notice in squirrel.h */ + +#include +#include +#include +#include + +#if defined(_MSC_VER) && defined(_DEBUG) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef SQUNICODE +#define scfprintf fwprintf +#define scvprintf vfwprintf +#else +#define scfprintf fprintf +#define scvprintf vfprintf +#endif + + +void PrintVersionInfos(); + +#if defined(_MSC_VER) && defined(_DEBUG) +int MemAllocHook( int allocType, void *userData, size_t size, int blockType, + long requestNumber, const unsigned char *filename, int lineNumber) +{ + //if(requestNumber==769)_asm int 3; + return 1; +} +#endif + + +SQInteger quit(HSQUIRRELVM v) +{ + int *done; + sq_getuserpointer(v,-1,(SQUserPointer*)&done); + *done=1; + return 0; +} + +void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stdout, s, vl); + va_end(vl); +} + +void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stderr, s, vl); + va_end(vl); +} + +void PrintVersionInfos() +{ + scfprintf(stdout,_SC("%s %s (%d bits)\n"),SQUIRREL_VERSION,SQUIRREL_COPYRIGHT,((int)(sizeof(SQInteger)*8))); +} + +void PrintUsage() +{ + scfprintf(stderr,_SC("usage: sq .\n") + _SC("Available options are:\n") + _SC(" -c compiles the file to bytecode(default output 'out.cnut')\n") + _SC(" -o specifies output file for the -c option\n") + _SC(" -c compiles only\n") + _SC(" -d generates debug infos\n") + _SC(" -v displays version infos\n") + _SC(" -h prints help\n")); +} + +#define _INTERACTIVE 0 +#define _DONE 2 +#define _ERROR 3 +//<> this func is a mess +int getargs(HSQUIRRELVM v,int argc, char* argv[],SQInteger *retval) +{ + int i; + int compiles_only = 0; +#ifdef SQUNICODE + static SQChar temp[500]; +#endif + char * output = NULL; + *retval = 0; + if(argc>1) + { + int arg=1,exitloop=0; + + while(arg < argc && !exitloop) + { + + if(argv[arg][0]=='-') + { + switch(argv[arg][1]) + { + case 'd': //DEBUG(debug infos) + sq_enabledebuginfo(v,1); + break; + case 'c': + compiles_only = 1; + break; + case 'o': + if(arg < argc) { + arg++; + output = argv[arg]; + } + break; + case 'v': + PrintVersionInfos(); + return _DONE; + + case 'h': + PrintVersionInfos(); + PrintUsage(); + return _DONE; + default: + PrintVersionInfos(); + scprintf(_SC("unknown prameter '-%c'\n"),argv[arg][1]); + PrintUsage(); + *retval = -1; + return _ERROR; + } + }else break; + arg++; + } + + // src file + + if(arg")); + for(;;) { + int c; + if(done)return; + c = getchar(); + if (c == _SC('\n')) { + if (i>0 && buffer[i-1] == _SC('\\')) + { + buffer[i-1] = _SC('\n'); + } + else if(blocks==0)break; + buffer[i++] = _SC('\n'); + } + else if (c==_SC('}')) {blocks--; buffer[i++] = (SQChar)c;} + else if(c==_SC('{') && !string){ + blocks++; + buffer[i++] = (SQChar)c; + } + else if(c==_SC('"') || c==_SC('\'')){ + string=!string; + buffer[i++] = (SQChar)c; + } + else if (i >= MAXINPUT-1) { + scfprintf(stderr, _SC("sq : input line too long\n")); + break; + } + else{ + buffer[i++] = (SQChar)c; + } + } + buffer[i] = _SC('\0'); + + if(buffer[0]==_SC('=')){ + scsprintf(sq_getscratchpad(v,MAXINPUT),(size_t)MAXINPUT,_SC("return (%s)"),&buffer[1]); + memcpy(buffer,sq_getscratchpad(v,-1),(scstrlen(sq_getscratchpad(v,-1))+1)*sizeof(SQChar)); + retval=1; + } + i=scstrlen(buffer); + if(i>0){ + SQInteger oldtop=sq_gettop(v); + if(SQ_SUCCEEDED(sq_compilebuffer(v,buffer,i,_SC("interactive console"),SQTrue))){ + sq_pushroottable(v); + if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue)) && retval){ + scprintf(_SC("\n")); + sq_pushroottable(v); + sq_pushstring(v,_SC("print"),-1); + sq_get(v,-2); + sq_pushroottable(v); + sq_push(v,-4); + sq_call(v,2,SQFalse,SQTrue); + retval=0; + scprintf(_SC("\n")); + } + } + + sq_settop(v,oldtop); + } + } +} + +int main(int argc, char* argv[]) +{ + HSQUIRRELVM v; + SQInteger retval = 0; +#if defined(_MSC_VER) && defined(_DEBUG) + _CrtSetAllocHook(MemAllocHook); +#endif + + v=sq_open(1024); + sq_setprintfunc(v,printfunc,errorfunc); + + sq_pushroottable(v); + + sqstd_register_bloblib(v); + sqstd_register_iolib(v); + sqstd_register_systemlib(v); + sqstd_register_mathlib(v); + sqstd_register_stringlib(v); + + //aux library + //sets error handlers + sqstd_seterrorhandlers(v); + + //gets arguments + switch(getargs(v,argc,argv,&retval)) + { + case _INTERACTIVE: + Interactive(v); + break; + case _DONE: + case _ERROR: + default: + break; + } + + sq_close(v); + +#if defined(_MSC_VER) && defined(_DEBUG) + _getch(); + _CrtMemDumpAllObjectsSince( NULL ); +#endif + return retval; +} + diff --git a/sp/src/vscript/squirrel/sq/sq.dsp b/sp/src/vscript/squirrel/sq/sq.dsp new file mode 100644 index 0000000000..a632d12b2e --- /dev/null +++ b/sp/src/vscript/squirrel/sq/sq.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="sq" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=sq - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sq.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sq.mak" CFG="sq - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sq - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "sq - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sq - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sqstdlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x410 /d "NDEBUG" +# ADD RSC /l 0x410 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 squirrel.lib sqstdlib.lib /nologo /subsystem:console /machine:I386 /out:"../bin/sq.exe" /libpath:"../lib" + +!ELSEIF "$(CFG)" == "sq - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\sqstdlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x410 /d "_DEBUG" +# ADD RSC /l 0x410 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 squirrel.lib sqstdlib.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/sq.exe" /pdbtype:sept /libpath:"../lib" + +!ENDIF + +# Begin Target + +# Name "sq - Win32 Release" +# Name "sq - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sq.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/sp/src/vscript/squirrel/sqstdlib/CMakeLists.txt b/sp/src/vscript/squirrel/sqstdlib/CMakeLists.txt new file mode 100644 index 0000000000..34fba82f6f --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/CMakeLists.txt @@ -0,0 +1,52 @@ +set(SQSTDLIB_SRC sqstdaux.cpp + sqstdblob.cpp + sqstdio.cpp + sqstdmath.cpp + sqstdrex.cpp + sqstdstream.cpp + sqstdstring.cpp + sqstdsystem.cpp) + +if(NOT DISABLE_DYNAMIC) + add_library(sqstdlib SHARED ${SQSTDLIB_SRC}) + add_library(squirrel::sqstdlib ALIAS sqstdlib) + set_property(TARGET sqstdlib PROPERTY EXPORT_NAME sqstdlib) + target_link_libraries(sqstdlib squirrel) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sqstdlib EXPORT squirrel + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Libraries + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries NAMELINK_SKIP + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries + ) + install(TARGETS sqstdlib EXPORT squirrel + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development NAMELINK_ONLY + ) + endif() + target_include_directories(sqstdlib PUBLIC + "$" + "$" + ) +endif() + +if(NOT DISABLE_STATIC) + add_library(sqstdlib_static STATIC ${SQSTDLIB_SRC}) + add_library(squirrel::sqstdlib_static ALIAS sqstdlib_static) + set_property(TARGET sqstdlib_static PROPERTY EXPORT_NAME sqstdlib_static) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS sqstdlib_static EXPORT squirrel ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development) + endif() + target_include_directories(sqstdlib_static PUBLIC + "$" + "$" + ) +endif() + +if(LONG_OUTPUT_NAMES) + if(NOT DISABLE_DYNAMIC) + set_target_properties(sqstdlib PROPERTIES OUTPUT_NAME sqstdlib3) + endif() + + if(NOT DISABLE_STATIC) + set_target_properties(sqstdlib_static PROPERTIES OUTPUT_NAME sqstdlib3_static) + endif() +endif() diff --git a/sp/src/vscript/squirrel/sqstdlib/Makefile b/sp/src/vscript/squirrel/sqstdlib/Makefile new file mode 100644 index 0000000000..6ed1c12bb2 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/Makefile @@ -0,0 +1,44 @@ +SQUIRREL= .. + + +CC?= gcc +OUT?= $(SQUIRREL)/lib/libsqstdlib.a +INCZ?= -I$(SQUIRREL)/include -I. -Iinclude +DEFS= $(CC_EXTRA_FLAGS) +LIB= + +OBJS= \ + sqstdblob.o \ + sqstdio.o \ + sqstdstream.o \ + sqstdmath.o \ + sqstdsystem.o \ + sqstdstring.o \ + sqstdaux.o \ + sqstdrex.o + +SRCS= \ + sqstdblob.cpp \ + sqstdio.cpp \ + sqstdstream.cpp \ + sqstdmath.cpp \ + sqstdsystem.cpp \ + sqstdstring.cpp \ + sqstdaux.cpp \ + sqstdrex.cpp + + +sq32: + $(CC) -O2 -fno-exceptions -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sqprof: + $(CC) -O2 -pg -fno-exceptions -fno-rtti -pie -gstabs -g3 -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sq64: + $(CC) -O2 -m64 -fno-exceptions -D_SQ64 -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp new file mode 100644 index 0000000000..75c165339f --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdaux.cpp @@ -0,0 +1,151 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include + +void sqstd_printcallstack(HSQUIRRELVM v) +{ + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { + SQStackInfos si; + SQInteger i; + SQFloat f; + const SQChar *s; + SQInteger level=1; //1 is to skip this function that is level 0 + const SQChar *name=0; + SQInteger seq=0; + pf(v,_SC("\nCALLSTACK\n")); + while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) + { + const SQChar *fn=_SC("unknown"); + const SQChar *src=_SC("unknown"); + if(si.funcname)fn=si.funcname; + if(si.source)src=si.source; + pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line); + level++; + } + level=0; + pf(v,_SC("\nLOCALS\n")); + + for(level=0;level<10;level++){ + seq=0; + while((name = sq_getlocal(v,level,seq))) + { + seq++; + switch(sq_gettype(v,-1)) + { + case OT_NULL: + pf(v,_SC("[%s] NULL\n"),name); + break; + case OT_INTEGER: + sq_getinteger(v,-1,&i); + pf(v,_SC("[%s] %d\n"),name,i); + break; + case OT_FLOAT: + sq_getfloat(v,-1,&f); + pf(v,_SC("[%s] %.14g\n"),name,f); + break; + case OT_USERPOINTER: + pf(v,_SC("[%s] USERPOINTER\n"),name); + break; + case OT_STRING: + sq_getstring(v,-1,&s); + pf(v,_SC("[%s] \"%s\"\n"),name,s); + break; + case OT_TABLE: + pf(v,_SC("[%s] TABLE\n"),name); + break; + case OT_ARRAY: + pf(v,_SC("[%s] ARRAY\n"),name); + break; + case OT_CLOSURE: + pf(v,_SC("[%s] CLOSURE\n"),name); + break; + case OT_NATIVECLOSURE: + pf(v,_SC("[%s] NATIVECLOSURE\n"),name); + break; + case OT_GENERATOR: + pf(v,_SC("[%s] GENERATOR\n"),name); + break; + case OT_USERDATA: + pf(v,_SC("[%s] USERDATA\n"),name); + break; + case OT_THREAD: + pf(v,_SC("[%s] THREAD\n"),name); + break; + case OT_CLASS: + pf(v,_SC("[%s] CLASS\n"),name); + break; + case OT_INSTANCE: + pf(v,_SC("[%s] INSTANCE\n"),name); + break; + case OT_WEAKREF: + pf(v,_SC("[%s] WEAKREF\n"),name); + break; + case OT_BOOL:{ + SQBool bval; + sq_getbool(v,-1,&bval); + pf(v,_SC("[%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); + } + break; + default: assert(0); break; + } + sq_pop(v,1); + } + } + } +} + +static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v) +{ + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { + const SQChar *sErr = 0; + if(sq_gettop(v)>=1) { + if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { + pf(v,_SC("\nAN ERROR HAS OCCURRED [%s]\n"),sErr); + } + else{ + pf(v,_SC("\nAN ERROR HAS OCCURRED [unknown]\n")); + } + sqstd_printcallstack(v); + } + } + return 0; +} + +void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column) +{ + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { + pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr); + } +} + +void sqstd_seterrorhandlers(HSQUIRRELVM v) +{ + sq_setcompilererrorhandler(v,_sqstd_compiler_error); + sq_newclosure(v,_sqstd_aux_printerror,0); + sq_seterrorhandler(v); +} + +SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const SQChar *err,...) +{ + SQInteger n=256; + va_list args; +begin: + va_start(args,err); + SQChar *b=sq_getscratchpad(v,n); + SQInteger r=scvsprintf(b,n,err,args); + va_end(args); + if (r>=n) { + n=r+1;//required+null + goto begin; + } else if (r<0) { + return sq_throwerror(v,_SC("@failed to generate formatted error message")); + } else { + return sq_throwerror(v,b); + } +} diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp new file mode 100644 index 0000000000..261b938bf5 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdblob.cpp @@ -0,0 +1,283 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include +#include "sqstdstream.h" +#include "sqstdblobimpl.h" + +#define SQSTD_BLOB_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000002)) + +//Blob + + +#define SETUP_BLOB(v) \ + SQBlob *self = NULL; \ + { if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) \ + return sq_throwerror(v,_SC("invalid type tag")); } \ + if(!self || !self->IsValid()) \ + return sq_throwerror(v,_SC("the blob is invalid")); + + +static SQInteger _blob_resize(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger size; + sq_getinteger(v,2,&size); + if(!self->Resize(size)) + return sq_throwerror(v,_SC("resize failed")); + return 0; +} + +static void __swap_dword(unsigned int *n) +{ + *n=(unsigned int)(((*n&0xFF000000)>>24) | + ((*n&0x00FF0000)>>8) | + ((*n&0x0000FF00)<<8) | + ((*n&0x000000FF)<<24)); +} + +static void __swap_word(unsigned short *n) +{ + *n=(unsigned short)((*n>>8)&0x00FF)| ((*n<<8)&0xFF00); +} + +static SQInteger _blob_swap4(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger num=(self->Len()-(self->Len()%4))>>2; + unsigned int *t=(unsigned int *)self->GetBuf(); + for(SQInteger i = 0; i < num; i++) { + __swap_dword(&t[i]); + } + return 0; +} + +static SQInteger _blob_swap2(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger num=(self->Len()-(self->Len()%2))>>1; + unsigned short *t = (unsigned short *)self->GetBuf(); + for(SQInteger i = 0; i < num; i++) { + __swap_word(&t[i]); + } + return 0; +} + +static SQInteger _blob__set(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger idx,val; + sq_getinteger(v,2,&idx); + sq_getinteger(v,3,&val); + if(idx < 0 || idx >= self->Len()) + return sq_throwerror(v,_SC("index out of range")); + ((unsigned char *)self->GetBuf())[idx] = (unsigned char) val; + sq_push(v,3); + return 1; +} + +static SQInteger _blob__get(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + SQInteger idx; + + if ((sq_gettype(v, 2) & SQOBJECT_NUMERIC) == 0) + { + sq_pushnull(v); + return sq_throwobject(v); + } + sq_getinteger(v,2,&idx); + if(idx < 0 || idx >= self->Len()) + return sq_throwerror(v,_SC("index out of range")); + sq_pushinteger(v,((unsigned char *)self->GetBuf())[idx]); + return 1; +} + +static SQInteger _blob__nexti(HSQUIRRELVM v) +{ + SETUP_BLOB(v); + if(sq_gettype(v,2) == OT_NULL) { + sq_pushinteger(v, 0); + return 1; + } + SQInteger idx; + if(SQ_SUCCEEDED(sq_getinteger(v, 2, &idx))) { + if(idx+1 < self->Len()) { + sq_pushinteger(v, idx+1); + return 1; + } + sq_pushnull(v); + return 1; + } + return sq_throwerror(v,_SC("internal error (_nexti) wrong argument type")); +} + +static SQInteger _blob__typeof(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("blob"),-1); + return 1; +} + +static SQInteger _blob_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) +{ + SQBlob *self = (SQBlob*)p; + self->~SQBlob(); + sq_free(self,sizeof(SQBlob)); + return 1; +} + +static SQInteger _blob_constructor(HSQUIRRELVM v) +{ + SQInteger nparam = sq_gettop(v); + SQInteger size = 0; + if(nparam == 2) { + sq_getinteger(v, 2, &size); + } + if(size < 0) return sq_throwerror(v, _SC("cannot create blob with negative size")); + //SQBlob *b = new SQBlob(size); + + SQBlob *b = new (sq_malloc(sizeof(SQBlob)))SQBlob(size); + if(SQ_FAILED(sq_setinstanceup(v,1,b))) { + b->~SQBlob(); + sq_free(b,sizeof(SQBlob)); + return sq_throwerror(v, _SC("cannot create blob")); + } + sq_setreleasehook(v,1,_blob_releasehook); + return 0; +} + +static SQInteger _blob__cloned(HSQUIRRELVM v) +{ + SQBlob *other = NULL; + { + if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&other,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) + return SQ_ERROR; + } + //SQBlob *thisone = new SQBlob(other->Len()); + SQBlob *thisone = new (sq_malloc(sizeof(SQBlob)))SQBlob(other->Len()); + memcpy(thisone->GetBuf(),other->GetBuf(),thisone->Len()); + if(SQ_FAILED(sq_setinstanceup(v,1,thisone))) { + thisone->~SQBlob(); + sq_free(thisone,sizeof(SQBlob)); + return sq_throwerror(v, _SC("cannot clone blob")); + } + sq_setreleasehook(v,1,_blob_releasehook); + return 0; +} + +#define _DECL_BLOB_FUNC(name,nparams,typecheck) {_SC(#name),_blob_##name,nparams,typecheck} +static const SQRegFunction _blob_methods[] = { + _DECL_BLOB_FUNC(constructor,-1,_SC("xn")), + _DECL_BLOB_FUNC(resize,2,_SC("xn")), + _DECL_BLOB_FUNC(swap2,1,_SC("x")), + _DECL_BLOB_FUNC(swap4,1,_SC("x")), + _DECL_BLOB_FUNC(_set,3,_SC("xnn")), + _DECL_BLOB_FUNC(_get,2,_SC("x.")), + _DECL_BLOB_FUNC(_typeof,1,_SC("x")), + _DECL_BLOB_FUNC(_nexti,2,_SC("x")), + _DECL_BLOB_FUNC(_cloned,2,_SC("xx")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + + + +//GLOBAL FUNCTIONS + +static SQInteger _g_blob_casti2f(HSQUIRRELVM v) +{ + SQInteger i; + sq_getinteger(v,2,&i); + sq_pushfloat(v,*((const SQFloat *)&i)); + return 1; +} + +static SQInteger _g_blob_castf2i(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat(v,2,&f); + sq_pushinteger(v,*((const SQInteger *)&f)); + return 1; +} + +static SQInteger _g_blob_swap2(HSQUIRRELVM v) +{ + SQInteger i; + sq_getinteger(v,2,&i); + short s=(short)i; + sq_pushinteger(v,(s<<8)|((s>>8)&0x00FF)); + return 1; +} + +static SQInteger _g_blob_swap4(HSQUIRRELVM v) +{ + SQInteger i; + sq_getinteger(v,2,&i); + unsigned int t4 = (unsigned int)i; + __swap_dword(&t4); + sq_pushinteger(v,(SQInteger)t4); + return 1; +} + +static SQInteger _g_blob_swapfloat(HSQUIRRELVM v) +{ + SQFloat f; + sq_getfloat(v,2,&f); + __swap_dword((unsigned int *)&f); + sq_pushfloat(v,f); + return 1; +} + +#define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck} +static const SQRegFunction bloblib_funcs[]={ + _DECL_GLOBALBLOB_FUNC(casti2f,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(castf2i,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + +SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr) +{ + SQBlob *blob; + if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) + return -1; + *ptr = blob->GetBuf(); + return SQ_OK; +} + +SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx) +{ + SQBlob *blob; + if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) + return -1; + return blob->Len(); +} + +SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) +{ + SQInteger top = sq_gettop(v); + sq_pushregistrytable(v); + sq_pushstring(v,_SC("std_blob"),-1); + if(SQ_SUCCEEDED(sq_get(v,-2))) { + sq_remove(v,-2); //removes the registry + sq_push(v,1); // push the this + sq_pushinteger(v,size); //size + SQBlob *blob = NULL; + if(SQ_SUCCEEDED(sq_call(v,2,SQTrue,SQFalse)) + && SQ_SUCCEEDED(sq_getinstanceup(v,-1,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) { + sq_remove(v,-2); + return blob->GetBuf(); + } + } + sq_settop(v,top); + return NULL; +} + +SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) +{ + return declare_stream(v,_SC("blob"),(SQUserPointer)SQSTD_BLOB_TYPE_TAG,_SC("std_blob"),_blob_methods,bloblib_funcs); +} + diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h b/sp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h new file mode 100644 index 0000000000..bfdaddc29e --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdblobimpl.h @@ -0,0 +1,108 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTD_BLOBIMPL_H_ +#define _SQSTD_BLOBIMPL_H_ + +struct SQBlob : public SQStream +{ + SQBlob(SQInteger size) { + _size = size; + _allocated = size; + _buf = (unsigned char *)sq_malloc(size); + memset(_buf, 0, _size); + _ptr = 0; + _owns = true; + } + virtual ~SQBlob() { + sq_free(_buf, _allocated); + } + SQInteger Write(void *buffer, SQInteger size) { + if(!CanAdvance(size)) { + GrowBufOf(_ptr + size - _size); + } + memcpy(&_buf[_ptr], buffer, size); + _ptr += size; + return size; + } + SQInteger Read(void *buffer,SQInteger size) { + SQInteger n = size; + if(!CanAdvance(size)) { + if((_size - _ptr) > 0) + n = _size - _ptr; + else return 0; + } + memcpy(buffer, &_buf[_ptr], n); + _ptr += n; + return n; + } + bool Resize(SQInteger n) { + if(!_owns) return false; + if(n != _allocated) { + unsigned char *newbuf = (unsigned char *)sq_malloc(n); + memset(newbuf,0,n); + if(_size > n) + memcpy(newbuf,_buf,n); + else + memcpy(newbuf,_buf,_size); + sq_free(_buf,_allocated); + _buf=newbuf; + _allocated = n; + if(_size > _allocated) + _size = _allocated; + if(_ptr > _allocated) + _ptr = _allocated; + } + return true; + } + bool GrowBufOf(SQInteger n) + { + bool ret = true; + if(_size + n > _allocated) { + if(_size + n > _size * 2) + ret = Resize(_size + n); + else + ret = Resize(_size * 2); + } + _size = _size + n; + return ret; + } + bool CanAdvance(SQInteger n) { + if(_ptr+n>_size)return false; + return true; + } + SQInteger Seek(SQInteger offset, SQInteger origin) { + switch(origin) { + case SQ_SEEK_SET: + if(offset > _size || offset < 0) return -1; + _ptr = offset; + break; + case SQ_SEEK_CUR: + if(_ptr + offset > _size || _ptr + offset < 0) return -1; + _ptr += offset; + break; + case SQ_SEEK_END: + if(_size + offset > _size || _size + offset < 0) return -1; + _ptr = _size + offset; + break; + default: return -1; + } + return 0; + } + bool IsValid() { + return _size == 0 || _buf?true:false; + } + bool EOS() { + return _ptr == _size; + } + SQInteger Flush() { return 0; } + SQInteger Tell() { return _ptr; } + SQInteger Len() { return _size; } + SQUserPointer GetBuf(){ return _buf; } +private: + SQInteger _size; + SQInteger _allocated; + SQInteger _ptr; + unsigned char *_buf; + bool _owns; +}; + +#endif //_SQSTD_BLOBIMPL_H_ diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdio.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdio.cpp new file mode 100644 index 0000000000..52cd515852 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdio.cpp @@ -0,0 +1,489 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include "sqstdstream.h" + +#define SQSTD_FILE_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000001)) +//basic API +SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode) +{ +#ifndef SQUNICODE + return (SQFILE)fopen(filename,mode); +#else + return (SQFILE)_wfopen(filename,mode); +#endif +} + +SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file) +{ + SQInteger ret = (SQInteger)fread(buffer,size,count,(FILE *)file); + return ret; +} + +SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file) +{ + return (SQInteger)fwrite(buffer,size,count,(FILE *)file); +} + +SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin) +{ + SQInteger realorigin; + switch(origin) { + case SQ_SEEK_CUR: realorigin = SEEK_CUR; break; + case SQ_SEEK_END: realorigin = SEEK_END; break; + case SQ_SEEK_SET: realorigin = SEEK_SET; break; + default: return -1; //failed + } + return fseek((FILE *)file,(long)offset,(int)realorigin); +} + +SQInteger sqstd_ftell(SQFILE file) +{ + return ftell((FILE *)file); +} + +SQInteger sqstd_fflush(SQFILE file) +{ + return fflush((FILE *)file); +} + +SQInteger sqstd_fclose(SQFILE file) +{ + return fclose((FILE *)file); +} + +SQInteger sqstd_feof(SQFILE file) +{ + return feof((FILE *)file); +} + +//File +struct SQFile : public SQStream { + SQFile() { _handle = NULL; _owns = false;} + SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;} + virtual ~SQFile() { Close(); } + bool Open(const SQChar *filename ,const SQChar *mode) { + Close(); + if( (_handle = sqstd_fopen(filename,mode)) ) { + _owns = true; + return true; + } + return false; + } + void Close() { + if(_handle && _owns) { + sqstd_fclose(_handle); + _handle = NULL; + _owns = false; + } + } + SQInteger Read(void *buffer,SQInteger size) { + return sqstd_fread(buffer,1,size,_handle); + } + SQInteger Write(void *buffer,SQInteger size) { + return sqstd_fwrite(buffer,1,size,_handle); + } + SQInteger Flush() { + return sqstd_fflush(_handle); + } + SQInteger Tell() { + return sqstd_ftell(_handle); + } + SQInteger Len() { + SQInteger prevpos=Tell(); + Seek(0,SQ_SEEK_END); + SQInteger size=Tell(); + Seek(prevpos,SQ_SEEK_SET); + return size; + } + SQInteger Seek(SQInteger offset, SQInteger origin) { + return sqstd_fseek(_handle,offset,origin); + } + bool IsValid() { return _handle?true:false; } + bool EOS() { return Tell()==Len()?true:false;} + SQFILE GetHandle() {return _handle;} +private: + SQFILE _handle; + bool _owns; +}; + +static SQInteger _file__typeof(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("file"),-1); + return 1; +} + +static SQInteger _file_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) +{ + SQFile *self = (SQFile*)p; + self->~SQFile(); + sq_free(self,sizeof(SQFile)); + return 1; +} + +static SQInteger _file_constructor(HSQUIRRELVM v) +{ + const SQChar *filename,*mode; + bool owns = true; + SQFile *f; + SQFILE newf; + if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) { + sq_getstring(v, 2, &filename); + sq_getstring(v, 3, &mode); + newf = sqstd_fopen(filename, mode); + if(!newf) return sq_throwerror(v, _SC("cannot open file")); + } else if(sq_gettype(v,2) == OT_USERPOINTER) { + owns = !(sq_gettype(v,3) == OT_NULL); + sq_getuserpointer(v,2,&newf); + } else { + return sq_throwerror(v,_SC("wrong parameter")); + } + + f = new (sq_malloc(sizeof(SQFile)))SQFile(newf,owns); + if(SQ_FAILED(sq_setinstanceup(v,1,f))) { + f->~SQFile(); + sq_free(f,sizeof(SQFile)); + return sq_throwerror(v, _SC("cannot create blob with negative size")); + } + sq_setreleasehook(v,1,_file_releasehook); + return 0; +} + +static SQInteger _file_close(HSQUIRRELVM v) +{ + SQFile *self = NULL; + if(SQ_SUCCEEDED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_FILE_TYPE_TAG)) + && self != NULL) + { + self->Close(); + } + return 0; +} + +//bindings +#define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck} +static const SQRegFunction _file_methods[] = { + _DECL_FILE_FUNC(constructor,3,_SC("x")), + _DECL_FILE_FUNC(_typeof,1,_SC("x")), + _DECL_FILE_FUNC(close,1,_SC("x")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + + + +SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own) +{ + SQInteger top = sq_gettop(v); + sq_pushregistrytable(v); + sq_pushstring(v,_SC("std_file"),-1); + if(SQ_SUCCEEDED(sq_get(v,-2))) { + sq_remove(v,-2); //removes the registry + sq_pushroottable(v); // push the this + sq_pushuserpointer(v,file); //file + if(own){ + sq_pushinteger(v,1); //true + } + else{ + sq_pushnull(v); //false + } + if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) { + sq_remove(v,-2); + return SQ_OK; + } + } + sq_settop(v,top); + return SQ_ERROR; +} + +SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file) +{ + SQFile *fileobj = NULL; + if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) { + *file = fileobj->GetHandle(); + return SQ_OK; + } + return sq_throwerror(v,_SC("not a file")); +} + + + +#define IO_BUFFER_SIZE 2048 +struct IOBuffer { + unsigned char buffer[IO_BUFFER_SIZE]; + SQInteger size; + SQInteger ptr; + SQFILE file; +}; + +SQInteger _read_byte(IOBuffer *iobuffer) +{ + if(iobuffer->ptr < iobuffer->size) { + + SQInteger ret = iobuffer->buffer[iobuffer->ptr]; + iobuffer->ptr++; + return ret; + } + else { + if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) + { + SQInteger ret = iobuffer->buffer[0]; + iobuffer->ptr = 1; + return ret; + } + } + + return 0; +} + +SQInteger _read_two_bytes(IOBuffer *iobuffer) +{ + if(iobuffer->ptr < iobuffer->size) { + if(iobuffer->size < 2) return 0; + SQInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]); + iobuffer->ptr += 2; + return ret; + } + else { + if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) + { + if(iobuffer->size < 2) return 0; + SQInteger ret = *((const wchar_t*)&iobuffer->buffer[0]); + iobuffer->ptr = 2; + return ret; + } + } + + return 0; +} + +static SQInteger _io_file_lexfeed_PLAIN(SQUserPointer iobuf) +{ + IOBuffer *iobuffer = (IOBuffer *)iobuf; + return _read_byte(iobuffer); + +} + +#ifdef SQUNICODE +static SQInteger _io_file_lexfeed_UTF8(SQUserPointer iobuf) +{ + IOBuffer *iobuffer = (IOBuffer *)iobuf; +#define READ(iobuf) \ + if((inchar = (unsigned char)_read_byte(iobuf)) == 0) \ + return 0; + + static const SQInteger utf8_lengths[16] = + { + 1,1,1,1,1,1,1,1, /* 0000 to 0111 : 1 byte (plain ASCII) */ + 0,0,0,0, /* 1000 to 1011 : not valid */ + 2,2, /* 1100, 1101 : 2 bytes */ + 3, /* 1110 : 3 bytes */ + 4 /* 1111 :4 bytes */ + }; + static const unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07}; + unsigned char inchar; + SQInteger c = 0; + READ(iobuffer); + c = inchar; + // + if(c >= 0x80) { + SQInteger tmp; + SQInteger codelen = utf8_lengths[c>>4]; + if(codelen == 0) + return 0; + //"invalid UTF-8 stream"; + tmp = c&byte_masks[codelen]; + for(SQInteger n = 0; n < codelen-1; n++) { + tmp<<=6; + READ(iobuffer); + tmp |= inchar & 0x3F; + } + c = tmp; + } + return c; +} +#endif + +static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer iobuf) +{ + SQInteger ret; + IOBuffer *iobuffer = (IOBuffer *)iobuf; + if( (ret = _read_two_bytes(iobuffer)) > 0 ) + return ret; + return 0; +} + +static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer iobuf) +{ + SQInteger c; + IOBuffer *iobuffer = (IOBuffer *)iobuf; + if( (c = _read_two_bytes(iobuffer)) > 0 ) { + c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00); + return c; + } + return 0; +} + +SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size) +{ + SQInteger ret; + if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret; + return -1; +} + +SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size) +{ + return sqstd_fwrite(p,1,size,(SQFILE)file); +} + +SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror) +{ + SQFILE file = sqstd_fopen(filename,_SC("rb")); + + SQInteger ret; + unsigned short us; + unsigned char uc; + SQLEXREADFUNC func = _io_file_lexfeed_PLAIN; + if(file){ + ret = sqstd_fread(&us,1,2,file); + if(ret != 2) { + //probably an empty file + us = 0; + } + if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE + sqstd_fseek(file,0,SQ_SEEK_SET); + if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) { + sqstd_fclose(file); + return SQ_OK; + } + } + else { //SCRIPT + + switch(us) + { + //gotta swap the next 2 lines on BIG endian machines + case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian; + case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian; + case 0xBBEF: + if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) { + sqstd_fclose(file); + return sq_throwerror(v,_SC("io error")); + } + if(uc != 0xBF) { + sqstd_fclose(file); + return sq_throwerror(v,_SC("Unrecognized encoding")); + } +#ifdef SQUNICODE + func = _io_file_lexfeed_UTF8; +#else + func = _io_file_lexfeed_PLAIN; +#endif + break;//UTF-8 ; + default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii + } + IOBuffer buffer; + buffer.ptr = 0; + buffer.size = 0; + buffer.file = file; + if(SQ_SUCCEEDED(sq_compile(v,func,&buffer,filename,printerror))){ + sqstd_fclose(file); + return SQ_OK; + } + } + sqstd_fclose(file); + return SQ_ERROR; + } + return sq_throwerror(v,_SC("cannot open the file")); +} + +SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror) +{ + //at least one entry must exist in order for us to push it as the environment + if(sq_gettop(v) == 0) + return sq_throwerror(v,_SC("environment table expected")); + + if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) { + sq_push(v,-2); + if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) { + sq_remove(v,retval?-2:-1); //removes the closure + return 1; + } + sq_pop(v,1); //removes the closure + } + return SQ_ERROR; +} + +SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename) +{ + SQFILE file = sqstd_fopen(filename,_SC("wb+")); + if(!file) return sq_throwerror(v,_SC("cannot open the file")); + if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) { + sqstd_fclose(file); + return SQ_OK; + } + sqstd_fclose(file); + return SQ_ERROR; //forward the error +} + +SQInteger _g_io_loadfile(HSQUIRRELVM v) +{ + const SQChar *filename; + SQBool printerror = SQFalse; + sq_getstring(v,2,&filename); + if(sq_gettop(v) >= 3) { + sq_getbool(v,3,&printerror); + } + if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) + return 1; + return SQ_ERROR; //propagates the error +} + +SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v) +{ + const SQChar *filename; + sq_getstring(v,2,&filename); + if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename))) + return 1; + return SQ_ERROR; //propagates the error +} + +SQInteger _g_io_dofile(HSQUIRRELVM v) +{ + const SQChar *filename; + SQBool printerror = SQFalse; + sq_getstring(v,2,&filename); + if(sq_gettop(v) >= 3) { + sq_getbool(v,3,&printerror); + } + sq_push(v,1); //repush the this + if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror))) + return 1; + return SQ_ERROR; //propagates the error +} + +#define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck} +static const SQRegFunction iolib_funcs[]={ + _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")), + _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")), + _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")), + {NULL,(SQFUNCTION)0,0,NULL} +}; + +SQRESULT sqstd_register_iolib(HSQUIRRELVM v) +{ + SQInteger top = sq_gettop(v); + //create delegate + declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs); + sq_pushstring(v,_SC("stdout"),-1); + sqstd_createfile(v,stdout,SQFalse); + sq_newslot(v,-3,SQFalse); + sq_pushstring(v,_SC("stdin"),-1); + sqstd_createfile(v,stdin,SQFalse); + sq_newslot(v,-3,SQFalse); + sq_pushstring(v,_SC("stderr"),-1); + sqstd_createfile(v,stderr,SQFalse); + sq_newslot(v,-3,SQFalse); + sq_settop(v,top); + return SQ_OK; +} diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp b/sp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp new file mode 100644 index 0000000000..ef5b7f486b --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdlib.dsp @@ -0,0 +1,131 @@ +# Microsoft Developer Studio Project File - Name="sqstdlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=sqstdlib - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "sqstdlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "sqstdlib.mak" CFG="sqstdlib - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "sqstdlib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "sqstdlib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "sqstdlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x410 /d "NDEBUG" +# ADD RSC /l 0x410 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\sqstdlib.lib" + +!ELSEIF "$(CFG)" == "sqstdlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x410 /d "_DEBUG" +# ADD RSC /l 0x410 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\sqstdlib.lib" + +!ENDIF + +# Begin Target + +# Name "sqstdlib - Win32 Release" +# Name "sqstdlib - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sqstdblob.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdio.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdmath.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdrex.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdstream.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdstring.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdaux.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqstdsystem.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\sqstdblobimpl.h +# End Source File +# Begin Source File + +SOURCE=.\sqstdstream.h +# End Source File +# End Group +# End Target +# End Project diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp new file mode 100644 index 0000000000..c41ffd5e0f --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdmath.cpp @@ -0,0 +1,107 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include + +#define SINGLE_ARG_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ + SQFloat f; \ + sq_getfloat(v,2,&f); \ + sq_pushfloat(v,(SQFloat)_funcname(f)); \ + return 1; \ +} + +#define TWO_ARGS_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ + SQFloat p1,p2; \ + sq_getfloat(v,2,&p1); \ + sq_getfloat(v,3,&p2); \ + sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \ + return 1; \ +} + +static SQInteger math_srand(HSQUIRRELVM v) +{ + SQInteger i; + if(SQ_FAILED(sq_getinteger(v,2,&i))) + return sq_throwerror(v,_SC("invalid param")); + srand((unsigned int)i); + return 0; +} + +static SQInteger math_rand(HSQUIRRELVM v) +{ + sq_pushinteger(v,rand()); + return 1; +} + +static SQInteger math_abs(HSQUIRRELVM v) +{ + SQInteger n; + sq_getinteger(v,2,&n); + sq_pushinteger(v,(SQInteger)abs((int)n)); + return 1; +} + +SINGLE_ARG_FUNC(sqrt) +SINGLE_ARG_FUNC(fabs) +SINGLE_ARG_FUNC(sin) +SINGLE_ARG_FUNC(cos) +SINGLE_ARG_FUNC(asin) +SINGLE_ARG_FUNC(acos) +SINGLE_ARG_FUNC(log) +SINGLE_ARG_FUNC(log10) +SINGLE_ARG_FUNC(tan) +SINGLE_ARG_FUNC(atan) +TWO_ARGS_FUNC(atan2) +TWO_ARGS_FUNC(pow) +SINGLE_ARG_FUNC(floor) +SINGLE_ARG_FUNC(ceil) +SINGLE_ARG_FUNC(exp) + +#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),math_##name,nparams,tycheck} +static const SQRegFunction mathlib_funcs[] = { + _DECL_FUNC(sqrt,2,_SC(".n")), + _DECL_FUNC(sin,2,_SC(".n")), + _DECL_FUNC(cos,2,_SC(".n")), + _DECL_FUNC(asin,2,_SC(".n")), + _DECL_FUNC(acos,2,_SC(".n")), + _DECL_FUNC(log,2,_SC(".n")), + _DECL_FUNC(log10,2,_SC(".n")), + _DECL_FUNC(tan,2,_SC(".n")), + _DECL_FUNC(atan,2,_SC(".n")), + _DECL_FUNC(atan2,3,_SC(".nn")), + _DECL_FUNC(pow,3,_SC(".nn")), + _DECL_FUNC(floor,2,_SC(".n")), + _DECL_FUNC(ceil,2,_SC(".n")), + _DECL_FUNC(exp,2,_SC(".n")), + _DECL_FUNC(srand,2,_SC(".n")), + _DECL_FUNC(rand,1,NULL), + _DECL_FUNC(fabs,2,_SC(".n")), + _DECL_FUNC(abs,2,_SC(".n")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_FUNC + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) +{ + SQInteger i=0; + while(mathlib_funcs[i].name!=0) { + sq_pushstring(v,mathlib_funcs[i].name,-1); + sq_newclosure(v,mathlib_funcs[i].f,0); + sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask); + sq_setnativeclosurename(v,-1,mathlib_funcs[i].name); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_pushstring(v,_SC("RAND_MAX"),-1); + sq_pushinteger(v,RAND_MAX); + sq_newslot(v,-3,SQFalse); + sq_pushstring(v,_SC("PI"),-1); + sq_pushfloat(v,(SQFloat)M_PI); + sq_newslot(v,-3,SQFalse); + return SQ_OK; +} diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp new file mode 100644 index 0000000000..d0583a6b49 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdrex.cpp @@ -0,0 +1,666 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include + +#ifdef _DEBUG +#include + +static const SQChar *g_nnames[] = +{ + _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"), + _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"), + _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"), + _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB"),_SC("OP_MB") +}; + +#endif + +#define OP_GREEDY (MAX_CHAR+1) // * + ? {n} +#define OP_OR (MAX_CHAR+2) +#define OP_EXPR (MAX_CHAR+3) //parentesis () +#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:) +#define OP_DOT (MAX_CHAR+5) +#define OP_CLASS (MAX_CHAR+6) +#define OP_CCLASS (MAX_CHAR+7) +#define OP_NCLASS (MAX_CHAR+8) //negates class the [^ +#define OP_RANGE (MAX_CHAR+9) +#define OP_CHAR (MAX_CHAR+10) +#define OP_EOL (MAX_CHAR+11) +#define OP_BOL (MAX_CHAR+12) +#define OP_WB (MAX_CHAR+13) +#define OP_MB (MAX_CHAR+14) //match balanced + +#define SQREX_SYMBOL_ANY_CHAR ('.') +#define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') +#define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') +#define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') +#define SQREX_SYMBOL_BRANCH ('|') +#define SQREX_SYMBOL_END_OF_STRING ('$') +#define SQREX_SYMBOL_BEGINNING_OF_STRING ('^') +#define SQREX_SYMBOL_ESCAPE_CHAR ('\\') + + +typedef int SQRexNodeType; + +typedef struct tagSQRexNode{ + SQRexNodeType type; + SQInteger left; + SQInteger right; + SQInteger next; +}SQRexNode; + +struct SQRex{ + const SQChar *_eol; + const SQChar *_bol; + const SQChar *_p; + SQInteger _first; + SQInteger _op; + SQRexNode *_nodes; + SQInteger _nallocated; + SQInteger _nsize; + SQInteger _nsubexpr; + SQRexMatch *_matches; + SQInteger _currsubexp; + void *_jmpbuf; + const SQChar **_error; +}; + +static SQInteger sqstd_rex_list(SQRex *exp); + +static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type) +{ + SQRexNode n; + n.type = type; + n.next = n.right = n.left = -1; + if(type == OP_EXPR) + n.right = exp->_nsubexpr++; + if(exp->_nallocated < (exp->_nsize + 1)) { + SQInteger oldsize = exp->_nallocated; + exp->_nallocated *= 2; + exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode)); + } + exp->_nodes[exp->_nsize++] = n; + SQInteger newid = exp->_nsize - 1; + return (SQInteger)newid; +} + +static void sqstd_rex_error(SQRex *exp,const SQChar *error) +{ + if(exp->_error) *exp->_error = error; + longjmp(*((jmp_buf*)exp->_jmpbuf),-1); +} + +static void sqstd_rex_expect(SQRex *exp, SQInteger n){ + if((*exp->_p) != n) + sqstd_rex_error(exp, _SC("expected paren")); + exp->_p++; +} + +static SQChar sqstd_rex_escapechar(SQRex *exp) +{ + if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){ + exp->_p++; + switch(*exp->_p) { + case 'v': exp->_p++; return '\v'; + case 'n': exp->_p++; return '\n'; + case 't': exp->_p++; return '\t'; + case 'r': exp->_p++; return '\r'; + case 'f': exp->_p++; return '\f'; + default: return (*exp->_p++); + } + } else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,_SC("letter expected")); + return (*exp->_p++); +} + +static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid) +{ + SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS); + exp->_nodes[n].left = classid; + return n; +} + +static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass) +{ + SQChar t; + if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) { + exp->_p++; + switch(*exp->_p) { + case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n'); + case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t'); + case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r'); + case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f'); + case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v'); + case 'a': case 'A': case 'w': case 'W': case 's': case 'S': + case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': + case 'p': case 'P': case 'l': case 'u': + { + t = *exp->_p; exp->_p++; + return sqstd_rex_charclass(exp,t); + } + case 'm': + { + SQChar cb, ce; //cb = character begin match ce = character end match + cb = *++exp->_p; //skip 'm' + ce = *++exp->_p; + exp->_p++; //points to the next char to be parsed + if ((!cb) || (!ce)) sqstd_rex_error(exp,_SC("balanced chars expected")); + if ( cb == ce ) sqstd_rex_error(exp,_SC("open/close char can't be the same")); + SQInteger node = sqstd_rex_newnode(exp,OP_MB); + exp->_nodes[node].left = cb; + exp->_nodes[node].right = ce; + return node; + } + case 0: + sqstd_rex_error(exp,_SC("letter expected for argument of escape sequence")); + break; + case 'b': + case 'B': + if(!isclass) { + SQInteger node = sqstd_rex_newnode(exp,OP_WB); + exp->_nodes[node].left = *exp->_p; + exp->_p++; + return node; + } //else default + default: + t = *exp->_p; exp->_p++; + return sqstd_rex_newnode(exp,t); + } + } + else if(!scisprint(*exp->_p)) { + + sqstd_rex_error(exp,_SC("letter expected")); + } + t = *exp->_p; exp->_p++; + return sqstd_rex_newnode(exp,t); +} +static SQInteger sqstd_rex_class(SQRex *exp) +{ + SQInteger ret = -1; + SQInteger first = -1,chain; + if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){ + ret = sqstd_rex_newnode(exp,OP_NCLASS); + exp->_p++; + }else ret = sqstd_rex_newnode(exp,OP_CLASS); + + if(*exp->_p == ']') sqstd_rex_error(exp,_SC("empty class")); + chain = ret; + while(*exp->_p != ']' && exp->_p != exp->_eol) { + if(*exp->_p == '-' && first != -1){ + SQInteger r; + if(*exp->_p++ == ']') sqstd_rex_error(exp,_SC("unfinished range")); + r = sqstd_rex_newnode(exp,OP_RANGE); + if(exp->_nodes[first].type>*exp->_p) sqstd_rex_error(exp,_SC("invalid range")); + if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,_SC("cannot use character classes in ranges")); + exp->_nodes[r].left = exp->_nodes[first].type; + SQInteger t = sqstd_rex_escapechar(exp); + exp->_nodes[r].right = t; + exp->_nodes[chain].next = r; + chain = r; + first = -1; + } + else{ + if(first!=-1){ + SQInteger c = first; + exp->_nodes[chain].next = c; + chain = c; + first = sqstd_rex_charnode(exp,SQTrue); + } + else{ + first = sqstd_rex_charnode(exp,SQTrue); + } + } + } + if(first!=-1){ + SQInteger c = first; + exp->_nodes[chain].next = c; + } + /* hack? */ + exp->_nodes[ret].left = exp->_nodes[ret].next; + exp->_nodes[ret].next = -1; + return ret; +} + +static SQInteger sqstd_rex_parsenumber(SQRex *exp) +{ + SQInteger ret = *exp->_p-'0'; + SQInteger positions = 10; + exp->_p++; + while(isdigit(*exp->_p)) { + ret = ret*10+(*exp->_p++-'0'); + if(positions==1000000000) sqstd_rex_error(exp,_SC("overflow in numeric constant")); + positions *= 10; + }; + return ret; +} + +static SQInteger sqstd_rex_element(SQRex *exp) +{ + SQInteger ret = -1; + switch(*exp->_p) + { + case '(': { + SQInteger expr; + exp->_p++; + + + if(*exp->_p =='?') { + exp->_p++; + sqstd_rex_expect(exp,':'); + expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR); + } + else + expr = sqstd_rex_newnode(exp,OP_EXPR); + SQInteger newn = sqstd_rex_list(exp); + exp->_nodes[expr].left = newn; + ret = expr; + sqstd_rex_expect(exp,')'); + } + break; + case '[': + exp->_p++; + ret = sqstd_rex_class(exp); + sqstd_rex_expect(exp,']'); + break; + case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break; + case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break; + default: + ret = sqstd_rex_charnode(exp,SQFalse); + break; + } + + + SQBool isgreedy = SQFalse; + unsigned short p0 = 0, p1 = 0; + switch(*exp->_p){ + case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; + case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; + case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break; + case '{': + exp->_p++; + if(!isdigit(*exp->_p)) sqstd_rex_error(exp,_SC("number expected")); + p0 = (unsigned short)sqstd_rex_parsenumber(exp); + /*******************************/ + switch(*exp->_p) { + case '}': + p1 = p0; exp->_p++; + break; + case ',': + exp->_p++; + p1 = 0xFFFF; + if(isdigit(*exp->_p)){ + p1 = (unsigned short)sqstd_rex_parsenumber(exp); + } + sqstd_rex_expect(exp,'}'); + break; + default: + sqstd_rex_error(exp,_SC(", or } expected")); + } + /*******************************/ + isgreedy = SQTrue; + break; + + } + if(isgreedy) { + SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY); + exp->_nodes[nnode].left = ret; + exp->_nodes[nnode].right = ((p0)<<16)|p1; + ret = nnode; + } + + if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { + SQInteger nnode = sqstd_rex_element(exp); + exp->_nodes[ret].next = nnode; + } + + return ret; +} + +static SQInteger sqstd_rex_list(SQRex *exp) +{ + SQInteger ret=-1,e; + if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) { + exp->_p++; + ret = sqstd_rex_newnode(exp,OP_BOL); + } + e = sqstd_rex_element(exp); + if(ret != -1) { + exp->_nodes[ret].next = e; + } + else ret = e; + + if(*exp->_p == SQREX_SYMBOL_BRANCH) { + SQInteger temp,tright; + exp->_p++; + temp = sqstd_rex_newnode(exp,OP_OR); + exp->_nodes[temp].left = ret; + tright = sqstd_rex_list(exp); + exp->_nodes[temp].right = tright; + ret = temp; + } + return ret; +} + +static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c) +{ + switch(cclass) { + case 'a': return isalpha(c)?SQTrue:SQFalse; + case 'A': return !isalpha(c)?SQTrue:SQFalse; + case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse; + case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse; + case 's': return isspace(c)?SQTrue:SQFalse; + case 'S': return !isspace(c)?SQTrue:SQFalse; + case 'd': return isdigit(c)?SQTrue:SQFalse; + case 'D': return !isdigit(c)?SQTrue:SQFalse; + case 'x': return isxdigit(c)?SQTrue:SQFalse; + case 'X': return !isxdigit(c)?SQTrue:SQFalse; + case 'c': return iscntrl(c)?SQTrue:SQFalse; + case 'C': return !iscntrl(c)?SQTrue:SQFalse; + case 'p': return ispunct(c)?SQTrue:SQFalse; + case 'P': return !ispunct(c)?SQTrue:SQFalse; + case 'l': return islower(c)?SQTrue:SQFalse; + case 'u': return isupper(c)?SQTrue:SQFalse; + } + return SQFalse; /*cannot happen*/ +} + +static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQChar c) +{ + do { + switch(node->type) { + case OP_RANGE: + if(c >= node->left && c <= node->right) return SQTrue; + break; + case OP_CCLASS: + if(sqstd_rex_matchcclass(node->left,c)) return SQTrue; + break; + default: + if(c == node->type)return SQTrue; + } + } while((node->next != -1) && (node = &exp->_nodes[node->next])); + return SQFalse; +} + +static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next) +{ + + SQRexNodeType type = node->type; + switch(type) { + case OP_GREEDY: { + //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; + SQRexNode *greedystop = NULL; + SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0; + const SQChar *s=str, *good = str; + + if(node->next != -1) { + greedystop = &exp->_nodes[node->next]; + } + else { + greedystop = next; + } + + while((nmaches == 0xFFFF || nmaches < p1)) { + + const SQChar *stop; + if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop))) + break; + nmaches++; + good=s; + if(greedystop) { + //checks that 0 matches satisfy the expression(if so skips) + //if not would always stop(for instance if is a '?') + if(greedystop->type != OP_GREEDY || + (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0)) + { + SQRexNode *gnext = NULL; + if(greedystop->next != -1) { + gnext = &exp->_nodes[greedystop->next]; + }else if(next && next->next != -1){ + gnext = &exp->_nodes[next->next]; + } + stop = sqstd_rex_matchnode(exp,greedystop,s,gnext); + if(stop) { + //if satisfied stop it + if(p0 == p1 && p0 == nmaches) break; + else if(nmaches >= p0 && p1 == 0xFFFF) break; + else if(nmaches >= p0 && nmaches <= p1) break; + } + } + } + + if(s >= exp->_eol) + break; + } + if(p0 == p1 && p0 == nmaches) return good; + else if(nmaches >= p0 && p1 == 0xFFFF) return good; + else if(nmaches >= p0 && nmaches <= p1) return good; + return NULL; + } + case OP_OR: { + const SQChar *asd = str; + SQRexNode *temp=&exp->_nodes[node->left]; + while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + asd = str; + temp = &exp->_nodes[node->right]; + while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + return NULL; + break; + } + case OP_EXPR: + case OP_NOCAPEXPR:{ + SQRexNode *n = &exp->_nodes[node->left]; + const SQChar *cur = str; + SQInteger capture = -1; + if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { + capture = exp->_currsubexp; + exp->_matches[capture].begin = cur; + exp->_currsubexp++; + } + SQInteger tempcap = exp->_currsubexp; + do { + SQRexNode *subnext = NULL; + if(n->next != -1) { + subnext = &exp->_nodes[n->next]; + }else { + subnext = next; + } + if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) { + if(capture != -1){ + exp->_matches[capture].begin = 0; + exp->_matches[capture].len = 0; + } + return NULL; + } + } while((n->next != -1) && (n = &exp->_nodes[n->next])); + + exp->_currsubexp = tempcap; + if(capture != -1) + exp->_matches[capture].len = cur - exp->_matches[capture].begin; + return cur; + } + case OP_WB: + if((str == exp->_bol && !isspace(*str)) + || (str == exp->_eol && !isspace(*(str-1))) + || (!isspace(*str) && isspace(*(str+1))) + || (isspace(*str) && !isspace(*(str+1))) ) { + return (node->left == 'b')?str:NULL; + } + return (node->left == 'b')?NULL:str; + case OP_BOL: + if(str == exp->_bol) return str; + return NULL; + case OP_EOL: + if(str == exp->_eol) return str; + return NULL; + case OP_DOT:{ + if (str == exp->_eol) return NULL; + str++; + } + return str; + case OP_NCLASS: + case OP_CLASS: + if (str == exp->_eol) return NULL; + if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) { + str++; + return str; + } + return NULL; + case OP_CCLASS: + if (str == exp->_eol) return NULL; + if(sqstd_rex_matchcclass(node->left,*str)) { + str++; + return str; + } + return NULL; + case OP_MB: + { + SQInteger cb = node->left; //char that opens a balanced expression + if(*str != cb) return NULL; // string doesnt start with open char + SQInteger ce = node->right; //char that closes a balanced expression + SQInteger cont = 1; + const SQChar *streol = exp->_eol; + while (++str < streol) { + if (*str == ce) { + if (--cont == 0) { + return ++str; + } + } + else if (*str == cb) cont++; + } + } + return NULL; // string ends out of balance + default: /* char */ + if (str == exp->_eol) return NULL; + if(*str != node->type) return NULL; + str++; + return str; + } + return NULL; +} + +/* public api */ +SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error) +{ + SQRex * volatile exp = (SQRex *)sq_malloc(sizeof(SQRex)); // "volatile" is needed for setjmp() + exp->_eol = exp->_bol = NULL; + exp->_p = pattern; + exp->_nallocated = (SQInteger)scstrlen(pattern) * sizeof(SQChar); + exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode)); + exp->_nsize = 0; + exp->_matches = 0; + exp->_nsubexpr = 0; + exp->_first = sqstd_rex_newnode(exp,OP_EXPR); + exp->_error = error; + exp->_jmpbuf = sq_malloc(sizeof(jmp_buf)); + if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { + SQInteger res = sqstd_rex_list(exp); + exp->_nodes[exp->_first].left = res; + if(*exp->_p!='\0') + sqstd_rex_error(exp,_SC("unexpected character")); +#ifdef _DEBUG + { + SQInteger nsize,i; + SQRexNode *t; + nsize = exp->_nsize; + t = &exp->_nodes[0]; + scprintf(_SC("\n")); + for(i = 0;i < nsize; i++) { + if(exp->_nodes[i].type>MAX_CHAR) + scprintf(_SC("[%02d] %10s "), (SQInt32)i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); + else + scprintf(_SC("[%02d] %10c "), (SQInt32)i,exp->_nodes[i].type); + scprintf(_SC("left %02d right %02d next %02d\n"), (SQInt32)exp->_nodes[i].left, (SQInt32)exp->_nodes[i].right, (SQInt32)exp->_nodes[i].next); + } + scprintf(_SC("\n")); + } +#endif + exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch)); + memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch)); + } + else{ + sqstd_rex_free(exp); + return NULL; + } + return exp; +} + +void sqstd_rex_free(SQRex *exp) +{ + if(exp) { + if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode)); + if(exp->_jmpbuf) sq_free(exp->_jmpbuf,sizeof(jmp_buf)); + if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch)); + sq_free(exp,sizeof(SQRex)); + } +} + +SQBool sqstd_rex_match(SQRex* exp,const SQChar* text) +{ + const SQChar* res = NULL; + exp->_bol = text; + exp->_eol = text + scstrlen(text); + exp->_currsubexp = 0; + res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL); + if(res == NULL || res != exp->_eol) + return SQFalse; + return SQTrue; +} + +SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end) +{ + const SQChar *cur = NULL; + SQInteger node = exp->_first; + if(text_begin >= text_end) return SQFalse; + exp->_bol = text_begin; + exp->_eol = text_end; + do { + cur = text_begin; + while(node != -1) { + exp->_currsubexp = 0; + cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL); + if(!cur) + break; + node = exp->_nodes[node].next; + } + text_begin++; + } while(cur == NULL && text_begin != text_end); + + if(cur == NULL) + return SQFalse; + + --text_begin; + + if(out_begin) *out_begin = text_begin; + if(out_end) *out_end = cur; + return SQTrue; +} + +SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end) +{ + return sqstd_rex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end); +} + +SQInteger sqstd_rex_getsubexpcount(SQRex* exp) +{ + return exp->_nsubexpr; +} + +SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp) +{ + if( n<0 || n >= exp->_nsubexpr) return SQFalse; + *subexp = exp->_matches[n]; + return SQTrue; +} + diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp new file mode 100644 index 0000000000..b5c47cfbb2 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdstream.cpp @@ -0,0 +1,336 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include +#include +#include +#include "sqstdstream.h" +#include "sqstdblobimpl.h" + +#define SETUP_STREAM(v) \ + SQStream *self = NULL; \ + if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)))) \ + return sq_throwerror(v,_SC("invalid type tag")); \ + if(!self || !self->IsValid()) \ + return sq_throwerror(v,_SC("the stream is invalid")); + +SQInteger _stream_readblob(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQUserPointer data,blobp; + SQInteger size,res; + sq_getinteger(v,2,&size); + if(size > self->Len()) { + size = self->Len(); + } + data = sq_getscratchpad(v,size); + res = self->Read(data,size); + if(res <= 0) + return sq_throwerror(v,_SC("no data left to read")); + blobp = sqstd_createblob(v,res); + memcpy(blobp,data,res); + return 1; +} + +#define SAFE_READN(ptr,len) { \ + if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \ + } +SQInteger _stream_readn(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQInteger format; + sq_getinteger(v, 2, &format); + switch(format) { + case 'l': { + SQInteger i; + SAFE_READN(&i, sizeof(i)); + sq_pushinteger(v, i); + } + break; + case 'i': { + SQInt32 i; + SAFE_READN(&i, sizeof(i)); + sq_pushinteger(v, i); + } + break; + case 's': { + short s; + SAFE_READN(&s, sizeof(short)); + sq_pushinteger(v, s); + } + break; + case 'w': { + unsigned short w; + SAFE_READN(&w, sizeof(unsigned short)); + sq_pushinteger(v, w); + } + break; + case 'c': { + char c; + SAFE_READN(&c, sizeof(char)); + sq_pushinteger(v, c); + } + break; + case 'b': { + unsigned char c; + SAFE_READN(&c, sizeof(unsigned char)); + sq_pushinteger(v, c); + } + break; + case 'f': { + float f; + SAFE_READN(&f, sizeof(float)); + sq_pushfloat(v, f); + } + break; + case 'd': { + double d; + SAFE_READN(&d, sizeof(double)); + sq_pushfloat(v, (SQFloat)d); + } + break; + default: + return sq_throwerror(v, _SC("invalid format")); + } + return 1; +} + +SQInteger _stream_writeblob(HSQUIRRELVM v) +{ + SQUserPointer data; + SQInteger size; + SETUP_STREAM(v); + if(SQ_FAILED(sqstd_getblob(v,2,&data))) + return sq_throwerror(v,_SC("invalid parameter")); + size = sqstd_getblobsize(v,2); + if(self->Write(data,size) != size) + return sq_throwerror(v,_SC("io error")); + sq_pushinteger(v,size); + return 1; +} + +SQInteger _stream_writen(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQInteger format, ti; + SQFloat tf; + sq_getinteger(v, 3, &format); + switch(format) { + case 'l': { + SQInteger i; + sq_getinteger(v, 2, &ti); + i = ti; + self->Write(&i, sizeof(SQInteger)); + } + break; + case 'i': { + SQInt32 i; + sq_getinteger(v, 2, &ti); + i = (SQInt32)ti; + self->Write(&i, sizeof(SQInt32)); + } + break; + case 's': { + short s; + sq_getinteger(v, 2, &ti); + s = (short)ti; + self->Write(&s, sizeof(short)); + } + break; + case 'w': { + unsigned short w; + sq_getinteger(v, 2, &ti); + w = (unsigned short)ti; + self->Write(&w, sizeof(unsigned short)); + } + break; + case 'c': { + char c; + sq_getinteger(v, 2, &ti); + c = (char)ti; + self->Write(&c, sizeof(char)); + } + break; + case 'b': { + unsigned char b; + sq_getinteger(v, 2, &ti); + b = (unsigned char)ti; + self->Write(&b, sizeof(unsigned char)); + } + break; + case 'f': { + float f; + sq_getfloat(v, 2, &tf); + f = (float)tf; + self->Write(&f, sizeof(float)); + } + break; + case 'd': { + double d; + sq_getfloat(v, 2, &tf); + d = tf; + self->Write(&d, sizeof(double)); + } + break; + default: + return sq_throwerror(v, _SC("invalid format")); + } + return 0; +} + +SQInteger _stream_seek(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + SQInteger offset, origin = SQ_SEEK_SET; + sq_getinteger(v, 2, &offset); + if(sq_gettop(v) > 2) { + SQInteger t; + sq_getinteger(v, 3, &t); + switch(t) { + case 'b': origin = SQ_SEEK_SET; break; + case 'c': origin = SQ_SEEK_CUR; break; + case 'e': origin = SQ_SEEK_END; break; + default: return sq_throwerror(v,_SC("invalid origin")); + } + } + sq_pushinteger(v, self->Seek(offset, origin)); + return 1; +} + +SQInteger _stream_tell(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + sq_pushinteger(v, self->Tell()); + return 1; +} + +SQInteger _stream_len(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + sq_pushinteger(v, self->Len()); + return 1; +} + +SQInteger _stream_flush(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + if(!self->Flush()) + sq_pushinteger(v, 1); + else + sq_pushnull(v); + return 1; +} + +SQInteger _stream_eos(HSQUIRRELVM v) +{ + SETUP_STREAM(v); + if(self->EOS()) + sq_pushinteger(v, 1); + else + sq_pushnull(v); + return 1; +} + + SQInteger _stream__cloned(HSQUIRRELVM v) + { + return sq_throwerror(v,_SC("this object cannot be cloned")); + } + +static const SQRegFunction _stream_methods[] = { + _DECL_STREAM_FUNC(readblob,2,_SC("xn")), + _DECL_STREAM_FUNC(readn,2,_SC("xn")), + _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")), + _DECL_STREAM_FUNC(writen,3,_SC("xnn")), + _DECL_STREAM_FUNC(seek,-2,_SC("xnn")), + _DECL_STREAM_FUNC(tell,1,_SC("x")), + _DECL_STREAM_FUNC(len,1,_SC("x")), + _DECL_STREAM_FUNC(eos,1,_SC("x")), + _DECL_STREAM_FUNC(flush,1,_SC("x")), + _DECL_STREAM_FUNC(_cloned,0,NULL), + {NULL,(SQFUNCTION)0,0,NULL} +}; + +void init_streamclass(HSQUIRRELVM v) +{ + sq_pushregistrytable(v); + sq_pushstring(v,_SC("std_stream"),-1); + if(SQ_FAILED(sq_get(v,-2))) { + sq_pushstring(v,_SC("std_stream"),-1); + sq_newclass(v,SQFalse); + sq_settypetag(v,-1,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)); + SQInteger i = 0; + while(_stream_methods[i].name != 0) { + const SQRegFunction &f = _stream_methods[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_newslot(v,-3,SQFalse); + sq_pushroottable(v); + sq_pushstring(v,_SC("stream"),-1); + sq_pushstring(v,_SC("std_stream"),-1); + sq_get(v,-4); + sq_newslot(v,-3,SQFalse); + sq_pop(v,1); + } + else { + sq_pop(v,1); //result + } + sq_pop(v,1); +} + +SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals) +{ + if(sq_gettype(v,-1) != OT_TABLE) + return sq_throwerror(v,_SC("table expected")); + SQInteger top = sq_gettop(v); + //create delegate + init_streamclass(v); + sq_pushregistrytable(v); + sq_pushstring(v,reg_name,-1); + sq_pushstring(v,_SC("std_stream"),-1); + if(SQ_SUCCEEDED(sq_get(v,-3))) { + sq_newclass(v,SQTrue); + sq_settypetag(v,-1,typetag); + SQInteger i = 0; + while(methods[i].name != 0) { + const SQRegFunction &f = methods[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_setnativeclosurename(v,-1,f.name); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_newslot(v,-3,SQFalse); + sq_pop(v,1); + + i = 0; + while(globals[i].name!=0) + { + const SQRegFunction &f = globals[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_setnativeclosurename(v,-1,f.name); + sq_newslot(v,-3,SQFalse); + i++; + } + //register the class in the target table + sq_pushstring(v,name,-1); + sq_pushregistrytable(v); + sq_pushstring(v,reg_name,-1); + sq_get(v,-2); + sq_remove(v,-2); + sq_newslot(v,-3,SQFalse); + + sq_settop(v,top); + return SQ_OK; + } + sq_settop(v,top); + return SQ_ERROR; +} diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdstream.h b/sp/src/vscript/squirrel/sqstdlib/sqstdstream.h new file mode 100644 index 0000000000..867c135fde --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdstream.h @@ -0,0 +1,18 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTD_STREAM_H_ +#define _SQSTD_STREAM_H_ + +SQInteger _stream_readblob(HSQUIRRELVM v); +SQInteger _stream_readline(HSQUIRRELVM v); +SQInteger _stream_readn(HSQUIRRELVM v); +SQInteger _stream_writeblob(HSQUIRRELVM v); +SQInteger _stream_writen(HSQUIRRELVM v); +SQInteger _stream_seek(HSQUIRRELVM v); +SQInteger _stream_tell(HSQUIRRELVM v); +SQInteger _stream_len(HSQUIRRELVM v); +SQInteger _stream_eos(HSQUIRRELVM v); +SQInteger _stream_flush(HSQUIRRELVM v); + +#define _DECL_STREAM_FUNC(name,nparams,typecheck) {_SC(#name),_stream_##name,nparams,typecheck} +SQRESULT declare_stream(HSQUIRRELVM v,const SQChar* name,SQUserPointer typetag,const SQChar* reg_name,const SQRegFunction *methods,const SQRegFunction *globals); +#endif /*_SQSTD_STREAM_H_*/ diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp new file mode 100644 index 0000000000..919bd9e924 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdstring.cpp @@ -0,0 +1,538 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FORMAT_LEN 20 +#define MAX_WFORMAT_LEN 3 +#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar)) + +static SQBool isfmtchr(SQChar ch) +{ + switch(ch) { + case '-': case '+': case ' ': case '#': case '0': return SQTrue; + } + return SQFalse; +} + +static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width) +{ + SQChar *dummy; + SQChar swidth[MAX_WFORMAT_LEN]; + SQInteger wc = 0; + SQInteger start = n; + fmt[0] = '%'; + while (isfmtchr(src[n])) n++; + while (scisdigit(src[n])) { + swidth[wc] = src[n]; + n++; + wc++; + if(wc>=MAX_WFORMAT_LEN) + return sq_throwerror(v,_SC("width format too long")); + } + swidth[wc] = '\0'; + if(wc > 0) { + width = scstrtol(swidth,&dummy,10); + } + else + width = 0; + if (src[n] == '.') { + n++; + + wc = 0; + while (scisdigit(src[n])) { + swidth[wc] = src[n]; + n++; + wc++; + if(wc>=MAX_WFORMAT_LEN) + return sq_throwerror(v,_SC("precision format too long")); + } + swidth[wc] = '\0'; + if(wc > 0) { + width += scstrtol(swidth,&dummy,10); + + } + } + if (n-start > MAX_FORMAT_LEN ) + return sq_throwerror(v,_SC("format too long")); + memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar)); + fmt[(n-start)+2] = '\0'; + return n; +} + +SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output) +{ + const SQChar *format; + SQChar *dest; + SQChar fmt[MAX_FORMAT_LEN]; + const SQRESULT res = sq_getstring(v,nformatstringidx,&format); + if (SQ_FAILED(res)) { + return res; // propagate the error + } + SQInteger format_size = sq_getsize(v,nformatstringidx); + SQInteger allocated = (format_size+2)*sizeof(SQChar); + dest = sq_getscratchpad(v,allocated); + SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0; + //while(format[n] != '\0') + while(n < format_size) + { + if(format[n] != '%') { + assert(i < allocated); + dest[i++] = format[n]; + n++; + } + else if(format[n+1] == '%') { //handles %% + dest[i++] = '%'; + n += 2; + } + else { + n++; + if( nparam > sq_gettop(v) ) + return sq_throwerror(v,_SC("not enough parameters for the given format string")); + n = validate_format(v,fmt,format,n,w); + if(n < 0) return -1; + SQInteger addlen = 0; + SQInteger valtype = 0; + const SQChar *ts = NULL; + SQInteger ti = 0; + SQFloat tf = 0; + switch(format[n]) { + case 's': + if(SQ_FAILED(sq_getstring(v,nparam,&ts))) + return sq_throwerror(v,_SC("string expected for the specified format")); + addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar)); + valtype = 's'; + break; + case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': +#ifdef _SQ64 + { + size_t flen = scstrlen(fmt); + SQInteger fpos = flen - 1; + SQChar f = fmt[fpos]; + const SQChar *prec = (const SQChar *)_PRINT_INT_PREC; + while(*prec != _SC('\0')) { + fmt[fpos++] = *prec++; + } + fmt[fpos++] = f; + fmt[fpos++] = _SC('\0'); + } +#endif + case 'c': + if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) + return sq_throwerror(v,_SC("integer expected for the specified format")); + addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar)); + valtype = 'i'; + break; + case 'f': case 'g': case 'G': case 'e': case 'E': + if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) + return sq_throwerror(v,_SC("float expected for the specified format")); + addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar)); + valtype = 'f'; + break; + default: + return sq_throwerror(v,_SC("invalid format")); + } + n++; + allocated += addlen + sizeof(SQChar); + dest = sq_getscratchpad(v,allocated); + switch(valtype) { + case 's': i += scsprintf(&dest[i],allocated,fmt,ts); break; + case 'i': i += scsprintf(&dest[i],allocated,fmt,ti); break; + case 'f': i += scsprintf(&dest[i],allocated,fmt,tf); break; + }; + nparam ++; + } + } + *outlen = i; + dest[i] = '\0'; + *output = dest; + return SQ_OK; +} + +void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...) +{ + SQInteger n=256; + va_list args; +begin: + va_start(args,s); + SQChar *b=sq_getscratchpad(v,n); + SQInteger r=scvsprintf(b,n,s,args); + va_end(args); + if (r>=n) { + n=r+1;//required+null + goto begin; + } else if (r<0) { + sq_pushnull(v); + } else { + sq_pushstring(v,b,r); + } +} + +static SQInteger _string_printf(HSQUIRRELVM v) +{ + SQChar *dest = NULL; + SQInteger length = 0; + if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) + return -1; + + SQPRINTFUNCTION printfunc = sq_getprintfunc(v); + if(printfunc) printfunc(v,_SC("%s"),dest); + + return 0; +} + +static SQInteger _string_format(HSQUIRRELVM v) +{ + SQChar *dest = NULL; + SQInteger length = 0; + if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) + return -1; + sq_pushstring(v,dest,length); + return 1; +} + +static void __strip_l(const SQChar *str,const SQChar **start) +{ + const SQChar *t = str; + while(((*t) != '\0') && scisspace(*t)){ t++; } + *start = t; +} + +static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end) +{ + if(len == 0) { + *end = str; + return; + } + const SQChar *t = &str[len-1]; + while(t >= str && scisspace(*t)) { t--; } + *end = t + 1; +} + +static SQInteger _string_strip(HSQUIRRELVM v) +{ + const SQChar *str,*start,*end; + sq_getstring(v,2,&str); + SQInteger len = sq_getsize(v,2); + __strip_l(str,&start); + __strip_r(str,len,&end); + sq_pushstring(v,start,end - start); + return 1; +} + +static SQInteger _string_lstrip(HSQUIRRELVM v) +{ + const SQChar *str,*start; + sq_getstring(v,2,&str); + __strip_l(str,&start); + sq_pushstring(v,start,-1); + return 1; +} + +static SQInteger _string_rstrip(HSQUIRRELVM v) +{ + const SQChar *str,*end; + sq_getstring(v,2,&str); + SQInteger len = sq_getsize(v,2); + __strip_r(str,len,&end); + sq_pushstring(v,str,end - str); + return 1; +} + +static SQInteger _string_split(HSQUIRRELVM v) +{ + const SQChar *str,*seps; + SQChar *stemp; + sq_getstring(v,2,&str); + sq_getstring(v,3,&seps); + SQInteger sepsize = sq_getsize(v,3); + if(sepsize == 0) return sq_throwerror(v,_SC("empty separators string")); + SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar); + stemp = sq_getscratchpad(v,memsize); + memcpy(stemp,str,memsize); + SQChar *start = stemp; + SQChar *end = stemp; + sq_newarray(v,0); + while(*end != '\0') + { + SQChar cur = *end; + for(SQInteger i = 0; i < sepsize; i++) + { + if(cur == seps[i]) + { + *end = 0; + sq_pushstring(v,start,-1); + sq_arrayappend(v,-2); + start = end + 1; + break; + } + } + end++; + } + if(end != start) + { + sq_pushstring(v,start,-1); + sq_arrayappend(v,-2); + } + return 1; +} + +static SQInteger _string_escape(HSQUIRRELVM v) +{ + const SQChar *str; + SQChar *dest,*resstr; + SQInteger size; + sq_getstring(v,2,&str); + size = sq_getsize(v,2); + if(size == 0) { + sq_push(v,2); + return 1; + } +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 + const SQChar *escpat = _SC("\\x%04x"); + const SQInteger maxescsize = 6; +#else //WCHAR_SIZE == 4 + const SQChar *escpat = _SC("\\x%08x"); + const SQInteger maxescsize = 10; +#endif +#else + const SQChar *escpat = _SC("\\x%02x"); + const SQInteger maxescsize = 4; +#endif + SQInteger destcharsize = (size * maxescsize); //assumes every char could be escaped + resstr = dest = (SQChar *)sq_getscratchpad(v,destcharsize * sizeof(SQChar)); + SQChar c; + SQChar escch; + SQInteger escaped = 0; + for(int n = 0; n < size; n++){ + c = *str++; + escch = 0; + if(scisprint(c) || c == 0) { + switch(c) { + case '\a': escch = 'a'; break; + case '\b': escch = 'b'; break; + case '\t': escch = 't'; break; + case '\n': escch = 'n'; break; + case '\v': escch = 'v'; break; + case '\f': escch = 'f'; break; + case '\r': escch = 'r'; break; + case '\\': escch = '\\'; break; + case '\"': escch = '\"'; break; + case '\'': escch = '\''; break; + case 0: escch = '0'; break; + } + if(escch) { + *dest++ = '\\'; + *dest++ = escch; + escaped++; + } + else { + *dest++ = c; + } + } + else { + + dest += scsprintf(dest, destcharsize, escpat, c); + escaped++; + } + } + + if(escaped) { + sq_pushstring(v,resstr,dest - resstr); + } + else { + sq_push(v,2); //nothing escaped + } + return 1; +} + +static SQInteger _string_startswith(HSQUIRRELVM v) +{ + const SQChar *str,*cmp; + sq_getstring(v,2,&str); + sq_getstring(v,3,&cmp); + SQInteger len = sq_getsize(v,2); + SQInteger cmplen = sq_getsize(v,3); + SQBool ret = SQFalse; + if(cmplen <= len) { + ret = memcmp(str,cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse; + } + sq_pushbool(v,ret); + return 1; +} + +static SQInteger _string_endswith(HSQUIRRELVM v) +{ + const SQChar *str,*cmp; + sq_getstring(v,2,&str); + sq_getstring(v,3,&cmp); + SQInteger len = sq_getsize(v,2); + SQInteger cmplen = sq_getsize(v,3); + SQBool ret = SQFalse; + if(cmplen <= len) { + ret = memcmp(&str[len - cmplen],cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse; + } + sq_pushbool(v,ret); + return 1; +} + +#define SETUP_REX(v) \ + SQRex *self = NULL; \ + sq_getinstanceup(v,1,(SQUserPointer *)&self,0); + +static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) +{ + SQRex *self = ((SQRex *)p); + sqstd_rex_free(self); + return 1; +} + +static SQInteger _regexp_match(HSQUIRRELVM v) +{ + SETUP_REX(v); + const SQChar *str; + sq_getstring(v,2,&str); + if(sqstd_rex_match(self,str) == SQTrue) + { + sq_pushbool(v,SQTrue); + return 1; + } + sq_pushbool(v,SQFalse); + return 1; +} + +static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end) +{ + sq_newtable(v); + sq_pushstring(v,_SC("begin"),-1); + sq_pushinteger(v,begin - str); + sq_rawset(v,-3); + sq_pushstring(v,_SC("end"),-1); + sq_pushinteger(v,end - str); + sq_rawset(v,-3); +} + +static SQInteger _regexp_search(HSQUIRRELVM v) +{ + SETUP_REX(v); + const SQChar *str,*begin,*end; + SQInteger start = 0; + sq_getstring(v,2,&str); + if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); + if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { + _addrexmatch(v,str,begin,end); + return 1; + } + return 0; +} + +static SQInteger _regexp_capture(HSQUIRRELVM v) +{ + SETUP_REX(v); + const SQChar *str,*begin,*end; + SQInteger start = 0; + sq_getstring(v,2,&str); + if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); + if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { + SQInteger n = sqstd_rex_getsubexpcount(self); + SQRexMatch match; + sq_newarray(v,0); + for(SQInteger i = 0;i < n; i++) { + sqstd_rex_getsubexp(self,i,&match); + if(match.len > 0) + _addrexmatch(v,str,match.begin,match.begin+match.len); + else + _addrexmatch(v,str,str,str); //empty match + sq_arrayappend(v,-2); + } + return 1; + } + return 0; +} + +static SQInteger _regexp_subexpcount(HSQUIRRELVM v) +{ + SETUP_REX(v); + sq_pushinteger(v,sqstd_rex_getsubexpcount(self)); + return 1; +} + +static SQInteger _regexp_constructor(HSQUIRRELVM v) +{ + const SQChar *error,*pattern; + sq_getstring(v,2,&pattern); + SQRex *rex = sqstd_rex_compile(pattern,&error); + if(!rex) return sq_throwerror(v,error); + sq_setinstanceup(v,1,rex); + sq_setreleasehook(v,1,_rexobj_releasehook); + return 0; +} + +static SQInteger _regexp__typeof(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("regexp"),-1); + return 1; +} + +#define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask} +static const SQRegFunction rexobj_funcs[]={ + _DECL_REX_FUNC(constructor,2,_SC(".s")), + _DECL_REX_FUNC(search,-2,_SC("xsn")), + _DECL_REX_FUNC(match,2,_SC("xs")), + _DECL_REX_FUNC(capture,-2,_SC("xsn")), + _DECL_REX_FUNC(subexpcount,1,_SC("x")), + _DECL_REX_FUNC(_typeof,1,_SC("x")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_REX_FUNC + +#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask} +static const SQRegFunction stringlib_funcs[]={ + _DECL_FUNC(format,-2,_SC(".s")), + _DECL_FUNC(printf,-2,_SC(".s")), + _DECL_FUNC(strip,2,_SC(".s")), + _DECL_FUNC(lstrip,2,_SC(".s")), + _DECL_FUNC(rstrip,2,_SC(".s")), + _DECL_FUNC(split,3,_SC(".ss")), + _DECL_FUNC(escape,2,_SC(".s")), + _DECL_FUNC(startswith,3,_SC(".ss")), + _DECL_FUNC(endswith,3,_SC(".ss")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_FUNC + + +SQInteger sqstd_register_stringlib(HSQUIRRELVM v) +{ + sq_pushstring(v,_SC("regexp"),-1); + sq_newclass(v,SQFalse); + SQInteger i = 0; + while(rexobj_funcs[i].name != 0) { + const SQRegFunction &f = rexobj_funcs[i]; + sq_pushstring(v,f.name,-1); + sq_newclosure(v,f.f,0); + sq_setparamscheck(v,f.nparamscheck,f.typemask); + sq_setnativeclosurename(v,-1,f.name); + sq_newslot(v,-3,SQFalse); + i++; + } + sq_newslot(v,-3,SQFalse); + + i = 0; + while(stringlib_funcs[i].name!=0) + { + sq_pushstring(v,stringlib_funcs[i].name,-1); + sq_newclosure(v,stringlib_funcs[i].f,0); + sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask); + sq_setnativeclosurename(v,-1,stringlib_funcs[i].name); + sq_newslot(v,-3,SQFalse); + i++; + } + return 1; +} diff --git a/sp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp b/sp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp new file mode 100644 index 0000000000..d326be1af9 --- /dev/null +++ b/sp/src/vscript/squirrel/sqstdlib/sqstdsystem.cpp @@ -0,0 +1,154 @@ +/* see copyright notice in squirrel.h */ +#include +#include +#include +#include +#include + +#ifdef SQUNICODE +#include +#define scgetenv _wgetenv +#define scsystem _wsystem +#define scasctime _wasctime +#define scremove _wremove +#define screname _wrename +#else +#define scgetenv getenv +#define scsystem system +#define scasctime asctime +#define scremove remove +#define screname rename +#endif +#ifdef IOS + #include + extern char **environ; +#endif + +static SQInteger _system_getenv(HSQUIRRELVM v) +{ + const SQChar *s; + if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ + sq_pushstring(v,scgetenv(s),-1); + return 1; + } + return 0; +} + +static SQInteger _system_system(HSQUIRRELVM v) +{ + const SQChar *s; + if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ + #ifdef IOS + pid_t pid; + posix_spawn(&pid, s, NULL, NULL, NULL, environ); + sq_pushinteger(v, 0); + #else + sq_pushinteger(v,scsystem(s)); + #endif + return 1; + } + return sq_throwerror(v,_SC("wrong param")); +} + +static SQInteger _system_clock(HSQUIRRELVM v) +{ + sq_pushfloat(v,((SQFloat)clock())/(SQFloat)CLOCKS_PER_SEC); + return 1; +} + +static SQInteger _system_time(HSQUIRRELVM v) +{ + SQInteger t = (SQInteger)time(NULL); + sq_pushinteger(v,t); + return 1; +} + +static SQInteger _system_remove(HSQUIRRELVM v) +{ + const SQChar *s; + sq_getstring(v,2,&s); + if(scremove(s)==-1) + return sq_throwerror(v,_SC("remove() failed")); + return 0; +} + +static SQInteger _system_rename(HSQUIRRELVM v) +{ + const SQChar *oldn,*newn; + sq_getstring(v,2,&oldn); + sq_getstring(v,3,&newn); + if(screname(oldn,newn)==-1) + return sq_throwerror(v,_SC("rename() failed")); + return 0; +} + +static void _set_integer_slot(HSQUIRRELVM v,const SQChar *name,SQInteger val) +{ + sq_pushstring(v,name,-1); + sq_pushinteger(v,val); + sq_rawset(v,-3); +} + +static SQInteger _system_date(HSQUIRRELVM v) +{ + time_t t; + SQInteger it; + SQInteger format = 'l'; + if(sq_gettop(v) > 1) { + sq_getinteger(v,2,&it); + t = it; + if(sq_gettop(v) > 2) { + sq_getinteger(v,3,(SQInteger*)&format); + } + } + else { + time(&t); + } + tm *date; + if(format == 'u') + date = gmtime(&t); + else + date = localtime(&t); + if(!date) + return sq_throwerror(v,_SC("crt api failure")); + sq_newtable(v); + _set_integer_slot(v, _SC("sec"), date->tm_sec); + _set_integer_slot(v, _SC("min"), date->tm_min); + _set_integer_slot(v, _SC("hour"), date->tm_hour); + _set_integer_slot(v, _SC("day"), date->tm_mday); + _set_integer_slot(v, _SC("month"), date->tm_mon); + _set_integer_slot(v, _SC("year"), date->tm_year+1900); + _set_integer_slot(v, _SC("wday"), date->tm_wday); + _set_integer_slot(v, _SC("yday"), date->tm_yday); + return 1; +} + + + +#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_system_##name,nparams,pmask} +static const SQRegFunction systemlib_funcs[]={ + _DECL_FUNC(getenv,2,_SC(".s")), + _DECL_FUNC(system,2,_SC(".s")), + _DECL_FUNC(clock,0,NULL), + _DECL_FUNC(time,1,NULL), + _DECL_FUNC(date,-1,_SC(".nn")), + _DECL_FUNC(remove,2,_SC(".s")), + _DECL_FUNC(rename,3,_SC(".ss")), + {NULL,(SQFUNCTION)0,0,NULL} +}; +#undef _DECL_FUNC + +SQInteger sqstd_register_systemlib(HSQUIRRELVM v) +{ + SQInteger i=0; + while(systemlib_funcs[i].name!=0) + { + sq_pushstring(v,systemlib_funcs[i].name,-1); + sq_newclosure(v,systemlib_funcs[i].f,0); + sq_setparamscheck(v,systemlib_funcs[i].nparamscheck,systemlib_funcs[i].typemask); + sq_setnativeclosurename(v,-1,systemlib_funcs[i].name); + sq_newslot(v,-3,SQFalse); + i++; + } + return 1; +} diff --git a/sp/src/vscript/squirrel/squirrel-config.cmake.in b/sp/src/vscript/squirrel/squirrel-config.cmake.in new file mode 100644 index 0000000000..6ed4a8aa43 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel-config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/squirrel-config-version.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/squirrel-targets.cmake") diff --git a/sp/src/vscript/squirrel/squirrel.dsw b/sp/src/vscript/squirrel/squirrel.dsw new file mode 100644 index 0000000000..721509106c --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel.dsw @@ -0,0 +1,77 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "sq"=.\sq\sq.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + . + end source code control +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name sqlibs + End Project Dependency + Begin Project Dependency + Project_Dep_Name squirrel + End Project Dependency + Begin Project Dependency + Project_Dep_Name sqstdlib + End Project Dependency +}}} + +############################################################################### + +Project: "sqstdlib"=.\sqstdlib\sqstdlib.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/squirrel", HAAAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "squirrel"=.\squirrel\squirrel.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/squirrel", HAAAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/squirrel", HAAAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/sp/src/vscript/squirrel/squirrel/CMakeLists.txt b/sp/src/vscript/squirrel/squirrel/CMakeLists.txt new file mode 100644 index 0000000000..f96b958444 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/CMakeLists.txt @@ -0,0 +1,55 @@ +set(SQUIRREL_SRC sqapi.cpp + sqbaselib.cpp + sqclass.cpp + sqcompiler.cpp + sqdebug.cpp + sqfuncstate.cpp + sqlexer.cpp + sqmem.cpp + sqobject.cpp + sqstate.cpp + sqtable.cpp + sqvm.cpp) + +if(NOT DISABLE_DYNAMIC) + add_library(squirrel SHARED ${SQUIRREL_SRC}) + add_library(squirrel::squirrel ALIAS squirrel) + set_property(TARGET squirrel PROPERTY EXPORT_NAME squirrel) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS squirrel EXPORT squirrel + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Libraries + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries NAMELINK_SKIP + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries + ) + install(TARGETS squirrel EXPORT squirrel + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development NAMELINK_ONLY + ) + endif() + target_include_directories(squirrel PUBLIC + "$" + "$" + ) +endif() + +if(NOT DISABLE_STATIC) + add_library(squirrel_static STATIC ${SQUIRREL_SRC}) + add_library(squirrel::squirrel_static ALIAS squirrel_static) + set_property(TARGET squirrel_static PROPERTY EXPORT_NAME squirrel_static) + if(NOT SQ_DISABLE_INSTALLER) + install(TARGETS squirrel_static EXPORT squirrel ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development) + endif() + target_include_directories(squirrel_static PUBLIC + "$" + "$" + ) +endif() + +if(LONG_OUTPUT_NAMES) + if(NOT DISABLE_DYNAMIC) + set_target_properties(squirrel PROPERTIES OUTPUT_NAME squirrel3) + endif() + + if(NOT DISABLE_STATIC) + set_target_properties(squirrel_static PROPERTIES OUTPUT_NAME squirrel3_static) + endif() +endif() diff --git a/sp/src/vscript/squirrel/squirrel/Makefile b/sp/src/vscript/squirrel/squirrel/Makefile new file mode 100644 index 0000000000..2422a834f2 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/Makefile @@ -0,0 +1,53 @@ +SQUIRREL= .. + + +CC?= gcc +OUT?= $(SQUIRREL)/lib/libsquirrel.a +INCZ?= -I$(SQUIRREL)/include -I. -Iinclude +DEFS= $(CC_EXTRA_FLAGS) +LIB= + +OBJS= \ + sqapi.o \ + sqbaselib.o \ + sqfuncstate.o \ + sqdebug.o \ + sqlexer.o \ + sqobject.o \ + sqcompiler.o \ + sqstate.o \ + sqtable.o \ + sqmem.o \ + sqvm.o \ + sqclass.o + +SRCS= \ + sqapi.cpp \ + sqbaselib.cpp \ + sqfuncstate.cpp \ + sqdebug.cpp \ + sqlexer.cpp \ + sqobject.cpp \ + sqcompiler.cpp \ + sqstate.cpp \ + sqtable.cpp \ + sqmem.cpp \ + sqvm.cpp \ + sqclass.cpp + + + +sq32: + $(CC) -O2 -fno-exceptions -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sqprof: + $(CC) -O2 -pg -fno-exceptions -fno-rtti -pie -gstabs -g3 -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o + +sq64: + $(CC) -O2 -m64 -D_SQ64 -fno-exceptions -fno-rtti -Wall -fno-strict-aliasing -c $(SRCS) $(INCZ) $(DEFS) + ar rc $(OUT) *.o + rm *.o diff --git a/sp/src/vscript/squirrel/squirrel/sqapi.cpp b/sp/src/vscript/squirrel/squirrel/sqapi.cpp new file mode 100644 index 0000000000..a3221503da --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqapi.cpp @@ -0,0 +1,1631 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqstring.h" +#include "sqtable.h" +#include "sqarray.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "squserdata.h" +#include "sqcompiler.h" +#include "sqfuncstate.h" +#include "sqclass.h" + +static bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o) +{ + *o = &stack_get(v,idx); + if(sq_type(**o) != type){ + SQObjectPtr oval = v->PrintObjVal(**o); + v->Raise_Error(_SC("wrong argument type, expected '%s' got '%.50s'"),IdType2Name(type),_stringval(oval)); + return false; + } + return true; +} + +#define _GETSAFE_OBJ(v,idx,type,o) { if(!sq_aux_gettypedarg(v,idx,type,&o)) return SQ_ERROR; } + +#define sq_aux_paramscheck(v,count) \ +{ \ + if(sq_gettop(v) < count){ v->Raise_Error(_SC("not enough params in the stack")); return SQ_ERROR; }\ +} + + +SQInteger sq_aux_invalidtype(HSQUIRRELVM v,SQObjectType type) +{ + SQUnsignedInteger buf_size = 100 *sizeof(SQChar); + scsprintf(_ss(v)->GetScratchPad(buf_size), buf_size, _SC("unexpected type %s"), IdType2Name(type)); + return sq_throwerror(v, _ss(v)->GetScratchPad(-1)); +} + +HSQUIRRELVM sq_open(SQInteger initialstacksize) +{ + SQSharedState *ss; + SQVM *v; + sq_new(ss, SQSharedState); + ss->Init(); + v = (SQVM *)SQ_MALLOC(sizeof(SQVM)); + new (v) SQVM(ss); + ss->_root_vm = v; + if(v->Init(NULL, initialstacksize)) { + return v; + } else { + sq_delete(v, SQVM); + return NULL; + } + return v; +} + +HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize) +{ + SQSharedState *ss; + SQVM *v; + ss=_ss(friendvm); + + v= (SQVM *)SQ_MALLOC(sizeof(SQVM)); + new (v) SQVM(ss); + + if(v->Init(friendvm, initialstacksize)) { + friendvm->Push(v); + return v; + } else { + sq_delete(v, SQVM); + return NULL; + } +} + +SQInteger sq_getvmstate(HSQUIRRELVM v) +{ + if(v->_suspended) + return SQ_VMSTATE_SUSPENDED; + else { + if(v->_callsstacksize != 0) return SQ_VMSTATE_RUNNING; + else return SQ_VMSTATE_IDLE; + } +} + +void sq_seterrorhandler(HSQUIRRELVM v) +{ + SQObject o = stack_get(v, -1); + if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { + v->_errorhandler = o; + v->Pop(); + } +} + +void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook) +{ + v->_debughook_native = hook; + v->_debughook_closure.Null(); + v->_debughook = hook?true:false; +} + +void sq_setdebughook(HSQUIRRELVM v) +{ + SQObject o = stack_get(v,-1); + if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { + v->_debughook_closure = o; + v->_debughook_native = NULL; + v->_debughook = !sq_isnull(o); + v->Pop(); + } +} + +void sq_close(HSQUIRRELVM v) +{ + SQSharedState *ss = _ss(v); + _thread(ss->_root_vm)->Finalize(); + sq_delete(ss, SQSharedState); +} + +SQInteger sq_getversion() +{ + return SQUIRREL_VERSION_NUMBER; +} + +SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror) +{ + SQObjectPtr o; +#ifndef NO_COMPILER + if(Compile(v, read, p, sourcename, o, raiseerror?true:false, _ss(v)->_debuginfo)) { + v->Push(SQClosure::Create(_ss(v), _funcproto(o), _table(v->_roottable)->GetWeakRef(OT_TABLE))); + return SQ_OK; + } + return SQ_ERROR; +#else + return sq_throwerror(v,_SC("this is a no compiler build")); +#endif +} + +void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable) +{ + _ss(v)->_debuginfo = enable?true:false; +} + +void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) +{ + _ss(v)->_notifyallexceptions = enable?true:false; +} + +void sq_addref(HSQUIRRELVM v,HSQOBJECT *po) +{ + if(!ISREFCOUNTED(sq_type(*po))) return; +#ifdef NO_GARBAGE_COLLECTOR + __AddRef(po->_type,po->_unVal); +#else + _ss(v)->_refs_table.AddRef(*po); +#endif +} + +SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po) +{ + if(!ISREFCOUNTED(sq_type(*po))) return 0; +#ifdef NO_GARBAGE_COLLECTOR + return po->_unVal.pRefCounted->_uiRef; +#else + return _ss(v)->_refs_table.GetRefCount(*po); +#endif +} + +SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po) +{ + if(!ISREFCOUNTED(sq_type(*po))) return SQTrue; +#ifdef NO_GARBAGE_COLLECTOR + bool ret = (po->_unVal.pRefCounted->_uiRef <= 1) ? SQTrue : SQFalse; + __Release(po->_type,po->_unVal); + return ret; //the ret val doesn't work(and cannot be fixed) +#else + return _ss(v)->_refs_table.Release(*po); +#endif +} + +SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM SQ_UNUSED_ARG(v), const HSQOBJECT *po) +{ + if (!ISREFCOUNTED(sq_type(*po))) return 0; + return po->_unVal.pRefCounted->_uiRef; +} + +const SQChar *sq_objtostring(const HSQOBJECT *o) +{ + if(sq_type(*o) == OT_STRING) { + return _stringval(*o); + } + return NULL; +} + +SQInteger sq_objtointeger(const HSQOBJECT *o) +{ + if(sq_isnumeric(*o)) { + return tointeger(*o); + } + return 0; +} + +SQFloat sq_objtofloat(const HSQOBJECT *o) +{ + if(sq_isnumeric(*o)) { + return tofloat(*o); + } + return 0; +} + +SQBool sq_objtobool(const HSQOBJECT *o) +{ + if(sq_isbool(*o)) { + return _integer(*o); + } + return SQFalse; +} + +SQUserPointer sq_objtouserpointer(const HSQOBJECT *o) +{ + if(sq_isuserpointer(*o)) { + return _userpointer(*o); + } + return 0; +} + +void sq_pushnull(HSQUIRRELVM v) +{ + v->PushNull(); +} + +void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len) +{ + if(s) + v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len))); + else v->PushNull(); +} + +void sq_pushinteger(HSQUIRRELVM v,SQInteger n) +{ + v->Push(n); +} + +void sq_pushbool(HSQUIRRELVM v,SQBool b) +{ + v->Push(b?true:false); +} + +void sq_pushfloat(HSQUIRRELVM v,SQFloat n) +{ + v->Push(n); +} + +void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p) +{ + v->Push(p); +} + +void sq_pushthread(HSQUIRRELVM v, HSQUIRRELVM thread) +{ + v->Push(thread); +} + +SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size) +{ + SQUserData *ud = SQUserData::Create(_ss(v), size + SQ_ALIGNMENT); + v->Push(ud); + return (SQUserPointer)sq_aligning(ud + 1); +} + +void sq_newtable(HSQUIRRELVM v) +{ + v->Push(SQTable::Create(_ss(v), 0)); +} + +void sq_newtableex(HSQUIRRELVM v,SQInteger initialcapacity) +{ + v->Push(SQTable::Create(_ss(v), initialcapacity)); +} + +void sq_newarray(HSQUIRRELVM v,SQInteger size) +{ + v->Push(SQArray::Create(_ss(v), size)); +} + +SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase) +{ + SQClass *baseclass = NULL; + if(hasbase) { + SQObjectPtr &base = stack_get(v,-1); + if(sq_type(base) != OT_CLASS) + return sq_throwerror(v,_SC("invalid base type")); + baseclass = _class(base); + } + SQClass *newclass = SQClass::Create(_ss(v), baseclass); + if(baseclass) v->Pop(); + v->Push(newclass); + return SQ_OK; +} + +SQBool sq_instanceof(HSQUIRRELVM v) +{ + SQObjectPtr &inst = stack_get(v,-1); + SQObjectPtr &cl = stack_get(v,-2); + if(sq_type(inst) != OT_INSTANCE || sq_type(cl) != OT_CLASS) + return sq_throwerror(v,_SC("invalid param type")); + return _instance(inst)->InstanceOf(_class(cl))?SQTrue:SQFalse; +} + +SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx) +{ + sq_aux_paramscheck(v,2); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + _array(*arr)->Append(v->GetUp(-1)); + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + if(_array(*arr)->Size() > 0) { + if(pushval != 0){ v->Push(_array(*arr)->Top()); } + _array(*arr)->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("empty array")); +} + +SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize) +{ + sq_aux_paramscheck(v,1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + if(newsize >= 0) { + _array(*arr)->Resize(newsize); + return SQ_OK; + } + return sq_throwerror(v,_SC("negative size")); +} + + +SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *o; + _GETSAFE_OBJ(v, idx, OT_ARRAY,o); + SQArray *arr = _array(*o); + if(arr->Size() > 0) { + SQObjectPtr t; + SQInteger size = arr->Size(); + SQInteger n = size >> 1; size -= 1; + for(SQInteger i = 0; i < n; i++) { + t = arr->_values[i]; + arr->_values[i] = arr->_values[size-i]; + arr->_values[size-i] = t; + } + return SQ_OK; + } + return SQ_OK; +} + +SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + return _array(*arr)->Remove(itemidx) ? SQ_OK : sq_throwerror(v,_SC("index out of range")); +} + +SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos) +{ + sq_aux_paramscheck(v, 1); + SQObjectPtr *arr; + _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); + SQRESULT ret = _array(*arr)->Insert(destpos, v->GetUp(-1)) ? SQ_OK : sq_throwerror(v,_SC("index out of range")); + v->Pop(); + return ret; +} + +void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars) +{ + SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func,nfreevars); + nc->_nparamscheck = 0; + for(SQUnsignedInteger i = 0; i < nfreevars; i++) { + nc->_outervalues[i] = v->Top(); + v->Pop(); + } + v->Push(SQObjectPtr(nc)); +} + +SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars) +{ + SQObject o = stack_get(v, idx); + if(sq_type(o) == OT_CLOSURE) { + SQClosure *c = _closure(o); + SQFunctionProto *proto = c->_function; + *nparams = proto->_nparameters; + *nfreevars = proto->_noutervalues; + return SQ_OK; + } + else if(sq_type(o) == OT_NATIVECLOSURE) + { + SQNativeClosure *c = _nativeclosure(o); + *nparams = c->_nparamscheck; + *nfreevars = (SQInteger)c->_noutervalues; + return SQ_OK; + } + return sq_throwerror(v,_SC("the object is not a closure")); +} + +SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name) +{ + SQObject o = stack_get(v, idx); + if(sq_isnativeclosure(o)) { + SQNativeClosure *nc = _nativeclosure(o); + nc->_name = SQString::Create(_ss(v),name); + return SQ_OK; + } + return sq_throwerror(v,_SC("the object is not a nativeclosure")); +} + +SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask) +{ + SQObject o = stack_get(v, -1); + if(!sq_isnativeclosure(o)) + return sq_throwerror(v, _SC("native closure expected")); + SQNativeClosure *nc = _nativeclosure(o); + nc->_nparamscheck = nparamscheck; + if(typemask) { + SQIntVec res; + if(!CompileTypemask(res, typemask)) + return sq_throwerror(v, _SC("invalid typemask")); + nc->_typecheck.copy(res); + } + else { + nc->_typecheck.resize(0); + } + if(nparamscheck == SQ_MATCHTYPEMASKSTRING) { + nc->_nparamscheck = nc->_typecheck.size(); + } + return SQ_OK; +} + +SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + if(!sq_isnativeclosure(o) && + !sq_isclosure(o)) + return sq_throwerror(v,_SC("the target is not a closure")); + SQObjectPtr &env = stack_get(v,-1); + if(!sq_istable(env) && + !sq_isarray(env) && + !sq_isclass(env) && + !sq_isinstance(env)) + return sq_throwerror(v,_SC("invalid environment")); + SQWeakRef *w = _refcounted(env)->GetWeakRef(sq_type(env)); + SQObjectPtr ret; + if(sq_isclosure(o)) { + SQClosure *c = _closure(o)->Clone(); + __ObjRelease(c->_env); + c->_env = w; + __ObjAddRef(c->_env); + if(_closure(o)->_base) { + c->_base = _closure(o)->_base; + __ObjAddRef(c->_base); + } + ret = c; + } + else { //then must be a native closure + SQNativeClosure *c = _nativeclosure(o)->Clone(); + __ObjRelease(c->_env); + c->_env = w; + __ObjAddRef(c->_env); + ret = c; + } + v->Pop(); + v->Push(ret); + return SQ_OK; +} + +SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + if(!sq_isnativeclosure(o) && + !sq_isclosure(o)) + return sq_throwerror(v,_SC("the target is not a closure")); + if(sq_isnativeclosure(o)) + { + v->Push(_nativeclosure(o)->_name); + } + else { //closure + v->Push(_closure(o)->_function->_name); + } + return SQ_OK; +} + +SQRESULT sq_setclosureroot(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &c = stack_get(v,idx); + SQObject o = stack_get(v, -1); + if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected")); + if(sq_istable(o)) { + _closure(c)->SetRoot(_table(o)->GetWeakRef(OT_TABLE)); + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("invalid type")); +} + +SQRESULT sq_getclosureroot(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &c = stack_get(v,idx); + if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected")); + v->Push(_closure(c)->_root->_obj); + return SQ_OK; +} + +SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx) +{ + SQObject &o=stack_get(v,idx); + switch(sq_type(o)) { + case OT_TABLE: _table(o)->Clear(); break; + case OT_ARRAY: _array(o)->Resize(0); break; + default: + return sq_throwerror(v, _SC("clear only works on table and array")); + break; + + } + return SQ_OK; +} + +void sq_pushroottable(HSQUIRRELVM v) +{ + v->Push(v->_roottable); +} + +void sq_pushregistrytable(HSQUIRRELVM v) +{ + v->Push(_ss(v)->_registry); +} + +void sq_pushconsttable(HSQUIRRELVM v) +{ + v->Push(_ss(v)->_consts); +} + +SQRESULT sq_setroottable(HSQUIRRELVM v) +{ + SQObject o = stack_get(v, -1); + if(sq_istable(o) || sq_isnull(o)) { + v->_roottable = o; + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("invalid type")); +} + +SQRESULT sq_setconsttable(HSQUIRRELVM v) +{ + SQObject o = stack_get(v, -1); + if(sq_istable(o)) { + _ss(v)->_consts = o; + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v, _SC("invalid type, expected table")); +} + +void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p) +{ + v->_foreignptr = p; +} + +SQUserPointer sq_getforeignptr(HSQUIRRELVM v) +{ + return v->_foreignptr; +} + +void sq_setsharedforeignptr(HSQUIRRELVM v,SQUserPointer p) +{ + _ss(v)->_foreignptr = p; +} + +SQUserPointer sq_getsharedforeignptr(HSQUIRRELVM v) +{ + return _ss(v)->_foreignptr; +} + +void sq_setvmreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) +{ + v->_releasehook = hook; +} + +SQRELEASEHOOK sq_getvmreleasehook(HSQUIRRELVM v) +{ + return v->_releasehook; +} + +void sq_setsharedreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) +{ + _ss(v)->_releasehook = hook; +} + +SQRELEASEHOOK sq_getsharedreleasehook(HSQUIRRELVM v) +{ + return _ss(v)->_releasehook; +} + +void sq_push(HSQUIRRELVM v,SQInteger idx) +{ + v->Push(stack_get(v, idx)); +} + +SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx) +{ + return sq_type(stack_get(v, idx)); +} + +SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + SQObjectPtr res; + if(!v->TypeOf(o,res)) { + return SQ_ERROR; + } + v->Push(res); + return SQ_OK; +} + +SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + SQObjectPtr res; + if(!v->ToString(o,res)) { + return SQ_ERROR; + } + v->Push(res); + return SQ_OK; +} + +void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b) +{ + SQObjectPtr &o = stack_get(v, idx); + *b = SQVM::IsFalse(o)?SQFalse:SQTrue; +} + +SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i) +{ + SQObjectPtr &o = stack_get(v, idx); + if(sq_isnumeric(o)) { + *i = tointeger(o); + return SQ_OK; + } + if(sq_isbool(o)) { + *i = SQVM::IsFalse(o)?SQFalse:SQTrue; + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f) +{ + SQObjectPtr &o = stack_get(v, idx); + if(sq_isnumeric(o)) { + *f = tofloat(o); + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b) +{ + SQObjectPtr &o = stack_get(v, idx); + if(sq_isbool(o)) { + *b = _integer(o); + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const SQChar **c,SQInteger *size) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_STRING,o); + *c = _stringval(*o); + *size = _string(*o)->_len; + return SQ_OK; +} + +SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_STRING,o); + *c = _stringval(*o); + return SQ_OK; +} + +SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_THREAD,o); + *thread = _thread(*o); + return SQ_OK; +} + +SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + v->PushNull(); + if(!v->Clone(o, stack_get(v, -1))){ + v->Pop(); + return SQ_ERROR; + } + return SQ_OK; +} + +SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + SQObjectType type = sq_type(o); + switch(type) { + case OT_STRING: return _string(o)->_len; + case OT_TABLE: return _table(o)->CountUsed(); + case OT_ARRAY: return _array(o)->Size(); + case OT_USERDATA: return _userdata(o)->_size; + case OT_INSTANCE: return _instance(o)->_class->_udsize; + case OT_CLASS: return _class(o)->_udsize; + default: + return sq_aux_invalidtype(v, type); + } +} + +SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx) +{ + SQObjectPtr &o = stack_get(v, idx); + return HashObj(o); +} + +SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_USERDATA,o); + (*p) = _userdataval(*o); + if(typetag) *typetag = _userdata(*o)->_typetag; + return SQ_OK; +} + +SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag) +{ + SQObjectPtr &o = stack_get(v,idx); + switch(sq_type(o)) { + case OT_USERDATA: _userdata(o)->_typetag = typetag; break; + case OT_CLASS: _class(o)->_typetag = typetag; break; + default: return sq_throwerror(v,_SC("invalid object type")); + } + return SQ_OK; +} + +SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag) +{ + switch(sq_type(*o)) { + case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break; + case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break; + case OT_CLASS: *typetag = _class(*o)->_typetag; break; + default: return SQ_ERROR; + } + return SQ_OK; +} + +SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag) +{ + SQObjectPtr &o = stack_get(v,idx); + if (SQ_FAILED(sq_getobjtypetag(&o, typetag))) + return SQ_ERROR;// this is not an error it should be a bool but would break backward compatibility + return SQ_OK; +} + +SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_USERPOINTER,o); + (*p) = _userpointer(*o); + return SQ_OK; +} + +SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); + _instance(o)->_userpointer = p; + return SQ_OK; +} + +SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_CLASS) return sq_throwerror(v,_SC("the object is not a class")); + if(_class(o)->_locked) return sq_throwerror(v,_SC("the class is locked")); + _class(o)->_udsize = udsize; + return SQ_OK; +} + + +SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,_SC("the object is not a class instance")); + (*p) = _instance(o)->_userpointer; + if(typetag != 0) { + SQClass *cl = _instance(o)->_class; + do{ + if(cl->_typetag == typetag) + return SQ_OK; + cl = cl->_base; + }while(cl != NULL); + return sq_throwerror(v,_SC("invalid type tag")); + } + return SQ_OK; +} + +SQInteger sq_gettop(HSQUIRRELVM v) +{ + return (v->_top) - v->_stackbase; +} + +void sq_settop(HSQUIRRELVM v, SQInteger newtop) +{ + SQInteger top = sq_gettop(v); + if(top > newtop) + sq_pop(v, top - newtop); + else + while(top++ < newtop) sq_pushnull(v); +} + +void sq_pop(HSQUIRRELVM v, SQInteger nelemstopop) +{ + assert(v->_top >= nelemstopop); + v->Pop(nelemstopop); +} + +void sq_poptop(HSQUIRRELVM v) +{ + assert(v->_top >= 1); + v->Pop(); +} + + +void sq_remove(HSQUIRRELVM v, SQInteger idx) +{ + v->Remove(idx); +} + +SQInteger sq_cmp(HSQUIRRELVM v) +{ + SQInteger res; + v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res); + return res; +} + +SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) +{ + sq_aux_paramscheck(v, 3); + SQObjectPtr &self = stack_get(v, idx); + if(sq_type(self) == OT_TABLE || sq_type(self) == OT_CLASS) { + SQObjectPtr &key = v->GetUp(-2); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); + v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false); + v->Pop(2); + } + return SQ_OK; +} + +SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) +{ + sq_aux_paramscheck(v, 2); + SQObjectPtr *self; + _GETSAFE_OBJ(v, idx, OT_TABLE,self); + SQObjectPtr &key = v->GetUp(-1); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null is not a valid key")); + SQObjectPtr res; + if(!v->DeleteSlot(*self, key, res)){ + v->Pop(); + return SQ_ERROR; + } + if(pushval) v->GetUp(-1) = res; + else v->Pop(); + return SQ_OK; +} + +SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self = stack_get(v, idx); + if(v->Set(self, v->GetUp(-2), v->GetUp(-1),DONT_FALL_BACK)) { + v->Pop(2); + return SQ_OK; + } + return SQ_ERROR; +} + +SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self = stack_get(v, idx); + SQObjectPtr &key = v->GetUp(-2); + if(sq_type(key) == OT_NULL) { + v->Pop(2); + return sq_throwerror(v, _SC("null key")); + } + switch(sq_type(self)) { + case OT_TABLE: + _table(self)->NewSlot(key, v->GetUp(-1)); + v->Pop(2); + return SQ_OK; + break; + case OT_CLASS: + _class(self)->NewSlot(_ss(v), key, v->GetUp(-1),false); + v->Pop(2); + return SQ_OK; + break; + case OT_INSTANCE: + if(_instance(self)->Set(key, v->GetUp(-1))) { + v->Pop(2); + return SQ_OK; + } + break; + case OT_ARRAY: + if(v->Set(self, key, v->GetUp(-1),false)) { + v->Pop(2); + return SQ_OK; + } + break; + default: + v->Pop(2); + return sq_throwerror(v, _SC("rawset works only on array/table/class and instance")); + } + v->Raise_IdxError(v->GetUp(-2));return SQ_ERROR; +} + +SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) +{ + SQObjectPtr &self = stack_get(v, idx); + if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); + SQObjectPtr &key = v->GetUp(-3); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); + if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,false)) { + v->Pop(3); + return SQ_ERROR; + } + v->Pop(3); + return SQ_OK; +} + +SQRESULT sq_rawnewmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) +{ + SQObjectPtr &self = stack_get(v, idx); + if(sq_type(self) != OT_CLASS) return sq_throwerror(v, _SC("new member only works with classes")); + SQObjectPtr &key = v->GetUp(-3); + if(sq_type(key) == OT_NULL) return sq_throwerror(v, _SC("null key")); + if(!v->NewSlotA(self,key,v->GetUp(-2),v->GetUp(-1),bstatic?true:false,true)) { + v->Pop(3); + return SQ_ERROR; + } + v->Pop(3); + return SQ_OK; +} + +SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self = stack_get(v, idx); + SQObjectPtr &mt = v->GetUp(-1); + SQObjectType type = sq_type(self); + switch(type) { + case OT_TABLE: + if(sq_type(mt) == OT_TABLE) { + if(!_table(self)->SetDelegate(_table(mt))) { + return sq_throwerror(v, _SC("delegate cycle")); + } + v->Pop(); + } + else if(sq_type(mt)==OT_NULL) { + _table(self)->SetDelegate(NULL); v->Pop(); } + else return sq_aux_invalidtype(v,type); + break; + case OT_USERDATA: + if(sq_type(mt)==OT_TABLE) { + _userdata(self)->SetDelegate(_table(mt)); v->Pop(); } + else if(sq_type(mt)==OT_NULL) { + _userdata(self)->SetDelegate(NULL); v->Pop(); } + else return sq_aux_invalidtype(v, type); + break; + default: + return sq_aux_invalidtype(v, type); + break; + } + return SQ_OK; +} + +SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) +{ + sq_aux_paramscheck(v, 2); + SQObjectPtr *self; + _GETSAFE_OBJ(v, idx, OT_TABLE,self); + SQObjectPtr &key = v->GetUp(-1); + SQObjectPtr t; + if(_table(*self)->Get(key,t)) { + _table(*self)->Remove(key); + } + if(pushval != 0) + v->GetUp(-1) = t; + else + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self=stack_get(v,idx); + switch(sq_type(self)){ + case OT_TABLE: + case OT_USERDATA: + if(!_delegable(self)->_delegate){ + v->PushNull(); + break; + } + v->Push(SQObjectPtr(_delegable(self)->_delegate)); + break; + default: return sq_throwerror(v,_SC("wrong type")); break; + } + return SQ_OK; + +} + +SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self=stack_get(v,idx); + SQObjectPtr &obj = v->GetUp(-1); + if(v->Get(self,obj,obj,false,DONT_FALL_BACK)) + return SQ_OK; + v->Pop(); + return SQ_ERROR; +} + +SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &self=stack_get(v,idx); + SQObjectPtr &obj = v->GetUp(-1); + switch(sq_type(self)) { + case OT_TABLE: + if(_table(self)->Get(obj,obj)) + return SQ_OK; + break; + case OT_CLASS: + if(_class(self)->Get(obj,obj)) + return SQ_OK; + break; + case OT_INSTANCE: + if(_instance(self)->Get(obj,obj)) + return SQ_OK; + break; + case OT_ARRAY:{ + if(sq_isnumeric(obj)){ + if(_array(self)->Get(tointeger(obj),obj)) { + return SQ_OK; + } + } + else { + v->Pop(); + return sq_throwerror(v,_SC("invalid index type for an array")); + } + } + break; + default: + v->Pop(); + return sq_throwerror(v,_SC("rawget works only on array/table/instance and class")); + } + v->Pop(); + return sq_throwerror(v,_SC("the index doesn't exist")); +} + +SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po) +{ + *po=stack_get(v,idx); + return SQ_OK; +} + +const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx) +{ + SQUnsignedInteger cstksize=v->_callsstacksize; + SQUnsignedInteger lvl=(cstksize-level)-1; + SQInteger stackbase=v->_stackbase; + if(lvl_callsstack[(cstksize-i)-1]; + stackbase-=ci._prevstkbase; + } + SQVM::CallInfo &ci=v->_callsstack[lvl]; + if(sq_type(ci._closure)!=OT_CLOSURE) + return NULL; + SQClosure *c=_closure(ci._closure); + SQFunctionProto *func=c->_function; + if(func->_noutervalues > (SQInteger)idx) { + v->Push(*_outer(c->_outervalues[idx])->_valptr); + return _stringval(func->_outervalues[idx]._name); + } + idx -= func->_noutervalues; + return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1); + } + return NULL; +} + +void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj) +{ + v->Push(SQObjectPtr(obj)); +} + +void sq_resetobject(HSQOBJECT *po) +{ + po->_unVal.pUserPointer=NULL;po->_type=OT_NULL; +} + +SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err) +{ + v->_lasterror=SQString::Create(_ss(v),err); + return SQ_ERROR; +} + +SQRESULT sq_throwobject(HSQUIRRELVM v) +{ + v->_lasterror = v->GetUp(-1); + v->Pop(); + return SQ_ERROR; +} + + +void sq_reseterror(HSQUIRRELVM v) +{ + v->_lasterror.Null(); +} + +void sq_getlasterror(HSQUIRRELVM v) +{ + v->Push(v->_lasterror); +} + +SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize) +{ + if (((SQUnsignedInteger)v->_top + nsize) > v->_stack.size()) { + if(v->_nmetamethodscall) { + return sq_throwerror(v,_SC("cannot resize stack while in a metamethod")); + } + v->_stack.resize(v->_stack.size() + ((v->_top + nsize) - v->_stack.size())); + } + return SQ_OK; +} + +SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) +{ + if (sq_type(v->GetUp(-1)) == OT_GENERATOR) + { + v->PushNull(); //retval + if (!v->Execute(v->GetUp(-2), 0, v->_top, v->GetUp(-1), raiseerror, SQVM::ET_RESUME_GENERATOR)) + {v->Raise_Error(v->_lasterror); return SQ_ERROR;} + if(!retval) + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v,_SC("only generators can be resumed")); +} + +SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) +{ + SQObjectPtr res; + if(!v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false)){ + v->Pop(params); //pop args + return SQ_ERROR; + } + if(!v->_suspended) + v->Pop(params); //pop args + if(retval) + v->Push(res); // push result + return SQ_OK; +} + +SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) +{ + SQObjectPtr &res = v->GetUp(-(nparams + 1)); + if (sq_type(res) != OT_CLOSURE) { + return sq_throwerror(v, _SC("only closure can be tail called")); + } + SQClosure *clo = _closure(res); + if (clo->_function->_bgenerator) + { + return sq_throwerror(v, _SC("generators cannot be tail called")); + } + + SQInteger stackbase = (v->_top - nparams) - v->_stackbase; + if (!v->TailCall(clo, stackbase, nparams)) { + return SQ_ERROR; + } + return SQ_TAILCALL_FLAG; +} + +SQRESULT sq_suspendvm(HSQUIRRELVM v) +{ + return v->Suspend(); +} + +SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseerror,SQBool throwerror) +{ + SQObjectPtr ret; + if(!v->_suspended) + return sq_throwerror(v,_SC("cannot resume a vm that is not running any code")); + SQInteger target = v->_suspended_target; + if(wakeupret) { + if(target != -1) { + v->GetAt(v->_stackbase+v->_suspended_target)=v->GetUp(-1); //retval + } + v->Pop(); + } else if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target).Null(); } + SQObjectPtr dummy; + if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM)) { + return SQ_ERROR; + } + if(retval) + v->Push(ret); + return SQ_OK; +} + +void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook) +{ + SQObjectPtr &ud=stack_get(v,idx); + switch(sq_type(ud) ) { + case OT_USERDATA: _userdata(ud)->_hook = hook; break; + case OT_INSTANCE: _instance(ud)->_hook = hook; break; + case OT_CLASS: _class(ud)->_hook = hook; break; + default: return; + } +} + +SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &ud=stack_get(v,idx); + switch(sq_type(ud) ) { + case OT_USERDATA: return _userdata(ud)->_hook; break; + case OT_INSTANCE: return _instance(ud)->_hook; break; + case OT_CLASS: return _class(ud)->_hook; break; + default: return NULL; + } +} + +void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f) +{ + _ss(v)->_compilererrorhandler = f; +} + +SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, -1, OT_CLOSURE,o); + unsigned short tag = SQ_BYTECODE_STREAM_TAG; + if(_closure(*o)->_function->_noutervalues) + return sq_throwerror(v,_SC("a closure with free variables bound cannot be serialized")); + if(w(up,&tag,2) != 2) + return sq_throwerror(v,_SC("io error")); + if(!_closure(*o)->Save(v,up,w)) + return SQ_ERROR; + return SQ_OK; +} + +SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up) +{ + SQObjectPtr closure; + + unsigned short tag; + if(r(up,&tag,2) != 2) + return sq_throwerror(v,_SC("io error")); + if(tag != SQ_BYTECODE_STREAM_TAG) + return sq_throwerror(v,_SC("invalid stream")); + if(!SQClosure::Load(v,up,r,closure)) + return SQ_ERROR; + v->Push(closure); + return SQ_OK; +} + +SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize) +{ + return _ss(v)->GetScratchPad(minsize); +} + +SQRESULT sq_resurrectunreachable(HSQUIRRELVM v) +{ +#ifndef NO_GARBAGE_COLLECTOR + _ss(v)->ResurrectUnreachable(v); + return SQ_OK; +#else + return sq_throwerror(v,_SC("sq_resurrectunreachable requires a garbage collector build")); +#endif +} + +SQInteger sq_collectgarbage(HSQUIRRELVM v) +{ +#ifndef NO_GARBAGE_COLLECTOR + return _ss(v)->CollectGarbage(v); +#else + return -1; +#endif +} + +SQRESULT sq_getcallee(HSQUIRRELVM v) +{ + if(v->_callsstacksize > 1) + { + v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); + return SQ_OK; + } + return sq_throwerror(v,_SC("no closure in the calls stack")); +} + +const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) +{ + SQObjectPtr &self=stack_get(v,idx); + const SQChar *name = NULL; + switch(sq_type(self)) + { + case OT_CLOSURE:{ + SQClosure *clo = _closure(self); + SQFunctionProto *fp = clo->_function; + if(((SQUnsignedInteger)fp->_noutervalues) > nval) { + v->Push(*(_outer(clo->_outervalues[nval])->_valptr)); + SQOuterVar &ov = fp->_outervalues[nval]; + name = _stringval(ov._name); + } + } + break; + case OT_NATIVECLOSURE:{ + SQNativeClosure *clo = _nativeclosure(self); + if(clo->_noutervalues > nval) { + v->Push(clo->_outervalues[nval]); + name = _SC("@NATIVE"); + } + } + break; + default: break; //shutup compiler + } + return name; +} + +SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) +{ + SQObjectPtr &self=stack_get(v,idx); + switch(sq_type(self)) + { + case OT_CLOSURE:{ + SQFunctionProto *fp = _closure(self)->_function; + if(((SQUnsignedInteger)fp->_noutervalues) > nval){ + *(_outer(_closure(self)->_outervalues[nval])->_valptr) = stack_get(v,-1); + } + else return sq_throwerror(v,_SC("invalid free var index")); + } + break; + case OT_NATIVECLOSURE: + if(_nativeclosure(self)->_noutervalues > nval){ + _nativeclosure(self)->_outervalues[nval] = stack_get(v,-1); + } + else return sq_throwerror(v,_SC("invalid free var index")); + break; + default: + return sq_aux_invalidtype(v, sq_type(self)); + } + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_setattributes(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + SQObjectPtr &key = stack_get(v,-2); + SQObjectPtr &val = stack_get(v,-1); + SQObjectPtr attrs; + if(sq_type(key) == OT_NULL) { + attrs = _class(*o)->_attributes; + _class(*o)->_attributes = val; + v->Pop(2); + v->Push(attrs); + return SQ_OK; + }else if(_class(*o)->GetAttributes(key,attrs)) { + _class(*o)->SetAttributes(key,val); + v->Pop(2); + v->Push(attrs); + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong index")); +} + +SQRESULT sq_getattributes(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + SQObjectPtr &key = stack_get(v,-1); + SQObjectPtr attrs; + if(sq_type(key) == OT_NULL) { + attrs = _class(*o)->_attributes; + v->Pop(); + v->Push(attrs); + return SQ_OK; + } + else if(_class(*o)->GetAttributes(key,attrs)) { + v->Pop(); + v->Push(attrs); + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong index")); +} + +SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + SQObjectPtr &key = stack_get(v,-1); + SQTable *m = _class(*o)->_members; + SQObjectPtr val; + if(m->Get(key,val)) { + handle->_static = _isfield(val) ? SQFalse : SQTrue; + handle->_index = _member_idx(val); + v->Pop(); + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong index")); +} + +SQRESULT _getmemberbyhandle(HSQUIRRELVM v,SQObjectPtr &self,const HSQMEMBERHANDLE *handle,SQObjectPtr *&val) +{ + switch(sq_type(self)) { + case OT_INSTANCE: { + SQInstance *i = _instance(self); + if(handle->_static) { + SQClass *c = i->_class; + val = &c->_methods[handle->_index].val; + } + else { + val = &i->_values[handle->_index]; + + } + } + break; + case OT_CLASS: { + SQClass *c = _class(self); + if(handle->_static) { + val = &c->_methods[handle->_index].val; + } + else { + val = &c->_defaultvalues[handle->_index].val; + } + } + break; + default: + return sq_throwerror(v,_SC("wrong type(expected class or instance)")); + } + return SQ_OK; +} + +SQRESULT sq_getbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) +{ + SQObjectPtr &self = stack_get(v,idx); + SQObjectPtr *val = NULL; + if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { + return SQ_ERROR; + } + v->Push(_realval(*val)); + return SQ_OK; +} + +SQRESULT sq_setbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) +{ + SQObjectPtr &self = stack_get(v,idx); + SQObjectPtr &newval = stack_get(v,-1); + SQObjectPtr *val = NULL; + if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { + return SQ_ERROR; + } + *val = newval; + v->Pop(); + return SQ_OK; +} + +SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + if(_class(*o)->_base) + v->Push(SQObjectPtr(_class(*o)->_base)); + else + v->PushNull(); + return SQ_OK; +} + +SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_INSTANCE,o); + v->Push(SQObjectPtr(_instance(*o)->_class)); + return SQ_OK; +} + +SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr *o = NULL; + _GETSAFE_OBJ(v, idx, OT_CLASS,o); + v->Push(_class(*o)->CreateInstance()); + return SQ_OK; +} + +void sq_weakref(HSQUIRRELVM v,SQInteger idx) +{ + SQObject &o=stack_get(v,idx); + if(ISREFCOUNTED(sq_type(o))) { + v->Push(_refcounted(o)->GetWeakRef(sq_type(o))); + return; + } + v->Push(o); +} + +SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr &o = stack_get(v,idx); + if(sq_type(o) != OT_WEAKREF) { + return sq_throwerror(v,_SC("the object must be a weakref")); + } + v->Push(_weakref(o)->_obj); + return SQ_OK; +} + +SQRESULT sq_getdefaultdelegate(HSQUIRRELVM v,SQObjectType t) +{ + SQSharedState *ss = _ss(v); + switch(t) { + case OT_TABLE: v->Push(ss->_table_default_delegate); break; + case OT_ARRAY: v->Push(ss->_array_default_delegate); break; + case OT_STRING: v->Push(ss->_string_default_delegate); break; + case OT_INTEGER: case OT_FLOAT: v->Push(ss->_number_default_delegate); break; + case OT_GENERATOR: v->Push(ss->_generator_default_delegate); break; + case OT_CLOSURE: case OT_NATIVECLOSURE: v->Push(ss->_closure_default_delegate); break; + case OT_THREAD: v->Push(ss->_thread_default_delegate); break; + case OT_CLASS: v->Push(ss->_class_default_delegate); break; + case OT_INSTANCE: v->Push(ss->_instance_default_delegate); break; + case OT_WEAKREF: v->Push(ss->_weakref_default_delegate); break; + default: return sq_throwerror(v,_SC("the type doesn't have a default delegate")); + } + return SQ_OK; +} + +SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx) +{ + SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val; + if(sq_type(o) == OT_GENERATOR) { + return sq_throwerror(v,_SC("cannot iterate a generator")); + } + int faketojump; + if(!v->FOREACH_OP(o,realkey,val,refpos,0,666,faketojump)) + return SQ_ERROR; + if(faketojump != 666) { + v->Push(realkey); + v->Push(val); + return SQ_OK; + } + return SQ_ERROR; +} + +struct BufState{ + const SQChar *buf; + SQInteger ptr; + SQInteger size; +}; + +SQInteger buf_lexfeed(SQUserPointer file) +{ + BufState *buf=(BufState*)file; + if(buf->size<(buf->ptr+1)) + return 0; + return buf->buf[buf->ptr++]; +} + +SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) { + BufState buf; + buf.buf = s; + buf.size = size; + buf.ptr = 0; + return sq_compile(v, buf_lexfeed, &buf, sourcename, raiseerror); +} + +void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx) +{ + dest->Push(stack_get(src,idx)); +} + +void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc,SQPRINTFUNCTION errfunc) +{ + _ss(v)->_printfunc = printfunc; + _ss(v)->_errorfunc = errfunc; +} + +SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v) +{ + return _ss(v)->_printfunc; +} + +SQPRINTFUNCTION sq_geterrorfunc(HSQUIRRELVM v) +{ + return _ss(v)->_errorfunc; +} + +void *sq_malloc(SQUnsignedInteger size) +{ + return SQ_MALLOC(size); +} + +void *sq_realloc(void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize) +{ + return SQ_REALLOC(p,oldsize,newsize); +} + +void sq_free(void *p,SQUnsignedInteger size) +{ + SQ_FREE(p,size); +} diff --git a/sp/src/vscript/squirrel/squirrel/sqarray.h b/sp/src/vscript/squirrel/squirrel/sqarray.h new file mode 100644 index 0000000000..7c6c204945 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqarray.h @@ -0,0 +1,94 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQARRAY_H_ +#define _SQARRAY_H_ + +struct SQArray : public CHAINABLE_OBJ +{ +private: + SQArray(SQSharedState *ss,SQInteger nsize){_values.resize(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} + ~SQArray() + { + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); + } +public: + static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){ + SQArray *newarray=(SQArray*)SQ_MALLOC(sizeof(SQArray)); + new (newarray) SQArray(ss,nInitialSize); + return newarray; + } +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + SQObjectType GetType() {return OT_ARRAY;} +#endif + void Finalize(){ + _values.resize(0); + } + bool Get(const SQInteger nidx,SQObjectPtr &val) + { + if(nidx>=0 && nidx<(SQInteger)_values.size()){ + SQObjectPtr &o = _values[nidx]; + val = _realval(o); + return true; + } + else return false; + } + bool Set(const SQInteger nidx,const SQObjectPtr &val) + { + if(nidx>=0 && nidx<(SQInteger)_values.size()){ + _values[nidx]=val; + return true; + } + else return false; + } + SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval) + { + SQUnsignedInteger idx=TranslateIndex(refpos); + while(idx<_values.size()){ + //first found + outkey=(SQInteger)idx; + SQObjectPtr &o = _values[idx]; + outval = _realval(o); + //return idx for the next iteration + return ++idx; + } + //nothing to iterate anymore + return -1; + } + SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),0); anew->_values.copy(_values); return anew; } + SQInteger Size() const {return _values.size();} + void Resize(SQInteger size) + { + SQObjectPtr _null; + Resize(size,_null); + } + void Resize(SQInteger size,SQObjectPtr &fill) { _values.resize(size,fill); ShrinkIfNeeded(); } + void Reserve(SQInteger size) { _values.reserve(size); } + void Append(const SQObject &o){_values.push_back(o);} + void Extend(const SQArray *a); + SQObjectPtr &Top(){return _values.top();} + void Pop(){_values.pop_back(); ShrinkIfNeeded(); } + bool Insert(SQInteger idx,const SQObject &val){ + if(idx < 0 || idx > (SQInteger)_values.size()) + return false; + _values.insert(idx,val); + return true; + } + void ShrinkIfNeeded() { + if(_values.size() <= _values.capacity()>>2) //shrink the array + _values.shrinktofit(); + } + bool Remove(SQInteger idx){ + if(idx < 0 || idx >= (SQInteger)_values.size()) + return false; + _values.remove(idx); + ShrinkIfNeeded(); + return true; + } + void Release() + { + sq_delete(this,SQArray); + } + + SQObjectPtrVec _values; +}; +#endif //_SQARRAY_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqbaselib.cpp b/sp/src/vscript/squirrel/squirrel/sqbaselib.cpp new file mode 100644 index 0000000000..fb6545f921 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqbaselib.cpp @@ -0,0 +1,1370 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqstring.h" +#include "sqtable.h" +#include "sqarray.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqclass.h" +#include +#include +#include + +static bool str2num(const SQChar *s,SQObjectPtr &res,SQInteger base) +{ + SQChar *end; + const SQChar *e = s; + bool iseintbase = base > 13; //to fix error converting hexadecimals with e like 56f0791e + bool isfloat = false; + SQChar c; + while((c = *e) != _SC('\0')) + { + if (c == _SC('.') || (!iseintbase && (c == _SC('E') || c == _SC('e')))) { //e and E is for scientific notation + isfloat = true; + break; + } + e++; + } + if(isfloat){ + SQFloat r = SQFloat(scstrtod(s,&end)); + if(s == end) return false; + res = r; + } + else{ + SQInteger r = SQInteger(scstrtol(s,&end,(int)base)); + if(s == end) return false; + res = r; + } + return true; +} + +static SQInteger base_dummy(HSQUIRRELVM SQ_UNUSED_ARG(v)) +{ + return 0; +} + +#ifndef NO_GARBAGE_COLLECTOR +static SQInteger base_collectgarbage(HSQUIRRELVM v) +{ + sq_pushinteger(v, sq_collectgarbage(v)); + return 1; +} +static SQInteger base_resurectureachable(HSQUIRRELVM v) +{ + sq_resurrectunreachable(v); + return 1; +} +#endif + +static SQInteger base_getroottable(HSQUIRRELVM v) +{ + v->Push(v->_roottable); + return 1; +} + +static SQInteger base_getconsttable(HSQUIRRELVM v) +{ + v->Push(_ss(v)->_consts); + return 1; +} + + +static SQInteger base_setroottable(HSQUIRRELVM v) +{ + SQObjectPtr o = v->_roottable; + if(SQ_FAILED(sq_setroottable(v))) return SQ_ERROR; + v->Push(o); + return 1; +} + +static SQInteger base_setconsttable(HSQUIRRELVM v) +{ + SQObjectPtr o = _ss(v)->_consts; + if(SQ_FAILED(sq_setconsttable(v))) return SQ_ERROR; + v->Push(o); + return 1; +} + +static SQInteger base_seterrorhandler(HSQUIRRELVM v) +{ + sq_seterrorhandler(v); + return 0; +} + +static SQInteger base_setdebughook(HSQUIRRELVM v) +{ + sq_setdebughook(v); + return 0; +} + +static SQInteger base_enabledebuginfo(HSQUIRRELVM v) +{ + SQObjectPtr &o=stack_get(v,2); + + sq_enabledebuginfo(v,SQVM::IsFalse(o)?SQFalse:SQTrue); + return 0; +} + +static SQInteger __getcallstackinfos(HSQUIRRELVM v,SQInteger level) +{ + SQStackInfos si; + SQInteger seq = 0; + const SQChar *name = NULL; + + if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si))) + { + const SQChar *fn = _SC("unknown"); + const SQChar *src = _SC("unknown"); + if(si.funcname)fn = si.funcname; + if(si.source)src = si.source; + sq_newtable(v); + sq_pushstring(v, _SC("func"), -1); + sq_pushstring(v, fn, -1); + sq_newslot(v, -3, SQFalse); + sq_pushstring(v, _SC("src"), -1); + sq_pushstring(v, src, -1); + sq_newslot(v, -3, SQFalse); + sq_pushstring(v, _SC("line"), -1); + sq_pushinteger(v, si.line); + sq_newslot(v, -3, SQFalse); + sq_pushstring(v, _SC("locals"), -1); + sq_newtable(v); + seq=0; + while ((name = sq_getlocal(v, level, seq))) { + sq_pushstring(v, name, -1); + sq_push(v, -2); + sq_newslot(v, -4, SQFalse); + sq_pop(v, 1); + seq++; + } + sq_newslot(v, -3, SQFalse); + return 1; + } + + return 0; +} +static SQInteger base_getstackinfos(HSQUIRRELVM v) +{ + SQInteger level; + sq_getinteger(v, -1, &level); + return __getcallstackinfos(v,level); +} + +static SQInteger base_assert(HSQUIRRELVM v) +{ + if(SQVM::IsFalse(stack_get(v,2))){ + SQInteger top = sq_gettop(v); + if (top>2 && SQ_SUCCEEDED(sq_tostring(v,3))) { + const SQChar *str = 0; + if (SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + return sq_throwerror(v, str); + } + } + return sq_throwerror(v, _SC("assertion failed")); + } + return 0; +} + +static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o) +{ + SQInteger top = sq_gettop(v); + sidx=0; + eidx=0; + o=stack_get(v,1); + if(top>1){ + SQObjectPtr &start=stack_get(v,2); + if(sq_type(start)!=OT_NULL && sq_isnumeric(start)){ + sidx=tointeger(start); + } + } + if(top>2){ + SQObjectPtr &end=stack_get(v,3); + if(sq_isnumeric(end)){ + eidx=tointeger(end); + } + } + else { + eidx = sq_getsize(v,1); + } + return 1; +} + +static SQInteger base_print(HSQUIRRELVM v) +{ + const SQChar *str; + if(SQ_SUCCEEDED(sq_tostring(v,2))) + { + if(SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,_SC("%s"),str); + return 0; + } + } + return SQ_ERROR; +} + +static SQInteger base_error(HSQUIRRELVM v) +{ + const SQChar *str; + if(SQ_SUCCEEDED(sq_tostring(v,2))) + { + if(SQ_SUCCEEDED(sq_getstring(v,-1,&str))) { + if(_ss(v)->_errorfunc) _ss(v)->_errorfunc(v,_SC("%s"),str); + return 0; + } + } + return SQ_ERROR; +} + +static SQInteger base_compilestring(HSQUIRRELVM v) +{ + SQInteger nargs=sq_gettop(v); + const SQChar *src=NULL,*name=_SC("unnamedbuffer"); + SQInteger size; + sq_getstring(v,2,&src); + size=sq_getsize(v,2); + if(nargs>2){ + sq_getstring(v,3,&name); + } + if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse))) + return 1; + else + return SQ_ERROR; +} + +static SQInteger base_newthread(HSQUIRRELVM v) +{ + SQObjectPtr &func = stack_get(v,2); + SQInteger stksize = (_closure(func)->_function->_stacksize << 1) +2; + HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize); + sq_move(newv,v,-2); + return 1; +} + +static SQInteger base_suspend(HSQUIRRELVM v) +{ + return sq_suspendvm(v); +} + +static SQInteger base_array(HSQUIRRELVM v) +{ + SQArray *a; + SQObject &size = stack_get(v,2); + if(sq_gettop(v) > 2) { + a = SQArray::Create(_ss(v),0); + a->Resize(tointeger(size),stack_get(v,3)); + } + else { + a = SQArray::Create(_ss(v),tointeger(size)); + } + v->Push(a); + return 1; +} + +static SQInteger base_type(HSQUIRRELVM v) +{ + SQObjectPtr &o = stack_get(v,2); + v->Push(SQString::Create(_ss(v),GetTypeName(o),-1)); + return 1; +} + +static SQInteger base_callee(HSQUIRRELVM v) +{ + if(v->_callsstacksize > 1) + { + v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); + return 1; + } + return sq_throwerror(v,_SC("no closure in the calls stack")); +} + +static const SQRegFunction base_funcs[]={ + //generic + {_SC("seterrorhandler"),base_seterrorhandler,2, NULL}, + {_SC("setdebughook"),base_setdebughook,2, NULL}, + {_SC("enabledebuginfo"),base_enabledebuginfo,2, NULL}, + {_SC("getstackinfos"),base_getstackinfos,2, _SC(".n")}, + {_SC("getroottable"),base_getroottable,1, NULL}, + {_SC("setroottable"),base_setroottable,2, NULL}, + {_SC("getconsttable"),base_getconsttable,1, NULL}, + {_SC("setconsttable"),base_setconsttable,2, NULL}, + {_SC("assert"),base_assert,-2, NULL}, + {_SC("print"),base_print,2, NULL}, + {_SC("error"),base_error,2, NULL}, + {_SC("compilestring"),base_compilestring,-2, _SC(".ss")}, + {_SC("newthread"),base_newthread,2, _SC(".c")}, + {_SC("suspend"),base_suspend,-1, NULL}, + {_SC("array"),base_array,-2, _SC(".n")}, + {_SC("type"),base_type,2, NULL}, + {_SC("callee"),base_callee,0,NULL}, + {_SC("dummy"),base_dummy,0,NULL}, +#ifndef NO_GARBAGE_COLLECTOR + {_SC("collectgarbage"),base_collectgarbage,0, NULL}, + {_SC("resurrectunreachable"),base_resurectureachable,0, NULL}, +#endif + {NULL,(SQFUNCTION)0,0,NULL} +}; + +void sq_base_register(HSQUIRRELVM v) +{ + SQInteger i=0; + sq_pushroottable(v); + while(base_funcs[i].name!=0) { + sq_pushstring(v,base_funcs[i].name,-1); + sq_newclosure(v,base_funcs[i].f,0); + sq_setnativeclosurename(v,-1,base_funcs[i].name); + sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask); + sq_newslot(v,-3, SQFalse); + i++; + } + + sq_pushstring(v,_SC("_versionnumber_"),-1); + sq_pushinteger(v,SQUIRREL_VERSION_NUMBER); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_version_"),-1); + sq_pushstring(v,SQUIRREL_VERSION,-1); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_charsize_"),-1); + sq_pushinteger(v,sizeof(SQChar)); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_intsize_"),-1); + sq_pushinteger(v,sizeof(SQInteger)); + sq_newslot(v,-3, SQFalse); + sq_pushstring(v,_SC("_floatsize_"),-1); + sq_pushinteger(v,sizeof(SQFloat)); + sq_newslot(v,-3, SQFalse); + sq_pop(v,1); +} + +static SQInteger default_delegate_len(HSQUIRRELVM v) +{ + v->Push(SQInteger(sq_getsize(v,1))); + return 1; +} + +static SQInteger default_delegate_tofloat(HSQUIRRELVM v) +{ + SQObjectPtr &o=stack_get(v,1); + switch(sq_type(o)){ + case OT_STRING:{ + SQObjectPtr res; + if(str2num(_stringval(o),res,10)){ + v->Push(SQObjectPtr(tofloat(res))); + break; + }} + return sq_throwerror(v, _SC("cannot convert the string")); + break; + case OT_INTEGER:case OT_FLOAT: + v->Push(SQObjectPtr(tofloat(o))); + break; + case OT_BOOL: + v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0))); + break; + default: + v->PushNull(); + break; + } + return 1; +} + +static SQInteger default_delegate_tointeger(HSQUIRRELVM v) +{ + SQObjectPtr &o=stack_get(v,1); + SQInteger base = 10; + if(sq_gettop(v) > 1) { + sq_getinteger(v,2,&base); + } + switch(sq_type(o)){ + case OT_STRING:{ + SQObjectPtr res; + if(str2num(_stringval(o),res,base)){ + v->Push(SQObjectPtr(tointeger(res))); + break; + }} + return sq_throwerror(v, _SC("cannot convert the string")); + break; + case OT_INTEGER:case OT_FLOAT: + v->Push(SQObjectPtr(tointeger(o))); + break; + case OT_BOOL: + v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0)); + break; + default: + v->PushNull(); + break; + } + return 1; +} + +static SQInteger default_delegate_tostring(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_tostring(v,1))) + return SQ_ERROR; + return 1; +} + +static SQInteger obj_delegate_weakref(HSQUIRRELVM v) +{ + sq_weakref(v,1); + return 1; +} + +static SQInteger obj_clear(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_clear(v,-1)) ? 1 : SQ_ERROR; +} + + +static SQInteger number_delegate_tochar(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + SQChar c = (SQChar)tointeger(o); + v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1)); + return 1; +} + + + +///////////////////////////////////////////////////////////////// +//TABLE DEFAULT DELEGATE + +static SQInteger table_rawdelete(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue))) + return SQ_ERROR; + return 1; +} + + +static SQInteger container_rawexists(HSQUIRRELVM v) +{ + if(SQ_SUCCEEDED(sq_rawget(v,-2))) { + sq_pushbool(v,SQTrue); + return 1; + } + sq_pushbool(v,SQFalse); + return 1; +} + +static SQInteger container_rawset(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_rawset(v,-3)) ? 1 : SQ_ERROR; +} + + +static SQInteger container_rawget(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR; +} + +static SQInteger table_setdelegate(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_setdelegate(v,-2))) + return SQ_ERROR; + sq_push(v,-1); // -1 because sq_setdelegate pops 1 + return 1; +} + +static SQInteger table_getdelegate(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_getdelegate(v,-1))?1:SQ_ERROR; +} + +static SQInteger table_filter(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQTable *tbl = _table(o); + SQObjectPtr ret = SQTable::Create(_ss(v),0); + + SQObjectPtr itr, key, val; + SQInteger nitr; + while((nitr = tbl->Next(false, itr, key, val)) != -1) { + itr = (SQInteger)nitr; + + v->Push(o); + v->Push(key); + v->Push(val); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + if(!SQVM::IsFalse(v->GetUp(-1))) { + _table(ret)->NewSlot(key, val); + } + v->Pop(); + } + + v->Push(ret); + return 1; +} + +#define TABLE_TO_ARRAY_FUNC(_funcname_,_valname_) static SQInteger _funcname_(HSQUIRRELVM v) \ +{ \ + SQObject &o = stack_get(v, 1); \ + SQTable *t = _table(o); \ + SQObjectPtr itr, key, val; \ + SQObjectPtr _null; \ + SQInteger nitr, n = 0; \ + SQInteger nitems = t->CountUsed(); \ + SQArray *a = SQArray::Create(_ss(v), nitems); \ + a->Resize(nitems, _null); \ + if (nitems) { \ + while ((nitr = t->Next(false, itr, key, val)) != -1) { \ + itr = (SQInteger)nitr; \ + a->Set(n, _valname_); \ + n++; \ + } \ + } \ + v->Push(a); \ + return 1; \ +} + +TABLE_TO_ARRAY_FUNC(table_keys, key) +TABLE_TO_ARRAY_FUNC(table_values, val) + + +const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("t")}, + {_SC("rawget"),container_rawget,2, _SC("t")}, + {_SC("rawset"),container_rawset,3, _SC("t")}, + {_SC("rawdelete"),table_rawdelete,2, _SC("t")}, + {_SC("rawin"),container_rawexists,2, _SC("t")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("clear"),obj_clear,1, _SC(".")}, + {_SC("setdelegate"),table_setdelegate,2, _SC(".t|o")}, + {_SC("getdelegate"),table_getdelegate,1, _SC(".")}, + {_SC("filter"),table_filter,2, _SC("tc")}, + {_SC("keys"),table_keys,1, _SC("t") }, + {_SC("values"),table_values,1, _SC("t") }, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//ARRAY DEFAULT DELEGATE/////////////////////////////////////// + +static SQInteger array_append(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_arrayappend(v,-2)) ? 1 : SQ_ERROR; +} + +static SQInteger array_extend(HSQUIRRELVM v) +{ + _array(stack_get(v,1))->Extend(_array(stack_get(v,2))); + sq_pop(v,1); + return 1; +} + +static SQInteger array_reverse(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_arrayreverse(v,-1)) ? 1 : SQ_ERROR; +} + +static SQInteger array_pop(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR; +} + +static SQInteger array_top(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + if(_array(o)->Size()>0){ + v->Push(_array(o)->Top()); + return 1; + } + else return sq_throwerror(v,_SC("top() on a empty array")); +} + +static SQInteger array_insert(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + SQObject &idx=stack_get(v,2); + SQObject &val=stack_get(v,3); + if(!_array(o)->Insert(tointeger(idx),val)) + return sq_throwerror(v,_SC("index out of range")); + sq_pop(v,2); + return 1; +} + +static SQInteger array_remove(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v, 1); + SQObject &idx = stack_get(v, 2); + if(!sq_isnumeric(idx)) return sq_throwerror(v, _SC("wrong type")); + SQObjectPtr val; + if(_array(o)->Get(tointeger(idx), val)) { + _array(o)->Remove(tointeger(idx)); + v->Push(val); + return 1; + } + return sq_throwerror(v, _SC("idx out of range")); +} + +static SQInteger array_resize(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v, 1); + SQObject &nsize = stack_get(v, 2); + SQObjectPtr fill; + if(sq_isnumeric(nsize)) { + SQInteger sz = tointeger(nsize); + if (sz<0) + return sq_throwerror(v, _SC("resizing to negative length")); + + if(sq_gettop(v) > 2) + fill = stack_get(v, 3); + _array(o)->Resize(sz,fill); + sq_settop(v, 1); + return 1; + } + return sq_throwerror(v, _SC("size must be a number")); +} + +static SQInteger __map_array(SQArray *dest,SQArray *src,HSQUIRRELVM v) { + SQObjectPtr temp; + SQInteger size = src->Size(); + SQObject &closure = stack_get(v, 2); + v->Push(closure); + + SQInteger nArgs = 0; + if(sq_type(closure) == OT_CLOSURE) { + nArgs = _closure(closure)->_function->_nparameters; + } + else if (sq_type(closure) == OT_NATIVECLOSURE) { + SQInteger nParamsCheck = _nativeclosure(closure)->_nparamscheck; + if (nParamsCheck > 0) + nArgs = nParamsCheck; + else // push all params when there is no check or only minimal count set + nArgs = 4; + } + + for(SQInteger n = 0; n < size; n++) { + src->Get(n,temp); + v->Push(src); + v->Push(temp); + if (nArgs >= 3) + v->Push(SQObjectPtr(n)); + if (nArgs >= 4) + v->Push(src); + if(SQ_FAILED(sq_call(v,nArgs,SQTrue,SQFalse))) { + return SQ_ERROR; + } + dest->Set(n,v->GetUp(-1)); + v->Pop(); + } + v->Pop(); + return 0; +} + +static SQInteger array_map(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQInteger size = _array(o)->Size(); + SQObjectPtr ret = SQArray::Create(_ss(v),size); + if(SQ_FAILED(__map_array(_array(ret),_array(o),v))) + return SQ_ERROR; + v->Push(ret); + return 1; +} + +static SQInteger array_apply(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + if(SQ_FAILED(__map_array(_array(o),_array(o),v))) + return SQ_ERROR; + sq_pop(v,1); + return 1; +} + +static SQInteger array_reduce(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQArray *a = _array(o); + SQInteger size = a->Size(); + SQObjectPtr res; + SQInteger iterStart; + if (sq_gettop(v)>2) { + res = stack_get(v,3); + iterStart = 0; + } else if (size==0) { + return 0; + } else { + a->Get(0,res); + iterStart = 1; + } + if (size > iterStart) { + SQObjectPtr other; + v->Push(stack_get(v,2)); + for (SQInteger n = iterStart; n < size; n++) { + a->Get(n,other); + v->Push(o); + v->Push(res); + v->Push(other); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + res = v->GetUp(-1); + v->Pop(); + } + v->Pop(); + } + v->Push(res); + return 1; +} + +static SQInteger array_filter(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQArray *a = _array(o); + SQObjectPtr ret = SQArray::Create(_ss(v),0); + SQInteger size = a->Size(); + SQObjectPtr val; + for(SQInteger n = 0; n < size; n++) { + a->Get(n,val); + v->Push(o); + v->Push(n); + v->Push(val); + if(SQ_FAILED(sq_call(v,3,SQTrue,SQFalse))) { + return SQ_ERROR; + } + if(!SQVM::IsFalse(v->GetUp(-1))) { + _array(ret)->Append(val); + } + v->Pop(); + } + v->Push(ret); + return 1; +} + +static SQInteger array_find(HSQUIRRELVM v) +{ + SQObject &o = stack_get(v,1); + SQObjectPtr &val = stack_get(v,2); + SQArray *a = _array(o); + SQInteger size = a->Size(); + SQObjectPtr temp; + for(SQInteger n = 0; n < size; n++) { + bool res = false; + a->Get(n,temp); + if(SQVM::IsEqual(temp,val,res) && res) { + v->Push(n); + return 1; + } + } + return 0; +} + + +static bool _sort_compare(HSQUIRRELVM v,SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) +{ + if(func < 0) { + if(!v->ObjCmp(a,b,ret)) return false; + } + else { + SQInteger top = sq_gettop(v); + sq_push(v, func); + sq_pushroottable(v); + v->Push(a); + v->Push(b); + if(SQ_FAILED(sq_call(v, 3, SQTrue, SQFalse))) { + if(!sq_isstring( v->_lasterror)) + v->Raise_Error(_SC("compare func failed")); + return false; + } + if(SQ_FAILED(sq_getinteger(v, -1, &ret))) { + v->Raise_Error(_SC("numeric value expected as return value of the compare function")); + return false; + } + sq_settop(v, top); + return true; + } + return true; +} + +static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, SQInteger func) +{ + SQInteger maxChild; + SQInteger done = 0; + SQInteger ret; + SQInteger root2; + while (((root2 = root * 2) <= bottom) && (!done)) + { + if (root2 == bottom) { + maxChild = root2; + } + else { + if(!_sort_compare(v,arr->_values[root2],arr->_values[root2 + 1],func,ret)) + return false; + if (ret > 0) { + maxChild = root2; + } + else { + maxChild = root2 + 1; + } + } + + if(!_sort_compare(v,arr->_values[root],arr->_values[maxChild],func,ret)) + return false; + if (ret < 0) { + if (root == maxChild) { + v->Raise_Error(_SC("inconsistent compare function")); + return false; // We'd be swapping ourselve. The compare function is incorrect + } + + _Swap(arr->_values[root],arr->_values[maxChild]); + root = maxChild; + } + else { + done = 1; + } + } + return true; +} + +static bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger SQ_UNUSED_ARG(l), SQInteger SQ_UNUSED_ARG(r),SQInteger func) +{ + SQArray *a = _array(arr); + SQInteger i; + SQInteger array_size = a->Size(); + for (i = (array_size / 2); i >= 0; i--) { + if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false; + } + + for (i = array_size-1; i >= 1; i--) + { + _Swap(a->_values[0],a->_values[i]); + if(!_hsort_sift_down(v,a, 0, i-1,func)) return false; + } + return true; +} + +static SQInteger array_sort(HSQUIRRELVM v) +{ + SQInteger func = -1; + SQObjectPtr &o = stack_get(v,1); + if(_array(o)->Size() > 1) { + if(sq_gettop(v) == 2) func = 2; + if(!_hsort(v, o, 0, _array(o)->Size()-1, func)) + return SQ_ERROR; + + } + sq_settop(v,1); + return 1; +} + +static SQInteger array_slice(HSQUIRRELVM v) +{ + SQInteger sidx,eidx; + SQObjectPtr o; + if(get_slice_params(v,sidx,eidx,o)==-1)return -1; + SQInteger alen = _array(o)->Size(); + if(sidx < 0)sidx = alen + sidx; + if(eidx < 0)eidx = alen + eidx; + if(eidx < sidx)return sq_throwerror(v,_SC("wrong indexes")); + if(eidx > alen || sidx < 0)return sq_throwerror(v, _SC("slice out of range")); + SQArray *arr=SQArray::Create(_ss(v),eidx-sidx); + SQObjectPtr t; + SQInteger count=0; + for(SQInteger i=sidx;iGet(i,t); + arr->Set(count++,t); + } + v->Push(arr); + return 1; + +} + +const SQRegFunction SQSharedState::_array_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("a")}, + {_SC("append"),array_append,2, _SC("a")}, + {_SC("extend"),array_extend,2, _SC("aa")}, + {_SC("push"),array_append,2, _SC("a")}, + {_SC("pop"),array_pop,1, _SC("a")}, + {_SC("top"),array_top,1, _SC("a")}, + {_SC("insert"),array_insert,3, _SC("an")}, + {_SC("remove"),array_remove,2, _SC("an")}, + {_SC("resize"),array_resize,-2, _SC("an")}, + {_SC("reverse"),array_reverse,1, _SC("a")}, + {_SC("sort"),array_sort,-1, _SC("ac")}, + {_SC("slice"),array_slice,-1, _SC("ann")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("clear"),obj_clear,1, _SC(".")}, + {_SC("map"),array_map,2, _SC("ac")}, + {_SC("apply"),array_apply,2, _SC("ac")}, + {_SC("reduce"),array_reduce,-2, _SC("ac.")}, + {_SC("filter"),array_filter,2, _SC("ac")}, + {_SC("find"),array_find,2, _SC("a.")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//STRING DEFAULT DELEGATE////////////////////////// +static SQInteger string_slice(HSQUIRRELVM v) +{ + SQInteger sidx,eidx; + SQObjectPtr o; + if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1; + SQInteger slen = _string(o)->_len; + if(sidx < 0)sidx = slen + sidx; + if(eidx < 0)eidx = slen + eidx; + if(eidx < sidx) return sq_throwerror(v,_SC("wrong indexes")); + if(eidx > slen || sidx < 0) return sq_throwerror(v, _SC("slice out of range")); + v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx)); + return 1; +} + +static SQInteger string_find(HSQUIRRELVM v) +{ + SQInteger top,start_idx=0; + const SQChar *str,*substr,*ret; + if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){ + if(top>2)sq_getinteger(v,3,&start_idx); + if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){ + ret=scstrstr(&str[start_idx],substr); + if(ret){ + sq_pushinteger(v,(SQInteger)(ret-str)); + return 1; + } + } + return 0; + } + return sq_throwerror(v,_SC("invalid param")); +} + +#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \ +{\ + SQInteger sidx,eidx; \ + SQObjectPtr str; \ + if(SQ_FAILED(get_slice_params(v,sidx,eidx,str)))return -1; \ + SQInteger slen = _string(str)->_len; \ + if(sidx < 0)sidx = slen + sidx; \ + if(eidx < 0)eidx = slen + eidx; \ + if(eidx < sidx) return sq_throwerror(v,_SC("wrong indexes")); \ + if(eidx > slen || sidx < 0) return sq_throwerror(v,_SC("slice out of range")); \ + SQInteger len=_string(str)->_len; \ + const SQChar *sthis=_stringval(str); \ + SQChar *snew=(_ss(v)->GetScratchPad(sq_rsl(len))); \ + memcpy(snew,sthis,sq_rsl(len));\ + for(SQInteger i=sidx;iPush(SQString::Create(_ss(v),snew,len)); \ + return 1; \ +} + + +STRING_TOFUNCZ(tolower) +STRING_TOFUNCZ(toupper) + +const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("s")}, + {_SC("tointeger"),default_delegate_tointeger,-1, _SC("sn")}, + {_SC("tofloat"),default_delegate_tofloat,1, _SC("s")}, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("slice"),string_slice,-1, _SC("s n n")}, + {_SC("find"),string_find,-2, _SC("s s n")}, + {_SC("tolower"),string_tolower,-1, _SC("s n n")}, + {_SC("toupper"),string_toupper,-1, _SC("s n n")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//INTEGER DEFAULT DELEGATE////////////////////////// +const SQRegFunction SQSharedState::_number_default_delegate_funcz[]={ + {_SC("tointeger"),default_delegate_tointeger,1, _SC("n|b")}, + {_SC("tofloat"),default_delegate_tofloat,1, _SC("n|b")}, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("tochar"),number_delegate_tochar,1, _SC("n|b")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//CLOSURE DEFAULT DELEGATE////////////////////////// +static SQInteger closure_pcall(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR; +} + +static SQInteger closure_call(HSQUIRRELVM v) +{ + SQObjectPtr &c = stack_get(v, -1); + if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) + { + return sq_tailcall(v, sq_gettop(v) - 1); + } + return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; +} + +static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror) +{ + SQArray *aparams=_array(stack_get(v,2)); + SQInteger nparams=aparams->Size(); + v->Push(stack_get(v,1)); + for(SQInteger i=0;iPush(aparams->_values[i]); + return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,raiseerror))?1:SQ_ERROR; +} + +static SQInteger closure_acall(HSQUIRRELVM v) +{ + return _closure_acall(v,SQTrue); +} + +static SQInteger closure_pacall(HSQUIRRELVM v) +{ + return _closure_acall(v,SQFalse); +} + +static SQInteger closure_bindenv(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_bindenv(v,1))) + return SQ_ERROR; + return 1; +} + +static SQInteger closure_getroot(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_getclosureroot(v,-1))) + return SQ_ERROR; + return 1; +} + +static SQInteger closure_setroot(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_setclosureroot(v,-2))) + return SQ_ERROR; + return 1; +} + +static SQInteger closure_getinfos(HSQUIRRELVM v) { + SQObject o = stack_get(v,1); + SQTable *res = SQTable::Create(_ss(v),4); + if(sq_type(o) == OT_CLOSURE) { + SQFunctionProto *f = _closure(o)->_function; + SQInteger nparams = f->_nparameters + (f->_varparams?1:0); + SQObjectPtr params = SQArray::Create(_ss(v),nparams); + SQObjectPtr defparams = SQArray::Create(_ss(v),f->_ndefaultparams); + for(SQInteger n = 0; n_nparameters; n++) { + _array(params)->Set((SQInteger)n,f->_parameters[n]); + } + for(SQInteger j = 0; j_ndefaultparams; j++) { + _array(defparams)->Set((SQInteger)j,_closure(o)->_defaultparams[j]); + } + if(f->_varparams) { + _array(params)->Set(nparams-1,SQString::Create(_ss(v),_SC("..."),-1)); + } + res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),false); + res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),f->_name); + res->NewSlot(SQString::Create(_ss(v),_SC("src"),-1),f->_sourcename); + res->NewSlot(SQString::Create(_ss(v),_SC("parameters"),-1),params); + res->NewSlot(SQString::Create(_ss(v),_SC("varargs"),-1),f->_varparams); + res->NewSlot(SQString::Create(_ss(v),_SC("defparams"),-1),defparams); + } + else { //OT_NATIVECLOSURE + SQNativeClosure *nc = _nativeclosure(o); + res->NewSlot(SQString::Create(_ss(v),_SC("native"),-1),true); + res->NewSlot(SQString::Create(_ss(v),_SC("name"),-1),nc->_name); + res->NewSlot(SQString::Create(_ss(v),_SC("paramscheck"),-1),nc->_nparamscheck); + SQObjectPtr typecheck; + if(nc->_typecheck.size() > 0) { + typecheck = + SQArray::Create(_ss(v), nc->_typecheck.size()); + for(SQUnsignedInteger n = 0; n_typecheck.size(); n++) { + _array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]); + } + } + res->NewSlot(SQString::Create(_ss(v),_SC("typecheck"),-1),typecheck); + } + v->Push(res); + return 1; +} + + + +const SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={ + {_SC("call"),closure_call,-1, _SC("c")}, + {_SC("pcall"),closure_pcall,-1, _SC("c")}, + {_SC("acall"),closure_acall,2, _SC("ca")}, + {_SC("pacall"),closure_pacall,2, _SC("ca")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("bindenv"),closure_bindenv,2, _SC("c x|y|t")}, + {_SC("getinfos"),closure_getinfos,1, _SC("c")}, + {_SC("getroot"),closure_getroot,1, _SC("c")}, + {_SC("setroot"),closure_setroot,2, _SC("ct")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//GENERATOR DEFAULT DELEGATE +static SQInteger generator_getstatus(HSQUIRRELVM v) +{ + SQObject &o=stack_get(v,1); + switch(_generator(o)->_state){ + case SQGenerator::eSuspended:v->Push(SQString::Create(_ss(v),_SC("suspended")));break; + case SQGenerator::eRunning:v->Push(SQString::Create(_ss(v),_SC("running")));break; + case SQGenerator::eDead:v->Push(SQString::Create(_ss(v),_SC("dead")));break; + } + return 1; +} + +const SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={ + {_SC("getstatus"),generator_getstatus,1, _SC("g")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +//THREAD DEFAULT DELEGATE +static SQInteger thread_call(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQInteger nparams = sq_gettop(v); + _thread(o)->Push(_thread(o)->_roottable); + for(SQInteger i = 2; i<(nparams+1); i++) + sq_move(_thread(o),v,i); + if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQTrue))) { + sq_move(v,_thread(o),-1); + sq_pop(_thread(o),1); + return 1; + } + v->_lasterror = _thread(o)->_lasterror; + return SQ_ERROR; + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +static SQInteger thread_wakeup(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQVM *thread = _thread(o); + SQInteger state = sq_getvmstate(thread); + if(state != SQ_VMSTATE_SUSPENDED) { + switch(state) { + case SQ_VMSTATE_IDLE: + return sq_throwerror(v,_SC("cannot wakeup a idle thread")); + break; + case SQ_VMSTATE_RUNNING: + return sq_throwerror(v,_SC("cannot wakeup a running thread")); + break; + } + } + + SQInteger wakeupret = sq_gettop(v)>1?SQTrue:SQFalse; + if(wakeupret) { + sq_move(thread,v,2); + } + if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,SQTrue,SQTrue,SQFalse))) { + sq_move(v,thread,-1); + sq_pop(thread,1); //pop retval + if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { + sq_settop(thread,1); //pop roottable + } + return 1; + } + sq_settop(thread,1); + v->_lasterror = thread->_lasterror; + return SQ_ERROR; + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +static SQInteger thread_wakeupthrow(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQVM *thread = _thread(o); + SQInteger state = sq_getvmstate(thread); + if(state != SQ_VMSTATE_SUSPENDED) { + switch(state) { + case SQ_VMSTATE_IDLE: + return sq_throwerror(v,_SC("cannot wakeup a idle thread")); + break; + case SQ_VMSTATE_RUNNING: + return sq_throwerror(v,_SC("cannot wakeup a running thread")); + break; + } + } + + sq_move(thread,v,2); + sq_throwobject(thread); + SQBool rethrow_error = SQTrue; + if(sq_gettop(v) > 2) { + sq_getbool(v,3,&rethrow_error); + } + if(SQ_SUCCEEDED(sq_wakeupvm(thread,SQFalse,SQTrue,SQTrue,SQTrue))) { + sq_move(v,thread,-1); + sq_pop(thread,1); //pop retval + if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { + sq_settop(thread,1); //pop roottable + } + return 1; + } + sq_settop(thread,1); + if(rethrow_error) { + v->_lasterror = thread->_lasterror; + return SQ_ERROR; + } + return SQ_OK; + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +static SQInteger thread_getstatus(HSQUIRRELVM v) +{ + SQObjectPtr &o = stack_get(v,1); + switch(sq_getvmstate(_thread(o))) { + case SQ_VMSTATE_IDLE: + sq_pushstring(v,_SC("idle"),-1); + break; + case SQ_VMSTATE_RUNNING: + sq_pushstring(v,_SC("running"),-1); + break; + case SQ_VMSTATE_SUSPENDED: + sq_pushstring(v,_SC("suspended"),-1); + break; + default: + return sq_throwerror(v,_SC("internal VM error")); + } + return 1; +} + +static SQInteger thread_getstackinfos(HSQUIRRELVM v) +{ + SQObjectPtr o = stack_get(v,1); + if(sq_type(o) == OT_THREAD) { + SQVM *thread = _thread(o); + SQInteger threadtop = sq_gettop(thread); + SQInteger level; + sq_getinteger(v,-1,&level); + SQRESULT res = __getcallstackinfos(thread,level); + if(SQ_FAILED(res)) + { + sq_settop(thread,threadtop); + if(sq_type(thread->_lasterror) == OT_STRING) { + sq_throwerror(v,_stringval(thread->_lasterror)); + } + else { + sq_throwerror(v,_SC("unknown error")); + } + } + if(res > 0) { + //some result + sq_move(v,thread,-1); + sq_settop(thread,threadtop); + return 1; + } + //no result + sq_settop(thread,threadtop); + return 0; + + } + return sq_throwerror(v,_SC("wrong parameter")); +} + +const SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = { + {_SC("call"), thread_call, -1, _SC("v")}, + {_SC("wakeup"), thread_wakeup, -1, _SC("v")}, + {_SC("wakeupthrow"), thread_wakeupthrow, -2, _SC("v.b")}, + {_SC("getstatus"), thread_getstatus, 1, _SC("v")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("getstackinfos"),thread_getstackinfos,2, _SC("vn")}, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +static SQInteger class_getattributes(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_getattributes(v,-2))?1:SQ_ERROR; +} + +static SQInteger class_setattributes(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_setattributes(v,-3))?1:SQ_ERROR; +} + +static SQInteger class_instance(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_createinstance(v,-1))?1:SQ_ERROR; +} + +static SQInteger class_getbase(HSQUIRRELVM v) +{ + return SQ_SUCCEEDED(sq_getbase(v,-1))?1:SQ_ERROR; +} + +static SQInteger class_newmember(HSQUIRRELVM v) +{ + SQInteger top = sq_gettop(v); + SQBool bstatic = SQFalse; + if(top == 5) + { + sq_tobool(v,-1,&bstatic); + sq_pop(v,1); + } + + if(top < 4) { + sq_pushnull(v); + } + return SQ_SUCCEEDED(sq_newmember(v,-4,bstatic))?1:SQ_ERROR; +} + +static SQInteger class_rawnewmember(HSQUIRRELVM v) +{ + SQInteger top = sq_gettop(v); + SQBool bstatic = SQFalse; + if(top == 5) + { + sq_tobool(v,-1,&bstatic); + sq_pop(v,1); + } + + if(top < 4) { + sq_pushnull(v); + } + return SQ_SUCCEEDED(sq_rawnewmember(v,-4,bstatic))?1:SQ_ERROR; +} + +const SQRegFunction SQSharedState::_class_default_delegate_funcz[] = { + {_SC("getattributes"), class_getattributes, 2, _SC("y.")}, + {_SC("setattributes"), class_setattributes, 3, _SC("y..")}, + {_SC("rawget"),container_rawget,2, _SC("y")}, + {_SC("rawset"),container_rawset,3, _SC("y")}, + {_SC("rawin"),container_rawexists,2, _SC("y")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {_SC("instance"),class_instance,1, _SC("y")}, + {_SC("getbase"),class_getbase,1, _SC("y")}, + {_SC("newmember"),class_newmember,-3, _SC("y")}, + {_SC("rawnewmember"),class_rawnewmember,-3, _SC("y")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + + +static SQInteger instance_getclass(HSQUIRRELVM v) +{ + if(SQ_SUCCEEDED(sq_getclass(v,1))) + return 1; + return SQ_ERROR; +} + +const SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = { + {_SC("getclass"), instance_getclass, 1, _SC("x")}, + {_SC("rawget"),container_rawget,2, _SC("x")}, + {_SC("rawset"),container_rawset,3, _SC("x")}, + {_SC("rawin"),container_rawexists,2, _SC("x")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; + +static SQInteger weakref_ref(HSQUIRRELVM v) +{ + if(SQ_FAILED(sq_getweakrefval(v,1))) + return SQ_ERROR; + return 1; +} + +const SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = { + {_SC("ref"),weakref_ref,1, _SC("r")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {_SC("tostring"),default_delegate_tostring,1, _SC(".")}, + {NULL,(SQFUNCTION)0,0,NULL} +}; diff --git a/sp/src/vscript/squirrel/squirrel/sqclass.cpp b/sp/src/vscript/squirrel/squirrel/sqclass.cpp new file mode 100644 index 0000000000..fc619616c0 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqclass.cpp @@ -0,0 +1,210 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqtable.h" +#include "sqclass.h" +#include "sqfuncproto.h" +#include "sqclosure.h" + + + +SQClass::SQClass(SQSharedState *ss,SQClass *base) +{ + _base = base; + _typetag = 0; + _hook = NULL; + _udsize = 0; + _locked = false; + _constructoridx = -1; + if(_base) { + _constructoridx = _base->_constructoridx; + _udsize = _base->_udsize; + _defaultvalues.copy(base->_defaultvalues); + _methods.copy(base->_methods); + _COPY_VECTOR(_metamethods,base->_metamethods,MT_LAST); + __ObjAddRef(_base); + } + _members = base?base->_members->Clone() : SQTable::Create(ss,0); + __ObjAddRef(_members); + + INIT_CHAIN(); + ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); +} + +void SQClass::Finalize() { + _attributes.Null(); + _NULL_SQOBJECT_VECTOR(_defaultvalues,_defaultvalues.size()); + _methods.resize(0); + _NULL_SQOBJECT_VECTOR(_metamethods,MT_LAST); + __ObjRelease(_members); + if(_base) { + __ObjRelease(_base); + } +} + +SQClass::~SQClass() +{ + REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); + Finalize(); +} + +bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) +{ + SQObjectPtr temp; + bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic; + if(_locked && !belongs_to_static_table) + return false; //the class already has an instance so cannot be modified + if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value + { + _defaultvalues[_member_idx(temp)].val = val; + return true; + } + if(belongs_to_static_table) { + SQInteger mmidx; + if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) && + (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) { + _metamethods[mmidx] = val; + } + else { + SQObjectPtr theval = val; + if(_base && sq_type(val) == OT_CLOSURE) { + theval = _closure(val)->Clone(); + _closure(theval)->_base = _base; + __ObjAddRef(_base); //ref for the closure + } + if(sq_type(temp) == OT_NULL) { + bool isconstructor; + SQVM::IsEqual(ss->_constructoridx, key, isconstructor); + if(isconstructor) { + _constructoridx = (SQInteger)_methods.size(); + } + SQClassMember m; + m.val = theval; + _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size()))); + _methods.push_back(m); + } + else { + _methods[_member_idx(temp)].val = theval; + } + } + return true; + } + SQClassMember m; + m.val = val; + _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size()))); + _defaultvalues.push_back(m); + return true; +} + +SQInstance *SQClass::CreateInstance() +{ + if(!_locked) Lock(); + return SQInstance::Create(_opt_ss(this),this); +} + +SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) +{ + SQObjectPtr oval; + SQInteger idx = _members->Next(false,refpos,outkey,oval); + if(idx != -1) { + if(_ismethod(oval)) { + outval = _methods[_member_idx(oval)].val; + } + else { + SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val; + outval = _realval(o); + } + } + return idx; +} + +bool SQClass::SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val) +{ + SQObjectPtr idx; + if(_members->Get(key,idx)) { + if(_isfield(idx)) + _defaultvalues[_member_idx(idx)].attrs = val; + else + _methods[_member_idx(idx)].attrs = val; + return true; + } + return false; +} + +bool SQClass::GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval) +{ + SQObjectPtr idx; + if(_members->Get(key,idx)) { + outval = (_isfield(idx)?_defaultvalues[_member_idx(idx)].attrs:_methods[_member_idx(idx)].attrs); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////// +void SQInstance::Init(SQSharedState *ss) +{ + _userpointer = NULL; + _hook = NULL; + __ObjAddRef(_class); + _delegate = _class->_members; + INIT_CHAIN(); + ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); +} + +SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize) +{ + _memsize = memsize; + _class = c; + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger n = 0; n < nvalues; n++) { + new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val); + } + Init(ss); +} + +SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize) +{ + _memsize = memsize; + _class = i->_class; + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger n = 0; n < nvalues; n++) { + new (&_values[n]) SQObjectPtr(i->_values[n]); + } + Init(ss); +} + +void SQInstance::Finalize() +{ + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + __ObjRelease(_class); + _NULL_SQOBJECT_VECTOR(_values,nvalues); +} + +SQInstance::~SQInstance() +{ + REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); + if(_class){ Finalize(); } //if _class is null it was already finalized by the GC +} + +bool SQInstance::GetMetaMethod(SQVM* SQ_UNUSED_ARG(v),SQMetaMethod mm,SQObjectPtr &res) +{ + if(sq_type(_class->_metamethods[mm]) != OT_NULL) { + res = _class->_metamethods[mm]; + return true; + } + return false; +} + +bool SQInstance::InstanceOf(SQClass *trg) +{ + SQClass *parent = _class; + while(parent != NULL) { + if(parent == trg) + return true; + parent = parent->_base; + } + return false; +} diff --git a/sp/src/vscript/squirrel/squirrel/sqclass.h b/sp/src/vscript/squirrel/squirrel/sqclass.h new file mode 100644 index 0000000000..7d4021721b --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqclass.h @@ -0,0 +1,162 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQCLASS_H_ +#define _SQCLASS_H_ + +struct SQInstance; + +struct SQClassMember { + SQObjectPtr val; + SQObjectPtr attrs; + void Null() { + val.Null(); + attrs.Null(); + } +}; + +typedef sqvector SQClassMemberVec; + +#define MEMBER_TYPE_METHOD 0x01000000 +#define MEMBER_TYPE_FIELD 0x02000000 + +#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD) +#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD) +#define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i)) +#define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i)) +#define _member_type(o) (_integer(o)&0xFF000000) +#define _member_idx(o) (_integer(o)&0x00FFFFFF) + +struct SQClass : public CHAINABLE_OBJ +{ + SQClass(SQSharedState *ss,SQClass *base); +public: + static SQClass* Create(SQSharedState *ss,SQClass *base) { + SQClass *newclass = (SQClass *)SQ_MALLOC(sizeof(SQClass)); + new (newclass) SQClass(ss, base); + return newclass; + } + ~SQClass(); + bool NewSlot(SQSharedState *ss, const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic); + bool Get(const SQObjectPtr &key,SQObjectPtr &val) { + if(_members->Get(key,val)) { + if(_isfield(val)) { + SQObjectPtr &o = _defaultvalues[_member_idx(val)].val; + val = _realval(o); + } + else { + val = _methods[_member_idx(val)].val; + } + return true; + } + return false; + } + bool GetConstructor(SQObjectPtr &ctor) + { + if(_constructoridx != -1) { + ctor = _methods[_constructoridx].val; + return true; + } + return false; + } + bool SetAttributes(const SQObjectPtr &key,const SQObjectPtr &val); + bool GetAttributes(const SQObjectPtr &key,SQObjectPtr &outval); + void Lock() { _locked = true; if(_base) _base->Lock(); } + void Release() { + if (_hook) { _hook(_typetag,0);} + sq_delete(this, SQClass); + } + void Finalize(); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable ** ); + SQObjectType GetType() {return OT_CLASS;} +#endif + SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); + SQInstance *CreateInstance(); + SQTable *_members; + SQClass *_base; + SQClassMemberVec _defaultvalues; + SQClassMemberVec _methods; + SQObjectPtr _metamethods[MT_LAST]; + SQObjectPtr _attributes; + SQUserPointer _typetag; + SQRELEASEHOOK _hook; + bool _locked; + SQInteger _constructoridx; + SQInteger _udsize; +}; + +#define calcinstancesize(_theclass_) \ + (_theclass_->_udsize + sq_aligning(sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))) + +struct SQInstance : public SQDelegable +{ + void Init(SQSharedState *ss); + SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize); + SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize); +public: + static SQInstance* Create(SQSharedState *ss,SQClass *theclass) { + + SQInteger size = calcinstancesize(theclass); + SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); + new (newinst) SQInstance(ss, theclass,size); + if(theclass->_udsize) { + newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize); + } + return newinst; + } + SQInstance *Clone(SQSharedState *ss) + { + SQInteger size = calcinstancesize(_class); + SQInstance *newinst = (SQInstance *)SQ_MALLOC(size); + new (newinst) SQInstance(ss, this,size); + if(_class->_udsize) { + newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize); + } + return newinst; + } + ~SQInstance(); + bool Get(const SQObjectPtr &key,SQObjectPtr &val) { + if(_class->_members->Get(key,val)) { + if(_isfield(val)) { + SQObjectPtr &o = _values[_member_idx(val)]; + val = _realval(o); + } + else { + val = _class->_methods[_member_idx(val)].val; + } + return true; + } + return false; + } + bool Set(const SQObjectPtr &key,const SQObjectPtr &val) { + SQObjectPtr idx; + if(_class->_members->Get(key,idx) && _isfield(idx)) { + _values[_member_idx(idx)] = val; + return true; + } + return false; + } + void Release() { + _uiRef++; + if (_hook) { _hook(_userpointer,0);} + _uiRef--; + if(_uiRef > 0) return; + SQInteger size = _memsize; + this->~SQInstance(); + SQ_FREE(this, size); + } + void Finalize(); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable ** ); + SQObjectType GetType() {return OT_INSTANCE;} +#endif + bool InstanceOf(SQClass *trg); + bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); + + SQClass *_class; + SQUserPointer _userpointer; + SQRELEASEHOOK _hook; + SQInteger _memsize; + SQObjectPtr _values[1]; +}; + +#endif //_SQCLASS_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqclosure.h b/sp/src/vscript/squirrel/squirrel/sqclosure.h new file mode 100644 index 0000000000..66495b9410 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqclosure.h @@ -0,0 +1,201 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQCLOSURE_H_ +#define _SQCLOSURE_H_ + + +#define _CALC_CLOSURE_SIZE(func) (sizeof(SQClosure) + (func->_noutervalues*sizeof(SQObjectPtr)) + (func->_ndefaultparams*sizeof(SQObjectPtr))) + +struct SQFunctionProto; +struct SQClass; +struct SQClosure : public CHAINABLE_OBJ +{ +private: + SQClosure(SQSharedState *ss,SQFunctionProto *func){_function = func; __ObjAddRef(_function); _base = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; _root=NULL;} +public: + static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func,SQWeakRef *root){ + SQInteger size = _CALC_CLOSURE_SIZE(func); + SQClosure *nc=(SQClosure*)SQ_MALLOC(size); + new (nc) SQClosure(ss,func); + nc->_outervalues = (SQObjectPtr *)(nc + 1); + nc->_defaultparams = &nc->_outervalues[func->_noutervalues]; + nc->_root = root; + __ObjAddRef(nc->_root); + _CONSTRUCT_VECTOR(SQObjectPtr,func->_noutervalues,nc->_outervalues); + _CONSTRUCT_VECTOR(SQObjectPtr,func->_ndefaultparams,nc->_defaultparams); + return nc; + } + void Release(){ + SQFunctionProto *f = _function; + SQInteger size = _CALC_CLOSURE_SIZE(f); + _DESTRUCT_VECTOR(SQObjectPtr,f->_noutervalues,_outervalues); + _DESTRUCT_VECTOR(SQObjectPtr,f->_ndefaultparams,_defaultparams); + __ObjRelease(_function); + this->~SQClosure(); + sq_vm_free(this,size); + } + void SetRoot(SQWeakRef *r) + { + __ObjRelease(_root); + _root = r; + __ObjAddRef(_root); + } + SQClosure *Clone() + { + SQFunctionProto *f = _function; + SQClosure * ret = SQClosure::Create(_opt_ss(this),f,_root); + ret->_env = _env; + if(ret->_env) __ObjAddRef(ret->_env); + _COPY_VECTOR(ret->_outervalues,_outervalues,f->_noutervalues); + _COPY_VECTOR(ret->_defaultparams,_defaultparams,f->_ndefaultparams); + return ret; + } + ~SQClosure(); + + bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); + static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){ + SQFunctionProto *f = _function; + _NULL_SQOBJECT_VECTOR(_outervalues,f->_noutervalues); + _NULL_SQOBJECT_VECTOR(_defaultparams,f->_ndefaultparams); + } + SQObjectType GetType() {return OT_CLOSURE;} +#endif + SQWeakRef *_env; + SQWeakRef *_root; + SQClass *_base; + SQFunctionProto *_function; + SQObjectPtr *_outervalues; + SQObjectPtr *_defaultparams; +}; + +////////////////////////////////////////////// +struct SQOuter : public CHAINABLE_OBJ +{ + +private: + SQOuter(SQSharedState *ss, SQObjectPtr *outer){_valptr = outer; _next = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } + +public: + static SQOuter *Create(SQSharedState *ss, SQObjectPtr *outer) + { + SQOuter *nc = (SQOuter*)SQ_MALLOC(sizeof(SQOuter)); + new (nc) SQOuter(ss, outer); + return nc; + } + ~SQOuter() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } + + void Release() + { + this->~SQOuter(); + sq_vm_free(this,sizeof(SQOuter)); + } + +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize() { _value.Null(); } + SQObjectType GetType() {return OT_OUTER;} +#endif + + SQObjectPtr *_valptr; /* pointer to value on stack, or _value below */ + SQInteger _idx; /* idx in stack array, for relocation */ + SQObjectPtr _value; /* value of outer after stack frame is closed */ + SQOuter *_next; /* pointer to next outer when frame is open */ +}; + +////////////////////////////////////////////// +struct SQGenerator : public CHAINABLE_OBJ +{ + enum SQGeneratorState{eRunning,eSuspended,eDead}; +private: + SQGenerator(SQSharedState *ss,SQClosure *closure){_closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);} +public: + static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){ + SQGenerator *nc=(SQGenerator*)SQ_MALLOC(sizeof(SQGenerator)); + new (nc) SQGenerator(ss,closure); + return nc; + } + ~SQGenerator() + { + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); + } + void Kill(){ + _state=eDead; + _stack.resize(0); + _closure.Null();} + void Release(){ + sq_delete(this,SQGenerator); + } + + bool Yield(SQVM *v,SQInteger target); + bool Resume(SQVM *v,SQObjectPtr &dest); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){_stack.resize(0);_closure.Null();} + SQObjectType GetType() {return OT_GENERATOR;} +#endif + SQObjectPtr _closure; + SQObjectPtrVec _stack; + SQVM::CallInfo _ci; + ExceptionsTraps _etraps; + SQGeneratorState _state; +}; + +#define _CALC_NATVIVECLOSURE_SIZE(noutervalues) (sizeof(SQNativeClosure) + (noutervalues*sizeof(SQObjectPtr))) + +struct SQNativeClosure : public CHAINABLE_OBJ +{ +private: + SQNativeClosure(SQSharedState *ss,SQFUNCTION func){_function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL;} +public: + static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func,SQInteger nouters) + { + SQInteger size = _CALC_NATVIVECLOSURE_SIZE(nouters); + SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(size); + new (nc) SQNativeClosure(ss,func); + nc->_outervalues = (SQObjectPtr *)(nc + 1); + nc->_noutervalues = nouters; + _CONSTRUCT_VECTOR(SQObjectPtr,nc->_noutervalues,nc->_outervalues); + return nc; + } + SQNativeClosure *Clone() + { + SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function,_noutervalues); + ret->_env = _env; + if(ret->_env) __ObjAddRef(ret->_env); + ret->_name = _name; + _COPY_VECTOR(ret->_outervalues,_outervalues,_noutervalues); + ret->_typecheck.copy(_typecheck); + ret->_nparamscheck = _nparamscheck; + return ret; + } + ~SQNativeClosure() + { + __ObjRelease(_env); + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); + } + void Release(){ + SQInteger size = _CALC_NATVIVECLOSURE_SIZE(_noutervalues); + _DESTRUCT_VECTOR(SQObjectPtr,_noutervalues,_outervalues); + this->~SQNativeClosure(); + sq_free(this,size); + } + +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize() { _NULL_SQOBJECT_VECTOR(_outervalues,_noutervalues); } + SQObjectType GetType() {return OT_NATIVECLOSURE;} +#endif + SQInteger _nparamscheck; + SQIntVec _typecheck; + SQObjectPtr *_outervalues; + SQUnsignedInteger _noutervalues; + SQWeakRef *_env; + SQFUNCTION _function; + SQObjectPtr _name; +}; + + + +#endif //_SQCLOSURE_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqcompiler.cpp b/sp/src/vscript/squirrel/squirrel/sqcompiler.cpp new file mode 100644 index 0000000000..095edd71c6 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqcompiler.cpp @@ -0,0 +1,1611 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#ifndef NO_COMPILER +#include +#include +#include "sqopcodes.h" +#include "sqstring.h" +#include "sqfuncproto.h" +#include "sqcompiler.h" +#include "sqfuncstate.h" +#include "sqlexer.h" +#include "sqvm.h" +#include "sqtable.h" + +#define EXPR 1 +#define OBJECT 2 +#define BASE 3 +#define LOCAL 4 +#define OUTER 5 + +struct SQExpState { + SQInteger etype; /* expr. type; one of EXPR, OBJECT, BASE, OUTER or LOCAL */ + SQInteger epos; /* expr. location on stack; -1 for OBJECT and BASE */ + bool donot_get; /* signal not to deref the next value */ +}; + +#define MAX_COMPILER_ERROR_LEN 256 + +struct SQScope { + SQInteger outers; + SQInteger stacksize; +}; + +#define BEGIN_SCOPE() SQScope __oldscope__ = _scope; \ + _scope.outers = _fs->_outers; \ + _scope.stacksize = _fs->GetStackSize(); + +#define RESOLVE_OUTERS() if(_fs->GetStackSize() != _scope.stacksize) { \ + if(_fs->CountOuters(_scope.stacksize)) { \ + _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ + } \ + } + +#define END_SCOPE_NO_CLOSE() { if(_fs->GetStackSize() != _scope.stacksize) { \ + _fs->SetStackSize(_scope.stacksize); \ + } \ + _scope = __oldscope__; \ + } + +#define END_SCOPE() { SQInteger oldouters = _fs->_outers;\ + if(_fs->GetStackSize() != _scope.stacksize) { \ + _fs->SetStackSize(_scope.stacksize); \ + if(oldouters != _fs->_outers) { \ + _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ + } \ + } \ + _scope = __oldscope__; \ + } + +#define BEGIN_BREAKBLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \ + SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \ + _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0); + +#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \ + __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \ + if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \ + if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \ + _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();} + +class SQCompiler +{ +public: + SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo) + { + _vm=v; + _lex.Init(_ss(v), rg, up,ThrowError,this); + _sourcename = SQString::Create(_ss(v), sourcename); + _lineinfo = lineinfo;_raiseerror = raiseerror; + _scope.outers = 0; + _scope.stacksize = 0; + _compilererror[0] = _SC('\0'); + } + static void ThrowError(void *ud, const SQChar *s) { + SQCompiler *c = (SQCompiler *)ud; + c->Error(s); + } + void Error(const SQChar *s, ...) + { + va_list vl; + va_start(vl, s); + scvsprintf(_compilererror, MAX_COMPILER_ERROR_LEN, s, vl); + va_end(vl); + longjmp(_errorjmp,1); + } + void Lex(){ _token = _lex.Lex();} + SQObject Expect(SQInteger tok) + { + + if(_token != tok) { + if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) { + //do nothing + } + else { + const SQChar *etypename; + if(tok > 255) { + switch(tok) + { + case TK_IDENTIFIER: + etypename = _SC("IDENTIFIER"); + break; + case TK_STRING_LITERAL: + etypename = _SC("STRING_LITERAL"); + break; + case TK_INTEGER: + etypename = _SC("INTEGER"); + break; + case TK_FLOAT: + etypename = _SC("FLOAT"); + break; + default: + etypename = _lex.Tok2Str(tok); + } + Error(_SC("expected '%s'"), etypename); + } + Error(_SC("expected '%c'"), tok); + } + } + SQObjectPtr ret; + switch(tok) + { + case TK_IDENTIFIER: + ret = _fs->CreateString(_lex._svalue); + break; + case TK_STRING_LITERAL: + ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); + break; + case TK_INTEGER: + ret = SQObjectPtr(_lex._nvalue); + break; + case TK_FLOAT: + ret = SQObjectPtr(_lex._fvalue); + break; + } + Lex(); + return ret; + } + bool IsEndOfStatement() { return ((_lex._prevtoken == _SC('\n')) || (_token == SQUIRREL_EOB) || (_token == _SC('}')) || (_token == _SC(';'))); } + void OptionalSemicolon() + { + if(_token == _SC(';')) { Lex(); return; } + if(!IsEndOfStatement()) { + Error(_SC("end of statement expected (; or lf)")); + } + } + void MoveIfCurrentTargetIsLocal() { + SQInteger trg = _fs->TopTarget(); + if(_fs->IsLocal(trg)) { + trg = _fs->PopTarget(); //pops the target and moves it + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); + } + } + bool Compile(SQObjectPtr &o) + { + _debugline = 1; + _debugop = 0; + + SQFuncState funcstate(_ss(_vm), NULL,ThrowError,this); + funcstate._name = SQString::Create(_ss(_vm), _SC("main")); + _fs = &funcstate; + _fs->AddParameter(_fs->CreateString(_SC("this"))); + _fs->AddParameter(_fs->CreateString(_SC("vargv"))); + _fs->_varparams = true; + _fs->_sourcename = _sourcename; + SQInteger stacksize = _fs->GetStackSize(); + if(setjmp(_errorjmp) == 0) { + Lex(); + while(_token > 0){ + Statement(); + if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + } + _fs->SetStackSize(stacksize); + _fs->AddLineInfos(_lex._currentline, _lineinfo, true); + _fs->AddInstruction(_OP_RETURN, 0xFF); + _fs->SetStackSize(0); + o =_fs->BuildProto(); +#ifdef _DEBUG_DUMP + _fs->Dump(_funcproto(o)); +#endif + } + else { + if(_raiseerror && _ss(_vm)->_compilererrorhandler) { + _ss(_vm)->_compilererrorhandler(_vm, _compilererror, sq_type(_sourcename) == OT_STRING?_stringval(_sourcename):_SC("unknown"), + _lex._currentline, _lex._currentcolumn); + } + _vm->_lasterror = SQString::Create(_ss(_vm), _compilererror, -1); + return false; + } + return true; + } + void Statements() + { + while(_token != _SC('}') && _token != TK_DEFAULT && _token != TK_CASE) { + Statement(); + if(_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + } + } + void Statement(bool closeframe = true) + { + _fs->AddLineInfos(_lex._currentline, _lineinfo); + switch(_token){ + case _SC(';'): Lex(); break; + case TK_IF: IfStatement(); break; + case TK_WHILE: WhileStatement(); break; + case TK_DO: DoWhileStatement(); break; + case TK_FOR: ForStatement(); break; + case TK_FOREACH: ForEachStatement(); break; + case TK_SWITCH: SwitchStatement(); break; + case TK_LOCAL: LocalDeclStatement(); break; + case TK_RETURN: + case TK_YIELD: { + SQOpcode op; + if(_token == TK_RETURN) { + op = _OP_RETURN; + } + else { + op = _OP_YIELD; + _fs->_bgenerator = true; + } + Lex(); + if(!IsEndOfStatement()) { + SQInteger retexp = _fs->GetCurrentPos()+1; + CommaExpr(); + if(op == _OP_RETURN && _fs->_traps > 0) + _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); + _fs->_returnexp = retexp; + _fs->AddInstruction(op, 1, _fs->PopTarget(),_fs->GetStackSize()); + } + else{ + if(op == _OP_RETURN && _fs->_traps > 0) + _fs->AddInstruction(_OP_POPTRAP, _fs->_traps ,0); + _fs->_returnexp = -1; + _fs->AddInstruction(op, 0xFF,0,_fs->GetStackSize()); + } + break;} + case TK_BREAK: + if(_fs->_breaktargets.size() <= 0)Error(_SC("'break' has to be in a loop block")); + if(_fs->_breaktargets.top() > 0){ + _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); + } + RESOLVE_OUTERS(); + _fs->AddInstruction(_OP_JMP, 0, -1234); + _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); + Lex(); + break; + case TK_CONTINUE: + if(_fs->_continuetargets.size() <= 0)Error(_SC("'continue' has to be in a loop block")); + if(_fs->_continuetargets.top() > 0) { + _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); + } + RESOLVE_OUTERS(); + _fs->AddInstruction(_OP_JMP, 0, -1234); + _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); + Lex(); + break; + case TK_FUNCTION: + FunctionStatement(); + break; + case TK_CLASS: + ClassStatement(); + break; + case TK_ENUM: + EnumStatement(); + break; + case _SC('{'):{ + BEGIN_SCOPE(); + Lex(); + Statements(); + Expect(_SC('}')); + if(closeframe) { + END_SCOPE(); + } + else { + END_SCOPE_NO_CLOSE(); + } + } + break; + case TK_TRY: + TryCatchStatement(); + break; + case TK_THROW: + Lex(); + CommaExpr(); + _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); + break; + case TK_CONST: + { + Lex(); + SQObject id = Expect(TK_IDENTIFIER); + Expect('='); + SQObject val = ExpectScalar(); + OptionalSemicolon(); + SQTable *enums = _table(_ss(_vm)->_consts); + SQObjectPtr strongid = id; + enums->NewSlot(strongid,SQObjectPtr(val)); + strongid.Null(); + } + break; + default: + CommaExpr(); + _fs->DiscardTarget(); + //_fs->PopTarget(); + break; + } + _fs->SnoozeOpt(); + } + void EmitDerefOp(SQOpcode op) + { + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(op,_fs->PushTarget(),src,key,val); + } + void Emit2ArgsOP(SQOpcode op, SQInteger p3 = 0) + { + SQInteger p2 = _fs->PopTarget(); //src in OP_GET + SQInteger p1 = _fs->PopTarget(); //key in OP_GET + _fs->AddInstruction(op,_fs->PushTarget(), p1, p2, p3); + } + void EmitCompoundArith(SQInteger tok, SQInteger etype, SQInteger pos) + { + /* Generate code depending on the expression type */ + switch(etype) { + case LOCAL:{ + SQInteger p2 = _fs->PopTarget(); //src in OP_GET + SQInteger p1 = _fs->PopTarget(); //key in OP_GET + _fs->PushTarget(p1); + //EmitCompArithLocal(tok, p1, p1, p2); + _fs->AddInstruction(ChooseArithOpByToken(tok),p1, p2, p1, 0); + _fs->SnoozeOpt(); + } + break; + case OBJECT: + case BASE: + { + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger src = _fs->PopTarget(); + /* _OP_COMPARITH mixes dest obj and source val in the arg1 */ + _fs->AddInstruction(_OP_COMPARITH, _fs->PushTarget(), (src<<16)|val, key, ChooseCompArithCharByToken(tok)); + } + break; + case OUTER: + { + SQInteger val = _fs->TopTarget(); + SQInteger tmp = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, tmp, pos); + _fs->AddInstruction(ChooseArithOpByToken(tok), tmp, val, tmp, 0); + _fs->PopTarget(); + _fs->PopTarget(); + _fs->AddInstruction(_OP_SETOUTER, _fs->PushTarget(), pos, tmp); + } + break; + } + } + void CommaExpr() + { + for(Expression();_token == ',';_fs->PopTarget(), Lex(), CommaExpr()); + } + void Expression() + { + SQExpState es = _es; + _es.etype = EXPR; + _es.epos = -1; + _es.donot_get = false; + LogicalOrExp(); + switch(_token) { + case _SC('='): + case TK_NEWSLOT: + case TK_MINUSEQ: + case TK_PLUSEQ: + case TK_MULEQ: + case TK_DIVEQ: + case TK_MODEQ:{ + SQInteger op = _token; + SQInteger ds = _es.etype; + SQInteger pos = _es.epos; + if(ds == EXPR) Error(_SC("can't assign expression")); + else if(ds == BASE) Error(_SC("'base' cannot be modified")); + Lex(); Expression(); + + switch(op){ + case TK_NEWSLOT: + if(ds == OBJECT || ds == BASE) + EmitDerefOp(_OP_NEWSLOT); + else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local + Error(_SC("can't 'create' a local slot")); + break; + case _SC('='): //ASSIGN + switch(ds) { + case LOCAL: + { + SQInteger src = _fs->PopTarget(); + SQInteger dst = _fs->TopTarget(); + _fs->AddInstruction(_OP_MOVE, dst, src); + } + break; + case OBJECT: + case BASE: + EmitDerefOp(_OP_SET); + break; + case OUTER: + { + SQInteger src = _fs->PopTarget(); + SQInteger dst = _fs->PushTarget(); + _fs->AddInstruction(_OP_SETOUTER, dst, pos, src); + } + } + break; + case TK_MINUSEQ: + case TK_PLUSEQ: + case TK_MULEQ: + case TK_DIVEQ: + case TK_MODEQ: + EmitCompoundArith(op, ds, pos); + break; + } + } + break; + case _SC('?'): { + Lex(); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + SQInteger jzpos = _fs->GetCurrentPos(); + SQInteger trg = _fs->PushTarget(); + Expression(); + SQInteger first_exp = _fs->PopTarget(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + SQInteger endfirstexp = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_JMP, 0, 0); + Expect(_SC(':')); + SQInteger jmppos = _fs->GetCurrentPos(); + Expression(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + _fs->SetInstructionParam(jzpos, 1, endfirstexp - jzpos + 1); + _fs->SnoozeOpt(); + } + break; + } + _es = es; + } + template void INVOKE_EXP(T f) + { + SQExpState es = _es; + _es.etype = EXPR; + _es.epos = -1; + _es.donot_get = false; + (this->*f)(); + _es = es; + } + template void BIN_EXP(SQOpcode op, T f,SQInteger op3 = 0) + { + Lex(); + INVOKE_EXP(f); + SQInteger op1 = _fs->PopTarget();SQInteger op2 = _fs->PopTarget(); + _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); + _es.etype = EXPR; + } + void LogicalOrExp() + { + LogicalAndExp(); + for(;;) if(_token == TK_OR) { + SQInteger first_exp = _fs->PopTarget(); + SQInteger trg = _fs->PushTarget(); + _fs->AddInstruction(_OP_OR, trg, 0, first_exp, 0); + SQInteger jpos = _fs->GetCurrentPos(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + Lex(); INVOKE_EXP(&SQCompiler::LogicalOrExp); + _fs->SnoozeOpt(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SnoozeOpt(); + _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _es.etype = EXPR; + break; + }else return; + } + void LogicalAndExp() + { + BitwiseOrExp(); + for(;;) switch(_token) { + case TK_AND: { + SQInteger first_exp = _fs->PopTarget(); + SQInteger trg = _fs->PushTarget(); + _fs->AddInstruction(_OP_AND, trg, 0, first_exp, 0); + SQInteger jpos = _fs->GetCurrentPos(); + if(trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); + Lex(); INVOKE_EXP(&SQCompiler::LogicalAndExp); + _fs->SnoozeOpt(); + SQInteger second_exp = _fs->PopTarget(); + if(trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); + _fs->SnoozeOpt(); + _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); + _es.etype = EXPR; + break; + } + + default: + return; + } + } + void BitwiseOrExp() + { + BitwiseXorExp(); + for(;;) if(_token == _SC('|')) + {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseXorExp,BW_OR); + }else return; + } + void BitwiseXorExp() + { + BitwiseAndExp(); + for(;;) if(_token == _SC('^')) + {BIN_EXP(_OP_BITW, &SQCompiler::BitwiseAndExp,BW_XOR); + }else return; + } + void BitwiseAndExp() + { + EqExp(); + for(;;) if(_token == _SC('&')) + {BIN_EXP(_OP_BITW, &SQCompiler::EqExp,BW_AND); + }else return; + } + void EqExp() + { + CompExp(); + for(;;) switch(_token) { + case TK_EQ: BIN_EXP(_OP_EQ, &SQCompiler::CompExp); break; + case TK_NE: BIN_EXP(_OP_NE, &SQCompiler::CompExp); break; + case TK_3WAYSCMP: BIN_EXP(_OP_CMP, &SQCompiler::CompExp,CMP_3W); break; + default: return; + } + } + void CompExp() + { + ShiftExp(); + for(;;) switch(_token) { + case _SC('>'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_G); break; + case _SC('<'): BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_L); break; + case TK_GE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_GE); break; + case TK_LE: BIN_EXP(_OP_CMP, &SQCompiler::ShiftExp,CMP_LE); break; + case TK_IN: BIN_EXP(_OP_EXISTS, &SQCompiler::ShiftExp); break; + case TK_INSTANCEOF: BIN_EXP(_OP_INSTANCEOF, &SQCompiler::ShiftExp); break; + default: return; + } + } + void ShiftExp() + { + PlusExp(); + for(;;) switch(_token) { + case TK_USHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_USHIFTR); break; + case TK_SHIFTL: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTL); break; + case TK_SHIFTR: BIN_EXP(_OP_BITW, &SQCompiler::PlusExp,BW_SHIFTR); break; + default: return; + } + } + SQOpcode ChooseArithOpByToken(SQInteger tok) + { + switch(tok) { + case TK_PLUSEQ: case '+': return _OP_ADD; + case TK_MINUSEQ: case '-': return _OP_SUB; + case TK_MULEQ: case '*': return _OP_MUL; + case TK_DIVEQ: case '/': return _OP_DIV; + case TK_MODEQ: case '%': return _OP_MOD; + default: assert(0); + } + return _OP_ADD; + } + SQInteger ChooseCompArithCharByToken(SQInteger tok) + { + SQInteger oper; + switch(tok){ + case TK_MINUSEQ: oper = '-'; break; + case TK_PLUSEQ: oper = '+'; break; + case TK_MULEQ: oper = '*'; break; + case TK_DIVEQ: oper = '/'; break; + case TK_MODEQ: oper = '%'; break; + default: oper = 0; //shut up compiler + assert(0); break; + }; + return oper; + } + void PlusExp() + { + MultExp(); + for(;;) switch(_token) { + case _SC('+'): case _SC('-'): + BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::MultExp); break; + default: return; + } + } + + void MultExp() + { + PrefixedExpr(); + for(;;) switch(_token) { + case _SC('*'): case _SC('/'): case _SC('%'): + BIN_EXP(ChooseArithOpByToken(_token), &SQCompiler::PrefixedExpr); break; + default: return; + } + } + //if 'pos' != -1 the previous variable is a local variable + void PrefixedExpr() + { + SQInteger pos = Factor(); + for(;;) { + switch(_token) { + case _SC('.'): + pos = -1; + Lex(); + + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + if(_es.etype==BASE) { + Emit2ArgsOP(_OP_GET); + pos = _fs->TopTarget(); + _es.etype = EXPR; + _es.epos = pos; + } + else { + if(NeedGet()) { + Emit2ArgsOP(_OP_GET); + } + _es.etype = OBJECT; + } + break; + case _SC('['): + if(_lex._prevtoken == _SC('\n')) Error(_SC("cannot break deref/or comma needed after [exp]=exp slot declaration")); + Lex(); Expression(); Expect(_SC(']')); + pos = -1; + if(_es.etype==BASE) { + Emit2ArgsOP(_OP_GET); + pos = _fs->TopTarget(); + _es.etype = EXPR; + _es.epos = pos; + } + else { + if(NeedGet()) { + Emit2ArgsOP(_OP_GET); + } + _es.etype = OBJECT; + } + break; + case TK_MINUSMINUS: + case TK_PLUSPLUS: + { + if(IsEndOfStatement()) return; + SQInteger diff = (_token==TK_MINUSMINUS) ? -1 : 1; + Lex(); + switch(_es.etype) + { + case EXPR: Error(_SC("can't '++' or '--' an expression")); break; + case OBJECT: + case BASE: + if(_es.donot_get == true) { Error(_SC("can't '++' or '--' an expression")); break; } //mmh dor this make sense? + Emit2ArgsOP(_OP_PINC, diff); + break; + case LOCAL: { + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(_OP_PINCL, _fs->PushTarget(), src, 0, diff); + } + break; + case OUTER: { + SQInteger tmp1 = _fs->PushTarget(); + SQInteger tmp2 = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, tmp2, _es.epos); + _fs->AddInstruction(_OP_PINCL, tmp1, tmp2, 0, diff); + _fs->AddInstruction(_OP_SETOUTER, tmp2, _es.epos, tmp2); + _fs->PopTarget(); + } + } + } + return; + break; + case _SC('('): + switch(_es.etype) { + case OBJECT: { + SQInteger key = _fs->PopTarget(); /* location of the key */ + SQInteger table = _fs->PopTarget(); /* location of the object */ + SQInteger closure = _fs->PushTarget(); /* location for the closure */ + SQInteger ttarget = _fs->PushTarget(); /* location for 'this' pointer */ + _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); + } + break; + case BASE: + //Emit2ArgsOP(_OP_GET); + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + break; + case OUTER: + _fs->AddInstruction(_OP_GETOUTER, _fs->PushTarget(), _es.epos); + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + break; + default: + _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); + } + _es.etype = EXPR; + Lex(); + FunctionCallArgs(); + break; + default: return; + } + } + } + SQInteger Factor() + { + //_es.etype = EXPR; + switch(_token) + { + case TK_STRING_LITERAL: + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); + Lex(); + break; + case TK_BASE: + Lex(); + _fs->AddInstruction(_OP_GETBASE, _fs->PushTarget()); + _es.etype = BASE; + _es.epos = _fs->TopTarget(); + return (_es.epos); + break; + case TK_IDENTIFIER: + case TK_CONSTRUCTOR: + case TK_THIS:{ + SQObject id; + SQObject constant; + + switch(_token) { + case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break; + case TK_THIS: id = _fs->CreateString(_SC("this"),4); break; + case TK_CONSTRUCTOR: id = _fs->CreateString(_SC("constructor"),11); break; + } + + SQInteger pos = -1; + Lex(); + if((pos = _fs->GetLocalVariable(id)) != -1) { + /* Handle a local variable (includes 'this') */ + _fs->PushTarget(pos); + _es.etype = LOCAL; + _es.epos = pos; + } + + else if((pos = _fs->GetOuterVariable(id)) != -1) { + /* Handle a free var */ + if(NeedGet()) { + _es.epos = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, _es.epos, pos); + /* _es.etype = EXPR; already default value */ + } + else { + _es.etype = OUTER; + _es.epos = pos; + } + } + + else if(_fs->IsConstant(id, constant)) { + /* Handle named constant */ + SQObjectPtr constval; + SQObject constid; + if(sq_type(constant) == OT_TABLE) { + Expect('.'); + constid = Expect(TK_IDENTIFIER); + if(!_table(constant)->Get(constid, constval)) { + constval.Null(); + Error(_SC("invalid constant [%s.%s]"), _stringval(id), _stringval(constid)); + } + } + else { + constval = constant; + } + _es.epos = _fs->PushTarget(); + + /* generate direct or literal function depending on size */ + SQObjectType ctype = sq_type(constval); + switch(ctype) { + case OT_INTEGER: EmitLoadConstInt(_integer(constval),_es.epos); break; + case OT_FLOAT: EmitLoadConstFloat(_float(constval),_es.epos); break; + case OT_BOOL: _fs->AddInstruction(_OP_LOADBOOL, _es.epos, _integer(constval)); break; + default: _fs->AddInstruction(_OP_LOAD,_es.epos,_fs->GetConstant(constval)); break; + } + _es.etype = EXPR; + } + else { + /* Handle a non-local variable, aka a field. Push the 'this' pointer on + * the virtual stack (always found in offset 0, so no instruction needs to + * be generated), and push the key next. Generate an _OP_LOAD instruction + * for the latter. If we are not using the variable as a dref expr, generate + * the _OP_GET instruction. + */ + _fs->PushTarget(0); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(NeedGet()) { + Emit2ArgsOP(_OP_GET); + } + _es.etype = OBJECT; + } + return _es.epos; + } + break; + case TK_DOUBLE_COLON: // "::" + _fs->AddInstruction(_OP_LOADROOT, _fs->PushTarget()); + _es.etype = OBJECT; + _token = _SC('.'); /* hack: drop into PrefixExpr, case '.'*/ + _es.epos = -1; + return _es.epos; + break; + case TK_NULL: + _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); + Lex(); + break; + case TK_INTEGER: EmitLoadConstInt(_lex._nvalue,-1); Lex(); break; + case TK_FLOAT: EmitLoadConstFloat(_lex._fvalue,-1); Lex(); break; + case TK_TRUE: case TK_FALSE: + _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(),_token == TK_TRUE?1:0); + Lex(); + break; + case _SC('['): { + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,0,NOT_ARRAY); + SQInteger apos = _fs->GetCurrentPos(),key = 0; + Lex(); + while(_token != _SC(']')) { + Expression(); + if(_token == _SC(',')) Lex(); + SQInteger val = _fs->PopTarget(); + SQInteger array = _fs->TopTarget(); + _fs->AddInstruction(_OP_APPENDARRAY, array, val, AAT_STACK); + key++; + } + _fs->SetInstructionParam(apos, 1, key); + Lex(); + } + break; + case _SC('{'): + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); + Lex();ParseTableOrClass(_SC(','),_SC('}')); + break; + case TK_FUNCTION: FunctionExp(_token);break; + case _SC('@'): FunctionExp(_token,true);break; + case TK_CLASS: Lex(); ClassExp();break; + case _SC('-'): + Lex(); + switch(_token) { + case TK_INTEGER: EmitLoadConstInt(-_lex._nvalue,-1); Lex(); break; + case TK_FLOAT: EmitLoadConstFloat(-_lex._fvalue,-1); Lex(); break; + default: UnaryOP(_OP_NEG); + } + break; + case _SC('!'): Lex(); UnaryOP(_OP_NOT); break; + case _SC('~'): + Lex(); + if(_token == TK_INTEGER) { EmitLoadConstInt(~_lex._nvalue,-1); Lex(); break; } + UnaryOP(_OP_BWNOT); + break; + case TK_TYPEOF : Lex() ;UnaryOP(_OP_TYPEOF); break; + case TK_RESUME : Lex(); UnaryOP(_OP_RESUME); break; + case TK_CLONE : Lex(); UnaryOP(_OP_CLONE); break; + case TK_RAWCALL: Lex(); Expect('('); FunctionCallArgs(true); break; + case TK_MINUSMINUS : + case TK_PLUSPLUS :PrefixIncDec(_token); break; + case TK_DELETE : DeleteExpr(); break; + case _SC('('): Lex(); CommaExpr(); Expect(_SC(')')); + break; + case TK___LINE__: EmitLoadConstInt(_lex._currentline,-1); Lex(); break; + case TK___FILE__: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_sourcename)); Lex(); break; + default: Error(_SC("expression expected")); + } + _es.etype = EXPR; + return -1; + } + void EmitLoadConstInt(SQInteger value,SQInteger target) + { + if(target < 0) { + target = _fs->PushTarget(); + } + if(value <= INT_MAX && value > INT_MIN) { //does it fit in 32 bits? + _fs->AddInstruction(_OP_LOADINT, target,value); + } + else { + _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); + } + } + void EmitLoadConstFloat(SQFloat value,SQInteger target) + { + if(target < 0) { + target = _fs->PushTarget(); + } + if(sizeof(SQFloat) == sizeof(SQInt32)) { + _fs->AddInstruction(_OP_LOADFLOAT, target,*((SQInt32 *)&value)); + } + else { + _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); + } + } + void UnaryOP(SQOpcode op) + { + PrefixedExpr(); + SQInteger src = _fs->PopTarget(); + _fs->AddInstruction(op, _fs->PushTarget(), src); + } + bool NeedGet() + { + switch(_token) { + case _SC('='): case _SC('('): case TK_NEWSLOT: case TK_MODEQ: case TK_MULEQ: + case TK_DIVEQ: case TK_MINUSEQ: case TK_PLUSEQ: + return false; + case TK_PLUSPLUS: case TK_MINUSMINUS: + if (!IsEndOfStatement()) { + return false; + } + break; + } + return (!_es.donot_get || ( _es.donot_get && (_token == _SC('.') || _token == _SC('[')))); + } + void FunctionCallArgs(bool rawcall = false) + { + SQInteger nargs = 1;//this + while(_token != _SC(')')) { + Expression(); + MoveIfCurrentTargetIsLocal(); + nargs++; + if(_token == _SC(',')){ + Lex(); + if(_token == ')') Error(_SC("expression expected, found ')'")); + } + } + Lex(); + if (rawcall) { + if (nargs < 3) Error(_SC("rawcall requires at least 2 parameters (callee and this)")); + nargs -= 2; //removes callee and this from count + } + for(SQInteger i = 0; i < (nargs - 1); i++) _fs->PopTarget(); + SQInteger stackbase = _fs->PopTarget(); + SQInteger closure = _fs->PopTarget(); + _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); + if (_token == '{') + { + SQInteger retval = _fs->TopTarget(); + SQInteger nkeys = 0; + Lex(); + while (_token != '}') { + switch (_token) { + case _SC('['): + Lex(); CommaExpr(); Expect(_SC(']')); + Expect(_SC('=')); Expression(); + break; + default: + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + Expect(_SC('=')); Expression(); + break; + } + if (_token == ',') Lex(); + nkeys++; + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + _fs->AddInstruction(_OP_SET, 0xFF, retval, key, val); + } + Lex(); + } + } + void ParseTableOrClass(SQInteger separator,SQInteger terminator) + { + SQInteger tpos = _fs->GetCurrentPos(),nkeys = 0; + while(_token != terminator) { + bool hasattrs = false; + bool isstatic = false; + //check if is an attribute + if(separator == ';') { + if(_token == TK_ATTR_OPEN) { + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); Lex(); + ParseTableOrClass(',',TK_ATTR_CLOSE); + hasattrs = true; + } + if(_token == TK_STATIC) { + isstatic = true; + Lex(); + } + } + switch(_token) { + case TK_FUNCTION: + case TK_CONSTRUCTOR:{ + SQInteger tk = _token; + Lex(); + SQObject id = tk == TK_FUNCTION ? Expect(TK_IDENTIFIER) : _fs->CreateString(_SC("constructor")); + Expect(_SC('(')); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + CreateFunction(id); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + } + break; + case _SC('['): + Lex(); CommaExpr(); Expect(_SC(']')); + Expect(_SC('=')); Expression(); + break; + case TK_STRING_LITERAL: //JSON + if(separator == ',') { //only works for tables + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_STRING_LITERAL))); + Expect(_SC(':')); Expression(); + break; + } + default : + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(Expect(TK_IDENTIFIER))); + Expect(_SC('=')); Expression(); + } + if(_token == separator) Lex();//optional comma/semicolon + nkeys++; + SQInteger val = _fs->PopTarget(); + SQInteger key = _fs->PopTarget(); + SQInteger attrs = hasattrs ? _fs->PopTarget():-1; + ((void)attrs); + assert((hasattrs && (attrs == key-1)) || !hasattrs); + unsigned char flags = (hasattrs?NEW_SLOT_ATTRIBUTES_FLAG:0)|(isstatic?NEW_SLOT_STATIC_FLAG:0); + SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOT, 0xFF, table, key, val); + } + else { + _fs->AddInstruction(_OP_NEWSLOTA, flags, table, key, val); //this for classes only as it invokes _newmember + } + } + if(separator == _SC(',')) //hack recognizes a table from the separator + _fs->SetInstructionParam(tpos, 1, nkeys); + Lex(); + } + void LocalDeclStatement() + { + SQObject varname; + Lex(); + if( _token == TK_FUNCTION) { + Lex(); + varname = Expect(TK_IDENTIFIER); + Expect(_SC('(')); + CreateFunction(varname,false); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + _fs->PopTarget(); + _fs->PushLocalVariable(varname); + return; + } + + do { + varname = Expect(TK_IDENTIFIER); + if(_token == _SC('=')) { + Lex(); Expression(); + SQInteger src = _fs->PopTarget(); + SQInteger dest = _fs->PushTarget(); + if(dest != src) _fs->AddInstruction(_OP_MOVE, dest, src); + } + else{ + _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(),1); + } + _fs->PopTarget(); + _fs->PushLocalVariable(varname); + if(_token == _SC(',')) Lex(); else break; + } while(1); + } + void IfBlock() + { + if (_token == _SC('{')) + { + BEGIN_SCOPE(); + Lex(); + Statements(); + Expect(_SC('}')); + if (true) { + END_SCOPE(); + } + else { + END_SCOPE_NO_CLOSE(); + } + } + else { + //BEGIN_SCOPE(); + Statement(); + if (_lex._prevtoken != _SC('}') && _lex._prevtoken != _SC(';')) OptionalSemicolon(); + //END_SCOPE(); + } + } + void IfStatement() + { + SQInteger jmppos; + bool haselse = false; + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + SQInteger jnepos = _fs->GetCurrentPos(); + + + + IfBlock(); + // + /*static int n = 0; + if (_token != _SC('}') && _token != TK_ELSE) { + printf("IF %d-----------------------!!!!!!!!!\n", n); + if (n == 5) + { + printf("asd"); + } + n++; + //OptionalSemicolon(); + }*/ + + + SQInteger endifblock = _fs->GetCurrentPos(); + if(_token == TK_ELSE){ + haselse = true; + //BEGIN_SCOPE(); + _fs->AddInstruction(_OP_JMP); + jmppos = _fs->GetCurrentPos(); + Lex(); + //Statement(); if(_lex._prevtoken != _SC('}')) OptionalSemicolon(); + IfBlock(); + //END_SCOPE(); + _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); + } + _fs->SetInstructionParam(jnepos, 1, endifblock - jnepos + (haselse?1:0)); + } + void WhileStatement() + { + SQInteger jzpos, jmppos; + jmppos = _fs->GetCurrentPos(); + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + + BEGIN_BREAKBLE_BLOCK(); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); + jzpos = _fs->GetCurrentPos(); + BEGIN_SCOPE(); + + Statement(); + + END_SCOPE(); + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); + _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + + END_BREAKBLE_BLOCK(jmppos); + } + void DoWhileStatement() + { + Lex(); + SQInteger jmptrg = _fs->GetCurrentPos(); + BEGIN_BREAKBLE_BLOCK() + BEGIN_SCOPE(); + Statement(); + END_SCOPE(); + Expect(TK_WHILE); + SQInteger continuetrg = _fs->GetCurrentPos(); + Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), 1); + _fs->AddInstruction(_OP_JMP, 0, jmptrg - _fs->GetCurrentPos() - 1); + END_BREAKBLE_BLOCK(continuetrg); + } + void ForStatement() + { + Lex(); + BEGIN_SCOPE(); + Expect(_SC('(')); + if(_token == TK_LOCAL) LocalDeclStatement(); + else if(_token != _SC(';')){ + CommaExpr(); + _fs->PopTarget(); + } + Expect(_SC(';')); + _fs->SnoozeOpt(); + SQInteger jmppos = _fs->GetCurrentPos(); + SQInteger jzpos = -1; + if(_token != _SC(';')) { CommaExpr(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } + Expect(_SC(';')); + _fs->SnoozeOpt(); + SQInteger expstart = _fs->GetCurrentPos() + 1; + if(_token != _SC(')')) { + CommaExpr(); + _fs->PopTarget(); + } + Expect(_SC(')')); + _fs->SnoozeOpt(); + SQInteger expend = _fs->GetCurrentPos(); + SQInteger expsize = (expend - expstart) + 1; + SQInstructionVec exp; + if(expsize > 0) { + for(SQInteger i = 0; i < expsize; i++) + exp.push_back(_fs->GetInstruction(expstart + i)); + _fs->PopInstructions(expsize); + } + BEGIN_BREAKBLE_BLOCK() + Statement(); + SQInteger continuetrg = _fs->GetCurrentPos(); + if(expsize > 0) { + for(SQInteger i = 0; i < expsize; i++) + _fs->AddInstruction(exp[i]); + } + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); + if(jzpos> 0) _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); + + END_BREAKBLE_BLOCK(continuetrg); + + END_SCOPE(); + } + void ForEachStatement() + { + SQObject idxname, valname; + Lex(); Expect(_SC('(')); valname = Expect(TK_IDENTIFIER); + if(_token == _SC(',')) { + idxname = valname; + Lex(); valname = Expect(TK_IDENTIFIER); + } + else{ + idxname = _fs->CreateString(_SC("@INDEX@")); + } + Expect(TK_IN); + + //save the stack size + BEGIN_SCOPE(); + //put the table in the stack(evaluate the table expression) + Expression(); Expect(_SC(')')); + SQInteger container = _fs->TopTarget(); + //push the index local var + SQInteger indexpos = _fs->PushLocalVariable(idxname); + _fs->AddInstruction(_OP_LOADNULLS, indexpos,1); + //push the value local var + SQInteger valuepos = _fs->PushLocalVariable(valname); + _fs->AddInstruction(_OP_LOADNULLS, valuepos,1); + //push reference index + SQInteger itrpos = _fs->PushLocalVariable(_fs->CreateString(_SC("@ITERATOR@"))); //use invalid id to make it inaccessible + _fs->AddInstruction(_OP_LOADNULLS, itrpos,1); + SQInteger jmppos = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_FOREACH, container, 0, indexpos); + SQInteger foreachpos = _fs->GetCurrentPos(); + _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos); + //generate the statement code + BEGIN_BREAKBLE_BLOCK() + Statement(); + _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); + _fs->SetInstructionParam(foreachpos, 1, _fs->GetCurrentPos() - foreachpos); + _fs->SetInstructionParam(foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos); + END_BREAKBLE_BLOCK(foreachpos - 1); + //restore the local variable stack(remove index,val and ref idx) + _fs->PopTarget(); + END_SCOPE(); + } + void SwitchStatement() + { + Lex(); Expect(_SC('(')); CommaExpr(); Expect(_SC(')')); + Expect(_SC('{')); + SQInteger expr = _fs->TopTarget(); + bool bfirst = true; + SQInteger tonextcondjmp = -1; + SQInteger skipcondjmp = -1; + SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size(); + _fs->_breaktargets.push_back(0); + while(_token == TK_CASE) { + if(!bfirst) { + _fs->AddInstruction(_OP_JMP, 0, 0); + skipcondjmp = _fs->GetCurrentPos(); + _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + } + //condition + Lex(); Expression(); Expect(_SC(':')); + SQInteger trg = _fs->PopTarget(); + SQInteger eqtarget = trg; + bool local = _fs->IsLocal(trg); + if(local) { + eqtarget = _fs->PushTarget(); //we need to allocate a extra reg + } + _fs->AddInstruction(_OP_EQ, eqtarget, trg, expr); + _fs->AddInstruction(_OP_JZ, eqtarget, 0); + if(local) { + _fs->PopTarget(); + } + + //end condition + if(skipcondjmp != -1) { + _fs->SetInstructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); + } + tonextcondjmp = _fs->GetCurrentPos(); + BEGIN_SCOPE(); + Statements(); + END_SCOPE(); + bfirst = false; + } + if(tonextcondjmp != -1) + _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); + if(_token == TK_DEFAULT) { + Lex(); Expect(_SC(':')); + BEGIN_SCOPE(); + Statements(); + END_SCOPE(); + } + Expect(_SC('}')); + _fs->PopTarget(); + __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; + if(__nbreaks__ > 0)ResolveBreaks(_fs, __nbreaks__); + _fs->_breaktargets.pop_back(); + } + void FunctionStatement() + { + SQObject id; + Lex(); id = Expect(TK_IDENTIFIER); + _fs->PushTarget(0); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); + + while(_token == TK_DOUBLE_COLON) { + Lex(); + id = Expect(TK_IDENTIFIER); + _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(id)); + if(_token == TK_DOUBLE_COLON) Emit2ArgsOP(_OP_GET); + } + Expect(_SC('(')); + CreateFunction(id); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0); + EmitDerefOp(_OP_NEWSLOT); + _fs->PopTarget(); + } + void ClassStatement() + { + SQExpState es; + Lex(); + es = _es; + _es.donot_get = true; + PrefixedExpr(); + if(_es.etype == EXPR) { + Error(_SC("invalid class name")); + } + else if(_es.etype == OBJECT || _es.etype == BASE) { + ClassExp(); + EmitDerefOp(_OP_NEWSLOT); + _fs->PopTarget(); + } + else { + Error(_SC("cannot create a class in a local with the syntax(class )")); + } + _es = es; + } + SQObject ExpectScalar() + { + SQObject val; + val._type = OT_NULL; val._unVal.nInteger = 0; //shut up GCC 4.x + switch(_token) { + case TK_INTEGER: + val._type = OT_INTEGER; + val._unVal.nInteger = _lex._nvalue; + break; + case TK_FLOAT: + val._type = OT_FLOAT; + val._unVal.fFloat = _lex._fvalue; + break; + case TK_STRING_LITERAL: + val = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); + break; + case TK_TRUE: + case TK_FALSE: + val._type = OT_BOOL; + val._unVal.nInteger = _token == TK_TRUE ? 1 : 0; + break; + case '-': + Lex(); + switch(_token) + { + case TK_INTEGER: + val._type = OT_INTEGER; + val._unVal.nInteger = -_lex._nvalue; + break; + case TK_FLOAT: + val._type = OT_FLOAT; + val._unVal.fFloat = -_lex._fvalue; + break; + default: + Error(_SC("scalar expected : integer, float")); + } + break; + default: + Error(_SC("scalar expected : integer, float, or string")); + } + Lex(); + return val; + } + void EnumStatement() + { + Lex(); + SQObject id = Expect(TK_IDENTIFIER); + Expect(_SC('{')); + + SQObject table = _fs->CreateTable(); + SQInteger nval = 0; + while(_token != _SC('}')) { + SQObject key = Expect(TK_IDENTIFIER); + SQObject val; + if(_token == _SC('=')) { + Lex(); + val = ExpectScalar(); + } + else { + val._type = OT_INTEGER; + val._unVal.nInteger = nval++; + } + _table(table)->NewSlot(SQObjectPtr(key),SQObjectPtr(val)); + if(_token == ',') Lex(); + } + SQTable *enums = _table(_ss(_vm)->_consts); + SQObjectPtr strongid = id; + enums->NewSlot(SQObjectPtr(strongid),SQObjectPtr(table)); + strongid.Null(); + Lex(); + } + void TryCatchStatement() + { + SQObject exid; + Lex(); + _fs->AddInstruction(_OP_PUSHTRAP,0,0); + _fs->_traps++; + if(_fs->_breaktargets.size()) _fs->_breaktargets.top()++; + if(_fs->_continuetargets.size()) _fs->_continuetargets.top()++; + SQInteger trappos = _fs->GetCurrentPos(); + { + BEGIN_SCOPE(); + Statement(); + END_SCOPE(); + } + _fs->_traps--; + _fs->AddInstruction(_OP_POPTRAP, 1, 0); + if(_fs->_breaktargets.size()) _fs->_breaktargets.top()--; + if(_fs->_continuetargets.size()) _fs->_continuetargets.top()--; + _fs->AddInstruction(_OP_JMP, 0, 0); + SQInteger jmppos = _fs->GetCurrentPos(); + _fs->SetInstructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); + Expect(TK_CATCH); Expect(_SC('(')); exid = Expect(TK_IDENTIFIER); Expect(_SC(')')); + { + BEGIN_SCOPE(); + SQInteger ex_target = _fs->PushLocalVariable(exid); + _fs->SetInstructionParam(trappos, 0, ex_target); + Statement(); + _fs->SetInstructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); + END_SCOPE(); + } + } + void FunctionExp(SQInteger ftype,bool lambda = false) + { + Lex(); Expect(_SC('(')); + SQObjectPtr dummy; + CreateFunction(dummy,lambda); + _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION?0:1); + } + void ClassExp() + { + SQInteger base = -1; + SQInteger attrs = -1; + if(_token == TK_EXTENDS) { + Lex(); Expression(); + base = _fs->TopTarget(); + } + if(_token == TK_ATTR_OPEN) { + Lex(); + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(),0,NOT_TABLE); + ParseTableOrClass(_SC(','),TK_ATTR_CLOSE); + attrs = _fs->TopTarget(); + } + Expect(_SC('{')); + if(attrs != -1) _fs->PopTarget(); + if(base != -1) _fs->PopTarget(); + _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), base, attrs,NOT_CLASS); + ParseTableOrClass(_SC(';'),_SC('}')); + } + void DeleteExpr() + { + SQExpState es; + Lex(); + es = _es; + _es.donot_get = true; + PrefixedExpr(); + if(_es.etype==EXPR) Error(_SC("can't delete an expression")); + if(_es.etype==OBJECT || _es.etype==BASE) { + Emit2ArgsOP(_OP_DELETE); + } + else { + Error(_SC("cannot delete an (outer) local")); + } + _es = es; + } + void PrefixIncDec(SQInteger token) + { + SQExpState es; + SQInteger diff = (token==TK_MINUSMINUS) ? -1 : 1; + Lex(); + es = _es; + _es.donot_get = true; + PrefixedExpr(); + if(_es.etype==EXPR) { + Error(_SC("can't '++' or '--' an expression")); + } + else if(_es.etype==OBJECT || _es.etype==BASE) { + Emit2ArgsOP(_OP_INC, diff); + } + else if(_es.etype==LOCAL) { + SQInteger src = _fs->TopTarget(); + _fs->AddInstruction(_OP_INCL, src, src, 0, diff); + + } + else if(_es.etype==OUTER) { + SQInteger tmp = _fs->PushTarget(); + _fs->AddInstruction(_OP_GETOUTER, tmp, _es.epos); + _fs->AddInstruction(_OP_INCL, tmp, tmp, 0, diff); + _fs->AddInstruction(_OP_SETOUTER, tmp, _es.epos, tmp); + } + _es = es; + } + void CreateFunction(SQObject &name,bool lambda = false) + { + SQFuncState *funcstate = _fs->PushChildState(_ss(_vm)); + funcstate->_name = name; + SQObject paramname; + funcstate->AddParameter(_fs->CreateString(_SC("this"))); + funcstate->_sourcename = _sourcename; + SQInteger defparams = 0; + while(_token!=_SC(')')) { + if(_token == TK_VARPARAMS) { + if(defparams > 0) Error(_SC("function with default parameters cannot have variable number of parameters")); + funcstate->AddParameter(_fs->CreateString(_SC("vargv"))); + funcstate->_varparams = true; + Lex(); + if(_token != _SC(')')) Error(_SC("expected ')'")); + break; + } + else { + paramname = Expect(TK_IDENTIFIER); + funcstate->AddParameter(paramname); + if(_token == _SC('=')) { + Lex(); + Expression(); + funcstate->AddDefaultParam(_fs->TopTarget()); + defparams++; + } + else { + if(defparams > 0) Error(_SC("expected '='")); + } + if(_token == _SC(',')) Lex(); + else if(_token != _SC(')')) Error(_SC("expected ')' or ','")); + } + } + Expect(_SC(')')); + for(SQInteger n = 0; n < defparams; n++) { + _fs->PopTarget(); + } + + SQFuncState *currchunk = _fs; + _fs = funcstate; + if(lambda) { + Expression(); + _fs->AddInstruction(_OP_RETURN, 1, _fs->PopTarget());} + else { + Statement(false); + } + funcstate->AddLineInfos(_lex._prevtoken == _SC('\n')?_lex._lasttokenline:_lex._currentline, _lineinfo, true); + funcstate->AddInstruction(_OP_RETURN, -1); + funcstate->SetStackSize(0); + + SQFunctionProto *func = funcstate->BuildProto(); +#ifdef _DEBUG_DUMP + funcstate->Dump(func); +#endif + _fs = currchunk; + _fs->_functions.push_back(func); + _fs->PopChildState(); + } + void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve) + { + while(ntoresolve > 0) { + SQInteger pos = funcstate->_unresolvedbreaks.back(); + funcstate->_unresolvedbreaks.pop_back(); + //set the jmp instruction + funcstate->SetInstructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); + ntoresolve--; + } + } + void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos) + { + while(ntoresolve > 0) { + SQInteger pos = funcstate->_unresolvedcontinues.back(); + funcstate->_unresolvedcontinues.pop_back(); + //set the jmp instruction + funcstate->SetInstructionParams(pos, 0, targetpos - pos, 0); + ntoresolve--; + } + } +private: + SQInteger _token; + SQFuncState *_fs; + SQObjectPtr _sourcename; + SQLexer _lex; + bool _lineinfo; + bool _raiseerror; + SQInteger _debugline; + SQInteger _debugop; + SQExpState _es; + SQScope _scope; + SQChar _compilererror[MAX_COMPILER_ERROR_LEN]; + jmp_buf _errorjmp; + SQVM *_vm; +}; + +bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo) +{ + SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo); + return p.Compile(out); +} + +#endif diff --git a/sp/src/vscript/squirrel/squirrel/sqcompiler.h b/sp/src/vscript/squirrel/squirrel/sqcompiler.h new file mode 100644 index 0000000000..e7da64232c --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqcompiler.h @@ -0,0 +1,79 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQCOMPILER_H_ +#define _SQCOMPILER_H_ + +struct SQVM; + +#define TK_IDENTIFIER 258 +#define TK_STRING_LITERAL 259 +#define TK_INTEGER 260 +#define TK_FLOAT 261 +#define TK_BASE 262 +#define TK_DELETE 263 +#define TK_EQ 264 +#define TK_NE 265 +#define TK_LE 266 +#define TK_GE 267 +#define TK_SWITCH 268 +#define TK_ARROW 269 +#define TK_AND 270 +#define TK_OR 271 +#define TK_IF 272 +#define TK_ELSE 273 +#define TK_WHILE 274 +#define TK_BREAK 275 +#define TK_FOR 276 +#define TK_DO 277 +#define TK_NULL 278 +#define TK_FOREACH 279 +#define TK_IN 280 +#define TK_NEWSLOT 281 +#define TK_MODULO 282 +#define TK_LOCAL 283 +#define TK_CLONE 284 +#define TK_FUNCTION 285 +#define TK_RETURN 286 +#define TK_TYPEOF 287 +#define TK_UMINUS 288 +#define TK_PLUSEQ 289 +#define TK_MINUSEQ 290 +#define TK_CONTINUE 291 +#define TK_YIELD 292 +#define TK_TRY 293 +#define TK_CATCH 294 +#define TK_THROW 295 +#define TK_SHIFTL 296 +#define TK_SHIFTR 297 +#define TK_RESUME 298 +#define TK_DOUBLE_COLON 299 +#define TK_CASE 300 +#define TK_DEFAULT 301 +#define TK_THIS 302 +#define TK_PLUSPLUS 303 +#define TK_MINUSMINUS 304 +#define TK_3WAYSCMP 305 +#define TK_USHIFTR 306 +#define TK_CLASS 307 +#define TK_EXTENDS 308 +#define TK_CONSTRUCTOR 310 +#define TK_INSTANCEOF 311 +#define TK_VARPARAMS 312 +#define TK___LINE__ 313 +#define TK___FILE__ 314 +#define TK_TRUE 315 +#define TK_FALSE 316 +#define TK_MULEQ 317 +#define TK_DIVEQ 318 +#define TK_MODEQ 319 +#define TK_ATTR_OPEN 320 +#define TK_ATTR_CLOSE 321 +#define TK_STATIC 322 +#define TK_ENUM 323 +#define TK_CONST 324 +#define TK_RAWCALL 325 + + + +typedef void(*CompilerErrorFunc)(void *ud, const SQChar *s); +bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo); +#endif //_SQCOMPILER_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqdebug.cpp b/sp/src/vscript/squirrel/squirrel/sqdebug.cpp new file mode 100644 index 0000000000..d55b1b2e4f --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqdebug.cpp @@ -0,0 +1,118 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include "sqvm.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqstring.h" + +SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi) +{ + SQInteger cssize = v->_callsstacksize; + if (cssize > level) { + SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; + if(sq_isclosure(ci._closure)) { + SQClosure *c = _closure(ci._closure); + SQFunctionProto *proto = c->_function; + fi->funcid = proto; + fi->name = sq_type(proto->_name) == OT_STRING?_stringval(proto->_name):_SC("unknown"); + fi->source = sq_type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):_SC("unknown"); + fi->line = proto->_lineinfos[0]._line; + return SQ_OK; + } + } + return sq_throwerror(v,_SC("the object is not a closure")); +} + +SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si) +{ + SQInteger cssize = v->_callsstacksize; + if (cssize > level) { + memset(si, 0, sizeof(SQStackInfos)); + SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; + switch (sq_type(ci._closure)) { + case OT_CLOSURE:{ + SQFunctionProto *func = _closure(ci._closure)->_function; + if (sq_type(func->_name) == OT_STRING) + si->funcname = _stringval(func->_name); + if (sq_type(func->_sourcename) == OT_STRING) + si->source = _stringval(func->_sourcename); + si->line = func->GetLine(ci._ip); + } + break; + case OT_NATIVECLOSURE: + si->source = _SC("NATIVE"); + si->funcname = _SC("unknown"); + if(sq_type(_nativeclosure(ci._closure)->_name) == OT_STRING) + si->funcname = _stringval(_nativeclosure(ci._closure)->_name); + si->line = -1; + break; + default: break; //shutup compiler + } + return SQ_OK; + } + return SQ_ERROR; +} + +void SQVM::Raise_Error(const SQChar *s, ...) +{ + va_list vl; + va_start(vl, s); + SQInteger buffersize = (SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2); + scvsprintf(_sp(sq_rsl(buffersize)),buffersize, s, vl); + va_end(vl); + _lasterror = SQString::Create(_ss(this),_spval,-1); +} + +void SQVM::Raise_Error(const SQObjectPtr &desc) +{ + _lasterror = desc; +} + +SQString *SQVM::PrintObjVal(const SQObjectPtr &o) +{ + switch(sq_type(o)) { + case OT_STRING: return _string(o); + case OT_INTEGER: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR), _PRINT_INT_FMT, _integer(o)); + return SQString::Create(_ss(this), _spval); + break; + case OT_FLOAT: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)), sq_rsl(NUMBER_MAX_CHAR), _SC("%.14g"), _float(o)); + return SQString::Create(_ss(this), _spval); + break; + default: + return SQString::Create(_ss(this), GetTypeName(o)); + } +} + +void SQVM::Raise_IdxError(const SQObjectPtr &o) +{ + SQObjectPtr oval = PrintObjVal(o); + Raise_Error(_SC("the index '%.50s' does not exist"), _stringval(oval)); +} + +void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2) +{ + SQObjectPtr oval1 = PrintObjVal(o1), oval2 = PrintObjVal(o2); + Raise_Error(_SC("comparison between '%.50s' and '%.50s'"), _stringval(oval1), _stringval(oval2)); +} + + +void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type) +{ + SQObjectPtr exptypes = SQString::Create(_ss(this), _SC(""), -1); + SQInteger found = 0; + for(SQInteger i=0; i<16; i++) + { + SQInteger mask = ((SQInteger)1) << i; + if(typemask & (mask)) { + if(found>0) StringCat(exptypes,SQString::Create(_ss(this), _SC("|"), -1), exptypes); + found ++; + StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes); + } + } + Raise_Error(_SC("parameter %d has an invalid type '%s' ; expected: '%s'"), nparam, IdType2Name((SQObjectType)type), _stringval(exptypes)); +} diff --git a/sp/src/vscript/squirrel/squirrel/sqfuncproto.h b/sp/src/vscript/squirrel/squirrel/sqfuncproto.h new file mode 100644 index 0000000000..546dbabb55 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqfuncproto.h @@ -0,0 +1,154 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQFUNCTION_H_ +#define _SQFUNCTION_H_ + +#include "sqopcodes.h" + +enum SQOuterType { + otLOCAL = 0, + otOUTER = 1 +}; + +struct SQOuterVar +{ + + SQOuterVar(){} + SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t) + { + _name = name; + _src=src; + _type=t; + } + SQOuterVar(const SQOuterVar &ov) + { + _type=ov._type; + _src=ov._src; + _name=ov._name; + } + SQOuterType _type; + SQObjectPtr _name; + SQObjectPtr _src; +}; + +struct SQLocalVarInfo +{ + SQLocalVarInfo():_start_op(0),_end_op(0),_pos(0){} + SQLocalVarInfo(const SQLocalVarInfo &lvi) + { + _name=lvi._name; + _start_op=lvi._start_op; + _end_op=lvi._end_op; + _pos=lvi._pos; + } + SQObjectPtr _name; + SQUnsignedInteger _start_op; + SQUnsignedInteger _end_op; + SQUnsignedInteger _pos; +}; + +struct SQLineInfo { SQInteger _line;SQInteger _op; }; + +typedef sqvector SQOuterVarVec; +typedef sqvector SQLocalVarInfoVec; +typedef sqvector SQLineInfoVec; + +#define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,localinf,defparams) (sizeof(SQFunctionProto) \ + +((ni-1)*sizeof(SQInstruction))+(nl*sizeof(SQObjectPtr)) \ + +(nparams*sizeof(SQObjectPtr))+(nfuncs*sizeof(SQObjectPtr)) \ + +(nouters*sizeof(SQOuterVar))+(nlineinf*sizeof(SQLineInfo)) \ + +(localinf*sizeof(SQLocalVarInfo))+(defparams*sizeof(SQInteger))) + + +struct SQFunctionProto : public CHAINABLE_OBJ +{ +private: + SQFunctionProto(SQSharedState *ss); + ~SQFunctionProto(); + +public: + static SQFunctionProto *Create(SQSharedState *ss,SQInteger ninstructions, + SQInteger nliterals,SQInteger nparameters, + SQInteger nfunctions,SQInteger noutervalues, + SQInteger nlineinfos,SQInteger nlocalvarinfos,SQInteger ndefaultparams) + { + SQFunctionProto *f; + //I compact the whole class and members in a single memory allocation + f = (SQFunctionProto *)sq_vm_malloc(_FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams)); + new (f) SQFunctionProto(ss); + f->_ninstructions = ninstructions; + f->_literals = (SQObjectPtr*)&f->_instructions[ninstructions]; + f->_nliterals = nliterals; + f->_parameters = (SQObjectPtr*)&f->_literals[nliterals]; + f->_nparameters = nparameters; + f->_functions = (SQObjectPtr*)&f->_parameters[nparameters]; + f->_nfunctions = nfunctions; + f->_outervalues = (SQOuterVar*)&f->_functions[nfunctions]; + f->_noutervalues = noutervalues; + f->_lineinfos = (SQLineInfo *)&f->_outervalues[noutervalues]; + f->_nlineinfos = nlineinfos; + f->_localvarinfos = (SQLocalVarInfo *)&f->_lineinfos[nlineinfos]; + f->_nlocalvarinfos = nlocalvarinfos; + f->_defaultparams = (SQInteger *)&f->_localvarinfos[nlocalvarinfos]; + f->_ndefaultparams = ndefaultparams; + + _CONSTRUCT_VECTOR(SQObjectPtr,f->_nliterals,f->_literals); + _CONSTRUCT_VECTOR(SQObjectPtr,f->_nparameters,f->_parameters); + _CONSTRUCT_VECTOR(SQObjectPtr,f->_nfunctions,f->_functions); + _CONSTRUCT_VECTOR(SQOuterVar,f->_noutervalues,f->_outervalues); + //_CONSTRUCT_VECTOR(SQLineInfo,f->_nlineinfos,f->_lineinfos); //not required are 2 integers + _CONSTRUCT_VECTOR(SQLocalVarInfo,f->_nlocalvarinfos,f->_localvarinfos); + return f; + } + void Release(){ + _DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals); + _DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters); + _DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions); + _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues); + //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers + _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos); + SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_nlocalvarinfos,_ndefaultparams); + this->~SQFunctionProto(); + sq_vm_free(this,size); + } + + const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop); + SQInteger GetLine(SQInstruction *curr); + bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); + static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){ _NULL_SQOBJECT_VECTOR(_literals,_nliterals); } + SQObjectType GetType() {return OT_FUNCPROTO;} +#endif + SQObjectPtr _sourcename; + SQObjectPtr _name; + SQInteger _stacksize; + bool _bgenerator; + SQInteger _varparams; + + SQInteger _nlocalvarinfos; + SQLocalVarInfo *_localvarinfos; + + SQInteger _nlineinfos; + SQLineInfo *_lineinfos; + + SQInteger _nliterals; + SQObjectPtr *_literals; + + SQInteger _nparameters; + SQObjectPtr *_parameters; + + SQInteger _nfunctions; + SQObjectPtr *_functions; + + SQInteger _noutervalues; + SQOuterVar *_outervalues; + + SQInteger _ndefaultparams; + SQInteger *_defaultparams; + + SQInteger _ninstructions; + SQInstruction _instructions[1]; +}; + +#endif //_SQFUNCTION_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqfuncstate.cpp b/sp/src/vscript/squirrel/squirrel/sqfuncstate.cpp new file mode 100644 index 0000000000..779b40df42 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqfuncstate.cpp @@ -0,0 +1,649 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#ifndef NO_COMPILER +#include "sqcompiler.h" +#include "sqstring.h" +#include "sqfuncproto.h" +#include "sqtable.h" +#include "sqopcodes.h" +#include "sqfuncstate.h" + +#ifdef _DEBUG_DUMP +SQInstructionDesc g_InstrDesc[]={ + {_SC("_OP_LINE")}, + {_SC("_OP_LOAD")}, + {_SC("_OP_LOADINT")}, + {_SC("_OP_LOADFLOAT")}, + {_SC("_OP_DLOAD")}, + {_SC("_OP_TAILCALL")}, + {_SC("_OP_CALL")}, + {_SC("_OP_PREPCALL")}, + {_SC("_OP_PREPCALLK")}, + {_SC("_OP_GETK")}, + {_SC("_OP_MOVE")}, + {_SC("_OP_NEWSLOT")}, + {_SC("_OP_DELETE")}, + {_SC("_OP_SET")}, + {_SC("_OP_GET")}, + {_SC("_OP_EQ")}, + {_SC("_OP_NE")}, + {_SC("_OP_ADD")}, + {_SC("_OP_SUB")}, + {_SC("_OP_MUL")}, + {_SC("_OP_DIV")}, + {_SC("_OP_MOD")}, + {_SC("_OP_BITW")}, + {_SC("_OP_RETURN")}, + {_SC("_OP_LOADNULLS")}, + {_SC("_OP_LOADROOT")}, + {_SC("_OP_LOADBOOL")}, + {_SC("_OP_DMOVE")}, + {_SC("_OP_JMP")}, + {_SC("_OP_JCMP")}, + {_SC("_OP_JZ")}, + {_SC("_OP_SETOUTER")}, + {_SC("_OP_GETOUTER")}, + {_SC("_OP_NEWOBJ")}, + {_SC("_OP_APPENDARRAY")}, + {_SC("_OP_COMPARITH")}, + {_SC("_OP_INC")}, + {_SC("_OP_INCL")}, + {_SC("_OP_PINC")}, + {_SC("_OP_PINCL")}, + {_SC("_OP_CMP")}, + {_SC("_OP_EXISTS")}, + {_SC("_OP_INSTANCEOF")}, + {_SC("_OP_AND")}, + {_SC("_OP_OR")}, + {_SC("_OP_NEG")}, + {_SC("_OP_NOT")}, + {_SC("_OP_BWNOT")}, + {_SC("_OP_CLOSURE")}, + {_SC("_OP_YIELD")}, + {_SC("_OP_RESUME")}, + {_SC("_OP_FOREACH")}, + {_SC("_OP_POSTFOREACH")}, + {_SC("_OP_CLONE")}, + {_SC("_OP_TYPEOF")}, + {_SC("_OP_PUSHTRAP")}, + {_SC("_OP_POPTRAP")}, + {_SC("_OP_THROW")}, + {_SC("_OP_NEWSLOTA")}, + {_SC("_OP_GETBASE")}, + {_SC("_OP_CLOSE")}, +}; +#endif +void DumpLiteral(SQObjectPtr &o) +{ + switch(sq_type(o)){ + case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break; + case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break; + case OT_INTEGER: scprintf(_SC("{") _PRINT_INT_FMT _SC("}"),_integer(o));break; + case OT_BOOL: scprintf(_SC("%s"),_integer(o)?_SC("true"):_SC("false"));break; + default: scprintf(_SC("(%s %p)"),GetTypeName(o),(void*)_rawval(o));break; break; //shut up compiler + } +} + +SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed) +{ + _nliterals = 0; + _literals = SQTable::Create(ss,0); + _strings = SQTable::Create(ss,0); + _sharedstate = ss; + _lastline = 0; + _optimization = true; + _parent = parent; + _stacksize = 0; + _traps = 0; + _returnexp = 0; + _varparams = false; + _errfunc = efunc; + _errtarget = ed; + _bgenerator = false; + _outers = 0; + _ss = ss; + +} + +void SQFuncState::Error(const SQChar *err) +{ + _errfunc(_errtarget,err); +} + +#ifdef _DEBUG_DUMP +void SQFuncState::Dump(SQFunctionProto *func) +{ + SQUnsignedInteger n=0,i; + SQInteger si; + scprintf(_SC("SQInstruction sizeof %d\n"),(SQInt32)sizeof(SQInstruction)); + scprintf(_SC("SQObject sizeof %d\n"), (SQInt32)sizeof(SQObject)); + scprintf(_SC("--------------------------------------------------------------------\n")); + scprintf(_SC("*****FUNCTION [%s]\n"),sq_type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown")); + scprintf(_SC("-----LITERALS\n")); + SQObjectPtr refidx,key,val; + SQInteger idx; + SQObjectPtrVec templiterals; + templiterals.resize(_nliterals); + while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { + refidx=idx; + templiterals[_integer(val)]=key; + } + for(i=0;i>\n")); + n=0; + for(i=0;i<_parameters.size();i++){ + scprintf(_SC("[%d] "), (SQInt32)n); + DumpLiteral(_parameters[i]); + scprintf(_SC("\n")); + n++; + } + scprintf(_SC("-----LOCALS\n")); + for(si=0;si_nlocalvarinfos;si++){ + SQLocalVarInfo lvi=func->_localvarinfos[si]; + scprintf(_SC("[%d] %s \t%d %d\n"), (SQInt32)lvi._pos,_stringval(lvi._name), (SQInt32)lvi._start_op, (SQInt32)lvi._end_op); + n++; + } + scprintf(_SC("-----LINE INFO\n")); + for(i=0;i<_lineinfos.size();i++){ + SQLineInfo li=_lineinfos[i]; + scprintf(_SC("op [%d] line [%d] \n"), (SQInt32)li._op, (SQInt32)li._line); + n++; + } + scprintf(_SC("-----dump\n")); + n=0; + for(i=0;i<_instructions.size();i++){ + SQInstruction &inst=_instructions[i]; + if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){ + + SQInteger lidx = inst._arg1; + scprintf(_SC("[%03d] %15s %d "), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0); + if(lidx >= 0xFFFFFFFF) + scprintf(_SC("null")); + else { + SQInteger refidx; + SQObjectPtr val,key,refo; + while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) { + refo = refidx; + } + DumpLiteral(key); + } + if(inst.op != _OP_DLOAD) { + scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3); + } + else { + scprintf(_SC(" %d "),inst._arg2); + lidx = inst._arg3; + if(lidx >= 0xFFFFFFFF) + scprintf(_SC("null")); + else { + SQInteger refidx; + SQObjectPtr val,key,refo; + while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) { + refo = refidx; + } + DumpLiteral(key); + scprintf(_SC("\n")); + } + } + } + else if(inst.op==_OP_LOADFLOAT) { + scprintf(_SC("[%03d] %15s %d %f %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,*((SQFloat*)&inst._arg1),inst._arg2,inst._arg3); + } + /* else if(inst.op==_OP_ARITH){ + scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); + }*/ + else { + scprintf(_SC("[%03d] %15s %d %d %d %d\n"), (SQInt32)n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); + } + n++; + } + scprintf(_SC("-----\n")); + scprintf(_SC("stack size[%d]\n"), (SQInt32)func->_stacksize); + scprintf(_SC("--------------------------------------------------------------------\n\n")); +} +#endif + +SQInteger SQFuncState::GetNumericConstant(const SQInteger cons) +{ + return GetConstant(SQObjectPtr(cons)); +} + +SQInteger SQFuncState::GetNumericConstant(const SQFloat cons) +{ + return GetConstant(SQObjectPtr(cons)); +} + +SQInteger SQFuncState::GetConstant(const SQObject &cons) +{ + SQObjectPtr val; + if(!_table(_literals)->Get(cons,val)) + { + val = _nliterals; + _table(_literals)->NewSlot(cons,val); + _nliterals++; + if(_nliterals > MAX_LITERALS) { + val.Null(); + Error(_SC("internal compiler error: too many literals")); + } + } + return _integer(val); +} + +void SQFuncState::SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) +{ + _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0); + _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1); + _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2); + _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3); +} + +void SQFuncState::SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val) +{ + switch(arg){ + case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break; + case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break; + case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break; + case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break; + }; +} + +SQInteger SQFuncState::AllocStackPos() +{ + SQInteger npos=_vlocals.size(); + _vlocals.push_back(SQLocalVarInfo()); + if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) { + if(_stacksize>MAX_FUNC_STACKSIZE) Error(_SC("internal compiler error: too many locals")); + _stacksize=_vlocals.size(); + } + return npos; +} + +SQInteger SQFuncState::PushTarget(SQInteger n) +{ + if(n!=-1){ + _targetstack.push_back(n); + return n; + } + n=AllocStackPos(); + _targetstack.push_back(n); + return n; +} + +SQInteger SQFuncState::GetUpTarget(SQInteger n){ + return _targetstack[((_targetstack.size()-1)-n)]; +} + +SQInteger SQFuncState::TopTarget(){ + return _targetstack.back(); +} +SQInteger SQFuncState::PopTarget() +{ + SQUnsignedInteger npos=_targetstack.back(); + assert(npos < _vlocals.size()); + SQLocalVarInfo &t = _vlocals[npos]; + if(sq_type(t._name)==OT_NULL){ + _vlocals.pop_back(); + } + _targetstack.pop_back(); + return npos; +} + +SQInteger SQFuncState::GetStackSize() +{ + return _vlocals.size(); +} + +SQInteger SQFuncState::CountOuters(SQInteger stacksize) +{ + SQInteger outers = 0; + SQInteger k = _vlocals.size() - 1; + while(k >= stacksize) { + SQLocalVarInfo &lvi = _vlocals[k]; + k--; + if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer + outers++; + } + } + return outers; +} + +void SQFuncState::SetStackSize(SQInteger n) +{ + SQInteger size=_vlocals.size(); + while(size>n){ + size--; + SQLocalVarInfo lvi = _vlocals.back(); + if(sq_type(lvi._name)!=OT_NULL){ + if(lvi._end_op == UINT_MINUS_ONE) { //this means is an outer + _outers--; + } + lvi._end_op = GetCurrentPos(); + _localvarinfos.push_back(lvi); + } + _vlocals.pop_back(); + } +} + +bool SQFuncState::IsConstant(const SQObject &name,SQObject &e) +{ + SQObjectPtr val; + if(_table(_sharedstate->_consts)->Get(name,val)) { + e = val; + return true; + } + return false; +} + +bool SQFuncState::IsLocal(SQUnsignedInteger stkpos) +{ + if(stkpos>=_vlocals.size())return false; + else if(sq_type(_vlocals[stkpos]._name)!=OT_NULL)return true; + return false; +} + +SQInteger SQFuncState::PushLocalVariable(const SQObject &name) +{ + SQInteger pos=_vlocals.size(); + SQLocalVarInfo lvi; + lvi._name=name; + lvi._start_op=GetCurrentPos()+1; + lvi._pos=_vlocals.size(); + _vlocals.push_back(lvi); + if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size(); + return pos; +} + + + +SQInteger SQFuncState::GetLocalVariable(const SQObject &name) +{ + SQInteger locals=_vlocals.size(); + while(locals>=1){ + SQLocalVarInfo &lvi = _vlocals[locals-1]; + if(sq_type(lvi._name)==OT_STRING && _string(lvi._name)==_string(name)){ + return locals-1; + } + locals--; + } + return -1; +} + +void SQFuncState::MarkLocalAsOuter(SQInteger pos) +{ + SQLocalVarInfo &lvi = _vlocals[pos]; + lvi._end_op = UINT_MINUS_ONE; + _outers++; +} + +SQInteger SQFuncState::GetOuterVariable(const SQObject &name) +{ + SQInteger outers = _outervalues.size(); + for(SQInteger i = 0; iGetLocalVariable(name); + if(pos == -1) { + pos = _parent->GetOuterVariable(name); + if(pos != -1) { + _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local + return _outervalues.size() - 1; + } + } + else { + _parent->MarkLocalAsOuter(pos); + _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local + return _outervalues.size() - 1; + + + } + } + return -1; +} + +void SQFuncState::AddParameter(const SQObject &name) +{ + PushLocalVariable(name); + _parameters.push_back(name); +} + +void SQFuncState::AddLineInfos(SQInteger line,bool lineop,bool force) +{ + if(_lastline!=line || force){ + SQLineInfo li; + li._line=line;li._op=(GetCurrentPos()+1); + if(lineop)AddInstruction(_OP_LINE,0,line); + if(_lastline!=line) { + _lineinfos.push_back(li); + } + _lastline=line; + } +} + +void SQFuncState::DiscardTarget() +{ + SQInteger discardedtarget = PopTarget(); + SQInteger size = _instructions.size(); + if(size > 0 && _optimization){ + SQInstruction &pi = _instructions[size-1];//previous instruction + switch(pi.op) { + case _OP_SET:case _OP_NEWSLOT:case _OP_SETOUTER:case _OP_CALL: + if(pi._arg0 == discardedtarget) { + pi._arg0 = 0xFF; + } + } + } +} + +void SQFuncState::AddInstruction(SQInstruction &i) +{ + SQInteger size = _instructions.size(); + if(size > 0 && _optimization){ //simple optimizer + SQInstruction &pi = _instructions[size-1];//previous instruction + switch(i.op) { + case _OP_JZ: + if( pi.op == _OP_CMP && pi._arg1 < 0xFF) { + pi.op = _OP_JCMP; + pi._arg0 = (unsigned char)pi._arg1; + pi._arg1 = i._arg1; + return; + } + break; + case _OP_SET: + case _OP_NEWSLOT: + if(i._arg0 == i._arg3) { + i._arg0 = 0xFF; + } + break; + case _OP_SETOUTER: + if(i._arg0 == i._arg2) { + i._arg0 = 0xFF; + } + break; + case _OP_RETURN: + if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) { + pi.op = _OP_TAILCALL; + } else if(pi.op == _OP_CLOSE){ + pi = i; + return; + } + break; + case _OP_GET: + if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){ + pi._arg2 = (unsigned char)i._arg1; + pi.op = _OP_GETK; + pi._arg0 = i._arg0; + + return; + } + break; + case _OP_PREPCALL: + if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ + pi.op = _OP_PREPCALLK; + pi._arg0 = i._arg0; + pi._arg2 = i._arg2; + pi._arg3 = i._arg3; + return; + } + break; + case _OP_APPENDARRAY: { + SQInteger aat = -1; + switch(pi.op) { + case _OP_LOAD: aat = AAT_LITERAL; break; + case _OP_LOADINT: aat = AAT_INT; break; + case _OP_LOADBOOL: aat = AAT_BOOL; break; + case _OP_LOADFLOAT: aat = AAT_FLOAT; break; + default: break; + } + if(aat != -1 && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ + pi.op = _OP_APPENDARRAY; + pi._arg0 = i._arg0; + pi._arg2 = (unsigned char)aat; + pi._arg3 = MAX_FUNC_STACKSIZE; + return; + } + } + break; + case _OP_MOVE: + switch(pi.op) { + case _OP_GET: case _OP_ADD: case _OP_SUB: case _OP_MUL: case _OP_DIV: case _OP_MOD: case _OP_BITW: + case _OP_LOADINT: case _OP_LOADFLOAT: case _OP_LOADBOOL: case _OP_LOAD: + + if(pi._arg0 == i._arg1) + { + pi._arg0 = i._arg0; + _optimization = false; + //_result_elimination = false; + return; + } + } + + if(pi.op == _OP_MOVE) + { + pi.op = _OP_DMOVE; + pi._arg2 = i._arg0; + pi._arg3 = (unsigned char)i._arg1; + return; + } + break; + case _OP_LOAD: + if(pi.op == _OP_LOAD && i._arg1 < 256) { + pi.op = _OP_DLOAD; + pi._arg2 = i._arg0; + pi._arg3 = (unsigned char)i._arg1; + return; + } + break; + case _OP_EQ:case _OP_NE: + if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) )) + { + pi.op = i.op; + pi._arg0 = i._arg0; + pi._arg2 = i._arg2; + pi._arg3 = MAX_FUNC_STACKSIZE; + return; + } + break; + case _OP_LOADNULLS: + if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) { + + pi._arg1 = pi._arg1 + 1; + pi.op = _OP_LOADNULLS; + return; + } + break; + case _OP_LINE: + if(pi.op == _OP_LINE) { + _instructions.pop_back(); + _lineinfos.pop_back(); + } + break; + } + } + _optimization = true; + _instructions.push_back(i); +} + +SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len) +{ + SQObjectPtr ns(SQString::Create(_sharedstate,s,len)); + _table(_strings)->NewSlot(ns,(SQInteger)1); + return ns; +} + +SQObject SQFuncState::CreateTable() +{ + SQObjectPtr nt(SQTable::Create(_sharedstate,0)); + _table(_strings)->NewSlot(nt,(SQInteger)1); + return nt; +} + +SQFunctionProto *SQFuncState::BuildProto() +{ + + SQFunctionProto *f=SQFunctionProto::Create(_ss,_instructions.size(), + _nliterals,_parameters.size(),_functions.size(),_outervalues.size(), + _lineinfos.size(),_localvarinfos.size(),_defaultparams.size()); + + SQObjectPtr refidx,key,val; + SQInteger idx; + + f->_stacksize = _stacksize; + f->_sourcename = _sourcename; + f->_bgenerator = _bgenerator; + f->_name = _name; + + while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { + f->_literals[_integer(val)]=key; + refidx=idx; + } + + for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf]; + for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) f->_parameters[np] = _parameters[np]; + for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no]; + for(SQUnsignedInteger nl = 0; nl < _localvarinfos.size(); nl++) f->_localvarinfos[nl] = _localvarinfos[nl]; + for(SQUnsignedInteger ni = 0; ni < _lineinfos.size(); ni++) f->_lineinfos[ni] = _lineinfos[ni]; + for(SQUnsignedInteger nd = 0; nd < _defaultparams.size(); nd++) f->_defaultparams[nd] = _defaultparams[nd]; + + memcpy(f->_instructions,&_instructions[0],_instructions.size()*sizeof(SQInstruction)); + + f->_varparams = _varparams; + + return f; +} + +SQFuncState *SQFuncState::PushChildState(SQSharedState *ss) +{ + SQFuncState *child = (SQFuncState *)sq_malloc(sizeof(SQFuncState)); + new (child) SQFuncState(ss,this,_errfunc,_errtarget); + _childstates.push_back(child); + return child; +} + +void SQFuncState::PopChildState() +{ + SQFuncState *child = _childstates.back(); + sq_delete(child,SQFuncState); + _childstates.pop_back(); +} + +SQFuncState::~SQFuncState() +{ + while(_childstates.size() > 0) + { + PopChildState(); + } +} + +#endif diff --git a/sp/src/vscript/squirrel/squirrel/sqfuncstate.h b/sp/src/vscript/squirrel/squirrel/sqfuncstate.h new file mode 100644 index 0000000000..130aa54ff6 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqfuncstate.h @@ -0,0 +1,91 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQFUNCSTATE_H_ +#define _SQFUNCSTATE_H_ +/////////////////////////////////// +#include "squtils.h" + +struct SQFuncState +{ + SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed); + ~SQFuncState(); +#ifdef _DEBUG_DUMP + void Dump(SQFunctionProto *func); +#endif + void Error(const SQChar *err); + SQFuncState *PushChildState(SQSharedState *ss); + void PopChildState(); + void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);} + void AddInstruction(SQInstruction &i); + void SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0); + void SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val); + SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];} + void PopInstructions(SQInteger size){for(SQInteger i=0;i _childstates; + SQInteger GetConstant(const SQObject &cons); +private: + CompilerErrorFunc _errfunc; + void *_errtarget; + SQSharedState *_ss; +}; + + +#endif //_SQFUNCSTATE_H_ + diff --git a/sp/src/vscript/squirrel/squirrel/sqlexer.cpp b/sp/src/vscript/squirrel/squirrel/sqlexer.cpp new file mode 100644 index 0000000000..6a8e7c4f3f --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqlexer.cpp @@ -0,0 +1,563 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include +#include "sqtable.h" +#include "sqstring.h" +#include "sqcompiler.h" +#include "sqlexer.h" + +#define CUR_CHAR (_currdata) +#define RETURN_TOKEN(t) { _prevtoken = _curtoken; _curtoken = t; return t;} +#define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB) +#define NEXT() {Next();_currentcolumn++;} +#define INIT_TEMP_STRING() { _longstr.resize(0);} +#define APPEND_CHAR(c) { _longstr.push_back(c);} +#define TERMINATE_BUFFER() {_longstr.push_back(_SC('\0'));} +#define ADD_KEYWORD(key,id) _keywords->NewSlot( SQString::Create(ss, _SC(#key)) ,SQInteger(id)) + +SQLexer::SQLexer(){} +SQLexer::~SQLexer() +{ + _keywords->Release(); +} + +void SQLexer::Init(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up,CompilerErrorFunc efunc,void *ed) +{ + _errfunc = efunc; + _errtarget = ed; + _sharedstate = ss; + _keywords = SQTable::Create(ss, 37); + ADD_KEYWORD(while, TK_WHILE); + ADD_KEYWORD(do, TK_DO); + ADD_KEYWORD(if, TK_IF); + ADD_KEYWORD(else, TK_ELSE); + ADD_KEYWORD(break, TK_BREAK); + ADD_KEYWORD(continue, TK_CONTINUE); + ADD_KEYWORD(return, TK_RETURN); + ADD_KEYWORD(null, TK_NULL); + ADD_KEYWORD(function, TK_FUNCTION); + ADD_KEYWORD(local, TK_LOCAL); + ADD_KEYWORD(for, TK_FOR); + ADD_KEYWORD(foreach, TK_FOREACH); + ADD_KEYWORD(in, TK_IN); + ADD_KEYWORD(typeof, TK_TYPEOF); + ADD_KEYWORD(base, TK_BASE); + ADD_KEYWORD(delete, TK_DELETE); + ADD_KEYWORD(try, TK_TRY); + ADD_KEYWORD(catch, TK_CATCH); + ADD_KEYWORD(throw, TK_THROW); + ADD_KEYWORD(clone, TK_CLONE); + ADD_KEYWORD(yield, TK_YIELD); + ADD_KEYWORD(resume, TK_RESUME); + ADD_KEYWORD(switch, TK_SWITCH); + ADD_KEYWORD(case, TK_CASE); + ADD_KEYWORD(default, TK_DEFAULT); + ADD_KEYWORD(this, TK_THIS); + ADD_KEYWORD(class,TK_CLASS); + ADD_KEYWORD(extends,TK_EXTENDS); + ADD_KEYWORD(constructor,TK_CONSTRUCTOR); + ADD_KEYWORD(instanceof,TK_INSTANCEOF); + ADD_KEYWORD(true,TK_TRUE); + ADD_KEYWORD(false,TK_FALSE); + ADD_KEYWORD(static,TK_STATIC); + ADD_KEYWORD(enum,TK_ENUM); + ADD_KEYWORD(const,TK_CONST); + ADD_KEYWORD(__LINE__,TK___LINE__); + ADD_KEYWORD(__FILE__,TK___FILE__); + ADD_KEYWORD(rawcall, TK_RAWCALL); + + + _readf = rg; + _up = up; + _lasttokenline = _currentline = 1; + _currentcolumn = 0; + _prevtoken = -1; + _reached_eof = SQFalse; + Next(); +} + +void SQLexer::Error(const SQChar *err) +{ + _errfunc(_errtarget,err); +} + +void SQLexer::Next() +{ + SQInteger t = _readf(_up); + if(t > MAX_CHAR) Error(_SC("Invalid character")); + if(t != 0) { + _currdata = (LexChar)t; + return; + } + _currdata = SQUIRREL_EOB; + _reached_eof = SQTrue; +} + +const SQChar *SQLexer::Tok2Str(SQInteger tok) +{ + SQObjectPtr itr, key, val; + SQInteger nitr; + while((nitr = _keywords->Next(false,itr, key, val)) != -1) { + itr = (SQInteger)nitr; + if(((SQInteger)_integer(val)) == tok) + return _stringval(key); + } + return NULL; +} + +void SQLexer::LexBlockComment() +{ + bool done = false; + while(!done) { + switch(CUR_CHAR) { + case _SC('*'): { NEXT(); if(CUR_CHAR == _SC('/')) { done = true; NEXT(); }}; continue; + case _SC('\n'): _currentline++; NEXT(); continue; + case SQUIRREL_EOB: Error(_SC("missing \"*/\" in comment")); + default: NEXT(); + } + } +} +void SQLexer::LexLineComment() +{ + do { NEXT(); } while (CUR_CHAR != _SC('\n') && (!IS_EOB())); +} + +SQInteger SQLexer::Lex() +{ + _lasttokenline = _currentline; + while(CUR_CHAR != SQUIRREL_EOB) { + switch(CUR_CHAR){ + case _SC('\t'): case _SC('\r'): case _SC(' '): NEXT(); continue; + case _SC('\n'): + _currentline++; + _prevtoken=_curtoken; + _curtoken=_SC('\n'); + NEXT(); + _currentcolumn=1; + continue; + case _SC('#'): LexLineComment(); continue; + case _SC('/'): + NEXT(); + switch(CUR_CHAR){ + case _SC('*'): + NEXT(); + LexBlockComment(); + continue; + case _SC('/'): + LexLineComment(); + continue; + case _SC('='): + NEXT(); + RETURN_TOKEN(TK_DIVEQ); + continue; + case _SC('>'): + NEXT(); + RETURN_TOKEN(TK_ATTR_CLOSE); + continue; + default: + RETURN_TOKEN('/'); + } + case _SC('='): + NEXT(); + if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('=') } + else { NEXT(); RETURN_TOKEN(TK_EQ); } + case _SC('<'): + NEXT(); + switch(CUR_CHAR) { + case _SC('='): + NEXT(); + if(CUR_CHAR == _SC('>')) { + NEXT(); + RETURN_TOKEN(TK_3WAYSCMP); + } + RETURN_TOKEN(TK_LE) + break; + case _SC('-'): NEXT(); RETURN_TOKEN(TK_NEWSLOT); break; + case _SC('<'): NEXT(); RETURN_TOKEN(TK_SHIFTL); break; + case _SC('/'): NEXT(); RETURN_TOKEN(TK_ATTR_OPEN); break; + } + RETURN_TOKEN('<'); + case _SC('>'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_GE);} + else if(CUR_CHAR == _SC('>')){ + NEXT(); + if(CUR_CHAR == _SC('>')){ + NEXT(); + RETURN_TOKEN(TK_USHIFTR); + } + RETURN_TOKEN(TK_SHIFTR); + } + else { RETURN_TOKEN('>') } + case _SC('!'): + NEXT(); + if (CUR_CHAR != _SC('=')){ RETURN_TOKEN('!')} + else { NEXT(); RETURN_TOKEN(TK_NE); } + case _SC('@'): { + SQInteger stype; + NEXT(); + if(CUR_CHAR != _SC('"')) { + RETURN_TOKEN('@'); + } + if((stype=ReadString('"',true))!=-1) { + RETURN_TOKEN(stype); + } + Error(_SC("error parsing the string")); + } + case _SC('"'): + case _SC('\''): { + SQInteger stype; + if((stype=ReadString(CUR_CHAR,false))!=-1){ + RETURN_TOKEN(stype); + } + Error(_SC("error parsing the string")); + } + case _SC('{'): case _SC('}'): case _SC('('): case _SC(')'): case _SC('['): case _SC(']'): + case _SC(';'): case _SC(','): case _SC('?'): case _SC('^'): case _SC('~'): + {SQInteger ret = CUR_CHAR; + NEXT(); RETURN_TOKEN(ret); } + case _SC('.'): + NEXT(); + if (CUR_CHAR != _SC('.')){ RETURN_TOKEN('.') } + NEXT(); + if (CUR_CHAR != _SC('.')){ Error(_SC("invalid token '..'")); } + NEXT(); + RETURN_TOKEN(TK_VARPARAMS); + case _SC('&'): + NEXT(); + if (CUR_CHAR != _SC('&')){ RETURN_TOKEN('&') } + else { NEXT(); RETURN_TOKEN(TK_AND); } + case _SC('|'): + NEXT(); + if (CUR_CHAR != _SC('|')){ RETURN_TOKEN('|') } + else { NEXT(); RETURN_TOKEN(TK_OR); } + case _SC(':'): + NEXT(); + if (CUR_CHAR != _SC(':')){ RETURN_TOKEN(':') } + else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); } + case _SC('*'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MULEQ);} + else RETURN_TOKEN('*'); + case _SC('%'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MODEQ);} + else RETURN_TOKEN('%'); + case _SC('-'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);} + else if (CUR_CHAR == _SC('-')){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);} + else RETURN_TOKEN('-'); + case _SC('+'): + NEXT(); + if (CUR_CHAR == _SC('=')){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);} + else if (CUR_CHAR == _SC('+')){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);} + else RETURN_TOKEN('+'); + case SQUIRREL_EOB: + return 0; + default:{ + if (scisdigit(CUR_CHAR)) { + SQInteger ret = ReadNumber(); + RETURN_TOKEN(ret); + } + else if (scisalpha(CUR_CHAR) || CUR_CHAR == _SC('_')) { + SQInteger t = ReadID(); + RETURN_TOKEN(t); + } + else { + SQInteger c = CUR_CHAR; + if (sciscntrl((int)c)) Error(_SC("unexpected character(control)")); + NEXT(); + RETURN_TOKEN(c); + } + RETURN_TOKEN(0); + } + } + } + return 0; +} + +SQInteger SQLexer::GetIDType(const SQChar *s,SQInteger len) +{ + SQObjectPtr t; + if(_keywords->GetStr(s,len, t)) { + return SQInteger(_integer(t)); + } + return TK_IDENTIFIER; +} + +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 +SQInteger SQLexer::AddUTF16(SQUnsignedInteger ch) +{ + if (ch >= 0x10000) + { + SQUnsignedInteger code = (ch - 0x10000); + APPEND_CHAR((SQChar)(0xD800 | (code >> 10))); + APPEND_CHAR((SQChar)(0xDC00 | (code & 0x3FF))); + return 2; + } + else { + APPEND_CHAR((SQChar)ch); + return 1; + } +} +#endif +#else +SQInteger SQLexer::AddUTF8(SQUnsignedInteger ch) +{ + if (ch < 0x80) { + APPEND_CHAR((char)ch); + return 1; + } + if (ch < 0x800) { + APPEND_CHAR((SQChar)((ch >> 6) | 0xC0)); + APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); + return 2; + } + if (ch < 0x10000) { + APPEND_CHAR((SQChar)((ch >> 12) | 0xE0)); + APPEND_CHAR((SQChar)(((ch >> 6) & 0x3F) | 0x80)); + APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); + return 3; + } + if (ch < 0x110000) { + APPEND_CHAR((SQChar)((ch >> 18) | 0xF0)); + APPEND_CHAR((SQChar)(((ch >> 12) & 0x3F) | 0x80)); + APPEND_CHAR((SQChar)(((ch >> 6) & 0x3F) | 0x80)); + APPEND_CHAR((SQChar)((ch & 0x3F) | 0x80)); + return 4; + } + return 0; +} +#endif + +SQInteger SQLexer::ProcessStringHexEscape(SQChar *dest, SQInteger maxdigits) +{ + NEXT(); + if (!isxdigit(CUR_CHAR)) Error(_SC("hexadecimal number expected")); + SQInteger n = 0; + while (isxdigit(CUR_CHAR) && n < maxdigits) { + dest[n] = CUR_CHAR; + n++; + NEXT(); + } + dest[n] = 0; + return n; +} + +SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim) +{ + INIT_TEMP_STRING(); + NEXT(); + if(IS_EOB()) return -1; + for(;;) { + while(CUR_CHAR != ndelim) { + SQInteger x = CUR_CHAR; + switch (x) { + case SQUIRREL_EOB: + Error(_SC("unfinished string")); + return -1; + case _SC('\n'): + if(!verbatim) Error(_SC("newline in a constant")); + APPEND_CHAR(CUR_CHAR); NEXT(); + _currentline++; + break; + case _SC('\\'): + if(verbatim) { + APPEND_CHAR('\\'); NEXT(); + } + else { + NEXT(); + switch(CUR_CHAR) { + case _SC('x'): { + const SQInteger maxdigits = sizeof(SQChar) * 2; + SQChar temp[maxdigits + 1]; + ProcessStringHexEscape(temp, maxdigits); + SQChar *stemp; + APPEND_CHAR((SQChar)scstrtoul(temp, &stemp, 16)); + } + break; + case _SC('U'): + case _SC('u'): { + const SQInteger maxdigits = CUR_CHAR == 'u' ? 4 : 8; + SQChar temp[8 + 1]; + ProcessStringHexEscape(temp, maxdigits); + SQChar *stemp; +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 + AddUTF16(scstrtoul(temp, &stemp, 16)); +#else + APPEND_CHAR((SQChar)scstrtoul(temp, &stemp, 16)); +#endif +#else + AddUTF8(scstrtoul(temp, &stemp, 16)); +#endif + } + break; + case _SC('t'): APPEND_CHAR(_SC('\t')); NEXT(); break; + case _SC('a'): APPEND_CHAR(_SC('\a')); NEXT(); break; + case _SC('b'): APPEND_CHAR(_SC('\b')); NEXT(); break; + case _SC('n'): APPEND_CHAR(_SC('\n')); NEXT(); break; + case _SC('r'): APPEND_CHAR(_SC('\r')); NEXT(); break; + case _SC('v'): APPEND_CHAR(_SC('\v')); NEXT(); break; + case _SC('f'): APPEND_CHAR(_SC('\f')); NEXT(); break; + case _SC('0'): APPEND_CHAR(_SC('\0')); NEXT(); break; + case _SC('\\'): APPEND_CHAR(_SC('\\')); NEXT(); break; + case _SC('"'): APPEND_CHAR(_SC('"')); NEXT(); break; + case _SC('\''): APPEND_CHAR(_SC('\'')); NEXT(); break; + default: + Error(_SC("unrecognised escaper char")); + break; + } + } + break; + default: + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + } + NEXT(); + if(verbatim && CUR_CHAR == '"') { //double quotation + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + else { + break; + } + } + TERMINATE_BUFFER(); + SQInteger len = _longstr.size()-1; + if(ndelim == _SC('\'')) { + if(len == 0) Error(_SC("empty constant")); + if(len > 1) Error(_SC("constant too long")); + _nvalue = _longstr[0]; + return TK_INTEGER; + } + _svalue = &_longstr[0]; + return TK_STRING_LITERAL; +} + +void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res) +{ + *res = 0; + while(*s != 0) + { + if(scisdigit(*s)) *res = (*res)*16+((*s++)-'0'); + else if(scisxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10); + else { assert(0); } + } +} + +void LexInteger(const SQChar *s,SQUnsignedInteger *res) +{ + *res = 0; + while(*s != 0) + { + *res = (*res)*10+((*s++)-'0'); + } +} + +SQInteger scisodigit(SQInteger c) { return c >= _SC('0') && c <= _SC('7'); } + +void LexOctal(const SQChar *s,SQUnsignedInteger *res) +{ + *res = 0; + while(*s != 0) + { + if(scisodigit(*s)) *res = (*res)*8+((*s++)-'0'); + else { assert(0); } + } +} + +SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; } + + +#define MAX_HEX_DIGITS (sizeof(SQInteger)*2) +SQInteger SQLexer::ReadNumber() +{ +#define TINT 1 +#define TFLOAT 2 +#define THEX 3 +#define TSCIENTIFIC 4 +#define TOCTAL 5 + SQInteger type = TINT, firstchar = CUR_CHAR; + SQChar *sTemp; + INIT_TEMP_STRING(); + NEXT(); + if(firstchar == _SC('0') && (toupper(CUR_CHAR) == _SC('X') || scisodigit(CUR_CHAR)) ) { + if(scisodigit(CUR_CHAR)) { + type = TOCTAL; + while(scisodigit(CUR_CHAR)) { + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + if(scisdigit(CUR_CHAR)) Error(_SC("invalid octal number")); + } + else { + NEXT(); + type = THEX; + while(isxdigit(CUR_CHAR)) { + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + if(_longstr.size() > MAX_HEX_DIGITS) Error(_SC("too many digits for an Hex number")); + } + } + else { + APPEND_CHAR((int)firstchar); + while (CUR_CHAR == _SC('.') || scisdigit(CUR_CHAR) || isexponent(CUR_CHAR)) { + if(CUR_CHAR == _SC('.') || isexponent(CUR_CHAR)) type = TFLOAT; + if(isexponent(CUR_CHAR)) { + if(type != TFLOAT) Error(_SC("invalid numeric format")); + type = TSCIENTIFIC; + APPEND_CHAR(CUR_CHAR); + NEXT(); + if(CUR_CHAR == '+' || CUR_CHAR == '-'){ + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + if(!scisdigit(CUR_CHAR)) Error(_SC("exponent expected")); + } + + APPEND_CHAR(CUR_CHAR); + NEXT(); + } + } + TERMINATE_BUFFER(); + switch(type) { + case TSCIENTIFIC: + case TFLOAT: + _fvalue = (SQFloat)scstrtod(&_longstr[0],&sTemp); + return TK_FLOAT; + case TINT: + LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue); + return TK_INTEGER; + case THEX: + LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); + return TK_INTEGER; + case TOCTAL: + LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); + return TK_INTEGER; + } + return 0; +} + +SQInteger SQLexer::ReadID() +{ + SQInteger res; + INIT_TEMP_STRING(); + do { + APPEND_CHAR(CUR_CHAR); + NEXT(); + } while(scisalnum(CUR_CHAR) || CUR_CHAR == _SC('_')); + TERMINATE_BUFFER(); + res = GetIDType(&_longstr[0],_longstr.size() - 1); + if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) { + _svalue = &_longstr[0]; + } + return res; +} diff --git a/sp/src/vscript/squirrel/squirrel/sqlexer.h b/sp/src/vscript/squirrel/squirrel/sqlexer.h new file mode 100644 index 0000000000..d731c20e47 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqlexer.h @@ -0,0 +1,55 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQLEXER_H_ +#define _SQLEXER_H_ + +#ifdef SQUNICODE +typedef SQChar LexChar; +#else +typedef unsigned char LexChar; +#endif + +struct SQLexer +{ + SQLexer(); + ~SQLexer(); + void Init(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up,CompilerErrorFunc efunc,void *ed); + void Error(const SQChar *err); + SQInteger Lex(); + const SQChar *Tok2Str(SQInteger tok); +private: + SQInteger GetIDType(const SQChar *s,SQInteger len); + SQInteger ReadString(SQInteger ndelim,bool verbatim); + SQInteger ReadNumber(); + void LexBlockComment(); + void LexLineComment(); + SQInteger ReadID(); + void Next(); +#ifdef SQUNICODE +#if WCHAR_SIZE == 2 + SQInteger AddUTF16(SQUnsignedInteger ch); +#endif +#else + SQInteger AddUTF8(SQUnsignedInteger ch); +#endif + SQInteger ProcessStringHexEscape(SQChar *dest, SQInteger maxdigits); + SQInteger _curtoken; + SQTable *_keywords; + SQBool _reached_eof; +public: + SQInteger _prevtoken; + SQInteger _currentline; + SQInteger _lasttokenline; + SQInteger _currentcolumn; + const SQChar *_svalue; + SQInteger _nvalue; + SQFloat _fvalue; + SQLEXREADFUNC _readf; + SQUserPointer _up; + LexChar _currdata; + SQSharedState *_sharedstate; + sqvector _longstr; + CompilerErrorFunc _errfunc; + void *_errtarget; +}; + +#endif diff --git a/sp/src/vscript/squirrel/squirrel/sqmem.cpp b/sp/src/vscript/squirrel/squirrel/sqmem.cpp new file mode 100644 index 0000000000..378e254b48 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqmem.cpp @@ -0,0 +1,11 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#ifndef SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS +void *sq_vm_malloc(SQUnsignedInteger size){ return malloc(size); } + +void *sq_vm_realloc(void *p, SQUnsignedInteger SQ_UNUSED_ARG(oldsize), SQUnsignedInteger size){ return realloc(p, size); } + +void sq_vm_free(void *p, SQUnsignedInteger SQ_UNUSED_ARG(size)){ free(p); } +#endif diff --git a/sp/src/vscript/squirrel/squirrel/sqobject.cpp b/sp/src/vscript/squirrel/squirrel/sqobject.cpp new file mode 100644 index 0000000000..7d7f297fcb --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqobject.cpp @@ -0,0 +1,677 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqstring.h" +#include "sqarray.h" +#include "sqtable.h" +#include "squserdata.h" +#include "sqfuncproto.h" +#include "sqclass.h" +#include "sqclosure.h" + + +const SQChar *IdType2Name(SQObjectType type) +{ + switch(_RAW_TYPE(type)) + { + case _RT_NULL:return _SC("null"); + case _RT_INTEGER:return _SC("integer"); + case _RT_FLOAT:return _SC("float"); + case _RT_BOOL:return _SC("bool"); + case _RT_STRING:return _SC("string"); + case _RT_TABLE:return _SC("table"); + case _RT_ARRAY:return _SC("array"); + case _RT_GENERATOR:return _SC("generator"); + case _RT_CLOSURE: + case _RT_NATIVECLOSURE: + return _SC("function"); + case _RT_USERDATA: + case _RT_USERPOINTER: + return _SC("userdata"); + case _RT_THREAD: return _SC("thread"); + case _RT_FUNCPROTO: return _SC("function"); + case _RT_CLASS: return _SC("class"); + case _RT_INSTANCE: return _SC("instance"); + case _RT_WEAKREF: return _SC("weakref"); + case _RT_OUTER: return _SC("outer"); + default: + return NULL; + } +} + +const SQChar *GetTypeName(const SQObjectPtr &obj1) +{ + return IdType2Name(sq_type(obj1)); +} + +SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len) +{ + SQString *str=ADD_STRING(ss,s,len); + return str; +} + +void SQString::Release() +{ + REMOVE_STRING(_sharedstate,this); +} + +SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) +{ + SQInteger idx = (SQInteger)TranslateIndex(refpos); + while(idx < _len){ + outkey = (SQInteger)idx; + outval = (SQInteger)((SQUnsignedInteger)_val[idx]); + //return idx for the next iteration + return ++idx; + } + //nothing to iterate anymore + return -1; +} + +SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx) +{ + switch(sq_type(idx)){ + case OT_NULL: + return 0; + case OT_INTEGER: + return (SQUnsignedInteger)_integer(idx); + default: assert(0); break; + } + return 0; +} + +SQWeakRef *SQRefCounted::GetWeakRef(SQObjectType type) +{ + if(!_weakref) { + sq_new(_weakref,SQWeakRef); +#if defined(SQUSEDOUBLE) && !defined(_SQ64) + _weakref->_obj._unVal.raw = 0; //clean the whole union on 32 bits with double +#endif + _weakref->_obj._type = type; + _weakref->_obj._unVal.pRefCounted = this; + } + return _weakref; +} + +SQRefCounted::~SQRefCounted() +{ + if(_weakref) { + _weakref->_obj._type = OT_NULL; + _weakref->_obj._unVal.pRefCounted = NULL; + } +} + +void SQWeakRef::Release() { + if(ISREFCOUNTED(_obj._type)) { + _obj._unVal.pRefCounted->_weakref = NULL; + } + sq_delete(this,SQWeakRef); +} + +bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) { + if(_delegate) { + return _delegate->Get((*_ss(v)->_metamethods)[mm],res); + } + return false; +} + +bool SQDelegable::SetDelegate(SQTable *mt) +{ + SQTable *temp = mt; + if(temp == this) return false; + while (temp) { + if (temp->_delegate == this) return false; //cycle detected + temp = temp->_delegate; + } + if (mt) __ObjAddRef(mt); + __ObjRelease(_delegate); + _delegate = mt; + return true; +} + +bool SQGenerator::Yield(SQVM *v,SQInteger target) +{ + if(_state==eSuspended) { v->Raise_Error(_SC("internal vm error, yielding dead generator")); return false;} + if(_state==eDead) { v->Raise_Error(_SC("internal vm error, yielding a dead generator")); return false; } + SQInteger size = v->_top-v->_stackbase; + + _stack.resize(size); + SQObject _this = v->_stack[v->_stackbase]; + _stack._vals[0] = ISREFCOUNTED(sq_type(_this)) ? SQObjectPtr(_refcounted(_this)->GetWeakRef(sq_type(_this))) : _this; + for(SQInteger n =1; n_stack[v->_stackbase+n]; + } + for(SQInteger j =0; j < size; j++) + { + v->_stack[v->_stackbase+j].Null(); + } + + _ci = *v->ci; + _ci._generator=NULL; + for(SQInteger i=0;i<_ci._etraps;i++) { + _etraps.push_back(v->_etraps.top()); + v->_etraps.pop_back(); + // store relative stack base and size in case of resume to other _top + SQExceptionTrap &et = _etraps.back(); + et._stackbase -= v->_stackbase; + et._stacksize -= v->_stackbase; + } + _state=eSuspended; + return true; +} + +bool SQGenerator::Resume(SQVM *v,SQObjectPtr &dest) +{ + if(_state==eDead){ v->Raise_Error(_SC("resuming dead generator")); return false; } + if(_state==eRunning){ v->Raise_Error(_SC("resuming active generator")); return false; } + SQInteger size = _stack.size(); + SQInteger target = &dest - &(v->_stack._vals[v->_stackbase]); + assert(target>=0 && target<=255); + SQInteger newbase = v->_top; + if(!v->EnterFrame(v->_top, v->_top + size, false)) + return false; + v->ci->_generator = this; + v->ci->_target = (SQInt32)target; + v->ci->_closure = _ci._closure; + v->ci->_ip = _ci._ip; + v->ci->_literals = _ci._literals; + v->ci->_ncalls = _ci._ncalls; + v->ci->_etraps = _ci._etraps; + v->ci->_root = _ci._root; + + + for(SQInteger i=0;i<_ci._etraps;i++) { + v->_etraps.push_back(_etraps.top()); + _etraps.pop_back(); + SQExceptionTrap &et = v->_etraps.back(); + // restore absolute stack base and size + et._stackbase += newbase; + et._stacksize += newbase; + } + SQObject _this = _stack._vals[0]; + v->_stack[v->_stackbase] = sq_type(_this) == OT_WEAKREF ? _weakref(_this)->_obj : _this; + + for(SQInteger n = 1; n_stack[v->_stackbase+n] = _stack._vals[n]; + _stack._vals[n].Null(); + } + + _state=eRunning; + if (v->_debughook) + v->CallDebugHook(_SC('c')); + + return true; +} + +void SQArray::Extend(const SQArray *a){ + SQInteger xlen; + if((xlen=a->Size())) + for(SQInteger i=0;i_values[i]); +} + +const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop) +{ + SQUnsignedInteger nvars=_nlocalvarinfos; + const SQChar *res=NULL; + if(nvars>=nseq){ + for(SQUnsignedInteger i=0;i=nop) + { + if(nseq==0){ + vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]); + res=_stringval(_localvarinfos[i]._name); + break; + } + nseq--; + } + } + } + return res; +} + + +SQInteger SQFunctionProto::GetLine(SQInstruction *curr) +{ + SQInteger op = (SQInteger)(curr-_instructions); + SQInteger line=_lineinfos[0]._line; + SQInteger low = 0; + SQInteger high = _nlineinfos - 1; + SQInteger mid = 0; + while(low <= high) + { + mid = low + ((high - low) >> 1); + SQInteger curop = _lineinfos[mid]._op; + if(curop > op) + { + high = mid - 1; + } + else if(curop < op) { + if(mid < (_nlineinfos - 1) + && _lineinfos[mid + 1]._op >= op) { + break; + } + low = mid + 1; + } + else { //equal + break; + } + } + + while(mid > 0 && _lineinfos[mid]._op >= op) mid--; + + line = _lineinfos[mid]._line; + + return line; +} + +SQClosure::~SQClosure() +{ + __ObjRelease(_root); + __ObjRelease(_env); + __ObjRelease(_base); + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); +} + +#define _CHECK_IO(exp) { if(!exp)return false; } +bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size) +{ + if(write(up,dest,size) != size) { + v->Raise_Error(_SC("io error (write function failure)")); + return false; + } + return true; +} + +bool SafeRead(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size) +{ + if(size && read(up,dest,size) != size) { + v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated")); + return false; + } + return true; +} + +bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUnsignedInteger32 tag) +{ + return SafeWrite(v,write,up,&tag,sizeof(tag)); +} + +bool CheckTag(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUnsignedInteger32 tag) +{ + SQUnsignedInteger32 t; + _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t))); + if(t != tag){ + v->Raise_Error(_SC("invalid or corrupted closure stream")); + return false; + } + return true; +} + +bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o) +{ + SQUnsignedInteger32 _type = (SQUnsignedInteger32)sq_type(o); + _CHECK_IO(SafeWrite(v,write,up,&_type,sizeof(_type))); + switch(sq_type(o)){ + case OT_STRING: + _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger))); + _CHECK_IO(SafeWrite(v,write,up,_stringval(o),sq_rsl(_string(o)->_len))); + break; + case OT_BOOL: + case OT_INTEGER: + _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break; + case OT_FLOAT: + _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break; + case OT_NULL: + break; + default: + v->Raise_Error(_SC("cannot serialize a %s"),GetTypeName(o)); + return false; + } + return true; +} + +bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o) +{ + SQUnsignedInteger32 _type; + _CHECK_IO(SafeRead(v,read,up,&_type,sizeof(_type))); + SQObjectType t = (SQObjectType)_type; + switch(t){ + case OT_STRING:{ + SQInteger len; + _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger))); + _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(sq_rsl(len)),sq_rsl(len))); + o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len); + } + break; + case OT_INTEGER:{ + SQInteger i; + _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break; + } + case OT_BOOL:{ + SQInteger i; + _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o._type = OT_BOOL; o._unVal.nInteger = i; break; + } + case OT_FLOAT:{ + SQFloat f; + _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break; + } + case OT_NULL: + o.Null(); + break; + default: + v->Raise_Error(_SC("cannot serialize a %s"),IdType2Name(t)); + return false; + } + return true; +} + +bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) +{ + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD)); + _CHECK_IO(WriteTag(v,write,up,sizeof(SQChar))); + _CHECK_IO(WriteTag(v,write,up,sizeof(SQInteger))); + _CHECK_IO(WriteTag(v,write,up,sizeof(SQFloat))); + _CHECK_IO(_function->Save(v,up,write)); + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL)); + return true; +} + +bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) +{ + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD)); + _CHECK_IO(CheckTag(v,read,up,sizeof(SQChar))); + _CHECK_IO(CheckTag(v,read,up,sizeof(SQInteger))); + _CHECK_IO(CheckTag(v,read,up,sizeof(SQFloat))); + SQObjectPtr func; + _CHECK_IO(SQFunctionProto::Load(v,up,read,func)); + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL)); + ret = SQClosure::Create(_ss(v),_funcproto(func),_table(v->_roottable)->GetWeakRef(OT_TABLE)); + //FIXME: load an root for this closure + return true; +} + +SQFunctionProto::SQFunctionProto(SQSharedState *ss) +{ + _stacksize=0; + _bgenerator=false; + INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); +} + +SQFunctionProto::~SQFunctionProto() +{ + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); +} + +bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) +{ + SQInteger i,nliterals = _nliterals,nparameters = _nparameters; + SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos; + SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions; + SQInteger ndefaultparams = _ndefaultparams; + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(WriteObject(v,up,write,_sourcename)); + _CHECK_IO(WriteObject(v,up,write,_name)); + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals))); + _CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters))); + _CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues))); + _CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos))); + _CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos))); + _CHECK_IO(SafeWrite(v,write,up,&ndefaultparams,sizeof(ndefaultparams))); + _CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions))); + _CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions))); + _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); + for(i=0;iSave(v,up,write)); + } + _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize))); + _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator))); + _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams))); + return true; +} + +bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) +{ + SQInteger i, nliterals,nparameters; + SQInteger noutervalues ,nlocalvarinfos ; + SQInteger nlineinfos,ninstructions ,nfunctions,ndefaultparams ; + SQObjectPtr sourcename, name; + SQObjectPtr o; + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(ReadObject(v, up, read, sourcename)); + _CHECK_IO(ReadObject(v, up, read, name)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals))); + _CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters))); + _CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues))); + _CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos))); + _CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos))); + _CHECK_IO(SafeRead(v,read,up, &ndefaultparams, sizeof(ndefaultparams))); + _CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions))); + _CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions))); + + + SQFunctionProto *f = SQFunctionProto::Create(_opt_ss(v),ninstructions,nliterals,nparameters, + nfunctions,noutervalues,nlineinfos,nlocalvarinfos,ndefaultparams); + SQObjectPtr proto = f; //gets a ref in case of failure + f->_sourcename = sourcename; + f->_name = name; + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0;i < nliterals; i++){ + _CHECK_IO(ReadObject(v, up, read, o)); + f->_literals[i] = o; + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0; i < nparameters; i++){ + _CHECK_IO(ReadObject(v, up, read, o)); + f->_parameters[i] = o; + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0; i < noutervalues; i++){ + SQUnsignedInteger type; + SQObjectPtr name; + _CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger))); + _CHECK_IO(ReadObject(v, up, read, o)); + _CHECK_IO(ReadObject(v, up, read, name)); + f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type); + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + + for(i = 0; i < nlocalvarinfos; i++){ + SQLocalVarInfo lvi; + _CHECK_IO(ReadObject(v, up, read, lvi._name)); + _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger))); + _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger))); + _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger))); + f->_localvarinfos[i] = lvi; + } + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, f->_lineinfos, sizeof(SQLineInfo)*nlineinfos)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, f->_defaultparams, sizeof(SQInteger)*ndefaultparams)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + _CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions)); + + _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); + for(i = 0; i < nfunctions; i++){ + _CHECK_IO(_funcproto(o)->Load(v, up, read, o)); + f->_functions[i] = o; + } + _CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize))); + _CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator))); + _CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams))); + + ret = f; + return true; +} + +#ifndef NO_GARBAGE_COLLECTOR + +#define START_MARK() if(!(_uiRef&MARK_FLAG)){ \ + _uiRef|=MARK_FLAG; + +#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \ + AddToChain(chain, this); } + +void SQVM::Mark(SQCollectable **chain) +{ + START_MARK() + SQSharedState::MarkObject(_lasterror,chain); + SQSharedState::MarkObject(_errorhandler,chain); + SQSharedState::MarkObject(_debughook_closure,chain); + SQSharedState::MarkObject(_roottable, chain); + SQSharedState::MarkObject(temp_reg, chain); + for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); + for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain); + END_MARK() +} + +void SQArray::Mark(SQCollectable **chain) +{ + START_MARK() + SQInteger len = _values.size(); + for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain); + END_MARK() +} +void SQTable::Mark(SQCollectable **chain) +{ + START_MARK() + if(_delegate) _delegate->Mark(chain); + SQInteger len = _numofnodes; + for(SQInteger i = 0; i < len; i++){ + SQSharedState::MarkObject(_nodes[i].key, chain); + SQSharedState::MarkObject(_nodes[i].val, chain); + } + END_MARK() +} + +void SQClass::Mark(SQCollectable **chain) +{ + START_MARK() + _members->Mark(chain); + if(_base) _base->Mark(chain); + SQSharedState::MarkObject(_attributes, chain); + for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { + SQSharedState::MarkObject(_defaultvalues[i].val, chain); + SQSharedState::MarkObject(_defaultvalues[i].attrs, chain); + } + for(SQUnsignedInteger j =0; j< _methods.size(); j++) { + SQSharedState::MarkObject(_methods[j].val, chain); + SQSharedState::MarkObject(_methods[j].attrs, chain); + } + for(SQUnsignedInteger k =0; k< MT_LAST; k++) { + SQSharedState::MarkObject(_metamethods[k], chain); + } + END_MARK() +} + +void SQInstance::Mark(SQCollectable **chain) +{ + START_MARK() + _class->Mark(chain); + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger i =0; i< nvalues; i++) { + SQSharedState::MarkObject(_values[i], chain); + } + END_MARK() +} + +void SQGenerator::Mark(SQCollectable **chain) +{ + START_MARK() + for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); + SQSharedState::MarkObject(_closure, chain); + END_MARK() +} + +void SQFunctionProto::Mark(SQCollectable **chain) +{ + START_MARK() + for(SQInteger i = 0; i < _nliterals; i++) SQSharedState::MarkObject(_literals[i], chain); + for(SQInteger k = 0; k < _nfunctions; k++) SQSharedState::MarkObject(_functions[k], chain); + END_MARK() +} + +void SQClosure::Mark(SQCollectable **chain) +{ + START_MARK() + if(_base) _base->Mark(chain); + SQFunctionProto *fp = _function; + fp->Mark(chain); + for(SQInteger i = 0; i < fp->_noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); + for(SQInteger k = 0; k < fp->_ndefaultparams; k++) SQSharedState::MarkObject(_defaultparams[k], chain); + END_MARK() +} + +void SQNativeClosure::Mark(SQCollectable **chain) +{ + START_MARK() + for(SQUnsignedInteger i = 0; i < _noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); + END_MARK() +} + +void SQOuter::Mark(SQCollectable **chain) +{ + START_MARK() + /* If the valptr points to a closed value, that value is alive */ + if(_valptr == &_value) { + SQSharedState::MarkObject(_value, chain); + } + END_MARK() +} + +void SQUserData::Mark(SQCollectable **chain){ + START_MARK() + if(_delegate) _delegate->Mark(chain); + END_MARK() +} + +void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; } + +#endif + diff --git a/sp/src/vscript/squirrel/squirrel/sqobject.h b/sp/src/vscript/squirrel/squirrel/sqobject.h new file mode 100644 index 0000000000..a202222748 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqobject.h @@ -0,0 +1,353 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQOBJECT_H_ +#define _SQOBJECT_H_ + +#include "squtils.h" + +#ifdef _SQ64 +#define UINT_MINUS_ONE (0xFFFFFFFFFFFFFFFF) +#else +#define UINT_MINUS_ONE (0xFFFFFFFF) +#endif + +#define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) +#define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T')) +#define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L')) + +struct SQSharedState; + +enum SQMetaMethod{ + MT_ADD=0, + MT_SUB=1, + MT_MUL=2, + MT_DIV=3, + MT_UNM=4, + MT_MODULO=5, + MT_SET=6, + MT_GET=7, + MT_TYPEOF=8, + MT_NEXTI=9, + MT_CMP=10, + MT_CALL=11, + MT_CLONED=12, + MT_NEWSLOT=13, + MT_DELSLOT=14, + MT_TOSTRING=15, + MT_NEWMEMBER=16, + MT_INHERITED=17, + MT_LAST = 18 +}; + +#define MM_ADD _SC("_add") +#define MM_SUB _SC("_sub") +#define MM_MUL _SC("_mul") +#define MM_DIV _SC("_div") +#define MM_UNM _SC("_unm") +#define MM_MODULO _SC("_modulo") +#define MM_SET _SC("_set") +#define MM_GET _SC("_get") +#define MM_TYPEOF _SC("_typeof") +#define MM_NEXTI _SC("_nexti") +#define MM_CMP _SC("_cmp") +#define MM_CALL _SC("_call") +#define MM_CLONED _SC("_cloned") +#define MM_NEWSLOT _SC("_newslot") +#define MM_DELSLOT _SC("_delslot") +#define MM_TOSTRING _SC("_tostring") +#define MM_NEWMEMBER _SC("_newmember") +#define MM_INHERITED _SC("_inherited") + + +#define _CONSTRUCT_VECTOR(type,size,ptr) { \ + for(SQInteger n = 0; n < ((SQInteger)size); n++) { \ + new (&ptr[n]) type(); \ + } \ +} + +#define _DESTRUCT_VECTOR(type,size,ptr) { \ + for(SQInteger nl = 0; nl < ((SQInteger)size); nl++) { \ + ptr[nl].~type(); \ + } \ +} + +#define _COPY_VECTOR(dest,src,size) { \ + for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ + dest[_n_] = src[_n_]; \ + } \ +} + +#define _NULL_SQOBJECT_VECTOR(vec,size) { \ + for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ + vec[_n_].Null(); \ + } \ +} + +#define MINPOWER2 4 + +struct SQRefCounted +{ + SQUnsignedInteger _uiRef; + struct SQWeakRef *_weakref; + SQRefCounted() { _uiRef = 0; _weakref = NULL; } + virtual ~SQRefCounted(); + SQWeakRef *GetWeakRef(SQObjectType type); + virtual void Release()=0; + +}; + +struct SQWeakRef : SQRefCounted +{ + void Release(); + SQObject _obj; +}; + +#define _realval(o) (sq_type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj) + +struct SQObjectPtr; + +#define __AddRef(type,unval) if(ISREFCOUNTED(type)) \ + { \ + unval.pRefCounted->_uiRef++; \ + } + +#define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)==0)) \ + { \ + unval.pRefCounted->Release(); \ + } + +#define __ObjRelease(obj) { \ + if((obj)) { \ + (obj)->_uiRef--; \ + if((obj)->_uiRef == 0) \ + (obj)->Release(); \ + (obj) = NULL; \ + } \ +} + +#define __ObjAddRef(obj) { \ + (obj)->_uiRef++; \ +} + +#define is_delegable(t) (sq_type(t)&SQOBJECT_DELEGABLE) +#define raw_type(obj) _RAW_TYPE((obj)._type) + +#define _integer(obj) ((obj)._unVal.nInteger) +#define _float(obj) ((obj)._unVal.fFloat) +#define _string(obj) ((obj)._unVal.pString) +#define _table(obj) ((obj)._unVal.pTable) +#define _array(obj) ((obj)._unVal.pArray) +#define _closure(obj) ((obj)._unVal.pClosure) +#define _generator(obj) ((obj)._unVal.pGenerator) +#define _nativeclosure(obj) ((obj)._unVal.pNativeClosure) +#define _userdata(obj) ((obj)._unVal.pUserData) +#define _userpointer(obj) ((obj)._unVal.pUserPointer) +#define _thread(obj) ((obj)._unVal.pThread) +#define _funcproto(obj) ((obj)._unVal.pFunctionProto) +#define _class(obj) ((obj)._unVal.pClass) +#define _instance(obj) ((obj)._unVal.pInstance) +#define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable) +#define _weakref(obj) ((obj)._unVal.pWeakRef) +#define _outer(obj) ((obj)._unVal.pOuter) +#define _refcounted(obj) ((obj)._unVal.pRefCounted) +#define _rawval(obj) ((obj)._unVal.raw) + +#define _stringval(obj) (obj)._unVal.pString->_val +#define _userdataval(obj) ((SQUserPointer)sq_aligning((obj)._unVal.pUserData + 1)) + +#define tofloat(num) ((sq_type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) +#define tointeger(num) ((sq_type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num)) +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +#if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) +#define SQ_REFOBJECT_INIT() SQ_OBJECT_RAWINIT() +#else +#define SQ_REFOBJECT_INIT() +#endif + +#define _REF_TYPE_DECL(type,_class,sym) \ + SQObjectPtr(_class * x) \ + { \ + SQ_OBJECT_RAWINIT() \ + _type=type; \ + _unVal.sym = x; \ + assert(_unVal.pTable); \ + _unVal.pRefCounted->_uiRef++; \ + } \ + inline SQObjectPtr& operator=(_class *x) \ + { \ + SQObjectType tOldType; \ + SQObjectValue unOldVal; \ + tOldType=_type; \ + unOldVal=_unVal; \ + _type = type; \ + SQ_REFOBJECT_INIT() \ + _unVal.sym = x; \ + _unVal.pRefCounted->_uiRef++; \ + __Release(tOldType,unOldVal); \ + return *this; \ + } + +#define _SCALAR_TYPE_DECL(type,_class,sym) \ + SQObjectPtr(_class x) \ + { \ + SQ_OBJECT_RAWINIT() \ + _type=type; \ + _unVal.sym = x; \ + } \ + inline SQObjectPtr& operator=(_class x) \ + { \ + __Release(_type,_unVal); \ + _type = type; \ + SQ_OBJECT_RAWINIT() \ + _unVal.sym = x; \ + return *this; \ + } +struct SQObjectPtr : public SQObject +{ + SQObjectPtr() + { + SQ_OBJECT_RAWINIT() + _type=OT_NULL; + _unVal.pUserPointer=NULL; + } + SQObjectPtr(const SQObjectPtr &o) + { + _type = o._type; + _unVal = o._unVal; + __AddRef(_type,_unVal); + } + SQObjectPtr(const SQObject &o) + { + _type = o._type; + _unVal = o._unVal; + __AddRef(_type,_unVal); + } + _REF_TYPE_DECL(OT_TABLE,SQTable,pTable) + _REF_TYPE_DECL(OT_CLASS,SQClass,pClass) + _REF_TYPE_DECL(OT_INSTANCE,SQInstance,pInstance) + _REF_TYPE_DECL(OT_ARRAY,SQArray,pArray) + _REF_TYPE_DECL(OT_CLOSURE,SQClosure,pClosure) + _REF_TYPE_DECL(OT_NATIVECLOSURE,SQNativeClosure,pNativeClosure) + _REF_TYPE_DECL(OT_OUTER,SQOuter,pOuter) + _REF_TYPE_DECL(OT_GENERATOR,SQGenerator,pGenerator) + _REF_TYPE_DECL(OT_STRING,SQString,pString) + _REF_TYPE_DECL(OT_USERDATA,SQUserData,pUserData) + _REF_TYPE_DECL(OT_WEAKREF,SQWeakRef,pWeakRef) + _REF_TYPE_DECL(OT_THREAD,SQVM,pThread) + _REF_TYPE_DECL(OT_FUNCPROTO,SQFunctionProto,pFunctionProto) + + _SCALAR_TYPE_DECL(OT_INTEGER,SQInteger,nInteger) + _SCALAR_TYPE_DECL(OT_FLOAT,SQFloat,fFloat) + _SCALAR_TYPE_DECL(OT_USERPOINTER,SQUserPointer,pUserPointer) + + SQObjectPtr(bool bBool) + { + SQ_OBJECT_RAWINIT() + _type = OT_BOOL; + _unVal.nInteger = bBool?1:0; + } + inline SQObjectPtr& operator=(bool b) + { + __Release(_type,_unVal); + SQ_OBJECT_RAWINIT() + _type = OT_BOOL; + _unVal.nInteger = b?1:0; + return *this; + } + + ~SQObjectPtr() + { + __Release(_type,_unVal); + } + + inline SQObjectPtr& operator=(const SQObjectPtr& obj) + { + SQObjectType tOldType; + SQObjectValue unOldVal; + tOldType=_type; + unOldVal=_unVal; + _unVal = obj._unVal; + _type = obj._type; + __AddRef(_type,_unVal); + __Release(tOldType,unOldVal); + return *this; + } + inline SQObjectPtr& operator=(const SQObject& obj) + { + SQObjectType tOldType; + SQObjectValue unOldVal; + tOldType=_type; + unOldVal=_unVal; + _unVal = obj._unVal; + _type = obj._type; + __AddRef(_type,_unVal); + __Release(tOldType,unOldVal); + return *this; + } + inline void Null() + { + SQObjectType tOldType = _type; + SQObjectValue unOldVal = _unVal; + _type = OT_NULL; + _unVal.raw = (SQRawObjectVal)NULL; + __Release(tOldType ,unOldVal); + } + private: + SQObjectPtr(const SQChar *){} //safety +}; + + +inline void _Swap(SQObject &a,SQObject &b) +{ + SQObjectType tOldType = a._type; + SQObjectValue unOldVal = a._unVal; + a._type = b._type; + a._unVal = b._unVal; + b._type = tOldType; + b._unVal = unOldVal; +} + +///////////////////////////////////////////////////////////////////////////////////// +#ifndef NO_GARBAGE_COLLECTOR +#define MARK_FLAG 0x80000000 +struct SQCollectable : public SQRefCounted { + SQCollectable *_next; + SQCollectable *_prev; + SQSharedState *_sharedstate; + virtual SQObjectType GetType()=0; + virtual void Release()=0; + virtual void Mark(SQCollectable **chain)=0; + void UnMark(); + virtual void Finalize()=0; + static void AddToChain(SQCollectable **chain,SQCollectable *c); + static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); +}; + + +#define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj) +#define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);} +#define CHAINABLE_OBJ SQCollectable +#define INIT_CHAIN() {_next=NULL;_prev=NULL;_sharedstate=ss;} +#else + +#define ADD_TO_CHAIN(chain,obj) ((void)0) +#define REMOVE_FROM_CHAIN(chain,obj) ((void)0) +#define CHAINABLE_OBJ SQRefCounted +#define INIT_CHAIN() ((void)0) +#endif + +struct SQDelegable : public CHAINABLE_OBJ { + bool SetDelegate(SQTable *m); + virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); + SQTable *_delegate; +}; + +SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx); +typedef sqvector SQObjectPtrVec; +typedef sqvector SQIntVec; +const SQChar *GetTypeName(const SQObjectPtr &obj1); +const SQChar *IdType2Name(SQObjectType type); + + + +#endif //_SQOBJECT_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqopcodes.h b/sp/src/vscript/squirrel/squirrel/sqopcodes.h new file mode 100644 index 0000000000..15d80e4bb0 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqopcodes.h @@ -0,0 +1,132 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQOPCODES_H_ +#define _SQOPCODES_H_ + +#define MAX_FUNC_STACKSIZE 0xFF +#define MAX_LITERALS ((SQInteger)0x7FFFFFFF) + +enum BitWiseOP { + BW_AND = 0, + BW_OR = 2, + BW_XOR = 3, + BW_SHIFTL = 4, + BW_SHIFTR = 5, + BW_USHIFTR = 6 +}; + +enum CmpOP { + CMP_G = 0, + CMP_GE = 2, + CMP_L = 3, + CMP_LE = 4, + CMP_3W = 5 +}; + +enum NewObjectType { + NOT_TABLE = 0, + NOT_ARRAY = 1, + NOT_CLASS = 2 +}; + +enum AppendArrayType { + AAT_STACK = 0, + AAT_LITERAL = 1, + AAT_INT = 2, + AAT_FLOAT = 3, + AAT_BOOL = 4 +}; + +enum SQOpcode +{ + _OP_LINE= 0x00, + _OP_LOAD= 0x01, + _OP_LOADINT= 0x02, + _OP_LOADFLOAT= 0x03, + _OP_DLOAD= 0x04, + _OP_TAILCALL= 0x05, + _OP_CALL= 0x06, + _OP_PREPCALL= 0x07, + _OP_PREPCALLK= 0x08, + _OP_GETK= 0x09, + _OP_MOVE= 0x0A, + _OP_NEWSLOT= 0x0B, + _OP_DELETE= 0x0C, + _OP_SET= 0x0D, + _OP_GET= 0x0E, + _OP_EQ= 0x0F, + _OP_NE= 0x10, + _OP_ADD= 0x11, + _OP_SUB= 0x12, + _OP_MUL= 0x13, + _OP_DIV= 0x14, + _OP_MOD= 0x15, + _OP_BITW= 0x16, + _OP_RETURN= 0x17, + _OP_LOADNULLS= 0x18, + _OP_LOADROOT= 0x19, + _OP_LOADBOOL= 0x1A, + _OP_DMOVE= 0x1B, + _OP_JMP= 0x1C, + //_OP_JNZ= 0x1D, + _OP_JCMP= 0x1D, + _OP_JZ= 0x1E, + _OP_SETOUTER= 0x1F, + _OP_GETOUTER= 0x20, + _OP_NEWOBJ= 0x21, + _OP_APPENDARRAY= 0x22, + _OP_COMPARITH= 0x23, + _OP_INC= 0x24, + _OP_INCL= 0x25, + _OP_PINC= 0x26, + _OP_PINCL= 0x27, + _OP_CMP= 0x28, + _OP_EXISTS= 0x29, + _OP_INSTANCEOF= 0x2A, + _OP_AND= 0x2B, + _OP_OR= 0x2C, + _OP_NEG= 0x2D, + _OP_NOT= 0x2E, + _OP_BWNOT= 0x2F, + _OP_CLOSURE= 0x30, + _OP_YIELD= 0x31, + _OP_RESUME= 0x32, + _OP_FOREACH= 0x33, + _OP_POSTFOREACH= 0x34, + _OP_CLONE= 0x35, + _OP_TYPEOF= 0x36, + _OP_PUSHTRAP= 0x37, + _OP_POPTRAP= 0x38, + _OP_THROW= 0x39, + _OP_NEWSLOTA= 0x3A, + _OP_GETBASE= 0x3B, + _OP_CLOSE= 0x3C +}; + +struct SQInstructionDesc { + const SQChar *name; +}; + +struct SQInstruction +{ + SQInstruction(){}; + SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0) + { op = (unsigned char)_op; + _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1; + _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; + } + + + SQInt32 _arg1; + unsigned char op; + unsigned char _arg0; + unsigned char _arg2; + unsigned char _arg3; +}; + +#include "squtils.h" +typedef sqvector SQInstructionVec; + +#define NEW_SLOT_ATTRIBUTES_FLAG 0x01 +#define NEW_SLOT_STATIC_FLAG 0x02 + +#endif // _SQOPCODES_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqpcheader.h b/sp/src/vscript/squirrel/squirrel/sqpcheader.h new file mode 100644 index 0000000000..8df5ef4c5e --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqpcheader.h @@ -0,0 +1,20 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQPCHEADER_H_ +#define _SQPCHEADER_H_ + +#if defined(_MSC_VER) && defined(_DEBUG) +#include +#endif + +#include +#include +#include +#include +#include +#include +//squirrel stuff +#include +#include "sqobject.h" +#include "sqstate.h" + +#endif //_SQPCHEADER_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqstate.cpp b/sp/src/vscript/squirrel/squirrel/sqstate.cpp new file mode 100644 index 0000000000..c89bdc4a25 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqstate.cpp @@ -0,0 +1,647 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqopcodes.h" +#include "sqvm.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqstring.h" +#include "sqtable.h" +#include "sqarray.h" +#include "squserdata.h" +#include "sqclass.h" + +SQSharedState::SQSharedState() +{ + _compilererrorhandler = NULL; + _printfunc = NULL; + _errorfunc = NULL; + _debuginfo = false; + _notifyallexceptions = false; + _foreignptr = NULL; + _releasehook = NULL; +} + +#define newsysstring(s) { \ + _systemstrings->push_back(SQString::Create(this,s)); \ + } + +#define newmetamethod(s) { \ + _metamethods->push_back(SQString::Create(this,s)); \ + _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \ + } + +bool CompileTypemask(SQIntVec &res,const SQChar *typemask) +{ + SQInteger i = 0; + SQInteger mask = 0; + while(typemask[i] != 0) { + switch(typemask[i]) { + case 'o': mask |= _RT_NULL; break; + case 'i': mask |= _RT_INTEGER; break; + case 'f': mask |= _RT_FLOAT; break; + case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break; + case 's': mask |= _RT_STRING; break; + case 't': mask |= _RT_TABLE; break; + case 'a': mask |= _RT_ARRAY; break; + case 'u': mask |= _RT_USERDATA; break; + case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break; + case 'b': mask |= _RT_BOOL; break; + case 'g': mask |= _RT_GENERATOR; break; + case 'p': mask |= _RT_USERPOINTER; break; + case 'v': mask |= _RT_THREAD; break; + case 'x': mask |= _RT_INSTANCE; break; + case 'y': mask |= _RT_CLASS; break; + case 'r': mask |= _RT_WEAKREF; break; + case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; + case ' ': i++; continue; //ignores spaces + default: + return false; + } + i++; + if(typemask[i] == '|') { + i++; + if(typemask[i] == 0) + return false; + continue; + } + res.push_back(mask); + mask = 0; + + } + return true; +} + +SQTable *CreateDefaultDelegate(SQSharedState *ss,const SQRegFunction *funcz) +{ + SQInteger i=0; + SQTable *t=SQTable::Create(ss,0); + while(funcz[i].name!=0){ + SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f,0); + nc->_nparamscheck = funcz[i].nparamscheck; + nc->_name = SQString::Create(ss,funcz[i].name); + if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask)) + return NULL; + t->NewSlot(SQString::Create(ss,funcz[i].name),nc); + i++; + } + return t; +} + +void SQSharedState::Init() +{ + _scratchpad=NULL; + _scratchpadsize=0; +#ifndef NO_GARBAGE_COLLECTOR + _gc_chain=NULL; +#endif + _stringtable = (SQStringTable*)SQ_MALLOC(sizeof(SQStringTable)); + new (_stringtable) SQStringTable(this); + sq_new(_metamethods,SQObjectPtrVec); + sq_new(_systemstrings,SQObjectPtrVec); + sq_new(_types,SQObjectPtrVec); + _metamethodsmap = SQTable::Create(this,MT_LAST-1); + //adding type strings to avoid memory trashing + //types names + newsysstring(_SC("null")); + newsysstring(_SC("table")); + newsysstring(_SC("array")); + newsysstring(_SC("closure")); + newsysstring(_SC("string")); + newsysstring(_SC("userdata")); + newsysstring(_SC("integer")); + newsysstring(_SC("float")); + newsysstring(_SC("userpointer")); + newsysstring(_SC("function")); + newsysstring(_SC("generator")); + newsysstring(_SC("thread")); + newsysstring(_SC("class")); + newsysstring(_SC("instance")); + newsysstring(_SC("bool")); + //meta methods + newmetamethod(MM_ADD); + newmetamethod(MM_SUB); + newmetamethod(MM_MUL); + newmetamethod(MM_DIV); + newmetamethod(MM_UNM); + newmetamethod(MM_MODULO); + newmetamethod(MM_SET); + newmetamethod(MM_GET); + newmetamethod(MM_TYPEOF); + newmetamethod(MM_NEXTI); + newmetamethod(MM_CMP); + newmetamethod(MM_CALL); + newmetamethod(MM_CLONED); + newmetamethod(MM_NEWSLOT); + newmetamethod(MM_DELSLOT); + newmetamethod(MM_TOSTRING); + newmetamethod(MM_NEWMEMBER); + newmetamethod(MM_INHERITED); + + _constructoridx = SQString::Create(this,_SC("constructor")); + _registry = SQTable::Create(this,0); + _consts = SQTable::Create(this,0); + _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz); + _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz); + _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz); + _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz); + _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz); + _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz); + _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz); + _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz); + _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz); + _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz); +} + +SQSharedState::~SQSharedState() +{ + if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } + _constructoridx.Null(); + _table(_registry)->Finalize(); + _table(_consts)->Finalize(); + _table(_metamethodsmap)->Finalize(); + _registry.Null(); + _consts.Null(); + _metamethodsmap.Null(); + while(!_systemstrings->empty()) { + _systemstrings->back().Null(); + _systemstrings->pop_back(); + } + _thread(_root_vm)->Finalize(); + _root_vm.Null(); + _table_default_delegate.Null(); + _array_default_delegate.Null(); + _string_default_delegate.Null(); + _number_default_delegate.Null(); + _closure_default_delegate.Null(); + _generator_default_delegate.Null(); + _thread_default_delegate.Null(); + _class_default_delegate.Null(); + _instance_default_delegate.Null(); + _weakref_default_delegate.Null(); + _refs_table.Finalize(); +#ifndef NO_GARBAGE_COLLECTOR + SQCollectable *t = _gc_chain; + SQCollectable *nx = NULL; + if(t) { + t->_uiRef++; + while(t) { + t->Finalize(); + nx = t->_next; + if(nx) nx->_uiRef++; + if(--t->_uiRef == 0) + t->Release(); + t = nx; + } + } + assert(_gc_chain==NULL); //just to proove a theory + while(_gc_chain){ + _gc_chain->_uiRef++; + _gc_chain->Release(); + } +#endif + + sq_delete(_types,SQObjectPtrVec); + sq_delete(_systemstrings,SQObjectPtrVec); + sq_delete(_metamethods,SQObjectPtrVec); + sq_delete(_stringtable,SQStringTable); + if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize); +} + + +SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) +{ + if(sq_type(name) != OT_STRING) + return -1; + SQObjectPtr ret; + if(_table(_metamethodsmap)->Get(name,ret)) { + return _integer(ret); + } + return -1; +} + +#ifndef NO_GARBAGE_COLLECTOR + +void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) +{ + switch(sq_type(o)){ + case OT_TABLE:_table(o)->Mark(chain);break; + case OT_ARRAY:_array(o)->Mark(chain);break; + case OT_USERDATA:_userdata(o)->Mark(chain);break; + case OT_CLOSURE:_closure(o)->Mark(chain);break; + case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; + case OT_GENERATOR:_generator(o)->Mark(chain);break; + case OT_THREAD:_thread(o)->Mark(chain);break; + case OT_CLASS:_class(o)->Mark(chain);break; + case OT_INSTANCE:_instance(o)->Mark(chain);break; + case OT_OUTER:_outer(o)->Mark(chain);break; + case OT_FUNCPROTO:_funcproto(o)->Mark(chain);break; + default: break; //shutup compiler + } +} + +void SQSharedState::RunMark(SQVM* SQ_UNUSED_ARG(vm),SQCollectable **tchain) +{ + SQVM *vms = _thread(_root_vm); + + vms->Mark(tchain); + + _refs_table.Mark(tchain); + MarkObject(_registry,tchain); + MarkObject(_consts,tchain); + MarkObject(_metamethodsmap,tchain); + MarkObject(_table_default_delegate,tchain); + MarkObject(_array_default_delegate,tchain); + MarkObject(_string_default_delegate,tchain); + MarkObject(_number_default_delegate,tchain); + MarkObject(_generator_default_delegate,tchain); + MarkObject(_thread_default_delegate,tchain); + MarkObject(_closure_default_delegate,tchain); + MarkObject(_class_default_delegate,tchain); + MarkObject(_instance_default_delegate,tchain); + MarkObject(_weakref_default_delegate,tchain); + +} + +SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) +{ + SQInteger n=0; + SQCollectable *tchain=NULL; + + RunMark(vm,&tchain); + + SQCollectable *resurrected = _gc_chain; + SQCollectable *t = resurrected; + + _gc_chain = tchain; + + SQArray *ret = NULL; + if(resurrected) { + ret = SQArray::Create(this,0); + SQCollectable *rlast = NULL; + while(t) { + rlast = t; + SQObjectType type = t->GetType(); + if(type != OT_FUNCPROTO && type != OT_OUTER) { + SQObject sqo; + sqo._type = type; + sqo._unVal.pRefCounted = t; + ret->Append(sqo); + } + t = t->_next; + n++; + } + + assert(rlast->_next == NULL); + rlast->_next = _gc_chain; + if(_gc_chain) + { + _gc_chain->_prev = rlast; + } + _gc_chain = resurrected; + } + + t = _gc_chain; + while(t) { + t->UnMark(); + t = t->_next; + } + + if(ret) { + SQObjectPtr temp = ret; + vm->Push(temp); + } + else { + vm->PushNull(); + } + return n; +} + +SQInteger SQSharedState::CollectGarbage(SQVM *vm) +{ + SQInteger n = 0; + SQCollectable *tchain = NULL; + + RunMark(vm,&tchain); + + SQCollectable *t = _gc_chain; + SQCollectable *nx = NULL; + if(t) { + t->_uiRef++; + while(t) { + t->Finalize(); + nx = t->_next; + if(nx) nx->_uiRef++; + if(--t->_uiRef == 0) + t->Release(); + t = nx; + n++; + } + } + + t = tchain; + while(t) { + t->UnMark(); + t = t->_next; + } + _gc_chain = tchain; + + return n; +} +#endif + +#ifndef NO_GARBAGE_COLLECTOR +void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c) +{ + c->_prev = NULL; + c->_next = *chain; + if(*chain) (*chain)->_prev = c; + *chain = c; +} + +void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c) +{ + if(c->_prev) c->_prev->_next = c->_next; + else *chain = c->_next; + if(c->_next) + c->_next->_prev = c->_prev; + c->_next = NULL; + c->_prev = NULL; +} +#endif + +SQChar* SQSharedState::GetScratchPad(SQInteger size) +{ + SQInteger newsize; + if(size>0) { + if(_scratchpadsize < size) { + newsize = size + (size>>1); + _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); + _scratchpadsize = newsize; + + }else if(_scratchpadsize >= (size<<5)) { + newsize = _scratchpadsize >> 1; + _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); + _scratchpadsize = newsize; + } + } + return _scratchpad; +} + +RefTable::RefTable() +{ + AllocNodes(4); +} + +void RefTable::Finalize() +{ + RefNode *nodes = _nodes; + for(SQUnsignedInteger n = 0; n < _numofslots; n++) { + nodes->obj.Null(); + nodes++; + } +} + +RefTable::~RefTable() +{ + SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode))); +} + +#ifndef NO_GARBAGE_COLLECTOR +void RefTable::Mark(SQCollectable **chain) +{ + RefNode *nodes = (RefNode *)_nodes; + for(SQUnsignedInteger n = 0; n < _numofslots; n++) { + if(sq_type(nodes->obj) != OT_NULL) { + SQSharedState::MarkObject(nodes->obj,chain); + } + nodes++; + } +} +#endif + +void RefTable::AddRef(SQObject &obj) +{ + SQHash mainpos; + RefNode *prev; + RefNode *ref = Get(obj,mainpos,&prev,true); + ref->refs++; +} + +SQUnsignedInteger RefTable::GetRefCount(SQObject &obj) +{ + SQHash mainpos; + RefNode *prev; + RefNode *ref = Get(obj,mainpos,&prev,true); + return ref->refs; +} + + +SQBool RefTable::Release(SQObject &obj) +{ + SQHash mainpos; + RefNode *prev; + RefNode *ref = Get(obj,mainpos,&prev,false); + if(ref) { + if(--ref->refs == 0) { + SQObjectPtr o = ref->obj; + if(prev) { + prev->next = ref->next; + } + else { + _buckets[mainpos] = ref->next; + } + ref->next = _freelist; + _freelist = ref; + _slotused--; + ref->obj.Null(); + //<>test for shrink? + return SQTrue; + } + } + else { + assert(0); + } + return SQFalse; +} + +void RefTable::Resize(SQUnsignedInteger size) +{ + RefNode **oldbucks = _buckets; + RefNode *t = _nodes; + SQUnsignedInteger oldnumofslots = _numofslots; + AllocNodes(size); + //rehash + SQUnsignedInteger nfound = 0; + for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { + if(sq_type(t->obj) != OT_NULL) { + //add back; + assert(t->refs != 0); + RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); + nn->refs = t->refs; + t->obj.Null(); + nfound++; + } + t++; + } + assert(nfound == oldnumofslots); + SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode))); +} + +RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj) +{ + RefNode *t = _buckets[mainpos]; + RefNode *newnode = _freelist; + newnode->obj = obj; + _buckets[mainpos] = newnode; + _freelist = _freelist->next; + newnode->next = t; + assert(newnode->refs == 0); + _slotused++; + return newnode; +} + +RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add) +{ + RefNode *ref; + mainpos = ::HashObj(obj)&(_numofslots-1); + *prev = NULL; + for (ref = _buckets[mainpos]; ref; ) { + if(_rawval(ref->obj) == _rawval(obj) && sq_type(ref->obj) == sq_type(obj)) + break; + *prev = ref; + ref = ref->next; + } + if(ref == NULL && add) { + if(_numofslots == _slotused) { + assert(_freelist == 0); + Resize(_numofslots*2); + mainpos = ::HashObj(obj)&(_numofslots-1); + } + ref = Add(mainpos,obj); + } + return ref; +} + +void RefTable::AllocNodes(SQUnsignedInteger size) +{ + RefNode **bucks; + RefNode *nodes; + bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode))); + nodes = (RefNode *)&bucks[size]; + RefNode *temp = nodes; + SQUnsignedInteger n; + for(n = 0; n < size - 1; n++) { + bucks[n] = NULL; + temp->refs = 0; + new (&temp->obj) SQObjectPtr; + temp->next = temp+1; + temp++; + } + bucks[n] = NULL; + temp->refs = 0; + new (&temp->obj) SQObjectPtr; + temp->next = NULL; + _freelist = nodes; + _nodes = nodes; + _buckets = bucks; + _slotused = 0; + _numofslots = size; +} +////////////////////////////////////////////////////////////////////////// +//SQStringTable +/* +* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) +* http://www.lua.org/copyright.html#4 +* http://www.lua.org/source/4.0.1/src_lstring.c.html +*/ + +SQStringTable::SQStringTable(SQSharedState *ss) +{ + _sharedstate = ss; + AllocNodes(4); + _slotused = 0; +} + +SQStringTable::~SQStringTable() +{ + SQ_FREE(_strings,sizeof(SQString*)*_numofslots); + _strings = NULL; +} + +void SQStringTable::AllocNodes(SQInteger size) +{ + _numofslots = size; + _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots); + memset(_strings,0,sizeof(SQString*)*_numofslots); +} + +SQString *SQStringTable::Add(const SQChar *news,SQInteger len) +{ + if(len<0) + len = (SQInteger)scstrlen(news); + SQHash newhash = ::_hashstr(news,len); + SQHash h = newhash&(_numofslots-1); + SQString *s; + for (s = _strings[h]; s; s = s->_next){ + if(s->_len == len && (!memcmp(news,s->_val,sq_rsl(len)))) + return s; //found + } + + SQString *t = (SQString *)SQ_MALLOC(sq_rsl(len)+sizeof(SQString)); + new (t) SQString; + t->_sharedstate = _sharedstate; + memcpy(t->_val,news,sq_rsl(len)); + t->_val[len] = _SC('\0'); + t->_len = len; + t->_hash = newhash; + t->_next = _strings[h]; + _strings[h] = t; + _slotused++; + if (_slotused > _numofslots) /* too crowded? */ + Resize(_numofslots*2); + return t; +} + +void SQStringTable::Resize(SQInteger size) +{ + SQInteger oldsize=_numofslots; + SQString **oldtable=_strings; + AllocNodes(size); + for (SQInteger i=0; i_next; + SQHash h = p->_hash&(_numofslots-1); + p->_next = _strings[h]; + _strings[h] = p; + p = next; + } + } + SQ_FREE(oldtable,oldsize*sizeof(SQString*)); +} + +void SQStringTable::Remove(SQString *bs) +{ + SQString *s; + SQString *prev=NULL; + SQHash h = bs->_hash&(_numofslots - 1); + + for (s = _strings[h]; s; ){ + if(s == bs){ + if(prev) + prev->_next = s->_next; + else + _strings[h] = s->_next; + _slotused--; + SQInteger slen = s->_len; + s->~SQString(); + SQ_FREE(s,sizeof(SQString) + sq_rsl(slen)); + return; + } + prev = s; + s = s->_next; + } + assert(0);//if this fail something is wrong +} diff --git a/sp/src/vscript/squirrel/squirrel/sqstate.h b/sp/src/vscript/squirrel/squirrel/sqstate.h new file mode 100644 index 0000000000..2cdc8da4fa --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqstate.h @@ -0,0 +1,136 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTATE_H_ +#define _SQSTATE_H_ + +#include "squtils.h" +#include "sqobject.h" +struct SQString; +struct SQTable; +//max number of character for a printed number +#define NUMBER_MAX_CHAR 50 + +struct SQStringTable +{ + SQStringTable(SQSharedState*ss); + ~SQStringTable(); + SQString *Add(const SQChar *,SQInteger len); + void Remove(SQString *); +private: + void Resize(SQInteger size); + void AllocNodes(SQInteger size); + SQString **_strings; + SQUnsignedInteger _numofslots; + SQUnsignedInteger _slotused; + SQSharedState *_sharedstate; +}; + +struct RefTable { + struct RefNode { + SQObjectPtr obj; + SQUnsignedInteger refs; + struct RefNode *next; + }; + RefTable(); + ~RefTable(); + void AddRef(SQObject &obj); + SQBool Release(SQObject &obj); + SQUnsignedInteger GetRefCount(SQObject &obj); +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); +#endif + void Finalize(); +private: + RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add); + RefNode *Add(SQHash mainpos,SQObject &obj); + void Resize(SQUnsignedInteger size); + void AllocNodes(SQUnsignedInteger size); + SQUnsignedInteger _numofslots; + SQUnsignedInteger _slotused; + RefNode *_nodes; + RefNode *_freelist; + RefNode **_buckets; +}; + +#define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len) +#define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr) + +struct SQObjectPtr; + +struct SQSharedState +{ + SQSharedState(); + ~SQSharedState(); + void Init(); +public: + SQChar* GetScratchPad(SQInteger size); + SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); +#ifndef NO_GARBAGE_COLLECTOR + SQInteger CollectGarbage(SQVM *vm); + void RunMark(SQVM *vm,SQCollectable **tchain); + SQInteger ResurrectUnreachable(SQVM *vm); + static void MarkObject(SQObjectPtr &o,SQCollectable **chain); +#endif + SQObjectPtrVec *_metamethods; + SQObjectPtr _metamethodsmap; + SQObjectPtrVec *_systemstrings; + SQObjectPtrVec *_types; + SQStringTable *_stringtable; + RefTable _refs_table; + SQObjectPtr _registry; + SQObjectPtr _consts; + SQObjectPtr _constructoridx; +#ifndef NO_GARBAGE_COLLECTOR + SQCollectable *_gc_chain; +#endif + SQObjectPtr _root_vm; + SQObjectPtr _table_default_delegate; + static const SQRegFunction _table_default_delegate_funcz[]; + SQObjectPtr _array_default_delegate; + static const SQRegFunction _array_default_delegate_funcz[]; + SQObjectPtr _string_default_delegate; + static const SQRegFunction _string_default_delegate_funcz[]; + SQObjectPtr _number_default_delegate; + static const SQRegFunction _number_default_delegate_funcz[]; + SQObjectPtr _generator_default_delegate; + static const SQRegFunction _generator_default_delegate_funcz[]; + SQObjectPtr _closure_default_delegate; + static const SQRegFunction _closure_default_delegate_funcz[]; + SQObjectPtr _thread_default_delegate; + static const SQRegFunction _thread_default_delegate_funcz[]; + SQObjectPtr _class_default_delegate; + static const SQRegFunction _class_default_delegate_funcz[]; + SQObjectPtr _instance_default_delegate; + static const SQRegFunction _instance_default_delegate_funcz[]; + SQObjectPtr _weakref_default_delegate; + static const SQRegFunction _weakref_default_delegate_funcz[]; + + SQCOMPILERERROR _compilererrorhandler; + SQPRINTFUNCTION _printfunc; + SQPRINTFUNCTION _errorfunc; + bool _debuginfo; + bool _notifyallexceptions; + SQUserPointer _foreignptr; + SQRELEASEHOOK _releasehook; +private: + SQChar *_scratchpad; + SQInteger _scratchpadsize; +}; + +#define _sp(s) (_sharedstate->GetScratchPad(s)) +#define _spval (_sharedstate->GetScratchPad(-1)) + +#define _table_ddel _table(_sharedstate->_table_default_delegate) +#define _array_ddel _table(_sharedstate->_array_default_delegate) +#define _string_ddel _table(_sharedstate->_string_default_delegate) +#define _number_ddel _table(_sharedstate->_number_default_delegate) +#define _generator_ddel _table(_sharedstate->_generator_default_delegate) +#define _closure_ddel _table(_sharedstate->_closure_default_delegate) +#define _thread_ddel _table(_sharedstate->_thread_default_delegate) +#define _class_ddel _table(_sharedstate->_class_default_delegate) +#define _instance_ddel _table(_sharedstate->_instance_default_delegate) +#define _weakref_ddel _table(_sharedstate->_weakref_default_delegate) + +bool CompileTypemask(SQIntVec &res,const SQChar *typemask); + + +#endif //_SQSTATE_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqstring.h b/sp/src/vscript/squirrel/squirrel/sqstring.h new file mode 100644 index 0000000000..82f1cdf46e --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqstring.h @@ -0,0 +1,31 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQSTRING_H_ +#define _SQSTRING_H_ + +inline SQHash _hashstr (const SQChar *s, size_t l) +{ + SQHash h = (SQHash)l; /* seed */ + size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */ + for (; l>=step; l-=step) + h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++)); + return h; +} + +struct SQString : public SQRefCounted +{ + SQString(){} + ~SQString(){} +public: + static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 ); + SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); + void Release(); + SQSharedState *_sharedstate; + SQString *_next; //chain for the string table + SQInteger _len; + SQHash _hash; + SQChar _val[1]; +}; + + + +#endif //_SQSTRING_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqtable.cpp b/sp/src/vscript/squirrel/squirrel/sqtable.cpp new file mode 100644 index 0000000000..3a89c459dc --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqtable.cpp @@ -0,0 +1,221 @@ +/* +see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include "sqvm.h" +#include "sqtable.h" +#include "sqfuncproto.h" +#include "sqclosure.h" + +SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize) +{ + SQInteger pow2size=MINPOWER2; + while(nInitialSize>pow2size)pow2size=pow2size<<1; + AllocNodes(pow2size); + _usednodes = 0; + _delegate = NULL; + INIT_CHAIN(); + ADD_TO_CHAIN(&_sharedstate->_gc_chain,this); +} + +void SQTable::Remove(const SQObjectPtr &key) +{ + + _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); + if (n) { + n->val.Null(); + n->key.Null(); + _usednodes--; + Rehash(false); + } +} + +void SQTable::AllocNodes(SQInteger nSize) +{ + _HashNode *nodes=(_HashNode *)SQ_MALLOC(sizeof(_HashNode)*nSize); + for(SQInteger i=0;i= oldsize-oldsize/4) /* using more than 3/4? */ + AllocNodes(oldsize*2); + else if (nelems <= oldsize/4 && /* less than 1/4? */ + oldsize > MINPOWER2) + AllocNodes(oldsize/2); + else if(force) + AllocNodes(oldsize); + else + return; + _usednodes = 0; + for (SQInteger i=0; ikey) != OT_NULL) + NewSlot(old->key,old->val); + } + for(SQInteger k=0;k_nodes; + _HashNode *src = _nodes; + _HashNode *dst = nt->_nodes; + SQInteger n = 0; + for(n = 0; n < _numofnodes; n++) { + dst->key = src->key; + dst->val = src->val; + if(src->next) { + assert(src->next > basesrc); + dst->next = basedst + (src->next - basesrc); + assert(dst != dst->next); + } + dst++; + src++; + } + assert(_firstfree > basesrc); + assert(_firstfree != NULL); + nt->_firstfree = basedst + (_firstfree - basesrc); + nt->_usednodes = _usednodes; +#else + SQInteger ridx=0; + SQObjectPtr key,val; + while((ridx=Next(true,ridx,key,val))!=-1){ + nt->NewSlot(key,val); + } +#endif + nt->SetDelegate(_delegate); + return nt; +} + +bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val) +{ + if(sq_type(key) == OT_NULL) + return false; + _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); + if (n) { + val = _realval(n->val); + return true; + } + return false; +} +bool SQTable::NewSlot(const SQObjectPtr &key,const SQObjectPtr &val) +{ + assert(sq_type(key) != OT_NULL); + SQHash h = HashObj(key) & (_numofnodes - 1); + _HashNode *n = _Get(key, h); + if (n) { + n->val = val; + return false; + } + _HashNode *mp = &_nodes[h]; + n = mp; + + + //key not found I'll insert it + //main pos is not free + + if(sq_type(mp->key) != OT_NULL) { + n = _firstfree; /* get a free place */ + SQHash mph = HashObj(mp->key) & (_numofnodes - 1); + _HashNode *othern; /* main position of colliding node */ + + if (mp > n && (othern = &_nodes[mph]) != mp){ + /* yes; move colliding node into free position */ + while (othern->next != mp){ + assert(othern->next != NULL); + othern = othern->next; /* find previous */ + } + othern->next = n; /* redo the chain with `n' in place of `mp' */ + n->key = mp->key; + n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */ + n->next = mp->next; + mp->key.Null(); + mp->val.Null(); + mp->next = NULL; /* now `mp' is free */ + } + else{ + /* new node will go into free position */ + n->next = mp->next; /* chain new position */ + mp->next = n; + mp = n; + } + } + mp->key = key; + + for (;;) { /* correct `firstfree' */ + if (sq_type(_firstfree->key) == OT_NULL && _firstfree->next == NULL) { + mp->val = val; + _usednodes++; + return true; /* OK; table still has a free place */ + } + else if (_firstfree == _nodes) break; /* cannot decrement from here */ + else (_firstfree)--; + } + Rehash(true); + return NewSlot(key, val); +} + +SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) +{ + SQInteger idx = (SQInteger)TranslateIndex(refpos); + while (idx < _numofnodes) { + if(sq_type(_nodes[idx].key) != OT_NULL) { + //first found + _HashNode &n = _nodes[idx]; + outkey = n.key; + outval = getweakrefs?(SQObject)n.val:_realval(n.val); + //return idx for the next iteration + return ++idx; + } + ++idx; + } + //nothing to iterate anymore + return -1; +} + + +bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val) +{ + _HashNode *n = _Get(key, HashObj(key) & (_numofnodes - 1)); + if (n) { + n->val = val; + return true; + } + return false; +} + +void SQTable::_ClearNodes() +{ + for(SQInteger i = 0;i < _numofnodes; i++) { _HashNode &n = _nodes[i]; n.key.Null(); n.val.Null(); } +} + +void SQTable::Finalize() +{ + _ClearNodes(); + SetDelegate(NULL); +} + +void SQTable::Clear() +{ + _ClearNodes(); + _usednodes = 0; + Rehash(true); +} diff --git a/sp/src/vscript/squirrel/squirrel/sqtable.h b/sp/src/vscript/squirrel/squirrel/sqtable.h new file mode 100644 index 0000000000..59db331796 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqtable.h @@ -0,0 +1,110 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQTABLE_H_ +#define _SQTABLE_H_ +/* +* The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) +* http://www.lua.org/copyright.html#4 +* http://www.lua.org/source/4.0.1/src_ltable.c.html +*/ + +#include "sqstring.h" + + +#define hashptr(p) ((SQHash)(((SQInteger)p) >> 3)) + +inline SQHash HashObj(const SQObjectPtr &key) +{ + switch(sq_type(key)) { + case OT_STRING: return _string(key)->_hash; + case OT_FLOAT: return (SQHash)((SQInteger)_float(key)); + case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key)); + default: return hashptr(key._unVal.pRefCounted); + } +} + +struct SQTable : public SQDelegable +{ +private: + struct _HashNode + { + _HashNode() { next = NULL; } + SQObjectPtr val; + SQObjectPtr key; + _HashNode *next; + }; + _HashNode *_firstfree; + _HashNode *_nodes; + SQInteger _numofnodes; + SQInteger _usednodes; + +/////////////////////////// + void AllocNodes(SQInteger nSize); + void Rehash(bool force); + SQTable(SQSharedState *ss, SQInteger nInitialSize); + void _ClearNodes(); +public: + static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize) + { + SQTable *newtable = (SQTable*)SQ_MALLOC(sizeof(SQTable)); + new (newtable) SQTable(ss, nInitialSize); + newtable->_delegate = NULL; + return newtable; + } + void Finalize(); + SQTable *Clone(); + ~SQTable() + { + SetDelegate(NULL); + REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); + for (SQInteger i = 0; i < _numofnodes; i++) _nodes[i].~_HashNode(); + SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode)); + } +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + SQObjectType GetType() {return OT_TABLE;} +#endif + inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash) + { + _HashNode *n = &_nodes[hash]; + do{ + if(_rawval(n->key) == _rawval(key) && sq_type(n->key) == sq_type(key)){ + return n; + } + }while((n = n->next)); + return NULL; + } + //for compiler use + inline bool GetStr(const SQChar* key,SQInteger keylen,SQObjectPtr &val) + { + SQHash hash = _hashstr(key,keylen); + _HashNode *n = &_nodes[hash & (_numofnodes - 1)]; + _HashNode *res = NULL; + do{ + if(sq_type(n->key) == OT_STRING && (scstrcmp(_stringval(n->key),key) == 0)){ + res = n; + break; + } + }while((n = n->next)); + if (res) { + val = _realval(res->val); + return true; + } + return false; + } + bool Get(const SQObjectPtr &key,SQObjectPtr &val); + void Remove(const SQObjectPtr &key); + bool Set(const SQObjectPtr &key, const SQObjectPtr &val); + //returns true if a new slot has been created false if it was already present + bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val); + SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); + + SQInteger CountUsed(){ return _usednodes;} + void Clear(); + void Release() + { + sq_delete(this, SQTable); + } + +}; + +#endif //_SQTABLE_H_ diff --git a/sp/src/vscript/squirrel/squirrel/squirrel.dsp b/sp/src/vscript/squirrel/squirrel/squirrel.dsp new file mode 100644 index 0000000000..66a84f7dd7 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/squirrel.dsp @@ -0,0 +1,302 @@ +# Microsoft Developer Studio Project File - Name="squirrel" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=squirrel - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "squirrel.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "squirrel.mak" CFG="squirrel - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "squirrel - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "squirrel - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "squirrel - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "GARBAGE_COLLECTOR" /YX /FD /c +# ADD BASE RSC /l 0x410 /d "NDEBUG" +# ADD RSC /l 0x410 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\squirrel.lib" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x410 /d "_DEBUG" +# ADD RSC /l 0x410 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\lib\squirrel.lib" + +!ENDIF + +# Begin Target + +# Name "squirrel - Win32 Release" +# Name "squirrel - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\sqapi.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqbaselib.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqcompiler.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqdebug.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqfuncstate.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqlexer.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqmem.cpp +# End Source File +# Begin Source File + +SOURCE=.\sqobject.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqstate.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqtable.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqclass.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sqvm.cpp + +!IF "$(CFG)" == "squirrel - Win32 Release" + +!ELSEIF "$(CFG)" == "squirrel - Win32 Debug" + +# ADD CPP /YX"stdafx.h" + +!ENDIF + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\sqarray.h +# End Source File +# Begin Source File + +SOURCE=.\sqclosure.h +# End Source File +# Begin Source File + +SOURCE=.\sqcompiler.h +# End Source File +# Begin Source File + +SOURCE=.\sqfuncproto.h +# End Source File +# Begin Source File + +SOURCE=.\sqfuncstate.h +# End Source File +# Begin Source File + +SOURCE=.\sqlexer.h +# End Source File +# Begin Source File + +SOURCE=.\sqobject.h +# End Source File +# Begin Source File + +SOURCE=.\sqopcodes.h +# End Source File +# Begin Source File + +SOURCE=.\sqpcheader.h +# End Source File +# Begin Source File + +SOURCE=.\sqstate.h +# End Source File +# Begin Source File + +SOURCE=.\sqstring.h +# End Source File +# Begin Source File + +SOURCE=.\sqtable.h +# End Source File +# Begin Source File + +SOURCE=.\squserdata.h +# End Source File +# Begin Source File + +SOURCE=.\squtils.h +# End Source File +# Begin Source File + +SOURCE=.\sqclass.h +# End Source File +# Begin Source File + +SOURCE=.\sqvm.h +# End Source File +# End Group +# End Target +# End Project diff --git a/sp/src/vscript/squirrel/squirrel/squserdata.h b/sp/src/vscript/squirrel/squirrel/squserdata.h new file mode 100644 index 0000000000..ec217313be --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/squserdata.h @@ -0,0 +1,40 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQUSERDATA_H_ +#define _SQUSERDATA_H_ + +struct SQUserData : SQDelegable +{ + SQUserData(SQSharedState *ss){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); } + ~SQUserData() + { + REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this); + SetDelegate(NULL); + } + static SQUserData* Create(SQSharedState *ss, SQInteger size) + { + SQUserData* ud = (SQUserData*)SQ_MALLOC(sq_aligning(sizeof(SQUserData))+size); + new (ud) SQUserData(ss); + ud->_size = size; + ud->_typetag = 0; + return ud; + } +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + void Finalize(){SetDelegate(NULL);} + SQObjectType GetType(){ return OT_USERDATA;} +#endif + void Release() { + if (_hook) _hook((SQUserPointer)sq_aligning(this + 1),_size); + SQInteger tsize = _size; + this->~SQUserData(); + SQ_FREE(this, sq_aligning(sizeof(SQUserData)) + tsize); + } + + + SQInteger _size; + SQRELEASEHOOK _hook; + SQUserPointer _typetag; + //SQChar _val[1]; +}; + +#endif //_SQUSERDATA_H_ diff --git a/sp/src/vscript/squirrel/squirrel/squtils.h b/sp/src/vscript/squirrel/squirrel/squtils.h new file mode 100644 index 0000000000..f3e819a58e --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/squtils.h @@ -0,0 +1,116 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQUTILS_H_ +#define _SQUTILS_H_ + +void *sq_vm_malloc(SQUnsignedInteger size); +void *sq_vm_realloc(void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size); +void sq_vm_free(void *p,SQUnsignedInteger size); + +#define sq_new(__ptr,__type) {__ptr=(__type *)sq_vm_malloc(sizeof(__type));new (__ptr) __type;} +#define sq_delete(__ptr,__type) {__ptr->~__type();sq_vm_free(__ptr,sizeof(__type));} +#define SQ_MALLOC(__size) sq_vm_malloc((__size)); +#define SQ_FREE(__ptr,__size) sq_vm_free((__ptr),(__size)); +#define SQ_REALLOC(__ptr,__oldsize,__size) sq_vm_realloc((__ptr),(__oldsize),(__size)); + +#define sq_aligning(v) (((size_t)(v) + (SQ_ALIGNMENT-1)) & (~(SQ_ALIGNMENT-1))) + +//sqvector mini vector class, supports objects by value +template class sqvector +{ +public: + sqvector() + { + _vals = NULL; + _size = 0; + _allocated = 0; + } + sqvector(const sqvector& v) + { + copy(v); + } + void copy(const sqvector& v) + { + if(_size) { + resize(0); //destroys all previous stuff + } + //resize(v._size); + if(v._size > _allocated) { + _realloc(v._size); + } + for(SQUnsignedInteger i = 0; i < v._size; i++) { + new ((void *)&_vals[i]) T(v._vals[i]); + } + _size = v._size; + } + ~sqvector() + { + if(_allocated) { + for(SQUnsignedInteger i = 0; i < _size; i++) + _vals[i].~T(); + SQ_FREE(_vals, (_allocated * sizeof(T))); + } + } + void reserve(SQUnsignedInteger newsize) { _realloc(newsize); } + void resize(SQUnsignedInteger newsize, const T& fill = T()) + { + if(newsize > _allocated) + _realloc(newsize); + if(newsize > _size) { + while(_size < newsize) { + new ((void *)&_vals[_size]) T(fill); + _size++; + } + } + else{ + for(SQUnsignedInteger i = newsize; i < _size; i++) { + _vals[i].~T(); + } + _size = newsize; + } + } + void shrinktofit() { if(_size > 4) { _realloc(_size); } } + T& top() const { return _vals[_size - 1]; } + inline SQUnsignedInteger size() const { return _size; } + bool empty() const { return (_size <= 0); } + inline T &push_back(const T& val = T()) + { + if(_allocated <= _size) + _realloc(_size * 2); + return *(new ((void *)&_vals[_size++]) T(val)); + } + inline void pop_back() + { + _size--; _vals[_size].~T(); + } + void insert(SQUnsignedInteger idx, const T& val) + { + resize(_size + 1); + for(SQUnsignedInteger i = _size - 1; i > idx; i--) { + _vals[i] = _vals[i - 1]; + } + _vals[idx] = val; + } + void remove(SQUnsignedInteger idx) + { + _vals[idx].~T(); + if(idx < (_size - 1)) { + memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - idx - 1)); + } + _size--; + } + SQUnsignedInteger capacity() { return _allocated; } + inline T &back() const { return _vals[_size - 1]; } + inline T& operator[](SQUnsignedInteger pos) const{ return _vals[pos]; } + T* _vals; +private: + void _realloc(SQUnsignedInteger newsize) + { + newsize = (newsize > 0)?newsize:4; + _vals = (T*)SQ_REALLOC(_vals, _allocated * sizeof(T), newsize * sizeof(T)); + _allocated = newsize; + } + SQUnsignedInteger _size; + SQUnsignedInteger _allocated; +}; + +#endif //_SQUTILS_H_ diff --git a/sp/src/vscript/squirrel/squirrel/sqvm.cpp b/sp/src/vscript/squirrel/squirrel/sqvm.cpp new file mode 100644 index 0000000000..dcf823d735 --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqvm.cpp @@ -0,0 +1,1791 @@ +/* + see copyright notice in squirrel.h +*/ +#include "sqpcheader.h" +#include +#include +#include "sqopcodes.h" +#include "sqvm.h" +#include "sqfuncproto.h" +#include "sqclosure.h" +#include "sqstring.h" +#include "sqtable.h" +#include "squserdata.h" +#include "sqarray.h" +#include "sqclass.h" + +#define TOP() (_stack._vals[_top-1]) +#define TARGET _stack._vals[_stackbase+arg0] +#define STK(a) _stack._vals[_stackbase+(a)] + +bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) +{ + SQInteger res; + if((sq_type(o1)| sq_type(o2)) == OT_INTEGER) + { + SQInteger i1 = _integer(o1), i2 = _integer(o2); + switch(op) { + case BW_AND: res = i1 & i2; break; + case BW_OR: res = i1 | i2; break; + case BW_XOR: res = i1 ^ i2; break; + case BW_SHIFTL: res = i1 << i2; break; + case BW_SHIFTR: res = i1 >> i2; break; + case BW_USHIFTR:res = (SQInteger)(*((SQUnsignedInteger*)&i1) >> i2); break; + default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; } + } + } + else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;} + trg = res; + return true; +} + +#define _ARITH_(op,trg,o1,o2) \ +{ \ + SQInteger tmask = sq_type(o1)|sq_type(o2); \ + switch(tmask) { \ + case OT_INTEGER: trg = _integer(o1) op _integer(o2);break; \ + case (OT_FLOAT|OT_INTEGER): \ + case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\ + default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ + } \ +} + +#define _ARITH_NOZERO(op,trg,o1,o2,err) \ +{ \ + SQInteger tmask = sq_type(o1)|sq_type(o2); \ + switch(tmask) { \ + case OT_INTEGER: { SQInteger i2 = _integer(o2); if(i2 == 0) { Raise_Error(err); SQ_THROW(); } trg = _integer(o1) op i2; } break;\ + case (OT_FLOAT|OT_INTEGER): \ + case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\ + default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ + } \ +} + +bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) +{ + SQInteger tmask = sq_type(o1)| sq_type(o2); + switch(tmask) { + case OT_INTEGER:{ + SQInteger res, i1 = _integer(o1), i2 = _integer(o2); + switch(op) { + case '+': res = i1 + i2; break; + case '-': res = i1 - i2; break; + case '/': if (i2 == 0) { Raise_Error(_SC("division by zero")); return false; } + else if (i2 == -1 && i1 == INT_MIN) { Raise_Error(_SC("integer overflow")); return false; } + res = i1 / i2; + break; + case '*': res = i1 * i2; break; + case '%': if (i2 == 0) { Raise_Error(_SC("modulo by zero")); return false; } + else if (i2 == -1 && i1 == INT_MIN) { res = 0; break; } + res = i1 % i2; + break; + default: res = 0xDEADBEEF; + } + trg = res; } + break; + case (OT_FLOAT|OT_INTEGER): + case (OT_FLOAT):{ + SQFloat res, f1 = tofloat(o1), f2 = tofloat(o2); + switch(op) { + case '+': res = f1 + f2; break; + case '-': res = f1 - f2; break; + case '/': res = f1 / f2; break; + case '*': res = f1 * f2; break; + case '%': res = SQFloat(fmod((double)f1,(double)f2)); break; + default: res = 0x0f; + } + trg = res; } + break; + default: + if(op == '+' && (tmask & _RT_STRING)){ + if(!StringCat(o1, o2, trg)) return false; + } + else if(!ArithMetaMethod(op,o1,o2,trg)) { + return false; + } + } + return true; +} + +SQVM::SQVM(SQSharedState *ss) +{ + _sharedstate=ss; + _suspended = SQFalse; + _suspended_target = -1; + _suspended_root = SQFalse; + _suspended_traps = -1; + _foreignptr = NULL; + _nnativecalls = 0; + _nmetamethodscall = 0; + _lasterror.Null(); + _errorhandler.Null(); + _debughook = false; + _debughook_native = NULL; + _debughook_closure.Null(); + _openouters = NULL; + ci = NULL; + _releasehook = NULL; + INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); +} + +void SQVM::Finalize() +{ + if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } + if(_openouters) CloseOuters(&_stack._vals[0]); + _roottable.Null(); + _lasterror.Null(); + _errorhandler.Null(); + _debughook = false; + _debughook_native = NULL; + _debughook_closure.Null(); + temp_reg.Null(); + _callstackdata.resize(0); + SQInteger size=_stack.size(); + for(SQInteger i=0;i_gc_chain,this); +} + +bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest) +{ + SQMetaMethod mm; + switch(op){ + case _SC('+'): mm=MT_ADD; break; + case _SC('-'): mm=MT_SUB; break; + case _SC('/'): mm=MT_DIV; break; + case _SC('*'): mm=MT_MUL; break; + case _SC('%'): mm=MT_MODULO; break; + default: mm = MT_ADD; assert(0); break; //shutup compiler + } + if(is_delegable(o1) && _delegable(o1)->_delegate) { + + SQObjectPtr closure; + if(_delegable(o1)->GetMetaMethod(this, mm, closure)) { + Push(o1);Push(o2); + return CallMetaMethod(closure,mm,2,dest); + } + } + Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); + return false; +} + +bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) +{ + + switch(sq_type(o)) { + case OT_INTEGER: + trg = -_integer(o); + return true; + case OT_FLOAT: + trg = -_float(o); + return true; + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o)->_delegate) { + SQObjectPtr closure; + if(_delegable(o)->GetMetaMethod(this, MT_UNM, closure)) { + Push(o); + if(!CallMetaMethod(closure, MT_UNM, 1, temp_reg)) return false; + _Swap(trg,temp_reg); + return true; + + } + } + default:break; //shutup compiler + } + Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o)); + return false; +} + +#define _RET_SUCCEED(exp) { result = (exp); return true; } +bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) +{ + SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); + if(t1 == t2) { + if(_rawval(o1) == _rawval(o2))_RET_SUCCEED(0); + SQObjectPtr res; + switch(t1){ + case OT_STRING: + _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2))); + case OT_INTEGER: + _RET_SUCCEED((_integer(o1)<_integer(o2))?-1:1); + case OT_FLOAT: + _RET_SUCCEED((_float(o1)<_float(o2))?-1:1); + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o1)->_delegate) { + SQObjectPtr closure; + if(_delegable(o1)->GetMetaMethod(this, MT_CMP, closure)) { + Push(o1);Push(o2); + if(CallMetaMethod(closure,MT_CMP,2,res)) { + if(sq_type(res) != OT_INTEGER) { + Raise_Error(_SC("_cmp must return an integer")); + return false; + } + _RET_SUCCEED(_integer(res)) + } + return false; + } + } + //continues through (no break needed) + default: + _RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 ); + } + assert(0); + //if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; } + // _RET_SUCCEED(_integer(res)); + + } + else{ + if(sq_isnumeric(o1) && sq_isnumeric(o2)){ + if((t1==OT_INTEGER) && (t2==OT_FLOAT)) { + if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); } + else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); } + _RET_SUCCEED(1); + } + else{ + if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); } + else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); } + _RET_SUCCEED(1); + } + } + else if(t1==OT_NULL) {_RET_SUCCEED(-1);} + else if(t2==OT_NULL) {_RET_SUCCEED(1);} + else { Raise_CompareError(o1,o2); return false; } + + } + assert(0); + _RET_SUCCEED(0); //cannot happen +} + +bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res) +{ + SQInteger r; + if(ObjCmp(o1,o2,r)) { + switch(op) { + case CMP_G: res = (r > 0); return true; + case CMP_GE: res = (r >= 0); return true; + case CMP_L: res = (r < 0); return true; + case CMP_LE: res = (r <= 0); return true; + case CMP_3W: res = r; return true; + } + assert(0); + } + return false; +} + +bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) +{ + switch(sq_type(o)) { + case OT_STRING: + res = o; + return true; + case OT_FLOAT: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR),_SC("%g"),_float(o)); + break; + case OT_INTEGER: + scsprintf(_sp(sq_rsl(NUMBER_MAX_CHAR+1)),sq_rsl(NUMBER_MAX_CHAR),_PRINT_INT_FMT,_integer(o)); + break; + case OT_BOOL: + scsprintf(_sp(sq_rsl(6)),sq_rsl(6),_integer(o)?_SC("true"):_SC("false")); + break; + case OT_NULL: + scsprintf(_sp(sq_rsl(5)),sq_rsl(5),_SC("null")); + break; + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o)->_delegate) { + SQObjectPtr closure; + if(_delegable(o)->GetMetaMethod(this, MT_TOSTRING, closure)) { + Push(o); + if(CallMetaMethod(closure,MT_TOSTRING,1,res)) { + if(sq_type(res) == OT_STRING) + return true; + } + else { + return false; + } + } + } + default: + scsprintf(_sp(sq_rsl((sizeof(void*)*2)+NUMBER_MAX_CHAR)),sq_rsl((sizeof(void*)*2)+NUMBER_MAX_CHAR),_SC("(%s : 0x%p)"),GetTypeName(o),(void*)_rawval(o)); + } + res = SQString::Create(_ss(this),_spval); + return true; +} + + +bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest) +{ + SQObjectPtr a, b; + if(!ToString(str, a)) return false; + if(!ToString(obj, b)) return false; + SQInteger l = _string(a)->_len , ol = _string(b)->_len; + SQChar *s = _sp(sq_rsl(l + ol + 1)); + memcpy(s, _stringval(a), sq_rsl(l)); + memcpy(s + l, _stringval(b), sq_rsl(ol)); + dest = SQString::Create(_ss(this), _spval, l + ol); + return true; +} + +bool SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest) +{ + if(is_delegable(obj1) && _delegable(obj1)->_delegate) { + SQObjectPtr closure; + if(_delegable(obj1)->GetMetaMethod(this, MT_TYPEOF, closure)) { + Push(obj1); + return CallMetaMethod(closure,MT_TYPEOF,1,dest); + } + } + dest = SQString::Create(_ss(this),GetTypeName(obj1)); + return true; +} + +bool SQVM::Init(SQVM *friendvm, SQInteger stacksize) +{ + _stack.resize(stacksize); + _alloccallsstacksize = 4; + _callstackdata.resize(_alloccallsstacksize); + _callsstacksize = 0; + _callsstack = &_callstackdata[0]; + _stackbase = 0; + _top = 0; + if(!friendvm) { + _roottable = SQTable::Create(_ss(this), 0); + sq_base_register(this); + } + else { + _roottable = friendvm->_roottable; + _errorhandler = friendvm->_errorhandler; + _debughook = friendvm->_debughook; + _debughook_native = friendvm->_debughook_native; + _debughook_closure = friendvm->_debughook_closure; + } + + + return true; +} + + +bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQInteger stackbase,bool tailcall) +{ + SQFunctionProto *func = closure->_function; + + SQInteger paramssize = func->_nparameters; + const SQInteger newtop = stackbase + func->_stacksize; + SQInteger nargs = args; + if(func->_varparams) + { + paramssize--; + if (nargs < paramssize) { + Raise_Error(_SC("wrong number of parameters (%d passed, at least %d required)"), + (int)nargs, (int)paramssize); + return false; + } + + //dumpstack(stackbase); + SQInteger nvargs = nargs - paramssize; + SQArray *arr = SQArray::Create(_ss(this),nvargs); + SQInteger pbase = stackbase+paramssize; + for(SQInteger n = 0; n < nvargs; n++) { + arr->_values[n] = _stack._vals[pbase]; + _stack._vals[pbase].Null(); + pbase++; + + } + _stack._vals[stackbase+paramssize] = arr; + //dumpstack(stackbase); + } + else if (paramssize != nargs) { + SQInteger ndef = func->_ndefaultparams; + SQInteger diff; + if(ndef && nargs < paramssize && (diff = paramssize - nargs) <= ndef) { + for(SQInteger n = ndef - diff; n < ndef; n++) { + _stack._vals[stackbase + (nargs++)] = closure->_defaultparams[n]; + } + } + else { + Raise_Error(_SC("wrong number of parameters (%d passed, %d required)"), + (int)nargs, (int)paramssize); + return false; + } + } + + if(closure->_env) { + _stack._vals[stackbase] = closure->_env->_obj; + } + + if(!EnterFrame(stackbase, newtop, tailcall)) return false; + + ci->_closure = closure; + ci->_literals = func->_literals; + ci->_ip = func->_instructions; + ci->_target = (SQInt32)target; + + if (_debughook) { + CallDebugHook(_SC('c')); + } + + if (closure->_function->_bgenerator) { + SQFunctionProto *f = closure->_function; + SQGenerator *gen = SQGenerator::Create(_ss(this), closure); + if(!gen->Yield(this,f->_stacksize)) + return false; + SQObjectPtr temp; + Return(1, target, temp); + STK(target) = gen; + } + + + return true; +} + +bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval) +{ + SQBool _isroot = ci->_root; + SQInteger callerbase = _stackbase - ci->_prevstkbase; + + if (_debughook) { + for(SQInteger i=0; i_ncalls; i++) { + CallDebugHook(_SC('r')); + } + } + + SQObjectPtr *dest; + if (_isroot) { + dest = &(retval); + } else if (ci->_target == -1) { + dest = NULL; + } else { + dest = &_stack._vals[callerbase + ci->_target]; + } + if (dest) { + if(_arg0 != 0xFF) { + *dest = _stack._vals[_stackbase+_arg1]; + } + else { + dest->Null(); + } + //*dest = (_arg0 != 0xFF) ? _stack._vals[_stackbase+_arg1] : _null_; + } + LeaveFrame(); + return _isroot ? true : false; +} + +#define _RET_ON_FAIL(exp) { if(!exp) return false; } + +bool SQVM::PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr) +{ + SQObjectPtr trg; + _RET_ON_FAIL(ARITH_OP( op , trg, a, incr)); + target = a; + a = trg; + return true; +} + +bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix,SQInteger selfidx) +{ + SQObjectPtr tmp, tself = self, tkey = key; + if (!Get(tself, tkey, tmp, 0, selfidx)) { return false; } + _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr)) + if (!Set(tself, tkey, target,selfidx)) { return false; } + if (postfix) target = tmp; + return true; +} + +#define arg0 (_i_._arg0) +#define sarg0 ((SQInteger)*((const signed char *)&_i_._arg0)) +#define arg1 (_i_._arg1) +#define sarg1 (*((const SQInt32 *)&_i_._arg1)) +#define arg2 (_i_._arg2) +#define arg3 (_i_._arg3) +#define sarg3 ((SQInteger)*((const signed char *)&_i_._arg3)) + +SQRESULT SQVM::Suspend() +{ + if (_suspended) + return sq_throwerror(this, _SC("cannot suspend an already suspended vm")); + if (_nnativecalls!=2) + return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods")); + return SQ_SUSPEND_FLAG; +} + + +#define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; } +bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr +&o3,SQObjectPtr &o4,SQInteger SQ_UNUSED_ARG(arg_2),int exitpos,int &jump) +{ + SQInteger nrefidx; + switch(sq_type(o1)) { + case OT_TABLE: + if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos); + o4 = (SQInteger)nrefidx; _FINISH(1); + case OT_ARRAY: + if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(exitpos); + o4 = (SQInteger) nrefidx; _FINISH(1); + case OT_STRING: + if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); + o4 = (SQInteger)nrefidx; _FINISH(1); + case OT_CLASS: + if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); + o4 = (SQInteger)nrefidx; _FINISH(1); + case OT_USERDATA: + case OT_INSTANCE: + if(_delegable(o1)->_delegate) { + SQObjectPtr itr; + SQObjectPtr closure; + if(_delegable(o1)->GetMetaMethod(this, MT_NEXTI, closure)) { + Push(o1); + Push(o4); + if(CallMetaMethod(closure, MT_NEXTI, 2, itr)) { + o4 = o2 = itr; + if(sq_type(itr) == OT_NULL) _FINISH(exitpos); + if(!Get(o1, itr, o3, 0, DONT_FALL_BACK)) { + Raise_Error(_SC("_nexti returned an invalid idx")); // cloud be changed + return false; + } + _FINISH(1); + } + else { + return false; + } + } + Raise_Error(_SC("_nexti failed")); + return false; + } + break; + case OT_GENERATOR: + if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos); + if(_generator(o1)->_state == SQGenerator::eSuspended) { + SQInteger idx = 0; + if(sq_type(o4) == OT_INTEGER) { + idx = _integer(o4) + 1; + } + o2 = idx; + o4 = idx; + _generator(o1)->Resume(this, o3); + _FINISH(0); + } + default: + Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1)); + } + return false; //cannot be hit(just to avoid warnings) +} + +#define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1)) + +#define SQ_THROW() { goto exception_trap; } + +#define _GUARD(exp) { if(!exp) { SQ_THROW();} } + +bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func) +{ + SQInteger nouters; + SQClosure *closure = SQClosure::Create(_ss(this), func,_table(_roottable)->GetWeakRef(OT_TABLE)); + if((nouters = func->_noutervalues)) { + for(SQInteger i = 0; i_outervalues[i]; + switch(v._type){ + case otLOCAL: + FindOuter(closure->_outervalues[i], &STK(_integer(v._src))); + break; + case otOUTER: + closure->_outervalues[i] = _closure(ci->_closure)->_outervalues[_integer(v._src)]; + break; + } + } + } + SQInteger ndefparams; + if((ndefparams = func->_ndefaultparams)) { + for(SQInteger i = 0; i < ndefparams; i++) { + SQInteger spos = func->_defaultparams[i]; + closure->_defaultparams[i] = _stack._vals[_stackbase + spos]; + } + } + target = closure; + return true; + +} + + +bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass,SQInteger attributes) +{ + SQClass *base = NULL; + SQObjectPtr attrs; + if(baseclass != -1) { + if(sq_type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; } + base = _class(_stack._vals[_stackbase + baseclass]); + } + if(attributes != MAX_FUNC_STACKSIZE) { + attrs = _stack._vals[_stackbase+attributes]; + } + target = SQClass::Create(_ss(this),base); + if(sq_type(_class(target)->_metamethods[MT_INHERITED]) != OT_NULL) { + int nparams = 2; + SQObjectPtr ret; + Push(target); Push(attrs); + if(!Call(_class(target)->_metamethods[MT_INHERITED],nparams,_top - nparams, ret, false)) { + Pop(nparams); + return false; + } + Pop(nparams); + } + _class(target)->_attributes = attrs; + return true; +} + +bool SQVM::IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res) +{ + SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); + if(t1 == t2) { + if (t1 == OT_FLOAT) { + res = (_float(o1) == _float(o2)); + } + else { + res = (_rawval(o1) == _rawval(o2)); + } + } + else { + if(sq_isnumeric(o1) && sq_isnumeric(o2)) { + res = (tofloat(o1) == tofloat(o2)); + } + else { + res = false; + } + } + return true; +} + +bool SQVM::IsFalse(SQObjectPtr &o) +{ + if(((sq_type(o) & SQOBJECT_CANBEFALSE) + && ( ((sq_type(o) == OT_FLOAT) && (_float(o) == SQFloat(0.0))) )) +#if !defined(SQUSEDOUBLE) || (defined(SQUSEDOUBLE) && defined(_SQ64)) + || (_integer(o) == 0) ) //OT_NULL|OT_INTEGER|OT_BOOL +#else + || (((sq_type(o) != OT_FLOAT) && (_integer(o) == 0))) ) //OT_NULL|OT_INTEGER|OT_BOOL +#endif + { + return true; + } + return false; +} +extern SQInstructionDesc g_InstrDesc[]; +bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et) +{ + if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; } + _nnativecalls++; + AutoDec ad(&_nnativecalls); + SQInteger traps = 0; + CallInfo *prevci = ci; + + switch(et) { + case ET_CALL: { + temp_reg = closure; + if(!StartCall(_closure(temp_reg), _top - nargs, nargs, stackbase, false)) { + //call the handler if there are no calls in the stack, if not relies on the previous node + if(ci == NULL) CallErrorHandler(_lasterror); + return false; + } + if(ci == prevci) { + outres = STK(_top-nargs); + return true; + } + ci->_root = SQTrue; + } + break; + case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, outres); ci->_root = SQTrue; traps += ci->_etraps; break; + case ET_RESUME_VM: + case ET_RESUME_THROW_VM: + traps = _suspended_traps; + ci->_root = _suspended_root; + _suspended = SQFalse; + if(et == ET_RESUME_THROW_VM) { SQ_THROW(); } + break; + } + +exception_restore: + // + { + for(;;) + { + const SQInstruction &_i_ = *ci->_ip++; + //dumpstack(_stackbase); + //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); + switch(_i_.op) + { + case _OP_LINE: if (_debughook) CallDebugHook(_SC('l'),arg1); continue; + case _OP_LOAD: TARGET = ci->_literals[arg1]; continue; + case _OP_LOADINT: +#ifndef _SQ64 + TARGET = (SQInteger)arg1; continue; +#else + TARGET = (SQInteger)((SQInt32)arg1); continue; +#endif + case _OP_LOADFLOAT: TARGET = *((const SQFloat *)&arg1); continue; + case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; + case _OP_TAILCALL:{ + SQObjectPtr &t = STK(arg1); + if (sq_type(t) == OT_CLOSURE + && (!_closure(t)->_function->_bgenerator)){ + SQObjectPtr clo = t; + SQInteger last_top = _top; + if(_openouters) CloseOuters(&(_stack._vals[_stackbase])); + for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i); + _GUARD(StartCall(_closure(clo), ci->_target, arg3, _stackbase, true)); + if (last_top >= _top) { + _top = last_top; + } + continue; + } + } + case _OP_CALL: { + SQObjectPtr clo = STK(arg1); + switch (sq_type(clo)) { + case OT_CLOSURE: + _GUARD(StartCall(_closure(clo), sarg0, arg3, _stackbase+arg2, false)); + continue; + case OT_NATIVECLOSURE: { + bool suspend; + bool tailcall; + _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo, (SQInt32)sarg0, suspend, tailcall)); + if(suspend){ + _suspended = SQTrue; + _suspended_target = sarg0; + _suspended_root = ci->_root; + _suspended_traps = traps; + outres = clo; + return true; + } + if(sarg0 != -1 && !tailcall) { + STK(arg0) = clo; + } + } + continue; + case OT_CLASS:{ + SQObjectPtr inst; + _GUARD(CreateClassInstance(_class(clo),inst,clo)); + if(sarg0 != -1) { + STK(arg0) = inst; + } + SQInteger stkbase; + switch(sq_type(clo)) { + case OT_CLOSURE: + stkbase = _stackbase+arg2; + _stack._vals[stkbase] = inst; + _GUARD(StartCall(_closure(clo), -1, arg3, stkbase, false)); + break; + case OT_NATIVECLOSURE: + bool dummy; + stkbase = _stackbase+arg2; + _stack._vals[stkbase] = inst; + _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo, -1, dummy, dummy)); + break; + default: break; //shutup GCC 4.x + } + } + break; + case OT_TABLE: + case OT_USERDATA: + case OT_INSTANCE:{ + SQObjectPtr closure; + if(_delegable(clo)->_delegate && _delegable(clo)->GetMetaMethod(this,MT_CALL,closure)) { + Push(clo); + for (SQInteger i = 0; i < arg3; i++) Push(STK(arg2 + i)); + if(!CallMetaMethod(closure, MT_CALL, arg3+1, clo)) SQ_THROW(); + if(sarg0 != -1) { + STK(arg0) = clo; + } + break; + } + + //Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo)); + //SQ_THROW(); + } + default: + Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo)); + SQ_THROW(); + } + } + continue; + case _OP_PREPCALL: + case _OP_PREPCALLK: { + SQObjectPtr &key = _i_.op == _OP_PREPCALLK?(ci->_literals)[arg1]:STK(arg1); + SQObjectPtr &o = STK(arg2); + if (!Get(o, key, temp_reg,0,arg2)) { + SQ_THROW(); + } + STK(arg3) = o; + _Swap(TARGET,temp_reg);//TARGET = temp_reg; + } + continue; + case _OP_GETK: + if (!Get(STK(arg2), ci->_literals[arg1], temp_reg, 0,arg2)) { SQ_THROW();} + _Swap(TARGET,temp_reg);//TARGET = temp_reg; + continue; + case _OP_MOVE: TARGET = STK(arg1); continue; + case _OP_NEWSLOT: + _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3),false)); + if(arg0 != 0xFF) TARGET = STK(arg3); + continue; + case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue; + case _OP_SET: + if (!Set(STK(arg1), STK(arg2), STK(arg3),arg1)) { SQ_THROW(); } + if (arg0 != 0xFF) TARGET = STK(arg3); + continue; + case _OP_GET: + if (!Get(STK(arg1), STK(arg2), temp_reg, 0,arg1)) { SQ_THROW(); } + _Swap(TARGET,temp_reg);//TARGET = temp_reg; + continue; + case _OP_EQ:{ + bool res; + if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); } + TARGET = res?true:false; + }continue; + case _OP_NE:{ + bool res; + if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); } + TARGET = (!res)?true:false; + } continue; + case _OP_ADD: _ARITH_(+,TARGET,STK(arg2),STK(arg1)); continue; + case _OP_SUB: _ARITH_(-,TARGET,STK(arg2),STK(arg1)); continue; + case _OP_MUL: _ARITH_(*,TARGET,STK(arg2),STK(arg1)); continue; + case _OP_DIV: _ARITH_NOZERO(/,TARGET,STK(arg2),STK(arg1),_SC("division by zero")); continue; + case _OP_MOD: ARITH_OP('%',TARGET,STK(arg2),STK(arg1)); continue; + case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue; + case _OP_RETURN: + if((ci)->_generator) { + (ci)->_generator->Kill(); + } + if(Return(arg0, arg1, temp_reg)){ + assert(traps==0); + //outres = temp_reg; + _Swap(outres,temp_reg); + return true; + } + continue; + case _OP_LOADNULLS:{ for(SQInt32 n=0; n < arg1; n++) STK(arg0+n).Null(); }continue; + case _OP_LOADROOT: { + SQWeakRef *w = _closure(ci->_closure)->_root; + if(sq_type(w->_obj) != OT_NULL) { + TARGET = w->_obj; + } else { + TARGET = _roottable; //shoud this be like this? or null + } + } + continue; + case _OP_LOADBOOL: TARGET = arg1?true:false; continue; + case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue; + case _OP_JMP: ci->_ip += (sarg1); continue; + //case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue; + case _OP_JCMP: + _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg0),temp_reg)); + if(IsFalse(temp_reg)) ci->_ip+=(sarg1); + continue; + case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue; + case _OP_GETOUTER: { + SQClosure *cur_cls = _closure(ci->_closure); + SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); + TARGET = *(otr->_valptr); + } + continue; + case _OP_SETOUTER: { + SQClosure *cur_cls = _closure(ci->_closure); + SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); + *(otr->_valptr) = STK(arg2); + if(arg0 != 0xFF) { + TARGET = STK(arg2); + } + } + continue; + case _OP_NEWOBJ: + switch(arg3) { + case NOT_TABLE: TARGET = SQTable::Create(_ss(this), arg1); continue; + case NOT_ARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue; + case NOT_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue; + default: assert(0); continue; + } + case _OP_APPENDARRAY: + { + SQObject val; + val._unVal.raw = 0; + switch(arg2) { + case AAT_STACK: + val = STK(arg1); break; + case AAT_LITERAL: + val = ci->_literals[arg1]; break; + case AAT_INT: + val._type = OT_INTEGER; +#ifndef _SQ64 + val._unVal.nInteger = (SQInteger)arg1; +#else + val._unVal.nInteger = (SQInteger)((SQInt32)arg1); +#endif + break; + case AAT_FLOAT: + val._type = OT_FLOAT; + val._unVal.fFloat = *((const SQFloat *)&arg1); + break; + case AAT_BOOL: + val._type = OT_BOOL; + val._unVal.nInteger = arg1; + break; + default: val._type = OT_INTEGER; assert(0); break; + + } + _array(STK(arg0))->Append(val); continue; + } + case _OP_COMPARITH: { + SQInteger selfidx = (((SQUnsignedInteger)arg1&0xFFFF0000)>>16); + _GUARD(DerefInc(arg3, TARGET, STK(selfidx), STK(arg2), STK(arg1&0x0000FFFF), false, selfidx)); + } + continue; + case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false, arg1));} continue; + case _OP_INCL: { + SQObjectPtr &a = STK(arg1); + if(sq_type(a) == OT_INTEGER) { + a._unVal.nInteger = _integer(a) + sarg3; + } + else { + SQObjectPtr o(sarg3); //_GUARD(LOCAL_INC('+',TARGET, STK(arg1), o)); + _ARITH_(+,a,a,o); + } + } continue; + case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true, arg1));} continue; + case _OP_PINCL: { + SQObjectPtr &a = STK(arg1); + if(sq_type(a) == OT_INTEGER) { + TARGET = a; + a._unVal.nInteger = _integer(a) + sarg3; + } + else { + SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o)); + } + + } continue; + case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue; + case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_RAW, DONT_FALL_BACK) ? true : false; continue; + case _OP_INSTANCEOF: + if(sq_type(STK(arg1)) != OT_CLASS) + {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();} + TARGET = (sq_type(STK(arg2)) == OT_INSTANCE) ? (_instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?true:false) : false; + continue; + case _OP_AND: + if(IsFalse(STK(arg2))) { + TARGET = STK(arg2); + ci->_ip += (sarg1); + } + continue; + case _OP_OR: + if(!IsFalse(STK(arg2))) { + TARGET = STK(arg2); + ci->_ip += (sarg1); + } + continue; + case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue; + case _OP_NOT: TARGET = IsFalse(STK(arg1)); continue; + case _OP_BWNOT: + if(sq_type(STK(arg1)) == OT_INTEGER) { + SQInteger t = _integer(STK(arg1)); + TARGET = SQInteger(~t); + continue; + } + Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1))); + SQ_THROW(); + case _OP_CLOSURE: { + SQClosure *c = ci->_closure._unVal.pClosure; + SQFunctionProto *fp = c->_function; + if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); } + continue; + } + case _OP_YIELD:{ + if(ci->_generator) { + if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1); + if (_openouters) CloseOuters(&_stack._vals[_stackbase]); + _GUARD(ci->_generator->Yield(this,arg2)); + traps -= ci->_etraps; + if(sarg1 != MAX_FUNC_STACKSIZE) _Swap(STK(arg1),temp_reg);//STK(arg1) = temp_reg; + } + else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();} + if(Return(arg0, arg1, temp_reg)){ + assert(traps == 0); + outres = temp_reg; + return true; + } + + } + continue; + case _OP_RESUME: + if(sq_type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();} + _GUARD(_generator(STK(arg1))->Resume(this, TARGET)); + traps += ci->_etraps; + continue; + case _OP_FOREACH:{ int tojump; + _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,sarg1,tojump)); + ci->_ip += tojump; } + continue; + case _OP_POSTFOREACH: + assert(sq_type(STK(arg0)) == OT_GENERATOR); + if(_generator(STK(arg0))->_state == SQGenerator::eDead) + ci->_ip += (sarg1 - 1); + continue; + case _OP_CLONE: _GUARD(Clone(STK(arg1), TARGET)); continue; + case _OP_TYPEOF: _GUARD(TypeOf(STK(arg1), TARGET)) continue; + case _OP_PUSHTRAP:{ + SQInstruction *_iv = _closure(ci->_closure)->_function->_instructions; + _etraps.push_back(SQExceptionTrap(_top,_stackbase, &_iv[(ci->_ip-_iv)+arg1], arg0)); traps++; + ci->_etraps++; + } + continue; + case _OP_POPTRAP: { + for(SQInteger i = 0; i < arg0; i++) { + _etraps.pop_back(); traps--; + ci->_etraps--; + } + } + continue; + case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue; + case _OP_NEWSLOTA: + _GUARD(NewSlotA(STK(arg1),STK(arg2),STK(arg3),(arg0&NEW_SLOT_ATTRIBUTES_FLAG) ? STK(arg2-1) : SQObjectPtr(),(arg0&NEW_SLOT_STATIC_FLAG)?true:false,false)); + continue; + case _OP_GETBASE:{ + SQClosure *clo = _closure(ci->_closure); + if(clo->_base) { + TARGET = clo->_base; + } + else { + TARGET.Null(); + } + continue; + } + case _OP_CLOSE: + if(_openouters) CloseOuters(&(STK(arg1))); + continue; + } + + } + } +exception_trap: + { + SQObjectPtr currerror = _lasterror; +// dumpstack(_stackbase); +// SQInteger n = 0; + SQInteger last_top = _top; + + if(_ss(this)->_notifyallexceptions || (!traps && raiseerror)) CallErrorHandler(currerror); + + while( ci ) { + if(ci->_etraps > 0) { + SQExceptionTrap &et = _etraps.top(); + ci->_ip = et._ip; + _top = et._stacksize; + _stackbase = et._stackbase; + _stack._vals[_stackbase + et._extarget] = currerror; + _etraps.pop_back(); traps--; ci->_etraps--; + while(last_top >= _top) _stack._vals[last_top--].Null(); + goto exception_restore; + } + else if (_debughook) { + //notify debugger of a "return" + //even if it really an exception unwinding the stack + for(SQInteger i = 0; i < ci->_ncalls; i++) { + CallDebugHook(_SC('r')); + } + } + if(ci->_generator) ci->_generator->Kill(); + bool mustbreak = ci && ci->_root; + LeaveFrame(); + if(mustbreak) break; + } + + _lasterror = currerror; + return false; + } + assert(0); +} + +bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor) +{ + inst = theclass->CreateInstance(); + if(!theclass->GetConstructor(constructor)) { + constructor.Null(); + } + return true; +} + +void SQVM::CallErrorHandler(SQObjectPtr &error) +{ + if(sq_type(_errorhandler) != OT_NULL) { + SQObjectPtr out; + Push(_roottable); Push(error); + Call(_errorhandler, 2, _top-2, out,SQFalse); + Pop(2); + } +} + + +void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) +{ + _debughook = false; + SQFunctionProto *func=_closure(ci->_closure)->_function; + if(_debughook_native) { + const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; + const SQChar *fname = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; + SQInteger line = forcedline?forcedline:func->GetLine(ci->_ip); + _debughook_native(this,type,src,line,fname); + } + else { + SQObjectPtr temp_reg; + SQInteger nparams=5; + Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name); + Call(_debughook_closure,nparams,_top-nparams,temp_reg,SQFalse); + Pop(nparams); + } + _debughook = true; +} + +bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target,bool &suspend, bool &tailcall) +{ + SQInteger nparamscheck = nclosure->_nparamscheck; + SQInteger newtop = newbase + nargs + nclosure->_noutervalues; + + if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { + Raise_Error(_SC("Native stack overflow")); + return false; + } + + if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || + ((nparamscheck < 0) && (nargs < (-nparamscheck))))) + { + Raise_Error(_SC("wrong number of parameters")); + return false; + } + + SQInteger tcs; + SQIntVec &tc = nclosure->_typecheck; + if((tcs = tc.size())) { + for(SQInteger i = 0; i < nargs && i < tcs; i++) { + if((tc._vals[i] != -1) && !(sq_type(_stack._vals[newbase+i]) & tc._vals[i])) { + Raise_ParamTypeError(i,tc._vals[i], sq_type(_stack._vals[newbase+i])); + return false; + } + } + } + + if(!EnterFrame(newbase, newtop, false)) return false; + ci->_closure = nclosure; + ci->_target = target; + + SQInteger outers = nclosure->_noutervalues; + for (SQInteger i = 0; i < outers; i++) { + _stack._vals[newbase+nargs+i] = nclosure->_outervalues[i]; + } + if(nclosure->_env) { + _stack._vals[newbase] = nclosure->_env->_obj; + } + + _nnativecalls++; + SQInteger ret = (nclosure->_function)(this); + _nnativecalls--; + + suspend = false; + tailcall = false; + if (ret == SQ_TAILCALL_FLAG) { + tailcall = true; + return true; + } + else if (ret == SQ_SUSPEND_FLAG) { + suspend = true; + } + else if (ret < 0) { + LeaveFrame(); + Raise_Error(_lasterror); + return false; + } + if(ret) { + retval = _stack._vals[_top-1]; + } + else { + retval.Null(); + } + //retval = ret ? _stack._vals[_top-1] : _null_; + LeaveFrame(); + return true; +} + +bool SQVM::TailCall(SQClosure *closure, SQInteger parambase,SQInteger nparams) +{ + SQInteger last_top = _top; + SQObjectPtr clo = closure; + if (ci->_root) + { + Raise_Error("root calls cannot invoke tailcalls"); + return false; + } + for (SQInteger i = 0; i < nparams; i++) STK(i) = STK(parambase + i); + bool ret = StartCall(closure, ci->_target, nparams, _stackbase, true); + if (last_top >= _top) { + _top = last_top; + } + return ret; +} + +#define FALLBACK_OK 0 +#define FALLBACK_NO_MATCH 1 +#define FALLBACK_ERROR 2 + +bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx) +{ + switch(sq_type(self)){ + case OT_TABLE: + if(_table(self)->Get(key,dest))return true; + break; + case OT_ARRAY: + if (sq_isnumeric(key)) { if (_array(self)->Get(tointeger(key), dest)) { return true; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return false; } + break; + case OT_INSTANCE: + if(_instance(self)->Get(key,dest)) return true; + break; + case OT_CLASS: + if(_class(self)->Get(key,dest)) return true; + break; + case OT_STRING: + if(sq_isnumeric(key)){ + SQInteger n = tointeger(key); + SQInteger len = _string(self)->_len; + if (n < 0) { n += len; } + if (n >= 0 && n < len) { + dest = SQInteger(_stringval(self)[n]); + return true; + } + if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); + return false; + } + break; + default:break; //shut up compiler + } + if ((getflags & GET_FLAG_RAW) == 0) { + switch(FallBackGet(self,key,dest)) { + case FALLBACK_OK: return true; //okie + case FALLBACK_NO_MATCH: break; //keep falling back + case FALLBACK_ERROR: return false; // the metamethod failed + } + if(InvokeDefaultDelegate(self,key,dest)) { + return true; + } + } +//#ifdef ROOT_FALLBACK + if(selfidx == 0) { + SQWeakRef *w = _closure(ci->_closure)->_root; + if(sq_type(w->_obj) != OT_NULL) + { + if(Get(*((const SQObjectPtr *)&w->_obj),key,dest,0,DONT_FALL_BACK)) return true; + } + + } +//#endif + if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); + return false; +} + +bool SQVM::InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) +{ + SQTable *ddel = NULL; + switch(sq_type(self)) { + case OT_CLASS: ddel = _class_ddel; break; + case OT_TABLE: ddel = _table_ddel; break; + case OT_ARRAY: ddel = _array_ddel; break; + case OT_STRING: ddel = _string_ddel; break; + case OT_INSTANCE: ddel = _instance_ddel; break; + case OT_INTEGER:case OT_FLOAT:case OT_BOOL: ddel = _number_ddel; break; + case OT_GENERATOR: ddel = _generator_ddel; break; + case OT_CLOSURE: case OT_NATIVECLOSURE: ddel = _closure_ddel; break; + case OT_THREAD: ddel = _thread_ddel; break; + case OT_WEAKREF: ddel = _weakref_ddel; break; + default: return false; + } + return ddel->Get(key,dest); +} + + +SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) +{ + switch(sq_type(self)){ + case OT_TABLE: + case OT_USERDATA: + //delegation + if(_delegable(self)->_delegate) { + if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,0,DONT_FALL_BACK)) return FALLBACK_OK; + } + else { + return FALLBACK_NO_MATCH; + } + //go through + case OT_INSTANCE: { + SQObjectPtr closure; + if(_delegable(self)->GetMetaMethod(this, MT_GET, closure)) { + Push(self);Push(key); + _nmetamethodscall++; + AutoDec ad(&_nmetamethodscall); + if(Call(closure, 2, _top - 2, dest, SQFalse)) { + Pop(2); + return FALLBACK_OK; + } + else { + Pop(2); + if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) + return FALLBACK_ERROR; + } + } + } + } + break; + default: break;//shutup GCC 4.x + } + // no metamethod or no fallback type + return FALLBACK_NO_MATCH; +} + +bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,SQInteger selfidx) +{ + switch(sq_type(self)){ + case OT_TABLE: + if(_table(self)->Set(key,val)) return true; + break; + case OT_INSTANCE: + if(_instance(self)->Set(key,val)) return true; + break; + case OT_ARRAY: + if(!sq_isnumeric(key)) { Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; } + if(!_array(self)->Set(tointeger(key),val)) { + Raise_IdxError(key); + return false; + } + return true; + case OT_USERDATA: break; // must fall back + default: + Raise_Error(_SC("trying to set '%s'"),GetTypeName(self)); + return false; + } + + switch(FallBackSet(self,key,val)) { + case FALLBACK_OK: return true; //okie + case FALLBACK_NO_MATCH: break; //keep falling back + case FALLBACK_ERROR: return false; // the metamethod failed + } + if(selfidx == 0) { + if(_table(_roottable)->Set(key,val)) + return true; + } + Raise_IdxError(key); + return false; +} + +SQInteger SQVM::FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val) +{ + switch(sq_type(self)) { + case OT_TABLE: + if(_table(self)->_delegate) { + if(Set(_table(self)->_delegate,key,val,DONT_FALL_BACK)) return FALLBACK_OK; + } + //keps on going + case OT_INSTANCE: + case OT_USERDATA:{ + SQObjectPtr closure; + SQObjectPtr t; + if(_delegable(self)->GetMetaMethod(this, MT_SET, closure)) { + Push(self);Push(key);Push(val); + _nmetamethodscall++; + AutoDec ad(&_nmetamethodscall); + if(Call(closure, 3, _top - 3, t, SQFalse)) { + Pop(3); + return FALLBACK_OK; + } + else { + Pop(3); + if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) + return FALLBACK_ERROR; + } + } + } + } + break; + default: break;//shutup GCC 4.x + } + // no metamethod or no fallback type + return FALLBACK_NO_MATCH; +} + +bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target) +{ + SQObjectPtr temp_reg; + SQObjectPtr newobj; + switch(sq_type(self)){ + case OT_TABLE: + newobj = _table(self)->Clone(); + goto cloned_mt; + case OT_INSTANCE: { + newobj = _instance(self)->Clone(_ss(this)); +cloned_mt: + SQObjectPtr closure; + if(_delegable(newobj)->_delegate && _delegable(newobj)->GetMetaMethod(this,MT_CLONED,closure)) { + Push(newobj); + Push(self); + if(!CallMetaMethod(closure,MT_CLONED,2,temp_reg)) + return false; + } + } + target = newobj; + return true; + case OT_ARRAY: + target = _array(self)->Clone(); + return true; + default: + Raise_Error(_SC("cloning a %s"), GetTypeName(self)); + return false; + } +} + +bool SQVM::NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw) +{ + if(sq_type(self) != OT_CLASS) { + Raise_Error(_SC("object must be a class")); + return false; + } + SQClass *c = _class(self); + if(!raw) { + SQObjectPtr &mm = c->_metamethods[MT_NEWMEMBER]; + if(sq_type(mm) != OT_NULL ) { + Push(self); Push(key); Push(val); + Push(attrs); + Push(bstatic); + return CallMetaMethod(mm,MT_NEWMEMBER,5,temp_reg); + } + } + if(!NewSlot(self, key, val,bstatic)) + return false; + if(sq_type(attrs) != OT_NULL) { + c->SetAttributes(key,attrs); + } + return true; +} + +bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) +{ + if(sq_type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; } + switch(sq_type(self)) { + case OT_TABLE: { + bool rawcall = true; + if(_table(self)->_delegate) { + SQObjectPtr res; + if(!_table(self)->Get(key,res)) { + SQObjectPtr closure; + if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { + Push(self);Push(key);Push(val); + if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { + return false; + } + rawcall = false; + } + else { + rawcall = true; + } + } + } + if(rawcall) _table(self)->NewSlot(key,val); //cannot fail + + break;} + case OT_INSTANCE: { + SQObjectPtr res; + SQObjectPtr closure; + if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { + Push(self);Push(key);Push(val); + if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { + return false; + } + break; + } + Raise_Error(_SC("class instances do not support the new slot operator")); + return false; + break;} + case OT_CLASS: + if(!_class(self)->NewSlot(_ss(this),key,val,bstatic)) { + if(_class(self)->_locked) { + Raise_Error(_SC("trying to modify a class that has already been instantiated")); + return false; + } + else { + SQObjectPtr oval = PrintObjVal(key); + Raise_Error(_SC("the property '%s' already exists"),_stringval(oval)); + return false; + } + } + break; + default: + Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); + return false; + break; + } + return true; +} + + + +bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res) +{ + switch(sq_type(self)) { + case OT_TABLE: + case OT_INSTANCE: + case OT_USERDATA: { + SQObjectPtr t; + //bool handled = false; + SQObjectPtr closure; + if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_DELSLOT,closure)) { + Push(self);Push(key); + return CallMetaMethod(closure,MT_DELSLOT,2,res); + } + else { + if(sq_type(self) == OT_TABLE) { + if(_table(self)->Get(key,t)) { + _table(self)->Remove(key); + } + else { + Raise_IdxError((const SQObject &)key); + return false; + } + } + else { + Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self)); + return false; + } + } + res = t; + } + break; + default: + Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self)); + return false; + } + return true; +} + +bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror) +{ +#ifdef _DEBUG +SQInteger prevstackbase = _stackbase; +#endif + switch(sq_type(closure)) { + case OT_CLOSURE: + return Execute(closure, nparams, stackbase, outres, raiseerror); + break; + case OT_NATIVECLOSURE:{ + bool dummy; + return CallNative(_nativeclosure(closure), nparams, stackbase, outres, -1, dummy, dummy); + + } + break; + case OT_CLASS: { + SQObjectPtr constr; + SQObjectPtr temp; + CreateClassInstance(_class(closure),outres,constr); + SQObjectType ctype = sq_type(constr); + if (ctype == OT_NATIVECLOSURE || ctype == OT_CLOSURE) { + _stack[stackbase] = outres; + return Call(constr,nparams,stackbase,temp,raiseerror); + } + return true; + } + break; + default: + Raise_Error(_SC("attempt to call '%s'"), GetTypeName(closure)); + return false; + } +#ifdef _DEBUG + if(!_suspended) { + assert(_stackbase == prevstackbase); + } +#endif + return true; +} + +bool SQVM::CallMetaMethod(SQObjectPtr &closure,SQMetaMethod SQ_UNUSED_ARG(mm),SQInteger nparams,SQObjectPtr &outres) +{ + //SQObjectPtr closure; + + _nmetamethodscall++; + if(Call(closure, nparams, _top - nparams, outres, SQFalse)) { + _nmetamethodscall--; + Pop(nparams); + return true; + } + _nmetamethodscall--; + //} + Pop(nparams); + return false; +} + +void SQVM::FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex) +{ + SQOuter **pp = &_openouters; + SQOuter *p; + SQOuter *otr; + + while ((p = *pp) != NULL && p->_valptr >= stackindex) { + if (p->_valptr == stackindex) { + target = SQObjectPtr(p); + return; + } + pp = &p->_next; + } + otr = SQOuter::Create(_ss(this), stackindex); + otr->_next = *pp; + otr->_idx = (stackindex - _stack._vals); + __ObjAddRef(otr); + *pp = otr; + target = SQObjectPtr(otr); +} + +bool SQVM::EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall) +{ + if( !tailcall ) { + if( _callsstacksize == _alloccallsstacksize ) { + GrowCallStack(); + } + ci = &_callsstack[_callsstacksize++]; + ci->_prevstkbase = (SQInt32)(newbase - _stackbase); + ci->_prevtop = (SQInt32)(_top - _stackbase); + ci->_etraps = 0; + ci->_ncalls = 1; + ci->_generator = NULL; + ci->_root = SQFalse; + } + else { + ci->_ncalls++; + } + + _stackbase = newbase; + _top = newtop; + if(newtop + MIN_STACK_OVERHEAD > (SQInteger)_stack.size()) { + if(_nmetamethodscall) { + Raise_Error(_SC("stack overflow, cannot resize stack while in a metamethod")); + return false; + } + _stack.resize(newtop + (MIN_STACK_OVERHEAD << 2)); + RelocateOuters(); + } + return true; +} + +void SQVM::LeaveFrame() { + SQInteger last_top = _top; + SQInteger last_stackbase = _stackbase; + SQInteger css = --_callsstacksize; + + /* First clean out the call stack frame */ + ci->_closure.Null(); + _stackbase -= ci->_prevstkbase; + _top = _stackbase + ci->_prevtop; + ci = (css) ? &_callsstack[css-1] : NULL; + + if(_openouters) CloseOuters(&(_stack._vals[last_stackbase])); + while (last_top >= _top) { + _stack._vals[last_top--].Null(); + } +} + +void SQVM::RelocateOuters() +{ + SQOuter *p = _openouters; + while (p) { + p->_valptr = _stack._vals + p->_idx; + p = p->_next; + } +} + +void SQVM::CloseOuters(SQObjectPtr *stackindex) { + SQOuter *p; + while ((p = _openouters) != NULL && p->_valptr >= stackindex) { + p->_value = *(p->_valptr); + p->_valptr = &p->_value; + _openouters = p->_next; + __ObjRelease(p); + } +} + +void SQVM::Remove(SQInteger n) { + n = (n >= 0)?n + _stackbase - 1:_top + n; + for(SQInteger i = n; i < _top; i++){ + _stack[i] = _stack[i+1]; + } + _stack[_top].Null(); + _top--; +} + +void SQVM::Pop() { + _stack[--_top].Null(); +} + +void SQVM::Pop(SQInteger n) { + for(SQInteger i = 0; i < n; i++){ + _stack[--_top].Null(); + } +} + +void SQVM::PushNull() { _stack[_top++].Null(); } +void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; } +SQObjectPtr &SQVM::Top() { return _stack[_top-1]; } +SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; } +SQObjectPtr &SQVM::GetUp(SQInteger n) { return _stack[_top+n]; } +SQObjectPtr &SQVM::GetAt(SQInteger n) { return _stack[n]; } + +#ifdef _DEBUG_DUMP +void SQVM::dumpstack(SQInteger stackbase,bool dumpall) +{ + SQInteger size=dumpall?_stack.size():_top; + SQInteger n=0; + scprintf(_SC("\n>>>>stack dump<<<<\n")); + CallInfo &ci=_callsstack[_callsstacksize-1]; + scprintf(_SC("IP: %p\n"),ci._ip); + scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase); + scprintf(_SC("prev top: %d\n"),ci._prevtop); + for(SQInteger i=0;i"));else scprintf(_SC(" ")); + scprintf(_SC("[" _PRINT_INT_FMT "]:"),n); + switch(sq_type(obj)){ + case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break; + case OT_INTEGER: scprintf(_SC("INTEGER " _PRINT_INT_FMT),_integer(obj));break; + case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break; + case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break; + case OT_NULL: scprintf(_SC("NULL")); break; + case OT_TABLE: scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break; + case OT_ARRAY: scprintf(_SC("ARRAY %p"),_array(obj));break; + case OT_CLOSURE: scprintf(_SC("CLOSURE [%p]"),_closure(obj));break; + case OT_NATIVECLOSURE: scprintf(_SC("NATIVECLOSURE"));break; + case OT_USERDATA: scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break; + case OT_GENERATOR: scprintf(_SC("GENERATOR %p"),_generator(obj));break; + case OT_THREAD: scprintf(_SC("THREAD [%p]"),_thread(obj));break; + case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break; + case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break; + case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break; + case OT_WEAKREF: scprintf(_SC("WEAKREF %p"),_weakref(obj));break; + default: + assert(0); + break; + }; + scprintf(_SC("\n")); + ++n; + } +} + + + +#endif diff --git a/sp/src/vscript/squirrel/squirrel/sqvm.h b/sp/src/vscript/squirrel/squirrel/sqvm.h new file mode 100644 index 0000000000..a75524daee --- /dev/null +++ b/sp/src/vscript/squirrel/squirrel/sqvm.h @@ -0,0 +1,213 @@ +/* see copyright notice in squirrel.h */ +#ifndef _SQVM_H_ +#define _SQVM_H_ + +#include "sqopcodes.h" +#include "sqobject.h" +#define MAX_NATIVE_CALLS 100 +#define MIN_STACK_OVERHEAD 15 + +#define SQ_SUSPEND_FLAG -666 +#define SQ_TAILCALL_FLAG -777 +#define DONT_FALL_BACK 666 +//#define EXISTS_FALL_BACK -1 + +#define GET_FLAG_RAW 0x00000001 +#define GET_FLAG_DO_NOT_RAISE_ERROR 0x00000002 +//base lib +void sq_base_register(HSQUIRRELVM v); + +struct SQExceptionTrap{ + SQExceptionTrap() {} + SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;} + SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et; } + SQInteger _stackbase; + SQInteger _stacksize; + SQInstruction *_ip; + SQInteger _extarget; +}; + +#define _INLINE + +typedef sqvector ExceptionsTraps; + +struct SQVM : public CHAINABLE_OBJ +{ + struct CallInfo{ + //CallInfo() { _generator = NULL;} + SQInstruction *_ip; + SQObjectPtr *_literals; + SQObjectPtr _closure; + SQGenerator *_generator; + SQInt32 _etraps; + SQInt32 _prevstkbase; + SQInt32 _prevtop; + SQInt32 _target; + SQInt32 _ncalls; + SQBool _root; + }; + +typedef sqvector CallInfoVec; +public: + void DebugHookProxy(SQInteger type, const SQChar * sourcename, SQInteger line, const SQChar * funcname); + static void _DebugHookProxy(HSQUIRRELVM v, SQInteger type, const SQChar * sourcename, SQInteger line, const SQChar * funcname); + enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM,ET_RESUME_THROW_VM }; + SQVM(SQSharedState *ss); + ~SQVM(); + bool Init(SQVM *friendvm, SQInteger stacksize); + bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL); + //starts a native call return when the NATIVE closure returns + bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); + bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); + //starts a SQUIRREL call in the same "Execution loop" + bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); + bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); + //call a generic closure pure SQUIRREL or NATIVE + bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror); + SQRESULT Suspend(); + + void CallDebugHook(SQInteger type,SQInteger forcedline=0); + void CallErrorHandler(SQObjectPtr &e); + bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags, SQInteger selfidx); + SQInteger FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); + bool InvokeDefaultDelegate(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); + bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val, SQInteger selfidx); + SQInteger FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val); + bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic); + bool NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw); + bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res); + bool Clone(const SQObjectPtr &self, SQObjectPtr &target); + bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res); + bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest); + static bool IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res); + bool ToString(const SQObjectPtr &o,SQObjectPtr &res); + SQString *PrintObjVal(const SQObjectPtr &o); + + + void Raise_Error(const SQChar *s, ...); + void Raise_Error(const SQObjectPtr &desc); + void Raise_IdxError(const SQObjectPtr &o); + void Raise_CompareError(const SQObject &o1, const SQObject &o2); + void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type); + + void FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex); + void RelocateOuters(); + void CloseOuters(SQObjectPtr *stackindex); + + bool TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest); + bool CallMetaMethod(SQObjectPtr &closure, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres); + bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest); + bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval); + //new stuff + _INLINE bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); + _INLINE bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); + _INLINE bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1); + _INLINE bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res); + bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func); + bool CLASS_OP(SQObjectPtr &target,SQInteger base,SQInteger attrs); + //return true if the loop is finished + bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,SQInteger arg_2,int exitpos,int &jump); + //_INLINE bool LOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); + _INLINE bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); + _INLINE bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix,SQInteger arg0); +#ifdef _DEBUG_DUMP + void dumpstack(SQInteger stackbase=-1, bool dumpall = false); +#endif + +#ifndef NO_GARBAGE_COLLECTOR + void Mark(SQCollectable **chain); + SQObjectType GetType() {return OT_THREAD;} +#endif + void Finalize(); + void GrowCallStack() { + SQInteger newsize = _alloccallsstacksize*2; + _callstackdata.resize(newsize); + _callsstack = &_callstackdata[0]; + _alloccallsstacksize = newsize; + } + bool EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall); + void LeaveFrame(); + void Release(){ sq_delete(this,SQVM); } +//////////////////////////////////////////////////////////////////////////// + //stack functions for the api + void Remove(SQInteger n); + + static bool IsFalse(SQObjectPtr &o); + + void Pop(); + void Pop(SQInteger n); + void Push(const SQObjectPtr &o); + void PushNull(); + SQObjectPtr &Top(); + SQObjectPtr &PopGet(); + SQObjectPtr &GetUp(SQInteger n); + SQObjectPtr &GetAt(SQInteger n); + + SQObjectPtrVec _stack; + + SQInteger _top; + SQInteger _stackbase; + SQOuter *_openouters; + SQObjectPtr _roottable; + SQObjectPtr _lasterror; + SQObjectPtr _errorhandler; + + bool _debughook; + SQDEBUGHOOK _debughook_native; + SQObjectPtr _debughook_closure; + + SQObjectPtr temp_reg; + + + CallInfo* _callsstack; + SQInteger _callsstacksize; + SQInteger _alloccallsstacksize; + sqvector _callstackdata; + + ExceptionsTraps _etraps; + CallInfo *ci; + SQUserPointer _foreignptr; + //VMs sharing the same state + SQSharedState *_sharedstate; + SQInteger _nnativecalls; + SQInteger _nmetamethodscall; + SQRELEASEHOOK _releasehook; + //suspend infos + SQBool _suspended; + SQBool _suspended_root; + SQInteger _suspended_target; + SQInteger _suspended_traps; +}; + +struct AutoDec{ + AutoDec(SQInteger *n) { _n = n; } + ~AutoDec() { (*_n)--; } + SQInteger *_n; +}; + +inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));} + +#define _ss(_vm_) (_vm_)->_sharedstate + +#ifndef NO_GARBAGE_COLLECTOR +#define _opt_ss(_vm_) (_vm_)->_sharedstate +#else +#define _opt_ss(_vm_) NULL +#endif + +#define PUSH_CALLINFO(v,nci){ \ + SQInteger css = v->_callsstacksize; \ + if(css == v->_alloccallsstacksize) { \ + v->GrowCallStack(); \ + } \ + v->ci = &v->_callsstack[css]; \ + *(v->ci) = nci; \ + v->_callsstacksize++; \ +} + +#define POP_CALLINFO(v){ \ + SQInteger css = --v->_callsstacksize; \ + v->ci->_closure.Null(); \ + v->ci = css?&v->_callsstack[css-1]:NULL; \ +} +#endif //_SQVM_H_ diff --git a/sp/src/vscript/vscript.cpp b/sp/src/vscript/vscript.cpp new file mode 100644 index 0000000000..95efbe50d1 --- /dev/null +++ b/sp/src/vscript/vscript.cpp @@ -0,0 +1,48 @@ +#include "vscript/ivscript.h" + +#include "tier1/tier1.h" + +IScriptVM* makeSquirrelVM(); + +int vscript_token = 0; + +class CScriptManager : public CTier1AppSystem +{ +public: + virtual IScriptVM* CreateVM(ScriptLanguage_t language) override + { + IScriptVM* pScriptVM = nullptr; + if (language == SL_SQUIRREL) + { + pScriptVM = makeSquirrelVM(); + } + else + { + return nullptr; + } + + if (pScriptVM == nullptr) + { + return nullptr; + } + + if (!pScriptVM->Init()) + { + delete pScriptVM; + return nullptr; + } + + return pScriptVM; + } + + virtual void DestroyVM(IScriptVM * pScriptVM) override + { + if (pScriptVM) + { + pScriptVM->Shutdown(); + delete pScriptVM; + } + } +}; + +EXPOSE_SINGLE_INTERFACE(CScriptManager, IScriptManager, VSCRIPT_INTERFACE_VERSION); \ No newline at end of file diff --git a/sp/src/vscript/vscript.vpc b/sp/src/vscript/vscript.vpc new file mode 100644 index 0000000000..1d660ccce4 --- /dev/null +++ b/sp/src/vscript/vscript.vpc @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// VSCRIPT.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR ".." + +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;.\squirrel\include" + } +} + +$Project "VScript" +{ + $Folder "Source Files" + { + + $File "vscript.cpp" + $File "vscript_squirrel.cpp" + + $Folder "squirrel" + { + $File ".\squirrel\sqstdlib\sqstdaux.cpp" \ + ".\squirrel\sqstdlib\sqstdblob.cpp" \ + ".\squirrel\sqstdlib\sqstdio.cpp" \ + ".\squirrel\sqstdlib\sqstdmath.cpp" \ + ".\squirrel\sqstdlib\sqstdrex.cpp" \ + ".\squirrel\sqstdlib\sqstdstream.cpp" \ + ".\squirrel\sqstdlib\sqstdstring.cpp" \ + ".\squirrel\sqstdlib\sqstdsystem.cpp" \ + ".\squirrel\squirrel\sqapi.cpp" \ + ".\squirrel\squirrel\sqbaselib.cpp" \ + ".\squirrel\squirrel\sqclass.cpp" \ + ".\squirrel\squirrel\sqcompiler.cpp" \ + ".\squirrel\squirrel\sqdebug.cpp" \ + ".\squirrel\squirrel\sqfuncstate.cpp" \ + ".\squirrel\squirrel\sqlexer.cpp" \ + ".\squirrel\squirrel\sqmem.cpp" \ + ".\squirrel\squirrel\sqobject.cpp" \ + ".\squirrel\squirrel\sqstate.cpp" \ + ".\squirrel\squirrel\sqtable.cpp" \ + ".\squirrel\squirrel\sqvm.cpp" + { + $Configuration + { + $Compiler + { + // Squirrel third party library is full of warnings + $AdditionalOptions "$BASE /wd4100 /wd4611 /wd4127 /wd4244 /wd4702 /wd4706 /wd4800" + $TreatWarningsAsErrors "No" + } + } + } + } + } +} diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp new file mode 100644 index 0000000000..81c2f308ee --- /dev/null +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -0,0 +1,3025 @@ +#include "vscript/ivscript.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlmap.h" +#include "tier1/utlstring.h" + +#include "squirrel.h" +#include "sqstdaux.h" +//#include "sqstdblob.h" +//#include "sqstdsystem.h" +//#include "sqstdio.h" +#include "sqstdmath.h" +#include "sqstdstring.h" + +// HACK: Include internal parts of squirrel for serialization +#include "squirrel/squirrel/sqobject.h" +#include "squirrel/squirrel/sqstate.h" +#include "squirrel/squirrel/sqtable.h" +#include "squirrel/squirrel/sqclass.h" +#include "squirrel/squirrel/sqfuncproto.h" +#include "squirrel/squirrel/sqvm.h" +#include "squirrel/squirrel/sqclosure.h" + + +#include "tier1/utlbuffer.h" + +#include + +struct WriteStateMap +{ + CUtlMap cache; + WriteStateMap() : cache(DefLessFunc(void*)) + {} + + bool CheckCache(CUtlBuffer* pBuffer, void* ptr) + { + auto idx = cache.Find(ptr); + if (idx != cache.InvalidIndex()) + { + pBuffer->PutInt(cache[idx]); + return true; + } + else + { + int newIdx = cache.Count(); + cache.Insert(ptr, newIdx); + pBuffer->PutInt(newIdx); + return false; + } + } +}; + +struct ReadStateMap +{ + CUtlMap cache; + HSQUIRRELVM vm_; + ReadStateMap(HSQUIRRELVM vm) : cache(DefLessFunc(int)), vm_(vm) + {} + + ~ReadStateMap() + { + FOR_EACH_MAP_FAST(cache, i) + { + HSQOBJECT& obj = cache[i]; + sq_release(vm_, &obj); + } + } + + bool CheckCache(CUtlBuffer* pBuffer, HSQOBJECT** obj) + { + int marker = pBuffer->GetInt(); + + auto idx = cache.Find(marker); + if (idx != cache.InvalidIndex()) + { + *obj = &cache[idx]; + return true; + } + else + { + HSQOBJECT temp; + sq_resetobject(&temp); + auto idx = cache.Insert(marker, temp); + *obj = &cache[idx]; + return false; + } + } +}; + +class SquirrelVM : public IScriptVM +{ +public: + virtual bool Init() override; + virtual void Shutdown() override; + + virtual bool ConnectDebugger() override; + virtual void DisconnectDebugger() override; + + virtual ScriptLanguage_t GetLanguage() override; + virtual const char* GetLanguageName() override; + + virtual void AddSearchPath(const char* pszSearchPath) override; + + //-------------------------------------------------------- + + virtual bool Frame(float simTime) override; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run(const char* pszScript, bool bWait = true) override; + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript(const char* pszScript, const char* pszId = NULL) override; + virtual void ReleaseScript(HSCRIPT) override; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run(HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true) override; + virtual ScriptStatus_t Run(HSCRIPT hScript, bool bWait) override; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope(const char* pszScope, HSCRIPT hParent = NULL) override; + virtual void ReleaseScope(HSCRIPT hScript) override; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction(const char* pszFunction, HSCRIPT hScope = NULL) override; + virtual void ReleaseFunction(HSCRIPT hScript) override; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) override; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) override; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass(ScriptClassDesc_t* pClassDesc) override; + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + + virtual HSCRIPT RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance) override; + virtual void SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) override; + virtual void RemoveInstance(HSCRIPT hInstance) override; + + virtual void* GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpectedType = NULL) override; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) override; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists(HSCRIPT hScope, const char* pszKey) override; + + virtual bool SetValue(HSCRIPT hScope, const char* pszKey, const char* pszValue) override; + virtual bool SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVariant_t& value) override; + + virtual void CreateTable(ScriptVariant_t& Table) override; + virtual int GetNumTableEntries(HSCRIPT hScope) override; + virtual int GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey, ScriptVariant_t* pValue) override; + + virtual bool GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) override; + virtual void ReleaseValue(ScriptVariant_t& value) override; + + virtual bool ClearValue(HSCRIPT hScope, const char* pszKey) override; + + //---------------------------------------------------------------------------- + + virtual void WriteState(CUtlBuffer* pBuffer) override; + virtual void ReadState(CUtlBuffer* pBuffer) override; + virtual void RemoveOrphanInstances() override; + + virtual void DumpState() override; + + virtual void SetOutputCallback(ScriptOutputFunc_t pFunc) override; + virtual void SetErrorCallback(ScriptErrorFunc_t pFunc) override; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException(const char* pszExceptionText) override; + + + void WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQInteger idx); + void ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState); + HSQUIRRELVM vm_ = nullptr; + HSQOBJECT lastError_; + HSQOBJECT vectorClass_; + HSQOBJECT regexpClass_; +}; + +SQUserPointer TYPETAG_VECTOR = "VectorTypeTag"; + +namespace SQVector +{ + SQInteger Construct(HSQUIRRELVM vm) + { + // TODO: There must be a nicer way to store the data with the actual instance, there are + // default slots but they are really just pointers anyway and you need to hold a member + // pointer which is yet another pointer dereference + int numParams = sq_gettop(vm); + + float x = 0; + float y = 0; + float z = 0; + + if ((numParams >= 2 && SQ_FAILED(sq_getfloat(vm, 2, &x))) || + (numParams >= 3 && SQ_FAILED(sq_getfloat(vm, 3, &y))) || + (numParams >= 4 && SQ_FAILED(sq_getfloat(vm, 4, &z)))) + { + return sq_throwerror(vm, "Expected Vector(float x, float y, float z)"); + } + + SQUserPointer p; + sq_getinstanceup(vm, 1, &p, 0); + new (p) Vector(x, y, z); + + return 0; + } + + SQInteger Get(HSQUIRRELVM vm) + { + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected Vector._get(string)"); + } + + if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + { + return sqstd_throwerrorf(vm, "Unexpected key %s\n", key); + } + + Vector* v = nullptr; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Unable to get Vector"); + } + + int idx = key[0] - 'x'; + sq_pushfloat(vm, (*v)[idx]); + return 1; + } + + SQInteger Set(HSQUIRRELVM vm) + { + const char* key = nullptr; + sq_getstring(vm, 2, &key); + + if (key == nullptr) + { + return sq_throwerror(vm, "Expected Vector._get(string)"); + } + + if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + { + return sqstd_throwerrorf(vm, "Unexpected key %s\n", key); + } + + Vector* v = nullptr; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Unable to get Vector"); + } + + float val = 0; + if (SQ_FAILED(sq_getfloat(vm, 3, &val))) + { + return sqstd_throwerrorf(vm, "Vector.%s expects float", key); + } + + int idx = key[0] - 'x'; + (*v)[idx] = val; + return 0; + } + + SQInteger Add(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) + (*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Sub(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) - (*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Multiply(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + + SQObjectType paramType = sq_gettype(vm, 2); + + float s = 0.0; + Vector* v2 = nullptr; + + if ((paramType & SQOBJECT_NUMERIC) && + SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * s); + sq_remove(vm, -2); + + return 1; + } + else if (paramType == OT_INSTANCE && + SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) * (*v2)); + sq_remove(vm, -2); + + return 1; + } + else + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + } + + SQInteger Divide(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + + SQObjectType paramType = sq_gettype(vm, 2); + + float s = 0.0; + Vector* v2 = nullptr; + + if ((paramType & SQOBJECT_NUMERIC) && + SQ_SUCCEEDED(sq_getfloat(vm, 2, &s))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) / s); + sq_remove(vm, -2); + + return 1; + } + else if (paramType == OT_INSTANCE && + SQ_SUCCEEDED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1) / (*v2)); + sq_remove(vm, -2); + + return 1; + } + else + { + return sq_throwerror(vm, "Expected (Vector, float) or (Vector, Vector)"); + } + } + + SQInteger Length(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length()); + return 1; + } + + SQInteger LengthSqr(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->LengthSqr()); + return 1; + } + + SQInteger Length2D(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length2D()); + return 1; + } + + SQInteger Length2DSqr(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->Length2DSqr()); + return 1; + } + + SQInteger Normalized(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1).Normalized()); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Dot(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_pushfloat(vm, v1->Dot(*v2)); + return 1; + } + + SQInteger ToKVString(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sqstd_pushstringf(vm, "%f %f %f", v1->x, v1->y, v1->z); + return 1; + } + + SQInteger Cross(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + Vector* v2 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR)) || + SQ_FAILED(sq_getinstanceup(vm, 2, (SQUserPointer*)&v2, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector, Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector((*v1).Cross(*v2)); + sq_remove(vm, -2); + + return 1; + } + + SQInteger ToString(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sqstd_pushstringf(vm, "(vector: (%f, %f, %f))", v1->x, v1->y, v1->z); + return 1; + } + + SQInteger TypeOf(HSQUIRRELVM vm) + { + sq_pushstring(vm, "Vector", -1); + return 1; + } + + SQInteger Nexti(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 2 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm, 2, &obj); + + const char* curkey = nullptr; + + if (sq_isnull(obj)) + { + curkey = "w"; + } + else if (sq_isstring(obj)) + { + curkey = sq_objtostring(&obj); + } + else + { + return sq_throwerror(vm, "Invalid iteartor"); + } + + Assert(curkey && curkey[0] >= 'w' && curkey[0] <= 'z'); + + if (curkey[0] == 'z') + { + // Reached the end + sq_pushnull(vm); + return 1; + } + + char newkey = curkey[0] + 1; + sq_pushstring(vm, &newkey, 1); + + return 1; + } + + static const SQRegFunction funcs[] = { + {_SC("constructor"), Construct,0,nullptr}, + {_SC("_get"), Get, 2, _SC(".s")}, + {_SC("_set"), Set, 3, _SC(".sn")}, + {_SC("_add"), Add, 2, _SC("..")}, + {_SC("_sub"), Sub, 2, _SC("..")}, + {_SC("_mul"), Multiply, 2, _SC("..")}, + {_SC("_div"), Divide, 2, _SC("..")}, + {_SC("Length"), Length, 1, _SC(".")}, + {_SC("LengthSqr"), LengthSqr, 1, _SC(".")}, + {_SC("Length2D"), Length2D, 1, _SC(".")}, + {_SC("Length2DSqr"), Length2DSqr, 1, _SC(".")}, + {_SC("Normalized"), Normalized, 1, _SC(".")}, + {_SC("Norm"), Normalized, 1, _SC(".")}, + {_SC("_div"), Divide, 2, _SC("..")}, + {_SC("Dot"), Dot, 2, _SC("..")}, + {_SC("Cross"), Cross, 2, _SC("..")}, + {_SC("ToKVString"), ToKVString, 1, _SC(".")}, + {_SC("_tostring"), ToString, 1, _SC(".")}, + {_SC("_typeof"), TypeOf, 1, _SC(".")}, + {_SC("_nexti"), Nexti, 2, _SC("..")}, + + {nullptr,(SQFUNCTION)0,0,nullptr} + }; + + HSQOBJECT register_class(HSQUIRRELVM v) + { + sq_pushstring(v, _SC("Vector"), -1); + sq_newclass(v, SQFalse); + sq_settypetag(v, -1, TYPETAG_VECTOR); + sq_setclassudsize(v, -1, sizeof(Vector)); + SQInteger i = 0; + while (funcs[i].name != 0) { + const SQRegFunction& f = funcs[i]; + sq_pushstring(v, f.name, -1); + sq_newclosure(v, f.f, 0); + sq_setparamscheck(v, f.nparamscheck, f.typemask); + sq_setnativeclosurename(v, -1, f.name); + sq_newslot(v, -3, SQFalse); + i++; + } + HSQOBJECT klass; + sq_resetobject(&klass); + sq_getstackobj(v, -1, &klass); + sq_addref(v, &klass); + sq_newslot(v, -3, SQFalse); + return klass; + } +} // SQVector + +struct ClassInstanceData +{ + ClassInstanceData(void* instance, ScriptClassDesc_t* desc, const char* instanceId = nullptr) : + instance(instance), + desc(desc), + instanceId(instanceId) + {} + + void* instance; + ScriptClassDesc_t* desc; + CUtlString instanceId; +}; + +bool CreateParamCheck(const ScriptFunctionBinding_t& func, char* output) +{ + *output++ = '.'; + for (int i = 0; i < func.m_desc.m_Parameters.Count(); ++i) + { + switch (func.m_desc.m_Parameters[i]) + { + case FIELD_FLOAT: + *output++ = 'n'; // NOTE: Can be int or float + break; + case FIELD_CSTRING: + *output++ = 's'; + break; + case FIELD_VECTOR: + *output++ = 'x'; // Generic instance, we validate on arrival + break; + case FIELD_INTEGER: + *output++ = 'i'; // could use 'n' also which is int or float + break; + case FIELD_BOOLEAN: + *output++ = 'b'; + break; + case FIELD_CHARACTER: + *output++ = 's'; + break; + case FIELD_HSCRIPT: + *output++ = '.'; + break; + default: + Assert(!"Unsupported type"); + return false; + }; + } + *output++ = 0; + return true; +} + +void PushVariant(HSQUIRRELVM vm, const ScriptVariant_t& value) +{ + switch (value.m_type) + { + case FIELD_VOID: + sq_pushnull(vm); + break; + case FIELD_FLOAT: + sq_pushfloat(vm, value); + break; + case FIELD_CSTRING: + sq_pushstring(vm, value, -1); + break; + case FIELD_VECTOR: + { + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + sq_pushobject(vm, pSquirrelVM->vectorClass_); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(value); + sq_remove(vm, -2); + break; + } + case FIELD_INTEGER: + sq_pushinteger(vm, value.m_int); + break; + case FIELD_BOOLEAN: + sq_pushbool(vm, value.m_bool); + break; + case FIELD_CHARACTER: + { + char buf[2] = { value.m_char, 0 }; + sq_pushstring(vm, buf, 1); + break; + } + case FIELD_HSCRIPT: + if (value.m_hScript) + { + sq_pushobject(vm, *((HSQOBJECT*)value.m_hScript)); + } + else + { + sq_pushnull(vm); + } + break; + } +} + +bool getVariant(HSQUIRRELVM vm, SQInteger idx, ScriptVariant_t& variant) +{ + switch (sq_gettype(vm, idx)) + { + case OT_NULL: + { + // TODO: Should this be (HSCRIPT)nullptr + variant.m_type = FIELD_VOID; + return true; + } + case OT_INTEGER: + { + SQInteger val; + if (SQ_FAILED(sq_getinteger(vm, idx, &val))) + { + return false; + } + variant = (int)val; + return true; + } + case OT_FLOAT: + { + SQFloat val; + if (SQ_FAILED(sq_getfloat(vm, idx, &val))) + { + return false; + } + variant = (float)val; + return true; + } + case OT_BOOL: + { + SQBool val; + if (SQ_FAILED(sq_getbool(vm, idx, &val))) + { + return false; + } + variant = val ? true : false; + return true; + } + case OT_STRING: + { + const char* val; + SQInteger size = 0; + if (SQ_FAILED(sq_getstringandsize(vm, idx, &val, &size))) + { + return false; + } + char* buffer = new char[size + 1]; + V_memcpy(buffer, val, size); + buffer[size] = 0; + variant = buffer; + variant.m_flags |= SV_FREE; + return true; + } + case OT_INSTANCE: + { + Vector* v = nullptr; + SQUserPointer tag; + if (SQ_SUCCEEDED(sq_gettypetag(vm, idx, &tag)) && + tag == TYPETAG_VECTOR && + SQ_SUCCEEDED(sq_getinstanceup(vm, idx, (SQUserPointer*)&v, TYPETAG_VECTOR))) + { + variant = new Vector(*v); + variant.m_flags |= SV_FREE; + return true; + } + // fall-through for non-vector + } + default: + { + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm, idx, obj); + sq_addref(vm, obj); + variant = (HSCRIPT)obj; + } + }; + + + return true; +} + +SQInteger function_stub(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + SQUserPointer userptr = nullptr; + sq_getuserpointer(vm, top, &userptr); + + Assert(userptr); + + ScriptFunctionBinding_t* pFunc = (ScriptFunctionBinding_t*)userptr; + + auto nargs = pFunc->m_desc.m_Parameters.Count(); + + if (nargs > top) + { + // TODO: Handle optional parameters? + return sq_throwerror(vm, "Invalid number of parameters"); + } + + CUtlVector params; + params.SetCount(nargs); + + for (int i = 0; i < nargs; ++i) + { + switch (pFunc->m_desc.m_Parameters[i]) + { + case FIELD_FLOAT: + { + float val = 0.0; + if (SQ_FAILED(sq_getfloat(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected float"); + params[i] = val; + break; + } + case FIELD_CSTRING: + { + const char* val; + if (SQ_FAILED(sq_getstring(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + params[i] = val; + break; + } + case FIELD_VECTOR: + { + Vector* val; + if (SQ_FAILED(sq_getinstanceup(vm, i + 2, (SQUserPointer*)&val, TYPETAG_VECTOR))) + return sq_throwerror(vm, "Expected Vector"); + params[i] = *val; + break; + } + case FIELD_INTEGER: + { + SQInteger val = 0; + if (SQ_FAILED(sq_getinteger(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected integer"); + params[i] = (int)val; + break; + } + case FIELD_BOOLEAN: + { + SQBool val = 0; + if (SQ_FAILED(sq_getbool(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected bool"); + params[i] = val ? true : false; + break; + } + case FIELD_CHARACTER: + { + const char* val; + if (SQ_FAILED(sq_getstring(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + params[i] = val[i]; + break; + } + case FIELD_HSCRIPT: + { + HSQOBJECT val; + if (SQ_FAILED(sq_getstackobj(vm, i + 2, &val))) + return sq_throwerror(vm, "Expected string"); + + if (sq_isnull(val)) + { + params[i] = (HSCRIPT)nullptr; + } + else + { + HSQOBJECT* pObject = new HSQOBJECT; + *pObject = val; + sq_addref(vm, pObject); + params[i] = (HSCRIPT)pObject; + } + break; + } + default: + Assert(!"Unsupported type"); + return false; + } + } + + void* instance = nullptr; + + if (pFunc->m_flags & SF_MEMBER_FUNC) + { + SQUserPointer self; + sq_getinstanceup(vm, 1, &self, nullptr); + + if (!self) + { + return sq_throwerror(vm, "Can't be used on a null instance"); + } + + instance = ((ClassInstanceData*)self)->instance; + } + + ScriptVariant_t retval; + + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + + sq_resetobject(&pSquirrelVM->lastError_); + + (*pFunc->m_pfnBinding)(pFunc->m_pFunction, instance, params.Base(), nargs, + pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &retval); + + if (!sq_isnull(pSquirrelVM->lastError_)) + { + sq_pushobject(vm, pSquirrelVM->lastError_); + sq_resetobject(&pSquirrelVM->lastError_); + return sq_throwobject(vm); + } + + PushVariant(vm, retval); + + return pFunc->m_desc.m_ReturnType != FIELD_VOID; +} + + +SQInteger destructor_stub(SQUserPointer p, SQInteger size) +{ + auto classInstanceData = (ClassInstanceData*)p; + classInstanceData->desc->m_pfnDestruct(classInstanceData->instance); + classInstanceData->~ClassInstanceData(); + return 0; +} + +SQInteger destructor_stub_instance(SQUserPointer p, SQInteger size) +{ + auto classInstanceData = (ClassInstanceData*)p; + // We don't call destructor here because this is owned by the game + classInstanceData->~ClassInstanceData(); + return 0; +} + +SQInteger constructor_stub(HSQUIRRELVM vm) +{ + ScriptClassDesc_t* pClassDesc = nullptr; + sq_gettypetag(vm, 1, (SQUserPointer*)&pClassDesc); + + if (!pClassDesc->m_pfnConstruct) + { + return sqstd_throwerrorf(vm, "Unable to construct instances of %s", pClassDesc->m_pszScriptName); + } + + + SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getforeignptr(vm); + assert(pSquirrelVM); + + sq_resetobject(&pSquirrelVM->lastError_); + + void* instance = pClassDesc->m_pfnConstruct(); + + if (!sq_isnull(pSquirrelVM->lastError_)) + { + sq_pushobject(vm, pSquirrelVM->lastError_); + sq_resetobject(&pSquirrelVM->lastError_); + return sq_throwobject(vm); + } + + { + SQUserPointer p; + sq_getinstanceup(vm, 1, &p, 0); + new(p) ClassInstanceData(instance, pClassDesc); + } + + sq_setreleasehook(vm, 1, &destructor_stub); + + return 0; +} + +SQInteger tostring_stub(HSQUIRRELVM vm) +{ + ClassInstanceData* classInstanceData = nullptr; + sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + + char buffer[128] = ""; + + if (classInstanceData && + classInstanceData->instance && + classInstanceData->desc->pHelper && + classInstanceData->desc->pHelper->ToString(classInstanceData->instance, buffer, sizeof(buffer))) + { + sq_pushstring(vm, buffer, -1); + } + else if (classInstanceData) + { + sqstd_pushstringf(vm, "(%s : 0x%p)", classInstanceData->desc->m_pszScriptName, classInstanceData->instance); + } + else + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm, 1, &obj); + // Semi-based on SQVM::ToString default case + sqstd_pushstringf(vm, "(%s: 0x%p)", IdType2Name(obj._type), (void*)_rawval(obj)); + } + + return 1; +} + +struct SquirrelSafeCheck +{ + SquirrelSafeCheck(HSQUIRRELVM vm, int outputCount = 0) : + vm_{ vm }, + top_{ sq_gettop(vm) }, + outputCount_{ outputCount } + {} + + ~SquirrelSafeCheck() + { + if (top_ != (sq_gettop(vm_) - outputCount_)) + { + Assert(!"Squirrel VM stack is not consistent"); + Error("Squirrel VM stack is not consistent\n"); + } + + // TODO: Handle error state checks + } + + HSQUIRRELVM vm_; + SQInteger top_; + SQInteger outputCount_; +}; + + +void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) +{ + va_list args; + va_start(args, format); + char buffer[256]; + vsprintf(buffer, format, args); + Msg("%s", buffer); + va_end(args); +} + +void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v), const SQChar* format, ...) +{ + // NOTE: This is only separate from printfunc to make it easier to add breakpoints + va_list args; + va_start(args, format); + char buffer[256]; + vsprintf(buffer, format, args); + Msg("%s", buffer); + va_end(args); +} + +const char * ScriptDataTypeToName(ScriptDataType_t datatype) +{ + switch (datatype) + { + 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 ""; + } +} + +void RegisterDocumentation(HSQUIRRELVM vm, const ScriptFuncDescriptor_t& pFuncDesc, ScriptClassDesc_t* pClassDesc = nullptr) +{ + SquirrelSafeCheck safeCheck(vm); + + if (pFuncDesc.m_pszDescription && pFuncDesc.m_pszDescription[0] == SCRIPT_HIDE[0]) + return; + + char name[256] = ""; + + if (pClassDesc) + { + V_strcat_safe(name, pClassDesc->m_pszScriptName); + V_strcat_safe(name, "::"); + } + + V_strcat_safe(name, pFuncDesc.m_pszScriptName); + + + char signature[256] = ""; + V_snprintf(signature, sizeof(signature), "%s %s(", ScriptDataTypeToName(pFuncDesc.m_ReturnType), name); + + for (int i = 0; i < pFuncDesc.m_Parameters.Count(); ++i) + { + if (i != 0) + V_strcat_safe(signature, ", "); + + V_strcat_safe(signature, ScriptDataTypeToName(pFuncDesc.m_Parameters[i])); + } + + V_strcat_safe(signature, ")"); + + // RegisterHelp(name, signature, description) + sq_pushroottable(vm); + sq_pushstring(vm, "RegisterHelp", -1); + sq_get(vm, -2); + sq_remove(vm, -2); + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + sq_pushstring(vm, signature, -1); + sq_pushstring(vm, pFuncDesc.m_pszDescription ? pFuncDesc.m_pszDescription : "", -1); + sq_call(vm, 4, SQFalse, SQFalse); + sq_pop(vm, 1); +} + + +bool SquirrelVM::Init() +{ + vm_ = sq_open(1024); //creates a VM with initial stack size 1024 + + if (vm_ == nullptr) + return false; + + sq_setforeignptr(vm_, this); + sq_resetobject(&lastError_); + + sq_setprintfunc(vm_, printfunc, errorfunc); + + + { + sq_pushroottable(vm_); + + sqstd_register_mathlib(vm_); + sqstd_register_stringlib(vm_); + vectorClass_ = SQVector::register_class(vm_); + + // We exclude these libraries as they create a security risk on the client even + // though I'm sure if someone tried hard enough they could achieve all sorts of + // things with the other APIs, this just makes it a little bit harder for a map + // created by someone in the community causing a bunch of security vulnerablilties. + // + // Pretty sure DoIncludeScript() is already a vulnerability vector here, however + // that also depends on compile errors not showing up and relies on IFilesystem with + // a path prefix. + // + //sqstd_register_bloblib(vm_); + //sqstd_register_iolib(vm_); + //sqstd_register_systemlib(vm_); + + + sqstd_seterrorhandlers(vm_); + + { + // Unfortunately we can not get the pattern from a regexp instance + // so we need to wrap it with our own to get it. + if (Run(R"script( + class regexp extends regexp + { + constructor(pattern) + { + base.constructor(pattern); + pattern_ = pattern; + } + pattern_=""; + } + )script") == SCRIPT_ERROR) + { + this->Shutdown(); + return false; + } + + sq_resetobject(®expClass_); + sq_pushstring(vm_, "regexp", -1); + sq_rawget(vm_, -2); + sq_getstackobj(vm_, -1, ®expClass_); + sq_addref(vm_, ®expClass_); + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + } + + if (Run( + R"script( + function printl( text ) + { + return print(text + "\n"); + } + + class CSimpleCallChainer + { + function constructor(prefixString, scopeForThis, exactMatch) + { + prefix = prefixString; + scope = scopeForThis; + chain = []; + scope["Dispatch" + prefixString] <- Call.bindenv(this); + } + + function PostScriptExecute() + { + local func = null; + try { + func = scope[prefix]; + } catch(e) { + return; + } + if (typeof(func) != "function") + return; + chain.push(func); + } + + function Call() + { + foreach (func in chain) + { + func.pcall(scope); + } + } + prefix = null; + scope= null; + chain = []; + } + + DocumentedFuncs <- {} + + function RegisterHelp(name, signature, description) + { + if (description.len() && description[0] == '#') + { + // This is an alias function, could use split() if we could guarantee + // that ':' would not occur elsewhere in the description and Squirrel had + // a convience join() function -- It has split() + local colon = description.find(":"); + if (colon == null) + colon = description.len(); + local alias = description.slice(1, colon); + description = description.slice(colon + 1); + name = alias; + signature = null; + } + DocumentedFuncs[name] <- [signature, description]; + } + + function PrintHelp(pattern = "*") + { + local foundMatches = false; + foreach(name, doc in DocumentedFuncs) + { + if (pattern == "*" || name.tolower().find(pattern.tolower()) != null) + { + foundMatches = true; + printl("Function: " + name); + if (doc[0] == null) + { + // Is an aliased function + print("Signature: function " + name + "("); + foreach(k,v in this[name].getinfos()["parameters"]) + { + if (k == 0 && v == "this") continue; + if (k > 1) print(", "); + print(v); + } + printl(")"); + } + else + { + printl("Signature: " + doc[0]); + } + if (doc[1].len()) + printl("Description: " + doc[1]); + print("\n"); + } + } + + if (!foundMatches) + printl("Pattern " + pattern + " not found"); + } + + )script") != SCRIPT_DONE) + { + this->Shutdown(); + return false; + } + + return true; +} + +void SquirrelVM::Shutdown() +{ + if (vm_) + { + sq_release(vm_, &vectorClass_); + sq_release(vm_, ®expClass_); + + sq_close(vm_); + vm_ = nullptr; + } +} + +bool SquirrelVM::ConnectDebugger() +{ + // TODO: Debugger support + return false; +} + +void SquirrelVM::DisconnectDebugger() +{ + // TODO: Debugger support +} + +ScriptLanguage_t SquirrelVM::GetLanguage() +{ + return SL_SQUIRREL; +} + +const char* SquirrelVM::GetLanguageName() +{ + return "squirrel"; +} + +void SquirrelVM::AddSearchPath(const char* pszSearchPath) +{ + // TODO: Search path support +} + +bool SquirrelVM::Frame(float simTime) +{ + // TODO: Frame support + return false; +} + +ScriptStatus_t SquirrelVM::Run(const char* pszScript, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), "", SQTrue))) + { + return SCRIPT_ERROR; + } + + sq_pushroottable(vm_); + if (SQ_FAILED(sq_call(vm_, 1, SQFalse, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +HSCRIPT SquirrelVM::CompileScript(const char* pszScript, const char* pszId) +{ + SquirrelSafeCheck safeCheck(vm_); + + Assert(vm_); + if (pszId == nullptr) pszId = ""; + if (SQ_FAILED(sq_compilebuffer(vm_, pszScript, strlen(pszScript), pszId, SQTrue))) + { + return nullptr; + } + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + return (HSCRIPT)obj; +} + +void SquirrelVM::ReleaseScript(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; +} + +ScriptStatus_t SquirrelVM::Run(HSCRIPT hScript, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + auto result = sq_call(vm_, 1, false, true); + sq_pop(vm_, 1); + if (SQ_FAILED(result)) + { + return SCRIPT_ERROR; + } + return SCRIPT_DONE; +} + +ScriptStatus_t SquirrelVM::Run(HSCRIPT hScript, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + sq_pushroottable(vm_); + auto result = sq_call(vm_, 1, false, true); + sq_pop(vm_, 1); + if (SQ_FAILED(result)) + { + return SCRIPT_ERROR; + } + return SCRIPT_DONE; +} + +HSCRIPT SquirrelVM::CreateScope(const char* pszScope, HSCRIPT hParent) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_newtable(vm_); + + if (hParent) + { + HSQOBJECT* parent = (HSQOBJECT*)hParent; + Assert(hParent != INVALID_HSCRIPT); + sq_pushobject(vm_, *parent); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszScope, -1); + sq_push(vm_, -3); + sq_rawset(vm_, -3); + + sq_pushstring(vm_, "__vname", -1); + sq_pushstring(vm_, pszScope, -1); + sq_rawset(vm_, -4); + + if (SQ_FAILED(sq_setdelegate(vm_, -2))) + { + sq_pop(vm_, 2); + return nullptr; + } + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + return (HSCRIPT)obj; +} + +void SquirrelVM::ReleaseScope(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_pushobject(vm_, *obj); + + sq_getdelegate(vm_, -1); + + sq_pushstring(vm_, "__vname", -1); + sq_rawdeleteslot(vm_, -2, SQFalse); + + sq_pop(vm_, 2); + + sq_release(vm_, obj); + delete obj; +} + +HSCRIPT SquirrelVM::LookupFunction(const char* pszFunction, HSCRIPT hScope) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + sq_pushstring(vm_, _SC(pszFunction), -1); + + HSQOBJECT obj; + sq_resetobject(&obj); + + if (sq_get(vm_, -2) == SQ_OK) + { + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + sq_pop(vm_, 1); + } + sq_pop(vm_, 1); + + if (!sq_isclosure(obj)) + { + sq_release(vm_, &obj); + return nullptr; + } + + HSQOBJECT* pObj = new HSQOBJECT; + *pObj = obj; + return (HSCRIPT)pObj; +} + +void SquirrelVM::ReleaseFunction(HSCRIPT hScript) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hScript) return; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; +} + +ScriptStatus_t SquirrelVM::ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hFunction) + return SCRIPT_ERROR; + + if (hFunction == INVALID_HSCRIPT) + return SCRIPT_ERROR; + + HSQOBJECT* pFunc = (HSQOBJECT*)hFunction; + sq_pushobject(vm_, *pFunc); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + for (int i = 0; i < nArgs; ++i) + { + PushVariant(vm_, pArgs[i]); + } + + bool hasReturn = pReturn != nullptr; + + if (SQ_FAILED(sq_call(vm_, nArgs + 1, hasReturn, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + if (hasReturn) + { + if (!getVariant(vm_, -1, *pReturn)) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + +void SquirrelVM::RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) +{ + SquirrelSafeCheck safeCheck(vm_); + Assert(pScriptFunction); + + if (!pScriptFunction) + return; + + char typemask[64]; + if (!CreateParamCheck(*pScriptFunction, typemask)) + { + return; + } + + sq_pushroottable(vm_); + + sq_pushstring(vm_, pScriptFunction->m_desc.m_pszScriptName, -1); + + sq_pushuserpointer(vm_, pScriptFunction); + sq_newclosure(vm_, function_stub, 1); + + sq_setnativeclosurename(vm_, -1, pScriptFunction->m_desc.m_pszScriptName); + + sq_setparamscheck(vm_, pScriptFunction->m_desc.m_Parameters.Count() + 1, typemask); + bool isStatic = false; + sq_newslot(vm_, -3, isStatic); + + sq_pop(vm_, 1); + + RegisterDocumentation(vm_, pScriptFunction->m_desc); +} + +bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_pushroottable(vm_); + sq_pushstring(vm_, pClassDesc->m_pszScriptName, -1); + + // Check if class name is already taken + if (sq_get(vm_, -2) == SQ_OK) + { + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + if (!sq_isnull(obj)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 1); + } + + // Register base in case it doesn't exist + if (pClassDesc->m_pBaseDesc) + { + RegisterClass(pClassDesc->m_pBaseDesc); + + // Check if the base class can be + sq_pushstring(vm_, pClassDesc->m_pBaseDesc->m_pszScriptName, -1); + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + } + + if (SQ_FAILED(sq_newclass(vm_, pClassDesc->m_pBaseDesc ? SQTrue : SQFalse))) + { + sq_pop(vm_, 2 + (pClassDesc->m_pBaseDesc ? 1 : 0)); + return false; + } + + sq_settypetag(vm_, -1, pClassDesc); + + sq_setclassudsize(vm_, -1, sizeof(ClassInstanceData)); + + sq_pushstring(vm_, "constructor", -1); + sq_newclosure(vm_, constructor_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + sq_pushstring(vm_, "_tostring", -1); + sq_newclosure(vm_, tostring_stub, 0); + sq_newslot(vm_, -3, SQFalse); + + + for (int i = 0; i < pClassDesc->m_FunctionBindings.Count(); ++i) + { + auto& scriptFunction = pClassDesc->m_FunctionBindings[i]; + + char typemask[64]; + if (!CreateParamCheck(scriptFunction, typemask)) + { + Warning("Unable to create param check for %s.%s\n", + pClassDesc->m_pszClassname, scriptFunction.m_desc.m_pszFunction); + break; + } + + sq_pushstring(vm_, scriptFunction.m_desc.m_pszScriptName, -1); + + sq_pushuserpointer(vm_, &scriptFunction); + sq_newclosure(vm_, function_stub, 1); + + sq_setnativeclosurename(vm_, -1, scriptFunction.m_desc.m_pszScriptName); + + sq_setparamscheck(vm_, scriptFunction.m_desc.m_Parameters.Count() + 1, typemask); + bool isStatic = false; + sq_newslot(vm_, -3, isStatic); + + RegisterDocumentation(vm_, scriptFunction.m_desc, pClassDesc); + } + + sq_pushstring(vm_, pClassDesc->m_pszScriptName, -1); + sq_push(vm_, -2); + + if (SQ_FAILED(sq_newslot(vm_, -4, SQFalse))) + { + sq_pop(vm_, 4); + return false; + } + + sq_pop(vm_, 2); + + return true; +} + +HSCRIPT SquirrelVM::RegisterInstance(ScriptClassDesc_t* pDesc, void* pInstance) +{ + SquirrelSafeCheck safeCheck(vm_); + + this->RegisterClass(pDesc); + + sq_pushroottable(vm_); + sq_pushstring(vm_, pDesc->m_pszScriptName, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return nullptr; + } + + if (SQ_FAILED(sq_createinstance(vm_, -1))) + { + sq_pop(vm_, 2); + return nullptr; + } + + { + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) ClassInstanceData(pInstance, pDesc); + } + + sq_setreleasehook(vm_, -1, &destructor_stub_instance); + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 3); + + return (HSCRIPT)obj; +} + +void SquirrelVM::SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + sq_pushobject(vm_, *obj); + + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + + auto classInstanceData = (ClassInstanceData*)self; + + classInstanceData->instanceId = pszId; + + sq_poptop(vm_); +} + +void SquirrelVM::RemoveInstance(HSCRIPT hInstance) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + sq_pushobject(vm_, *obj); + + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + + ((ClassInstanceData*)self)->~ClassInstanceData(); + + sq_setinstanceup(vm_, -1, nullptr); + sq_setreleasehook(vm_, -1, nullptr); + sq_pop(vm_, 1); + + sq_release(vm_, obj); + delete obj; +} + +void* SquirrelVM::GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpectedType) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hInstance) return nullptr; + HSQOBJECT* obj = (HSQOBJECT*)hInstance; + + if (pExpectedType) + { + sq_pushroottable(vm_); + sq_pushstring(vm_, pExpectedType->m_pszScriptName, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return nullptr; + } + + sq_pushobject(vm_, *obj); + + if (!sq_instanceof(vm_)) + { + sq_pop(vm_, 3); + return nullptr; + } + + sq_pop(vm_, 3); + } + + sq_pushobject(vm_, *obj); + SQUserPointer self; + sq_getinstanceup(vm_, -1, &self, nullptr); + sq_pop(vm_, 1); + + auto classInstanceData = (ClassInstanceData*)self; + + if (!classInstanceData) + { + return nullptr; + } + + + return classInstanceData->instance; +} + +bool SquirrelVM::GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) +{ + static int keyIdx = 0; + // This gets used for script scope, still confused why it needs to be inside IScriptVM + // is it just to be a compatible name for CreateScope? + SquirrelSafeCheck safeCheck(vm_); + V_snprintf(pBuf, nBufSize, "%08X_%s", ++keyIdx, pszRoot); + return true; +} + +bool SquirrelVM::ValueExists(HSCRIPT hScope, const char* pszKey) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 2); + return true; +} + +bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const char* pszValue) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + sq_pushstring(vm_, pszValue, -1); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + return true; +} + +bool SquirrelVM::SetValue(HSCRIPT hScope, const char* pszKey, const ScriptVariant_t& value) +{ + SquirrelSafeCheck safeCheck(vm_); + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + PushVariant(vm_, value); + + sq_newslot(vm_, -3, SQFalse); + + sq_pop(vm_, 1); + return true; +} + +void SquirrelVM::CreateTable(ScriptVariant_t& Table) +{ + SquirrelSafeCheck safeCheck(vm_); + + sq_newtable(vm_); + + HSQOBJECT* obj = new HSQOBJECT; + sq_resetobject(obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + sq_pop(vm_, 1); + + Table = (HSCRIPT)obj; +} + +int SquirrelVM::GetNumTableEntries(HSCRIPT hScope) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (!hScope) + { + // TODO: This is called hScope but seems like just a table so + // lets not fallback to root table + return 0; + } + + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + + int count = sq_getsize(vm_, -1); + + sq_pop(vm_, 1); + + return count; +} + +int SquirrelVM::GetKeyValue(HSCRIPT hScope, int nIterator, ScriptVariant_t* pKey, ScriptVariant_t* pValue) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + Assert(hScope != INVALID_HSCRIPT); + HSQOBJECT* scope = (HSQOBJECT*)hScope; + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + if (nIterator == -1) + { + sq_pushnull(vm_); + } + else + { + sq_pushinteger(vm_, nIterator); + } + + SQInteger nextiter = -1; + + if (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + if (pKey) getVariant(vm_, -2, *pKey); + if (pValue) getVariant(vm_, -1, *pValue); + + sq_getinteger(vm_, -3, &nextiter); + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + return nextiter; +} + +bool SquirrelVM::GetValue(HSCRIPT hScope, const char* pszKey, ScriptVariant_t* pValue) +{ + SquirrelSafeCheck safeCheck(vm_); + + Assert(pValue); + if (!pValue) + { + return false; + } + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + + if (sq_get(vm_, -2) != SQ_OK) + { + sq_pop(vm_, 1); + return false; + } + + if (!getVariant(vm_, -1, *pValue)) + { + sq_pop(vm_, 2); + return false; + } + + sq_pop(vm_, 2); + + return true; +} + +void SquirrelVM::ReleaseValue(ScriptVariant_t& value) +{ + SquirrelSafeCheck safeCheck(vm_); + if (value.m_type == FIELD_HSCRIPT) + { + HSCRIPT hScript = value; + HSQOBJECT* obj = (HSQOBJECT*)hScript; + sq_release(vm_, obj); + delete obj; + } + else + { + value.Free(); + } + + // Let's prevent this being called again and giving some UB + value.m_type = FIELD_VOID; +} + +bool SquirrelVM::ClearValue(HSCRIPT hScope, const char* pszKey) +{ + SquirrelSafeCheck safeCheck(vm_); + + if (hScope) + { + HSQOBJECT* scope = (HSQOBJECT*)hScope; + Assert(hScope != INVALID_HSCRIPT); + sq_pushobject(vm_, *scope); + } + else + { + sq_pushroottable(vm_); + } + + sq_pushstring(vm_, pszKey, -1); + if (SQ_FAILED(sq_deleteslot(vm_, -2, SQFalse))) + { + sq_pop(vm_, 1); + return false; + } + + sq_pop(vm_, 1); + return true; +} + +enum ClassType +{ + VectorClassType = 0, + NativeClassType = 1, + ScriptClassType = 2 +}; + +SQInteger closure_write(SQUserPointer file, SQUserPointer p, SQInteger size) +{ + ((CUtlBuffer*)file)->Put(p, size); + return size; +} + +void SquirrelVM::WriteObject(CUtlBuffer* pBuffer, WriteStateMap& writeState, SQInteger idx) +{ + SquirrelSafeCheck safeCheck(vm_); + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, idx, &obj); + + switch (obj._type) + { + case OT_NULL: + { + pBuffer->PutInt(OT_NULL); + break; + } + case OT_INTEGER: + { + pBuffer->PutInt(OT_INTEGER); + pBuffer->PutInt64(sq_objtointeger(&obj)); + break; + } + case OT_FLOAT: + { + pBuffer->PutInt(OT_FLOAT); + pBuffer->PutFloat(sq_objtofloat(&obj)); + break; + } + case OT_BOOL: + { + pBuffer->PutInt(OT_BOOL); + pBuffer->PutChar(sq_objtobool(&obj)); + break; + } + case OT_STRING: + { + pBuffer->PutInt(OT_STRING); + const char* val = nullptr; + SQInteger size = 0; + sq_getstringandsize(vm_, idx, &val, &size); + pBuffer->PutInt(size); + pBuffer->Put(val, size); + break; + } + case OT_TABLE: + { + pBuffer->PutInt(OT_TABLE); + if (writeState.CheckCache(pBuffer, obj._unVal.pTable)) + { + break; + } + sq_getdelegate(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + int count = sq_getsize(vm_, idx); + sq_push(vm_, idx); + sq_pushnull(vm_); + pBuffer->PutInt(count); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); + break; + } + case OT_ARRAY: + { + pBuffer->PutInt(OT_ARRAY); + if (writeState.CheckCache(pBuffer, obj._unVal.pArray)) + { + break; + } + int count = sq_getsize(vm_, idx); + pBuffer->PutInt(count); + sq_push(vm_, idx); + sq_pushnull(vm_); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); + break; + } + case OT_CLOSURE: + { + pBuffer->PutInt(OT_CLOSURE); + if (writeState.CheckCache(pBuffer, obj._unVal.pClosure)) + { + break; + } + + SQInteger nparams = 0, nfreevars = 0; + sq_getclosureinfo(vm_, idx, &nparams, &nfreevars); + if (nfreevars == 0) + { + pBuffer->PutChar(0); + + sq_push(vm_, idx); + if (SQ_FAILED(sq_writeclosure(vm_, closure_write, pBuffer))) + { + Error("Failed to write closure"); + } + sq_pop(vm_, 1); + } + else + { + // Unfortunately we can't use sq_writeclosure because it doesn't work well with + // outer variables + + pBuffer->PutChar(1); + + if (!_closure(obj)->Save(vm_, pBuffer, closure_write)) + { + Error("Failed to write closure\n"); + } + + int noutervalues = _closure(obj)->_function->_noutervalues; + for (int i = 0; i < noutervalues; ++i) + { + sq_pushobject(vm_, _closure(obj)->_outervalues[i]); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + } + } + + if (_closure(obj)->_env) + { + sq_pushobject(vm_, _closure(obj)->_env->_obj); + } + else + { + sq_pushnull(vm_); + } + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + + break; + } + case OT_NATIVECLOSURE: + { + pBuffer->PutInt(OT_NATIVECLOSURE); + sq_getclosurename(vm_, idx); + + const char* name = nullptr; + sq_getstring(vm_, -1, &name); + pBuffer->PutString(name); + + sq_pop(vm_, 1); + break; + } + case OT_CLASS: + { + pBuffer->PutInt(OT_CLASS); + if (writeState.CheckCache(pBuffer, obj._unVal.pClass)) + { + break; + } + SQUserPointer typetag = nullptr; + sq_gettypetag(vm_, idx, &typetag); + if (typetag == TYPETAG_VECTOR) + { + pBuffer->PutInt(VectorClassType); + } + else if (typetag != nullptr) + { + // Seems so dangerous to treat typetag as ScriptClassDesc_t* + // however we don't really have an option without some sort of tagged + // pointer. + pBuffer->PutInt(NativeClassType); + pBuffer->PutString(((ScriptClassDesc_t*)typetag)->m_pszScriptName); + } + else + { + // HACK: We can't easily identify when the type is a builtin to exclude + // so we just check against the only class we need to deal with at the moment + // which is "regexp" + const char* builtinName = nullptr; + if (_class(obj) == _class(regexpClass_)) + { + builtinName = "regexp"; + } + + if (builtinName) + { + pBuffer->PutInt(NativeClassType); + pBuffer->PutString(builtinName); + break; + } + + pBuffer->PutInt(ScriptClassType); + + sq_getbase(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + sq_push(vm_, idx); + sq_pushnull(vm_); + sq_getattributes(vm_, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + sq_pushnull(vm_); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + pBuffer->PutChar(1); + // TODO: Member Attributes + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + { + // HACK: Meta-methods are not included in an iterator of OT_CLASS + SQObjectPtrVec& metamethods = *(_ss(vm_)->_metamethods); + for (int i = 0; i < MT_LAST; ++i) + { + if (sq_type(_class(obj)->_metamethods[i]) != OT_NULL) + { + pBuffer->PutChar(1); + sq_pushobject(vm_, metamethods[i]); + sq_pushobject(vm_, _class(obj)->_metamethods[i]); + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + } + } + } + + pBuffer->PutChar(0); + } + break; + } + case OT_INSTANCE: + { + pBuffer->PutInt(OT_INSTANCE); + if (writeState.CheckCache(pBuffer, obj._unVal.pInstance)) + { + break; + } + sq_getclass(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + + if (_instance(obj)->_class == _class(regexpClass_)) + { + sq_push(vm_, idx); + sq_pushstring(vm_, "pattern_", -1); + sq_rawget(vm_, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + break; + } + + { + // HACK: No way to get the default values part from accessing the class directly + SQUnsignedInteger nvalues = _instance(obj)->_class->_defaultvalues.size(); + for (SQUnsignedInteger n = 0; n < nvalues; n++) { + sq_pushobject(vm_, _instance(obj)->_values[n]); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + } + } + + SQUserPointer typetag; + sq_gettypetag(vm_, idx, &typetag); + + if (typetag == TYPETAG_VECTOR) + { + Vector* v = nullptr; + sq_getinstanceup(vm_, idx, (SQUserPointer*)&v, TYPETAG_VECTOR); + Assert(v); + pBuffer->PutFloat(v->x); + pBuffer->PutFloat(v->y); + pBuffer->PutFloat(v->z); + } + else if (typetag) + { + ClassInstanceData* pClassInstanceData; + sq_getinstanceup(vm_, idx, (SQUserPointer*)&pClassInstanceData, typetag); + + if (pClassInstanceData) + { + if (pClassInstanceData->desc->m_pszDescription[0] == SCRIPT_SINGLETON[0]) + { + // Do nothing, singleton should be created from just the class + } + else if (!pClassInstanceData->instanceId.IsEmpty()) + { + pBuffer->PutString(pClassInstanceData->instanceId); + } + else + { + Warning("SquirrelVM::WriteObject: Unable to find instanceID for object of type %s, unable to serialize\n", + pClassInstanceData->desc->m_pszClassname); + pBuffer->PutString(""); + } + } + else + { + pBuffer->PutString(""); + } + } + + break; + } + case OT_WEAKREF: + { + pBuffer->PutInt(OT_WEAKREF); + sq_getweakrefval(vm_, idx); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 1); + break; + } + case OT_FUNCPROTO: //internal usage only + { + pBuffer->PutInt(OT_FUNCPROTO); + + if (writeState.CheckCache(pBuffer, obj._unVal.pFunctionProto)) + { + break; + } + + _funcproto(obj)->Save(vm_, pBuffer, closure_write); + } + case OT_OUTER: //internal usage only + { + pBuffer->PutInt(OT_OUTER); + + if (writeState.CheckCache(pBuffer, obj._unVal.pOuter)) + { + break; + } + + sq_pushobject(vm_, *_outer(obj)->_valptr); + WriteObject(pBuffer, writeState, -1); + sq_poptop(vm_); + + break; + } + // case OT_USERDATA: + // case OT_GENERATOR: + // case OT_USERPOINTER: + // case OT_THREAD: + // + default: + Warning("SquirrelVM::WriteObject: Unexpected type %d", sq_gettype(vm_, idx)); + // Save a null instead + pBuffer->PutInt(OT_NULL); + } +} + +void SquirrelVM::WriteState(CUtlBuffer* pBuffer) +{ + SquirrelSafeCheck safeCheck(vm_); + + WriteStateMap writeState; + + sq_pushroottable(vm_); + + // Not really a check cache, but adds the root + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + writeState.CheckCache(pBuffer, _table(obj)); + + int count = sq_getsize(vm_, 1); + sq_pushnull(vm_); + pBuffer->PutInt(count); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + WriteObject(pBuffer, writeState, -2); + WriteObject(pBuffer, writeState, -1); + sq_pop(vm_, 2); + --count; + } + sq_pop(vm_, 2); + Assert(count == 0); +} + +SQInteger closure_read(SQUserPointer file, SQUserPointer buf, SQInteger size) +{ + CUtlBuffer* pBuffer = (CUtlBuffer*)file; + pBuffer->Get(buf, size); + return pBuffer->IsValid() ? size : -1; +} + +void SquirrelVM::ReadObject(CUtlBuffer* pBuffer, ReadStateMap& readState) +{ + SquirrelSafeCheck safeCheck(vm_, 1); + + int thisType = pBuffer->GetInt(); + + switch (thisType) + { + case OT_NULL: + { + sq_pushnull(vm_); + break; + } + case OT_INTEGER: + { + sq_pushinteger(vm_, pBuffer->GetInt64()); + break; + } + case OT_FLOAT: + { + sq_pushfloat(vm_, pBuffer->GetFloat()); + break; + } + case OT_BOOL: + { + sq_pushbool(vm_, pBuffer->GetChar()); + break; + } + case OT_STRING: + { + int size = pBuffer->GetInt(); + char* buffer = new char[size + 1]; + pBuffer->Get(buffer, size); + buffer[size] = 0; + sq_pushstring(vm_, buffer, size); + delete[] buffer; + break; + } + case OT_TABLE: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ReadObject(pBuffer, readState); + + int count = pBuffer->GetInt(); + sq_newtableex(vm_, count); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_push(vm_, -2); + sq_setdelegate(vm_, -2); + + sq_remove(vm_, -2); + + for (int i = 0; i < count; ++i) + { + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + sq_rawset(vm_, -3); + } + + break; + } + case OT_ARRAY: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + int count = pBuffer->GetInt(); + sq_newarray(vm_, count); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + for (int i = 0; i < count; ++i) + { + sq_pushinteger(vm_, i); + ReadObject(pBuffer, readState); + sq_rawset(vm_, -3); + } + break; + } + case OT_CLOSURE: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + if (pBuffer->GetChar() == 0) + { + if (SQ_FAILED(sq_readclosure(vm_, closure_read, pBuffer))) + { + Error("Failed to read closure\n"); + sq_pushnull(vm_); + break; + } + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + else + { + SQObjectPtr ret; + if (!SQClosure::Load(vm_, pBuffer, closure_read, ret)) + { + Error("Failed to read closure\n"); + sq_pushnull(vm_); + break; + } + + int noutervalues = _closure(ret)->_function->_noutervalues; + for (int i = 0; i < noutervalues; ++i) + { + ReadObject(pBuffer, readState); + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + _closure(ret)->_outervalues[i] = obj; + sq_poptop(vm_); + } + + *obj = ret; + sq_addref(vm_, obj); + sq_pushobject(vm_, *obj); + } + + ReadObject(pBuffer, readState); + HSQOBJECT env; + sq_resetobject(&env); + sq_getstackobj(vm_, -1, &env); + if (!sq_isnull(env)) + { + _closure(*obj)->_env = _refcounted(env)->GetWeakRef(sq_type(env)); + } + sq_poptop(vm_); + + break; + } + case OT_NATIVECLOSURE: + { + char closureName[128] = ""; + pBuffer->GetString(closureName, sizeof(closureName)); + + sq_pushroottable(vm_); + sq_pushstring(vm_, closureName, -1); + if (SQ_FAILED(sq_get(vm_, -2))) + { + Warning("SquirrelVM::ReadObject: Failed to find native closure\n"); + sq_pop(vm_, 1); + sq_pushnull(vm_); + } + sq_remove(vm_, -2); + + break; + } + case OT_CLASS: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ClassType classType = (ClassType)pBuffer->GetInt(); + + if (classType == VectorClassType) + { + sq_pushobject(vm_, vectorClass_); + } + else if (classType == NativeClassType) + { + char className[128] = ""; + pBuffer->GetString(className, sizeof(className)); + + sq_pushroottable(vm_); + sq_pushstring(vm_, className, -1); + if (SQ_FAILED(sq_get(vm_, -2))) + { + Warning("SquirrelVM::ReadObject: Failed to find native class: %s\n", className); + sq_pushnull(vm_); + } + sq_remove(vm_, -2); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + else if (classType == ScriptClassType) + { + ReadObject(pBuffer, readState); + bool hasBase = sq_gettype(vm_, -1) != OT_NULL; + if (!hasBase) + { + sq_poptop(vm_); + } + + sq_newclass(vm_, hasBase); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_pushnull(vm_); + ReadObject(pBuffer, readState); + sq_setattributes(vm_, -3); + sq_poptop(vm_); // Returns the old attributes + + while (pBuffer->GetChar()) + { + // TODO: Member Attributes + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + sq_newslot(vm_, -3, false); + } + } + else + { + Error("SquirrelVM::ReadObject: Unknown class type\n"); + sq_pushnull(vm_); + } + break; + } + case OT_INSTANCE: + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ReadObject(pBuffer, readState); + + HSQOBJECT klass; + sq_resetobject(&klass); + sq_getstackobj(vm_, -1, &klass); + if (_class(klass) == _class(regexpClass_)) + { + sq_pushnull(vm_); + ReadObject(pBuffer, readState); + sq_call(vm_, 2, SQTrue, SQFalse); + + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_remove(vm_, -2); + + break; + } + + SQUserPointer typetag; + sq_gettypetag(vm_, -1, &typetag); + + if (typetag && typetag != TYPETAG_VECTOR && + ((ScriptClassDesc_t*)typetag)->m_pszDescription[0] == SCRIPT_SINGLETON[0]) + { + sq_poptop(vm_); + + Assert(sq_isclass(klass)); + + // singleton, lets find an equivlent in the root + bool foundSingleton = false; + sq_pushroottable(vm_); + sq_pushnull(vm_); + HSQOBJECT singleton; + sq_resetobject(&singleton); + while (SQ_SUCCEEDED(sq_next(vm_, -2))) + { + sq_getstackobj(vm_, -1, &singleton); + if (sq_isinstance(singleton) && _instance(singleton)->_class == _class(klass)) + { + foundSingleton = true; + *obj = singleton; + sq_addref(vm_, obj); + sq_pop(vm_, 2); + break; + } + sq_pop(vm_, 2); + } + sq_pop(vm_, 2); + + if (!foundSingleton) + { + Warning("SquirrelVM::ReadObject: Failed to find singleton for %s\n", + ((ScriptClassDesc_t*)typetag)->m_pszScriptName); + } + + sq_pushobject(vm_, *obj); + break; + } + + sq_createinstance(vm_, -1); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + sq_remove(vm_, -2); + + { + // HACK: No way to get the default values part from accessing the class directly + SQUnsignedInteger nvalues = _instance(*obj)->_class->_defaultvalues.size(); + for (SQUnsignedInteger n = 0; n < nvalues; n++) { + ReadObject(pBuffer, readState); + HSQOBJECT val; + sq_resetobject(&val); + sq_getstackobj(vm_, -1, &val); + _instance(*obj)->_values[n] = val; + sq_pop(vm_, 1); + } + } + + if (typetag == TYPETAG_VECTOR) + { + float x = pBuffer->GetFloat(); + float y = pBuffer->GetFloat(); + float z = pBuffer->GetFloat(); + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) Vector(x, y, z); + } + else if (typetag) + { + ScriptClassDesc_t* pClassDesc = (ScriptClassDesc_t*)typetag; + + char instanceName[128] = ""; + pBuffer->GetString(instanceName, sizeof(instanceName)); + + HSQOBJECT* hinstance = new HSQOBJECT; + sq_resetobject(hinstance); + sq_getstackobj(vm_, -1, hinstance); + sq_addref(vm_, hinstance); + + if (*instanceName) + { + auto instance = pClassDesc->pHelper->BindOnRead((HSCRIPT)hinstance, nullptr, instanceName); + if (instance == nullptr) + { + sq_release(vm_, hinstance); + delete hinstance; + sq_poptop(vm_); + sq_pushnull(vm_); + break; + } + + { + SQUserPointer p; + sq_getinstanceup(vm_, -1, &p, 0); + new(p) ClassInstanceData(instance, pClassDesc, instanceName); + } + sq_setreleasehook(vm_, -1, &destructor_stub); + } + else + { + sq_setinstanceup(vm_, -1, nullptr); + } + } + + break; + } + case OT_WEAKREF: + { + ReadObject(pBuffer, readState); + sq_weakref(vm_, -1); + break; + } + case OT_FUNCPROTO: //internal usage only + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + SQObjectPtr ret; + if (!SQFunctionProto::Load(vm_, pBuffer, closure_read, ret)) + { + Error("Failed to deserialize OT_FUNCPROTO\n"); + sq_pushnull(vm_); + break; + } + + vm_->Push(ret); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + } + case OT_OUTER: //internal usage only + { + HSQOBJECT* obj = nullptr; + if (readState.CheckCache(pBuffer, &obj)) + { + sq_pushobject(vm_, *obj); + break; + } + + ReadObject(pBuffer, readState); + HSQOBJECT inner; + sq_resetobject(&inner); + sq_getstackobj(vm_, -1, &inner); + SQOuter* outer = SQOuter::Create(_ss(vm_), nullptr); + outer->_value = inner; + outer->_valptr = &(outer->_value); + sq_poptop(vm_); + vm_->Push(outer); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + break; + } + // case OT_USERDATA: + // case OT_GENERATOR: + // case OT_USERPOINTER: + // case OT_THREAD: + // + default: + Error("SquirrelVM::ReadObject: Unexpected type %d", thisType); + } +} + +void SquirrelVM::ReadState(CUtlBuffer* pBuffer) +{ + SquirrelSafeCheck safeCheck(vm_); + + ReadStateMap readState(vm_); + + sq_pushroottable(vm_); + + HSQOBJECT* obj = nullptr; + readState.CheckCache(pBuffer, &obj); + sq_getstackobj(vm_, -1, obj); + sq_addref(vm_, obj); + + int count = pBuffer->GetInt(); + + for (int i = 0; i < count; ++i) + { + ReadObject(pBuffer, readState); + ReadObject(pBuffer, readState); + + sq_rawset(vm_, -3); + } + + sq_pop(vm_, 1); +} + +void SquirrelVM::RemoveOrphanInstances() +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Is this the right thing to do here? It's not really removing orphan instances + sq_collectgarbage(vm_); +} + +void SquirrelVM::DumpState() +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Dump state +} + +void SquirrelVM::SetOutputCallback(ScriptOutputFunc_t pFunc) +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Support output callbacks +} + +void SquirrelVM::SetErrorCallback(ScriptErrorFunc_t pFunc) +{ + SquirrelSafeCheck safeCheck(vm_); + // TODO: Support error callbacks +} + +bool SquirrelVM::RaiseException(const char* pszExceptionText) +{ + SquirrelSafeCheck safeCheck(vm_); + sq_pushstring(vm_, pszExceptionText, -1); + sq_resetobject(&lastError_); + sq_getstackobj(vm_, -1, &lastError_); + sq_addref(vm_, &lastError_); + sq_pop(vm_, 1); + return true; +} + + +IScriptVM* makeSquirrelVM() +{ + return new SquirrelVM; +} \ No newline at end of file