using System.Reflection; using System.Text; using HarmonyLib; using Microsoft.CodeAnalysis; namespace PluginLoader.Compiler; public static class RoslynReferences { private static readonly Dictionary allReferences = new(); private static readonly HashSet referenceBlacklist = new(new[] { "System.ValueTuple" }); public static void GenerateAssemblyList() { if (allReferences.Count > 0) return; var harmonyInfo = typeof(Harmony).Assembly.GetName(); var loadedAssemblies = new Stack(AppDomain.CurrentDomain.GetAssemblies().Where(IsValidReference)); var sb = new StringBuilder(); sb.AppendLine(); var line = "==================================="; sb.AppendLine(line); sb.AppendLine("Assembly References"); sb.AppendLine(line); try { foreach (var a in loadedAssemblies) { // Prevent other Harmony versions from being loaded var name = a.GetName(); if (name.Name == harmonyInfo.Name && name.Version != harmonyInfo.Version) { LogFile.WriteLine( $"WARNING: Multiple Harmony assemblies are loaded. Plugin Loader is using {harmonyInfo} but found {name}"); continue; } AddAssemblyReference(a); sb.AppendLine(a.FullName); } foreach(var a in GetOtherReferences()) { AddAssemblyReference(a); sb.AppendLine(a.FullName); } sb.AppendLine(line); while (loadedAssemblies.Count > 0) { var a = loadedAssemblies.Pop(); foreach (var name in a.GetReferencedAssemblies()) { // Prevent other Harmony versions from being loaded if (name.Name == harmonyInfo.Name && name.Version != harmonyInfo.Version) { LogFile.WriteLine( $"WARNING: Multiple Harmony assemblies are loaded. Plugin Loader is using {harmonyInfo} but found {name}"); continue; } if (!ContainsReference(name) && TryLoadAssembly(name, out var aRef) && IsValidReference(aRef)) { AddAssemblyReference(aRef); sb.AppendLine(name.FullName); loadedAssemblies.Push(aRef); } } } sb.AppendLine(line); } catch (Exception e) { sb.Append("Error: ").Append(e).AppendLine(); } LogFile.WriteLine(sb.ToString(), false); } /// /// This method is used to load references that otherwise would not exist or be optimized out /// private static IEnumerable GetOtherReferences() { yield return typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly; } private static bool ContainsReference(AssemblyName name) { return allReferences.ContainsKey(name.Name); } private static bool TryLoadAssembly(AssemblyName name, out Assembly aRef) { try { aRef = Assembly.Load(name); return true; } catch (IOException) { aRef = null; return false; } } private static void AddAssemblyReference(Assembly a) { var name = a.GetName().Name; if (!allReferences.ContainsKey(name)) allReferences.Add(name, MetadataReference.CreateFromFile(a.Location)); } public static IEnumerable EnumerateAllReferences() { return allReferences.Values; } private static bool IsValidReference(Assembly a) { return !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location) && !referenceBlacklist.Contains(a.GetName().Name); } public static void LoadReference(string name) { try { var aName = new AssemblyName(name); if (!allReferences.ContainsKey(aName.Name)) { var a = Assembly.Load(aName); LogFile.WriteLine("Reference added at runtime: " + a.FullName); MetadataReference aRef = MetadataReference.CreateFromFile(a.Location); allReferences[a.GetName().Name] = aRef; } } catch (IOException) { LogFile.WriteLine("WARNING: Unable to find the assembly '" + name + "'!"); } } }