From 2d0df349d87710db2baf65b2a3257ce711585bdc Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 19 Oct 2020 21:44:35 +0200 Subject: [PATCH] Refactor resource loading. --- ICSharpCode.Decompiler/Metadata/Dom.cs | 17 ++++++++ ILSpy.BamlDecompiler/BamlResourceEntryNode.cs | 8 ++-- .../BamlResourceNodeFactory.cs | 9 +--- .../ResourceNodes/CursorResourceEntryNode.cs | 41 ++++++++----------- .../ResourceNodes/IResourceNodeFactory.cs | 1 - .../ResourceNodes/IconResourceEntryNode.cs | 26 ++++-------- .../ImageListResourceEntryNode.cs | 5 ++- .../ResourceNodes/ImageResourceEntryNode.cs | 26 +++--------- .../ResourceNodes/ResourceEntryNode.cs | 41 ++++++++++--------- .../ResourceNodes/ResourceTreeNode.cs | 9 +--- .../ResourceNodes/ResourcesFileTreeNode.cs | 9 +--- .../ResourceNodes/XamlResourceNode.cs | 20 +++++---- .../ResourceNodes/XmlResourceNode.cs | 24 +++++------ 13 files changed, 104 insertions(+), 132 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/Dom.cs b/ICSharpCode.Decompiler/Metadata/Dom.cs index a8a7668ad..de21e0ba3 100644 --- a/ICSharpCode.Decompiler/Metadata/Dom.cs +++ b/ICSharpCode.Decompiler/Metadata/Dom.cs @@ -35,6 +35,23 @@ namespace ICSharpCode.Decompiler.Metadata public abstract Stream TryOpenStream(); } + public class ByteArrayResource : Resource + { + public override string Name { get; } + byte[] data; + + public ByteArrayResource(string name, byte[] data) + { + this.Name = name ?? throw new ArgumentNullException(nameof(name)); + this.data = data ?? throw new ArgumentNullException(nameof(data)); + } + + public override Stream TryOpenStream() + { + return new MemoryStream(data); + } + } + sealed class MetadataResource : Resource { public PEFile Module { get; } diff --git a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs index af79c363e..f1d6dafee 100644 --- a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs +++ b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs @@ -42,7 +42,7 @@ namespace ILSpy.BamlDecompiler { public sealed class BamlResourceEntryNode : ResourceEntryNode { - public BamlResourceEntryNode(string key, Stream data) : base(key, data) + public BamlResourceEntryNode(string key, Func data) : base(key, data) { } @@ -74,8 +74,8 @@ namespace ILSpy.BamlDecompiler bool LoadBaml(AvalonEditTextOutput output, CancellationToken cancellationToken) { var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; - Data.Position = 0; - XDocument xamlDocument = LoadIntoDocument(asm.GetPEFileOrNull(), asm.GetAssemblyResolver(), Data, cancellationToken); + using var data = OpenStream(); + XDocument xamlDocument = LoadIntoDocument(asm.GetPEFileOrNull(), asm.GetAssemblyResolver(), data, cancellationToken); output.Write(xamlDocument.ToString()); return true; } @@ -203,4 +203,4 @@ namespace ILSpy.BamlDecompiler public new MetadataModule MainModule { get; } } } -} \ No newline at end of file +} diff --git a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs index 551874112..107949a3a 100644 --- a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs +++ b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs @@ -31,13 +31,8 @@ namespace ILSpy.BamlDecompiler { public ILSpyTreeNode CreateNode(Resource resource) { - return null; - } - - public ILSpyTreeNode CreateNode(string key, object data) - { - if (key.EndsWith(".baml", StringComparison.OrdinalIgnoreCase) && data is Stream stream) - return new BamlResourceEntryNode(key, stream); + if (resource.Name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) + return new BamlResourceEntryNode(resource.Name, resource.TryOpenStream); else return null; } diff --git a/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs index c3ecedae6..dfe12d2af 100644 --- a/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs @@ -36,20 +36,11 @@ namespace ICSharpCode.ILSpy.TreeNodes public ILSpyTreeNode CreateNode(Resource resource) { - Stream stream = resource.TryOpenStream(); - if (stream == null) - return null; - return CreateNode(resource.Name, stream); - } - - public ILSpyTreeNode CreateNode(string key, object data) - { - if (!(data is Stream)) - return null; + string key = resource.Name; foreach (string fileExt in imageFileExtensions) { if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) - return new CursorResourceEntryNode(key, (Stream)data); + return new CursorResourceEntryNode(key, resource.TryOpenStream); } return null; } @@ -57,8 +48,8 @@ namespace ICSharpCode.ILSpy.TreeNodes sealed class CursorResourceEntryNode : ResourceEntryNode { - public CursorResourceEntryNode(string key, Stream data) - : base(key, data) + public CursorResourceEntryNode(string key, Func openStream) + : base(key, openStream) { } @@ -69,19 +60,23 @@ namespace ICSharpCode.ILSpy.TreeNodes try { AvalonEditTextOutput output = new AvalonEditTextOutput(); - Data.Position = 0; BitmapImage image = new BitmapImage(); - - //HACK: windows imaging does not understand that .cur files have the same layout as .ico - // so load to data, and modify the ResourceType in the header to make look like an icon... - MemoryStream s = Data as MemoryStream; - if (null == s) + byte[] curData; + using (var data = OpenStream()) { - // data was stored in another stream type (e.g. PinnedBufferedMemoryStream) - s = new MemoryStream(); - Data.CopyTo(s); + if (data == null) + return false; + //HACK: windows imaging does not understand that .cur files have the same layout as .ico + // so load to data, and modify the ResourceType in the header to make look like an icon... + MemoryStream s = data as MemoryStream; + if (s == null) + { + // data was stored in another stream type (e.g. PinnedBufferedMemoryStream) + s = new MemoryStream(); + data.CopyTo(s); + } + curData = s.ToArray(); } - byte[] curData = s.ToArray(); curData[2] = 1; using (Stream stream = new MemoryStream(curData)) { diff --git a/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs b/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs index a9dda017a..94a3ce3cd 100644 --- a/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs +++ b/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs @@ -26,6 +26,5 @@ namespace ICSharpCode.ILSpy.TreeNodes public interface IResourceNodeFactory { ILSpyTreeNode CreateNode(Resource resource); - ILSpyTreeNode CreateNode(string key, object data); } } diff --git a/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs index acb57fbcc..95a606592 100644 --- a/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/IconResourceEntryNode.cs @@ -34,29 +34,17 @@ namespace ICSharpCode.ILSpy.TreeNodes { public ILSpyTreeNode CreateNode(Resource resource) { - Stream stream = resource.TryOpenStream(); - if (stream == null) - return null; - return CreateNode(resource.Name, stream); - } - - public ILSpyTreeNode CreateNode(string key, object data) - { - if (data is System.Drawing.Icon) + if (resource.Name.EndsWith(".ico", StringComparison.OrdinalIgnoreCase)) { - MemoryStream s = new MemoryStream(); - ((System.Drawing.Icon)data).Save(s); - return new IconResourceEntryNode(key, s); + return new IconResourceEntryNode(resource.Name, resource.TryOpenStream); } - if (data is Stream && key.EndsWith(".ico", StringComparison.OrdinalIgnoreCase)) - return new IconResourceEntryNode(key, (Stream)data); return null; } } sealed class IconResourceEntryNode : ResourceEntryNode { - public IconResourceEntryNode(string key, Stream data) + public IconResourceEntryNode(string key, Func data) : base(key, data) { } @@ -68,8 +56,10 @@ namespace ICSharpCode.ILSpy.TreeNodes try { AvalonEditTextOutput output = new AvalonEditTextOutput(); - Data.Position = 0; - IconBitmapDecoder decoder = new IconBitmapDecoder(Data, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None); + using var data = OpenStream(); + if (data == null) + return false; + IconBitmapDecoder decoder = new IconBitmapDecoder(data, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None); foreach (var frame in decoder.Frames) { output.Write(String.Format("{0}x{1}, {2} bit: ", frame.PixelHeight, frame.PixelWidth, frame.Thumbnail.Format.BitsPerPixel)); @@ -94,4 +84,4 @@ namespace ICSharpCode.ILSpy.TreeNodes output.AddUIElement(() => new Image { Source = frame }); } } -} \ No newline at end of file +} diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs index 56276dd14..4a8a6f395 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs @@ -18,6 +18,7 @@ using System.ComponentModel.Composition; using System.Drawing; +using System.IO; using System.Windows.Forms; using ICSharpCode.Decompiler; @@ -65,7 +66,9 @@ namespace ICSharpCode.ILSpy.TreeNodes int i = 0; foreach (Image image in this.data.Images) { - var node = ResourceEntryNode.Create("Image" + i.ToString(), image); + using var s = new MemoryStream(); + image.Save(s, System.Drawing.Imaging.ImageFormat.Bmp); + var node = ResourceEntryNode.Create("Image" + i.ToString(), s.ToArray()); if (node != null) Children.Add(node); ++i; diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs index ea0ecc0d4..e03f56df3 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs @@ -36,26 +36,11 @@ namespace ICSharpCode.ILSpy.TreeNodes public ILSpyTreeNode CreateNode(Resource resource) { - Stream stream = resource.TryOpenStream(); - if (stream == null) - return null; - return CreateNode(resource.Name, stream); - } - - public ILSpyTreeNode CreateNode(string key, object data) - { - if (data is System.Drawing.Image) - { - MemoryStream s = new MemoryStream(); - ((System.Drawing.Image)data).Save(s, System.Drawing.Imaging.ImageFormat.Bmp); - return new ImageResourceEntryNode(key, s); - } - if (!(data is Stream)) - return null; + string key = resource.Name; foreach (string fileExt in imageFileExtensions) { if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) - return new ImageResourceEntryNode(key, (Stream)data); + return new ImageResourceEntryNode(key, resource.TryOpenStream); } return null; } @@ -63,8 +48,8 @@ namespace ICSharpCode.ILSpy.TreeNodes sealed class ImageResourceEntryNode : ResourceEntryNode { - public ImageResourceEntryNode(string key, Stream data) - : base(key, data) + public ImageResourceEntryNode(string key, Func openStream) + : base(key, openStream) { } @@ -75,10 +60,9 @@ namespace ICSharpCode.ILSpy.TreeNodes try { AvalonEditTextOutput output = new AvalonEditTextOutput(); - Data.Position = 0; BitmapImage image = new BitmapImage(); image.BeginInit(); - image.StreamSource = Data; + image.StreamSource = OpenStream(); image.EndInit(); output.AddUIElement(() => new Image { Source = image }); output.WriteLine(); diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index 2eaaf73f6..659f72639 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -20,6 +20,7 @@ using System; using System.IO; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; using ICSharpCode.ILSpy.TextView; using Microsoft.Win32; @@ -32,42 +33,47 @@ namespace ICSharpCode.ILSpy.TreeNodes public class ResourceEntryNode : ILSpyTreeNode { private readonly string key; - private readonly Stream data; + private readonly Func openStream; public override object Text => this.key; public override object Icon => Images.Resource; - protected Stream Data => data; + protected Stream OpenStream() + { + return openStream(); + } - public ResourceEntryNode(string key, Stream data) + public ResourceEntryNode(string key, Func openStream) { if (key == null) throw new ArgumentNullException(nameof(key)); - if (data == null) - throw new ArgumentNullException(nameof(data)); + if (openStream == null) + throw new ArgumentNullException(nameof(openStream)); this.key = key; - this.data = data; + this.openStream = openStream; } - public static ILSpyTreeNode Create(string key, object data) + public static ILSpyTreeNode Create(Resource resource) { ILSpyTreeNode result = null; foreach (var factory in App.ExportProvider.GetExportedValues()) { - result = factory.CreateNode(key, data); + result = factory.CreateNode(resource); if (result != null) - return result; + break; } - var streamData = data as Stream; - if (streamData != null) - result = new ResourceEntryNode(key, data as Stream); + return result ?? new ResourceTreeNode(resource); + } - return result; + public static ILSpyTreeNode Create(string name, byte[] data) + { + return Create(new ByteArrayResource(name, data)); } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { + using var data = OpenStream(); language.WriteCommentLine(output, string.Format("{0} = {1}", key, data)); } @@ -77,12 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); if (dlg.ShowDialog() == true) { - var data = this.Data; - data.Position = 0; - using (var fs = dlg.OpenFile()) - { - data.CopyTo(fs); - } + using var data = OpenStream(); + using var fs = dlg.OpenFile(); + data.CopyTo(fs); } return true; } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index c1fa916cc..16e378f43 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -119,14 +119,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public static ILSpyTreeNode Create(Resource resource) { - ILSpyTreeNode result = null; - foreach (var factory in App.ExportProvider.GetExportedValues()) - { - result = factory.CreateNode(resource); - if (result != null) - break; - } - return result ?? new ResourceTreeNode(resource); + return ResourceEntryNode.Create(resource); } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs index 28662c597..2a1af4a81 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -99,14 +99,7 @@ namespace ICSharpCode.ILSpy.TreeNodes if (entry.Value is byte[]) { - Children.Add(ResourceEntryNode.Create(entry.Key, new MemoryStream((byte[])entry.Value))); - return; - } - - var node = ResourceEntryNode.Create(entry.Key, entry.Value); - if (node != null) - { - Children.Add(node); + Children.Add(ResourceEntryNode.Create(entry.Key, (byte[])entry.Value)); return; } diff --git a/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs index caf9f5fd1..a673ea074 100644 --- a/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs @@ -34,13 +34,8 @@ namespace ICSharpCode.ILSpy.Xaml { public ILSpyTreeNode CreateNode(Resource resource) { - return null; - } - - public ILSpyTreeNode CreateNode(string key, object data) - { - if (key.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase) && data is Stream) - return new XamlResourceEntryNode(key, (Stream)data); + if (resource.Name.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + return new XamlResourceEntryNode(resource.Name, resource.TryOpenStream); else return null; } @@ -50,7 +45,7 @@ namespace ICSharpCode.ILSpy.Xaml { string xaml; - public XamlResourceEntryNode(string key, Stream data) : base(key, data) + public XamlResourceEntryNode(string key, Func openStream) : base(key, openStream) { } @@ -67,7 +62,14 @@ namespace ICSharpCode.ILSpy.Xaml // cache read XAML because stream will be closed after first read if (xaml == null) { - using (var reader = new StreamReader(Data)) + using var data = OpenStream(); + if (data == null) + { + output.Write("ILSpy: Failed opening resource stream."); + output.WriteLine(); + return output; + } + using (var reader = new StreamReader(data)) { xaml = reader.ReadToEnd(); } diff --git a/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs index 3c10fc964..3d544e13b 100644 --- a/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs @@ -36,20 +36,11 @@ namespace ICSharpCode.ILSpy.Xaml public ILSpyTreeNode CreateNode(Resource resource) { - Stream stream = resource.TryOpenStream(); - if (stream == null) - return null; - return CreateNode(resource.Name, stream); - } - - public ILSpyTreeNode CreateNode(string key, object data) - { - if (!(data is Stream)) - return null; + string key = resource.Name; foreach (string fileExt in xmlFileExtensions) { if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) - return new XmlResourceEntryNode(key, (Stream)data); + return new XmlResourceEntryNode(key, resource.TryOpenStream); } return null; } @@ -59,7 +50,7 @@ namespace ICSharpCode.ILSpy.Xaml { string xml; - public XmlResourceEntryNode(string key, Stream data) + public XmlResourceEntryNode(string key, Func data) : base(key, data) { } @@ -91,7 +82,14 @@ namespace ICSharpCode.ILSpy.Xaml // cache read XAML because stream will be closed after first read if (xml == null) { - using (var reader = new StreamReader(Data)) + using var data = OpenStream(); + if (data == null) + { + output.Write("ILSpy: Failed opening resource stream."); + output.WriteLine(); + return output; + } + using (var reader = new StreamReader(data)) { xml = reader.ReadToEnd(); }