mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge pull request #793 from wabbajack-tools/game-sourced-downloads
Game sourced downloads
This commit is contained in:
commit
5d71fc4618
@ -19,7 +19,8 @@ namespace Wabbajack.CLI
|
||||
typeof(DeleteFile),
|
||||
typeof(Changelog),
|
||||
typeof(FindSimilar),
|
||||
typeof(BSADump)
|
||||
typeof(BSADump),
|
||||
typeof(MigrateGameFolderFiles)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace Wabbajack.CLI
|
||||
(Changelog opts) => opts.Execute(),
|
||||
(FindSimilar opts) => opts.Execute(),
|
||||
(BSADump opts) => opts.Execute(),
|
||||
(MigrateGameFolderFiles opts) => opts.Execute(),
|
||||
errs => 1);
|
||||
}
|
||||
}
|
||||
|
25
Wabbajack.CLI/Verbs/MigrateGameFolderFiles.cs
Normal file
25
Wabbajack.CLI/Verbs/MigrateGameFolderFiles.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Threading.Tasks;
|
||||
using CommandLine;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Tasks;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs
|
||||
{
|
||||
[Verb("migrate-game-folder", HelpText = "Migrates game files into the 'Game Folder Files' in a MO2 directory")]
|
||||
public class MigrateGameFolderFiles : AVerb
|
||||
{
|
||||
[IsDirectory(CustomMessage = "Downloads folder at %1 does not exist!")]
|
||||
[Option('i', "input", HelpText = "Input Mod Organizer 2 Folder", Required = true)]
|
||||
public string MO2Folder { get; set; } = "";
|
||||
|
||||
|
||||
protected override async Task<ExitCode> Run()
|
||||
{
|
||||
if (await MigrateGameFolder.Execute((AbsolutePath)MO2Folder))
|
||||
{
|
||||
return ExitCode.Ok;
|
||||
};
|
||||
return ExitCode.Error;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,5 +24,20 @@ namespace Wabbajack.Common.Test
|
||||
|
||||
tempFile.Path.MoveTo(tempFile2.Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGetTopParentOfPath()
|
||||
{
|
||||
var path = (RelativePath)"foo/bar";
|
||||
Assert.Equal((RelativePath)"foo", path.TopParent);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanGetTopParentOfSinglePath()
|
||||
{
|
||||
var path = (RelativePath)"foo";
|
||||
Assert.Equal((RelativePath)"foo", path.TopParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ namespace Wabbajack.Common
|
||||
|
||||
public AbsolutePath? TryGetGameLocation()
|
||||
{
|
||||
return Consts.TestMode ? AbsolutePath.GetCurrentDirectory() : StoreHandler.Instance.TryGetGamePath(Game);
|
||||
return StoreHandler.Instance.TryGetGamePath(Game);
|
||||
}
|
||||
|
||||
public bool TryGetGameLocation(out AbsolutePath path)
|
||||
@ -180,10 +180,20 @@ namespace Wabbajack.Common
|
||||
|
||||
result = GetByMO2ArchiveName(someName);
|
||||
if (result != null) return result;
|
||||
|
||||
result = GetByMO2Name(someName);
|
||||
if (result != null) return result;
|
||||
|
||||
|
||||
return int.TryParse(someName, out int id) ? GetBySteamID(id) : null;
|
||||
}
|
||||
|
||||
private static GameMetaData? GetByMO2Name(string gameName)
|
||||
{
|
||||
gameName = gameName.ToLower();
|
||||
return Games.Values.FirstOrDefault(g => g.MO2Name?.ToLower() == gameName);
|
||||
}
|
||||
|
||||
public static bool TryGetByFuzzyName(string someName, [MaybeNullWhen(false)] out GameMetaData gameMetaData)
|
||||
{
|
||||
var result = TryGetByFuzzyName(someName);
|
||||
|
@ -6,6 +6,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
@ -208,12 +209,10 @@ namespace Wabbajack.Common
|
||||
|
||||
public RelativePath RelativeTo(AbsolutePath p)
|
||||
{
|
||||
if (_path.Substring(0, p._path.Length + 1) != p._path + "\\")
|
||||
{
|
||||
throw new InvalidDataException("Not a parent path");
|
||||
}
|
||||
|
||||
return new RelativePath(_path.Substring(p._path.Length + 1));
|
||||
var relPath = Path.GetRelativePath(p._path, _path);
|
||||
if (relPath == _path)
|
||||
throw new ArgumentException($"{_path} is not a subpath of {p._path}");
|
||||
return new RelativePath(relPath);
|
||||
}
|
||||
|
||||
public async Task<string> ReadAllTextAsync()
|
||||
@ -342,6 +341,31 @@ namespace Wabbajack.Common
|
||||
{
|
||||
File.Copy(_path, dest._path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
|
||||
private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
|
||||
|
||||
public bool HardLinkTo(AbsolutePath destination)
|
||||
{
|
||||
Utils.Log($"Hard Linking {_path} to {destination}");
|
||||
return CreateHardLink((string)destination, (string)this, IntPtr.Zero);
|
||||
}
|
||||
|
||||
public async ValueTask HardLinkIfOversize(AbsolutePath destination)
|
||||
{
|
||||
if (!destination.Parent.Exists)
|
||||
destination.Parent.CreateDirectory();
|
||||
|
||||
if (Root == destination.Root && Consts.SupportedBSAs.Contains(Extension))
|
||||
{
|
||||
if (HardLinkTo(destination))
|
||||
return;
|
||||
}
|
||||
|
||||
await CopyToAsync(destination);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> ReadAllLinesAsync()
|
||||
{
|
||||
@ -350,7 +374,8 @@ namespace Wabbajack.Common
|
||||
|
||||
public byte[] ReadAllBytes()
|
||||
{
|
||||
return File.ReadAllBytes(_path);
|
||||
using var file = OpenShared();
|
||||
return file.ReadAll();
|
||||
}
|
||||
|
||||
public static AbsolutePath GetCurrentDirectory()
|
||||
@ -393,7 +418,7 @@ namespace Wabbajack.Common
|
||||
|
||||
public FileStream OpenShared()
|
||||
{
|
||||
return File.Open(_path, FileMode.Open, FileAccess.Read);
|
||||
return File.Open(_path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
public FileStream WriteShared()
|
||||
|
@ -155,7 +155,7 @@ namespace Wabbajack.Common.StoreHandlers
|
||||
if (!l.ContainsCaseInsensitive("\"installdir\""))
|
||||
return;
|
||||
|
||||
var path = new RelativePath($"common//{GetVdfValue(l)}").RelativeTo(u);
|
||||
var path = new RelativePath("common").Combine(GetVdfValue(l)).RelativeTo(u);
|
||||
if (path.Exists)
|
||||
game.Path = path;
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
@ -100,6 +101,15 @@ namespace Wabbajack.Lib
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
internal async Task<(RelativePath, AbsolutePath)> IncludeString(string str)
|
||||
{
|
||||
var id = IncludeId();
|
||||
var fullPath = ModListOutputFolder.Combine(id);
|
||||
await fullPath.WriteAllTextAsync(str);
|
||||
return (id, fullPath);
|
||||
}
|
||||
|
||||
public async Task<bool> GatherMetaData()
|
||||
{
|
||||
Utils.Log($"Getting meta data for {SelectedArchives.Count} archives");
|
||||
|
@ -29,9 +29,11 @@ namespace Wabbajack.Lib
|
||||
public ModList ModList { get; private set; }
|
||||
public Dictionary<Hash, AbsolutePath> HashedArchives { get; } = new Dictionary<Hash, AbsolutePath>();
|
||||
|
||||
public GameMetaData Game { get; }
|
||||
|
||||
public SystemParameters? SystemParameters { get; set; }
|
||||
|
||||
public AInstaller(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters? parameters, int steps)
|
||||
public AInstaller(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters? parameters, int steps, Game game)
|
||||
: base(steps)
|
||||
{
|
||||
ModList = modList;
|
||||
@ -39,6 +41,7 @@ namespace Wabbajack.Lib
|
||||
OutputFolder = outputFolder;
|
||||
DownloadFolder = downloadFolder;
|
||||
SystemParameters = parameters;
|
||||
Game = game.MetaData();
|
||||
}
|
||||
|
||||
public void Info(string msg)
|
||||
@ -138,7 +141,7 @@ namespace Wabbajack.Lib
|
||||
|
||||
Status($"Copying files for {archive.Name}");
|
||||
|
||||
async ValueTask CopyFile(AbsolutePath from, AbsolutePath to, bool useMove)
|
||||
async ValueTask CopyFile(AbsolutePath from, AbsolutePath to)
|
||||
{
|
||||
if (to.Exists)
|
||||
{
|
||||
@ -153,11 +156,7 @@ namespace Wabbajack.Lib
|
||||
from.IsReadOnly = false;
|
||||
}
|
||||
|
||||
|
||||
if (useMove)
|
||||
from.MoveTo(to);
|
||||
else
|
||||
from.CopyTo(to);
|
||||
await @from.CopyToAsync(to);
|
||||
// If we don't do this, the file will use the last-modified date of the file when it was compressed
|
||||
// into an archive, which isn't really what we want in the case of files installed archives
|
||||
to.LastModified = DateTime.Now;
|
||||
@ -173,11 +172,18 @@ namespace Wabbajack.Lib
|
||||
}
|
||||
var firstDest = OutputFolder.Combine(group.First().To);
|
||||
|
||||
await group.Key.StagedFile.MoveTo(firstDest);
|
||||
|
||||
if (group.Key.IsNative)
|
||||
{
|
||||
await group.Key.AbsoluteName.HardLinkIfOversize(firstDest);
|
||||
}
|
||||
else
|
||||
{
|
||||
await group.Key.StagedFile.MoveTo(firstDest);
|
||||
}
|
||||
|
||||
foreach (var copy in group.Skip(1))
|
||||
{
|
||||
await CopyFile(firstDest, OutputFolder.Combine(copy.To), false);
|
||||
await CopyFile(firstDest, OutputFolder.Combine(copy.To));
|
||||
}
|
||||
});
|
||||
|
||||
@ -280,7 +286,9 @@ namespace Wabbajack.Lib
|
||||
|
||||
public async Task HashArchives()
|
||||
{
|
||||
var hashResults = await DownloadFolder.EnumerateFiles()
|
||||
var hashResults = await
|
||||
DownloadFolder.EnumerateFiles()
|
||||
.Concat(Game.GameLocation().EnumerateFiles())
|
||||
.Where(e => e.Extension != Consts.HashFileExtension)
|
||||
.PMap(Queue, async e => (await e.FileHashCachedAsync(), e));
|
||||
HashedArchives.SetTo(hashResults
|
||||
|
@ -17,17 +17,14 @@ namespace Wabbajack.Lib.CompilationSteps
|
||||
|
||||
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||
{
|
||||
if (_gameFolderFilesExists)
|
||||
{
|
||||
if (source.AbsolutePath.InFolder(_gameFolder))
|
||||
{
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = $"Ignoring game files because {Consts.GameFolderFilesDir} exists";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (!_gameFolderFilesExists) return null;
|
||||
|
||||
if (!source.AbsolutePath.InFolder(_gameFolder)) return null;
|
||||
|
||||
var result = source.EvolveTo<IgnoredDirectly>();
|
||||
result.Reason = $"Ignoring game files because {Consts.GameFolderFilesDir} exists";
|
||||
return result;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IState GetState()
|
||||
|
@ -94,5 +94,10 @@ namespace Wabbajack.Lib.Downloaders
|
||||
|
||||
public abstract string? GetManifestURL(Archive a);
|
||||
public abstract string[] GetMetaIni();
|
||||
|
||||
public string GetMetaIniString()
|
||||
{
|
||||
return string.Join("\n", GetMetaIni());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,13 +46,18 @@ namespace Wabbajack.Lib.Downloaders
|
||||
public Game Game { get; set; }
|
||||
public RelativePath GameFile { get; set; }
|
||||
public Hash Hash { get; set; }
|
||||
public string GameVersion { get; }
|
||||
public string GameVersion { get; set; } = "";
|
||||
|
||||
public State(string gameVersion)
|
||||
{
|
||||
GameVersion = gameVersion;
|
||||
}
|
||||
|
||||
public State()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
internal AbsolutePath SourcePath => Game.MetaData().GameLocation().Combine(GameFile);
|
||||
|
||||
@ -93,6 +98,7 @@ namespace Wabbajack.Lib.Downloaders
|
||||
{
|
||||
return new[] {"[General]", $"gameName={Game.MetaData().MO2ArchiveName}", $"gameFile={GameFile}"};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,17 @@
|
||||
using Compression.BSA;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.CompilationSteps;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.FileUploader;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.Validation;
|
||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
|
||||
using Game = Wabbajack.Common.Game;
|
||||
using Wabbajack.VirtualFileSystem;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
||||
namespace Wabbajack.Lib
|
||||
@ -105,7 +98,7 @@ namespace Wabbajack.Lib
|
||||
|
||||
var roots = new List<AbsolutePath>
|
||||
{
|
||||
MO2Folder, GamePath, MO2DownloadsFolder
|
||||
MO2Folder, GamePath, MO2DownloadsFolder, CompilingGame.GameLocation()
|
||||
};
|
||||
|
||||
// TODO: make this generic so we can add more paths
|
||||
@ -172,6 +165,7 @@ namespace Wabbajack.Lib
|
||||
UpdateTracker.NextStep("Pre-validating Archives");
|
||||
|
||||
|
||||
// Find all Downloads
|
||||
IndexedArchives = (await MO2DownloadsFolder.EnumerateFiles()
|
||||
.Where(f => f.WithExtension(Consts.MetaFileExtension).Exists)
|
||||
.PMap(Queue, async f => new IndexedArchive(VFS.Index.ByRootPath[f])
|
||||
@ -180,9 +174,37 @@ namespace Wabbajack.Lib
|
||||
IniData = f.WithExtension(Consts.MetaFileExtension).LoadIniFile(),
|
||||
Meta = await f.WithExtension(Consts.MetaFileExtension).ReadAllTextAsync()
|
||||
})).ToList();
|
||||
|
||||
|
||||
var stockGameFolder = CompilingGame.GameLocation();
|
||||
|
||||
foreach (var (relativePath, hash) in await ClientAPI.GetGameFiles(CompilingGame.Game, Version.Parse(CompilingGame.InstalledVersion)))
|
||||
{
|
||||
if (!VFS.Index.ByRootPath.TryGetValue(relativePath.RelativeTo(stockGameFolder), out var virtualFile))
|
||||
continue;
|
||||
if (virtualFile.Hash != hash)
|
||||
{
|
||||
Utils.Log(
|
||||
$"File {relativePath} int the game folder appears to be modified, it will not be used during compilation");
|
||||
continue;
|
||||
}
|
||||
|
||||
var state = new GameFileSourceDownloader.State
|
||||
{
|
||||
Game = CompilingGame.Game, GameVersion = CompilingGame.InstalledVersion, GameFile = relativePath
|
||||
};
|
||||
|
||||
Utils.Log($"Adding Game file: {relativePath}");
|
||||
IndexedArchives.Add(new IndexedArchive(virtualFile)
|
||||
{
|
||||
Name = (string)relativePath.FileName,
|
||||
IniData = state.GetMetaIniString().LoadIniString(),
|
||||
Meta = state.GetMetaIniString()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
await CleanInvalidArchives();
|
||||
|
||||
UpdateTracker.NextStep("Finding Install Files");
|
||||
@ -395,11 +417,13 @@ namespace Wabbajack.Lib
|
||||
await SelectedArchives.PMap(Queue, async a =>
|
||||
{
|
||||
var source = MO2DownloadsFolder.Combine(a.Name + Consts.MetaFileExtension);
|
||||
var ini = a.State.GetMetaIniString();
|
||||
var (id, fullPath) = await IncludeString(ini);
|
||||
InstallDirectives.Add(new ArchiveMeta
|
||||
{
|
||||
SourceDataID = await IncludeFile(source),
|
||||
SourceDataID = id,
|
||||
Size = source.Size,
|
||||
Hash = await source.FileHashAsync(),
|
||||
Hash = await fullPath.FileHashAsync(),
|
||||
To = source.FileName
|
||||
});
|
||||
});
|
||||
@ -523,9 +547,6 @@ namespace Wabbajack.Lib
|
||||
// Ignore the ModOrganizer.ini file it contains info created by MO2 on startup
|
||||
new IncludeStubbedConfigFiles(this),
|
||||
new IncludeLootFiles(this),
|
||||
new IgnoreStartsWith(this, Path.Combine((string)Consts.GameFolderFilesDir, "Data")),
|
||||
new IgnoreStartsWith(this, Path.Combine((string)Consts.GameFolderFilesDir, "Papyrus Compiler")),
|
||||
new IgnoreStartsWith(this, Path.Combine((string)Consts.GameFolderFilesDir, "Skyrim")),
|
||||
new IgnoreRegex(this, Consts.GameFolderFilesDir + "\\\\.*\\.bsa"),
|
||||
new IncludeRegex(this, "^[^\\\\]*\\.bat$"),
|
||||
new IncludeModIniData(this),
|
||||
|
@ -32,8 +32,6 @@ namespace Wabbajack.Lib
|
||||
|
||||
public AbsolutePath? GameFolder { get; set; }
|
||||
|
||||
public GameMetaData Game { get; }
|
||||
|
||||
public MO2Installer(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
|
||||
: base(
|
||||
archive: archive,
|
||||
@ -41,11 +39,15 @@ namespace Wabbajack.Lib
|
||||
outputFolder: outputFolder,
|
||||
downloadFolder: downloadFolder,
|
||||
parameters: parameters,
|
||||
steps: 20)
|
||||
steps: 20,
|
||||
game: modList.GameType)
|
||||
{
|
||||
Game = ModList.GameType.MetaData();
|
||||
var gameExe = Consts.GameFolderFilesDir.Combine(modList.GameType.MetaData().MainExecutable!);
|
||||
RedirectGamePath = modList.Directives.Any(d => d.To == gameExe);
|
||||
}
|
||||
|
||||
public bool RedirectGamePath { get; }
|
||||
|
||||
protected override async Task<bool> _Begin(CancellationToken cancel)
|
||||
{
|
||||
if (cancel.IsCancellationRequested) return false;
|
||||
@ -379,9 +381,12 @@ namespace Wabbajack.Lib
|
||||
{
|
||||
var data = Encoding.UTF8.GetString(await LoadBytesFromPath(directive.SourceDataID));
|
||||
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_BACK, (string)GameFolder!);
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_DOUBLE_BACK, ((string)GameFolder!).Replace("\\", "\\\\"));
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_FORWARD, ((string)GameFolder).Replace("\\", "/"));
|
||||
var gameFolder = (string)(RedirectGamePath ? Consts.GameFolderFilesDir.RelativeTo(OutputFolder) : GameFolder!);
|
||||
|
||||
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_BACK, gameFolder);
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_DOUBLE_BACK, gameFolder.Replace("\\", "\\\\"));
|
||||
data = data.Replace(Consts.GAME_PATH_MAGIC_FORWARD, gameFolder.Replace("\\", "/"));
|
||||
|
||||
data = data.Replace(Consts.MO2_PATH_MAGIC_BACK, (string)OutputFolder);
|
||||
data = data.Replace(Consts.MO2_PATH_MAGIC_DOUBLE_BACK, ((string)OutputFolder).Replace("\\", "\\\\"));
|
||||
|
53
Wabbajack.Lib/Tasks/MigrateGameFolder.cs
Normal file
53
Wabbajack.Lib/Tasks/MigrateGameFolder.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack.Lib.Tasks
|
||||
{
|
||||
public class MigrateGameFolder
|
||||
{
|
||||
public static async Task<bool> Execute(AbsolutePath mo2Folder)
|
||||
{
|
||||
var iniPath = mo2Folder.Combine(Consts.ModOrganizer2Ini);
|
||||
if (!iniPath.Exists)
|
||||
{
|
||||
Utils.Log($"Game folder conversion failed, {Consts.ModOrganizer2Ini} does not exist in {mo2Folder}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var newGamePath = mo2Folder.Combine(Consts.GameFolderFilesDir);
|
||||
newGamePath.CreateDirectory();
|
||||
var gameIni = iniPath.LoadIniFile();
|
||||
|
||||
if (!GameRegistry.TryGetByFuzzyName((string)gameIni.General.gameName, out var gameMeta))
|
||||
{
|
||||
Utils.Log($"Could not locate game for {gameIni.General.gameName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var orginGamePath = gameMeta.GameLocation();
|
||||
foreach (var file in gameMeta.GameLocation().EnumerateFiles())
|
||||
{
|
||||
var relPath = file.RelativeTo(orginGamePath);
|
||||
var newFile = relPath.RelativeTo(newGamePath);
|
||||
if (newFile.Exists)
|
||||
{
|
||||
Utils.Log($"Skipping {relPath} it already exists in the target path");
|
||||
continue;
|
||||
}
|
||||
|
||||
Utils.Log($"Copying/Linking {relPath}");
|
||||
await file.HardLinkIfOversize(newFile);
|
||||
}
|
||||
|
||||
Utils.Log("Remapping INI");
|
||||
var iniString = await iniPath.ReadAllTextAsync();
|
||||
iniString = iniString.Replace((string)orginGamePath, (string)newGamePath);
|
||||
iniString = iniString.Replace(((string)orginGamePath).Replace(@"\", @"\\"), ((string)newGamePath).Replace(@"\", @"\\"));
|
||||
iniString = iniString.Replace(((string)orginGamePath).Replace(@"\", @"/"), ((string)newGamePath).Replace(@"\", @"/"));
|
||||
await iniPath.WriteAllTextAsync(iniString);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -81,7 +81,7 @@ namespace Wabbajack.Lib.WebAutomation
|
||||
IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser? newBrowser)
|
||||
{
|
||||
// Block popups
|
||||
newBrowser = chromiumWebBrowser;
|
||||
newBrowser = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace Wabbajack.Lib.WebAutomation
|
||||
{
|
||||
|
||||
_browser = new ChromiumWebBrowser();
|
||||
|
||||
_driver = new CefSharpWrapper(_browser);
|
||||
}
|
||||
public static async Task<Driver> Create()
|
||||
|
@ -505,7 +505,7 @@ namespace Wabbajack.Test
|
||||
await folder.DeleteDirectory();
|
||||
folder.CreateDirectory();
|
||||
|
||||
var inst = new TestInstaller(default, null, default, folder, null);
|
||||
var inst = new TestInstaller(default, new ModList {GameType = Game.SkyrimSpecialEdition}, default, folder, null);
|
||||
|
||||
await inst.DownloadMissingArchives(archivesa, true);
|
||||
await inst.DownloadMissingArchives(archivesb, true);
|
||||
@ -551,7 +551,7 @@ namespace Wabbajack.Test
|
||||
class TestInstaller : AInstaller
|
||||
{
|
||||
public TestInstaller(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
|
||||
: base(archive, modList, outputFolder, downloadFolder, parameters, steps: 1)
|
||||
: base(archive, modList, outputFolder, downloadFolder, parameters, steps: 1, modList.GameType)
|
||||
{
|
||||
Queue.SetActiveThreadsObservable(Observable.Return(1));
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ namespace Wabbajack.Test
|
||||
DownloadAndInstall(Game.SkyrimSpecialEdition, 12604, "SkyUI"),
|
||||
DownloadAndInstall(Game.Fallout4, 11925, "Anti-Tank Rifle"),
|
||||
DownloadAndInstall(Game.SkyrimSpecialEdition, 4783, "Frost Armor UNP"),
|
||||
DownloadAndInstall(Game.Fallout4, 43474, "EM 2 Rifle No.9 Mk1"),
|
||||
DownloadAndInstall(Game.SkyrimSpecialEdition, 32359, "Frost Armor HDT"));
|
||||
|
||||
// We're going to fully patch this mod from another source.
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -159,31 +160,6 @@ namespace Wabbajack.Test
|
||||
Assert.False(extraFolder.Exists);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task CleanedESMTest()
|
||||
{
|
||||
var profile = utils.AddProfile();
|
||||
var mod = utils.AddMod("Cleaned ESMs");
|
||||
var updateEsm = utils.AddModFile(mod, @"Update.esm", 10);
|
||||
|
||||
await utils.Configure();
|
||||
|
||||
var gameFile = utils.GameFolder.Combine("Data", "Update.esm");
|
||||
utils.GenerateRandomFileData(gameFile, 20);
|
||||
|
||||
var modlist = await CompileAndInstall(profile);
|
||||
|
||||
utils.VerifyInstalledFile(mod, @"Update.esm");
|
||||
|
||||
var compiler = await ConfigureAndRunCompiler(profile);
|
||||
|
||||
// Update the file and verify that it throws an error.
|
||||
utils.GenerateRandomFileData(gameFile, 20);
|
||||
var exception = await Assert.ThrowsAsync<InvalidGameESMError>(async () => await Install(compiler));
|
||||
Assert.IsAssignableFrom<InvalidGameESMError>(exception);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SetScreenSizeTest()
|
||||
{
|
||||
@ -421,6 +397,40 @@ namespace Wabbajack.Test
|
||||
utils.VerifyInstalledFile(mod, @"baz.bsa");
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanSourceFilesFromStockGameFiles()
|
||||
{
|
||||
Consts.TestMode = false;
|
||||
|
||||
var profile = utils.AddProfile();
|
||||
var mod = utils.AddMod();
|
||||
var skyrimExe = utils.AddModFile(mod, @"Data\test.exe", 10);
|
||||
|
||||
var gameFolder = Consts.GameFolderFilesDir.RelativeTo(utils.MO2Folder);
|
||||
gameFolder.CreateDirectory();
|
||||
|
||||
var gameMeta = Game.SkyrimSpecialEdition.MetaData();
|
||||
await gameMeta.GameLocation().Combine(gameMeta.MainExecutable!).CopyToAsync(skyrimExe);
|
||||
await gameMeta.GameLocation().Combine(gameMeta.MainExecutable!).CopyToAsync(gameFolder.Combine(gameMeta.MainExecutable!));
|
||||
|
||||
await utils.Configure();
|
||||
|
||||
await CompileAndInstall(profile);
|
||||
|
||||
utils.VerifyInstalledFile(mod, @"Data\test.exe");
|
||||
|
||||
Assert.False("SkyrimSE.exe".RelativeTo(utils.DownloadsFolder).Exists, "File should not appear in the download folder because it should be copied from the game folder");
|
||||
|
||||
var file = "ModOrganizer.ini".RelativeTo(utils.InstallFolder);
|
||||
Assert.True(file.Exists);
|
||||
|
||||
var ini = file.LoadIniFile();
|
||||
Assert.Equal(((AbsolutePath)(string)ini?.General?.gamePath).Combine(gameMeta.MainExecutable),
|
||||
Consts.GameFolderFilesDir.Combine(gameMeta.MainExecutable).RelativeTo(utils.InstallFolder));
|
||||
|
||||
Consts.TestMode = true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NoMatchIncludeIncludesNonMatchingFiles()
|
||||
|
49
Wabbajack.Test/TasksTests.cs
Normal file
49
Wabbajack.Test/TasksTests.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Tasks;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Wabbajack.Test
|
||||
{
|
||||
public class TasksTests : ACompilerTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanRemapGameFolder()
|
||||
{
|
||||
await using var tempFolder = await TempFolder.Create();
|
||||
var gameff = tempFolder.Dir.Combine(Consts.GameFolderFilesDir);
|
||||
gameff.CreateDirectory();
|
||||
|
||||
await gameff.Combine("some_file.txt").WriteAllTextAsync("some_file");
|
||||
await gameff.Combine("steam_api64.dll").WriteAllTextAsync("steam_api");
|
||||
|
||||
|
||||
var meta = Game.SkyrimSpecialEdition.MetaData();
|
||||
await tempFolder.Dir.Combine(Consts.ModOrganizer2Ini)
|
||||
.WriteAllLinesAsync(
|
||||
"[General]",
|
||||
$"gameName={meta.MO2Name}",
|
||||
$"gamePath={meta.GameLocation()}",
|
||||
$"pathDouble={meta.GameLocation().ToString().Replace(@"\", @"\\")}",
|
||||
$"pathForward={meta.GameLocation().ToString().Replace(@"\", @"/")}");
|
||||
|
||||
Assert.True(await MigrateGameFolder.Execute(tempFolder.Dir));
|
||||
|
||||
Assert.Equal("some_file", await gameff.Combine("some_file.txt").ReadAllTextAsync());
|
||||
Assert.Equal("steam_api", await gameff.Combine("steam_api64.dll").ReadAllTextAsync());
|
||||
Assert.Equal(Hash.FromBase64("k5EWx/9Woqg="), await gameff.Combine(@"Data\Skyrim - Interface.bsa").FileHashAsync());
|
||||
|
||||
var ini = tempFolder.Dir.Combine(Consts.ModOrganizer2Ini).LoadIniFile();
|
||||
Assert.Equal(gameff, (AbsolutePath)(string)ini.General.gamePath);
|
||||
Assert.Equal(gameff, (AbsolutePath)(string)ini.General.pathDouble);
|
||||
Assert.Equal(gameff, (AbsolutePath)(string)ini.General.pathForward);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public TasksTests(ITestOutputHelper helper) : base(helper)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user