diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index ba62db3..1877a2a 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -19,23 +19,28 @@ on:
- 'mkdocs.yml'
jobs:
- build-test:
+ build-test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
- sln:
- - MutagenMerger.sln
-
+
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.*
- name: Install dependencies
run: |
- dotnet clean ${{ matrix.sln }} -c Release && dotnet nuget locals all --clear
- dotnet restore ${{ matrix.sln }}
+ dotnet clean -c Release && dotnet nuget locals all --clear
+ dotnet restore
+ dotnet tool restore
- name: Build
- run: dotnet build ${{ matrix.sln }} -c Release --no-restore /p:GeneratePackageOnBuild=false
+ run: dotnet build -c Release --no-restore /p:GeneratePackageOnBuild=false
- name: Test
- run: dotnet test ${{ matrix.sln }} -c Release --no-build
+ run: dotnet test -c Release --no-build
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..d02544a
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,14 @@
+
+
+ true
+ enable
+ true
+ GPL-3.0-only
+ https://github.com/Mutagen-Modding/Mutagen.Bethesda.Merge
+ https://github.com/Mutagen-Modding/Mutagen.Bethesda.Merge
+ GPL-3.0-only
+ 2025
+ Mutagen
+ Mutagen
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 0000000..e1f31ab
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
diff --git a/Mutagen.Bethesda.Merge.CLI/MainModule.cs b/Mutagen.Bethesda.Merge.CLI/MainModule.cs
new file mode 100644
index 0000000..3e0cb61
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.CLI/MainModule.cs
@@ -0,0 +1,15 @@
+using System.IO.Abstractions;
+using Autofac;
+using Mutagen.Bethesda.Merge.Lib;
+
+namespace Mutagen.Bethesda.Merge.CLI;
+
+public class MainModule : Autofac.Module
+{
+ protected override void Load(ContainerBuilder builder)
+ {
+ builder.RegisterType().As()
+ .SingleInstance();
+ builder.RegisterModule();
+ }
+}
diff --git a/Mutagen.Bethesda.Merge.CLI/Mutagen.Bethesda.Merge.CLI.csproj b/Mutagen.Bethesda.Merge.CLI/Mutagen.Bethesda.Merge.CLI.csproj
new file mode 100644
index 0000000..1d8f272
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.CLI/Mutagen.Bethesda.Merge.CLI.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net9.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mutagen.Bethesda.Merge.CLI/Options.cs b/Mutagen.Bethesda.Merge.CLI/Options.cs
new file mode 100644
index 0000000..c17fc5a
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.CLI/Options.cs
@@ -0,0 +1,24 @@
+using CommandLine;
+
+namespace Mutagen.Bethesda.Merge.CLI;
+
+public class Options
+{
+ [Option("game", HelpText = "Game to mod (Default SkyrimSE)")]
+ public GameRelease Game { get; set; } = GameRelease.SkyrimSE;
+
+ [Option("data", HelpText = "Path to the data folder", Required = true)]
+ public string DataFolder { get; set; } = string.Empty;
+
+ [Option("merge", Min = 1, Max = 4096, HelpText = "Plugins to merge")]
+ public IEnumerable PluginsToMerge { get; set; } = [];
+
+ [Option("mergefile", HelpText = "Get plugins to merge from file")]
+ public string PluginsMergeTxt { get; set; } = string.Empty;
+
+ [Option("output", Required = true, HelpText = "Output merge folder")]
+ public string Output { get; set; } = string.Empty;
+
+ [Option("mergename", Required = true, HelpText = "Name of Merge")]
+ public string MergeName { get; set; } = string.Empty;
+}
diff --git a/Mutagen.Bethesda.Merge.CLI/Program.cs b/Mutagen.Bethesda.Merge.CLI/Program.cs
new file mode 100644
index 0000000..58c9f35
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.CLI/Program.cs
@@ -0,0 +1,68 @@
+using System.Diagnostics;
+using Autofac;
+using CommandLine;
+using Mutagen.Bethesda.Environments.DI;
+using Mutagen.Bethesda.Fallout4;
+using Mutagen.Bethesda.Merge.Lib.DI;
+using Mutagen.Bethesda.Oblivion;
+using Mutagen.Bethesda.Plugins;
+using Mutagen.Bethesda.Skyrim;
+
+namespace Mutagen.Bethesda.Merge.CLI;
+
+public static class Program
+{
+ public static async Task Main(string[] args)
+ {
+ await Parser.Default.ParseArguments(args)
+ .WithParsedAsync(Run);
+ }
+
+ private static async Task Run(Options options)
+ {
+ var modsToMerge = options.PluginsMergeTxt != string.Empty
+ ? (await File.ReadAllLinesAsync(options.PluginsMergeTxt))
+ .Select(x => ModKey.FromNameAndExtension(x))
+ .ToList()
+ : options.PluginsToMerge
+ .Select(x => ModKey.FromNameAndExtension(x))
+ .ToList();
+
+ if (Directory.Exists(options.Output)) Directory.Delete(options.Output, true);
+
+ var sw = new Stopwatch();
+ sw.Start();
+
+ Type[] genericTypes;
+ switch (options.Game.ToCategory())
+ {
+ case GameCategory.Oblivion:
+ genericTypes = new Type[] { typeof(IOblivionMod), typeof(IOblivionModGetter),typeof(IOblivionMajorRecord), typeof(IOblivionMajorRecordGetter) };
+ break;
+ case GameCategory.Fallout4:
+ genericTypes = new Type[] { typeof(IFallout4Mod), typeof(IFallout4ModGetter), typeof(IFallout4MajorRecord), typeof(IFallout4MajorRecordGetter) };
+ break;
+ case GameCategory.Skyrim:
+ default:
+ genericTypes = new Type[] { typeof(ISkyrimMod), typeof(ISkyrimModGetter), typeof(ISkyrimMajorRecord), typeof(ISkyrimMajorRecordGetter) };
+ break;
+ }
+
+ ContainerBuilder builder = new();
+ builder.RegisterModule();
+ builder.RegisterInstance(
+ new DataDirectoryInjection(options.DataFolder));
+ builder.RegisterInstance(
+ new GameReleaseInjection(options.Game));
+ var container = builder.Build();
+ var merger = container.Resolve(typeof(Merger<,,,>).MakeGenericType(genericTypes)) as IMerger;
+
+ merger!.Merge(
+ modsToMerge,
+ ModKey.FromNameAndExtension(options.MergeName),
+ options.Output);
+
+ Console.WriteLine($"Merged {modsToMerge.Count} plugins in {sw.ElapsedMilliseconds}ms");
+ sw.Stop();
+ }
+}
diff --git a/Mutagen.Bethesda.Merge.Lib/DI/AssetMerge.cs b/Mutagen.Bethesda.Merge.Lib/DI/AssetMerge.cs
new file mode 100644
index 0000000..b7381f6
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.Lib/DI/AssetMerge.cs
@@ -0,0 +1,254 @@
+using System.IO.Abstractions;
+using Microsoft.Extensions.FileSystemGlobbing;
+using Mutagen.Bethesda.Archives;
+using Mutagen.Bethesda.Plugins;
+using Mutagen.Bethesda.Plugins.Masters;
+using Mutagen.Bethesda.Plugins.Records;
+using Noggog;
+using Noggog.IO;
+using DirectoryInfoWrapper = Microsoft.Extensions.FileSystemGlobbing.Abstractions.DirectoryInfoWrapper;
+
+namespace Mutagen.Bethesda.Merge.Lib.DI;
+
+public class AssetMerge
+ where TModGetter : class, IModGetter, IMajorRecordContextEnumerable, IMajorRecordGetterEnumerable, IContextGetterMod
+ where TMod : class, IMod, IContextMod, TModGetter
+ where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
+ where TMajorRecordGetter : class, IMajorRecordGetter
+{
+ private readonly IFileSystem _fileSystem;
+ private readonly MergeState _mergeState;
+ private readonly string _outputDir;
+ private readonly string _mergeName;
+ private readonly List _rules;
+
+ public delegate AssetMerge Factory(
+ MergeState mergeState);
+
+ public AssetMerge(
+ MergeState mergeState,
+ IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ _mergeState = mergeState;
+ _rules = GetRules();
+ _outputDir = Path.GetDirectoryName(_mergeState.OutputPath) ?? "";
+ _mergeName = Path.GetFileName(_mergeState.OutputPath);
+ }
+
+ private List GetRules()
+ {
+ var rules = new List() {"**/*.@(esp|esm|bsa|ba2|bsl)", "meta.ini",
+ "interface/translations/*.txt", "TES5Edit Backups/**/*",
+ "fomod/**/*", "screenshot?(s)/**/*", "scripts/source/*.psc", "source/scripts/*.psc"};
+
+ _mergeState.ModsToMerge.ForEach(x =>
+ {
+ rules.Add($"**/{x.Name.ToLower()}.seq");
+ rules.Add($"**/{x.Name.ToLower()}.ini");
+ rules.Add($"**/{x.Name.ToLower()}_DISTR.ini");
+ rules.Add($"**/{x.Name.ToLower()}_ANIO.ini");
+ rules.Add($"**/{x.Name.ToLower()}_SWAP.ini");
+ rules.Add($"**/{x.Name.ToLower()}_KID.ini");
+ rules.Add($"**/{x.FileName.String.ToLower()}/**/*");
+ });
+ return rules;
+ }
+
+ public void Handle()
+ {
+ using var temp = TempFolder.Factory();
+ var matcher = new Matcher();
+ matcher.AddIncludePatterns(new string[] { "**/*" });
+ matcher.AddExcludePatterns(_rules);
+ Parallel.ForEach(_mergeState.ModsToMerge, mod => {
+ var bsaPattern = mod.FileName.NameWithoutExtension + "*." + (_mergeState.Release == GameRelease.Fallout4 ? "b2a" : "bsa");
+ string[] bsaFiles = _fileSystem.Directory.GetFiles(_mergeState.DataPath, bsaPattern);
+
+ // bsaFiles.ForEach(Console.WriteLine);
+
+ foreach (string bsa in bsaFiles)
+ {
+ ExtractBSA(bsa, temp.Dir);
+ }
+ // Console.WriteLine();
+ });
+
+ var matches = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(temp.Dir)));
+
+ Parallel.ForEach(matches.Files, file => {
+ _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(_outputDir, file.Path)) ?? "");
+ Console.WriteLine(" Copying extracted asset \"" + file.Path + "\"");
+ _fileSystem.File.Copy(Path.Combine(temp.Dir, file.Path), Path.Combine(_outputDir, file.Path));
+ });
+
+ foreach (var mod in _mergeState.ModsToMerge)
+ {
+ CopyAssets(_mergeState.DataPath, mod);
+ CopyAssets(temp.Dir, mod);
+ }
+
+ BuildSeqFile(_mergeState.DataPath, temp.Dir, _mergeState.OutgoingMod);
+ }
+
+ private void BuildSeqFile(string dataPath, DirectoryPath temp, TMod outputMod)
+ {
+ var formIds = GetSeqQuests(outputMod);
+ if (formIds.Count == 0) return;
+ var fileName = _mergeName.Substring(0, _mergeName.Length - 4) + ".seq";
+ var filePath = Path.Combine(_outputDir, "seq", fileName);
+ var buffer = new byte[formIds.Count * sizeof(UInt32)];
+
+ for (int i = 0; i < formIds.Count; i++)
+ {
+ Buffer.BlockCopy(BitConverter.GetBytes(formIds[i]), 0, buffer, i * 4, 4);
+ }
+ if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
+ _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? "");
+ _fileSystem.File.WriteAllBytes(filePath, buffer);
+ Console.WriteLine();
+ Console.WriteLine(" Created SEQ file: " + fileName);
+
+ // if (!formIds.length) return;
+ // let filename = fh.getFileBase(merge.filename) + '.seq',
+ // filePath = `${ merge.dataPath}\\seq\\${ filename}`,
+ // buffer = new Buffer(formIds.length * 4);
+ // formIds.forEach((fid, n) => buffer.writeUInt32LE(fid, n * 4));
+ // fh.jetpack.write(filePath, buffer);
+ // progressLogger.log('Created SEQ file: ' + filePath);
+ }
+
+ private List GetSeqQuests(TMod merge)
+ {
+ var masterColl = MasterReferenceCollection.FromPath(
+ Path.Combine(_outputDir, _mergeName),
+ _mergeState.Release,
+ _fileSystem);
+
+ IGroup quests = _mergeState.Release switch
+ {
+ GameRelease.Oblivion => merge.GetTopLevelGroup(),
+ GameRelease.Fallout4 => merge.GetTopLevelGroup(),
+ _ => merge.GetTopLevelGroup(),
+ };
+
+ List formIds = new List();
+
+ if (quests.Count == 0) return formIds;
+
+ Parallel.ForEach(quests.Records, quest => {
+ bool startGameEnabled = _mergeState.Release switch {
+ GameRelease.Oblivion => ((Oblivion.Quest)quest).Data?.Flags.HasFlag(Oblivion.Quest.Flag.StartGameEnabled) ?? false,
+ GameRelease.Fallout4 => ((Fallout4.Quest)quest).Data?.Flags.HasFlag(Fallout4.Quest.Flag.StartGameEnabled) ?? false,
+ _ => ((Skyrim.Quest)quest).Flags.HasFlag(Skyrim.Quest.Flag.StartGameEnabled)
+ };
+ if (startGameEnabled)
+ {
+ throw new NotImplementedException();
+ // This got hidden in latest mutagen after Starfield flipped the table.
+ // Will need to re-expose how FormIDs are generated in a tool the public has access to.
+ // var fid = masterColl.GetFormID(quest.FormKey).Raw;
+ // formIds.Add(fid);
+ }
+ });
+
+ return formIds;
+ }
+
+ private void CopyAssets(DirectoryPath path, ModKey mod)
+ {
+ CopyActorAssets(path, "textures/actors/character/facegendata/facetint", mod);
+ CopyActorAssets(path, "meshes/actors/character/facegendata/facegeom", mod);
+ CopyActorAssets(path, "sound/voice", mod);
+
+ CopyTranslations(path, mod);
+ }
+
+ private void ExtractBSA(string bsa, DirectoryPath temp)
+ {
+ Console.WriteLine();
+
+ var reader = Archive.CreateReader(_mergeState.Release, bsa);
+ var files = reader.Files.ToArray();
+ Parallel.For(0,files.Count(), i => {
+ var file = files[i];
+ var filePath = file.Path.Replace("\\", "/").ToLower();
+ _fileSystem.Directory.CreateDirectory(Path.Combine(temp, Path.GetDirectoryName(filePath) ?? ""));
+ Console.SetCursorPosition(0, Console.CursorTop);
+ Console.Write(" Extracting Archive \"" + Path.GetFileName(bsa) + "\" " + ((decimal)i / files.Count()).ToString("0.00%"));
+ File.WriteAllBytes(Path.Combine(temp, filePath), file.GetBytes());
+ });
+ Console.SetCursorPosition(0, Console.CursorTop);
+ Console.Write(" Extracting Archive \"" + Path.GetFileName(bsa) + "\" 100.00%");
+ }
+
+ private void CopyTranslations(string dir, ModKey mod)
+ {
+ var path = "interface/translations/";
+ var srcPath = Path.Combine(dir, path);
+ var dstPath = Path.Combine(_outputDir, path);
+
+ if (!_fileSystem.Directory.Exists(srcPath)) return;
+
+ foreach (var file in _fileSystem.Directory.GetFiles(srcPath,
+ mod.Name.ToLower() + "_*.txt",
+ new EnumerationOptions() { RecurseSubdirectories = true, MatchCasing = MatchCasing.CaseInsensitive }))
+ {
+ var language = Path.GetFileNameWithoutExtension(file).Replace(mod.Name.ToLower() + "_", "");
+ var dst = dstPath + _mergeName.Substring(0, _mergeName.Length - 4) + "_" + language + ".txt";
+ _fileSystem.Directory.CreateDirectory(dstPath);
+
+ var writer = _fileSystem.File.AppendText(dst);
+ writer.Write(_fileSystem.File.ReadAllText(file));
+ writer.Close();
+
+ Console.WriteLine(" Appending " + mod.Name.ToLower() + "_" + "language to " + _mergeName.Substring(0, _mergeName.Length - 4) + "_" + language);
+
+
+ };
+ }
+
+ private void CopyActorAssets(string dir, string _path, ModKey mod)
+ {
+ var path = _path.Replace("\\", "/");
+
+ var srcPath = Path.Combine(dir, path, mod.FileName.String.ToLower());
+ var dstPath = Path.Combine(_outputDir, path, _mergeName);
+ _fileSystem.Directory.CreateDirectory(dstPath);
+
+ if (!_fileSystem.Directory.Exists(srcPath)) return;
+
+ Console.WriteLine(" Copying assets from directory \"" + Path.Combine(path, mod.FileName.String.ToLower()) + "\"");
+ Console.WriteLine(" Copying assets to directory \"" + Path.Combine(path, _mergeName) + "\"");
+
+ _mergeState.Mapping.Where(x => x.Key.ModKey == mod).ForEach(x =>
+ {
+ var srcId = x.Key.ID;
+ var srcIdString = x.Key.IDString().ToLower();
+
+ foreach (var file in _fileSystem.Directory.GetFiles(srcPath,
+ "*" + srcIdString + "*",
+ new EnumerationOptions() { RecurseSubdirectories = true, MatchCasing = MatchCasing.CaseInsensitive }))
+ {
+ if (_mergeState.Mapping.Select(x => x.Key.ModKey).Contains(mod) && _mergeState.Mapping.Select(x => x.Key.ID).Contains(srcId))
+ {
+ var newId = "00" + _mergeState.Mapping[new FormKey(mod, srcId)].IDString().ToLower();
+ var dstFile = file.Replace(srcIdString, newId).Replace(srcPath, dstPath);
+ _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(dstFile) ?? "");
+ Console.WriteLine(" Asset renumbered from " + srcIdString + " to " + newId);
+ Console.WriteLine(" Copying asset \"" + file.Replace(srcPath + "/", "") + "\" to \"" + dstFile.Replace(dstPath + "/", "") + "\"");
+ _fileSystem.File.Copy(file, dstFile);
+ }
+ else
+ {
+ _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(file.Replace(srcPath, dstPath)) ?? "");
+ Console.WriteLine(" Asset not renumbered.");
+ Console.WriteLine(" Copying asset \"" + file.Replace(srcPath + "/", "") + "\"");
+ _fileSystem.File.Copy(file, file.Replace(srcPath, dstPath));
+
+ }
+
+ }
+ });
+ }
+}
diff --git a/MutagenMerger.Lib/DI/CopyRecordProcessor.cs b/Mutagen.Bethesda.Merge.Lib/DI/CopyRecordProcessor.cs
similarity index 89%
rename from MutagenMerger.Lib/DI/CopyRecordProcessor.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/CopyRecordProcessor.cs
index 5e20a16..16b21f5 100644
--- a/MutagenMerger.Lib/DI/CopyRecordProcessor.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/CopyRecordProcessor.cs
@@ -1,40 +1,34 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Loqui;
-using Mutagen.Bethesda;
+using Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications;
using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
+using Noggog;
using SkyrimRecord = Mutagen.Bethesda.Skyrim;
using Fallout4Record = Mutagen.Bethesda.Fallout4;
-using OblivionRecord = Mutagen.Bethesda.Oblivion;
-using MutagenMerger.Lib.DI.GameSpecifications;
-using Noggog;
-namespace MutagenMerger.Lib.DI;
+namespace Mutagen.Bethesda.Merge.Lib.DI;
public class CopyRecordProcessor
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
{
- private readonly Dictionary> _copyOverrides;
+ private readonly Dictionary> _copyOverrides;
public CopyRecordProcessor(ICopyOverride[] copyOverrides)
{
_copyOverrides = copyOverrides
- // .GroupBy(x => x.ObjectKey)
+ // .GroupBy(x => x.ProtocolKey)
// .ToDictionary(x => x.Key, x => x.First());
- .ToDictionary(x => x.ObjectKey, x => x);
+ .ToDictionary(x => x.ClassType, x => x);
}
public void CopyRecords(
MergeState mergeState)
{
foreach (var rec in mergeState.Mods.Where(mod => mergeState.ModsToMerge.Contains(mod.ModKey))
- .WinningOverrideContexts(mergeState
+ .WinningContextOverrides(mergeState
.LinkCache))
{
- if (_copyOverrides.TryGetValue(rec.Record.Registration.ObjectKey, out var copyOverride))
+ if (_copyOverrides.TryGetValue(rec.Record.Registration.ClassType, out var copyOverride))
{
copyOverride.HandleCopyFor(mergeState, rec);
}
@@ -66,12 +60,13 @@ private void DuplicateAsNewRecord(
Console.WriteLine(" Renumbering Record [" + rec.Record.FormKey.ModKey.Name + "] " +
rec.Record.FormKey.IDString() + " to [" + mergeState.OutgoingMod.ModKey.Name + "] " +
duplicated.FormKey.IDString());
- mergeState.Mapping.Add(rec.Record.FormKey, duplicated.FormKey);
}
else {
Console.WriteLine(" Copying Record [" + rec.Record.FormKey.ModKey.Name + "] " +
rec.Record.FormKey.IDString());
}
+
+ mergeState.Mapping.Add(rec.Record.FormKey, duplicated.FormKey);
}
private static MajorRecord DuplicateAndRenumber(MergeState mergeState, IModContext rec)
@@ -86,6 +81,7 @@ private static MajorRecord DuplicateAndRenumber(MergeState mer
type = typeof(SkyrimRecord.Global);
}
var group = mergeState.OutgoingMod.GetTopLevelGroup(type);
+ Console.WriteLine(group.ToString());
group.AddUntyped(duplicated);
return duplicated;
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Base/BlankOverride.Generated.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/BlankOverride.Generated.cs
similarity index 99%
rename from MutagenMerger.Lib/DI/GameSpecifications/Base/BlankOverride.Generated.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/BlankOverride.Generated.cs
index c23f424..c3b9702 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Base/BlankOverride.Generated.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/BlankOverride.Generated.cs
@@ -7,7 +7,7 @@
using Mutagen.Bethesda.Fallout4;
using Mutagen.Bethesda.Oblivion;
-namespace MutagenMerger.Lib.DI.GameSpecifications;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications;
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Base/BlankOverride.tt b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/BlankOverride.tt
similarity index 97%
rename from MutagenMerger.Lib/DI/GameSpecifications/Base/BlankOverride.tt
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/BlankOverride.tt
index de9f218..3e30a67 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Base/BlankOverride.tt
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/BlankOverride.tt
@@ -8,7 +8,7 @@ using Mutagen.Bethesda.Skyrim;
using Mutagen.Bethesda.Fallout4;
using Mutagen.Bethesda.Oblivion;
-namespace MutagenMerger.Lib.DI.GameSpecifications;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications;
<# var placedVariants = new string[][] {
new string[] {"Skyrim","PlacedObject"},
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Base/CellOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/CellOverride.cs
similarity index 89%
rename from MutagenMerger.Lib/DI/GameSpecifications/Base/CellOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/CellOverride.cs
index afd1d56..2010c1a 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Base/CellOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Base/CellOverride.cs
@@ -1,21 +1,18 @@
-using System;
-using Mutagen.Bethesda;
+using Mutagen.Bethesda.Fallout4;
+using Mutagen.Bethesda.Oblivion;
using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
+using Mutagen.Bethesda.Skyrim;
using SkyrimRecord = Mutagen.Bethesda.Skyrim;
using Fallout4Record = Mutagen.Bethesda.Fallout4;
using OblivionRecord = Mutagen.Bethesda.Oblivion;
-using System.Collections.Generic;
-using Mutagen.Bethesda.Oblivion;
-using System.Linq;
-using Mutagen.Bethesda.Fallout4;
-using Mutagen.Bethesda.Skyrim;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Base;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Base;
public class CellOverride
{
- public static IMajorRecord CopyCellAsOverride(MergeState state,
+ public static IMajorRecord CopyCellAsOverride(
+ MergeState state,
IModContext context)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
@@ -52,9 +49,10 @@ public static IMajorRecord CopyCellAsOverride(MergeState state,
- IModContext context, MajorRecord.TranslationMask mask)
-
+ public static IMajorRecord DuplicateCell(
+ MergeState state,
+ IModContext context,
+ MajorRecord.TranslationMask mask)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
@@ -73,7 +71,6 @@ public static bool RecordExists(
MergeState state,
IMajorRecord newRecord,
IMajorRecordGetter temp)
-
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
{
@@ -98,9 +95,10 @@ public static bool RecordExists(
}
- public static void CopySubRecords(MergeState state, IModContext context, IMajorRecord newRecord)
-
-
+ public static void CopySubRecords(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
@@ -132,9 +130,10 @@ public static void CopySubRecords(MergeState state, IModContext context, IMajorRecord newRecord)
-
-
+ private static void CopyNavmesh(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
@@ -172,9 +171,10 @@ private static void CopyNavmesh(MergeState state, IModContext context, IMajorRecord newRecord)
-
-
+ private static void CopyLandscape(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
@@ -221,13 +221,12 @@ private static void CopyLandscape(MergeState state, IModContext context, IMajorRecord newRecord)
-
-
+
+ private static void CopyPathing(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
@@ -262,20 +261,17 @@ private static void CopyPathing(MergeState state, IModContext context, IMajorRecord newRecord)
-
-
+
+ private static void CopyPersistent(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
where TMajorRecordGetter : class, IMajorRecordGetter
{
-
IReadOnlyList list =
state.Release == GameRelease.Oblivion ? ((OblivionRecord.ICellGetter)context.Record).Persistent :
state.Release == GameRelease.Fallout4 ? ((Fallout4Record.ICellGetter)context.Record).Persistent :
@@ -296,8 +292,7 @@ private static void CopyPersistent(MergeState state, IModContext context, IMajorRecord newRecord)
-
-
+
+ private static void CopyTemporary(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
where TMajorRecordGetter : class, IMajorRecordGetter
{
-
IReadOnlyList list =
state.Release == GameRelease.Oblivion ? ((OblivionRecord.ICellGetter)context.Record).Temporary :
state.Release == GameRelease.Fallout4 ? ((Fallout4Record.ICellGetter)context.Record).Temporary :
@@ -364,15 +357,16 @@ private static void CopyTemporary(MergeState state, IModContext context, IMajorRecord newRecord)
-
- where TModGetter : class, IModGetter, IContextGetterMod
- where TMod : class, IMod, IContextMod, TModGetter
- where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
- where TMajorRecordGetter : class, IMajorRecordGetter
+ private static void CopyDistance(
+ MergeState state,
+ IModContext context,
+ IMajorRecord newRecord)
+ where TModGetter : class, IModGetter, IContextGetterMod
+ where TMod : class, IMod, IContextMod, TModGetter
+ where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
+ where TMajorRecordGetter : class, IMajorRecordGetter
{
-
IReadOnlyList list = ((OblivionRecord.ICellGetter)context.Record).VisibleWhenDistant;
foreach (var vis in list)
{
@@ -386,18 +380,13 @@ private static void CopyDistance(MergeStat
public static IMajorRecord DuplicateDialogTopic(MergeState state,
IMajorRecordGetter record, MajorRecord.TranslationMask mask)
-
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
{
// Don't duplicate branches, as they will be added below
IMajorRecord newRecord = record.Duplicate(state.GetFormKey(record.FormKey),mask);
-
-
+
switch (state.Release)
{
case GameRelease.Oblivion:
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/CellOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/CellOverride.cs
similarity index 64%
rename from MutagenMerger.Lib/DI/GameSpecifications/Fallout4/CellOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/CellOverride.cs
index 2d5a172..98fc84c 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/CellOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/CellOverride.cs
@@ -1,14 +1,12 @@
-using System;
-using Mutagen.Bethesda;
+using Mutagen.Bethesda.Fallout4;
using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
-using Mutagen.Bethesda.Fallout4;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Fallout4;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Fallout4;
public class CellOverride : ACopyOverride
{
- public static readonly Cell.TranslationMask CellMask = new Mutagen.Bethesda.Fallout4.Cell.TranslationMask(defaultOn: true)
+ private static readonly Cell.TranslationMask CellMask = new(defaultOn: true)
{
Persistent = false,
Temporary = false,
@@ -21,26 +19,23 @@ public class CellOverride : ACopyOverride state,
IModContext context)
{
-
- Mutagen.Bethesda.Fallout4.Cell? newRecord;
-
-
+ IMajorRecord? newRecord;
+
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (Mutagen.Bethesda.Fallout4.Cell)Base.CellOverride.CopyCellAsOverride(state, context);
-
+ newRecord = Base.CellOverride.CopyCellAsOverride(state, context);
}
else
{
// Don't duplicate branches, as they will be added below
- newRecord = (Mutagen.Bethesda.Fallout4.Cell)Base.CellOverride.DuplicateCell(state, context, CellMask);
+ newRecord = Base.CellOverride.DuplicateCell(state, context, CellMask);
}
Base.CellOverride.CopySubRecords(state, context, newRecord);
}
-
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/Fallout4Specifications.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/Fallout4Specifications.cs
similarity index 76%
rename from MutagenMerger.Lib/DI/GameSpecifications/Fallout4/Fallout4Specifications.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/Fallout4Specifications.cs
index a799f58..eb66fb8 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/Fallout4Specifications.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/Fallout4Specifications.cs
@@ -1,6 +1,6 @@
using Mutagen.Bethesda.Fallout4;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Fallout4;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Fallout4;
public class Fallout4Specifications : IGameSpecifications
{
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/QuestOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/QuestOverride.cs
similarity index 79%
rename from MutagenMerger.Lib/DI/GameSpecifications/Fallout4/QuestOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/QuestOverride.cs
index c28cc06..cb16af7 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/QuestOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/QuestOverride.cs
@@ -1,33 +1,30 @@
-using System;
-using Mutagen.Bethesda;
+using Mutagen.Bethesda.Fallout4;
using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
-using Mutagen.Bethesda.Fallout4;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Fallout4;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Fallout4;
public class QuestOverride : ACopyOverride
{
- public static readonly Quest.TranslationMask QuestMask = new Quest.TranslationMask(defaultOn: true)
+ private static readonly Quest.TranslationMask QuestMask = new(defaultOn: true)
{
DialogTopics = false
};
- public static readonly DialogTopic.TranslationMask DialogTopicMask = new DialogTopic.TranslationMask(defaultOn: true)
+
+ private static readonly DialogTopic.TranslationMask DialogTopicMask = new(defaultOn: true)
{
Responses = false
};
+
public override void HandleCopyFor(
MergeState state,
IModContext context)
{
-
IQuest? newRecord;
-
-
+
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
newRecord = context.GetOrAddAsOverride(state.OutgoingMod);
-
}
else
{
@@ -50,20 +47,18 @@ public override void HandleCopyFor(
{
Base.DialogTopicOverride.CopyDialogResponses(state, context.ModKey, newTopic, response);
}
-
}
foreach (var branch in context.Record.DialogBranches)
{
-
DialogBranch newBranch;
if (state.IsOverride(branch.FormKey, context.ModKey))
{
- newBranch = (DialogBranch)branch.DeepCopy();
+ newBranch = branch.DeepCopy();
}
else
{
- newBranch = (DialogBranch)branch.Duplicate(state.GetFormKey(branch.FormKey));
+ newBranch = branch.Duplicate(state.GetFormKey(branch.FormKey));
}
state.Mapping.Add(branch.FormKey, newBranch.FormKey);
@@ -72,5 +67,4 @@ public override void HandleCopyFor(
Console.WriteLine(" Deep Copying [" + branch.FormKey.ModKey.Name + "] " + branch.FormKey.IDString() + " to [" + newBranch.FormKey.ModKey.Name + "] " + newBranch.FormKey.IDString());
}
}
-
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/WorldspaceOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/WorldspaceOverride.cs
similarity index 74%
rename from MutagenMerger.Lib/DI/GameSpecifications/Fallout4/WorldspaceOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/WorldspaceOverride.cs
index 7d234aa..917976f 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Fallout4/WorldspaceOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Fallout4/WorldspaceOverride.cs
@@ -1,12 +1,11 @@
-using System;
+using Mutagen.Bethesda.Fallout4;
using Mutagen.Bethesda.Plugins.Cache;
-using Mutagen.Bethesda.Fallout4;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Fallout4;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Fallout4;
public class WorldspaceOverride : ACopyOverride
{
- private static readonly Worldspace.TranslationMask WorldspaceMask = new Mutagen.Bethesda.Fallout4.Worldspace.TranslationMask(defaultOn: true)
+ private static readonly Worldspace.TranslationMask WorldspaceMask = new(defaultOn: true)
{
SubCells = false,
TopCell = false,
@@ -20,10 +19,9 @@ public override void HandleCopyFor(
MergeState state,
IModContext context)
{
- Mutagen.Bethesda.Fallout4.Worldspace newRecord;
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (Mutagen.Bethesda.Fallout4.Worldspace)context.GetOrAddAsOverride(state.OutgoingMod);
+ var newRecord = (Mutagen.Bethesda.Fallout4.Worldspace)context.GetOrAddAsOverride(state.OutgoingMod);
// Readd branches below
newRecord.LargeReferences.Clear();
newRecord.SubCells.Clear();
@@ -34,7 +32,7 @@ public override void HandleCopyFor(
else
{
// Don't duplicate branches, as they will be added below
- newRecord = (Mutagen.Bethesda.Fallout4.Worldspace)context.Record.Duplicate(state.GetFormKey(context.Record.FormKey));
+ var newRecord = (Mutagen.Bethesda.Fallout4.Worldspace)context.Record.Duplicate(state.GetFormKey(context.Record.FormKey));
state.OutgoingMod.Worldspaces.Add(newRecord);
@@ -42,8 +40,5 @@ public override void HandleCopyFor(
Console.WriteLine(" Deep Copying [" + context.Record.FormKey.ModKey.Name + "] " + context.Record.FormKey.IDString() + " to [" + newRecord.FormKey.ModKey.Name + "] " + newRecord.FormKey.IDString());
}
-
-
-
- }
+ }
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/ICopyOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/ICopyOverride.cs
similarity index 85%
rename from MutagenMerger.Lib/DI/GameSpecifications/ICopyOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/ICopyOverride.cs
index 1cf9ea9..04d9e39 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/ICopyOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/ICopyOverride.cs
@@ -1,15 +1,14 @@
using Loqui;
-using Mutagen.Bethesda;
using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
-namespace MutagenMerger.Lib.DI.GameSpecifications;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications;
public interface ICopyOverride
where TModGetter : class, IModGetter, IContextGetterMod
where TMod : class, IMod, IContextMod, TModGetter
{
- ObjectKey ObjectKey { get; }
+ Type ClassType { get; }
public void HandleCopyFor(
MergeState state,
IModContext context);
@@ -21,7 +20,7 @@ public abstract class ACopyOverride state,
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/IGameSpecifications.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/IGameSpecifications.cs
similarity index 89%
rename from MutagenMerger.Lib/DI/GameSpecifications/IGameSpecifications.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/IGameSpecifications.cs
index dd56e85..8f07bc5 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/IGameSpecifications.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/IGameSpecifications.cs
@@ -1,6 +1,6 @@
using Mutagen.Bethesda.Plugins.Records;
-namespace MutagenMerger.Lib.DI.GameSpecifications;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications;
public interface IGameSpecifications
where TModGetter : class, IModGetter, IContextGetterMod
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/CellOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/CellOverride.cs
similarity index 62%
rename from MutagenMerger.Lib/DI/GameSpecifications/Oblivion/CellOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/CellOverride.cs
index c9de5f3..1721c82 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/CellOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/CellOverride.cs
@@ -1,14 +1,12 @@
-using System;
-using Mutagen.Bethesda;
+using Mutagen.Bethesda.Oblivion;
using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
-using Mutagen.Bethesda.Oblivion;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Oblivion;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Oblivion;
public class CellOverride : ACopyOverride
{
- public static readonly Cell.TranslationMask CellMask = new Mutagen.Bethesda.Oblivion.Cell.TranslationMask(defaultOn: true)
+ public static readonly Cell.TranslationMask CellMask = new(defaultOn: true)
{
Persistent = false,
Temporary = false,
@@ -19,26 +17,23 @@ public class CellOverride : ACopyOverride state,
IModContext context)
{
-
- Mutagen.Bethesda.Oblivion.Cell? newRecord;
-
+ IMajorRecord? newRecord;
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (Mutagen.Bethesda.Oblivion.Cell)Base.CellOverride.CopyCellAsOverride(state, context);
-
+ newRecord = Base.CellOverride.CopyCellAsOverride(state, context);
}
else
{
// Don't duplicate branches, as they will be added below
- newRecord = (Mutagen.Bethesda.Oblivion.Cell)Base.CellOverride.DuplicateCell(state, context, CellMask);
+ newRecord = Base.CellOverride.DuplicateCell(state, context, CellMask);
}
Base.CellOverride.CopySubRecords(state, context, newRecord);
}
-
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/DialogTopicOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/DialogTopicOverride.cs
similarity index 84%
rename from MutagenMerger.Lib/DI/GameSpecifications/Oblivion/DialogTopicOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/DialogTopicOverride.cs
index d064064..eb7165c 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/DialogTopicOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/DialogTopicOverride.cs
@@ -1,22 +1,22 @@
-using System;
+using Mutagen.Bethesda.Oblivion;
using Mutagen.Bethesda.Plugins;
using Mutagen.Bethesda.Plugins.Cache;
-using Mutagen.Bethesda.Oblivion;
using Mutagen.Bethesda.Plugins.Records;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Oblivion;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Oblivion;
public class DialogTopicOverride : ACopyOverride
{
- private static readonly DialogTopic.TranslationMask DialogTopicMask = new Mutagen.Bethesda.Oblivion.DialogTopic.TranslationMask(defaultOn: true)
+ private static readonly DialogTopic.TranslationMask DialogTopicMask = new(defaultOn: true)
{
Items = false
};
+
public override void HandleCopyFor(
MergeState state,
IModContext context)
{
- Mutagen.Bethesda.Oblivion.DialogTopic newRecord;
+ DialogTopic newRecord;
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
newRecord = (DialogTopic)Base.DialogTopicOverride.CopyDialogTopicAsOverride(state, (IMajorRecordGetter)context.Record);
@@ -32,15 +32,14 @@ public override void HandleCopyFor(
CopyDialogItem(state, context.ModKey, newRecord, branch);
}
}
-
-
+
private void CopyDialogItem(
MergeState state,
ModKey currentMod,
- Mutagen.Bethesda.Oblivion.DialogTopic topic,
+ DialogTopic topic,
IDialogItemGetter item)
{
- Mutagen.Bethesda.Oblivion.DialogItem newRecord;
+ DialogItem newRecord;
if (state.IsOverride(item.FormKey, currentMod))
{
newRecord = item.DeepCopy();
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/OblivionSpecifications.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/OblivionSpecifications.cs
similarity index 76%
rename from MutagenMerger.Lib/DI/GameSpecifications/Oblivion/OblivionSpecifications.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/OblivionSpecifications.cs
index 10b618d..2d21b1e 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/OblivionSpecifications.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/OblivionSpecifications.cs
@@ -1,6 +1,6 @@
using Mutagen.Bethesda.Oblivion;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Oblivion;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Oblivion;
public class OblivionSpecifications : IGameSpecifications
{
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/WorldspaceOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/WorldspaceOverride.cs
similarity index 80%
rename from MutagenMerger.Lib/DI/GameSpecifications/Oblivion/WorldspaceOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/WorldspaceOverride.cs
index 29cab00..81cb205 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Oblivion/WorldspaceOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Oblivion/WorldspaceOverride.cs
@@ -1,12 +1,11 @@
-using System;
+using Mutagen.Bethesda.Oblivion;
using Mutagen.Bethesda.Plugins.Cache;
-using Mutagen.Bethesda.Oblivion;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Oblivion;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Oblivion;
public class WorldspaceOverride : ACopyOverride
{
- private static readonly Worldspace.TranslationMask WorldspaceMask = new Mutagen.Bethesda.Oblivion.Worldspace.TranslationMask(defaultOn: true)
+ private static readonly Worldspace.TranslationMask WorldspaceMask = new(defaultOn: true)
{
SubCells = false,
TopCell = false,
@@ -18,10 +17,10 @@ public override void HandleCopyFor(
MergeState state,
IModContext context)
{
- Mutagen.Bethesda.Oblivion.Worldspace newRecord;
+ Worldspace newRecord;
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (Mutagen.Bethesda.Oblivion.Worldspace)context.GetOrAddAsOverride(state.OutgoingMod);
+ newRecord = (Worldspace)context.GetOrAddAsOverride(state.OutgoingMod);
// Readd branches below
newRecord.SubCells.Clear();
newRecord.TopCell?.Clear();
@@ -39,8 +38,5 @@ public override void HandleCopyFor(
Console.WriteLine(" Deep Copying [" + context.Record.FormKey.ModKey.Name + "] " + context.Record.FormKey.IDString() + " to [" + newRecord.FormKey.ModKey.Name + "] " + newRecord.FormKey.IDString());
}
-
-
-
- }
+ }
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/CellOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/CellOverride.cs
similarity index 64%
rename from MutagenMerger.Lib/DI/GameSpecifications/Skyrim/CellOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/CellOverride.cs
index e88491d..b491d3d 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/CellOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/CellOverride.cs
@@ -1,14 +1,12 @@
-using System;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Plugins.Cache;
+using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
using Mutagen.Bethesda.Skyrim;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Skyrim;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Skyrim;
public class CellOverride : ACopyOverride
{
- public static readonly Cell.TranslationMask CellMask = new Mutagen.Bethesda.Skyrim.Cell.TranslationMask(defaultOn: true)
+ public static readonly Cell.TranslationMask CellMask = new(defaultOn: true)
{
Persistent = false,
Temporary = false,
@@ -21,26 +19,23 @@ public class CellOverride : ACopyOverride state,
IModContext context)
{
-
- Mutagen.Bethesda.Skyrim.Cell? newRecord;
-
-
+ IMajorRecord? newRecord;
+
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (Mutagen.Bethesda.Skyrim.Cell)Base.CellOverride.CopyCellAsOverride(state, context);
-
+ newRecord = Base.CellOverride.CopyCellAsOverride(state, context);
}
else
{
// Don't duplicate branches, as they will be added below
- newRecord = (Mutagen.Bethesda.Skyrim.Cell)Base.CellOverride.DuplicateCell(state, context, CellMask);
+ newRecord = Base.CellOverride.DuplicateCell(state, context, CellMask);
}
Base.CellOverride.CopySubRecords(state, context, newRecord);
}
-
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/DialogTopicOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/DialogTopicOverride.cs
similarity index 59%
rename from MutagenMerger.Lib/DI/GameSpecifications/Skyrim/DialogTopicOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/DialogTopicOverride.cs
index d408960..70676b6 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/DialogTopicOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/DialogTopicOverride.cs
@@ -1,14 +1,12 @@
-using System;
-using Mutagen.Bethesda.Plugins;
-using Mutagen.Bethesda.Plugins.Cache;
+using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Plugins.Records;
using Mutagen.Bethesda.Skyrim;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Skyrim;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Skyrim;
public class DialogTopicOverride : ACopyOverride
{
- private static readonly DialogTopic.TranslationMask DialogTopicMask = new Mutagen.Bethesda.Skyrim.DialogTopic.TranslationMask(defaultOn: true)
+ private static readonly DialogTopic.TranslationMask DialogTopicMask = new(defaultOn: true)
{
Responses = false
};
@@ -17,14 +15,14 @@ public override void HandleCopyFor(
MergeState state,
IModContext context)
{
- Mutagen.Bethesda.Skyrim.DialogTopic newRecord;
+ IMajorRecord newRecord;
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (DialogTopic)Base.DialogTopicOverride.CopyDialogTopicAsOverride(state, (IMajorRecordGetter)context.Record);
+ newRecord = Base.DialogTopicOverride.CopyDialogTopicAsOverride(state, (IMajorRecordGetter)context.Record);
}
else
{
- newRecord = (DialogTopic)Base.DialogTopicOverride.DuplicateDialogTopic(state, (IMajorRecordGetter)context.Record, DialogTopicMask);
+ newRecord = Base.DialogTopicOverride.DuplicateDialogTopic(state, (IMajorRecordGetter)context.Record, DialogTopicMask);
}
// Do the branches
@@ -33,5 +31,4 @@ public override void HandleCopyFor(
Base.DialogTopicOverride.CopyDialogResponses(state, context.ModKey, newRecord, response);
}
}
-
}
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/SkyrimSpecifications.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/SkyrimSpecifications.cs
similarity index 75%
rename from MutagenMerger.Lib/DI/GameSpecifications/Skyrim/SkyrimSpecifications.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/SkyrimSpecifications.cs
index 6cf3cb9..4687b72 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/SkyrimSpecifications.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/SkyrimSpecifications.cs
@@ -1,6 +1,6 @@
using Mutagen.Bethesda.Skyrim;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Skyrim;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Skyrim;
public class SkyrimSpecifications : IGameSpecifications
{
diff --git a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/WorldspaceOverride.cs b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/WorldspaceOverride.cs
similarity index 74%
rename from MutagenMerger.Lib/DI/GameSpecifications/Skyrim/WorldspaceOverride.cs
rename to Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/WorldspaceOverride.cs
index d9c9ad2..ead6516 100644
--- a/MutagenMerger.Lib/DI/GameSpecifications/Skyrim/WorldspaceOverride.cs
+++ b/Mutagen.Bethesda.Merge.Lib/DI/GameSpecifications/Skyrim/WorldspaceOverride.cs
@@ -1,12 +1,11 @@
-using System;
-using Mutagen.Bethesda.Plugins.Cache;
+using Mutagen.Bethesda.Plugins.Cache;
using Mutagen.Bethesda.Skyrim;
-namespace MutagenMerger.Lib.DI.GameSpecifications.Skyrim;
+namespace Mutagen.Bethesda.Merge.Lib.DI.GameSpecifications.Skyrim;
public class WorldspaceOverride : ACopyOverride
{
- private static readonly Worldspace.TranslationMask WorldspaceMask = new Mutagen.Bethesda.Skyrim.Worldspace.TranslationMask(defaultOn: true)
+ private static readonly Worldspace.TranslationMask WorldspaceMask = new(defaultOn: true)
{
SubCells = false,
TopCell = false,
@@ -20,10 +19,9 @@ public override void HandleCopyFor(
MergeState state,
IModContext context)
{
- Mutagen.Bethesda.Skyrim.Worldspace newRecord;
if (state.IsOverride(context.Record.FormKey, context.ModKey))
{
- newRecord = (Mutagen.Bethesda.Skyrim.Worldspace)context.GetOrAddAsOverride(state.OutgoingMod);
+ var newRecord = context.GetOrAddAsOverride(state.OutgoingMod);
// Readd branches below
newRecord.LargeReferences.Clear();
newRecord.SubCells.Clear();
@@ -34,7 +32,7 @@ public override void HandleCopyFor(
else
{
// Don't duplicate branches, as they will be added below
- newRecord = (Mutagen.Bethesda.Skyrim.Worldspace)context.Record.Duplicate(state.GetFormKey(context.Record.FormKey), WorldspaceMask);
+ var newRecord = context.Record.Duplicate(state.GetFormKey(context.Record.FormKey), WorldspaceMask);
state.OutgoingMod.Worldspaces.Add(newRecord);
@@ -42,8 +40,5 @@ public override void HandleCopyFor(
Console.WriteLine(" Deep Copying [" + context.Record.FormKey.ModKey.Name + "] " + context.Record.FormKey.IDString() + " to [" + newRecord.FormKey.ModKey.Name + "] " + newRecord.FormKey.IDString());
}
-
-
-
- }
+ }
}
diff --git a/Mutagen.Bethesda.Merge.Lib/DI/Merger.cs b/Mutagen.Bethesda.Merge.Lib/DI/Merger.cs
new file mode 100644
index 0000000..3e55907
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.Lib/DI/Merger.cs
@@ -0,0 +1,206 @@
+using System.IO.Abstractions;
+using System.Json;
+using System.Security.Cryptography;
+using Mutagen.Bethesda.Environments;
+using Mutagen.Bethesda.Environments.DI;
+using Mutagen.Bethesda.Plugins;
+using Mutagen.Bethesda.Plugins.Order.DI;
+using Mutagen.Bethesda.Plugins.Records;
+using Noggog;
+
+namespace Mutagen.Bethesda.Merge.Lib.DI;
+
+public interface IMerger
+{
+ void Merge(
+ IEnumerable modsToMerge,
+ ModKey outputKey,
+ DirectoryPath outputFolder);
+}
+
+public sealed class Merger : IMerger
+ where TMod : class, TModGetter, IMod, IContextMod
+ where TModGetter : class, IModGetter, IContextGetterMod
+ where TMajorRecord : class, TMajorRecordGetter, IMajorRecord
+ where TMajorRecordGetter : class, IMajorRecordGetter
+{
+ private readonly IFileSystem _fileSystem;
+ private readonly IDataDirectoryProvider _dataDirectoryProvider;
+ private readonly IGameReleaseContext _gameReleaseContext;
+ private readonly ILoadOrderListingsProvider _loadOrderListingsProvider;
+ private readonly AssetMerge.Factory _assetMergeFactory;
+ private readonly CopyRecordProcessor _copyRecordProcessor;
+ private readonly MD5 _md5;
+
+ public Merger(
+ IFileSystem fileSystem,
+ IDataDirectoryProvider dataDirectoryProvider,
+ IGameReleaseContext gameReleaseContext,
+ ILoadOrderListingsProvider loadOrderListingsProvider,
+ AssetMerge.Factory assetMergeFactory,
+ CopyRecordProcessor copyRecordProcessor)
+ {
+ _fileSystem = fileSystem;
+ _dataDirectoryProvider = dataDirectoryProvider;
+ _gameReleaseContext = gameReleaseContext;
+ _loadOrderListingsProvider = loadOrderListingsProvider;
+ _assetMergeFactory = assetMergeFactory;
+ _copyRecordProcessor = copyRecordProcessor;
+ _md5 = MD5.Create();
+ }
+
+ public void Merge(
+ IEnumerable modsToMerge,
+ ModKey outputKey,
+ DirectoryPath outputFolder)
+ {
+ var outputMod = ModInstantiator.Activator(outputKey, _gameReleaseContext.Release);
+ var env = GameEnvironmentBuilder
+ .Create(_gameReleaseContext.Release)
+ .WithTargetDataFolder(_dataDirectoryProvider.Path)
+ .WithOutputMod(outputMod)
+ .WithLoadOrder(_loadOrderListingsProvider.Get()
+ .Where(x => x.Enabled)
+ .Select(x => x.ModKey)
+ .ToArray())
+ .WithFileSystem(_fileSystem)
+ .Build();
+ var mods = env.LoadOrder.PriorityOrder.ResolveAllModsExist().ToArray();
+ var mergingMods = mods.Where(x => modsToMerge.Contains(x.ModKey)).ToArray();
+
+ var outputFile = Path.Combine(outputFolder, outputKey.FileName);
+ // env.LoadOrder.ForEach(x => Console.WriteLine(x.Value.ModKey));
+
+ Console.WriteLine("Merging " + String.Join(", ",mergingMods.Select(x => x.ModKey.FileName.String)) + " into " + Path.GetFileName(outputFile));
+ Console.WriteLine();
+
+ var modsToMergeSet = modsToMerge.ToHashSet();
+
+ var linkCache = mergingMods.ToImmutableLinkCache();
+
+ var state = new MergeState(
+ _gameReleaseContext.Release,
+ mods,
+ modsToMergeSet,
+ outputMod,
+ OutputPath: outputFile,
+ DataPath: _dataDirectoryProvider.Path,
+ LinkCache: linkCache,
+ env: env);
+
+ _copyRecordProcessor.CopyRecords(state);
+ // state.Mapping.ForEach(x => Console.WriteLine(x.Key.ToString() + " " + x.Value.ToString()));
+ state.OutgoingMod.RemapLinks(state.Mapping);
+
+ _fileSystem.Directory.CreateDirectory(state.OutputPath.Directory ?? "");
+ state.OutgoingMod.BeginWrite
+ .ToPath(state.OutputPath)
+ .WithLoadOrder(env.LoadOrder.Keys)
+ .WithDataFolder(env.DataFolderPath)
+ .WithFileSystem(_fileSystem)
+ .Write();
+
+ // foreach (var rec in state.OutgoingMod.EnumerateMajorRecords())
+ // {
+ // foreach (var link in rec.EnumerateFormLinks())
+ // {
+ // IMajorRecordGetter? majorRecord = null;
+ // link.TryResolveCommon(state.LinkCache, out majorRecord);
+ // Console.WriteLine(majorRecord?.ToString() + ":" +link.FormKey);
+ // }
+ // }
+
+ HandleAssets(state);
+
+ HandleScripts(state);
+
+ MergeJson(state);
+ }
+
+ private void MergeJson(MergeState state)
+ {
+ var outputDir = Path.GetDirectoryName(state.OutputPath) ?? "";
+ var mergeName = Path.GetFileNameWithoutExtension(state.OutputPath);
+ var mergePlugin = state.OutgoingMod.ModKey.FileName;
+ var mergeDir = Path.Combine(outputDir, "merge - " + mergeName);
+ if (!_fileSystem.Directory.Exists(mergeDir))
+ {
+ _fileSystem.Directory.CreateDirectory(mergeDir);
+ }
+
+ JsonObject? _mergeJson = new()
+ {
+ { "name", new JsonPrimitive(mergeName) },
+ { "filename", new JsonPrimitive(mergePlugin)},
+ { "method", new JsonPrimitive("Mutagen.Bethesda.Merge")},
+ { "loadOrder", new JsonArray (
+ state.env.LoadOrder.PriorityOrder
+ .Select(x => x.ModKey.FileName)
+ .Select(x => new JsonPrimitive(x))
+ .Select(x => (JsonValue)x)
+ .ToArray()
+ )},
+ {"dateBuilt", new JsonPrimitive(DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.zzZ"))},
+ {"plugins", new JsonArray (
+ state.ModsToMerge
+ .Select(x =>
+ {
+ return new JsonObject
+ {
+ { "filename", new JsonPrimitive(x.FileName) },
+ { "hash", new JsonPrimitive(BitConverter.ToString(_md5.ComputeHash(_fileSystem.File.ReadAllBytes(Path.Combine(state.env.DataFolderPath,x.FileName)))).Replace("-", "").ToLowerInvariant()) },
+ { "dataFolder", new JsonPrimitive(state.env.DataFolderPath)}
+ };
+ })
+ .Select(x => (JsonValue)x)
+ .ToArray()
+ )}
+ };
+
+ _fileSystem.File.WriteAllText(Path.Combine(mergeDir, "merge.json"), _mergeJson.ToString());
+
+ JsonObject? mapJson = new(
+ state.ModsToMerge.Select(
+ x => new KeyValuePair(
+ x.FileName,
+ new JsonObject(state.Mapping.Where(y => y.Key.ModKey.Equals(x) && y.Key.ID != y.Value.ID)
+ .Select(
+ y => new KeyValuePair(
+ y.Key.IDString(),
+ new JsonPrimitive(y.Value.IDString())
+ )
+ ).ToArray()))
+ )
+ );
+
+ _fileSystem.File.WriteAllText(Path.Combine(mergeDir, "map.json"), mapJson.ToString());
+
+ JsonObject? fidJson = new(
+ state.ModsToMerge.Select(
+ x => new KeyValuePair(
+ x.FileName,
+ new JsonArray(
+ state.Mapping
+ .Where(y => y.Key.ModKey.Equals(x))
+ .Select(y => new JsonPrimitive(y.Value.IDString()))
+ .Select(x => (JsonValue)x)
+ .ToArray()))
+ )
+ );
+
+ _fileSystem.File.WriteAllText(Path.Combine(mergeDir, "fidCache.json"), fidJson.ToString());
+
+ }
+
+ private void HandleScripts(
+ MergeState mergeState)
+ {
+ // Console.WriteLine("HandleScript");
+ }
+
+ private void HandleAssets(MergeState mergeState)
+ {
+ var assetMerge = _assetMergeFactory(mergeState);
+ assetMerge.Handle();
+ }
+}
diff --git a/MutagenMerger.Lib/MergeState.cs b/Mutagen.Bethesda.Merge.Lib/MergeState.cs
similarity index 88%
rename from MutagenMerger.Lib/MergeState.cs
rename to Mutagen.Bethesda.Merge.Lib/MergeState.cs
index afd22b4..d3804fc 100644
--- a/MutagenMerger.Lib/MergeState.cs
+++ b/Mutagen.Bethesda.Merge.Lib/MergeState.cs
@@ -1,13 +1,10 @@
-using System.Collections.Generic;
-using System.Linq;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Environments;
+using Mutagen.Bethesda.Environments;
using Mutagen.Bethesda.Plugins;
using Mutagen.Bethesda.Plugins.Cache.Internals.Implementations;
using Mutagen.Bethesda.Plugins.Records;
using Noggog;
-namespace MutagenMerger.Lib;
+namespace Mutagen.Bethesda.Merge.Lib;
public record MergeState(
GameRelease Release,
@@ -36,5 +33,4 @@ public FormKey GetFormKey(FormKey key) {
return FormKey.Factory(key.IDString() + ":" + OutgoingMod.ModKey.FileName);
}
}
-
}
diff --git a/MutagenMerger.CLI/Container/MainModule.cs b/Mutagen.Bethesda.Merge.Lib/MergerModule.cs
similarity index 59%
rename from MutagenMerger.CLI/Container/MainModule.cs
rename to Mutagen.Bethesda.Merge.Lib/MergerModule.cs
index 9642b07..c717fc5 100644
--- a/MutagenMerger.CLI/Container/MainModule.cs
+++ b/Mutagen.Bethesda.Merge.Lib/MergerModule.cs
@@ -1,16 +1,16 @@
using Autofac;
-using MutagenMerger.Lib.DI;
-using MutagenMerger.Lib.DI.GameSpecifications.Fallout4;
-using MutagenMerger.Lib.DI.GameSpecifications.Oblivion;
-using MutagenMerger.Lib.DI.GameSpecifications.Skyrim;
+using Mutagen.Bethesda.Merge.Lib.DI;
using Noggog.Autofac;
+using Noggog.Autofac.Modules;
-namespace MutagenMerger.CLI.Container;
+namespace Mutagen.Bethesda.Merge.Lib;
-public class MainModule : Autofac.Module
+public class MergerModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
+ builder.RegisterModule();
+ builder.RegisterGeneric(typeof(AssetMerge<,,,>)).AsSelf();
builder.RegisterGeneric(typeof(Merger<,,,>)).AsSelf();
builder.RegisterGeneric(typeof(CopyRecordProcessor<,>)).AsSelf();
builder.RegisterAssemblyTypes(typeof(IMerger).Assembly)
diff --git a/Mutagen.Bethesda.Merge.Lib/Mutagen.Bethesda.Merge.Lib.csproj b/Mutagen.Bethesda.Merge.Lib/Mutagen.Bethesda.Merge.Lib.csproj
new file mode 100644
index 0000000..cb43b99
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.Lib/Mutagen.Bethesda.Merge.Lib.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net9.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mutagen.Bethesda.Merge.Tests/MergerTests.cs b/Mutagen.Bethesda.Merge.Tests/MergerTests.cs
new file mode 100644
index 0000000..5712e7b
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.Tests/MergerTests.cs
@@ -0,0 +1,134 @@
+using System.IO.Abstractions;
+using Autofac;
+using Mutagen.Bethesda.Merge.Lib;
+using Mutagen.Bethesda.Merge.Lib.DI;
+using Mutagen.Bethesda.Plugins;
+using Mutagen.Bethesda.Skyrim;
+using Mutagen.Bethesda.Testing.AutoData;
+using Mutagen.Bethesda.Testing.Fakes;
+using Noggog.Testing.Extensions;
+using Shouldly;
+using Xunit;
+
+namespace Mutagen.Bethesda.Merge.Tests;
+
+public class MergerTests
+{
+ public MergerTests()
+ {
+ Warmup.Init();
+ }
+
+ public class TestModule : Module
+ {
+ protected override void Load(ContainerBuilder builder)
+ {
+ builder.RegisterModule();
+ builder.RegisterType().AsSelf();
+ }
+ }
+
+ [Theory, MutagenContainerAutoData(GameRelease.SkyrimSE)]
+ public void TestMerging(
+ IFileSystem fileSystem,
+ ManualLoadOrderProvider loadOrderProvider,
+ ManualDataDirectoryProvider dataDirectory,
+ MutagenTestHelpers testHelpers,
+ ModKey modKey1,
+ ModKey modKey2,
+ ModKey modKey3,
+ string editorId1,
+ string editorId2,
+ string editorId3,
+ string editorId4,
+ string modifiedEditorId,
+ ModKey outputModKey,
+ Merger sut)
+ {
+ var mod1 = testHelpers.CreateDummyPlugin(modKey1, mod =>
+ {
+ mod.Actions.AddNew(editorId1);
+ mod.Actions.AddNew(editorId2);
+ });
+
+ var mod2 = testHelpers.CreateDummyPlugin(modKey2, mod =>
+ {
+ mod.Actions.AddNew(editorId3);
+ mod.Actions.AddNew(editorId4);
+ });
+
+ var mods = new List
+ {
+ mod1,
+ mod2,
+ };
+
+ using (var testMod1 = SkyrimMod.Create(SkyrimRelease.SkyrimSE)
+ .FromPath(Path.Combine(dataDirectory.Path, mod1))
+ .WithFileSystem(fileSystem)
+ .Construct())
+ {
+ var action1 = testMod1.Actions.First();
+
+ var mod3 = testHelpers.CreateDummyPlugin(modKey3, mod =>
+ {
+ var copy = action1.DeepCopy();
+ copy.EditorID = modifiedEditorId;
+ mod.Actions.Add(copy);
+ });
+
+ mods.Add(mod3);
+ }
+
+ loadOrderProvider.SetTo(mods.ToArray());
+
+ sut.Merge(
+ modsToMerge: mods,
+ outputKey: outputModKey,
+ outputFolder: dataDirectory.Path);
+
+ var outputFile = Path.Combine(dataDirectory.Path, outputModKey.FileName);
+ testHelpers.TestPlugin(outputFile, mod =>
+ {
+ mod.Actions.Count.ShouldEqual(4);
+
+ mod.Actions.ShouldContain(x => x.EditorID == modifiedEditorId);
+ mod.Actions.ShouldContain(x => x.EditorID == editorId2);
+ mod.Actions.ShouldContain(x => x.EditorID == editorId3);
+ mod.Actions.ShouldContain(x => x.EditorID == editorId4);
+ });
+ }
+
+ [Fact]
+ public void TestBasePluginsMerging()
+ {
+ //requires manual activation
+ const string folder = "base-plugins";
+ if (!Directory.Exists(folder)) return;
+
+ var mods = new List
+ {
+ "Skyrim.esm",
+ "Update.esm",
+ "Dawnguard.esm",
+ "HearthFires.esm",
+ "Dragonborn.esm"
+ };
+
+ if (!mods.All(x => File.Exists(Path.Combine(folder, x.FileName))))
+ return;
+
+ const string outputMod = "output.esp";
+ var outputPath = Path.Combine(folder, outputMod);
+ if (File.Exists(outputPath))
+ File.Delete(outputPath);
+
+ throw new NotImplementedException();
+ // using (var merger = new Merger(folder, mods, mods, outputMod, folder, GameRelease.SkyrimSE))
+ // {
+ // merger.Merge();
+ // }
+
+ Assert.True(File.Exists(outputPath));
+ }
+}
diff --git a/MutagenMerger.Tests/MutagenMerger.Tests.csproj b/Mutagen.Bethesda.Merge.Tests/Mutagen.Bethesda.Merge.Tests.csproj
similarity index 59%
rename from MutagenMerger.Tests/MutagenMerger.Tests.csproj
rename to Mutagen.Bethesda.Merge.Tests/Mutagen.Bethesda.Merge.Tests.csproj
index ce3b8fd..48367df 100644
--- a/MutagenMerger.Tests/MutagenMerger.Tests.csproj
+++ b/Mutagen.Bethesda.Merge.Tests/Mutagen.Bethesda.Merge.Tests.csproj
@@ -1,25 +1,27 @@
- net6.0;net7.0
+ net9.0
false
-
-
-
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/Mutagen.Bethesda.Merge.Tests/MutagenTestHelpers.cs b/Mutagen.Bethesda.Merge.Tests/MutagenTestHelpers.cs
new file mode 100644
index 0000000..f0ffeef
--- /dev/null
+++ b/Mutagen.Bethesda.Merge.Tests/MutagenTestHelpers.cs
@@ -0,0 +1,48 @@
+using System.IO.Abstractions;
+using Mutagen.Bethesda.Environments.DI;
+using Mutagen.Bethesda.Plugins;
+using Mutagen.Bethesda.Skyrim;
+using Shouldly;
+
+namespace Mutagen.Bethesda.Merge.Tests;
+
+public class MutagenTestHelpers
+{
+ private readonly IFileSystem _fileSystem;
+ private readonly IDataDirectoryProvider _dataDirectoryProvider;
+
+ public MutagenTestHelpers(
+ IFileSystem fileSystem,
+ IDataDirectoryProvider dataDirectoryProvider)
+ {
+ _fileSystem = fileSystem;
+ _dataDirectoryProvider = dataDirectoryProvider;
+ }
+
+ public ModPath CreateDummyPlugin(ModKey modName, Action addRecords)
+ {
+ var modPath = ModPath.FromPath(Path.Combine(_dataDirectoryProvider.Path, modName.FileName));
+ modPath.Path.Directory?.Create(_fileSystem);
+
+ var mod = new SkyrimMod(modPath.ModKey, SkyrimRelease.SkyrimSE);
+ addRecords(mod);
+ mod.BeginWrite
+ .ToPath(modPath.Path)
+ .WithNoLoadOrder()
+ .WithFileSystem(_fileSystem)
+ .Write();
+
+ return modPath;
+ }
+
+ public void TestPlugin(ModPath path, Action verify)
+ {
+ _fileSystem.File.Exists(path).ShouldBeTrue();
+
+ using var mod = SkyrimMod.Create(SkyrimRelease.SkyrimSE)
+ .FromPath(path)
+ .WithFileSystem(_fileSystem)
+ .Construct();
+ verify(mod);
+ }
+}
diff --git a/MutagenMerger.sln b/Mutagen.Bethesda.Merge.sln
similarity index 70%
rename from MutagenMerger.sln
rename to Mutagen.Bethesda.Merge.sln
index f6e6e09..4c92e40 100644
--- a/MutagenMerger.sln
+++ b/Mutagen.Bethesda.Merge.sln
@@ -1,17 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MutagenMerger.CLI", "MutagenMerger.CLI\MutagenMerger.CLI.csproj", "{7AA24461-4268-4136-8AA7-57AD53CA5047}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mutagen.Bethesda.Merge.CLI", "Mutagen.Bethesda.Merge.CLI\Mutagen.Bethesda.Merge.CLI.csproj", "{7AA24461-4268-4136-8AA7-57AD53CA5047}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MutagenMerger.Lib", "MutagenMerger.Lib\MutagenMerger.Lib.csproj", "{44BA9DBC-62D1-45FD-9D5F-5C5BA5B1026D}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mutagen.Bethesda.Merge.Lib", "Mutagen.Bethesda.Merge.Lib\Mutagen.Bethesda.Merge.Lib.csproj", "{44BA9DBC-62D1-45FD-9D5F-5C5BA5B1026D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A86B978E-588D-414D-8E68-6FC71A0673D7}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
+ Directory.Packages.props = Directory.Packages.props
+ Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MutagenMerger.Tests", "MutagenMerger.Tests\MutagenMerger.Tests.csproj", "{B5E60238-BEAF-4E63-8EDE-BE5A3443B64A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mutagen.Bethesda.Merge.Tests", "Mutagen.Bethesda.Merge.Tests\Mutagen.Bethesda.Merge.Tests.csproj", "{B5E60238-BEAF-4E63-8EDE-BE5A3443B64A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/MutagenMerger.sln.DotSettings b/Mutagen.Bethesda.Merge.sln.DotSettings
similarity index 100%
rename from MutagenMerger.sln.DotSettings
rename to Mutagen.Bethesda.Merge.sln.DotSettings
diff --git a/MutagenMerger.CLI/MutagenMerger.CLI.csproj b/MutagenMerger.CLI/MutagenMerger.CLI.csproj
deleted file mode 100644
index 9acc3ab..0000000
--- a/MutagenMerger.CLI/MutagenMerger.CLI.csproj
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- Exe
- net7.0
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MutagenMerger.CLI/Options.cs b/MutagenMerger.CLI/Options.cs
deleted file mode 100644
index 4b5eb97..0000000
--- a/MutagenMerger.CLI/Options.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using CommandLine;
-using Mutagen.Bethesda;
-
-namespace MutagenMerger.CLI
-{
- public class Options
- {
-
- [Option("game", HelpText = "Game to mod (Default SkyrimSE)")]
- public GameRelease Game { get; set; } = GameRelease.SkyrimSE;
-
- [Option("data", HelpText = "Path to the data folder", Required = true)]
- public string DataFolder { get; set; } = string.Empty;
-
- [Option("merge", Min = 1, Max = 4096, HelpText = "Plugins to merge")]
- public IEnumerable PluginsToMerge { get; set; } = Array.Empty();
-
- [Option("mergefile", HelpText = "Get plugins to merge from file")]
- public string PluginsMergeTxt { get; set; } = string.Empty;
-
- [Option("output", Required = true, HelpText = "Output merge folder")]
- public string Output { get; set; } = string.Empty;
-
- [Option("mergename", Required = true, HelpText = "Name of Merge")]
- public string MergeName { get; set; } = string.Empty;
- }
-}
diff --git a/MutagenMerger.CLI/Program.cs b/MutagenMerger.CLI/Program.cs
deleted file mode 100644
index f4eac83..0000000
--- a/MutagenMerger.CLI/Program.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Autofac;
-using CommandLine;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Fallout4;
-using Mutagen.Bethesda.Oblivion;
-using Mutagen.Bethesda.Plugins;
-using Mutagen.Bethesda.Skyrim;
-using MutagenMerger.CLI.Container;
-using MutagenMerger.Lib.DI;
-
-namespace MutagenMerger.CLI
-{
- public static class Program
- {
- public static async Task Main(string[] args)
- {
- await Parser.Default.ParseArguments(args)
- .WithParsedAsync(Run);
- }
-
- private static async Task Run(Options options)
- {
- var modsToMerge = options.PluginsMergeTxt != string.Empty
- ? (await File.ReadAllLinesAsync(options.PluginsMergeTxt))
- .Select(x => ModKey.FromNameAndExtension(x))
- .ToList()
- : options.PluginsToMerge
- .Select(x => ModKey.FromNameAndExtension(x))
- .ToList();
-
- if (Directory.Exists(options.Output)) Directory.Delete(options.Output, true);
-
- var sw = new Stopwatch();
- sw.Start();
-
- Type[] genericTypes;
- switch (options.Game.ToCategory())
- {
- case GameCategory.Oblivion:
- genericTypes = new Type[] { typeof(IOblivionModGetter), typeof(IOblivionMod), typeof(IOblivionMajorRecord), typeof(IOblivionMajorRecordGetter) };
- break;
- case GameCategory.Fallout4:
- genericTypes = new Type[] { typeof(IFallout4ModGetter), typeof(IFallout4Mod), typeof(IFallout4MajorRecord), typeof(IFallout4MajorRecordGetter) };
- break;
- case GameCategory.Skyrim:
- default:
- genericTypes = new Type[] { typeof(ISkyrimModGetter), typeof(ISkyrimMod), typeof(ISkyrimMajorRecord), typeof(ISkyrimMajorRecordGetter) };
- break;
- }
-
- ContainerBuilder builder = new();
- builder.RegisterModule();
- var container = builder.Build();
- var merger = container.Resolve(typeof(Merger<,,,>).MakeGenericType(genericTypes)) as IMerger;
-
- merger!.Merge(options.DataFolder, modsToMerge,
- ModKey.FromNameAndExtension(options.MergeName), options.Output, options.Game);
-
- Console.WriteLine($"Merged {modsToMerge.Count} plugins in {sw.ElapsedMilliseconds}ms");
- sw.Stop();
- }
- }
-}
diff --git a/MutagenMerger.Lib/Assets.cs b/MutagenMerger.Lib/Assets.cs
deleted file mode 100644
index 099f0b2..0000000
--- a/MutagenMerger.Lib/Assets.cs
+++ /dev/null
@@ -1,261 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.IO;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Plugins;
-using Mutagen.Bethesda.Plugins.Records;
-using Mutagen.Bethesda.Archives;
-using Microsoft.Extensions.FileSystemGlobbing;
-using Noggog;
-using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
-using Skyrim = Mutagen.Bethesda.Skyrim;
-using Fallout4 = Mutagen.Bethesda.Fallout4;
-using Oblivion = Mutagen.Bethesda.Oblivion;
-using Mutagen.Bethesda.Plugins.Masters;
-using System.Threading.Tasks;
-
-namespace MutagenMerger.Lib
-{
- public static class AssetMerge
- where TModGetter : class, IModGetter, IMajorRecordContextEnumerable, IMajorRecordGetterEnumerable, IContextGetterMod
- where TMod : class, IMod, IContextMod, TModGetter
- where TMajorRecord : class, IMajorRecord, TMajorRecordGetter
- where TMajorRecordGetter : class, IMajorRecordGetter
- {
- static MergeState _mergeState = null!;
- static string _outputDir = "";
- static string _mergeName = "";
- static string temp = GetTemporaryDirectory();
- public static List Rules
- {
- get
- {
- var rules = new List() {"**/*.@(esp|esm|bsa|ba2|bsl)", "meta.ini",
- "interface/translations/*.txt", "TES5Edit Backups/**/*",
- "fomod/**/*", "screenshot?(s)/**/*", "scripts/source/*.psc", "source/scripts/*.psc"};
-
- _mergeState.ModsToMerge.ForEach(x =>
- {
- rules.Add($"**/{x.Name.ToLower()}.seq");
- rules.Add($"**/{x.Name.ToLower()}.ini");
- rules.Add($"**/{x.Name.ToLower()}_DISTR.ini");
- rules.Add($"**/{x.Name.ToLower()}_ANIO.ini");
- rules.Add($"**/{x.Name.ToLower()}_SWAP.ini");
- rules.Add($"**/{x.Name.ToLower()}_KID.ini");
- rules.Add($"**/{x.FileName.String.ToLower()}/**/*");
- });
- return rules;
- }
- }
-
- public static void Handle(
- MergeState mergeState)
- {
- _mergeState = mergeState;
- _outputDir = Path.GetDirectoryName(mergeState.OutputPath) ?? "";
- _mergeName = Path.GetFileName(mergeState.OutputPath);
- var matcher = new Matcher();
- matcher.AddIncludePatterns(new string[] { "**/*" });
- matcher.AddExcludePatterns(Rules);
- Parallel.ForEach(_mergeState.ModsToMerge, mod => {
- var bsaPattern = mod.FileName.NameWithoutExtension + "*." + (_mergeState.Release == GameRelease.Fallout4 ? "b2a" : "bsa");
- string[] bsaFiles = Directory.GetFiles(_mergeState.DataPath, bsaPattern);
-
- // bsaFiles.ForEach(Console.WriteLine);
-
- foreach (string bsa in bsaFiles)
- {
- ExtractBSA(bsa);
- }
- // Console.WriteLine();
- });
-
- var matches = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(temp)));
-
- Parallel.ForEach(matches.Files, file => {
- Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(_outputDir, file.Path)) ?? "");
- Console.WriteLine(" Copying extracted asset \"" + file.Path + "\"");
- File.Copy(Path.Combine(temp, file.Path), Path.Combine(_outputDir, file.Path));
-
- });
-
- foreach (var mod in _mergeState.ModsToMerge)
- {
- CopyAssets(_mergeState.DataPath, mod);
- CopyAssets(temp, mod);
- }
-
- BuildSeqFile(_mergeState.DataPath, temp, _mergeState.OutgoingMod);
-
-
-
-
- Directory.Delete(temp, true);
- }
-
- private static void BuildSeqFile(string dataPath, string temp, TMod outputMod)
- {
- var formIds = GetSeqQuests(outputMod);
- if (formIds.Count == 0) return;
- var fileName = _mergeName.Substring(0, _mergeName.Length - 4) + ".seq";
- var filePath = Path.Combine(_outputDir, "seq", fileName);
- var buffer = new byte[formIds.Count * sizeof(UInt32)];
-
- for (int i = 0; i < formIds.Count; i++)
- {
- Buffer.BlockCopy(BitConverter.GetBytes(formIds[i]), 0, buffer, i * 4, 4);
- }
- if (!BitConverter.IsLittleEndian) Array.Reverse(buffer);
- Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? "");
- File.WriteAllBytes(filePath, buffer);
- Console.WriteLine();
- Console.WriteLine(" Created SEQ file: " + fileName);
-
- // if (!formIds.length) return;
- // let filename = fh.getFileBase(merge.filename) + '.seq',
- // filePath = `${ merge.dataPath}\\seq\\${ filename}`,
- // buffer = new Buffer(formIds.length * 4);
- // formIds.forEach((fid, n) => buffer.writeUInt32LE(fid, n * 4));
- // fh.jetpack.write(filePath, buffer);
- // progressLogger.log('Created SEQ file: ' + filePath);
- }
-
- private static List GetSeqQuests(TMod merge)
- {
- var masterColl = MasterReferenceCollection.FromPath(Path.Combine(_outputDir, _mergeName), _mergeState.Release);
-
- IGroup quests = _mergeState.Release switch
- {
- GameRelease.Oblivion => merge.GetTopLevelGroup(),
- GameRelease.Fallout4 => merge.GetTopLevelGroup(),
- _ => merge.GetTopLevelGroup(),
- };
-
- List formIds = new List();
-
- if (quests.Count == 0) return formIds;
-
- Parallel.ForEach(quests.Records, quest => {
- bool startGameEnabled = _mergeState.Release switch {
- GameRelease.Oblivion => ((Oblivion.Quest)quest).Data?.Flags.HasFlag(Oblivion.Quest.Flag.StartGameEnabled) ?? false,
- GameRelease.Fallout4 => ((Fallout4.Quest)quest).Data?.Flags.HasFlag(Fallout4.Quest.Flag.StartGameEnabled) ?? false,
- _ => ((Skyrim.Quest)quest).Flags.HasFlag(Skyrim.Quest.Flag.StartGameEnabled)
- };
- if (startGameEnabled) {
- var fid = masterColl.GetFormID(quest.FormKey).Raw;
- formIds.Add(fid);
- }
- });
-
- return formIds;
- }
-
- private static void CopyAssets(string path, ModKey mod)
- {
- CopyActorAssets(path, "textures/actors/character/facegendata/facetint", mod);
- CopyActorAssets(path, "meshes/actors/character/facegendata/facegeom", mod);
- CopyActorAssets(path, "sound/voice", mod);
-
- CopyTranslations(path, mod);
- }
-
- private static void ExtractBSA(string bsa)
- {
- Console.WriteLine();
-
- var reader = Archive.CreateReader(_mergeState.Release, bsa);
- var files = reader.Files.ToArray();
- Parallel.For(0,files.Count(), i => {
- var file = files[i];
- var filePath = file.Path.Replace("\\", "/").ToLower();
- Directory.CreateDirectory(Path.Combine(temp, Path.GetDirectoryName(filePath) ?? ""));
- Console.SetCursorPosition(0, Console.CursorTop);
- Console.Write(" Extracting Archive \"" + Path.GetFileName(bsa) + "\" " + ((decimal)i / files.Count()).ToString("0.00%"));
- File.WriteAllBytes(Path.Combine(temp, filePath), file.GetBytes());
- });
- Console.SetCursorPosition(0, Console.CursorTop);
- Console.Write(" Extracting Archive \"" + Path.GetFileName(bsa) + "\" 100.00%");
- }
-
- private static void CopyTranslations(string dir, ModKey mod)
- {
- var path = "interface/translations/";
- var srcPath = Path.Combine(dir, path);
- var dstPath = Path.Combine(_outputDir, path);
-
- if (!Directory.Exists(srcPath)) return;
-
- foreach (var file in Directory.GetFiles(srcPath,
- mod.Name.ToLower() + "_*.txt",
- new EnumerationOptions() { RecurseSubdirectories = true, MatchCasing = MatchCasing.CaseInsensitive }))
- {
- var language = Path.GetFileNameWithoutExtension(file).Replace(mod.Name.ToLower() + "_", "");
- var dst = dstPath + _mergeName.Substring(0, _mergeName.Length - 4) + "_" + language + ".txt";
- Directory.CreateDirectory(dstPath);
-
- var writer = File.AppendText(dst);
- writer.Write(File.ReadAllText(file));
- writer.Close();
-
- Console.WriteLine(" Appending " + mod.Name.ToLower() + "_" + "language to " + _mergeName.Substring(0, _mergeName.Length - 4) + "_" + language);
-
-
- };
- }
-
- private static void CopyActorAssets(string dir, string _path, ModKey mod)
- {
- var path = _path.Replace("\\", "/");
-
- var srcPath = Path.Combine(dir, path, mod.FileName.String.ToLower());
- var dstPath = Path.Combine(_outputDir, path, _mergeName);
- Directory.CreateDirectory(dstPath);
-
- if (!Directory.Exists(srcPath)) return;
-
- Console.WriteLine(" Copying assets from directory \"" + Path.Combine(path, mod.FileName.String.ToLower()) + "\"");
- Console.WriteLine(" Copying assets to directory \"" + Path.Combine(path, _mergeName) + "\"");
-
-
- _mergeState.Mapping.Where(x => x.Key.ModKey == mod).ForEach(x =>
- {
- var srcId = x.Key.ID;
- var srcIdString = x.Key.IDString().ToLower();
-
- foreach (var file in Directory.GetFiles(srcPath,
- "*" + srcIdString + "*",
- new EnumerationOptions() { RecurseSubdirectories = true, MatchCasing = MatchCasing.CaseInsensitive }))
- {
- if (_mergeState.Mapping.Select(x => x.Key.ModKey).Contains(mod) && _mergeState.Mapping.Select(x => x.Key.ID).Contains(srcId))
- {
- var newId = "00" + _mergeState.Mapping[new FormKey(mod, srcId)].IDString().ToLower();
- var dstFile = file.Replace(srcIdString, newId).Replace(srcPath, dstPath);
- Directory.CreateDirectory(Path.GetDirectoryName(dstFile) ?? "");
- Console.WriteLine(" Asset renumbered from " + srcIdString + " to " + newId);
- Console.WriteLine(" Copying asset \"" + file.Replace(srcPath + "/", "") + "\" to \"" + dstFile.Replace(dstPath + "/", "") + "\"");
- File.Copy(file, dstFile);
- }
- else
- {
- Directory.CreateDirectory(Path.GetDirectoryName(file.Replace(srcPath, dstPath)) ?? "");
- Console.WriteLine(" Asset not renumbered.");
- Console.WriteLine(" Copying asset \"" + file.Replace(srcPath + "/", "") + "\"");
- File.Copy(file, file.Replace(srcPath, dstPath));
-
- }
-
- }
-
-
- });
- }
-
- private static string GetTemporaryDirectory()
- {
- string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- Directory.CreateDirectory(tempDirectory);
- return tempDirectory;
- }
- }
-}
diff --git a/MutagenMerger.Lib/DI/Merger.cs b/MutagenMerger.Lib/DI/Merger.cs
deleted file mode 100644
index 41d86fe..0000000
--- a/MutagenMerger.Lib/DI/Merger.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Json;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Environments;
-using Mutagen.Bethesda.Plugins;
-using Mutagen.Bethesda.Plugins.Records;
-using MutagenMerger.Lib.DI.GameSpecifications;
-using Noggog;
-using System.Security.Cryptography;
-
-namespace MutagenMerger.Lib.DI
-{
- public interface IMerger
- {
- void Merge(
- DirectoryPath dataFolderPath,
- IEnumerable modsToMerge,
- ModKey outputKey,
- DirectoryPath outputFolder,
- GameRelease game);
- }
-
- public sealed class Merger : IMerger
- where TModGetter : class, IModGetter, IContextGetterMod
- where TMod : class, TModGetter, IMod, IContextMod
- where TMajorRecord : class, TMajorRecordGetter, IMajorRecord
- where TMajorRecordGetter : class, IMajorRecordGetter
- {
- private readonly IGameSpecifications _gameSpecs;
- private readonly CopyRecordProcessor _copyRecordProcessor;
-
- public Merger(
- IGameSpecifications gameSpecs,
- CopyRecordProcessor copyRecordProcessor)
- {
- _gameSpecs = gameSpecs;
- _copyRecordProcessor = copyRecordProcessor;
- }
-
- public void Merge(
- DirectoryPath dataFolderPath,
- IEnumerable modsToMerge,
- ModKey outputKey,
- DirectoryPath outputFolder,
- GameRelease game)
- {
- var outputMod = ModInstantiator.Activator(outputKey, game) as TMod ?? throw new Exception("Could not instantiate mod");
- var env = GameEnvironmentBuilder.Create(game).WithTargetDataFolder(dataFolderPath).WithOutputMod(outputMod).Build();
- var mods = env.LoadOrder.PriorityOrder.Resolve().ToArray();
- var mergingMods = mods.Where(x => modsToMerge.Contains(x.ModKey)).ToArray();
-
- var outputFile = Path.Combine(outputFolder, outputKey.FileName);
- // env.LoadOrder.ForEach(x => Console.WriteLine(x.Value.ModKey));
-
- Console.WriteLine("Merging " + String.Join(", ",mergingMods.Select(x => x.ModKey.FileName.String)) + " into " + Path.GetFileName(outputFile));
- Console.WriteLine();
-
- var modsToMergeSet = modsToMerge.ToHashSet();
-
- var linkCache = mergingMods.ToImmutableLinkCache();
-
- var state = new MergeState(
- game,
- mods,
- modsToMergeSet,
- outputMod,
- OutputPath: outputFile,
- DataPath: dataFolderPath,
- LinkCache: linkCache,
- env: env);
-
- _copyRecordProcessor.CopyRecords(state);
- // state.Mapping.ForEach(x => Console.WriteLine(x.Key.ToString() + " " + x.Value.ToString()));
- state.OutgoingMod.RemapLinks(state.Mapping);
-
- Directory.CreateDirectory(state.OutputPath.Directory ?? "");
- state.OutgoingMod.WriteToBinary(state.OutputPath, Utils.SafeBinaryWriteParameters(env.LoadOrder.Keys));
-
- // foreach (var rec in state.OutgoingMod.EnumerateMajorRecords())
- // {
- // foreach (var link in rec.EnumerateFormLinks())
- // {
- // IMajorRecordGetter? majorRecord = null;
- // link.TryResolveCommon(state.LinkCache, out majorRecord);
- // Console.WriteLine(majorRecord?.ToString() + ":" +link.FormKey);
- // }
- // }
-
- HandleAssets(state);
-
- HandleScripts(state);
-
- MergeJson(state);
- }
-
- private void MergeJson(MergeState state)
- {
- var _outputDir = Path.GetDirectoryName(state.OutputPath) ?? "";
- var _mergeName = Path.GetFileNameWithoutExtension(state.OutputPath);
- var _mergePlugin = state.OutgoingMod.ModKey.FileName;
- var _mergeDir = Path.Combine(_outputDir, "merge - " + _mergeName);
- if (!Directory.Exists(_mergeDir))
- {
- Directory.CreateDirectory(_mergeDir);
- }
-
- JsonObject? _mergeJson = new()
- {
- { "name", new JsonPrimitive(_mergeName) },
- { "filename", new JsonPrimitive(_mergePlugin)},
- { "method", new JsonPrimitive("Mutagen.Bethesda.Merge")},
- { "loadOrder", new JsonArray (
- state.env.LoadOrder.PriorityOrder.Resolve().Select(x => new JsonPrimitive(x.ModKey.FileName)).ToArray()
- )},
- {"dateBuilt", new JsonPrimitive(DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.zzZ"))},
- {"plugins", new JsonArray (
- state.ModsToMerge.Select(x => { MD5 md5 = MD5.Create(); return new JsonObject
- {
- { "filename", new JsonPrimitive(x.FileName) },
- { "hash", new JsonPrimitive(BitConverter.ToString(md5.ComputeHash(File.ReadAllBytes(Path.Combine(state.env.DataFolderPath,x.FileName)))).Replace("-", "").ToLowerInvariant()) },
- { "dataFolder", new JsonPrimitive(state.env.DataFolderPath)}
-
- }; }).ToArray()
- )}
- };
-
- File.WriteAllText(Path.Combine(_mergeDir, "merge.json"), _mergeJson.ToString());
-
- JsonObject? _mapJson = new(
- state.ModsToMerge.Select(
- x => new KeyValuePair(
- x.FileName,
- new JsonObject(state.Mapping.Where(y => y.Key.ModKey.Equals(x) && y.Key.ID != y.Value.ID)
- .Select(
- y => new KeyValuePair(
- y.Key.IDString(),
- new JsonPrimitive(y.Value.IDString())
- )
- ).ToArray()))
- )
- );
-
- File.WriteAllText(Path.Combine(_mergeDir, "map.json"), _mapJson.ToString());
-
-
-
- JsonObject? _fidJson = new(
- state.ModsToMerge.Select(
- x => new KeyValuePair(
- x.FileName,
- new JsonArray(state.Mapping.Where(y => y.Key.ModKey.Equals(x))
- .Select(
- y => new JsonPrimitive(y.Value.IDString())
- ).ToArray()))
- )
- );
-
- File.WriteAllText(Path.Combine(_mergeDir, "fidCache.json"), _fidJson.ToString());
-
- }
-
- private void HandleScripts(
- MergeState mergeState)
- {
- // Console.WriteLine("HandleScript");
- }
-
- private void HandleAssets(MergeState mergeState)
- {
- AssetMerge.Handle(mergeState);
- }
- }
-}
diff --git a/MutagenMerger.Lib/MutagenMerger.Lib.csproj b/MutagenMerger.Lib/MutagenMerger.Lib.csproj
deleted file mode 100644
index 98c3d47..0000000
--- a/MutagenMerger.Lib/MutagenMerger.Lib.csproj
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- net6.0;net7.0
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MutagenMerger.Lib/Utils.cs b/MutagenMerger.Lib/Utils.cs
deleted file mode 100644
index 6b71137..0000000
--- a/MutagenMerger.Lib/Utils.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Collections.Generic;
-using Mutagen.Bethesda.Plugins;
-using Mutagen.Bethesda.Plugins.Binary.Parameters;
-
-namespace MutagenMerger.Lib
-{
- public static class Utils
- {
- public static BinaryWriteParameters SafeBinaryWriteParameters (IEnumerable loadOrder) => new()
- {
- MasterFlag = MasterFlagOption.ChangeToMatchModKey,
- ModKey = ModKeyOption.CorrectToPath,
- RecordCount = RecordCountOption.Iterate,
- LightMasterLimit = LightMasterLimitOption.ExceptionOnOverflow,
- MastersListContent = MastersListContentOption.Iterate,
- FormIDUniqueness = FormIDUniquenessOption.Iterate,
- NextFormID = NextFormIDOption.Iterate,
- MastersListOrdering = new MastersListOrderingByLoadOrder(loadOrder)
- };
- }
-}
diff --git a/MutagenMerger.Tests/MergerTests.cs b/MutagenMerger.Tests/MergerTests.cs
deleted file mode 100644
index 41f6d4c..0000000
--- a/MutagenMerger.Tests/MergerTests.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Skyrim;
-using Xunit;
-
-namespace MutagenMerger.Tests
-{
- public class MergerTests
- {
- public MergerTests()
- {
- Warmup.Init();
- }
-
- [Fact]
- public void TestMerging()
- {
- const string testFolder = "merging-test-folder";
- if (Directory.Exists(testFolder))
- Directory.Delete(testFolder, true);
-
- var mod1 = MutagenTestHelpers.CreateDummyPlugin(testFolder, "test-file-1.esp", mod =>
- {
- mod.Actions.AddNew("Action1");
- mod.Actions.AddNew("Action2");
- });
-
- var mod2 = MutagenTestHelpers.CreateDummyPlugin(testFolder, "test-file-2.esp", mod =>
- {
- mod.Actions.AddNew("Action3");
- mod.Actions.AddNew("Action4");
- });
-
- var mods = new List
- {
- mod1,
- mod2,
- };
-
- using (var testMod1 =
- SkyrimMod.CreateFromBinaryOverlay(Path.Combine(testFolder, mod1), SkyrimRelease.SkyrimSE))
- {
- var action1 = testMod1.Actions.First();
-
- var mod3 = MutagenTestHelpers.CreateDummyPlugin(testFolder, "test-file-3.esp", mod =>
- {
- var copy = action1.DeepCopy();
- copy.EditorID = "Action1x";
- mod.Actions.Add(copy);
- });
-
- mods.Add(mod3);
- }
-
- const string outputFileName = "output.esp";
- using (var merger = new Merger(testFolder, mods, mods, outputFileName, testFolder, GameRelease.SkyrimSE))
- {
- merger.Merge();
- }
-
- var outputFile = Path.Combine(testFolder, outputFileName);
- MutagenTestHelpers.TestPlugin(outputFile, mod =>
- {
- Assert.Equal(4, mod.Actions.Count);
-
- Assert.Contains(mod.Actions, x => x.EditorID == "Action1x");
- Assert.Contains(mod.Actions, x => x.EditorID == "Action2");
- Assert.Contains(mod.Actions, x => x.EditorID == "Action3");
- Assert.Contains(mod.Actions, x => x.EditorID == "Action4");
- });
- }
-
- [Fact]
- public void TestBasePluginsMerging()
- {
- //requires manual activation
- const string folder = "base-plugins";
- if (!Directory.Exists(folder)) return;
-
- var mods = new List
- {
- "Skyrim.esm",
- "Update.esm",
- "Dawnguard.esm",
- "HearthFires.esm",
- "Dragonborn.esm"
- };
-
- if (!mods.All(x => File.Exists(Path.Combine(folder, x.FileName))))
- return;
-
- const string outputMod = "output.esp";
- var outputPath = Path.Combine(folder, outputMod);
- if (File.Exists(outputPath))
- File.Delete(outputPath);
-
- using (var merger = new Merger(folder, mods, mods, outputMod, folder, GameRelease.SkyrimSE))
- {
- merger.Merge();
- }
-
- Assert.True(File.Exists(outputPath));
- }
- }
-}
diff --git a/MutagenMerger.Tests/MutagenTestHelpers.cs b/MutagenMerger.Tests/MutagenTestHelpers.cs
deleted file mode 100644
index 4919ae9..0000000
--- a/MutagenMerger.Tests/MutagenTestHelpers.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.IO;
-using Mutagen.Bethesda;
-using Mutagen.Bethesda.Plugins;
-using Mutagen.Bethesda.Skyrim;
-using MutagenMerger.Lib;
-using Xunit;
-
-namespace MutagenMerger.Tests
-{
- public static class MutagenTestHelpers
- {
- public static string CreateDummyPlugin(string folder, string fileName, Action addRecords)
- {
- Directory.CreateDirectory(folder);
- var outputPath = Path.Combine(folder, fileName);
-
- var mod = new SkyrimMod(ModKey.FromNameAndExtension(fileName), SkyrimRelease.SkyrimSE);
- addRecords(mod);
- mod.WriteToBinary(outputPath, Utils.SafeBinaryWriteParameters);
- return fileName;
- }
-
- public static void TestPlugin(string path, Action verify)
- {
- Assert.True(File.Exists(path));
-
- using var mod = SkyrimMod.CreateFromBinaryOverlay(ModPath.FromPath(path), SkyrimRelease.SkyrimSE);
- verify(mod);
- }
- }
-}
diff --git a/merge.sh b/merge.sh
index 566314e..5d40b73 100755
--- a/merge.sh
+++ b/merge.sh
@@ -1,5 +1,5 @@
#/bin/bash
-project="MutagenMerger.CLI/MutagenMerger.CLI.csproj"
+project="Synthesis.Bethesda.CLI/Synthesis.Bethesda.CLI.csproj"
files=$(echo "$_data"$(cat "$MO2_Instance/profiles/$profile/modlist.txt" | tac | grep -ve '^-' -ve '^#' -ve '^\*' | sed 's#^\+#'"$MO2_Instance"'/mods/#' | sed 's/.*/:&/g' | tr -d "\n\r"));
cp "$MO2_Instance/profiles/$profile/plugins.txt" "$LocalAppData/$game/Plugins.txt"