using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Security.Cryptography; namespace CVRManager.Services; public record AssemblyDetails(string FileName, string AssemblyName, string Version, string MelonName, string MelonVersion, string MelonAuthor, string MelonLink, string Sha256); public static class AssemblyReader { public static AssemblyDetails Read(string path) { var file = Path.GetFileName(path); var bytes = File.ReadAllBytes(path); var hash = Convert.ToBase64String(SHA256.HashData(bytes)); string asmName = Path.GetFileNameWithoutExtension(file), version = "", mName = "", mVer = "", mAuthor = "", mLink = ""; try { using var pe = new PEReader(new MemoryStream(bytes)); if (pe.HasMetadata) { var md = pe.GetMetadataReader(); var def = md.GetAssemblyDefinition(); asmName = md.GetString(def.Name); version = def.Version.ToString(); foreach (var h in def.GetCustomAttributes()) { var attr = md.GetCustomAttribute(h); if (!IsMelonInfo(md, attr.Constructor)) continue; var strings = attr.DecodeValue(new Provider()).FixedArguments .Where(a => a.Type is PrimitiveTypeCode.String && a.Value is string).Select(a => (string)a.Value!).ToList(); if (strings.Count > 0) mName = strings[0]; if (strings.Count > 1) mVer = strings[1]; if (strings.Count > 2) mAuthor = strings[2]; if (strings.Count > 3) mLink = strings[3]; break; } } } catch (Exception e) when (e is BadImageFormatException or InvalidOperationException or ArgumentException) { } var fv = System.Diagnostics.FileVersionInfo.GetVersionInfo(path).ProductVersion?.Split('+', '-')[0]; return new AssemblyDetails(file, asmName, string.IsNullOrEmpty(fv) ? version : fv, mName, mVer, mAuthor, mLink, hash); } static bool IsMelonInfo(MetadataReader md, EntityHandle ctor) { if (ctor.Kind != HandleKind.MemberReference) return false; var member = md.GetMemberReference((MemberReferenceHandle)ctor); if (member.Parent.Kind != HandleKind.TypeReference) return false; var type = md.GetTypeReference((TypeReferenceHandle)member.Parent); return md.GetString(type.Name) == "MelonInfoAttribute"; } sealed class Provider : ICustomAttributeTypeProvider { const string SystemType = "System.Type"; public object? GetPrimitiveType(PrimitiveTypeCode typeCode) => typeCode; public object? GetSystemType() => SystemType; public object? GetSZArrayType(object? elementType) => elementType; public object? GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) => new object(); public object? GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var t = reader.GetTypeReference(handle); return reader.GetString(t.Name) == "Type" && reader.GetString(t.Namespace) == "System" ? SystemType : new object(); } public object? GetTypeFromSerializedName(string name) => name; public PrimitiveTypeCode GetUnderlyingEnumType(object? type) => PrimitiveTypeCode.Int32; public bool IsSystemType(object? type) => type is string s && s == SystemType; } }