protected override async Task <bool> _Begin(CancellationToken cancel) { if (cancel.IsCancellationRequested) { return(false); } await Metrics.Send(Metrics.BeginInstall, ModList.Name); Utils.Log("Configuring Processor"); FileExtractor2.FavorPerfOverRAM = FavorPerfOverRam; if (GameFolder == null) { GameFolder = Game.TryGetGameLocation(); } if (GameFolder == null) { var otherGame = Game.CommonlyConfusedWith.Where(g => g.MetaData().IsInstalled).Select(g => g.MetaData()).FirstOrDefault(); if (otherGame != null) { await Utils.Log(new CriticalFailureIntervention( $"In order to do a proper install Wabbajack needs to know where your {Game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed, we did however find a installed " + $"copy of {otherGame.HumanFriendlyGameName}, did you install the wrong game?", $"Could not locate {Game.HumanFriendlyGameName}")) .Task; } else { await Utils.Log(new CriticalFailureIntervention( $"In order to do a proper install Wabbajack needs to know where your {Game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed", $"Could not locate {Game.HumanFriendlyGameName}")) .Task; } Utils.Log("Exiting because we couldn't find the game folder."); return(false); } Utils.Log($"Install Folder: {OutputFolder}"); Utils.Log($"Downloads Folder: {DownloadFolder}"); Utils.Log($"Game Folder: {GameFolder.Value}"); Utils.Log($"Wabbajack Folder: {AbsolutePath.EntryPoint}"); var watcher = new DiskSpaceWatcher(cancel, new[] { OutputFolder, DownloadFolder, GameFolder.Value, AbsolutePath.EntryPoint }, (long)2 << 31, drive => { Utils.Log($"Aborting due to low space on {drive.Name}"); Abort(); }); var watcherTask = watcher.Start(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Validating Game ESMs"); await ValidateGameESMs(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Creating Output Folders"); OutputFolder.CreateDirectory(); DownloadFolder.CreateDirectory(); if (OutputFolder.Combine(Consts.MO2ModFolderName).IsDirectory&& WarnOnOverwrite) { if ((await Utils.Log(new ConfirmUpdateOfExistingInstall { ModListName = ModList.Name, OutputFolder = OutputFolder }).Task) == ConfirmUpdateOfExistingInstall.Choice.Abort) { Utils.Log("Exiting installation at the request of the user, existing mods folder found."); return(false); } } // Reduce to one thread if downloads on HDD, else use specified. Hashing on HDD has no benefit with more threads. if (new PhysicalDisk(DownloadFolder.DriveInfo().Name).MediaType == PhysicalDisk.MediaTypes.HDD && ReduceHDDThreads) { DesiredThreads.OnNext(1); } else { DesiredThreads.OnNext(DiskThreads); } if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Optimizing ModList"); await OptimizeModlist(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Hashing Archives"); await HashArchives(); // Set to download thread count. DesiredThreads.OnNext(DownloadThreads); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Downloading Missing Archives"); await DownloadArchives(); // Reduce to one thread if downloads on HDD, else use specified. Hashing on HDD has no benefit with more threads. if (new PhysicalDisk(DownloadFolder.DriveInfo().Name).MediaType == PhysicalDisk.MediaTypes.HDD && ReduceHDDThreads) { DesiredThreads.OnNext(1); } else { DesiredThreads.OnNext(DiskThreads); } if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Hashing Remaining Archives"); await HashArchives(); var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList(); if (missing.Count > 0) { foreach (var a in missing) { Info($"Unable to download {a.Name} ({a.State.PrimaryKeyString})"); } if (IgnoreMissingFiles) { Info("Missing some archives, but continuing anyways at the request of the user"); } else { Error("Cannot continue, was unable to download one or more archives"); } } // Reduce to two threads if output on HDD, else use specified. Installing files seems to have a slight benefit with two threads. if (new PhysicalDisk(OutputFolder.DriveInfo().Name).MediaType == PhysicalDisk.MediaTypes.HDD && ReduceHDDThreads) { DesiredThreads.OnNext(2); } else { DesiredThreads.OnNext(DiskThreads); } if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Extracting Modlist contents"); await ExtractModlist(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Priming VFS"); await PrimeVFS(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Building Folder Structure"); BuildFolderStructure(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Installing Archives"); await InstallArchives(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Installing Included files"); await InstallIncludedFiles(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Installing Archive Metas"); await InstallIncludedDownloadMetas(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Building BSAs"); await BuildBSAs(); if (cancel.IsCancellationRequested) { return(false); } UpdateTracker.NextStep("Generating Merges"); await zEditIntegration.GenerateMerges(this); UpdateTracker.NextStep("Set MO2 into portable"); await ForcePortable(); UpdateTracker.NextStep("Create Empty Output Mods"); CreateOutputMods(); UpdateTracker.NextStep("Updating System-specific ini settings"); SetScreenSizeInPrefs(); UpdateTracker.NextStep("Compacting files"); await CompactFiles(); UpdateTracker.NextStep("Installation complete! You may exit the program."); await ExtractedModlistFolder !.DisposeAsync(); await Metrics.Send(Metrics.FinishInstall, ModList.Name); return(true); }