9bb4f85802
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
71 lines
3.5 KiB
C#
71 lines
3.5 KiB
C#
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<object?>
|
|
{
|
|
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;
|
|
}
|
|
}
|