using Dalamud.Configuration; using Dalamud.Interface.Internal.Notifications; using Newtonsoft.Json; using OtterGui; using OtterGui.Classes; using OtterGui.Filesystem; using OtterGui.Widgets; using Penumbra.Import.Structs; using Penumbra.Interop.Services; using Penumbra.Mods; using Penumbra.Mods.Manager; using Penumbra.Services; using Penumbra.UI.Classes; using Penumbra.UI.ResourceWatcher; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Penumbra; [Serializable] public class Configuration : IPluginConfiguration, ISavable { [JsonIgnore] private readonly SaveService _saveService; [JsonIgnore] public readonly EphemeralConfig Ephemeral; public int Version { get; set; } = Constants.CurrentVersion; public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; public bool EnableMods { get; set; } = true; public string ModDirectory { get; set; } = string.Empty; public string ExportDirectory { get; set; } = string.Empty; public bool OpenWindowAtStart { get; set; } = false; public bool HideUiInGPose { get; set; } = false; public bool HideUiInCutscenes { get; set; } = true; public bool HideUiWhenUiHidden { get; set; } = false; public bool UseDalamudUiTextureRedirection { get; set; } = true; public bool UseCharacterCollectionInMainWindow { get; set; } = true; public bool UseCharacterCollectionsInCards { get; set; } = true; public bool UseCharacterCollectionInInspect { get; set; } = true; public bool UseCharacterCollectionInTryOn { get; set; } = true; public bool UseOwnerNameForCharacterCollection { get; set; } = true; public bool UseNoModsInInspect { get; set; } = false; public bool HideChangedItemFilters { get; set; } = false; public bool ReplaceNonAsciiOnImport { get; set; } = false; public bool HidePrioritiesInSelector { get; set; } = false; public bool HideRedrawBar { get; set; } = false; public int OptionGroupCollapsibleMin { get; set; } = 5; public Vector2 MinimumSize = new(Constants.MinimumSizeX, Constants.MinimumSizeY); #if DEBUG public bool DebugMode { get; set; } = true; #else public bool DebugMode { get; set; } = false; #endif public int MaxResourceWatcherRecords { get; set; } = ResourceWatcher.DefaultMaxEntries; [JsonConverter(typeof(SortModeConverter))] [JsonProperty(Order = int.MaxValue)] public ISortMode SortMode = ISortMode.FoldersFirst; public bool ScaleModSelector { get; set; } = false; public float ModSelectorAbsoluteSize { get; set; } = Constants.DefaultAbsoluteSize; public int ModSelectorScaledSize { get; set; } = Constants.DefaultScaledSize; public bool OpenFoldersByDefault { get; set; } = false; public int SingleGroupRadioMax { get; set; } = 2; public string DefaultImportFolder { get; set; } = string.Empty; public string QuickMoveFolder1 { get; set; } = string.Empty; public string QuickMoveFolder2 { get; set; } = string.Empty; public string QuickMoveFolder3 { get; set; } = string.Empty; public DoubleModifier DeleteModModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public bool PrintSuccessfulCommandsToChat { get; set; } = true; public bool AutoDeduplicateOnImport { get; set; } = true; public bool UseFileSystemCompression { get; set; } = true; public bool EnableHttpApi { get; set; } = true; public string DefaultModImportPath { get; set; } = string.Empty; public bool AlwaysOpenDefaultImport { get; set; } = false; public bool KeepDefaultMetaChanges { get; set; } = false; public string DefaultModAuthor { get; set; } = DefaultTexToolsData.Author; public Dictionary Colors { get; set; } = Enum.GetValues().ToDictionary(c => c, c => c.Data().DefaultColor); /// /// Load the current configuration. /// Includes adding new colors and migrating from old versions. /// public Configuration(CharacterUtility utility, ConfigMigrationService migrator, SaveService saveService, EphemeralConfig ephemeral) { _saveService = saveService; Ephemeral = ephemeral; Load(utility, migrator); } public void Load(CharacterUtility utility, ConfigMigrationService migrator) { static void HandleDeserializationError(object? sender, ErrorEventArgs errorArgs) { Penumbra.Log.Error( $"Error parsing Configuration at {errorArgs.ErrorContext.Path}, using default or migrating:\n{errorArgs.ErrorContext.Error}"); errorArgs.ErrorContext.Handled = true; } if (File.Exists(_saveService.FileNames.ConfigFile)) try { var text = File.ReadAllText(_saveService.FileNames.ConfigFile); JsonConvert.PopulateObject(text, this, new JsonSerializerSettings { Error = HandleDeserializationError, }); } catch (Exception ex) { Penumbra.Messager.NotificationMessage(ex, "Error reading Configuration, reverting to default.\nYou may be able to restore your configuration using the rolling backups in the XIVLauncher/backups/Penumbra directory.", "Error reading Configuration", NotificationType.Error); } migrator.Migrate(utility, this); } /// Save the current configuration. public void Save() => _saveService.QueueSave(this); /// Contains some default values or boundaries for config values. public static class Constants { public const int CurrentVersion = 8; public const float MaxAbsoluteSize = 600; public const int DefaultAbsoluteSize = 250; public const float MinAbsoluteSize = 50; public const int MaxScaledSize = 80; public const int DefaultScaledSize = 20; public const int MinScaledSize = 5; public const int MinimumSizeX = 900; public const int MinimumSizeY = 675; public static readonly ISortMode[] ValidSortModes = { ISortMode.FoldersFirst, ISortMode.Lexicographical, new ModFileSystem.ImportDate(), new ModFileSystem.InverseImportDate(), ISortMode.InverseFoldersFirst, ISortMode.InverseLexicographical, ISortMode.FoldersLast, ISortMode.InverseFoldersLast, ISortMode.InternalOrder, ISortMode.InverseInternalOrder, }; } /// Convert SortMode Types to their name. private class SortModeConverter : JsonConverter> { public override void WriteJson(JsonWriter writer, ISortMode? value, JsonSerializer serializer) { value ??= ISortMode.FoldersFirst; serializer.Serialize(writer, value.GetType().Name); } public override ISortMode ReadJson(JsonReader reader, Type objectType, ISortMode? existingValue, bool hasExistingValue, JsonSerializer serializer) { var name = serializer.Deserialize(reader); if (name == null || !Constants.ValidSortModes.FindFirst(s => s.GetType().Name == name, out var mode)) return existingValue ?? ISortMode.FoldersFirst; return mode; } } public string ToFilename(FilenameService fileNames) => fileNames.ConfigFile; public void Save(StreamWriter writer) { using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }; var serializer = new JsonSerializer { Formatting = Formatting.Indented }; serializer.Serialize(jWriter, this); } }