Lua向C#逻辑迁移 一期 #13

将整个插件代码上传
This commit is contained in:
2025-10-26 21:48:39 +08:00
parent 56994b3927
commit 648386cd73
785 changed files with 53683 additions and 2 deletions

View File

@ -0,0 +1,113 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class AutocastExporter
{
private static readonly ConcurrentDictionary<UhtStruct, List<UhtFunction>> ExportedAutocasts = new();
public static void AddAutocastFunction(UhtStruct conversionStruct, UhtFunction function)
{
if (!ExportedAutocasts.TryGetValue(conversionStruct, out List<UhtFunction>? value))
{
value = new List<UhtFunction>();
ExportedAutocasts[conversionStruct] = value;
}
value!.Add(function);
}
public static void StartExportingAutocastFunctions(List<Task> tasks)
{
foreach (KeyValuePair<UhtStruct, List<UhtFunction>> pair in ExportedAutocasts)
{
tasks.Add(Program.Factory.CreateTask(_ =>
{
ExportAutocast(pair.Key, pair.Value);
})!);
}
}
static void ExportAutocast(UhtStruct conversionStruct, List<UhtFunction> functions)
{
GeneratorStringBuilder stringBuilder = new();
stringBuilder.GenerateTypeSkeleton(conversionStruct);
stringBuilder.DeclareType(conversionStruct, "struct", conversionStruct.GetStructName());
string conversionStructName = conversionStruct.GetFullManagedName();
HashSet<UhtFunction> exportedFunctions = new();
foreach (UhtFunction function in functions)
{
string returnType = PropertyTranslatorManager.GetTranslator(function.ReturnProperty!)!.GetManagedType(function.ReturnProperty!);
string functionCall = $"{function.Outer!.GetFullManagedName()}.{function.GetFunctionName()}";
if (SharesSignature(function, exportedFunctions) || ReturnValueIsSameAsParameter(function))
{
continue;
}
stringBuilder.AppendLine($"public static implicit operator {returnType}({conversionStructName} value) => {functionCall}(value);");
exportedFunctions.Add(function);
}
stringBuilder.CloseBrace();
string directory = FileExporter.GetDirectoryPath(conversionStruct.Package);
string fileName = $"{conversionStruct.EngineName}.Autocast";
FileExporter.SaveGlueToDisk(conversionStruct.Package, directory, fileName, stringBuilder.ToString());
}
static bool SharesSignature(UhtFunction function, IEnumerable<UhtFunction> otherFunctions)
{
foreach (UhtFunction otherFunction in otherFunctions)
{
if (function == otherFunction)
{
continue;
}
bool sharesSignature = true;
int parameterCount = function.Children.Count;
for (int i = 0; i < parameterCount; i++)
{
UhtProperty parameter = (UhtProperty) function.Children[i];
UhtProperty otherParameter = (UhtProperty) otherFunction.Children[i];
if (parameter.IsSameType(otherParameter))
{
continue;
}
sharesSignature = false;
break;
}
if (sharesSignature)
{
return true;
}
}
return false;
}
static bool ReturnValueIsSameAsParameter(UhtFunction function)
{
UhtProperty returnProperty = function.ReturnProperty!;
foreach (UhtType uhtType in function.Children)
{
UhtProperty parameter = (UhtProperty) uhtType;
if (parameter != returnProperty && parameter.IsSameType(returnProperty))
{
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,199 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using EpicGames.Core;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Tooltip;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class ClassExporter
{
public static void ExportClass(UhtClass classObj, bool isManualExport)
{
GeneratorStringBuilder stringBuilder = new();
string typeNameSpace = classObj.GetNamespace();
List<UhtFunction> exportedFunctions = new();
List<UhtFunction> exportedOverrides = new();
Dictionary<string, GetterSetterPair> exportedGetterSetters = new();
Dictionary<string, GetterSetterPair> getSetOverrides = new();
ScriptGeneratorUtilities.GetExportedFunctions(classObj, exportedFunctions,
exportedOverrides,
exportedGetterSetters, getSetOverrides);
List<UhtProperty> exportedProperties = new List<UhtProperty>();
Dictionary<UhtProperty, GetterSetterPair> getSetBackedProperties = new();
ScriptGeneratorUtilities.GetExportedProperties(classObj, exportedProperties, getSetBackedProperties);
List<UhtClass> interfaces = classObj.GetInterfaces();
bool nullableEnabled = classObj.HasMetadata(UhtTypeUtilities.NullableEnable);
stringBuilder.GenerateTypeSkeleton(typeNameSpace, nullableEnabled: nullableEnabled);
stringBuilder.AppendTooltip(classObj);
AttributeBuilder attributeBuilder = new AttributeBuilder(classObj);
if (classObj.ClassFlags.HasAnyFlags(EClassFlags.Abstract))
{
attributeBuilder.AddArgument("ClassFlags.Abstract");
}
attributeBuilder.AddGeneratedTypeAttribute(classObj);
attributeBuilder.Finish();
stringBuilder.AppendLine(attributeBuilder.ToString());
string superClassName;
if (classObj.SuperClass != null)
{
superClassName = classObj.SuperClass.GetFullManagedName();
}
else
{
superClassName = "UnrealSharp.Core.UnrealSharpObject";
}
stringBuilder.DeclareType(classObj, "class", classObj.GetStructName(), superClassName, nativeInterfaces: interfaces);
// For manual exports we just want to generate attributes
if (!isManualExport)
{
StaticConstructorUtilities.ExportStaticConstructor(stringBuilder, classObj,
exportedProperties,
exportedFunctions,
exportedGetterSetters,
getSetBackedProperties,
exportedOverrides);
HashSet<string> exportedPropertyNames = new();
HashSet<string> exportedFunctionNames = new();
ExportClassProperties(stringBuilder, exportedProperties, exportedPropertyNames);
ExportGetSetProperties(stringBuilder, getSetBackedProperties, exportedPropertyNames, exportedFunctionNames);
ExportCustomProperties(stringBuilder, exportedGetterSetters, exportedPropertyNames, exportedFunctionNames);
ExportClassFunctions(classObj, stringBuilder, exportedFunctions, exportedFunctionNames);
ExportGetSetOverrides(stringBuilder, getSetOverrides, exportedPropertyNames, exportedFunctionNames);
ExportOverrides(stringBuilder, exportedOverrides, exportedFunctionNames);
stringBuilder.AppendLine();
}
stringBuilder.CloseBrace();
FileExporter.SaveGlueToDisk(classObj, stringBuilder);
}
static void ExportClassProperties(GeneratorStringBuilder generatorStringBuilder, List<UhtProperty> exportedProperties, HashSet<string> exportedPropertyNames)
{
foreach (UhtProperty property in exportedProperties)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportProperty(generatorStringBuilder, property);
exportedPropertyNames.Add(property.SourceName);
}
}
public static void ExportGetSetProperties(GeneratorStringBuilder builder,
Dictionary<UhtProperty, GetterSetterPair> getSetBackedProperties,
HashSet<string> exportedPropertyNames,
HashSet<string> exportedFunctionNames)
{
Dictionary<UhtFunction, FunctionExporter> exportedGetterSetters = new();
foreach (KeyValuePair<UhtProperty, GetterSetterPair> pair in getSetBackedProperties)
{
UhtProperty property = pair.Key;
GetterSetterPair getterSetterPair = pair.Value;
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportGetSetProperty(builder, getterSetterPair, property, exportedGetterSetters, exportedFunctionNames);
exportedPropertyNames.Add(getterSetterPair.PropertyName);
foreach (UhtFunction function in getterSetterPair.Accessors)
{
exportedFunctionNames.Add(function.SourceName);
}
}
}
private static void ExportGetSetOverrides(GeneratorStringBuilder builder, Dictionary<string, GetterSetterPair> getSetBackedProperties,
HashSet<string> exportedPropertyNames, HashSet<string> exportedFunctionNames)
{
foreach (KeyValuePair<string, GetterSetterPair> pair in getSetBackedProperties)
{
if (pair.Value.Property == null)
{
throw new InvalidDataException($"Property '{pair.Value.PropertyName}' does not have a UProperty");
}
UhtFunction firstAccessor = pair.Value.Accessors.First();
UhtProperty firstProperty = pair.Value.Property;
string propertyName = pair.Value.PropertyName;
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(firstProperty)!;
builder.TryAddWithEditor(firstAccessor);
translator.ExportCustomProperty(builder, pair.Value, propertyName, firstProperty,
exportedPropertyNames.Contains(propertyName), exportedFunctionNames);
builder.TryEndWithEditor(firstAccessor);
exportedPropertyNames.Add(propertyName);
foreach (UhtFunction function in pair.Value.Accessors)
{
exportedFunctionNames.Add(function.SourceName);
}
}
}
static void ExportOverrides(GeneratorStringBuilder builder, List<UhtFunction> exportedOverrides, HashSet<string> exportedFunctionNames)
{
foreach (UhtFunction function in exportedOverrides)
{
FunctionExporter.ExportOverridableFunction(builder, function, exportedFunctionNames);
exportedFunctionNames.Add(function.SourceName);
}
}
static void ExportClassFunctions(UhtClass owner, GeneratorStringBuilder builder, List<UhtFunction> exportedFunctions,
HashSet<string> exportedFunctionNames)
{
bool isBlueprintFunctionLibrary = owner.IsChildOf(Program.BlueprintFunctionLibrary);
foreach (UhtFunction function in exportedFunctions)
{
if (function.HasAllFlags(EFunctionFlags.Static) && isBlueprintFunctionLibrary)
{
FunctionExporter.TryAddExtensionMethod(function);
}
FunctionExporter.ExportFunction(builder, function, FunctionType.Normal, exportedFunctionNames);
exportedFunctionNames.Add(function.SourceName);
}
}
public static void ExportCustomProperties(GeneratorStringBuilder builder,
Dictionary<string, GetterSetterPair> exportedGetterSetters,
HashSet<string> exportedPropertyNames,
HashSet<string> exportedFunctionNames)
{
foreach (KeyValuePair<string, GetterSetterPair> pair in exportedGetterSetters)
{
if (pair.Value.Property == null)
{
throw new InvalidDataException($"Property '{pair.Value.PropertyName}' does not have a UProperty");
}
UhtFunction firstAccessor = pair.Value.Accessors.First();
UhtProperty firstProperty = pair.Value.Property;
string propertyName = pair.Value.PropertyName;
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(firstProperty)!;
builder.TryAddWithEditor(firstAccessor);
translator.ExportCustomProperty(builder, pair.Value, propertyName, firstProperty, exportedFunctionNames: exportedFunctionNames);
builder.TryEndWithEditor(firstAccessor);
exportedPropertyNames.Add(propertyName);
foreach (UhtFunction function in pair.Value.Accessors)
{
exportedFunctionNames.Add(function.SourceName);
}
}
}
}

View File

@ -0,0 +1,60 @@
using EpicGames.Core;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class DelegateExporter
{
public static void ExportDelegate(UhtFunction function)
{
string delegateName = DelegateBasePropertyTranslator.GetDelegateName(function);
string delegateNamespace = function.GetNamespace();
GeneratorStringBuilder builder = new();
builder.GenerateTypeSkeleton(delegateNamespace);
builder.AppendLine();
string superClass;
if (function.HasAllFlags(EFunctionFlags.MulticastDelegate))
{
superClass = $"MulticastDelegate<{delegateName}>";
}
else
{
superClass = $"Delegate<{delegateName}>";
}
FunctionExporter functionExporter = FunctionExporter.ExportDelegateSignature(builder, function, delegateName);
builder.DeclareType(function, "class", $"U{delegateName}", superClass);
FunctionExporter.ExportDelegateGlue(builder, functionExporter);
builder.AppendLine("static public void InitializeUnrealDelegate(IntPtr nativeDelegateProperty)");
builder.OpenBrace();
ExportDelegateFunctionStaticConstruction(builder, function);
builder.CloseBrace();
builder.CloseBrace();
FileExporter.SaveGlueToDisk(function, builder);
}
private static void ExportDelegateFunctionStaticConstruction(GeneratorStringBuilder builder, UhtFunction function)
{
string delegateName = function.SourceName;
builder.AppendLine($"{delegateName}_NativeFunction = FMulticastDelegatePropertyExporter.CallGetSignatureFunction(nativeDelegateProperty);");
if (function.HasParameters)
{
builder.AppendLine($"{delegateName}_ParamsSize = {ExporterCallbacks.UFunctionCallbacks}.CallGetNativeFunctionParamsSize({delegateName}_NativeFunction);");
}
foreach (UhtProperty parameter in function.Properties)
{
PropertyTranslator propertyTranslator = PropertyTranslatorManager.GetTranslator(parameter)!;
propertyTranslator.ExportParameterStaticConstructor(builder, parameter, function, parameter.SourceName, delegateName);
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.Tooltip;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class EnumExporter
{
public static void ExportEnum(UhtEnum enumObj)
{
GeneratorStringBuilder stringBuilder = new GeneratorStringBuilder();
string moduleName = enumObj.GetNamespace();
stringBuilder.GenerateTypeSkeleton(moduleName);
stringBuilder.AppendTooltip(enumObj);
AttributeBuilder attributeBuilder = new AttributeBuilder(enumObj);
attributeBuilder.AddGeneratedTypeAttribute(enumObj);
attributeBuilder.Finish();
stringBuilder.AppendLine(attributeBuilder.ToString());
string underlyingType = UnderlyingTypeToString(enumObj.UnderlyingType);
stringBuilder.DeclareType(enumObj, "enum", enumObj.GetStructName(), underlyingType, isPartial: false);
int enumValuesCount = enumObj.EnumValues.Count;
for (int i = 0; i < enumValuesCount; i++)
{
UhtEnumValue enumValue = enumObj.EnumValues[i];
string toolTip = enumObj.GetMetadata("Tooltip", i);
stringBuilder.AppendTooltip(toolTip);
string cleanValueName = ScriptGeneratorUtilities.GetCleanEnumValueName(enumObj, enumValue);
string value = enumValue.Value == -1 ? "," : $" = {enumValue.Value},";
stringBuilder.AppendLine($"{cleanValueName}{value}");
}
stringBuilder.CloseBrace();
FileExporter.SaveGlueToDisk(enumObj, stringBuilder);
}
public static string UnderlyingTypeToString(UhtEnumUnderlyingType underlyingType)
{
return underlyingType switch
{
UhtEnumUnderlyingType.Unspecified => "",
UhtEnumUnderlyingType.Uint8 => "byte",
UhtEnumUnderlyingType.Int8 => "sbyte",
UhtEnumUnderlyingType.Int16 => "short",
UhtEnumUnderlyingType.Int => "int",
UhtEnumUnderlyingType.Int32 => "int",
UhtEnumUnderlyingType.Int64 => "long",
UhtEnumUnderlyingType.Uint16 => "ushort",
UhtEnumUnderlyingType.Uint32 => "uint",
UhtEnumUnderlyingType.Uint64 => "ulong",
_ => throw new ArgumentOutOfRangeException(nameof(underlyingType), underlyingType, null)
};
}
}

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class ExtensionsClassExporter
{
public static void ExportExtensionsClass(UhtPackage package, List<ExtensionMethod> extensionMethods)
{
Dictionary<UhtType, List<ExtensionMethod>?> libraryToExtensionMethod = new();
foreach (ExtensionMethod extensionMethod in extensionMethods)
{
UhtType outerClass = extensionMethod.Function.Outer!;
if (!libraryToExtensionMethod.TryGetValue(outerClass, out List<ExtensionMethod>? libraryExtensions))
{
libraryExtensions = new List<ExtensionMethod>();
libraryToExtensionMethod[outerClass] = libraryExtensions;
}
libraryExtensions!.Add(extensionMethod);
}
foreach (KeyValuePair<UhtType, List<ExtensionMethod>?> pair in libraryToExtensionMethod)
{
UhtType libraryClass = pair.Key;
List<ExtensionMethod>? libraryExtensions = pair.Value;
ExportLibrary(package, libraryClass, libraryExtensions!);
}
}
public static void ExportLibrary(UhtPackage package, UhtType libraryClass, List<ExtensionMethod> extensionMethods)
{
string typeNamespace = package.GetNamespace();
string className = $"{libraryClass.EngineName}_Extensions";
GeneratorStringBuilder stringBuilder = new();
stringBuilder.GenerateTypeSkeleton(typeNamespace);
stringBuilder.DeclareType(package, "static class", className, null, false);
foreach (ExtensionMethod extensionMethod in extensionMethods)
{
FunctionExporter exporter = new FunctionExporter(extensionMethod);
exporter.Initialize(OverloadMode.AllowOverloads, EFunctionProtectionMode.UseUFunctionProtection, EBlueprintVisibility.Call);
exporter.ExportExtensionMethodOverloads(stringBuilder);
exporter.ExportExtensionMethod(stringBuilder);
}
stringBuilder.CloseBrace();
string directory = FileExporter.GetDirectoryPath(package);
FileExporter.SaveGlueToDisk(package, directory, className, stringBuilder.ToString());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
using EpicGames.Core;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public enum GetterSetterMode
{
Get,
Set
}
public class GetterSetterFunctionExporter : FunctionExporter
{
private readonly UhtProperty _propertyGetterSetter;
private readonly GetterSetterMode _getterSetterMode;
private string _outParameterName;
public static GetterSetterFunctionExporter Create(UhtFunction function,
UhtProperty propertyGetterSetter,
GetterSetterMode getterSetterMode,
EFunctionProtectionMode protectionMode)
{
GetterSetterFunctionExporter exporter = new GetterSetterFunctionExporter(function, propertyGetterSetter, getterSetterMode);
exporter.Initialize(OverloadMode.SuppressOverloads, protectionMode, EBlueprintVisibility.GetterSetter);
return exporter;
}
private GetterSetterFunctionExporter(UhtFunction function, UhtProperty propertyGetterSetter, GetterSetterMode getterSetterMode) : base(function)
{
_outParameterName = string.Empty;
_propertyGetterSetter = propertyGetterSetter;
_getterSetterMode = getterSetterMode;
Initialize(OverloadMode.SuppressOverloads, EFunctionProtectionMode.OverrideWithInternal, EBlueprintVisibility.GetterSetter);
}
protected override string GetParameterName(UhtProperty parameter)
{
return _getterSetterMode == GetterSetterMode.Get ? _propertyGetterSetter.GetParameterName() : "value";
}
protected override string MakeOutMarshalDestination(UhtProperty parameter, PropertyTranslator propertyTranslator, GeneratorStringBuilder builder)
{
_outParameterName = GetParameterName(parameter) + "_Out";
builder.AppendLine($"{propertyTranslator.GetManagedType(parameter)} {_outParameterName};");
return _outParameterName;
}
protected override void ExportReturnStatement(GeneratorStringBuilder builder)
{
if (Function.ReturnProperty != null && Function.ReturnProperty.IsSameType(_propertyGetterSetter))
{
string castOperation = _propertyGetterSetter.HasAllFlags(EPropertyFlags.BlueprintReadOnly)
? $"({ReturnValueTranslator!.GetManagedType(_propertyGetterSetter)})" : string.Empty;
builder.AppendLine($"return {castOperation}returnValue;");
}
else if (Function.ReturnProperty != null)
{
// Types differ (e.g., getter returns FText, property bound as string). Still return and rely on
// available implicit/user-defined conversions on the managed types (FText -> string, etc.).
builder.AppendLine("return returnValue;");
}
if (string.IsNullOrEmpty(_outParameterName))
{
return;
}
builder.AppendLine($"return {_outParameterName};");
}
}

View File

@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using EpicGames.Core;
using EpicGames.UHT.Types;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Tooltip;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class InterfaceExporter
{
public static void ExportInterface(UhtClass interfaceObj)
{
GeneratorStringBuilder stringBuilder = new();
bool nullableEnabled = interfaceObj.HasMetadata(UhtTypeUtilities.NullableEnable);
string interfaceName = interfaceObj.GetStructName();
string typeNamespace = interfaceObj.GetNamespace();
stringBuilder.GenerateTypeSkeleton(typeNamespace, nullableEnabled: nullableEnabled);
stringBuilder.AppendTooltip(interfaceObj);
AttributeBuilder attributeBuilder = new AttributeBuilder(interfaceObj);
attributeBuilder.AddGeneratedTypeAttribute(interfaceObj);
attributeBuilder.Finish();
stringBuilder.AppendLine(attributeBuilder.ToString());
stringBuilder.DeclareType(interfaceObj, "interface", interfaceName);
stringBuilder.AppendLine();
stringBuilder.AppendLine($"static {interfaceName} Wrap(UnrealSharp.CoreUObject.UObject obj)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return new {interfaceName}Wrapper(obj);");
stringBuilder.CloseBrace();
List<UhtFunction> exportedFunctions = new();
List<UhtFunction> exportedOverrides = new();
Dictionary<string, GetterSetterPair> exportedGetterSetters = new();
Dictionary<string, GetterSetterPair> getSetOverrides = new();
if (interfaceObj.AlternateObject is UhtClass alternateObject)
{
ScriptGeneratorUtilities.GetExportedFunctions(alternateObject, exportedFunctions, exportedOverrides, exportedGetterSetters, getSetOverrides);
}
ScriptGeneratorUtilities.GetExportedFunctions(interfaceObj, exportedFunctions, exportedOverrides, exportedGetterSetters, getSetOverrides);
ExportInterfaceProperties(stringBuilder, exportedGetterSetters);
ExportInterfaceFunctions(stringBuilder, exportedFunctions);
ExportInterfaceFunctions(stringBuilder, exportedOverrides);
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine($"internal sealed class {interfaceName}Wrapper : {interfaceName}, UnrealSharp.CoreUObject.IScriptInterface");
stringBuilder.OpenBrace();
stringBuilder.AppendLine("public UnrealSharp.CoreUObject.UObject Object { get; }");
stringBuilder.AppendLine("private IntPtr NativeObject => Object.NativeObject;");
stringBuilder.AppendLine();
stringBuilder.AppendLine($"internal {interfaceName}Wrapper(UnrealSharp.CoreUObject.UObject obj)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine("Object = obj;");
stringBuilder.CloseBrace();
StaticConstructorUtilities.ExportStaticConstructor(stringBuilder, interfaceObj,
new List<UhtProperty>(),
exportedFunctions,
exportedGetterSetters,
new Dictionary<UhtProperty, GetterSetterPair>(),
exportedOverrides,
false, interfaceName + "Wrapper");
ClassExporter.ExportCustomProperties(stringBuilder, exportedGetterSetters, [], []);
ExportWrapperFunctions(stringBuilder, exportedFunctions);
ExportWrapperFunctions(stringBuilder, exportedOverrides);
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static class {interfaceName}Marshaller");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"public static void ToNative(IntPtr nativeBuffer, int arrayIndex, {interfaceName} obj)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"UnrealSharp.CoreUObject.ScriptInterfaceMarshaller<{interfaceName}>.ToNative(nativeBuffer, arrayIndex, obj);");
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {interfaceName} FromNative(IntPtr nativeBuffer, int arrayIndex)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return UnrealSharp.CoreUObject.ScriptInterfaceMarshaller<{interfaceName}>.FromNative(nativeBuffer, arrayIndex);");
stringBuilder.CloseBrace();
stringBuilder.CloseBrace();
FileExporter.SaveGlueToDisk(interfaceObj, stringBuilder);
}
static void ExportInterfaceProperties(GeneratorStringBuilder stringBuilder,
Dictionary<string, GetterSetterPair> exportedGetterSetters)
{
foreach (var (_, getterSetterPair) in exportedGetterSetters)
{
if (getterSetterPair.Property is null)
{
throw new InvalidDataException("Properties should have a UProperty associated with them.");
}
UhtFunction firstAccessor = getterSetterPair.Accessors.First();
UhtProperty firstProperty = getterSetterPair.Property;
string propertyName = getterSetterPair.PropertyName;
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(firstProperty)!;
stringBuilder.TryAddWithEditor(firstAccessor);
string protection = firstProperty.GetProtection();
stringBuilder.AppendTooltip(firstProperty);
string managedType = translator.GetManagedType(firstProperty);
stringBuilder.AppendLine($"{managedType} {propertyName}");
stringBuilder.OpenBrace();
if (getterSetterPair.Getter is not null)
{
AppendPropertyFunctionDeclaration(stringBuilder, getterSetterPair.Getter);
stringBuilder.AppendLine("get;");
}
if (getterSetterPair.Setter is not null)
{
AppendPropertyFunctionDeclaration(stringBuilder, getterSetterPair.Setter);
stringBuilder.AppendLine("set;");
}
stringBuilder.CloseBrace();
stringBuilder.TryEndWithEditor(firstAccessor);
}
}
private static void AppendPropertyFunctionDeclaration(GeneratorStringBuilder stringBuilder, UhtFunction function)
{
AttributeBuilder attributeBuilder = new AttributeBuilder(function);
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent))
{
attributeBuilder.AddArgument("FunctionFlags.BlueprintEvent");
}
attributeBuilder.AddGeneratedTypeAttribute(function);
attributeBuilder.Finish();
stringBuilder.AppendLine(attributeBuilder.ToString());
}
static void ExportInterfaceFunctions(GeneratorStringBuilder stringBuilder, List<UhtFunction> exportedFunctions)
{
foreach (UhtFunction function in exportedFunctions)
{
FunctionExporter.ExportInterfaceFunction(stringBuilder, function);
}
}
static void ExportWrapperFunctions(GeneratorStringBuilder stringBuilder, List<UhtFunction> exportedFunctions)
{
foreach (UhtFunction function in exportedFunctions)
{
FunctionType functionType = function.FunctionFlags.HasFlag(EFunctionFlags.BlueprintEvent)
? FunctionType.BlueprintEvent
: FunctionType.Normal;
FunctionExporter.ExportFunction(stringBuilder, function, functionType);
}
}
}

View File

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using EpicGames.Core;
using EpicGames.UHT.Parsers;
using EpicGames.UHT.Tables;
using EpicGames.UHT.Tokenizer;
using EpicGames.UHT.Types;
using EpicGames.UHT.Utils;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
[UnrealHeaderTool]
public static class NativeBindExporter
{
private struct NativeBindMethod
{
public readonly string MethodName;
public NativeBindMethod(string methodName)
{
MethodName = methodName;
}
}
private class NativeBindTypeInfo
{
public readonly UhtType Type;
public readonly List<NativeBindMethod> Methods;
public NativeBindTypeInfo(UhtType type, List<NativeBindMethod> methods)
{
Type = type;
Methods = methods;
}
}
private static readonly Dictionary<UhtHeaderFile, List<NativeBindTypeInfo>> NativeBindTypes = new();
[UhtExporter(Name = "UnrealSharpNativeGlue",
Description = "Exports Native Glue",
Options = UhtExporterOptions.Default,
ModuleName = "UnrealSharpCore", CppFilters = new string [] { "*.unrealsharp.gen.cpp" })]
private static void Main(IUhtExportFactory factory)
{
ExportBindMethods(factory);
}
[UhtKeyword(Extends = UhtTableNames.Default, Keyword = "UNREALSHARP_FUNCTION")]
private static UhtParseResult UNREALSHARP_FUNCTIONKeyword(UhtParsingScope topScope, UhtParsingScope actionScope, ref UhtToken token)
{
return ParseUnrealSharpBind(topScope, actionScope, ref token);
}
[UhtSpecifier(Extends = UhtTableNames.Function, ValueType = UhtSpecifierValueType.Legacy)]
private static void ScriptCallableSpecifier(UhtSpecifierContext specifierContext)
{
UhtFunction function = (UhtFunction)specifierContext.Type;
function.MetaData.Add("ScriptCallable", "");
}
private static UhtParseResult ParseUnrealSharpBind(UhtParsingScope topScope, UhtParsingScope actionScope, ref UhtToken token)
{
UhtHeaderFile headerFile = topScope.ScopeType.HeaderFile;
topScope.TokenReader.EnableRecording();
topScope.TokenReader
.Require('(')
.Require(')')
.Require("static")
.ConsumeUntil('(');
int recordedTokensCount = topScope.TokenReader.RecordedTokens.Count;
string methodName = topScope.TokenReader.RecordedTokens[recordedTokensCount - 2].Value.ToString();
topScope.TokenReader.DisableRecording();
NativeBindMethod methodInfo = new(methodName);
if (!NativeBindTypes.TryGetValue(headerFile, out List<NativeBindTypeInfo>? value))
{
value = new List<NativeBindTypeInfo>();
NativeBindTypes.Add(headerFile, value);
}
UhtType type = topScope.ScopeType;
NativeBindTypeInfo? nativeBindTypeInfo = null;
foreach (NativeBindTypeInfo bindTypeInfo in value)
{
if (bindTypeInfo.Type != type)
{
continue;
}
nativeBindTypeInfo = bindTypeInfo;
break;
}
if (nativeBindTypeInfo == null)
{
nativeBindTypeInfo = new NativeBindTypeInfo(type, new List<NativeBindMethod>());
value.Add(nativeBindTypeInfo);
}
nativeBindTypeInfo.Methods.Add(methodInfo);
return UhtParseResult.Handled;
}
public static void ExportBindMethods(IUhtExportFactory factory)
{
foreach (KeyValuePair<UhtHeaderFile, List<NativeBindTypeInfo>> bindMethod in NativeBindTypes)
{
UhtHeaderFile headerFile = bindMethod.Key;
List<NativeBindTypeInfo> containingTypesInHeader = bindMethod.Value;
GeneratorStringBuilder builder = new();
builder.AppendLine("#include \"UnrealSharpBinds.h\"");
builder.AppendLine($"#include \"{headerFile.FilePath}\"");
builder.AppendLine();
foreach (NativeBindTypeInfo containingType in containingTypesInHeader)
{
UhtType topType = containingType.Type;
List<NativeBindMethod> methods = containingType.Methods;
string typeName = $"Z_Construct_U{topType.EngineClassName}_UnrealSharp_Binds_" + topType.SourceName;
builder.Append($"struct {typeName}");
builder.OpenBrace();
foreach (NativeBindMethod method in methods)
{
builder.AppendLine($"static const FCSExportedFunction UnrealSharpBind_{method.MethodName};");
}
builder.CloseBrace();
builder.Append(";");
foreach (NativeBindMethod method in methods)
{
string functionReference = $"{topType.SourceName}::{method.MethodName}";
builder.AppendLine($"const FCSExportedFunction {typeName}::UnrealSharpBind_{method.MethodName}");
builder.Append($" = FCSExportedFunction(\"{topType.EngineName}\", \"{method.MethodName}\", (void*)&{functionReference}, GetFunctionSize({functionReference}));");
}
builder.AppendLine();
builder.AppendLine();
}
UHTManifest.Module manifestModule;
#if UE_5_5_OR_LATER
manifestModule = headerFile.Module.Module;
#else
manifestModule= headerFile.Package.GetModule();
#endif
string outputDirectory = manifestModule.OutputDirectory;
string fileName = headerFile.FileNameWithoutExtension + ".unrealsharp.gen.cpp";
string filePath = Path.Combine(outputDirectory, fileName);
factory.CommitOutput(filePath, builder.ToString());
}
}
}

View File

@ -0,0 +1,541 @@
using EpicGames.UHT.Types;
using System;
using System.Collections.Generic;
using System.Text;
using UnrealSharpScriptGenerator.PropertyTranslators;
using UnrealSharpScriptGenerator.Tooltip;
using UnrealSharpScriptGenerator.Utilities;
namespace UnrealSharpScriptGenerator.Exporters;
public static class StructExporter
{
public static void ExportStruct(UhtScriptStruct structObj, bool isManualExport)
{
GeneratorStringBuilder stringBuilder = new();
List<UhtProperty> exportedProperties = new();
Dictionary<UhtProperty, GetterSetterPair> getSetBackedProperties = new();
List<UhtStruct> inheritanceHierarchy = new();
UhtStruct? currentStruct = structObj;
while (currentStruct is not null)
{
inheritanceHierarchy.Add(currentStruct);
currentStruct = currentStruct.SuperStruct;
}
inheritanceHierarchy.Reverse();
foreach (UhtStruct inheritance in inheritanceHierarchy)
{
ScriptGeneratorUtilities.GetExportedProperties(inheritance, exportedProperties, getSetBackedProperties);
}
// Check there are not properties with the same name, remove otherwise
List<string> propertyNames = new();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetParameterName();
if (propertyNames.Contains(scriptName))
{
exportedProperties.RemoveAt(i);
i--;
}
else
{
propertyNames.Add(scriptName);
}
}
bool nullableEnabled = structObj.HasMetadata(UhtTypeUtilities.NullableEnable);
bool isRecordStruct = structObj.HasMetadata("RecordStruct");
bool isReadOnly = structObj.HasMetadata("ReadOnly");
bool useProperties = structObj.HasMetadata("UseProperties");
bool isBlittable = structObj.IsStructBlittable();
bool isCopyable = structObj.IsStructNativelyCopyable();
bool isDestructible = structObj.IsStructNativelyDestructible();
bool isEquatable = structObj.IsStructEquatable(exportedProperties);
string typeNameSpace = structObj.GetNamespace();
stringBuilder.GenerateTypeSkeleton(typeNameSpace, isBlittable, nullableEnabled);
stringBuilder.AppendTooltip(structObj);
AttributeBuilder attributeBuilder = new AttributeBuilder(structObj);
if (isBlittable)
{
attributeBuilder.AddIsBlittableAttribute();
attributeBuilder.AddStructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential);
}
attributeBuilder.AddGeneratedTypeAttribute(structObj);
attributeBuilder.Finish();
stringBuilder.AppendLine(attributeBuilder.ToString());
string structName = structObj.GetStructName();
List<string>? csInterfaces = null;
if (isBlittable || !isManualExport)
{
csInterfaces = new List<string> { $"MarshalledStruct<{structName}>" };
if (isDestructible)
{
csInterfaces.Add("IDisposable");
}
}
if (isEquatable)
{
// If null create the list and add the interface
(csInterfaces ??= new()).Add($"IEquatable<{structName}>");
}
stringBuilder.DeclareType(structObj, isRecordStruct ? "record struct" : "struct", structName, csInterfaces: csInterfaces,
modifiers: isReadOnly ? " readonly" : null);
if (isCopyable)
{
stringBuilder.AppendLine(isDestructible
? "private NativeStructHandle NativeHandle;"
: "private byte[] Allocation;");
}
// For manual exports we just want to generate attributes
if (!isManualExport)
{
List<string> reservedNames = GetReservedNames(exportedProperties);
ExportStructProperties(structObj, stringBuilder, exportedProperties, isBlittable, reservedNames, isReadOnly, useProperties);
}
if (isBlittable)
{
StaticConstructorUtilities.ExportStaticConstructor(stringBuilder, structObj,
new List<UhtProperty>(),
new List<UhtFunction>(),
new Dictionary<string, GetterSetterPair>(),
new Dictionary<UhtProperty, GetterSetterPair>(),
new List<UhtFunction>(),
true);
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {structName} FromNative(IntPtr buffer) => BlittableMarshaller<{structName}>.FromNative(buffer, 0);");
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public void ToNative(IntPtr buffer) => BlittableMarshaller<{structName}>.ToNative(buffer, 0, this);");
}
else if (!isManualExport)
{
stringBuilder.AppendLine();
StaticConstructorUtilities.ExportStaticConstructor(stringBuilder, structObj, exportedProperties,
new List<UhtFunction>(),
new Dictionary<string, GetterSetterPair>(),
new Dictionary<UhtProperty, GetterSetterPair>(),
new List<UhtFunction>());
stringBuilder.AppendLine();
ExportMirrorStructMarshalling(stringBuilder, structObj, exportedProperties);
if (isDestructible)
{
stringBuilder.AppendLine();
stringBuilder.AppendLine("public void Dispose()");
stringBuilder.OpenBrace();
stringBuilder.AppendLine("NativeHandle?.Dispose();");
stringBuilder.CloseBrace();
}
}
if (isEquatable)
{
ExportStructEquality(structObj, structName, stringBuilder, exportedProperties);
}
if (structObj.CanSupportArithmetic(exportedProperties))
{
ExportStructArithmetic(structObj, structName, stringBuilder, exportedProperties);
}
stringBuilder.CloseBrace();
if (!isBlittable && !isManualExport)
{
ExportStructMarshaller(stringBuilder, structObj);
}
FileExporter.SaveGlueToDisk(structObj, stringBuilder);
}
public static void ExportStructEquality(UhtStruct structObj, string structName, GeneratorStringBuilder stringBuilder, List<UhtProperty> exportedProperties)
{
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public override bool Equals(object? obj)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return obj is {structName} other && Equals(other);");
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public bool Equals({structName} other)");
stringBuilder.OpenBrace();
if (exportedProperties.Count == 0)
{
stringBuilder.AppendLine("return true;");
}
else
{
StringBuilder equalitySb = new StringBuilder();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
equalitySb.Append($"this.{scriptName} == other.{scriptName}");
if (i < exportedProperties.Count - 1)
{
equalitySb.Append(" && ");
}
}
stringBuilder.AppendLine($"return {equalitySb};");
}
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine("public override int GetHashCode()");
stringBuilder.OpenBrace();
if (exportedProperties.Count == 0)
{
stringBuilder.AppendLine("return 0;");
}
// More accurate hashcode equality
else if (exportedProperties.Count <= 8)
{
StringBuilder hashSb = new StringBuilder();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
hashSb.Append($"{scriptName}");
if (i < exportedProperties.Count - 1)
{
hashSb.Append(", ");
}
}
stringBuilder.AppendLine($"return HashCode.Combine({hashSb});");
}
// Fallback to xor for more than 8 properties as HashCode.Combine only supports up to 8 parameters
else
{
StringBuilder hashSb = new StringBuilder();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
hashSb.Append($"{scriptName}.GetHashCode()");
if (i < exportedProperties.Count - 1)
{
hashSb.Append(" ^ ");
}
}
stringBuilder.AppendLine($"return {hashSb};");
}
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static bool operator ==({structName} left, {structName} right)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine("return left.Equals(right);");
stringBuilder.CloseBrace();
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static bool operator !=({structName} left, {structName} right)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine("return !(left == right);");
stringBuilder.CloseBrace();
}
public static void ExportStructArithmetic(UhtStruct structObj, string structName, GeneratorStringBuilder stringBuilder, List<UhtProperty> exportedProperties)
{
// Addition operator
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {structName} operator +({structName} lhs, {structName} rhs)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return new {structName}");
stringBuilder.OpenBrace();
stringBuilder.AppendLine();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportPropertyArithmetic(stringBuilder, property, ArithmeticKind.Add);
if (i < exportedProperties.Count - 1)
{
stringBuilder.Append(", ");
stringBuilder.AppendLine();
}
}
stringBuilder.UnIndent();
stringBuilder.AppendLine("};");
stringBuilder.CloseBrace();
// Subtraction operator
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {structName} operator -({structName} lhs, {structName} rhs)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return new {structName}");
stringBuilder.OpenBrace();
stringBuilder.AppendLine();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportPropertyArithmetic(stringBuilder, property, ArithmeticKind.Subtract);
if (i < exportedProperties.Count - 1)
{
stringBuilder.Append(", ");
stringBuilder.AppendLine();
}
}
stringBuilder.UnIndent();
stringBuilder.AppendLine("};");
stringBuilder.CloseBrace();
// Multiplication operator
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {structName} operator *({structName} lhs, {structName} rhs)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return new {structName}");
stringBuilder.OpenBrace();
stringBuilder.AppendLine();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportPropertyArithmetic(stringBuilder, property, ArithmeticKind.Multiply);
if (i < exportedProperties.Count - 1)
{
stringBuilder.Append(", ");
stringBuilder.AppendLine();
}
}
stringBuilder.UnIndent();
stringBuilder.AppendLine("};");
stringBuilder.CloseBrace();
// Division operator
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {structName} operator /({structName} lhs, {structName} rhs)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return new {structName}");
stringBuilder.OpenBrace();
stringBuilder.AppendLine();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportPropertyArithmetic(stringBuilder, property, ArithmeticKind.Divide);
if (i < exportedProperties.Count - 1)
{
stringBuilder.Append(", ");
stringBuilder.AppendLine();
}
}
stringBuilder.UnIndent();
stringBuilder.AppendLine("};");
stringBuilder.CloseBrace();
// Modulo operator
stringBuilder.AppendLine();
stringBuilder.AppendLine($"public static {structName} operator %({structName} lhs, {structName} rhs)");
stringBuilder.OpenBrace();
stringBuilder.AppendLine($"return new {structName}");
stringBuilder.OpenBrace();
stringBuilder.AppendLine();
for (int i = 0; i < exportedProperties.Count; i++)
{
UhtProperty property = exportedProperties[i];
string scriptName = property.GetPropertyName();
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportPropertyArithmetic(stringBuilder, property, ArithmeticKind.Modulo);
if (i < exportedProperties.Count - 1)
{
stringBuilder.Append(", ");
stringBuilder.AppendLine();
}
}
stringBuilder.UnIndent();
stringBuilder.AppendLine("};");
stringBuilder.CloseBrace();
}
public static void ExportStructProperties(UhtStruct structObj, GeneratorStringBuilder stringBuilder, List<UhtProperty> exportedProperties, bool suppressOffsets, List<string> reservedNames, bool isReadOnly, bool useProperties)
{
foreach (UhtProperty property in exportedProperties)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
translator.ExportMirrorProperty(structObj, stringBuilder, property, suppressOffsets, reservedNames, isReadOnly, useProperties);
}
}
public static List<string> GetReservedNames(List<UhtProperty> properties)
{
List<string> reservedNames = new();
foreach (UhtProperty property in properties)
{
if (reservedNames.Contains(property.SourceName))
{
continue;
}
reservedNames.Add(property.SourceName);
}
return reservedNames;
}
public static void ExportStructMarshaller(GeneratorStringBuilder builder, UhtScriptStruct structObj)
{
string structName = structObj.GetStructName();
builder.AppendLine();
builder.AppendLine($"public static class {structName}Marshaller");
builder.OpenBrace();
builder.AppendLine($"public static {structName} FromNative(IntPtr nativeBuffer, int arrayIndex)");
builder.OpenBrace();
builder.AppendLine($"return new {structName}(nativeBuffer + (arrayIndex * GetNativeDataSize()));");
builder.CloseBrace();
builder.AppendLine();
builder.AppendLine($"public static void ToNative(IntPtr nativeBuffer, int arrayIndex, {structName} obj)");
builder.OpenBrace();
builder.AppendLine($"obj.ToNative(nativeBuffer + (arrayIndex * GetNativeDataSize()));");
builder.CloseBrace();
builder.AppendLine();
builder.AppendLine($"public static int GetNativeDataSize()");
builder.OpenBrace();
builder.AppendLine($"return {structName}.NativeDataSize;");
builder.CloseBrace();
builder.CloseBrace();
}
public static void ExportMirrorStructMarshalling(GeneratorStringBuilder builder, UhtScriptStruct structObj, List<UhtProperty> properties)
{
string structName = structObj.GetStructName();
bool isCopyable = structObj.IsStructNativelyCopyable();
bool isDestructible = structObj.IsStructNativelyDestructible();
if (isCopyable)
{
builder.AppendLine();
builder.AppendLine($"public {structName}()");
builder.OpenBrace();
builder.AppendLine(isDestructible
? "NativeHandle = new NativeStructHandle(NativeClassPtr);"
: "Allocation = new byte[NativeDataSize];");
builder.CloseBrace();
}
builder.AppendLine();
builder.AppendLine("[System.Diagnostics.CodeAnalysis.SetsRequiredMembers]");
builder.AppendLine($"public {structName}(IntPtr InNativeStruct)");
builder.OpenBrace();
builder.BeginUnsafeBlock();
if (isCopyable)
{
if (isDestructible)
{
builder.AppendLine("NativeHandle = new NativeStructHandle(NativeClassPtr);");
builder.AppendLine("fixed (NativeStructHandleData* StructDataPointer = &NativeHandle.Data)");
builder.OpenBrace();
builder.AppendLine($"IntPtr AllocationPointer = {ExporterCallbacks.UScriptStructCallbacks}.CallGetStructLocation(StructDataPointer, NativeClassPtr);");
}
else
{
builder.AppendLine("Allocation = new byte[NativeDataSize];");
builder.AppendLine("fixed (byte* AllocationPointer = Allocation)");
builder.OpenBrace();
}
builder.AppendLine($"{ExporterCallbacks.UScriptStructCallbacks}.CallNativeCopy(NativeClassPtr, InNativeStruct, (nint) AllocationPointer);");
builder.CloseBrace();
}
else
{
foreach (UhtProperty property in properties)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
string scriptName = property.GetPropertyName();
string assignmentOrReturn = $"{scriptName} =";
string offsetName = $"{property.SourceName}_Offset";
builder.TryAddWithEditor(property);
translator.ExportFromNative(builder, property, property.SourceName, assignmentOrReturn, "InNativeStruct", offsetName, false, false);
builder.TryEndWithEditor(property);
}
}
builder.EndUnsafeBlock();
builder.CloseBrace();
builder.AppendLine();
builder.AppendLine($"public static {structName} FromNative(IntPtr buffer) => new {structName}(buffer);");
builder.AppendLine();
builder.AppendLine("public void ToNative(IntPtr buffer)");
builder.OpenBrace();
builder.BeginUnsafeBlock();
if (structObj.IsStructNativelyCopyable())
{
if (structObj.IsStructNativelyDestructible())
{
builder.AppendLine("if (NativeHandle is null)");
builder.OpenBrace();
builder.AppendLine("NativeHandle = new NativeStructHandle(NativeClassPtr);");
builder.CloseBrace();
builder.AppendLine();
builder.AppendLine("fixed (NativeStructHandleData* StructDataPointer = &NativeHandle.Data)");
builder.OpenBrace();
builder.AppendLine($"IntPtr AllocationPointer = {ExporterCallbacks.UScriptStructCallbacks}.CallGetStructLocation(StructDataPointer, NativeClassPtr);");
}
else
{
builder.AppendLine("if (Allocation is null)");
builder.OpenBrace();
builder.AppendLine("Allocation = new byte[NativeDataSize];");
builder.AppendLine();
builder.CloseBrace();
builder.AppendLine("fixed (byte* AllocationPointer = Allocation)");
builder.OpenBrace();
}
builder.AppendLine($"{ExporterCallbacks.UScriptStructCallbacks}.CallNativeCopy(NativeClassPtr, (nint) AllocationPointer, buffer);");
builder.CloseBrace();
}
else
{
foreach (UhtProperty property in properties)
{
PropertyTranslator translator = PropertyTranslatorManager.GetTranslator(property)!;
string scriptName = property.GetPropertyName();
string offsetName = $"{property.SourceName}_Offset";
builder.TryAddWithEditor(property);
translator.ExportToNative(builder, property, property.SourceName, "buffer", offsetName, scriptName);
builder.TryEndWithEditor(property);
}
}
builder.EndUnsafeBlock();
builder.CloseBrace();
}
}