mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							294 lines
						
					
					
						
							7.6 KiB
						
					
					
				
			
		
		
	
	
							294 lines
						
					
					
						
							7.6 KiB
						
					
					
				#nullable enable | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Diagnostics; | 
						|
using System.Reflection.PortableExecutable; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.Util | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Represents win32 resources | 
						|
	/// </summary> | 
						|
	public static class Win32Resources | 
						|
	{ | 
						|
		/// <summary> | 
						|
		/// Reads win32 resource root directory | 
						|
		/// </summary> | 
						|
		/// <param name="pe"></param> | 
						|
		/// <returns></returns> | 
						|
		public static unsafe Win32ResourceDirectory? ReadWin32Resources(this PEReader pe) | 
						|
		{ | 
						|
			if (pe == null) | 
						|
			{ | 
						|
				throw new ArgumentNullException(nameof(pe)); | 
						|
			} | 
						|
 | 
						|
			int rva = pe.PEHeaders.PEHeader?.ResourceTableDirectory.RelativeVirtualAddress ?? 0; | 
						|
			if (rva == 0) | 
						|
				return null; | 
						|
			byte* pRoot = pe.GetSectionData(rva).Pointer; | 
						|
			return new Win32ResourceDirectory(pe, pRoot, 0, new Win32ResourceName("Root")); | 
						|
		} | 
						|
 | 
						|
		public static Win32ResourceDirectory? Find(this Win32ResourceDirectory root, Win32ResourceName type) | 
						|
		{ | 
						|
			if (root is null) | 
						|
				throw new ArgumentNullException(nameof(root)); | 
						|
			if (!root.Name.HasName || root.Name.Name != "Root") | 
						|
				throw new ArgumentOutOfRangeException(nameof(root)); | 
						|
			if (type is null) | 
						|
				throw new ArgumentNullException(nameof(type)); | 
						|
 | 
						|
			return root.FindDirectory(type); | 
						|
		} | 
						|
 | 
						|
		public static Win32ResourceDirectory? Find(this Win32ResourceDirectory root, Win32ResourceName type, Win32ResourceName name) | 
						|
		{ | 
						|
			if (root is null) | 
						|
				throw new ArgumentNullException(nameof(root)); | 
						|
			if (!root.Name.HasName || root.Name.Name != "Root") | 
						|
				throw new ArgumentOutOfRangeException(nameof(root)); | 
						|
			if (type is null) | 
						|
				throw new ArgumentNullException(nameof(type)); | 
						|
			if (name is null) | 
						|
				throw new ArgumentNullException(nameof(name)); | 
						|
 | 
						|
			return root.FindDirectory(type)?.FindDirectory(name); | 
						|
		} | 
						|
 | 
						|
		public static Win32ResourceData? Find(this Win32ResourceDirectory root, Win32ResourceName type, Win32ResourceName name, Win32ResourceName langId) | 
						|
		{ | 
						|
			if (root is null) | 
						|
				throw new ArgumentNullException(nameof(root)); | 
						|
			if (!root.Name.HasName || root.Name.Name != "Root") | 
						|
				throw new ArgumentOutOfRangeException(nameof(root)); | 
						|
			if (type is null) | 
						|
				throw new ArgumentNullException(nameof(type)); | 
						|
			if (name is null) | 
						|
				throw new ArgumentNullException(nameof(name)); | 
						|
			if (langId is null) | 
						|
				throw new ArgumentNullException(nameof(langId)); | 
						|
 | 
						|
			return root.FindDirectory(type)?.FindDirectory(name)?.FindData(langId); | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	[DebuggerDisplay("Directory: {Name}")] | 
						|
	public sealed class Win32ResourceDirectory | 
						|
	{ | 
						|
		#region Structure | 
						|
		public uint Characteristics { get; } | 
						|
		public uint TimeDateStamp { get; } | 
						|
		public ushort MajorVersion { get; } | 
						|
		public ushort MinorVersion { get; } | 
						|
		public ushort NumberOfNamedEntries { get; } | 
						|
		public ushort NumberOfIdEntries { get; } | 
						|
		#endregion | 
						|
 | 
						|
		public Win32ResourceName Name { get; } | 
						|
 | 
						|
		public IList<Win32ResourceDirectory> Directories { get; } | 
						|
 | 
						|
		public IList<Win32ResourceData> Datas { get; } | 
						|
 | 
						|
		internal unsafe Win32ResourceDirectory(PEReader pe, byte* pRoot, int offset, Win32ResourceName name) | 
						|
		{ | 
						|
			var p = (IMAGE_RESOURCE_DIRECTORY*)(pRoot + offset); | 
						|
			Characteristics = p->Characteristics; | 
						|
			TimeDateStamp = p->TimeDateStamp; | 
						|
			MajorVersion = p->MajorVersion; | 
						|
			MinorVersion = p->MinorVersion; | 
						|
			NumberOfNamedEntries = p->NumberOfNamedEntries; | 
						|
			NumberOfIdEntries = p->NumberOfIdEntries; | 
						|
 | 
						|
			Name = name; | 
						|
			Directories = new List<Win32ResourceDirectory>(); | 
						|
			Datas = new List<Win32ResourceData>(); | 
						|
			var pEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(p + 1); | 
						|
			int total = NumberOfNamedEntries + NumberOfIdEntries; | 
						|
			for (int i = 0; i < total; i++) | 
						|
			{ | 
						|
				var pEntry = pEntries + i; | 
						|
				name = new Win32ResourceName(pRoot, pEntry); | 
						|
				if ((pEntry->OffsetToData & 0x80000000) == 0) | 
						|
					Datas.Add(new Win32ResourceData(pe, pRoot, (int)pEntry->OffsetToData, name)); | 
						|
				else | 
						|
					Directories.Add(new Win32ResourceDirectory(pe, pRoot, (int)(pEntry->OffsetToData & 0x7FFFFFFF), name)); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		static unsafe string ReadString(byte* pRoot, int offset) | 
						|
		{ | 
						|
			var pString = (IMAGE_RESOURCE_DIRECTORY_STRING*)(pRoot + offset); | 
						|
			return new string(pString->NameString, 0, pString->Length); | 
						|
		} | 
						|
 | 
						|
		public Win32ResourceDirectory? FindDirectory(Win32ResourceName name) | 
						|
		{ | 
						|
			foreach (var directory in Directories) | 
						|
			{ | 
						|
				if (directory.Name == name) | 
						|
					return directory; | 
						|
			} | 
						|
			return null; | 
						|
		} | 
						|
 | 
						|
		public Win32ResourceData? FindData(Win32ResourceName name) | 
						|
		{ | 
						|
			foreach (var data in Datas) | 
						|
			{ | 
						|
				if (data.Name == name) | 
						|
					return data; | 
						|
			} | 
						|
			return null; | 
						|
		} | 
						|
 | 
						|
		public Win32ResourceDirectory? FirstDirectory() | 
						|
		{ | 
						|
			return Directories.Count != 0 ? Directories[0] : null; | 
						|
		} | 
						|
 | 
						|
		public Win32ResourceData? FirstData() | 
						|
		{ | 
						|
			return Datas.Count != 0 ? Datas[0] : null; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	[DebuggerDisplay("Data: {Name}")] | 
						|
	public sealed unsafe class Win32ResourceData | 
						|
	{ | 
						|
		#region Structure | 
						|
		public uint OffsetToData { get; } | 
						|
		public uint Size { get; } | 
						|
		public uint CodePage { get; } | 
						|
		public uint Reserved { get; } | 
						|
		#endregion | 
						|
 | 
						|
		private readonly void* _pointer; | 
						|
 | 
						|
		public Win32ResourceName Name { get; } | 
						|
 | 
						|
		public byte[] Data { | 
						|
			get { | 
						|
				byte[] data = new byte[Size]; | 
						|
				fixed (void* pData = data) | 
						|
					Buffer.MemoryCopy(_pointer, pData, Size, Size); | 
						|
				return data; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		internal Win32ResourceData(PEReader pe, byte* pRoot, int offset, Win32ResourceName name) | 
						|
		{ | 
						|
			var p = (IMAGE_RESOURCE_DATA_ENTRY*)(pRoot + offset); | 
						|
			OffsetToData = p->OffsetToData; | 
						|
			Size = p->Size; | 
						|
			CodePage = p->CodePage; | 
						|
			Reserved = p->Reserved; | 
						|
 | 
						|
			_pointer = pe.GetSectionData((int)OffsetToData).Pointer; | 
						|
			Name = name; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	public sealed class Win32ResourceName | 
						|
	{ | 
						|
		private readonly object _name; | 
						|
 | 
						|
		public bool HasName => _name is string; | 
						|
 | 
						|
		public bool HasId => _name is ushort; | 
						|
 | 
						|
		public string Name => (string)_name; | 
						|
 | 
						|
		public ushort Id => (ushort)_name; | 
						|
 | 
						|
		public Win32ResourceName(string name) | 
						|
		{ | 
						|
			_name = name ?? throw new ArgumentNullException(nameof(name)); | 
						|
		} | 
						|
 | 
						|
		public Win32ResourceName(int id) : this(checked((ushort)id)) | 
						|
		{ | 
						|
		} | 
						|
 | 
						|
		public Win32ResourceName(ushort id) | 
						|
		{ | 
						|
			_name = id; | 
						|
		} | 
						|
 | 
						|
		internal unsafe Win32ResourceName(byte* pRoot, IMAGE_RESOURCE_DIRECTORY_ENTRY* pEntry) | 
						|
		{ | 
						|
			_name = (pEntry->Name & 0x80000000) == 0 ? (object)(ushort)pEntry->Name : ReadString(pRoot, (int)(pEntry->Name & 0x7FFFFFFF)); | 
						|
 | 
						|
			static string ReadString(byte* pRoot, int offset) | 
						|
			{ | 
						|
				var pString = (IMAGE_RESOURCE_DIRECTORY_STRING*)(pRoot + offset); | 
						|
				return new string(pString->NameString, 0, pString->Length); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public static bool operator ==(Win32ResourceName x, Win32ResourceName y) | 
						|
		{ | 
						|
			if (x.HasName) | 
						|
			{ | 
						|
				return y.HasName ? string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase) == 0 : false; | 
						|
			} | 
						|
			else | 
						|
			{ | 
						|
				return y.HasId ? x.Id == y.Id : false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public static bool operator !=(Win32ResourceName x, Win32ResourceName y) | 
						|
		{ | 
						|
			return !(x == y); | 
						|
		} | 
						|
 | 
						|
		public override int GetHashCode() | 
						|
		{ | 
						|
			return _name.GetHashCode(); | 
						|
		} | 
						|
 | 
						|
		public override bool Equals(object? obj) | 
						|
		{ | 
						|
			if (!(obj is Win32ResourceName name)) | 
						|
				return false; | 
						|
			return this == name; | 
						|
		} | 
						|
 | 
						|
		public override string ToString() | 
						|
		{ | 
						|
			return HasName ? $"Name: {Name}" : $"Id: {Id}"; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	internal struct IMAGE_RESOURCE_DIRECTORY | 
						|
	{ | 
						|
		public uint Characteristics; | 
						|
		public uint TimeDateStamp; | 
						|
		public ushort MajorVersion; | 
						|
		public ushort MinorVersion; | 
						|
		public ushort NumberOfNamedEntries; | 
						|
		public ushort NumberOfIdEntries; | 
						|
	} | 
						|
 | 
						|
	internal struct IMAGE_RESOURCE_DIRECTORY_ENTRY | 
						|
	{ | 
						|
		public uint Name; | 
						|
		public uint OffsetToData; | 
						|
	} | 
						|
 | 
						|
	internal unsafe struct IMAGE_RESOURCE_DIRECTORY_STRING | 
						|
	{ | 
						|
		public ushort Length; | 
						|
		public fixed char NameString[1]; | 
						|
	} | 
						|
 | 
						|
	internal struct IMAGE_RESOURCE_DATA_ENTRY | 
						|
	{ | 
						|
		public uint OffsetToData; | 
						|
		public uint Size; | 
						|
		public uint CodePage; | 
						|
		public uint Reserved; | 
						|
	} | 
						|
}
 | 
						|
 |