// dnlib: See LICENSE.txt for more info
using System;
using System.Threading;
using dnlib.Utils;
using dnlib.IO;
using dnlib.PE;
using dnlib.Threading;
namespace dnlib.W32Resources {
///
/// Win32 resources base class
///
public abstract class Win32Resources : IDisposable {
///
/// Gets/sets the root directory
///
public abstract ResourceDirectory Root { get; set; }
///
/// Finds a
///
/// Type
/// The or null if none found
public ResourceDirectory Find(ResourceName type) {
var dir = Root;
if (dir is null)
return null;
return dir.FindDirectory(type);
}
///
/// Finds a
///
/// Type
/// Name
/// The or null if none found
public ResourceDirectory Find(ResourceName type, ResourceName name) {
var dir = Find(type);
if (dir is null)
return null;
return dir.FindDirectory(name);
}
///
/// Finds a
///
/// Type
/// Name
/// Language ID
/// The or null if none found
public ResourceData Find(ResourceName type, ResourceName name, ResourceName langId) {
var dir = Find(type, name);
if (dir is null)
return null;
return dir.FindData(langId);
}
///
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Dispose method
///
/// true if called by
protected virtual void Dispose(bool disposing) {
if (!disposing)
return;
Root = null; // Property handler will call Dispose()
}
}
///
/// Win32 resources class created by the user
///
public class Win32ResourcesUser : Win32Resources {
ResourceDirectory root = new ResourceDirectoryUser(new ResourceName("root"));
///
public override ResourceDirectory Root {
get => root;
set => Interlocked.Exchange(ref root, value);
}
}
///
/// Win32 resources class created from a PE file
///
public sealed class Win32ResourcesPE : Win32Resources {
///
/// Converts data RVAs to file offsets in
///
readonly IRvaFileOffsetConverter rvaConverter;
///
/// This reader only reads the raw data. The data RVA is found in the data header and
/// it's first converted to a file offset using . This file
/// offset is where we'll read from using this reader.
///
DataReaderFactory dataReader_factory;
uint dataReader_offset;
uint dataReader_length;
bool owns_dataReader_factory;
///
/// This reader only reads the directory entries and data headers. The data is read
/// by
///
DataReaderFactory rsrcReader_factory;
uint rsrcReader_offset;
uint rsrcReader_length;
bool owns_rsrcReader_factory;
UserValue root;
#if THREAD_SAFE
readonly Lock theLock = Lock.Create();
#endif
///
public override ResourceDirectory Root {
get => root.Value;
set {
if (root.IsValueInitialized) {
var origValue = root.Value;
if (origValue == value)
return;
}
root.Value = value;
}
}
///
/// Gets the resource reader
///
internal DataReader GetResourceReader() => rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length);
///
/// Constructor
///
/// / converter
/// Reader for the whole Win32 resources section (usually
/// the .rsrc section). It's used to read 's and
/// 's but not the actual data blob.
/// Offset of resource section
/// Length of resource section
/// true if this instance can dispose of
/// Data reader (it's used after converting an
/// to a )
/// Offset of resource section
/// Length of resource section
/// true if this instance can dispose of
public Win32ResourcesPE(IRvaFileOffsetConverter rvaConverter, DataReaderFactory rsrcReader_factory, uint rsrcReader_offset, uint rsrcReader_length, bool owns_rsrcReader_factory, DataReaderFactory dataReader_factory, uint dataReader_offset, uint dataReader_length, bool owns_dataReader_factory) {
this.rvaConverter = rvaConverter ?? throw new ArgumentNullException(nameof(rvaConverter));
this.rsrcReader_factory = rsrcReader_factory ?? throw new ArgumentNullException(nameof(rsrcReader_factory));
this.rsrcReader_offset = rsrcReader_offset;
this.rsrcReader_length = rsrcReader_length;
this.owns_rsrcReader_factory = owns_rsrcReader_factory;
this.dataReader_factory = dataReader_factory ?? throw new ArgumentNullException(nameof(dataReader_factory));
this.dataReader_offset = dataReader_offset;
this.dataReader_length = dataReader_length;
this.owns_dataReader_factory = owns_dataReader_factory;
Initialize();
}
///
/// Constructor
///
/// The PE image
public Win32ResourcesPE(IPEImage peImage)
: this(peImage, null, 0, 0, false) {
}
///
/// Constructor
///
/// The PE image
/// Reader for the whole Win32 resources section (usually
/// the .rsrc section) or null if we should create one from the resource data
/// directory in the optional header
/// Offset of resource section
/// Length of resource section
/// true if this instance can dispose of
public Win32ResourcesPE(IPEImage peImage, DataReaderFactory rsrcReader_factory, uint rsrcReader_offset, uint rsrcReader_length, bool owns_rsrcReader_factory) {
rvaConverter = peImage ?? throw new ArgumentNullException(nameof(peImage));
dataReader_factory = peImage.DataReaderFactory;
dataReader_offset = 0;
dataReader_length = dataReader_factory.Length;
if (rsrcReader_factory is not null) {
this.rsrcReader_factory = rsrcReader_factory;
this.rsrcReader_offset = rsrcReader_offset;
this.rsrcReader_length = rsrcReader_length;
this.owns_rsrcReader_factory = owns_rsrcReader_factory;
}
else {
var dataDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[2];
if (dataDir.VirtualAddress != 0 && dataDir.Size != 0) {
var reader = peImage.CreateReader(dataDir.VirtualAddress, dataDir.Size);
this.rsrcReader_factory = peImage.DataReaderFactory;
this.rsrcReader_offset = reader.StartOffset;
this.rsrcReader_length = reader.Length;
}
else {
this.rsrcReader_factory = ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null);
this.rsrcReader_offset = 0;
this.rsrcReader_length = 0;
}
}
Initialize();
}
void Initialize() {
root.ReadOriginalValue = () => {
var rsrcReader_factory = this.rsrcReader_factory;
if (rsrcReader_factory is null)
return null; // It's disposed
var reader = rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length);
return new ResourceDirectoryPE(0, new ResourceName("root"), this, ref reader);
};
#if THREAD_SAFE
root.Lock = theLock;
#endif
}
///
/// Creates a new data reader
///
/// RVA of data
/// Size of data
///
public DataReader CreateReader(RVA rva, uint size) {
GetDataReaderInfo(rva, size, out var dataReaderFactory, out uint dataOffset, out uint dataLength);
return dataReaderFactory.CreateReader(dataOffset, dataLength);
}
internal void GetDataReaderInfo(RVA rva, uint size, out DataReaderFactory dataReaderFactory, out uint dataOffset, out uint dataLength) {
dataOffset = (uint)rvaConverter.ToFileOffset(rva);
if ((ulong)dataOffset + size <= dataReader_factory.Length) {
dataReaderFactory = dataReader_factory;
dataLength = size;
return;
}
else {
dataReaderFactory = ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null);
dataOffset = 0;
dataLength = 0;
}
}
///
protected override void Dispose(bool disposing) {
if (!disposing)
return;
if (owns_dataReader_factory)
dataReader_factory?.Dispose();
if (owns_rsrcReader_factory)
rsrcReader_factory?.Dispose();
dataReader_factory = null;
rsrcReader_factory = null;
base.Dispose(disposing);
}
}
}