using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; // ReSharper disable InconsistentNaming namespace Godot.NativeInterop { // We want to use full name qualifiers here even if redundant for clarity [SuppressMessage("ReSharper", "RedundantNameQualifier")] public static class Marshaling { public static Variant.Type managed_to_variant_type(Type type, out bool r_nil_is_variant) { r_nil_is_variant = false; switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: return Variant.Type.Bool; case TypeCode.Char: return Variant.Type.Int; case TypeCode.SByte: return Variant.Type.Int; case TypeCode.Int16: return Variant.Type.Int; case TypeCode.Int32: return Variant.Type.Int; case TypeCode.Int64: return Variant.Type.Int; case TypeCode.Byte: return Variant.Type.Int; case TypeCode.UInt16: return Variant.Type.Int; case TypeCode.UInt32: return Variant.Type.Int; case TypeCode.UInt64: return Variant.Type.Int; case TypeCode.Single: return Variant.Type.Float; case TypeCode.Double: return Variant.Type.Float; case TypeCode.String: return Variant.Type.String; default: { if (type == typeof(Vector2)) return Variant.Type.Vector2; if (type == typeof(Vector2i)) return Variant.Type.Vector2i; if (type == typeof(Rect2)) return Variant.Type.Rect2; if (type == typeof(Rect2i)) return Variant.Type.Rect2i; if (type == typeof(Transform2D)) return Variant.Type.Transform2d; if (type == typeof(Vector3)) return Variant.Type.Vector3; if (type == typeof(Vector3i)) return Variant.Type.Vector3i; if (type == typeof(Basis)) return Variant.Type.Basis; if (type == typeof(Quaternion)) return Variant.Type.Quaternion; if (type == typeof(Transform3D)) return Variant.Type.Transform3d; if (type == typeof(AABB)) return Variant.Type.Aabb; if (type == typeof(Color)) return Variant.Type.Color; if (type == typeof(Plane)) return Variant.Type.Plane; if (type == typeof(Callable)) return Variant.Type.Callable; if (type == typeof(SignalInfo)) return Variant.Type.Signal; if (type.IsEnum) return Variant.Type.Int; if (type.IsArray || type.IsSZArray) { if (type == typeof(Byte[])) return Variant.Type.RawArray; if (type == typeof(Int32[])) return Variant.Type.Int32Array; if (type == typeof(Int64[])) return Variant.Type.Int64Array; if (type == typeof(float[])) return Variant.Type.Float32Array; if (type == typeof(double[])) return Variant.Type.Float64Array; if (type == typeof(string[])) return Variant.Type.StringArray; if (type == typeof(Vector2[])) return Variant.Type.Vector2Array; if (type == typeof(Vector3[])) return Variant.Type.Vector3Array; if (type == typeof(Color[])) return Variant.Type.ColorArray; if (typeof(Godot.Object[]).IsAssignableFrom(type)) return Variant.Type.Array; if (type == typeof(object[])) return Variant.Type.Array; } else if (type.IsGenericType) { var genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) return Variant.Type.Dictionary; if (genericTypeDefinition == typeof(Collections.Array<>)) return Variant.Type.Array; if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) return Variant.Type.Dictionary; if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) return Variant.Type.Array; if (genericTypeDefinition == typeof(IDictionary<,>)) return Variant.Type.Dictionary; if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) return Variant.Type.Array; } else if (type == typeof(object)) { r_nil_is_variant = true; return Variant.Type.Nil; } else { if (typeof(Godot.Object).IsAssignableFrom(type)) return Variant.Type.Object; if (typeof(StringName) == type) return Variant.Type.StringName; if (typeof(NodePath) == type) return Variant.Type.NodePath; if (typeof(RID) == type) return Variant.Type.Rid; if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) return Variant.Type.Dictionary; if (typeof(Collections.Array) == type || typeof(System.Collections.ICollection) == type || typeof(System.Collections.IEnumerable) == type) { return Variant.Type.Array; } } break; } } // Unknown return Variant.Type.Nil; } public static bool try_get_array_element_type(Type p_array_type, out Type r_elem_type) { if (p_array_type.IsArray || p_array_type.IsSZArray) { r_elem_type = p_array_type.GetElementType(); return true; } else if (p_array_type.IsGenericType) { var genericTypeDefinition = p_array_type.GetGenericTypeDefinition(); if (typeof(Collections.Array) == genericTypeDefinition || typeof(System.Collections.Generic.List<>) == genericTypeDefinition || typeof(System.Collections.ICollection) == genericTypeDefinition || typeof(System.Collections.IEnumerable) == genericTypeDefinition) { r_elem_type = p_array_type.GetGenericArguments()[0]; return true; } } r_elem_type = null; return false; } /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ public static godot_variant mono_object_to_variant(object p_obj) { return mono_object_to_variant_impl(p_obj); } public static godot_variant mono_object_to_variant_no_err(object p_obj) { return mono_object_to_variant_impl(p_obj); } private static unsafe godot_variant mono_object_to_variant_impl(object p_obj, bool p_fail_with_err = true) { if (p_obj == null) return new godot_variant(); switch (p_obj) { case bool @bool: return VariantUtils.CreateFromBool(@bool); case char @char: return VariantUtils.CreateFromInt(@char); case SByte @int8: return VariantUtils.CreateFromInt(@int8); case Int16 @int16: return VariantUtils.CreateFromInt(@int16); case Int32 @int32: return VariantUtils.CreateFromInt(@int32); case Int64 @int64: return VariantUtils.CreateFromInt(@int64); case Byte @uint8: return VariantUtils.CreateFromInt(@uint8); case UInt16 @uint16: return VariantUtils.CreateFromInt(@uint16); case UInt32 @uint32: return VariantUtils.CreateFromInt(@uint32); case UInt64 @uint64: return VariantUtils.CreateFromInt(@uint64); case float @float: return VariantUtils.CreateFromFloat(@float); case double @double: return VariantUtils.CreateFromFloat(@double); case Vector2 @vector2: return VariantUtils.CreateFromVector2(@vector2); case Vector2i @vector2i: return VariantUtils.CreateFromVector2i(@vector2i); case Rect2 @rect2: return VariantUtils.CreateFromRect2(@rect2); case Rect2i @rect2i: return VariantUtils.CreateFromRect2i(@rect2i); case Transform2D @transform2D: return VariantUtils.CreateFromTransform2D(@transform2D); case Vector3 @vector3: return VariantUtils.CreateFromVector3(@vector3); case Vector3i @vector3i: return VariantUtils.CreateFromVector3i(@vector3i); case Basis @basis: return VariantUtils.CreateFromBasis(@basis); case Quaternion @quaternion: return VariantUtils.CreateFromQuaternion(@quaternion); case Transform3D @transform3d: return VariantUtils.CreateFromTransform3D(@transform3d); case AABB @aabb: return VariantUtils.CreateFromAABB(@aabb); case Color @color: return VariantUtils.CreateFromColor(@color); case Plane @plane: return VariantUtils.CreateFromPlane(@plane); case Callable @callable: return VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue( ConvertCallableToNative(ref @callable)); case SignalInfo @signalInfo: return VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue( ConvertSignalToNative(ref @signalInfo)); case Enum @enum: return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); case string @string: { return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue( mono_string_to_godot(@string)); } case Byte[] byteArray: { using godot_packed_byte_array array = mono_array_to_PackedByteArray(byteArray); return VariantUtils.CreateFromPackedByteArray(&array); } case Int32[] int32Array: { using godot_packed_int32_array array = mono_array_to_PackedInt32Array(int32Array); return VariantUtils.CreateFromPackedInt32Array(&array); } case Int64[] int64Array: { using godot_packed_int64_array array = mono_array_to_PackedInt64Array(int64Array); return VariantUtils.CreateFromPackedInt64Array(&array); } case float[] floatArray: { using godot_packed_float32_array array = mono_array_to_PackedFloat32Array(floatArray); return VariantUtils.CreateFromPackedFloat32Array(&array); } case double[] doubleArray: { using godot_packed_float64_array array = mono_array_to_PackedFloat64Array(doubleArray); return VariantUtils.CreateFromPackedFloat64Array(&array); } case string[] stringArray: { using godot_packed_string_array array = mono_array_to_PackedStringArray(stringArray); return VariantUtils.CreateFromPackedStringArray(&array); } case Vector2[] vector2Array: { using godot_packed_vector2_array array = mono_array_to_PackedVector2Array(vector2Array); return VariantUtils.CreateFromPackedVector2Array(&array); } case Vector3[] vector3Array: { using godot_packed_vector3_array array = mono_array_to_PackedVector3Array(vector3Array); return VariantUtils.CreateFromPackedVector3Array(&array); } case Color[] colorArray: { using godot_packed_color_array array = mono_array_to_PackedColorArray(colorArray); return VariantUtils.CreateFromPackedColorArray(&array); } case Godot.Object[] godotObjectArray: { // ReSharper disable once CoVariantArrayConversion using godot_array array = mono_array_to_Array(godotObjectArray); return VariantUtils.CreateFromArray(&array); } case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[] { // The pattern match for `object[]` catches arrays on any reference type, // so we need to check the actual type to make sure it's truly `object[]`. if (objectArray.GetType() == typeof(object[])) { using godot_array array = mono_array_to_Array(objectArray); return VariantUtils.CreateFromArray(&array); } if (p_fail_with_err) { GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant."); return new godot_variant(); } else { return new godot_variant(); } } case Godot.Object godotObject: return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance); case StringName stringName: return VariantUtils.CreateFromStringName(ref stringName.NativeValue); case NodePath nodePath: return VariantUtils.CreateFromNodePath(ref nodePath.NativeValue); case RID rid: return VariantUtils.CreateFromRID(rid); case Collections.Dictionary godotDictionary: return VariantUtils.CreateFromDictionary(godotDictionary.NativeValue); case Collections.Array godotArray: return VariantUtils.CreateFromArray(godotArray.NativeValue); case Collections.IGenericGodotDictionary genericGodotDictionary: { var godotDict = genericGodotDictionary.UnderlyingDictionary; if (godotDict == null) return new godot_variant(); return VariantUtils.CreateFromDictionary(godotDict.NativeValue); } case Collections.IGenericGodotArray genericGodotArray: { var godotArray = genericGodotArray.UnderlyingArray; if (godotArray == null) return new godot_variant(); return VariantUtils.CreateFromArray(godotArray.NativeValue); } default: { var type = p_obj.GetType(); if (type.IsGenericType) { var genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) { // TODO: Validate key and value types are compatible with Variant var godotDict = new Collections.Dictionary(); foreach (KeyValuePair entry in (IDictionary)p_obj) godotDict.Add(entry.Key, entry.Value); return VariantUtils.CreateFromDictionary(godotDict.NativeValue); } if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { // TODO: Validate element type is compatible with Variant var nativeGodotArray = mono_array_to_Array((IList)p_obj); return VariantUtils.CreateFromArray(&nativeGodotArray); } } break; } } if (p_fail_with_err) { GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + p_obj.GetType().FullName + "."); return new godot_variant(); } else { return new godot_variant(); } } public static unsafe string variant_to_mono_string(godot_variant* p_var) { switch ((*p_var)._type) { case Variant.Type.Nil: return null; // Otherwise, Variant -> String would return the string "Null" case Variant.Type.String: { // We avoid the internal call if the stored type is the same we want. return mono_string_from_godot((*p_var)._data._m_string); } default: { using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var); return mono_string_from_godot(godotString); } } } public static unsafe object variant_to_mono_object_of_type(godot_variant* p_var, Type type) { // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant. switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: return VariantUtils.ConvertToBool(p_var); case TypeCode.Char: return VariantUtils.ConvertToChar(p_var); case TypeCode.SByte: return VariantUtils.ConvertToInt8(p_var); case TypeCode.Int16: return VariantUtils.ConvertToInt16(p_var); case TypeCode.Int32: return VariantUtils.ConvertToInt32(p_var); case TypeCode.Int64: return VariantUtils.ConvertToInt64(p_var); case TypeCode.Byte: return VariantUtils.ConvertToUInt8(p_var); case TypeCode.UInt16: return VariantUtils.ConvertToUInt16(p_var); case TypeCode.UInt32: return VariantUtils.ConvertToUInt32(p_var); case TypeCode.UInt64: return VariantUtils.ConvertToUInt64(p_var); case TypeCode.Single: return VariantUtils.ConvertToFloat32(p_var); case TypeCode.Double: return VariantUtils.ConvertToFloat64(p_var); case TypeCode.String: return variant_to_mono_string(p_var); default: { if (type == typeof(Vector2)) return VariantUtils.ConvertToVector2(p_var); if (type == typeof(Vector2i)) return VariantUtils.ConvertToVector2i(p_var); if (type == typeof(Rect2)) return VariantUtils.ConvertToRect2(p_var); if (type == typeof(Rect2i)) return VariantUtils.ConvertToRect2i(p_var); if (type == typeof(Transform2D)) return VariantUtils.ConvertToTransform2D(p_var); if (type == typeof(Vector3)) return VariantUtils.ConvertToVector3(p_var); if (type == typeof(Vector3i)) return VariantUtils.ConvertToVector3i(p_var); if (type == typeof(Basis)) return VariantUtils.ConvertToBasis(p_var); if (type == typeof(Quaternion)) return VariantUtils.ConvertToQuaternion(p_var); if (type == typeof(Transform3D)) return VariantUtils.ConvertToTransform3D(p_var); if (type == typeof(AABB)) return VariantUtils.ConvertToAABB(p_var); if (type == typeof(Color)) return VariantUtils.ConvertToColor(p_var); if (type == typeof(Plane)) return VariantUtils.ConvertToPlane(p_var); if (type == typeof(Callable)) { using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var); return ConvertCallableToManaged(&callable); } if (type == typeof(SignalInfo)) { using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var); return ConvertSignalToManaged(&signal); } if (type.IsEnum) { var enumUnderlyingType = type.GetEnumUnderlyingType(); switch (Type.GetTypeCode(enumUnderlyingType)) { case TypeCode.SByte: return VariantUtils.ConvertToInt8(p_var); case TypeCode.Int16: return VariantUtils.ConvertToInt16(p_var); case TypeCode.Int32: return VariantUtils.ConvertToInt32(p_var); case TypeCode.Int64: return VariantUtils.ConvertToInt64(p_var); case TypeCode.Byte: return VariantUtils.ConvertToUInt8(p_var); case TypeCode.UInt16: return VariantUtils.ConvertToUInt16(p_var); case TypeCode.UInt32: return VariantUtils.ConvertToUInt32(p_var); case TypeCode.UInt64: return VariantUtils.ConvertToUInt64(p_var); default: { GD.PushError( "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + type.FullName + " : " + enumUnderlyingType.FullName + "."); return null; } } } if (type.IsArray || type.IsSZArray) return variant_to_mono_array_of_type(p_var, type); else if (type.IsGenericType) return variant_to_mono_object_of_genericinst(p_var, type); else if (type == typeof(object)) return variant_to_mono_object(p_var); if (variant_to_mono_object_of_class(p_var, type, out object res)) return res; break; } } GD.PushError("Attempted to convert Variant to unsupported type. Name: " + type.FullName + "."); return null; } private static unsafe object variant_to_mono_array_of_type(godot_variant* p_var, Type type) { if (type == typeof(Byte[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); return PackedByteArray_to_mono_array(&packedArray); } if (type == typeof(Int32[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); return PackedInt32Array_to_mono_array(&packedArray); } if (type == typeof(Int64[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); return PackedInt64Array_to_mono_array(&packedArray); } if (type == typeof(float[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); return PackedFloat32Array_to_mono_array(&packedArray); } if (type == typeof(double[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); return PackedFloat64Array_to_mono_array(&packedArray); } if (type == typeof(string[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); return PackedStringArray_to_mono_array(&packedArray); } if (type == typeof(Vector2[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); return PackedVector2Array_to_mono_array(&packedArray); } if (type == typeof(Vector3[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); return PackedVector3Array_to_mono_array(&packedArray); } if (type == typeof(Color[])) { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); return PackedColorArray_to_mono_array(&packedArray); } if (typeof(Godot.Object[]).IsAssignableFrom(type)) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); return Array_to_mono_array_of_godot_object_type(&godotArray, type); } if (type == typeof(object[])) { using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var); return Array_to_mono_array(&godotArray); } GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + type.GetElementType()!.FullName + "."); return null; } private static unsafe bool variant_to_mono_object_of_class(godot_variant* p_var, Type type, out object res) { if (typeof(Godot.Object).IsAssignableFrom(type)) { res = InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var)); return true; } if (typeof(StringName) == type) { res = StringName.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToStringName(p_var)); return true; } if (typeof(NodePath) == type) { res = NodePath.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToNodePath(p_var)); return true; } if (typeof(RID) == type) { res = VariantUtils.ConvertToRID(p_var); return true; } if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type) { res = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToDictionary(p_var)); return true; } if (typeof(Collections.Array) == type || typeof(System.Collections.ICollection) == type || typeof(System.Collections.IEnumerable) == type) { res = Collections.Array.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToArray(p_var)); return true; } res = null; return false; } private static unsafe object variant_to_mono_object_of_genericinst(godot_variant* p_var, Type type) { static object variant_to_generic_godot_collections_dictionary(godot_variant* p_var, Type fullType) { var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToDictionary(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, args: new object[] { underlyingDict }, null); } static object variant_to_generic_godot_collections_array(godot_variant* p_var, Type fullType) { var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToArray(p_var)); return Activator.CreateInstance(fullType, BindingFlags.Public | BindingFlags.Instance, null, args: new object[] { underlyingArray }, null); } var genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Collections.Dictionary<,>)) return variant_to_generic_godot_collections_dictionary(p_var, type); if (genericTypeDefinition == typeof(Collections.Array<>)) return variant_to_generic_godot_collections_array(p_var, type); if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>)) { using var godotDictionary = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToDictionary(p_var)); var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.Instance, null, args: new object[] { /* capacity: */ godotDictionary.Count }, null); foreach (System.Collections.DictionaryEntry pair in godotDictionary) dictionary.Add(pair.Key, pair.Value); return dictionary; } if (genericTypeDefinition == typeof(System.Collections.Generic.List<>)) { using var godotArray = Collections.Array.CreateTakingOwnershipOfDisposableValue( VariantUtils.ConvertToArray(p_var)); var list = (System.Collections.IList)Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.Instance, null, args: new object[] { /* capacity: */ godotArray.Count }, null); foreach (object elem in godotArray) list.Add(elem); return list; } if (genericTypeDefinition == typeof(IDictionary<,>)) { var genericArgs = type.GetGenericArguments(); var keyType = genericArgs[0]; var valueType = genericArgs[1]; var genericGodotDictionaryType = typeof(Collections.Dictionary<,>) .MakeGenericType(keyType, valueType); return variant_to_generic_godot_collections_dictionary(p_var, genericGodotDictionaryType); } if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>)) { var elementType = type.GetGenericArguments()[0]; var genericGodotArrayType = typeof(Collections.Array<>) .MakeGenericType(elementType); return variant_to_generic_godot_collections_array(p_var, genericGodotArrayType); } return null; } public static unsafe object variant_to_mono_object(godot_variant* p_var) { switch ((*p_var)._type) { case Variant.Type.Bool: return (*p_var)._data._bool.ToBool(); case Variant.Type.Int: return (*p_var)._data._int; case Variant.Type.Float: { #if REAL_T_IS_DOUBLE return (*p_var)._data._float; #else return (float)(*p_var)._data._float; #endif } case Variant.Type.String: return mono_string_from_godot((*p_var)._data._m_string); case Variant.Type.Vector2: return (*p_var)._data._m_vector2; case Variant.Type.Vector2i: return (*p_var)._data._m_vector2i; case Variant.Type.Rect2: return (*p_var)._data._m_rect2; case Variant.Type.Rect2i: return (*p_var)._data._m_rect2i; case Variant.Type.Vector3: return (*p_var)._data._m_vector3; case Variant.Type.Vector3i: return (*p_var)._data._m_vector3i; case Variant.Type.Transform2d: return *(*p_var)._data._transform2d; case Variant.Type.Plane: return (*p_var)._data._m_plane; case Variant.Type.Quaternion: return (*p_var)._data._m_quaternion; case Variant.Type.Aabb: return *(*p_var)._data._aabb; case Variant.Type.Basis: return *(*p_var)._data._basis; case Variant.Type.Transform3d: return *(*p_var)._data._transform3d; case Variant.Type.Color: return (*p_var)._data._m_color; case Variant.Type.StringName: { // The Variant owns the value, so we need to make a copy return StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name)); } case Variant.Type.NodePath: { // The Variant owns the value, so we need to make a copy return NodePath.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path)); } case Variant.Type.Rid: return (*p_var)._data._m_rid; case Variant.Type.Object: return InteropUtils.UnmanagedGetManaged((*p_var)._data._m_obj_data.obj); case Variant.Type.Callable: return ConvertCallableToManaged(&(*p_var)._data._m_callable); case Variant.Type.Signal: return ConvertSignalToManaged(&(*p_var)._data._m_signal); case Variant.Type.Dictionary: { // The Variant owns the value, so we need to make a copy return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary)); } case Variant.Type.Array: { // The Variant owns the value, so we need to make a copy return Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array)); } case Variant.Type.RawArray: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var); return PackedByteArray_to_mono_array(&packedArray); } case Variant.Type.Int32Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var); return PackedInt32Array_to_mono_array(&packedArray); } case Variant.Type.Int64Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var); return PackedInt64Array_to_mono_array(&packedArray); } case Variant.Type.Float32Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var); return PackedFloat32Array_to_mono_array(&packedArray); } case Variant.Type.Float64Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var); return PackedFloat64Array_to_mono_array(&packedArray); } case Variant.Type.StringArray: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var); return PackedStringArray_to_mono_array(&packedArray); } case Variant.Type.Vector2Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var); return PackedVector2Array_to_mono_array(&packedArray); } case Variant.Type.Vector3Array: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var); return PackedVector3Array_to_mono_array(&packedArray); } case Variant.Type.ColorArray: { using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var); return PackedColorArray_to_mono_array(&packedArray); } default: return null; } } // String public static unsafe godot_string mono_string_to_godot(string p_mono_string) { if (p_mono_string == null) return new godot_string(); fixed (char* methodChars = p_mono_string) { godot_string dest; NativeFuncs.godotsharp_string_new_with_utf16_chars(&dest, methodChars); return dest; } } public static unsafe string mono_string_from_godot(in godot_string p_string) { if (p_string._ptr == IntPtr.Zero) return string.Empty; const int sizeOfChar32 = 4; byte* bytes = (byte*)p_string._ptr; int size = p_string.Size; if (size == 0) return string.Empty; size -= 1; // zero at the end int sizeInBytes = size * sizeOfChar32; return System.Text.Encoding.UTF32.GetString(bytes, sizeInBytes); } // Callable public static godot_callable ConvertCallableToNative(ref Callable p_managed_callable) { if (p_managed_callable.Delegate != null) { unsafe { godot_callable callable; NativeFuncs.godotsharp_callable_new_with_delegate( GCHandle.ToIntPtr(GCHandle.Alloc(p_managed_callable.Delegate)), &callable); return callable; } } else { unsafe { godot_string_name method; if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty) { godot_string_name src = p_managed_callable.Method.NativeValue; method = NativeFuncs.godotsharp_string_name_new_copy(&src); } else { method = default; } return new godot_callable { _method = method, // Takes ownership of disposable _objectId = p_managed_callable.Target.GetInstanceId() }; } } } public static unsafe Callable ConvertCallableToManaged(godot_callable* p_callable) { IntPtr delegateGCHandle; IntPtr godotObject; godot_string_name name; if (NativeFuncs.godotsharp_callable_get_data_for_marshalling( p_callable, &delegateGCHandle, &godotObject, &name).ToBool()) { if (delegateGCHandle != IntPtr.Zero) { return new Callable((Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target); } else { return new Callable( InteropUtils.UnmanagedGetManaged(godotObject), StringName.CreateTakingOwnershipOfDisposableValue(name)); } } // Some other unsupported callable return new Callable(); } // SignalInfo public static godot_signal ConvertSignalToNative(ref SignalInfo p_managed_signal) { ulong ownerId = p_managed_signal.Owner.GetInstanceId(); unsafe { godot_string_name name; if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty) { godot_string_name src = p_managed_signal.Name.NativeValue; name = NativeFuncs.godotsharp_string_name_new_copy(&src); } else { name = default; } return new godot_signal() { _name = name, _objectId = ownerId }; } } public static unsafe SignalInfo ConvertSignalToManaged(godot_signal* p_signal) { var owner = GD.InstanceFromId((*p_signal)._objectId); var name = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(&(*p_signal)._name)); return new SignalInfo(owner, name); } // Array public static unsafe object[] Array_to_mono_array(godot_array* p_array) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); int length = array.Count; var ret = new object[length]; array.CopyTo(ret, 0); // variant_to_mono_object handled by Collections.Array return ret; } public static unsafe object Array_to_mono_array_of_godot_object_type(godot_array* p_array, Type type) { var array = Collections.Array.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_array_new_copy(p_array)); int length = array.Count; object ret = Activator.CreateInstance(type, length); // variant_to_mono_object handled by Collections.Array // variant_to_mono_object_of_type is not needed because target element types are Godot.Object (or derived) array.CopyTo((object[])ret, 0); return ret; } public static godot_array mono_array_to_Array(object[] p_array) { int length = p_array.Length; if (length == 0) return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); array.Resize(length); for (int i = 0; i < length; i++) array[i] = p_array[i]; godot_array src = array.NativeValue; unsafe { return NativeFuncs.godotsharp_array_new_copy(&src); } } public static godot_array mono_array_to_Array(IList p_array) { int length = p_array.Count; if (length == 0) return NativeFuncs.godotsharp_array_new(); using var array = new Collections.Array(); array.Resize(length); for (int i = 0; i < length; i++) array[i] = p_array[i]; godot_array src = array.NativeValue; unsafe { return NativeFuncs.godotsharp_array_new_copy(&src); } } // PackedByteArray public static unsafe byte[] PackedByteArray_to_mono_array(godot_packed_byte_array* p_array) { byte* buffer = (*p_array)._ptr; int size = (*p_array).Size; var array = new byte[size]; fixed (byte* dest = array) Buffer.MemoryCopy(buffer, dest, size, size); return array; } public static unsafe godot_packed_byte_array mono_array_to_PackedByteArray(Span p_array) { if (p_array.IsEmpty) return new godot_packed_byte_array(); fixed (byte* src = p_array) return NativeFuncs.godotsharp_packed_byte_array_new_mem_copy(src, p_array.Length); } // PackedInt32Array public static unsafe int[] PackedInt32Array_to_mono_array(godot_packed_int32_array* p_array) { int* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(int); var array = new int[size]; fixed (int* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_int32_array mono_array_to_PackedInt32Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_int32_array(); fixed (int* src = p_array) return NativeFuncs.godotsharp_packed_int32_array_new_mem_copy(src, p_array.Length); } // PackedInt64Array public static unsafe long[] PackedInt64Array_to_mono_array(godot_packed_int64_array* p_array) { long* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(long); var array = new long[size]; fixed (long* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_int64_array mono_array_to_PackedInt64Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_int64_array(); fixed (long* src = p_array) return NativeFuncs.godotsharp_packed_int64_array_new_mem_copy(src, p_array.Length); } // PackedFloat32Array public static unsafe float[] PackedFloat32Array_to_mono_array(godot_packed_float32_array* p_array) { float* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(float); var array = new float[size]; fixed (float* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_float32_array mono_array_to_PackedFloat32Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_float32_array(); fixed (float* src = p_array) return NativeFuncs.godotsharp_packed_float32_array_new_mem_copy(src, p_array.Length); } // PackedFloat64Array public static unsafe double[] PackedFloat64Array_to_mono_array(godot_packed_float64_array* p_array) { double* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(double); var array = new double[size]; fixed (double* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_float64_array mono_array_to_PackedFloat64Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_float64_array(); fixed (double* src = p_array) return NativeFuncs.godotsharp_packed_float64_array_new_mem_copy(src, p_array.Length); } // PackedStringArray public static unsafe string[] PackedStringArray_to_mono_array(godot_packed_string_array* p_array) { godot_string* buffer = (*p_array)._ptr; if (buffer == null) return new string[] { }; int size = (*p_array).Size; var array = new string[size]; for (int i = 0; i < size; i++) array[i] = mono_string_from_godot(buffer[i]); return array; } public static unsafe godot_packed_string_array mono_array_to_PackedStringArray(Span p_array) { godot_packed_string_array dest = new godot_packed_string_array(); if (p_array.IsEmpty) return dest; /* TODO: Replace godotsharp_packed_string_array_add with a single internal call to get the write address. We can't use `dest._ptr` directly for writing due to COW. */ for (int i = 0; i < p_array.Length; i++) { using godot_string godotStrElem = mono_string_to_godot(p_array[i]); NativeFuncs.godotsharp_packed_string_array_add(&dest, &godotStrElem); } return dest; } // PackedVector2Array public static unsafe Vector2[] PackedVector2Array_to_mono_array(godot_packed_vector2_array* p_array) { Vector2* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector2); var array = new Vector2[size]; fixed (Vector2* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_vector2_array mono_array_to_PackedVector2Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_vector2_array(); fixed (Vector2* src = p_array) return NativeFuncs.godotsharp_packed_vector2_array_new_mem_copy(src, p_array.Length); } // PackedVector3Array public static unsafe Vector3[] PackedVector3Array_to_mono_array(godot_packed_vector3_array* p_array) { Vector3* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(Vector3); var array = new Vector3[size]; fixed (Vector3* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_vector3_array mono_array_to_PackedVector3Array(Span p_array) { if (p_array.IsEmpty) return new godot_packed_vector3_array(); fixed (Vector3* src = p_array) return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length); } // PackedColorArray public static unsafe Color[] PackedColorArray_to_mono_array(godot_packed_color_array* p_array) { Color* buffer = (*p_array)._ptr; int size = (*p_array).Size; int sizeInBytes = size * sizeof(Color); var array = new Color[size]; fixed (Color* dest = array) Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes); return array; } public static unsafe godot_packed_color_array mono_array_to_PackedColorArray(Span p_array) { if (p_array.IsEmpty) return new godot_packed_color_array(); fixed (Color* src = p_array) return NativeFuncs.godotsharp_packed_color_array_new_mem_copy(src, p_array.Length); } } }