using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using BepInEx.Logging;
using Mono.Cecil;
namespace BepInEx.Bootstrap
{
///
/// Provides methods for loading specified types from an assembly.
///
public static class TypeLoader
{
private static DefaultAssemblyResolver resolver;
private static ReaderParameters readerParameters;
public static event AssemblyResolveEventHandler AssemblyResolve;
static TypeLoader()
{
resolver = new DefaultAssemblyResolver();
readerParameters = new ReaderParameters { AssemblyResolver = resolver };
resolver.ResolveFailure += (sender, reference) =>
{
var name = new AssemblyName(reference.FullName);
if (Utility.TryResolveDllAssembly(name, Paths.BepInExAssemblyDirectory, readerParameters, out AssemblyDefinition assembly) ||
Utility.TryResolveDllAssembly(name, Paths.PluginPath, readerParameters, out assembly) ||
Utility.TryResolveDllAssembly(name, Paths.ManagedPath, readerParameters, out assembly))
return assembly;
return AssemblyResolve?.Invoke(sender, reference);
};
}
///
/// Loads a list of types from a directory containing assemblies, that derive from a base type.
///
/// The specific base type to search for.
/// The directory to search for assemblies.
/// Returns a list of found derivative types.
public static Dictionary> FindPluginTypes(string directory, Func typeSelector) where T : class
{
var result = new Dictionary>();
foreach (string dll in Directory.GetFiles(Path.GetFullPath(directory), "*.dll", SearchOption.AllDirectories))
{
try
{
var ass = AssemblyDefinition.ReadAssembly(dll, readerParameters);
var matches = ass.MainModule.Types.Select(typeSelector).Where(t => t != null).ToList();
if (matches.Count == 0)
{
ass.Dispose();
continue;
}
result[ass] = matches;
}
catch (Exception e)
{
Logger.LogError(e.ToString());
}
}
return result;
}
public static string TypeLoadExceptionToString(ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
if (exSub is FileNotFoundException exFileNotFound)
{
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
else if (exSub is FileLoadException exLoad)
{
if (!string.IsNullOrEmpty(exLoad.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exLoad.FusionLog);
}
}
sb.AppendLine();
}
return sb.ToString();
}
}
}