mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
[WIP] First pass at script decompilation
This commit is contained in:
@@ -0,0 +1,199 @@
|
|||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Iris.DecompXml.Mock;
|
||||||
|
using Microsoft.Iris.Markup;
|
||||||
|
using Microsoft.Iris.Markup.UIX;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
|
namespace Microsoft.Iris.DecompXml;
|
||||||
|
|
||||||
|
partial class Decompiler
|
||||||
|
{
|
||||||
|
private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export)
|
||||||
|
{
|
||||||
|
var methodBody = _context.GetMethodBody(startOffset);
|
||||||
|
|
||||||
|
List<StatementSyntax> topStatements = [];
|
||||||
|
Stack<object> stack = new();
|
||||||
|
|
||||||
|
for (int i = 0; i < methodBody.Length; i++)
|
||||||
|
{
|
||||||
|
var instruction = methodBody[i];
|
||||||
|
var opCode = instruction.OpCode;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (opCode)
|
||||||
|
{
|
||||||
|
case OpCode.PushConstant:
|
||||||
|
var constant = _context.GetConstant(instruction.Operands.First());
|
||||||
|
stack.Push(IrisExpression.ToSyntax(constant, _context));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.PushNull:
|
||||||
|
stack.Push(LiteralExpression(SyntaxKind.NullLiteralExpression));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.DiscardValue:
|
||||||
|
stack.Pop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.ConstructObject:
|
||||||
|
var typeToCtor = _context.GetImportedType(instruction.Operands.First());
|
||||||
|
stack.Push(ObjectCreationExpression(
|
||||||
|
IdentifierName(_context.GetQualifiedName(typeToCtor).ToString()),
|
||||||
|
ArgumentList(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.LookupSymbol:
|
||||||
|
var symbolIndex = (ushort)instruction.Operands.First().Value;
|
||||||
|
stack.Push(IdentifierName(export.SymbolReferenceTable[symbolIndex].Symbol));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.WriteSymbol:
|
||||||
|
case OpCode.WriteSymbolPeek:
|
||||||
|
var writeSymbolIndex = (ushort)instruction.Operands.First().Value;
|
||||||
|
var writeSymbolExpr = IdentifierName(export.SymbolReferenceTable[writeSymbolIndex].Symbol);
|
||||||
|
|
||||||
|
var newSymbolValue = opCode is OpCode.WriteSymbolPeek
|
||||||
|
? stack.Peek() : stack.Pop();
|
||||||
|
|
||||||
|
var symbolAssignmentExpr = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
|
||||||
|
writeSymbolExpr, IrisExpression.ToSyntax(newSymbolValue, _context));
|
||||||
|
|
||||||
|
topStatements.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
|
||||||
|
{
|
||||||
|
topStatements.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 propTarget = instruction.OpCode switch
|
||||||
|
{
|
||||||
|
OpCode.PropertyGet => stack.Pop(),
|
||||||
|
OpCode.PropertyGetPeek => stack.Peek(),
|
||||||
|
_ => propToGet.Owner,
|
||||||
|
};
|
||||||
|
|
||||||
|
var propertyGetExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||||
|
IrisExpression.ToSyntax(propTarget, _context),
|
||||||
|
IdentifierName(propToGet.Name));
|
||||||
|
|
||||||
|
stack.Push(propertyGetExpression);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.PropertyInitialize:
|
||||||
|
var propertyToInit = _context.GetImportedProperty(instruction.Operands.First());
|
||||||
|
var newPropValue = stack.Pop();
|
||||||
|
|
||||||
|
var target = stack.Pop();
|
||||||
|
var xTarget = (XElement)ToXmlFriendlyObject(target);
|
||||||
|
|
||||||
|
PropertyAssignOnXElement(xTarget, propertyToInit, IrisObject.Create(newPropValue, propertyToInit.PropertyType, _context));
|
||||||
|
|
||||||
|
stack.Push(new IrisObject(xTarget, propertyToInit.Owner));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.PropertyDictionaryAdd:
|
||||||
|
var targetDictProperty = _context.GetImportedProperty(instruction.Operands.ElementAt(0));
|
||||||
|
|
||||||
|
var keyReference = instruction.Operands.ElementAt(1);
|
||||||
|
var key = _context.GetConstant(keyReference).Value.ToString();
|
||||||
|
|
||||||
|
var dictValue = stack.Pop();
|
||||||
|
|
||||||
|
var targetInstance = (XElement)stack.Peek();
|
||||||
|
|
||||||
|
PropertyDictionaryAddOnXElement(targetInstance, targetDictProperty, IrisObject.Create(dictValue, null, _context), key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OpCode.PropertyListAdd:
|
||||||
|
var targetListProperty = _context.GetImportedProperty(instruction.Operands.First());
|
||||||
|
var valueToAdd = stack.Pop();
|
||||||
|
var targetInstance2 = (XElement)ToXmlFriendlyObject(stack.Peek());
|
||||||
|
|
||||||
|
PropertyListAddOnXElement(targetInstance2, targetListProperty, IrisObject.Create(valueToAdd, null, _context));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to decompile instruction `{instruction}` @ 0x{instruction.Offset:X} in script for {export.Name}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SyntaxTree(
|
||||||
|
CompilationUnit().WithMembers(
|
||||||
|
[..topStatements.Select(GlobalStatement)]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatScript(SyntaxTree tree, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return tree
|
||||||
|
.GetRoot(token)
|
||||||
|
.NormalizeWhitespace()
|
||||||
|
.SyntaxTree
|
||||||
|
.GetText(token)
|
||||||
|
.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ using System.Xml.Linq;
|
|||||||
|
|
||||||
namespace Microsoft.Iris.DecompXml;
|
namespace Microsoft.Iris.DecompXml;
|
||||||
|
|
||||||
public class Decompiler
|
public partial class Decompiler
|
||||||
{
|
{
|
||||||
private static readonly XNamespace _nsUix = XNamespace.Get("http://schemas.microsoft.com/2007/uix");
|
private static readonly XNamespace _nsUix = XNamespace.Get("http://schemas.microsoft.com/2007/uix");
|
||||||
private readonly DecompileContext _context;
|
private readonly DecompileContext _context;
|
||||||
@@ -51,6 +51,22 @@ public class Decompiler
|
|||||||
if (export.InitializePropertiesOffset is not uint.MaxValue)
|
if (export.InitializePropertiesOffset is not uint.MaxValue)
|
||||||
AnalyzeMethodForInit(export.InitializePropertiesOffset, xExport, export, name + "_prop");
|
AnalyzeMethodForInit(export.InitializePropertiesOffset, xExport, export, name + "_prop");
|
||||||
|
|
||||||
|
if (export.InitialEvaluateOffsets is { Length: > 0 })
|
||||||
|
{
|
||||||
|
XElement xScripts = new(_nsUix + "Scripts");
|
||||||
|
|
||||||
|
foreach (var offset in export.InitialEvaluateOffsets)
|
||||||
|
{
|
||||||
|
var syntaxTree = DecompileScript(offset, export);
|
||||||
|
var scriptText = FormatScript(syntaxTree);
|
||||||
|
|
||||||
|
XElement xScript = new(_nsUix + "Script", scriptText);
|
||||||
|
xScripts.Add(xScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
xExport.Add(xScripts);
|
||||||
|
}
|
||||||
|
|
||||||
if (export.InitializeContentOffset is not uint.MaxValue)
|
if (export.InitializeContentOffset is not uint.MaxValue)
|
||||||
AnalyzeMethodForInit(export.InitializeContentOffset, xExport, export, name + "_cont");
|
AnalyzeMethodForInit(export.InitializeContentOffset, xExport, export, name + "_cont");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
using Microsoft.Iris.Markup;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Iris.Markup;
|
||||||
|
using System;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
namespace Microsoft.Iris.DecompXml.Mock;
|
namespace Microsoft.Iris.DecompXml.Mock;
|
||||||
|
|
||||||
internal class IrisConstantExpression : IrisExpression, IReturnValueProvider
|
internal class IrisConstantExpression : IrisExpression, IReturnValueProvider
|
||||||
@@ -19,16 +24,17 @@ internal class IrisConstantExpression : IrisExpression, IReturnValueProvider
|
|||||||
|
|
||||||
public TypeSchema ReturnType => TypeSchema;
|
public TypeSchema ReturnType => TypeSchema;
|
||||||
|
|
||||||
public override string Decompile(DecompileContext context)
|
public override ExpressionSyntax ToSyntax(DecompileContext context)
|
||||||
{
|
{
|
||||||
if (TypeSchema.IsEnum)
|
|
||||||
return $"{context.GetQualifiedName(TypeSchema)}.{Value}";
|
|
||||||
|
|
||||||
return Value switch
|
return Value switch
|
||||||
{
|
{
|
||||||
string str => str,
|
Enum enumValue => MemberAccessExpression(
|
||||||
IStringEncodable strEnc => strEnc.EncodeString(),
|
SyntaxKind.SimpleMemberAccessExpression,
|
||||||
_ => Value?.ToString() ?? "null",
|
IdentifierName(context.GetQualifiedName(TypeSchema).ToString()),
|
||||||
|
IdentifierName(Value.ToString())
|
||||||
|
),
|
||||||
|
|
||||||
|
_ => ToSyntax(Value, context)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
using Microsoft.Iris.Asm;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Iris.Asm;
|
||||||
|
using Microsoft.Iris.Markup;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
namespace Microsoft.Iris.DecompXml.Mock;
|
namespace Microsoft.Iris.DecompXml.Mock;
|
||||||
|
|
||||||
internal class IrisExpression : Expression
|
internal abstract class IrisExpression : Expression
|
||||||
{
|
{
|
||||||
public virtual string Decompile(DecompileContext context) => ToString();
|
public virtual string Decompile(DecompileContext context) => ToSyntax(context).ToString();
|
||||||
|
|
||||||
|
public abstract ExpressionSyntax ToSyntax(DecompileContext context);
|
||||||
|
|
||||||
public static Expression Wrap(object p)
|
public static Expression Wrap(object p)
|
||||||
{
|
{
|
||||||
@@ -15,7 +23,7 @@ internal class IrisExpression : Expression
|
|||||||
null => Constant(null),
|
null => Constant(null),
|
||||||
Expression expr => expr,
|
Expression expr => expr,
|
||||||
Disassembler.RawConstantInfo constantInfo => new IrisConstantExpression(constantInfo.Value, constantInfo.Type),
|
Disassembler.RawConstantInfo constantInfo => new IrisConstantExpression(constantInfo.Value, constantInfo.Type),
|
||||||
Markup.SymbolReference symbolRef => Constant(symbolRef),
|
SymbolReference symbolRef => Constant(symbolRef),
|
||||||
|
|
||||||
_ => throw new NotImplementedException($"Unable to wrap '{p}' in an expression")
|
_ => throw new NotImplementedException($"Unable to wrap '{p}' in an expression")
|
||||||
};
|
};
|
||||||
@@ -27,4 +35,25 @@ internal class IrisExpression : Expression
|
|||||||
? irisExpr.Decompile(context)
|
? irisExpr.Decompile(context)
|
||||||
: expr.ToString();
|
: expr.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ExpressionSyntax ToSyntax(object obj, DecompileContext context)
|
||||||
|
{
|
||||||
|
return obj switch
|
||||||
|
{
|
||||||
|
null => LiteralExpression(SyntaxKind.NullLiteralExpression),
|
||||||
|
int intValue => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(intValue)),
|
||||||
|
string strValue => LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(strValue)),
|
||||||
|
IStringEncodable strEnc => ParseExpression(strEnc.EncodeString()),
|
||||||
|
|
||||||
|
Disassembler.RawConstantInfo constantInfo => new IrisConstantExpression(constantInfo.Value, constantInfo.Type).ToSyntax(context),
|
||||||
|
SymbolReference symbolRef => IdentifierName(symbolRef.Symbol),
|
||||||
|
TypeSchema typeSchema => IdentifierName(context.GetQualifiedName(typeSchema).ToString()),
|
||||||
|
|
||||||
|
IrisExpression irisExpr => irisExpr.ToSyntax(context),
|
||||||
|
Expression expr => ParseExpression(expr.ToString()),
|
||||||
|
ExpressionSyntax exprSyn => exprSyn,
|
||||||
|
|
||||||
|
_ => IdentifierName(obj.ToString())
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Microsoft.Iris.Markup;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Iris.Markup;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Text;
|
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
namespace Microsoft.Iris.DecompXml.Mock;
|
namespace Microsoft.Iris.DecompXml.Mock;
|
||||||
|
|
||||||
@@ -29,27 +31,22 @@ internal class IrisMethodCallExpression : IrisExpression, IArgumentProvider, IRe
|
|||||||
|
|
||||||
public Expression GetArgument(int index) => _arguments[index];
|
public Expression GetArgument(int index) => _arguments[index];
|
||||||
|
|
||||||
public override string Decompile(DecompileContext context)
|
public override ExpressionSyntax ToSyntax(DecompileContext context)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
var targetExpression = Target switch
|
||||||
|
|
||||||
if (Target is null)
|
|
||||||
{
|
{
|
||||||
var qfn = context.GetQualifiedName(Method.Owner);
|
null => IdentifierName(context.GetQualifiedName(Method.Owner).ToString()),
|
||||||
sb.Append(qfn);
|
IrisExpression irisExpr => irisExpr.ToSyntax(context),
|
||||||
}
|
_ => IdentifierName(Target.ToString())
|
||||||
else
|
};
|
||||||
{
|
|
||||||
sb.Append(Decompile(Target, context));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('.');
|
var methodExpression = MemberAccessExpression(CodeAnalysis.CSharp.SyntaxKind.SimpleMemberAccessExpression,
|
||||||
sb.Append(Method.Name);
|
targetExpression, IdentifierName(Method.Name));
|
||||||
|
|
||||||
sb.Append('(');
|
var argumentExpressions = _arguments
|
||||||
sb.Append(string.Join(", ", _arguments.Select(x => Decompile(x, context))));
|
.Select(expr => ToSyntax(expr, context))
|
||||||
sb.Append(')');
|
.Select(Argument);
|
||||||
|
|
||||||
return sb.ToString();
|
return InvocationExpression(methodExpression, ArgumentList([..argumentExpressions]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Microsoft.Iris.Markup;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Iris.Markup;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Text;
|
|
||||||
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||||
|
|
||||||
namespace Microsoft.Iris.DecompXml.Mock;
|
namespace Microsoft.Iris.DecompXml.Mock;
|
||||||
|
|
||||||
@@ -20,24 +22,18 @@ internal class IrisPropertyExpression : IrisExpression, IReturnValueProvider
|
|||||||
|
|
||||||
public TypeSchema ReturnType => Property.PropertyType;
|
public TypeSchema ReturnType => Property.PropertyType;
|
||||||
|
|
||||||
|
public override ExpressionSyntax ToSyntax(DecompileContext context)
|
||||||
public override string Decompile(DecompileContext context)
|
|
||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
var targetExpression = Target switch
|
||||||
|
|
||||||
if (Target is null)
|
|
||||||
{
|
{
|
||||||
var qfn = context.GetQualifiedName(Property.Owner);
|
null => IdentifierName(context.GetQualifiedName(Property.Owner).ToString()),
|
||||||
sb.Append(qfn);
|
IrisExpression irisExpr => irisExpr.ToSyntax(context),
|
||||||
}
|
_ => IdentifierName(Target.ToString())
|
||||||
else
|
};
|
||||||
{
|
|
||||||
sb.Append(Decompile(Target, context));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('.');
|
var propertyAccessExpression = MemberAccessExpression(CodeAnalysis.CSharp.SyntaxKind.SimpleMemberAccessExpression,
|
||||||
sb.Append(Property.Name);
|
targetExpression, IdentifierName(Property.Name));
|
||||||
|
|
||||||
return sb.ToString();
|
return propertyAccessExpression;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\UIX.Asm\UIX.Asm.csproj" />
|
<ProjectReference Include="..\UIX.Asm\UIX.Asm.csproj" />
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||||
|
|
||||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
|
|||||||
@@ -9,6 +9,14 @@
|
|||||||
</Properties>
|
</Properties>
|
||||||
</Class>
|
</Class>
|
||||||
<UI Name="AboutDialogContentUI" Base="dialog:DialogContentUI">
|
<UI Name="AboutDialogContentUI" Base="dialog:DialogContentUI">
|
||||||
|
<Scripts>
|
||||||
|
<Script>DialogTitle = zuneUI:Shell.LoadString(zuneUI:StringId.IDS_ABOUTDIALOG_TITLE);</Script>
|
||||||
|
<Script>ButtonCommands = new List();
|
||||||
|
ButtonCommands.Add(Dialog.Cancel);
|
||||||
|
DefaultButtonModel = Dialog.Cancel;
|
||||||
|
ButtonModelToFocus = Dialog.Cancel;</Script>
|
||||||
|
<Script></Script>
|
||||||
|
</Scripts>
|
||||||
<Content>
|
<Content>
|
||||||
<Panel MaximumSize="360, 0">
|
<Panel MaximumSize="360, 0">
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user