diff --git a/IPA.Loader/Config/Config.cs b/IPA.Loader/Config/Config.cs
index 6d66174e..ecb77848 100644
--- a/IPA.Loader/Config/Config.cs
+++ b/IPA.Loader/Config/Config.cs
@@ -1,174 +1,175 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
-using IPA.Config.Providers;
-using IPA.Utilities;
-#if NET3
-using Net3_Proxy;
-using Path = Net3_Proxy.Path;
-using Array = Net3_Proxy.Array;
-#endif
-
-namespace IPA.Config
-{
- ///
- /// A class to handle updating ConfigProviders automatically
- ///
- public class Config
- {
- static Config()
- {
- JsonConfigProvider.RegisterConfig();
- }
-
- ///
- /// Specifies that a particular parameter is preferred to use a particular .
- /// If it is not available, also specifies backups. If none are available, the default is used.
- ///
- [AttributeUsage(AttributeTargets.Parameter)]
- public sealed class PreferAttribute : Attribute
- {
- ///
- /// The order of preference for the config type.
- ///
- /// the list of config extensions in order of preference
- // ReSharper disable once UnusedAutoPropertyAccessor.Global
- public string[] PreferenceOrder { get; private set; }
-
- ///
- ///
- /// Constructs the attribute with a specific preference list. Each entry is the extension without a '.'
- ///
- /// The preferences in order of preference.
- public PreferAttribute(params string[] preference)
- {
- PreferenceOrder = preference;
- }
- }
-
- ///
- /// Specifies a preferred config name, instead of using the plugin's name.
- ///
- [AttributeUsage(AttributeTargets.Parameter)]
- public sealed class NameAttribute : Attribute
- {
- ///
- /// The name to use for the config.
- ///
- /// the name to use for the config
- // ReSharper disable once UnusedAutoPropertyAccessor.Global
- public string Name { get; private set; }
-
- ///
- ///
- /// Constructs the attribute with a specific name.
- ///
- /// the name to use for the config.
- public NameAttribute(string name)
- {
- Name = name;
- }
- }
-
- private static readonly Dictionary registeredProviders = new Dictionary();
-
- ///
- /// Registers a to use for configs.
- ///
- /// the type to register
- public static void Register() where T : IConfigProvider => Register(typeof(T));
-
- ///
- /// Registers a to use for configs.
- ///
- /// the type to register
- public static void Register(Type type)
- {
- var inst = Activator.CreateInstance(type) as IConfigProvider;
- if (inst == null)
- throw new ArgumentException($"Type not an {nameof(IConfigProvider)}");
-
- if (registeredProviders.ContainsKey(inst.Extension))
- throw new InvalidOperationException($"Extension provider for {inst.Extension} already exists");
-
- registeredProviders.Add(inst.Extension, inst);
- }
-
- ///
- /// Gets a object using the specified list of preferred config types.
- ///
- /// the name of the mod for this config
- /// the preferred config types to try to get
- /// a using the requested format, or of type JSON.
- public static Config GetConfigFor(string configName, params string[] extensions)
- {
- var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json";
- var provider = registeredProviders[chosenExt];
-
- var filename = Path.Combine(BeatSaber.UserDataPath, configName + "." + provider.Extension);
- var config = new Config(configName, provider, new FileInfo(filename));
-
- ConfigRuntime.RegisterConfig(config);
-
- return config;
- }
-
- internal static Config GetConfigFor(string modName, ParameterInfo info)
- {
- var prefs = Array.Empty();
- if (info.GetCustomAttribute() is PreferAttribute prefer)
- prefs = prefer.PreferenceOrder;
- if (info.GetCustomAttribute() is NameAttribute name)
- modName = name.Name;
-
- return GetConfigFor(modName, prefs);
- }
-
- ///
- /// Gets the name associated with this object.
- ///
- public string Name { get; }
- ///
- /// Gets the associated with this object.
- ///
- public IConfigProvider Provider { get; }
-
- internal IConfigStore Store = null;
- internal readonly FileInfo File;
- internal readonly ConfigProvider configProvider;
- internal int Writes = 0;
-
- ///
- /// Sets this object's . Can only be called once.
- ///
- /// the to add to this instance
- /// If this was called before.
- public void SetStore(IConfigStore store)
- {
- if (Store != null)
- throw new InvalidOperationException($"{nameof(SetStore)} can only be called once");
- Store = store;
- ConfigRuntime.ConfigChanged();
- }
-
- ///
- /// Forces a synchronous load from disk.
- ///
- public void LoadSync() => LoadAsync().Wait();
-
- ///
- /// Forces an asynchronous load from disk.
- ///
- public Task LoadAsync() => ConfigRuntime.TriggerFileLoad(this);
-
- private Config(string name, IConfigProvider provider, FileInfo file)
- {
- Name = name; Provider = provider; File = file;
- configProvider = new ConfigProvider(file, provider);
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using IPA.Config.Providers;
+using IPA.Utilities;
+#if NET3
+using Net3_Proxy;
+using Path = Net3_Proxy.Path;
+using Array = Net3_Proxy.Array;
+#endif
+
+namespace IPA.Config
+{
+ ///
+ /// An abstraction of a config file on disk, which handles synchronizing between a memory representation and the
+ /// disk representation.
+ ///
+ public class Config
+ {
+ static Config()
+ {
+ JsonConfigProvider.RegisterConfig();
+ }
+
+ ///
+ /// Specifies that a particular parameter is preferred to use a particular .
+ /// If it is not available, also specifies backups. If none are available, the default is used.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public sealed class PreferAttribute : Attribute
+ {
+ ///
+ /// The order of preference for the config type.
+ ///
+ /// the list of config extensions in order of preference
+ // ReSharper disable once UnusedAutoPropertyAccessor.Global
+ public string[] PreferenceOrder { get; private set; }
+
+ ///
+ ///
+ /// Constructs the attribute with a specific preference list. Each entry is the extension without a '.'
+ ///
+ /// The preferences in order of preference.
+ public PreferAttribute(params string[] preference)
+ {
+ PreferenceOrder = preference;
+ }
+ }
+
+ ///
+ /// Specifies a preferred config name, instead of using the plugin's name.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public sealed class NameAttribute : Attribute
+ {
+ ///
+ /// The name to use for the config.
+ ///
+ /// the name to use for the config
+ // ReSharper disable once UnusedAutoPropertyAccessor.Global
+ public string Name { get; private set; }
+
+ ///
+ ///
+ /// Constructs the attribute with a specific name.
+ ///
+ /// the name to use for the config.
+ public NameAttribute(string name)
+ {
+ Name = name;
+ }
+ }
+
+ private static readonly Dictionary registeredProviders = new Dictionary();
+
+ ///
+ /// Registers a to use for configs.
+ ///
+ /// the type to register
+ public static void Register() where T : IConfigProvider => Register(typeof(T));
+
+ ///
+ /// Registers a to use for configs.
+ ///
+ /// the type to register
+ public static void Register(Type type)
+ {
+ var inst = Activator.CreateInstance(type) as IConfigProvider;
+ if (inst == null)
+ throw new ArgumentException($"Type not an {nameof(IConfigProvider)}");
+
+ if (registeredProviders.ContainsKey(inst.Extension))
+ throw new InvalidOperationException($"Extension provider for {inst.Extension} already exists");
+
+ registeredProviders.Add(inst.Extension, inst);
+ }
+
+ ///
+ /// Gets a object using the specified list of preferred config types.
+ ///
+ /// the name of the mod for this config
+ /// the preferred config types to try to get
+ /// a using the requested format, or of type JSON.
+ public static Config GetConfigFor(string configName, params string[] extensions)
+ {
+ var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json";
+ var provider = registeredProviders[chosenExt];
+
+ var filename = Path.Combine(BeatSaber.UserDataPath, configName + "." + provider.Extension);
+ var config = new Config(configName, provider, new FileInfo(filename));
+
+ ConfigRuntime.RegisterConfig(config);
+
+ return config;
+ }
+
+ internal static Config GetConfigFor(string modName, ParameterInfo info)
+ {
+ var prefs = Array.Empty();
+ if (info.GetCustomAttribute() is PreferAttribute prefer)
+ prefs = prefer.PreferenceOrder;
+ if (info.GetCustomAttribute() is NameAttribute name)
+ modName = name.Name;
+
+ return GetConfigFor(modName, prefs);
+ }
+
+ ///
+ /// Gets the name associated with this object.
+ ///
+ public string Name { get; }
+ ///
+ /// Gets the associated with this object.
+ ///
+ public IConfigProvider Provider { get; }
+
+ internal IConfigStore Store = null;
+ internal readonly FileInfo File;
+ internal readonly ConfigProvider configProvider;
+ internal int Writes = 0;
+
+ ///
+ /// Sets this object's . Can only be called once.
+ ///
+ /// the to add to this instance
+ /// If this was called before.
+ public void SetStore(IConfigStore store)
+ {
+ if (Store != null)
+ throw new InvalidOperationException($"{nameof(SetStore)} can only be called once");
+ Store = store;
+ ConfigRuntime.ConfigChanged();
+ }
+
+ ///
+ /// Forces a synchronous load from disk.
+ ///
+ public void LoadSync() => LoadAsync().Wait();
+
+ ///
+ /// Forces an asynchronous load from disk.
+ ///
+ public Task LoadAsync() => ConfigRuntime.TriggerFileLoad(this);
+
+ private Config(string name, IConfigProvider provider, FileInfo file)
+ {
+ Name = name; Provider = provider; File = file;
+ configProvider = new ConfigProvider(file, provider);
+ }
+ }
+}
diff --git a/IPA.Loader/Loader/PluginInitInjector.cs b/IPA.Loader/Loader/PluginInitInjector.cs
index 06196c33..9d9adc27 100644
--- a/IPA.Loader/Loader/PluginInitInjector.cs
+++ b/IPA.Loader/Loader/PluginInitInjector.cs
@@ -15,6 +15,34 @@ namespace IPA.Loader
///
/// The type that handles value injecting into a plugin's initialization methods.
///
+ ///
+ /// The default injectors and what they provide are shown in this table.
+ ///
+ ///
+ /// Parameter Type
+ /// Injected Value
+ ///
+ /// -
+ ///
+ /// A specialized for the plugin being injected
+ ///
+ /// -
+ ///
+ /// The of the plugin being injected
+ ///
+ /// -
+ ///
+ ///
+ /// A object for the plugin being injected.
+ ///
+ /// These parameters may have and to control
+ /// how it is constructed.
+ ///
+ ///
+ ///
+ ///
+ /// For all of the default injectors, only one of each will be generated, and any later parameters will recieve the same value as the first one.
+ ///
public static class PluginInitInjector
{