Skip to content

Commit 88ee7fd

Browse files
committed
ImportSetterWithMultiFileDetection usage
1 parent c74174f commit 88ee7fd

File tree

4 files changed

+40
-109
lines changed

4 files changed

+40
-109
lines changed

Directory.Packages.props

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
2828
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
2929
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
30-
<PackageVersion Include="Mutagen.Bethesda" Version="0.54.0-alpha.18" />
31-
<PackageVersion Include="Mutagen.Bethesda.Autofac" Version="0.54.0-alpha.18" />
32-
<PackageVersion Include="Mutagen.Bethesda.Core" Version="0.54.0-alpha.18" />
33-
<PackageVersion Include="Mutagen.Bethesda.Json" Version="0.54.0-alpha.18" />
34-
<PackageVersion Include="Mutagen.Bethesda.Kernel" Version="0.54.0-alpha.18" />
35-
<PackageVersion Include="Mutagen.Bethesda.Skyrim" Version="0.54.0-alpha.18" />
36-
<PackageVersion Include="Mutagen.Bethesda.Testing" Version="0.54.0-alpha.18" />
37-
<PackageVersion Include="Mutagen.Bethesda.WPF" Version="0.54.0-alpha.18" />
30+
<PackageVersion Include="Mutagen.Bethesda" Version="0.54.0-alpha.19" />
31+
<PackageVersion Include="Mutagen.Bethesda.Autofac" Version="0.54.0-alpha.19" />
32+
<PackageVersion Include="Mutagen.Bethesda.Core" Version="0.54.0-alpha.19" />
33+
<PackageVersion Include="Mutagen.Bethesda.Json" Version="0.54.0-alpha.19" />
34+
<PackageVersion Include="Mutagen.Bethesda.Kernel" Version="0.54.0-alpha.19" />
35+
<PackageVersion Include="Mutagen.Bethesda.Skyrim" Version="0.54.0-alpha.19" />
36+
<PackageVersion Include="Mutagen.Bethesda.Testing" Version="0.54.0-alpha.19" />
37+
<PackageVersion Include="Mutagen.Bethesda.WPF" Version="0.54.0-alpha.19" />
3838
<PackageVersion Include="Newtonsoft.Json">
3939
<Version>13.0.4</Version>
4040
</PackageVersion>

Mutagen.Bethesda.Synthesis/States/DI/PatcherStateFactory.cs

Lines changed: 9 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -114,58 +114,6 @@ private static void RemoveModKeysFromList<T>(IList<T> list, HashSet<ModKey> modK
114114
}
115115
}
116116

