From 4325bc3e73d4566958df53ef52fa206a8b33400b Mon Sep 17 00:00:00 2001 From: Yoshi Askharoun Date: Sun, 20 Jul 2025 02:03:16 -0500 Subject: [PATCH] More inline scripts, shared classes --- UIXC/Properties/launchSettings.json | 2 +- libs/MicrosoftIris | 2 +- libs/UIX.DecompXml/DecompileContext.cs | 11 +- libs/UIX.DecompXml/Decompiler.Script.cs | 267 ++- libs/UIX.DecompXml/Decompiler.cs | 222 +- libs/UIX.DecompXml/Mock/IrisObject.cs | 4 + test/ABOUTDIALOG_decomp.uix | 78 + test/STYLES_decomp.uix | 2625 +++++++++++++++++++++++ 8 files changed, 3059 insertions(+), 152 deletions(-) create mode 100644 test/ABOUTDIALOG_decomp.uix create mode 100644 test/STYLES_decomp.uix diff --git a/UIXC/Properties/launchSettings.json b/UIXC/Properties/launchSettings.json index 6227e60..f490fc0 100644 --- a/UIXC/Properties/launchSettings.json +++ b/UIXC/Properties/launchSettings.json @@ -3,7 +3,7 @@ "UIXC": { "commandName": "Project", - "commandLineArgs": "decompile E:\\Repos\\ZuneDev\\ZuneUIXTools\\test\\ADDTOCOLLECTION.31_48.UIB -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test", + "commandLineArgs": "decompile E:\\Repos\\ZuneDev\\ZuneUIXTools\\test\\4.8\\STYLES.UIB -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test", //"commandLineArgs": "compile E:\\Repos\\ZuneDev\\ZuneUIXTools\\test\\ADDTOCOLLECTION.31_48.UIX -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test", //"commandLineArgs": "decompile E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48\\ABOUTDIALOG.UIX -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48_xml -t E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48\\_DATATABLE.uib", diff --git a/libs/MicrosoftIris b/libs/MicrosoftIris index 32555f3..1667d35 160000 --- a/libs/MicrosoftIris +++ b/libs/MicrosoftIris @@ -1 +1 @@ -Subproject commit 32555f355c70398fe2853169d57ac7e23693eb8b +Subproject commit 1667d353c2d05365fdf6cfc118bc7b16386935ca diff --git a/libs/UIX.DecompXml/DecompileContext.cs b/libs/UIX.DecompXml/DecompileContext.cs index 7f8be4c..f938b42 100644 --- a/libs/UIX.DecompXml/DecompileContext.cs +++ b/libs/UIX.DecompXml/DecompileContext.cs @@ -78,6 +78,8 @@ internal class DecompileContext public PropertySchema GetImportedProperty(Operand op) => ImportTables.PropertyImports[(ushort)op.Value]; + public ConstructorSchema GetImportedConstructor(Operand op) => ImportTables.ConstructorImports[(ushort)op.Value]; + public IEnumerable GetMethodBody(uint startOffset) { foreach (var instruction in Instructions.SkipWhile(i => i.Offset < startOffset)) @@ -111,7 +113,14 @@ internal class DecompileContext { var prefix = MapNamespaceToPrefix(schema.Owner.Uri); var ns = _namespaces[prefix ?? ""]; - return ns + schema.Name; + + // Strip potential generic type parameters + var name = schema.Name; + var genericIndex = name.IndexOf('`'); + if (genericIndex > 0) + name = name[..genericIndex]; + + return ns + name; } private void GenerateNamespaces() diff --git a/libs/UIX.DecompXml/Decompiler.Script.cs b/libs/UIX.DecompXml/Decompiler.Script.cs index 65067d3..a1fdd98 100644 --- a/libs/UIX.DecompXml/Decompiler.Script.cs +++ b/libs/UIX.DecompXml/Decompiler.Script.cs @@ -1,6 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Iris.Asm.Models; using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.Markup; using Microsoft.Iris.Markup.UIX; @@ -67,18 +68,9 @@ partial class Decompiler stack.Pop(); break; - case OpCode.ConstructObject: - var typeToCtor = _context.GetImportedType(instruction.Operands.First()); - stack.Push(ObjectCreationExpression( - IrisExpression.ToSyntax(typeToCtor, _context), - ArgumentList(), - null - )); - break; - case OpCode.LookupSymbol: - var symbolIndex = (ushort)instruction.Operands.First().Value; - stack.Push(IrisExpression.ToSyntax(export.SymbolReferenceTable[symbolIndex])); + var symbolIndex = (ushort)instruction.Operands.ElementAt(0).Value; + stack.Push(export.SymbolReferenceTable[symbolIndex]); break; case OpCode.WriteSymbol: @@ -96,75 +88,6 @@ partial class Decompiler blockStack.Peek().Statements.Add(ExpressionStatement(symbolAssignmentExpr)); break; - case OpCode.MethodInvoke: - case OpCode.MethodInvokePeek: - case OpCode.MethodInvokeStatic: - case OpCode.MethodInvokePushLastParam: - case OpCode.MethodInvokeStaticPushLastParam: - var methodSchema = _context.GetImportedMethod(instruction.Operands.First()); - - int parameterCount = methodSchema.ParameterTypes.Length; - var parameters = new ArgumentSyntax[parameterCount]; - for (parameterCount--; parameterCount >= 0; parameterCount--) - { - var parameter = IrisExpression.ToSyntax(stack.Pop(), _context); - parameters[parameterCount] = Argument(parameter); - } - - bool isStatic = opCode is OpCode.MethodInvokeStatic or OpCode.MethodInvokeStaticPushLastParam; - bool peek = opCode is OpCode.MethodInvokePeek; - bool pushLastParam = opCode is OpCode.MethodInvokePushLastParam or OpCode.MethodInvokeStaticPushLastParam; - - var targetObj = opCode switch - { - OpCode.MethodInvokeStatic or - OpCode.MethodInvokeStaticPushLastParam => methodSchema.Owner, - _ when peek => stack.Peek(), - _ => stack.Pop(), - }; - - var methodExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IrisExpression.ToSyntax(targetObj, _context), - IdentifierName(methodSchema.Name) - ); - - var methodResult = InvocationExpression(methodExpression, ArgumentList([.. parameters])); - - if (methodSchema.ReturnType != VoidSchema.Type) - { - stack.Push(methodResult); - } - else - { - blockStack.Peek().Statements.Add(ExpressionStatement(methodResult)); - } - - if (pushLastParam) - { - stack.Push(parameters[^1].Expression); - } - break; - - case OpCode.PropertyGet: - case OpCode.PropertyGetPeek: - case OpCode.PropertyGetStatic: - var propToGet = _context.GetImportedProperty(instruction.Operands.First()); - - var propGetTarget = instruction.OpCode switch - { - OpCode.PropertyGet => stack.Pop(), - OpCode.PropertyGetPeek => stack.Peek(), - _ => propToGet.Owner, - }; - - var propertyGetExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - IrisExpression.ToSyntax(propGetTarget, _context), - IdentifierName(propToGet.Name) - ); - - stack.Push(propertyGetExpression); - break; - case OpCode.PropertyAssign: case OpCode.PropertyAssignStatic: var propToSet = _context.GetImportedProperty(instruction.Operands.First()); @@ -241,33 +164,9 @@ partial class Decompiler blockStack.Push(ifBlock); break; - case OpCode.Operation: - var opHost = _context.GetImportedType(instruction.Operands.ElementAt(0)); - - var op = (OperationType)(int)(byte)instruction.Operands.ElementAt(1).Value; - var isUnary = TypeSchema.IsUnaryOperation(op); - var opSyntax = OperationToSyntaxKind(op); - - ExpressionSyntax operationExpr; - - if (isUnary) - { - var left = IrisExpression.ToSyntax(stack.Pop(), _context); - var isPostfix = op is OperationType.PostIncrement or OperationType.PostDecrement; - - operationExpr = isPostfix - ? PostfixUnaryExpression(opSyntax, left) - : PrefixUnaryExpression(opSyntax, left); - } - else - { - var right = IrisExpression.ToSyntax(stack.Pop(), _context); - var left = IrisExpression.ToSyntax(stack.Pop(), _context); - - operationExpr = BinaryExpression(opSyntax, left, right); - } - - stack.Push(operationExpr); + default: + if (!TryDecompileExpression(instruction, stack, blockStack)) + throw new NotImplementedException(); break; } } @@ -292,6 +191,16 @@ partial class Decompiler ); } + public static string FormatInlineExpression(ExpressionSyntax expr, CancellationToken token = default) + { + var exprStr = expr + .NormalizeWhitespace() + .SyntaxTree + .GetText(token) + .ToString(); + return '{' + exprStr + '}'; + } + public static string FormatScript(SyntaxTree tree, CancellationToken token = default) { return tree @@ -309,6 +218,150 @@ partial class Decompiler blockStack.Push(block); } + private bool TryDecompileExpression(Instruction instruction, Stack stack, Stack blockStack = null) + { + var opCode = instruction.OpCode; + + switch (opCode) + { + case OpCode.ConstructObject: + case OpCode.ConstructObjectParam: + var typeToCtor = _context.GetImportedType(instruction.Operands.First()); + + List ctorParameters = []; + if (opCode is OpCode.ConstructObjectParam) + { + var ctorSchema = _context.GetImportedConstructor(instruction.Operands.ElementAt(1)); + + int ctorParameterCount = ctorSchema.ParameterTypes.Length; + ctorParameters.Capacity = ctorParameterCount; + + for (ctorParameterCount--; ctorParameterCount >= 0; ctorParameterCount--) + { + var parameter = IrisExpression.ToSyntax(stack.Pop(), _context); + ctorParameters.Add(Argument(parameter)); + } + ctorParameters.Reverse(); + } + + stack.Push(ObjectCreationExpression( + IrisExpression.ToSyntax(typeToCtor, _context), + ArgumentList([..ctorParameters]), + null + )); + break; + + case OpCode.MethodInvoke: + case OpCode.MethodInvokePeek: + case OpCode.MethodInvokeStatic: + case OpCode.MethodInvokePushLastParam: + case OpCode.MethodInvokeStaticPushLastParam: + var methodSchema = _context.GetImportedMethod(instruction.Operands.First()); + + int parameterCount = methodSchema.ParameterTypes.Length; + var parameters = new ArgumentSyntax[parameterCount]; + for (parameterCount--; parameterCount >= 0; parameterCount--) + { + var parameter = IrisExpression.ToSyntax(stack.Pop(), _context); + parameters[parameterCount] = Argument(parameter); + } + + bool isStatic = opCode is OpCode.MethodInvokeStatic or OpCode.MethodInvokeStaticPushLastParam; + bool peek = opCode is OpCode.MethodInvokePeek; + bool pushLastParam = opCode is OpCode.MethodInvokePushLastParam or OpCode.MethodInvokeStaticPushLastParam; + + var targetObj = opCode switch + { + OpCode.MethodInvokeStatic or + OpCode.MethodInvokeStaticPushLastParam => methodSchema.Owner, + _ when peek => stack.Peek(), + _ => stack.Pop(), + }; + + var methodExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IrisExpression.ToSyntax(targetObj, _context), + IdentifierName(methodSchema.Name) + ); + + var methodResult = InvocationExpression(methodExpression, ArgumentList([.. parameters])); + + if (methodSchema.ReturnType != VoidSchema.Type) + { + stack.Push(methodResult); + } + else + { + blockStack?.Peek().Statements.Add(ExpressionStatement(methodResult)); + } + + if (pushLastParam) + { + stack.Push(parameters[^1].Expression); + } + break; + + case OpCode.PropertyGet: + case OpCode.PropertyGetPeek: + case OpCode.PropertyGetStatic: + var propToGet = _context.GetImportedProperty(instruction.Operands.First()); + + var propGetTarget = instruction.OpCode switch + { + OpCode.PropertyGet => stack.Pop(), + OpCode.PropertyGetPeek => stack.Peek(), + _ => propToGet.Owner, + }; + + var propertyGetExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + IrisExpression.ToSyntax(propGetTarget, _context), + IdentifierName(propToGet.Name) + ); + + stack.Push(propertyGetExpression); + break; + + case OpCode.Operation: + DecompileOperation(instruction, stack); + break; + + default: + return false; + } + + return true; + } + + private ExpressionSyntax DecompileOperation(Instruction instruction, Stack stack) + { + var opHost = _context.GetImportedType(instruction.Operands.ElementAt(0)); + + var op = (OperationType)(int)(byte)instruction.Operands.ElementAt(1).Value; + var isUnary = TypeSchema.IsUnaryOperation(op); + var opSyntax = OperationToSyntaxKind(op); + + ExpressionSyntax operationExpr; + + if (isUnary) + { + var left = IrisExpression.ToSyntax(stack.Pop(), _context); + var isPostfix = op is OperationType.PostIncrement or OperationType.PostDecrement; + + operationExpr = isPostfix + ? PostfixUnaryExpression(opSyntax, left) + : PrefixUnaryExpression(opSyntax, left); + } + else + { + var right = IrisExpression.ToSyntax(stack.Pop(), _context); + var left = IrisExpression.ToSyntax(stack.Pop(), _context); + + operationExpr = BinaryExpression(opSyntax, left, right); + } + + stack.Push(operationExpr); + return operationExpr; + } + private static SyntaxKind OperationToSyntaxKind(OperationType operation) { return operation switch diff --git a/libs/UIX.DecompXml/Decompiler.cs b/libs/UIX.DecompXml/Decompiler.cs index f93d3e5..9a22006 100644 --- a/libs/UIX.DecompXml/Decompiler.cs +++ b/libs/UIX.DecompXml/Decompiler.cs @@ -1,4 +1,6 @@ -using Microsoft.Iris.Asm; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Iris.Asm; +using Microsoft.Iris.Debug.Data; using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.Markup; using System; @@ -51,6 +53,12 @@ public partial class Decompiler xExport.SetAttributeValue("Base", baseTypeName); } + if (export is ClassTypeSchema classExport) + { + if (classExport.IsShared) + xExport.SetAttributeValue("Shared", true); + } + if (export.InitializePropertiesOffset is not uint.MaxValue) AnalyzeMethodForInit(export.InitializePropertiesOffset, xExport, export, name + "_prop"); @@ -59,7 +67,7 @@ public partial class Decompiler if (export.InitialEvaluateOffsets is { Length: > 0 }) { - XElement xScripts = new(_nsUix + "Scripts"); + var xScripts = GetOrCreateElement(xExport, _nsUix + "Scripts"); foreach (var offset in export.InitialEvaluateOffsets) { @@ -69,8 +77,19 @@ public partial class Decompiler XElement xScript = new(_nsUix + "Script", scriptText); xScripts.Add(xScript); } + } - xExport.Add(xScripts); + if (export.RefreshGroupOffsets is { Length: > 0 }) + { + var xScripts = GetOrCreateElement(xExport, _nsUix + "Scripts"); + + foreach (var offset in export.RefreshGroupOffsets) + { + var scriptText = AnalyzeRefreshMethod(offset, export, $"{name}_rfsh_0x{offset:X}"); + + XElement xScript = new(_nsUix + "Script", scriptText); + xScripts.Add(xScript); + } } if (export.InitializeContentOffset is not uint.MaxValue) @@ -99,6 +118,7 @@ public partial class Decompiler { Indent = true, NamespaceHandling = NamespaceHandling.OmitDuplicates, + Encoding = Encoding.UTF8, }; StringBuilder sb = new(); @@ -132,6 +152,10 @@ public partial class Decompiler stack.Push(null); break; + case OpCode.PushThis: + stack.Push(elemToInit); + break; + case OpCode.ConstructObject: var typeToCtor = _context.GetImportedType(instruction.Operands.ElementAt(0)); var xObj = new XElement(_context.GetXName(typeToCtor)); @@ -140,37 +164,7 @@ public partial class Decompiler case OpCode.LookupSymbol: var symbolIndex = (ushort)instruction.Operands.ElementAt(0).Value; - var symbol = initType.SymbolReferenceTable[symbolIndex]; - stack.Push(symbol); - break; - - case OpCode.MethodInvokeStatic: - var method = _context.GetImportedMethod(instruction.Operands.First()); - - int parameterCount = method.ParameterTypes.Length; - object[] parameters = new object[parameterCount]; - for (parameterCount--; parameterCount >= 0; parameterCount--) - parameters[parameterCount] = stack.Pop(); - - var callExpression = new IrisMethodCallExpression(method, null, parameters.Select(IrisExpression.Wrap)); - - stack.Push(callExpression); - break; - - case OpCode.PropertyGet: - case OpCode.PropertyGetPeek: - case OpCode.PropertyGetStatic: - var propToGet = _context.GetImportedProperty(instruction.Operands.ElementAt(0)); - - var propTarget = instruction.OpCode switch - { - OpCode.PropertyGet => IrisExpression.Wrap(stack.Pop()), - OpCode.PropertyGetPeek => IrisExpression.Wrap(stack.Peek()), - _ => null, - }; - - var propertyGetExpression = new IrisPropertyExpression(propToGet, propTarget); - stack.Push(propertyGetExpression); + stack.Push(initType.SymbolReferenceTable[symbolIndex]); break; case OpCode.PropertyInitialize: @@ -178,7 +172,11 @@ public partial class Decompiler var newPropValue = stack.Pop(); var target = stack.Pop(); - var xTarget = (XElement)ToXmlFriendlyObject(target); + var xTarget = ToXmlFriendlyObject(target) as XElement; + if (xTarget is null) + { + + } PropertyAssignOnXElement(xTarget, propertyToInit, IrisObject.Create(newPropValue, propertyToInit.PropertyType, _context)); @@ -196,21 +194,73 @@ public partial class Decompiler .FirstOrDefault(s => s.Name == key)? .Type; - var targetInstance = (XElement)stack.Peek(); + var targetInstance = stack.Peek() as XElement; + if (targetInstance is null) + { + + } PropertyDictionaryAddOnXElement(targetInstance, targetDictProperty, IrisObject.Create(dictValue, dictValueType, _context), key); break; case OpCode.PropertyListAdd: - var targetListProperty = _context.GetImportedProperty(instruction.Operands.ElementAt(0)); var valueToAdd = stack.Pop(); + TypeSchema valueToAddType = null; + + if (valueToAdd is SymbolReference symRef) + { + valueToAddType = initType.InheritableSymbolsTable? + .FirstOrDefault(s => s.Name == symRef.Symbol)? + .Type; + } + + var valueToAddObj = IrisObject.Create(valueToAdd, valueToAddType, _context); + var targetInstance2 = (XElement)ToXmlFriendlyObject(stack.Peek()); - PropertyListAddOnXElement(targetInstance2, targetListProperty, IrisObject.Create(valueToAdd, null, _context)); + var targetListPropertyIndex = (ushort)instruction.Operands.First().Value; + if (targetListPropertyIndex != ushort.MaxValue) + { + var targetListProperty = _context.ImportTables.PropertyImports[targetListPropertyIndex]; + + if (valueToAddObj.Type is null) + { + var valueRuntimeType = targetListProperty.PropertyType.RuntimeType.GetGenericArguments().FirstOrDefault(); + valueToAddType = _context.ImportTables.TypeImports.FirstOrDefault(t => t.RuntimeType == valueRuntimeType); + valueToAddObj = valueToAddObj with { Type = valueToAddType }; + } + + PropertyListAddOnXElement(targetInstance2, targetListProperty, valueToAddObj); + } + else + { + PropertyListAddOnXElement(targetInstance2, valueToAddObj); + } break; + case OpCode.InitializeInstance: + case OpCode.JumpIfDictionaryContains: case OpCode.ConstructListenerStorage: - var listenerCount = (ushort)instruction.Operands.First().Value; + // These instructions are inconsequential for determining how objects are initialized + break; + + case OpCode.ConstructObjectParam: + case OpCode.MethodInvoke: + case OpCode.MethodInvokePeek: + case OpCode.MethodInvokeStatic: + case OpCode.MethodInvokePushLastParam: + case OpCode.MethodInvokeStaticPushLastParam: + case OpCode.PropertyGet: + case OpCode.PropertyGetPeek: + case OpCode.PropertyGetStatic: + case OpCode.Operation: + // These instructions only appear in initializers as inline expressions + if (!TryDecompileExpression(instruction, stack)) + throw new NotImplementedException(); + break; + + case not OpCode.ReturnVoid: + Console.WriteLine($"Unsupported instruction: {instruction}"); break; } } @@ -223,6 +273,69 @@ public partial class Decompiler return stack; } + private string AnalyzeRefreshMethod(uint startOffset, MarkupTypeSchema initType, string methodName = "") + { + var methodBody = _context.GetMethodBody(startOffset).ToArray(); + + Stack stack = new(); + + for (int i = 0; i < methodBody.Length; i++) + { + var instruction = methodBody[i]; + + try + { + switch (instruction.OpCode) + { + case OpCode.Listen: + case OpCode.DestructiveListen: + var listenerIndex = (ushort)instruction.Operands.ElementAt(0).Value; + var listenerType = (ListenerType)(byte)instruction.Operands.ElementAt(1).Value; + var watchIndex = (ushort)instruction.Operands.ElementAt(2).Value; + var scriptId = (uint)instruction.Operands.ElementAt(3).Value; + + var refreshOffset = uint.MaxValue; + if (instruction.OpCode is OpCode.DestructiveListen) + refreshOffset = (uint)instruction.Operands.ElementAt(4).Value; + + MarkupTypeSchema markupTypeSchema = initType; + uint num = scriptId >> 27; + while (num != markupTypeSchema.TypeDepth) + markupTypeSchema = markupTypeSchema.MarkupTypeBase; + + var scriptOffset = scriptId & 0x07FFFFFFU; + + string watch = null; + InstructionObjectSource watchSource = InstructionObjectSource.Dynamic; + switch (listenerType) + { + case ListenerType.Property: + watch = _context.ImportTables.PropertyImports[watchIndex].Name; + watchSource = InstructionObjectSource.PropertyImports; + break; + case ListenerType.Event: + watch = _context.ImportTables.EventImports[watchIndex].Name; + watchSource = InstructionObjectSource.EventImports; + break; + case ListenerType.Symbol: + watch = initType.SymbolReferenceTable[watchIndex].Symbol; + watchSource = InstructionObjectSource.SymbolReference; + break; + } + + //object handlerObj = stack.Peek(); + break; + } + } + catch (Exception ex) + { + throw new Exception($"Failed to analyze instruction `{instruction}` @ 0x{instruction.Offset:X}, {methodName}[{i}]", ex); + } + } + + return ""; + } + private static XElement GetOrCreateElement(XElement parent, XName name) { var elem = parent.Element(name); @@ -247,6 +360,8 @@ public partial class Decompiler obj = irisObj.Object; } + + return obj switch { string str => str, @@ -254,6 +369,8 @@ public partial class Decompiler bool b => b ? "true" : "false", IStringEncodable strEnc => strEnc.EncodeString(), IrisExpression expr => '{' + expr.Decompile(_context) + '}', + ExpressionSyntax expr => FormatInlineExpression(expr), + SymbolReference symRef => '{' + symRef.Symbol + '}', IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture), Layout.ILayout layoutObj @@ -295,7 +412,18 @@ public partial class Decompiler { case XElement xValue: var xProperty = GetOrCreateElement(xTarget, _nsUix + property.Name); - xProperty.Add(xValue); + + // Flatten collections + if (typeof(System.Collections.IList).IsAssignableFrom(value.Type.RuntimeType) + || typeof(System.Collections.IDictionary).IsAssignableFrom(value.Type.RuntimeType)) + { + xProperty.Add(xValue.Elements()); + } + else + { + xProperty.Add(xValue); + } + return xProperty; case string strValue: @@ -311,7 +439,11 @@ public partial class Decompiler private XElement PropertyListAddOnXElement(XElement xTarget, PropertySchema property, IrisObject value) { var xList = GetOrCreateElement(xTarget, _nsUix + property.Name); + return PropertyListAddOnXElement(xList, value); + } + private XElement PropertyListAddOnXElement(XElement xList, IrisObject value) + { object xValue = ToXmlFriendlyObject(value); XElement xListEntry; @@ -334,10 +466,16 @@ public partial class Decompiler return xListEntry; } - private XElement PropertyDictionaryAddOnXElement(XElement xTarget, PropertySchema property, IrisObject value, string key) + private XElement PropertyDictionaryAddOnXElement(XElement xDictionary, IrisObject value, string key) { - var xDictionaryEntry = PropertyListAddOnXElement(xTarget, property, value); + var xDictionaryEntry = PropertyListAddOnXElement(xDictionary, value); xDictionaryEntry.SetAttributeValue("Name", key); return xDictionaryEntry; } + + private XElement PropertyDictionaryAddOnXElement(XElement xTarget, PropertySchema property, IrisObject value, string key) + { + var xDictionary = GetOrCreateElement(xTarget, _nsUix + property.Name); + return PropertyDictionaryAddOnXElement(xDictionary, value, key); + } } diff --git a/libs/UIX.DecompXml/Mock/IrisObject.cs b/libs/UIX.DecompXml/Mock/IrisObject.cs index 31a5f7e..1daf0b1 100644 --- a/libs/UIX.DecompXml/Mock/IrisObject.cs +++ b/libs/UIX.DecompXml/Mock/IrisObject.cs @@ -15,6 +15,10 @@ internal record IrisObject(object Object, TypeSchema Type) ? (irisObj with { Type = type }) : irisObj; } + else if (objIn is null) + { + type ??= UIXTypes.MapIDToType(UIXTypeID.Null); + } if (objIn is Disassembler.RawConstantInfo constantInfo) { diff --git a/test/ABOUTDIALOG_decomp.uix b/test/ABOUTDIALOG_decomp.uix new file mode 100644 index 0000000..c1fe300 --- /dev/null +++ b/test/ABOUTDIALOG_decomp.uix @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/STYLES_decomp.uix b/test/STYLES_decomp.uix new file mode 100644 index 0000000..a0a0aaa --- /dev/null +++ b/test/STYLES_decomp.uix @@ -0,0 +1,2625 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file