using System; using System.Collections.Generic; using System.IO; using System.Linq; using MelonLoader; using MelonLoader.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace ADOFAIRenderer.Data { internal class RenderPreset { public string Name { get; set; } = string.Empty; public RenderConfig Config { get; set; } = null!; } internal static class PresetManager { private static string GetPresetsDirectory() { return Path.Combine(MelonEnvironment.GameRootDirectory, "UserData", "ADOFAIRenderer", "Presets"); } private static string GetSafeFileName(string name) { return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_')); } public static void SavePreset(string name, RenderConfig config) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("프리셋 이름은 비어 있을 수 없습니다."); } string dir = GetPresetsDirectory(); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } var preset = new RenderPreset { Name = name, Config = config }; string json = JsonConvert.SerializeObject(preset, Formatting.Indented); string safeName = GetSafeFileName(name); string filePath = Path.Combine(dir, safeName + ".json"); File.WriteAllText(filePath, json); } public static List GetPresets() { var presets = new List(); var dir = GetPresetsDirectory(); if (!Directory.Exists(dir)) return presets; try { var files = Directory.GetFiles(dir, "*.json"); foreach (var file in files) { try { var json = File.ReadAllText(file); var jObject = JsonConvert.DeserializeObject(json); if (jObject == null) continue; var configToken = jObject["Config"]; if (configToken is JObject configObj) { TryChangeKey(configObj, "DelayAfterLevelEnd", "DelayAfterEnd"); // 1. legacy EncodingConfig -> VideoConfig if (configObj["EncodingConfig"] is JObject oldEncodingConfigObj) { configObj["VideoConfig"] ??= oldEncodingConfigObj; configObj.Remove("EncodingConfig"); } // 2. legacy top-level Codec -> VideoConfig.Codec var legacyCodecToken = configObj["Codec"]; if (legacyCodecToken != null && legacyCodecToken.Type != JTokenType.Null) { if (configObj["VideoConfig"] == null || configObj["VideoConfig"]!.Type == JTokenType.Null) { configObj["VideoConfig"] = new JObject(); } var videoConfigObj = (JObject)configObj["VideoConfig"]!; videoConfigObj["Codec"] ??= legacyCodecToken; configObj.Remove("Codec"); } // 3. legacy top-level Audio properties -> AudioConfig string[] legacyAudioProps = { "RenderAudio", "AudioSampleRate", "AudioChannels", "AudioBitrate" }; foreach (var prop in legacyAudioProps) { var legacyToken = configObj[prop]; if (legacyToken != null && legacyToken.Type != JTokenType.Null) { if (configObj["AudioConfig"] == null || configObj["AudioConfig"]!.Type == JTokenType.Null) { configObj["AudioConfig"] = new JObject(); } var audioConfigObj = (JObject)configObj["AudioConfig"]!; audioConfigObj[prop] ??= legacyToken; } configObj.Remove(prop); } if (configObj["AudioConfig"] != null && configObj["AudioConfig"]!.Type == JTokenType.Null) { configObj.Remove("AudioConfig"); } // 4. legacy top-level Advanced properties -> AdvancedConfig string[] legacyAdvancedProps = { "RandomSeed", "ProcessAudio", "NonTile" }; foreach (var prop in legacyAdvancedProps) { var legacyToken = configObj[prop]; if (legacyToken != null && legacyToken.Type != JTokenType.Null) { if (configObj["AdvancedConfig"] == null || configObj["AdvancedConfig"]!.Type == JTokenType.Null) { configObj["AdvancedConfig"] = new JObject(); } var advancedConfigObj = (JObject)configObj["AdvancedConfig"]!; advancedConfigObj[prop] ??= legacyToken; } configObj.Remove(prop); } if (configObj["AdvancedConfig"] != null && configObj["AdvancedConfig"]!.Type == JTokenType.Null) { configObj.Remove("AdvancedConfig"); } } var preset = jObject.ToObject(); if (preset != null && !string.IsNullOrEmpty(preset.Name)) { presets.Add(preset); } } catch (Exception ex) { MelonLogger.Warning($"프리셋 파일 '{Path.GetFileName(file)}' 로드 중 오류 발생: {ex.Message}"); } } } catch (Exception ex) { MelonLogger.Error($"프리셋 목록을 불러오는 중 오류 발생: {ex.Message}"); } return presets; } private static void TryChangeKey(JObject configObj, string keyBefore, string targetKey) { var legacyToken = configObj[keyBefore]; if (legacyToken != null && legacyToken.Type != JTokenType.Null) configObj[targetKey] = legacyToken; configObj.Remove(keyBefore); } public static void DeletePreset(string name) { string dir = GetPresetsDirectory(); string safeName = GetSafeFileName(name); string filePath = Path.Combine(dir, safeName + ".json"); if (File.Exists(filePath)) { File.Delete(filePath); } } } }