/// <summary> /// Finds or loads the UScriptStruct for the given struct type /// </summary> /// <param name="type">The type of the struct</param> /// <returns>The UScriptStruct for the given struct type</returns> public static UScriptStruct ResolveStruct(Type type) { UScriptStruct unrealStruct = GetStruct(type); if (unrealStruct == null) { unrealStruct = LoadStruct(type); } return(unrealStruct); }
/// <summary> /// Finds or loads the UScriptStruct for the given struct type /// </summary> /// <typeparam name="T">The type of the struct</typeparam> /// <returns>The UScriptStruct for the given struct type</returns> public static UScriptStruct ResolveStruct <T>() where T : struct { UScriptStruct unrealStruct = GetStruct <T>(); if (unrealStruct == null) { unrealStruct = LoadStruct <T>(); } return(unrealStruct); }
/// <summary> /// Finds or loads the UScriptStruct for the given path (e.g. "/Script/CoreUObject.Guid") /// </summary> /// <param name="path">The path of the UScriptStruct</param> /// <returns>The UScriptStruct for the given path</returns> public static UScriptStruct ResolveStruct(string path) { UScriptStruct unrealStruct = GetStruct(path); if (unrealStruct == null) { unrealStruct = LoadStruct(path); } return(unrealStruct); }
/// <summary> /// Sets the IsValid state for the given struct based on the state of all properties IsValid state /// (used when generating with safeguards enabled) /// </summary> private void AppendStructIsValid(CSharpTextBuilder builder, string structTypeName, StructInfo structInfo, UStruct parentStruct) { if (!Settings.GenerateIsValidSafeguards) { return; } List <UProperty> allProperties = new List <UProperty>(); foreach (UProperty property in structInfo.GetProperties()) { if (!structInfo.IsCollapsedProperty(property)) { allProperties.Add(property); } } if (parentStruct != null && (Settings.InlineBaseStruct || structInfo.StructAsClass)) { UScriptStruct tempParentStruct = parentStruct as UScriptStruct; while (tempParentStruct != null) { StructInfo tempParentStructInfo = GetStructInfo(tempParentStruct); if (tempParentStructInfo != null) { foreach (UProperty property in tempParentStructInfo.GetProperties()) { if (!tempParentStructInfo.IsCollapsedProperty(property)) { allProperties.Add(property); } } } tempParentStruct = tempParentStruct.GetSuperStruct() as UScriptStruct; } } StringBuilder isValidCheck = new StringBuilder(); isValidCheck.Append(Settings.VarNames.ClassAddress + " != IntPtr.Zero"); foreach (UProperty property in allProperties) { string propertyName = GetMemberName(property, structInfo.GetPropertyName(property)); isValidCheck.Append(" && " + propertyName + Settings.VarNames.IsValid); } isValidCheck.Insert(0, structTypeName + Settings.VarNames.IsValid + " = "); isValidCheck.Append(";"); builder.AppendLine(isValidCheck.ToString()); builder.AppendLine(Names.NativeReflection_LogStructIsValid + "(\"" + structInfo.Struct.GetPathName() + "\", " + structTypeName + Settings.VarNames.IsValid + ");"); }
/// <summary> /// Returns the size of the struct using ICppStructOps, otherwise falls back to GetStructureSize(). /// </summary> /// <returns></returns> public int GetStructSize() { UScriptStruct scriptStruct = this as UScriptStruct; if (scriptStruct != null) { IntPtr cppStructOps = Native_UScriptStruct.GetCppStructOps(scriptStruct.Address); if (cppStructOps != IntPtr.Zero) { return(Native_ICppStructOps.GetSize(cppStructOps)); } } return(GetStructureSize()); }
/// <summary> /// Sets all properties in the given struct to their default values within the struct constructor. /// (used when generating with safeguards enabled) /// </summary> private void AppendStructDefaultValuesOnInvalid(CSharpTextBuilder builder, StructInfo structInfo, UStruct parentStruct, List <string> namespaces) { if (!Settings.GenerateIsValidSafeguards) { return; } List <UProperty> allProperties = new List <UProperty>(); foreach (UProperty property in structInfo.GetProperties()) { if (!structInfo.IsCollapsedProperty(property)) { allProperties.Add(property); } } if (parentStruct != null && Settings.InlineBaseStruct) { UScriptStruct tempParentStruct = parentStruct as UScriptStruct; while (tempParentStruct != null) { StructInfo tempParentStructInfo = GetStructInfo(tempParentStruct); if (tempParentStructInfo != null) { foreach (UProperty property in tempParentStructInfo.GetProperties()) { if (!tempParentStructInfo.IsCollapsedProperty(property)) { allProperties.Add(property); } } } tempParentStruct = tempParentStruct.GetSuperStruct() as UScriptStruct; } } foreach (UProperty property in allProperties) { string propertyName = GetMemberName(property, structInfo.GetPropertyName(property)); builder.AppendLine(propertyName + " = " + GetPropertyMarshalerDefaultValue(property, namespaces) + ";"); } }
static void Load() { unrealStruct = UScriptStruct.ResolveStructAddress(structPath); if (unrealStruct != IntPtr.Zero) { isLoaded = true; structSize = NativeReflection.GetStructSize(unrealStruct); if (!IsStructAsClass) { // NOTE: UScriptStruct::CopyScriptStruct uses the following logic which we may want to use instead // // if (StructFlags & STRUCT_CopyNative) { GetCppStructOps()->Copy(); } // else if (StructFlags & STRUCT_IsPlainOldData) { FMemory::Memcpy(); } // else { foreach(prop) CopyCompleteValue_InContainer(prop, ...); } if (UScriptStruct.IsPODZeroInit(unrealStruct)) { useDefaultT = true; } } } }
private void AppendAttribute(CSharpTextBuilder builder, UField field, UnrealModuleInfo module, bool isCollapsedMember) { UnrealModuleType moduleType; UnrealModuleType moduleAssetType; string moduleName = GetModuleName(field, out moduleType, out moduleAssetType); if (string.IsNullOrEmpty(moduleName)) { moduleName = module.Name; } List <string> attributes = new List <string>(); // TODO: Combine all of this into EPropertyType (add some TypeCode into UField?) bool isInterface = false; UEnum unrealEnum = field as UEnum; UClass unrealClass = field as UClass; UScriptStruct unrealStruct = field as UScriptStruct; UFunction unrealFunction = field as UFunction; if (unrealFunction != null) { if (unrealFunction.HasAnyFunctionFlags(EFunctionFlags.Delegate)) { attributes.Add("UDelegate"); } else { string additionalFunctionInfo = string.Empty; // TODO: Only get the script name for virtual functions / interface functions as we currently only need // this for finding the base function for hooking things up to the native base types. string scriptFunctionName; if (unrealFunction.GetScriptName(out scriptFunctionName)) { additionalFunctionInfo += ", OriginalName=\"" + unrealFunction.GetName() + "\""; } if (isCollapsedMember) { // The Flags here might not contain too useful information if there is both a get/set function. // Maybe include a second flags var? attributes.Add("UFunctionAsProp(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + additionalFunctionInfo + ")"); } else { attributes.Add("UFunction(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + additionalFunctionInfo + ")"); } } } UProperty unrealProperty = field as UProperty; if (unrealProperty != null) { attributes.Add("UProperty(Flags=(PropFlags)0x" + ((ulong)unrealProperty.PropertyFlags).ToString("X16") + ")"); } if (unrealStruct != null) { attributes.Add("UStruct(Flags=0x" + ((uint)unrealStruct.StructFlags).ToString("X8") + ")"); } else if (unrealClass != null) { // Abstract isn't really required but might help with code browsing to know what is abstract // and what isn't. Therefore put it at the start of the attributes list. if (unrealClass.HasAnyClassFlags(EClassFlags.Abstract)) { attributes.Add("Abstract"); } isInterface = unrealClass.IsChildOf <UInterface>(); if (isInterface) { attributes.Add("UInterface(Flags=0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + ")"); } else { attributes.Add("UClass(Flags=(ClassFlags)0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + ")"); } } if (unrealEnum != null) { attributes.Add("UEnum"); } if (unrealEnum != null || unrealClass != null || unrealStruct != null) { bool blueprintType = false; bool blueprintable = false; if (unrealEnum != null) { blueprintType = field.GetBoolMetaData(MDClass.BlueprintType); } else { GetBlueprintability(field as UStruct, out blueprintType, out blueprintable); } if (blueprintType) { attributes.Add(UMeta.GetKey(MDClass.BlueprintType)); } if (unrealClass != null && blueprintable) { attributes.Add(UMeta.GetKey(MDClass.Blueprintable)); } attributes.Add("UMetaPath(\"" + field.GetPathName() + "\"" + (isInterface ? ", InterfaceImpl=typeof(" + GetTypeName(unrealClass, null) + "Impl" + ")" : string.Empty) + ")"); } else { attributes.Add("UMetaPath(\"" + field.GetPathName() + "\")"); } if (attributes.Count > 0) { builder.AppendLine("[" + string.Join(", ", attributes) + "]"); } }
private void AppendStructMarshalerBody(CSharpTextBuilder builder, string structTypeName, StructInfo structInfo, UStruct parentStruct, bool toNative, List <string> namespaces) { if (Settings.GenerateIsValidSafeguards) { builder.AppendLine("if (!" + structTypeName + Settings.VarNames.IsValid + ")"); builder.OpenBrace(); builder.AppendLine(Names.NativeReflection_LogInvalidStructAccessed + "(\"" + structInfo.Struct.GetPathName() + "\");"); if (!toNative) { // Set default values for all properties AppendStructDefaultValuesOnInvalid(builder, structInfo, parentStruct, namespaces); } builder.AppendLine("return;"); builder.CloseBrace(); } foreach (UProperty property in structInfo.GetProperties()) { if (!structInfo.IsCollapsedProperty(property)) { string propertyName = GetMemberName(property, structInfo.GetPropertyName(property)); if (toNative) { AppendPropertyToNative(builder, property, propertyName, "nativeStruct", "null", propertyName, false, namespaces); } else { AppendPropertyFromNative(builder, property, propertyName, "nativeStruct", propertyName, "null", false, namespaces); } } } if (parentStruct != null) { if (Settings.InlineBaseStruct) { UScriptStruct tempParentStruct = parentStruct as UScriptStruct; while (tempParentStruct != null) { StructInfo tempParentStructInfo = GetStructInfo(tempParentStruct); if (tempParentStructInfo != null) { foreach (UProperty property in tempParentStructInfo.GetProperties()) { if (!tempParentStructInfo.IsCollapsedProperty(property)) { string propertyName = GetMemberName(property, tempParentStructInfo.GetPropertyName(property)); if (toNative) { AppendPropertyToNative(builder, property, propertyName, "nativeStruct", "null", propertyName, false, namespaces); } else { AppendPropertyFromNative(builder, property, propertyName, "nativeStruct", propertyName, "null", false, namespaces); } } } } tempParentStruct = tempParentStruct.GetSuperStruct() as UScriptStruct; } } else { if (toNative) { builder.AppendLine("Base.ToNative(nativeStruct);"); } else { builder.AppendLine("Base = new " + GetTypeName(parentStruct, namespaces) + "(nativeStruct);"); } } } }
private void GenerateCodeForStruct(UnrealModuleInfo module, UStruct unrealStruct) { bool isBlueprintType = unrealStruct.IsA <UUserDefinedStruct>() || unrealStruct.IsA <UBlueprintGeneratedClass>(); StructInfo structInfo = GetStructInfo(unrealStruct, isBlueprintType); string typeName = GetTypeName(unrealStruct); UnrealModuleType moduleAssetType; string currentNamespace = GetModuleNamespace(unrealStruct, out moduleAssetType); List <string> namespaces = GetDefaultNamespaces(); CSharpTextBuilder builder = new CSharpTextBuilder(Settings.IndentType); if (!string.IsNullOrEmpty(currentNamespace)) { builder.AppendLine("namespace " + currentNamespace); builder.OpenBrace(); } string accessSpecifier = "public"; StringBuilder modifiers = new StringBuilder(accessSpecifier); if (Settings.UseAbstractTypes && structInfo.IsClass && structInfo.Class.HasAnyClassFlags(EClassFlags.Abstract)) { modifiers.Append(" abstract"); } StringBuilder baseTypeStr = new StringBuilder(); UStruct parentStruct = unrealStruct.GetSuperStruct(); if (parentStruct != null && parentStruct != UClass.GetClass <UInterface>() && unrealStruct != UClass.GetClass <UInterface>()) { baseTypeStr.Append(GetTypeName(parentStruct, namespaces)); } if (structInfo.IsClass) { foreach (FImplementedInterface implementedInterface in structInfo.Class.Interfaces) { if (baseTypeStr.Length > 0) { baseTypeStr.Append(", "); } baseTypeStr.Append(GetTypeName(implementedInterface.InterfaceClass, namespaces)); } } if (baseTypeStr.Length > 0) { baseTypeStr.Insert(0, " : "); } AppendDocComment(builder, unrealStruct, isBlueprintType); AppendAttribute(builder, unrealStruct, module, structInfo); if (structInfo.IsInterface) { System.Diagnostics.Debug.Assert(structInfo.Class.Interfaces.Length == 0, "TODO: Interfaces inheriting other interfaces"); string baseInterface = unrealStruct == UClass.GetClass <UInterface>() ? string.Empty : (baseTypeStr.Length == 0 ? " : " : ", ") + Names.IInterface; builder.AppendLine(modifiers + " interface " + typeName + baseTypeStr + baseInterface); } else if (structInfo.IsClass) { builder.AppendLine(modifiers + " partial class " + typeName + baseTypeStr); } else { if (structInfo.StructAsClass) { builder.AppendLine(modifiers + " partial class " + typeName + " : " + Names.StructAsClass); } else { if (structInfo.IsBlittable) { string structLayout = UpdateTypeNameNamespace("StructLayout", "System.Runtime.InteropServices", namespaces); string layoutKind = UpdateTypeNameNamespace("LayoutKind", "System.Runtime.InteropServices", namespaces); builder.AppendLine("[" + structLayout + "(" + layoutKind + ".Sequential)]"); } builder.AppendLine(modifiers + " partial struct " + typeName); } } builder.OpenBrace(); string typeNameEx = structInfo.IsInterface ? typeName + "Impl" : typeName; // Create a seperate builder for building the interface "Impl" class CSharpTextBuilder interfaceImplBuilder = null; if (structInfo.IsInterface) { interfaceImplBuilder = new CSharpTextBuilder(); interfaceImplBuilder.AppendLine(accessSpecifier + " sealed class " + typeNameEx + " : " + Names.IInterfaceImpl + ", " + typeName); interfaceImplBuilder.Indent(); // Move the indent to the same as builder for this point interfaceImplBuilder.OpenBrace(); // Open the class brace } // Create a seperate builder for properties which will be inserted into the native type info initializer CSharpTextBuilder offsetsBuilder = new CSharpTextBuilder(Settings.IndentType); offsetsBuilder.AppendLine("static " + typeNameEx + "()"); offsetsBuilder.IndentCount = builder.IndentCount;// Move the indent to the same as builder offsetsBuilder.OpenBrace(); offsetsBuilder.AppendLine("if (" + Names.UnrealTypes_CanLazyLoadNativeType + "(typeof(" + typeNameEx + ")))"); offsetsBuilder.OpenBrace(); offsetsBuilder.AppendLine(Settings.VarNames.LoadNativeType + "();"); offsetsBuilder.CloseBrace(); offsetsBuilder.AppendLine(Names.UnrealTypes_OnCCtorCalled + "(typeof(" + typeNameEx + "));"); offsetsBuilder.CloseBrace(); offsetsBuilder.AppendLine(); offsetsBuilder.AppendLine("static void " + Settings.VarNames.LoadNativeType + "()"); offsetsBuilder.OpenBrace(); if (structInfo.HasStaticFunction) { builder.AppendLine("static IntPtr " + Settings.VarNames.ClassAddress + ";"); offsetsBuilder.AppendLine(Settings.VarNames.ClassAddress + " = " + (structInfo.IsStruct ? Names.NativeReflection_GetStruct : Names.NativeReflection_GetClass) + "(\"" + unrealStruct.GetPathName() + "\");"); } else { offsetsBuilder.AppendLine("IntPtr " + Settings.VarNames.ClassAddress + " = " + (structInfo.IsStruct ? Names.NativeReflection_GetStruct : Names.NativeReflection_GetClass) + "(\"" + unrealStruct.GetPathName() + "\");"); } if (structInfo.StructAsClass) { offsetsBuilder.AppendLine(typeName + Settings.VarNames.StructAddress + " = " + Settings.VarNames.ClassAddress + ";"); } else if (structInfo.IsStruct) { offsetsBuilder.AppendLine(typeName + Settings.VarNames.StructSize + " = " + Names.NativeReflection_GetStructSize + "(" + Settings.VarNames.ClassAddress + ");"); } if (structInfo.IsStruct && parentStruct != null) { // Export base properties if (Settings.InlineBaseStruct || structInfo.StructAsClass) { UScriptStruct tempParentStruct = parentStruct as UScriptStruct; while (tempParentStruct != null) { StructInfo tempParentStructInfo = GetStructInfo(tempParentStruct); if (tempParentStructInfo != null) { foreach (UProperty property in tempParentStructInfo.GetProperties()) { if (!tempParentStructInfo.IsCollapsedProperty(property)) { GenerateCodeForProperty(module, builder, offsetsBuilder, property, tempParentStructInfo.IsBlueprintType, structInfo, namespaces, tempParentStructInfo.GetPropertyName(property)); } } } tempParentStruct = tempParentStruct.GetSuperStruct() as UScriptStruct; } } else { builder.AppendLine(GetTypeName(parentStruct, namespaces) + " Base;"); } } // Export properties foreach (UProperty property in structInfo.GetProperties()) { if (!structInfo.IsCollapsedProperty(property)) { GenerateCodeForProperty(module, builder, offsetsBuilder, property, isBlueprintType, structInfo, namespaces, structInfo.GetPropertyName(property)); } } foreach (CollapsedMember collapsedMember in structInfo.GetCollapsedMembers()) { GenerateCodeForProperty(module, builder, offsetsBuilder, collapsedMember, isBlueprintType, namespaces); } // Export functions List <ExtensionMethodInfo> extensionMethods = new List <ExtensionMethodInfo>(); foreach (UFunction function in structInfo.GetFunctions()) { if (!structInfo.IsCollapsedFunction(function)) { if (!structInfo.IsInterface) { // If this isn't an interface and the function can be made into an extension method then do so ExtensionMethodInfo extensionMethodInfo = ExtensionMethodInfo.Create(function); if (extensionMethodInfo != null) { extensionMethods.Add(extensionMethodInfo); } } if (function.HasAnyFunctionFlags(EFunctionFlags.Delegate | EFunctionFlags.MulticastDelegate)) { AppendDelegateSignature(module, builder, function, unrealStruct, isBlueprintType, namespaces); builder.AppendLine(); } else if (structInfo.IsInterface) { AppendFunctionOffsets(interfaceImplBuilder, offsetsBuilder, function, false, false, namespaces); AppendDocComment(builder, function, isBlueprintType); AppendAttribute(builder, function, module); builder.AppendLine(GetFunctionSignature(module, function, unrealStruct, namespaces) + ";"); builder.AppendLine(); // Always require a per-instance function address on the interfaces "Impl" class. // This isn't really required if the target function isn't an event but since we are working // with interfaces it is probably best to make sure functions are resolved properly. If we decide // to change this to not require a per-instance function address make sure to update AppendFunctionOffsets // which adds the per-instance function address to the class. Also update AssemblyRewriter.Interface.cs // Also update the "ResetInterface" code which always expects an per-instance address to reset. AppendAttribute(interfaceImplBuilder, function, module); interfaceImplBuilder.AppendLine(GetFunctionSignature(module, function, unrealStruct, null, "public", FunctionSigFlags.None, namespaces)); interfaceImplBuilder.OpenBrace(); AppendFunctionBody(interfaceImplBuilder, function, false, false, true, namespaces); interfaceImplBuilder.CloseBrace(); builder.AppendLine(); } else { AppendFunctionOffsets(builder, offsetsBuilder, function, false, false, namespaces); AppendDocComment(builder, function, isBlueprintType); AppendAttribute(builder, function, module); bool hasSuperFunction = function.GetSuperFunction() != null; if (function.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent) && !hasSuperFunction) { // Define the declaration method which will call the correct UFunction for the UObject instance builder.AppendLine(GetFunctionSignature(module, function, unrealStruct, namespaces)); builder.OpenBrace(); AppendFunctionBody(builder, function, false, false, true, namespaces); builder.CloseBrace(); builder.AppendLine(); } if (function.HasAnyFunctionFlags(EFunctionFlags.BlueprintEvent)) { if (!Settings.UseExplicitImplementationMethods) { // Used to hide the _Implementation method from being visible in intellisense builder.AppendLine("[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]"); } // Define the _Implementation method builder.AppendLine(GetFunctionSignatureImpl(module, function, unrealStruct, namespaces)); } else { builder.AppendLine(GetFunctionSignature(module, function, unrealStruct, namespaces)); } builder.OpenBrace(); AppendFunctionBody(builder, function, false, false, false, namespaces); builder.CloseBrace(); builder.AppendLine(); } } } if (structInfo.StructAsClass) { if (Settings.GenerateIsValidSafeguards) { builder.AppendLine("static bool " + typeName + Settings.VarNames.IsValid + ";"); // Append XXXX_IsValid = Prop1_IsValid && Prop2_IsValid && Prop3_IsValid...; AppendStructIsValid(offsetsBuilder, typeName, structInfo, parentStruct); } builder.AppendLine("static IntPtr " + typeName + Settings.VarNames.StructAddress + ";"); builder.AppendLine(); builder.AppendLine("protected override IntPtr GetStructAddress()"); builder.OpenBrace(); builder.AppendLine("return " + typeName + Settings.VarNames.StructAddress + ";"); builder.CloseBrace(); builder.AppendLine(); } else if (structInfo.IsStruct) { if (Settings.GenerateIsValidSafeguards && !structInfo.IsBlittable) { builder.AppendLine("static bool " + typeName + Settings.VarNames.IsValid + ";"); } builder.AppendLine("static int " + typeName + Settings.VarNames.StructSize + ";"); builder.AppendLine(); // Add the struct Copy() method (for non blittable structs) builder.AppendLine("public " + typeName + " " + Settings.VarNames.StructCopy + "()"); builder.OpenBrace(); builder.AppendLine(typeName + " result = this;"); foreach (UProperty property in structInfo.GetProperties()) { if (!structInfo.IsCollapsedProperty(property) && IsCollectionProperty(property)) { string propertyName = GetMemberName(property, structInfo.GetPropertyName(property)); builder.AppendLine("if (this." + propertyName + " != null)"); builder.OpenBrace(); UStructProperty structProperty = property as UStructProperty; StructInfo propertyStructInfo = structProperty == null || structProperty.Struct == null ? null : GetStructInfo(structProperty.Struct); if (propertyStructInfo != null && !propertyStructInfo.IsBlittable) { builder.AppendLine("result." + propertyName + " = new " + GetTypeName(property, namespaces) + "();"); builder.AppendLine("for (int i = 0; i < this." + propertyName + ".Count; ++i)"); builder.OpenBrace(); builder.AppendLine("result." + propertyName + ".Add(this." + propertyName + "[i]." + Settings.VarNames.StructCopy + "());"); builder.CloseBrace(); } else { builder.AppendLine("result." + propertyName + " = new " + GetTypeName(property, namespaces) + "(" + "this." + propertyName + ");"); } builder.CloseBrace(); } } if (Settings.InlineBaseStruct) { UScriptStruct tempParentStruct = parentStruct as UScriptStruct; while (tempParentStruct != null) { StructInfo tempParentStructInfo = GetStructInfo(tempParentStruct); if (tempParentStructInfo != null) { foreach (UProperty property in tempParentStructInfo.GetProperties()) { if (!tempParentStructInfo.IsCollapsedProperty(property) && IsCollectionProperty(property)) { string propertyName = GetMemberName(property, tempParentStructInfo.GetPropertyName(property)); builder.AppendLine("if (this." + propertyName + " != null)"); builder.OpenBrace(); builder.AppendLine("result." + propertyName + " = new " + GetTypeName(property, namespaces) + "(" + "this." + propertyName + ");"); builder.CloseBrace(); } } } tempParentStruct = tempParentStruct.GetSuperStruct() as UScriptStruct; } } builder.AppendLine("return result;"); builder.CloseBrace(); builder.AppendLine(); if (structInfo.IsBlittable) { // Validate the size of the blittable struct offsetsBuilder.AppendLine(Names.NativeReflection_ValidateBlittableStructSize + "(" + Settings.VarNames.ClassAddress + ", typeof(" + typeName + "));"); } else { if (Settings.GenerateIsValidSafeguards) { // Append XXXX_IsValid = Prop1_IsValid && Prop2_IsValid && Prop3_IsValid...; AppendStructIsValid(offsetsBuilder, typeName, structInfo, parentStruct); } builder.AppendLine("public static " + typeName + " FromNative(IntPtr nativeBuffer)"); builder.OpenBrace(); builder.AppendLine("return new " + typeName + "(nativeBuffer);"); builder.CloseBrace(); builder.AppendLine(); builder.AppendLine("public static void ToNative(IntPtr nativeBuffer, " + typeName + " value)"); builder.OpenBrace(); builder.AppendLine("value.ToNative(nativeBuffer);"); builder.CloseBrace(); builder.AppendLine(); builder.AppendLine("public static " + typeName + " FromNative(IntPtr nativeBuffer, int arrayIndex, IntPtr prop)"); builder.OpenBrace(); builder.AppendLine("return new " + typeName + "(nativeBuffer + (arrayIndex * " + typeName + Settings.VarNames.StructSize + "));"); builder.CloseBrace(); builder.AppendLine(); builder.AppendLine("public static void ToNative(IntPtr nativeBuffer, int arrayIndex, IntPtr prop, " + typeName + " value)"); builder.OpenBrace(); builder.AppendLine("value.ToNative(nativeBuffer + (arrayIndex * " + typeName + Settings.VarNames.StructSize + "));"); builder.CloseBrace(); builder.AppendLine(); builder.AppendLine("public void ToNative(IntPtr nativeStruct)"); builder.OpenBrace(); AppendStructMarshalerBody(builder, typeName, structInfo, parentStruct, true, namespaces); builder.CloseBrace(); builder.AppendLine(); builder.AppendLine("public " + typeName + "(IntPtr nativeStruct)"); builder.OpenBrace(); /*if (Settings.UObjectAsBlittableType) * { * // UObject types will have an additional backing field which needs to be assigned before being able to * // assign the property * // - The alternative would be to modify the property assignment code to target the backing field instead of * // the property. This is probably the ideal way of doing it but we would need to use a different marshaler * // (BlittableTypeMarshaler<> instead of UObjectMarshaler<>) and ensure the marshaler change doesn't impact * // other generated code elsewhere. * foreach (UProperty property in structInfo.GetProperties()) * { * UObjectProperty objectProperty = property as UObjectProperty; * if (objectProperty != null) * { * string propertyName = GetMemberName(property, structInfo.GetPropertyName(property)); * builder.AppendLine(propertyName + Settings.VarNames.UObjectBlittableName + " = IntPtr.Zero;"); * } * } * }*/ AppendStructMarshalerBody(builder, typeName, structInfo, parentStruct, false, namespaces); builder.CloseBrace(); builder.AppendLine(); } } if (loadNativeTypeInjected.Contains(typeName)) { offsetsBuilder.AppendLine(Settings.VarNames.LoadNativeTypeInjected + "(" + Settings.VarNames.ClassAddress + ");"); } offsetsBuilder.CloseBrace(); // Add the offsets builder if it isn't empty (always required for structs due to struct size export) // Interfaces are built up seperately in a different class which must be added after the interface body. if (!structInfo.IsInterface && (structInfo.HasContent || structInfo.IsStruct)) { builder.AppendLine(offsetsBuilder.ToString()); builder.AppendLine(); } // Remove any trailing empty lines before adding the close brace builder.RemovePreviousEmptyLines(); builder.CloseBrace(); // Add the "Impl" wrapper class for interfaces. This is always required so that we have a default // interface implementation that we can call. if (structInfo.IsInterface) { if (structInfo.HasContent) { interfaceImplBuilder.AppendLine();// Whitespace // Add the ResetInterface method to reset the state of a pooled interface instance interfaceImplBuilder.AppendLine("public override void ResetInterface()"); interfaceImplBuilder.OpenBrace(); foreach (UFunction function in structInfo.GetFunctions()) { interfaceImplBuilder.AppendLine(GetFunctionName(function) + Settings.VarNames.InstanceFunctionAddress + " = IntPtr.Zero;"); } interfaceImplBuilder.CloseBrace(); interfaceImplBuilder.AppendLine(); // Whitespace } interfaceImplBuilder.AppendLine(offsetsBuilder.ToString()); // Add the offsets to the "Impl" class interfaceImplBuilder.CloseBrace(); // Add the close brace for the "Impl" class builder.AppendLine(); // Empty line between interface and "Impl" class builder.AppendLine(interfaceImplBuilder.ToString()); // Add the "Impl" class below the interface } if (!string.IsNullOrEmpty(currentNamespace)) { builder.CloseBrace(); } builder.InsertNamespaces(currentNamespace, namespaces, Settings.SortNamespaces); OnCodeGenerated(module, moduleAssetType, typeName, unrealStruct.GetPathName(), builder); if (extensionMethods.Count > 0) { GenerateCodeForExtensionMethods(module, unrealStruct, extensionMethods); } }
private void AppendAttribute(CSharpTextBuilder builder, UField field, UnrealModuleInfo module, bool isCollapsedMember) { UnrealModuleType moduleType; UnrealModuleType moduleAssetType; string moduleName = GetModuleName(field, out moduleType, out moduleAssetType); if (string.IsNullOrEmpty(moduleName)) { moduleName = module.Name; } List <string> attributes = new List <string>(); // TODO: Combine all of this into EPropertyType (add some TypeCode into UField?) bool isInterface = false; UEnum unrealEnum = field as UEnum; UClass unrealClass = field as UClass; UScriptStruct unrealStruct = field as UScriptStruct; UFunction unrealFunction = field as UFunction; if (unrealFunction != null) { if (unrealFunction.HasAnyFunctionFlags(EFunctionFlags.Delegate)) { attributes.Add("UDelegate"); } else { if (isCollapsedMember) { // The Flags here might not contain too useful information if there is both a get/set function. // Maybe include a second flags var? attributes.Add("UFunctionAsProp(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + ")"); } else { attributes.Add("UFunction(Flags=0x" + ((uint)unrealFunction.FunctionFlags).ToString("X8") + ")"); } } } UProperty unrealProperty = field as UProperty; if (unrealProperty != null) { attributes.Add("UProperty(Flags=(PropFlags)0x" + ((ulong)unrealProperty.PropertyFlags).ToString("X16") + ")"); } if (unrealStruct != null) { attributes.Add("UStruct(Flags=0x" + ((uint)unrealStruct.StructFlags).ToString("X8") + ")"); } else if (unrealClass != null) { // Abstract isn't really required but might help with code browsing to know what is abstract // and what isn't. Therefore put it at the start of the attributes list. if (unrealClass.HasAnyClassFlags(EClassFlags.Abstract)) { attributes.Add("Abstract"); } isInterface = unrealClass.IsChildOf <UInterface>(); if (isInterface) { attributes.Add("UInterface(Flags=0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + ")"); } else { // Should we skip "inherit" config name? string configNameStr = string.Empty; if (unrealClass.ClassConfigName != FName.None && !unrealClass.ClassConfigName.ToString().Equals("inherit", StringComparison.InvariantCultureIgnoreCase)) { configNameStr = ", Config=\"" + unrealClass.ClassConfigName + "\""; } attributes.Add("UClass(Flags=(ClassFlags)0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + configNameStr + ")"); } } if (unrealEnum != null) { attributes.Add("UEnum"); } if (unrealEnum != null || unrealClass != null || unrealStruct != null) { bool blueprintType = false; bool blueprintable = false; if (unrealEnum != null) { blueprintType = field.GetBoolMetaData(MDClass.BlueprintType); } else { GetBlueprintability(field as UStruct, out blueprintType, out blueprintable); } if (blueprintType) { attributes.Add(UMeta.GetKey(MDClass.BlueprintType)); } if (unrealClass != null && blueprintable) { attributes.Add(UMeta.GetKey(MDClass.Blueprintable)); } if (isInterface) { } attributes.Add("UMetaPath(\"" + field.GetPathName() + "\", \"" + moduleName + "\", UnrealModuleType." + GetUnrealModuleTypeString(moduleType, moduleAssetType) + (isInterface ? ", InterfaceImpl=typeof(" + GetTypeName(unrealClass, null) + "Impl" + ")" : string.Empty) + ")"); } else { attributes.Add("UMetaPath(\"" + field.GetPathName() + "\")"); } if (attributes.Count > 0) { builder.AppendLine("[" + string.Join(", ", attributes) + "]"); } }
private void ProcessCommon(EExprToken opcode) { switch (opcode) { case EExprToken.EX_PrimitiveCast: { // A type conversion. byte conversionType = ReadByte(); output.AppendLine(FmtOpcodeIndent(opcode) + "PrimitiveCast of type " + conversionType); AddIndent(); output.AppendLine(indents + " Argument:"); ProcessCastByte(conversionType); //@TODO: //Ar.Logf(TEXT("%s Expression:"), *Indents); //SerializeExpr( ScriptIndex ); break; } case EExprToken.EX_SetSet: { output.AppendLine(FmtOpcodeIndent(opcode) + "set set"); SerializeExpr(); ReadInt32(); while (SerializeExpr() != EExprToken.EX_EndSet) { // Set contents } break; } case EExprToken.EX_EndSet: { output.AppendLine(FmtOpcodeIndent(opcode) + "EX_EndSet"); break; } case EExprToken.EX_SetConst: { UProperty innerProp = ReadPointer <UProperty>(); int num = ReadInt32(); output.AppendLine(FmtOpcodeIndent(opcode) + "set set const - elements number: " + num + ", inner property: " + UObject.GetNameSafe(innerProp)); while (SerializeExpr() != EExprToken.EX_EndSetConst) { // Set contents } break; } case EExprToken.EX_EndSetConst: { output.AppendLine(FmtOpcodeIndent(opcode) + "EX_EndSetConst"); break; } case EExprToken.EX_SetMap: { output.AppendLine(FmtOpcodeIndent(opcode) + "set map"); SerializeExpr(); ReadInt32(); while (SerializeExpr() != EExprToken.EX_EndMap) { // Map contents } break; } case EExprToken.EX_EndMap: { output.AppendLine(FmtOpcodeIndent(opcode) + "EX_EndMap"); break; } case EExprToken.EX_MapConst: { UProperty keyProp = ReadPointer <UProperty>(); UProperty valProp = ReadPointer <UProperty>(); int num = ReadInt32(); output.AppendLine(FmtOpcodeIndent(opcode) + "set map const - elements number: " + num + ", key property: " + UObject.GetNameSafe(keyProp) + ", val property: " + UObject.GetNameSafe(valProp)); while (SerializeExpr() != EExprToken.EX_EndMapConst) { // Map contents } break; } case EExprToken.EX_ObjToInterfaceCast: { // A conversion from an object variable to a native interface variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass interfaceClass = ReadPointer <UClass>(); output.AppendLine(FmtOpcodeIndent(opcode) + "ObjToInterfaceCast to " + interfaceClass.GetName()); SerializeExpr(); break; } case EExprToken.EX_CrossInterfaceCast: { // A conversion from one interface variable to a different interface variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass interfaceClass = ReadPointer <UClass>(); output.AppendLine(FmtOpcodeIndent(opcode) + "InterfaceToInterfaceCast to " + interfaceClass.GetName()); SerializeExpr(); break; } case EExprToken.EX_InterfaceToObjCast: { // A conversion from an interface variable to a object variable. // We use a different bytecode to avoid the branching each time we process a cast token // the interface class to convert to UClass objectClass = ReadPointer <UClass>(); output.AppendLine(FmtOpcodeIndent(opcode) + "InterfaceToObjCast to " + objectClass.GetName()); SerializeExpr(); break; } case EExprToken.EX_Let: { output.AppendLine(FmtOpcodeIndent(opcode) + "Let (Variable = Expression)"); AddIndent(); ReadPointer <UProperty>(); // Variable expr. output.AppendLine(indents + " Variable:"); SerializeExpr(); // Assignment expr. output.AppendLine(indents + " Expression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_LetObj: case EExprToken.EX_LetWeakObjPtr: { if (opcode == EExprToken.EX_LetObj) { output.AppendLine(FmtOpcodeIndent(opcode) + "Let Obj (Variable = Expression)"); } else { output.AppendLine(FmtOpcodeIndent(opcode) + "Let WeakObjPtr (Variable = Expression)"); } AddIndent(); // Variable expr. output.AppendLine(indents + " Variable:"); SerializeExpr(); // Assignment expr. output.AppendLine(indents + " Expression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_LetBool: { output.AppendLine(FmtOpcodeIndent(opcode) + "LetBool (Variable = Expression)"); AddIndent(); // Variable expr. output.AppendLine(indents + " Variable:"); SerializeExpr(); // Assignment expr. output.AppendLine(indents + " Expression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_LetValueOnPersistentFrame: { output.AppendLine(FmtOpcodeIndent(opcode) + "LetValueOnPersistentFrame"); AddIndent(); UProperty prop = ReadPointer <UProperty>(); output.AppendLine(indents + " Destination variable: " + UObject.GetNameSafe(prop) + ", offset: " + (prop != null ? prop.GetOffset_ForDebug() : 0)); output.AppendLine(indents + " Expression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_StructMemberContext: { output.AppendLine(FmtOpcodeIndent(opcode) + "Struct member context"); AddIndent(); UProperty prop = ReadPointer <UProperty>(); // although that isn't a UFunction, we are not going to indirect the props of a struct, so this should be fine output.AppendLine(indents + " Expression within struct " + prop.GetName() + ", offset " + prop.GetOffset_ForDebug()); output.AppendLine(indents + " Expression to struct:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_LetDelegate: { output.AppendLine(FmtOpcodeIndent(opcode) + "LetDelegate (Variable = Expression)"); AddIndent(); // Variable expr. output.AppendLine(indents + " Variable:"); SerializeExpr(); // Assignment expr. output.AppendLine(indents + " Expression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_LetMulticastDelegate: { output.AppendLine(FmtOpcodeIndent(opcode) + "LetMulticastDelegate (Variable = Expression)"); AddIndent(); // Variable expr. output.AppendLine(indents + " Variable:"); SerializeExpr(); // Assignment expr. output.AppendLine(indents + " Expression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_ComputedJump: { output.AppendLine(FmtOpcodeIndent(opcode) + "Computed Jump, offset specified by expression:"); AddIndent(); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_Jump: { uint skipCount = ReadSkipCount(); output.AppendLine(FmtOpcodeIndent(opcode) + "Jump to offset " + FmtSkipCount(skipCount)); break; } case EExprToken.EX_LocalVariable: { UProperty property = ReadPointer <UProperty>(); output.AppendLine(FmtOpcodeIndent(opcode) + "Local variable named " + FmtObjNameOrNull(property)); break; } case EExprToken.EX_DefaultVariable: { UProperty property = ReadPointer <UProperty>(); output.AppendLine(FmtOpcodeIndent(opcode) + "Default variable named " + FmtObjNameOrNull(property)); break; } case EExprToken.EX_InstanceVariable: { UProperty property = ReadPointer <UProperty>(); output.AppendLine(FmtOpcodeIndent(opcode) + "Instance variable named " + FmtObjNameOrNull(property)); break; } case EExprToken.EX_LocalOutVariable: { UProperty property = ReadPointer <UProperty>(); output.AppendLine(FmtOpcodeIndent(opcode) + "Local out variable named " + FmtObjNameOrNull(property)); break; } case EExprToken.EX_InterfaceContext: { output.AppendLine(FmtOpcodeIndent(opcode) + "EX_InterfaceContext:"); SerializeExpr(); break; } case EExprToken.EX_DeprecatedOp4A: { output.AppendLine(FmtOpcodeIndent(opcode) + "This opcode has been removed and does nothing."); break; } case EExprToken.EX_Nothing: case EExprToken.EX_EndOfScript: case EExprToken.EX_EndFunctionParms: case EExprToken.EX_EndStructConst: case EExprToken.EX_EndArray: case EExprToken.EX_EndArrayConst: case EExprToken.EX_IntZero: case EExprToken.EX_IntOne: case EExprToken.EX_True: case EExprToken.EX_False: case EExprToken.EX_NoObject: case EExprToken.EX_NoInterface: case EExprToken.EX_Self: case EExprToken.EX_EndParmValue: { output.AppendLine(FmtOpcodeIndent(opcode) + opcode.ToString()); break; } case EExprToken.EX_Return: { output.AppendLine(FmtOpcodeIndent(opcode) + opcode.ToString()); SerializeExpr(); // Return expression. break; } case EExprToken.EX_CallMath: { UStruct stackNode = ReadPointer <UStruct>(); output.AppendLine(FmtOpcodeIndent(opcode) + "Call Math (stack node " + UObject.GetNameSafe(stackNode != null ? stackNode.GetOuter() : null) + "::" + UObject.GetNameSafe(stackNode) + ")"); while (SerializeExpr() != EExprToken.EX_EndFunctionParms) { // Params } break; } case EExprToken.EX_FinalFunction: { UStruct stackNode = ReadPointer <UStruct>(); output.AppendLine(FmtOpcodeIndent(opcode) + "Final Function (stack node " + FmtObjOuterNameOrNull(stackNode) + "::" + FmtObjNameOrNull(stackNode) + ")"); while (SerializeExpr() != EExprToken.EX_EndFunctionParms) { // Params } break; } case EExprToken.EX_CallMulticastDelegate: { UStruct stackNode = ReadPointer <UStruct>(); output.AppendLine(FmtOpcodeIndent(opcode) + "CallMulticastDelegate (signature " + FmtObjOuterNameOrNull(stackNode) + "::" + FmtObjNameOrNull(stackNode) + ") delegate:"); SerializeExpr(); output.AppendLine(FmtOpcodeIndent(opcode) + "Params:"); while (SerializeExpr() != EExprToken.EX_EndFunctionParms) { // Params } break; } case EExprToken.EX_VirtualFunction: { string functionName = ReadName(); output.AppendLine(FmtOpcodeIndent(opcode) + "Virtual Function named " + functionName); while (SerializeExpr() != EExprToken.EX_EndFunctionParms) { } break; } case EExprToken.EX_ClassContext: case EExprToken.EX_Context: case EExprToken.EX_Context_FailSilent: { output.AppendLine(FmtOpcodeIndent(opcode) + (opcode == EExprToken.EX_ClassContext ? "Class Context" : "Context")); AddIndent(); // Object expression. output.AppendLine(indents + " ObjectExpression:"); SerializeExpr(); if (opcode == EExprToken.EX_Context_FailSilent) { output.AppendLine(indents + " Can fail silently on access none "); } // Code offset for NULL expressions. uint skipCount = ReadSkipCount(); output.AppendLine(indents + " Skip Bytes: " + FmtSkipCount(skipCount)); // Property corresponding to the r-value data, in case the l-value needs to be mem-zero'd UField field = ReadPointer <UField>(); output.AppendLine(indents + " R-Value Property: " + FmtObjNameOrNull(field)); // Context expression. output.AppendLine(indents + " ContextExpression:"); SerializeExpr(); DropIndent(); break; } case EExprToken.EX_IntConst: { int constValue = ReadInt32(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal int32 " + constValue); break; } case EExprToken.EX_SkipOffsetConst: { uint constValue = ReadSkipCount(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal CodeSkipSizeType " + FmtSkipCount(constValue)); break; } case EExprToken.EX_FloatConst: { float constValue = ReadFloat(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal float " + constValue); break; } case EExprToken.EX_StringConst: { string constValue = ReadString8(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal ansi string \"" + constValue + "\""); break; } case EExprToken.EX_UnicodeStringConst: { string constValue = ReadString16(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal unicode string \"" + constValue + "\""); break; } case EExprToken.EX_TextConst: { // What kind of text are we dealing with? EBlueprintTextLiteralType textLiteralType = (EBlueprintTextLiteralType)script[scriptIndex++]; switch (textLiteralType) { case EBlueprintTextLiteralType.Empty: { output.AppendLine(FmtOpcodeIndent(opcode) + "literal text - empty"); break; } case EBlueprintTextLiteralType.LocalizedText: { string sourceString = ReadString(); string keyString = ReadString(); string namespaceString = ReadString(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal text - localized text { namespace: \"" + namespaceString + "\", key: \"" + keyString + "\", source: \"" + sourceString + "\" }"); break; } case EBlueprintTextLiteralType.InvariantText: { string sourceString = ReadString(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal text - invariant text: \"" + sourceString + "\""); break; } case EBlueprintTextLiteralType.LiteralString: { string sourceString = ReadString(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal text - literal string: \"" + sourceString + "\""); break; } case EBlueprintTextLiteralType.StringTableEntry: { ReadPointer <UObject>(); // String Table asset (if any) string tableIdString = ReadString(); string keyString = ReadString(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal text - string table entry { tableid: \"" + tableIdString + "\", key: \"" + keyString + "\" }"); break; } default: throw new Exception("Unknown EBlueprintTextLiteralType! Please update ProcessCommon() to handle this type of text."); } break; } case EExprToken.EX_ObjectConst: { UObject pointer = ReadPointer <UObject>(); output.AppendLine(FmtOpcodeIndent(opcode) + "EX_ObjectConst (" + FmtPtr(pointer) + ":" + pointer.GetFullName()); break; } case EExprToken.EX_SoftObjectConst: { output.AppendLine(FmtOpcodeIndent(opcode) + "EX_SoftObjectConst"); SerializeExpr(); break; } case EExprToken.EX_NameConst: { string constValue = ReadName(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal name " + constValue); break; } case EExprToken.EX_RotationConst: { float pitch = ReadFloat(); float yaw = ReadFloat(); float roll = ReadFloat(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal rotation (" + pitch + "," + yaw + "," + roll + ")"); break; } case EExprToken.EX_VectorConst: { float x = ReadFloat(); float y = ReadFloat(); float z = ReadFloat(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal vector (" + x + "," + y + "," + z + ")"); break; } case EExprToken.EX_TransformConst: { float rotX = ReadFloat(); float rotY = ReadFloat(); float rotZ = ReadFloat(); float rotW = ReadFloat(); float transX = ReadFloat(); float transY = ReadFloat(); float transZ = ReadFloat(); float scaleX = ReadFloat(); float scaleY = ReadFloat(); float scaleZ = ReadFloat(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal transform " + "R(" + rotX + "," + rotY + "," + rotZ + "," + rotW + "," + ") " + "T(" + transX + "," + transY + "," + transZ + ") " + "T(" + scaleX + "," + scaleY + "," + scaleZ + ")"); break; } case EExprToken.EX_StructConst: { UScriptStruct unrealStruct = ReadPointer <UScriptStruct>(); int serializedSize = ReadInt32(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal struct " + unrealStruct.GetName() + " (serialized size: " + serializedSize + ")"); break; } case EExprToken.EX_SetArray: { output.AppendLine(FmtOpcodeIndent(opcode) + "set array"); SerializeExpr(); while (SerializeExpr() != EExprToken.EX_EndArray) { // Array contents } break; } case EExprToken.EX_ArrayConst: { UProperty innerProp = ReadPointer <UProperty>(); int num = ReadInt32(); output.AppendLine(FmtOpcodeIndent(opcode) + "set array const - elements number: " + num + ", inner property: " + UObject.GetNameSafe(innerProp)); break; } case EExprToken.EX_ByteConst: { byte constValue = ReadByte(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal byte " + constValue); break; } case EExprToken.EX_IntConstByte: { int constValue = ReadByte(); output.AppendLine(FmtOpcodeIndent(opcode) + "literal int " + constValue); break; } case EExprToken.EX_MetaCast: { UClass unrealClass = ReadPointer <UClass>(); output.AppendLine(FmtOpcodeIndent(opcode) + "MetaCast to " + unrealClass.GetName() + " of expr:"); SerializeExpr(); break; } case EExprToken.EX_DynamicCast: { UClass unrealClass = ReadPointer <UClass>(); output.AppendLine(FmtOpcodeIndent(opcode) + "DynamicCast to " + unrealClass.GetName() + " of expr:"); SerializeExpr(); break; } case EExprToken.EX_JumpIfNot: { // Code offset. uint skipCount = ReadSkipCount(); output.AppendLine(FmtOpcodeIndent(opcode) + "Jump to offset " + FmtSkipCount(skipCount) + " if not expr:"); // Boolean expr. SerializeExpr(); break; } case EExprToken.EX_Assert: { ushort lineNumber = ReadUInt16(); byte inDebugMode = ReadByte(); output.AppendLine(FmtOpcodeIndent(opcode) + "assert at line " + lineNumber + ", in debug mode = " + inDebugMode + " with expr:"); SerializeExpr(); // Assert expr. break; } case EExprToken.EX_Skip: { uint w = ReadSkipCount(); output.AppendLine(FmtOpcodeIndent(opcode) + "possibly skip " + FmtSkipCount(w) + " bytes of expr:"); // Expression to possibly skip. SerializeExpr(); break; } case EExprToken.EX_InstanceDelegate: { // the name of the function assigned to the delegate. string funcName = ReadName(); output.AppendLine(FmtOpcodeIndent(opcode) + "instance delegate function named " + funcName); break; } case EExprToken.EX_AddMulticastDelegate: { output.AppendLine(FmtOpcodeIndent(opcode) + "Add MC delegate"); SerializeExpr(); SerializeExpr(); break; } case EExprToken.EX_RemoveMulticastDelegate: { output.AppendLine(FmtOpcodeIndent(opcode) + "Remove MC delegate"); SerializeExpr(); SerializeExpr(); break; } case EExprToken.EX_ClearMulticastDelegate: { output.AppendLine(FmtOpcodeIndent(opcode) + "Clear MC delegate"); SerializeExpr(); break; } case EExprToken.EX_BindDelegate: { // the name of the function assigned to the delegate. string funcName = ReadName(); output.AppendLine(FmtOpcodeIndent(opcode) + "BindDelegate '" + funcName + "'"); output.AppendLine(indents + " Delegate:"); SerializeExpr(); output.AppendLine(indents + " Object:"); SerializeExpr(); break; } case EExprToken.EX_PushExecutionFlow: { uint skipCount = ReadSkipCount(); output.AppendLine(FmtOpcodeIndent(opcode) + "FlowStack.Push(" + FmtSkipCount(skipCount) + ");"); break; } case EExprToken.EX_PopExecutionFlow: { output.AppendLine(FmtOpcodeIndent(opcode) + "if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! }"); break; } case EExprToken.EX_PopExecutionFlowIfNot: { output.AppendLine(FmtOpcodeIndent(opcode) + "if (!condition) { if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! } }"); // Boolean expr. SerializeExpr(); break; } case EExprToken.EX_Breakpoint: { output.AppendLine(FmtOpcodeIndent(opcode) + "<<< BREAKPOINT >>>"); break; } case EExprToken.EX_WireTracepoint: { output.AppendLine(FmtOpcodeIndent(opcode) + ".. wire debug site .."); break; } case EExprToken.EX_InstrumentationEvent: { EScriptInstrumentation eventType = (EScriptInstrumentation)ReadByte(); switch (eventType) { case EScriptInstrumentation.InlineEvent: output.AppendLine(FmtOpcodeIndent(opcode) + ".. instrumented inline event .."); break; case EScriptInstrumentation.Stop: output.AppendLine(FmtOpcodeIndent(opcode) + ".. instrumented event stop .."); break; case EScriptInstrumentation.PureNodeEntry: output.AppendLine(FmtOpcodeIndent(opcode) + ".. instrumented pure node entry site .."); break; case EScriptInstrumentation.NodeDebugSite: output.AppendLine(FmtOpcodeIndent(opcode) + ".. instrumented debug site .."); break; case EScriptInstrumentation.NodeEntry: output.AppendLine(FmtOpcodeIndent(opcode) + ".. instrumented wire entry site .."); break; case EScriptInstrumentation.NodeExit: output.AppendLine(FmtOpcodeIndent(opcode) + ".. instrumented wire exit site .."); break; case EScriptInstrumentation.PushState: output.AppendLine(FmtOpcodeIndent(opcode) + ".. push execution state .."); break; case EScriptInstrumentation.RestoreState: output.AppendLine(FmtOpcodeIndent(opcode) + ".. restore execution state .."); break; case EScriptInstrumentation.ResetState: output.AppendLine(FmtOpcodeIndent(opcode) + ".. reset execution state .."); break; case EScriptInstrumentation.SuspendState: output.AppendLine(FmtOpcodeIndent(opcode) + ".. suspend execution state .."); break; case EScriptInstrumentation.PopState: output.AppendLine(FmtOpcodeIndent(opcode) + ".. pop execution state .."); break; case EScriptInstrumentation.TunnelEndOfThread: output.AppendLine(FmtOpcodeIndent(opcode) + ".. tunnel end of thread .."); break; } break; } case EExprToken.EX_Tracepoint: { output.AppendLine(FmtOpcodeIndent(opcode) + ".. debug site .."); break; } case EExprToken.EX_SwitchValue: { ushort numCases = ReadUInt16(); uint afterSkip = ReadSkipCount(); output.AppendLine(FmtOpcodeIndent(opcode) + "Switch Value " + numCases + " cases, end in " + FmtSkipCount(afterSkip)); AddIndent(); output.AppendLine(indents + " Index:"); SerializeExpr(); for (ushort caseIndex = 0; caseIndex < numCases; ++caseIndex) { output.AppendLine(indents + " [" + caseIndex + "] Case Index (label: " + FmtScriptIndex(scriptIndex) + ")"); SerializeExpr(); // case index value term uint offsetToNextCase = ReadSkipCount(); output.AppendLine(indents + " [" + caseIndex + "] Offset to the next case: " + FmtSkipCount(offsetToNextCase)); output.AppendLine(indents + " [" + caseIndex + "] Case Result:"); SerializeExpr(); // case term } output.AppendLine(indents + " Default result (label: " + FmtScriptIndex(scriptIndex) + ")"); SerializeExpr(); output.AppendLine(indents + " (label: " + FmtScriptIndex(scriptIndex) + ")"); DropIndent(); break; } case EExprToken.EX_ArrayGetByRef: { output.AppendLine(FmtOpcodeIndent(opcode) + "Array Get-by-Ref Index"); AddIndent(); SerializeExpr(); SerializeExpr(); DropIndent(); break; } default: { string error = "Unknown bytecode 0x" + ((byte)opcode).ToString("X2") + "; ignoring it"; output.AppendLine(FmtOpcodeIndent(opcode) + "!!!" + error); FMessage.Log(ELogVerbosity.Warning, error); } break; } }
/// <summary> /// Gets the UClass for the given type /// </summary> /// <param name="type">The type</param> /// <returns>The UClass for the given type</returns> public static UClass GetClass(Type type) { UClass result = null; if (classes.TryGetValue(type, out result)) { return(result); } if (type.IsEnum || type.IsValueType || typeof(IDelegateBase).IsAssignableFrom(type)) { // Find the top-most UClass (UUserDefinedEnum, UEnum, UUserDefinedStruct, UUserStruct, etc) // NOTE: This wont contain any useful information about the actual type itself IntPtr address = IntPtr.Zero; if (type.IsEnum) { address = UEnum.GetEnumAddress(type); } else if (type.IsValueType) { address = UScriptStruct.GetStructAddress(type); } else { address = UFunction.GetDelegateSignatureAddress(type); } if (address != IntPtr.Zero) { return(GetClass(address)); } return(null); } if (!type.IsSameOrSubclassOf(typeof(UObject)) && (!type.IsInterface || !typeof(IInterface).IsAssignableFrom(type)) || type == typeof(IInterface)) { return(null); } if (seenClasses.Contains(type)) { // Note: GetModuleCount uses a lock // TODO: Find some multicast delegate which is called when a module is loaded or a new class type is created. // - FModuleManager::Get().OnProcessLoadedObjectsCallback if (FModuleManager.Get().GetModuleCount() != lastModuleCount) { seenClasses.Clear(); } else { return(null); } } if (!seenClasses.Contains(type)) { seenClasses.Add(type); UMetaPathAttribute pathAttribute; if (UnrealTypes.Native.TryGetValue(type, out pathAttribute)) { IntPtr classAddress = GetClassAddress(pathAttribute.Path); if (classAddress == IntPtr.Zero) { // Fallback if this class isn't loaded yet. TODO: Check if this is the correct method to call. classAddress = NativeReflection.LoadObject(Classes.UClass, IntPtr.Zero, pathAttribute.Path); } if (classAddress != IntPtr.Zero) { UClass unrealClass = GCHelper.Find <UClass>(classAddress); if (unrealClass != null) { classesByAddress[classAddress] = type; classes[type] = unrealClass; return(unrealClass); } } } } return(null); }
// Move this somewhere else? Where would this be more appropriate? public static Type GetTypeFromProperty(UProperty prop) { if (prop == null) { return(null); } switch (prop.PropertyType) { case EPropertyType.Bool: return(typeof(bool)); case EPropertyType.Int8: return(typeof(sbyte)); case EPropertyType.Byte: return(typeof(byte)); case EPropertyType.Int16: return(typeof(short)); case EPropertyType.UInt16: return(typeof(ushort)); case EPropertyType.Int: return(typeof(int)); case EPropertyType.UInt32: return(typeof(uint)); case EPropertyType.Int64: return(typeof(long)); case EPropertyType.UInt64: return(typeof(ulong)); case EPropertyType.Float: return(typeof(float)); case EPropertyType.Double: return(typeof(double)); case EPropertyType.Enum: { UEnum unrealEnum = (prop as UEnumProperty).GetEnum(); if (unrealEnum == null) { return(null); } Type enumType; ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(unrealEnum.GetPathName(), out enumType); return(enumType); } case EPropertyType.Str: return(typeof(string)); case EPropertyType.Name: return(typeof(FName)); case EPropertyType.Text: return(typeof(FText)); case EPropertyType.Interface: { UClass unrealClassInterface = (prop as UInterfaceProperty).InterfaceClass; if (unrealClassInterface == null) { return(null); } Type interfaceType; ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(unrealClassInterface.GetPathName(), out interfaceType); return(interfaceType); } case EPropertyType.Struct: { UScriptStruct unrealStruct = (prop as UStructProperty).Struct; if (unrealStruct == null) { return(null); } Type structType; ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(unrealStruct.GetPathName(), out structType); return(structType); } case EPropertyType.Class: case EPropertyType.Object: case EPropertyType.LazyObject: case EPropertyType.WeakObject: case EPropertyType.SoftClass: case EPropertyType.SoftObject: { UClass objectClass = (prop as UObjectPropertyBase).PropertyClass; switch (prop.PropertyType) { case EPropertyType.Class: objectClass = (prop as UClassProperty).MetaClass; break; case EPropertyType.SoftClass: objectClass = (prop as USoftClassProperty).MetaClass; break; } Type type = null; if (objectClass != null) { // Could use UClass.GetType but using AllKnownUnrealTypes for slightly more coverage // UClass.GetType(objectClass) ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(objectClass.GetPathName(), out type); } if (type == null) { //classType = typeof(UObject);// Fall back to UObject? Return null? return(null); } switch (prop.PropertyType) { case EPropertyType.Class: return(typeof(TSubclassOf <>).MakeGenericType(type)); case EPropertyType.LazyObject: return(typeof(TLazyObject <>).MakeGenericType(type)); case EPropertyType.WeakObject: return(typeof(TWeakObject <>).MakeGenericType(type)); case EPropertyType.SoftClass: return(typeof(TSoftClass <>).MakeGenericType(type)); case EPropertyType.SoftObject: return(typeof(TSoftObject <>).MakeGenericType(type)); case EPropertyType.Object: return(type); } return(type); } case EPropertyType.Delegate: case EPropertyType.MulticastDelegate: Type delegateType = null; UFunction signatureFunc = null; if (prop.PropertyType == EPropertyType.Delegate) { signatureFunc = (prop as UDelegateProperty).SignatureFunction; } else if (prop.PropertyType == EPropertyType.MulticastDelegate) { signatureFunc = (prop as UMulticastDelegateProperty).SignatureFunction; } if (signatureFunc != null) { if (ManagedUnrealModuleInfo.AllKnownUnrealTypes.TryGetValue(signatureFunc.GetPathName(), out delegateType)) { if (prop.PropertyType == EPropertyType.Delegate) { if (!delegateType.IsSameOrSubclassOfGeneric(typeof(FDelegate <>))) { delegateType = null; } } else if (prop.PropertyType == EPropertyType.MulticastDelegate) { if (!delegateType.IsSameOrSubclassOfGeneric(typeof(FMulticastDelegate <>))) { delegateType = null; } } } } return(delegateType); case EPropertyType.Array: { UArrayProperty arrayProp = prop as UArrayProperty; Type innerType = GetTypeFromProperty(arrayProp.Inner); if (innerType != null) { // Possibly handle IReadOnlyList? return(typeof(IList <>).MakeGenericType(innerType)); } return(null); } case EPropertyType.Set: { USetProperty setProp = prop as USetProperty; Type innerType = GetTypeFromProperty(setProp.ElementProp); if (innerType != null) { return(typeof(ISet <>).MakeGenericType(innerType)); } return(null); } case EPropertyType.Map: { UMapProperty mapProp = prop as UMapProperty; Type keyType = GetTypeFromProperty(mapProp.KeyProp); Type valueType = GetTypeFromProperty(mapProp.ValueProp); if (keyType != null && valueType != null) { // Possibly handle IReadOnlyDictionary? return(typeof(IDictionary <,>).MakeGenericType(keyType, valueType)); } return(null); } } return(null); }
internal static void GenerateCode(string[] args) { try { bool invalidArgs = false; if (args.Length > 0) { CodeGenerator codeGenerator = null; switch (args[0]) { /*case "blueprints": * AssetLoadMode loadMode = AssetLoadMode.Game; * bool clearAssetCache = false; * bool skipLevels = false; * if (args.Length > 1) * { * switch (args[1]) * { * case "game": * loadMode = CodeGenerator.AssetLoadMode.Game; * break; * case "gameplugins": * loadMode = CodeGenerator.AssetLoadMode.GamePlugins; * break; * case "engine": * loadMode = CodeGenerator.AssetLoadMode.Engine; * break; * case "engineplugins": * loadMode = CodeGenerator.AssetLoadMode.EnginePlugins; * break; * case "all": * loadMode = CodeGenerator.AssetLoadMode.All; * break; * } * } * if (args.Length > 2) * { * bool.TryParse(args[2], out clearAssetCache); * } * if (args.Length > 3) * { * bool.TryParse(args[3], out skipLevels); * } * codeGenerator = new CodeGenerator(); * codeGenerator.GenerateCodeForBlueprints(loadMode, clearAssetCache, skipLevels); * break;*/ case "game": codeGenerator = new CodeGenerator(); codeGenerator.GenerateCodeForModules(new UnrealModuleType[] { UnrealModuleType.Game }); break; case "gameplugins": codeGenerator = new CodeGenerator(); codeGenerator.GenerateCodeForModules(new UnrealModuleType[] { UnrealModuleType.GamePlugin }); break; case "modules": // Engine modules (whitelisted) codeGenerator = new CodeGenerator(); //codeGenerator.Settings.ExportMode = CodeGeneratorSettings.CodeExportMode.All; //codeGenerator.Settings.ExportAllFunctions = true; //codeGenerator.Settings.ExportAllProperties = true; string whitelistFile = Path.Combine(codeGenerator.Settings.GetManagedPluginSettingsDir(), "ModulesWhitelist.txt"); string blacklistFile = Path.Combine(codeGenerator.Settings.GetManagedPluginSettingsDir(), "ModulesBlacklist.txt"); if (File.Exists(whitelistFile)) { foreach (string line in File.ReadAllLines(whitelistFile)) { if (!string.IsNullOrEmpty(line)) { codeGenerator.ModulesNamesWhitelist.Add(line); } } } if (File.Exists(blacklistFile)) { foreach (string line in File.ReadAllLines(blacklistFile)) { if (!string.IsNullOrEmpty(line)) { codeGenerator.ModulesNamesBlacklist.Add(line); } } } codeGenerator.GenerateCodeForEngineModules(); break; case "all_modules": codeGenerator = new CodeGenerator(); codeGenerator.GenerateCodeForAllModules(); break; case "engine_modules": codeGenerator = new CodeGenerator(); codeGenerator.GenerateCodeForEngineModules(); break; case "module": if (args.Length > 1) { bool forceExport = false; if (args.Length > 2) { bool.TryParse(args[2], out forceExport); } codeGenerator = new CodeGenerator(); // Tests / using these for types for use in this lib //codeGenerator.Settings.CheckUObjectDestroyed = false; //codeGenerator.Settings.GenerateIsValidSafeguards = false; //codeGenerator.Settings.MergeEnumFiles = false; if (forceExport) { codeGenerator.Settings.ExportMode = CodeGeneratorSettings.CodeExportMode.All; codeGenerator.Settings.ExportAllFunctions = true; codeGenerator.Settings.ExportAllProperties = true; } codeGenerator.GenerateCodeForModule(args[1], true); } else { invalidArgs = true; } break; case "check_flags": { // This checks the flags for manually wrapped types Assembly[] assemblies = new Assembly[2]; assemblies[0] = Assembly.GetExecutingAssembly(); UClass engineClass = UClass.GetClass("/Script/Engine.Engine"); if (engineClass != null) { Type type = UClass.GetType(engineClass); if (type == null) { FMessage.Log(ELogVerbosity.Warning, "Failed to fine UEngine type"); } else { assemblies[1] = type.Assembly; } } else { FMessage.Log(ELogVerbosity.Warning, "Failed to fine UEngine class"); } foreach (Assembly assembly in assemblies) { if (assembly == null) { continue; } foreach (Type type in assembly.GetTypes()) { UMetaPathAttribute pathAttribute = type.GetCustomAttribute <UMetaPathAttribute>(false); if (pathAttribute != null && !string.IsNullOrEmpty(pathAttribute.Path)) { uint oldFlags = 0; if (type.IsSameOrSubclassOf(typeof(UObject))) { UClassAttribute classAttribute = type.GetCustomAttribute <UClassAttribute>(false); if (classAttribute != null) { oldFlags = (uint)classAttribute.Flags; } UClass unrealClass = UClass.GetClass(pathAttribute.Path); if (unrealClass != null) { if (oldFlags != (uint)unrealClass.ClassFlags) { FMessage.Log("old: 0x" + oldFlags.ToString("X8") + " new: 0x" + ((uint)unrealClass.ClassFlags).ToString("X8") + " path: " + unrealClass.GetPathName()); } } } else { UStructAttribute structAttribute = type.GetCustomAttribute <UStructAttribute>(false); if (structAttribute != null) { oldFlags = (uint)structAttribute.Flags; } UScriptStruct unrealStruct = UScriptStruct.GetStruct(pathAttribute.Path); if (unrealStruct != null) { if (oldFlags != (uint)unrealStruct.StructFlags) { FMessage.Log("old: 0x" + oldFlags.ToString("X8") + " new: 0x" + ((uint)unrealStruct.StructFlags).ToString("X8") + " path: " + unrealStruct.GetPathName()); } } } } } FMessage.Log("--------"); } } break; case "compile": CompileGeneratedCode(); break; default: invalidArgs = true; break; } } else { invalidArgs = true; } if (invalidArgs) { FMessage.Log(ELogVerbosity.Warning, "Invalid input. Provide one of the following: game, gameplugins, modules, module [ModuleName], compile"); } } catch (Exception e) { FMessage.Log(ELogVerbosity.Error, "Generate code failed. Error: \n" + e); } }