diff --git a/libs/UIX.DecompXml/Decompiler.cs b/libs/UIX.DecompXml/Decompiler.cs index 5566982..8d4f6e5 100644 --- a/libs/UIX.DecompXml/Decompiler.cs +++ b/libs/UIX.DecompXml/Decompiler.cs @@ -1,11 +1,14 @@ using Humanizer; using Microsoft.Iris.Asm; using Microsoft.Iris.Asm.Models; +using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.Markup; +using Microsoft.Iris.Markup.Validation; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Linq.Expressions; using System.Text; using System.Xml; using System.Xml.Linq; @@ -100,8 +103,7 @@ public class Decompiler var propertyElements = new XElement[export.Properties.Length]; - Stack xStack = new([xExport]); - Stack constantsStack = new(); + Stack stack = new([xExport]); for (int i = 0; i < initPropsBody.Length; i++) { @@ -114,13 +116,64 @@ public class Decompiler { var constantOperand = (OperandReference)instruction.Operands.First(); var constant = _constants[constantOperand.Index]; - constantsStack.Push(constant); - //xStack.Push(IntoXNode(constant.Value)); + stack.Push(constant); } else if (instruction.OpCode is OpCode.PushNull) { - constantsStack.Push(null); - //xStack.Push(IntoXNode(null)); + stack.Push(null); + } + else if (instruction.OpCode is OpCode.ConstructObject) + { + var type = _loadResult.ImportTables.TypeImports[(ushort)instruction.Operands.ElementAt(0).Value]; + var xObj = new XElement(GetXName(type)); + stack.Push(xObj); + } + else if (instruction.OpCode is OpCode.MethodInvokeStatic) + { + var method = _loadResult.ImportTables.MethodImports[(ushort)instruction.Operands.First().Value]; + + 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(p => + { + return p switch + { + null => Expression.Constant(null), + Expression expr => expr, + Disassembler.RawConstantInfo constantInfo => new IrisConstantExpression(constantInfo.Value, constantInfo.Type), + + _ => throw new NotImplementedException() + }; + })); + + stack.Push(callExpression); + } + else if (instruction.OpCode is OpCode.PropertyInitialize) + { + var property = _loadResult.ImportTables.PropertyImports[(ushort)instruction.Operands.ElementAt(0).Value]; + var value = stack.Pop(); + var xTarget = (XElement)stack.Peek(); + + if (value is XElement xValue) + { + var xProperty = new XElement(property.Name); + xProperty.Add(xValue); + xTarget.Add(xProperty); + } + else + { + string strValue = value switch + { + IStringEncodable strEnc => strEnc.EncodeString(), + IrisExpression irisExpr => irisExpr.Decompile(this), + _ => value.ToString() + }; + + xTarget.SetAttributeValue(property.Name, strValue); + } } else if (instruction.OpCode is OpCode.PropertyDictionaryAdd) { @@ -129,24 +182,42 @@ public class Decompiler var keyReference = (OperandReference)instruction.Operands.ElementAt(1); var key = _constants[keyReference.Index].Value.ToString(); - //var xValue = xStack.Pop(); - var value = constantsStack.Pop(); + var value = stack.Pop(); - var targetInstance = xStack.Peek() as XElement; + var targetInstance = stack.Peek() as XElement; var xDictionary = GetOrCreateElement(targetInstance, nsUix + targetProperty.Name); - XElement xDictionaryEntry = new(GetXName(value.Type)); - xDictionaryEntry.SetAttributeValue("Name", key); - xDictionary.Add(xDictionaryEntry); + XElement xDictionaryEntry; - if (value.Value is IStringEncodable valStrEnc) + if (value is Disassembler.RawConstantInfo constantValue) { - xDictionaryEntry.SetAttributeValue(value.Type.Name, valStrEnc); + xDictionaryEntry = new(GetXName(constantValue.Type)); + if (constantValue.Value is IStringEncodable valStrEnc) + { + xDictionaryEntry.SetAttributeValue(constantValue.Type.Name, valStrEnc); + } + else + { + xDictionaryEntry.SetAttributeValue(constantValue.Type.Name, constantValue.Value.ToString()); + } + } + else if (value is XElement xValue) + { + xDictionaryEntry = xValue; + } + else if (value is IrisExpression exprValue and IReturnValueProvider exprWithReturnValue) + { + var returnType = exprWithReturnValue.ReturnType; + xDictionaryEntry = new(GetXName(returnType)); + xDictionaryEntry.SetAttributeValue(returnType.Name, exprValue.Decompile(this)); } else { - xDictionaryEntry.SetAttributeValue(value.Type.Name, value.Value.ToString()); + throw new InvalidOperationException(); } + + xDictionaryEntry.SetAttributeValue("Name", key); + xDictionary.Add(xDictionaryEntry); } } @@ -257,7 +328,7 @@ public class Decompiler }; } - private QualifiedTypeName GetQualifiedName(TypeSchema schema) + internal QualifiedTypeName GetQualifiedName(TypeSchema schema) { _uriAliasMap.TryGetValue(schema.Owner.Uri, out string prefix); return new(prefix, schema.Name); diff --git a/libs/UIX.DecompXml/Mock/IReturnValueProvider.cs b/libs/UIX.DecompXml/Mock/IReturnValueProvider.cs new file mode 100644 index 0000000..ceb18a7 --- /dev/null +++ b/libs/UIX.DecompXml/Mock/IReturnValueProvider.cs @@ -0,0 +1,8 @@ +using Microsoft.Iris.Markup; + +namespace Microsoft.Iris.DecompXml.Mock; + +internal interface IReturnValueProvider +{ + TypeSchema ReturnType { get; } +} diff --git a/libs/UIX.DecompXml/Mock/IrisConstantExpression.cs b/libs/UIX.DecompXml/Mock/IrisConstantExpression.cs new file mode 100644 index 0000000..493904a --- /dev/null +++ b/libs/UIX.DecompXml/Mock/IrisConstantExpression.cs @@ -0,0 +1,34 @@ +using Microsoft.Iris.Markup; +using System.Linq.Expressions; + +namespace Microsoft.Iris.DecompXml.Mock; + +internal class IrisConstantExpression : IrisExpression, IReturnValueProvider +{ + public IrisConstantExpression(object value, TypeSchema typeSchema) + { + Value = value; + TypeSchema = typeSchema; + } + + public sealed override ExpressionType NodeType => ExpressionType.Constant; + + public object Value { get; } + + public TypeSchema TypeSchema { get; } + + public TypeSchema ReturnType => TypeSchema; + + public override string Decompile(Decompiler decompiler) + { + if (TypeSchema.IsEnum) + return $"{decompiler.GetQualifiedName(TypeSchema)}.{Value}"; + + return Value switch + { + string str => str, + IStringEncodable strEnc => strEnc.EncodeString(), + _ => Value?.ToString() ?? "null", + }; + } +} diff --git a/libs/UIX.DecompXml/Mock/IrisExpression.cs b/libs/UIX.DecompXml/Mock/IrisExpression.cs new file mode 100644 index 0000000..1059cfc --- /dev/null +++ b/libs/UIX.DecompXml/Mock/IrisExpression.cs @@ -0,0 +1,8 @@ +using System.Linq.Expressions; + +namespace Microsoft.Iris.DecompXml.Mock; + +internal class IrisExpression : Expression +{ + public virtual string Decompile(Decompiler decompiler) => ToString(); +} diff --git a/libs/UIX.DecompXml/Mock/IrisMethodCallExpression.cs b/libs/UIX.DecompXml/Mock/IrisMethodCallExpression.cs new file mode 100644 index 0000000..2d5cdf3 --- /dev/null +++ b/libs/UIX.DecompXml/Mock/IrisMethodCallExpression.cs @@ -0,0 +1,54 @@ +using Microsoft.Iris.Markup; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Microsoft.Iris.DecompXml.Mock; + +internal class IrisMethodCallExpression : IrisExpression, IArgumentProvider, IReturnValueProvider +{ + private readonly Expression[] _arguments; + + public IrisMethodCallExpression(MethodSchema method, Expression? target, IEnumerable arguments) + { + Method = method; + Target = target; + _arguments = arguments.ToArray(); + } + + public MethodSchema Method { get; } + + public sealed override ExpressionType NodeType => ExpressionType.Call; + + public Expression? Target { get; } + + public int ArgumentCount => _arguments.Length; + + public TypeSchema ReturnType => Method.ReturnType; + + public Expression GetArgument(int index) => _arguments[index]; + + public override string Decompile(Decompiler decompiler) + { + StringBuilder sb = new(); + + var qfn = decompiler.GetQualifiedName(Method.Owner); + sb.Append(qfn); + sb.Append('.'); + sb.Append(Method.Name); + sb.Append('('); + + sb.Append(string.Join(", ", _arguments.Select(exprToString))); + + sb.Append(')'); + return sb.ToString(); + + string exprToString(Expression x) + { + return x is IrisExpression irisExpr + ? irisExpr.Decompile(decompiler) + : x.ToString(); + } + } +} diff --git a/test/ABOUTDIALOG.31_48_decomp.uix b/test/ABOUTDIALOG.31_48_decomp.uix index 2d42305..243d972 100644 --- a/test/ABOUTDIALOG.31_48_decomp.uix +++ b/test/ABOUTDIALOG.31_48_decomp.uix @@ -2,10 +2,10 @@ - - - - + + + +