using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; 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 GetProviderFor(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; private set; } /// /// Gets the associated with this object. /// public IConfigProvider Provider { get; private set; } internal readonly HashSet Stores = new HashSet(); internal readonly FileInfo File; /// /// Adds an to this object. /// /// the to add to this instance /// if the was not already registered to this object, /// otherwise public bool AddStore(IConfigStore store) => Stores.Add(store); private Config(string name, IConfigProvider provider, FileInfo file) { Name = name; Provider = provider; File = file; } } }