/// <summary> /// Takes in the main line command arguments, and handles PatcherRunSettings CLI inputs. /// </summary> /// <typeparam name="TMod">Setter mod interface</typeparam> /// <typeparam name="TModGetter">Getter only mod interface</typeparam> /// <param name="settings">Patcher run settings</param> /// <param name="patcher">Patcher func that processes a load order, and returns a mod object to export.</param> /// <param name="userPreferences">Any custom user preferences</param> public void Patch <TMod, TModGetter>( RunSynthesisMutagenPatcher settings, PatcherFunction <TMod, TModGetter> patcher, UserPreferences?userPreferences = null) where TMod : class, IMod, TModGetter where TModGetter : class, IModGetter { try { System.Console.WriteLine("Prepping state."); WarmupAll.Init(); using var state = Utility.ToState <TMod, TModGetter>(settings, userPreferences ?? new UserPreferences()); System.Console.WriteLine("Running patch."); patcher(state); if (!settings.OutputPath.IsNullOrWhitespace()) { System.Console.WriteLine($"Writing to output: {settings.OutputPath}"); state.PatchMod.WriteToBinaryParallel(path: settings.OutputPath, param: GetWriteParams(state.RawLoadOrder.Select(x => x.ModKey))); } } catch (Exception ex) when(Environment.GetCommandLineArgs().Length == 0 && (userPreferences?.ActionsForEmptyArgs?.BlockAutomaticExit ?? false)) { System.Console.Error.WriteLine(ex); System.Console.Error.WriteLine("Error occurred. Press enter to exit"); System.Console.ReadLine(); } }
public async Task Run(RunSynthesisPatcher settings, CancellationToken cancel) { if (cancel.IsCancellationRequested) { return; } var internalSettings = RunSynthesisMutagenPatcher.Factory(settings); internalSettings.ExtraDataFolder = PathToExtraData; var args = Parser.Default.FormatCommandLine(internalSettings); try { using ProcessWrapper process = ProcessWrapper.Create( new ProcessStartInfo(PathToExecutable, args) { WorkingDirectory = Path.GetDirectoryName(PathToExecutable) ! }, cancel); using var outputSub = process.Output.Subscribe(_output); using var errSub = process.Error.Subscribe(_error); var result = await process.Run(); if (result != 0) { throw new CliUnsuccessfulRunException( result, $"Process exited in failure: {process.StartInfo.FileName} {internalSettings}"); } }
public async Task Run(RunSynthesisPatcher settings, CancellationToken?cancel = null) { var repoPath = Path.GetDirectoryName(PathToSolution); if (Repository.IsValid(repoPath)) { using var repo = new Repository(repoPath); _output.OnNext($"Sha {repo.Head.Tip.Sha}"); } var internalSettings = new RunSynthesisMutagenPatcher() { DataFolderPath = settings.DataFolderPath, ExtraDataFolder = Path.Combine(PathToExtraDataBaseFolder, Name), GameRelease = settings.GameRelease, LoadOrderFilePath = settings.LoadOrderFilePath, OutputPath = settings.OutputPath, SourcePath = settings.SourcePath }; var args = Parser.Default.FormatCommandLine(internalSettings); using var process = ProcessWrapper.Start( new ProcessStartInfo("dotnet", $"run --project \"{PathToProject}\" --runtime win-x64 --no-build {args}"), cancel: cancel); using var outputSub = process.Output.Subscribe(_output); using var errSub = process.Error.Subscribe(_error); var result = await process.Start().ConfigureAwait(false); if (result != 0) { throw new CliUnsuccessfulRunException(result, "Error running solution patcher"); } }
public SynthesisState( RunSynthesisMutagenPatcher runArguments, IReadOnlyList <IModListingGetter> rawLoadOrder, ILoadOrder <IModListing <TModGetter> > loadOrder, ILinkCache <TModSetter, TModGetter> linkCache, TModSetter patchMod, string extraDataPath, string?internalDataPath, string?defaultDataPath, CancellationToken cancellation, IFormKeyAllocator?formKeyAllocator) { LinkCache = linkCache; RawLoadOrder = rawLoadOrder; LoadOrder = loadOrder; PatchMod = patchMod; ExtraSettingsDataPath = extraDataPath; InternalDataPath = internalDataPath; DefaultSettingsDataPath = defaultDataPath; Cancel = cancellation; LoadOrderFilePath = runArguments.LoadOrderFilePath; DataFolderPath = runArguments.DataFolderPath; GameRelease = runArguments.GameRelease; OutputPath = runArguments.OutputPath; SourcePath = runArguments.SourcePath; _formKeyAllocator = formKeyAllocator; }
public IEnumerable <LoadOrderListing> GetLoadOrder( RunSynthesisMutagenPatcher settings, UserPreferences?userPrefs = null, bool throwOnMissingMods = true) { return(GetLoadOrder( release: settings.GameRelease, loadOrderFilePath: settings.LoadOrderFilePath, dataFolderPath: settings.DataFolderPath, userPrefs: userPrefs, throwOnMissingMods: throwOnMissingMods)); }
public async Task Run(RunSynthesisPatcher settings, CancellationToken cancel) { var repoPath = Path.GetDirectoryName(PathToSolution); if (Repository.IsValid(repoPath)) { using var repo = new Repository(repoPath); _output.OnNext($"Sha {repo.Head.Tip.Sha}"); } var runnability = await Synthesis.Bethesda.Execution.CLI.Commands.CheckRunnability( PathToProject, directExe : false, release : settings.GameRelease, dataFolder : settings.DataFolderPath, loadOrderPath : settings.LoadOrderFilePath, cancel : cancel); if (runnability.Failed) { throw new CliUnsuccessfulRunException((int)Codes.NotRunnable, runnability.Reason); } var internalSettings = new RunSynthesisMutagenPatcher() { DataFolderPath = settings.DataFolderPath, ExtraDataFolder = Path.Combine(PathToExtraDataBaseFolder, Name), GameRelease = settings.GameRelease, LoadOrderFilePath = settings.LoadOrderFilePath, OutputPath = settings.OutputPath, SourcePath = settings.SourcePath }; var args = Parser.Default.FormatCommandLine(internalSettings); using var process = ProcessWrapper.Create( new ProcessStartInfo("dotnet", $"run --project \"{PathToProject}\" --runtime win-x64 --no-build {args}"), cancel: cancel); _output.OnNext("Running"); _output.OnNext($"({process.StartInfo.WorkingDirectory}): {process.StartInfo.FileName} {process.StartInfo.Arguments}"); using var outputSub = process.Output.Subscribe(_output); using var errSub = process.Error.Subscribe(_error); var result = await process.Run().ConfigureAwait(false); if (result != 0) { throw new CliUnsuccessfulRunException(result, "Error running solution patcher"); } }
public static IPatcherState ToState(GameCategory category, RunSynthesisMutagenPatcher settings, PatcherPreferences userPrefs, ModKey exportKey) { var regis = category.ToModRegistration(); var method = typeof(Utility).GetMethods() .Where(m => m.Name == nameof(ToState)) .Where(m => m.ContainsGenericParameters) .First() .MakeGenericMethod(regis.SetterType, regis.GetterType); return((IPatcherState)method.Invoke(null, new object[] { settings, userPrefs, exportKey, }) !); }
public SynthesisState( RunSynthesisMutagenPatcher settings, IReadOnlyList <LoadOrderListing> rawLoadOrder, LoadOrder <IModListing <TModGetter> > loadOrder, ILinkCache <TModSetter, TModGetter> linkCache, TModSetter patchMod, string extraDataPath, CancellationToken cancellation) { Settings = settings; LinkCache = linkCache; RawLoadOrder = rawLoadOrder; LoadOrder = loadOrder; PatchMod = patchMod; ExtraSettingsDataPath = extraDataPath; Cancel = cancellation; }
public static void AddImplicitMasters(RunSynthesisMutagenPatcher settings, ExtendedList <LoadOrderListing> loadOrderListing) { HashSet <ModKey> referencedMasters = new HashSet <ModKey>(); foreach (var item in loadOrderListing.OnlyEnabled()) { MasterReferenceReader reader = MasterReferenceReader.FromPath(Path.Combine(settings.DataFolderPath, item.ModKey.FileName), settings.GameRelease); referencedMasters.Add(reader.Masters.Select(m => m.Master)); } for (int i = 0; i < loadOrderListing.Count; i++) { var listing = loadOrderListing[i]; if (!listing.Enabled && referencedMasters.Contains(listing.ModKey)) { loadOrderListing[i] = new LoadOrderListing(listing.ModKey, enabled: true); } } }
public void ConstructStateFactory() { using var tmpFolder = Utility.GetTempFolder(); using var dataFolder = Utility.SetupDataFolder(tmpFolder, GameRelease.Oblivion); var output = Utility.TypicalOutputFile(tmpFolder); var settings = new RunSynthesisMutagenPatcher() { DataFolderPath = dataFolder.Dir.Path, GameRelease = GameRelease.Oblivion, LoadOrderFilePath = Utility.PathToLoadOrderFile, OutputPath = output, SourcePath = null }; var factory = CodeSnippetPatcherRun.ConstructStateFactory(GameRelease.Oblivion); var stateObj = factory(settings, new PatcherPreferences(), Synthesis.Bethesda.Constants.SynthesisModKey); Assert.NotNull(stateObj); using var state = stateObj as SynthesisState <IOblivionMod, IOblivionModGetter>; Assert.NotNull(state); }
public void NonSynthesisTarget() { var env = Utility.SetupEnvironment(GameRelease.SkyrimLE); var output = ModPath.FromPath(Path.Combine(env.DataFolder, Utility.OtherFileName)); var pluginPath = Path.Combine(env.DataFolder, "Plugins.txt"); env.FileSystem.File.WriteAllLines(pluginPath, new string[] { $"*{Utility.TestFileName}", $"*{Utility.OverrideFileName}", $"*{output.ModKey.FileName}", $"*{Utility.OtherFileName}", $"*{Utility.SynthesisModKey}", }); var settings = new RunSynthesisMutagenPatcher() { DataFolderPath = env.DataFolder, GameRelease = GameRelease.SkyrimLE, LoadOrderFilePath = env.PluginPath, OutputPath = output, SourcePath = null }; var stateFactory = env.GetStateFactory(); using var state = stateFactory.ToState( GameCategory.Skyrim, settings, new PatcherPreferences(), output.ModKey); state.RawLoadOrder.Should().HaveCount(3); state.RawLoadOrder.Select(l => l.ModKey).Should().Equal(new ModKey[] { Utility.TestFileName, Utility.OverrideModKey, output.ModKey, }); }
public void NonSynthesisTarget() { using var tmpFolder = Utility.GetTempFolder(); using var dataFolder = Utility.SetupDataFolder(tmpFolder, GameRelease.SkyrimLE); var output = ModPath.FromPath(Path.Combine(dataFolder.Dir.Path, Utility.OtherFileName)); var pluginPath = Path.Combine(tmpFolder.Dir.Path, "Plugins.txt"); File.WriteAllLines(pluginPath, new string[] { $"*{Utility.TestFileName}", $"*{Utility.OverrideFileName}", $"*{output.ModKey.FileName}", $"*{Utility.OtherFileName}", $"*{Utility.SynthesisModKey}", }); var settings = new RunSynthesisMutagenPatcher() { DataFolderPath = dataFolder.Dir.Path, GameRelease = GameRelease.SkyrimLE, LoadOrderFilePath = Utility.PathToLoadOrderFile, OutputPath = output, SourcePath = null }; using var state = Mutagen.Bethesda.Synthesis.Internal.Utility.ToState( GameCategory.Skyrim, settings, new PatcherPreferences(), output.ModKey); state.RawLoadOrder.Should().HaveCount(3); state.RawLoadOrder.Select(l => l.ModKey).Should().Equal(new ModKey[] { Utility.TestFileName, Utility.OverrideModKey, output.ModKey, }); }
public async Task Run(RunSynthesisPatcher settings, CancellationToken?cancel = null) { if (_assembly == null) { throw new ArgumentException("Tried to run a code snippet patcher that did not have an assembly."); } Type?type = _assembly.GetType(ClassName); if (type == null) { throw new ArgumentException("Could not find compiled class within assembly."); } var patcherCodeClass = System.Activator.CreateInstance(type); var userPrefs = new UserPreferences() { Cancel = cancel ?? CancellationToken.None }; var internalSettings = RunSynthesisMutagenPatcher.Factory(settings); using var synthesisState = ConstructStateFactory(settings.GameRelease)(internalSettings, userPrefs); Task t = (Task)type.InvokeMember("Run", BindingFlags.Default | BindingFlags.InvokeMethod, null, patcherCodeClass, new[] { synthesisState, }); await t; synthesisState.PatchMod.WriteToBinaryParallel( settings.OutputPath, new BinaryWriteParameters() { ModKey = BinaryWriteParameters.ModKeyOption.NoCheck, MastersListOrdering = new BinaryWriteParameters.MastersListOrderingByLoadOrder(synthesisState.LoadOrder.Where(i => i.Enabled).Select(i => i.ModKey)) }); }
public void NoPatch() { using var tmpFolder = Utility.GetTempFolder(); using var dataFolder = Utility.SetupDataFolder(tmpFolder, GameRelease.Oblivion); var modPath = PatchModPath(dataFolder); var settings = new RunSynthesisMutagenPatcher() { DataFolderPath = dataFolder.Dir.Path, GameRelease = Mutagen.Bethesda.GameRelease.Oblivion, OutputPath = string.Empty, SourcePath = null, LoadOrderFilePath = Utility.PathToLoadOrderFile }; new SynthesisPipeline().Patch <IOblivionMod, IOblivionModGetter>( settings, (state) => { }, new UserPreferences() { NoPatch = true }); File.Exists(modPath.Path).Should().BeFalse(); }
public async Task TypicalPatcher_HasSource() { using var tmpFolder = Utility.GetTempFolder(); using var dataFolder = Utility.SetupDataFolder(tmpFolder, GameRelease.Oblivion); var modPath = PatchModPath(dataFolder); var settings = new RunSynthesisMutagenPatcher() { DataFolderPath = dataFolder.Dir.Path, GameRelease = Mutagen.Bethesda.GameRelease.Oblivion, OutputPath = modPath, SourcePath = null, LoadOrderFilePath = Utility.PathToLoadOrderFile }; await new SynthesisPipeline().Patch <IOblivionMod, IOblivionModGetter>(settings, PatchFunction); Assert.True(File.Exists(modPath.Path)); using (var patch = OblivionMod.CreateFromBinaryOverlay(modPath)) { Assert.Equal(3, patch.Npcs.Count); Assert.Equal(1, patch.Npcs[new FormKey(Utility.TestModKey, 0xD62)].Items.Count); Assert.Equal(1, patch.Npcs[new FormKey(Utility.TestModKey, 0xD63)].Items.Count); Assert.Equal(1, patch.Npcs[new FormKey(PatchModKey, 0xD62)].Items.Count); } // Run a second time, with sourcepath set containing previous patch settings.SourcePath = modPath; await new SynthesisPipeline().Patch <IOblivionMod, IOblivionModGetter>(settings, PatchFunction); Assert.True(File.Exists(modPath.Path)); using (var patch = OblivionMod.CreateFromBinaryOverlay(modPath)) { Assert.Equal(4, patch.Npcs.Count); Assert.Equal(2, patch.Npcs[new FormKey(Utility.TestModKey, 0xD62)].Items.Count); Assert.Equal(2, patch.Npcs[new FormKey(Utility.TestModKey, 0xD63)].Items.Count); Assert.Equal(2, patch.Npcs[new FormKey(PatchModKey, 0xD62)].Items.Count); Assert.Equal(1, patch.Npcs[new FormKey(PatchModKey, 0xD63)].Items.Count); } }
public SynthesisState <TModSetter, TModGetter> ToState <TModSetter, TModGetter>(RunSynthesisMutagenPatcher settings, PatcherPreferences userPrefs, ModKey exportKey) where TModSetter : class, IContextMod <TModSetter, TModGetter>, TModGetter where TModGetter : class, IContextGetterMod <TModSetter, TModGetter> { // Confirm target game release matches var regis = settings.GameRelease.ToCategory().ToModRegistration(); if (!typeof(TModSetter).IsAssignableFrom(regis.SetterType)) { throw new ArgumentException($"Target mod type {typeof(TModSetter)} was not of the expected type {regis.SetterType}"); } if (!typeof(TModGetter).IsAssignableFrom(regis.GetterType)) { throw new ArgumentException($"Target mod type {typeof(TModGetter)} was not of the expected type {regis.GetterType}"); } // Set up target language System.Console.WriteLine($"Language: {settings.TargetLanguage}"); TranslatedString.DefaultLanguage = settings.TargetLanguage; // Get load order var loadOrderListing = _getStateLoadOrder.GetLoadOrder(!settings.LoadOrderIncludesCreationClub, userPrefs) .ToExtendedList(); var rawLoadOrder = loadOrderListing.Select(x => new ModListing(x.ModKey, x.Enabled)).ToExtendedList(); // Trim past export key var synthIndex = loadOrderListing.IndexOf(exportKey, (listing, key) => listing.ModKey == key); if (synthIndex != -1) { loadOrderListing.RemoveToCount(synthIndex); } if (userPrefs.AddImplicitMasters) { _enableImplicitMasters .Get(settings.DataFolderPath, settings.GameRelease) .Add(loadOrderListing); } // Remove disabled mods if (!userPrefs.IncludeDisabledMods) { loadOrderListing = loadOrderListing.OnlyEnabled().ToExtendedList(); } var loadOrder = _loadOrderImporter .Get <TModGetter>( settings.DataFolderPath, loadOrderListing, settings.GameRelease) .Import(); // Create or import patch mod TModSetter patchMod; ILinkCache <TModSetter, TModGetter> cache; IFormKeyAllocator?formKeyAllocator = null; if (userPrefs.NoPatch) { // Pass null, even though it isn't normally patchMod = null !; TModGetter readOnlyPatchMod; if (settings.SourcePath == null) { readOnlyPatchMod = ModInstantiator <TModGetter> .Activator(exportKey, settings.GameRelease); } else { readOnlyPatchMod = ModInstantiator <TModGetter> .Importer(new ModPath(exportKey, settings.SourcePath), settings.GameRelease, fileSystem : _fileSystem); } loadOrder.Add(new ModListing <TModGetter>(readOnlyPatchMod, enabled: true)); rawLoadOrder.Add(new ModListing(readOnlyPatchMod.ModKey, enabled: true)); cache = loadOrder.ToImmutableLinkCache <TModSetter, TModGetter>(); } else { if (settings.SourcePath == null) { patchMod = ModInstantiator <TModSetter> .Activator(exportKey, settings.GameRelease); } else { patchMod = ModInstantiator <TModSetter> .Importer(new ModPath(exportKey, settings.SourcePath), settings.GameRelease, fileSystem : _fileSystem); } if (settings.PersistencePath is not null && settings.PatcherName is not null) { if (TextFileSharedFormKeyAllocator.IsPathOfAllocatorType(settings.PersistencePath)) { System.Console.WriteLine($"Using {nameof(TextFileSharedFormKeyAllocator)} allocator"); patchMod.SetAllocator(formKeyAllocator = new TextFileSharedFormKeyAllocator(patchMod, settings.PersistencePath, settings.PatcherName, fileSystem: _fileSystem)); } // else if (SQLiteFormKeyAllocator.IsPathOfAllocatorType(settings.PersistencePath)) // { // System.Console.WriteLine($"Using {nameof(SQLiteFormKeyAllocator)} allocator"); // patchMod.SetAllocator(formKeyAllocator = new SQLiteFormKeyAllocator(patchMod, settings.PersistencePath, settings.PatcherName)); // } else { System.Console.WriteLine($"Allocation systems were marked to be on, but could not identify allocation system to be used"); } } cache = loadOrder.ToMutableLinkCache(patchMod); loadOrder.Add(new ModListing <TModGetter>(patchMod, enabled: true)); rawLoadOrder.Add(new ModListing(patchMod.ModKey, enabled: true)); System.Console.WriteLine($"Can use localization: {patchMod.CanUseLocalization}"); if (patchMod.CanUseLocalization) { System.Console.WriteLine($"Localized: {settings.Localize}"); patchMod.UsingLocalization = settings.Localize; } } return(new SynthesisState <TModSetter, TModGetter>( runArguments: settings, loadOrder: loadOrder, rawLoadOrder: rawLoadOrder, linkCache: cache, internalDataPath: settings.InternalDataFolder, patchMod: patchMod, extraDataPath: settings.ExtraDataFolder == null ? string.Empty : Path.GetFullPath(settings.ExtraDataFolder), defaultDataPath: settings.DefaultDataFolderPath, cancellation: userPrefs.Cancel, formKeyAllocator: formKeyAllocator)); }
public static SynthesisState <TMod, TModGetter> ToState <TMod, TModGetter>(RunSynthesisMutagenPatcher settings, UserPreferences userPrefs) where TMod : class, IMod, TModGetter where TModGetter : class, IModGetter { // Confirm target game release matches var regis = settings.GameRelease.ToCategory().ToModRegistration(); if (!typeof(TMod).IsAssignableFrom(regis.SetterType)) { throw new ArgumentException($"Target mod type {typeof(TMod)} was not of the expected type {regis.SetterType}"); } if (!typeof(TModGetter).IsAssignableFrom(regis.GetterType)) { throw new ArgumentException($"Target mod type {typeof(TModGetter)} was not of the expected type {regis.GetterType}"); } // Get load order var loadOrderListing = SynthesisPipeline.Instance.GetLoadOrder(settings, userPrefs) .ToExtendedList(); var rawLoadOrder = loadOrderListing.Select(x => new LoadOrderListing(x.ModKey, x.Enabled)).ToExtendedList(); // Trim past Synthesis.esp var synthIndex = loadOrderListing.IndexOf(BaseSynthesis.Constants.SynthesisModKey, (listing, key) => listing.ModKey == key); if (synthIndex != -1) { loadOrderListing.RemoveToCount(synthIndex); } if (userPrefs.AddImplicitMasters) { AddImplicitMasters(settings, loadOrderListing); } // Remove disabled mods if (!userPrefs.IncludeDisabledMods) { loadOrderListing = loadOrderListing.OnlyEnabled().ToExtendedList(); } var loadOrder = LoadOrder.Import <TModGetter>( settings.DataFolderPath, loadOrderListing, settings.GameRelease); // Get Modkey from output path var modKey = BaseSynthesis.Constants.SynthesisModKey; // Create or import patch mod TMod patchMod; ILinkCache cache; if (userPrefs.NoPatch) { // Pass null, even though it isn't normally patchMod = null !; TModGetter readOnlyPatchMod; if (settings.SourcePath == null) { readOnlyPatchMod = ModInstantiator <TModGetter> .Activator(modKey, settings.GameRelease); } else { readOnlyPatchMod = ModInstantiator <TModGetter> .Importer(new ModPath(modKey, settings.SourcePath), settings.GameRelease); } loadOrder.Add(new ModListing <TModGetter>(readOnlyPatchMod, enabled: true)); cache = loadOrder.ToImmutableLinkCache(); } else { if (settings.SourcePath == null) { patchMod = ModInstantiator <TMod> .Activator(modKey, settings.GameRelease); } else { patchMod = ModInstantiator <TMod> .Importer(new ModPath(modKey, settings.SourcePath), settings.GameRelease); } cache = loadOrder.ToMutableLinkCache(patchMod); loadOrder.Add(new ModListing <TModGetter>(patchMod, enabled: true)); } return(new SynthesisState <TMod, TModGetter>( settings: settings, loadOrder: loadOrder, rawLoadOrder: rawLoadOrder, linkCache: cache, patchMod: patchMod, extraDataPath: settings.ExtraDataFolder == null ? string.Empty : Path.GetFullPath(settings.ExtraDataFolder), cancellation: userPrefs.Cancel)); }