wabbajack/Wabbajack.Lib/VortexInstaller.cs

225 lines
9.4 KiB
C#
Raw Normal View History

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
2019-11-09 20:40:25 +00:00
using Wabbajack.Common;
using Wabbajack.Common.StoreHandlers;
2019-11-09 21:45:10 +00:00
using Directory = Alphaleonis.Win32.Filesystem.Directory;
using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo;
2019-11-09 21:45:10 +00:00
using File = Alphaleonis.Win32.Filesystem.File;
using Path = Alphaleonis.Win32.Filesystem.Path;
2019-11-09 20:40:25 +00:00
namespace Wabbajack.Lib
{
public class VortexInstaller : AInstaller
2019-11-09 20:40:25 +00:00
{
2019-11-09 21:45:10 +00:00
public GameMetaData GameInfo { get; internal set; }
public override ModManager ModManager => ModManager.Vortex;
2019-11-09 21:45:10 +00:00
2020-01-07 13:50:11 +00:00
public VortexInstaller(string archive, ModList modList, string outputFolder, string downloadFolder, SystemParameters parameters)
: base(
2019-12-14 17:30:52 +00:00
archive: archive,
modList: modList,
outputFolder: outputFolder,
2020-01-07 13:50:11 +00:00
downloadFolder: downloadFolder,
parameters: parameters)
{
#if DEBUG
2019-11-09 21:45:10 +00:00
// TODO: only for testing
IgnoreMissingFiles = true;
#endif
2019-11-09 21:45:10 +00:00
GameInfo = ModList.GameType.MetaData();
2019-11-09 20:40:25 +00:00
}
protected override async Task<bool> _Begin(CancellationToken cancel)
2019-11-09 21:45:10 +00:00
{
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. " +
2020-01-13 21:11:07 +00:00
"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()));
2019-11-09 21:45:10 +00:00
Directory.CreateDirectory(DownloadFolder);
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();
2019-11-09 21:45:10 +00:00
if (cancel.IsCancellationRequested) return false;
2019-11-09 21:45:10 +00:00
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");
2019-12-07 02:54:27 +00:00
await PrimeVFS();
2019-11-09 21:45:10 +00:00
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Building Folder Structure");
2019-11-09 21:45:10 +00:00
BuildFolderStructure();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Installing Archives");
await InstallArchives();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Installing Included files");
await InstallIncludedFiles();
2019-12-14 15:08:22 +00:00
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Installing Manual files");
2019-12-14 15:08:22 +00:00
await InstallManualGameFiles();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Installing SteamWorkshopItems");
await InstallSteamWorkshopItems();
2019-11-09 21:45:10 +00:00
2019-12-15 04:33:48 +00:00
//InstallIncludedDownloadMetas();
var metric2 = Metrics.Send(Metrics.FinishInstall, ModList.Name);
UpdateTracker.NextStep("Installation complete! You may exit the program.");
return true;
2019-11-09 21:45:10 +00:00
}
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 " +
2020-01-13 21:11:07 +00:00
"to do this for you?", "Move mods into game folder?")).Task;
if (result != ConfirmationIntervention.Choice.Continue)
return;
var manualFilesDir = Path.Combine(OutputFolder, Consts.ManualGameFilesDir);
var gameFolder = GameInfo.GameLocation();
Info($"Copying files from {manualFilesDir} " +
$"to the game folder at {gameFolder}");
if (!Directory.Exists(manualFilesDir))
{
Info($"{manualFilesDir} does not exist!");
return;
}
await Directory.EnumerateDirectories(manualFilesDir).PMap(Queue, dir =>
{
var dirInfo = new DirectoryInfo(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(
2020-01-13 21:11:07 +00:00
"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, item =>
{
Status("Extracting Steam meta file to temp folder");
var path = Path.Combine(DownloadFolder, $"steamWorkshopItem_{item.ItemID}.meta");
if (!File.Exists(path))
File.WriteAllBytes(path, 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()
2019-11-09 21:45:10 +00:00
{
Info("Writing inline files");
await ModList.Directives.OfType<InlineFile>()
2019-11-17 04:16:42 +00:00
.PMap(Queue,directive =>
2019-11-09 21:45:10 +00:00
{
if (directive.To.EndsWith(".meta"))
return;
Info($"Writing included file {directive.To}");
var outPath = Path.Combine(OutputFolder, directive.To);
2019-11-09 21:45:10 +00:00
if(File.Exists(outPath)) File.Delete(outPath);
File.WriteAllBytes(outPath, LoadBytesFromPath(directive.SourceDataID));
});
}
2019-11-09 20:40:25 +00:00
}
}