diff --git a/Plugins/dnlib.dll b/Plugins/dnlib.dll
deleted file mode 100644
index ba1f36d..0000000
Binary files a/Plugins/dnlib.dll and /dev/null differ
diff --git a/Plugins/dnlib/DefaultDllImportSearchPathsAttribute.cs b/Plugins/dnlib/DefaultDllImportSearchPathsAttribute.cs
new file mode 100644
index 0000000..ad4b10d
--- /dev/null
+++ b/Plugins/dnlib/DefaultDllImportSearchPathsAttribute.cs
@@ -0,0 +1,23 @@
+// dnlib: See LICENSE.txt for more info
+
+#if NET35
+namespace System.Runtime.InteropServices {
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)]
+ sealed class DefaultDllImportSearchPathsAttribute : Attribute {
+ public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) => _paths = paths;
+ public DllImportSearchPath Paths => _paths;
+ internal DllImportSearchPath _paths;
+ }
+
+ [Flags]
+ enum DllImportSearchPath {
+ LegacyBehavior = 0,
+ AssemblyDirectory = 2,
+ UseDllDirectoryForDependencies = 0x100,
+ ApplicationDirectory = 0x200,
+ UserDirectories = 0x400,
+ System32 = 0x800,
+ SafeDirectories = 0x1000,
+ }
+}
+#endif
diff --git a/Plugins/dnlib/DotNet/AllTypesHelper.cs b/Plugins/dnlib/DotNet/AllTypesHelper.cs
new file mode 100644
index 0000000..2dcac1f
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AllTypesHelper.cs
@@ -0,0 +1,35 @@
+// dnlib: See LICENSE.txt for more info
+
+using System.Collections.Generic;
+
+namespace dnlib.DotNet {
+ ///
+ /// Returns types without getting stuck in an infinite loop
+ ///
+ readonly struct AllTypesHelper {
+ ///
+ /// Gets a list of all types and nested types
+ ///
+ /// A list of types
+ public static IEnumerable Types(IEnumerable types) {
+ var visited = new Dictionary();
+ var stack = new Stack>();
+ if (types is not null)
+ stack.Push(types.GetEnumerator());
+ while (stack.Count > 0) {
+ var enumerator = stack.Pop();
+ while (enumerator.MoveNext()) {
+ var type = enumerator.Current;
+ if (visited.ContainsKey(type))
+ continue;
+ visited[type] = true;
+ yield return type;
+ if (type.NestedTypes.Count > 0) {
+ stack.Push(enumerator);
+ enumerator = type.NestedTypes.GetEnumerator();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyAttributes.cs b/Plugins/dnlib/DotNet/AssemblyAttributes.cs
new file mode 100644
index 0000000..c4a687f
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyAttributes.cs
@@ -0,0 +1,58 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+
+namespace dnlib.DotNet {
+ ///
+ /// Assembly flags from Assembly.Flags column.
+ ///
+ /// See CorHdr.h/CorAssemblyFlags
+ [Flags]
+ public enum AssemblyAttributes : uint {
+ /// No flags set
+ None = 0,
+
+ /// The assembly ref holds the full (unhashed) public key.
+ PublicKey = 1,
+
+ /// Processor Architecture unspecified
+ PA_None = 0x0000,
+ /// Processor Architecture: neutral (PE32)
+ PA_MSIL = 0x0010,
+ /// Processor Architecture: x86 (PE32)
+ PA_x86 = 0x0020,
+ /// Processor Architecture: Itanium (PE32+)
+ PA_IA64 = 0x0030,
+ /// Processor Architecture: AMD X64 (PE32+)
+ PA_AMD64 = 0x0040,
+ /// Processor Architecture: ARM (PE32)
+ PA_ARM = 0x0050,
+ /// Processor Architecture: ARM64 (PE32+)
+ PA_ARM64 = 0x0060,
+ /// applies to any platform but cannot run on any (e.g. reference assembly), should not have "specified" set
+ PA_NoPlatform = 0x0070,
+ /// Propagate PA flags to AssemblyRef record
+ PA_Specified = 0x0080,
+ /// Bits describing the processor architecture
+ PA_Mask = 0x0070,
+ /// Bits describing the PA incl. Specified
+ PA_FullMask = 0x00F0,
+ /// NOT A FLAG, shift count in PA flags <--> index conversion
+ PA_Shift = 0x0004,
+
+ /// From "DebuggableAttribute".
+ EnableJITcompileTracking = 0x8000,
+ /// From "DebuggableAttribute".
+ DisableJITcompileOptimizer = 0x4000,
+
+ /// The assembly can be retargeted (at runtime) to an assembly from a different publisher.
+ Retargetable = 0x0100,
+
+ ///
+ ContentType_Default = 0x0000,
+ ///
+ ContentType_WindowsRuntime = 0x0200,
+ /// Bits describing ContentType
+ ContentType_Mask = 0x0E00,
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyDef.cs b/Plugins/dnlib/DotNet/AssemblyDef.cs
new file mode 100644
index 0000000..0e89652
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyDef.cs
@@ -0,0 +1,1050 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using dnlib.Utils;
+using dnlib.DotNet.MD;
+using dnlib.DotNet.Writer;
+using System.Text.RegularExpressions;
+using dnlib.DotNet.Pdb;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace dnlib.DotNet {
+ ///
+ /// A high-level representation of a row in the Assembly table
+ ///
+ public abstract class AssemblyDef : IHasCustomAttribute, IHasDeclSecurity, IHasCustomDebugInformation, IAssembly, IListListener, ITypeDefFinder, IDnlibDef {
+ ///
+ /// The row id in its table
+ ///
+ protected uint rid;
+
+ ///
+ public MDToken MDToken => new MDToken(Table.Assembly, rid);
+
+ ///
+ public uint Rid {
+ get => rid;
+ set => rid = value;
+ }
+
+ ///
+ public int HasCustomAttributeTag => 14;
+
+ ///
+ public int HasDeclSecurityTag => 2;
+
+ ///
+ /// From column Assembly.HashAlgId
+ ///
+ public AssemblyHashAlgorithm HashAlgorithm {
+ get => hashAlgorithm;
+ set => hashAlgorithm = value;
+ }
+ ///
+ protected AssemblyHashAlgorithm hashAlgorithm;
+
+ ///
+ /// From columns Assembly.MajorVersion, Assembly.MinorVersion, Assembly.BuildNumber,
+ /// Assembly.RevisionNumber.
+ ///
+ /// If is null
+ public Version Version {
+ get => version;
+ set => version = value ?? throw new ArgumentNullException(nameof(value));
+ }
+ ///
+ protected Version version;
+
+ ///
+ /// From column Assembly.Flags
+ ///
+ public AssemblyAttributes Attributes {
+ get => (AssemblyAttributes)attributes;
+ set => attributes = (int)value;
+ }
+ /// Attributes
+ protected int attributes;
+
+ ///
+ /// From column Assembly.PublicKey
+ ///
+ /// An empty is created if the caller writes null
+ public PublicKey PublicKey {
+ get => publicKey;
+ set => publicKey = value ?? new PublicKey();
+ }
+ ///
+ protected PublicKey publicKey;
+
+ ///
+ /// Gets the public key token which is calculated from
+ ///
+ public PublicKeyToken PublicKeyToken => publicKey.Token;
+
+ ///
+ /// From column Assembly.Name
+ ///
+ public UTF8String Name {
+ get => name;
+ set => name = value;
+ }
+ /// Name
+ protected UTF8String name;
+
+ ///
+ /// From column Assembly.Locale
+ ///
+ public UTF8String Culture {
+ get => culture;
+ set => culture = value;
+ }
+ /// Name
+ protected UTF8String culture;
+
+ ///
+ public IList DeclSecurities {
+ get {
+ if (declSecurities is null)
+ InitializeDeclSecurities();
+ return declSecurities;
+ }
+ }
+ ///
+ protected IList declSecurities;
+ /// Initializes
+ protected virtual void InitializeDeclSecurities() =>
+ Interlocked.CompareExchange(ref declSecurities, new List(), null);
+
+ ///
+ public PublicKeyBase PublicKeyOrToken => publicKey;
+
+ ///
+ public string FullName => GetFullNameWithPublicKeyToken();
+
+ ///
+ public string FullNameToken => GetFullNameWithPublicKeyToken();
+
+ ///
+ /// Gets all modules. The first module is always the .
+ ///
+ public IList Modules {
+ get {
+ if (modules is null)
+ InitializeModules();
+ return modules;
+ }
+ }
+ ///
+ protected LazyList modules;
+ /// Initializes
+ protected virtual void InitializeModules() =>
+ Interlocked.CompareExchange(ref modules, new LazyList(this), null);
+
+ ///
+ /// 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 => 14;
+
+ ///
+ public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
+
+ ///
+ /// 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 HasDeclSecurities => DeclSecurities.Count > 0;
+
+ ///
+ /// true if is not empty
+ ///
+ public bool HasModules => Modules.Count > 0;
+
+ ///
+ /// Gets the manifest (main) module. This is always the first module in .
+ /// null is returned if is empty.
+ ///
+ public ModuleDef ManifestModule => Modules.Count == 0 ? null : Modules[0];
+
+ ///
+ /// Modify property: =
+ /// ( & ) | .
+ ///
+ /// Value to AND
+ /// Value to OR
+ void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) =>
+ attributes = (attributes & (int)andMask) | (int)orMask;
+
+ ///
+ /// Set or clear flags in
+ ///
+ /// true if flags should be set, false if flags should
+ /// be cleared
+ /// Flags to set or clear
+ void ModifyAttributes(bool set, AssemblyAttributes flags) {
+ if (set)
+ attributes |= (int)flags;
+ else
+ attributes &= ~(int)flags;
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool HasPublicKey {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.PublicKey);
+ }
+
+ ///
+ /// Gets/sets the processor architecture
+ ///
+ public AssemblyAttributes ProcessorArchitecture {
+ get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask;
+ set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask);
+ }
+
+ ///
+ /// Gets/sets the processor architecture
+ ///
+ public AssemblyAttributes ProcessorArchitectureFull {
+ get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask;
+ set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask);
+ }
+
+ ///
+ /// true if unspecified processor architecture
+ ///
+ public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None;
+
+ ///
+ /// true if neutral (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL;
+
+ ///
+ /// true if x86 (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86;
+
+ ///
+ /// true if IA-64 (PE32+) architecture
+ ///
+ public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64;
+
+ ///
+ /// true if x64 (PE32+) architecture
+ ///
+ public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64;
+
+ ///
+ /// true if ARM (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM;
+
+ ///
+ /// true if eg. reference assembly (not runnable)
+ ///
+ public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform;
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool IsProcessorArchitectureSpecified {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.PA_Specified);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool EnableJITcompileTracking {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool DisableJITcompileOptimizer {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool IsRetargetable {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.Retargetable);
+ }
+
+ ///
+ /// Gets/sets the content type
+ ///
+ public AssemblyAttributes ContentType {
+ get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask;
+ set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask);
+ }
+
+ ///
+ /// true if content type is Default
+ ///
+ public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default;
+
+ ///
+ /// true if content type is WindowsRuntime
+ ///
+ public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime;
+
+ ///
+ /// Finds a module in this assembly
+ ///
+ /// Name of module
+ /// A instance or null if it wasn't found.
+ public ModuleDef FindModule(UTF8String name) {
+ var modules = Modules;
+ int count = modules.Count;
+ for (int i = 0; i < count; i++) {
+ var module = modules[i];
+ if (module is null)
+ continue;
+ if (UTF8String.CaseInsensitiveEquals(module.Name, name))
+ return module;
+ }
+ return null;
+ }
+
+ ///
+ /// Creates an instance from a file
+ ///
+ /// File name of an existing .NET assembly
+ /// Module context or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(string fileName, ModuleContext context) =>
+ Load(fileName, new ModuleCreationOptions(context));
+
+ ///
+ /// Creates an instance from a file
+ ///
+ /// File name of an existing .NET assembly
+ /// Module creation options or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(string fileName, ModuleCreationOptions options = null) {
+ if (fileName is null)
+ throw new ArgumentNullException(nameof(fileName));
+ ModuleDef module = null;
+ try {
+ module = ModuleDefMD.Load(fileName, options);
+ var asm = module.Assembly;
+ if (asm is null)
+ throw new BadImageFormatException($"{fileName} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
+ return asm;
+ }
+ catch {
+ if (module is not null)
+ module.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Creates an instance from a byte[]
+ ///
+ /// Contents of a .NET assembly
+ /// Module context or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(byte[] data, ModuleContext context) =>
+ Load(data, new ModuleCreationOptions(context));
+
+ ///
+ /// Creates an instance from a byte[]
+ ///
+ /// Contents of a .NET assembly
+ /// Module creation options or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(byte[] data, ModuleCreationOptions options = null) {
+ if (data is null)
+ throw new ArgumentNullException(nameof(data));
+ ModuleDef module = null;
+ try {
+ module = ModuleDefMD.Load(data, options);
+ var asm = module.Assembly;
+ if (asm is null)
+ throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
+ return asm;
+ }
+ catch {
+ if (module is not null)
+ module.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Creates an instance from a memory location
+ ///
+ /// Address of a .NET assembly
+ /// Module context or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(IntPtr addr, ModuleContext context) =>
+ Load(addr, new ModuleCreationOptions(context));
+
+ ///
+ /// Creates an instance from a memory location
+ ///
+ /// Address of a .NET assembly
+ /// Module creation options or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(IntPtr addr, ModuleCreationOptions options = null) {
+ if (addr == IntPtr.Zero)
+ throw new ArgumentNullException(nameof(addr));
+ ModuleDef module = null;
+ try {
+ module = ModuleDefMD.Load(addr, options);
+ var asm = module.Assembly;
+ if (asm is null)
+ throw new BadImageFormatException($"{module.ToString()} (addr: {addr.ToInt64():X8}) is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
+ return asm;
+ }
+ catch {
+ if (module is not null)
+ module.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Creates an instance from a stream
+ ///
+ /// This will read all bytes from the stream and call .
+ /// It's better to use one of the other Load() methods.
+ /// The stream
+ /// Module context or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(Stream stream, ModuleContext context) =>
+ Load(stream, new ModuleCreationOptions(context));
+
+ ///
+ /// Creates an instance from a stream
+ ///
+ /// This will read all bytes from the stream and call .
+ /// It's better to use one of the other Load() methods.
+ /// The stream
+ /// Module creation options or null
+ /// A new instance
+ /// If is null
+ /// If it's not a .NET assembly (eg. not a .NET file or only a .NET module)
+ public static AssemblyDef Load(Stream stream, ModuleCreationOptions options = null) {
+ if (stream is null)
+ throw new ArgumentNullException(nameof(stream));
+ ModuleDef module = null;
+ try {
+ module = ModuleDefMD.Load(stream, options);
+ var asm = module.Assembly;
+ if (asm is null)
+ throw new BadImageFormatException($"{module.ToString()} is only a .NET module, not a .NET assembly. Use ModuleDef.Load().");
+ return asm;
+ }
+ catch {
+ if (module is not null)
+ module.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Gets the assembly name with the public key
+ ///
+ public string GetFullNameWithPublicKey() => FullNameFactory.AssemblyFullName(this, false);
+
+ ///
+ /// Gets the assembly name with the public key token
+ ///
+ public string GetFullNameWithPublicKeyToken() => FullNameFactory.AssemblyFullName(this, true);
+
+ ///
+ /// Finds a . For speed, enable
+ /// if possible (read the documentation first).
+ ///
+ /// Full name of the type (no assembly information)
+ /// true if it's a reflection name, and nested
+ /// type names are separated by a + character. If false, nested type names
+ /// are separated by a / character.
+ /// An existing or null if it wasn't found.
+ public TypeDef Find(string fullName, bool isReflectionName) {
+ var modules = Modules;
+ int count = modules.Count;
+ for (int i = 0; i < count; i++) {
+ var module = modules[i];
+ if (module is null)
+ continue;
+ var type = module.Find(fullName, isReflectionName);
+ if (type is not null)
+ return type;
+ }
+ return null;
+ }
+
+ ///
+ /// Finds a . Its scope (i.e., module or assembly) is ignored when
+ /// looking up the type. For speed, enable
+ /// if possible (read the documentation first).
+ ///
+ /// The type ref
+ /// An existing or null if it wasn't found.
+ public TypeDef Find(TypeRef typeRef) {
+ var modules = Modules;
+ int count = modules.Count;
+ for (int i = 0; i < count; i++) {
+ var module = modules[i];
+ if (module is null)
+ continue;
+ var type = module.Find(typeRef);
+ if (type is not null)
+ return type;
+ }
+ return null;
+ }
+
+ ///
+ /// Writes the assembly to a file on disk. If the file exists, it will be truncated.
+ ///
+ /// Filename
+ /// Writer options
+ public void Write(string filename, ModuleWriterOptions options = null) =>
+ ManifestModule.Write(filename, options);
+
+ ///
+ /// Writes the assembly to a stream.
+ ///
+ /// Destination stream
+ /// Writer options
+ public void Write(Stream dest, ModuleWriterOptions options = null) =>
+ ManifestModule.Write(dest, options);
+
+ ///
+ /// Checks whether this assembly is a friend assembly of
+ ///
+ /// Target assembly
+ public bool IsFriendAssemblyOf(AssemblyDef targetAsm) {
+ if (targetAsm is null)
+ return false;
+ if (this == targetAsm)
+ return true;
+
+ // Both must be unsigned or both must be signed according to the
+ // InternalsVisibleToAttribute documentation.
+ if (PublicKeyBase.IsNullOrEmpty2(publicKey) != PublicKeyBase.IsNullOrEmpty2(targetAsm.PublicKey))
+ return false;
+
+ foreach (var ca in targetAsm.CustomAttributes.FindAll("System.Runtime.CompilerServices.InternalsVisibleToAttribute")) {
+ if (ca.ConstructorArguments.Count != 1)
+ continue;
+ var arg = ca.ConstructorArguments.Count == 0 ? default : ca.ConstructorArguments[0];
+ if (arg.Type.GetElementType() != ElementType.String)
+ continue;
+ var asmName = arg.Value as UTF8String;
+ if (UTF8String.IsNull(asmName))
+ continue;
+
+ var asmInfo = new AssemblyNameInfo(asmName);
+ if (asmInfo.Name != name)
+ continue;
+ if (!PublicKeyBase.IsNullOrEmpty2(publicKey)) {
+ if (!PublicKey.Equals(asmInfo.PublicKeyOrToken as PublicKey))
+ continue;
+ }
+ else if (!PublicKeyBase.IsNullOrEmpty2(asmInfo.PublicKeyOrToken))
+ continue;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Adds or updates an existing System.Reflection.AssemblySignatureKeyAttribute
+ /// attribute. This attribute is used in enhanced strong naming with key migration.
+ /// See http://msdn.microsoft.com/en-us/library/hh415055.aspx
+ ///
+ /// Identity public key
+ /// Identity strong name key pair
+ /// Signature public key
+ public void UpdateOrCreateAssemblySignatureKeyAttribute(StrongNamePublicKey identityPubKey, StrongNameKey identityKey, StrongNamePublicKey signaturePubKey) {
+ var manifestModule = ManifestModule;
+ if (manifestModule is null)
+ return;
+
+ // Remove all existing attributes
+ CustomAttribute ca = null;
+ for (int i = 0; i < CustomAttributes.Count; i++) {
+ var caTmp = CustomAttributes[i];
+ if (caTmp.TypeFullName != "System.Reflection.AssemblySignatureKeyAttribute")
+ continue;
+ CustomAttributes.RemoveAt(i);
+ i--;
+ if (ca is null)
+ ca = caTmp;
+ }
+
+ if (IsValidAssemblySignatureKeyAttribute(ca))
+ ca.NamedArguments.Clear();
+ else
+ ca = CreateAssemblySignatureKeyAttribute();
+
+ var counterSig = StrongNameKey.CreateCounterSignatureAsString(identityPubKey, identityKey, signaturePubKey);
+ ca.ConstructorArguments[0] = new CAArgument(manifestModule.CorLibTypes.String, new UTF8String(signaturePubKey.ToString()));
+ ca.ConstructorArguments[1] = new CAArgument(manifestModule.CorLibTypes.String, new UTF8String(counterSig));
+ CustomAttributes.Add(ca);
+ }
+
+ bool IsValidAssemblySignatureKeyAttribute(CustomAttribute ca) {
+ if (dnlib.Settings.IsThreadSafe)
+ return false;
+ if (ca is null)
+ return false;
+ var ctor = ca.Constructor;
+ if (ctor is null)
+ return false;
+ var sig = ctor.MethodSig;
+ if (sig is null || sig.Params.Count != 2)
+ return false;
+ if (sig.Params[0].GetElementType() != ElementType.String)
+ return false;
+ if (sig.Params[1].GetElementType() != ElementType.String)
+ return false;
+ if (ca.ConstructorArguments.Count != 2)
+ return false;
+ return true;
+ }
+
+ CustomAttribute CreateAssemblySignatureKeyAttribute() {
+ var manifestModule = ManifestModule;
+ var owner = manifestModule.UpdateRowId(new TypeRefUser(manifestModule, "System.Reflection", "AssemblySignatureKeyAttribute", manifestModule.CorLibTypes.AssemblyRef));
+ var methodSig = MethodSig.CreateInstance(manifestModule.CorLibTypes.Void, manifestModule.CorLibTypes.String, manifestModule.CorLibTypes.String);
+ var ctor = manifestModule.UpdateRowId(new MemberRefUser(manifestModule, MethodDef.InstanceConstructorName, methodSig, owner));
+ var ca = new CustomAttribute(ctor);
+ ca.ConstructorArguments.Add(new CAArgument(manifestModule.CorLibTypes.String, UTF8String.Empty));
+ ca.ConstructorArguments.Add(new CAArgument(manifestModule.CorLibTypes.String, UTF8String.Empty));
+ return ca;
+ }
+
+ ///
+ /// Gets the original System.Runtime.Versioning.TargetFrameworkAttribute custom attribute information if possible.
+ /// It reads this from the original metadata and doesn't use .
+ /// Returns false if the custom attribute isn't present or if it is invalid.
+ ///
+ /// Framework name
+ /// Version
+ /// Profile
+ ///
+ public virtual bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) {
+ framework = null;
+ version = null;
+ profile = null;
+ return false;
+ }
+
+ ///
+ void IListListener.OnLazyAdd(int index, ref ModuleDef module) {
+ if (module is null)
+ return;
+#if DEBUG
+ if (module.Assembly is null)
+ throw new InvalidOperationException("Module.Assembly is null");
+#endif
+ }
+
+ ///
+ void IListListener.OnAdd(int index, ModuleDef module) {
+ if (module is null)
+ return;
+ if (module.Assembly is not null)
+ throw new InvalidOperationException("Module already has an assembly. Remove it from that assembly before adding it to this assembly.");
+ module.Assembly = this;
+ }
+
+ ///
+ void IListListener.OnRemove(int index, ModuleDef module) {
+ if (module is not null)
+ module.Assembly = null;
+ }
+
+ ///
+ void IListListener.OnResize(int index) {
+ }
+
+ ///
+ void IListListener.OnClear() {
+ foreach (var module in modules.GetEnumerable_NoLock()) {
+ if (module is not null)
+ module.Assembly = null;
+ }
+ }
+
+ ///
+ public override string ToString() => FullName;
+ }
+
+ ///
+ /// An Assembly row created by the user and not present in the original .NET file
+ ///
+ public class AssemblyDefUser : AssemblyDef {
+ ///
+ /// Default constructor
+ ///
+ public AssemblyDefUser()
+ : this(UTF8String.Empty, new Version(0, 0, 0, 0)) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// If any of the args is invalid
+ public AssemblyDefUser(UTF8String name)
+ : this(name, new Version(0, 0, 0, 0), new PublicKey()) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// Version
+ /// If any of the args is invalid
+ public AssemblyDefUser(UTF8String name, Version version)
+ : this(name, version, new PublicKey()) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// Version
+ /// Public key
+ /// If any of the args is invalid
+ public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey)
+ : this(name, version, publicKey, UTF8String.Empty) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// Version
+ /// Public key
+ /// Locale
+ /// If any of the args is invalid
+ public AssemblyDefUser(UTF8String name, Version version, PublicKey publicKey, UTF8String locale) {
+ if (name is null)
+ throw new ArgumentNullException(nameof(name));
+ if (locale is null)
+ throw new ArgumentNullException(nameof(locale));
+ modules = new LazyList(this);
+ this.name = name;
+ this.version = version ?? throw new ArgumentNullException(nameof(version));
+ this.publicKey = publicKey ?? new PublicKey();
+ culture = locale;
+ attributes = (int)AssemblyAttributes.None;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Assembly name info
+ /// If is null
+ public AssemblyDefUser(AssemblyName asmName)
+ : this(new AssemblyNameInfo(asmName)) {
+ hashAlgorithm = (AssemblyHashAlgorithm)asmName.HashAlgorithm;
+ attributes = (int)asmName.Flags;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Assembly name info
+ /// If is null
+ public AssemblyDefUser(IAssembly asmName) {
+ if (asmName is null)
+ throw new ArgumentNullException(nameof(asmName));
+ modules = new LazyList(this);
+ name = asmName.Name;
+ version = asmName.Version ?? new Version(0, 0, 0, 0);
+ publicKey = asmName.PublicKeyOrToken as PublicKey ?? new PublicKey();
+ culture = asmName.Culture;
+ attributes = (int)AssemblyAttributes.None;
+ hashAlgorithm = AssemblyHashAlgorithm.SHA1;
+ }
+ }
+
+ ///
+ /// Created from a row in the Assembly table
+ ///
+ sealed class AssemblyDefMD : AssemblyDef, IMDTokenProviderMD {
+ /// The module where this instance is located
+ readonly ModuleDefMD readerModule;
+
+ readonly uint origRid;
+
+ ///
+ public uint OrigRid => origRid;
+
+ ///
+ protected override void InitializeDeclSecurities() {
+ var list = readerModule.Metadata.GetDeclSecurityRidList(Table.Assembly, origRid);
+ var tmp = new LazyList(list.Count, list, (list2, index) => readerModule.ResolveDeclSecurity(list2[index]));
+ Interlocked.CompareExchange(ref declSecurities, tmp, null);
+ }
+
+ ///
+ protected override void InitializeModules() {
+ var list = readerModule.GetModuleRidList();
+ var tmp = new LazyList(list.Count + 1, this, list, (list2, index) => {
+ ModuleDef module;
+ if (index == 0)
+ module = readerModule;
+ else
+ module = readerModule.ReadModule(list2[index - 1], this);
+ if (module is null)
+ module = new ModuleDefUser("INVALID", Guid.NewGuid());
+ module.Assembly = this;
+ return module;
+ });
+ Interlocked.CompareExchange(ref modules, tmp, null);
+ }
+
+ ///
+ protected override void InitializeCustomAttributes() {
+ var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, 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 override bool TryGetOriginalTargetFrameworkAttribute(out string framework, out Version version, out string profile) {
+ if (!hasInitdTFA)
+ InitializeTargetFrameworkAttribute();
+ framework = tfaFramework;
+ version = tfaVersion;
+ profile = tfaProfile;
+ return tfaReturnValue;
+ }
+ volatile bool hasInitdTFA;
+ string tfaFramework;
+ Version tfaVersion;
+ string tfaProfile;
+ bool tfaReturnValue;
+
+ void InitializeTargetFrameworkAttribute() {
+ if (hasInitdTFA)
+ return;
+
+ var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Assembly, origRid);
+ var gpContext = new GenericParamContext();
+ for (int i = 0; i < list.Count; i++) {
+ var caRid = list[i];
+ if (!readerModule.TablesStream.TryReadCustomAttributeRow(caRid, out var caRow))
+ continue;
+ var caType = readerModule.ResolveCustomAttributeType(caRow.Type, gpContext);
+ if (!TryGetName(caType, out var ns, out var name))
+ continue;
+ if (ns != nameSystemRuntimeVersioning || name != nameTargetFrameworkAttribute)
+ continue;
+ var ca = CustomAttributeReader.Read(readerModule, caType, caRow.Value, gpContext);
+ if (ca is null || ca.ConstructorArguments.Count != 1)
+ continue;
+ var s = ca.ConstructorArguments[0].Value as UTF8String;
+ if (s is null)
+ continue;
+ if (TryCreateTargetFrameworkInfo(s, out var tmpFramework, out var tmpVersion, out var tmpProfile)) {
+ tfaFramework = tmpFramework;
+ tfaVersion = tmpVersion;
+ tfaProfile = tmpProfile;
+ tfaReturnValue = true;
+ break;
+ }
+ }
+
+ hasInitdTFA = true;
+ }
+ static readonly UTF8String nameSystemRuntimeVersioning = new UTF8String("System.Runtime.Versioning");
+ static readonly UTF8String nameTargetFrameworkAttribute = new UTF8String("TargetFrameworkAttribute");
+
+ static bool TryGetName(ICustomAttributeType caType, out UTF8String ns, out UTF8String name) {
+ ITypeDefOrRef type;
+ if (caType is MemberRef mr)
+ type = mr.DeclaringType;
+ else
+ type = (caType as MethodDef)?.DeclaringType;
+ if (type is TypeRef tr) {
+ ns = tr.Namespace;
+ name = tr.Name;
+ return true;
+ }
+ if (type is TypeDef td) {
+ ns = td.Namespace;
+ name = td.Name;
+ return true;
+ }
+ ns = null;
+ name = null;
+ return false;
+ }
+
+ static bool TryCreateTargetFrameworkInfo(string attrString, out string framework, out Version version, out string profile) {
+ framework = null;
+ version = null;
+ profile = null;
+
+ // See corclr/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs
+ var values = attrString.Split(new char[] { ',' });
+ if (values.Length < 2 || values.Length > 3)
+ return false;
+ var frameworkRes = values[0].Trim();
+ if (frameworkRes.Length == 0)
+ return false;
+
+ Version versionRes = null;
+ string profileRes = null;
+ for (int i = 1; i < values.Length; i++) {
+ var kvp = values[i].Split('=');
+ if (kvp.Length != 2)
+ return false;
+
+ var key = kvp[0].Trim();
+ var value = kvp[1].Trim();
+
+ if (key.Equals("Version", StringComparison.OrdinalIgnoreCase)) {
+ if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
+ value = value.Substring(1);
+ if (!TryParse(value, out versionRes))
+ return false;
+ versionRes = new Version(versionRes.Major, versionRes.Minor, versionRes.Build == -1 ? 0 : versionRes.Build, 0);
+ }
+ else if (key.Equals("Profile", StringComparison.OrdinalIgnoreCase)) {
+ if (!string.IsNullOrEmpty(value))
+ profileRes = value;
+ }
+ }
+ if (versionRes is null)
+ return false;
+
+ framework = frameworkRes;
+ version = versionRes;
+ profile = profileRes;
+ return true;
+ }
+
+ static int ParseInt32(string s) => int.TryParse(s, out int res) ? res : 0;
+
+ static bool TryParse(string s, out Version version) {
+ Match m;
+
+ m = Regex.Match(s, @"^(\d+)\.(\d+)$");
+ if (m.Groups.Count == 3) {
+ version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value));
+ return true;
+ }
+
+ m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)$");
+ if (m.Groups.Count == 4) {
+ version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value));
+ return true;
+ }
+
+ m = Regex.Match(s, @"^(\d+)\.(\d+)\.(\d+)\.(\d+)$");
+ if (m.Groups.Count == 5) {
+ version = new Version(ParseInt32(m.Groups[1].Value), ParseInt32(m.Groups[2].Value), ParseInt32(m.Groups[3].Value), ParseInt32(m.Groups[4].Value));
+ return true;
+ }
+
+ version = null;
+ return false;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// The module which contains this Assembly row
+ /// Row ID
+ /// If is null
+ /// If is invalid
+ public AssemblyDefMD(ModuleDefMD readerModule, uint rid) {
+#if DEBUG
+ if (readerModule is null)
+ throw new ArgumentNullException("readerModule");
+ if (readerModule.TablesStream.AssemblyTable.IsInvalidRID(rid))
+ throw new BadImageFormatException($"Assembly rid {rid} does not exist");
+#endif
+ origRid = rid;
+ this.rid = rid;
+ this.readerModule = readerModule;
+ if (rid != 1)
+ modules = new LazyList(this);
+ bool b = readerModule.TablesStream.TryReadAssemblyRow(origRid, out var row);
+ Debug.Assert(b);
+ hashAlgorithm = (AssemblyHashAlgorithm)row.HashAlgId;
+ version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber);
+ attributes = (int)row.Flags;
+ name = readerModule.StringsStream.ReadNoNull(row.Name);
+ culture = readerModule.StringsStream.ReadNoNull(row.Locale);
+ publicKey = new PublicKey(readerModule.BlobStream.Read(row.PublicKey));
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyHash.cs b/Plugins/dnlib/DotNet/AssemblyHash.cs
new file mode 100644
index 0000000..22de5f3
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyHash.cs
@@ -0,0 +1,111 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.IO;
+using System.Security.Cryptography;
+
+namespace dnlib.DotNet {
+ ///
+ /// Hashes some data according to a
+ ///
+ readonly struct AssemblyHash : IDisposable {
+ readonly HashAlgorithm hasher;
+
+ ///
+ /// Constructor
+ ///
+ /// If is an unsupported hash algorithm, then
+ /// will be used as the hash algorithm.
+ /// The algorithm to use
+ public AssemblyHash(AssemblyHashAlgorithm hashAlgo) =>
+ hasher = hashAlgo switch {
+ AssemblyHashAlgorithm.MD5 => MD5.Create(),
+ AssemblyHashAlgorithm.SHA_256 => SHA256.Create(),
+ AssemblyHashAlgorithm.SHA_384 => SHA384.Create(),
+ AssemblyHashAlgorithm.SHA_512 => SHA512.Create(),
+ _ => SHA1.Create(),
+ };
+
+ ///
+ public void Dispose() {
+ if (hasher is not null)
+ ((IDisposable)hasher).Dispose();
+ }
+
+ ///
+ /// Hash data
+ ///
+ /// If is an unsupported hash algorithm, then
+ /// will be used as the hash algorithm.
+ /// The data
+ /// The algorithm to use
+ /// Hashed data or null if was null
+ public static byte[] Hash(byte[] data, AssemblyHashAlgorithm hashAlgo) {
+ if (data is null)
+ return null;
+
+ using (var asmHash = new AssemblyHash(hashAlgo)) {
+ asmHash.Hash(data);
+ return asmHash.ComputeHash();
+ }
+ }
+
+ ///
+ /// Hash data
+ ///
+ /// Data
+ public void Hash(byte[] data) => Hash(data, 0, data.Length);
+
+ ///
+ /// Hash data
+ ///
+ /// Data
+ /// Offset
+ /// Length
+ public void Hash(byte[] data, int offset, int length) {
+ if (hasher.TransformBlock(data, offset, length, data, offset) != length)
+ throw new IOException("Could not calculate hash");
+ }
+
+ ///
+ /// Hash stream data
+ ///
+ /// Stream
+ /// Number of bytes to hash
+ /// Temp buffer
+ public void Hash(Stream stream, uint length, byte[] buffer) {
+ while (length > 0) {
+ int len = length > (uint)buffer.Length ? buffer.Length : (int)length;
+ if (stream.Read(buffer, 0, len) != len)
+ throw new IOException("Could not read data");
+ Hash(buffer, 0, len);
+ length -= (uint)len;
+ }
+ }
+
+ ///
+ /// Computes the hash
+ ///
+ public byte[] ComputeHash() {
+ hasher.TransformFinalBlock(Array2.Empty(), 0, 0);
+ return hasher.Hash;
+ }
+
+ ///
+ /// Creates a public key token from the hash of some
+ ///
+ /// A public key is hashed, and the last 8 bytes of the hash, in reverse
+ /// order, is used as the public key token
+ /// The data
+ /// A new instance
+ public static PublicKeyToken CreatePublicKeyToken(byte[] publicKeyData) {
+ if (publicKeyData is null)
+ return new PublicKeyToken();
+ var hash = Hash(publicKeyData, AssemblyHashAlgorithm.SHA1);
+ var pkt = new byte[8];
+ for (int i = 0; i < pkt.Length && i < hash.Length; i++)
+ pkt[i] = hash[hash.Length - i - 1];
+ return new PublicKeyToken(pkt);
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyHashAlgorithm.cs b/Plugins/dnlib/DotNet/AssemblyHashAlgorithm.cs
new file mode 100644
index 0000000..8c5913c
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyHashAlgorithm.cs
@@ -0,0 +1,54 @@
+// dnlib: See LICENSE.txt for more info
+
+namespace dnlib.DotNet {
+ ///
+ /// Any ALG_CLASS_HASH type in WinCrypt.h can be used by Microsoft's CLI implementation
+ ///
+ public enum AssemblyHashAlgorithm : uint {
+ ///
+ None = 0,
+ ///
+ MD2 = 0x8001,
+ ///
+ MD4 = 0x8002,
+ /// This is a reserved value in the CLI
+ MD5 = 0x8003,
+ /// The only algorithm supported by the CLI
+ SHA1 = 0x8004,
+ ///
+ MAC = 0x8005,
+ ///
+ SSL3_SHAMD5 = 0x8008,
+ ///
+ HMAC = 0x8009,
+ ///
+ TLS1PRF = 0x800A,
+ ///
+ HASH_REPLACE_OWF = 0x800B,
+ ///
+ SHA_256 = 0x800C,
+ ///
+ SHA_384 = 0x800D,
+ ///
+ SHA_512 = 0x800E,
+ }
+
+ public static partial class Extensions {
+ internal static string GetName(this AssemblyHashAlgorithm hashAlg) =>
+ hashAlg switch {
+ AssemblyHashAlgorithm.MD2 => null,
+ AssemblyHashAlgorithm.MD4 => null,
+ AssemblyHashAlgorithm.MD5 => "MD5",
+ AssemblyHashAlgorithm.SHA1 => "SHA1",
+ AssemblyHashAlgorithm.MAC => null,
+ AssemblyHashAlgorithm.SSL3_SHAMD5 => null,
+ AssemblyHashAlgorithm.HMAC => null,
+ AssemblyHashAlgorithm.TLS1PRF => null,
+ AssemblyHashAlgorithm.HASH_REPLACE_OWF => null,
+ AssemblyHashAlgorithm.SHA_256 => "SHA256",
+ AssemblyHashAlgorithm.SHA_384 => "SHA384",
+ AssemblyHashAlgorithm.SHA_512 => "SHA512",
+ _ => null,
+ };
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyNameComparer.cs b/Plugins/dnlib/DotNet/AssemblyNameComparer.cs
new file mode 100644
index 0000000..4c4a078
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyNameComparer.cs
@@ -0,0 +1,259 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Collections.Generic;
+
+namespace dnlib.DotNet {
+ ///
+ /// Flags used by
+ ///
+ [Flags]
+ public enum AssemblyNameComparerFlags {
+ ///
+ /// Compare assembly simple name
+ ///
+ Name = 1,
+
+ ///
+ /// Compare assembly version
+ ///
+ Version = 2,
+
+ ///
+ /// Compare assembly public key token
+ ///
+ PublicKeyToken = 4,
+
+ ///
+ /// Compare assembly culture
+ ///
+ Culture = 8,
+
+ ///
+ /// Compare content type
+ ///
+ ContentType = 0x10,
+
+ ///
+ /// Compare assembly simple name, version, public key token, locale and content type
+ ///
+ All = Name | Version | PublicKeyToken | Culture | ContentType,
+ }
+
+ ///
+ /// Compares two assembly names
+ ///
+ public readonly struct AssemblyNameComparer : IEqualityComparer {
+ ///
+ /// Compares the name, version, public key token, culture and content type
+ ///
+ public static readonly AssemblyNameComparer CompareAll = new AssemblyNameComparer(AssemblyNameComparerFlags.All);
+
+ ///
+ /// Compares only the name and the public key token
+ ///
+ public static readonly AssemblyNameComparer NameAndPublicKeyTokenOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name | AssemblyNameComparerFlags.PublicKeyToken);
+
+ ///
+ /// Compares only the name
+ ///
+ public static readonly AssemblyNameComparer NameOnly = new AssemblyNameComparer(AssemblyNameComparerFlags.Name);
+
+ readonly AssemblyNameComparerFlags flags;
+
+ ///
+ /// Gets the bit
+ ///
+ public bool CompareName => (flags & AssemblyNameComparerFlags.Name) != 0;
+
+ ///
+ /// Gets the bit
+ ///
+ public bool CompareVersion => (flags & AssemblyNameComparerFlags.Version) != 0;
+
+ ///
+ /// Gets the bit
+ ///
+ public bool ComparePublicKeyToken => (flags & AssemblyNameComparerFlags.PublicKeyToken) != 0;
+
+ ///
+ /// Gets the bit
+ ///
+ public bool CompareCulture => (flags & AssemblyNameComparerFlags.Culture) != 0;
+
+ ///
+ /// Gets the bit
+ ///
+ public bool CompareContentType => (flags & AssemblyNameComparerFlags.ContentType) != 0;
+
+ ///
+ /// Constructor
+ ///
+ /// Comparison flags
+ public AssemblyNameComparer(AssemblyNameComparerFlags flags) => this.flags = flags;
+
+ ///
+ /// Compares two assembly names
+ ///
+ /// First
+ /// Second
+ /// < 0 if a < b, 0 if a == b, > 0 if a > b
+ public int CompareTo(IAssembly a, IAssembly b) {
+ if (a == b)
+ return 0;
+ if (a is null)
+ return -1;
+ if (b is null)
+ return 1;
+
+ int v;
+
+ if (CompareName && (v = UTF8String.CaseInsensitiveCompareTo(a.Name, b.Name)) != 0)
+ return v;
+ if (CompareVersion && (v = Utils.CompareTo(a.Version, b.Version)) != 0)
+ return v;
+ if (ComparePublicKeyToken && (v = PublicKeyBase.TokenCompareTo(a.PublicKeyOrToken, b.PublicKeyOrToken)) != 0)
+ return v;
+ if (CompareCulture && (v = Utils.LocaleCompareTo(a.Culture, b.Culture)) != 0)
+ return v;
+ if (CompareContentType && (v = a.ContentType.CompareTo(b.ContentType)) != 0)
+ return v;
+
+ return 0;
+ }
+
+ ///
+ /// Compares two assembly names
+ ///
+ /// First
+ /// Second
+ /// true if equal, false otherwise
+ public bool Equals(IAssembly a, IAssembly b) => CompareTo(a, b) == 0;
+
+ ///
+ /// Figures out which of two assembly names is closer to another assembly name
+ ///
+ /// Requested assembly name
+ /// First
+ /// Second
+ /// -1 if both are equally close, 0 if is closest, 1 if
+ /// is closest
+ public int CompareClosest(IAssembly requested, IAssembly a, IAssembly b) {
+ if (a == b)
+ return 0;
+ if (a is null)
+ return !CompareName ? 1 : UTF8String.CaseInsensitiveEquals(requested.Name, b.Name) ? 1 : 0;
+ if (b is null)
+ return !CompareName ? 0 : UTF8String.CaseInsensitiveEquals(requested.Name, a.Name) ? 0 : 1;
+
+ // Compare the most important parts first:
+ // 1. Assembly simple name
+ // 2. Public key token
+ // 3. Version
+ // 4. Locale
+ // 5. Content type
+
+ if (CompareName) {
+ // If the name only matches one of a or b, return that one.
+ bool na = UTF8String.CaseInsensitiveEquals(requested.Name, a.Name);
+ bool nb = UTF8String.CaseInsensitiveEquals(requested.Name, b.Name);
+ if (na && !nb)
+ return 0;
+ if (!na && nb)
+ return 1;
+ if (!na && !nb)
+ return -1;
+ }
+
+ if (ComparePublicKeyToken) {
+ bool pa, pb;
+ if (PublicKeyBase.IsNullOrEmpty2(requested.PublicKeyOrToken)) {
+ // If one of them has a pkt but the other one hasn't, return the one with
+ // no pkt.
+ pa = PublicKeyBase.IsNullOrEmpty2(a.PublicKeyOrToken);
+ pb = PublicKeyBase.IsNullOrEmpty2(b.PublicKeyOrToken);
+ }
+ else {
+ // If one of them has the correct pkt, but the other one has an incorrect
+ // pkt, return the one with the correct pkt.
+ pa = PublicKeyBase.TokenEquals(requested.PublicKeyOrToken, a.PublicKeyOrToken);
+ pb = PublicKeyBase.TokenEquals(requested.PublicKeyOrToken, b.PublicKeyOrToken);
+ }
+ if (pa && !pb)
+ return 0;
+ if (!pa && pb)
+ return 1;
+ }
+
+ if (CompareVersion && !Utils.Equals(a.Version, b.Version)) {
+ var rv = Utils.CreateVersionWithNoUndefinedValues(requested.Version);
+ if (rv == new Version(0, 0, 0, 0))
+ rv = new Version(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue);
+ int va = Utils.CompareTo(a.Version, rv);
+ int vb = Utils.CompareTo(b.Version, rv);
+ if (va == 0)
+ return 0; // vb != 0 so return 0
+ if (vb == 0)
+ return 1; // va != 0 so return 1
+ if (va > 0 && vb < 0)
+ return 0;
+ if (va < 0 && vb > 0)
+ return 1;
+ // Now either both a and b's version > req version or both are < req version
+ if (va > 0) {
+ // a.Version and b.Version > req.Version. Pick the one that is closest.
+ return Utils.CompareTo(a.Version, b.Version) < 0 ? 0 : 1;
+ }
+ else {
+ // a.Version and b.Version < req.Version. Pick the one that is closest.
+ return Utils.CompareTo(a.Version, b.Version) > 0 ? 0 : 1;
+ }
+ }
+
+ if (CompareCulture) {
+ bool la = Utils.LocaleEquals(requested.Culture, a.Culture);
+ bool lb = Utils.LocaleEquals(requested.Culture, b.Culture);
+ if (la && !lb)
+ return 0;
+ if (!la && lb)
+ return 1;
+ }
+
+ if (CompareContentType) {
+ bool ca = requested.ContentType == a.ContentType;
+ bool cb = requested.ContentType == b.ContentType;
+ if (ca && !cb)
+ return 0;
+ if (!ca && cb)
+ return 1;
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Gets the hash code of an assembly name
+ ///
+ /// Assembly name
+ /// The hash code
+ public int GetHashCode(IAssembly a) {
+ if (a is null)
+ return 0;
+
+ int hash = 0;
+
+ if (CompareName)
+ hash += UTF8String.GetHashCode(a.Name);
+ if (CompareVersion)
+ hash += Utils.CreateVersionWithNoUndefinedValues(a.Version).GetHashCode();
+ if (ComparePublicKeyToken)
+ hash += PublicKeyBase.GetHashCodeToken(a.PublicKeyOrToken);
+ if (CompareCulture)
+ hash += Utils.GetHashCodeLocale(a.Culture);
+ if (CompareContentType)
+ hash += (int)a.ContentType;
+
+ return hash;
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyNameInfo.cs b/Plugins/dnlib/DotNet/AssemblyNameInfo.cs
new file mode 100644
index 0000000..9e76d40
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyNameInfo.cs
@@ -0,0 +1,255 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Reflection;
+
+namespace dnlib.DotNet {
+ ///
+ /// Stores assembly name information
+ ///
+ public sealed class AssemblyNameInfo : IAssembly {
+ AssemblyHashAlgorithm hashAlgId;
+ Version version;
+ AssemblyAttributes flags;
+ PublicKeyBase publicKeyOrToken;
+ UTF8String name;
+ UTF8String culture;
+
+ ///
+ /// Gets/sets the
+ ///
+ public AssemblyHashAlgorithm HashAlgId {
+ get => hashAlgId;
+ set => hashAlgId = value;
+ }
+
+ ///
+ /// Gets/sets the or null if none specified
+ ///
+ public Version Version {
+ get => version;
+ set => version = value;
+ }
+
+ ///
+ /// Gets/sets the
+ ///
+ public AssemblyAttributes Attributes {
+ get => flags;
+ set => flags = value;
+ }
+
+ ///
+ /// Gets/sets the public key or token
+ ///
+ public PublicKeyBase PublicKeyOrToken {
+ get => publicKeyOrToken;
+ set => publicKeyOrToken = value;
+ }
+
+ ///
+ /// Gets/sets the name
+ ///
+ public UTF8String Name {
+ get => name;
+ set => name = value;
+ }
+
+ ///
+ /// Gets/sets the culture or null if none specified
+ ///
+ public UTF8String Culture {
+ get => culture;
+ set => culture = value;
+ }
+
+ ///
+ /// Gets the full name of the assembly
+ ///
+ public string FullName => FullNameToken;
+
+ ///
+ /// Gets the full name of the assembly but use a public key token
+ ///
+ public string FullNameToken => FullNameFactory.AssemblyFullName(this, true);
+
+ ///
+ /// Modify property: =
+ /// ( & ) | .
+ ///
+ /// Value to AND
+ /// Value to OR
+ void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) => Attributes = (Attributes & andMask) | orMask;
+
+ ///
+ /// Set or clear flags in
+ ///
+ /// true if flags should be set, false if flags should
+ /// be cleared
+ /// Flags to set or clear
+ void ModifyAttributes(bool set, AssemblyAttributes flags) {
+ if (set)
+ Attributes |= flags;
+ else
+ Attributes &= ~flags;
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool HasPublicKey {
+ get => (Attributes & AssemblyAttributes.PublicKey) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.PublicKey);
+ }
+
+ ///
+ /// Gets/sets the processor architecture
+ ///
+ public AssemblyAttributes ProcessorArchitecture {
+ get => Attributes & AssemblyAttributes.PA_Mask;
+ set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask);
+ }
+
+ ///
+ /// Gets/sets the processor architecture
+ ///
+ public AssemblyAttributes ProcessorArchitectureFull {
+ get => Attributes & AssemblyAttributes.PA_FullMask;
+ set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask);
+ }
+
+ ///
+ /// true if unspecified processor architecture
+ ///
+ public bool IsProcessorArchitectureNone => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None;
+
+ ///
+ /// true if neutral (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureMSIL => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL;
+
+ ///
+ /// true if x86 (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureX86 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86;
+
+ ///
+ /// true if IA-64 (PE32+) architecture
+ ///
+ public bool IsProcessorArchitectureIA64 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64;
+
+ ///
+ /// true if x64 (PE32+) architecture
+ ///
+ public bool IsProcessorArchitectureX64 => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64;
+
+ ///
+ /// true if ARM (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureARM => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM;
+
+ ///
+ /// true if eg. reference assembly (not runnable)
+ ///
+ public bool IsProcessorArchitectureNoPlatform => (Attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform;
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool IsProcessorArchitectureSpecified {
+ get => (Attributes & AssemblyAttributes.PA_Specified) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.PA_Specified);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool EnableJITcompileTracking {
+ get => (Attributes & AssemblyAttributes.EnableJITcompileTracking) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool DisableJITcompileOptimizer {
+ get => (Attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool IsRetargetable {
+ get => (Attributes & AssemblyAttributes.Retargetable) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.Retargetable);
+ }
+
+ ///
+ /// Gets/sets the content type
+ ///
+ public AssemblyAttributes ContentType {
+ get => Attributes & AssemblyAttributes.ContentType_Mask;
+ set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask);
+ }
+
+ ///
+ /// true if content type is Default
+ ///
+ public bool IsContentTypeDefault => (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default;
+
+ ///
+ /// true if content type is WindowsRuntime
+ ///
+ public bool IsContentTypeWindowsRuntime => (Attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime;
+
+ ///
+ /// Default constructor
+ ///
+ public AssemblyNameInfo() {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// An assembly name
+ public AssemblyNameInfo(string asmFullName)
+ : this(ReflectionTypeNameParser.ParseAssemblyRef(asmFullName)) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// The assembly
+ public AssemblyNameInfo(IAssembly asm) {
+ if (asm is null)
+ return;
+ var asmDef = asm as AssemblyDef;
+ hashAlgId = asmDef is null ? 0 : asmDef.HashAlgorithm;
+ version = asm.Version ?? new Version(0, 0, 0, 0);
+ flags = asm.Attributes;
+ publicKeyOrToken = asm.PublicKeyOrToken;
+ name = UTF8String.IsNullOrEmpty(asm.Name) ? UTF8String.Empty : asm.Name;
+ culture = UTF8String.IsNullOrEmpty(asm.Culture) ? UTF8String.Empty : asm.Culture;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Assembly name info
+ public AssemblyNameInfo(AssemblyName asmName) {
+ if (asmName is null)
+ return;
+ hashAlgId = (AssemblyHashAlgorithm)asmName.HashAlgorithm;
+ version = asmName.Version ?? new Version(0, 0, 0, 0);
+ flags = (AssemblyAttributes)asmName.Flags;
+ publicKeyOrToken = (PublicKeyBase)PublicKeyBase.CreatePublicKey(asmName.GetPublicKey()) ??
+ PublicKeyBase.CreatePublicKeyToken(asmName.GetPublicKeyToken());
+ name = asmName.Name ?? string.Empty;
+ culture = asmName.CultureInfo is not null && asmName.CultureInfo.Name is not null ? asmName.CultureInfo.Name : string.Empty;
+ }
+
+ ///
+ public override string ToString() => FullName;
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyRef.cs b/Plugins/dnlib/DotNet/AssemblyRef.cs
new file mode 100644
index 0000000..f025cff
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyRef.cs
@@ -0,0 +1,466 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using System.Threading;
+using dnlib.DotNet.MD;
+using dnlib.DotNet.Pdb;
+
+namespace dnlib.DotNet {
+ ///
+ /// A high-level representation of a row in the AssemblyRef table
+ ///
+ public abstract class AssemblyRef : IHasCustomAttribute, IImplementation, IResolutionScope, IHasCustomDebugInformation, IAssembly, IScope {
+ ///
+ /// An assembly ref that can be used to indicate that it references the current assembly
+ /// when the current assembly is not known (eg. a type string without any assembly info
+ /// when it references a type in the current assembly).
+ ///
+ public static readonly AssemblyRef CurrentAssembly = new AssemblyRefUser("<<>>");
+
+ ///
+ /// The row id in its table
+ ///
+ protected uint rid;
+
+ ///
+ public MDToken MDToken => new MDToken(Table.AssemblyRef, rid);
+
+ ///
+ public uint Rid {
+ get => rid;
+ set => rid = value;
+ }
+
+ ///
+ public int HasCustomAttributeTag => 15;
+
+ ///
+ public int ImplementationTag => 1;
+
+ ///
+ public int ResolutionScopeTag => 2;
+
+ ///
+ public ScopeType ScopeType => ScopeType.AssemblyRef;
+
+ ///
+ public string ScopeName => FullName;
+
+ ///
+ /// From columns AssemblyRef.MajorVersion, AssemblyRef.MinorVersion,
+ /// AssemblyRef.BuildNumber, AssemblyRef.RevisionNumber
+ ///
+ /// If is null
+ public Version Version {
+ get => version;
+ set => version = value ?? throw new ArgumentNullException(nameof(value));
+ }
+ ///
+ protected Version version;
+
+ ///
+ /// From column AssemblyRef.Flags
+ ///
+ public AssemblyAttributes Attributes {
+ get => (AssemblyAttributes)attributes;
+ set => attributes = (int)value;
+ }
+ /// Attributes
+ protected int attributes;
+
+ ///
+ /// From column AssemblyRef.PublicKeyOrToken
+ ///
+ /// If is null
+ public PublicKeyBase PublicKeyOrToken {
+ get => publicKeyOrToken;
+ set => publicKeyOrToken = value ?? throw new ArgumentNullException(nameof(value));
+ }
+ ///
+ protected PublicKeyBase publicKeyOrToken;
+
+ ///
+ /// From column AssemblyRef.Name
+ ///
+ public UTF8String Name {
+ get => name;
+ set => name = value;
+ }
+ /// Name
+ protected UTF8String name;
+
+ ///
+ /// From column AssemblyRef.Locale
+ ///
+ public UTF8String Culture {
+ get => culture;
+ set => culture = value;
+ }
+ /// Culture
+ protected UTF8String culture;
+
+ ///
+ /// From column AssemblyRef.HashValue
+ ///
+ public byte[] Hash {
+ get => hashValue;
+ set => hashValue = value;
+ }
+ ///
+ protected byte[] hashValue;
+
+ ///
+ /// 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 => 15;
+
+ ///
+ public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
+
+ ///
+ /// 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 string FullName => FullNameToken;
+
+ ///
+ /// Same as , except that it uses the PublicKey if available.
+ ///
+ public string RealFullName => FullNameFactory.AssemblyFullName(this, false);
+
+ ///
+ /// Gets the full name of the assembly but use a public key token
+ ///
+ public string FullNameToken => FullNameFactory.AssemblyFullName(this, true);
+
+ ///
+ /// Modify property: =
+ /// ( & ) | .
+ ///
+ /// Value to AND
+ /// Value to OR
+ void ModifyAttributes(AssemblyAttributes andMask, AssemblyAttributes orMask) =>
+ attributes = (attributes & (int)andMask) | (int)orMask;
+
+ ///
+ /// Set or clear flags in
+ ///
+ /// true if flags should be set, false if flags should
+ /// be cleared
+ /// Flags to set or clear
+ void ModifyAttributes(bool set, AssemblyAttributes flags) {
+ if (set)
+ attributes |= (int)flags;
+ else
+ attributes &= ~(int)flags;
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool HasPublicKey {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.PublicKey) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.PublicKey);
+ }
+
+ ///
+ /// Gets/sets the processor architecture
+ ///
+ public AssemblyAttributes ProcessorArchitecture {
+ get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask;
+ set => ModifyAttributes(~AssemblyAttributes.PA_Mask, value & AssemblyAttributes.PA_Mask);
+ }
+
+ ///
+ /// Gets/sets the processor architecture
+ ///
+ public AssemblyAttributes ProcessorArchitectureFull {
+ get => (AssemblyAttributes)attributes & AssemblyAttributes.PA_FullMask;
+ set => ModifyAttributes(~AssemblyAttributes.PA_FullMask, value & AssemblyAttributes.PA_FullMask);
+ }
+
+ ///
+ /// true if unspecified processor architecture
+ ///
+ public bool IsProcessorArchitectureNone => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_None;
+
+ ///
+ /// true if neutral (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureMSIL => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_MSIL;
+
+ ///
+ /// true if x86 (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureX86 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_x86;
+
+ ///
+ /// true if IA-64 (PE32+) architecture
+ ///
+ public bool IsProcessorArchitectureIA64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_IA64;
+
+ ///
+ /// true if x64 (PE32+) architecture
+ ///
+ public bool IsProcessorArchitectureX64 => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_AMD64;
+
+ ///
+ /// true if ARM (PE32) architecture
+ ///
+ public bool IsProcessorArchitectureARM => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_ARM;
+
+ ///
+ /// true if eg. reference assembly (not runnable)
+ ///
+ public bool IsProcessorArchitectureNoPlatform => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Mask) == AssemblyAttributes.PA_NoPlatform;
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool IsProcessorArchitectureSpecified {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.PA_Specified) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.PA_Specified);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool EnableJITcompileTracking {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.EnableJITcompileTracking) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.EnableJITcompileTracking);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool DisableJITcompileOptimizer {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.DisableJITcompileOptimizer) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.DisableJITcompileOptimizer);
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool IsRetargetable {
+ get => ((AssemblyAttributes)attributes & AssemblyAttributes.Retargetable) != 0;
+ set => ModifyAttributes(value, AssemblyAttributes.Retargetable);
+ }
+
+ ///
+ /// Gets/sets the content type
+ ///
+ public AssemblyAttributes ContentType {
+ get => (AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask;
+ set => ModifyAttributes(~AssemblyAttributes.ContentType_Mask, value & AssemblyAttributes.ContentType_Mask);
+ }
+
+ ///
+ /// true if content type is Default
+ ///
+ public bool IsContentTypeDefault => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_Default;
+
+ ///
+ /// true if content type is WindowsRuntime
+ ///
+ public bool IsContentTypeWindowsRuntime => ((AssemblyAttributes)attributes & AssemblyAttributes.ContentType_Mask) == AssemblyAttributes.ContentType_WindowsRuntime;
+
+ ///
+ public override string ToString() => FullName;
+ }
+
+ ///
+ /// An AssemblyRef row created by the user and not present in the original .NET file
+ ///
+ public class AssemblyRefUser : AssemblyRef {
+ ///
+ /// Creates a reference to CLR 1.0's mscorlib
+ ///
+ public static AssemblyRefUser CreateMscorlibReferenceCLR10() => new AssemblyRefUser("mscorlib", new Version(1, 0, 3300, 0), new PublicKeyToken("b77a5c561934e089"));
+
+ ///
+ /// Creates a reference to CLR 1.1's mscorlib
+ ///
+ public static AssemblyRefUser CreateMscorlibReferenceCLR11() => new AssemblyRefUser("mscorlib", new Version(1, 0, 5000, 0), new PublicKeyToken("b77a5c561934e089"));
+
+ ///
+ /// Creates a reference to CLR 2.0's mscorlib
+ ///
+ public static AssemblyRefUser CreateMscorlibReferenceCLR20() => new AssemblyRefUser("mscorlib", new Version(2, 0, 0, 0), new PublicKeyToken("b77a5c561934e089"));
+
+ ///
+ /// Creates a reference to CLR 4.0's mscorlib
+ ///
+ public static AssemblyRefUser CreateMscorlibReferenceCLR40() => new AssemblyRefUser("mscorlib", new Version(4, 0, 0, 0), new PublicKeyToken("b77a5c561934e089"));
+
+ ///
+ /// Default constructor
+ ///
+ public AssemblyRefUser()
+ : this(UTF8String.Empty) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// If any of the args is invalid
+ public AssemblyRefUser(UTF8String name)
+ : this(name, new Version(0, 0, 0, 0)) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// Version
+ /// If any of the args is invalid
+ public AssemblyRefUser(UTF8String name, Version version)
+ : this(name, version, new PublicKey()) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// Version
+ /// Public key or public key token
+ /// If any of the args is invalid
+ public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey)
+ : this(name, version, publicKey, UTF8String.Empty) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Simple name
+ /// Version
+ /// Public key or public key token
+ /// Locale
+ /// If any of the args is invalid
+ public AssemblyRefUser(UTF8String name, Version version, PublicKeyBase publicKey, UTF8String locale) {
+ if (name is null)
+ throw new ArgumentNullException(nameof(name));
+ if (locale is null)
+ throw new ArgumentNullException(nameof(locale));
+ this.name = name;
+ this.version = version ?? throw new ArgumentNullException(nameof(version));
+ publicKeyOrToken = publicKey;
+ culture = locale;
+ attributes = (int)(publicKey is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Assembly name info
+ /// If is null
+ public AssemblyRefUser(AssemblyName asmName)
+ : this(new AssemblyNameInfo(asmName)) => attributes = (int)asmName.Flags;
+
+ ///
+ /// Constructor
+ ///
+ /// Assembly
+ public AssemblyRefUser(IAssembly assembly) {
+ if (assembly is null)
+ throw new ArgumentNullException("asmName");
+
+ version = assembly.Version ?? new Version(0, 0, 0, 0);
+ publicKeyOrToken = assembly.PublicKeyOrToken;
+ name = UTF8String.IsNullOrEmpty(assembly.Name) ? UTF8String.Empty : assembly.Name;
+ culture = assembly.Culture;
+ attributes = (int)((publicKeyOrToken is PublicKey ? AssemblyAttributes.PublicKey : AssemblyAttributes.None) | assembly.ContentType);
+ }
+ }
+
+ ///
+ /// Created from a row in the AssemblyRef table
+ ///
+ sealed class AssemblyRefMD : AssemblyRef, 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.AssemblyRef, 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);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// The module which contains this AssemblyRef row
+ /// Row ID
+ /// If is null
+ /// If is invalid
+ public AssemblyRefMD(ModuleDefMD readerModule, uint rid) {
+#if DEBUG
+ if (readerModule is null)
+ throw new ArgumentNullException("readerModule");
+ if (readerModule.TablesStream.AssemblyRefTable.IsInvalidRID(rid))
+ throw new BadImageFormatException($"AssemblyRef rid {rid} does not exist");
+#endif
+ origRid = rid;
+ this.rid = rid;
+ this.readerModule = readerModule;
+ bool b = readerModule.TablesStream.TryReadAssemblyRefRow(origRid, out var row);
+ Debug.Assert(b);
+ version = new Version(row.MajorVersion, row.MinorVersion, row.BuildNumber, row.RevisionNumber);
+ attributes = (int)row.Flags;
+ var pkData = readerModule.BlobStream.Read(row.PublicKeyOrToken);
+ if ((attributes & (uint)AssemblyAttributes.PublicKey) != 0)
+ publicKeyOrToken = new PublicKey(pkData);
+ else
+ publicKeyOrToken = new PublicKeyToken(pkData);
+ name = readerModule.StringsStream.ReadNoNull(row.Name);
+ culture = readerModule.StringsStream.ReadNoNull(row.Locale);
+ hashValue = readerModule.BlobStream.Read(row.HashValue);
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/AssemblyResolver.cs b/Plugins/dnlib/DotNet/AssemblyResolver.cs
new file mode 100644
index 0000000..356da04
--- /dev/null
+++ b/Plugins/dnlib/DotNet/AssemblyResolver.cs
@@ -0,0 +1,832 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using dnlib.Threading;
+
+namespace dnlib.DotNet {
+ ///
+ /// Resolves assemblies
+ ///
+ public class AssemblyResolver : IAssemblyResolver {
+ static readonly ModuleDef nullModule = new ModuleDefUser();
+
+ // DLL files are searched before EXE files
+ static readonly string[] assemblyExtensions = new string[] { ".dll", ".exe" };
+ static readonly string[] winMDAssemblyExtensions = new string[] { ".winmd" };
+
+ static readonly List gacInfos;
+ static readonly string[] extraMonoPaths;
+ static readonly string[] monoVerDirs = new string[] {
+ // The "-api" dirs are reference assembly dirs.
+ "4.5", @"4.5\Facades", "4.5-api", @"4.5-api\Facades", "4.0", "4.0-api",
+ "3.5", "3.5-api", "3.0", "3.0-api", "2.0", "2.0-api",
+ "1.1", "1.0",
+ };
+
+ ModuleContext defaultModuleContext;
+ readonly Dictionary> moduleSearchPaths = new Dictionary>();
+ readonly Dictionary cachedAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ readonly List preSearchPaths = new List();
+ readonly List postSearchPaths = new List();
+ bool findExactMatch;
+ bool enableFrameworkRedirect;
+ bool enableTypeDefCache = true;
+ bool useGac = true;
+#if THREAD_SAFE
+ readonly Lock theLock = Lock.Create();
+#endif
+
+ sealed class GacInfo {
+ public readonly int Version;
+ public readonly string Path;
+ public readonly string Prefix;
+ public readonly string[] SubDirs;
+
+ public GacInfo(int version, string prefix, string path, string[] subDirs) {
+ Version = version;
+ Prefix = prefix;
+ Path = path;
+ SubDirs = subDirs;
+ }
+ }
+
+ static AssemblyResolver() {
+ gacInfos = new List();
+
+ if (Type.GetType("Mono.Runtime") is not null) {
+ var dirs = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var extraMonoPathsList = new List();
+ foreach (var prefix in FindMonoPrefixes()) {
+ var dir = Path.Combine(Path.Combine(Path.Combine(prefix, "lib"), "mono"), "gac");
+ if (dirs.ContainsKey(dir))
+ continue;
+ dirs[dir] = true;
+
+ if (Directory.Exists(dir)) {
+ gacInfos.Add(new GacInfo(-1, "", Path.GetDirectoryName(dir), new string[] {
+ Path.GetFileName(dir)
+ }));
+ }
+
+ dir = Path.GetDirectoryName(dir);
+ foreach (var verDir in monoVerDirs) {
+ var dir2 = dir;
+ foreach (var d in verDir.Split(new char[] { '\\' }))
+ dir2 = Path.Combine(dir2, d);
+ if (Directory.Exists(dir2))
+ extraMonoPathsList.Add(dir2);
+ }
+ }
+
+ var paths = Environment.GetEnvironmentVariable("MONO_PATH");
+ if (paths is not null) {
+ foreach (var tmp in paths.Split(Path.PathSeparator)) {
+ var path = tmp.Trim();
+ if (path != string.Empty && Directory.Exists(path))
+ extraMonoPathsList.Add(path);
+ }
+ }
+ extraMonoPaths = extraMonoPathsList.ToArray();
+ }
+ else {
+ var windir = Environment.GetEnvironmentVariable("WINDIR");
+ if (!string.IsNullOrEmpty(windir)) {
+ string path;
+
+ // .NET Framework 1.x and 2.x
+ path = Path.Combine(windir, "assembly");
+ if (Directory.Exists(path)) {
+ gacInfos.Add(new GacInfo(2, "", path, new string[] {
+ "GAC_32", "GAC_64", "GAC_MSIL", "GAC"
+ }));
+ }
+
+ // .NET Framework 4.x
+ path = Path.Combine(Path.Combine(windir, "Microsoft.NET"), "assembly");
+ if (Directory.Exists(path)) {
+ gacInfos.Add(new GacInfo(4, "v4.0_", path, new string[] {
+ "GAC_32", "GAC_64", "GAC_MSIL"
+ }));
+ }
+ }
+ }
+ }
+
+ static string GetCurrentMonoPrefix() {
+ var path = typeof(object).Module.FullyQualifiedName;
+ for (int i = 0; i < 4; i++)
+ path = Path.GetDirectoryName(path);
+ return path;
+ }
+
+ static IEnumerable FindMonoPrefixes() {
+ yield return GetCurrentMonoPrefix();
+
+ var prefixes = Environment.GetEnvironmentVariable("MONO_GAC_PREFIX");
+ if (!string.IsNullOrEmpty(prefixes)) {
+ foreach (var tmp in prefixes.Split(Path.PathSeparator)) {
+ var prefix = tmp.Trim();
+ if (prefix != string.Empty)
+ yield return prefix;
+ }
+ }
+ }
+
+ ///
+ /// Gets/sets the default
+ ///
+ public ModuleContext DefaultModuleContext {
+ get => defaultModuleContext;
+ set => defaultModuleContext = value;
+ }
+
+ ///
+ /// true if should find an assembly that matches exactly.
+ /// false if it first tries to match exactly, and if that fails, it picks an
+ /// assembly that is closest to the requested assembly.
+ ///
+ public bool FindExactMatch {
+ get => findExactMatch;
+ set => findExactMatch = value;
+ }
+
+ ///
+ /// true if resolved .NET framework assemblies can be redirected to the source
+ /// module's framework assembly version. Eg. if a resolved .NET Framework 3.5 assembly can be
+ /// redirected to a .NET Framework 4.0 assembly if the source module is a .NET Framework 4.0 assembly. This is
+ /// ignored if is true.
+ ///
+ public bool EnableFrameworkRedirect {
+ get => enableFrameworkRedirect;
+ set => enableFrameworkRedirect = value;
+ }
+
+ ///
+ /// If true, all modules in newly resolved assemblies will have their
+ /// property set to true. This is
+ /// enabled by default since these modules shouldn't be modified by the user.
+ ///
+ public bool EnableTypeDefCache {
+ get => enableTypeDefCache;
+ set => enableTypeDefCache = value;
+ }
+
+ ///
+ /// true to search the Global Assembly Cache. Default value is true.
+ ///
+ public bool UseGAC {
+ get => useGac;
+ set => useGac = value;
+ }
+
+ ///
+ /// Gets paths searched before trying the standard locations
+ ///
+ public IList PreSearchPaths => preSearchPaths;
+
+ ///
+ /// Gets paths searched after trying the standard locations
+ ///
+ public IList PostSearchPaths => postSearchPaths;
+
+ ///
+ /// Default constructor
+ ///
+ public AssemblyResolver()
+ : this(null) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Module context for all resolved assemblies
+ public AssemblyResolver(ModuleContext defaultModuleContext) {
+ this.defaultModuleContext = defaultModuleContext;
+ enableFrameworkRedirect = true;
+ }
+
+ ///
+ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) {
+ if (assembly is null)
+ return null;
+
+ if (EnableFrameworkRedirect && !FindExactMatch)
+ FrameworkRedirect.ApplyFrameworkRedirect(ref assembly, sourceModule);
+
+#if THREAD_SAFE
+ theLock.EnterWriteLock(); try {
+#endif
+ var resolvedAssembly = Resolve2(assembly, sourceModule);
+ if (resolvedAssembly is null) {
+ string asmName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
+ string asmNameTrimmed = asmName.Trim();
+ if (asmName != asmNameTrimmed) {
+ assembly = new AssemblyNameInfo {
+ Name = asmNameTrimmed,
+ Version = assembly.Version,
+ PublicKeyOrToken = assembly.PublicKeyOrToken,
+ Culture = assembly.Culture,
+ };
+ resolvedAssembly = Resolve2(assembly, sourceModule);
+ }
+ }
+
+ if (resolvedAssembly is null) {
+ // Make sure we don't search for this assembly again. This speeds up callers who
+ // keep asking for this assembly when trying to resolve many different TypeRefs
+ cachedAssemblies[GetAssemblyNameKey(assembly)] = null;
+ return null;
+ }
+
+ var key1 = GetAssemblyNameKey(resolvedAssembly);
+ var key2 = GetAssemblyNameKey(assembly);
+ cachedAssemblies.TryGetValue(key1, out var asm1);
+ cachedAssemblies.TryGetValue(key2, out var asm2);
+
+ if (asm1 != resolvedAssembly && asm2 != resolvedAssembly) {
+ // This assembly was just resolved
+ if (enableTypeDefCache) {
+ var modules = resolvedAssembly.Modules;
+ int count = modules.Count;
+ for (int i = 0; i < count; i++) {
+ var module = modules[i];
+ if (module is not null)
+ module.EnableTypeDefFindCache = true;
+ }
+ }
+ }
+
+ bool inserted = false;
+ if (!cachedAssemblies.ContainsKey(key1)) {
+ cachedAssemblies.Add(key1, resolvedAssembly);
+ inserted = true;
+ }
+ if (!cachedAssemblies.ContainsKey(key2)) {
+ cachedAssemblies.Add(key2, resolvedAssembly);
+ inserted = true;
+ }
+ if (inserted || asm1 == resolvedAssembly || asm2 == resolvedAssembly)
+ return resolvedAssembly;
+
+ // Dupe assembly. Don't insert it.
+ var dupeModule = resolvedAssembly.ManifestModule;
+ if (dupeModule is not null)
+ dupeModule.Dispose();
+ return asm1 ?? asm2;
+#if THREAD_SAFE
+ } finally { theLock.ExitWriteLock(); }
+#endif
+ }
+
+ ///
+ /// Add a module's assembly to the assembly cache
+ ///
+ /// The module whose assembly should be cached
+ /// true if 's assembly is cached, false
+ /// if it's not cached because some other assembly with the exact same full name has
+ /// already been cached or if or its assembly is null.
+ public bool AddToCache(ModuleDef module) => module is not null && AddToCache(module.Assembly);
+
+ ///
+ /// Add an assembly to the assembly cache
+ ///
+ /// The assembly
+ /// true if is cached, false if it's not
+ /// cached because some other assembly with the exact same full name has already been
+ /// cached or if is null.
+ public bool AddToCache(AssemblyDef asm) {
+ if (asm is null)
+ return false;
+ var asmKey = GetAssemblyNameKey(asm);
+#if THREAD_SAFE
+ theLock.EnterWriteLock(); try {
+#endif
+ if (cachedAssemblies.TryGetValue(asmKey, out var cachedAsm) && cachedAsm is not null)
+ return asm == cachedAsm;
+
+ if (enableTypeDefCache)
+ {
+ var modules = asm.Modules;
+ int count = modules.Count;
+ for (int i = 0; i < count; i++)
+ {
+ var module = modules[i];
+ if (module is not null)
+ module.EnableTypeDefFindCache = true;
+ }
+ }
+ cachedAssemblies[asmKey] = asm;
+ return true;
+#if THREAD_SAFE
+ } finally { theLock.ExitWriteLock(); }
+#endif
+ }
+
+ ///
+ /// Removes a module's assembly from the cache
+ ///
+ /// The module
+ /// true if its assembly was removed, false if it wasn't removed
+ /// since it wasn't in the cache, it has no assembly, or was
+ /// null
+ public bool Remove(ModuleDef module) => module is not null && Remove(module.Assembly);
+
+ ///
+ /// Removes the assembly from the cache
+ ///
+ /// The assembly
+ /// true if it was removed, false if it wasn't removed since it
+ /// wasn't in the cache or if was null
+ public bool Remove(AssemblyDef asm) {
+ if (asm is null)
+ return false;
+ var asmKey = GetAssemblyNameKey(asm);
+#if THREAD_SAFE
+ theLock.EnterWriteLock(); try {
+#endif
+ return cachedAssemblies.Remove(asmKey);
+#if THREAD_SAFE
+ } finally { theLock.ExitWriteLock(); }
+#endif
+ }
+
+ ///
+ /// Clears the cache and calls on each cached module.
+ /// Use to remove any assemblies you added yourself
+ /// using before calling this method if you don't want
+ /// them disposed.
+ ///
+ public void Clear() {
+ List asms;
+#if THREAD_SAFE
+ theLock.EnterWriteLock(); try {
+#endif
+ asms = new List(cachedAssemblies.Values);
+ cachedAssemblies.Clear();
+#if THREAD_SAFE
+ } finally { theLock.ExitWriteLock(); }
+#endif
+ foreach (var asm in asms) {
+ if (asm is null)
+ continue;
+ foreach (var mod in asm.Modules)
+ mod.Dispose();
+ }
+ }
+
+ ///
+ /// Gets the cached assemblies in this resolver.
+ ///
+ /// The cached assemblies.
+ public IEnumerable GetCachedAssemblies() {
+ AssemblyDef[] assemblies;
+#if THREAD_SAFE
+ theLock.EnterReadLock(); try {
+#endif
+ assemblies = cachedAssemblies.Values.ToArray();
+#if THREAD_SAFE
+ } finally { theLock.ExitReadLock(); }
+#endif
+ return assemblies;
+ }
+
+ static string GetAssemblyNameKey(IAssembly asmName) {
+ // Make sure the name contains PublicKeyToken= and not PublicKey=
+ return asmName.FullNameToken;
+ }
+
+ AssemblyDef Resolve2(IAssembly assembly, ModuleDef sourceModule) {
+ if (cachedAssemblies.TryGetValue(GetAssemblyNameKey(assembly), out var resolvedAssembly))
+ return resolvedAssembly;
+
+ var moduleContext = defaultModuleContext;
+ if (moduleContext is null && sourceModule is not null)
+ moduleContext = sourceModule.Context;
+
+ resolvedAssembly = FindExactAssembly(assembly, PreFindAssemblies(assembly, sourceModule, true), moduleContext) ??
+ FindExactAssembly(assembly, FindAssemblies(assembly, sourceModule, true), moduleContext) ??
+ FindExactAssembly(assembly, PostFindAssemblies(assembly, sourceModule, true), moduleContext);
+ if (resolvedAssembly is not null)
+ return resolvedAssembly;
+
+ if (!findExactMatch) {
+ resolvedAssembly = FindClosestAssembly(assembly);
+ resolvedAssembly = FindClosestAssembly(assembly, resolvedAssembly, PreFindAssemblies(assembly, sourceModule, false), moduleContext);
+ resolvedAssembly = FindClosestAssembly(assembly, resolvedAssembly, FindAssemblies(assembly, sourceModule, false), moduleContext);
+ resolvedAssembly = FindClosestAssembly(assembly, resolvedAssembly, PostFindAssemblies(assembly, sourceModule, false), moduleContext);
+ }
+
+ return resolvedAssembly;
+ }
+
+ ///
+ /// Finds an assembly that exactly matches the requested assembly
+ ///
+ /// Assembly to find
+ /// Search paths or null if none
+ /// Module context
+ /// An instance or null if an exact match
+ /// couldn't be found.
+ AssemblyDef FindExactAssembly(IAssembly assembly, IEnumerable paths, ModuleContext moduleContext) {
+ if (paths is null)
+ return null;
+ var asmComparer = AssemblyNameComparer.CompareAll;
+ foreach (var path in paths) {
+ ModuleDefMD mod = null;
+ try {
+ mod = ModuleDefMD.Load(path, moduleContext);
+ var asm = mod.Assembly;
+ if (asm is not null && asmComparer.Equals(assembly, asm)) {
+ mod = null;
+ return asm;
+ }
+ }
+ catch {
+ }
+ finally {
+ if (mod is not null)
+ mod.Dispose();
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Finds the closest assembly from the already cached assemblies
+ ///
+ /// Assembly to find
+ /// The closest or null if none found
+ AssemblyDef FindClosestAssembly(IAssembly assembly) {
+ AssemblyDef closest = null;
+ var asmComparer = AssemblyNameComparer.CompareAll;
+ foreach (var kv in cachedAssemblies) {
+ var asm = kv.Value;
+ if (asm is null)
+ continue;
+ if (asmComparer.CompareClosest(assembly, closest, asm) == 1)
+ closest = asm;
+ }
+ return closest;
+ }
+
+ AssemblyDef FindClosestAssembly(IAssembly assembly, AssemblyDef closest, IEnumerable paths, ModuleContext moduleContext) {
+ if (paths is null)
+ return closest;
+ var asmComparer = AssemblyNameComparer.CompareAll;
+ foreach (var path in paths) {
+ ModuleDefMD mod = null;
+ try {
+ mod = ModuleDefMD.Load(path, moduleContext);
+ var asm = mod.Assembly;
+ if (asm is not null && asmComparer.CompareClosest(assembly, closest, asm) == 1) {
+ if (!IsCached(closest) && closest is not null) {
+ var closeMod = closest.ManifestModule;
+ if (closeMod is not null)
+ closeMod.Dispose();
+ }
+ closest = asm;
+ mod = null;
+ }
+ }
+ catch {
+ }
+ finally {
+ if (mod is not null)
+ mod.Dispose();
+ }
+ }
+
+ return closest;
+ }
+
+ ///
+ /// Returns true if is inserted in
+ ///
+ /// Assembly to check
+ bool IsCached(AssemblyDef asm) {
+ if (asm is null)
+ return false;
+ return cachedAssemblies.TryGetValue(GetAssemblyNameKey(asm), out var cachedAsm) &&
+ cachedAsm == asm;
+ }
+
+ IEnumerable FindAssemblies2(IAssembly assembly, IEnumerable paths) {
+ if (paths is not null) {
+ var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
+ var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions;
+ foreach (var ext in exts) {
+ foreach (var path in paths) {
+ string fullPath;
+ try {
+ fullPath = Path.Combine(path, asmSimpleName + ext);
+ }
+ catch (ArgumentException) {
+ // Invalid path chars
+ yield break;
+ }
+ if (File.Exists(fullPath))
+ yield return fullPath;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Called before
+ ///
+ /// Assembly to find
+ /// The module that needs to resolve an assembly or null
+ /// We're trying to find an exact match
+ /// null or an enumerable of full paths to try
+ protected virtual IEnumerable PreFindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
+ foreach (var path in FindAssemblies2(assembly, preSearchPaths))
+ yield return path;
+ }
+
+ ///
+ /// Called after (if it fails)
+ ///
+ /// Assembly to find
+ /// The module that needs to resolve an assembly or null
+ /// We're trying to find an exact match
+ /// null or an enumerable of full paths to try
+ protected virtual IEnumerable PostFindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
+ foreach (var path in FindAssemblies2(assembly, postSearchPaths))
+ yield return path;
+ }
+
+ ///
+ /// Called after (if it fails)
+ ///
+ /// Assembly to find
+ /// The module that needs to resolve an assembly or null
+ /// We're trying to find an exact match
+ /// null or an enumerable of full paths to try
+ protected virtual IEnumerable FindAssemblies(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
+ if (assembly.IsContentTypeWindowsRuntime) {
+ string path;
+ try {
+ path = Path.Combine(Path.Combine(Environment.SystemDirectory, "WinMetadata"), assembly.Name + ".winmd");
+ }
+ catch (ArgumentException) {
+ // Invalid path chars
+ path = null;
+ }
+ if (File.Exists(path))
+ yield return path;
+ }
+ else {
+ if (UseGAC) {
+ foreach (var path in FindAssembliesGac(assembly, sourceModule, matchExactly))
+ yield return path;
+ }
+ }
+ foreach (var path in FindAssembliesModuleSearchPaths(assembly, sourceModule, matchExactly))
+ yield return path;
+ }
+
+ IEnumerable FindAssembliesGac(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
+ if (matchExactly)
+ return FindAssembliesGacExactly(assembly, sourceModule);
+ return FindAssembliesGacAny(assembly, sourceModule);
+ }
+
+ IEnumerable GetGacInfos(ModuleDef sourceModule) {
+ int version = sourceModule is null ? int.MinValue : sourceModule.IsClr40 ? 4 : 2;
+ // Try the correct GAC first (eg. GAC4 if it's a .NET Framework 4 assembly)
+ foreach (var gacInfo in gacInfos) {
+ if (gacInfo.Version == version)
+ yield return gacInfo;
+ }
+ foreach (var gacInfo in gacInfos) {
+ if (gacInfo.Version != version)
+ yield return gacInfo;
+ }
+ }
+
+ IEnumerable FindAssembliesGacExactly(IAssembly assembly, ModuleDef sourceModule) {
+ foreach (var gacInfo in GetGacInfos(sourceModule)) {
+ foreach (var path in FindAssembliesGacExactly(gacInfo, assembly, sourceModule))
+ yield return path;
+ }
+ if (extraMonoPaths is not null) {
+ foreach (var path in GetExtraMonoPaths(assembly, sourceModule))
+ yield return path;
+ }
+ }
+
+ static IEnumerable GetExtraMonoPaths(IAssembly assembly, ModuleDef sourceModule) {
+ if (extraMonoPaths is not null) {
+ foreach (var dir in extraMonoPaths) {
+ string file;
+ try {
+ file = Path.Combine(dir, assembly.Name + ".dll");
+ }
+ catch (ArgumentException) {
+ // Invalid path chars
+ break;
+ }
+ if (File.Exists(file))
+ yield return file;
+ }
+ }
+ }
+
+ IEnumerable FindAssembliesGacExactly(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) {
+ var pkt = PublicKeyBase.ToPublicKeyToken(assembly.PublicKeyOrToken);
+ if (gacInfo is not null && pkt is not null) {
+ string pktString = pkt.ToString();
+ string verString = Utils.CreateVersionWithNoUndefinedValues(assembly.Version).ToString();
+ var cultureString = UTF8String.ToSystemStringOrEmpty(assembly.Culture);
+ if (cultureString.Equals("neutral", StringComparison.OrdinalIgnoreCase))
+ cultureString = string.Empty;
+ var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
+ foreach (var subDir in gacInfo.SubDirs) {
+ var baseDir = Path.Combine(gacInfo.Path, subDir);
+ try {
+ baseDir = Path.Combine(baseDir, asmSimpleName);
+ }
+ catch (ArgumentException) {
+ // Invalid path chars
+ break;
+ }
+ baseDir = Path.Combine(baseDir, $"{gacInfo.Prefix}{verString}_{cultureString}_{pktString}");
+ var pathName = Path.Combine(baseDir, asmSimpleName + ".dll");
+ if (File.Exists(pathName))
+ yield return pathName;
+ }
+ }
+ }
+
+ IEnumerable FindAssembliesGacAny(IAssembly assembly, ModuleDef sourceModule) {
+ foreach (var gacInfo in GetGacInfos(sourceModule)) {
+ foreach (var path in FindAssembliesGacAny(gacInfo, assembly, sourceModule))
+ yield return path;
+ }
+ if (extraMonoPaths is not null) {
+ foreach (var path in GetExtraMonoPaths(assembly, sourceModule))
+ yield return path;
+ }
+ }
+
+ IEnumerable FindAssembliesGacAny(GacInfo gacInfo, IAssembly assembly, ModuleDef sourceModule) {
+ if (gacInfo is not null) {
+ var asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
+ foreach (var subDir in gacInfo.SubDirs) {
+ var baseDir = Path.Combine(gacInfo.Path, subDir);
+ try {
+ baseDir = Path.Combine(baseDir, asmSimpleName);
+ }
+ catch (ArgumentException) {
+ // Invalid path chars
+ break;
+ }
+ foreach (var dir in GetDirs(baseDir)) {
+ var pathName = Path.Combine(dir, asmSimpleName + ".dll");
+ if (File.Exists(pathName))
+ yield return pathName;
+ }
+ }
+ }
+ }
+
+ IEnumerable GetDirs(string baseDir) {
+ if (!Directory.Exists(baseDir))
+ return Array2.Empty();
+ var dirs = new List();
+ try {
+ foreach (var di in new DirectoryInfo(baseDir).GetDirectories())
+ dirs.Add(di.FullName);
+ }
+ catch {
+ }
+ return dirs;
+ }
+
+ IEnumerable FindAssembliesModuleSearchPaths(IAssembly assembly, ModuleDef sourceModule, bool matchExactly) {
+ string asmSimpleName = UTF8String.ToSystemStringOrEmpty(assembly.Name);
+ var searchPaths = GetSearchPaths(sourceModule);
+ var exts = assembly.IsContentTypeWindowsRuntime ? winMDAssemblyExtensions : assemblyExtensions;
+ foreach (var ext in exts) {
+ foreach (var path in searchPaths) {
+ for (int i = 0; i < 2; i++) {
+ string path2;
+ try {
+ if (i == 0)
+ path2 = Path.Combine(path, asmSimpleName + ext);
+ else
+ path2 = Path.Combine(Path.Combine(path, asmSimpleName), asmSimpleName + ext);
+ }
+ catch (ArgumentException) {
+ // Invalid path chars
+ yield break;
+ }
+ if (File.Exists(path2))
+ yield return path2;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets all search paths to use for this module
+ ///
+ /// The module or null if unknown
+ /// A list of all search paths to use for this module
+ IEnumerable GetSearchPaths(ModuleDef module) {
+ var keyModule = module;
+ if (keyModule is null)
+ keyModule = nullModule;
+ if (moduleSearchPaths.TryGetValue(keyModule, out var searchPaths))
+ return searchPaths;
+ moduleSearchPaths[keyModule] = searchPaths = new List(GetModuleSearchPaths(module));
+ return searchPaths;
+ }
+
+ ///
+ /// Gets all module search paths. This is usually empty unless its assembly has
+ /// a .config file specifying any additional private search paths in a
+ /// <probing/> element.
+ ///
+ /// The module or null if unknown
+ /// A list of search paths
+ protected virtual IEnumerable GetModuleSearchPaths(ModuleDef module) => GetModulePrivateSearchPaths(module);
+
+ ///
+ /// Gets all private assembly search paths as found in the module's .config file.
+ ///
+ /// The module or null if unknown
+ /// A list of search paths
+ protected IEnumerable GetModulePrivateSearchPaths(ModuleDef module) {
+ if (module is null)
+ return Array2.Empty();
+ var asm = module.Assembly;
+ if (asm is null)
+ return Array2.Empty();
+ module = asm.ManifestModule;
+ if (module is null)
+ return Array2.Empty(); // Should never happen
+
+ string baseDir = null;
+ try {
+ var imageName = module.Location;
+ if (imageName != string.Empty) {
+ var directoryInfo = Directory.GetParent(imageName);
+ if (directoryInfo is not null) {
+ baseDir = directoryInfo.FullName;
+ var configName = imageName + ".config";
+ if (File.Exists(configName))
+ return GetPrivatePaths(baseDir, configName);
+ }
+ }
+ }
+ catch {
+ }
+ if (baseDir is not null)
+ return new List { baseDir };
+ return Array2.Empty();
+ }
+
+ IEnumerable GetPrivatePaths(string baseDir, string configFileName) {
+ var searchPaths = new List();
+
+ try {
+ var dirName = Path.GetDirectoryName(Path.GetFullPath(configFileName));
+ searchPaths.Add(dirName);
+
+ using (var xmlStream = new FileStream(configFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
+ var doc = new XmlDocument();
+ doc.Load(XmlReader.Create(xmlStream));
+ foreach (var tmp in doc.GetElementsByTagName("probing")) {
+ var probingElem = tmp as XmlElement;
+ if (probingElem is null)
+ continue;
+ var privatePath = probingElem.GetAttribute("privatePath");
+ if (string.IsNullOrEmpty(privatePath))
+ continue;
+ foreach (var tmp2 in privatePath.Split(';')) {
+ var path = tmp2.Trim();
+ if (path == "")
+ continue;
+ var newPath = Path.GetFullPath(Path.Combine(dirName, path.Replace('\\', Path.DirectorySeparatorChar)));
+ if (Directory.Exists(newPath) && newPath.StartsWith(baseDir + Path.DirectorySeparatorChar))
+ searchPaths.Add(newPath);
+ }
+ }
+ }
+ }
+ catch (ArgumentException) {
+ }
+ catch (IOException) {
+ }
+ catch (XmlException) {
+ }
+
+ return searchPaths;
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/CallingConvention.cs b/Plugins/dnlib/DotNet/CallingConvention.cs
new file mode 100644
index 0000000..da2d655
--- /dev/null
+++ b/Plugins/dnlib/DotNet/CallingConvention.cs
@@ -0,0 +1,48 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+
+namespace dnlib.DotNet {
+ ///
+ /// See CorHdr.h/CorCallingConvention
+ ///
+ [Flags]
+ public enum CallingConvention : byte {
+ /// The managed calling convention
+ Default = 0x0,
+ ///
+ C = 0x1,
+ ///
+ StdCall = 0x2,
+ ///
+ ThisCall = 0x3,
+ ///
+ FastCall = 0x4,
+ ///
+ VarArg = 0x5,
+ ///
+ Field = 0x6,
+ ///
+ LocalSig = 0x7,
+ ///
+ Property = 0x8,
+ /// Unmanaged calling convention encoded as modopts
+ Unmanaged = 0x9,
+ /// generic method instantiation
+ GenericInst = 0xA,
+ /// used ONLY for 64bit vararg PInvoke calls
+ NativeVarArg = 0xB,
+
+ /// Calling convention is bottom 4 bits
+ Mask = 0x0F,
+
+ /// Generic method
+ Generic = 0x10,
+ /// Method needs a 'this' parameter
+ HasThis = 0x20,
+ /// 'this' parameter is the first arg if set (else it's hidden)
+ ExplicitThis = 0x40,
+ /// Used internally by the CLR
+ ReservedByCLR = 0x80,
+ }
+}
diff --git a/Plugins/dnlib/DotNet/CallingConventionSig.cs b/Plugins/dnlib/DotNet/CallingConventionSig.cs
new file mode 100644
index 0000000..98c182d
--- /dev/null
+++ b/Plugins/dnlib/DotNet/CallingConventionSig.cs
@@ -0,0 +1,1018 @@
+// dnlib: See LICENSE.txt for more info
+
+using System.Collections.Generic;
+
+/*
+All signature classes:
+
+CallingConventionSig
+ FieldSig
+ MethodBaseSig
+ MethodSig
+ PropertySig
+ LocalSig
+ GenericInstMethodSig
+*/
+
+namespace dnlib.DotNet {
+ ///
+ /// Base class for sigs with a calling convention
+ ///
+ public abstract class CallingConventionSig : IContainsGenericParameter {
+ ///
+ /// The calling convention
+ ///
+ protected CallingConvention callingConvention;
+
+ byte[] extraData;
+
+ ///
+ /// Gets/sets the extra data found after the signature
+ ///
+ public byte[] ExtraData {
+ get => extraData;
+ set => extraData = value;
+ }
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsDefault => (callingConvention & CallingConvention.Mask) == CallingConvention.Default;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsC => (callingConvention & CallingConvention.Mask) == CallingConvention.C;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsStdCall => (callingConvention & CallingConvention.Mask) == CallingConvention.StdCall;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsThisCall => (callingConvention & CallingConvention.Mask) == CallingConvention.ThisCall;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsFastCall => (callingConvention & CallingConvention.Mask) == CallingConvention.FastCall;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsVarArg => (callingConvention & CallingConvention.Mask) == CallingConvention.VarArg;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsField => (callingConvention & CallingConvention.Mask) == CallingConvention.Field;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsLocalSig => (callingConvention & CallingConvention.Mask) == CallingConvention.LocalSig;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsProperty => (callingConvention & CallingConvention.Mask) == CallingConvention.Property;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsUnmanaged => (callingConvention & CallingConvention.Mask) == CallingConvention.Unmanaged;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsGenericInst => (callingConvention & CallingConvention.Mask) == CallingConvention.GenericInst;
+
+ ///
+ /// Returns true if is set
+ ///
+ public bool IsNativeVarArg => (callingConvention & CallingConvention.Mask) == CallingConvention.NativeVarArg;
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool Generic {
+ get => (callingConvention & CallingConvention.Generic) != 0;
+ set {
+ if (value)
+ callingConvention |= CallingConvention.Generic;
+ else
+ callingConvention &= ~CallingConvention.Generic;
+ }
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool HasThis {
+ get => (callingConvention & CallingConvention.HasThis) != 0;
+ set {
+ if (value)
+ callingConvention |= CallingConvention.HasThis;
+ else
+ callingConvention &= ~CallingConvention.HasThis;
+ }
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool ExplicitThis {
+ get => (callingConvention & CallingConvention.ExplicitThis) != 0;
+ set {
+ if (value)
+ callingConvention |= CallingConvention.ExplicitThis;
+ else
+ callingConvention &= ~CallingConvention.ExplicitThis;
+ }
+ }
+
+ ///
+ /// Gets/sets the bit
+ ///
+ public bool ReservedByCLR {
+ get => (callingConvention & CallingConvention.ReservedByCLR) != 0;
+ set {
+ if (value)
+ callingConvention |= CallingConvention.ReservedByCLR;
+ else
+ callingConvention &= ~CallingConvention.ReservedByCLR;
+ }
+ }
+
+ ///
+ /// true if there's an implicit this parameter
+ ///
+ public bool ImplicitThis => HasThis && !ExplicitThis;
+
+ ///
+ /// true if this contains a
+ /// or a .
+ ///
+ public bool ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this);
+
+ ///
+ /// Default constructor
+ ///
+ protected CallingConventionSig() {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// The calling convention
+ protected CallingConventionSig(CallingConvention callingConvention) => this.callingConvention = callingConvention;
+
+ ///
+ /// Gets the calling convention
+ ///
+ public CallingConvention GetCallingConvention() => callingConvention;
+ }
+
+ ///
+ /// A field signature
+ ///
+ public sealed class FieldSig : CallingConventionSig {
+ TypeSig type;
+
+ ///
+ /// Gets/sets the field type
+ ///
+ public TypeSig Type {
+ get => type;
+ set => type = value;
+ }
+
+ ///
+ /// Default constructor
+ ///
+ public FieldSig() => callingConvention = CallingConvention.Field;
+
+ ///
+ /// Constructor
+ ///
+ /// Field type
+ public FieldSig(TypeSig type) {
+ callingConvention = CallingConvention.Field;
+ this.type = type;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Field type
+ /// The calling convention (must have Field set)
+ internal FieldSig(CallingConvention callingConvention, TypeSig type) {
+ this.callingConvention = callingConvention;
+ this.type = type;
+ }
+
+ ///
+ /// Clone this
+ ///
+ public FieldSig Clone() => new FieldSig(callingConvention, type);
+
+ ///
+ public override string ToString() => FullNameFactory.FullName(type, false, null, null, null, null);
+ }
+
+ ///
+ /// Method sig base class
+ ///
+ public abstract class MethodBaseSig : CallingConventionSig {
+ ///
+ protected TypeSig retType;
+ ///
+ protected IList parameters;
+ ///
+ protected uint genParamCount;
+ ///
+ protected IList paramsAfterSentinel;
+
+ ///
+ /// Gets/sets the calling convention
+ ///
+ public CallingConvention CallingConvention {
+ get => callingConvention;
+ set => callingConvention = value;
+ }
+
+ ///
+ /// Gets/sets the return type
+ ///
+ public TypeSig RetType {
+ get => retType;
+ set => retType = value;
+ }
+
+ ///
+ /// Gets the parameters. This is never null
+ ///
+ public IList Params => parameters;
+
+ ///
+ /// Gets/sets the generic param count
+ ///
+ public uint GenParamCount {
+ get => genParamCount;
+ set => genParamCount = value;
+ }
+
+ ///
+ /// Gets the parameters that are present after the sentinel. Note that this is null
+ /// if there's no sentinel. It can still be empty even if it's not null.
+ ///
+ public IList ParamsAfterSentinel {
+ get => paramsAfterSentinel;
+ set => paramsAfterSentinel = value;
+ }
+ }
+
+ ///
+ /// A method signature
+ ///
+ public sealed class MethodSig : MethodBaseSig {
+ uint origToken;
+
+ ///
+ /// Gets/sets the original token. It's set when reading calli instruction operands
+ /// and it's a hint to the module writer if it tries to re-use the same token.
+ ///
+ public uint OriginalToken {
+ get => origToken;
+ set => origToken = value;
+ }
+
+ ///
+ /// Creates a static MethodSig
+ ///
+ /// Return type
+ public static MethodSig CreateStatic(TypeSig retType) => new MethodSig(CallingConvention.Default, 0, retType);
+
+ ///
+ /// Creates a static MethodSig
+ ///
+ /// Return type
+ /// Arg type #1
+ public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default, 0, retType, argType1);
+
+ ///
+ /// Creates a static MethodSig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default, 0, retType, argType1, argType2);
+
+ ///
+ /// Creates a static MethodSig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public static MethodSig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default, 0, retType, argType1, argType2, argType3);
+
+ ///
+ /// Creates a static MethodSig
+ ///
+ /// Return type
+ /// Argument types
+ public static MethodSig CreateStatic(TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default, 0, retType, argTypes);
+
+ ///
+ /// Creates an instance MethodSig
+ ///
+ /// Return type
+ public static MethodSig CreateInstance(TypeSig retType) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType);
+
+ ///
+ /// Creates an instance MethodSig
+ ///
+ /// Return type
+ /// Arg type #1
+ public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1);
+
+ ///
+ /// Creates an instance MethodSig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1, argType2);
+
+ ///
+ /// Creates an instance MethodSig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public static MethodSig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argType1, argType2, argType3);
+
+ ///
+ /// Creates an instance MethodSig
+ ///
+ /// Return type
+ /// Argument types
+ public static MethodSig CreateInstance(TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis, 0, retType, argTypes);
+
+ ///
+ /// Creates a static generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType);
+
+ ///
+ /// Creates a static generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1);
+
+ ///
+ /// Creates a static generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1, argType2);
+
+ ///
+ /// Creates a static generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argType1, argType2, argType3);
+
+ ///
+ /// Creates a static generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Argument types
+ public static MethodSig CreateStaticGeneric(uint genParamCount, TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default | CallingConvention.Generic, genParamCount, retType, argTypes);
+
+ ///
+ /// Creates an instance generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType);
+
+ ///
+ /// Creates an instance generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1);
+
+ ///
+ /// Creates an instance generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1, argType2);
+
+ ///
+ /// Creates an instance generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argType1, argType2, argType3);
+
+ ///
+ /// Creates an instance generic MethodSig
+ ///
+ /// Number of generic parameters
+ /// Return type
+ /// Argument types
+ public static MethodSig CreateInstanceGeneric(uint genParamCount, TypeSig retType, params TypeSig[] argTypes) => new MethodSig(CallingConvention.Default | CallingConvention.HasThis | CallingConvention.Generic, genParamCount, retType, argTypes);
+
+ ///
+ /// Default constructor
+ ///
+ public MethodSig() => parameters = new List();
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ public MethodSig(CallingConvention callingConvention) {
+ this.callingConvention = callingConvention;
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ public MethodSig(CallingConvention callingConvention, uint genParamCount) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, TypeSig argType1) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List { argType1 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List { argType1, argType2 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List { argType1, argType2, argType3 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Argument types
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, params TypeSig[] argTypes) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List(argTypes);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Argument types
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, IList argTypes) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List(argTypes);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Argument types
+ /// Parameters after sentinel
+ public MethodSig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, IList argTypes, IList paramsAfterSentinel) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List(argTypes);
+ this.paramsAfterSentinel = paramsAfterSentinel is null ? null : new List(paramsAfterSentinel);
+ }
+
+ ///
+ /// Clone this
+ ///
+ public MethodSig Clone() => new MethodSig(callingConvention, genParamCount, retType, parameters, paramsAfterSentinel);
+
+ ///
+ public override string ToString() => FullNameFactory.MethodBaseSigFullName(this, null);
+ }
+
+ ///
+ /// A property signature
+ ///
+ public sealed class PropertySig : MethodBaseSig {
+ ///
+ /// Creates a static PropertySig
+ ///
+ /// Return type
+ public static PropertySig CreateStatic(TypeSig retType) => new PropertySig(false, retType);
+
+ ///
+ /// Creates a static PropertySig
+ ///
+ /// Return type
+ /// Arg type #1
+ public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1) => new PropertySig(false, retType, argType1);
+
+ ///
+ /// Creates a static PropertySig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2) => new PropertySig(false, retType, argType1, argType2);
+
+ ///
+ /// Creates a static PropertySig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public static PropertySig CreateStatic(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new PropertySig(false, retType, argType1, argType2, argType3);
+
+ ///
+ /// Creates a static PropertySig
+ ///
+ /// Return type
+ /// Argument types
+ public static PropertySig CreateStatic(TypeSig retType, params TypeSig[] argTypes) => new PropertySig(false, retType, argTypes);
+
+ ///
+ /// Creates an instance PropertySig
+ ///
+ /// Return type
+ public static PropertySig CreateInstance(TypeSig retType) => new PropertySig(true, retType);
+
+ ///
+ /// Creates an instance PropertySig
+ ///
+ /// Return type
+ /// Arg type #1
+ public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1) => new PropertySig(true, retType, argType1);
+
+ ///
+ /// Creates an instance PropertySig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2) => new PropertySig(true, retType, argType1, argType2);
+
+ ///
+ /// Creates an instance PropertySig
+ ///
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public static PropertySig CreateInstance(TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) => new PropertySig(true, retType, argType1, argType2, argType3);
+
+ ///
+ /// Creates an instance PropertySig
+ ///
+ /// Return type
+ /// Argument types
+ public static PropertySig CreateInstance(TypeSig retType, params TypeSig[] argTypes) => new PropertySig(true, retType, argTypes);
+
+ ///
+ /// Default constructor
+ ///
+ public PropertySig() {
+ callingConvention = CallingConvention.Property;
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention (must have Property set)
+ internal PropertySig(CallingConvention callingConvention) {
+ this.callingConvention = callingConvention;
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if instance, false if static
+ public PropertySig(bool hasThis) {
+ callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0);
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if instance, false if static
+ /// Return type
+ public PropertySig(bool hasThis, TypeSig retType) {
+ callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0);
+ this.retType = retType;
+ parameters = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if instance, false if static
+ /// Return type
+ /// Arg type #1
+ public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1) {
+ callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0);
+ this.retType = retType;
+ parameters = new List { argType1 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if instance, false if static
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1, TypeSig argType2) {
+ callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0);
+ this.retType = retType;
+ parameters = new List { argType1, argType2 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if instance, false if static
+ /// Return type
+ /// Arg type #1
+ /// Arg type #2
+ /// Arg type #3
+ public PropertySig(bool hasThis, TypeSig retType, TypeSig argType1, TypeSig argType2, TypeSig argType3) {
+ callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0);
+ this.retType = retType;
+ parameters = new List { argType1, argType2, argType3 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if instance, false if static
+ /// Return type
+ /// Argument types
+ public PropertySig(bool hasThis, TypeSig retType, params TypeSig[] argTypes) {
+ callingConvention = CallingConvention.Property | (hasThis ? CallingConvention.HasThis : 0);
+ this.retType = retType;
+ parameters = new List(argTypes);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention
+ /// Number of generic parameters
+ /// Return type
+ /// Argument types
+ /// Parameters after sentinel
+ internal PropertySig(CallingConvention callingConvention, uint genParamCount, TypeSig retType, IList argTypes, IList paramsAfterSentinel) {
+ this.callingConvention = callingConvention;
+ this.genParamCount = genParamCount;
+ this.retType = retType;
+ parameters = new List(argTypes);
+ this.paramsAfterSentinel = paramsAfterSentinel is null ? null : new List(paramsAfterSentinel);
+ }
+
+ ///
+ /// Clone this
+ ///
+ public PropertySig Clone() => new PropertySig(callingConvention, genParamCount, retType, parameters, paramsAfterSentinel);
+
+ ///
+ public override string ToString() => FullNameFactory.MethodBaseSigFullName(this, null);
+ }
+
+ ///
+ /// A local variables signature
+ ///
+ public sealed class LocalSig : CallingConventionSig {
+ readonly IList locals;
+
+ ///
+ /// All local types. This is never null.
+ ///
+ public IList Locals => locals;
+
+ ///
+ /// Default constructor
+ ///
+ public LocalSig() {
+ callingConvention = CallingConvention.LocalSig;
+ locals = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention (must have LocalSig set)
+ /// Number of locals
+ internal LocalSig(CallingConvention callingConvention, uint count) {
+ this.callingConvention = callingConvention;
+ locals = new List((int)count);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Local type #1
+ public LocalSig(TypeSig local1) {
+ callingConvention = CallingConvention.LocalSig;
+ locals = new List { local1 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Local type #1
+ /// Local type #2
+ public LocalSig(TypeSig local1, TypeSig local2) {
+ callingConvention = CallingConvention.LocalSig;
+ locals = new List { local1, local2 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Local type #1
+ /// Local type #2
+ /// Local type #3
+ public LocalSig(TypeSig local1, TypeSig local2, TypeSig local3) {
+ callingConvention = CallingConvention.LocalSig;
+ locals = new List { local1, local2, local3 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// All locals
+ public LocalSig(params TypeSig[] locals) {
+ callingConvention = CallingConvention.LocalSig;
+ this.locals = new List(locals);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// All locals
+ public LocalSig(IList locals) {
+ callingConvention = CallingConvention.LocalSig;
+ this.locals = new List(locals);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// All locals (this instance now owns it)
+ /// Dummy
+ internal LocalSig(IList locals, bool dummy) {
+ callingConvention = CallingConvention.LocalSig;
+ this.locals = locals;
+ }
+
+ ///
+ /// Clone this
+ ///
+ public LocalSig Clone() => new LocalSig(locals);
+ }
+
+ ///
+ /// An instantiated generic method signature
+ ///
+ public sealed class GenericInstMethodSig : CallingConventionSig {
+ readonly IList genericArgs;
+
+ ///
+ /// Gets the generic arguments (must be instantiated types, i.e., closed types)
+ ///
+ public IList GenericArguments => genericArgs;
+
+ ///
+ /// Default constructor
+ ///
+ public GenericInstMethodSig() {
+ callingConvention = CallingConvention.GenericInst;
+ genericArgs = new List();
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Calling convention (must have GenericInst set)
+ /// Number of generic args
+ internal GenericInstMethodSig(CallingConvention callingConvention, uint size) {
+ this.callingConvention = callingConvention;
+ genericArgs = new List((int)size);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Generic arg #1
+ public GenericInstMethodSig(TypeSig arg1) {
+ callingConvention = CallingConvention.GenericInst;
+ genericArgs = new List { arg1 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Generic arg #1
+ /// Generic arg #2
+ public GenericInstMethodSig(TypeSig arg1, TypeSig arg2) {
+ callingConvention = CallingConvention.GenericInst;
+ genericArgs = new List { arg1, arg2 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Generic arg #1
+ /// Generic arg #2
+ /// Generic arg #3
+ public GenericInstMethodSig(TypeSig arg1, TypeSig arg2, TypeSig arg3) {
+ callingConvention = CallingConvention.GenericInst;
+ genericArgs = new List { arg1, arg2, arg3 };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Generic args
+ public GenericInstMethodSig(params TypeSig[] args) {
+ callingConvention = CallingConvention.GenericInst;
+ genericArgs = new List(args);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Generic args
+ public GenericInstMethodSig(IList args) {
+ callingConvention = CallingConvention.GenericInst;
+ genericArgs = new List(args);
+ }
+
+ ///
+ /// Clone this
+ ///
+ public GenericInstMethodSig Clone() => new GenericInstMethodSig(genericArgs);
+ }
+
+ public static partial class Extensions {
+ ///
+ /// Gets the field type
+ ///
+ /// this
+ /// Field type or null if none
+ public static TypeSig GetFieldType(this FieldSig sig) => sig?.Type;
+
+ ///
+ /// Gets the return type
+ ///
+ /// this
+ /// Return type or null if none
+ public static TypeSig GetRetType(this MethodBaseSig sig) => sig?.RetType;
+
+ ///
+ /// Gets the parameters
+ ///
+ /// this
+ /// The parameters
+ public static IList GetParams(this MethodBaseSig sig) => sig?.Params ?? new List();
+
+ ///
+ /// Gets the parameter count
+ ///
+ /// this
+ /// Parameter count
+ public static int GetParamCount(this MethodBaseSig sig) => sig?.Params.Count ?? 0;
+
+ ///
+ /// Gets the generic parameter count
+ ///
+ /// this
+ /// Generic parameter count
+ public static uint GetGenParamCount(this MethodBaseSig sig) => sig?.GenParamCount ?? 0;
+
+ ///
+ /// Gets the parameters after the sentinel
+ ///
+ /// this
+ /// Parameters after sentinel or null if none
+ public static IList GetParamsAfterSentinel(this MethodBaseSig sig) => sig?.ParamsAfterSentinel;
+
+ ///
+ /// Gets the locals
+ ///
+ /// this
+ /// All locals
+ public static IList GetLocals(this LocalSig sig) => sig?.Locals ?? new List();
+
+ ///
+ /// Gets the generic arguments
+ ///
+ /// this
+ /// All generic arguments
+ public static IList GetGenericArguments(this GenericInstMethodSig sig) => sig?.GenericArguments ?? new List();
+
+ ///
+ /// Gets the property
+ ///
+ /// this
+ /// The type's property or
+ /// false if input isnull
+ public static bool GetIsDefault(this CallingConventionSig sig) => sig?.IsDefault ?? false;
+ }
+}
diff --git a/Plugins/dnlib/DotNet/ClassLayout.cs b/Plugins/dnlib/DotNet/ClassLayout.cs
new file mode 100644
index 0000000..e11c698
--- /dev/null
+++ b/Plugins/dnlib/DotNet/ClassLayout.cs
@@ -0,0 +1,99 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Diagnostics;
+using dnlib.DotNet.MD;
+
+namespace dnlib.DotNet {
+ ///
+ /// A high-level representation of a row in the ClassLayout table
+ ///
+ public abstract class ClassLayout : IMDTokenProvider {
+ ///
+ /// The row id in its table
+ ///
+ protected uint rid;
+
+ ///
+ public MDToken MDToken => new MDToken(Table.ClassLayout, rid);
+
+ ///
+ public uint Rid {
+ get => rid;
+ set => rid = value;
+ }
+
+ ///
+ /// From column ClassLayout.PackingSize
+ ///
+ public ushort PackingSize {
+ get => packingSize;
+ set => packingSize = value;
+ }
+ ///
+ protected ushort packingSize;
+
+ ///
+ /// From column ClassLayout.ClassSize
+ ///
+ public uint ClassSize {
+ get => classSize;
+ set => classSize = value;
+ }
+ ///
+ protected uint classSize;
+ }
+
+ ///
+ /// A ClassLayout row created by the user and not present in the original .NET file
+ ///
+ public class ClassLayoutUser : ClassLayout {
+ ///
+ /// Default constructor
+ ///
+ public ClassLayoutUser() {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// PackingSize
+ /// ClassSize
+ public ClassLayoutUser(ushort packingSize, uint classSize) {
+ this.packingSize = packingSize;
+ this.classSize = classSize;
+ }
+ }
+
+ ///
+ /// Created from a row in the ClassLayout table
+ ///
+ sealed class ClassLayoutMD : ClassLayout, IMDTokenProviderMD {
+ readonly uint origRid;
+
+ ///
+ public uint OrigRid => origRid;
+
+ ///
+ /// Constructor
+ ///
+ /// The module which contains this ClassLayout row
+ /// Row ID
+ /// If is null
+ /// If is invalid
+ public ClassLayoutMD(ModuleDefMD readerModule, uint rid) {
+#if DEBUG
+ if (readerModule is null)
+ throw new ArgumentNullException("readerModule");
+ if (readerModule.TablesStream.ClassLayoutTable.IsInvalidRID(rid))
+ throw new BadImageFormatException($"ClassLayout rid {rid} does not exist");
+#endif
+ origRid = rid;
+ this.rid = rid;
+ bool b = readerModule.TablesStream.TryReadClassLayoutRow(origRid, out var row);
+ Debug.Assert(b);
+ classSize = row.ClassSize;
+ packingSize = row.PackingSize;
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/Constant.cs b/Plugins/dnlib/DotNet/Constant.cs
new file mode 100644
index 0000000..9d10006
--- /dev/null
+++ b/Plugins/dnlib/DotNet/Constant.cs
@@ -0,0 +1,204 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Diagnostics;
+using dnlib.DotNet.MD;
+using dnlib.IO;
+
+namespace dnlib.DotNet {
+ ///
+ /// A high-level representation of a row in the Constant table
+ ///
+ public abstract class Constant : IMDTokenProvider {
+ ///
+ /// The row id in its table
+ ///
+ protected uint rid;
+
+ ///
+ public MDToken MDToken => new MDToken(Table.Constant, rid);
+
+ ///
+ public uint Rid {
+ get => rid;
+ set => rid = value;
+ }
+
+ ///
+ /// From column Constant.Type
+ ///
+ public ElementType Type {
+ get => type;
+ set => type = value;
+ }
+ ///
+ protected ElementType type;
+
+ ///
+ /// From column Constant.Value
+ ///
+ public object Value {
+ get => value;
+ set => this.value = value;
+ }
+ ///
+ protected object value;
+ }
+
+ ///
+ /// A Constant row created by the user and not present in the original .NET file
+ ///
+ public class ConstantUser : Constant {
+ ///
+ /// Default constructor
+ ///
+ public ConstantUser() {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Value
+ public ConstantUser(object value) {
+ type = GetElementType(value);
+ this.value = value;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Value
+ /// Type
+ public ConstantUser(object value, ElementType type) {
+ this.type = type;
+ this.value = value;
+ }
+
+ static ElementType GetElementType(object value) {
+ if (value is null)
+ return ElementType.Class;
+ return System.Type.GetTypeCode(value.GetType()) switch {
+ TypeCode.Boolean => ElementType.Boolean,
+ TypeCode.Char => ElementType.Char,
+ TypeCode.SByte => ElementType.I1,
+ TypeCode.Byte => ElementType.U1,
+ TypeCode.Int16 => ElementType.I2,
+ TypeCode.UInt16 => ElementType.U2,
+ TypeCode.Int32 => ElementType.I4,
+ TypeCode.UInt32 => ElementType.U4,
+ TypeCode.Int64 => ElementType.I8,
+ TypeCode.UInt64 => ElementType.U8,
+ TypeCode.Single => ElementType.R4,
+ TypeCode.Double => ElementType.R8,
+ TypeCode.String => ElementType.String,
+ _ => ElementType.Void,
+ };
+ }
+ }
+
+ ///
+ /// Created from a row in the Constant table
+ ///
+ sealed class ConstantMD : Constant, IMDTokenProviderMD {
+ readonly uint origRid;
+
+ ///
+ public uint OrigRid => origRid;
+
+ ///
+ /// Constructor
+ ///
+ /// The module which contains this Constant row
+ /// Row ID
+ /// If is null
+ /// If is invalid
+ public ConstantMD(ModuleDefMD readerModule, uint rid) {
+#if DEBUG
+ if (readerModule is null)
+ throw new ArgumentNullException("readerModule");
+ if (readerModule.TablesStream.ConstantTable.IsInvalidRID(rid))
+ throw new BadImageFormatException($"Constant rid {rid} does not exist");
+#endif
+ origRid = rid;
+ this.rid = rid;
+ bool b = readerModule.TablesStream.TryReadConstantRow(origRid, out var row);
+ Debug.Assert(b);
+ type = (ElementType)row.Type;
+ var reader = readerModule.BlobStream.CreateReader(row.Value);
+ value = GetValue(type, ref reader);
+ }
+
+ static object GetValue(ElementType etype, ref DataReader reader) {
+ switch (etype) {
+ case ElementType.Boolean:
+ if (reader.Length < 1)
+ return false;
+ return reader.ReadBoolean();
+
+ case ElementType.Char:
+ if (reader.Length < 2)
+ return (char)0;
+ return reader.ReadChar();
+
+ case ElementType.I1:
+ if (reader.Length < 1)
+ return (sbyte)0;
+ return reader.ReadSByte();
+
+ case ElementType.U1:
+ if (reader.Length < 1)
+ return (byte)0;
+ return reader.ReadByte();
+
+ case ElementType.I2:
+ if (reader.Length < 2)
+ return (short)0;
+ return reader.ReadInt16();
+
+ case ElementType.U2:
+ if (reader.Length < 2)
+ return (ushort)0;
+ return reader.ReadUInt16();
+
+ case ElementType.I4:
+ if (reader.Length < 4)
+ return (int)0;
+ return reader.ReadInt32();
+
+ case ElementType.U4:
+ if (reader.Length < 4)
+ return (uint)0;
+ return reader.ReadUInt32();
+
+ case ElementType.I8:
+ if (reader.Length < 8)
+ return (long)0;
+ return reader.ReadInt64();
+
+ case ElementType.U8:
+ if (reader.Length < 8)
+ return (ulong)0;
+ return reader.ReadUInt64();
+
+ case ElementType.R4:
+ if (reader.Length < 4)
+ return (float)0;
+ return reader.ReadSingle();
+
+ case ElementType.R8:
+ if (reader.Length < 8)
+ return (double)0;
+ return reader.ReadDouble();
+
+ case ElementType.String:
+ return reader.ReadUtf16String((int)(reader.BytesLeft / 2));
+
+ case ElementType.Class:
+ return null;
+
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/CorLibTypes.cs b/Plugins/dnlib/DotNet/CorLibTypes.cs
new file mode 100644
index 0000000..8a2f598
--- /dev/null
+++ b/Plugins/dnlib/DotNet/CorLibTypes.cs
@@ -0,0 +1,143 @@
+// dnlib: See LICENSE.txt for more info
+
+namespace dnlib.DotNet {
+ ///
+ /// Default implementation of
+ ///
+ public sealed class CorLibTypes : ICorLibTypes {
+ readonly ModuleDef module;
+ CorLibTypeSig typeVoid;
+ CorLibTypeSig typeBoolean;
+ CorLibTypeSig typeChar;
+ CorLibTypeSig typeSByte;
+ CorLibTypeSig typeByte;
+ CorLibTypeSig typeInt16;
+ CorLibTypeSig typeUInt16;
+ CorLibTypeSig typeInt32;
+ CorLibTypeSig typeUInt32;
+ CorLibTypeSig typeInt64;
+ CorLibTypeSig typeUInt64;
+ CorLibTypeSig typeSingle;
+ CorLibTypeSig typeDouble;
+ CorLibTypeSig typeString;
+ CorLibTypeSig typeTypedReference;
+ CorLibTypeSig typeIntPtr;
+ CorLibTypeSig typeUIntPtr;
+ CorLibTypeSig typeObject;
+ readonly AssemblyRef corLibAssemblyRef;
+
+ ///
+ public CorLibTypeSig Void => typeVoid;
+
+ ///
+ public CorLibTypeSig Boolean => typeBoolean;
+
+ ///
+ public CorLibTypeSig Char => typeChar;
+
+ ///
+ public CorLibTypeSig SByte => typeSByte;
+
+ ///
+ public CorLibTypeSig Byte => typeByte;
+
+ ///
+ public CorLibTypeSig Int16 => typeInt16;
+
+ ///
+ public CorLibTypeSig UInt16 => typeUInt16;
+
+ ///
+ public CorLibTypeSig Int32 => typeInt32;
+
+ ///
+ public CorLibTypeSig UInt32 => typeUInt32;
+
+ ///
+ public CorLibTypeSig Int64 => typeInt64;
+
+ ///
+ public CorLibTypeSig UInt64 => typeUInt64;
+
+ ///
+ public CorLibTypeSig Single => typeSingle;
+
+ ///
+ public CorLibTypeSig Double => typeDouble;
+
+ ///
+ public CorLibTypeSig String => typeString;
+
+ ///
+ public CorLibTypeSig TypedReference => typeTypedReference;
+
+ ///
+ public CorLibTypeSig IntPtr => typeIntPtr;
+
+ ///
+ public CorLibTypeSig UIntPtr => typeUIntPtr;
+
+ ///
+ public CorLibTypeSig Object => typeObject;
+
+ ///
+ public AssemblyRef AssemblyRef => corLibAssemblyRef;
+
+ ///
+ /// Constructor
+ ///
+ /// The owner module
+ public CorLibTypes(ModuleDef module)
+ : this(module, null) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// The owner module
+ /// Corlib assembly reference or null if a default
+ /// assembly reference should be created
+ public CorLibTypes(ModuleDef module, AssemblyRef corLibAssemblyRef) {
+ this.module = module;
+ this.corLibAssemblyRef = corLibAssemblyRef ?? CreateCorLibAssemblyRef();
+ Initialize();
+ }
+
+ AssemblyRef CreateCorLibAssemblyRef() => module.UpdateRowId(AssemblyRefUser.CreateMscorlibReferenceCLR20());
+
+ void Initialize() {
+ bool isCorLib = module.Assembly.IsCorLib();
+ typeVoid = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Void"), ElementType.Void);
+ typeBoolean = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Boolean"), ElementType.Boolean);
+ typeChar = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Char"), ElementType.Char);
+ typeSByte = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "SByte"), ElementType.I1);
+ typeByte = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Byte"), ElementType.U1);
+ typeInt16 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Int16"), ElementType.I2);
+ typeUInt16 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UInt16"), ElementType.U2);
+ typeInt32 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Int32"), ElementType.I4);
+ typeUInt32 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UInt32"), ElementType.U4);
+ typeInt64 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Int64"), ElementType.I8);
+ typeUInt64 = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UInt64"), ElementType.U8);
+ typeSingle = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Single"), ElementType.R4);
+ typeDouble = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Double"), ElementType.R8);
+ typeString = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "String"), ElementType.String);
+ typeTypedReference = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "TypedReference"), ElementType.TypedByRef);
+ typeIntPtr = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "IntPtr"), ElementType.I);
+ typeUIntPtr = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "UIntPtr"), ElementType.U);
+ typeObject = new CorLibTypeSig(CreateCorLibTypeRef(isCorLib, "Object"), ElementType.Object);
+ }
+
+ ITypeDefOrRef CreateCorLibTypeRef(bool isCorLib, string name) {
+ var tr = new TypeRefUser(module, "System", name, corLibAssemblyRef);
+ if (isCorLib) {
+ var td = module.Find(tr);
+ if (td is not null)
+ return td;
+ }
+ return module.UpdateRowId(tr);
+ }
+
+ ///
+ public TypeRef GetTypeRef(string @namespace, string name) => module.UpdateRowId(new TypeRefUser(module, @namespace, name, corLibAssemblyRef));
+ }
+}
diff --git a/Plugins/dnlib/DotNet/CpuArch.cs b/Plugins/dnlib/DotNet/CpuArch.cs
new file mode 100644
index 0000000..2e0c27a
--- /dev/null
+++ b/Plugins/dnlib/DotNet/CpuArch.cs
@@ -0,0 +1,427 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using dnlib.DotNet.Writer;
+using dnlib.IO;
+using dnlib.PE;
+
+namespace dnlib.DotNet {
+ enum StubType {
+ Export,
+ EntryPoint,
+ }
+
+ abstract class CpuArch {
+ // To support a new CPU arch, the easiest way is to check coreclr/src/ilasm/writer.cpp or
+ // coreclr/src/dlls/mscorpe/stubs.h, eg. ExportStubAMD64Template, ExportStubX86Template,
+ // ExportStubARMTemplate, ExportStubIA64Template, or use ilasm to generate a file with
+ // exports and check the stub
+ static readonly X86CpuArch x86CpuArch = new X86CpuArch();
+ static readonly X64CpuArch x64CpuArch = new X64CpuArch();
+ static readonly ItaniumCpuArch itaniumCpuArch = new ItaniumCpuArch();
+ static readonly ArmCpuArch armCpuArch = new ArmCpuArch();
+
+ ///
+ /// Gets the required alignment for the stubs, must be a power of 2
+ ///
+ /// Stub type
+ ///
+ public abstract uint GetStubAlignment(StubType stubType);
+
+ ///
+ /// Gets the size of a stub, it doesn't have to be a multiple of
+ ///
+ /// Stub type
+ ///
+ public abstract uint GetStubSize(StubType stubType);
+
+ ///
+ /// Gets the offset of the code (entry point) relative to the start of the stub
+ ///
+ /// Stub type
+ ///
+ public abstract uint GetStubCodeOffset(StubType stubType);
+
+ public static bool TryGetCpuArch(Machine machine, out CpuArch cpuArch) {
+ switch (machine) {
+ case Machine.I386:
+ case Machine.I386_Native_Apple:
+ case Machine.I386_Native_FreeBSD:
+ case Machine.I386_Native_Linux:
+ case Machine.I386_Native_NetBSD:
+ case Machine.I386_Native_Sun:
+ cpuArch = x86CpuArch;
+ return true;
+
+ case Machine.AMD64:
+ case Machine.AMD64_Native_Apple:
+ case Machine.AMD64_Native_FreeBSD:
+ case Machine.AMD64_Native_Linux:
+ case Machine.AMD64_Native_NetBSD:
+ case Machine.AMD64_Native_Sun:
+ cpuArch = x64CpuArch;
+ return true;
+
+ case Machine.IA64:
+ cpuArch = itaniumCpuArch;
+ return true;
+
+ case Machine.ARMNT:
+ case Machine.ARMNT_Native_Apple:
+ case Machine.ARMNT_Native_FreeBSD:
+ case Machine.ARMNT_Native_Linux:
+ case Machine.ARMNT_Native_NetBSD:
+ case Machine.ARMNT_Native_Sun:
+ cpuArch = armCpuArch;
+ return true;
+
+ case Machine.ARM64:
+ case Machine.ARM64_Native_Apple:
+ case Machine.ARM64_Native_FreeBSD:
+ case Machine.ARM64_Native_Linux:
+ case Machine.ARM64_Native_NetBSD:
+ case Machine.ARM64_Native_Sun:
+ //TODO: Support ARM64
+ goto default;
+
+ default:
+ cpuArch = null;
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the RVA of the func field that the stub jumps to
+ ///
+ /// Reader, positioned at the stub func
+ /// PE image
+ /// Updated with RVA of func field
+ ///
+ public bool TryGetExportedRvaFromStub(ref DataReader reader, IPEImage peImage, out uint funcRva) =>
+ TryGetExportedRvaFromStubCore(ref reader, peImage, out funcRva);
+
+ protected abstract bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva);
+
+ ///
+ /// Writes stub relocs, if needed
+ ///
+ /// Stub type
+ /// Reloc directory
+ /// The chunk where this stub will be written to
+ /// Offset of this stub in
+ public abstract void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset);
+
+ ///
+ /// Writes the stub that jumps to the managed function
+ ///
+ /// Stub type
+ /// Writer
+ /// Image base
+ /// RVA of this stub
+ /// RVA of a pointer-sized field that contains the absolute address of the managed function
+ public abstract void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva);
+ }
+
+ sealed class X86CpuArch : CpuArch {
+ public override uint GetStubAlignment(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 4;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubSize(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 2/*padding*/ + 6;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubCodeOffset(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 2/*padding*/;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
+ funcRva = 0;
+
+ // FF25xxxxxxxx jmp DWORD PTR [xxxxxxxx]
+ if (reader.ReadUInt16() != 0x25FF)
+ return false;
+ funcRva = reader.ReadUInt32() - (uint)peImage.ImageNTHeaders.OptionalHeader.ImageBase;
+ return true;
+ }
+
+ public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ relocDirectory.Add(chunk, stubOffset + 4);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ writer.WriteUInt16(0);// padding
+ writer.WriteUInt16(0x25FF);
+ writer.WriteUInt32((uint)imageBase + managedFuncRva);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ sealed class X64CpuArch : CpuArch {
+ public override uint GetStubAlignment(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 4;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubSize(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 2/*padding*/ + 12;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubCodeOffset(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 2/*padding*/;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
+ funcRva = 0;
+
+ // 48A1xxxxxxxxxxxxxxxx movabs rax,[xxxxxxxxxxxxxxxx]
+ // FFE0 jmp rax
+ if (reader.ReadUInt16() != 0xA148)
+ return false;
+ ulong absAddr = reader.ReadUInt64();
+ if (reader.ReadUInt16() != 0xE0FF)
+ return false;
+ ulong rva = absAddr - peImage.ImageNTHeaders.OptionalHeader.ImageBase;
+ if (rva > uint.MaxValue)
+ return false;
+ funcRva = (uint)rva;
+ return true;
+ }
+
+ public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ relocDirectory.Add(chunk, stubOffset + 4);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ writer.WriteUInt16(0);// padding
+ writer.WriteUInt16(0xA148);
+ writer.WriteUInt64(imageBase + managedFuncRva);
+ writer.WriteUInt16(0xE0FF);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ sealed class ItaniumCpuArch : CpuArch {
+ public override uint GetStubAlignment(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 16;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubSize(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 0x30;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubCodeOffset(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 0x20;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
+ funcRva = 0;
+
+ // From ExportStubIA64Template in coreclr/src/ilasm/writer.cpp
+ //
+ // ld8 r9 = [gp] ;;
+ // ld8 r10 = [r9],8
+ // nop.i ;;
+ // ld8 gp = [r9]
+ // mov b6 = r10
+ // br.cond.sptk.few b6
+ //
+ // 0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40,
+ // 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00,
+ // 0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50,
+ // 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00,
+ // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//address of the template
+ // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //address of VTFixup slot
+ ulong addrTemplate = reader.ReadUInt64();
+ ulong absAddr = reader.ReadUInt64();
+ reader.Position = (uint)peImage.ToFileOffset((RVA)(addrTemplate - peImage.ImageNTHeaders.OptionalHeader.ImageBase));
+ if (reader.ReadUInt64() != 0x40A010180200480BUL)
+ return false;
+ if (reader.ReadUInt64() != 0x0004000000283024UL)
+ return false;
+ if (reader.ReadUInt64() != 0x5060101812000810UL)
+ return false;
+ if (reader.ReadUInt64() != 0x0080006000038004UL)
+ return false;
+
+ ulong rva = absAddr - peImage.ImageNTHeaders.OptionalHeader.ImageBase;
+ if (rva > uint.MaxValue)
+ return false;
+ funcRva = (uint)rva;
+ return true;
+ }
+
+ public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ relocDirectory.Add(chunk, stubOffset + 0x20);
+ relocDirectory.Add(chunk, stubOffset + 0x28);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ writer.WriteUInt64(0x40A010180200480BUL);
+ writer.WriteUInt64(0x0004000000283024UL);
+ writer.WriteUInt64(0x5060101812000810UL);
+ writer.WriteUInt64(0x0080006000038004UL);
+ writer.WriteUInt64(imageBase + stubRva);
+ writer.WriteUInt64(imageBase + managedFuncRva);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ sealed class ArmCpuArch : CpuArch {
+ public override uint GetStubAlignment(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 4;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubSize(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 8;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override uint GetStubCodeOffset(StubType stubType) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ return 0;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ protected override bool TryGetExportedRvaFromStubCore(ref DataReader reader, IPEImage peImage, out uint funcRva) {
+ funcRva = 0;
+
+ // DFF800F0 ldr.w pc,[pc]
+ // xxxxxxxx
+ if (reader.ReadUInt32() != 0xF000F8DF)
+ return false;
+ funcRva = reader.ReadUInt32() - (uint)peImage.ImageNTHeaders.OptionalHeader.ImageBase;
+ return true;
+ }
+
+ public override void WriteStubRelocs(StubType stubType, RelocDirectory relocDirectory, IChunk chunk, uint stubOffset) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ relocDirectory.Add(chunk, stubOffset + 4);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public override void WriteStub(StubType stubType, DataWriter writer, ulong imageBase, uint stubRva, uint managedFuncRva) {
+ switch (stubType) {
+ case StubType.Export:
+ case StubType.EntryPoint:
+ writer.WriteUInt32(0xF000F8DF);
+ writer.WriteUInt32((uint)imageBase + managedFuncRva);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+}
diff --git a/Plugins/dnlib/DotNet/CustomAttribute.cs b/Plugins/dnlib/DotNet/CustomAttribute.cs
new file mode 100644
index 0000000..36367e9
--- /dev/null
+++ b/Plugins/dnlib/DotNet/CustomAttribute.cs
@@ -0,0 +1,462 @@
+// dnlib: See LICENSE.txt for more info
+
+using System;
+using System.Collections.Generic;
+
+namespace dnlib.DotNet {
+ ///
+ /// A custom attribute
+ ///
+ public sealed class CustomAttribute : ICustomAttribute {
+ ICustomAttributeType ctor;
+ byte[] rawData;
+ readonly IList arguments;
+ readonly IList namedArguments;
+ uint caBlobOffset;
+
+ ///
+ /// Gets/sets the custom attribute constructor
+ ///
+ public ICustomAttributeType Constructor {
+ get => ctor;
+ set => ctor = value;
+ }
+
+ ///
+ /// Gets the attribute type
+ ///
+ public ITypeDefOrRef AttributeType => ctor?.DeclaringType;
+
+ ///
+ /// Gets the full name of the attribute type
+ ///
+ public string TypeFullName {
+ get {
+ if (ctor is MemberRef mrCtor)
+ return mrCtor.GetDeclaringTypeFullName() ?? string.Empty;
+
+ if (ctor is MethodDef mdCtor) {
+ var declType = mdCtor.DeclaringType;
+ if (declType is not null)
+ return declType.FullName;
+ }
+
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// Gets the name of the attribute type
+ ///
+ internal string TypeName {
+ get {
+ if (ctor is MemberRef mrCtor)
+ return mrCtor.GetDeclaringTypeName() ?? string.Empty;
+
+ if (ctor is MethodDef mdCtor) {
+ var declType = mdCtor.DeclaringType;
+ if (declType is not null)
+ return declType.Name;
+ }
+
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// true if the raw custom attribute blob hasn't been parsed
+ ///
+ public bool IsRawBlob => rawData is not null;
+
+ ///
+ /// Gets the raw custom attribute blob or null if the CA was successfully parsed.
+ ///
+ public byte[] RawData => rawData;
+
+ ///
+ /// Gets all constructor arguments
+ ///
+ public IList ConstructorArguments => arguments;
+
+ ///
+ /// true if is not empty
+ ///
+ public bool HasConstructorArguments => arguments.Count > 0;
+
+ ///
+ /// Gets all named arguments (field and property values)
+ ///
+ public IList NamedArguments => namedArguments;
+
+ ///
+ /// true if is not empty
+ ///
+ public bool HasNamedArguments => namedArguments.Count > 0;
+
+ ///
+ /// Gets all s that are field arguments
+ ///
+ public IEnumerable Fields {
+ get {
+ var namedArguments = this.namedArguments;
+ int count = namedArguments.Count;
+ for (int i = 0; i < count; i++) {
+ var namedArg = namedArguments[i];
+ if (namedArg.IsField)
+ yield return namedArg;
+ }
+ }
+ }
+
+ ///
+ /// Gets all s that are property arguments
+ ///
+ public IEnumerable Properties {
+ get {
+ var namedArguments = this.namedArguments;
+ int count = namedArguments.Count;
+ for (int i = 0; i < count; i++) {
+ var namedArg = namedArguments[i];
+ if (namedArg.IsProperty)
+ yield return namedArg;
+ }
+ }
+ }
+
+ ///
+ /// Gets the #Blob offset or 0 if unknown
+ ///
+ public uint BlobOffset => caBlobOffset;
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ /// Raw custom attribute blob
+ public CustomAttribute(ICustomAttributeType ctor, byte[] rawData)
+ : this(ctor, null, null, 0) => this.rawData = rawData;
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ public CustomAttribute(ICustomAttributeType ctor)
+ : this(ctor, null, null, 0) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ /// Constructor arguments or null if none
+ public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments)
+ : this(ctor, arguments, null) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ /// Named arguments or null if none
+ public CustomAttribute(ICustomAttributeType ctor, IEnumerable namedArguments)
+ : this(ctor, null, namedArguments) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ /// Constructor arguments or null if none
+ /// Named arguments or null if none
+ public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments)
+ : this(ctor, arguments, namedArguments, 0) {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ /// Constructor arguments or null if none
+ /// Named arguments or null if none
+ /// Original custom attribute #Blob offset or 0
+ public CustomAttribute(ICustomAttributeType ctor, IEnumerable arguments, IEnumerable namedArguments, uint caBlobOffset) {
+ this.ctor = ctor;
+ this.arguments = arguments is null ? new List() : new List(arguments);
+ this.namedArguments = namedArguments is null ? new List() : new List(namedArguments);
+ this.caBlobOffset = caBlobOffset;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Custom attribute constructor
+ /// Constructor arguments. The list is now owned by this instance.
+ /// Named arguments. The list is now owned by this instance.
+ /// Original custom attribute #Blob offset or 0
+ internal CustomAttribute(ICustomAttributeType ctor, List arguments, List namedArguments, uint caBlobOffset) {
+ this.ctor = ctor;
+ this.arguments = arguments ?? new List();
+ this.namedArguments = namedArguments ?? new List();
+ this.caBlobOffset = caBlobOffset;
+ }
+
+ ///
+ /// Gets the field named
+ ///
+ /// Name of field
+ /// A instance or null if not found
+ public CANamedArgument GetField(string name) => GetNamedArgument(name, true);
+
+ ///
+ /// Gets the field named
+ ///
+ /// Name of field
+ /// A instance or null if not found
+ public CANamedArgument GetField(UTF8String name) => GetNamedArgument(name, true);
+
+ ///
+ /// Gets the property named
+ ///
+ /// Name of property
+ /// A instance or null if not found
+ public CANamedArgument GetProperty(string name) => GetNamedArgument(name, false);
+
+ ///
+ /// Gets the property named
+ ///
+ /// Name of property
+ /// A instance or null if not found
+ public CANamedArgument GetProperty(UTF8String name) => GetNamedArgument(name, false);
+
+ ///
+ /// Gets the property/field named
+ ///
+ /// Name of property/field
+ /// true if it's a field, false if it's a property
+ /// A instance or null if not found
+ public CANamedArgument GetNamedArgument(string name, bool isField) {
+ var namedArguments = this.namedArguments;
+ int count = namedArguments.Count;
+ for (int i = 0; i < count; i++) {
+ var namedArg = namedArguments[i];
+ if (namedArg.IsField == isField && UTF8String.ToSystemStringOrEmpty(namedArg.Name) == name)
+ return namedArg;
+ }
+ return null;
+ }
+
+ ///
+ /// Gets the property/field named
+ ///
+ /// Name of property/field
+ /// true if it's a field, false if it's a property
+ /// A instance or null if not found
+ public CANamedArgument GetNamedArgument(UTF8String name, bool isField) {
+ var namedArguments = this.namedArguments;
+ int count = namedArguments.Count;
+ for (int i = 0; i < count; i++) {
+ var namedArg = namedArguments[i];
+ if (namedArg.IsField == isField && UTF8String.Equals(namedArg.Name, name))
+ return namedArg;
+ }
+ return null;
+ }
+
+ ///
+ public override string ToString() => TypeFullName;
+ }
+
+ ///
+ /// A custom attribute constructor argument
+ ///
+ public struct CAArgument : ICloneable {
+ TypeSig type;
+ object value;
+
+ ///
+ /// Gets/sets the argument type
+ ///
+ public TypeSig Type {
+ readonly get => type;
+ set => type = value;
+ }
+
+ ///
+ /// Gets/sets the argument value
+ ///
+ public object Value {
+ readonly get => value;
+ set => this.value = value;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Argument type
+ public CAArgument(TypeSig type) {
+ this.type = type;
+ value = null;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Argument type
+ /// Argument value
+ public CAArgument(TypeSig type, object value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ readonly object ICloneable.Clone() => Clone();
+
+ ///
+ /// Clones this instance and any s and s
+ /// referenced from this instance.
+ ///
+ ///
+ public readonly CAArgument Clone() {
+ var value = this.value;
+ if (value is CAArgument)
+ value = ((CAArgument)value).Clone();
+ else if (value is IList args) {
+ var newArgs = new List(args.Count);
+ int count = args.Count;
+ for (int i = 0; i < count; i++) {
+ var arg = args[i];
+ newArgs.Add(arg.Clone());
+ }
+ value = newArgs;
+ }
+ return new CAArgument(type, value);
+ }
+
+ ///
+ public override readonly string ToString() => $"{value ?? "null"} ({type})";
+ }
+
+ ///
+ /// A custom attribute field/property argument
+ ///
+ public sealed class CANamedArgument : ICloneable {
+ bool isField;
+ TypeSig type;
+ UTF8String name;
+ CAArgument argument;
+
+ ///
+ /// true if it's a field
+ ///
+ public bool IsField {
+ get => isField;
+ set => isField = value;
+ }
+
+ ///
+ /// true if it's a property
+ ///
+ public bool IsProperty {
+ get => !isField;
+ set => isField = !value;
+ }
+
+ ///
+ /// Gets/sets the field/property type
+ ///
+ public TypeSig Type {
+ get => type;
+ set => type = value;
+ }
+
+ ///
+ /// Gets/sets the property/field name
+ ///
+ public UTF8String Name {
+ get => name;
+ set => name = value;
+ }
+
+ ///
+ /// Gets/sets the argument
+ ///
+ public CAArgument Argument {
+ get => argument;
+ set => argument = value;
+ }
+
+ ///
+ /// Gets/sets the argument type
+ ///
+ public TypeSig ArgumentType {
+ get => argument.Type;
+ set => argument.Type = value;
+ }
+
+ ///
+ /// Gets/sets the argument value
+ ///
+ public object Value {
+ get => argument.Value;
+ set => argument.Value = value;
+ }
+
+ ///
+ /// Default constructor
+ ///
+ public CANamedArgument() {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if field, false if property
+ public CANamedArgument(bool isField) => this.isField = isField;
+
+ ///
+ /// Constructor
+ ///
+ /// true if field, false if property
+ /// Field/property type
+ public CANamedArgument(bool isField, TypeSig type) {
+ this.isField = isField;
+ this.type = type;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if field, false if property
+ /// Field/property type
+ /// Name of field/property
+ public CANamedArgument(bool isField, TypeSig type, UTF8String name) {
+ this.isField = isField;
+ this.type = type;
+ this.name = name;
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// true if field, false if property
+ /// Field/property type
+ /// Name of field/property
+ /// Field/property argument
+ public CANamedArgument(bool isField, TypeSig type, UTF8String name, CAArgument argument) {
+ this.isField = isField;
+ this.type = type;
+ this.name = name;
+ this.argument = argument;
+ }
+
+ object ICloneable.Clone() => Clone();
+
+ ///
+ /// Clones this instance and any s referenced from this instance.
+ ///
+ ///
+ public CANamedArgument Clone() => new CANamedArgument(isField, type, name, argument.Clone());
+
+ ///
+ public override string ToString() => $"({(isField ? "field" : "property")}) {type} {name} = {Value ?? "null"} ({ArgumentType})";
+ }
+}
diff --git a/Plugins/dnlib/DotNet/CustomAttributeCollection.cs b/Plugins/dnlib/DotNet/CustomAttributeCollection.cs
new file mode 100644
index 0000000..07fba46
--- /dev/null
+++ b/Plugins/dnlib/DotNet/CustomAttributeCollection.cs
@@ -0,0 +1,125 @@
+// dnlib: See LICENSE.txt for more info
+
+using System.Collections.Generic;
+using dnlib.Utils;
+using System;
+
+namespace dnlib.DotNet {
+ ///
+ /// Stores s
+ ///
+ public class CustomAttributeCollection : LazyList {
+ ///
+ /// Default constructor
+ ///
+ public CustomAttributeCollection() {
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Initial length of the list
+ /// Context passed to
+ /// Delegate instance that returns original values
+ public CustomAttributeCollection(int length, object context, Func