// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.Threading; using dnlib.IO; using dnlib.DotNet.MD; using dnlib.DotNet.Pdb; namespace dnlib.DotNet { /// /// Type of resource /// public enum ResourceType { /// /// It's a /// Embedded, /// /// It's a /// AssemblyLinked, /// /// It's a /// Linked, } /// /// Resource base class /// public abstract class Resource : IMDTokenProvider, IHasCustomAttribute, IHasCustomDebugInformation { private protected uint rid; private protected uint? offset; UTF8String name; ManifestResourceAttributes flags; /// public MDToken MDToken => new MDToken(Table.ManifestResource, rid); /// public uint Rid { get => rid; set => rid = value; } /// /// Gets/sets the offset of the resource /// public uint? Offset { get => offset; set => offset = value; } /// /// Gets/sets the name /// public UTF8String Name { get => name; set => name = value; } /// /// Gets/sets the flags /// public ManifestResourceAttributes Attributes { get => flags; set => flags = value; } /// /// Gets the type of resource /// public abstract ResourceType ResourceType { get; } /// /// Gets/sets the visibility /// public ManifestResourceAttributes Visibility { get => flags & ManifestResourceAttributes.VisibilityMask; set => flags = (flags & ~ManifestResourceAttributes.VisibilityMask) | (value & ManifestResourceAttributes.VisibilityMask); } /// /// true if is set /// public bool IsPublic => (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Public; /// /// true if is set /// public bool IsPrivate => (flags & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private; /// public int HasCustomAttributeTag => 18; /// /// Gets all custom attributes /// public CustomAttributeCollection CustomAttributes { get { if (customAttributes is null) InitializeCustomAttributes(); return customAttributes; } } /// protected CustomAttributeCollection customAttributes; /// Initializes protected virtual void InitializeCustomAttributes() => Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null); /// public bool HasCustomAttributes => CustomAttributes.Count > 0; /// public int HasCustomDebugInformationTag => 18; /// /// Gets all custom debug infos /// public IList CustomDebugInfos { get { if (customDebugInfos is null) InitializeCustomDebugInfos(); return customDebugInfos; } } /// protected IList customDebugInfos; /// Initializes protected virtual void InitializeCustomDebugInfos() => Interlocked.CompareExchange(ref customDebugInfos, new List(), null); /// public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0; /// /// Constructor /// /// Name /// flags protected Resource(UTF8String name, ManifestResourceAttributes flags) { this.name = name; this.flags = flags; } } /// /// A resource that is embedded in a .NET module. This is the most common type of resource. /// public class EmbeddedResource : Resource { readonly DataReaderFactory dataReaderFactory; readonly uint resourceStartOffset; readonly uint resourceLength; /// /// Gets the length of the data /// public uint Length => resourceLength; /// public override ResourceType ResourceType => ResourceType.Embedded; /// /// Constructor /// /// Name of resource /// Resource data /// Resource flags public EmbeddedResource(UTF8String name, byte[] data, ManifestResourceAttributes flags = ManifestResourceAttributes.Private) : this(name, ByteArrayDataReaderFactory.Create(data, filename: null), 0, (uint)data.Length, flags) { } /// /// Constructor /// /// Name of resource /// Data reader factory /// Offset of resource data /// Length of resource data /// Resource flags public EmbeddedResource(UTF8String name, DataReaderFactory dataReaderFactory, uint offset, uint length, ManifestResourceAttributes flags = ManifestResourceAttributes.Private) : base(name, flags) { this.dataReaderFactory = dataReaderFactory ?? throw new ArgumentNullException(nameof(dataReaderFactory)); resourceStartOffset = offset; resourceLength = length; } /// /// Gets a data reader that can access the resource /// /// public DataReader CreateReader() => dataReaderFactory.CreateReader(resourceStartOffset, resourceLength); /// public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - size: {(resourceLength)}"; } sealed class EmbeddedResourceMD : EmbeddedResource, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// protected override void InitializeCustomDebugInfos() { var list = new List(); readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } public EmbeddedResourceMD(ModuleDefMD readerModule, ManifestResource mr, byte[] data) : this(readerModule, mr, ByteArrayDataReaderFactory.Create(data, filename: null), 0, (uint)data.Length) { } public EmbeddedResourceMD(ModuleDefMD readerModule, ManifestResource mr, DataReaderFactory dataReaderFactory, uint offset, uint length) : base(mr.Name, dataReaderFactory, offset, length, mr.Flags) { this.readerModule = readerModule; origRid = rid = mr.Rid; this.offset = mr.Offset; } } /// /// A reference to a resource in another assembly /// public class AssemblyLinkedResource : Resource { AssemblyRef asmRef; /// public override ResourceType ResourceType => ResourceType.AssemblyLinked; /// /// Gets/sets the assembly reference /// public AssemblyRef Assembly { get => asmRef; set => asmRef = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Constructor /// /// Name of resource /// Assembly reference /// Resource flags public AssemblyLinkedResource(UTF8String name, AssemblyRef asmRef, ManifestResourceAttributes flags) : base(name, flags) => this.asmRef = asmRef ?? throw new ArgumentNullException(nameof(asmRef)); /// public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - assembly: {asmRef.FullName}"; } sealed class AssemblyLinkedResourceMD : AssemblyLinkedResource, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// protected override void InitializeCustomDebugInfos() { var list = new List(); readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } public AssemblyLinkedResourceMD(ModuleDefMD readerModule, ManifestResource mr, AssemblyRef asmRef) : base(mr.Name, asmRef, mr.Flags) { this.readerModule = readerModule; origRid = rid = mr.Rid; offset = mr.Offset; } } /// /// A resource that is stored in a file on disk /// public class LinkedResource : Resource { FileDef file; /// public override ResourceType ResourceType => ResourceType.Linked; /// /// Gets/sets the file /// public FileDef File { get => file; set => file = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets/sets the hash /// public byte[] Hash { get => file.HashValue; set => file.HashValue = value; } /// /// Gets/sets the file name /// public UTF8String FileName => file is null ? UTF8String.Empty : file.Name; /// /// Constructor /// /// Name of resource /// The file /// Resource flags public LinkedResource(UTF8String name, FileDef file, ManifestResourceAttributes flags) : base(name, flags) => this.file = file; /// public override string ToString() => $"{UTF8String.ToSystemStringOrEmpty(Name)} - file: {UTF8String.ToSystemStringOrEmpty(FileName)}"; } sealed class LinkedResourceMD : LinkedResource, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.ManifestResource, origRid); var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index])); Interlocked.CompareExchange(ref customAttributes, tmp, null); } /// protected override void InitializeCustomDebugInfos() { var list = new List(); readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } public LinkedResourceMD(ModuleDefMD readerModule, ManifestResource mr, FileDef file) : base(mr.Name, file, mr.Flags) { this.readerModule = readerModule; origRid = rid = mr.Rid; offset = mr.Offset; } } }