diff --git a/IPA.Loader/Config/ModPrefs.cs b/IPA.Loader/Config/ModPrefs.cs
index b9635c30..e1147395 100644
--- a/IPA.Loader/Config/ModPrefs.cs
+++ b/IPA.Loader/Config/ModPrefs.cs
@@ -103,7 +103,7 @@ namespace IPA.Config
/// Constructs a ModPrefs object for the provide plugin.
///
/// the plugin to get the preferences file for
- public ModPrefs(PluginLoader.PluginMetadata plugin) {
+ public ModPrefs(PluginMetadata plugin) {
_instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "ModPrefs",
$"{plugin.Name}.ini"));
}
diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj
index c2bd030a..1687171b 100644
--- a/IPA.Loader/IPA.Loader.csproj
+++ b/IPA.Loader/IPA.Loader.csproj
@@ -112,15 +112,16 @@
-
+
+
diff --git a/IPA.Loader/Loader/Features/ConfigProviderFeature.cs b/IPA.Loader/Loader/Features/ConfigProviderFeature.cs
index b22c71ee..caec1aad 100644
--- a/IPA.Loader/Loader/Features/ConfigProviderFeature.cs
+++ b/IPA.Loader/Loader/Features/ConfigProviderFeature.cs
@@ -1,65 +1,65 @@
-using System;
-using System.IO;
-
-namespace IPA.Loader.Features
-{
- internal class ConfigProviderFeature : Feature
- {
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {// parameters should be (fully qualified name of provider type)
- if (parameters.Length != 1)
- {
- InvalidMessage = "Incorrect number of parameters";
- return false;
- }
-
- RequireLoaded(meta);
-
- Type getType;
- try
- {
- getType = meta.Assembly.GetType(parameters[0]);
- }
- catch (ArgumentException)
- {
- InvalidMessage = $"Invalid type name {parameters[0]}";
- return false;
- }
- catch (Exception e) when (e is FileNotFoundException || e is FileLoadException || e is BadImageFormatException)
- {
- string filename;
-
- switch (e)
- {
- case FileNotFoundException fn:
- filename = fn.FileName;
- goto hasFilename;
- case FileLoadException fl:
- filename = fl.FileName;
- goto hasFilename;
- case BadImageFormatException bi:
- filename = bi.FileName;
- hasFilename:
- InvalidMessage = $"Could not find {filename} while loading type";
- break;
- default:
- InvalidMessage = $"Error while loading type: {e}";
- break;
- }
-
- return false;
- }
-
- try
- {
- Config.Config.Register(getType);
- return true;
- }
- catch (Exception e)
- {
- InvalidMessage = $"Error generated while creating delegate: {e}";
- return false;
- }
- }
- }
-}
+using System;
+using System.IO;
+
+namespace IPA.Loader.Features
+{
+ internal class ConfigProviderFeature : Feature
+ {
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ {// parameters should be (fully qualified name of provider type)
+ if (parameters.Length != 1)
+ {
+ InvalidMessage = "Incorrect number of parameters";
+ return false;
+ }
+
+ RequireLoaded(meta);
+
+ Type getType;
+ try
+ {
+ getType = meta.Assembly.GetType(parameters[0]);
+ }
+ catch (ArgumentException)
+ {
+ InvalidMessage = $"Invalid type name {parameters[0]}";
+ return false;
+ }
+ catch (Exception e) when (e is FileNotFoundException || e is FileLoadException || e is BadImageFormatException)
+ {
+ string filename;
+
+ switch (e)
+ {
+ case FileNotFoundException fn:
+ filename = fn.FileName;
+ goto hasFilename;
+ case FileLoadException fl:
+ filename = fl.FileName;
+ goto hasFilename;
+ case BadImageFormatException bi:
+ filename = bi.FileName;
+ hasFilename:
+ InvalidMessage = $"Could not find {filename} while loading type";
+ break;
+ default:
+ InvalidMessage = $"Error while loading type: {e}";
+ break;
+ }
+
+ return false;
+ }
+
+ try
+ {
+ Config.Config.Register(getType);
+ return true;
+ }
+ catch (Exception e)
+ {
+ InvalidMessage = $"Error generated while creating delegate: {e}";
+ return false;
+ }
+ }
+ }
+}
diff --git a/IPA.Loader/Loader/Features/DefineFeature.cs b/IPA.Loader/Loader/Features/DefineFeature.cs
index 092c90e4..6835cc32 100644
--- a/IPA.Loader/Loader/Features/DefineFeature.cs
+++ b/IPA.Loader/Loader/Features/DefineFeature.cs
@@ -1,68 +1,74 @@
-using System;
-using System.IO;
-
-namespace IPA.Loader.Features
-{
- internal class DefineFeature : Feature
- {
- public static bool NewFeature = true;
-
- protected internal override bool StoreOnPlugin => false;
-
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- { // parameters should be (name, fully qualified type)
- if (parameters.Length != 2)
- {
- InvalidMessage = "Incorrect number of parameters";
- return false;
- }
-
- RequireLoaded(meta);
-
- Type type;
- try
- {
- type = meta.Assembly.GetType(parameters[1]);
- }
- catch (ArgumentException)
- {
- InvalidMessage = $"Invalid type name {parameters[1]}";
- return false;
- }
- catch (Exception e) when (e is FileNotFoundException || e is FileLoadException || e is BadImageFormatException)
- {
- var filename = "";
-
- switch (e)
- {
- case FileNotFoundException fn:
- filename = fn.FileName;
- break;
- case FileLoadException fl:
- filename = fl.FileName;
- break;
- case BadImageFormatException bi:
- filename = bi.FileName;
- break;
- }
-
- InvalidMessage = $"Could not find {filename} while loading type";
- return false;
- }
-
- try
- {
- if (RegisterFeature(parameters[0], type)) return NewFeature = true;
-
- InvalidMessage = $"Feature with name {parameters[0]} already exists";
- return false;
-
- }
- catch (ArgumentException)
- {
- InvalidMessage = $"{type.FullName} not a subclass of {nameof(Feature)}";
- return false;
- }
- }
- }
-}
+using System;
+using System.IO;
+
+namespace IPA.Loader.Features
+{
+ internal class DefineFeature : Feature
+ {
+ public static bool NewFeature = true;
+
+ protected internal override bool StoreOnPlugin => false;
+
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ { // parameters should be (name, fully qualified type)
+ if (parameters.Length != 2)
+ {
+ InvalidMessage = "Incorrect number of parameters";
+ return false;
+ }
+
+ RequireLoaded(meta);
+
+ Type type;
+ try
+ {
+ type = meta.Assembly.GetType(parameters[1]);
+ }
+ catch (ArgumentException)
+ {
+ InvalidMessage = $"Invalid type name {parameters[1]}";
+ return false;
+ }
+ catch (Exception e) when (e is FileNotFoundException || e is FileLoadException || e is BadImageFormatException)
+ {
+ var filename = "";
+
+ switch (e)
+ {
+ case FileNotFoundException fn:
+ filename = fn.FileName;
+ break;
+ case FileLoadException fl:
+ filename = fl.FileName;
+ break;
+ case BadImageFormatException bi:
+ filename = bi.FileName;
+ break;
+ }
+
+ InvalidMessage = $"Could not find {filename} while loading type";
+ return false;
+ }
+
+ if (type == null)
+ {
+ InvalidMessage = $"Invalid type name {parameters[1]}";
+ return false;
+ }
+
+ try
+ {
+ if (RegisterFeature(parameters[0], type)) return NewFeature = true;
+
+ InvalidMessage = $"Feature with name {parameters[0]} already exists";
+ return false;
+
+ }
+ catch (ArgumentException)
+ {
+ InvalidMessage = $"{type.FullName} not a subclass of {nameof(Feature)}";
+ return false;
+ }
+ }
+ }
+}
diff --git a/IPA.Loader/Loader/Features/Feature.cs b/IPA.Loader/Loader/Features/Feature.cs
index b790f2c0..039d3d3b 100644
--- a/IPA.Loader/Loader/Features/Feature.cs
+++ b/IPA.Loader/Loader/Features/Feature.cs
@@ -1,224 +1,224 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-#if NET3
-using Net3_Proxy;
-#endif
-
-namespace IPA.Loader.Features
-{
- ///
- /// The root interface for a mod Feature.
- ///
- ///
- /// Avoid storing any data in any subclasses. If you do, it may result in a failure to load the feature.
- ///
- public abstract class Feature
- {
- ///
- /// Initializes the feature with the parameters provided in the definition.
- ///
- /// Note: When no parenthesis are provided, is an empty array.
- ///
- ///
- /// This gets called BEFORE *your* `Init` method.
- ///
- /// Returning does *not* prevent the plugin from being loaded. It simply prevents the feature from being used.
- ///
- /// the metadata of the plugin that is being prepared
- /// the parameters passed to the feature definition, or null
- /// if the feature is valid for the plugin, otherwise
- public abstract bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters);
-
- ///
- /// Evaluates the Feature for use in conditional meta-Features. This should be re-calculated on every call, unless it can be proven to not change.
- ///
- /// This will be called on every feature that returns from
- ///
- /// the truthiness of the Feature.
- public virtual bool Evaluate() => true;
-
- ///
- /// The message to be logged when the feature is not valid for a plugin.
- /// This should also be set whenever either or returns false.
- ///
- /// the message to show when the feature is marked invalid
- public virtual string InvalidMessage { get; protected set; }
-
- ///
- /// Called before a plugin is loaded. This should never throw an exception. An exception will abort the loading of the plugin with an error.
- ///
- ///
- /// The assembly will still be loaded, but the plugin will not be constructed if this returns .
- /// Any features it defines, for example, will still be loaded.
- ///
- /// the plugin about to be loaded
- /// whether or not the plugin should be loaded
- public virtual bool BeforeLoad(PluginLoader.PluginMetadata plugin) => true;
-
- ///
- /// Called before a plugin's `Init` method is called. This will not be called if there is no `Init` method. This should never throw an exception. An exception will abort the loading of the plugin with an error.
- ///
- /// the plugin to be initialized
- /// whether or not to call the Init method
- public virtual bool BeforeInit(PluginLoader.PluginInfo plugin) => true;
-
- ///
- /// Called after a plugin has been fully initialized, whether or not there is an `Init` method. This should never throw an exception.
- ///
- /// the plugin that was just initialized
- /// the instance of the plugin being initialized
- public virtual void AfterInit(PluginLoader.PluginInfo plugin, IPlugin pluginInstance) => AfterInit(plugin);
-
- ///
- /// Called after a plugin has been fully initialized, whether or not there is an `Init` method. This should never throw an exception.
- ///
- /// the plugin that was just initialized
- public virtual void AfterInit(PluginLoader.PluginInfo plugin) { }
-
- ///
- /// Ensures a plugin's assembly is loaded. Do not use unless you need to.
- ///
- /// the plugin to ensure is loaded.
- protected void RequireLoaded(PluginLoader.PluginMetadata plugin) => PluginLoader.Load(plugin);
-
- ///
- /// Defines whether or not this feature will be accessible from the plugin metadata once loaded.
- ///
- /// if this will be stored on the plugin metadata, otherwise
- protected internal virtual bool StoreOnPlugin => true;
-
- static Feature()
- {
- Reset();
- }
-
- internal static void Reset()
- {
- featureTypes = new Dictionary
- {
- { "define-feature", typeof(DefineFeature) }
- };
- }
-
- private static Dictionary featureTypes;
-
- internal static bool HasFeature(string name) => featureTypes.ContainsKey(name);
-
- internal static bool RegisterFeature(string name, Type type)
- {
- if (!typeof(Feature).IsAssignableFrom(type))
- throw new ArgumentException($"Feature type not subclass of {nameof(Feature)}", nameof(type));
- if (featureTypes.ContainsKey(name)) return false;
- featureTypes.Add(name, type);
- return true;
- }
-
- internal struct FeatureParse
- {
- public readonly string Name;
- public readonly string[] Parameters;
-
- public FeatureParse(string name, string[] parameters)
- {
- Name = name;
- Parameters = parameters;
- }
- }
-
- // returns false with both outs null for no such feature
- internal static bool TryParseFeature(string featureString, PluginLoader.PluginMetadata plugin,
- out Feature feature, out Exception failException, out bool featureValid, out FeatureParse parsed,
- FeatureParse? preParsed = null)
- {
- failException = null;
- feature = null;
- featureValid = false;
-
- if (preParsed == null)
- {
- var builder = new StringBuilder();
- string name = null;
- var parameters = new List();
-
- bool escape = false;
- int parens = 0;
- bool removeWhitespace = true;
- foreach (var chr in featureString)
- {
- if (escape)
- {
- builder.Append(chr);
- escape = false;
- }
- else
- {
- switch (chr)
- {
- case '\\':
- escape = true;
- break;
- case '(':
- parens++;
- if (parens != 1) goto default;
- removeWhitespace = true;
- name = builder.ToString();
- builder.Clear();
- break;
- case ')':
- parens--;
- if (parens != 0) goto default;
- goto case ',';
- case ',':
- if (parens > 1) goto default;
- parameters.Add(builder.ToString());
- builder.Clear();
- removeWhitespace = true;
- break;
- default:
- if (removeWhitespace && !char.IsWhiteSpace(chr))
- removeWhitespace = false;
- if (!removeWhitespace)
- builder.Append(chr);
- break;
- }
- }
- }
-
- if (name == null)
- name = builder.ToString();
-
- parsed = new FeatureParse(name, parameters.ToArray());
-
- if (parens != 0)
- {
- failException = new Exception("Malformed feature definition");
- return false;
- }
- }
- else
- parsed = preParsed.Value;
-
- if (!featureTypes.TryGetValue(parsed.Name, out var featureType))
- return false;
-
- try
- {
- if (!(Activator.CreateInstance(featureType) is Feature aFeature))
- {
- failException = new InvalidCastException("Feature type not a subtype of Feature");
- return false;
- }
-
- featureValid = aFeature.Initialize(plugin, parsed.Parameters);
- feature = aFeature;
- return true;
- }
- catch (Exception e)
- {
- failException = e;
- return false;
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Text;
+#if NET3
+using Net3_Proxy;
+#endif
+
+namespace IPA.Loader.Features
+{
+ ///
+ /// The root interface for a mod Feature.
+ ///
+ ///
+ /// Avoid storing any data in any subclasses. If you do, it may result in a failure to load the feature.
+ ///
+ public abstract class Feature
+ {
+ ///
+ /// Initializes the feature with the parameters provided in the definition.
+ ///
+ /// Note: When no parenthesis are provided, is an empty array.
+ ///
+ ///
+ /// This gets called BEFORE *your* `Init` method.
+ ///
+ /// Returning does *not* prevent the plugin from being loaded. It simply prevents the feature from being used.
+ ///
+ /// the metadata of the plugin that is being prepared
+ /// the parameters passed to the feature definition, or null
+ /// if the feature is valid for the plugin, otherwise
+ public abstract bool Initialize(PluginMetadata meta, string[] parameters);
+
+ ///
+ /// Evaluates the Feature for use in conditional meta-Features. This should be re-calculated on every call, unless it can be proven to not change.
+ ///
+ /// This will be called on every feature that returns from
+ ///
+ /// the truthiness of the Feature.
+ public virtual bool Evaluate() => true;
+
+ ///
+ /// The message to be logged when the feature is not valid for a plugin.
+ /// This should also be set whenever either or returns false.
+ ///
+ /// the message to show when the feature is marked invalid
+ public virtual string InvalidMessage { get; protected set; }
+
+ ///
+ /// Called before a plugin is loaded. This should never throw an exception. An exception will abort the loading of the plugin with an error.
+ ///
+ ///
+ /// The assembly will still be loaded, but the plugin will not be constructed if this returns .
+ /// Any features it defines, for example, will still be loaded.
+ ///
+ /// the plugin about to be loaded
+ /// whether or not the plugin should be loaded
+ public virtual bool BeforeLoad(PluginMetadata plugin) => true;
+
+ ///
+ /// Called before a plugin's `Init` method is called. This will not be called if there is no `Init` method. This should never throw an exception. An exception will abort the loading of the plugin with an error.
+ ///
+ /// the plugin to be initialized
+ /// whether or not to call the Init method
+ public virtual bool BeforeInit(PluginLoader.PluginInfo plugin) => true;
+
+ ///
+ /// Called after a plugin has been fully initialized, whether or not there is an `Init` method. This should never throw an exception.
+ ///
+ /// the plugin that was just initialized
+ /// the instance of the plugin being initialized
+ public virtual void AfterInit(PluginLoader.PluginInfo plugin, IPlugin pluginInstance) => AfterInit(plugin);
+
+ ///
+ /// Called after a plugin has been fully initialized, whether or not there is an `Init` method. This should never throw an exception.
+ ///
+ /// the plugin that was just initialized
+ public virtual void AfterInit(PluginLoader.PluginInfo plugin) { }
+
+ ///
+ /// Ensures a plugin's assembly is loaded. Do not use unless you need to.
+ ///
+ /// the plugin to ensure is loaded.
+ protected void RequireLoaded(PluginMetadata plugin) => PluginLoader.Load(plugin);
+
+ ///
+ /// Defines whether or not this feature will be accessible from the plugin metadata once loaded.
+ ///
+ /// if this will be stored on the plugin metadata, otherwise
+ protected internal virtual bool StoreOnPlugin => true;
+
+ static Feature()
+ {
+ Reset();
+ }
+
+ internal static void Reset()
+ {
+ featureTypes = new Dictionary
+ {
+ { "define-feature", typeof(DefineFeature) }
+ };
+ }
+
+ private static Dictionary featureTypes;
+
+ internal static bool HasFeature(string name) => featureTypes.ContainsKey(name);
+
+ internal static bool RegisterFeature(string name, Type type)
+ {
+ if (!typeof(Feature).IsAssignableFrom(type))
+ throw new ArgumentException($"Feature type not subclass of {nameof(Feature)}", nameof(type));
+ if (featureTypes.ContainsKey(name)) return false;
+ featureTypes.Add(name, type);
+ return true;
+ }
+
+ internal struct FeatureParse
+ {
+ public readonly string Name;
+ public readonly string[] Parameters;
+
+ public FeatureParse(string name, string[] parameters)
+ {
+ Name = name;
+ Parameters = parameters;
+ }
+ }
+
+ // returns false with both outs null for no such feature
+ internal static bool TryParseFeature(string featureString, PluginMetadata plugin,
+ out Feature feature, out Exception failException, out bool featureValid, out FeatureParse parsed,
+ FeatureParse? preParsed = null)
+ {
+ failException = null;
+ feature = null;
+ featureValid = false;
+
+ if (preParsed == null)
+ {
+ var builder = new StringBuilder();
+ string name = null;
+ var parameters = new List();
+
+ bool escape = false;
+ int parens = 0;
+ bool removeWhitespace = true;
+ foreach (var chr in featureString)
+ {
+ if (escape)
+ {
+ builder.Append(chr);
+ escape = false;
+ }
+ else
+ {
+ switch (chr)
+ {
+ case '\\':
+ escape = true;
+ break;
+ case '(':
+ parens++;
+ if (parens != 1) goto default;
+ removeWhitespace = true;
+ name = builder.ToString();
+ builder.Clear();
+ break;
+ case ')':
+ parens--;
+ if (parens != 0) goto default;
+ goto case ',';
+ case ',':
+ if (parens > 1) goto default;
+ parameters.Add(builder.ToString());
+ builder.Clear();
+ removeWhitespace = true;
+ break;
+ default:
+ if (removeWhitespace && !char.IsWhiteSpace(chr))
+ removeWhitespace = false;
+ if (!removeWhitespace)
+ builder.Append(chr);
+ break;
+ }
+ }
+ }
+
+ if (name == null)
+ name = builder.ToString();
+
+ parsed = new FeatureParse(name, parameters.ToArray());
+
+ if (parens != 0)
+ {
+ failException = new Exception("Malformed feature definition");
+ return false;
+ }
+ }
+ else
+ parsed = preParsed.Value;
+
+ if (!featureTypes.TryGetValue(parsed.Name, out var featureType))
+ return false;
+
+ try
+ {
+ if (!(Activator.CreateInstance(featureType) is Feature aFeature))
+ {
+ failException = new InvalidCastException("Feature type not a subtype of Feature");
+ return false;
+ }
+
+ featureValid = aFeature.Initialize(plugin, parsed.Parameters);
+ feature = aFeature;
+ return true;
+ }
+ catch (Exception e)
+ {
+ failException = e;
+ return false;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/IPA.Loader/Loader/Features/InitInjectorFeature.cs b/IPA.Loader/Loader/Features/InitInjectorFeature.cs
index d4f069dc..af6487f9 100644
--- a/IPA.Loader/Loader/Features/InitInjectorFeature.cs
+++ b/IPA.Loader/Loader/Features/InitInjectorFeature.cs
@@ -1,102 +1,105 @@
-using System;
-using System.IO;
-using System.Reflection;
-
-namespace IPA.Loader.Features
-{
- internal class InitInjectorFeature : Feature
- {
- protected internal override bool StoreOnPlugin => false;
-
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- { // parameters should be (assembly qualified lookup type, [fully qualified type]:[method name])
- // method should be static
- if (parameters.Length != 2)
- {
- InvalidMessage = "Incorrect number of parameters";
- return false;
- }
-
- RequireLoaded(meta);
-
- var methodParts = parameters[1].Split(':');
-
- var type = Type.GetType(parameters[0], false);
- if (type == null)
- {
- InvalidMessage = $"Could not find type {parameters[0]}";
- return false;
- }
-
- Type getType;
- try
- {
- getType = meta.Assembly.GetType(methodParts[0]);
- }
- catch (ArgumentException)
- {
- InvalidMessage = $"Invalid type name {methodParts[0]}";
- return false;
- }
- catch (Exception e) when (e is FileNotFoundException || e is FileLoadException || e is BadImageFormatException)
- {
- string filename;
-
- switch (e)
- {
- case FileNotFoundException fn:
- filename = fn.FileName;
- goto hasFilename;
- case FileLoadException fl:
- filename = fl.FileName;
- goto hasFilename;
- case BadImageFormatException bi:
- filename = bi.FileName;
- hasFilename:
- InvalidMessage = $"Could not find {filename} while loading type";
- break;
- default:
- InvalidMessage = $"Error while loading type: {e}";
- break;
- }
-
- return false;
- }
-
- MethodInfo method;
- try
- {
- method = getType.GetMethod(methodParts[1], BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
- null, new[]
- {
- typeof(object),
- typeof(ParameterInfo),
- typeof(PluginLoader.PluginMetadata)
- }, new ParameterModifier[0]);
- }
- catch (Exception e)
- {
- InvalidMessage = $"Error while loading type: {e}";
- return false;
- }
-
- if (method == null)
- {
- InvalidMessage = $"Could not find method {methodParts[1]} in type {methodParts[0]}";
- return false;
- }
-
- try
- {
- var del = (PluginInitInjector.InjectParameter)Delegate.CreateDelegate(typeof(PluginInitInjector.InjectParameter), null, method);
- PluginInitInjector.AddInjector(type, del);
- return true;
- }
- catch (Exception e)
- {
- InvalidMessage = $"Error generated while creating delegate: {e}";
- return false;
- }
- }
- }
-}
+using System;
+using System.IO;
+using System.Reflection;
+#if NET3
+using Array = Net3_Proxy.Array;
+#endif
+
+namespace IPA.Loader.Features
+{
+ internal class InitInjectorFeature : Feature
+ {
+ protected internal override bool StoreOnPlugin => false;
+
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ { // parameters should be (assembly qualified lookup type, [fully qualified type]:[method name])
+ // method should be static
+ if (parameters.Length != 2)
+ {
+ InvalidMessage = "Incorrect number of parameters";
+ return false;
+ }
+
+ RequireLoaded(meta);
+
+ var methodParts = parameters[1].Split(':');
+
+ var type = Type.GetType(parameters[0], false);
+ if (type == null)
+ {
+ InvalidMessage = $"Could not find type {parameters[0]}";
+ return false;
+ }
+
+ Type getType;
+ try
+ {
+ getType = meta.Assembly.GetType(methodParts[0]);
+ }
+ catch (ArgumentException)
+ {
+ InvalidMessage = $"Invalid type name {methodParts[0]}";
+ return false;
+ }
+ catch (Exception e) when (e is FileNotFoundException || e is FileLoadException || e is BadImageFormatException)
+ {
+ string filename;
+
+ switch (e)
+ {
+ case FileNotFoundException fn:
+ filename = fn.FileName;
+ goto hasFilename;
+ case FileLoadException fl:
+ filename = fl.FileName;
+ goto hasFilename;
+ case BadImageFormatException bi:
+ filename = bi.FileName;
+ hasFilename:
+ InvalidMessage = $"Could not find {filename} while loading type";
+ break;
+ default:
+ InvalidMessage = $"Error while loading type: {e}";
+ break;
+ }
+
+ return false;
+ }
+
+ MethodInfo method;
+ try
+ {
+ method = getType.GetMethod(methodParts[1], BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
+ null, new[]
+ {
+ typeof(object),
+ typeof(ParameterInfo),
+ typeof(PluginMetadata)
+ }, Array.Empty());
+ }
+ catch (Exception e)
+ {
+ InvalidMessage = $"Error while loading type: {e}";
+ return false;
+ }
+
+ if (method == null)
+ {
+ InvalidMessage = $"Could not find method {methodParts[1]} in type {methodParts[0]}";
+ return false;
+ }
+
+ try
+ {
+ var del = (PluginInitInjector.InjectParameter)Delegate.CreateDelegate(typeof(PluginInitInjector.InjectParameter), null, method);
+ PluginInitInjector.AddInjector(type, del);
+ return true;
+ }
+ catch (Exception e)
+ {
+ InvalidMessage = $"Error generated while creating delegate: {e}";
+ return false;
+ }
+ }
+ }
+}
diff --git a/IPA.Loader/Loader/Features/NoRuntimeEnableFeature.cs b/IPA.Loader/Loader/Features/NoRuntimeEnableFeature.cs
deleted file mode 100644
index 36fe6cfb..00000000
--- a/IPA.Loader/Loader/Features/NoRuntimeEnableFeature.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IPA.Loader.Features
-{
- internal class NoRuntimeEnableFeature : Feature
- {
- internal static bool HaveLoadedPlugins = false;
-
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {
- return parameters.Length == 0;
- }
-
- public override bool BeforeLoad(PluginLoader.PluginMetadata plugin)
- {
- return !HaveLoadedPlugins;
- }
-
- public override string InvalidMessage
- {
- get => "Plugin requested to not be loaded after initial plugin load";
- protected set { }
- }
- }
-}
diff --git a/IPA.Loader/Loader/Features/NoUpdateFeature.cs b/IPA.Loader/Loader/Features/NoUpdateFeature.cs
index ed5ebd97..afe38972 100644
--- a/IPA.Loader/Loader/Features/NoUpdateFeature.cs
+++ b/IPA.Loader/Loader/Features/NoUpdateFeature.cs
@@ -1,12 +1,12 @@
-namespace IPA.Loader.Features
-{
- internal class NoUpdateFeature : Feature
- {
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {
- return meta.Id != null;
- }
-
- public override string InvalidMessage { get; protected set; } = "No ID specified; cannot update anyway";
- }
-}
+namespace IPA.Loader.Features
+{
+ internal class NoUpdateFeature : Feature
+ {
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ {
+ return meta.Id != null;
+ }
+
+ public override string InvalidMessage { get; protected set; } = "No ID specified; cannot update anyway";
+ }
+}
diff --git a/IPA.Loader/Loader/Features/PrintFeature.cs b/IPA.Loader/Loader/Features/PrintFeature.cs
index 236aeb23..2acc48d8 100644
--- a/IPA.Loader/Loader/Features/PrintFeature.cs
+++ b/IPA.Loader/Loader/Features/PrintFeature.cs
@@ -1,32 +1,32 @@
-
-using IPA.Logging;
-
-namespace IPA.Loader.Features
-{
- internal class PrintFeature : Feature
- {
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {
- Logger.features.Info($"{meta.Name}: {string.Join(" ", parameters)}");
- return true;
- }
- }
-
- internal class DebugFeature : Feature
- {
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {
- Logger.features.Debug($"{meta.Name}: {string.Join(" ", parameters)}");
- return true;
- }
- }
-
- internal class WarnFeature : Feature
- {
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {
- Logger.features.Warn($"{meta.Name}: {string.Join(" ", parameters)}");
- return true;
- }
- }
-}
+
+using IPA.Logging;
+
+namespace IPA.Loader.Features
+{
+ internal class PrintFeature : Feature
+ {
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ {
+ Logger.features.Info($"{meta.Name}: {string.Join(" ", parameters)}");
+ return true;
+ }
+ }
+
+ internal class DebugFeature : Feature
+ {
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ {
+ Logger.features.Debug($"{meta.Name}: {string.Join(" ", parameters)}");
+ return true;
+ }
+ }
+
+ internal class WarnFeature : Feature
+ {
+ public override bool Initialize(PluginMetadata meta, string[] parameters)
+ {
+ Logger.features.Warn($"{meta.Name}: {string.Join(" ", parameters)}");
+ return true;
+ }
+ }
+}
diff --git a/IPA.Loader/Loader/PluginExecutor.cs b/IPA.Loader/Loader/PluginExecutor.cs
new file mode 100644
index 00000000..f7a0bfc4
--- /dev/null
+++ b/IPA.Loader/Loader/PluginExecutor.cs
@@ -0,0 +1,189 @@
+using IPA.Logging;
+using IPA.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Linq.Expressions;
+#if NET4
+using Task = System.Threading.Tasks.Task;
+#endif
+#if NET3
+using Net3_Proxy;
+using Path = Net3_Proxy.Path;
+using File = Net3_Proxy.File;
+using Directory = Net3_Proxy.Directory;
+#endif
+
+namespace IPA.Loader
+{
+ internal class PluginExecutor
+ {
+ public PluginMetadata Metadata { get; }
+ public PluginExecutor(PluginMetadata meta)
+ {
+ Metadata = meta;
+ PrepareDelegates();
+ }
+
+
+ private object pluginObject = null;
+ private Func CreatePlugin { get; set; }
+ private Action