From 59d16a7e90ca78c026f5500653ab104478c546c598f9e7d437c1451ccb9aaf77 Mon Sep 17 00:00:00 2001 From: copecone Date: Fri, 3 Jul 2026 12:25:54 +0900 Subject: [PATCH] Add Preset Feature to Render Settings --- Data/EnumDropdownBinder.cs | 14 + Data/PresetManager.cs | 103 +++++++ Data/RenderConfig.cs | 32 +- UI/RenderConfigUIManager.cs | 589 ++++++++++++++++++++++++++++++++---- obfuscar.xml | 7 +- 5 files changed, 674 insertions(+), 71 deletions(-) create mode 100644 Data/PresetManager.cs diff --git a/Data/EnumDropdownBinder.cs b/Data/EnumDropdownBinder.cs index d200ee2..f3b68e0 100644 --- a/Data/EnumDropdownBinder.cs +++ b/Data/EnumDropdownBinder.cs @@ -68,5 +68,19 @@ namespace ADOFAIRenderer.Data callback(Value); } } + + /// + /// Sets the selected enum value and updates the dropdown index. + /// + /// The enum value to set. + public void SetValue(T value) + { + if (_dropdown == null) return; + int idx = _values.IndexOf(value); + if (idx >= 0) + { + _dropdown.index = idx; + } + } } } diff --git a/Data/PresetManager.cs b/Data/PresetManager.cs new file mode 100644 index 0000000..76a9ce7 --- /dev/null +++ b/Data/PresetManager.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MelonLoader; +using MelonLoader.Utils; +using Newtonsoft.Json; + +namespace ADOFAIRenderer.Data +{ + public class RenderPreset + { + public string Name { get; set; } = string.Empty; + public RenderConfig Config { get; set; } = null!; + } + + public 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(); + string dir = GetPresetsDirectory(); + if (!Directory.Exists(dir)) + { + return presets; + } + + try + { + string[] files = Directory.GetFiles(dir, "*.json"); + foreach (string file in files) + { + try + { + string json = File.ReadAllText(file); + var preset = JsonConvert.DeserializeObject(json); + if (preset != null && !string.IsNullOrEmpty(preset.Name) && preset.Config != null) + { + presets.Add(preset); + } + } + catch (Exception ex) + { + MelonLogger.Warning($"프리셋 파일 '{Path.GetFileName(file)}' 로드 중 오류 발생: {ex.Message}"); + } + } + } + catch (Exception ex) + { + MelonLogger.Error($"프리셋 목록을 불러오는 중 오류 발생: {ex.Message}"); + } + + return presets; + } + + 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); + } + } + } +} diff --git a/Data/RenderConfig.cs b/Data/RenderConfig.cs index 2de90c3..19e12ca 100644 --- a/Data/RenderConfig.cs +++ b/Data/RenderConfig.cs @@ -4,31 +4,31 @@ namespace ADOFAIRenderer.Data { public class RenderConfig { - public double RenderFPS; - public double VideoFPS; + public double RenderFPS { get; set; } + public double VideoFPS { get; set; } - public int RenderWidth; - public int RenderHeight; + public int RenderWidth { get; set; } + public int RenderHeight { get; set; } - public Codec Codec; - public EncodingConfig? EncodingConfig; + public Codec Codec { get; set; } + public EncodingConfig? EncodingConfig { get; set; } - public double DelayAfterLevelEnd; - public bool ShowJudgementText; + public double DelayAfterLevelEnd { get; set; } + public bool ShowJudgementText { get; set; } - public int? RandomSeed = null; - public bool ProcessAudio; + public int? RandomSeed { get; set; } = null; + public bool ProcessAudio { get; set; } } public class EncodingConfig { - public RateControlMode RateControlMode; + public RateControlMode RateControlMode { get; set; } - public int Bitrate; - public int MaxBitrate; + public int Bitrate { get; set; } + public int MaxBitrate { get; set; } // ReSharper disable once InconsistentNaming - public int QP; - public double KeyframeInterval; - public bool ForceSoftwareEncoder; + public int QP { get; set; } + public double KeyframeInterval { get; set; } + public bool ForceSoftwareEncoder { get; set; } } } \ No newline at end of file diff --git a/UI/RenderConfigUIManager.cs b/UI/RenderConfigUIManager.cs index 1dabc36..4f9acab 100644 --- a/UI/RenderConfigUIManager.cs +++ b/UI/RenderConfigUIManager.cs @@ -63,6 +63,17 @@ namespace ADOFAIRenderer.UI { Melon.Logger.Error("Button 'EncodingSettingsButton'을(를) 찾을 수 없습니다."); } + + var presetSettingsButton = _root.Q