initial commit
This commit is contained in:
807
add_on/scriptgrid/scriptgrid.cpp
Normal file
807
add_on/scriptgrid/scriptgrid.cpp
Normal file
@@ -0,0 +1,807 @@
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h> // sprintf
|
||||
|
||||
#include "scriptgrid.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// Set the default memory routines
|
||||
// Use the angelscript engine's memory routines by default
|
||||
static asALLOCFUNC_t userAlloc = asAllocMem;
|
||||
static asFREEFUNC_t userFree = asFreeMem;
|
||||
|
||||
// Allows the application to set which memory routines should be used by the array object
|
||||
void CScriptGrid::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc)
|
||||
{
|
||||
userAlloc = allocFunc;
|
||||
userFree = freeFunc;
|
||||
}
|
||||
|
||||
static void RegisterScriptGrid_Native(asIScriptEngine *engine);
|
||||
|
||||
struct SGridBuffer
|
||||
{
|
||||
asDWORD width;
|
||||
asDWORD height;
|
||||
asBYTE data[1];
|
||||
};
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti)
|
||||
{
|
||||
return CScriptGrid::Create(ti, 0, 0);
|
||||
}
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h)
|
||||
{
|
||||
// Allocate the memory
|
||||
void *mem = userAlloc(sizeof(CScriptGrid));
|
||||
if( mem == 0 )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the object
|
||||
CScriptGrid *a = new(mem) CScriptGrid(w, h, ti);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, void *initList)
|
||||
{
|
||||
// Allocate the memory
|
||||
void *mem = userAlloc(sizeof(CScriptGrid));
|
||||
if( mem == 0 )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the object
|
||||
CScriptGrid *a = new(mem) CScriptGrid(ti, initList);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h, void *defVal)
|
||||
{
|
||||
// Allocate the memory
|
||||
void *mem = userAlloc(sizeof(CScriptGrid));
|
||||
if( mem == 0 )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the object
|
||||
CScriptGrid *a = new(mem) CScriptGrid(w, h, defVal, ti);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// This optional callback is called when the template type is first used by the compiler.
|
||||
// It allows the application to validate if the template can be instantiated for the requested
|
||||
// subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
|
||||
// allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
|
||||
// i.e. no asOBJ_GC flag.
|
||||
static bool ScriptGridTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollect)
|
||||
{
|
||||
// Make sure the subtype can be instantiated with a default factory/constructor,
|
||||
// otherwise we won't be able to instantiate the elements.
|
||||
int typeId = ti->GetSubTypeId();
|
||||
if( typeId == asTYPEID_VOID )
|
||||
return false;
|
||||
if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
|
||||
{
|
||||
asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
|
||||
asDWORD flags = subtype->GetFlags();
|
||||
if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
|
||||
{
|
||||
// Verify that there is a default constructor
|
||||
bool found = false;
|
||||
for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
|
||||
{
|
||||
asEBehaviours beh;
|
||||
asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
|
||||
if( beh != asBEHAVE_CONSTRUCT ) continue;
|
||||
|
||||
if( func->GetParamCount() == 0 )
|
||||
{
|
||||
// Found the default constructor
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !found )
|
||||
{
|
||||
// There is no default constructor
|
||||
ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( (flags & asOBJ_REF) )
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
// If value assignment for ref type has been disabled then the array
|
||||
// can be created if the type has a default factory function
|
||||
if( !ti->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE) )
|
||||
{
|
||||
// Verify that there is a default factory
|
||||
for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
|
||||
{
|
||||
asIScriptFunction *func = subtype->GetFactoryByIndex(n);
|
||||
if( func->GetParamCount() == 0 )
|
||||
{
|
||||
// Found the default factory
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !found )
|
||||
{
|
||||
// No default factory
|
||||
ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the object type is not garbage collected then the array also doesn't need to be
|
||||
if( !(flags & asOBJ_GC) )
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
else if( !(typeId & asTYPEID_OBJHANDLE) )
|
||||
{
|
||||
// Arrays with primitives cannot form circular references,
|
||||
// thus there is no need to garbage collect them
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( typeId & asTYPEID_OBJHANDLE );
|
||||
|
||||
// It is not necessary to set the array as garbage collected for all handle types.
|
||||
// If it is possible to determine that the handle cannot refer to an object type
|
||||
// that can potentially form a circular reference with the array then it is not
|
||||
// necessary to make the array garbage collected.
|
||||
asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
|
||||
asDWORD flags = subtype->GetFlags();
|
||||
if( !(flags & asOBJ_GC) )
|
||||
{
|
||||
if( (flags & asOBJ_SCRIPT_OBJECT) )
|
||||
{
|
||||
// Even if a script class is by itself not garbage collected, it is possible
|
||||
// that classes that derive from it may be, so it is not possible to know
|
||||
// that no circular reference can occur.
|
||||
if( (flags & asOBJ_NOINHERIT) )
|
||||
{
|
||||
// A script class declared as final cannot be inherited from, thus
|
||||
// we can be certain that the object cannot be garbage collected.
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For application registered classes we assume the application knows
|
||||
// what it is doing and don't mark the array as garbage collected unless
|
||||
// the type is also garbage collected.
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The type is ok
|
||||
return true;
|
||||
}
|
||||
|
||||
// Registers the template array type
|
||||
void RegisterScriptGrid(asIScriptEngine *engine)
|
||||
{
|
||||
// TODO: Implement the generic calling convention
|
||||
RegisterScriptGrid_Native(engine);
|
||||
}
|
||||
|
||||
static void RegisterScriptGrid_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Register the grid type as a template
|
||||
r = engine->RegisterObjectType("grid<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
|
||||
|
||||
// Register a callback for validating the subtype before it is used
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptGridTemplateCallback), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Templates receive the object type as the first parameter. To the script writer this is hidden
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in, uint, uint)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in, uint, uint, const T &in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT, void *), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Register the factory that will be used for initialization lists
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_LIST_FACTORY, "grid<T>@ f(int&in type, int&in list) {repeat {repeat_same T}}", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, void*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// The memory management methods
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptGrid,AddRef), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptGrid,Release), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// The index operator returns the template subtype
|
||||
r = engine->RegisterObjectMethod("grid<T>", "T &opIndex(uint, uint)", asMETHODPR(CScriptGrid, At, (asUINT, asUINT), void*), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("grid<T>", "const T &opIndex(uint, uint) const", asMETHODPR(CScriptGrid, At, (asUINT, asUINT) const, const void*), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Other methods
|
||||
r = engine->RegisterObjectMethod("grid<T>", "void resize(uint width, uint height)", asMETHODPR(CScriptGrid, Resize, (asUINT, asUINT), void), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("grid<T>", "uint width() const", asMETHOD(CScriptGrid, GetWidth), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("grid<T>", "uint height() const", asMETHOD(CScriptGrid, GetHeight), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Register GC behaviours in case the array needs to be garbage collected
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptGrid, GetRefCount), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptGrid, SetFlag), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptGrid, GetFlag), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptGrid, EnumReferences), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptGrid, ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
|
||||
}
|
||||
|
||||
CScriptGrid::CScriptGrid(asITypeInfo *ti, void *buf)
|
||||
{
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
objType = ti;
|
||||
objType->AddRef();
|
||||
buffer = 0;
|
||||
subTypeId = objType->GetSubTypeId();
|
||||
|
||||
asIScriptEngine *engine = ti->GetEngine();
|
||||
|
||||
// Determine element size
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
elementSize = sizeof(asPWORD);
|
||||
else
|
||||
elementSize = engine->GetSizeOfPrimitiveType(subTypeId);
|
||||
|
||||
// Determine the initial size from the buffer
|
||||
asUINT height = *(asUINT*)buf;
|
||||
asUINT width = height ? *(asUINT*)((char*)(buf)+4) : 0;
|
||||
|
||||
// Make sure the grid size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
{
|
||||
// Don't continue with the initialization
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the height value at the start of the buffer
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the values of the grid elements from the buffer
|
||||
if( (ti->GetSubTypeId() & asTYPEID_MASK_OBJECT) == 0 )
|
||||
{
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Copy the values of the primitive type into the internal buffer
|
||||
for( asUINT y = 0; y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the line
|
||||
if( width > 0 )
|
||||
memcpy(At(0,y), buf, width*elementSize);
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*elementSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
else if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Copy the handles into the internal buffer
|
||||
for( asUINT y = 0; y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the line
|
||||
if( width > 0 )
|
||||
memcpy(At(0,y), buf, width*elementSize);
|
||||
|
||||
// With object handles it is safe to clear the memory in the received buffer
|
||||
// instead of increasing the ref count. It will save time both by avoiding the
|
||||
// call the increase ref, and also relieve the engine from having to release
|
||||
// its references too
|
||||
memset(buf, 0, width*elementSize);
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*elementSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
else if( ti->GetSubType()->GetFlags() & asOBJ_REF )
|
||||
{
|
||||
// Only allocate the buffer, but not the objects
|
||||
subTypeId |= asTYPEID_OBJHANDLE;
|
||||
CreateBuffer(&buffer, width, height);
|
||||
subTypeId &= ~asTYPEID_OBJHANDLE;
|
||||
|
||||
// Copy the handles into the internal buffer
|
||||
for( asUINT y = 0; y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the line
|
||||
if( width > 0 )
|
||||
memcpy(At(0,y), buf, width*elementSize);
|
||||
|
||||
// With object handles it is safe to clear the memory in the received buffer
|
||||
// instead of increasing the ref count. It will save time both by avoiding the
|
||||
// call the increase ref, and also relieve the engine from having to release
|
||||
// its references too
|
||||
memset(buf, 0, width*elementSize);
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*elementSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Optimize by calling the copy constructor of the object instead of
|
||||
// constructing with the default constructor and then assigning the value
|
||||
// TODO: With C++11 ideally we should be calling the move constructor, instead
|
||||
// of the copy constructor as the engine will just discard the objects in the
|
||||
// buffer afterwards.
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// For value types we need to call the opAssign for each individual object
|
||||
asITypeInfo *subType = ti->GetSubType();
|
||||
asUINT subTypeSize = subType->GetSize();
|
||||
for( asUINT y = 0;y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Call opAssign for each of the objects on the row
|
||||
for( asUINT x = 0; x < width; x++ )
|
||||
{
|
||||
void *obj = At(x,y);
|
||||
asBYTE *srcObj = (asBYTE*)(buf) + x*subTypeSize;
|
||||
engine->AssignScriptObject(obj, srcObj, subType);
|
||||
}
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*subTypeSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if( objType->GetFlags() & asOBJ_GC )
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
|
||||
}
|
||||
|
||||
CScriptGrid::CScriptGrid(asUINT width, asUINT height, asITypeInfo *ti)
|
||||
{
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
objType = ti;
|
||||
objType->AddRef();
|
||||
buffer = 0;
|
||||
subTypeId = objType->GetSubTypeId();
|
||||
|
||||
// Determine element size
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
elementSize = sizeof(asPWORD);
|
||||
else
|
||||
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
|
||||
|
||||
// Make sure the array size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
{
|
||||
// Don't continue with the initialization
|
||||
return;
|
||||
}
|
||||
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if( objType->GetFlags() & asOBJ_GC )
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
|
||||
}
|
||||
|
||||
void CScriptGrid::Resize(asUINT width, asUINT height)
|
||||
{
|
||||
// Make sure the size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
return;
|
||||
|
||||
// Create a new buffer
|
||||
SGridBuffer *tmpBuffer = 0;
|
||||
CreateBuffer(&tmpBuffer, width, height);
|
||||
if( tmpBuffer == 0 )
|
||||
return;
|
||||
|
||||
if( buffer )
|
||||
{
|
||||
// Copy the existing values to the new buffer
|
||||
asUINT w = width > buffer->width ? buffer->width : width;
|
||||
asUINT h = height > buffer->height ? buffer->height : height;
|
||||
for( asUINT y = 0; y < h; y++ )
|
||||
for( asUINT x = 0; x < w; x++ )
|
||||
SetValue(tmpBuffer, x, y, At(buffer, x, y));
|
||||
|
||||
// Replace the internal buffer
|
||||
DeleteBuffer(buffer);
|
||||
}
|
||||
|
||||
buffer = tmpBuffer;
|
||||
}
|
||||
|
||||
CScriptGrid::CScriptGrid(asUINT width, asUINT height, void *defVal, asITypeInfo *ti)
|
||||
{
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
objType = ti;
|
||||
objType->AddRef();
|
||||
buffer = 0;
|
||||
subTypeId = objType->GetSubTypeId();
|
||||
|
||||
// Determine element size
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
elementSize = sizeof(asPWORD);
|
||||
else
|
||||
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
|
||||
|
||||
// Make sure the array size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
{
|
||||
// Don't continue with the initialization
|
||||
return;
|
||||
}
|
||||
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if( objType->GetFlags() & asOBJ_GC )
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
|
||||
|
||||
// Initialize the elements with the default value
|
||||
for( asUINT y = 0; y < GetHeight(); y++ )
|
||||
for( asUINT x = 0; x < GetWidth(); x++ )
|
||||
SetValue(x, y, defVal);
|
||||
}
|
||||
|
||||
void CScriptGrid::SetValue(asUINT x, asUINT y, void *value)
|
||||
{
|
||||
SetValue(buffer, x, y, value);
|
||||
}
|
||||
|
||||
void CScriptGrid::SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value)
|
||||
{
|
||||
// At() will take care of the out-of-bounds checking, though
|
||||
// if called from the application then nothing will be done
|
||||
void *ptr = At(buf, x, y);
|
||||
if( ptr == 0 ) return;
|
||||
|
||||
if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
|
||||
objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType());
|
||||
else if( subTypeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
void *tmp = *(void**)ptr;
|
||||
*(void**)ptr = *(void**)value;
|
||||
objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
|
||||
if( tmp )
|
||||
objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
|
||||
}
|
||||
else if( subTypeId == asTYPEID_BOOL ||
|
||||
subTypeId == asTYPEID_INT8 ||
|
||||
subTypeId == asTYPEID_UINT8 )
|
||||
*(char*)ptr = *(char*)value;
|
||||
else if( subTypeId == asTYPEID_INT16 ||
|
||||
subTypeId == asTYPEID_UINT16 )
|
||||
*(short*)ptr = *(short*)value;
|
||||
else if( subTypeId == asTYPEID_INT32 ||
|
||||
subTypeId == asTYPEID_UINT32 ||
|
||||
subTypeId == asTYPEID_FLOAT ||
|
||||
subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
|
||||
*(int*)ptr = *(int*)value;
|
||||
else if( subTypeId == asTYPEID_INT64 ||
|
||||
subTypeId == asTYPEID_UINT64 ||
|
||||
subTypeId == asTYPEID_DOUBLE )
|
||||
*(double*)ptr = *(double*)value;
|
||||
}
|
||||
|
||||
CScriptGrid::~CScriptGrid()
|
||||
{
|
||||
if( buffer )
|
||||
{
|
||||
DeleteBuffer(buffer);
|
||||
buffer = 0;
|
||||
}
|
||||
if( objType ) objType->Release();
|
||||
}
|
||||
|
||||
asUINT CScriptGrid::GetWidth() const
|
||||
{
|
||||
if( buffer )
|
||||
return buffer->width;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
asUINT CScriptGrid::GetHeight() const
|
||||
{
|
||||
if( buffer )
|
||||
return buffer->height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// internal
|
||||
bool CScriptGrid::CheckMaxSize(asUINT width, asUINT height)
|
||||
{
|
||||
// This code makes sure the size of the buffer that is allocated
|
||||
// for the array doesn't overflow and becomes smaller than requested
|
||||
|
||||
asUINT maxSize = 0xFFFFFFFFul - sizeof(SGridBuffer) + 1;
|
||||
if( elementSize > 0 )
|
||||
maxSize /= elementSize;
|
||||
|
||||
asINT64 numElements = width * height;
|
||||
|
||||
if( (numElements >> 32) || numElements > maxSize )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Too large grid size");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// OK
|
||||
return true;
|
||||
}
|
||||
|
||||
asITypeInfo *CScriptGrid::GetGridObjectType() const
|
||||
{
|
||||
return objType;
|
||||
}
|
||||
|
||||
int CScriptGrid::GetGridTypeId() const
|
||||
{
|
||||
return objType->GetTypeId();
|
||||
}
|
||||
|
||||
int CScriptGrid::GetElementTypeId() const
|
||||
{
|
||||
return subTypeId;
|
||||
}
|
||||
|
||||
void *CScriptGrid::At(asUINT x, asUINT y)
|
||||
{
|
||||
return At(buffer, x, y);
|
||||
}
|
||||
|
||||
// Return a pointer to the array element. Returns 0 if the index is out of bounds
|
||||
void *CScriptGrid::At(SGridBuffer *buf, asUINT x, asUINT y)
|
||||
{
|
||||
if( buf == 0 || x >= buf->width || y >= buf->height )
|
||||
{
|
||||
// If this is called from a script we raise a script exception
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Index out of bounds");
|
||||
return 0;
|
||||
}
|
||||
|
||||
asUINT index = x+y*buf->width;
|
||||
if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
|
||||
return *(void**)(buf->data + elementSize*index);
|
||||
else
|
||||
return buf->data + elementSize*index;
|
||||
}
|
||||
const void *CScriptGrid::At(asUINT x, asUINT y) const
|
||||
{
|
||||
return const_cast<CScriptGrid*>(this)->At(const_cast<SGridBuffer*>(buffer), x, y);
|
||||
}
|
||||
|
||||
|
||||
// internal
|
||||
void CScriptGrid::CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h)
|
||||
{
|
||||
asUINT numElements = w * h;
|
||||
|
||||
*buf = reinterpret_cast<SGridBuffer*>(userAlloc(sizeof(SGridBuffer)-1+elementSize*numElements));
|
||||
|
||||
if( *buf )
|
||||
{
|
||||
(*buf)->width = w;
|
||||
(*buf)->height = h;
|
||||
Construct(*buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, out of memory
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
// internal
|
||||
void CScriptGrid::DeleteBuffer(SGridBuffer *buf)
|
||||
{
|
||||
assert( buf );
|
||||
|
||||
Destruct(buf);
|
||||
|
||||
// Free the buffer
|
||||
userFree(buf);
|
||||
}
|
||||
|
||||
// internal
|
||||
void CScriptGrid::Construct(SGridBuffer *buf)
|
||||
{
|
||||
assert( buf );
|
||||
|
||||
if( subTypeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// Set all object handles to null
|
||||
void *d = (void*)(buf->data);
|
||||
memset(d, 0, (buf->width*buf->height)*sizeof(void*));
|
||||
}
|
||||
else if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
|
||||
void **d = (void**)(buf->data);
|
||||
|
||||
asIScriptEngine *engine = objType->GetEngine();
|
||||
asITypeInfo *subType = objType->GetSubType();
|
||||
|
||||
for( ; d < max; d++ )
|
||||
{
|
||||
*d = (void*)engine->CreateScriptObject(subType);
|
||||
if( *d == 0 )
|
||||
{
|
||||
// Set the remaining entries to null so the destructor
|
||||
// won't attempt to destroy invalid objects later
|
||||
memset(d, 0, sizeof(void*)*(max-d));
|
||||
|
||||
// There is no need to set an exception on the context,
|
||||
// as CreateScriptObject has already done that
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// internal
|
||||
void CScriptGrid::Destruct(SGridBuffer *buf)
|
||||
{
|
||||
assert( buf );
|
||||
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
asIScriptEngine *engine = objType->GetEngine();
|
||||
|
||||
void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
|
||||
void **d = (void**)(buf->data);
|
||||
|
||||
for( ; d < max; d++ )
|
||||
{
|
||||
if( *d )
|
||||
engine->ReleaseScriptObject(*d, objType->GetSubType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptGrid::EnumReferences(asIScriptEngine *engine)
|
||||
{
|
||||
if( buffer == 0 ) return;
|
||||
|
||||
// If the grid is holding handles, then we need to notify the GC of them
|
||||
if (subTypeId & asTYPEID_MASK_OBJECT)
|
||||
{
|
||||
asUINT numElements = buffer->width * buffer->height;
|
||||
void **d = (void**)buffer->data;
|
||||
asITypeInfo *subType = engine->GetTypeInfoById(subTypeId);
|
||||
if ((subType->GetFlags() & asOBJ_REF))
|
||||
{
|
||||
// For reference types we need to notify the GC of each instance
|
||||
for (asUINT n = 0; n < numElements; n++)
|
||||
{
|
||||
if (d[n])
|
||||
engine->GCEnumCallback(d[n]);
|
||||
}
|
||||
}
|
||||
else if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
|
||||
{
|
||||
// For value types we need to forward the enum callback
|
||||
// to the object so it can decide what to do
|
||||
for (asUINT n = 0; n < numElements; n++)
|
||||
{
|
||||
if (d[n])
|
||||
engine->ForwardGCEnumReferences(d[n], subType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptGrid::ReleaseAllHandles(asIScriptEngine*)
|
||||
{
|
||||
if( buffer == 0 ) return;
|
||||
|
||||
DeleteBuffer(buffer);
|
||||
buffer = 0;
|
||||
}
|
||||
|
||||
void CScriptGrid::AddRef() const
|
||||
{
|
||||
// Clear the GC flag then increase the counter
|
||||
gcFlag = false;
|
||||
asAtomicInc(refCount);
|
||||
}
|
||||
|
||||
void CScriptGrid::Release() const
|
||||
{
|
||||
// Clearing the GC flag then descrease the counter
|
||||
gcFlag = false;
|
||||
if( asAtomicDec(refCount) == 0 )
|
||||
{
|
||||
// When reaching 0 no more references to this instance
|
||||
// exists and the object should be destroyed
|
||||
this->~CScriptGrid();
|
||||
userFree(const_cast<CScriptGrid*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
int CScriptGrid::GetRefCount()
|
||||
{
|
||||
return refCount;
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptGrid::SetFlag()
|
||||
{
|
||||
gcFlag = true;
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
bool CScriptGrid::GetFlag()
|
||||
{
|
||||
return gcFlag;
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
82
add_on/scriptgrid/scriptgrid.h
Normal file
82
add_on/scriptgrid/scriptgrid.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef SCRIPTGRID_H
|
||||
#define SCRIPTGRID_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
struct SGridBuffer;
|
||||
|
||||
class CScriptGrid
|
||||
{
|
||||
public:
|
||||
// Set the memory functions that should be used by all CScriptGrids
|
||||
static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc);
|
||||
|
||||
// Factory functions
|
||||
static CScriptGrid *Create(asITypeInfo *ot);
|
||||
static CScriptGrid *Create(asITypeInfo *ot, asUINT width, asUINT height);
|
||||
static CScriptGrid *Create(asITypeInfo *ot, asUINT width, asUINT height, void *defaultValue);
|
||||
static CScriptGrid *Create(asITypeInfo *ot, void *listBuffer);
|
||||
|
||||
// Memory management
|
||||
void AddRef() const;
|
||||
void Release() const;
|
||||
|
||||
// Type information
|
||||
asITypeInfo *GetGridObjectType() const;
|
||||
int GetGridTypeId() const;
|
||||
int GetElementTypeId() const;
|
||||
|
||||
// Size
|
||||
asUINT GetWidth() const;
|
||||
asUINT GetHeight() const;
|
||||
void Resize(asUINT width, asUINT height);
|
||||
|
||||
// Get a pointer to an element. Returns 0 if out of bounds
|
||||
void *At(asUINT x, asUINT y);
|
||||
const void *At(asUINT x, asUINT y) const;
|
||||
|
||||
// Set value of an element
|
||||
// Remember, if the grid holds handles the value parameter should be the
|
||||
// address of the handle. The refCount of the object will also be incremented
|
||||
void SetValue(asUINT x, asUINT y, void *value);
|
||||
|
||||
// GC methods
|
||||
int GetRefCount();
|
||||
void SetFlag();
|
||||
bool GetFlag();
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
void ReleaseAllHandles(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
mutable int refCount;
|
||||
mutable bool gcFlag;
|
||||
asITypeInfo *objType;
|
||||
SGridBuffer *buffer;
|
||||
int elementSize;
|
||||
int subTypeId;
|
||||
|
||||
// Constructors
|
||||
CScriptGrid(asITypeInfo *ot, void *initBuf); // Called from script when initialized with list
|
||||
CScriptGrid(asUINT w, asUINT h, asITypeInfo *ot);
|
||||
CScriptGrid(asUINT w, asUINT h, void *defVal, asITypeInfo *ot);
|
||||
virtual ~CScriptGrid();
|
||||
|
||||
bool CheckMaxSize(asUINT x, asUINT y);
|
||||
void CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h);
|
||||
void DeleteBuffer(SGridBuffer *buf);
|
||||
void Construct(SGridBuffer *buf);
|
||||
void Destruct(SGridBuffer *buf);
|
||||
void SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value);
|
||||
void *At(SGridBuffer *buf, asUINT x, asUINT y);
|
||||
};
|
||||
|
||||
void RegisterScriptGrid(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user