From db0168c6eff2d42e05d88ffa22d49e736ed2da8e Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Fri, 19 Mar 2021 16:37:58 -0600 Subject: [PATCH] Create a global registry of the location of all installed modlists --- Wabbajack.Common/Consts.cs | 2 + Wabbajack.Common/Json.cs | 8 ++- Wabbajack.Lib/MO2Installer.cs | 6 +- .../ModListRegistry/InstalledModLists.cs | 66 +++++++++++++++++++ .../View Models/Gallery/ModListMetadataVM.cs | 3 + .../View Models/Installers/MO2InstallerVM.cs | 1 + Wabbajack/View Models/ModListVM.cs | 14 ++++ 7 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 Wabbajack.Lib/ModListRegistry/InstalledModLists.cs diff --git a/Wabbajack.Common/Consts.cs b/Wabbajack.Common/Consts.cs index a2507b51..49819adf 100644 --- a/Wabbajack.Common/Consts.cs +++ b/Wabbajack.Common/Consts.cs @@ -138,6 +138,8 @@ namespace Wabbajack.Common public static RelativePath SettingsIni = (RelativePath)"settings.ini"; public static byte SettingsVersion => 2; public static TimeSpan MaxVerifyTime => TimeSpan.FromMinutes(10); + public static Extension ModlistMetadataExtension = new(".modlist_metadata"); + public static readonly string WabbajackAuthoredFilesPrefix = "https://wabbajack.b-cdn.net/"; public static RelativePath NativeSettingsJson = (RelativePath)"native_compiler_settings.json"; diff --git a/Wabbajack.Common/Json.cs b/Wabbajack.Common/Json.cs index 1b9f9f5d..93dffe6b 100644 --- a/Wabbajack.Common/Json.cs +++ b/Wabbajack.Common/Json.cs @@ -62,7 +62,7 @@ namespace Wabbajack.Common public static void ToJson(this T obj, Stream stream, bool useGenericSettings = false, bool prettyPrint = false) { - using var tw = new StreamWriter(stream, Encoding.UTF8, bufferSize: 1024, leaveOpen: true); + using var tw = new StreamWriter(stream, new UTF8Encoding(false), bufferSize: 1024, leaveOpen: true); using var writer = new JsonTextWriter(tw); JsonSerializerSettings settings = (useGenericSettings, prettyPrint) switch @@ -93,6 +93,12 @@ namespace Wabbajack.Common return JsonConvert.DeserializeObject(filename.ReadAllText(), JsonSettings)!; } + + public static async Task FromJsonAsync(this AbsolutePath filename) + { + return JsonConvert.DeserializeObject(await filename.ReadAllTextAsync(), JsonSettings)!; + } + public static T FromJsonString(this string data) { return JsonConvert.DeserializeObject(data, JsonSettings)!; diff --git a/Wabbajack.Lib/MO2Installer.cs b/Wabbajack.Lib/MO2Installer.cs index 33f93b59..f8de1072 100644 --- a/Wabbajack.Lib/MO2Installer.cs +++ b/Wabbajack.Lib/MO2Installer.cs @@ -22,6 +22,7 @@ using File = Alphaleonis.Win32.Filesystem.File; using Path = Alphaleonis.Win32.Filesystem.Path; using SectionData = Wabbajack.Common.SectionData; using System.Collections.Generic; +using Wabbajack.Lib.ModListRegistry; using Wabbajack.VirtualFileSystem; namespace Wabbajack.Lib @@ -33,6 +34,8 @@ namespace Wabbajack.Lib public override ModManager ModManager => ModManager.MO2; public AbsolutePath? GameFolder { get; set; } + + public ModlistMetadata? Metadata { get; set; } public MO2Installer(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters) : base( @@ -194,8 +197,9 @@ namespace Wabbajack.Lib UpdateTracker.NextStep("Create Empty Output Mods"); CreateOutputMods(); - UpdateTracker.NextStep("Updating System-specific ini settings"); + UpdateTracker.NextStep("Updating System-specific ini settings and writing metadata"); SetScreenSizeInPrefs(); + await InstalledModLists.AddModListInstall(Metadata, ModList, OutputFolder, DownloadFolder, ModListArchive); UpdateTracker.NextStep("Compacting files"); await CompactFiles(); diff --git a/Wabbajack.Lib/ModListRegistry/InstalledModLists.cs b/Wabbajack.Lib/ModListRegistry/InstalledModLists.cs new file mode 100644 index 00000000..c946a1bb --- /dev/null +++ b/Wabbajack.Lib/ModListRegistry/InstalledModLists.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Wabbajack.Common; +using Wabbajack.Common.Serialization.Json; + +namespace Wabbajack.Lib.ModListRegistry +{ + public class InstalledModLists + { + public static AbsolutePath InstalledModlistsLocation = Consts.LocalAppDataPath.Combine("installed_modlists.json"); + private static AsyncLock _lock = new(); + + public static async Task AddModListInstall(ModlistMetadata? metadata, ModList modList, AbsolutePath installPath, + AbsolutePath downloadPath, AbsolutePath wabbjackPath) + { + modList = modList.Clone(); + modList.Directives = new List(); + modList.Archives = new List(); + + var newRecord = new ModListInstall() + { + Metadata = metadata, + ModList = modList, + InstallationPath = installPath, + DownloadPath = downloadPath, + WabbajackPath = wabbjackPath, + }; + await UpsertInstall(newRecord); + } + + public static async Task UpsertInstall(ModListInstall newRecord) + { + using var _ = await _lock.WaitAsync(); + Dictionary oldRecords = new(); + if (InstalledModlistsLocation.Exists) + oldRecords = await InstalledModlistsLocation.FromJsonAsync>(); + + oldRecords[newRecord.InstallationPath] = newRecord; + + CleanEntries(oldRecords); + + await oldRecords.ToJsonAsync(InstalledModlistsLocation); + } + + private static void CleanEntries(Dictionary oldRecords) + { + oldRecords.Keys + .Where(k => !k.IsDirectory) + .ToArray() + .Do(k => oldRecords.Remove(k)); + } + } + + [JsonName("ModListInstall")] + public class ModListInstall + { + public ModlistMetadata? Metadata { get; set; } + public ModList ModList { get; set; } = new(); + public AbsolutePath InstallationPath { get; set; } + public AbsolutePath DownloadPath { get; set; } + public AbsolutePath WabbajackPath { get; set; } + public DateTime InstalledAt { get; set; } = DateTime.UtcNow; + } +} diff --git a/Wabbajack/View Models/Gallery/ModListMetadataVM.cs b/Wabbajack/View Models/Gallery/ModListMetadataVM.cs index cf16ecd9..bb650868 100644 --- a/Wabbajack/View Models/Gallery/ModListMetadataVM.cs +++ b/Wabbajack/View Models/Gallery/ModListMetadataVM.cs @@ -199,6 +199,9 @@ namespace Wabbajack // Want to rehash to current file, even if failed? await Location.FileHashCachedAsync(); Utils.Log($"Done hashing {Metadata.Links.MachineURL}"); + + await Metadata.ToJsonAsync(Location.WithExtension(Consts.ModlistMetadataExtension)); + tcs.SetResult(result); } catch (Exception ex) diff --git a/Wabbajack/View Models/Installers/MO2InstallerVM.cs b/Wabbajack/View Models/Installers/MO2InstallerVM.cs index 4c61e179..c183b679 100644 --- a/Wabbajack/View Models/Installers/MO2InstallerVM.cs +++ b/Wabbajack/View Models/Installers/MO2InstallerVM.cs @@ -168,6 +168,7 @@ namespace Wabbajack downloadFolder: DownloadLocation.TargetPath, parameters: SystemParametersConstructor.Create())) { + installer.Metadata = Parent.ModList.SourceModListMetadata; installer.UseCompression = Parent.MWVM.Settings.Filters.UseCompression; Parent.MWVM.Settings.Performance.SetProcessorSettings(installer); diff --git a/Wabbajack/View Models/ModListVM.cs b/Wabbajack/View Models/ModListVM.cs index ed20c653..82c934a3 100644 --- a/Wabbajack/View Models/ModListVM.cs +++ b/Wabbajack/View Models/ModListVM.cs @@ -7,12 +7,14 @@ using System.Reactive.Linq; using System.Windows.Media.Imaging; using Wabbajack.Common; using Wabbajack.Lib; +using Wabbajack.Lib.ModListRegistry; namespace Wabbajack { public class ModListVM : ViewModel { public ModList SourceModList { get; private set; } + public ModlistMetadata SourceModListMetadata { get; private set; } public Exception Error { get; } public AbsolutePath ModListPath { get; } public string Name => SourceModList?.Name; @@ -36,6 +38,18 @@ namespace Wabbajack try { SourceModList = AInstaller.LoadFromFile(modListPath); + var metadataPath = modListPath.WithExtension(Consts.ModlistMetadataExtension); + if (metadataPath.Exists) + { + try + { + SourceModListMetadata = metadataPath.FromJson(); + } + catch (Exception) + { + SourceModListMetadata = null; + } + } } catch (Exception ex) {