Files
ADOFAIRenderer/Core/Data/GlobalSettings.cs
T

143 lines
5.4 KiB
C#

using System;
using System.IO;
using MelonLoader;
using MelonLoader.Utils;
using Newtonsoft.Json;
namespace ADOFAIRenderer.Core.Data;
/// <summary>
/// Mod-wide settings that are not part of a render preset, stored as JSON in
/// &lt;game root&gt;/UserData/ADOFAIRenderer/global_settings.json (see <see cref="GlobalSettings"/>).
/// </summary>
internal class GlobalConfig
{
/// <summary>
/// Output directory chosen in the render configuration window; null or empty selects
/// GlobalSettings.DefaultOutputDirectory. Ignored while the directory does not exist (see EffectiveOutputDirectory).
/// </summary>
public string? OutputDirectory { get; set; }
/// <summary>
/// File name format template chosen in the render configuration window; null or empty selects
/// GlobalSettings.DefaultFileNameFormat ("{date} {time}").
/// </summary>
public string? FileNameFormat { get; set; }
/// <summary>Name of the preset last selected or saved in the render configuration window; selected again when it opens.</summary>
public string? LastPresetName { get; set; }
/// <summary>
/// True once PresetManager.EnsureDefaultPresetsCreated has written the built-in preset files. Built-in presets the user
/// deleted afterwards are not written again.
/// </summary>
public bool DefaultPresetsInitialized { get; set; }
}
/// <summary>
/// Loads and saves <see cref="GlobalConfig"/>. The file is read once by the static constructor and again by
/// RenderMod.OnInitializeMelon (Load), and written by Save. Read and write errors are logged as warnings and never
/// thrown; a file that cannot be read yields the default settings. Main thread.
/// </summary>
internal static class GlobalSettings
{
/// <summary>MelonLoader's game root directory, or the application base directory when it is empty.</summary>
private static string GetGameRootDir()
{
var root = MelonEnvironment.GameRootDirectory;
return string.IsNullOrEmpty(root) ? AppDomain.CurrentDomain.BaseDirectory : root;
}
/// <summary>&lt;game root&gt;/UserData/ADOFAIRenderer/global_settings.json.</summary>
private static string SettingsFilePath =>
Path.Combine(GetGameRootDir(), "UserData", "ADOFAIRenderer", "global_settings.json");
/// <summary>
/// Settings in memory. Changes are kept in memory until <see cref="Save"/> is called; <see cref="Load"/> replaces the
/// object.
/// </summary>
public static GlobalConfig Current { get; private set; } = new();
/// <summary>Output directory used when none is configured: &lt;game root&gt;/Render.</summary>
public static string DefaultOutputDirectory =>
Path.Combine(GetGameRootDir(), "Render");
/// <summary>
/// Directory where renders without an explicit output path are written: Current.OutputDirectory when it is set and
/// exists, otherwise <see cref="DefaultOutputDirectory"/>. The default directory is not created here
/// (RenderManager.SetupCaptureDirectory creates it).
/// </summary>
public static string EffectiveOutputDirectory
{
get
{
if (!string.IsNullOrWhiteSpace(Current.OutputDirectory) && Directory.Exists(Current.OutputDirectory))
{
return Current.OutputDirectory;
}
return DefaultOutputDirectory;
}
}
/// <summary>Default file name format template: "{date} {time}".</summary>
public const string DefaultFileNameFormat = "{date} {time}";
/// <summary>
/// File name format used for renders without an explicit output path: Current.FileNameFormat when it is set,
/// otherwise <see cref="DefaultFileNameFormat"/>.
/// </summary>
public static string EffectiveFileNameFormat =>
string.IsNullOrWhiteSpace(Current.FileNameFormat)
? DefaultFileNameFormat
: Current.FileNameFormat;
static GlobalSettings()
{
Load();
}
/// <summary>
/// Replaces <see cref="Current"/> with the content of the settings file. A missing, empty or unreadable file gives the
/// default settings (an unreadable file is logged as a warning).
/// </summary>
public static void Load()
{
try
{
if (File.Exists(SettingsFilePath))
{
var json = File.ReadAllText(SettingsFilePath);
Current = JsonConvert.DeserializeObject<GlobalConfig>(json) ?? new GlobalConfig();
return;
}
}
catch (Exception ex)
{
MelonLogger.Warning($"[GlobalSettings] Failed to load global settings: {ex.Message}");
}
Current = new GlobalConfig();
}
/// <summary>
/// Writes <see cref="Current"/> as indented JSON to the settings file, creating the directory when needed.
/// Errors are logged as warnings.
/// </summary>
public static void Save()
{
try
{
var dir = Path.GetDirectoryName(SettingsFilePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var json = JsonConvert.SerializeObject(Current, Formatting.Indented);
File.WriteAllText(SettingsFilePath, json);
}
catch (Exception ex)
{
MelonLogger.Warning($"[GlobalSettings] Failed to save global settings: {ex.Message}");
}
}
}