2018-05-13 19:49:26 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2022-02-27 05:45:31 +00:00
|
|
|
|
using System.Diagnostics;
|
2018-05-13 19:49:26 +00:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
2022-02-27 05:45:31 +00:00
|
|
|
|
using static PKHeX.WinForms.PluginLoadSetting;
|
2018-05-13 19:49:26 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
namespace PKHeX.WinForms;
|
|
|
|
|
|
|
|
|
|
public static class PluginLoader
|
2018-05-13 19:49:26 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
public static IEnumerable<T> LoadPlugins<T>(string pluginPath, PluginLoadSetting loadSetting) where T : class
|
2018-05-13 19:49:26 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
var dllFileNames = !Directory.Exists(pluginPath)
|
|
|
|
|
? Array.Empty<string>() // Don't immediately return, as we may be loading plugins merged with this .exe
|
|
|
|
|
: Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.AllDirectories);
|
|
|
|
|
var assemblies = GetAssemblies(dllFileNames, loadSetting);
|
|
|
|
|
var pluginTypes = GetPluginsOfType<T>(assemblies);
|
|
|
|
|
return LoadPlugins<T>(pluginTypes);
|
|
|
|
|
}
|
2018-07-24 04:36:26 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
private static IEnumerable<T> LoadPlugins<T>(IEnumerable<Type> pluginTypes) where T : class
|
|
|
|
|
{
|
|
|
|
|
foreach (var t in pluginTypes)
|
2018-05-13 19:49:26 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
T? activate;
|
|
|
|
|
try { activate = (T?)Activator.CreateInstance(t); }
|
|
|
|
|
catch (Exception ex)
|
2020-10-18 18:02:39 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
Debug.WriteLine($"Unable to load plugin [{t.Name}]: {t.FullName}");
|
|
|
|
|
Debug.WriteLine(ex.Message);
|
|
|
|
|
continue;
|
2020-10-18 18:02:39 +00:00
|
|
|
|
}
|
2022-02-27 05:45:31 +00:00
|
|
|
|
if (activate != null)
|
|
|
|
|
yield return activate;
|
2018-05-13 19:49:26 +00:00
|
|
|
|
}
|
2022-02-27 05:45:31 +00:00
|
|
|
|
}
|
2018-07-24 04:36:26 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
private static IEnumerable<Assembly> GetAssemblies(IEnumerable<string> dllFileNames, PluginLoadSetting loadSetting)
|
|
|
|
|
{
|
|
|
|
|
var loadMethod = GetPluginLoadMethod(loadSetting);
|
|
|
|
|
var assemblies = dllFileNames.Select(loadMethod);
|
|
|
|
|
if (loadSetting.IsMerged())
|
|
|
|
|
assemblies = assemblies.Concat(new[] { Assembly.GetExecutingAssembly() }); // load merged too
|
|
|
|
|
return assemblies;
|
|
|
|
|
}
|
2018-07-24 04:36:26 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
private static Func<string, Assembly> GetPluginLoadMethod(PluginLoadSetting pls) => pls switch
|
|
|
|
|
{
|
|
|
|
|
LoadFrom or LoadFromMerged => Assembly.LoadFrom,
|
|
|
|
|
LoadFile or LoadFileMerged => Assembly.LoadFile,
|
|
|
|
|
UnsafeLoadFrom or UnsafeMerged => Assembly.UnsafeLoadFrom,
|
|
|
|
|
_ => throw new IndexOutOfRangeException($"PluginLoadSetting: {pls} method not defined."),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public static bool IsMerged(this PluginLoadSetting loadSetting) => loadSetting is LoadFromMerged or LoadFileMerged or UnsafeMerged;
|
2021-10-06 21:48:17 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
private static IEnumerable<Type> GetPluginsOfType<T>(IEnumerable<Assembly> assemblies)
|
|
|
|
|
{
|
|
|
|
|
var interfaceTypeName = typeof(T).FullName;
|
|
|
|
|
if (interfaceTypeName is null)
|
|
|
|
|
return Array.Empty<Type>();
|
|
|
|
|
return assemblies.SelectMany(z => GetPluginTypes(z, interfaceTypeName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static IEnumerable<Type> GetPluginTypes(Assembly z, string interfaceTypeName)
|
|
|
|
|
{
|
|
|
|
|
try
|
2018-05-13 19:49:26 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
// Handle Costura merged plugin dll's; need to Attach for them to correctly retrieve their dependencies.
|
|
|
|
|
var assemblyLoaderType = z.GetType("Costura.AssemblyLoader", false);
|
|
|
|
|
var attachMethod = assemblyLoaderType?.GetMethod("Attach", BindingFlags.Static | BindingFlags.Public);
|
|
|
|
|
attachMethod?.Invoke(null, Array.Empty<object>());
|
2018-07-24 04:36:26 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
var types = z.GetTypes();
|
|
|
|
|
return types.Where(type => IsTypePlugin(type, interfaceTypeName));
|
|
|
|
|
}
|
|
|
|
|
// User plugins can be out of date, with mismatching API surfaces.
|
|
|
|
|
catch (Exception ex)
|
2018-07-24 04:36:26 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
Debug.WriteLine($"Unable to load plugin [{interfaceTypeName}]: {z.FullName}");
|
|
|
|
|
Debug.WriteLine(ex.Message);
|
|
|
|
|
if (ex is not ReflectionTypeLoadException rtle)
|
|
|
|
|
return Array.Empty<Type>();
|
2021-11-27 21:54:38 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
foreach (var le in rtle.LoaderExceptions)
|
2018-05-13 19:49:26 +00:00
|
|
|
|
{
|
2022-02-27 05:45:31 +00:00
|
|
|
|
if (le is not null)
|
|
|
|
|
Debug.WriteLine(le.Message);
|
2018-05-13 19:49:26 +00:00
|
|
|
|
}
|
2022-02-27 05:45:31 +00:00
|
|
|
|
return Array.Empty<Type>();
|
2018-05-13 19:49:26 +00:00
|
|
|
|
}
|
2022-02-27 05:45:31 +00:00
|
|
|
|
}
|
2018-07-24 04:36:26 +00:00
|
|
|
|
|
2022-02-27 05:45:31 +00:00
|
|
|
|
private static bool IsTypePlugin(Type type, string interfaceTypeName)
|
|
|
|
|
{
|
|
|
|
|
if (type.IsInterface || type.IsAbstract)
|
|
|
|
|
return false;
|
|
|
|
|
if (type.GetInterface(interfaceTypeName) == null)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
2018-05-13 19:49:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|