/* 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.Threading; using dnlib.DotNet; using dnlib.IO; using dnSpy.Contracts.Decompiler; using dnSpy.Contracts.DnSpy.Properties; using dnSpy.Contracts.Images; using dnSpy.Contracts.Text; using dnSpy.Contracts.TreeView; using dnSpy.Contracts.Utilities; namespace dnSpy.Contracts.Documents.TreeView.Resources { /// /// Resource node base class /// public abstract class ResourceNode : DocumentTreeNodeData, IResourceNode { /// /// Gets the resource /// public Resource Resource { get; set; } /// /// Gets the name /// public string Name => Resource.Name; /// protected sealed override void WriteCore(ITextColorWriter output, IDecompiler decompiler, DocumentNodeWriteOptions options) { output.WriteFilename(Resource.Name); if ((options & DocumentNodeWriteOptions.ToolTip) != 0) { output.WriteLine(); WriteFilename(output); } } /// protected sealed override ImageReference? GetExpandedIcon(IDotNetImageService dnImgMgr) => null; /// protected sealed override ImageReference GetIcon(IDotNetImageService dnImgMgr) { var imgRef = GetIcon(); if (!imgRef.IsDefault) return imgRef; var asm = dnImgMgr.GetType().Assembly; return ResourceUtilities.TryGetImageReference(asm, Resource.Name) ?? DsImages.Dialog; } /// /// Gets the icon /// /// protected virtual ImageReference GetIcon() => new ImageReference(); /// public sealed override NodePathName NodePathName => new NodePathName(Guid, NameUtilities.CleanName(Resource.Name)); /// /// Gets the offset of the resource /// public uint FileOffset { get { GetModuleOffset(out var fo); return (uint)fo; } } /// /// Gets the length of the resource /// public uint Length { get { var er = Resource as EmbeddedResource; return er is null ? 0 : er.Length; } } /// /// Gets the RVA of the resource /// public uint RVA { get { var module = GetModuleOffset(out var fo); if (module is null) return 0; return (uint)module.Metadata.PEImage.ToRVA(fo); } } ModuleDefMD? GetModuleOffset(out FileOffset fileOffset) => GetModuleOffset(this, Resource, out fileOffset); internal static ModuleDefMD? GetModuleOffset(DocumentTreeNodeData node, Resource resource, out FileOffset fileOffset) { fileOffset = 0; var er = resource as EmbeddedResource; if (er is null) return null; var module = node.GetModule() as ModuleDefMD;//TODO: Support CorModuleDef if (module is null) return null; fileOffset = (FileOffset)er.CreateReader().StartOffset; return module; } /// public override ITreeNodeGroup? TreeNodeGroup => treeNodeGroup; readonly ITreeNodeGroup treeNodeGroup; /// /// Constructor /// /// Treenode group /// Resource protected ResourceNode(ITreeNodeGroup treeNodeGroup, Resource resource) { this.treeNodeGroup = treeNodeGroup ?? throw new ArgumentNullException(nameof(treeNodeGroup)); Resource = resource ?? throw new ArgumentNullException(nameof(resource)); } /// /// Saves the resource /// protected void Save() => SaveResources.Save(new IResourceDataProvider[] { this }, false, ResourceDataType.Deserialized); /// public virtual void WriteShort(IDecompilerOutput output, IDecompiler decompiler, bool showOffset) { decompiler.WriteCommentBegin(output, true); output.WriteOffsetComment(this, showOffset); const string LTR = "\u200E"; output.Write(NameUtilities.CleanName(Name) + LTR, this, DecompilerReferenceFlags.Local | DecompilerReferenceFlags.Definition, BoxedTextColor.Comment); string? extra = null; switch (Resource.ResourceType) { case ResourceType.AssemblyLinked: extra = ((AssemblyLinkedResource)Resource).Assembly.FullName; break; case ResourceType.Linked: var file = ((LinkedResource)Resource).File; extra = $"{file.Name}, {(file.ContainsNoMetadata ? "ContainsNoMetaData" : "ContainsMetaData")}, {SimpleTypeConverter.ByteArrayToString(file.HashValue)}"; break; case ResourceType.Embedded: extra = string.Format(dnSpy_Contracts_DnSpy_Resources.NumberOfBytes, ((EmbeddedResource)Resource).Length); break; } output.Write($" ({(extra is null ? string.Empty : $"{extra}, ")}{Resource.ResourceType}, {Resource.Attributes})", BoxedTextColor.Comment); decompiler.WriteCommentEnd(output, true); output.WriteLine(); } /// /// Converts the value to a string /// /// Cancellation token /// true if the data can be decompiled /// public virtual string? ToString(CancellationToken token, bool canDecompile) => null; /// public IEnumerable GetResourceData(ResourceDataType type) { switch (type) { case ResourceDataType.Deserialized: return GetDeserializedData(); case ResourceDataType.Serialized: return GetSerializedData(); default: throw new InvalidOperationException(); } } /// /// Gets the deserialized data /// /// protected virtual IEnumerable GetDeserializedData() => GetSerializedData(); /// /// Gets the serialized data /// /// protected virtual IEnumerable GetSerializedData() { if (Resource is EmbeddedResource er) yield return new ResourceData(Resource.Name, token => er.CreateReader().AsStream()); } /// public sealed override FilterType GetFilterType(IDocumentTreeNodeFilter filter) => filter.GetResult(this).FilterType; sealed class Data { public readonly Resource Resource; public Data(Resource resource) => Resource = resource; } /// /// Gets the resource or null /// /// Node /// public static Resource? GetResource(DocumentTreeNodeData node) { if (node is ResourceNode resourceNode) return resourceNode.Resource; if (node.TryGetData(out Data? data)) return data.Resource; return null; } /// /// Adds the resource to a resource node /// /// Node /// Resource public static void AddResource(DocumentTreeNodeData node, Resource resource) { if (node is ResourceNode resourceNode) { if (resourceNode.Resource != resource) throw new InvalidOperationException(); } else { if (node.TryGetData(out _)) throw new InvalidOperationException(); node.AddData(new Data(resource)); } } } }