Пример #1
0
 /// <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();
         }
 }
Пример #2
0
        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}");
                }
            }
Пример #3
0
        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");
            }
        }
Пример #4
0
 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;
 }
Пример #5
0
 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));
 }
Пример #6
0
        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");
            }
        }
Пример #7
0
        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,
            }) !);
        }
Пример #8
0
 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;
 }
Пример #9
0
        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);
                }
            }
        }
Пример #10
0
        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);
        }
Пример #11
0
        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,
            });
        }
Пример #12
0
        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,
            });
        }
Пример #13
0
        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))
            });
        }
Пример #14
0
        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();
        }
Пример #15
0
        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);
            }
        }
Пример #16
0
        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));
        }
Пример #17
0
        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));
        }