Merge remote-tracking branch 'origin/master' into 1.8
# Conflicts: # Source/Editor/Utilities/EditorUtilities.cpp # Source/Editor/Utilities/EditorUtilities.h
This commit is contained in:
@@ -22,7 +22,7 @@ namespace Flax.Build.Bindings
|
||||
public string[] Comment;
|
||||
public bool IsInBuild;
|
||||
public bool IsDeprecated;
|
||||
public string MarshalAs;
|
||||
public TypeInfo MarshalAs;
|
||||
internal bool IsInited;
|
||||
internal TypedefInfo Instigator;
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ namespace Flax.Build.Bindings
|
||||
if (apiType != null)
|
||||
{
|
||||
if (apiType.MarshalAs != null)
|
||||
return UsePassByReference(buildData, new TypeInfo(apiType.MarshalAs), caller);
|
||||
return UsePassByReference(buildData, apiType.MarshalAs, caller);
|
||||
|
||||
// Skip for scripting objects
|
||||
if (apiType.IsScriptingObject)
|
||||
|
||||
@@ -90,6 +90,15 @@ namespace Flax.Build.Bindings
|
||||
"Int4",
|
||||
};
|
||||
|
||||
private static bool GenerateCSharpUseFixedBuffer(string managedType)
|
||||
{
|
||||
return managedType == "byte" || managedType == "char" ||
|
||||
managedType == "short" || managedType == "ushort" ||
|
||||
managedType == "int" || managedType == "uint" ||
|
||||
managedType == "long" || managedType == "ulong" ||
|
||||
managedType == "float" || managedType == "double";
|
||||
}
|
||||
|
||||
private static string GenerateCSharpDefaultValueNativeToManaged(BuildData buildData, string value, ApiTypeInfo caller, TypeInfo valueType = null, bool attribute = false, string managedType = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
@@ -261,7 +270,7 @@ namespace Flax.Build.Bindings
|
||||
return value;
|
||||
}
|
||||
|
||||
private static string GenerateCSharpNativeToManaged(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
|
||||
private static string GenerateCSharpNativeToManaged(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, bool marshalling = false)
|
||||
{
|
||||
string result;
|
||||
if (typeInfo?.Type == null)
|
||||
@@ -271,7 +280,7 @@ namespace Flax.Build.Bindings
|
||||
if (typeInfo.IsArray)
|
||||
{
|
||||
typeInfo.IsArray = false;
|
||||
result = GenerateCSharpNativeToManaged(buildData, typeInfo, caller);
|
||||
result = GenerateCSharpNativeToManaged(buildData, typeInfo, caller, marshalling);
|
||||
typeInfo.IsArray = true;
|
||||
return result + "[]";
|
||||
}
|
||||
@@ -298,7 +307,7 @@ namespace Flax.Build.Bindings
|
||||
|
||||
// Object reference property
|
||||
if (typeInfo.IsObjectRef)
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller);
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller, marshalling);
|
||||
if (typeInfo.Type == "SoftTypeReference" || typeInfo.Type == "SoftObjectReference")
|
||||
return typeInfo.Type;
|
||||
|
||||
@@ -308,15 +317,25 @@ namespace Flax.Build.Bindings
|
||||
#else
|
||||
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null)
|
||||
#endif
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller) + "[]";
|
||||
{
|
||||
var arrayTypeInfo = typeInfo.GenericArgs[0];
|
||||
if (marshalling)
|
||||
{
|
||||
// Convert array that uses different type for marshalling
|
||||
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
arrayTypeInfo = arrayApiType.MarshalAs;
|
||||
}
|
||||
return GenerateCSharpNativeToManaged(buildData, arrayTypeInfo, caller) + "[]";
|
||||
}
|
||||
|
||||
// Dictionary
|
||||
if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null)
|
||||
return string.Format("System.Collections.Generic.Dictionary<{0}, {1}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller), GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller));
|
||||
return string.Format("System.Collections.Generic.Dictionary<{0}, {1}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller, marshalling), GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller, marshalling));
|
||||
|
||||
// HashSet
|
||||
if (typeInfo.Type == "HashSet" && typeInfo.GenericArgs != null)
|
||||
return string.Format("System.Collections.Generic.HashSet<{0}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller));
|
||||
return string.Format("System.Collections.Generic.HashSet<{0}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller, marshalling));
|
||||
|
||||
// BitArray
|
||||
if (typeInfo.Type == "BitArray" && typeInfo.GenericArgs != null)
|
||||
@@ -339,16 +358,16 @@ namespace Flax.Build.Bindings
|
||||
// TODO: generate delegates globally in the module namespace to share more code (smaller binary size)
|
||||
var key = string.Empty;
|
||||
for (int i = 0; i < typeInfo.GenericArgs.Count; i++)
|
||||
key += GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[i], caller);
|
||||
key += GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[i], caller, marshalling);
|
||||
if (!CSharpAdditionalCodeCache.TryGetValue(key, out var delegateName))
|
||||
{
|
||||
delegateName = "Delegate" + CSharpAdditionalCodeCache.Count;
|
||||
var signature = $"public delegate {GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller)} {delegateName}(";
|
||||
var signature = $"public delegate {GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller, marshalling)} {delegateName}(";
|
||||
for (int i = 1; i < typeInfo.GenericArgs.Count; i++)
|
||||
{
|
||||
if (i != 1)
|
||||
signature += ", ";
|
||||
signature += GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[i], caller);
|
||||
signature += GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[i], caller, marshalling);
|
||||
signature += $" arg{(i - 1)}";
|
||||
}
|
||||
signature += ");";
|
||||
@@ -381,11 +400,14 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
typeName += '<';
|
||||
foreach (var arg in typeInfo.GenericArgs)
|
||||
typeName += GenerateCSharpNativeToManaged(buildData, arg, caller);
|
||||
typeName += GenerateCSharpNativeToManaged(buildData, arg, caller, marshalling);
|
||||
typeName += '>';
|
||||
}
|
||||
if (apiType != null)
|
||||
{
|
||||
if (marshalling && apiType.MarshalAs != null)
|
||||
return GenerateCSharpNativeToManaged(buildData, apiType.MarshalAs, caller);
|
||||
|
||||
// Add reference to the namespace
|
||||
CSharpUsedNamespaces.Add(apiType.Namespace);
|
||||
var apiTypeParent = apiType.Parent;
|
||||
@@ -410,11 +432,11 @@ namespace Flax.Build.Bindings
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private static string GenerateCSharpManagedToNativeType(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
|
||||
private static string GenerateCSharpManagedToNativeType(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, bool marshalling = false)
|
||||
{
|
||||
// Fixed-size array
|
||||
if (typeInfo.IsArray)
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo, caller);
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo, caller, marshalling);
|
||||
|
||||
// Find API type info
|
||||
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
|
||||
@@ -430,7 +452,7 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
|
||||
if (apiType.MarshalAs != null)
|
||||
return GenerateCSharpManagedToNativeType(buildData, new TypeInfo(apiType.MarshalAs), caller);
|
||||
return GenerateCSharpManagedToNativeType(buildData, apiType.MarshalAs, caller, marshalling);
|
||||
if (apiType.IsScriptingObject || apiType.IsInterface)
|
||||
return "IntPtr";
|
||||
}
|
||||
@@ -443,7 +465,7 @@ namespace Flax.Build.Bindings
|
||||
if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null)
|
||||
return "IntPtr";
|
||||
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo, caller);
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo, caller, marshalling);
|
||||
}
|
||||
|
||||
private static string GenerateCSharpManagedToNativeConverter(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
|
||||
@@ -476,6 +498,18 @@ namespace Flax.Build.Bindings
|
||||
case "Function":
|
||||
// delegate
|
||||
return "NativeInterop.GetFunctionPointerForDelegate({0})";
|
||||
case "Array":
|
||||
case "Span":
|
||||
case "DataContainer":
|
||||
if (typeInfo.GenericArgs != null)
|
||||
{
|
||||
// Convert array that uses different type for marshalling
|
||||
var arrayTypeInfo = typeInfo.GenericArgs[0];
|
||||
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
return $"{{0}}.ConvertArray(x => ({GenerateCSharpNativeToManaged(buildData, arrayApiType.MarshalAs, caller)})x)";
|
||||
}
|
||||
return string.Empty;
|
||||
default:
|
||||
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
|
||||
if (apiType != null)
|
||||
@@ -522,9 +556,9 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
var apiType = FindApiTypeInfo(buildData, functionInfo.ReturnType, caller);
|
||||
if (apiType != null && apiType.MarshalAs != null)
|
||||
returnValueType = GenerateCSharpNativeToManaged(buildData, new TypeInfo(apiType.MarshalAs), caller);
|
||||
returnValueType = GenerateCSharpNativeToManaged(buildData, apiType.MarshalAs, caller, true);
|
||||
else
|
||||
returnValueType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType, caller);
|
||||
returnValueType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType, caller, true);
|
||||
}
|
||||
|
||||
#if USE_NETCORE
|
||||
@@ -585,7 +619,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(", ");
|
||||
separator = true;
|
||||
|
||||
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller);
|
||||
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller, true);
|
||||
#if USE_NETCORE
|
||||
string parameterMarshalType = "";
|
||||
if (nativeType == "System.Type")
|
||||
@@ -638,7 +672,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(", ");
|
||||
separator = true;
|
||||
|
||||
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller);
|
||||
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller, true);
|
||||
#if USE_NETCORE
|
||||
string parameterMarshalType = "";
|
||||
if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var __resultAsRef")
|
||||
@@ -751,7 +785,16 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
contents.Append(");");
|
||||
contents.Append(')');
|
||||
if ((functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer") && functionInfo.ReturnType.GenericArgs != null)
|
||||
{
|
||||
// Convert array that uses different type for marshalling
|
||||
var arrayTypeInfo = functionInfo.ReturnType.GenericArgs[0];
|
||||
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
contents.Append($".ConvertArray(x => ({GenerateCSharpNativeToManaged(buildData, arrayTypeInfo, caller)})x)");
|
||||
}
|
||||
contents.Append(';');
|
||||
|
||||
// Return result
|
||||
if (functionInfo.Glue.UseReferenceForResult)
|
||||
@@ -1439,10 +1482,10 @@ namespace Flax.Build.Bindings
|
||||
|
||||
indent += " ";
|
||||
|
||||
StringBuilder toManagedContent = new StringBuilder();
|
||||
StringBuilder toNativeContent = new StringBuilder();
|
||||
StringBuilder freeContents = new StringBuilder();
|
||||
StringBuilder freeContents2 = new StringBuilder();
|
||||
var toManagedContent = GetStringBuilder();
|
||||
var toNativeContent = GetStringBuilder();
|
||||
var freeContents = GetStringBuilder();
|
||||
var freeContents2 = GetStringBuilder();
|
||||
|
||||
{
|
||||
// Native struct begin
|
||||
@@ -1457,10 +1500,9 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(indent + "{");
|
||||
indent += " ";
|
||||
|
||||
toNativeContent.Append($"return new {structureInfo.Name}Internal() {{ ");
|
||||
toManagedContent.Append($"return new {structureInfo.Name}() {{ ");
|
||||
toNativeContent.Append($"var unmanaged = new {structureInfo.Name}Internal();").AppendLine();
|
||||
toManagedContent.Append($"var managed = new {structureInfo.Name}();").AppendLine();
|
||||
|
||||
bool useSeparator = false;
|
||||
contents.AppendLine();
|
||||
foreach (var fieldInfo in structureInfo.Fields)
|
||||
{
|
||||
@@ -1478,11 +1520,7 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
|
||||
|
||||
contents.Append(indent).Append(GenerateCSharpAccessLevel(fieldInfo.Access));
|
||||
if (fieldInfo.IsConstexpr)
|
||||
contents.Append("const ");
|
||||
else if (fieldInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.Append(indent).Append("public ");
|
||||
|
||||
var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
|
||||
bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
|
||||
@@ -1490,16 +1528,43 @@ namespace Flax.Build.Bindings
|
||||
|
||||
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
|
||||
{
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
|
||||
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
|
||||
#if USE_NETCORE
|
||||
if (GenerateCSharpUseFixedBuffer(originalType))
|
||||
{
|
||||
contents.AppendLine();
|
||||
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
contents.Append(indent).Append(GenerateCSharpAccessLevel(fieldInfo.Access));
|
||||
if (fieldInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
|
||||
// Use fixed statement with primitive types of buffers
|
||||
contents.Append($"fixed {originalType} {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}];").AppendLine();
|
||||
|
||||
// Copy fixed-size array
|
||||
toManagedContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(managed.{fieldInfo.Name}0), new IntPtr(unmanaged.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
|
||||
toNativeContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(unmanaged.{fieldInfo.Name}0), new IntPtr(managed.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Padding in structs for fixed-size array
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name).Append("0;").AppendLine();
|
||||
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
|
||||
{
|
||||
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
contents.Append(indent).Append("public ");
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
|
||||
}
|
||||
|
||||
// Copy fixed-size array item one-by-one
|
||||
if (fieldInfo.Access == AccessLevel.Public || fieldInfo.Access == AccessLevel.Internal)
|
||||
{
|
||||
for (int i = 0; i < fieldInfo.Type.ArraySize; i++)
|
||||
{
|
||||
toManagedContent.AppendLine($"managed.{fieldInfo.Name}{i} = unmanaged.{fieldInfo.Name}{i};");
|
||||
toNativeContent.AppendLine($"unmanaged.{fieldInfo.Name}{i} = managed.{fieldInfo.Name}{i};");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("TODO: generate utility method to copy private/protected array data items");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1529,34 +1594,17 @@ namespace Flax.Build.Bindings
|
||||
//else if (type == "Guid")
|
||||
// type = "GuidNative";
|
||||
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name);
|
||||
contents.Append(';').AppendLine();
|
||||
contents.Append(type).Append(' ').Append(fieldInfo.Name).Append(';').AppendLine();
|
||||
}
|
||||
|
||||
// Generate struct constructor/getter and deconstructor/setter function
|
||||
if (fieldInfo.NoArray && fieldInfo.Type.IsArray)
|
||||
continue;
|
||||
|
||||
if (useSeparator)
|
||||
{
|
||||
toManagedContent.Append(", ");
|
||||
toNativeContent.Append(", ");
|
||||
freeContents2.Append("");
|
||||
freeContents.Append("");
|
||||
}
|
||||
useSeparator = true;
|
||||
|
||||
toManagedContent.Append(fieldInfo.Name);
|
||||
toManagedContent.Append(" = ");
|
||||
|
||||
toNativeContent.Append(fieldInfo.Name);
|
||||
toNativeContent.Append(" = ");
|
||||
|
||||
toManagedContent.Append("managed.").Append(fieldInfo.Name).Append(" = ");
|
||||
toNativeContent.Append("unmanaged.").Append(fieldInfo.Name).Append(" = ");
|
||||
if (fieldInfo.Type.IsObjectRef)
|
||||
{
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1564,8 +1612,8 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "ScriptingObject")
|
||||
{
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1573,8 +1621,8 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
else if (fieldInfo.Type.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
|
||||
{
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1582,8 +1630,8 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Dictionary")
|
||||
{
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
|
||||
toNativeContent.Append($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak)");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
@@ -1595,16 +1643,16 @@ namespace Flax.Build.Bindings
|
||||
// Marshal blittable array elements back to original non-blittable elements
|
||||
string originalElementTypeMarshaller = originalElementType + "Marshaller";
|
||||
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementType}Internal";
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.ConvertArray((Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ConvertArray(managed.{fieldInfo.Name}, {originalElementTypeMarshaller}.ToNative)), GCHandleType.Weak) : IntPtr.Zero");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.ConvertArray((Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ConvertArray(managed.{fieldInfo.Name}, {originalElementTypeMarshaller}.ToNative)), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.GenericArgs[0].IsObjectRef)
|
||||
{
|
||||
// Array elements passed as GCHandles
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)) : null");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1613,53 +1661,53 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
{
|
||||
// Blittable array elements
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? (Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)).ToArray<{originalElementType}>() : null");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? (Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToArray<{originalElementType}>() : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
}
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Version")
|
||||
{
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
|
||||
toNativeContent.Append($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak)");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
else if (originalType == "string")
|
||||
{
|
||||
toManagedContent.Append($"ManagedString.ToManaged(managed.{fieldInfo.Name})");
|
||||
toNativeContent.Append($"ManagedString.ToNative(managed.{fieldInfo.Name})");
|
||||
toManagedContent.AppendLine($"ManagedString.ToManaged(unmanaged.{fieldInfo.Name});");
|
||||
toNativeContent.AppendLine($"ManagedString.ToNative(managed.{fieldInfo.Name});");
|
||||
freeContents.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
|
||||
freeContents2.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
|
||||
}
|
||||
else if (originalType == "bool")
|
||||
{
|
||||
toManagedContent.Append($"managed.{fieldInfo.Name} != 0");
|
||||
toNativeContent.Append($"managed.{fieldInfo.Name} ? (byte)1 : (byte)0");
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != 0;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} ? (byte)1 : (byte)0;");
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Variant")
|
||||
{
|
||||
// Variant passed as boxed object handle
|
||||
toManagedContent.Append($"ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(managed.{fieldInfo.Name})");
|
||||
toNativeContent.Append($"ManagedHandleMarshaller.NativeToManaged.ConvertToUnmanaged(managed.{fieldInfo.Name})");
|
||||
toManagedContent.AppendLine($"ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged.{fieldInfo.Name});");
|
||||
toNativeContent.AppendLine($"ManagedHandleMarshaller.NativeToManaged.ConvertToUnmanaged(managed.{fieldInfo.Name});");
|
||||
}
|
||||
else if (internalType)
|
||||
{
|
||||
toManagedContent.Append($"{internalTypeMarshaller}.ToManaged(managed.{fieldInfo.Name})");
|
||||
toNativeContent.Append($"{internalTypeMarshaller}.ToNative(managed.{fieldInfo.Name})");
|
||||
toManagedContent.AppendLine($"{internalTypeMarshaller}.ToManaged(unmanaged.{fieldInfo.Name});");
|
||||
toNativeContent.AppendLine($"{internalTypeMarshaller}.ToNative(managed.{fieldInfo.Name});");
|
||||
freeContents.AppendLine($"{internalTypeMarshaller}.Free(unmanaged.{fieldInfo.Name});");
|
||||
freeContents2.AppendLine($"{internalTypeMarshaller}.Free(unmanaged.{fieldInfo.Name});");
|
||||
}
|
||||
/*else if (originalType == "Guid")
|
||||
{
|
||||
toManagedContent.Append("(Guid)managed.").Append(fieldInfo.Name);
|
||||
toManagedContent.Append("(Guid)unmanaged.").Append(fieldInfo.Name);
|
||||
toNativeContent.Append("(GuidNative)managed.").Append(fieldInfo.Name);
|
||||
}*/
|
||||
else
|
||||
{
|
||||
toManagedContent.Append("managed.").Append(fieldInfo.Name);
|
||||
toNativeContent.Append("managed.").Append(fieldInfo.Name);
|
||||
toManagedContent.Append("unmanaged.").Append(fieldInfo.Name).AppendLine(";");
|
||||
toNativeContent.Append("managed.").Append(fieldInfo.Name).AppendLine(";");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1667,8 +1715,8 @@ namespace Flax.Build.Bindings
|
||||
indent = indent.Substring(0, indent.Length - 4);
|
||||
contents.AppendLine(indent + "}").AppendLine();
|
||||
|
||||
toManagedContent.AppendLine(" };");
|
||||
toNativeContent.AppendLine(" };");
|
||||
toNativeContent.Append("return unmanaged;");
|
||||
toManagedContent.Append("return managed;");
|
||||
}
|
||||
|
||||
var indent2 = indent + " ";
|
||||
@@ -1716,7 +1764,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(freeContents.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
|
||||
|
||||
// Managed/native converters
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ToManaged({structureInfo.Name}Internal managed)");
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ToManaged({structureInfo.Name}Internal unmanaged)");
|
||||
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toManagedContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
|
||||
contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ToNative({structureInfo.Name} managed)");
|
||||
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toNativeContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
|
||||
@@ -1724,6 +1772,11 @@ namespace Flax.Build.Bindings
|
||||
contents.AppendLine("#pragma warning restore 1591");
|
||||
indent = indent.Substring(0, indent.Length - 4);
|
||||
contents.Append(indent).AppendLine("}").AppendLine();
|
||||
|
||||
PutStringBuilder(toManagedContent);
|
||||
PutStringBuilder(toNativeContent);
|
||||
PutStringBuilder(freeContents);
|
||||
PutStringBuilder(freeContents2);
|
||||
}
|
||||
#endif
|
||||
// Struct docs
|
||||
@@ -1771,15 +1824,12 @@ namespace Flax.Build.Bindings
|
||||
managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
|
||||
fieldInfo.Type.IsArray = true;
|
||||
#if USE_NETCORE
|
||||
// Use fixed statement with primitive types of buffers
|
||||
if (managedType == "char")
|
||||
if (GenerateCSharpUseFixedBuffer(managedType))
|
||||
{
|
||||
// char's are not blittable, store as short instead
|
||||
contents.Append($"fixed short {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}]; // {managedType}*").AppendLine();
|
||||
}
|
||||
else if (managedType == "byte")
|
||||
{
|
||||
contents.Append($"fixed byte {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}]; // {managedType}*").AppendLine();
|
||||
// Use fixed statement with primitive types of buffers
|
||||
if (managedType == "char")
|
||||
managedType = "short"; // char's are not blittable, store as short instead
|
||||
contents.Append($"fixed {managedType} {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}];").AppendLine();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -1789,7 +1839,6 @@ namespace Flax.Build.Bindings
|
||||
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
|
||||
{
|
||||
contents.AppendLine();
|
||||
GenerateCSharpComment(contents, indent, fieldInfo.Comment);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
contents.Append(indent).Append(GenerateCSharpAccessLevel(fieldInfo.Access));
|
||||
if (fieldInfo.IsStatic)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Flax.Build.Bindings
|
||||
partial class BindingsGenerator
|
||||
{
|
||||
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
|
||||
private const int CacheVersion = 21;
|
||||
private const int CacheVersion = 22;
|
||||
|
||||
internal static void Write(BinaryWriter writer, string e)
|
||||
{
|
||||
|
||||
@@ -167,11 +167,7 @@ namespace Flax.Build.Bindings
|
||||
return $"Variant(StringView({value}))";
|
||||
if (typeInfo.Type == "StringAnsi")
|
||||
return $"Variant(StringAnsiView({value}))";
|
||||
if (typeInfo.Type == "AssetReference" ||
|
||||
typeInfo.Type == "WeakAssetReference" ||
|
||||
typeInfo.Type == "SoftAssetReference" ||
|
||||
typeInfo.Type == "ScriptingObjectReference" ||
|
||||
typeInfo.Type == "SoftObjectReference")
|
||||
if (typeInfo.IsObjectRef)
|
||||
return $"Variant({value}.Get())";
|
||||
if (typeInfo.IsArray)
|
||||
{
|
||||
@@ -227,10 +223,10 @@ namespace Flax.Build.Bindings
|
||||
return $"(StringAnsiView){value}";
|
||||
if (typeInfo.IsPtr && typeInfo.IsConst && typeInfo.Type == "Char")
|
||||
return $"((StringView){value}).GetText()"; // (StringView)Variant, if not empty, is guaranteed to point to a null-terminated buffer.
|
||||
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftAssetReference")
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})";
|
||||
if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference")
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})";
|
||||
if (typeInfo.IsObjectRef)
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})";
|
||||
if (typeInfo.IsArray)
|
||||
throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'.");
|
||||
if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
|
||||
@@ -309,7 +305,7 @@ namespace Flax.Build.Bindings
|
||||
private static string GenerateCppGetMClass(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, FunctionInfo functionInfo)
|
||||
{
|
||||
// Optimal path for in-build types
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, typeInfo, caller);
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, typeInfo, caller, true);
|
||||
switch (managedType)
|
||||
{
|
||||
// In-built types (cached by the engine on startup)
|
||||
@@ -392,7 +388,7 @@ namespace Flax.Build.Bindings
|
||||
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h");
|
||||
|
||||
// Optimal path for in-build types
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, typeInfo, caller);
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, typeInfo, caller, true);
|
||||
switch (managedType)
|
||||
{
|
||||
case "bool":
|
||||
@@ -514,11 +510,7 @@ namespace Flax.Build.Bindings
|
||||
return "MUtils::ToManaged({0})";
|
||||
default:
|
||||
// Object reference property
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" ||
|
||||
typeInfo.Type == "AssetReference" ||
|
||||
typeInfo.Type == "WeakAssetReference" ||
|
||||
typeInfo.Type == "SoftAssetReference" ||
|
||||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
if (typeInfo.IsObjectRef)
|
||||
{
|
||||
type = "MObject*";
|
||||
return "{0}.GetManagedInstance()";
|
||||
@@ -527,16 +519,28 @@ namespace Flax.Build.Bindings
|
||||
// Array or DataContainer
|
||||
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null)
|
||||
{
|
||||
var arrayTypeInfo = typeInfo.GenericArgs[0];
|
||||
#if USE_NETCORE
|
||||
// Boolean arrays does not support custom marshalling for some unknown reason
|
||||
if (typeInfo.GenericArgs[0].Type == "bool")
|
||||
if (arrayTypeInfo.Type == "bool")
|
||||
{
|
||||
type = "bool*";
|
||||
return "MUtils::ToBoolArray({0})";
|
||||
}
|
||||
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
|
||||
#endif
|
||||
type = "MArray*";
|
||||
return "MUtils::ToArray({0}, " + GenerateCppGetMClass(buildData, typeInfo.GenericArgs[0], caller, functionInfo) + ")";
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
{
|
||||
// Convert array that uses different type for marshalling
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
arrayTypeInfo = arrayApiType.MarshalAs; // Convert array that uses different type for marshalling
|
||||
var genericArgs = arrayApiType.MarshalAs.GetFullNameNative(buildData, caller);
|
||||
if (typeInfo.GenericArgs.Count != 1)
|
||||
genericArgs += ", " + typeInfo.GenericArgs[1];
|
||||
return "MUtils::ToArray(Array<" + genericArgs + ">({0}), " + GenerateCppGetMClass(buildData, arrayTypeInfo, caller, functionInfo) + ")";
|
||||
}
|
||||
return "MUtils::ToArray({0}, " + GenerateCppGetMClass(buildData, arrayTypeInfo, caller, functionInfo) + ")";
|
||||
}
|
||||
|
||||
// Span
|
||||
@@ -597,7 +601,7 @@ namespace Flax.Build.Bindings
|
||||
CppReferencesFiles.Add(apiType.File);
|
||||
|
||||
if (apiType.MarshalAs != null)
|
||||
return GenerateCppWrapperNativeToManaged(buildData, new TypeInfo(apiType.MarshalAs), caller, out type, functionInfo);
|
||||
return GenerateCppWrapperNativeToManaged(buildData, apiType.MarshalAs, caller, out type, functionInfo);
|
||||
|
||||
// Scripting Object
|
||||
if (apiType.IsScriptingObject)
|
||||
@@ -704,11 +708,7 @@ namespace Flax.Build.Bindings
|
||||
return "MUtils::ToNative({0})";
|
||||
default:
|
||||
// Object reference property
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" ||
|
||||
typeInfo.Type == "AssetReference" ||
|
||||
typeInfo.Type == "WeakAssetReference" ||
|
||||
typeInfo.Type == "SoftAssetReference" ||
|
||||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
if (typeInfo.IsObjectRef)
|
||||
{
|
||||
// For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code)
|
||||
if (CppNonPodTypesConvertingGeneration)
|
||||
@@ -731,11 +731,26 @@ namespace Flax.Build.Bindings
|
||||
// Array
|
||||
if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
|
||||
{
|
||||
var T = typeInfo.GenericArgs[0].GetFullNameNative(buildData, caller);
|
||||
type = "MArray*";
|
||||
var arrayTypeInfo = typeInfo.GenericArgs[0];
|
||||
var arrayApiType = FindApiTypeInfo(buildData, arrayTypeInfo, caller);
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
arrayTypeInfo = arrayApiType.MarshalAs;
|
||||
var genericArgs = arrayTypeInfo.GetFullNameNative(buildData, caller);
|
||||
if (typeInfo.GenericArgs.Count != 1)
|
||||
return "MUtils::ToArray<" + T + ", " + typeInfo.GenericArgs[1] + ">({0})";
|
||||
return "MUtils::ToArray<" + T + ">({0})";
|
||||
genericArgs += ", " + typeInfo.GenericArgs[1];
|
||||
|
||||
type = "MArray*";
|
||||
var result = "MUtils::ToArray<" + genericArgs + ">({0})";
|
||||
|
||||
if (arrayApiType != null && arrayApiType.MarshalAs != null)
|
||||
{
|
||||
// Convert array that uses different type for marshalling
|
||||
genericArgs = typeInfo.GenericArgs[0].GetFullNameNative(buildData, caller);
|
||||
if (typeInfo.GenericArgs.Count != 1)
|
||||
genericArgs += ", " + typeInfo.GenericArgs[1];
|
||||
result = $"Array<{genericArgs}>({result})";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Span or DataContainer
|
||||
@@ -801,7 +816,7 @@ namespace Flax.Build.Bindings
|
||||
if (apiType != null)
|
||||
{
|
||||
if (apiType.MarshalAs != null)
|
||||
return GenerateCppWrapperManagedToNative(buildData, new TypeInfo(apiType.MarshalAs), caller, out type, out apiType, functionInfo, out needLocalVariable);
|
||||
return GenerateCppWrapperManagedToNative(buildData, apiType.MarshalAs, caller, out type, out apiType, functionInfo, out needLocalVariable);
|
||||
|
||||
// Scripting Object (for non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code)
|
||||
if (CppNonPodTypesConvertingGeneration && apiType.IsScriptingObject && typeInfo.IsPtr)
|
||||
|
||||
@@ -185,6 +185,11 @@ namespace Flax.Build.Bindings
|
||||
tag.Value = tag.Value.Substring(1, tag.Value.Length - 2);
|
||||
if (tag.Value.Contains("\\\""))
|
||||
tag.Value = tag.Value.Replace("\\\"", "\"");
|
||||
token = context.Tokenizer.NextToken();
|
||||
if (token.Type == TokenType.Multiply)
|
||||
tag.Value += token.Value;
|
||||
else
|
||||
context.Tokenizer.PreviousToken();
|
||||
parameters.Add(tag);
|
||||
break;
|
||||
case TokenType.Whitespace:
|
||||
@@ -647,7 +652,7 @@ namespace Flax.Build.Bindings
|
||||
desc.Namespace = tag.Value;
|
||||
break;
|
||||
case "marshalas":
|
||||
desc.MarshalAs = tag.Value;
|
||||
desc.MarshalAs = TypeInfo.FromString(tag.Value);
|
||||
break;
|
||||
case "tag":
|
||||
ParseTag(ref desc.Tags, tag);
|
||||
@@ -1236,7 +1241,7 @@ namespace Flax.Build.Bindings
|
||||
desc.Namespace = tag.Value;
|
||||
break;
|
||||
case "marshalas":
|
||||
desc.MarshalAs = tag.Value;
|
||||
desc.MarshalAs = TypeInfo.FromString(tag.Value);
|
||||
break;
|
||||
case "tag":
|
||||
ParseTag(ref desc.Tags, tag);
|
||||
|
||||
@@ -180,6 +180,17 @@ namespace Flax.Build.Bindings
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static TypeInfo FromString(string text)
|
||||
{
|
||||
var result = new TypeInfo(text);
|
||||
if (result.Type.EndsWith('*'))
|
||||
{
|
||||
result.IsPtr = true;
|
||||
result.Type = result.Type.Substring(0, result.Type.Length - 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ToString(bool canRef = true)
|
||||
{
|
||||
var sb = new StringBuilder(64);
|
||||
|
||||
@@ -9,32 +9,30 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
public static class FileCache
|
||||
{
|
||||
private static Dictionary<string, FileInfo> fileInfoCache = new Dictionary<string, FileInfo>();
|
||||
private static readonly Dictionary<string, FileInfo> _cache = new();
|
||||
|
||||
public static void FileRemoveFromCache(string path)
|
||||
{
|
||||
//fileInfoCache[path].Refresh();
|
||||
fileInfoCache.Remove(path);
|
||||
_cache.Remove(path);
|
||||
}
|
||||
|
||||
|
||||
public static bool Exists(string path)
|
||||
{
|
||||
if (fileInfoCache.TryGetValue(path, out var fileInfo))
|
||||
if (_cache.TryGetValue(path, out var fileInfo))
|
||||
return fileInfo.Exists;
|
||||
|
||||
fileInfo = new FileInfo(path);
|
||||
fileInfoCache.Add(path, fileInfo);
|
||||
_cache.Add(path, fileInfo);
|
||||
return fileInfo.Exists;
|
||||
}
|
||||
|
||||
public static DateTime GetLastWriteTime(string path)
|
||||
{
|
||||
|
||||
if (fileInfoCache.TryGetValue(path, out var fileInfo))
|
||||
if (_cache.TryGetValue(path, out var fileInfo))
|
||||
return fileInfo.LastWriteTime;
|
||||
|
||||
fileInfo = new FileInfo(path);
|
||||
fileInfoCache.Add(path, fileInfo);
|
||||
_cache.Add(path, fileInfo);
|
||||
return fileInfo.LastWriteTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,11 +845,11 @@ namespace Flax.Build
|
||||
foreach (var moduleName in moduleOptions.PrivateDependencies.Concat(moduleOptions.PublicDependencies))
|
||||
{
|
||||
var dependencyModule = buildData.Rules.GetModule(moduleName);
|
||||
if (dependencyModule != null &&
|
||||
!string.IsNullOrEmpty(dependencyModule.BinaryModuleName) &&
|
||||
dependencyModule.BinaryModuleName != binaryModule.Key &&
|
||||
if (dependencyModule != null &&
|
||||
!string.IsNullOrEmpty(dependencyModule.BinaryModuleName) &&
|
||||
dependencyModule.BinaryModuleName != binaryModule.Key &&
|
||||
!moduleNamesUsed.Contains(dependencyModule.BinaryModuleName) &&
|
||||
GetModuleProject(dependencyModule, project) != null &&
|
||||
GetModuleProject(dependencyModule, project) != null &&
|
||||
buildData.Modules.TryGetValue(dependencyModule, out var dependencyOptions))
|
||||
{
|
||||
// Import symbols from referenced binary module
|
||||
|
||||
@@ -77,14 +77,10 @@ namespace Flax.Build
|
||||
var architectureId = RuntimeInformation.ProcessArchitecture;
|
||||
switch (architectureId)
|
||||
{
|
||||
case Architecture.X86:
|
||||
return TargetArchitecture.x86;
|
||||
case Architecture.X64:
|
||||
return TargetArchitecture.x64;
|
||||
case Architecture.Arm:
|
||||
return TargetArchitecture.ARM;
|
||||
case Architecture.Arm64:
|
||||
return TargetArchitecture.ARM64;
|
||||
case Architecture.X86: return TargetArchitecture.x86;
|
||||
case Architecture.X64: return TargetArchitecture.x64;
|
||||
case Architecture.Arm: return TargetArchitecture.ARM;
|
||||
case Architecture.Arm64: return TargetArchitecture.ARM64;
|
||||
default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", architectureId));
|
||||
}
|
||||
}
|
||||
@@ -290,12 +286,9 @@ namespace Flax.Build
|
||||
var subdir = "Binaries/Editor/";
|
||||
switch (Platform.BuildTargetPlatform)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
return subdir + "Win64";
|
||||
case TargetPlatform.Linux:
|
||||
return subdir + "Linux";
|
||||
case TargetPlatform.Mac:
|
||||
return subdir + "Mac";
|
||||
case TargetPlatform.Windows: return subdir + "Win64";
|
||||
case TargetPlatform.Linux: return subdir + "Linux";
|
||||
case TargetPlatform.Mac: return subdir + "Mac";
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -802,9 +802,12 @@ namespace Flax.Build.Plugins
|
||||
// Serialize base type
|
||||
if (type.BaseType != null && type.BaseType.FullName != "System.ValueType" && type.BaseType.FullName != "FlaxEngine.Object" && type.BaseType.CanBeResolved())
|
||||
{
|
||||
GenerateSerializeCallback(module, il, type.BaseType.Resolve(), serialize);
|
||||
GenerateSerializeCallback(module, il, type.BaseType, serialize);
|
||||
}
|
||||
|
||||
if (type.HasGenericParameters) // TODO: implement network replication for generic classes
|
||||
MonoCecil.CompilationError($"Not supported generic type '{type.FullName}' for network replication.");
|
||||
|
||||
var ildContext = new DotnetIlContext(il);
|
||||
|
||||
// Serialize all type fields marked with NetworkReplicated attribute
|
||||
@@ -874,12 +877,13 @@ namespace Flax.Build.Plugins
|
||||
return m;
|
||||
}
|
||||
|
||||
private static void GenerateSerializeCallback(ModuleDefinition module, ILProcessor il, TypeDefinition type, bool serialize)
|
||||
private static void GenerateSerializeCallback(ModuleDefinition module, ILProcessor il, TypeReference type, bool serialize)
|
||||
{
|
||||
if (type.IsScriptingObject())
|
||||
{
|
||||
// NetworkReplicator.InvokeSerializer(typeof(<type>), instance, stream, <serialize>)
|
||||
il.Emit(OpCodes.Ldtoken, module.ImportReference(type));
|
||||
module.ImportReference(type);
|
||||
il.Emit(OpCodes.Ldtoken, type);
|
||||
module.GetType("System.Type", out var typeType);
|
||||
var getTypeFromHandle = typeType.Resolve().GetMethod("GetTypeFromHandle");
|
||||
il.Emit(OpCodes.Call, module.ImportReference(getTypeFromHandle));
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Flax.Deploy
|
||||
DeployFile(src, dst, buildToolExe);
|
||||
CodeSign(Path.Combine(dst, buildToolExe));
|
||||
var buildToolDll = "Flax.Build.dll";
|
||||
DeployFile(src, dst,buildToolDll);
|
||||
DeployFile(src, dst, buildToolDll);
|
||||
CodeSign(Path.Combine(dst, buildToolDll));
|
||||
DeployFile(src, dst, "Flax.Build.xml", true);
|
||||
DeployFile(src, dst, "Flax.Build.pdb");
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Flax.Deps.Dependencies
|
||||
case TargetPlatform.Mac:
|
||||
{
|
||||
// Build for Mac
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
RunCmake(root, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig);
|
||||
Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError);
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Flax.Deps.Dependencies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Build(BuildOptions options)
|
||||
{
|
||||
@@ -185,16 +185,16 @@ namespace Flax.Deps.Dependencies
|
||||
|
||||
// Print the NvCloth version
|
||||
Log.Info($"Building {File.ReadAllLines(Path.Combine(root, "README.md"))[0].Trim()} to {platform} {architecture}");
|
||||
|
||||
|
||||
// Generate project files
|
||||
SetupDirectory(buildFolder, false);
|
||||
Utilities.FileDelete(Path.Combine(cmakeFolder, "CMakeCache.txt"));
|
||||
cmakeArgs += $" -DPX_STATIC_LIBRARIES=1 -DPX_OUTPUT_DLL_DIR=\"{Path.Combine(buildFolder, "bin")}\" -DPX_OUTPUT_LIB_DIR=\"{Path.Combine(buildFolder, "lib")}\" -DPX_OUTPUT_EXE_DIR=\"{Path.Combine(buildFolder, "bin")}\"";
|
||||
RunCmake(cmakeFolder, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + cmakeArgs, envVars);
|
||||
|
||||
|
||||
// Run build
|
||||
Utilities.Run("cmake", "--build . --config Release", null, cmakeFolder, Utilities.RunOptions.ThrowExceptionOnError, envVars);
|
||||
|
||||
|
||||
// Deploy binaries
|
||||
var libs = new[]
|
||||
{
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Flax.Deps.Dependencies
|
||||
var buildDir = Path.Combine(root, "build");
|
||||
|
||||
// Build for Mac
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
SetupDirectory(buildDir, true);
|
||||
RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config);
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Flax.Deps.Dependencies
|
||||
case TargetPlatform.Linux:
|
||||
{
|
||||
// Build for Linux
|
||||
var settings = new []
|
||||
var settings = new[]
|
||||
{
|
||||
"--without-librtmp",
|
||||
"--without-ssl",
|
||||
@@ -126,7 +126,7 @@ namespace Flax.Deps.Dependencies
|
||||
case TargetPlatform.Mac:
|
||||
{
|
||||
// Build for Mac
|
||||
var settings = new []
|
||||
var settings = new[]
|
||||
{
|
||||
"--with-secure-transport",
|
||||
"--without-librtmp",
|
||||
@@ -137,7 +137,7 @@ namespace Flax.Deps.Dependencies
|
||||
"--enable-static",
|
||||
"-disable-ldap --disable-sspi --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb",
|
||||
};
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
var arch = GetAppleArchName(architecture);
|
||||
var archName = arch + "-apple-darwin19";
|
||||
@@ -146,7 +146,7 @@ namespace Flax.Deps.Dependencies
|
||||
var compilerFlags = string.Format("-mmacosx-version-min={0} -arch {1}", Configuration.MacOSXMinVer, arch);
|
||||
var envVars = new Dictionary<string, string>
|
||||
{
|
||||
{ "CC", "clang" },
|
||||
{ "CC", "clang" },
|
||||
{ "CXX", "clang" },
|
||||
{ "CFLAGS", compilerFlags },
|
||||
{ "CXXFLAGS", compilerFlags },
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace Flax.Deps.Dependencies
|
||||
case TargetPlatform.Mac:
|
||||
{
|
||||
// Build for Mac
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
SetupDirectory(buildDir, true);
|
||||
RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release");
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Flax.Deps.Dependencies
|
||||
};
|
||||
|
||||
// Build for Mac
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
RunCmake(root, platform, architecture, cmakeArgs);
|
||||
Utilities.Run("cmake", string.Format("--build . --config {0} --target install", configuration), null, buildDir, Utilities.RunOptions.ConsoleLogOutput);
|
||||
|
||||
@@ -217,7 +217,7 @@ namespace Flax.Deps.Dependencies
|
||||
case TargetPlatform.Mac:
|
||||
{
|
||||
// Build for Mac
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
SetupDirectory(buildDir, true);
|
||||
RunCmake(buildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release");
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace Flax.Deps.Dependencies
|
||||
GitCheckout(oggRoot, "master", "4380566a44b8d5e85ad511c9c17eb04197863ec5");
|
||||
|
||||
// Build for Mac
|
||||
foreach (var architecture in new []{ TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
|
||||
{
|
||||
SetupDirectory(oggBuildDir, true);
|
||||
RunCmake(oggBuildDir, platform, architecture, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\"");
|
||||
|
||||
@@ -105,8 +105,7 @@ namespace Flax.Deps
|
||||
if (totalBytes.HasValue)
|
||||
progress.Update(totalBytesRead, totalBytes.Value);
|
||||
}
|
||||
}
|
||||
while (hasMoreToRead);
|
||||
} while (hasMoreToRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Flax.Deps
|
||||
Console.WriteLine();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Flax.Build.Platforms
|
||||
public static readonly AndroidSdk Instance = new AndroidSdk();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TargetPlatform[] Platforms => new []
|
||||
public override TargetPlatform[] Platforms => new[]
|
||||
{
|
||||
TargetPlatform.Windows,
|
||||
TargetPlatform.Linux,
|
||||
|
||||
@@ -90,15 +90,15 @@ namespace Flax.Build.Platforms
|
||||
/// Returns true if running an x64 binary an arm64 host machine.
|
||||
/// </summary>
|
||||
public unsafe static bool GetProcessIsTranslated()
|
||||
{
|
||||
int ret = 0;
|
||||
ulong size = sizeof(int);
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, null, 0) == -1)
|
||||
return false;
|
||||
return ret != 0;
|
||||
}
|
||||
{
|
||||
int ret = 0;
|
||||
ulong size = sizeof(int);
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, null, 0) == -1)
|
||||
return false;
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
[DllImport("c")]
|
||||
private static unsafe extern int sysctlbyname(string name, void* oldp, ulong* oldlenp, void* newp, ulong newlen);
|
||||
private static unsafe extern int sysctlbyname(string name, void* oldp, ulong* oldlenp, void* newp, ulong newlen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,7 +702,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
{
|
||||
// Build command for the build tool
|
||||
var buildToolPath = Path.ChangeExtension(typeof(Builder).Assembly.Location, null);
|
||||
|
||||
|
||||
var targetsFileContent = new StringBuilder();
|
||||
targetsFileContent.AppendLine("<Project>");
|
||||
targetsFileContent.AppendLine(" <!-- Custom Flax.Build scripts for C# projects. -->");
|
||||
|
||||
@@ -410,8 +410,7 @@ namespace Flax.Build.Projects.VisualStudioCode
|
||||
json.AddField("stopAtEntry", false);
|
||||
json.AddField("externalConsole", true);
|
||||
break;
|
||||
case TargetPlatform.Linux:
|
||||
break;
|
||||
case TargetPlatform.Linux: break;
|
||||
}
|
||||
}
|
||||
json.EndObject();
|
||||
@@ -622,7 +621,7 @@ namespace Flax.Build.Projects.VisualStudioCode
|
||||
json.AddField("**/Output", true);
|
||||
json.AddField("**/*.flax", true);
|
||||
json.EndObject();
|
||||
|
||||
|
||||
// Extension settings
|
||||
json.AddField("omnisharp.useModernNet", true);
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@ namespace Flax.Build
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidPlatformException(buildData.Platform.Target);
|
||||
default: throw new InvalidPlatformException(buildData.Platform.Target);
|
||||
}
|
||||
var result = sb.ToString();
|
||||
BindingsGenerator.PutStringBuilder(sb);
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Flax.Build
|
||||
System = 0x00001000,
|
||||
Task = 0x00002000
|
||||
}
|
||||
|
||||
|
||||
public enum Icon : uint
|
||||
{
|
||||
Warning = 0x00000030,
|
||||
|
||||
Reference in New Issue
Block a user