From 00bcb7ca160e1297761936e6d6f4ebc1eb7ddfa7 Mon Sep 17 00:00:00 2001 From: Asval Date: Sun, 6 Jul 2025 18:52:16 +0200 Subject: [PATCH] refactored parts of this hell --- FModel/Extensions/KismetExtensions.cs | 313 +++++------ FModel/Extensions/StringExtensions.cs | 23 +- FModel/MainWindow.xaml | 13 +- FModel/MainWindow.xaml.cs | 12 - FModel/ViewModels/CUE4ParseViewModel.cs | 703 ++++++++---------------- FModel/Views/SettingsView.xaml | 2 +- 6 files changed, 383 insertions(+), 683 deletions(-) diff --git a/FModel/Extensions/KismetExtensions.cs b/FModel/Extensions/KismetExtensions.cs index 0ac16af9..0dde9710 100644 --- a/FModel/Extensions/KismetExtensions.cs +++ b/FModel/Extensions/KismetExtensions.cs @@ -3,10 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; -using System.Text.RegularExpressions; -using CUE4Parse.FileProvider; -using CUE4Parse.FileProvider.Objects; -using CUE4Parse.UE4.Assets; using CUE4Parse.UE4.Assets.Objects.Properties; using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Kismet; @@ -16,14 +12,12 @@ using CUE4Parse.UE4.Objects.Engine.Ai; using CUE4Parse.UE4.Objects.Engine.GameFramework; using CUE4Parse.UE4.Objects.GameplayTags; using CUE4Parse.UE4.Objects.UObject; -using FModel.Settings; -using Microsoft.VisualBasic.Logging; namespace FModel.Extensions; public static class KismetExtensions { - public static string GetPrefix(string? type, string? extra = "") + public static string GetPrefix(string type, string extra = "") { return type switch { @@ -43,18 +37,9 @@ public static class KismetExtensions { string typeName = field.GetType().Name; int suffixIndex = typeName.IndexOf("Property", StringComparison.Ordinal); - if (suffixIndex < 0) - return typeName; - return typeName.Substring(1, suffixIndex - 1); - } - public static string GetUnknownFieldType(FField field) - { - string typeName = field.GetType().Name; - int suffixIndex = typeName.IndexOf("Property", StringComparison.Ordinal); - if (suffixIndex < 0) - return typeName; - return typeName.Substring(1, suffixIndex - 1); + return suffixIndex < 0 ? typeName : typeName.Substring(1, suffixIndex - 1); } + public static string GetPropertyType(object? property) { if (property is null) @@ -62,33 +47,33 @@ public static class KismetExtensions return property switch { - FIntProperty => "int", - FInt8Property => "int8", - FInt16Property => "int16", - FInt64Property => "int64", - FUInt16Property => "uint16", - FUInt32Property => "uint32", - FUInt64Property => "uint64", - FBoolProperty or Boolean => "bool", - FStrProperty => "FString", - FFloatProperty or Single => "float", - FDoubleProperty or Double => "double", + FIntProperty or int => "int", + FInt8Property or byte => "int8", + FInt16Property or short => "int16", + FInt64Property or long => "int64", + FUInt16Property or ushort => "uint16", + FUInt32Property or uint => "uint32", + FUInt64Property or ulong => "uint64", + FBoolProperty or bool => "bool", + FStrProperty or string => "FString", + FFloatProperty or float => "float", + FDoubleProperty or double => "double", FObjectProperty objct => property switch { - FClassProperty clss => $"{clss.MetaClass?.Name ?? "UNKNOWN"}", - FSoftClassProperty softClass => $"{softClass.MetaClass?.Name ?? "UNKNOWN"}", - _ => objct.PropertyClass?.Name ?? "UNKNOWN" + FClassProperty clss => $"{clss.MetaClass?.Name ?? "UKN_ObjectMetaClass"} Class", + FSoftClassProperty softClass => $"{softClass.MetaClass?.Name ?? "UKN_ObjectMetaClass"} Class (soft)", + _ => objct.PropertyClass?.Name ?? "UKN_ObjectPropertyClass" }, - FPackageIndex pkg => pkg?.ResolvedObject?.Class?.Name.ToString() ?? "Package", - FName fme => fme.PlainText.Contains("::") ? fme.PlainText.Split("::")[0] : fme.PlainText ?? "FName", - FEnumProperty enm => enm.Enum?.Name.ToString() ?? "Enum", - FByteProperty bt => bt.Enum.ResolvedObject?.Name.Text ?? "Byte", - FInterfaceProperty intrfc => $"{intrfc.InterfaceClass.Name} interface", - FStructProperty strct => strct.Struct.ResolvedObject?.Name.Text ?? "Struct", + FPackageIndex pkg => pkg.ResolvedObject?.Class?.Name.ToString() ?? "Package", + FName fme => fme.PlainText.Contains("::") ? fme.PlainText.Split("::")[0] : fme.PlainText, + FEnumProperty enm => enm.Enum?.Name ?? "Enum", + FByteProperty bt => bt.Enum?.ResolvedObject?.Name.Text ?? "Byte", + FInterfaceProperty intrfc => $"{intrfc.InterfaceClass?.Name ?? "UKN_InterfaceClass"} interface", + FStructProperty strct => strct.Struct?.ResolvedObject?.Name.Text ?? "Struct", FFieldPathProperty fieldPath => $"{fieldPath.PropertyClass.Text} field path", - FDelegateProperty dlgt => $"{dlgt.SignatureFunction?.Name ?? "UNKNOWN"} (Delegate)", - FMulticastDelegateProperty mdlgt => $"{mdlgt.SignatureFunction?.Name ?? "UNKNOWN"} (MulticastDelegateProperty)", - FMulticastInlineDelegateProperty midlgt => $"{midlgt.SignatureFunction?.Name ?? "UNKNOWN"} (MulticastInlineDelegateProperty)", + FDelegateProperty dlgt => $"{dlgt.SignatureFunction?.Name ?? "UKN_SignatureFunction"} (Delegate)", + FMulticastDelegateProperty mdlgt => $"{mdlgt.SignatureFunction?.Name ?? "UKN_SignatureFunction"} (MulticastDelegateProperty)", + FMulticastInlineDelegateProperty midlgt => $"{midlgt.SignatureFunction?.Name ?? "UKN_SignatureFunction"} (MulticastInlineDelegateProperty)", _ => GetUnknownFieldType(property) }; } @@ -99,31 +84,13 @@ public static class KismetExtensions return property switch { - FIntProperty => "int", - FBoolProperty => "bool", - FStrProperty => "FString", - FFloatProperty => "float", - FDoubleProperty => "double", - FObjectProperty objct => property switch - { - FClassProperty clss => $"{clss.MetaClass?.Name ?? "UNKNOWN"} Class", - FSoftClassProperty softClass => $"{softClass.MetaClass?.Name ?? "UNKNOWN"} Class (soft)", - _ => objct.PropertyClass?.Name ?? "UNKNOWN" - }, - FEnumProperty enm => enm.Enum?.Name.ToString() ?? "Enum", FSetProperty set => $"TSet<{GetPrefix(set.ElementProp.GetType().Name)}{GetPropertyType(set.ElementProp)}{(set.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || property.PropertyFlags.HasFlag(EPropertyFlags.ReferenceParm) || set.PropertyFlags.HasFlag(EPropertyFlags.ContainsInstancedReference) ? "*" : string.Empty)}>", - FByteProperty bt => bt.Enum.ResolvedObject?.Name.Text ?? "Byte", - FInterfaceProperty intrfc => $"{intrfc.InterfaceClass.Name} interface", - FStructProperty strct => strct.Struct.ResolvedObject?.Name.Text ?? "Struct", - FFieldPathProperty fieldPath => $"{fieldPath.PropertyClass.Text} field path", - FDelegateProperty dlgt => $"{dlgt.SignatureFunction?.Name ?? "UNKNOWN"} (Delegate)", FMapProperty map => $"TMap<{GetPrefix(map.ValueProp.GetType().Name)}{GetPropertyType(map.KeyProp)}, {GetPrefix(map.ValueProp.GetType().Name)}{GetPropertyType(map.ValueProp)}{(map.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || property.PropertyFlags.HasFlag(EPropertyFlags.ReferenceParm) || map.PropertyFlags.HasFlag(EPropertyFlags.ContainsInstancedReference) ? "*" : string.Empty)}>", - FMulticastDelegateProperty mdlgt => $"{mdlgt.SignatureFunction?.Name ?? "UNKNOWN"} (MulticastDelegateProperty)", - FMulticastInlineDelegateProperty midlgt => $"{midlgt.SignatureFunction?.Name ?? "UNKNOWN"} (MulticastInlineDelegateProperty)", FArrayProperty array => $"TArray<{GetPrefix(array.Inner.GetType().Name)}{GetPropertyType(array.Inner)}{(array.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || property.PropertyFlags.HasFlag(EPropertyFlags.ReferenceParm) || array.PropertyFlags.HasFlag(EPropertyFlags.ContainsInstancedReference) || GetPropertyProperty(array.Inner.GetType().Name) ? "*" : string.Empty)}>", - _ => GetUnknownFieldType(property) + _ => GetPropertyType((object)property) }; } + public static bool GetPropertyProperty(object? property) { if (property is null) @@ -131,21 +98,11 @@ public static class KismetExtensions return property switch { - FObjectProperty objct => true, + FObjectProperty => true, _ => false }; } - public static bool GetPropertyProperty(FProperty? property) - { - if (property is null) - return false; - return property switch - { - FObjectProperty objct => true, - _ => false - }; - } public static string FormatStructFallback(FStructFallback fallback) { if (fallback.Properties.Count == 0) @@ -155,44 +112,48 @@ public static class KismetExtensions { string tagDataFormatted; - if (tag.Tag is TextProperty text) + switch (tag.Tag) { - tagDataFormatted = $"\"{text.Value.Text}\""; - } - else if (tag.Tag is NameProperty name) - { - tagDataFormatted = $"\"{name.Value.Text}\""; - } - else if (tag.Tag is ObjectProperty obj) - { - tagDataFormatted = $"\"{obj.Value}\""; - } - else if (tag.Tag.GenericValue is FScriptStruct innerStruct && innerStruct.StructType is FStructFallback nestedFallback) - { - if (nestedFallback.Properties.Count > 0) + case TextProperty text: + tagDataFormatted = $"\"{text.Value.Text}\""; + break; + case NameProperty name: + tagDataFormatted = $"\"{name.Value.Text}\""; + break; + case ObjectProperty obj: + tagDataFormatted = $"\"{obj.Value}\""; + break; + default: { - tagDataFormatted = "{ " + string.Join(", ", - nestedFallback.Properties.Select(nested => + if (tag.Tag.GenericValue is FScriptStruct { StructType: FStructFallback nestedFallback }) + { + if (nestedFallback.Properties.Count > 0) { - string nestedVal; - if (nested.Tag is TextProperty textProp) - nestedVal = $"\"{textProp.Value.Text}\""; - else if (nested.Tag is NameProperty nameProp) - nestedVal = $"\"{nameProp.Value.Text}\""; - else - nestedVal = $"\"{nested.Tag.GenericValue}\""; + tagDataFormatted = "{ " + string.Join(", ", + nestedFallback.Properties.Select(nested => + { + string nestedVal = nested.Tag switch + { + TextProperty textProp => $"\"{textProp.Value.Text}\"", + NameProperty nameProp => $"\"{nameProp.Value.Text}\"", + _ => $"\"{nested.Tag.GenericValue}\"" + }; - return $"\"{nested.Name}\": {nestedVal}"; - })) + " }"; + return $"\"{nested.Name}\": {nestedVal}"; + })) + " }"; + } + else + { + tagDataFormatted = "{}"; + } + } + else + { + tagDataFormatted = tag.Tag.GenericValue != null ? $"\"{tag.Tag.GenericValue}\"" : "{}"; + } + + break; } - else - { - tagDataFormatted = "{}"; - } - } - else - { - tagDataFormatted = tag.Tag.GenericValue != null ? $"\"{tag.Tag.GenericValue}\"" : "{}"; } return $"\t\t{{ \"{tag.Name}\": {tagDataFormatted} }}"; @@ -200,6 +161,7 @@ public static class KismetExtensions return "[\n" + string.Join(",\n", tags) + "\n\t]"; } + public static string FormatGameplayTagContainer(FGameplayTagContainer container) { var tags = container.GameplayTags.ToList(); @@ -210,49 +172,31 @@ public static class KismetExtensions _ => "[\n" + string.Join(",\n", tags.Select(tag => $"\t\t\"{tag.TagName}\"")) + "\n\t]" }; } + public static string FormatStructType(object structType) { - switch (structType) + return structType switch { - case FVector vector: - return $"FVector({vector.X}, {vector.Y}, {vector.Z})"; - case FVector2D vector2D: - return $"FVector2D({vector2D.X}, {vector2D.Y})"; - case FRotator rotator: - return $"FRotator({rotator.Pitch}, {rotator.Yaw}, {rotator.Roll})"; - case FQuat quat: - return $"FQuat({quat.X}, {quat.Y}, {quat.Z}, {quat.W})"; - case FGuid guid: - return $"FGuid({guid.A}, {guid.B}, {guid.C}, {guid.D})"; - case FColor color: - return $"FColor({color.R}, {color.G}, {color.B}, {color.A})"; - case FLinearColor linearColor: - return $"FLinearColor({linearColor.R}, {linearColor.G}, {linearColor.B}, {linearColor.A})"; - case FSoftObjectPath path: - return $"FSoftObjectPath({path.AssetPathName})"; - case FUniqueNetIdRepl netId: - return $"FUniqueNetIdRepl({netId.UniqueNetId})"; - case FNavAgentSelector agent: - return $"FNavAgentSelector({agent.PackedBits})"; - case FBox box: - return $"FBox(FVector({box.Max.X}, {box.Max.Y}, {box.Max.Z}), FVector({box.Min.X}, {box.Min.Y}, {box.Min.Z}))"; - case FBox2D box2D: - return $"FBox2D(FVector2D({box2D.Max.X}, {box2D.Max.Y}), FVector2D({box2D.Min.X}, {box2D.Min.Y}))"; - case TIntVector3 intVec: - return $"FVector({intVec.X}, {intVec.Y}, {intVec.Z})"; - case TIntVector3 floatVec: - return $"FVector({floatVec.X}, {floatVec.Y}, {floatVec.Z})"; - case TIntVector2 floatVec2: - return $"FVector2D({floatVec2.X}, {floatVec2.Y})"; - case FDateTime dateTime: - return $"FDateTime({dateTime})"; - case FStructFallback fallback: - return FormatStructFallback(fallback); - case FGameplayTagContainer tagContainer: - return FormatGameplayTagContainer(tagContainer); - default: - return structType?.ToString() ?? "Issue here"; - } + FVector vector => $"FVector({vector.X}, {vector.Y}, {vector.Z})", + FVector2D vector2D => $"FVector2D({vector2D.X}, {vector2D.Y})", + FRotator rotator => $"FRotator({rotator.Pitch}, {rotator.Yaw}, {rotator.Roll})", + FQuat quat => $"FQuat({quat.X}, {quat.Y}, {quat.Z}, {quat.W})", + FGuid guid => $"FGuid({guid.A}, {guid.B}, {guid.C}, {guid.D})", + FColor color => $"FColor({color.R}, {color.G}, {color.B}, {color.A})", + FLinearColor linearColor => $"FLinearColor({linearColor.R}, {linearColor.G}, {linearColor.B}, {linearColor.A})", + FSoftObjectPath path => $"FSoftObjectPath({path.AssetPathName})", + FUniqueNetIdRepl netId => $"FUniqueNetIdRepl({netId.UniqueNetId})", + FNavAgentSelector agent => $"FNavAgentSelector({agent.PackedBits})", + FBox box => $"FBox(FVector({box.Max.X}, {box.Max.Y}, {box.Max.Z}), FVector({box.Min.X}, {box.Min.Y}, {box.Min.Z}))", + FBox2D box2D => $"FBox2D(FVector2D({box2D.Max.X}, {box2D.Max.Y}), FVector2D({box2D.Min.X}, {box2D.Min.Y}))", + TIntVector3 intVec => $"FVector({intVec.X}, {intVec.Y}, {intVec.Z})", + TIntVector3 floatVec => $"FVector({floatVec.X}, {floatVec.Y}, {floatVec.Z})", + TIntVector2 floatVec2 => $"FVector2D({floatVec2.X}, {floatVec2.Y})", + FDateTime dateTime => $"FDateTime({dateTime})", + FStructFallback fallback => FormatStructFallback(fallback), + FGameplayTagContainer tagContainer => FormatGameplayTagContainer(tagContainer), + _ => structType?.ToString() ?? "Issue here" + }; } private static string ProcessTextProperty(FKismetPropertyPointer property, bool temp) @@ -263,12 +207,14 @@ public static class KismetExtensions } return string.Join('.', property.New.Path.Select(n => n.Text)).Replace(" ", ""); } + public static void ProcessExpression(EExprToken token, KismetExpression expression, StringBuilder outputBuilder, List jumpCodeOffsets, bool isParameter = false) { if (jumpCodeOffsets.Contains(expression.StatementIndex)) { outputBuilder.Append("\t\tLabel_" + expression.StatementIndex + ":\n"); } + switch (token) { case EExprToken.EX_LetValueOnPersistentFrame: @@ -406,8 +352,7 @@ public static class KismetExtensions { ProcessExpression(oppMath.Token, oppMath, outputBuilder, jumpCodeOffsets, true); } - else - {} + break; } case EExprToken.EX_PopExecutionFlowIfNot: @@ -423,7 +368,7 @@ public static class KismetExtensions { EX_Cast op = (EX_Cast) expression;// support CST_ObjectToInterface when I have an example of how it works - if (ECastToken.CST_ObjectToBool == op.ConversionType || ECastToken.CST_InterfaceToBool == op.ConversionType) + if (op.ConversionType is ECastToken.CST_ObjectToBool or ECastToken.CST_InterfaceToBool) { outputBuilder.Append("(bool)"); } @@ -455,7 +400,7 @@ public static class KismetExtensions } outputBuilder.Append(op.Elements.Length < 1 ? " " : ' '); - outputBuilder.Append("}"); + outputBuilder.Append('}'); break; } case EExprToken.EX_SetArray: @@ -575,8 +520,8 @@ public static class KismetExtensions { EX_SwitchValue op = (EX_SwitchValue) expression; - bool useTernary = op.Cases.Length <= 2 - && op.Cases.All(c => c.CaseIndexValueTerm.Token == EExprToken.EX_True || c.CaseIndexValueTerm.Token == EExprToken.EX_False); + bool useTernary = op.Cases.Length <= 2 && + op.Cases.All(c => c.CaseIndexValueTerm.Token is EExprToken.EX_True or EExprToken.EX_False); if (useTernary) { @@ -644,9 +589,9 @@ public static class KismetExtensions { EX_ArrayGetByRef op = (EX_ArrayGetByRef) expression; // FortniteGame/Plugins/GameFeatures/FM/PilgrimCore/Content/Player/Components/BP_PilgrimPlayerControllerComponent.uasset ProcessExpression(op.ArrayVariable.Token, op.ArrayVariable, outputBuilder, jumpCodeOffsets, true); - outputBuilder.Append("["); + outputBuilder.Append('['); ProcessExpression(op.ArrayIndex.Token, op.ArrayIndex, outputBuilder, jumpCodeOffsets); - outputBuilder.Append("]"); + outputBuilder.Append(']'); break; } case EExprToken.EX_MetaCast: @@ -658,14 +603,14 @@ public static class KismetExtensions EX_CastBase op = (EX_CastBase) expression; outputBuilder.Append($"Cast(");// m? ProcessExpression(op.Target.Token, op.Target, outputBuilder, jumpCodeOffsets, true); - outputBuilder.Append(")"); + outputBuilder.Append(')'); break; } case EExprToken.EX_StructConst: { EX_StructConst op = (EX_StructConst) expression; outputBuilder.Append($"{GetPrefix(op.Struct.GetType().Name)}{op.Struct.Name}"); - outputBuilder.Append($"("); + outputBuilder.Append('('); for (int i = 0; i < op.Properties.Length; i++) { var property = op.Properties[i]; @@ -673,19 +618,19 @@ public static class KismetExtensions if (i < op.Properties.Length - 1 && property.Token != EExprToken.EX_ArrayConst) outputBuilder.Append(", "); } - outputBuilder.Append($")"); + outputBuilder.Append(')'); break; } case EExprToken.EX_ObjectConst: { EX_ObjectConst op = (EX_ObjectConst) expression; - outputBuilder.Append(!isParameter ? "\t\tFindObject<" : outputBuilder.ToString().EndsWith("\n") ? "\t\tFindObject<" : "FindObject<"); // please don't complain, i know this is bad but i MUST do it. - string classString = op?.Value?.ResolvedObject?.Class?.ToString()?.Replace("'", ""); + outputBuilder.Append(!isParameter ? "\t\tFindObject<" : outputBuilder.ToString().EndsWith('\n') ? "\t\tFindObject<" : "FindObject<"); // please don't complain, i know this is bad but i MUST do it. + string classString = op.Value.ResolvedObject?.Class?.ToString().Replace("'", ""); - if (classString?.Contains(".") == true) + if (classString?.Contains('.') == true) { - outputBuilder.Append(GetPrefix(op?.Value?.ResolvedObject?.Class?.GetType().Name) + classString.Split(".")[1]); + outputBuilder.Append(GetPrefix(op?.Value?.ResolvedObject?.Class?.GetType().Name) + classString.Split('.')[1]); } else { @@ -698,15 +643,7 @@ public static class KismetExtensions var name = op?.Value?.Name ?? string.Empty; outputBuilder.Append(outerString.Replace(outerClassString, "") + "." + name); - - if (isParameter) - { - outputBuilder.Append("\")"); - } - else - { - outputBuilder.Append("\")"); - } + outputBuilder.Append("\")"); break; } case EExprToken.EX_BindDelegate: @@ -714,23 +651,23 @@ public static class KismetExtensions EX_BindDelegate op = (EX_BindDelegate) expression; outputBuilder.Append("\t\t"); ProcessExpression(op.Delegate.Token, op.Delegate, outputBuilder, jumpCodeOffsets); - outputBuilder.Append($".BindUFunction("); + outputBuilder.Append(".BindUFunction("); ProcessExpression(op.ObjectTerm.Token, op.ObjectTerm, outputBuilder, jumpCodeOffsets); outputBuilder.Append($", \"{op.FunctionName}\""); - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); break; } // all the delegate functions suck case EExprToken.EX_AddMulticastDelegate: { EX_AddMulticastDelegate op = (EX_AddMulticastDelegate) expression; - if (op.Delegate.Token == EExprToken.EX_LocalVariable || op.Delegate.Token == EExprToken.EX_InstanceVariable) + if (op.Delegate.Token is EExprToken.EX_LocalVariable or EExprToken.EX_InstanceVariable) { outputBuilder.Append("\t\t"); ProcessExpression(op.Delegate.Token, op.Delegate, outputBuilder, jumpCodeOffsets, true); outputBuilder.Append(".AddDelegate("); ProcessExpression(op.DelegateToAdd.Token, op.DelegateToAdd, outputBuilder, jumpCodeOffsets); - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); } else if (op.Delegate.Token != EExprToken.EX_Context) {} @@ -743,23 +680,25 @@ public static class KismetExtensions //ProcessExpression(opp.ContextExpression.Token, opp.ContextExpression, outputBuilder, jumpCodeOffsets); outputBuilder.Append(".AddDelegate("); ProcessExpression(op.DelegateToAdd.Token, op.DelegateToAdd, outputBuilder, jumpCodeOffsets); - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); } break; } case EExprToken.EX_RemoveMulticastDelegate: // everything here has been guessed not compared to actual UE but does work fine and displays all information { EX_RemoveMulticastDelegate op = (EX_RemoveMulticastDelegate) expression; - if (op.Delegate.Token == EExprToken.EX_LocalVariable || op.Delegate.Token == EExprToken.EX_InstanceVariable) + if (op.Delegate.Token is EExprToken.EX_LocalVariable or EExprToken.EX_InstanceVariable) { outputBuilder.Append("\t\t"); ProcessExpression(op.Delegate.Token, op.Delegate, outputBuilder, jumpCodeOffsets, true); outputBuilder.Append(".RemoveDelegate("); ProcessExpression(op.DelegateToAdd.Token, op.DelegateToAdd, outputBuilder, jumpCodeOffsets); - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); } else if (op.Delegate.Token != EExprToken.EX_Context) - {} + { + + } else { EX_Context opp = (EX_Context) op.Delegate; @@ -769,7 +708,7 @@ public static class KismetExtensions ProcessExpression(opp.ContextExpression.Token, opp.ContextExpression, outputBuilder, jumpCodeOffsets); outputBuilder.Append(".RemoveDelegate("); ProcessExpression(op.DelegateToAdd.Token, op.DelegateToAdd, outputBuilder, jumpCodeOffsets); - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); } break; } @@ -800,10 +739,12 @@ public static class KismetExtensions outputBuilder.Append(", "); } } - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); } else if (op.Delegate.Token != EExprToken.EX_Context) - {} + { + + } else { outputBuilder.Append("\t\t"); @@ -819,7 +760,7 @@ public static class KismetExtensions outputBuilder.Append(", "); } } - outputBuilder.Append($");\n\n"); + outputBuilder.Append(");\n\n"); } break; } @@ -827,7 +768,7 @@ public static class KismetExtensions case EExprToken.EX_Context: { EX_Context op = (EX_Context) expression; - outputBuilder.Append(outputBuilder.ToString().EndsWith("\n") ? "\t\t" : ""); + outputBuilder.Append(outputBuilder.ToString().EndsWith('\n') ? "\t\t" : ""); ProcessExpression(op.ObjectExpression.Token, op.ObjectExpression, outputBuilder, jumpCodeOffsets, true); outputBuilder.Append("->"); @@ -847,7 +788,7 @@ public static class KismetExtensions { outputBuilder.Append("->"); ProcessExpression(op.ContextExpression.Token, op.ContextExpression, outputBuilder, jumpCodeOffsets, true); - outputBuilder.Append($";\n\n"); + outputBuilder.Append(";\n\n"); } break; } @@ -883,11 +824,11 @@ public static class KismetExtensions ProcessExpression(op.Assignment.Token, op.Assignment, outputBuilder, jumpCodeOffsets, true); if (!isParameter || op.Assignment.Token == EExprToken.EX_LocalFinalFunction || op.Assignment.Token == EExprToken.EX_FinalFunction || op.Assignment.Token == EExprToken.EX_CallMath) { - outputBuilder.Append($";\n\n"); + outputBuilder.Append(";\n\n"); } else { - outputBuilder.Append($";"); + outputBuilder.Append(';'); } break; } @@ -941,7 +882,7 @@ public static class KismetExtensions { EX_Return op = (EX_Return) expression; bool check = op.ReturnExpression.Token == EExprToken.EX_Nothing; - outputBuilder.Append($"\t\treturn"); + outputBuilder.Append("\t\treturn"); if (!check) outputBuilder.Append(' '); ProcessExpression(op.ReturnExpression.Token, op.ReturnExpression, outputBuilder, jumpCodeOffsets, true); diff --git a/FModel/Extensions/StringExtensions.cs b/FModel/Extensions/StringExtensions.cs index 63cd033a..359ad0e0 100644 --- a/FModel/Extensions/StringExtensions.cs +++ b/FModel/Extensions/StringExtensions.cs @@ -24,29 +24,21 @@ public static partial class StringExtensions return $"{size:# ###.##} {sizes[order]}".TrimStart(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetNameLineNumberText(this string s, string lineToFind) - { - using var reader = new StringReader(s); - var lineNum = 0; - while (reader.ReadLine() is { } line) - { - lineNum++; - if (line.Contains(lineToFind, StringComparison.OrdinalIgnoreCase)) - return lineNum; - } - return -1; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetNameLineNumber(this string s, string lineToFind) { if (KismetRegex().IsMatch(lineToFind)) return s.GetKismetLineNumber(lineToFind); + if (int.TryParse(lineToFind, out var index)) return s.GetLineNumber(index); - lineToFind = $" \"Name\": \"{lineToFind}\","; + return s.GetNameLineNumberText($" \"Name\": \"{lineToFind}\","); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetNameLineNumberText(this string s, string lineToFind) + { using var reader = new StringReader(s); var lineNum = 0; while (reader.ReadLine() is { } line) @@ -55,7 +47,6 @@ public static partial class StringExtensions if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase)) return lineNum; } - return -1; } diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index 428464e2..9021fa43 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -468,7 +468,7 @@ - + @@ -501,7 +501,7 @@ - + @@ -515,6 +515,15 @@ + + + diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index a678bc44..e1d71bc3 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -165,18 +165,6 @@ public partial class MainWindow await _threadWorkerView.Begin(cancellationToken => { _applicationView.CUE4Parse.ExtractSelected(cancellationToken, selectedItems); }); } - private void AssetsListName_ContextMenuOpening(object sender, ContextMenuEventArgs e) - { - var listBox = sender as ListBox; - var contextMenu = listBox.ContextMenu; - - var decompItem = contextMenu.Items - .OfType() - .FirstOrDefault(mi => mi.Name == "Decompile"); - - decompItem.Visibility = UserSettings.Default.ShowDecompileOption ? Visibility.Visible : Visibility.Collapsed; - } - private async void OnFolderExtractClick(object sender, RoutedEventArgs e) { if (AssetsFolderName.SelectedItem is TreeItem folder) diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 598c4ce9..d203b34f 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -960,15 +960,11 @@ public class CUE4ParseViewModel : ViewModel public void Decompile(GameFile entry) { - var package = Provider.LoadPackage(entry); - - if (TabControl.CanAddTabs) - TabControl.AddTab(entry); - else - TabControl.SelectedTab.SoftReset(entry); + if (TabControl.CanAddTabs) TabControl.AddTab(entry); + else TabControl.SelectedTab.SoftReset(entry); TabControl.SelectedTab.TitleExtra = "Decompiled"; - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("txt"); + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("cpp"); var pkg = Provider.LoadPackage(entry); @@ -979,472 +975,247 @@ public class CUE4ParseViewModel : ViewModel if (pointer?.Object is null) continue; - var dummy = ((AbstractUePackage) pkg).ConstructObject( - pointer.Class?.Object?.Value as UStruct, pkg); - switch (dummy) + var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg); + if (dummy is not UBlueprintGeneratedClass || pointer.Object.Value is not UBlueprintGeneratedClass blueprint) + continue; + + var type = blueprint.GetType().Name; + var typePrefix = KismetExtensions.GetPrefix(type); + var className = blueprint.Name; + var superClassName = blueprint?.SuperStruct?.Name ?? string.Empty; + + outputBuilder.AppendLine($"class {typePrefix}{className} : public {typePrefix}{superClassName}\n{{\npublic:"); + + if (!blueprint.ClassDefaultObject.TryLoad(out var bpObject)) + continue; + + var strings = new List(); + foreach (var property in bpObject.Properties) { - case UBlueprintGeneratedClass _: - case UVerseClass _: + var propertyName = property.Name.ToString(); + var propertyValue = property.Tag?.GenericValue; + strings.Add(propertyName); + string placeholder = $"{propertyName}placeholder"; + + void ShouldAppend(string value) + { + if (outputBuilder.ToString().Contains(placeholder)) { - var blueprintGeneratedClass = pkg.ExportsLazy.FirstOrDefault(e => e.Value is UBlueprintGeneratedClass)?.Value as UBlueprintGeneratedClass; - var verseClass = pkg.ExportsLazy.Where(export => export.Value is UVerseClass).Select(export => (UVerseClass) export.Value).FirstOrDefault(); - - var stringsarray = new List(); - if (blueprintGeneratedClass != null) - { - { - var mainClass = blueprintGeneratedClass?.Name ?? verseClass?.Name; - var superStructName = blueprintGeneratedClass?.SuperStruct?.Name ?? verseClass?.SuperStruct?.Name ?? string.Empty; - outputBuilder.AppendLine( - $"class {KismetExtensions.GetPrefix(blueprintGeneratedClass?.GetType().Name ?? verseClass?.GetType().Name)}{mainClass} : public {KismetExtensions.GetPrefix(blueprintGeneratedClass?.GetType().Name ?? verseClass?.GetType().Name)}{superStructName}\n{{\npublic:"); - - foreach (var export in pkg.ExportsLazy) - { - if (export.Value is not UBlueprintGeneratedClass) - - if (export.Value.Name.StartsWith("Default__") && - export.Value.Name.EndsWith(mainClass ?? string.Empty)) - { - var exportObject = export.Value; - foreach (var key in exportObject.Properties) - { - stringsarray.Add(key.Name.PlainText); - string placeholder = $"{key.Name}placenolder"; - string result = key.Tag.GenericValue.ToString(); - string keyName = key.Name.PlainText.Replace(" ", ""); - - var propertyTag = key.Tag.GetValue(typeof(object)); - - void ShouldAppend(string? value) - { - if (value == null) - return; - if (outputBuilder.ToString().Contains(placeholder)) - { - outputBuilder.Replace(placeholder, value); - } - else - { - outputBuilder.AppendLine( - $"\t{KismetExtensions.GetPropertyType(propertyTag)} {keyName} = {value};"); - } - } - - if (key.Tag.GenericValue is FScriptStruct structTag) - { - if (structTag.StructType is FVector vector) - { - ShouldAppend( - $"FVector({vector.X}, {vector.Y}, {vector.Z})"); - } - else if (structTag.StructType is FGuid guid) - { - ShouldAppend( - $"FGuid({guid.A}, {guid.B}, {guid.C}, {guid.D})"); - } - else if (structTag.StructType is TIntVector3 vector3) - { - ShouldAppend( - $"FVector({vector3.X}, {vector3.Y}, {vector3.Z})"); - } - else if (structTag.StructType is TIntVector3 - floatVector3) - { - ShouldAppend( - $"FVector({floatVector3.X}, {floatVector3.Y}, {floatVector3.Z})"); - } - else if (structTag.StructType is TIntVector2 - floatVector2) - { - ShouldAppend( - $"FVector2D({floatVector2.X}, {floatVector2.Y})"); - } - else if (structTag.StructType is FVector2D vector2d) - { - ShouldAppend($"FVector2D({vector2d.X}, {vector2d.Y})"); - } - else if (structTag.StructType is FRotator rotator) - { - ShouldAppend( - $"FRotator({rotator.Pitch}, {rotator.Yaw}, {rotator.Roll})"); - } - else if (structTag.StructType is FStructFallback fallback) - { - string formattedTags; - if (fallback.Properties.Count > 0) - { - formattedTags = "[\n" + string.Join(",\n", - fallback.Properties.Select(tag => - { - string tagDataFormatted; - if (tag.Tag is TextProperty text) - { - tagDataFormatted = $"\"{text.Value.Text}\""; - } - else if (tag.Tag is NameProperty name) - { - tagDataFormatted = $"\"{name.Value.Text}\""; - } - else if (tag.Tag is ObjectProperty - objectproperty) - { - tagDataFormatted = - $"\"{objectproperty.Value}\""; - } - else if (tag.Tag.GenericValue is FScriptStruct innerStruct && innerStruct.StructType is FStructFallback nestedFallback) - { - if (nestedFallback.Properties.Count > 0) - { - tagDataFormatted = "{ " + string.Join(", ", - nestedFallback.Properties.Select(nested => - { - string nestedVal; - if (nested.Tag is TextProperty textProp) - nestedVal = $"\"{textProp.Value.Text}\""; - else if (nested.Tag is NameProperty nameProp) - nestedVal = $"\"{nameProp.Value.Text}\""; - else - nestedVal = $"\"{nested.Tag.GenericValue}\""; - - return $"\"{nested.Name}\": {nestedVal}"; - })) + " }"; - } - else - { - tagDataFormatted = "{}"; - } - } - else - { - tagDataFormatted = tag.Tag.GenericValue != null ? tag.Tag.GenericValue.ToString() : "{}"; - } - ; - - return $"\t\t{{ \"{tag.Name}\": {tagDataFormatted} }}"; - })) + "\n\t]"; - } - else - { - formattedTags = "[]"; - } - - ShouldAppend(formattedTags); - } - else if (structTag.StructType is FGameplayTagContainer - gameplayTag) - { - var tags = gameplayTag.GameplayTags.ToList(); - if (tags.Count > 1) - { - var formattedTags = "[\n" + string.Join(",\n", - tags.Select(tag => - $"\t\t\"{tag.TagName}\"")) + - "\n\t]"; - ShouldAppend(formattedTags); - } - else if (tags.Any()) - { - ShouldAppend($"\"{tags.First().TagName}\""); - } - else - { - ShouldAppend("[]"); - } - } - else if (structTag.StructType is FLinearColor color) - { - ShouldAppend( - $"FLinearColor({color.R}, {color.G}, {color.B}, {color.A})"); - } - else - { - ShouldAppend($"\"{result}\""); - } - } - else if (key.Tag.GetType().Name == "ObjectProperty" || - key.Tag.GetType().Name == "TextProperty" || - key.PropertyType == "StrProperty" || - key.PropertyType == "NameProperty" || - key.PropertyType == "ClassProperty") - { - ShouldAppend($"\"{result}\""); - } - else if (key.Tag.GenericValue is UScriptSet set) - { - var formattedSet = "[\n" + string.Join(",\n", - set.Properties.Select(p => - $"\t\"{p.GenericValue}\"")) + - "\n\t]"; - ShouldAppend(formattedSet); - } - else if (key.Tag.GenericValue is UScriptMap map) - { - var formattedMap = "[\n" + string.Join(",\n", - map.Properties.Select(kvp => - $"\t{{\n\t\t\"{kvp.Key}\": \"{kvp.Value}\"\n\t}}")) + - "\n\t]"; - ShouldAppend(formattedMap); - } - else if (key.Tag.GenericValue is UScriptArray array) - { - var formattedArray = "[\n" + string.Join(",\n", - array.Properties.Select(p => - { - if (p.GenericValue is FScriptStruct vectorInArray && - vectorInArray.StructType is FVector vector) - { - return - $"FVector({vector.X}, {vector.Y}, {vector.Z})"; - } - - if (p.GenericValue is FScriptStruct - vector2dInArray && - vector2dInArray - .StructType is FVector2D vector2d) - { - return $"FVector2D({vector2d.X}, {vector2d.Y})"; - } - - if (p.GenericValue is FScriptStruct structInArray && - structInArray.StructType is FRotator rotator) - { - return - $"FRotator({rotator.Pitch}, {rotator.Yaw}, {rotator.Roll})"; - } - else if - (p.GenericValue is FScriptStruct - fallbacksInArray && - fallbacksInArray.StructType is FStructFallback - fallback) - { - string formattedTags; - if (fallback.Properties.Count > 0) - { - formattedTags = "\t[\n" + string.Join(",\n", - fallback.Properties.Select(tag => - { - string tagDataFormatted; - if (tag.Tag is TextProperty text) - { - tagDataFormatted = - $"\"{text.Value.Text}\""; - } - else if (tag.Tag is NameProperty - name) - { - tagDataFormatted = - $"\"{name.Value.Text}\""; - } - else if (tag.Tag is ObjectProperty - objectproperty) - { - tagDataFormatted = - $"\"{objectproperty.Value}\""; - } - else - { - tagDataFormatted = - $"\"{tag.Tag.GenericValue}\""; - } - - return - $"\t\t\"{tag.Name}\": {tagDataFormatted}"; - })) + "\n\t]"; - } - else - { - formattedTags = "{}"; - } - - return formattedTags; - } - else if - (p.GenericValue is FScriptStruct - gameplayTagsInArray && - gameplayTagsInArray.StructType is - FGameplayTagContainer gameplayTag) - { - var tags = gameplayTag.GameplayTags.ToList(); - if (tags.Count > 1) - { - var formattedTags = - "[\n" + string.Join(",\n", - tags.Select(tag => - $"\t\t\"{tag.TagName}\"")) + - "\n\t]"; - return formattedTags; - } - else - { - return $"\"{tags.First().TagName}\""; - } - } - - return $"\t\t\"{p.GenericValue}\""; - })) + "\n\t]"; - ShouldAppend(formattedArray); - } - else if (key.Tag.GenericValue is FMulticastScriptDelegate multicast) - { - var list = multicast.InvocationList; - ShouldAppend(list.Length == 0 ? "[]" : $"[{string.Join(", ", list.Select(x => $"\"{x.FunctionName}\""))}]"); - } - - else if (key.Tag.GenericValue is bool boolResult) - { - ShouldAppend(boolResult.ToString().ToLower()); - } - else - { - ShouldAppend(result); - } - } - } - else - { - //outputBuilder.Append($"\nType: {export.Value.Name}"); - } - } - } - - var childProperties = blueprintGeneratedClass?.ChildProperties ?? - verseClass?.ChildProperties; - if (childProperties != null) - { - foreach (FProperty property in childProperties) - { - if (!stringsarray.Contains(property.Name.PlainText)) - outputBuilder.AppendLine( - $"\t{KismetExtensions.GetPrefix(property.GetType().Name)}{KismetExtensions.GetPropertyType(property)}{(property.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || property.PropertyFlags.HasFlag(EPropertyFlags.ReferenceParm) || KismetExtensions.GetPropertyProperty(property) ? "*" : string.Empty)} {property.Name.PlainText.Replace(" ", "")} = {property.Name.PlainText.Replace(" ", "")}placenolder;"); - } - } - var funcMapOrder = - blueprintGeneratedClass?.FuncMap?.Keys.Select(fname => fname.ToString()) - .ToList() ?? verseClass?.FuncMap.Keys.Select(fname => fname.ToString()) - .ToList(); - var functions = pkg.ExportsLazy - .Where(e => e.Value is UFunction) - .Select(e => (UFunction) e.Value) - .OrderBy(f => - { - if (funcMapOrder != null) - { - var functionName = f.Name.ToString(); - int indexx = funcMapOrder.IndexOf(functionName); - return indexx >= 0 ? indexx : int.MaxValue; - } - - return int.MaxValue; - }) - .ThenBy(f => f.Name.ToString()) - .ToList(); - - var jumpCodeOffsetsMap = new Dictionary>(); - - foreach (var function in functions.AsEnumerable().Reverse()) - { - if (function?.ScriptBytecode == null) - continue; - - foreach (var property in function.ScriptBytecode) - { - string? label = null; - int? offset = null; - - switch (property.Token) - { - case EExprToken.EX_JumpIfNot: - label = ((EX_JumpIfNot) property).ObjectPath?.ToString()?.Split('.').Last().Split('[')[0]; - offset = (int) ((EX_JumpIfNot) property).CodeOffset; - break; - - case EExprToken.EX_Jump: - label = ((EX_Jump) property).ObjectPath?.ToString()?.Split('.').Last().Split('[')[0]; - offset = (int) ((EX_Jump) property).CodeOffset; - break; - - case EExprToken.EX_LocalFinalFunction: - { - EX_FinalFunction op = (EX_FinalFunction) property; - label = op.StackNode?.Name?.ToString()?.Split('.').Last().Split('[')[0]; - - if (op.Parameters.Length == 1 && op.Parameters[0] is EX_IntConst intConst) - offset = intConst.Value; - break; - } - } - - if (!string.IsNullOrEmpty(label) && offset.HasValue) - { - if (!jumpCodeOffsetsMap.TryGetValue(label, out var list)) - jumpCodeOffsetsMap[label] = list = new List(); - - list.Add(offset.Value); - } - } - } - - - - foreach (var function in functions) - { - string argsList = ""; - string returnFunc = "void"; - if (function?.ChildProperties != null) - { - foreach (FProperty property in function.ChildProperties) - { - if (property.Name.PlainText == "ReturnValue") - { - returnFunc = - $"{(property.PropertyFlags.HasFlag(EPropertyFlags.ConstParm) ? "const " : string.Empty)}{KismetExtensions.GetPrefix(property.GetType().Name)}{KismetExtensions.GetPropertyType(property)}{(property.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || KismetExtensions.GetPrefix(property.GetType().Name) == "U" ? "*" : string.Empty)}"; - } - else if (!(property.Name.ToString().EndsWith("_ReturnValue") || - property.Name.ToString().StartsWith("CallFunc_") || - property.Name.ToString().StartsWith("K2Node_") || - property.Name.ToString() - .StartsWith("Temp_")) || // removes useless args - property.PropertyFlags.HasFlag(EPropertyFlags.Edit)) - { - argsList += - $"{(property.PropertyFlags.HasFlag(EPropertyFlags.ConstParm) ? "const " : string.Empty)}{KismetExtensions.GetPrefix(property.GetType().Name)}{KismetExtensions.GetPropertyType(property)}{(property.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || KismetExtensions.GetPrefix(property.GetType().Name) == "U" ? "*" : string.Empty)}{(property.PropertyFlags.HasFlag(EPropertyFlags.OutParm) ? "&" : string.Empty)} {Regex.Replace(property.Name.ToString(), @"^__verse_0x[0-9A-Fa-f]+_", "")}, "; - } - } - } - argsList = argsList.TrimEnd(',', ' '); - - outputBuilder.AppendLine($"\n\t{returnFunc} {function.Name.Replace(" ", "")}({argsList})\n\t{{"); - if (function?.ScriptBytecode != null) - { - var jumpCodeOffsets = jumpCodeOffsetsMap.TryGetValue(function.Name, out var list) ? list : new List(); - foreach (KismetExpression property in function.ScriptBytecode) - { - KismetExtensions.ProcessExpression(property.Token, property, outputBuilder, jumpCodeOffsets); - } - } - else - { - outputBuilder.Append( - "\n\t // This function does not have Bytecode \n\n"); - outputBuilder.Append("\t}\n"); - } - } - - outputBuilder.Append("\n\n}"); - } - else - { - continue; - } - break; + outputBuilder.Replace(placeholder, value); } + else + { + outputBuilder.AppendLine($"\t{KismetExtensions.GetPropertyType(propertyValue)} {propertyName.Replace(" ", "")} = {value};"); + } + } + + string GetLineOfText(object value) + { + string? text = null; + switch (value) + { + case FScriptStruct structTag: + switch (structTag.StructType) + { + case FVector vector: + text = $"FVector({vector.X}, {vector.Y}, {vector.Z})"; + break; + case FGuid guid: + text = $"FGuid({guid.A}, {guid.B}, {guid.C}, {guid.D})"; + break; + case TIntVector3 vector3: + text = $"FVector({vector3.X}, {vector3.Y}, {vector3.Z})"; + break; + case TIntVector3 floatVector3: + text = $"FVector({floatVector3.X}, {floatVector3.Y}, {floatVector3.Z})"; + break; + case TIntVector2 floatVector2: + text = $"FVector2D({floatVector2.X}, {floatVector2.Y})"; + break; + case FVector2D vector2d: + text = $"FVector2D({vector2d.X}, {vector2d.Y})"; + break; + case FRotator rotator: + text = $"FRotator({rotator.Pitch}, {rotator.Yaw}, {rotator.Roll})"; + break; + case FLinearColor linearColor: + text = $"FLinearColor({linearColor.R}, {linearColor.G}, {linearColor.B}, {linearColor.A})"; + break; + case FGameplayTagContainer gTag: + text = gTag.GameplayTags.Length switch + { + > 1 => "[\n" + string.Join(",\n", gTag.GameplayTags.Select(tag => $"\t\t\"{tag.TagName}\"")) + "\n\t]", + > 0 => $"\"{gTag.GameplayTags[0].TagName}\"", + _ => "[]" + }; + break; + case FStructFallback fallback: + if (fallback.Properties.Count > 0) + { + text = "[\n" + string.Join(",\n", fallback.Properties.Select(p => $"\t\"{GetLineOfText(p)}\"")) + "\n\t]"; + } + else + { + text = "[]"; + } + break; + } + break; + case UScriptSet: + case UScriptMap: + case UScriptArray: + IEnumerable inner = value switch + { + UScriptSet set => set.Properties.Select(p => $"\t\"{p.GenericValue}\""), + UScriptMap map => map.Properties.Select(kvp => $"\t{{\n\t\t\"{kvp.Key}\": \"{kvp.Value}\"\n\t}}"), + UScriptArray array => array.Properties.Select(p => $"\t\"{GetLineOfText(p)}\""), + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }; + + text = "[\n" + string.Join(",\n", inner) + "\n\t]"; + break; + case FMulticastScriptDelegate multicast: + text = multicast.InvocationList.Length == 0 ? "[]" : $"[{string.Join(", ", multicast.InvocationList.Select(x => $"\"{x.FunctionName}\""))}]"; + break; + case bool: + text = value.ToString()?.ToLowerInvariant(); + break; + } + + return text ?? value.ToString(); + } + + ShouldAppend(GetLineOfText(propertyValue)); + } + + { + var childProperties = blueprint?.ChildProperties; + if (childProperties != null) + { + foreach (FProperty property in childProperties) + { + if (!strings.Contains(property.Name.PlainText)) + outputBuilder.AppendLine( + $"\t{KismetExtensions.GetPrefix(property.GetType().Name)}{KismetExtensions.GetPropertyType(property)}{(property.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || property.PropertyFlags.HasFlag(EPropertyFlags.ReferenceParm) || KismetExtensions.GetPropertyProperty(property) ? "*" : string.Empty)} {property.Name.PlainText.Replace(" ", "")} = {property.Name.PlainText.Replace(" ", "")}placeholder;"); + } + } + + var funcMapOrder = blueprint?.FuncMap?.Keys.Select(fname => fname.ToString()).ToList(); + var functions = pkg.ExportsLazy + .Where(e => e.Value is UFunction) + .Select(e => (UFunction) e.Value) + .OrderBy(f => + { + if (funcMapOrder != null) + { + var functionName = f.Name.ToString(); + int indexx = funcMapOrder.IndexOf(functionName); + return indexx >= 0 ? indexx : int.MaxValue; + } + + return int.MaxValue; + }) + .ThenBy(f => f.Name.ToString()) + .ToList(); + + var jumpCodeOffsetsMap = new Dictionary>(); + + foreach (var function in functions.AsEnumerable().Reverse()) + { + if (function?.ScriptBytecode == null) + continue; + + foreach (var property in function.ScriptBytecode) + { + string? label = null; + int? offset = null; + + switch (property.Token) + { + case EExprToken.EX_JumpIfNot: + label = ((EX_JumpIfNot) property).ObjectPath?.ToString()?.Split('.').Last().Split('[')[0]; + offset = (int) ((EX_JumpIfNot) property).CodeOffset; + break; + + case EExprToken.EX_Jump: + label = ((EX_Jump) property).ObjectPath?.ToString()?.Split('.').Last().Split('[')[0]; + offset = (int) ((EX_Jump) property).CodeOffset; + break; + + case EExprToken.EX_LocalFinalFunction: + { + EX_FinalFunction op = (EX_FinalFunction) property; + label = op.StackNode?.Name?.ToString()?.Split('.').Last().Split('[')[0]; + + if (op.Parameters.Length == 1 && op.Parameters[0] is EX_IntConst intConst) + offset = intConst.Value; + break; + } + } + + if (!string.IsNullOrEmpty(label) && offset.HasValue) + { + if (!jumpCodeOffsetsMap.TryGetValue(label, out var list)) + jumpCodeOffsetsMap[label] = list = new List(); + + list.Add(offset.Value); + } + } + } + + + + foreach (var function in functions) + { + string argsList = ""; + string returnFunc = "void"; + if (function?.ChildProperties != null) + { + foreach (FProperty property in function.ChildProperties) + { + if (property.Name.PlainText == "ReturnValue") + { + returnFunc = + $"{(property.PropertyFlags.HasFlag(EPropertyFlags.ConstParm) ? "const " : string.Empty)}{KismetExtensions.GetPrefix(property.GetType().Name)}{KismetExtensions.GetPropertyType(property)}{(property.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || KismetExtensions.GetPrefix(property.GetType().Name) == "U" ? "*" : string.Empty)}"; + } + else if (!(property.Name.ToString().EndsWith("_ReturnValue") || + property.Name.ToString().StartsWith("CallFunc_") || + property.Name.ToString().StartsWith("K2Node_") || + property.Name.ToString() + .StartsWith("Temp_")) || // removes useless args + property.PropertyFlags.HasFlag(EPropertyFlags.Edit)) + { + argsList += + $"{(property.PropertyFlags.HasFlag(EPropertyFlags.ConstParm) ? "const " : string.Empty)}{KismetExtensions.GetPrefix(property.GetType().Name)}{KismetExtensions.GetPropertyType(property)}{(property.PropertyFlags.HasFlag(EPropertyFlags.InstancedReference) || KismetExtensions.GetPrefix(property.GetType().Name) == "U" ? "*" : string.Empty)}{(property.PropertyFlags.HasFlag(EPropertyFlags.OutParm) ? "&" : string.Empty)} {Regex.Replace(property.Name.ToString(), @"^__verse_0x[0-9A-Fa-f]+_", "")}, "; + } + } + } + argsList = argsList.TrimEnd(',', ' '); + + outputBuilder.AppendLine($"\n\t{returnFunc} {function.Name.Replace(" ", "")}({argsList})\n\t{{"); + if (function?.ScriptBytecode != null) + { + var jumpCodeOffsets = jumpCodeOffsetsMap.TryGetValue(function.Name, out var list) ? list : new List(); + foreach (KismetExpression property in function.ScriptBytecode) + { + KismetExtensions.ProcessExpression(property.Token, property, outputBuilder, jumpCodeOffsets); + } + } + else + { + outputBuilder.Append("\n\t // This function does not have Bytecode \n\n"); + outputBuilder.Append("\t}\n"); + } + } + + outputBuilder.Append("\n\n}"); } } - string pattern = $@"\w+placenolder"; - string updatedOutput = Regex.Replace(outputBuilder.ToString(), pattern, "nullptr"); - TabControl.SelectedTab.SetDocumentText(updatedOutput, false, false); + var cpp = Regex.Replace(outputBuilder.ToString(), @"\w+placeholder", "nullptr"); + TabControl.SelectedTab.SetDocumentText(cpp, false, false); } + private void SaveAndPlaySound(string fullPath, string ext, byte[] data) { if (fullPath.StartsWith("/")) fullPath = fullPath[1..]; diff --git a/FModel/Views/SettingsView.xaml b/FModel/Views/SettingsView.xaml index 378e71c5..a45da7b0 100644 --- a/FModel/Views/SettingsView.xaml +++ b/FModel/Views/SettingsView.xaml @@ -229,7 +229,7 @@ - +