wabbajack/Wabbajack.Lib/VortexInstaller.cs
2020-04-03 16:40:20 -06:00

227 lines
9.4 KiB
C#

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
using Wabbajack.Common;
using Wabbajack.Common.StoreHandlers;
using Directory = Alphaleonis.Win32.Filesystem.Directory;
using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo;
using File = Alphaleonis.Win32.Filesystem.File;
using Path = Alphaleonis.Win32.Filesystem.Path;
namespace Wabbajack.Lib
{
public class VortexInstaller : AInstaller
{
public GameMetaData GameInfo { get; internal set; }
public override ModManager ModManager => ModManager.Vortex;
public VortexInstaller(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
: base(
archive: archive,
modList: modList,
outputFolder: outputFolder,
downloadFolder: downloadFolder,
parameters: parameters)
{
#if DEBUG
// TODO: only for testing
IgnoreMissingFiles = true;
#endif
GameInfo = ModList.GameType.MetaData();
}
protected override async Task<bool> _Begin(CancellationToken cancel)
{
if (cancel.IsCancellationRequested) return false;
var metric = Metrics.Send(Metrics.BeginInstall, ModList.Name);
var result = await Utils.Log(new YesNoIntervention(
"Vortex Support is still experimental and may produce unexpected results. " +
"If anything fails please go to the special Vortex support channels on the Wabbajack Discord and contact @erri120#2285 " +
"for support.", "Continue with experimental feature?")).Task;
if (result == ConfirmationIntervention.Choice.Abort)
{
Utils.Log("Exiting at request of user");
return false;
}
if (cancel.IsCancellationRequested) return false;
ConfigureProcessor(10, ConstructDynamicNumThreads(await RecommendQueueSize()));
DownloadFolder.CreateDirectory();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Hashing Archives");
await HashArchives();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Downloading Missing Archives");
await DownloadArchives();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Hashing Remaining Archives");
await HashArchives();
if (cancel.IsCancellationRequested) return false;
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}");
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");
}
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 Manual files");
await InstallManualGameFiles();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Installing SteamWorkshopItems");
await InstallSteamWorkshopItems();
//InstallIncludedDownloadMetas();
var metric2 = Metrics.Send(Metrics.FinishInstall, ModList.Name);
UpdateTracker.NextStep("Installation complete! You may exit the program.");
return true;
}
private async Task InstallManualGameFiles()
{
if (!ModList.Directives.Any(d => d.To.StartsWith(Consts.ManualGameFilesDir)))
return;
var result = await Utils.Log(new YesNoIntervention("Some mods from this ModList must be installed directly into " +
"the game folder. Do you want to do this manually or do you want Wabbajack " +
"to do this for you?", "Move mods into game folder?")).Task;
if (result != ConfirmationIntervention.Choice.Continue)
return;
var manualFilesDir = OutputFolder.Combine(Consts.ManualGameFilesDir);
var gameFolder = GameInfo.GameLocation();
Info($"Copying files from {manualFilesDir} " +
$"to the game folder at {gameFolder}");
if (!manualFilesDir.Exists)
{
Info($"{manualFilesDir} does not exist!");
return;
}
/* TODO FIX THIS
await manualFilesDir.EnumerateFiles().PMap(Queue, dir =>
{
dirInfo.GetDirectories("*", SearchOption.AllDirectories).Do(d =>
{
var destPath = d.FullName.Replace(manualFilesDir, gameFolder);
Status($"Creating directory {destPath}");
Directory.CreateDirectory(destPath);
});
dirInfo.GetFiles("*", SearchOption.AllDirectories).Do(f =>
{
var destPath = f.FullName.Replace(manualFilesDir, gameFolder);
Status($"Copying file {f.FullName} to {destPath}");
try
{
File.Copy(f.FullName, destPath);
}
catch (Exception)
{
Info($"Could not copy file {f.FullName} to {destPath}. The file may already exist, skipping...");
}
});
});
*/
}
private async Task InstallSteamWorkshopItems()
{
//var currentLib = "";
SteamGame currentSteamGame = null;
StoreHandler.Instance.SteamHandler.Games.Where(g => g.Game == GameInfo.Game).Do(s => currentSteamGame = (SteamGame)s);
/*SteamHandler.Instance.InstallFolders.Where(f => f.Contains(currentSteamGame.InstallDir)).Do(s => currentLib = s);
var downloadFolder = Path.Combine(currentLib, "workshop", "downloads", currentSteamGame.AppId.ToString());
var contentFolder = Path.Combine(currentLib, "workshop", "content", currentSteamGame.AppId.ToString());
*/
if (!ModList.Directives.Any(s => s is SteamMeta))
return;
var result = await Utils.Log(new YesNoIntervention(
"The ModList you are installing requires Steam Workshop items to exist. " +
"You can check the Workshop Items in the manifest of this ModList. Wabbajack can start Steam for you " +
"and download the Items automatically. Do you want to proceed with this step?",
"Download Steam Workshop Items?")).Task;
if (result != ConfirmationIntervention.Choice.Continue)
return;
await ModList.Directives.OfType<SteamMeta>()
.PMap(Queue, async item =>
{
Status("Extracting Steam meta file to temp folder");
var path = DownloadFolder.Combine($"steamWorkshopItem_{item.ItemID}.meta");
if (!path.Exists)
await path.WriteAllBytesAsync(await LoadBytesFromPath(item.SourceDataID));
Status("Downloading Steam Workshop Item through steam cmd");
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = Path.Combine(StoreHandler.Instance.SteamHandler.SteamPath, "steam.exe"),
CreateNoWindow = true,
Arguments = $"console +workshop_download_item {currentSteamGame.ID} {currentSteamGame.ID}"
}
};
p.Start();
});
}
private async Task InstallIncludedFiles()
{
Info("Writing inline files");
await ModList.Directives.OfType<InlineFile>()
.PMap(Queue,async directive =>
{
if (directive.To.Extension == Consts.MetaFileExtension)
return;
Info($"Writing included file {directive.To}");
var outPath = OutputFolder.Combine(directive.To);
outPath.Delete();
await outPath.WriteAllBytesAsync(await LoadBytesFromPath(directive.SourceDataID));
});
}
}
}