diff --git a/Engine/ac/dynobj/cc_dynamicarray.cpp b/Engine/ac/dynobj/cc_dynamicarray.cpp index 9d36360ff3..e379fd6f84 100644 --- a/Engine/ac/dynobj/cc_dynamicarray.cpp +++ b/Engine/ac/dynobj/cc_dynamicarray.cpp @@ -76,16 +76,18 @@ void CCDynamicArray::Unserialize(int index, Stream *in, size_t data_sz) ccRegisterUnserializedObject(index, &new_arr[MemHeaderSz], this); } -/* static */ DynObjectRef CCDynamicArray::Create(int numElements, int elementSize, bool isManagedType) +/* static */ DynObjectRef CCDynamicArray::Create(uint32_t elem_count, uint32_t elem_size, bool is_managed) { - assert(numElements >= 0); - if (numElements < 0) + assert(elem_count <= INT32_MAX); + assert(!is_managed || elem_size == sizeof(int32_t)); + if (elem_count > INT32_MAX || (is_managed && elem_size != sizeof(int32_t))) return {}; - uint8_t *new_arr = new uint8_t[numElements * elementSize + MemHeaderSz]; - memset(new_arr, 0, numElements * elementSize + MemHeaderSz); + + uint8_t *new_arr = new uint8_t[elem_count * elem_size + MemHeaderSz]; + memset(new_arr, 0, elem_count * elem_size + MemHeaderSz); Header &hdr = reinterpret_cast(*new_arr); - hdr.ElemCount = numElements | (ARRAY_MANAGED_TYPE_FLAG * isManagedType); - hdr.TotalSize = elementSize * numElements; + hdr.ElemCount = elem_count | (ARRAY_MANAGED_TYPE_FLAG * is_managed); + hdr.TotalSize = elem_size * elem_count; void *obj_ptr = &new_arr[MemHeaderSz]; int32_t handle = ccRegisterManagedObject(obj_ptr, &globalDynamicArray); if (handle == 0) diff --git a/Engine/ac/dynobj/cc_dynamicarray.h b/Engine/ac/dynobj/cc_dynamicarray.h index 956fe58502..a84038541a 100644 --- a/Engine/ac/dynobj/cc_dynamicarray.h +++ b/Engine/ac/dynobj/cc_dynamicarray.h @@ -47,7 +47,7 @@ struct CCDynamicArray final : AGSCCDynamicObject } // Create managed array object and return a pointer to the beginning of a buffer - static DynObjectRef Create(int numElements, int elementSize, bool isManagedType); + static DynObjectRef Create(uint32_t elem_count, uint32_t elem_size, bool is_managed); // return the type name of the object const char *GetType() override; diff --git a/Engine/plugin/agsplugin.cpp b/Engine/plugin/agsplugin.cpp index 6ca6afc423..c64159d82f 100644 --- a/Engine/plugin/agsplugin.cpp +++ b/Engine/plugin/agsplugin.cpp @@ -37,6 +37,7 @@ #include "ac/sys_events.h" #include "ac/view.h" #include "ac/dynobj/dynobj_manager.h" +#include "ac/dynobj/cc_dynamicarray.h" #include "ac/dynobj/scriptstring.h" #include "ac/dynobj/scriptsystem.h" #include "debug/debug_log.h" @@ -98,7 +99,7 @@ extern RoomStatus *croom; // **************** PLUGIN IMPLEMENTATION **************** -const int PLUGIN_API_VERSION = 29; +const int PLUGIN_API_VERSION = 30; struct EnginePlugin { EnginePlugin() { @@ -717,7 +718,7 @@ void* IAGSEngine::GetManagedObjectAddressByKey(int key) { const char* IAGSEngine::CreateScriptString(const char *fromText) { const char *string = CreateNewScriptString(fromText); - // Should be still standard dynamic object, because not managed by plugin + // Should be standard dynamic object, because not managed by plugin ccInstance::SetPluginReturnValue(RuntimeScriptValue().SetScriptObject((void*)string, &myScriptStringImpl)); return string; } @@ -848,6 +849,35 @@ void IAGSEngine::Log(int level, const char *fmt, ...) va_end(argptr); } +void *IAGSEngine::CreateDynamicArray(size_t elem_count, size_t elem_size, bool is_managed_type) +{ + if (elem_count > INT32_MAX || elem_size > INT32_MAX || (static_cast(elem_count) * elem_size) > UINT32_MAX) + { + debug_script_warn("IAGSEngine::CreateDynamicArray: requested array size exceeds the supported limit"); + return nullptr; + } + if (is_managed_type && elem_size != sizeof(int32_t)) + { + debug_script_warn("IAGSEngine::CreateDynamicArray: managed handles must have elem_size = 4, requested %zu instead", elem_size); + return nullptr; + } + + auto obj_ref = CCDynamicArray::Create(static_cast(elem_count), static_cast(elem_size), is_managed_type); + // Should be standard dynamic object, because not managed by plugin + ccInstance::SetPluginReturnValue(RuntimeScriptValue().SetScriptObject(obj_ref.Obj, &globalDynamicArray)); + return obj_ref.Obj; +} + +size_t IAGSEngine::GetDynamicArrayLength(const void *arr) +{ + return arr ? CCDynamicArray::GetHeader(arr).ElemCount : 0u; +} + +size_t IAGSEngine::GetDynamicArraySize(const void *arr) +{ + return arr ? CCDynamicArray::GetHeader(arr).TotalSize : 0u; +} + // *********** General plugin implementation ********** void pl_stop_plugins() { diff --git a/Engine/plugin/agsplugin.h b/Engine/plugin/agsplugin.h index 5454aa0371..b717e28f87 100644 --- a/Engine/plugin/agsplugin.h +++ b/Engine/plugin/agsplugin.h @@ -665,6 +665,27 @@ class IAGSEngine { // *** BELOW ARE INTERFACE VERSION 29 AND ABOVE ONLY // Print message to the engine's log, under one of the log levels AGSLOG_LEVEL_*. AGSIFUNC(void) Log(int level, const char *fmt, ...); + + // *** BELOW ARE INTERFACE VERSION 30 AND ABOVE ONLY + // Create a new dynamic array, allocating space for the given number of elements + // of the given size. Optionally instructs to create an array for managed handles, + // in which case the element size must be sizeof(int32). + // IMPORTANT: you MUST correctly tell if this is going to be an array of handles, because + // otherwise engine won't know to release their references, which may lead to memory leaks. + // IMPORTANT: when writing handles into this array, you MUST inc ref count for each one + // of them (see IncrementManagedObjectRefCount), otherwise these objects may get disposed + // before the array itself, making these handles invalid! + // Dynamic arrays have their meta data allocated prior to array of elements; + // this function returns a pointer to the element array, which you may write to. + // You may return this pointer from the registered plugin's function just like any other + // managed object pointer. + AGSIFUNC(void*) CreateDynamicArray(size_t elem_count, size_t elem_size, bool is_managed_type); + // Retrieves dynamic array's length (number of elements). + // You should pass a dynamic array object either received from the engine in your registered + // script function, or created by you with CreateDynamicArray(). + AGSIFUNC(size_t) GetDynamicArrayLength(const void *arr); + // Retrieves dynamic array's size (total capacity in bytes). + AGSIFUNC(size_t) GetDynamicArraySize(const void *arr); }; diff --git a/Engine/script/cc_instance.cpp b/Engine/script/cc_instance.cpp index 02c0b002b3..adecf979c1 100644 --- a/Engine/script/cc_instance.cpp +++ b/Engine/script/cc_instance.cpp @@ -1432,25 +1432,25 @@ ccInstError ccInstance::Run(int32_t curpc) case SCMD_NEWARRAY: { auto ®1 = _registers[codeOp.Arg1i()]; - const auto arg_elsize = codeOp.Arg2i(); - const auto arg_managed = codeOp.Arg3().GetAsBool(); - int numElements = reg1.IValue; - if (numElements < 0) + const int arg_elnum = reg1.IValue; + const uint32_t arg_elsize = static_cast(codeOp.Arg2i()); + const bool arg_managed = codeOp.Arg3().GetAsBool(); + if (arg_elnum < 0) { - cc_error("Invalid size for dynamic array; requested: %d, range: 0..%d", numElements, INT32_MAX); + cc_error("Invalid size for dynamic array; requested: %d, range: 0..%d", arg_elnum, INT32_MAX); return kInstErr_Generic; } - DynObjectRef ref = CCDynamicArray::Create(numElements, arg_elsize, arg_managed); + DynObjectRef ref = CCDynamicArray::Create(static_cast(arg_elnum), arg_elsize, arg_managed); reg1.SetScriptObject(ref.Obj, &globalDynamicArray); break; } case SCMD_NEWUSEROBJECT: { auto ®1 = _registers[codeOp.Arg1i()]; - const auto arg_size = codeOp.Arg2i(); - if (arg_size < 0) + const uint32_t arg_size = static_cast(codeOp.Arg2i()); + if (arg_size > INT32_MAX) { - cc_error("Invalid size for user object; requested: %d (or %d), range: 0..%d", arg_size, arg_size, INT_MAX); + cc_error("Invalid size for user object; requested: %u, range: 0..%d", arg_size, INT32_MAX); return kInstErr_Generic; } DynObjectRef ref = ScriptUserObject::Create(arg_size);