From a0e2b62bc34a2f356f0e635e0f05741e0df56cc2 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sun, 8 Sep 2024 03:29:03 -0500 Subject: [PATCH 01/13] Revert "IModMasterFlagsGetter" This reverts commit d6975d37a21f394b61a592064e259b84045f0675. --- .../Binary/Parameters/BinaryWriteParameters.cs | 2 +- .../Binary/Translations/ModHeaderWriteLogic.cs | 2 +- .../Plugins/Masters/SeparatedMasterPackage.cs | 6 +++--- Mutagen.Bethesda.Core/Plugins/Records/AMod.cs | 6 +++--- Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs | 15 ++++++--------- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs index f82bc5dd2..eb1a5e72d 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs @@ -101,7 +101,7 @@ public sealed record BinaryWriteParameters /// /// Load order. Required for games with Separated Load Order lists per master type /// - public ILoadOrderGetter? LoadOrder { get; init; } + public ILoadOrderGetter? LoadOrder { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs index cee39c1d1..8d2835b9a 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs @@ -155,7 +155,7 @@ private void PostProcessAdjustments( MutagenWriter writer, IModGetter mod, IModHeaderCommon modHeader, - ILoadOrderGetter? loadOrder) + ILoadOrderGetter? loadOrder) { HandleDisallowedLowerFormIDs(); writer.MetaData.MasterReferences = ConstructWriteMasters(mod); diff --git a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs index ee2b15508..374ab40d8 100644 --- a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs +++ b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs @@ -51,7 +51,7 @@ public static IReadOnlySeparatedMasterPackage Factory( ModKey currentModKey, MasterStyle style, IReadOnlyMasterReferenceCollection masters, - ILoadOrderGetter? loadOrder) + ILoadOrderGetter? loadOrder) { var constants = GameConstants.Get(release); if (constants.SeparateMasterLoadOrders) @@ -151,13 +151,13 @@ internal static IReadOnlySeparatedMasterPackage Separate( ModKey currentModKey, MasterStyle style, IReadOnlyMasterReferenceCollection masters, - ILoadOrderGetter? loadOrder) + ILoadOrderGetter? loadOrder) { var normal = new List(); var medium = new List(); var small = new List(); - void AddToList(IModMasterFlagsGetter mod, ModKey modKey) + void AddToList(IModFlagsGetter mod, ModKey modKey) { if (mod.IsMediumMaster) { diff --git a/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs b/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs index 396faec97..215053299 100644 --- a/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs +++ b/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs @@ -50,13 +50,13 @@ public AMod(ModKey modKey) public abstract bool UsingLocalization { get; set; } bool IModFlagsGetter.UsingLocalization => throw new NotImplementedException(); public abstract bool IsMaster { get; set; } - bool IModMasterFlagsGetter.IsMaster => throw new NotImplementedException(); + bool IModFlagsGetter.IsMaster => throw new NotImplementedException(); public abstract bool CanBeSmallMaster { get; } public abstract bool IsSmallMaster { get; set; } - bool IModMasterFlagsGetter.IsSmallMaster => throw new NotImplementedException(); + bool IModFlagsGetter.IsSmallMaster => throw new NotImplementedException(); public abstract bool CanBeMediumMaster { get; } public abstract bool IsMediumMaster { get; set; } - bool IModMasterFlagsGetter.IsMediumMaster => throw new NotImplementedException(); + bool IModFlagsGetter.IsMediumMaster => throw new NotImplementedException(); public abstract bool ListsOverriddenForms { get; } IGroup? IMod.TryGetTopLevelGroup() => throw new NotImplementedException(); IGroup? IMod.TryGetTopLevelGroup(Type t) => throw new NotImplementedException(); diff --git a/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs b/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs index d344b017e..4134bdbf1 100644 --- a/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs +++ b/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs @@ -1,6 +1,6 @@ namespace Mutagen.Bethesda.Plugins.Records; -public interface IModFlagsGetter : IModMasterFlagsGetter, IModKeyed +public interface IModFlagsGetter : IModKeyed { /// /// Whether a mod supports localization features @@ -12,14 +12,6 @@ public interface IModFlagsGetter : IModMasterFlagsGetter, IModKeyed /// bool UsingLocalization { get; } - /// - /// Whether a mod lists overridden forms in its header - /// - bool ListsOverriddenForms { get; } -} - -public interface IModMasterFlagsGetter : IModKeyed -{ /// /// Whether a mod supports Small Master features /// @@ -44,6 +36,11 @@ public interface IModMasterFlagsGetter : IModKeyed /// Whether a mod has Master flag enabled /// bool IsMaster { get; } + + /// + /// Whether a mod lists overridden forms in its header + /// + bool ListsOverriddenForms { get; } } public record ModFlags : IModFlagsGetter From f90c30eea98fffef1b75e60e569aaaba0b2233b6 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sun, 8 Sep 2024 03:41:58 -0500 Subject: [PATCH 02/13] LoadOrder.ResolveExistingMods --- .../Extensions/LoadOrderExt.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs b/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs index cb0226549..c782d7892 100644 --- a/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs +++ b/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs @@ -3,6 +3,7 @@ using Mutagen.Bethesda.Plugins.Exceptions; using Mutagen.Bethesda.Plugins.Order; using Mutagen.Bethesda.Plugins.Records; +using Noggog; namespace Mutagen.Bethesda; @@ -78,6 +79,28 @@ public static IEnumerable ResolveAllModsExist(this IEnumerab return loadOrder.Select(x => x.Mod!); } + /// + /// Converts any listings that have mods into Mods. Will not throw + /// + /// Listings to convert + /// Mods contained in the listings that exist + public static IEnumerable ResolveExistingMods(this IEnumerable> loadOrder) + where TModItem : class, IModKeyed + { + loadOrder = loadOrder.ToArray(); + var missingMods = loadOrder.Where(x => x.Mod == null) + .ToArray(); + + if (missingMods.Length > 0) + { + throw new MissingModException(missingMods.Select(x => x.ModKey)); + } + + return loadOrder + .Select(x => x.Mod) + .NotNull(); + } + /// /// Converts listings to Mods. Will throw if any mods do not exist /// From 29f770d81571716fdb32b5bfa72fa66093aa75c7 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sun, 8 Sep 2024 03:42:45 -0500 Subject: [PATCH 03/13] IModMasterStyled --- .../Placeholders/TestMod.cs | 2 ++ Mutagen.Bethesda.Core/Plugins/Records/AMod.cs | 2 ++ Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs | 11 +++++++++-- Mutagen.Bethesda.Fallout4/Records/Fallout4Mod.cs | 2 ++ Mutagen.Bethesda.Oblivion/Records/OblivionMod.cs | 2 ++ Mutagen.Bethesda.Skyrim/Records/SkyrimMod.cs | 2 ++ Mutagen.Bethesda.Starfield/Records/StarfieldMod.cs | 2 ++ 7 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Mutagen.Bethesda.Core.UnitTests/Placeholders/TestMod.cs b/Mutagen.Bethesda.Core.UnitTests/Placeholders/TestMod.cs index a1eb44816..8d6cd6d2d 100644 --- a/Mutagen.Bethesda.Core.UnitTests/Placeholders/TestMod.cs +++ b/Mutagen.Bethesda.Core.UnitTests/Placeholders/TestMod.cs @@ -48,6 +48,8 @@ public uint GetRecordCount() public bool IsMediumMaster { get; set; } public bool ListsOverriddenForms { get; } + public MasterStyle MasterStyle { get; } + IGroup? IMod.TryGetTopLevelGroup(Type type) { throw new NotImplementedException(); diff --git a/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs b/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs index 215053299..e04c66fa1 100644 --- a/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs +++ b/Mutagen.Bethesda.Core/Plugins/Records/AMod.cs @@ -91,6 +91,8 @@ public AMod(ModKey modKey) uint IModGetter.GetRecordCount()=> throw new NotImplementedException(); #endregion + public MasterStyle MasterStyle => this.GetMasterStyle(); + /// public FormKey GetNextFormKey() { diff --git a/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs b/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs index 4134bdbf1..50f836b61 100644 --- a/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs +++ b/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs @@ -1,6 +1,6 @@ -namespace Mutagen.Bethesda.Plugins.Records; +namespace Mutagen.Bethesda.Plugins.Records; -public interface IModFlagsGetter : IModKeyed +public interface IModFlagsGetter : IModMasterStyled { /// /// Whether a mod supports localization features @@ -43,6 +43,11 @@ public interface IModFlagsGetter : IModKeyed bool ListsOverriddenForms { get; } } +public interface IModMasterStyled : IModKeyed +{ + MasterStyle MasterStyle { get; } +} + public record ModFlags : IModFlagsGetter { public ModKey ModKey { get; init; } @@ -72,4 +77,6 @@ public ModFlags(IModFlagsGetter flags) IsMaster = flags.IsMaster; ListsOverriddenForms = flags.ListsOverriddenForms; } + + public MasterStyle MasterStyle => this.GetMasterStyle(); } \ No newline at end of file diff --git a/Mutagen.Bethesda.Fallout4/Records/Fallout4Mod.cs b/Mutagen.Bethesda.Fallout4/Records/Fallout4Mod.cs index 3d7f2a7af..082a57e53 100644 --- a/Mutagen.Bethesda.Fallout4/Records/Fallout4Mod.cs +++ b/Mutagen.Bethesda.Fallout4/Records/Fallout4Mod.cs @@ -188,6 +188,8 @@ public uint GetDefaultInitialNextFormID(bool? forceUseLowerFormIDRanges = false) public bool CanBeMediumMaster => false; public bool IsMediumMaster => false; public bool ListsOverriddenForms => true; + public MasterStyle MasterStyle => this.GetMasterStyle(); + public IReadOnlyList>? OverriddenForms => this.ModHeader.OverriddenForms; diff --git a/Mutagen.Bethesda.Oblivion/Records/OblivionMod.cs b/Mutagen.Bethesda.Oblivion/Records/OblivionMod.cs index 1d9fdd9c7..c4d4a465e 100644 --- a/Mutagen.Bethesda.Oblivion/Records/OblivionMod.cs +++ b/Mutagen.Bethesda.Oblivion/Records/OblivionMod.cs @@ -177,6 +177,8 @@ public uint GetDefaultInitialNextFormID(bool? forceUseLowerFormIDRanges = false) public bool CanBeMediumMaster => false; public bool IsMediumMaster => false; public bool ListsOverriddenForms => false; + public MasterStyle MasterStyle => this.GetMasterStyle(); + public IReadOnlyList>? OverriddenForms => null; public IBinaryModdedWriteBuilderLoadOrderChoice diff --git a/Mutagen.Bethesda.Skyrim/Records/SkyrimMod.cs b/Mutagen.Bethesda.Skyrim/Records/SkyrimMod.cs index f922517f5..eaecf6d0b 100644 --- a/Mutagen.Bethesda.Skyrim/Records/SkyrimMod.cs +++ b/Mutagen.Bethesda.Skyrim/Records/SkyrimMod.cs @@ -201,6 +201,8 @@ public uint GetDefaultInitialNextFormID(bool? forceUseLowerFormIDRanges = false) public bool CanBeMediumMaster => false; public bool IsMediumMaster => false; public bool ListsOverriddenForms => true; + public MasterStyle MasterStyle => this.GetMasterStyle(); + public IReadOnlyList>? OverriddenForms => this.ModHeader.OverriddenForms; diff --git a/Mutagen.Bethesda.Starfield/Records/StarfieldMod.cs b/Mutagen.Bethesda.Starfield/Records/StarfieldMod.cs index 94b6b0f47..2a2d94b2a 100644 --- a/Mutagen.Bethesda.Starfield/Records/StarfieldMod.cs +++ b/Mutagen.Bethesda.Starfield/Records/StarfieldMod.cs @@ -195,6 +195,8 @@ public uint GetDefaultInitialNextFormID(bool? forceUseLowerFormIDRanges = false) public bool CanBeMediumMaster => true; public bool IsMediumMaster => this.ModHeader.Flags.HasFlag(StarfieldModHeader.HeaderFlag.Medium); public bool ListsOverriddenForms => true; + public MasterStyle MasterStyle => this.GetMasterStyle(); + public IReadOnlyList>? OverriddenForms => this.ModHeader.OverriddenForms; From 571ddcca3418771393cd5bf4aebdbe42f3b7cc85 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sun, 8 Sep 2024 03:45:15 -0500 Subject: [PATCH 04/13] BinaryReadParameters takes IModMasterStyled --- .../Plugins/Masters/MastersTestUtil.cs | 1 + .../Binary/Parameters/BinaryReadParameters.cs | 2 +- .../Plugins/Masters/SeparatedMasterPackage.cs | 34 ++++++++----------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/Mutagen.Bethesda.Core.UnitTests/Plugins/Masters/MastersTestUtil.cs b/Mutagen.Bethesda.Core.UnitTests/Plugins/Masters/MastersTestUtil.cs index 526cf1fe2..327d1dcb2 100644 --- a/Mutagen.Bethesda.Core.UnitTests/Plugins/Masters/MastersTestUtil.cs +++ b/Mutagen.Bethesda.Core.UnitTests/Plugins/Masters/MastersTestUtil.cs @@ -13,6 +13,7 @@ internal static IModFlagsGetter GetFlags(ModKey modKey, MasterStyle style) modGetter.ModKey.Returns(modKey); modGetter.CanBeSmallMaster.Returns(true); modGetter.CanBeMediumMaster.Returns(true); + modGetter.MasterStyle.Returns(style); switch (style) { case MasterStyle.Full: diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs index 55cae1c76..6c438cb3c 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs @@ -17,7 +17,7 @@ public record BinaryReadParameters /// /// Load order. Required for games with Separated Load Order lists per master type /// - public ILoadOrderGetter? LoadOrder { get; init; } + public ILoadOrderGetter? LoadOrder { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs index 374ab40d8..0a8a866df 100644 --- a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs +++ b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs @@ -51,7 +51,7 @@ public static IReadOnlySeparatedMasterPackage Factory( ModKey currentModKey, MasterStyle style, IReadOnlyMasterReferenceCollection masters, - ILoadOrderGetter? loadOrder) + ILoadOrderGetter? loadOrder) { var constants = GameConstants.Get(release); if (constants.SeparateMasterLoadOrders) @@ -151,25 +151,27 @@ internal static IReadOnlySeparatedMasterPackage Separate( ModKey currentModKey, MasterStyle style, IReadOnlyMasterReferenceCollection masters, - ILoadOrderGetter? loadOrder) + ILoadOrderGetter? loadOrder) { var normal = new List(); var medium = new List(); var small = new List(); - void AddToList(IModFlagsGetter mod, ModKey modKey) + void AddToList(IModMasterStyled mod, ModKey modKey) { - if (mod.IsMediumMaster) + switch (mod.MasterStyle) { - AddToListViaStyle(MasterStyle.Medium, modKey); - } - else if (mod.IsSmallMaster) - { - AddToListViaStyle(MasterStyle.Small, modKey); - } - else - { - AddToListViaStyle(MasterStyle.Full, modKey); + case MasterStyle.Full: + AddToListViaStyle(MasterStyle.Full, modKey); + break; + case MasterStyle.Small: + AddToListViaStyle(MasterStyle.Small, modKey); + break; + case MasterStyle.Medium: + AddToListViaStyle(MasterStyle.Medium, modKey); + break; + default: + throw new ArgumentOutOfRangeException(); } } @@ -201,12 +203,6 @@ void AddToListViaStyle(MasterStyle style, ModKey modKey) "Mod was missing from load order when constructing the separate mod lists needed for FormID translation."); } - if (mod.IsSmallMaster && mod.IsMediumMaster) - { - throw new ModHeaderMalformedException(mod.ModKey, - "Mod had both Light and Medium master flags enabled"); - } - AddToList(mod, master.Master); } // Don't have a load order, assume normal From b24e8215443d9109b77cb42d5b81ffcceec5ced3 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sun, 8 Sep 2024 15:47:52 -0500 Subject: [PATCH 05/13] BinaryReadBuilder _loadOrderSetter refactors --- .../Binary/Translations/BinaryReadBuilder.cs | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index 2451f558d..0afd938a1 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -29,7 +29,7 @@ internal record BinaryReadBuilderParams internal TGroupMask? GroupMask { get; init; } internal BinaryReadParameters Params { get; init; } = BinaryReadParameters.Default; internal IBinaryReadBuilderInstantiator _instantiator { get; init; } = null!; - internal Func, BinaryReadParameters>? _loadOrderSetter { get; init; } + internal Func, IEnumerable>? _loadOrderSetter { get; init; } internal Func, DirectoryPath>? _dataFolderGetter { get; init; } } @@ -273,12 +273,7 @@ public BinaryReadBuilder WithDefaultLoadOrder() dataFolder, param.GameRelease, param.Params.FileSystem); - return param.Params with - { - LoadOrder = new LoadOrder( - lo.ListedOrder.ResolveAllModsExist(), - disposeItems: false), - }; + return lo.ListedOrder.ResolveExistingMods(); } }); } @@ -326,7 +321,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO var dataFolder = param._dataFolderGetter?.Invoke(param); if (dataFolder == null) { - return param.Params; + return Array.Empty(); } ModHeaderFrame modHeader; @@ -359,12 +354,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO var lo = LoadOrder.Import( dataFolder.Value, masters.Masters.Select(x => x.Master), param.GameRelease, param.Params.FileSystem); - return param.Params with - { - LoadOrder = new LoadOrder( - lo.ListedOrder.ResolveAllModsExist(), - disposeItems: false), - }; + return lo.ListedOrder.ResolveExistingMods(); } }); } @@ -434,13 +424,7 @@ internal BinaryReadBuilder( /// A readonly mod object with minimal initial parsing done public TModGetter Construct() { - if (_param._loadOrderSetter != null) - { - _param = _param with - { - Params = _param._loadOrderSetter(_param) - }; - } + _param = BinaryReadBuilderHelper.RunLoadOrderSetter(_param); return _param._instantiator.Readonly(this); } @@ -1061,13 +1045,37 @@ public BinaryReadMutableBuilder WithGroupMask(TGro /// A mutable mod object with all the data loaded public new TMod Construct() { - if (_param._loadOrderSetter != null) + _param = BinaryReadBuilderHelper.RunLoadOrderSetter(_param); + return _param._instantiator.Mutable(this); + } +} + +internal static class BinaryReadBuilderHelper +{ + public static BinaryReadBuilderParams RunLoadOrderSetter( + BinaryReadBuilderParams p) + where TMod : IMod + where TModGetter : IModDisposeGetter + { + IReadOnlyCollection loadOrder = Array.Empty(); + + if (p._loadOrderSetter != null) { - _param = _param with - { - Params = _param._loadOrderSetter(_param) - }; + loadOrder = p._loadOrderSetter(p).ToArray(); } - return _param._instantiator.Mutable(this); + + if (loadOrder.Count == 0) + { + return p; + } + + return p with + { + Params = p.Params with + { + LoadOrder = new LoadOrder( + loadOrder.Distinct(x => x.ModKey)) + } + }; } } \ No newline at end of file From 3ecc73946fd04fd182ddf4278de006c196e9d443 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sun, 8 Sep 2024 15:56:03 -0500 Subject: [PATCH 06/13] BinaryReadBuilder more WithLoadOrder options --- .../Binary/Translations/BinaryReadBuilder.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index 0afd938a1..38073ca8a 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -278,6 +278,94 @@ public BinaryReadBuilder WithDefaultLoadOrder() }); } + /// + /// Provides a load order of mod objects to look to.
+ /// This is used to construct the separated load order needed to interpret FormIDs.
+ /// It is expected to contain all of the mods that this mod has as masters. + ///
+ /// Load order to refer to when parsing + /// Builder object to continue customization + public BinaryReadBuilderDataFolderChoice WithLoadOrder(IEnumerable? loadOrder) + { + return WithLoadOrder(loadOrder?.ToArray() ?? Array.Empty()); + } + + /// + /// Provides a load order of mod objects to look to.
+ /// This is used to construct the separated load order needed to interpret FormIDs.
+ /// It is expected to contain all of the mods that this mod has as masters. + ///
+ /// Load order to refer to when parsing + /// Builder object to continue customization + public BinaryReadBuilderDataFolderChoice WithLoadOrder(params ModKey[] loadOrder) + { + return new BinaryReadBuilderDataFolderChoice(_param with + { + _loadOrderSetter = (param) => + { + if (loadOrder.Length == 0) + { + return Array.Empty(); + } + + var dataFolder = param._dataFolderGetter?.Invoke(param); + if (dataFolder == null) + { + return Array.Empty(); + } + + var lo = LoadOrder.Import( + dataFolder.Value, loadOrder, + param.GameRelease, param.Params.FileSystem); + return lo.ListedOrder.ResolveExistingMods(); + } + }); + } + + /// + /// Provides a load order of mod objects to look to.
+ /// This is used to construct the separated load order needed to interpret FormIDs.
+ /// It is expected to contain all of the mods that this mod has as masters. + ///
+ /// Load order to refer to when parsing + /// Builder object to continue customization + public BinaryReadBuilderDataFolderChoice WithLoadOrder(IEnumerable? loadOrder) + { + return WithLoadOrder(loadOrder?.ToArray() ?? Array.Empty()); + } + + /// + /// Provides a load order of mod objects to look to.
+ /// This is used to construct the separated load order needed to interpret FormIDs.
+ /// It is expected to contain all of the mods that this mod has as masters. + ///
+ /// Load order to refer to when parsing + /// Builder object to continue customization + public BinaryReadBuilderDataFolderChoice WithLoadOrder(params IModFlagsGetter[] loadOrder) + { + return new BinaryReadBuilderDataFolderChoice(_param with + { + _loadOrderSetter = (param) => + { + if (loadOrder.Length == 0) + { + return Array.Empty(); + } + + var dataFolder = param._dataFolderGetter?.Invoke(param); + if (dataFolder == null) + { + return Array.Empty(); + } + + var lo = LoadOrder.Import( + dataFolder.Value, loadOrder.Select(x => x.ModKey), + param.GameRelease, param.Params.FileSystem); + return lo.ListedOrder.ResolveExistingMods(); + } + }); + } + /// /// Provides a load order of mod objects to look to.
/// This is used to construct the separated load order needed to interpret FormIDs.
From c52e4a9debe71fed097b316827eee780f2105c38 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Thu, 12 Sep 2024 15:24:44 -0500 Subject: [PATCH 07/13] MasterFlagsLookup instead of LoadOrder naming --- .../Binary/Parameters/BinaryReadParameters.cs | 4 ++-- .../Parameters/BinaryWriteParameters.cs | 4 ++-- .../Plugins/Binary/Streams/ParsingMeta.cs | 4 ++-- .../Binary/Translations/BinaryReadBuilder.cs | 4 ++-- .../Binary/Translations/BinaryWriteBuilder.cs | 20 +++++++++---------- .../Translations/ModHeaderWriteLogic.cs | 2 +- .../Plugins/Order/DI/LoadOrderImporter.cs | 4 ++-- .../Masters/SeparatedMastersTesting.cs | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs index 6c438cb3c..c879feec3 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs @@ -15,9 +15,9 @@ public record BinaryReadParameters public StringsReadParameters? StringsParam { get; init; } /// - /// Load order. Required for games with Separated Load Order lists per master type + /// Required for games with Separated Load Order lists per master type /// - public ILoadOrderGetter? LoadOrder { get; init; } + public ILoadOrderGetter? MasterFlagsLookup { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs index eb1a5e72d..64c7d780f 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs @@ -99,9 +99,9 @@ public sealed record BinaryWriteParameters public ALowerRangeDisallowedHandlerOption LowerRangeDisallowedHandler { get; init; } = new AddPlaceholderMasterIfLowerRangeDisallowed(); /// - /// Load order. Required for games with Separated Load Order lists per master type + /// Required for games with Separated Load Order lists per master type /// - public ILoadOrderGetter? LoadOrder { get; init; } + public ILoadOrderGetter? MasterFlagsLookup { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs index 9d8ad0f74..e0b58cd93 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs @@ -135,7 +135,7 @@ public static ParsingMeta Factory( { var header = ModHeaderFrame.FromPath(modPath, release, fileSystem: param.FileSystem); var rawMasters = MasterReferenceCollection.FromModHeader(modPath.ModKey, header); - var masters = SeparatedMasterPackage.Factory(release, modPath, header.MasterStyle, rawMasters, param.LoadOrder); + var masters = SeparatedMasterPackage.Factory(release, modPath, header.MasterStyle, rawMasters, param.MasterFlagsLookup); var meta = new ParsingMeta(GameConstants.Get(release), modPath.ModKey, masters); meta.Absorb(param); return meta; @@ -150,7 +150,7 @@ public static ParsingMeta Factory( var header = ModHeaderFrame.FromStream(stream, modKey, release); var rawMasters = MasterReferenceCollection.FromModHeader(modKey, header); stream.Position = 0; - var masters = SeparatedMasterPackage.Factory(release, modKey, header.MasterStyle, rawMasters, param.LoadOrder); + var masters = SeparatedMasterPackage.Factory(release, modKey, header.MasterStyle, rawMasters, param.MasterFlagsLookup); var meta = new ParsingMeta(GameConstants.Get(release), modKey, masters); meta.Absorb(param); return meta; diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index 38073ca8a..d37d33f60 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -380,7 +380,7 @@ _param with { Params = _param.Params with { - LoadOrder = loadOrder + MasterFlagsLookup = loadOrder } }); } @@ -1161,7 +1161,7 @@ public static BinaryReadBuilderParams RunLoadOrder { Params = p.Params with { - LoadOrder = new LoadOrder( + MasterFlagsLookup = new LoadOrder( loadOrder.Distinct(x => x.ModKey)) } }; diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs index a5aa60149..feb3ab5b6 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs @@ -145,7 +145,7 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( } return p._param with { - LoadOrder = loadOrder.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = loadOrder.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -172,7 +172,7 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( } return p._param with { - LoadOrder = loadOrder, + MasterFlagsLookup = loadOrder, MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -206,7 +206,7 @@ public BinaryModdedWriteBuilderTargetChoice WithDefaultLoadOrder() } return p._param with { - LoadOrder = modFlagsLo, + MasterFlagsLookup = modFlagsLo, MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -252,7 +252,7 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrder( m.GameRelease, p._param.FileSystem); return p._param with { - LoadOrder = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -306,7 +306,7 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrderFromHea } return p._param with { - LoadOrder = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -364,7 +364,7 @@ public BinaryWriteBuilderTargetChoice WithLoadOrder( } return p._param with { - LoadOrder = loadOrder.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = loadOrder.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -390,7 +390,7 @@ public BinaryWriteBuilderTargetChoice WithLoadOrder( } return p._param with { - LoadOrder = loadOrder, + MasterFlagsLookup = loadOrder, MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -418,7 +418,7 @@ public BinaryWriteBuilderTargetChoice WithDefaultLoadOrder() } return p._param with { - LoadOrder = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo) }; } @@ -447,7 +447,7 @@ public BinaryWriteBuilderDataFolderChoice WithLoadOrder( } return p._param with { - LoadOrder = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -482,7 +482,7 @@ public BinaryWriteBuilderDataFolderChoice WithLoadOrderFromHeaderMas } return p._param with { - LoadOrder = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs index 8d2835b9a..406932ec9 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs @@ -59,7 +59,7 @@ public static void WriteHeader( modHeaderWriter.RunProcessors(mod); modHeaderWriter.PostProcessAdjustments(writer, mod, modHeader, modHeaderWriter._constants.SeparateMasterLoadOrders - ? param.LoadOrder + ? param.MasterFlagsLookup : null); modHeader.WriteToBinary(writer); } diff --git a/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs b/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs index 9802689ca..00667d607 100644 --- a/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs +++ b/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs @@ -128,11 +128,11 @@ public ILoadOrder> Import(BinaryReadParameters? param = var loList = LoadOrderListingsProvider.Get().ToList(); var results = new (ModKey ModKey, int ModIndex, TryGet Mod, bool Enabled)[loList.Count]; param ??= BinaryReadParameters.Default; - if (param.LoadOrder == null) + if (param.MasterFlagsLookup == null) { param = param with { - LoadOrder = new LoadOrder(loList + MasterFlagsLookup = new LoadOrder(loList .Select(listing => { var modPath = new ModPath(listing.ModKey, diff --git a/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs b/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs index e14608ea8..f2bb045ca 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs @@ -94,7 +94,7 @@ public void NonSeparated( using var reimport = SkyrimMod.CreateFromBinaryOverlay(modPath, SkyrimRelease.SkyrimSE, new BinaryReadParameters() { FileSystem = fileSystem, - LoadOrder = lo + MasterFlagsLookup = lo }); reimport.Npcs.Select(x => x.FormKey).Should().Equal(modANpc.FormKey, originatingNpc.FormKey); var reimportNpc = reimport.Npcs[originatingNpc.FormKey]; @@ -255,7 +255,7 @@ public void Separated( using var reimport = StarfieldMod.CreateFromBinaryOverlay(modPath, StarfieldRelease.Starfield, new BinaryReadParameters() { FileSystem = fileSystem, - LoadOrder = lo + MasterFlagsLookup = lo }); reimport.Npcs.Select(x => x.FormKey).Should().Equal( modANpc.FormKey, From 0d6905ef2dd528e54615d9f8684007fd77678702 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Thu, 12 Sep 2024 15:59:23 -0500 Subject: [PATCH 08/13] MasterFlagsLookup swapped to Cache object --- .../Binary/Parameters/BinaryReadParameters.cs | 3 ++- .../Binary/Parameters/BinaryWriteParameters.cs | 2 +- .../Processing/Alignment/ModRecordAligner.cs | 2 +- .../Binary/Translations/BinaryReadBuilder.cs | 6 +++--- .../Binary/Translations/ModHeaderWriteLogic.cs | 4 ++-- .../Plugins/Masters/SeparatedMasterPackage.cs | 18 +++++++++--------- .../Plugins/Order/LoadOrder.cs | 4 +++- .../Plugins/Records/ModFlags.cs | 4 ++-- 8 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs index c879feec3..9c66e3559 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryReadParameters.cs @@ -2,6 +2,7 @@ using Mutagen.Bethesda.Plugins.Order; using Mutagen.Bethesda.Plugins.Records; using Mutagen.Bethesda.Strings; +using Noggog; namespace Mutagen.Bethesda.Plugins.Binary.Parameters; @@ -17,7 +18,7 @@ public record BinaryReadParameters /// /// Required for games with Separated Load Order lists per master type /// - public ILoadOrderGetter? MasterFlagsLookup { get; init; } + public IReadOnlyCache? MasterFlagsLookup { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs index 64c7d780f..576c55462 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs @@ -101,7 +101,7 @@ public sealed record BinaryWriteParameters /// /// Required for games with Separated Load Order lists per master type /// - public ILoadOrderGetter? MasterFlagsLookup { get; init; } + public IReadOnlyCache? MasterFlagsLookup { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Processing/Alignment/ModRecordAligner.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Processing/Alignment/ModRecordAligner.cs index 205d0af98..37a96f8c6 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Processing/Alignment/ModRecordAligner.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Processing/Alignment/ModRecordAligner.cs @@ -29,7 +29,7 @@ public static void Align( interest.InterestingTypes.Add("QUST"); interest.InterestingTypes.Add("REFR"); - var masters = SeparatedMasterPackage.Factory(gameMode, inputPath, loadOrder: null, fileSystem: null); + var masters = SeparatedMasterPackage.Factory(gameMode, inputPath, masterFlagLookup: null, fileSystem: null); var meta = new ParsingMeta(gameMode, inputPath.ModKey, masters); using (var inputStream = new MutagenBinaryReadStream(inputPath, meta)) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index d37d33f60..fddedfb95 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -29,7 +29,7 @@ internal record BinaryReadBuilderParams internal TGroupMask? GroupMask { get; init; } internal BinaryReadParameters Params { get; init; } = BinaryReadParameters.Default; internal IBinaryReadBuilderInstantiator _instantiator { get; init; } = null!; - internal Func, IEnumerable>? _loadOrderSetter { get; init; } + internal Func, IEnumerable>? _loadOrderSetter { get; init; } internal Func, DirectoryPath>? _dataFolderGetter { get; init; } } @@ -1145,7 +1145,7 @@ public static BinaryReadBuilderParams RunLoadOrder where TMod : IMod where TModGetter : IModDisposeGetter { - IReadOnlyCollection loadOrder = Array.Empty(); + IReadOnlyCollection loadOrder = Array.Empty(); if (p._loadOrderSetter != null) { @@ -1161,7 +1161,7 @@ public static BinaryReadBuilderParams RunLoadOrder { Params = p.Params with { - MasterFlagsLookup = new LoadOrder( + MasterFlagsLookup = new LoadOrder( loadOrder.Distinct(x => x.ModKey)) } }; diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs index 406932ec9..6436df263 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs @@ -155,7 +155,7 @@ private void PostProcessAdjustments( MutagenWriter writer, IModGetter mod, IModHeaderCommon modHeader, - ILoadOrderGetter? loadOrder) + IReadOnlyCache? masterFlagLookup) { HandleDisallowedLowerFormIDs(); writer.MetaData.MasterReferences = ConstructWriteMasters(mod); @@ -164,7 +164,7 @@ private void PostProcessAdjustments( mod.ModKey, mod.GetMasterStyle(), writer.MetaData.MasterReferences, - loadOrder); + masterFlagLookup); modHeader.MasterReferences.SetTo(writer.MetaData.MasterReferences!.Masters.Select(m => m.DeepCopy())); if (_params.RecordCount != RecordCountOption.NoCheck) { diff --git a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs index 0a8a866df..967b1bc21 100644 --- a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs +++ b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs @@ -51,12 +51,12 @@ public static IReadOnlySeparatedMasterPackage Factory( ModKey currentModKey, MasterStyle style, IReadOnlyMasterReferenceCollection masters, - ILoadOrderGetter? loadOrder) + IReadOnlyCache? masterFlagLookup) { var constants = GameConstants.Get(release); if (constants.SeparateMasterLoadOrders) { - return SeparatedMasterPackage.Separate(currentModKey, style, masters, loadOrder); + return SeparatedMasterPackage.Separate(currentModKey, style, masters, masterFlagLookup); } else { @@ -67,12 +67,12 @@ public static IReadOnlySeparatedMasterPackage Factory( public static IReadOnlySeparatedMasterPackage Factory( GameRelease release, ModPath modPath, - ILoadOrderGetter? loadOrder, + IReadOnlyCache? masterFlagLookup, IFileSystem? fileSystem = null) { var header = ModHeaderFrame.FromPath(modPath, release, fileSystem: fileSystem); var masters = MasterReferenceCollection.FromModHeader(modPath.ModKey, header); - return Factory(release, modPath.ModKey, header.MasterStyle, masters, loadOrder); + return Factory(release, modPath.ModKey, header.MasterStyle, masters, masterFlagLookup); } internal class NotSeparatedMasterPackage : IReadOnlySeparatedMasterPackage @@ -151,13 +151,13 @@ internal static IReadOnlySeparatedMasterPackage Separate( ModKey currentModKey, MasterStyle style, IReadOnlyMasterReferenceCollection masters, - ILoadOrderGetter? loadOrder) + IReadOnlyCache? masterFlagLookup) { var normal = new List(); var medium = new List(); var small = new List(); - void AddToList(IModMasterStyled mod, ModKey modKey) + void AddToList(IModMasterStyledGetter mod, ModKey modKey) { switch (mod.MasterStyle) { @@ -195,12 +195,12 @@ void AddToListViaStyle(MasterStyle style, ModKey modKey) foreach (var master in masters.Masters) { - if (loadOrder != null) + if (masterFlagLookup != null) { - if (!loadOrder.TryGetValue(master.Master, out var mod)) + if (!masterFlagLookup.TryGetValue(master.Master, out var mod)) { throw new MissingModException(master.Master, - "Mod was missing from load order when constructing the separate mod lists needed for FormID translation."); + "Mod was missing from master flag lookup when constructing the separate mod lists needed for FormID translation."); } AddToList(mod, master.Master); diff --git a/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs b/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs index 06a6b4ee8..11e85ffdf 100644 --- a/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs @@ -922,7 +922,9 @@ public interface ILoadOrderGetter : IDisposable bool ContainsKey(ModKey key); } -public interface ILoadOrderGetter : ILoadOrderGetter, IReadOnlyList>, +public interface ILoadOrderGetter : + ILoadOrderGetter, + IReadOnlyList>, IReadOnlyCache where TListing : IModKeyed { diff --git a/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs b/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs index 50f836b61..af976d0d5 100644 --- a/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs +++ b/Mutagen.Bethesda.Core/Plugins/Records/ModFlags.cs @@ -1,6 +1,6 @@ namespace Mutagen.Bethesda.Plugins.Records; -public interface IModFlagsGetter : IModMasterStyled +public interface IModFlagsGetter : IModMasterStyledGetter { /// /// Whether a mod supports localization features @@ -43,7 +43,7 @@ public interface IModFlagsGetter : IModMasterStyled bool ListsOverriddenForms { get; } } -public interface IModMasterStyled : IModKeyed +public interface IModMasterStyledGetter : IModKeyed { MasterStyle MasterStyle { get; } } From ffeb35189661e58e548396b940091a641bde93c8 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Fri, 13 Sep 2024 23:13:32 -0500 Subject: [PATCH 09/13] LoadOrderExt.Where --- Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs b/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs index c782d7892..0488e40e9 100644 --- a/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs +++ b/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs @@ -172,4 +172,12 @@ public static LoadOrder Transform(this ILoad loadOrder.ListedOrder .Select(transformer)); } + + public static LoadOrder Where(this ILoadOrderGetter loadOrder, Func filter) + where TListing : IModKeyed + { + return new LoadOrder( + loadOrder.ListedOrder + .Where(filter)); + } } \ No newline at end of file From 039c605878ba90965449303dba6847610a2086a0 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Fri, 13 Sep 2024 23:14:14 -0500 Subject: [PATCH 10/13] MasterFlagsLookup swapped to IModMasterStyledGetter --- .../Binary/Parameters/BinaryWriteParameters.cs | 2 +- .../Binary/Translations/BinaryReadBuilder.cs | 18 +++++++++--------- .../Binary/Translations/BinaryWriteBuilder.cs | 18 +++++++++--------- .../Binary/Translations/ModHeaderWriteLogic.cs | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs index 576c55462..0a2572958 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Parameters/BinaryWriteParameters.cs @@ -101,7 +101,7 @@ public sealed record BinaryWriteParameters /// /// Required for games with Separated Load Order lists per master type /// - public IReadOnlyCache? MasterFlagsLookup { get; init; } + public IReadOnlyCache? MasterFlagsLookup { get; init; } /// /// Whether to use multithreading when possible diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index fddedfb95..bf100d0ff 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -305,13 +305,13 @@ public BinaryReadBuilderDataFolderChoice WithLoadO { if (loadOrder.Length == 0) { - return Array.Empty(); + return Array.Empty(); } var dataFolder = param._dataFolderGetter?.Invoke(param); if (dataFolder == null) { - return Array.Empty(); + return Array.Empty(); } var lo = LoadOrder.Import( @@ -329,9 +329,9 @@ public BinaryReadBuilderDataFolderChoice WithLoadO /// /// Load order to refer to when parsing /// Builder object to continue customization - public BinaryReadBuilderDataFolderChoice WithLoadOrder(IEnumerable? loadOrder) + public BinaryReadBuilderDataFolderChoice WithLoadOrder(IEnumerable? loadOrder) { - return WithLoadOrder(loadOrder?.ToArray() ?? Array.Empty()); + return WithLoadOrder(loadOrder?.ToArray() ?? Array.Empty()); } /// @@ -341,7 +341,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO /// /// Load order to refer to when parsing /// Builder object to continue customization - public BinaryReadBuilderDataFolderChoice WithLoadOrder(params IModFlagsGetter[] loadOrder) + public BinaryReadBuilderDataFolderChoice WithLoadOrder(params IModMasterStyledGetter[] loadOrder) { return new BinaryReadBuilderDataFolderChoice(_param with { @@ -349,13 +349,13 @@ public BinaryReadBuilderDataFolderChoice WithLoadO { if (loadOrder.Length == 0) { - return Array.Empty(); + return Array.Empty(); } var dataFolder = param._dataFolderGetter?.Invoke(param); if (dataFolder == null) { - return Array.Empty(); + return Array.Empty(); } var lo = LoadOrder.Import( @@ -373,7 +373,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO /// /// Load order to refer to when parsing /// Builder object to continue customization - public BinaryReadBuilder WithLoadOrder(ILoadOrderGetter? loadOrder) + public BinaryReadBuilder WithLoadOrder(ILoadOrderGetter? loadOrder) { return new BinaryReadBuilder( _param with @@ -409,7 +409,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO var dataFolder = param._dataFolderGetter?.Invoke(param); if (dataFolder == null) { - return Array.Empty(); + return Array.Empty(); } ModHeaderFrame modHeader; diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs index feb3ab5b6..84865b472 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs @@ -56,7 +56,7 @@ public interface IBinaryModdedWriteBuilderLoadOrderChoice /// Load order to reference /// Builder object to continue customization public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( - ILoadOrderGetter> loadOrder); + ILoadOrderGetter> loadOrder); /// /// Writes the mod with given load order as reference @@ -64,7 +64,7 @@ public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( /// Load order to reference /// Builder object to continue customization public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( - ILoadOrderGetter loadOrder); + ILoadOrderGetter loadOrder); /// /// Writes the mod with the default load order and data folder as reference. @@ -133,7 +133,7 @@ public BinaryModdedWriteBuilderTargetChoice WithNoLoadOrder() /// Load order to reference /// Builder object to continue customization public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( - ILoadOrderGetter> loadOrder) + ILoadOrderGetter> loadOrder) { return new BinaryModdedWriteBuilderTargetChoice(_mod, _params with { @@ -152,7 +152,7 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( } }); } - IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(ILoadOrderGetter> loadOrder) => WithLoadOrder(loadOrder); + IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(ILoadOrderGetter> loadOrder) => WithLoadOrder(loadOrder); /// /// Writes the mod with given load order as reference @@ -160,7 +160,7 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( /// Load order to reference /// Builder object to continue customization public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( - ILoadOrderGetter loadOrder) + ILoadOrderGetter loadOrder) { return new BinaryModdedWriteBuilderTargetChoice(_mod, _params with { @@ -179,7 +179,7 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( } }); } - IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(ILoadOrderGetter loadOrder) => WithLoadOrder(loadOrder); + IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(ILoadOrderGetter loadOrder) => WithLoadOrder(loadOrder); /// /// Writes the mod with the default load order and data folder as reference. @@ -199,7 +199,7 @@ public BinaryModdedWriteBuilderTargetChoice WithDefaultLoadOrder() { lo = lo.TrimAt(m.ModKey); } - ILoadOrderGetter? modFlagsLo = null; + ILoadOrderGetter? modFlagsLo = null; if (GameConstants.Get(m.GameRelease).SeparateMasterLoadOrders) { modFlagsLo = lo.ResolveAllModsExist(disposeItems: false); @@ -352,7 +352,7 @@ public BinaryWriteBuilderTargetChoice WithNoLoadOrder() /// Load order to reference /// Builder object to continue customization public BinaryWriteBuilderTargetChoice WithLoadOrder( - ILoadOrderGetter> loadOrder) + ILoadOrderGetter> loadOrder) { return new BinaryWriteBuilderTargetChoice(_params with { @@ -378,7 +378,7 @@ public BinaryWriteBuilderTargetChoice WithLoadOrder( /// Load order to reference /// Builder object to continue customization public BinaryWriteBuilderTargetChoice WithLoadOrder( - ILoadOrderGetter loadOrder) + ILoadOrderGetter loadOrder) { return new BinaryWriteBuilderTargetChoice(_params with { diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs index 6436df263..8231d5961 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/ModHeaderWriteLogic.cs @@ -155,7 +155,7 @@ private void PostProcessAdjustments( MutagenWriter writer, IModGetter mod, IModHeaderCommon modHeader, - IReadOnlyCache? masterFlagLookup) + IReadOnlyCache? masterFlagLookup) { HandleDisallowedLowerFormIDs(); writer.MetaData.MasterReferences = ConstructWriteMasters(mod); From 1eff6dbf49f6fcd212c7e140e9acfa6dccacec84 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sat, 14 Sep 2024 00:55:23 -0500 Subject: [PATCH 11/13] ResolveExistingMods fix --- .../Extensions/LoadOrderExt.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs b/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs index 0488e40e9..50761357f 100644 --- a/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs +++ b/Mutagen.Bethesda.Core/Extensions/LoadOrderExt.cs @@ -87,15 +87,6 @@ public static IEnumerable ResolveAllModsExist(this IEnumerab public static IEnumerable ResolveExistingMods(this IEnumerable> loadOrder) where TModItem : class, IModKeyed { - loadOrder = loadOrder.ToArray(); - var missingMods = loadOrder.Where(x => x.Mod == null) - .ToArray(); - - if (missingMods.Length > 0) - { - throw new MissingModException(missingMods.Select(x => x.ModKey)); - } - return loadOrder .Select(x => x.Mod) .NotNull(); @@ -115,6 +106,19 @@ public static LoadOrder ResolveAllModsExist( return new LoadOrder(ResolveAllModsExist(loadOrder.ListedOrder), disposeItems: disposeItems ?? loadOrder.DisposingItems); } + /// + /// Converts any listings that have mods into Mods. Will not throw + /// + /// Listings to convert + /// Mods contained in the listings that exist + public static LoadOrder ResolveExistingMods( + this ILoadOrderGetter> loadOrder, + bool? disposeItems = null) + where TModItem : class, IModKeyed + { + return new LoadOrder(ResolveExistingMods(loadOrder.ListedOrder), disposeItems: disposeItems ?? loadOrder.DisposingItems); + } + /// /// Converts ModKeys to LoadOrderListing objects /// From a2dc133773e07a1ae077e865e8a0ad8e74c27110 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sat, 14 Sep 2024 01:16:16 -0500 Subject: [PATCH 12/13] KeyedMasterStyle /w builders using for load order importing --- .../Mutagen.Bethesda.Core.csproj | 1 + .../Binary/Translations/BinaryReadBuilder.cs | 24 ++++++---- .../Binary/Translations/BinaryWriteBuilder.cs | 44 +++++++++++++------ .../Plugins/Order/DI/LoadOrderImporter.cs | 4 +- .../Plugins/Order/LoadOrder.cs | 12 ++--- .../Plugins/Order/ModListing.cs | 4 +- .../Plugins/Records/DI/ModImporter.cs | 4 +- .../Plugins/Records/KeyedMasterStyle.cs | 21 +++++++++ 8 files changed, 79 insertions(+), 35 deletions(-) create mode 100644 Mutagen.Bethesda.Core/Plugins/Records/KeyedMasterStyle.cs diff --git a/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj b/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj index 698f2030a..701705a16 100644 --- a/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj +++ b/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj @@ -498,6 +498,7 @@ content Compile + cs diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index bf100d0ff..f1b7aef36 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -269,9 +269,10 @@ public BinaryReadBuilder WithDefaultLoadOrder() _loadOrderSetter = static (param) => { var dataFolder = param._dataFolderGetter?.Invoke(param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import( + var lo = LoadOrder.Import( dataFolder, param.GameRelease, + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), param.Params.FileSystem); return lo.ListedOrder.ResolveExistingMods(); } @@ -314,9 +315,10 @@ public BinaryReadBuilderDataFolderChoice WithLoadO return Array.Empty(); } - var lo = LoadOrder.Import( + var lo = LoadOrder.Import( dataFolder.Value, loadOrder, - param.GameRelease, param.Params.FileSystem); + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), + param.Params.FileSystem); return lo.ListedOrder.ResolveExistingMods(); } }); @@ -358,9 +360,11 @@ public BinaryReadBuilderDataFolderChoice WithLoadO return Array.Empty(); } - var lo = LoadOrder.Import( - dataFolder.Value, loadOrder.Select(x => x.ModKey), - param.GameRelease, param.Params.FileSystem); + var lo = LoadOrder.Import( + dataFolder.Value, + loadOrder.Select(x => x.ModKey), + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), + param.Params.FileSystem); return lo.ListedOrder.ResolveExistingMods(); } }); @@ -439,9 +443,11 @@ public BinaryReadBuilderDataFolderChoice WithLoadO var masters = MasterReferenceCollection.FromModHeader( param.ModKey, modHeader); - var lo = LoadOrder.Import( - dataFolder.Value, masters.Masters.Select(x => x.Master), - param.GameRelease, param.Params.FileSystem); + var lo = LoadOrder.Import( + dataFolder.Value, + masters.Masters.Select(x => x.Master), + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), + param.Params.FileSystem); return lo.ListedOrder.ResolveExistingMods(); } }); diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs index 84865b472..d652c49a7 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs @@ -193,7 +193,11 @@ public BinaryModdedWriteBuilderTargetChoice WithDefaultLoadOrder() _loadOrderSetter = (m, p) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import(dataFolder, m.GameRelease, p._param.FileSystem); + var lo = LoadOrder.Import( + dataFolder, + m.GameRelease, + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), + p._param.FileSystem); if (p.TrimLoadOrderAtSelf) { @@ -247,9 +251,11 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrder( } else { - var lo = LoadOrder.Import( - dataFolder.Value, loArray, - m.GameRelease, p._param.FileSystem); + var lo = LoadOrder.Import( + dataFolder.Value, + loArray, + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), + p._param.FileSystem); return p._param with { MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), @@ -297,9 +303,11 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrderFromHea } else { - var lo = LoadOrder.Import( - dataFolder.Value, _mod.MasterReferences.Select(x => x.Master), - m.GameRelease, p._param.FileSystem); + var lo = LoadOrder.Import( + dataFolder.Value, + _mod.MasterReferences.Select(x => x.Master), + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), + p._param.FileSystem); if (p.TrimLoadOrderAtSelf) { lo = lo.TrimAt(m.ModKey); @@ -410,7 +418,11 @@ public BinaryWriteBuilderTargetChoice WithDefaultLoadOrder() _loadOrderSetter = static (m, p) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import(dataFolder, m.GameRelease, p._param.FileSystem); + var lo = LoadOrder.Import( + dataFolder, + m.GameRelease, + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), + p._param.FileSystem); if (p.TrimLoadOrderAtSelf) { @@ -438,9 +450,11 @@ public BinaryWriteBuilderDataFolderChoice WithLoadOrder( _loadOrderSetter = (m, p) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import( - dataFolder, loadOrder, - m.GameRelease, p._param.FileSystem); + var lo = LoadOrder.Import( + dataFolder, + loadOrder, + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), + p._param.FileSystem); if (p.TrimLoadOrderAtSelf) { lo = lo.TrimAt(m.ModKey); @@ -473,9 +487,11 @@ public BinaryWriteBuilderDataFolderChoice WithLoadOrderFromHeaderMas _loadOrderSetter = static (m, p) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import( - dataFolder, m.MasterReferences.Select(x => x.Master), - m.GameRelease, p._param.FileSystem); + var lo = LoadOrder.Import( + dataFolder, + m.MasterReferences.Select(x => x.Master), + factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), + p._param.FileSystem); if (p.TrimLoadOrderAtSelf) { lo = lo.TrimAt(m.ModKey); diff --git a/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs b/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs index 00667d607..5d009a57e 100644 --- a/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs +++ b/Mutagen.Bethesda.Core/Plugins/Order/DI/LoadOrderImporter.cs @@ -20,7 +20,7 @@ public interface ILoadOrderImporter } public interface ILoadOrderImporter - where TMod : class, IModGetter + where TMod : class, IModKeyed { /// /// Returns a load order filled with mods constructed @@ -29,7 +29,7 @@ public interface ILoadOrderImporter } public sealed class LoadOrderImporter : ILoadOrderImporter - where TMod : class, IModGetter + where TMod : class, IModKeyed { private readonly IFileSystem _fileSystem; private readonly IDataDirectoryProvider _dataDirectoryProvider; diff --git a/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs b/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs index 11e85ffdf..ce2ad6cc1 100644 --- a/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Order/LoadOrder.cs @@ -395,7 +395,7 @@ public static ILoadOrder> Import( IEnumerable loadOrder, Func factory, IFileSystem? fileSystem = null) - where TMod : class, IModGetter + where TMod : class, IModKeyed { fileSystem ??= IFileSystemExt.DefaultFilesystem; return new LoadOrderImporter( @@ -419,7 +419,7 @@ public static ILoadOrder> Import( IEnumerable loadOrder, Func factory, IFileSystem? fileSystem = null) - where TMod : class, IModGetter + where TMod : class, IModKeyed { fileSystem ??= IFileSystemExt.DefaultFilesystem; return new LoadOrderImporter( @@ -501,7 +501,7 @@ public static ILoadOrder> Import( GameRelease gameRelease, Func factory, IFileSystem? fileSystem = null) - where TMod : class, IModGetter + where TMod : class, IModKeyed { fileSystem ??= IFileSystemExt.DefaultFilesystem; var gameReleaseInjection = new GameReleaseInjection(gameRelease); @@ -530,7 +530,7 @@ public static ILoadOrder> Import( GameRelease gameRelease, Func factory, IFileSystem? fileSystem = null) - where TMod : class, IModGetter + where TMod : class, IModKeyed { fileSystem ??= IFileSystemExt.DefaultFilesystem; var gameReleaseInjection = new GameReleaseInjection(gameRelease); @@ -626,7 +626,7 @@ public static ILoadOrder> Import( GameRelease gameRelease, Func factory, IFileSystem? fileSystem = null) - where TMod : class, IModGetter + where TMod : class, IModKeyed { fileSystem ??= IFileSystemExt.DefaultFilesystem; var gameReleaseInjection = new GameReleaseInjection(gameRelease); @@ -769,7 +769,7 @@ public static ILoadOrder> Import( GameRelease gameRelease, Func factory, IFileSystem? fileSystem = null) - where TMod : class, IModGetter + where TMod : class, IModKeyed { fileSystem ??= IFileSystemExt.DefaultFilesystem; var gameReleaseInjection = new GameReleaseInjection(gameRelease); diff --git a/Mutagen.Bethesda.Core/Plugins/Order/ModListing.cs b/Mutagen.Bethesda.Core/Plugins/Order/ModListing.cs index d75659082..1107997aa 100644 --- a/Mutagen.Bethesda.Core/Plugins/Order/ModListing.cs +++ b/Mutagen.Bethesda.Core/Plugins/Order/ModListing.cs @@ -55,7 +55,7 @@ public static Comparer GetComparer(Comparer comparer /// [DebuggerDisplay("{ToString()}")] public sealed record ModListing : IModListing - where TMod : class, IModGetter + where TMod : class, IModKeyed { /// public ModKey ModKey { get; init; } @@ -145,7 +145,7 @@ public interface IModListingGetter : IModListingGetter, IDisposable /// public interface IModListing : IModListingGetter - where TMod : class, IModGetter + where TMod : class, IModKeyed { /// /// Mod object diff --git a/Mutagen.Bethesda.Core/Plugins/Records/DI/ModImporter.cs b/Mutagen.Bethesda.Core/Plugins/Records/DI/ModImporter.cs index 42f3ba586..e8b54eed3 100644 --- a/Mutagen.Bethesda.Core/Plugins/Records/DI/ModImporter.cs +++ b/Mutagen.Bethesda.Core/Plugins/Records/DI/ModImporter.cs @@ -13,7 +13,7 @@ TMod Import(ModPath modPath, BinaryReadParameters? param = null) } public interface IModImporter - where TMod : IModGetter + where TMod : IModKeyed { TMod Import(ModPath modPath, BinaryReadParameters? param = null); } @@ -85,7 +85,7 @@ public TMod Import(ModPath modPath, BinaryReadParameters? param = null) } public sealed class ModImporterWrapper : IModImporter - where TMod : IModGetter + where TMod : IModKeyed { private readonly Func _factory; diff --git a/Mutagen.Bethesda.Core/Plugins/Records/KeyedMasterStyle.cs b/Mutagen.Bethesda.Core/Plugins/Records/KeyedMasterStyle.cs new file mode 100644 index 000000000..ebb00e0bb --- /dev/null +++ b/Mutagen.Bethesda.Core/Plugins/Records/KeyedMasterStyle.cs @@ -0,0 +1,21 @@ +using System.IO.Abstractions; +using Mutagen.Bethesda.Plugins.Binary.Headers; + +namespace Mutagen.Bethesda.Plugins.Records; + +public record KeyedMasterStyle(ModKey ModKey, MasterStyle MasterStyle) : IModMasterStyledGetter +{ + public static KeyedMasterStyle FromMod(IModFlagsGetter masterFlagsGetter) + { + return new KeyedMasterStyle(masterFlagsGetter.ModKey, masterFlagsGetter.GetMasterStyle()); + } + + public static KeyedMasterStyle FromPath( + ModPath path, + GameRelease release, + IFileSystem? fileSystem = null) + { + var header = ModHeaderFrame.FromPath(path, release, fileSystem: fileSystem); + return new KeyedMasterStyle(path.ModKey, header.MasterStyle); + } +} \ No newline at end of file From 6bd9c11694a79638b0f963d4198c6d731b772ccc Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Sat, 14 Sep 2024 02:21:52 -0500 Subject: [PATCH 13/13] SeparatedMasters no longer assume Full; Throw. KnownMasters systems --- .../Mutagen.Bethesda.Core.csproj | 2 +- .../Plugins/Binary/Streams/ParsingMeta.cs | 10 + .../Binary/Translations/BinaryReadBuilder.cs | 193 +++++-- .../Binary/Translations/BinaryWriteBuilder.cs | 510 ++++++++++++------ .../Exceptions/MissingLoadOrderException.cs | 16 - .../Exceptions/MissingModMappingException.cs | 16 + .../Plugins/Masters/SeparatedMasterPackage.cs | 22 +- .../Masters/TransitiveMasterLocator.cs | 12 +- .../Builders/BuilderPassthroughTests.cs | 17 +- .../Plugins/Builders/ReadBuilderTests.cs | 250 ++++++++- .../Plugins/Builders/WriteBuilderTests.cs | 139 ++--- .../Plugins/Masters/DuplicateMasterTesting.cs | 11 +- .../Masters/SeparatedMastersTesting.cs | 2 +- .../Plugins/OverriddenFormsWritingTests.cs | 56 +- 14 files changed, 879 insertions(+), 377 deletions(-) delete mode 100644 Mutagen.Bethesda.Core/Plugins/Exceptions/MissingLoadOrderException.cs create mode 100644 Mutagen.Bethesda.Core/Plugins/Exceptions/MissingModMappingException.cs diff --git a/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj b/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj index 701705a16..80320405f 100644 --- a/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj +++ b/Mutagen.Bethesda.Core/Mutagen.Bethesda.Core.csproj @@ -272,7 +272,7 @@ - + diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs index e0b58cd93..bf2f7eabe 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Streams/ParsingMeta.cs @@ -155,4 +155,14 @@ public static ParsingMeta Factory( meta.Absorb(param); return meta; } + + public static ParsingMeta FactoryNoMasters( + BinaryReadParameters param, + GameRelease release, + ModKey modKey) + { + var meta = new ParsingMeta(GameConstants.Get(release), modKey, null!); + meta.Absorb(param); + return meta; + } } \ No newline at end of file diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs index f1b7aef36..555602a1a 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryReadBuilder.cs @@ -29,8 +29,9 @@ internal record BinaryReadBuilderParams internal TGroupMask? GroupMask { get; init; } internal BinaryReadParameters Params { get; init; } = BinaryReadParameters.Default; internal IBinaryReadBuilderInstantiator _instantiator { get; init; } = null!; - internal Func, IEnumerable>? _loadOrderSetter { get; init; } + internal Func, IReadOnlyCollection, IEnumerable>? _loadOrderSetter { get; init; } internal Func, DirectoryPath>? _dataFolderGetter { get; init; } + internal IModMasterStyledGetter[] KnownMasters { get; init; } = Array.Empty(); } /// @@ -266,7 +267,7 @@ public BinaryReadBuilder WithDefaultLoadOrder() return new BinaryReadBuilder(_param with { _dataFolderGetter = static (param) => GameLocator.Instance.GetDataDirectory(param.GameRelease), - _loadOrderSetter = static (param) => + _loadOrderSetter = static (param, alreadyKnownMasters) => { var dataFolder = param._dataFolderGetter?.Invoke(param) ?? throw new ArgumentNullException("Data folder source was not set"); var lo = LoadOrder.Import( @@ -274,7 +275,9 @@ public BinaryReadBuilder WithDefaultLoadOrder() param.GameRelease, factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), param.Params.FileSystem); - return lo.ListedOrder.ResolveExistingMods(); + return lo.ListedOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(); } }); } @@ -302,7 +305,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO { return new BinaryReadBuilderDataFolderChoice(_param with { - _loadOrderSetter = (param) => + _loadOrderSetter = (param, alreadyKnownMasters) => { if (loadOrder.Length == 0) { @@ -316,10 +319,13 @@ public BinaryReadBuilderDataFolderChoice WithLoadO } var lo = LoadOrder.Import( - dataFolder.Value, loadOrder, + dataFolder.Value, + loadOrder, factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), param.Params.FileSystem); - return lo.ListedOrder.ResolveExistingMods(); + return lo.ListedOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(); } }); } @@ -331,7 +337,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO /// /// Load order to refer to when parsing /// Builder object to continue customization - public BinaryReadBuilderDataFolderChoice WithLoadOrder(IEnumerable? loadOrder) + public BinaryReadBuilder WithLoadOrder(IEnumerable? loadOrder) { return WithLoadOrder(loadOrder?.ToArray() ?? Array.Empty()); } @@ -343,29 +349,15 @@ public BinaryReadBuilderDataFolderChoice WithLoadO /// /// Load order to refer to when parsing /// Builder object to continue customization - public BinaryReadBuilderDataFolderChoice WithLoadOrder(params IModMasterStyledGetter[] loadOrder) + public BinaryReadBuilder WithLoadOrder(params IModMasterStyledGetter[] loadOrder) { - return new BinaryReadBuilderDataFolderChoice(_param with + return new BinaryReadBuilder(_param with { - _loadOrderSetter = (param) => + _loadOrderSetter = (param, alreadyKnownMasters) => { - if (loadOrder.Length == 0) - { - return Array.Empty(); - } - - var dataFolder = param._dataFolderGetter?.Invoke(param); - if (dataFolder == null) - { - return Array.Empty(); - } - - var lo = LoadOrder.Import( - dataFolder.Value, - loadOrder.Select(x => x.ModKey), - factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), - param.Params.FileSystem); - return lo.ListedOrder.ResolveExistingMods(); + var lo = new LoadOrder(loadOrder, disposeItems: false); + return lo.ListedOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)); } }); } @@ -379,14 +371,19 @@ public BinaryReadBuilderDataFolderChoice WithLoadO /// Builder object to continue customization public BinaryReadBuilder WithLoadOrder(ILoadOrderGetter? loadOrder) { - return new BinaryReadBuilder( - _param with + if (loadOrder == null) + { + return new BinaryReadBuilder(_param); + } + + return new BinaryReadBuilder(_param with + { + _loadOrderSetter = (param, alreadyKnownMasters) => { - Params = _param.Params with - { - MasterFlagsLookup = loadOrder - } - }); + return loadOrder.ListedOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)); + } + }); } /// @@ -408,7 +405,7 @@ public BinaryReadBuilderDataFolderChoice WithLoadO { return new BinaryReadBuilderDataFolderChoice(_param with { - _loadOrderSetter = static (param) => + _loadOrderSetter = static (param, alreadyKnownMasters) => { var dataFolder = param._dataFolderGetter?.Invoke(param); if (dataFolder == null) @@ -448,10 +445,46 @@ public BinaryReadBuilderDataFolderChoice WithLoadO masters.Masters.Select(x => x.Master), factory: (modPath) => KeyedMasterStyle.FromPath(modPath, param.GameRelease, param.Params.FileSystem), param.Params.FileSystem); - return lo.ListedOrder.ResolveExistingMods(); + return lo.ListedOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(); } }); } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryReadBuilder WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _param.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + + return new BinaryReadBuilder(_param with + { + KnownMasters = _param.KnownMasters.And(knownMasters).ToArray() + }); + } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryReadBuilder WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } } /// @@ -768,6 +801,42 @@ public BinaryReadBuilder ThrowIfUnknownSubrecord(b } }; } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryReadBuilder WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _param.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + return this with + { + _param = _param with + { + KnownMasters = _param.KnownMasters.And(knownMasters).ToArray() + } + }; + } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryReadBuilder WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } /// /// Sets the filesystem to use @@ -1054,6 +1123,42 @@ public BinaryReadMutableBuilder ThrowIfUnknownSubr } }; } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryReadBuilder WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _param.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + return this with + { + _param = _param with + { + KnownMasters = _param.KnownMasters.And(knownMasters).ToArray() + } + }; + } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryReadBuilder WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } /// /// Sets the filesystem to use @@ -1151,24 +1256,26 @@ public static BinaryReadBuilderParams RunLoadOrder where TMod : IMod where TModGetter : IModDisposeGetter { - IReadOnlyCollection loadOrder = Array.Empty(); + var knownSet = new HashSet(p.KnownMasters.Select(x => x.ModKey)); + IReadOnlyCollection? loadOrder = null; if (p._loadOrderSetter != null) { - loadOrder = p._loadOrderSetter(p).ToArray(); + loadOrder = p._loadOrderSetter(p, knownSet).ToArray(); } - - if (loadOrder.Count == 0) + else if (p.KnownMasters.Length > 0) { - return p; + loadOrder = p.KnownMasters; } return p with { Params = p.Params with { - MasterFlagsLookup = new LoadOrder( - loadOrder.Distinct(x => x.ModKey)) + MasterFlagsLookup = loadOrder != null && loadOrder.Count > 0 + ? new LoadOrder( + p.KnownMasters.And(loadOrder).Distinct(x => x.ModKey)) + : null } }; } diff --git a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs index d652c49a7..9e4fa0b23 100644 --- a/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs +++ b/Mutagen.Bethesda.Core/Plugins/Binary/Translations/BinaryWriteBuilder.cs @@ -23,9 +23,9 @@ internal record BinaryWriteBuilderParams internal FilePath? _path { get; init; } internal Stream? _stream { get; init; } internal Func, BinaryWriteParameters>? _masterSyncAction { get; init; } - internal Func, BinaryWriteParameters>? _loadOrderSetter { get; init; } + internal Func, IReadOnlyCollection, BinaryWriteParameters>? _loadOrderSetter { get; init; } internal Func? _dataFolderGetter { get; init; } - internal bool TrimLoadOrderAtSelf { get; init; } = true; + internal IModMasterStyledGetter[] KnownMasters { get; init; } = Array.Empty(); } /// @@ -66,6 +66,22 @@ public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( ILoadOrderGetter loadOrder); + /// + /// Writes the mod with given load order as reference + /// + /// Load order to reference + /// Builder object to continue customization + public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( + IEnumerable loadOrder); + + /// + /// Writes the mod with given load order as reference + /// + /// Load order to reference + /// Builder object to continue customization + public IBinaryModdedWriteBuilderTargetChoice WithLoadOrder( + params IModMasterStyledGetter[] loadOrder); + /// /// Writes the mod with the default load order and data folder as reference. /// @@ -137,15 +153,13 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( { return new BinaryModdedWriteBuilderTargetChoice(_mod, _params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { - if (p.TrimLoadOrderAtSelf) - { - loadOrder = loadOrder.TrimAt(m.ModKey); - } return p._param with { - MasterFlagsLookup = loadOrder.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = loadOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(), MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -164,15 +178,12 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( { return new BinaryModdedWriteBuilderTargetChoice(_mod, _params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { - if (p.TrimLoadOrderAtSelf) - { - loadOrder = loadOrder.TrimAt(m.ModKey); - } return p._param with { - MasterFlagsLookup = loadOrder, + MasterFlagsLookup = loadOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)), MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -180,6 +191,30 @@ public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( }); } IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(ILoadOrderGetter loadOrder) => WithLoadOrder(loadOrder); + + /// + /// Writes the mod with given load order as reference + /// + /// Load order to reference + /// Builder object to continue customization + public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( + params IModMasterStyledGetter[] loadOrder) + { + return WithLoadOrder(new LoadOrder(loadOrder, disposeItems: false)); + } + IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(params IModMasterStyledGetter[] loadOrder) => WithLoadOrder(loadOrder); + + /// + /// Writes the mod with given load order as reference + /// + /// Load order to reference + /// Builder object to continue customization + public BinaryModdedWriteBuilderTargetChoice WithLoadOrder( + IEnumerable loadOrder) + { + return WithLoadOrder(new LoadOrder(loadOrder, disposeItems: false)); + } + IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderLoadOrderChoice.WithLoadOrder(IEnumerable loadOrder) => WithLoadOrder(loadOrder); /// /// Writes the mod with the default load order and data folder as reference. @@ -190,7 +225,7 @@ public BinaryModdedWriteBuilderTargetChoice WithDefaultLoadOrder() return new BinaryModdedWriteBuilderTargetChoice(_mod, _params with { _dataFolderGetter = (m, p) => GameLocator.Instance.GetDataDirectory(m.GameRelease), - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); var lo = LoadOrder.Import( @@ -199,18 +234,15 @@ public BinaryModdedWriteBuilderTargetChoice WithDefaultLoadOrder() factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), p._param.FileSystem); - if (p.TrimLoadOrderAtSelf) - { - lo = lo.TrimAt(m.ModKey); - } ILoadOrderGetter? modFlagsLo = null; if (GameConstants.Get(m.GameRelease).SeparateMasterLoadOrders) { - modFlagsLo = lo.ResolveAllModsExist(disposeItems: false); + modFlagsLo = lo + .ResolveExistingMods(disposeItems: false); } return p._param with { - MasterFlagsLookup = modFlagsLo, + MasterFlagsLookup = modFlagsLo?.Where(x => !alreadyKnownMasters.Contains(x.ModKey)), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -229,17 +261,9 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrder( { return new BinaryModdedWriteBuilderDataFolderChoice(_mod, _params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { - ModKey[] loArray; - if (p.TrimLoadOrderAtSelf) - { - loArray = loadOrder.TrimAt(m.ModKey).ToArray(); - } - else - { - loArray = loadOrder.ToArray(); - } + ModKey[] loArray = loadOrder.ToArray(); var dataFolder = p._dataFolderGetter?.Invoke(m, p._param); if (dataFolder == null || !GameConstants.Get(m.GameRelease).SeparateMasterLoadOrders) { @@ -258,7 +282,9 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrder( p._param.FileSystem); return p._param with { - MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -284,17 +310,13 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrderFromHea { return new BinaryModdedWriteBuilderDataFolderChoice(_mod, _params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param); if (dataFolder == null || !GameConstants.Get(m.GameRelease).SeparateMasterLoadOrders) { var lo = _mod.MasterReferences.Select(x => x.Master).ToArray(); - if (p.TrimLoadOrderAtSelf) - { - lo = lo.TrimAt(m.ModKey).ToArray(); - } return p._param with { MastersListOrdering = new MastersListOrderingByLoadOrder(lo), @@ -303,18 +325,17 @@ public BinaryModdedWriteBuilderDataFolderChoice WithLoadOrderFromHea } else { - var lo = LoadOrder.Import( - dataFolder.Value, - _mod.MasterReferences.Select(x => x.Master), - factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), - p._param.FileSystem); - if (p.TrimLoadOrderAtSelf) - { - lo = lo.TrimAt(m.ModKey); - } + var lo = LoadOrder.Import( + dataFolder: dataFolder.Value, + loadOrder: _mod.MasterReferences.Select(x => x.Master), + m.GameRelease, + p._param.FileSystem); + return p._param with { - MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -364,15 +385,13 @@ public BinaryWriteBuilderTargetChoice WithLoadOrder( { return new BinaryWriteBuilderTargetChoice(_params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { - if (p.TrimLoadOrderAtSelf) - { - loadOrder = loadOrder.TrimAt(m.ModKey); - } return p._param with { - MasterFlagsLookup = loadOrder.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = loadOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; @@ -390,21 +409,40 @@ public BinaryWriteBuilderTargetChoice WithLoadOrder( { return new BinaryWriteBuilderTargetChoice(_params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { - if (p.TrimLoadOrderAtSelf) - { - loadOrder = loadOrder.TrimAt(m.ModKey); - } return p._param with { - MasterFlagsLookup = loadOrder, + MasterFlagsLookup = loadOrder + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)), MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(loadOrder) }; } }); } + + /// + /// Writes the mod with given load order as reference + /// + /// Load order to reference + /// Builder object to continue customization + public BinaryWriteBuilderTargetChoice WithLoadOrder( + params IModMasterStyledGetter[] loadOrder) + { + return WithLoadOrder(new LoadOrder(loadOrder, disposeItems: false)); + } + + /// + /// Writes the mod with given load order as reference + /// + /// Load order to reference + /// Builder object to continue customization + public BinaryWriteBuilderTargetChoice WithLoadOrder( + IEnumerable loadOrder) + { + return WithLoadOrder(new LoadOrder(loadOrder, disposeItems: false)); + } /// /// Writes the mod with the default load order and data folder as reference. @@ -415,7 +453,7 @@ public BinaryWriteBuilderTargetChoice WithDefaultLoadOrder() return new BinaryWriteBuilderTargetChoice(_params with { _dataFolderGetter = static (m, p) => GameLocator.Instance.GetDataDirectory(m.GameRelease), - _loadOrderSetter = static (m, p) => + _loadOrderSetter = static (m, p, alreadyKnownMasters) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); var lo = LoadOrder.Import( @@ -424,13 +462,11 @@ public BinaryWriteBuilderTargetChoice WithDefaultLoadOrder() factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), p._param.FileSystem); - if (p.TrimLoadOrderAtSelf) - { - lo = lo.TrimAt(m.ModKey); - } return p._param with { - MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo) }; } @@ -447,21 +483,17 @@ public BinaryWriteBuilderDataFolderChoice WithLoadOrder( { return new BinaryWriteBuilderDataFolderChoice(_params with { - _loadOrderSetter = (m, p) => + _loadOrderSetter = (m, p, alreadyKnownMasters) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import( - dataFolder, - loadOrder, - factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), - p._param.FileSystem); - if (p.TrimLoadOrderAtSelf) - { - lo = lo.TrimAt(m.ModKey); - } + var lo = LoadOrder.Import( + dataFolder, loadOrder, + m.GameRelease, p._param.FileSystem); return p._param with { - MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -484,21 +516,18 @@ public BinaryWriteBuilderDataFolderChoice WithLoadOrderFromHeaderMas { return new BinaryWriteBuilderDataFolderChoice(_params with { - _loadOrderSetter = static (m, p) => + _loadOrderSetter = static (m, p, alreadyKnownMasters) => { var dataFolder = p._dataFolderGetter?.Invoke(m, p._param) ?? throw new ArgumentNullException("Data folder source was not set"); - var lo = LoadOrder.Import( - dataFolder, - m.MasterReferences.Select(x => x.Master), - factory: (modPath) => KeyedMasterStyle.FromPath(modPath, p._gameRelease, p._param.FileSystem), - p._param.FileSystem); - if (p.TrimLoadOrderAtSelf) - { - lo = lo.TrimAt(m.ModKey); - } + var lo = LoadOrder.Import( + dataFolder, m.MasterReferences.Select(x => x.Master), + m.GameRelease, p._param.FileSystem); + return p._param with { - MasterFlagsLookup = lo.ResolveAllModsExist(disposeItems: false), + MasterFlagsLookup = lo + .Where(x => !alreadyKnownMasters.Contains(x.ModKey)) + .ResolveExistingMods(disposeItems: false), MastersListOrdering = new MastersListOrderingByLoadOrder(lo), LowerRangeDisallowedHandler = ALowerRangeDisallowedHandlerOption.AddPlaceholder(lo) }; @@ -536,6 +565,40 @@ public BinaryWriteBuilderTargetChoice WithDataFolder(DirectoryPath? _dataFolderGetter = (m, p) => dataFolder.Value }); } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryWriteBuilderTargetChoice WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _param.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + + return new BinaryWriteBuilderTargetChoice(_param with + { + KnownMasters = _param.KnownMasters.And(knownMasters).ToArray() + }); + } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryWriteBuilderTargetChoice WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } } public interface IBinaryModdedWriteBuilderDataFolderChoice @@ -543,6 +606,8 @@ public interface IBinaryModdedWriteBuilderDataFolderChoice IBinaryModdedWriteBuilderTargetChoice WithDefaultDataFolder(); IBinaryModdedWriteBuilderTargetChoice WithDataFolder(DirectoryPath? dataFolder); IBinaryModdedWriteBuilderTargetChoice WithNoDataFolder(); + IBinaryModdedWriteBuilderTargetChoice WithKnownMasters(params IModMasterStyledGetter[] knownMasters); + IBinaryModdedWriteBuilderTargetChoice WithKnownMasters(params KeyedMasterStyle[] knownMasters); } public record BinaryModdedWriteBuilderDataFolderChoice : IBinaryModdedWriteBuilderDataFolderChoice @@ -590,6 +655,44 @@ public BinaryModdedWriteBuilderTargetChoice WithNoDataFolder() IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderDataFolderChoice.WithNoDataFolder() => WithNoDataFolder(); + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryModdedWriteBuilderTargetChoice WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _param.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + + return new BinaryModdedWriteBuilderTargetChoice(_mod, _param with + { + KnownMasters = _param.KnownMasters.And(knownMasters).ToArray() + }); + } + IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderDataFolderChoice.WithKnownMasters(params IModMasterStyledGetter[] knownMasters) => + WithKnownMasters(knownMasters); + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public BinaryModdedWriteBuilderTargetChoice WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } + IBinaryModdedWriteBuilderTargetChoice IBinaryModdedWriteBuilderDataFolderChoice.WithKnownMasters(params KeyedMasterStyle[] knownMasters) => + WithKnownMasters(knownMasters); } public interface IBinaryModdedWriteBuilderTargetChoice @@ -897,11 +1000,24 @@ IFileBinaryModdedWriteBuilder WithMastersListOrdering( IFileBinaryModdedWriteBuilder WithAllParentMasters(); /// - /// Load order is typically "trimmed" to not include anything after the mod being written.
- /// This turns that logic off, to include the full load order withing the write logic + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder ///
+ /// Master information to hand provide /// Builder object to continue customization - IFileBinaryModdedWriteBuilder DoNotTrimLoadOrderAtSelf(); + IFileBinaryModdedWriteBuilder WithKnownMasters(params IModMasterStyledGetter[] knownMasters); + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + IFileBinaryModdedWriteBuilder WithKnownMasters(params KeyedMasterStyle[] knownMasters); + + internal IFileBinaryModdedWriteBuilder WithOverriddenFormsOption(OverriddenFormsOption option); /// /// Executes the instructions to write the mod. @@ -1548,45 +1664,71 @@ public FileBinaryModdedWriteBuilder WithAllParentMasters() }; } IFileBinaryModdedWriteBuilder IFileBinaryModdedWriteBuilder.WithAllParentMasters() => WithAllParentMasters(); + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public FileBinaryModdedWriteBuilder WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _params.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + + return this with + { + _params = _params with + { + KnownMasters = _params.KnownMasters.And(knownMasters).ToArray() + } + }; + } + IFileBinaryModdedWriteBuilder IFileBinaryModdedWriteBuilder.WithKnownMasters(params IModMasterStyledGetter[] knownMasters) => WithKnownMasters(knownMasters); /// - /// Load order is typically "trimmed" to not include anything after the mod being written.
- /// This turns that logic off, to include the full load order withing the write logic + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder ///
+ /// Master information to hand provide /// Builder object to continue customization - public FileBinaryModdedWriteBuilder DoNotTrimLoadOrderAtSelf() + public FileBinaryModdedWriteBuilder WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } + IFileBinaryModdedWriteBuilder IFileBinaryModdedWriteBuilder.WithKnownMasters(params KeyedMasterStyle[] knownMasters) => WithKnownMasters(knownMasters); + + internal FileBinaryModdedWriteBuilder WithOverriddenFormsOption(OverriddenFormsOption option) { return this with { _params = _params with { - TrimLoadOrderAtSelf = false, + _param = _params._param with + { + OverriddenFormsOption = option + } } }; } - IFileBinaryModdedWriteBuilder IFileBinaryModdedWriteBuilder.DoNotTrimLoadOrderAtSelf() => DoNotTrimLoadOrderAtSelf(); + IFileBinaryModdedWriteBuilder IFileBinaryModdedWriteBuilder.WithOverriddenFormsOption(OverriddenFormsOption option) => WithOverriddenFormsOption(option); + /// /// Executes the instructions to write the mod. /// /// A task to await for writing completion public async Task WriteAsync() { - if (_params._loadOrderSetter != null) - { - _params = _params with - { - _param = _params._loadOrderSetter(_mod, _params) - }; - } - if (_params._masterSyncAction != null) - { - _params = _params with - { - _param = _params._masterSyncAction(_mod, _params) - }; - } - await _params._writer.WriteAsync(_mod, _params); + await _params._writer.WriteAsync( + _mod, + BinaryWriteBuilderHelper.RunPreWriteSetters(_mod, _params)); } /// @@ -1595,21 +1737,9 @@ public async Task WriteAsync() /// A task to await for writing completion public void Write() { - if (_params._loadOrderSetter != null) - { - _params = _params with - { - _param = _params._loadOrderSetter(_mod, _params) - }; - } - if (_params._masterSyncAction != null) - { - _params = _params with - { - _param = _params._masterSyncAction(_mod, _params) - }; - } - _params._writer.Write(_mod, _params); + _params._writer.Write( + _mod, + BinaryWriteBuilderHelper.RunPreWriteSetters(_mod, _params)); } } @@ -2214,17 +2344,52 @@ public FileBinaryWriteBuilder WithAllParentMasters() } /// - /// Load order is typically "trimmed" to not include anything after the mod being written.
- /// This turns that logic off, to include the full load order withing the write logic + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder + ///
+ /// Master information to hand provide + /// Builder object to continue customization + public FileBinaryWriteBuilder WithKnownMasters(params IModMasterStyledGetter[] knownMasters) + { + var match = _params.KnownMasters.FirstOrDefault(existingKnownMaster => + knownMasters.Any(x => x.ModKey == existingKnownMaster.ModKey)); + if (match != null) + { + throw new ArgumentException($"ModKey was already added as a known master: {match.ModKey}"); + } + + return this with + { + _params = _params with + { + KnownMasters = _params.KnownMasters.And(knownMasters).ToArray() + } + }; + } + + /// + /// Separated master games (like Starfield) need to know what their masters styles are in order to parse correctly, + /// which is normally retrieved via looking at the mod files themselves from the Data Folder.
+ /// This is an alternative to hand provide the information so that they do not need to be present in the Data folder ///
+ /// Master information to hand provide /// Builder object to continue customization - public FileBinaryWriteBuilder DoNotTrimLoadOrderAtSelf() + public FileBinaryWriteBuilder WithKnownMasters(params KeyedMasterStyle[] knownMasters) + { + return WithKnownMasters(knownMasters.Cast().ToArray()); + } + + internal FileBinaryWriteBuilder WithOverriddenFormsOption(OverriddenFormsOption option) { return this with { _params = _params with { - TrimLoadOrderAtSelf = false + _param = _params._param with + { + OverriddenFormsOption = option + } } }; } @@ -2236,21 +2401,9 @@ public FileBinaryWriteBuilder DoNotTrimLoadOrderAtSelf() /// A task to await for writing completion public async Task WriteAsync(TModGetter mod) { - if (_params._loadOrderSetter != null) - { - _params = _params with - { - _param = _params._loadOrderSetter(mod, _params) - }; - } - if (_params._masterSyncAction != null) - { - _params = _params with - { - _param = _params._masterSyncAction(mod, _params) - }; - } - await _params._writer.WriteAsync(mod, _params); + await _params._writer.WriteAsync( + mod, + BinaryWriteBuilderHelper.RunPreWriteSetters(mod, _params)); } /// @@ -2259,24 +2412,73 @@ public async Task WriteAsync(TModGetter mod) /// A task to await for writing completion public void Write(TModGetter mod) { - if (_params._gameRelease != mod.GameRelease) + _params._writer.Write( + mod, + BinaryWriteBuilderHelper.RunPreWriteSetters(mod, _params)); + } +} + +internal static class BinaryWriteBuilderHelper +{ + public static BinaryWriteBuilderParams RunPreWriteSetters( + TModGetter mod, + BinaryWriteBuilderParams p) + where TModGetter : class, IModGetter + { + var knownSet = new HashSet(p.KnownMasters.Select(x => x.ModKey)); + if (p._gameRelease != mod.GameRelease) { - throw new ArgumentException($"GameRelease did not match provided mod: {_params._gameRelease} != {mod.GameRelease}"); + throw new ArgumentException($"GameRelease did not match provided mod: {p._gameRelease} != {mod.GameRelease}"); } - if (_params._loadOrderSetter != null) + if (p._loadOrderSetter != null) { - _params = _params with + p = p with { - _param = _params._loadOrderSetter(mod, _params) + _param = p._loadOrderSetter(mod, p, knownSet) }; } - if (_params._masterSyncAction != null) + + + if (p._param.MasterFlagsLookup != null) { - _params = _params with + Cache? masterFlagsLookup = new(x => x.ModKey); + masterFlagsLookup.SetTo(p._param.MasterFlagsLookup.Items); + p.KnownMasters.ForEach(x => masterFlagsLookup.Add(x)); + + if (masterFlagsLookup.Count == 0) + { + masterFlagsLookup = null; + } + + p = p with { - _param = _params._masterSyncAction(mod, _params) + _param = p._param with + { + MasterFlagsLookup = masterFlagsLookup + } + }; + } + else if (p.KnownMasters.Length > 0) + { + Cache masterFlagsLookup = new(x => x.ModKey); + masterFlagsLookup.SetTo(p.KnownMasters); + p = p with + { + _param = p._param with + { + MasterFlagsLookup = masterFlagsLookup + } }; } - _params._writer.Write(mod, _params); + + if (p._masterSyncAction != null) + { + p = p with + { + _param = p._masterSyncAction(mod, p) + }; + } + + return p; } -} +} \ No newline at end of file diff --git a/Mutagen.Bethesda.Core/Plugins/Exceptions/MissingLoadOrderException.cs b/Mutagen.Bethesda.Core/Plugins/Exceptions/MissingLoadOrderException.cs deleted file mode 100644 index 8db6a1b26..000000000 --- a/Mutagen.Bethesda.Core/Plugins/Exceptions/MissingLoadOrderException.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Mutagen.Bethesda.Plugins.Exceptions; - -public class MissingLoadOrderException : Exception -{ - public MissingLoadOrderException() - { - } - - public MissingLoadOrderException(string? message) : base(message) - { - } - - public MissingLoadOrderException(string? message, Exception? innerException) : base(message, innerException) - { - } -} \ No newline at end of file diff --git a/Mutagen.Bethesda.Core/Plugins/Exceptions/MissingModMappingException.cs b/Mutagen.Bethesda.Core/Plugins/Exceptions/MissingModMappingException.cs new file mode 100644 index 000000000..ecdb81b01 --- /dev/null +++ b/Mutagen.Bethesda.Core/Plugins/Exceptions/MissingModMappingException.cs @@ -0,0 +1,16 @@ +namespace Mutagen.Bethesda.Plugins.Exceptions; + +public class MissingModMappingException : Exception +{ + public MissingModMappingException() + { + } + + public MissingModMappingException(string? message) : base(message) + { + } + + public MissingModMappingException(string? message, Exception? innerException) : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs index 967b1bc21..5b8e1e98c 100644 --- a/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs +++ b/Mutagen.Bethesda.Core/Plugins/Masters/SeparatedMasterPackage.cs @@ -193,25 +193,19 @@ void AddToListViaStyle(MasterStyle style, ModKey modKey) } } - foreach (var master in masters.Masters) + foreach (var master in masters.Masters.Select(x => x. Master)) { - if (masterFlagLookup != null) + if (masterFlagLookup == null) { - if (!masterFlagLookup.TryGetValue(master.Master, out var mod)) - { - throw new MissingModException(master.Master, - "Mod was missing from master flag lookup when constructing the separate mod lists needed for FormID translation."); - } - - AddToList(mod, master.Master); + throw new MissingModMappingException(); } - // Don't have a load order, assume normal - // Viewed as user error if this turns out to be wrong - // They should provide load order unless they're sure it's not needed - else + if (!masterFlagLookup.TryGetValue(master, out var mod)) { - normal.Add(master.Master); + throw new MissingModException(master, + "Mod was missing from load order when constructing the separate mod lists needed for FormID translation."); } + + AddToList(mod, master); } if (_starfieldMasters.Contains(currentModKey)) diff --git a/Mutagen.Bethesda.Core/Plugins/Masters/TransitiveMasterLocator.cs b/Mutagen.Bethesda.Core/Plugins/Masters/TransitiveMasterLocator.cs index 1c116203f..68f724b77 100644 --- a/Mutagen.Bethesda.Core/Plugins/Masters/TransitiveMasterLocator.cs +++ b/Mutagen.Bethesda.Core/Plugins/Masters/TransitiveMasterLocator.cs @@ -1,4 +1,5 @@ using System.IO.Abstractions; +using Mutagen.Bethesda.Plugins.Binary.Headers; using Mutagen.Bethesda.Plugins.Binary.Parameters; using Mutagen.Bethesda.Plugins.Records; using Noggog; @@ -22,14 +23,11 @@ public static IReadOnlyCollection GetAllMasters( var master = remainingMasters.Dequeue(); masters.Add(master); - var modPath = Path.Combine(dataFolder, master.FileName); + var modPath = new ModPath(Path.Combine(dataFolder, master.FileName)); - var masterMod = ModInstantiator.ImportGetter(modPath, release, new BinaryReadParameters() - { - FileSystem = fileSystem - }); - - foreach (var parent in masterMod.MasterReferences) + var header = ModHeaderFrame.FromPath(modPath, release, fileSystem: fileSystem); + + foreach (var parent in header.Masters(modPath.ModKey)) { remainingMasters.Enqueue(parent.Master); masters.Add(parent.Master); diff --git a/Mutagen.Bethesda.UnitTests/Plugins/Builders/BuilderPassthroughTests.cs b/Mutagen.Bethesda.UnitTests/Plugins/Builders/BuilderPassthroughTests.cs index b8b070505..99c247585 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/Builders/BuilderPassthroughTests.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/Builders/BuilderPassthroughTests.cs @@ -113,7 +113,7 @@ public async Task NonSeparated( } [Theory, MutagenAutoData] - public async Task Separated( + public async Task SeparatedCollision( Payload payload) { var normalMaster = new StarfieldMod(payload.NormalMasterKey, StarfieldRelease.Starfield) @@ -158,16 +158,13 @@ public async Task Separated( var modPath = await payload.WriteMod( originating, useDataFolder: false); - using var mod = StarfieldMod.Create(StarfieldRelease.Starfield) - .FromPath(modPath) - .WithLoadOrderFromHeaderMasters() - .WithNoDataFolder() - .WithFileSystem(payload.FileSystem) - .Construct(); - - Assert.Throws(() => + Assert.Throws(() => { - mod.Npcs.Should().HaveCount(4); + using var mod = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithNoLoadOrder() + .WithFileSystem(payload.FileSystem) + .Construct(); }); } diff --git a/Mutagen.Bethesda.UnitTests/Plugins/Builders/ReadBuilderTests.cs b/Mutagen.Bethesda.UnitTests/Plugins/Builders/ReadBuilderTests.cs index 75cb08376..f51f55671 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/Builders/ReadBuilderTests.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/Builders/ReadBuilderTests.cs @@ -2,6 +2,7 @@ using FluentAssertions; using Mutagen.Bethesda.Plugins; using Mutagen.Bethesda.Plugins.Binary.Parameters; +using Mutagen.Bethesda.Plugins.Exceptions; using Mutagen.Bethesda.Starfield; using Mutagen.Bethesda.Testing.AutoData; using Noggog; @@ -12,7 +13,31 @@ namespace Mutagen.Bethesda.UnitTests.Plugins.Builders; public class ReadBuilderTests { [Theory, MutagenAutoData] - public void NoDataFolderWithHeaderMasters( + public void SeparatedNoMasterNoDataFolderWithHeaderMasters( + IFileSystem fileSystem, + ModKey modKey, + DirectoryPath existingDir) + { + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + var modPath = Path.Combine(existingDir, mod.ModKey.FileName); + mod.WriteToBinary(modPath, new BinaryWriteParameters() + { + FileSystem = fileSystem + }); + + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrderFromHeaderMasters() + .WithNoDataFolder() + .WithFileSystem(fileSystem) + .Mutable() + .Construct(); + reimport.MasterReferences.Select(x => x.Master).Should() + .Equal(); + } + + [Theory, MutagenAutoData] + public void SeparatedNoDataFolderWithHeaderMasters( IFileSystem fileSystem, ModKey masterMod, ModKey modKey, @@ -23,18 +48,237 @@ public void NoDataFolderWithHeaderMasters( var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); mod.Weapons.GetOrAddAsOverride(masterWeapon); var modPath = Path.Combine(existingDir, mod.ModKey.FileName); + mod.BeginWrite + .WithLoadOrder(master) + .ToPath(modPath) + .WithFileSystem(fileSystem) + .Write(); + + try + { + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrderFromHeaderMasters() + .WithNoDataFolder() + .WithFileSystem(fileSystem) + .Mutable() + .Construct(); + } + catch (Exception e) + { + if ((e as RecordException)?.InnerException is not MissingModMappingException) + { + throw new Exception(); + } + } + } + + [Theory, MutagenAutoData] + public void SeparatedNoMasterNoLoadOrder( + IFileSystem fileSystem, + ModKey modKey, + DirectoryPath existingDir) + { + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + var modPath = Path.Combine(existingDir, mod.ModKey.FileName); mod.WriteToBinary(modPath, new BinaryWriteParameters() { FileSystem = fileSystem }); - using var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithNoLoadOrder() + .WithFileSystem(fileSystem) + .Mutable() + .Construct(); + reimport.MasterReferences.Select(x => x.Master).Should() + .Equal(); + } + + [Theory, MutagenAutoData] + public void SeparatedNoLoadOrder( + IFileSystem fileSystem, + ModKey masterMod, + ModKey modKey, + DirectoryPath existingDir) + { + var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var masterWeapon = master.Weapons.AddNew(); + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + mod.Weapons.GetOrAddAsOverride(masterWeapon); + var modPath = Path.Combine(existingDir, mod.ModKey.FileName); + mod.BeginWrite + .WithLoadOrder(master) + .ToPath(modPath) + .WithFileSystem(fileSystem) + .Write(); + + try + { + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithNoLoadOrder() + .WithFileSystem(fileSystem) + .Mutable() + .Construct(); + } + catch (Exception e) + { + if ((e as RecordException)?.InnerException is not MissingModMappingException) + { + throw new Exception(); + } + } + } + + [Theory, MutagenAutoData] + public void SeparatedDataFolder( + IFileSystem fileSystem, + ModKey masterMod, + ModKey modKey, + DirectoryPath existingDataDir) + { + var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var masterWeapon = master.Weapons.AddNew(); + var masterPath = Path.Combine(existingDataDir, masterMod.FileName); + master.WriteToBinary(masterPath, new BinaryWriteParameters() + { + FileSystem = fileSystem + }); + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + mod.Weapons.GetOrAddAsOverride(masterWeapon); + var modPath = Path.Combine(existingDataDir, mod.ModKey.FileName); + mod.BeginWrite + .WithLoadOrder(master) + .ToPath(modPath) + .WithFileSystem(fileSystem) + .Write(); + + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) .FromPath(modPath) .WithLoadOrderFromHeaderMasters() - .WithNoDataFolder() + .WithDataFolder(existingDataDir) + .WithFileSystem(fileSystem) + .Mutable() + .Construct(); + reimport.MasterReferences.Select(x => x.Master).Should() + .Equal(masterMod); + } + + [Theory, MutagenAutoData] + public void SeparatedDataFolderMissingMaster( + IFileSystem fileSystem, + ModKey masterMod, + ModKey modKey, + DirectoryPath existingDataDir) + { + var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var masterWeapon = master.Weapons.AddNew(); + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + mod.Weapons.GetOrAddAsOverride(masterWeapon); + var modPath = Path.Combine(existingDataDir, mod.ModKey.FileName); + mod.BeginWrite + .WithLoadOrder(master) + .ToPath(modPath) + .WithFileSystem(fileSystem) + .Write(); + + try + { + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrderFromHeaderMasters() + .WithDataFolder(existingDataDir) + .WithFileSystem(fileSystem) + .Mutable() + .Construct(); + } + catch (Exception e) + { + if ((e as RecordException)?.InnerException is not MissingModMappingException) + { + throw new Exception(); + } + } + } + + [Theory, MutagenAutoData] + public void SeparatedDataFolderMissingUnrelated( + IFileSystem fileSystem, + ModKey masterMod, + ModKey unrelatedModKey, + ModKey modKey, + DirectoryPath existingDataDir) + { + var unrelated = new StarfieldMod(unrelatedModKey, StarfieldRelease.Starfield); + var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var masterWeapon = master.Weapons.AddNew(); + var masterPath = Path.Combine(existingDataDir, masterMod.FileName); + master.WriteToBinary(masterPath, new BinaryWriteParameters() + { + FileSystem = fileSystem + }); + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + mod.Weapons.GetOrAddAsOverride(masterWeapon); + var modPath = Path.Combine(existingDataDir, mod.ModKey.FileName); + mod.BeginWrite + .WithLoadOrder(master) + .ToPath(modPath) .WithFileSystem(fileSystem) + .Write(); + + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrder(masterMod, unrelatedModKey, modKey) + .WithDataFolder(existingDataDir) + .WithFileSystem(fileSystem) + .Mutable() .Construct(); reimport.MasterReferences.Select(x => x.Master).Should() .Equal(masterMod); } + + [Theory, MutagenAutoData] + public void KnownMasterTests( + IFileSystem fileSystem, + ModKey masterMod, + ModKey masterMod2, + ModKey modKey, + DirectoryPath existingDataDir) + { + var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var masterWeapon = master.Weapons.AddNew(); + var masterPath = Path.Combine(existingDataDir, masterMod.FileName); + master.WriteToBinary(masterPath, new BinaryWriteParameters() + { + FileSystem = fileSystem + }); + + var master2 = new StarfieldMod(masterMod2, StarfieldRelease.Starfield); + var master2Weapon = master2.Weapons.AddNew(); + + var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); + mod.Weapons.GetOrAddAsOverride(masterWeapon); + mod.Weapons.GetOrAddAsOverride(master2Weapon); + var modPath = Path.Combine(existingDataDir, mod.ModKey.FileName); + mod.BeginWrite + .WithLoadOrder(masterMod, masterMod2) + .WithDataFolder(existingDataDir) + .ToPath(modPath) + .WithKnownMasters(master2) + .WithFileSystem(fileSystem) + .Write(); + + var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrder(masterMod, masterMod2) + .WithDataFolder(existingDataDir) + .WithFileSystem(fileSystem) + .WithKnownMasters(master2) + .Mutable() + .Construct(); + reimport.MasterReferences.Select(x => x.Master).Should() + .Equal(masterMod, masterMod2); + } } \ No newline at end of file diff --git a/Mutagen.Bethesda.UnitTests/Plugins/Builders/WriteBuilderTests.cs b/Mutagen.Bethesda.UnitTests/Plugins/Builders/WriteBuilderTests.cs index 67760e328..fb8e059fe 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/Builders/WriteBuilderTests.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/Builders/WriteBuilderTests.cs @@ -24,140 +24,75 @@ public void NoDataFolderWithHeaderMasters( var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); mod.Weapons.GetOrAddAsOverride(masterWeapon); var modPath = Path.Combine(existingDir, mod.ModKey.FileName); - mod.BeginWrite - .WithLoadOrderFromHeaderMasters() - .WithNoDataFolder() - .ToPath(modPath) - .WithFileSystem(fileSystem) - .Write(); - - using var reimport = StarfieldMod.CreateFromBinaryOverlay(modPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem - }); - reimport.MasterReferences.Select(x => x.Master).Should() - .Equal(masterMod); + Assert.Throws(() => + { + mod.BeginWrite + .WithLoadOrderFromHeaderMasters() + .WithNoDataFolder() + .ToPath(modPath) + .WithFileSystem(fileSystem) + .Write(); + }); } [Theory, MutagenAutoData] public void ExtraIncludedMasters( IFileSystem fileSystem, - ModKey masterMod, + ModKey masterModKey, ModKey modKey, - ModKey extraMaster, + ModKey extraMasterKey, DirectoryPath existingDir) { - var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var extraMaster = new StarfieldMod(extraMasterKey, StarfieldRelease.Starfield); + var master = new StarfieldMod(masterModKey, StarfieldRelease.Starfield); var masterWeapon = master.Weapons.AddNew(); var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); mod.Weapons.GetOrAddAsOverride(masterWeapon); var modPath = Path.Combine(existingDir, mod.ModKey.FileName); mod.BeginWrite - .WithLoadOrder(masterMod, extraMaster) - .WithNoDataFolder() + .WithLoadOrder(master, extraMaster) .ToPath(modPath) .WithFileSystem(fileSystem) - .WithExtraIncludedMasters(extraMaster) + .WithExtraIncludedMasters(extraMasterKey) .Write(); - using var reimport = StarfieldMod.CreateFromBinaryOverlay(modPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem - }); + using var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrder(master, extraMaster) + .WithFileSystem(fileSystem) + .Construct(); reimport.MasterReferences.Select(x => x.Master).Should() - .Equal(masterMod, extraMaster); + .Equal(masterModKey, extraMasterKey); } [Theory, MutagenAutoData] public void OverrideMasters( IFileSystem fileSystem, - ModKey masterMod, + ModKey masterModKey, ModKey modKey, - ModKey overrideMaster, + ModKey overrideMasterKey, DirectoryPath existingDir) { - var master = new StarfieldMod(masterMod, StarfieldRelease.Starfield); + var overrideMod = new StarfieldMod(overrideMasterKey, StarfieldRelease.Starfield); + var master = new StarfieldMod(masterModKey, StarfieldRelease.Starfield); var masterWeapon = master.Weapons.AddNew(); var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); mod.Weapons.GetOrAddAsOverride(masterWeapon); var modPath = Path.Combine(existingDir, mod.ModKey.FileName); mod.BeginWrite - .WithLoadOrder(masterMod, overrideMaster) - .WithNoDataFolder() + .WithLoadOrder(master, overrideMod) .ToPath(modPath) .WithFileSystem(fileSystem) - .WithExplicitOverridingMasterList(overrideMaster, masterMod) + .WithExplicitOverridingMasterList(overrideMasterKey, masterModKey) .Write(); - using var reimport = StarfieldMod.CreateFromBinaryOverlay(modPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem - }); - reimport.MasterReferences.Select(x => x.Master).Should() - .Equal(masterMod, overrideMaster); - } - - [Theory, MutagenAutoData] - public void WithLoadOrderTrimsSelfMod( - IFileSystem fileSystem, - ModKey otherModKey, - ModKey modKey, - ModKey afterModKey, - DirectoryPath existingDataDir) - { - var master = new StarfieldMod(otherModKey, StarfieldRelease.Starfield); - var weapon = master.Weapons.AddNew(); - var masterModPath = Path.Combine(existingDataDir, master.ModKey.FileName); - master.BeginWrite - .WithLoadOrder(otherModKey, modKey, afterModKey) - .WithDataFolder(existingDataDir) - .ToPath(masterModPath) - .WithFileSystem(fileSystem) - .Write(); - - var mod = new StarfieldMod(modKey, StarfieldRelease.Starfield); - mod.Weapons.GetOrAddAsOverride(weapon); - - var modPath = Path.Combine(existingDataDir, mod.ModKey.FileName); - mod.BeginWrite - .WithLoadOrder(otherModKey, modKey, afterModKey) - .WithDataFolder(existingDataDir) - .ToPath(modPath) + using var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithLoadOrder(master, overrideMod) .WithFileSystem(fileSystem) - .Write(); - - using var reimport = StarfieldMod.CreateFromBinaryOverlay(modPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem - }); + .Construct(); reimport.MasterReferences.Select(x => x.Master).Should() - .Equal(otherModKey); - } - - [Theory, MutagenAutoData] - public void WithLoadOrderNoTrim( - IFileSystem fileSystem, - ModKey otherModKey, - ModKey modKey, - ModKey afterModKey, - DirectoryPath existingDataDir) - { - var master = new StarfieldMod(otherModKey, StarfieldRelease.Starfield); - var masterModPath = Path.Combine(existingDataDir, master.ModKey.FileName); - Assert.Throws(() => - { - master.BeginWrite - .WithLoadOrder(otherModKey, modKey, afterModKey) - .WithDataFolder(existingDataDir) - .ToPath(masterModPath) - .WithFileSystem(fileSystem) - .DoNotTrimLoadOrderAtSelf() - .Write(); - }); + .Equal(masterModKey, overrideMasterKey); } [Theory, MutagenAutoData] @@ -201,11 +136,11 @@ public void WithAllParentMasters( .WithAllParentMasters() .Write(); - using var reimport = StarfieldMod.CreateFromBinaryOverlay(modPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem - }); + using var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(modPath) + .WithKnownMasters(transientMasterMod, master) + .WithFileSystem(fileSystem) + .Construct(); reimport.MasterReferences.Select(x => x.Master).Should() .Equal(transientMasterModKey, masterModKey); } diff --git a/Mutagen.Bethesda.UnitTests/Plugins/Masters/DuplicateMasterTesting.cs b/Mutagen.Bethesda.UnitTests/Plugins/Masters/DuplicateMasterTesting.cs index b361d1bf5..32930fd9a 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/Masters/DuplicateMasterTesting.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/Masters/DuplicateMasterTesting.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Mutagen.Bethesda.Plugins; +using Mutagen.Bethesda.Plugins.Records; using Mutagen.Bethesda.Skyrim; using Mutagen.Bethesda.Starfield; using Mutagen.Bethesda.Testing; @@ -28,8 +29,14 @@ public void NotSeparated() [Fact] public void Separated() { - var mod = StarfieldMod.CreateFromBinaryOverlay(TestDataPathing.StarfieldDuplicateMasterListing, - StarfieldRelease.Starfield); + var mod = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(TestDataPathing.StarfieldDuplicateMasterListing) + .WithKnownMasters( + new KeyedMasterStyle(ModKey.FromFileName("DuplicateMasterListing.esp"), MasterStyle.Full), + new KeyedMasterStyle(ModKey.FromFileName("MasterA.esm"), MasterStyle.Full), + new KeyedMasterStyle(ModKey.FromFileName("MasterB.esm"), MasterStyle.Full), + new KeyedMasterStyle(ModKey.FromFileName("MasterA.esm"), MasterStyle.Full)) + .Construct(); mod.Npcs.Count.Should().Be(4); mod.Npcs.Records.First(n => n.EditorID == "Originating") .FormKey.ModKey.FileName.String.Should().Be("DuplicateMasterListing.esp"); diff --git a/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs b/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs index f2bb045ca..5448686ac 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/Masters/SeparatedMastersTesting.cs @@ -187,7 +187,7 @@ public void Separated( lightModBAim.FormKey, mediumModBAim.FormKey); { - var meta = ParsingMeta.Factory(new BinaryReadParameters() + var meta = ParsingMeta.FactoryNoMasters(new BinaryReadParameters() { FileSystem = fileSystem }, GameRelease.Starfield, modPath); diff --git a/Mutagen.Bethesda.UnitTests/Plugins/OverriddenFormsWritingTests.cs b/Mutagen.Bethesda.UnitTests/Plugins/OverriddenFormsWritingTests.cs index b6ba7b896..98b65b0cb 100644 --- a/Mutagen.Bethesda.UnitTests/Plugins/OverriddenFormsWritingTests.cs +++ b/Mutagen.Bethesda.UnitTests/Plugins/OverriddenFormsWritingTests.cs @@ -19,20 +19,23 @@ private void TestExpectedOverriddenForms( IFileSystem fileSystem, ModPath existingModPath, IModGetter mod, + IModGetter master, params FormKey[] expectedFormkeys) { - mod.WriteToBinary(existingModPath, new BinaryWriteParameters() - { - ModKey = ModKeyOption.NoCheck, - OverriddenFormsOption = OverriddenFormsOption.Iterate, - FileSystem = fileSystem - }); - - using var reimport = StarfieldMod.CreateFromBinaryOverlay(existingModPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem, - }); + mod.BeginWrite + .WithLoadOrderFromHeaderMasters() + .WithKnownMasters(master) + .ToPath(existingModPath) + .WithFileSystem(fileSystem) + .NoModKeySync() + .WithOverriddenFormsOption(OverriddenFormsOption.Iterate) + .Write(); + + using var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(existingModPath) + .WithKnownMasters(master) + .WithFileSystem(fileSystem) + .Construct(); reimport.ModHeader.OverriddenForms.Should().NotBeNull(); reimport.ModHeader.OverriddenForms! .Select(x => x.FormKey) @@ -90,18 +93,20 @@ public void OverriddenFormsNoCheck( var fk = new FormKey(TestConstants.PluginModKey2, 0x123456); mod.ModHeader.OverriddenForms.Add(fk); - mod.WriteToBinary(existingModPath, new BinaryWriteParameters() - { - ModKey = ModKeyOption.NoCheck, - OverriddenFormsOption = OverriddenFormsOption.NoCheck, - FileSystem = fileSystem - }); - - using var reimport = StarfieldMod.CreateFromBinaryOverlay(existingModPath, StarfieldRelease.Starfield, - new BinaryReadParameters() - { - FileSystem = fileSystem, - }); + mod.BeginWrite + .WithLoadOrderFromHeaderMasters() + .WithKnownMasters(mod2) + .ToPath(existingModPath) + .WithFileSystem(fileSystem) + .NoModKeySync() + .WithOverriddenFormsOption(OverriddenFormsOption.NoCheck) + .Write(); + + using var reimport = StarfieldMod.Create(StarfieldRelease.Starfield) + .FromPath(existingModPath) + .WithKnownMasters(mod2) + .WithFileSystem(fileSystem) + .Construct(); reimport.ModHeader.OverriddenForms.Should().NotBeNull(); reimport.ModHeader.OverriddenForms! .Select(x => x.FormKey) @@ -159,6 +164,7 @@ public void OverriddenFormsCellsPlacedIncluded( fileSystem, existingModPath, mod, + mod2, masterPlaced.FormKey, worldspacePlaced.FormKey); } @@ -184,6 +190,7 @@ public void OverriddenFormsCellsDialogsIncluded( fileSystem, existingModPath, mod, + mod2, dialogTopic.FormKey, dialogResponse.FormKey); } @@ -208,6 +215,7 @@ public void OverriddenFormsCellsScenesIncluded( fileSystem, existingModPath, mod, + mod2, scene.FormKey); } } \ No newline at end of file