/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Xml; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnSpy.Contracts.Decompiler; using dnSpy.Decompiler.Properties; namespace dnSpy.Decompiler.MSBuild { sealed class BamlResourceProjectFile : ProjectFile { public override string Description => dnSpy_Decompiler_Resources.MSBuild_DecompileBaml; public bool IsAppDef { get; set; } public override BuildAction BuildAction => IsAppDef ? BuildAction.ApplicationDefinition : BuildAction.Page; public override string Filename { get; } public string TypeFullName { get; } public bool IsSatelliteFile { get; set; } readonly byte[] bamlData; readonly Func> decompileBaml; public IEnumerable AssemblyReferences => asmRefs; readonly HashSet asmRefs; public BamlResourceProjectFile(string filename, byte[] bamlData, string typeFullName, Func> decompileBaml) { Filename = filename; this.bamlData = bamlData; TypeFullName = typeFullName; SubType = "Designer"; Generator = "MSBuild:Compile"; this.decompileBaml = decompileBaml; asmRefs = new HashSet(AssemblyNameComparer.CompareAll); } public override void Create(DecompileContext ctx) { IList refs; using (var stream = File.Create(Filename)) refs = decompileBaml(bamlData, stream); foreach (var asmRef in refs) { var a = new AssemblyNameInfo(asmRef); if (!UTF8String.IsNullOrEmpty(a.Name)) asmRefs.Add(a); } } } // App.xaml isn't always created, so we must recreate it from the info found in the class. sealed class AppBamlResourceProjectFile : ProjectFile { public override string Description => dnSpy_Decompiler_Resources.MSBuild_CreateAppXaml; public override BuildAction BuildAction { get; } public override string Filename { get; } readonly TypeDef type; readonly IDecompiler decompiler; public AppBamlResourceProjectFile(string filename, TypeDef type, IDecompiler decompiler) { Filename = filename; this.type = type; SubType = "Designer"; Generator = "MSBuild:Compile"; BuildAction = DotNetUtils.IsStartUpClass(type) ? BuildAction.ApplicationDefinition : BuildAction.Page; this.decompiler = decompiler; } CilBody? GetInitializeComponentBody() { var m = type.FindMethods("InitializeComponent").FirstOrDefault(a => a.Parameters.Count == 1 && !a.IsStatic); return m?.Body; } string? GetStartupUri(CilBody body) => body?.Instructions.Where(a => a.Operand is string && ((string)a.Operand).EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)).Select(a => (string)a.Operand).FirstOrDefault(); public override void Create(DecompileContext ctx) { var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true, OmitXmlDeclaration = true, }; using (var writer = XmlWriter.Create(Filename, settings)) { writer.WriteStartDocument(); writer.WriteStartElement("Application", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); writer.WriteAttributeString("x", "Class", "http://schemas.microsoft.com/winfx/2006/xaml", type.ReflectionFullName); if (type.IsNotPublic) { var opts = BamlDecompilerOptions.Create(decompiler); writer.WriteAttributeString("x", "ClassModifier", "http://schemas.microsoft.com/winfx/2006/xaml", opts.InternalClassModifier); } var body = GetInitializeComponentBody(); Debug2.Assert(body is not null); if (body is not null) { var startupUri = GetStartupUri(body); if (startupUri is not null) writer.WriteAttributeString("StartupUri", startupUri); foreach (var info in GetEvents(body)) writer.WriteAttributeString(info.Item1, info.Item2); } writer.WriteElementString("Application.Resources", "\r\n"); writer.WriteEndElement(); writer.WriteEndDocument(); } } IEnumerable<(string, string)> GetEvents(CilBody body) { var instrs = body.Instructions; for (int i = 0; i + 2 < instrs.Count; i++) { if (instrs[i].OpCode.Code != Code.Ldftn && instrs[i].OpCode.Code != Code.Ldvirtftn) continue; var m = instrs[i].Operand as MethodDef; if (m is null) continue; if (instrs[i + 1].OpCode.Code != Code.Newobj) continue; if (instrs[i + 2].OpCode.Code != Code.Call) continue; var addMethod = instrs[i + 2].Operand as IMethod; if (addMethod is null || addMethod.MethodSig.GetParamCount() != 1) continue; if (!addMethod.Name.StartsWith("add_")) continue; yield return (addMethod.Name.String.Substring(4), m.Name.String); } } } }