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(UnityGame.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); } } }