diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07223e2b..d9c89541 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,3 +32,5 @@ jobs: run: dotnet restore "${{ env.WORKING_DIRECTORY }}" - name: Build run: dotnet build "${{ env.WORKING_DIRECTORY }}" --no-restore + - name: Test + run: dotnet test "${{ env.WORKING_DIRECTORY }}" diff --git a/.gitignore b/.gitignore index 869d2bdb..75beb9e9 100644 --- a/.gitignore +++ b/.gitignore @@ -332,4 +332,4 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ - +.direnv/ diff --git a/Stardrop.Test/EnumParserTests.cs b/Stardrop.Test/EnumParserTests.cs new file mode 100644 index 00000000..d31a830f --- /dev/null +++ b/Stardrop.Test/EnumParserTests.cs @@ -0,0 +1,62 @@ +namespace Stardrop.Test; + +using NUnit.Framework; +using Stardrop.Models.Data.Enums; +using Stardrop.Utilities.Internal; + +[TestFixture] +public class EnumParserTests +{ + [Test] + public void GetDescription_NullEnum_ReturnsNull() + { + NexusServers? value = null; + Assert.That(value.GetDescription(), Is.Null); + } + + [Test] + [TestCase(NexusServers.NexusCDN, "Nexus CDN")] + [TestCase(NexusServers.LosAngeles, "Los Angeles")] + public void GetDescription_NexusServers_WithDescription_ReturnsAttributeText(NexusServers server, string expected) + { + Assert.That(server.GetDescription(), Is.EqualTo(expected)); + } + + [Test] + [TestCase(NexusServers.Chicago, "Chicago")] + [TestCase(NexusServers.Paris, "Paris")] + [TestCase(NexusServers.Amsterdam, "Amsterdam")] + [TestCase(NexusServers.Prague, "Prague")] + [TestCase(NexusServers.Miami, "Miami")] + [TestCase(NexusServers.Singapore, "Singapore")] + public void GetDescription_NexusServers_WithoutDescription_ReturnsMemberName(NexusServers server, string expected) + { + Assert.That(server.GetDescription(), Is.EqualTo(expected)); + } + + [Test] + [TestCase(ModGrouping.ContentPack, "Content Pack")] + [TestCase(ModGrouping.FolderCondensed, "Folder (Condensed)")] + public void GetDescription_ModGrouping_WithDescription_ReturnsAttributeText(ModGrouping grouping, string expected) + { + Assert.That(grouping.GetDescription(), Is.EqualTo(expected)); + } + + [Test] + [TestCase(ModGrouping.None, "None")] + [TestCase(ModGrouping.Folder, "Folder")] + public void GetDescription_ModGrouping_WithoutDescription_ReturnsMemberName(ModGrouping grouping, string expected) + { + Assert.That(grouping.GetDescription(), Is.EqualTo(expected)); + } + + [Test] + [TestCase(DisplayFilter.None, "None")] + [TestCase(DisplayFilter.ShowEnabled, "ShowEnabled")] + [TestCase(DisplayFilter.ShowDisabled, "ShowDisabled")] + [TestCase(DisplayFilter.RequireConfig, "RequireConfig")] + public void GetDescription_DisplayFilter_NeverHasDescription_ReturnsMemberName(DisplayFilter filter, string expected) + { + Assert.That(filter.GetDescription(), Is.EqualTo(expected)); + } +} diff --git a/Stardrop.Test/MSTestSettings.cs b/Stardrop.Test/MSTestSettings.cs new file mode 100644 index 00000000..69ebfad8 --- /dev/null +++ b/Stardrop.Test/MSTestSettings.cs @@ -0,0 +1,3 @@ +using NUnit.Framework; + +[assembly: Parallelizable(ParallelScope.All)] diff --git a/Stardrop.Test/ModConfigServiceTests.cs b/Stardrop.Test/ModConfigServiceTests.cs new file mode 100644 index 00000000..0177f062 --- /dev/null +++ b/Stardrop.Test/ModConfigServiceTests.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using NUnit.Framework; +using Stardrop.Models; +using Stardrop.Models.SMAPI; +using Stardrop.Utilities.Internal; + +namespace Stardrop.Test; + +[TestFixture] +[NonParallelizable] +public class ModConfigServiceTests +{ + private string _tempDir; + private ModConfigService _service; + private FakeModDiscoveryService _discoveryService; + private Settings _savedSettings; + + [SetUp] + public void SetUp() + { + _tempDir = Path.Combine(Path.GetTempPath(), $"stardrop-test-{Guid.NewGuid()}"); + Directory.CreateDirectory(_tempDir); + + _savedSettings = Program.settings; + Program.settings = new Settings { ModFolderPath = _tempDir, IgnoreHiddenFolders = true }; + + _discoveryService = new FakeModDiscoveryService(); + _service = new ModConfigService(Program.settings, _discoveryService); + } + + [TearDown] + public void TearDown() + { + Program.settings = _savedSettings; + if (Directory.Exists(_tempDir)) + Directory.Delete(_tempDir, recursive: true); + } + + + + [Test] + public void GetConfigFiles_EmptyDirectory_ReturnsEmpty() + { + var result = _service.GetConfigFiles(new DirectoryInfo(_tempDir)); + Assert.That(result, Is.Empty); + } + + [Test] + public void GetConfigFiles_ConfigWithoutManifest_NotIncluded() + { + var modDir = CreateSubdir("Mod1"); + File.WriteAllText(Path.Combine(modDir, "config.json"), "{}"); + + var result = _service.GetConfigFiles(new DirectoryInfo(_tempDir)); + + Assert.That(result, Is.Empty); + } + + [Test] + public void GetConfigFiles_ConfigWithManifest_Included() + { + var modDir = CreateSubdir("Mod1"); + var configPath = Path.Combine(modDir, "config.json"); + File.WriteAllText(configPath, "{}"); + File.WriteAllText(Path.Combine(modDir, "manifest.json"), "{}"); + + var result = _service.GetConfigFiles(new DirectoryInfo(_tempDir)); + + Assert.That(result, Has.Count.EqualTo(1)); + Assert.That(result[0].FullName, Is.EqualTo(configPath)); + } + + [Test] + public void GetConfigFiles_MultipleModDirs_AllIncluded() + { + foreach (var name in new[] { "Mod1", "Mod2", "Mod3" }) + { + var dir = CreateSubdir(name); + File.WriteAllText(Path.Combine(dir, "config.json"), "{}"); + File.WriteAllText(Path.Combine(dir, "manifest.json"), "{}"); + } + + var result = _service.GetConfigFiles(new DirectoryInfo(_tempDir)); + + Assert.That(result, Has.Count.EqualTo(3)); + } + + + + + [Test] + public void DiscoverConfigs_NonExistentPath_DoesNotThrow() + { + var mods = new List(); + Assert.DoesNotThrow(() => _service.DiscoverConfigs(Path.Combine(_tempDir, "missing"), mods)); + } + + [Test] + public void DiscoverConfigs_NonExistentPath_ModConfigRemainsNull() + { + var mod = CreateMod("Author.Mod1"); + + _service.DiscoverConfigs(Path.Combine(_tempDir, "missing"), new List { mod }); + + Assert.That(mod.Config, Is.Null); + } + + [Test] + public void DiscoverConfigs_MatchingMod_ConfigAssigned() + { + var mod = CreateMod("Author.Mod1"); + var configContent = """{"key": "value"}"""; + File.WriteAllText(Path.Combine(mod.ModFileInfo.DirectoryName!, "config.json"), configContent); + + _service.DiscoverConfigs(_tempDir, new List { mod }); + + Assert.That(mod.Config, Is.Not.Null); + Assert.That(mod.Config!.UniqueId, Is.EqualTo("Author.Mod1")); + Assert.That(mod.Config.Data, Is.EqualTo(configContent)); + } + + [Test] + public void DiscoverConfigs_NoMatchingMod_ConfigRemainsNull() + { + var mod = CreateMod("Author.Mod1"); + var otherDir = CreateSubdir("Author.Mod2"); + File.WriteAllText(Path.Combine(otherDir, "config.json"), "{}"); + File.WriteAllText(Path.Combine(otherDir, "manifest.json"), "{}"); + + _service.DiscoverConfigs(_tempDir, new List { mod }); + + Assert.That(mod.Config, Is.Null); + } + + + + + private string CreateSubdir(string name) + { + var path = Path.Combine(_tempDir, name); + Directory.CreateDirectory(path); + return path; + } + + private Mod CreateMod(string uniqueId, string version = "1.0.0", string name = "Test Mod", string author = "Test Author") + { + var modDir = CreateSubdir(uniqueId); + var manifestPath = Path.Combine(modDir, "manifest.json"); + File.WriteAllText(manifestPath, "{}"); + var manifest = new Manifest { UniqueID = uniqueId, Name = name, Author = author, Version = version }; + return new Mod(manifest, new FileInfo(manifestPath), uniqueId, version, name, null, author); + } + + private sealed class FakeModDiscoveryService : IModDiscoveryService + { + public bool ReturnValue { get; set; } + + public bool ParentFolderContainsPeriod(string oldestAncestorPath, DirectoryInfo? directoryInfo) + => ReturnValue; + } +} diff --git a/Stardrop.Test/Stardrop.Test.csproj b/Stardrop.Test/Stardrop.Test.csproj new file mode 100644 index 00000000..98f73be2 --- /dev/null +++ b/Stardrop.Test/Stardrop.Test.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + latest + enable + enable + + + + + + + + + + + + + + + + diff --git a/Stardrop/Stardrop.csproj b/Stardrop/Stardrop.csproj index 7f0dbc29..446fcc6c 100644 --- a/Stardrop/Stardrop.csproj +++ b/Stardrop/Stardrop.csproj @@ -12,6 +12,11 @@ true true + + + <_Parameter1>Stardrop.Test + + diff --git a/Stardrop/Stardrop.sln b/Stardrop/Stardrop.sln index 56fa6bf9..1a674115 100644 --- a/Stardrop/Stardrop.sln +++ b/Stardrop/Stardrop.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.31729.503 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stardrop", "Stardrop.csproj", "{68543B63-0EB4-43E8-9B7B-7AFA64097CAF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stardrop.Test", "..\Stardrop.Test\Stardrop.Test.csproj", "{2A4FE1F1-6518-4C01-8C7D-EA14F29BF929}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU {68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU {68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Release|Any CPU.Build.0 = Release|Any CPU + {2A4FE1F1-6518-4C01-8C7D-EA14F29BF929}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A4FE1F1-6518-4C01-8C7D-EA14F29BF929}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A4FE1F1-6518-4C01-8C7D-EA14F29BF929}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A4FE1F1-6518-4C01-8C7D-EA14F29BF929}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Stardrop/Utilities/Internal/IModConfigService.cs b/Stardrop/Utilities/Internal/IModConfigService.cs new file mode 100644 index 00000000..9b5585ab --- /dev/null +++ b/Stardrop/Utilities/Internal/IModConfigService.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Stardrop.Models; + +namespace Stardrop.Utilities.Internal; + +public interface IModConfigService +{ + void DiscoverConfigs(string modsFilePath, IReadOnlyList mods, bool useArchive = false); + + List GetPendingConfigUpdates(Profile profile, IReadOnlyList mods, + bool excludeMissingConfigs = false, bool useArchiveAsBase = false); + +} \ No newline at end of file diff --git a/Stardrop/Utilities/Internal/IModDiscoveryService.cs b/Stardrop/Utilities/Internal/IModDiscoveryService.cs new file mode 100644 index 00000000..a5f0dd9e --- /dev/null +++ b/Stardrop/Utilities/Internal/IModDiscoveryService.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace Stardrop.Utilities.Internal; + +public interface IModDiscoveryService +{ + + bool ParentFolderContainsPeriod(string oldestAncestorPath, DirectoryInfo? directoryInfo); +} \ No newline at end of file diff --git a/Stardrop/Utilities/Internal/ModConfigService.cs b/Stardrop/Utilities/Internal/ModConfigService.cs new file mode 100644 index 00000000..137bb082 --- /dev/null +++ b/Stardrop/Utilities/Internal/ModConfigService.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using Json.More; +using Stardrop.Models; + +namespace Stardrop.Utilities.Internal; + +public class ModConfigService : IModConfigService +{ + private readonly Settings _settings; + private readonly IModDiscoveryService _discoveryService; + + public ModConfigService(Settings settings, IModDiscoveryService discoveryService) + { + _settings = settings; + _discoveryService = discoveryService; + } + + public List GetConfigFiles(DirectoryInfo modDirectory) + { + var configs = new List(); + foreach (var directory in modDirectory.EnumerateDirectories()) + { + var localConfigs = directory.EnumerateFiles("config.json").ToList(); + if (localConfigs.Count == 0) + { + configs.AddRange(GetConfigFiles(directory)); + continue; + } + + var localConfig = localConfigs.First(); + if (localConfig.Directory is not null && + localConfig.Directory.EnumerateFiles("manifest.json", SearchOption.TopDirectoryOnly).Any()) + { + configs.Add(localConfig); + } + } + + return configs; + } + + public void DiscoverConfigs(string modsFilePath, IReadOnlyList mods, bool useArchive = false) + { + if (!Directory.Exists(modsFilePath)) + { + return; + } + + foreach (var fileInfo in GetConfigFiles(new DirectoryInfo(modsFilePath))) + { + if (fileInfo.DirectoryName is null || + (Program.settings.IgnoreHiddenFolders && + _discoveryService.ParentFolderContainsPeriod(modsFilePath, fileInfo.Directory))) + { + continue; + } + + var mod = mods.FirstOrDefault(m => m.ModFileInfo.DirectoryName == fileInfo.DirectoryName); + if (mod is null) + { + continue; + } + else if (useArchive && mod.Config is not null) + { + if (fileInfo.LastWriteTimeUtc <= mod.Config.LastWriteTimeUtc) + { + continue; + } + + mod.Config.Data = File.ReadAllText(fileInfo.FullName); + mod.Config.LastWriteTimeUtc = fileInfo.LastWriteTimeUtc; + } + else + { + mod.Config = new Config() + { + UniqueId = mod.UniqueId, FilePath = fileInfo.FullName, LastWriteTimeUtc = fileInfo.LastWriteTimeUtc, + Data = File.ReadAllText(fileInfo.FullName) + }; + } + } + } + + + public List GetPendingConfigUpdates(Profile profile, IReadOnlyList mods, + bool excludeMissingConfigs = false, bool useArchiveAsBase = false) + { + // Merge any existing preserved configs + List pendingConfigUpdates = new List(); + foreach (var modId in profile.EnabledModIds.Select(id => id.ToLower())) + { + var mod = mods.FirstOrDefault(m => m.UniqueId.Equals(modId, StringComparison.OrdinalIgnoreCase)); + if (mod is null) + { + continue; + } + + try + { + if (profile.PreservedModConfigs.ContainsKey(modId)) + { + // Write the archived config, if the current one doesn't exist + if (mod.Config is null) + { + if (excludeMissingConfigs || string.IsNullOrEmpty(mod.ModFileInfo.DirectoryName)) + { + continue; + } + + mod.Config = new Config() + { + UniqueId = modId, FilePath = Path.Combine(mod.ModFileInfo.DirectoryName, "config.json"), + Data = JsonTools.ParseDocumentToString(profile.PreservedModConfigs[modId]) + }; + pendingConfigUpdates.Add(mod.Config); + } + else + { + // Merge the config + var currentJson = mod.Config.Data; + var archivedJson = JsonTools.ParseDocumentToString(profile.PreservedModConfigs[modId]); + if (JsonDocumentEqualityComparer.Instance.Equals(JsonDocument.Parse(mod.Config.Data), + profile.PreservedModConfigs[modId]) is false) + { + // JsonTools.Merge will preserve the originalJson values, but will add new properties from archivedJson + var mergedJson = useArchiveAsBase + ? JsonTools.Merge(currentJson, archivedJson, false) + : JsonTools.Merge(archivedJson, currentJson, false); + + // Apply the changes to the config file + //Program.helper.Log($"The mod {modId} does not have its current configuration preserved\nCurrent:\n{currentJson}\nArchived:\n{archivedJson}", Helper.Status.Warning); + pendingConfigUpdates.Add(new Config() + { UniqueId = modId, FilePath = mod.Config.FilePath, Data = mergedJson }); + } + } + } + else if (mod.Config is not null) + { + pendingConfigUpdates.Add(new Config() + { UniqueId = modId, FilePath = mod.Config.FilePath, Data = mod.Config.Data }); + } + } + catch (Exception ex) + { + Program.helper.Log($"Failed to process config.json for mod {modId}: {ex}", Helper.Status.Warning); + } + } + + return pendingConfigUpdates; + } +} \ No newline at end of file diff --git a/Stardrop/Utilities/Internal/ModDiscoveryService.cs b/Stardrop/Utilities/Internal/ModDiscoveryService.cs new file mode 100644 index 00000000..baaad7d5 --- /dev/null +++ b/Stardrop/Utilities/Internal/ModDiscoveryService.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; + +namespace Stardrop.Utilities.Internal; + +public class ModDiscoveryService : IModDiscoveryService +{ + public bool ParentFolderContainsPeriod(string oldestAncestorPath, DirectoryInfo? directoryInfo) + { + if (directoryInfo is null) + { + return false; + } + if (directoryInfo.Name[0] == '.') + { + return true; + } + + var ancestorFolder = directoryInfo.Parent; + while (ancestorFolder is not null && + !ancestorFolder.FullName.Equals(oldestAncestorPath, StringComparison.OrdinalIgnoreCase)) + { + if (ancestorFolder.Name[0] == '.') + { + return true; + } + + ancestorFolder = ancestorFolder.Parent; + } + + return false; + } +} \ No newline at end of file diff --git a/Stardrop/ViewModels/MainWindowViewModel.cs b/Stardrop/ViewModels/MainWindowViewModel.cs index 5ce97e05..93e559a8 100644 --- a/Stardrop/ViewModels/MainWindowViewModel.cs +++ b/Stardrop/ViewModels/MainWindowViewModel.cs @@ -27,6 +27,9 @@ namespace Stardrop.ViewModels { public class MainWindowViewModel : ViewModelBase { + private readonly IModDiscoveryService _modDiscoveryService; + private readonly IModConfigService _modConfigService; + private string ChromeHint { get; set; } = "NoChrome"; private bool HasSystemDecorations { get; set; } = true; private bool ShowTitle { get; set; } = true; @@ -88,8 +91,10 @@ public class MainWindowViewModel : ViewModelBase public string ModGroupsStateButtonText { get { return _modGroupsStateButtonText; } set { this.RaiseAndSetIfChanged(ref _modGroupsStateButtonText, value); } } private string _modGroupsStateButtonText = Program.settings.ModGroupingMethod != ModGrouping.None ? Program.translation.Get("ui.main_window.buttons.mod_groups_state.collapse") : Program.translation.Get("ui.main_window.buttons.mod_groups_state.expand"); - public MainWindowViewModel(string modsFilePath, string version) + public MainWindowViewModel(string modsFilePath, string version, IModConfigService modConfigService, IModDiscoveryService modDiscoveryService) { + _modConfigService = modConfigService; + _modDiscoveryService = modDiscoveryService; DiscoverMods(modsFilePath); Version = $"v{version}"; SmapiVersion = Program.settings.GameDetails?.SmapiVersion; @@ -528,6 +533,7 @@ public void DiscoverMods(string modsFilePath) ActualModCount = Mods.Count(m => !m.IsHidden); } + public void HideRequiredMods() { @@ -605,106 +611,11 @@ public List GetConfigFiles(DirectoryInfo modDirectory) return configs; } - public void DiscoverConfigs(string modsFilePath, bool useArchive = false) - { - if (modsFilePath is null || !Directory.Exists(modsFilePath)) - { - return; - } - - foreach (var fileInfo in GetConfigFiles(new DirectoryInfo(modsFilePath))) - { - if (fileInfo.DirectoryName is null || (Program.settings.IgnoreHiddenFolders && ParentFolderContainsPeriod(modsFilePath, fileInfo.Directory))) - { - continue; - } - - var mod = Mods.FirstOrDefault(m => m.ModFileInfo is not null && m.ModFileInfo.DirectoryName == fileInfo.DirectoryName); - if (mod is null) - { - continue; - } - else if (useArchive && mod.Config is not null) - { - if (fileInfo.LastWriteTimeUtc <= mod.Config.LastWriteTimeUtc) - { - continue; - } - - mod.Config.Data = File.ReadAllText(fileInfo.FullName); - mod.Config.LastWriteTimeUtc = fileInfo.LastWriteTimeUtc; - } - else - { - mod.Config = new Config() { UniqueId = mod.UniqueId, FilePath = fileInfo.FullName, LastWriteTimeUtc = fileInfo.LastWriteTimeUtc, Data = File.ReadAllText(fileInfo.FullName) }; - } - } - } + public void DiscoverConfigs(string modFilePath, bool useArchive = false) + => _modConfigService.DiscoverConfigs(modFilePath, Mods, useArchive); internal List GetPendingConfigUpdates(Profile profile, bool excludeMissingConfigs = false, bool useArchiveAsBase = false) - { - // Merge any existing preserved configs - List pendingConfigUpdates = new List(); - foreach (var modId in profile.EnabledModIds.Select(id => id.ToLower())) - { - var mod = Mods.FirstOrDefault(m => m.UniqueId.Equals(modId, StringComparison.OrdinalIgnoreCase)); - if (mod is null || mod.ModFileInfo is null) - { - continue; - } - - try - { - if (profile.PreservedModConfigs.ContainsKey(modId)) - { - // Write the archived config, if the current one doesn't exist - if (mod.Config is null) - { - if (excludeMissingConfigs || String.IsNullOrEmpty(mod.ModFileInfo.DirectoryName)) - { - continue; - } - - mod.Config = new Config() { UniqueId = modId, FilePath = Path.Combine(mod.ModFileInfo.DirectoryName, "config.json"), Data = JsonTools.ParseDocumentToString(profile.PreservedModConfigs[modId]) }; - pendingConfigUpdates.Add(mod.Config); - } - else - { - // Merge the config - var currentJson = mod.Config.Data; - var archivedJson = JsonTools.ParseDocumentToString(profile.PreservedModConfigs[modId]); - if (JsonDocumentEqualityComparer.Instance.Equals(JsonDocument.Parse(mod.Config.Data), profile.PreservedModConfigs[modId]) is false) - { - // JsonTools.Merge will preserve the originalJson values, but will add new properties from archivedJson - string mergedJson = String.Empty; - if (useArchiveAsBase is false) - { - mergedJson = JsonTools.Merge(archivedJson, currentJson, false); ; - } - else - { - mergedJson = JsonTools.Merge(currentJson, archivedJson, false); - } - - // Apply the changes to the config file - //Program.helper.Log($"The mod {modId} does not have its current configuration preserved\nCurrent:\n{currentJson}\nArchived:\n{archivedJson}", Helper.Status.Warning); - pendingConfigUpdates.Add(new Config() { UniqueId = modId, FilePath = mod.Config.FilePath, Data = mergedJson }); - } - } - } - else if (mod.Config is not null) - { - pendingConfigUpdates.Add(new Config() { UniqueId = modId, FilePath = mod.Config.FilePath, Data = mod.Config.Data }); - } - } - catch (Exception ex) - { - Program.helper.Log($"Failed to process config.json for mod {modId}: {ex}", Helper.Status.Warning); - } - } - - return pendingConfigUpdates; - } + => _modConfigService.GetPendingConfigUpdates(profile, Mods, excludeMissingConfigs, useArchiveAsBase); internal async void UpdateEndorsements() { diff --git a/Stardrop/Views/MainWindow.axaml.cs b/Stardrop/Views/MainWindow.axaml.cs index 039266dc..2948bb06 100644 --- a/Stardrop/Views/MainWindow.axaml.cs +++ b/Stardrop/Views/MainWindow.axaml.cs @@ -59,7 +59,10 @@ public MainWindow() InitializeComponent(); // Set the main window view - _viewModel = new MainWindowViewModel(Pathing.defaultModPath, Program.ApplicationVersion); + var modDiscoveryService = new ModDiscoveryService(); + var modConfigService = new ModConfigService(Program.settings, modDiscoveryService); + _viewModel = new MainWindowViewModel(Pathing.defaultModPath, Program.ApplicationVersion, + modConfigService, modDiscoveryService); DataContext = _viewModel; // Set the path according to the environmental variable SMAPI_MODS_PATH