using System;
using System.IO;
using MelonLoader;
using MelonLoader.Utils;
using Newtonsoft.Json;
namespace ADOFAIRenderer.Core.Data;
///
/// Mod-wide settings that are not part of a render preset, stored as JSON in
/// <game root>/UserData/ADOFAIRenderer/global_settings.json (see ).
///
internal class GlobalConfig
{
///
/// Output directory chosen in the render configuration window; null or empty selects
/// GlobalSettings.DefaultOutputDirectory. Ignored while the directory does not exist (see EffectiveOutputDirectory).
///
public string? OutputDirectory { get; set; }
/// Name of the preset last selected or saved in the render configuration window; selected again when it opens.
public string? LastPresetName { get; set; }
///
/// True once PresetManager.EnsureDefaultPresetsCreated has written the built-in preset files. Built-in presets the user
/// deleted afterwards are not written again.
///
public bool DefaultPresetsInitialized { get; set; }
}
///
/// Loads and saves . 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.
///
internal static class GlobalSettings
{
/// MelonLoader's game root directory, or the application base directory when it is empty.
private static string GetGameRootDir()
{
var root = MelonEnvironment.GameRootDirectory;
return string.IsNullOrEmpty(root) ? AppDomain.CurrentDomain.BaseDirectory : root;
}
/// <game root>/UserData/ADOFAIRenderer/global_settings.json.
private static string SettingsFilePath =>
Path.Combine(GetGameRootDir(), "UserData", "ADOFAIRenderer", "global_settings.json");
///
/// Settings in memory. Changes are kept in memory until is called; replaces the
/// object.
///
public static GlobalConfig Current { get; private set; } = new();
/// Output directory used when none is configured: <game root>/Render.
public static string DefaultOutputDirectory =>
Path.Combine(GetGameRootDir(), "Render");
///
/// Directory where renders without an explicit output path are written: Current.OutputDirectory when it is set and
/// exists, otherwise . The default directory is not created here
/// (RenderManager.SetupCaptureDirectory creates it).
///
public static string EffectiveOutputDirectory
{
get
{
if (!string.IsNullOrWhiteSpace(Current.OutputDirectory) && Directory.Exists(Current.OutputDirectory))
{
return Current.OutputDirectory;
}
return DefaultOutputDirectory;
}
}
static GlobalSettings()
{
Load();
}
///
/// Replaces 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).
///
public static void Load()
{
try
{
if (File.Exists(SettingsFilePath))
{
var json = File.ReadAllText(SettingsFilePath);
Current = JsonConvert.DeserializeObject(json) ?? new GlobalConfig();
return;
}
}
catch (Exception ex)
{
MelonLogger.Warning($"[GlobalSettings] Failed to load global settings: {ex.Message}");
}
Current = new GlobalConfig();
}
///
/// Writes as indented JSON to the settings file, creating the directory when needed.
/// Errors are logged as warnings.
///
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}");
}
}
}