// 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; } } }