// 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 {
///
/// Creates resource data
///
public class ResourceDataFactory {
readonly ModuleDef module;
readonly ModuleDefMD moduleMD;
readonly Dictionary dict = new Dictionary(StringComparer.Ordinal);
readonly Dictionary asmNameToAsmFullName = new Dictionary(StringComparer.Ordinal);
///
/// Gets the owner module
///
protected ModuleDef Module => module;
///
/// Constructor
///
/// Owner module
public ResourceDataFactory(ModuleDef module) {
this.module = module;
moduleMD = module as ModuleDefMD;
}
///
/// Gets number of user data types
///
public int Count => dict.Count;
///
/// Create null data
///
///
public BuiltInResourceData CreateNull() => new BuiltInResourceData(ResourceTypeCode.Null, null);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(string value) => new BuiltInResourceData(ResourceTypeCode.String, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(bool value) => new BuiltInResourceData(ResourceTypeCode.Boolean, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(char value) => new BuiltInResourceData(ResourceTypeCode.Char, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(byte value) => new BuiltInResourceData(ResourceTypeCode.Byte, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(sbyte value) => new BuiltInResourceData(ResourceTypeCode.SByte, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(short value) => new BuiltInResourceData(ResourceTypeCode.Int16, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(ushort value) => new BuiltInResourceData(ResourceTypeCode.UInt16, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(int value) => new BuiltInResourceData(ResourceTypeCode.Int32, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(uint value) => new BuiltInResourceData(ResourceTypeCode.UInt32, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(long value) => new BuiltInResourceData(ResourceTypeCode.Int64, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(ulong value) => new BuiltInResourceData(ResourceTypeCode.UInt64, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(float value) => new BuiltInResourceData(ResourceTypeCode.Single, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(double value) => new BuiltInResourceData(ResourceTypeCode.Double, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(decimal value) => new BuiltInResourceData(ResourceTypeCode.Decimal, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(DateTime value) => new BuiltInResourceData(ResourceTypeCode.DateTime, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData Create(TimeSpan value) => new BuiltInResourceData(ResourceTypeCode.TimeSpan, value);
///
/// Creates array data
///
/// Value
///
public BuiltInResourceData Create(byte[] value) => new BuiltInResourceData(ResourceTypeCode.ByteArray, value);
///
/// Creates data
///
/// Value
///
public BuiltInResourceData CreateStream(byte[] value) => new BuiltInResourceData(ResourceTypeCode.Stream, value);
///
/// Creates serialized data
///
/// Serialized data
/// Format of the serialized data
/// Type of serialized data
///
public BinaryResourceData CreateSerialized(byte[] value, SerializationFormat format, UserResourceType type) => new BinaryResourceData(CreateUserResourceType(type.Name, true), value, format);
///
/// Creates serialized data
///
/// Serialized data
///
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;
}
///
/// Creates a user type for a built-in resource type code.
/// Useful when writing V1 resources.
///
/// The built-in resource type code or null if not supported
///
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);
}
///
/// Creates a user type. If the type already exists, the existing value is returned.
///
/// Full name of type
///
public UserResourceType CreateUserResourceType(string fullName) => CreateUserResourceType(fullName, false);
///
/// Creates a user type. If the type already exists, the existing value is returned.
///
/// Full name of type
/// Use without converting it to a
/// type in an existing assembly reference
///
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);
}
///
/// Converts an assembly simple name (eg. mscorlib) to the full name of the assembly,
/// which includes the version, public key token, etc. Returns null if it's
/// unknown.
///
/// Simple name of assembly
///
protected virtual string GetAssemblyFullName(string simpleName) => null;
///
/// Gets all types sorted by
///
///
public List GetSortedTypes() {
var list = new List(dict.Values);
list.Sort((a, b) => ((int)a.Code).CompareTo((int)b.Code));
return list;
}
}
}