obfuz/Plugins/dnlib/DotNet/Resources/ResourceDataFactory.cs

340 lines
12 KiB
C#

// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace dnlib.DotNet.Resources {
/// <summary>
/// Creates resource data
/// </summary>
public class ResourceDataFactory {
readonly ModuleDef module;
readonly ModuleDefMD moduleMD;
readonly Dictionary<string, UserResourceType> dict = new Dictionary<string, UserResourceType>(StringComparer.Ordinal);
readonly Dictionary<string, string> asmNameToAsmFullName = new Dictionary<string, string>(StringComparer.Ordinal);
/// <summary>
/// Gets the owner module
/// </summary>
protected ModuleDef Module => module;
/// <summary>
/// Constructor
/// </summary>
/// <param name="module">Owner module</param>
public ResourceDataFactory(ModuleDef module) {
this.module = module;
moduleMD = module as ModuleDefMD;
}
/// <summary>
/// Gets number of user data types
/// </summary>
public int Count => dict.Count;
/// <summary>
/// Create null data
/// </summary>
/// <returns></returns>
public BuiltInResourceData CreateNull() => new BuiltInResourceData(ResourceTypeCode.Null, null);
/// <summary>
/// Creates <see cref="string"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(string value) => new BuiltInResourceData(ResourceTypeCode.String, value);
/// <summary>
/// Creates <see cref="bool"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(bool value) => new BuiltInResourceData(ResourceTypeCode.Boolean, value);
/// <summary>
/// Creates <see cref="char"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(char value) => new BuiltInResourceData(ResourceTypeCode.Char, value);
/// <summary>
/// Creates <see cref="byte"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(byte value) => new BuiltInResourceData(ResourceTypeCode.Byte, value);
/// <summary>
/// Creates <see cref="sbyte"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(sbyte value) => new BuiltInResourceData(ResourceTypeCode.SByte, value);
/// <summary>
/// Creates <see cref="short"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(short value) => new BuiltInResourceData(ResourceTypeCode.Int16, value);
/// <summary>
/// Creates <see cref="ushort"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(ushort value) => new BuiltInResourceData(ResourceTypeCode.UInt16, value);
/// <summary>
/// Creates <see cref="int"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(int value) => new BuiltInResourceData(ResourceTypeCode.Int32, value);
/// <summary>
/// Creates <see cref="uint"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(uint value) => new BuiltInResourceData(ResourceTypeCode.UInt32, value);
/// <summary>
/// Creates <see cref="long"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(long value) => new BuiltInResourceData(ResourceTypeCode.Int64, value);
/// <summary>
/// Creates <see cref="ulong"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(ulong value) => new BuiltInResourceData(ResourceTypeCode.UInt64, value);
/// <summary>
/// Creates <see cref="float"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(float value) => new BuiltInResourceData(ResourceTypeCode.Single, value);
/// <summary>
/// Creates <see cref="double"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(double value) => new BuiltInResourceData(ResourceTypeCode.Double, value);
/// <summary>
/// Creates <see cref="decimal"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(decimal value) => new BuiltInResourceData(ResourceTypeCode.Decimal, value);
/// <summary>
/// Creates <see cref="DateTime"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(DateTime value) => new BuiltInResourceData(ResourceTypeCode.DateTime, value);
/// <summary>
/// Creates <see cref="TimeSpan"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(TimeSpan value) => new BuiltInResourceData(ResourceTypeCode.TimeSpan, value);
/// <summary>
/// Creates <see cref="byte"/> array data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData Create(byte[] value) => new BuiltInResourceData(ResourceTypeCode.ByteArray, value);
/// <summary>
/// Creates <see cref="Stream"/> data
/// </summary>
/// <param name="value">Value</param>
/// <returns></returns>
public BuiltInResourceData CreateStream(byte[] value) => new BuiltInResourceData(ResourceTypeCode.Stream, value);
/// <summary>
/// Creates serialized data
/// </summary>
/// <param name="value">Serialized data</param>
/// <param name="format">Format of the serialized data</param>
/// <param name="type">Type of serialized data</param>
/// <returns></returns>
public BinaryResourceData CreateSerialized(byte[] value, SerializationFormat format, UserResourceType type) => new BinaryResourceData(CreateUserResourceType(type.Name, true), value, format);
/// <summary>
/// Creates serialized data
/// </summary>
/// <param name="value">Serialized data</param>
/// <returns></returns>
public BinaryResourceData CreateBinaryFormatterSerialized(byte[] value) {
if (!GetSerializedTypeAndAssemblyName(value, out var assemblyName, out var typeName))
throw new ApplicationException("Could not get serialized type name");
string fullName = $"{typeName}, {assemblyName}";
return new BinaryResourceData(CreateUserResourceType(fullName), value, SerializationFormat.BinaryFormatter);
}
sealed class MyBinder : SerializationBinder {
public class OkException : Exception {
public string AssemblyName { get; set; }
public string TypeName { get; set; }
}
public override Type BindToType(string assemblyName, string typeName) =>
throw new OkException {
AssemblyName = assemblyName,
TypeName = typeName,
};
}
bool GetSerializedTypeAndAssemblyName(byte[] value, out string assemblyName, out string typeName) {
try {
var formatter = new BinaryFormatter();
formatter.Binder = new MyBinder();
#pragma warning disable SYSLIB0011 // Type or member is obsolete
formatter.Deserialize(new MemoryStream(value));
#pragma warning restore SYSLIB0011 // Type or member is obsolete
}
catch (MyBinder.OkException ex) {
assemblyName = ex.AssemblyName;
typeName = ex.TypeName;
return true;
}
catch {
}
assemblyName = null;
typeName = null;
return false;
}
/// <summary>
/// Creates a user type for a built-in resource type code.
/// Useful when writing V1 resources.
/// </summary>
/// <param name="typeCode">The built-in resource type code or null if not supported</param>
/// <returns></returns>
public UserResourceType CreateBuiltinResourceType(ResourceTypeCode typeCode) {
string typeName = typeCode switch {
ResourceTypeCode.String => "System.String",
ResourceTypeCode.Boolean => "System.Boolean",
ResourceTypeCode.Char => "System.Char",
ResourceTypeCode.Byte => "System.Byte",
ResourceTypeCode.SByte => "System.SByte",
ResourceTypeCode.Int16 => "System.Int16",
ResourceTypeCode.UInt16 => "System.UInt16",
ResourceTypeCode.Int32 => "System.Int32",
ResourceTypeCode.UInt32 => "System.UInt32",
ResourceTypeCode.Int64 => "System.Int64",
ResourceTypeCode.UInt64 => "System.UInt64",
ResourceTypeCode.Single => "System.Single",
ResourceTypeCode.Double => "System.Double",
ResourceTypeCode.Decimal => "System.Decimal",
ResourceTypeCode.DateTime => "System.DateTime",
ResourceTypeCode.TimeSpan => "System.TimeSpan",
_ => null
};
if (typeName is null)
return null;
return CreateUserResourceType($"{typeName}, {module.CorLibTypes.AssemblyRef.FullName}", true);
}
/// <summary>
/// Creates a user type. If the type already exists, the existing value is returned.
/// </summary>
/// <param name="fullName">Full name of type</param>
/// <returns></returns>
public UserResourceType CreateUserResourceType(string fullName) => CreateUserResourceType(fullName, false);
/// <summary>
/// Creates a user type. If the type already exists, the existing value is returned.
/// </summary>
/// <param name="fullName">Full name of type</param>
/// <param name="useFullName">Use <paramref name="fullName"/> without converting it to a
/// type in an existing assembly reference</param>
/// <returns></returns>
UserResourceType CreateUserResourceType(string fullName, bool useFullName) {
if (dict.TryGetValue(fullName, out var type))
return type;
var newFullName = useFullName ? fullName : GetRealTypeFullName(fullName);
type = new UserResourceType(newFullName, ResourceTypeCode.UserTypes + dict.Count);
dict[fullName] = type;
dict[newFullName] = type;
return type;
}
string GetRealTypeFullName(string fullName) {
var tr = TypeNameParser.ParseReflection(module, fullName, null);
if (tr is null)
return fullName;
var asmRef = tr.DefinitionAssembly;
if (asmRef is null)
return fullName;
var newFullName = fullName;
string assemblyName = GetRealAssemblyName(asmRef);
if (!string.IsNullOrEmpty(assemblyName))
newFullName = $"{tr.ReflectionFullName}, {assemblyName}";
return newFullName;
}
string GetRealAssemblyName(IAssembly asm) {
string assemblyName = asm.FullName;
if (!asmNameToAsmFullName.TryGetValue(assemblyName, out var newAsmName))
asmNameToAsmFullName[assemblyName] = newAsmName = TryGetRealAssemblyName(asm);
return newAsmName;
}
string TryGetRealAssemblyName(IAssembly asm) {
var simpleName = asm.Name;
if (simpleName == module.CorLibTypes.AssemblyRef.Name)
return module.CorLibTypes.AssemblyRef.FullName;
if (moduleMD is not null) {
var asmRef = moduleMD.GetAssemblyRef(simpleName);
if (asmRef is not null)
return asmRef.FullName;
}
return GetAssemblyFullName(simpleName);
}
/// <summary>
/// Converts an assembly simple name (eg. mscorlib) to the full name of the assembly,
/// which includes the version, public key token, etc. Returns <c>null</c> if it's
/// unknown.
/// </summary>
/// <param name="simpleName">Simple name of assembly</param>
/// <returns></returns>
protected virtual string GetAssemblyFullName(string simpleName) => null;
/// <summary>
/// Gets all types sorted by <see cref="UserResourceType"/>
/// </summary>
/// <returns></returns>
public List<UserResourceType> GetSortedTypes() {
var list = new List<UserResourceType>(dict.Values);
list.Sort((a, b) => ((int)a.Code).CompareTo((int)b.Code));
return list;
}
}
}