using System;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StardewModdingAPI.Framework;
namespace StardewModdingAPI
{
/// A dynamic configuration class for a mod.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public abstract class Config
{
/*********
** Properties
*********/
/// Manages deprecation warnings.
private static DeprecationManager DeprecationManager;
/*********
** Accessors
*********/
/// The full path to the configuration file.
[JsonIgnore]
public virtual string ConfigLocation { get; protected internal set; }
/// The directory path containing the configuration file.
[JsonIgnore]
public virtual string ConfigDir => Path.GetDirectoryName(this.ConfigLocation);
/*********
** Public methods
*********/
/// Injects types required for backwards compatibility.
/// Manages deprecation warnings.
internal static void Shim(DeprecationManager deprecationManager)
{
Config.DeprecationManager = deprecationManager;
}
/// Construct an instance of the config class.
/// The config class type.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public virtual Config Instance() where T : Config => Activator.CreateInstance();
/// Load the config from the JSON file, saving it to disk if needed.
/// The config class type.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public virtual T LoadConfig() where T : Config
{
// validate
if (string.IsNullOrEmpty(this.ConfigLocation))
{
Log.Error("A config tried to load without specifying a location on the disk.");
return null;
}
// read or generate config
T returnValue;
if (!File.Exists(this.ConfigLocation))
{
T config = this.GenerateDefaultConfig();
config.ConfigLocation = this.ConfigLocation;
returnValue = config;
}
else
{
try
{
T config = JsonConvert.DeserializeObject(File.ReadAllText(this.ConfigLocation));
config.ConfigLocation = this.ConfigLocation;
returnValue = config.UpdateConfig();
}
catch (Exception ex)
{
Log.Error($"Invalid JSON ({this.GetType().Name}): {this.ConfigLocation} \n{ex}");
return this.GenerateDefaultConfig();
}
}
returnValue.WriteConfig();
return returnValue;
}
/// Get the default config values.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public virtual T GenerateDefaultConfig() where T : Config
{
return null;
}
/// Get the current configuration with missing values defaulted.
/// The config class type.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public virtual T UpdateConfig() where T : Config
{
try
{
// get default + user config
JObject defaultConfig = JObject.FromObject(this.Instance().GenerateDefaultConfig());
JObject currentConfig = JObject.FromObject(this);
defaultConfig.Merge(currentConfig, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace });
// cast json object to config
T config = defaultConfig.ToObject();
// update location
config.ConfigLocation = this.ConfigLocation;
return config;
}
catch (Exception ex)
{
Log.Error($"An error occured when updating a config: {ex}");
return this as T;
}
}
/*********
** Protected methods
*********/
/// Construct an instance.
protected Config()
{
Config.DeprecationManager.Warn("the Config class", "1.0", DeprecationLevel.Notice);
Config.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(Mod.BaseConfigPath)}", "1.0"); // typically used to construct config, avoid redundant warnings
}
}
/// Provides extension methods for classes.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public static class ConfigExtensions
{
/// Initialise the configuration. That includes loading, saving, and merging the config file and in memory at a default state. This method should not be used to reload or to resave a config. NOTE: You MUST set your config EQUAL to the return of this method!
/// The config class type.
/// The base configuration to initialise.
/// The base configuration file path.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public static T InitializeConfig(this T baseConfig, string configLocation) where T : Config
{
if (baseConfig == null)
baseConfig = Activator.CreateInstance();
if (string.IsNullOrEmpty(configLocation))
{
Log.Error("A config tried to initialize without specifying a location on the disk.");
return null;
}
baseConfig.ConfigLocation = configLocation;
return baseConfig.LoadConfig();
}
/// Writes the configuration to the JSON file.
/// The config class type.
/// The base configuration to initialise.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public static void WriteConfig(this T baseConfig) where T : Config
{
if (string.IsNullOrEmpty(baseConfig?.ConfigLocation) || string.IsNullOrEmpty(baseConfig.ConfigDir))
{
Log.Error("A config attempted to save when it itself or it's location were null.");
return;
}
string json = JsonConvert.SerializeObject(baseConfig, Formatting.Indented);
if (!Directory.Exists(baseConfig.ConfigDir))
Directory.CreateDirectory(baseConfig.ConfigDir);
if (!File.Exists(baseConfig.ConfigLocation) || !File.ReadAllText(baseConfig.ConfigLocation).SequenceEqual(json))
File.WriteAllText(baseConfig.ConfigLocation, json);
}
/// Rereads the JSON file and merges its values with a default config. NOTE: You MUST set your config EQUAL to the return of this method!
/// The config class type.
/// The base configuration to initialise.
[Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
public static T ReloadConfig(this T baseConfig) where T : Config
{
return baseConfig.LoadConfig();
}
}
}