117-
private TModSetter ImportAndMergeSplitFiles<TModSetter, TModGetter>(
118-
ModPath sourceModPath,
119-
ModKey exportKey,
120-
RunSynthesisMutagenPatcher settings,
121-
StringsReadParameters stringReadParams)
122-
where TModSetter : class, IContextMod<TModSetter, TModGetter>, TModGetter
123-
where TModGetter : class, IContextGetterMod<TModSetter, TModGetter>
124-
{
125-
var sourcePathModKey = ModKey.FromFileName(Path.GetFileName(sourceModPath.Path));
126-
var splitFiles = MultiModFileAnalysis.GetSplitModFiles(sourceModPath, fileSystem: _fileSystem);
127-
128-
System.Console.WriteLine($"Detected split source files for {sourcePathModKey}");
129-
System.Console.WriteLine($" Found {splitFiles.Count} split files");
130-
131-
// Create a new mod to merge into
132-
var patchMod = ModFactory<TModSetter>.Activator(
133-
exportKey,
134-
settings.GameRelease,
135-
headerVersion: settings.HeaderVersionOverride,
136-
forceUseLowerFormIDRanges: settings.FormIDRangeMode.ToForceBool());
137-
138-
// Read each split file and copy records into the merged mod
139-
foreach (var splitFile in splitFiles)
140-
{
141-
System.Console.WriteLine($" Reading split file: {splitFile}");
142-
// Use the export mod key when reading, so records have correct FormKeys
143-
var splitMod = ModFactory<TModGetter>.Importer(
144-
new ModPath(exportKey, splitFile),
145-
settings.GameRelease,
146-
new BinaryReadParameters()
147-
{
148-
FileSystem = _fileSystem,
149-
StringsParam = stringReadParams
150-
});
151-
152-
// Copy all records from split mod to merged mod
153-
foreach (var rec in splitMod.EnumerateMajorRecords())
154-
{
155-
var recRegis = rec.Registration;
156-
patchMod.GetTopLevelGroup(recRegis.GetterType).AddUntyped(rec.DeepCopy());
157-
}
158-
159-
// Dispose if possible
160-
if (splitMod is IDisposable disposable)
161-
{
162-
disposable.Dispose();
163-
}
164-
}
165-
166-
return patchMod;
167-
}
168-
169117
#pragma warning disable CS0618
170118
public SynthesisState<TModSetter, TModGetter> ToState<TModSetter, TModGetter>(RunSynthesisMutagenPatcher settings, PatcherPreferences userPrefs, ModKey exportKey)
171119
where TModSetter : class, IContextMod<TModSetter, TModGetter>, TModGetter
@@ -291,36 +239,17 @@ public SynthesisState<TModSetter, TModGetter> ToState<TModSetter, TModGetter>(Ru
291239
{
292240
var modPath = new ModPath(exportKey, settings.SourcePath!);
293241

294-
// Check for split files first when enabled, since the base name
295-
// file may itself be the first split file
296242
if (settings.SplitIfMaxMastersExceeded)
297243
{
298-
var sourceModKey = ModKey.FromFileName(Path.GetFileName(settings.SourcePath!));
299-
var sourceModPath = new ModPath(sourceModKey, settings.SourcePath!);
300-
301-
if (MultiModFileAnalysis.IsMultiModFile(sourceModPath, fileSystem: _fileSystem))
302-
{
303-
patchMod = ImportAndMergeSplitFiles<TModSetter, TModGetter>(
304-
sourceModPath,
305-
exportKey,
306-
settings,
307-
stringReadParams);
308-
}
309-
else if (_fileSystem.File.Exists(modPath.Path))
310-
{
311-
patchMod = ModFactory<TModSetter>.Importer(
312-
modPath,
313-
settings.GameRelease,
314-
new BinaryReadParameters()
315-
{
316-
FileSystem = _fileSystem,
317-
StringsParam = stringReadParams
318-
});
319-
}
320-
else
321-
{
322-
throw new FileNotFoundException(modPath.Path);
323-
}
244+
patchMod = (TModSetter)ModFactory.ImportSetterWithMultiFileDetection(
245+
modPath,
246+
loadOrderListing.ProcessedLoadOrder.Select(x => x.ModKey),
247+
settings.GameRelease,
248+
new BinaryReadParameters()
249+
{
250+
FileSystem = _fileSystem,
251+
StringsParam = stringReadParams
252+
});
324253
}
325254
else if (_fileSystem.File.Exists(modPath.Path))
326255
{

Synthesis.Bethesda.Execution/Running/Runner/PostRunProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public async Task<FilePath> Run(
6969

7070
var mod = ModFactory.ImportSetterWithMultiFileDetection(
7171
path,
72-
lo.ListedOrder,
72+
lo.ListedOrder.Select(x => x.ModKey),
7373
_gameReleaseContext.Release,
7474
new BinaryReadParameters()
7575
{

Synthesis.Bethesda.UnitTests/Pipeline/SynthesisPipelineTests.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -767,13 +767,16 @@ public async Task ImportsSplitSourceFiles(
767767
}
768768

769769
// Create split source files simulating a previous patcher's output
770-
// Note: When Mutagen splits a mod, records keep FormKeys pointing to the merged mod key
771-
var sourceModKey = ModKey.FromFileName("PreviousPatch.esp");
770+
// In real usage, source and output share the same mod name but live in different directories
771+
var patchModKey = ModKey.FromFileName("Patch.esp");
772772
var expectedFormLists = new List<(string EditorID, List<FormKey> Items)>();
773773

774+
// Create a source directory (simulating a previous run's working directory)
775+
var sourceDir = Path.Combine(dataFolder, "Previous");
776+
fileSystem.Directory.CreateDirectory(sourceDir);
777+
774778
// Create first split file with first 128 masters
775-
// Use merged mod key for the mod, then write with NoModKeySync to preserve FormKeys
776-
var splitMod1 = new SkyrimMod(sourceModKey, SkyrimRelease.SkyrimSE);
779+
var splitMod1 = new SkyrimMod(patchModKey, SkyrimRelease.SkyrimSE);
777780
var formList1 = splitMod1.FormLists.AddNew();
778781
formList1.EditorID = "NpcFormList_0";
779782
var items1 = new List<FormKey>();
@@ -785,7 +788,7 @@ public async Task ImportsSplitSourceFiles(
785788
expectedFormLists.Add((formList1.EditorID, items1));
786789

787790
// First split file uses the base name (no suffix)
788-
var splitFile1Path = Path.Combine(dataFolder, sourceModKey.FileName);
791+
var splitFile1Path = Path.Combine(sourceDir, patchModKey.FileName);
789792
splitMod1.BeginWrite
790793
.ToPath(splitFile1Path)
791794
.WithNoLoadOrder()
@@ -794,9 +797,9 @@ public async Task ImportsSplitSourceFiles(
794797
.Write();
795798

796799
// Create second split file with remaining masters
797-
var splitMod2 = new SkyrimMod(sourceModKey, SkyrimRelease.SkyrimSE);
800+
var splitMod2 = new SkyrimMod(patchModKey, SkyrimRelease.SkyrimSE);
798801
// Use RecordWith to create a record with a specific FormKey to avoid conflicts
799-
var formList2 = new Mutagen.Bethesda.Skyrim.FormList(new FormKey(sourceModKey, 0x900), SkyrimRelease.SkyrimSE);
802+
var formList2 = new Mutagen.Bethesda.Skyrim.FormList(new FormKey(patchModKey, 0x900), SkyrimRelease.SkyrimSE);
800803
formList2.EditorID = "NpcFormList_1";
801804
splitMod2.FormLists.RecordCache.Set(formList2);
802805
var items2 = new List<FormKey>();
@@ -808,7 +811,7 @@ public async Task ImportsSplitSourceFiles(
808811
expectedFormLists.Add((formList2.EditorID, items2));
809812

810813
// Second split file uses _2 suffix
811-
var splitFile2Path = Path.Combine(dataFolder, $"{sourceModKey.Name}_2{Path.GetExtension(sourceModKey.FileName)}");
814+
var splitFile2Path = Path.Combine(sourceDir, $"{patchModKey.Name}_2{Path.GetExtension(patchModKey.FileName)}");
812815
splitMod2.BeginWrite
813816
.ToPath(splitFile2Path)
814817
.WithNoLoadOrder()
@@ -819,20 +822,19 @@ public async Task ImportsSplitSourceFiles(
819822
// Create plugins.txt with all masters AND the split files
820823
var pluginPath = Path.Combine(dataFolder, "Plugins.txt");
821824
var pluginLines = masterModKeys.Select(k => $"*{k.FileName}").ToList();
822-
pluginLines.Add($"*{sourceModKey.FileName}");
823-
pluginLines.Add($"*{sourceModKey.Name}_2{Path.GetExtension(sourceModKey.FileName)}");
825+
pluginLines.Add($"*{patchModKey.FileName}");
826+
pluginLines.Add($"*{patchModKey.Name}_2{Path.GetExtension(patchModKey.FileName)}");
824827
fileSystem.File.WriteAllLines(pluginPath, pluginLines);
825828

826-
// Setup run arguments - SourcePath points to base name that doesn't exist
827-
var outputModKey = ModKey.FromFileName("Output.esp");
829+
// Setup run arguments - SourcePath is in a different directory than output
828830
var runArgs = new RunSynthesisMutagenPatcher
829831
{
830-
ModKey = outputModKey.FileName,
832+
ModKey = patchModKey.FileName,
831833
DataFolderPath = dataFolder,
832834
LoadOrderFilePath = pluginPath,
833835
GameRelease = GameRelease.SkyrimSE,
834-
SourcePath = Path.Combine(dataFolder, sourceModKey.FileName), // Base name (first split file)
835-
OutputPath = Path.Combine(outputPath, outputModKey.FileName),
836+
SourcePath = splitFile1Path, // Source in Previous/ directory
837+
OutputPath = Path.Combine(outputPath, patchModKey.FileName),
836838
LoadOrderIncludesCreationClub = true,
837839
SplitIfMaxMastersExceeded = true // Enable split file detection
838840
};
@@ -874,15 +876,15 @@ await SynthesisPipeline.Instance
874876
// Verify load order was modified correctly
875877
capturedLoadOrder.ShouldNotBeNull();
876878

877-
// The load order should contain the output mod key (patchMod), not the split files
879+
// The load order should contain the patch mod key, not the split files
878880
var loadOrderKeys = capturedLoadOrder!.ListedOrder.Select(x => x.ModKey).ToList();
879881

880-
// Should contain the output mod key (the patchMod)
881-
loadOrderKeys.ShouldContain(outputModKey,
882-
$"Load order should contain output ModKey {outputModKey}");
882+
// Should contain the patch mod key
883+
loadOrderKeys.ShouldContain(patchModKey,
884+
$"Load order should contain patch ModKey {patchModKey}");
883885

884886
// Should NOT contain the split sibling file key
885-
var splitKey2 = new ModKey($"{sourceModKey.Name}_2", sourceModKey.Type);
887+
var splitKey2 = new ModKey($"{patchModKey.Name}_2", patchModKey.Type);
886888
loadOrderKeys.ShouldNotContain(splitKey2,
887889
$"Load order should not contain split file {splitKey2}");
888890
}

0 commit comments

Comments
 (0)