private void GenerateCodeForEnum(UnrealModuleInfo module, UEnum unrealEnum) { UnrealModuleType moduleAssetType; string currentNamespace = GetModuleNamespace(unrealEnum, out moduleAssetType); List <string> namespaces = GetDefaultNamespaces(); CSharpTextBuilder builder = new CSharpTextBuilder(Settings.IndentType); if (!string.IsNullOrEmpty(currentNamespace)) { builder.AppendLine("namespace " + currentNamespace); builder.OpenBrace(); } GenerateCodeForEnum(module, builder, unrealEnum); if (!string.IsNullOrEmpty(currentNamespace)) { builder.CloseBrace(); } builder.InsertNamespaces(currentNamespace, namespaces, Settings.SortNamespaces); OnCodeGenerated(module, moduleAssetType, GetTypeName(unrealEnum), unrealEnum.GetPathName(), builder); }
private bool IsBlueprintVisibleEnum(UEnum unrealEnum) { if (forceExportEnums.Contains(unrealEnum.GetPathName())) { return(true); } return(unrealEnum.GetBoolMetaData(MDEnum.BlueprintType)); }
/// <summary> /// Finds or loads the UEnum for the given enum type /// </summary> /// <param name="type">The type of the enum</param> /// <returns>The UEnum for the given enum type</returns> public static UEnum ResolveEnum(Type type) { UEnum unrealEnum = GetEnum(type); if (unrealEnum == null) { unrealEnum = LoadEnum(type); } return(unrealEnum); }
private bool CanExportEnum(UEnum unrealEnum) { // Skip enums which are already defined in this project if (projectDefinedTypes.ContainsKey(unrealEnum.GetPathName())) { return(false); } return(true); }
/// <summary> /// Finds or loads the UEnum for the given path (e.g. "/Script/CoreUObject.ESearchCase") /// </summary> /// <param name="path">The path of the UEnum</param> /// <returns>The UEnum for the given path</returns> public static UEnum ResolveEnum(string path) { UEnum unrealEnum = GetEnum(path); if (unrealEnum == null) { unrealEnum = LoadEnum(path); } return(unrealEnum); }
/// <summary> /// Finds or loads the UEnum for the given enum type /// </summary> /// <typeparam name="T">The type of the enum</typeparam> /// <returns>The UEnum for the given enum type</returns> public static UEnum ResolveEnum <T>() where T : struct, IConvertible { UEnum unrealEnum = GetEnum <T>(); if (unrealEnum == null) { unrealEnum = LoadEnum <T>(); } return(unrealEnum); }
private void GenerateCodeForEnums(UnrealModuleInfo module, UEnum[] enums, bool combine) { if (enums.Length == 0) { return; } if (combine) { // Put all enums into a single file prefixed with the module name string enumsName = module.Name + "Enums"; UnrealModuleType moduleAssetType; string currentNamespace = GetModuleNamespace(enums[0], out moduleAssetType, false); List <string> namespaces = GetDefaultNamespaces(); CSharpTextBuilder builder = new CSharpTextBuilder(Settings.IndentType); if (!string.IsNullOrEmpty(currentNamespace)) { builder.AppendLine("namespace " + currentNamespace); builder.OpenBrace(); } UEnum lastEnum = enums.Last(); foreach (UEnum unrealEnum in enums) { SlowTaskStep(unrealEnum); GenerateCodeForEnum(module, builder, unrealEnum); if (unrealEnum != lastEnum) { builder.AppendLine(); } } if (!string.IsNullOrEmpty(currentNamespace)) { builder.CloseBrace(); } builder.InsertNamespaces(currentNamespace, namespaces, Settings.SortNamespaces); OnCodeGenerated(module, moduleAssetType, enumsName, null, builder); } else { foreach (UEnum unrealEnum in enums) { SlowTaskStep(unrealEnum); GenerateCodeForEnum(module, unrealEnum); } } }
private string GetEnumValueName(UEnum unrealEnum, int index) { string qualifiedValueName = unrealEnum.GetNameByIndex((byte)index).ToString(); int colonPos = qualifiedValueName.IndexOf("::"); string rawName = null; if (colonPos >= 0) { rawName = qualifiedValueName.Substring(colonPos + 2); } else { rawName = qualifiedValueName; } return(rawName); }
private string GetEnumValuePrefix(UEnum unrealEnum) { string enumPrefix; if (enumValuePrefixCache.TryGetValue(unrealEnum.GetPathName(), out enumPrefix)) { return(enumPrefix); } GetEnumValues(unrealEnum, false); if (enumValuePrefixCache.TryGetValue(unrealEnum.GetPathName(), out enumPrefix)) { return(enumPrefix); } return(null); }
private string GetMarshalerFromProperty(UProperty property, List <string> namespaces, bool isFunction, bool fixedSizeArrayInnerMarshaler) { if (property.IsFixedSizeArray && !fixedSizeArrayInnerMarshaler) { if (IsOwnerClassOrStructAsClass(property)) { return(GetTypeName(property, namespaces)); } else { // Should expect either a UClass or a UScriptStruct. Fixed sized arrays aren't supported on functions in unreal. System.Diagnostics.Debug.Assert(property.GetOwnerStruct().IsA <UScriptStruct>()); return(Names.TFixedSizeArrayMarshaler + "<" + GetTypeName(property, namespaces) + ">"); //// FixedSizeArrayMarshaler<int, BlittableTypeMarshaler<int>> //return Names.FixedSizeArrayMarshaler + "<" + GetTypeName(property, namespaces) + ", " + // GetMarshalerFromProperty(property, namespaces, isFunction, true) + ">"; } } UNumericProperty numericProperty = property as UNumericProperty; if ((numericProperty != null && numericProperty.IsEnum && numericProperty.GetIntPropertyEnum() != null) || property.PropertyType == EPropertyType.Enum) { UEnum unrealEnum = null; if (property.PropertyType == EPropertyType.Enum) { unrealEnum = (property as UEnumProperty).GetEnum(); } else { unrealEnum = numericProperty.GetIntPropertyEnum(); } return(Names.EnumMarshaler + "<" + GetTypeName(unrealEnum, namespaces) + ">"); } string blittableTypeName = GetBlittablePropertyTypeName(property, namespaces); if (!string.IsNullOrEmpty(blittableTypeName)) { return(Names.BlittableTypeMarshaler + "<" + blittableTypeName + ">"); } switch (property.PropertyType) { case EPropertyType.Bool: return(Names.BoolMarshaler); case EPropertyType.Str: return(Names.FStringMarshaler); case EPropertyType.Text: return(Names.FTextMarshaler); case EPropertyType.Struct: { UStruct unrealStruct = (property as UStructProperty).Struct; if (IsClassOrStructAsClass(unrealStruct)) { return(Names.StructAsClassMarshaler + "<" + GetTypeName(property, namespaces) + ">"); } else { // Normal structs use their own type name and have static FromNative/ToNative methods return(GetTypeName(property, namespaces)); } } case EPropertyType.Delegate: { string delegateTypeName = GetTypeName(property, namespaces); return(Names.FDelegateMarshaler + "<" + delegateTypeName + ">"); } case EPropertyType.MulticastDelegate: { string delegateTypeName = GetTypeName(property, namespaces); return(Names.FMulticastDelegateMarshaler + "<" + delegateTypeName + ">"); } case EPropertyType.Array: { string arrayMarshalerName = Names.TArrayReadWriteMarshaler; if (IsOwnerClassOrStructAsClass(property)) { if (property.HasAnyPropertyFlags(EPropertyFlags.BlueprintReadOnly)) { arrayMarshalerName = Names.TArrayReadOnlyMarshaler; } } else { arrayMarshalerName = Names.TArrayCopyMarshaler; } UArrayProperty arrayProperty = property as UArrayProperty; return(arrayMarshalerName + "<" + GetTypeName(arrayProperty.Inner, namespaces) + ">"); } case EPropertyType.Set: { string setMarshalerName = Names.TSetReadWriteMarshaler; if (IsOwnerClassOrStructAsClass(property)) { if (property.HasAnyPropertyFlags(EPropertyFlags.BlueprintReadOnly)) { setMarshalerName = Names.TSetReadOnlyMarshaler; } } else { setMarshalerName = Names.TSetCopyMarshaler; } USetProperty setProperty = property as USetProperty; return(setMarshalerName + "<" + GetTypeName(setProperty.ElementProp, namespaces) + ">"); } case EPropertyType.Map: { string mapMarshalerName = Names.TMapReadWriteMarshaler; if (IsOwnerClassOrStructAsClass(property)) { if (property.HasAnyPropertyFlags(EPropertyFlags.BlueprintReadOnly)) { mapMarshalerName = Names.TMapReadOnlyMarshaler; } } else { mapMarshalerName = Names.TMapCopyMarshaler; } UMapProperty mapProperty = property as UMapProperty; return(mapMarshalerName + "<" + GetTypeName(mapProperty.KeyProp, namespaces) + ", " + GetTypeName(mapProperty.ValueProp, namespaces) + ">"); } case EPropertyType.Class: { UClass targetClass = (property as UClassProperty).MetaClass; string subclassOfMarshalerName = null; if (targetClass.ClassFlags.HasFlag(EClassFlags.Interface)) { subclassOfMarshalerName = Names.TSubclassOfInterfaceMarshaler; } else { subclassOfMarshalerName = Names.TSubclassOfMarshaler; } return(subclassOfMarshalerName + "<" + GetTypeName(targetClass, namespaces) + ">"); } case EPropertyType.Interface: return(Names.InterfaceMarshaler + "<" + GetTypeName((property as UInterfaceProperty).InterfaceClass, namespaces) + ">"); case EPropertyType.Object: return(Names.UObjectMarshaler + "<" + GetTypeName((property as UObjectProperty).PropertyClass, namespaces) + ">"); case EPropertyType.WeakObject: return(Names.TWeakObjectMarshaler + "<" + GetTypeName((property as UWeakObjectProperty).PropertyClass, namespaces) + ">"); case EPropertyType.LazyObject: return(Names.TLazyObjectMarshaler + "<" + GetTypeName((property as ULazyObjectProperty).PropertyClass, namespaces) + ">"); case EPropertyType.SoftClass: return(Names.TSoftClassMarshaler + "<" + GetTypeName((property as USoftClassProperty).MetaClass, namespaces) + ">"); case EPropertyType.SoftObject: return(Names.TSoftObjectMarshaler + "<" + GetTypeName((property as USoftObjectProperty).PropertyClass, namespaces) + ">"); default: return(null); } }
private List <EnumValueInfo> GetEnumValues(UEnum unrealEnum, bool getDocumentation) { int valueCount = unrealEnum.NumEnums(); bool isBlueprintEnum = unrealEnum.IsA <UUserDefinedEnum>(); List <EnumValueInfo> enumValues = new List <EnumValueInfo>(valueCount); // Try to identify a common prefix of the form PRE_, so we can strip it from all values. // We'll only strip it if it's present on all values not explicitly skipped. string commonPrefix = null; int commonPrefixCount = 0; int skippedValueCount = 0; int numMax = 0; if (Settings.RemoveEnumMAX) { // Skip all ending "MAX" values (Some enums have duplicate MAX (DORN_MAX / ENetDormancy_MAX)) for (int i = valueCount - 1; i >= 0; --i) { string rawName = GetEnumValueName(unrealEnum, i); // Using case sensitive here to avoid removing genuine "Max" values (still may be removing genuine "MAX" values) if (rawName.EndsWith("MAX")) { ++numMax; ++skippedValueCount; // Duplicate MAX should be "XXX_MAX" for last value and "MAX" for second to last value if (i < valueCount - 1 || !rawName.EndsWith("_MAX")) { break; } } else { break; } } } //if (numMax > 1) //{ // FMessage.Log("Duplicate MAX in enum " + unrealEnum.GetPathName()); //} for (int i = 0; i < valueCount - numMax; ++i) { string rawName = GetEnumValueName(unrealEnum, i); EnumValueInfo enumValue = new EnumValueInfo(); enumValue.Index = i; enumValue.Value = unrealEnum.GetValueByIndex(i); enumValue.Name = rawName; enumValue.DisplayName = MakeValidName(unrealEnum.GetDisplayNameTextStringByIndex(i)); if (getDocumentation) { enumValue.DocCommentSummary = unrealEnum.GetToolTipByIndex(i); } enumValues.Add(enumValue); // We can skip all of the common prefix checks for enums that are already namespaced in C++. // In the cases where a namespaced enum does have a common prefix for its values, it doesn't // match the PRE_* pattern, and it's generally necessary for syntactic reasons, // i.e. Touch1, Touch2, and so on in ETouchIndex. if (unrealEnum.GetCppForm() == UEnum.ECppForm.Regular) { // A handful of enums have bad values named this way in C++. if (rawName.StartsWith("TEMP_BROKEN")) { ++skippedValueCount; } // UHT inserts spacers for sparse enums. Since we're omitting the _MAX value, we'll // still export these to ensure that C# reflection gives an accurate value count, but // don't hold them against the common prefix count. else if (rawName.StartsWith("UnusedSpacer_")) { ++skippedValueCount; } // Infer the prefix from the first unskipped value. else if (string.IsNullOrEmpty(commonPrefix)) { int underscorePos = rawName.IndexOf("_"); if (underscorePos >= 0) { commonPrefix = rawName.Substring(0, underscorePos + 1); ++commonPrefixCount; } } else if (rawName.StartsWith(commonPrefix)) { ++commonPrefixCount; } } } if (valueCount != (commonPrefixCount + skippedValueCount)) { //if (!string.IsNullOrEmpty(commonPrefix)) //{ // FMessage.Log(string.Format("Rejecting common prefix '{0}' for '{1}' ({2}). ValueCount={3}, CommonPrefixCount={4}, SkippedValueCount={5}", // commonPrefix, unrealEnum.GetName(), unrealEnum.GetFName().DisplayIndex, valueCount, commonPrefixCount, skippedValueCount)); //} commonPrefix = null; } foreach (EnumValueInfo enumValue in enumValues) { if (!string.IsNullOrEmpty(commonPrefix)) { enumValue.Name = enumValue.Name.RemoveFromStart(commonPrefix); } //one enum has a member called "float" which isn't valid in C#. That said, C# enum values should be PascalCase anyway, so just uppercase it. if (char.IsLower(enumValue.Name[0])) { enumValue.Name = char.ToUpperInvariant(enumValue.Name[0]) + enumValue.Name.Substring(1); } if (char.IsDigit(enumValue.Name[0])) { enumValue.Name = "_" + enumValue.Name; } } // Update the enum prefix cache for lookup with default function params enumValuePrefixCache[unrealEnum.GetPathName()] = commonPrefix; return(enumValues); }
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 GenerateCodeForEnum(UnrealModuleInfo module, CSharpTextBuilder builder, UEnum unrealEnum) { bool isBlueprintType = unrealEnum.IsA <UUserDefinedEnum>(); AppendDocComment(builder, unrealEnum, isBlueprintType); AppendAttribute(builder, unrealEnum, module); // Set the underlying enum type if this enum is tagged as a BlueprintType string enumUnderlyingType = string.Empty; if (unrealEnum.HasMetaData(MDEnum.BlueprintType)) { enumUnderlyingType = " : byte"; } builder.AppendLine("public enum " + GetTypeName(unrealEnum) + enumUnderlyingType); builder.OpenBrace(); // TODO: Blueprint value bitflags // According to issue UE-32816 "enum values are currently assumed to be flag indices and not actual flag mask values" List <EnumValueInfo> enumValues = GetEnumValues(unrealEnum, true); int lastEnumIndex = enumValues.Count; foreach (EnumValueInfo enumValue in enumValues) { AppendDocComment(builder, enumValue.DocCommentSummary); if (isBlueprintType) { builder.AppendLine("[EnumValueName(\"" + enumValue.Name + "\")]"); } builder.AppendLine(string.Format("{0}={1}{2}", isBlueprintType ? enumValue.DisplayName : enumValue.Name, enumValue.Value, --lastEnumIndex > 0 ? "," : string.Empty)); } builder.CloseBrace(); }
private bool IsBlueprintVisibleEnum(UEnum unrealEnum) { return(unrealEnum.GetBoolMetaData(MDEnum.BlueprintType)); }
/// <summary> /// Set the UEnum of this property. /// Note: May only be called once to lazily initialize the property when using the default constructor. /// </summary> /// <param name="unrealEnum"></param> public void SetEnum(UEnum unrealEnum) { Native_UEnumProperty.SetEnum(Address, unrealEnum == null ? IntPtr.Zero : unrealEnum.Address); }
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 bool CanExportEnum(UEnum unrealEnum) { return(true); }
// 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); }
/// <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); }