Files
FlaxEngine/Source/Engine/Utilities/VariantUtils.cs

1375 lines
54 KiB
C#

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if USE_LARGE_WORLDS
using Real = System.Double;
#else
using Real = System.Single;
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
#if FLAX_EDITOR
using FlaxEditor.Scripting;
using FlaxEditor.Utilities;
#endif
using Newtonsoft.Json;
namespace FlaxEngine.Utilities
{
/// <summary>
/// Editor utilities and helper functions for Variant type.
/// </summary>
public static class VariantUtils
{
/// <summary>
/// Casts the generic value to a given type. Matches native Variant casting logic that favors returning null/zero value rather than error.
/// </summary>
/// <typeparam name="T">The destination value type.</typeparam>
/// <param name="value">The input value to cast.</param>
/// <returns>The result value.</returns>
internal static T Cast<T>(object value)
{
if (value == null)
return default;
var type = value.GetType();
if (type != typeof(T) && !typeof(T).IsAssignableFrom(type))
{
if (typeof(T) == typeof(Vector2))
{
if (value is Float2 asFloat2)
return (T)(object)new Vector2((Real)asFloat2.X, (Real)asFloat2.Y);
if (value is Float3 asFloat3)
return (T)(object)new Vector2((Real)asFloat3.X, (Real)asFloat3.Y);
if (value is Float4 asFloat4)
return (T)(object)new Vector2((Real)asFloat4.X, (Real)asFloat4.Y);
}
else if (typeof(T) == typeof(Vector3))
{
if (value is Float2 asFloat2)
return (T)(object)new Vector3((Real)asFloat2.X, (Real)asFloat2.Y, (Real)0);
if (value is Float3 asFloat3)
return (T)(object)new Vector3((Real)asFloat3.X, (Real)asFloat3.Y, (Real)asFloat3.Z);
if (value is Float4 asFloat4)
return (T)(object)new Vector3((Real)asFloat4.X, (Real)asFloat4.Y, (Real)asFloat4.Z);
}
else if (typeof(T) == typeof(Vector4))
{
if (value is Float2 asFloat2)
return (T)(object)new Vector4((Real)asFloat2.X, (Real)asFloat2.Y, (Real)0, (Real)0);
if (value is Float3 asFloat3)
return (T)(object)new Vector4((Real)asFloat3.X, (Real)asFloat3.Y, (Real)asFloat3.Z, (Real)0);
if (value is Vector4 asFloat4)
return (T)(object)new Vector4((Real)asFloat4.X, (Real)asFloat4.Y, (Real)asFloat4.Z, (Real)asFloat4.W);
}
else if (typeof(T) == typeof(Float2))
{
if (value is Vector2 asVector2)
return (T)(object)new Float2((float)asVector2.X, (float)asVector2.Y);
if (value is Vector3 asVector3)
return (T)(object)new Float2((float)asVector3.X, (float)asVector3.Y);
if (value is Vector4 asVector4)
return (T)(object)new Float2((float)asVector4.X, (float)asVector4.Y);
}
else if (typeof(T) == typeof(Float3))
{
if (value is Vector2 asVector2)
return (T)(object)new Float3((float)asVector2.X, (float)asVector2.Y, (float)0);
if (value is Vector3 asVector3)
return (T)(object)new Float3((float)asVector3.X, (float)asVector3.Y, (float)asVector3.Z);
if (value is Vector4 asFloat4)
return (T)(object)new Float3((float)asFloat4.X, (float)asFloat4.Y, (float)asFloat4.Z);
}
else if (typeof(T) == typeof(Float4))
{
if (value is Vector2 asVector2)
return (T)(object)new Float4((float)asVector2.X, (float)asVector2.Y, (float)0, (float)0);
if (value is Vector3 asVector3)
return (T)(object)new Float4((float)asVector3.X, (float)asVector3.Y, (float)asVector3.Z, (float)0);
if (value is Vector4 asVector4)
return (T)(object)new Float4((float)asVector4.X, (float)asVector4.Y, (float)asVector4.Z, (float)asVector4.W);
}
return (T)Convert.ChangeType(value, typeof(T));
}
return (T)value;
}
internal enum VariantType
{
Null = 0,
Void,
Bool,
Int,
Uint,
Int64,
Uint64,
Float,
Double,
Pointer,
String,
Object,
Structure,
Asset,
Blob,
Enum,
Float2,
Float3,
Float4,
Color,
Guid,
BoundingBox,
BoundingSphere,
Quaternion,
Transform,
Rectangle,
Ray,
Matrix,
Array,
Dictionary,
ManagedObject,
Typename,
Int2,
Int3,
Int4,
Int16,
Uint16,
Double2,
Double3,
Double4,
#if USE_LARGE_WORLDS
Vector2 = Double2,
Vector3 = Double3,
Vector4 = Double4,
#else
Vector2 = Float2,
Vector3 = Float3,
Vector4 = Float4,
#endif
}
#if FLAX_EDITOR
internal static VariantType ToVariantType(this Type type)
{
VariantType variantType;
if (type == null)
variantType = VariantType.Null;
else if (type == typeof(void))
variantType = VariantType.Void;
else if (type == typeof(bool))
variantType = VariantType.Bool;
else if (type == typeof(short))
variantType = VariantType.Int16;
else if (type == typeof(ushort))
variantType = VariantType.Uint16;
else if (type == typeof(int))
variantType = VariantType.Int;
else if (type == typeof(uint))
variantType = VariantType.Uint;
else if (type == typeof(long))
variantType = VariantType.Int64;
else if (type == typeof(ulong))
variantType = VariantType.Uint64;
else if (type.IsEnum)
variantType = VariantType.Enum;
else if (type == typeof(float))
variantType = VariantType.Float;
else if (type == typeof(double))
variantType = VariantType.Double;
else if (type == typeof(IntPtr))
variantType = VariantType.Pointer;
else if (type == typeof(string))
variantType = VariantType.String;
else if (type == typeof(Type))
variantType = VariantType.Typename;
else if (type == typeof(ScriptType))
variantType = VariantType.Typename;
else if (typeof(Asset).IsAssignableFrom(type))
variantType = VariantType.Asset;
else if (typeof(FlaxEngine.Object).IsAssignableFrom(type))
variantType = VariantType.Object;
else if (type == typeof(BoundingBox))
variantType = VariantType.BoundingBox;
else if (type == typeof(Transform))
variantType = VariantType.Transform;
else if (type == typeof(Ray))
variantType = VariantType.Ray;
else if (type == typeof(Matrix))
variantType = VariantType.Matrix;
else if (type == typeof(Float2))
variantType = VariantType.Float2;
else if (type == typeof(Float3))
variantType = VariantType.Float3;
else if (type == typeof(Float4))
variantType = VariantType.Float4;
else if (type == typeof(Double2))
variantType = VariantType.Double2;
else if (type == typeof(Double3))
variantType = VariantType.Double3;
else if (type == typeof(Double4))
variantType = VariantType.Double4;
else if (type == typeof(Vector2))
variantType = VariantType.Vector2;
else if (type == typeof(Vector3))
variantType = VariantType.Vector3;
else if (type == typeof(Vector4))
variantType = VariantType.Vector4;
else if (type == typeof(Int2))
variantType = VariantType.Int2;
else if (type == typeof(Int3))
variantType = VariantType.Int3;
else if (type == typeof(Int4))
variantType = VariantType.Int4;
else if (type == typeof(Color))
variantType = VariantType.Color;
else if (type == typeof(Guid))
variantType = VariantType.Guid;
else if (type == typeof(Quaternion))
variantType = VariantType.Quaternion;
else if (type == typeof(Rectangle))
variantType = VariantType.Rectangle;
else if (type == typeof(BoundingSphere))
variantType = VariantType.BoundingSphere;
else if (type.IsValueType)
variantType = VariantType.Structure;
else if (type == typeof(byte[]))
variantType = VariantType.Blob;
else if (type.IsArray)
variantType = VariantType.Array;
else if (type == typeof(Dictionary<object, object>) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
variantType = VariantType.Dictionary;
else if (type.IsPointer || type.IsByRef)
{
// Find underlying type without `*` or `&`
var typeName = type.FullName;
type = TypeUtils.GetManagedType(typeName.Substring(0, typeName.Length - 1));
return ToVariantType(type);
}
else
variantType = VariantType.ManagedObject;
return variantType;
}
internal static void WriteVariantType(this BinaryWriter stream, ScriptType type)
{
if (type == ScriptType.Null)
{
stream.Write((byte)0);
stream.Write(0);
return;
}
if (type.Type != null)
{
WriteVariantType(stream, type.Type);
}
else
{
if (type.IsEnum)
stream.Write((byte)VariantType.Enum);
else if (type.IsValueType)
stream.Write((byte)VariantType.Structure);
else
stream.Write((byte)VariantType.Object);
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.TypeName, 77);
}
}
internal static void WriteVariantType(this BinaryWriter stream, Type type)
{
if (type == null)
{
stream.Write((byte)0);
stream.Write(0);
return;
}
var variantType = ToVariantType(type);
stream.Write((byte)variantType);
switch (variantType)
{
case VariantType.Object:
if (type != typeof(FlaxEngine.Object))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
}
else
stream.Write(0);
break;
case VariantType.Asset:
if (type != typeof(Asset))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
}
else
stream.Write(0);
break;
case VariantType.Enum:
case VariantType.Structure:
case VariantType.ManagedObject:
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
break;
case VariantType.Typename:
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.GetTypeName(), 77);
break;
case VariantType.Array:
if (type != typeof(object[]))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
}
else
stream.Write(0);
break;
case VariantType.Dictionary:
if (type != typeof(Dictionary<object, object>))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.GetTypeName(), 77);
}
else
stream.Write(0);
break;
default:
stream.Write(0);
break;
}
}
internal static ScriptType ReadVariantScriptType(this BinaryReader stream)
{
var variantType = (VariantType)stream.ReadByte();
var typeNameLength = stream.ReadInt32();
if (typeNameLength == int.MaxValue)
{
typeNameLength = stream.ReadInt32();
var data = new byte[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadByte();
data[i] = (byte)(c ^ 77);
}
var typeName = System.Text.Encoding.ASCII.GetString(data);
return TypeUtils.GetType(typeName);
}
if (typeNameLength > 0)
{
// [Deprecated on 27.08.2020, expires on 27.08.2021]
var data = new char[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadUInt16();
data[i] = (char)(c ^ 77);
}
var typeName = new string(data);
return TypeUtils.GetType(typeName);
}
switch (variantType)
{
case VariantType.Null: return ScriptType.Null;
case VariantType.Void: return new ScriptType(typeof(void));
case VariantType.Bool: return new ScriptType(typeof(bool));
case VariantType.Int16: return new ScriptType(typeof(short));
case VariantType.Uint16: return new ScriptType(typeof(ushort));
case VariantType.Int: return new ScriptType(typeof(int));
case VariantType.Uint: return new ScriptType(typeof(uint));
case VariantType.Int64: return new ScriptType(typeof(long));
case VariantType.Uint64: return new ScriptType(typeof(ulong));
case VariantType.Float: return new ScriptType(typeof(float));
case VariantType.Double: return new ScriptType(typeof(double));
case VariantType.Pointer: return new ScriptType(typeof(IntPtr));
case VariantType.String: return new ScriptType(typeof(string));
case VariantType.Typename: return new ScriptType(typeof(Type));
case VariantType.Object: return ScriptType.FlaxObject;
case VariantType.Asset: return new ScriptType(typeof(Asset));
case VariantType.Float2: return new ScriptType(typeof(Float2));
case VariantType.Float3: return new ScriptType(typeof(Float3));
case VariantType.Float4: return new ScriptType(typeof(Float4));
case VariantType.Double2: return new ScriptType(typeof(Double2));
case VariantType.Double3: return new ScriptType(typeof(Double3));
case VariantType.Double4: return new ScriptType(typeof(Double4));
case VariantType.Int2: return new ScriptType(typeof(Int2));
case VariantType.Int3: return new ScriptType(typeof(Int3));
case VariantType.Int4: return new ScriptType(typeof(Int4));
case VariantType.Color: return new ScriptType(typeof(Color));
case VariantType.Guid: return new ScriptType(typeof(Guid));
case VariantType.BoundingBox: return new ScriptType(typeof(BoundingBox));
case VariantType.BoundingSphere: return new ScriptType(typeof(BoundingSphere));
case VariantType.Quaternion: return new ScriptType(typeof(Quaternion));
case VariantType.Transform: return new ScriptType(typeof(Transform));
case VariantType.Rectangle: return new ScriptType(typeof(Rectangle));
case VariantType.Ray: return new ScriptType(typeof(Ray));
case VariantType.Matrix: return new ScriptType(typeof(Matrix));
case VariantType.Array: return new ScriptType(typeof(object[]));
case VariantType.Dictionary: return new ScriptType(typeof(Dictionary<object, object>));
case VariantType.ManagedObject: return ScriptType.Object;
case VariantType.Blob: return new ScriptType(typeof(byte[]));
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename.");
}
}
internal static Type ReadVariantType(this BinaryReader stream)
{
var variantType = (VariantType)stream.ReadByte();
var typeNameLength = stream.ReadInt32();
if (typeNameLength == int.MaxValue)
{
typeNameLength = stream.ReadInt32();
var data = new byte[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadByte();
data[i] = (byte)(c ^ 77);
}
var typeName = System.Text.Encoding.ASCII.GetString(data);
return TypeUtils.GetManagedType(typeName);
}
if (typeNameLength > 0)
{
// [Deprecated on 27.08.2020, expires on 27.08.2021]
var data = new char[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadUInt16();
data[i] = (char)(c ^ 77);
}
var typeName = new string(data);
return TypeUtils.GetManagedType(typeName);
}
switch (variantType)
{
case VariantType.Null: return null;
case VariantType.Void: return typeof(void);
case VariantType.Bool: return typeof(bool);
case VariantType.Int16: return typeof(short);
case VariantType.Uint16: return typeof(ushort);
case VariantType.Int: return typeof(int);
case VariantType.Uint: return typeof(uint);
case VariantType.Int64: return typeof(long);
case VariantType.Uint64: return typeof(ulong);
case VariantType.Float: return typeof(float);
case VariantType.Double: return typeof(double);
case VariantType.Pointer: return typeof(IntPtr);
case VariantType.String: return typeof(string);
case VariantType.Typename: return typeof(Type);
case VariantType.Object: return typeof(FlaxEngine.Object);
case VariantType.Asset: return typeof(Asset);
case VariantType.Float2: return typeof(Float2);
case VariantType.Float3: return typeof(Float3);
case VariantType.Float4: return typeof(Float4);
case VariantType.Double2: return typeof(Double2);
case VariantType.Double3: return typeof(Double3);
case VariantType.Double4: return typeof(Double4);
case VariantType.Int2: return typeof(Int2);
case VariantType.Int3: return typeof(Int3);
case VariantType.Int4: return typeof(Int4);
case VariantType.Color: return typeof(Color);
case VariantType.Guid: return typeof(Guid);
case VariantType.BoundingBox: return typeof(BoundingBox);
case VariantType.BoundingSphere: return typeof(BoundingSphere);
case VariantType.Quaternion: return typeof(Quaternion);
case VariantType.Transform: return typeof(Transform);
case VariantType.Rectangle: return typeof(Rectangle);
case VariantType.Ray: return typeof(Ray);
case VariantType.Matrix: return typeof(Matrix);
case VariantType.Array: return typeof(object[]);
case VariantType.Dictionary: return typeof(Dictionary<object, object>);
case VariantType.ManagedObject: return typeof(object);
case VariantType.Blob: return typeof(byte[]);
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename.");
}
}
internal static unsafe object ReadVariant(this BinaryReader stream)
{
Type type = null;
var variantType = (VariantType)stream.ReadByte();
var typeNameLength = stream.ReadInt32();
string typeName = string.Empty;
if (typeNameLength == int.MaxValue)
{
typeNameLength = stream.ReadInt32();
var data = new byte[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadByte();
data[i] = (byte)(c ^ 77);
}
typeName = System.Text.Encoding.ASCII.GetString(data);
type = TypeUtils.GetManagedType(typeName);
}
else if (typeNameLength > 0)
{
// [Deprecated on 27.08.2020, expires on 27.08.2021]
var data = new char[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadUInt16();
data[i] = (char)(c ^ 77);
}
typeName = new string(data);
type = TypeUtils.GetManagedType(typeName);
}
switch (variantType)
{
case VariantType.Null:
case VariantType.Void: return null;
case VariantType.Bool: return stream.ReadByte() != 0;
case VariantType.Int16: return stream.ReadInt16();
case VariantType.Uint16: return stream.ReadUInt16();
case VariantType.Int: return stream.ReadInt32();
case VariantType.Uint: return stream.ReadUInt32();
case VariantType.Int64: return stream.ReadInt64();
case VariantType.Uint64: return stream.ReadUInt64();
case VariantType.Float: return stream.ReadSingle();
case VariantType.Double: return stream.ReadDouble();
case VariantType.Pointer: return new IntPtr((void*)stream.ReadUInt64());
case VariantType.String:
{
typeNameLength = stream.ReadInt32();
var data = new char[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadUInt16();
data[i] = (char)(c ^ -14);
}
return new string(data);
}
case VariantType.Object:
{
var id = stream.ReadGuid();
return FlaxEngine.Object.Find(ref id, type ?? typeof(FlaxEngine.Object));
}
case VariantType.Asset:
{
var id = stream.ReadGuid();
return FlaxEngine.Object.Find(ref id, type ?? typeof(Asset));
}
case VariantType.Blob: return stream.ReadBytes(stream.ReadInt32());
case VariantType.Enum:
{
if (type == null)
throw new Exception("Missing enum type of the Variant.");
return Enum.ToObject(type, stream.ReadUInt64());
}
case VariantType.Float2: return stream.ReadFloat2();
case VariantType.Float3: return stream.ReadFloat3();
case VariantType.Float4: return stream.ReadFloat4();
case VariantType.Double2: return stream.ReadDouble2();
case VariantType.Double3: return stream.ReadDouble3();
case VariantType.Double4: return stream.ReadDouble4();
case VariantType.Int2: return stream.ReadInt2();
case VariantType.Int3: return stream.ReadInt3();
case VariantType.Int4: return stream.ReadInt4();
case VariantType.Color: return stream.ReadColor();
case VariantType.Guid: return stream.ReadGuid();
case VariantType.BoundingBox: return stream.ReadBoundingBox();
case VariantType.BoundingSphere: return stream.ReadBoundingSphere();
case VariantType.Quaternion: return stream.ReadQuaternion();
case VariantType.Transform: return stream.ReadTransform();
case VariantType.Rectangle: return stream.ReadRectangle();
case VariantType.Ray: return stream.ReadRay();
case VariantType.Matrix: return stream.ReadMatrix();
case VariantType.Array:
{
if (type == null)
type = typeof(object[]);
else if (!type.IsArray)
throw new Exception("Invalid type for the Variant array " + typeName);
var count = stream.ReadInt32();
var result = Array.CreateInstance(type.GetElementType(), count);
for (int i = 0; i < count; i++)
result.SetValue(stream.ReadVariant(), i);
return result;
}
case VariantType.Dictionary:
{
if (type == null)
type = typeof(Dictionary<object, object>);
else if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Dictionary<,>))
throw new Exception("Invalid type for the Variant dictionary " + typeName);
var count = stream.ReadInt32();
var result = (IDictionary)Activator.CreateInstance(type);
for (int i = 0; i < count; i++)
result.Add(stream.ReadVariant(), stream.ReadVariant());
return result;
}
case VariantType.Typename:
{
typeNameLength = stream.ReadInt32();
var data = new byte[typeNameLength];
for (int i = 0; i < typeNameLength; i++)
{
var c = stream.ReadByte();
data[i] = (byte)(c ^ -14);
}
typeName = System.Text.Encoding.ASCII.GetString(data);
return TypeUtils.GetType(typeName);
}
case VariantType.ManagedObject:
case VariantType.Structure:
{
if (type == null)
throw new Exception("Missing type of the Variant typename " + typeName);
var format = stream.ReadByte();
if (format == 0)
{
// No Data
}
else if (format == 1)
{
// Json
var json = stream.ReadStrAnsi(-71);
if (json.Length != 0)
return FlaxEngine.Json.JsonSerializer.Deserialize(json, type);
return Activator.CreateInstance(type);
}
return null;
}
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType}." + (type != null ? $" Type: {type.GetTypeDisplayName()}" : string.Empty));
}
}
internal static void WriteVariant(this BinaryWriter stream, object value)
{
if (value == null)
{
stream.Write((byte)0);
stream.Write(0);
return;
}
var type = value.GetType();
var variantType = ToVariantType(type);
stream.Write((byte)variantType);
switch (variantType)
{
case VariantType.Object:
if (type != typeof(FlaxEngine.Object))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
}
else
stream.Write(0);
break;
case VariantType.Asset:
if (type != typeof(Asset))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
}
else
stream.Write(0);
break;
case VariantType.Enum:
case VariantType.Structure:
case VariantType.ManagedObject:
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
break;
case VariantType.Array:
if (type != typeof(object[]))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.FullName, 77);
}
else
stream.Write(0);
break;
case VariantType.Dictionary:
if (type != typeof(Dictionary<object, object>))
{
stream.Write(int.MaxValue);
stream.WriteStrAnsi(type.GetTypeName(), 77);
}
else
stream.Write(0);
break;
default:
stream.Write(0);
break;
}
Guid id;
switch (variantType)
{
case VariantType.Bool:
stream.Write((byte)((bool)value ? 1 : 0));
break;
case VariantType.Int16:
stream.Write((short)value);
break;
case VariantType.Uint16:
stream.Write((ushort)value);
break;
case VariantType.Int:
stream.Write((int)value);
break;
case VariantType.Uint:
stream.Write((uint)value);
break;
case VariantType.Int64:
stream.Write((long)value);
break;
case VariantType.Uint64:
stream.Write((ulong)value);
break;
case VariantType.Float:
stream.Write((float)value);
break;
case VariantType.Double:
stream.Write((double)value);
break;
case VariantType.Pointer:
stream.Write((ulong)(IntPtr)value);
break;
case VariantType.String:
stream.WriteStr((string)value, -14);
break;
case VariantType.Object:
id = ((FlaxEngine.Object)value).ID;
stream.WriteGuid(ref id);
break;
case VariantType.Asset:
id = ((Asset)value).ID;
stream.WriteGuid(ref id);
break;
case VariantType.Blob:
stream.Write(((byte[])value).Length);
stream.Write((byte[])value);
break;
case VariantType.Enum:
stream.Write(Convert.ToUInt64(value));
break;
case VariantType.Float2:
{
var asVector2 = value is Float2 v ? v : (Float2)(Vector2)value;
stream.Write(asVector2);
break;
}
case VariantType.Float3:
{
var asVector3 = value is Float3 v ? v : (Float3)(Vector3)value;
stream.Write(asVector3);
break;
}
case VariantType.Float4:
{
var asVector4 = value is Float4 v ? v : (Float4)(Vector4)value;
stream.Write(asVector4);
break;
}
case VariantType.Double2:
{
var asVector2 = value is Double2 v ? v : (Double2)(Vector2)value;
stream.Write(asVector2);
break;
}
case VariantType.Double3:
{
var asVector3 = value is Double3 v ? v : (Double3)(Vector3)value;
stream.Write(asVector3);
break;
}
case VariantType.Double4:
{
var asVector4 = value is Double4 v ? v : (Double4)(Vector4)value;
stream.Write(asVector4);
break;
}
case VariantType.Int2:
stream.Write((Int2)value);
break;
case VariantType.Int3:
stream.Write((Int3)value);
break;
case VariantType.Int4:
stream.Write((Int4)value);
break;
case VariantType.Color:
stream.Write((Color)value);
break;
case VariantType.Guid:
id = (Guid)value;
stream.WriteGuid(ref id);
break;
case VariantType.BoundingBox:
stream.Write((BoundingBox)value);
break;
case VariantType.BoundingSphere:
stream.Write((BoundingSphere)value);
break;
case VariantType.Quaternion:
stream.Write((Quaternion)value);
break;
case VariantType.Transform:
stream.Write((Transform)value);
break;
case VariantType.Rectangle:
stream.Write((Rectangle)value);
break;
case VariantType.Ray:
stream.Write((Ray)value);
break;
case VariantType.Matrix:
stream.Write((Matrix)value);
break;
case VariantType.Array:
{
var array = (Array)value;
stream.Write(array.Length);
foreach (var e in array)
stream.WriteVariant(e);
break;
}
case VariantType.Dictionary:
{
var dictionary = (IDictionary)value;
stream.Write(dictionary.Count);
foreach (var key in dictionary.Keys)
{
stream.WriteVariant(key);
stream.WriteVariant(dictionary[key]);
}
break;
}
case VariantType.Typename:
if (value is Type)
stream.WriteStrAnsi(((Type)value).GetTypeName(), -14);
else if (value is ScriptType)
stream.WriteStrAnsi(((ScriptType)value).TypeName, -14);
break;
case VariantType.ManagedObject:
case VariantType.Structure:
{
stream.Write((byte)1);
var json = FlaxEngine.Json.JsonSerializer.Serialize(value, type);
stream.WriteStrAnsi(json, -71);
break;
}
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} for type {type.FullName}.");
}
}
internal static void WriteVariantType(this JsonWriter stream, Type value)
{
var variantType = ToVariantType(value);
var withoutTypeName = true;
switch (variantType)
{
case VariantType.Object:
if (value != typeof(FlaxEngine.Object))
withoutTypeName = false;
break;
case VariantType.Asset:
if (value != typeof(Asset))
withoutTypeName = false;
break;
case VariantType.Enum:
case VariantType.Structure:
case VariantType.ManagedObject:
withoutTypeName = false;
break;
case VariantType.Array:
if (value != typeof(object[]))
withoutTypeName = false;
break;
case VariantType.Dictionary:
if (value != typeof(Dictionary<object, object>))
withoutTypeName = false;
break;
}
if (withoutTypeName)
{
stream.WriteValue((int)variantType);
}
else
{
stream.WriteStartObject();
stream.WritePropertyName("Type");
stream.WriteValue((int)variantType);
stream.WritePropertyName("TypeName");
stream.WriteValue(value.GetTypeName());
stream.WriteEndObject();
}
}
internal static void WriteVariant(this JsonWriter stream, object value)
{
var type = value?.GetType();
stream.WriteStartObject();
stream.WritePropertyName("Type");
stream.WriteVariantType(type);
stream.WritePropertyName("Value");
var variantType = ToVariantType(type);
// ReSharper disable PossibleNullReferenceException
switch (variantType)
{
case VariantType.Null:
case VariantType.Void:
stream.WriteStartObject();
stream.WriteEndObject();
break;
case VariantType.Bool:
stream.WriteValue((bool)value);
break;
case VariantType.Int16:
stream.WriteValue((short)value);
break;
case VariantType.Uint16:
stream.WriteValue((ushort)value);
break;
case VariantType.Int:
stream.WriteValue((int)value);
break;
case VariantType.Uint:
stream.WriteValue((uint)value);
break;
case VariantType.Int64:
stream.WriteValue((long)value);
break;
case VariantType.Uint64:
stream.WriteValue((ulong)value);
break;
case VariantType.Float:
stream.WriteValue((float)value);
break;
case VariantType.Double:
stream.WriteValue((double)value);
break;
case VariantType.Pointer:
stream.WriteValue((ulong)(IntPtr)value);
break;
case VariantType.String:
stream.WriteValue((string)value);
break;
case VariantType.Object:
stream.WriteValue(((FlaxEngine.Object)value).ID);
break;
case VariantType.Asset:
stream.WriteValue(((Asset)value).ID);
break;
case VariantType.Blob:
stream.WriteValue(Convert.ToBase64String((byte[])value));
break;
case VariantType.Enum:
stream.WriteValue(Convert.ToUInt64(value));
break;
case VariantType.Float2:
{
var asVector2 = value is Float2 v ? v : (Float2)(Vector2)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asVector2.X);
stream.WritePropertyName("Y");
stream.WriteValue(asVector2.Y);
stream.WriteEndObject();
break;
}
case VariantType.Float3:
{
var asVector3 = value is Float3 v ? v : (Float3)(Vector3)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asVector3.X);
stream.WritePropertyName("Y");
stream.WriteValue(asVector3.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asVector3.Z);
stream.WriteEndObject();
break;
}
case VariantType.Float4:
{
var asVector4 = value is Float4 v ? v : (Float4)(Vector4)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asVector4.X);
stream.WritePropertyName("Y");
stream.WriteValue(asVector4.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asVector4.Z);
stream.WritePropertyName("W");
stream.WriteValue(asVector4.W);
stream.WriteEndObject();
break;
}
case VariantType.Double2:
{
var asVector2 = value is Double2 v ? v : (Double2)(Vector2)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asVector2.X);
stream.WritePropertyName("Y");
stream.WriteValue(asVector2.Y);
stream.WriteEndObject();
break;
}
case VariantType.Double3:
{
var asVector3 = value is Double3 v ? v : (Double3)(Vector3)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asVector3.X);
stream.WritePropertyName("Y");
stream.WriteValue(asVector3.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asVector3.Z);
stream.WriteEndObject();
break;
}
case VariantType.Double4:
{
var asVector4 = value is Double4 v ? v : (Double4)(Vector4)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asVector4.X);
stream.WritePropertyName("Y");
stream.WriteValue(asVector4.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asVector4.Z);
stream.WritePropertyName("W");
stream.WriteValue(asVector4.W);
stream.WriteEndObject();
break;
}
case VariantType.Int2:
{
var asInt2 = (Int2)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asInt2.X);
stream.WritePropertyName("Y");
stream.WriteValue(asInt2.Y);
stream.WriteEndObject();
break;
}
case VariantType.Int3:
{
var asInt3 = (Int3)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asInt3.X);
stream.WritePropertyName("Y");
stream.WriteValue(asInt3.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asInt3.Z);
stream.WriteEndObject();
break;
}
case VariantType.Int4:
{
var asInt4 = (Int4)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asInt4.X);
stream.WritePropertyName("Y");
stream.WriteValue(asInt4.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asInt4.Z);
stream.WritePropertyName("W");
stream.WriteValue(asInt4.W);
stream.WriteEndObject();
break;
}
case VariantType.Color:
{
var asColor = (Color)value;
stream.WriteStartObject();
stream.WritePropertyName("R");
stream.WriteValue(asColor.R);
stream.WritePropertyName("G");
stream.WriteValue(asColor.G);
stream.WritePropertyName("B");
stream.WriteValue(asColor.B);
stream.WritePropertyName("A");
stream.WriteValue(asColor.A);
stream.WriteEndObject();
break;
}
case VariantType.Guid:
stream.WriteValue((Guid)value);
break;
case VariantType.BoundingBox:
{
var asBoundingBox = (BoundingBox)value;
stream.WriteStartObject();
stream.WritePropertyName("Minimum");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asBoundingBox.Minimum.X);
stream.WritePropertyName("Y");
stream.WriteValue(asBoundingBox.Minimum.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asBoundingBox.Minimum.Z);
stream.WriteEndObject();
stream.WritePropertyName("Maximum");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asBoundingBox.Maximum.X);
stream.WritePropertyName("Y");
stream.WriteValue(asBoundingBox.Maximum.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asBoundingBox.Maximum.Z);
stream.WriteEndObject();
stream.WriteEndObject();
break;
}
case VariantType.BoundingSphere:
{
var asBoundingSphere = (BoundingSphere)value;
stream.WriteStartObject();
stream.WritePropertyName("Center");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asBoundingSphere.Center.X);
stream.WritePropertyName("Y");
stream.WriteValue(asBoundingSphere.Center.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asBoundingSphere.Center.Z);
stream.WriteEndObject();
stream.WritePropertyName("Radius");
stream.WriteValue(asBoundingSphere.Radius);
stream.WriteEndObject();
break;
}
case VariantType.Quaternion:
{
var asQuaternion = (Quaternion)value;
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asQuaternion.X);
stream.WritePropertyName("Y");
stream.WriteValue(asQuaternion.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asQuaternion.Z);
stream.WritePropertyName("W");
stream.WriteValue(asQuaternion.W);
stream.WriteEndObject();
break;
}
case VariantType.Transform:
{
var asTransform = (Transform)value;
stream.WriteStartObject();
stream.WritePropertyName("Translation");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asTransform.Translation.X);
stream.WritePropertyName("Y");
stream.WriteValue(asTransform.Translation.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asTransform.Translation.Z);
stream.WriteEndObject();
stream.WritePropertyName("Orientation");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asTransform.Orientation.X);
stream.WritePropertyName("Y");
stream.WriteValue(asTransform.Orientation.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asTransform.Orientation.Z);
stream.WritePropertyName("W");
stream.WriteValue(asTransform.Orientation.W);
stream.WriteEndObject();
stream.WritePropertyName("Scale");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asTransform.Scale.X);
stream.WritePropertyName("Y");
stream.WriteValue(asTransform.Scale.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asTransform.Scale.Z);
stream.WriteEndObject();
stream.WriteEndObject();
break;
}
case VariantType.Rectangle:
{
var asRectangle = (Rectangle)value;
stream.WriteStartObject();
stream.WritePropertyName("Location");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asRectangle.Location.X);
stream.WritePropertyName("Y");
stream.WriteValue(asRectangle.Location.Y);
stream.WriteEndObject();
stream.WritePropertyName("Size");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asRectangle.Size.X);
stream.WritePropertyName("Y");
stream.WriteValue(asRectangle.Size.Y);
stream.WriteEndObject();
stream.WriteEndObject();
break;
}
case VariantType.Ray:
{
var asRay = (Ray)value;
stream.WriteStartObject();
stream.WritePropertyName("Position");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asRay.Position.X);
stream.WritePropertyName("Y");
stream.WriteValue(asRay.Position.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asRay.Position.Z);
stream.WriteEndObject();
stream.WritePropertyName("Direction");
stream.WriteStartObject();
stream.WritePropertyName("X");
stream.WriteValue(asRay.Direction.X);
stream.WritePropertyName("Y");
stream.WriteValue(asRay.Direction.Y);
stream.WritePropertyName("Z");
stream.WriteValue(asRay.Direction.Z);
stream.WriteEndObject();
stream.WriteEndObject();
break;
}
case VariantType.Matrix:
{
var asMatrix = (Matrix)value;
stream.WriteStartObject();
stream.WritePropertyName("M11");
stream.WriteValue(asMatrix.M11);
stream.WritePropertyName("M12");
stream.WriteValue(asMatrix.M12);
stream.WritePropertyName("M13");
stream.WriteValue(asMatrix.M13);
stream.WritePropertyName("M14");
stream.WriteValue(asMatrix.M14);
stream.WritePropertyName("M21");
stream.WriteValue(asMatrix.M21);
stream.WritePropertyName("M22");
stream.WriteValue(asMatrix.M22);
stream.WritePropertyName("M23");
stream.WriteValue(asMatrix.M23);
stream.WritePropertyName("M24");
stream.WriteValue(asMatrix.M24);
stream.WritePropertyName("M31");
stream.WriteValue(asMatrix.M31);
stream.WritePropertyName("M32");
stream.WriteValue(asMatrix.M32);
stream.WritePropertyName("M33");
stream.WriteValue(asMatrix.M33);
stream.WritePropertyName("M34");
stream.WriteValue(asMatrix.M34);
stream.WritePropertyName("M41");
stream.WriteValue(asMatrix.M41);
stream.WritePropertyName("M42");
stream.WriteValue(asMatrix.M42);
stream.WritePropertyName("M43");
stream.WriteValue(asMatrix.M43);
stream.WritePropertyName("M44");
stream.WriteValue(asMatrix.M44);
stream.WriteEndObject();
break;
}
case VariantType.Array:
{
stream.WriteStartArray();
foreach (var e in (Array)value)
stream.WriteVariant(e);
stream.WriteEndArray();
break;
}
case VariantType.Dictionary:
{
stream.WriteStartArray();
var dictionary = (IDictionary)value;
foreach (var key in dictionary.Keys)
{
stream.WritePropertyName("Key");
stream.WriteVariant(key);
stream.WritePropertyName("Value");
stream.WriteVariant(dictionary[key]);
}
stream.WriteEndArray();
break;
}
case VariantType.Typename:
if (value is Type)
stream.WriteValue(((Type)value).GetTypeName());
else if (value is ScriptType)
stream.WriteValue(((ScriptType)value).TypeName);
break;
case VariantType.ManagedObject:
case VariantType.Structure:
{
var json = FlaxEngine.Json.JsonSerializer.Serialize(value, type);
stream.WriteRaw(json);
break;
}
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} for type {type.GetTypeDisplayName()}.");
}
// ReSharper restore PossibleNullReferenceException
stream.WriteEndObject();
}
#endif
}
}