Only 149 errors left in Wabbajack.Lib

This commit is contained in:
Timothy Baldridge 2020-03-25 16:30:43 -06:00
parent 035e376a09
commit defbc15593
43 changed files with 316 additions and 308 deletions

View File

@ -53,10 +53,10 @@ namespace Compression.BSA
}
}
public void Build(string filename)
public void Build(AbsolutePath filename)
{
SortEntries();
using (var fs = File.Open(filename, FileMode.Create))
using (var fs = filename.Create())
using (var bw = new BinaryWriter(fs))
{
bw.Write(Encoding.ASCII.GetBytes(_state.HeaderMagic));

View File

@ -91,10 +91,10 @@ namespace Compression.BSA
}
}
public void Build(string outputName)
public void Build(AbsolutePath outputName)
{
RegenFolderRecords();
using (var fs = File.Open(outputName, FileMode.Create))
using (var fs = outputName.Create())
using (var wtr = new BinaryWriter(fs))
{
wtr.Write(_fileId);

View File

@ -18,7 +18,7 @@ namespace Compression.BSA
public interface IBSABuilder : IDisposable
{
void AddFile(FileStateObject state, Stream src);
void Build(string filename);
void Build(AbsolutePath filename);
}
public class ArchiveStateObject

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using Wabbajack.Common;
using File = Alphaleonis.Win32.Filesystem.File;
namespace Compression.BSA
@ -22,9 +23,9 @@ namespace Compression.BSA
_files[state.Index] = (cstate, src);
}
public void Build(string filename)
public void Build(AbsolutePath filename)
{
using var fs = File.Create(filename);
using var fs = filename.Create();
using var bw = new BinaryWriter(fs);
bw.Write(_state.VersionNumber);

View File

@ -12,10 +12,10 @@ namespace Wabbajack.Common
{
public static bool TestMode { get; set; } = false;
public static string GameFolderFilesDir = "Game Folder Files";
public static string ManualGameFilesDir = "Manual Game Files";
public static string LOOTFolderFilesDir = "LOOT Config Files";
public static string BSACreationDir = "TEMP_BSA_FILES";
public static RelativePath GameFolderFilesDir = (RelativePath)"Game Folder Files";
public static RelativePath ManualGameFilesDir = (RelativePath)"Manual Game Files";
public static RelativePath LOOTFolderFilesDir = (RelativePath)"LOOT Config Files";
public static RelativePath BSACreationDir = (RelativePath)"TEMP_BSA_FILES";
public static string ModListDownloadFolder = "downloaded_mod_lists";
@ -109,7 +109,7 @@ namespace Wabbajack.Common
public static string WabbajackCacheHostname = "build.wabbajack.org";
public static int WabbajackCachePort = 80;
public static int MaxHTTPRetries = 4;
public const string MO2ModFolderName = "mods";
public static RelativePath MO2ModFolderName = (RelativePath)"mods";
public static AbsolutePath PatchCacheFolder => LocalAppDataPath.Combine("patch_cache");
public static int MaxConnectionsPerServer = 4;
@ -119,8 +119,11 @@ namespace Wabbajack.Common
public static AbsolutePath LogFile = LogsFolder.Combine(EntryPoint.FileNameWithoutExtension + ".current.log");
public static int MaxOldLogs = 50;
public static Extension BSA = new Extension(".BSA");
public static Extension MOHIDDEN = new Extension(".mohidden");
public static AbsolutePath SettingsFile => LocalAppDataPath.Combine("settings.json");
public static RelativePath SettingsIni = (RelativePath)"settings.ini";
public static byte SettingsVersion => 1;
public static RelativePath ModListTxt = (RelativePath)"modlist.txt";
}
}

View File

@ -90,7 +90,7 @@ namespace Wabbajack.Common
if (MainExecutable == null)
throw new NotImplementedException();
return FileVersionInfo.GetVersionInfo(Path.Combine(GameLocation(), MainExecutable)).ProductVersion;
return FileVersionInfo.GetVersionInfo((string)GameLocation()?.Combine(MainExecutable)).ProductVersion;
}
}
@ -98,9 +98,9 @@ namespace Wabbajack.Common
public string MainExecutable { get; internal set; }
public string GameLocation()
public AbsolutePath? GameLocation()
{
return Consts.TestMode ? Directory.GetCurrentDirectory() : StoreHandler.Instance.GetGamePath(Game);
return Consts.TestMode ? AbsolutePath.GetCurrentDirectory() : StoreHandler.Instance.GetGamePath(Game);
}
}

View File

@ -212,9 +212,9 @@ namespace Wabbajack.Common
}
}
public bool InFolder(AbsolutePath gameFolder)
public bool InFolder(AbsolutePath folder)
{
throw new NotImplementedException();
return _path.StartsWith(folder._path + Path.DirectorySeparator);
}
public async Task<byte[]> ReadAllBytesAsync()
@ -295,6 +295,18 @@ namespace Wabbajack.Common
{
return File.ReadAllBytes(_path);
}
public static AbsolutePath GetCurrentDirectory()
{
return new AbsolutePath(Directory.GetCurrentDirectory());
}
public async Task CopyToAsync(AbsolutePath destFile)
{
await using var src = OpenRead();
await using var dest = destFile.Create();
await src.CopyToAsync(dest);
}
}
public struct RelativePath : IPath, IEquatable<RelativePath>
@ -367,6 +379,8 @@ namespace Wabbajack.Common
public RelativePath FileName => new RelativePath(Path.GetFileName(_path));
public RelativePath FileNameWithoutExtension => (RelativePath)Path.GetFileNameWithoutExtension(_path);
public bool Equals(RelativePath other)
{
return _path == other._path;
@ -391,6 +405,16 @@ namespace Wabbajack.Common
{
return _path.StartsWith(s);
}
public bool StartsWith(RelativePath s)
{
return _path.StartsWith(s._path);
}
public RelativePath Combine(params RelativePath[] paths )
{
return (RelativePath)Path.Combine(paths.Select(p => (string)p).Cons(_path).ToArray());
}
}
public static partial class Utils
@ -615,7 +639,7 @@ namespace Wabbajack.Common
}
}
public struct FullPath : IEquatable<FullPath>
public struct FullPath : IEquatable<FullPath>, IPath
{
public AbsolutePath Base { get; }
public RelativePath[] Paths { get; }
@ -675,5 +699,7 @@ namespace Wabbajack.Common
{
return obj is FullPath other && Equals(other);
}
public RelativePath FileName => Paths.Length == 0 ? Base.FileName : Paths.Last().FileName;
}
}

View File

@ -11,7 +11,7 @@ namespace Wabbajack.Common.StoreHandlers
{
public override Game Game { get; internal set; }
public override string Name { get; internal set; }
public override string Path { get; internal set; }
public override AbsolutePath Path { get; internal set; }
public override int ID { get; internal set; }
public override StoreType Type { get; internal set; } = StoreType.GOG;
}
@ -101,7 +101,7 @@ namespace Wabbajack.Common.StoreHandlers
{
ID = gameID,
Name = gameName,
Path = path
Path = (AbsolutePath)path
};
var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g => (g.GOGIDs?.Contains(gameID) ?? false));

View File

@ -12,7 +12,7 @@ namespace Wabbajack.Common.StoreHandlers
{
public override Game Game { get; internal set; }
public override string Name { get; internal set; }
public override string Path { get; internal set; }
public override AbsolutePath Path { get; internal set; }
public override int ID { get; internal set; }
public override StoreType Type { get; internal set; } = StoreType.STEAM;
@ -153,15 +153,15 @@ namespace Wabbajack.Common.StoreHandlers
var path = Path.Combine(u, "common", GetVdfValue(l));
if (Directory.Exists(path))
game.Path = path;
game.Path = (AbsolutePath)path;
});
if (!gotID || !Directory.Exists(game.Path)) return;
if (!gotID || !game.Path.IsDirectory) return;
var gameMeta = GameRegistry.Games.Values.FirstOrDefault(g =>
{
return (g.SteamIDs?.Contains(game.ID) ?? false)
&& (g.RequiredFiles?.TrueForAll(file => File.Exists(Path.Combine(game.Path, file))) ?? true);
&& (g.RequiredFiles?.TrueForAll(file => game.Path.Combine(file).Exists) ?? true);
});
if (gameMeta == null)

View File

@ -52,27 +52,17 @@ namespace Wabbajack.Common.StoreHandlers
}
}
public string GetGamePath(Game game)
public AbsolutePath? GetGamePath(Game game)
{
return StoreGames.FirstOrDefault(g => g.Game == game)?.Path;
}
public string GetGamePath(Game game, StoreType type)
{
return StoreGames.FirstOrDefault(g => g.Type == type && g.Game == game)?.Path;
}
public string GetGamePath(int id)
{
return StoreGames.FirstOrDefault(g => g.ID == id)?.Path;
}
}
public abstract class AStoreGame
{
public abstract Game Game { get; internal set; }
public abstract string Name { get; internal set; }
public abstract string Path { get; internal set; }
public abstract AbsolutePath Path { get; internal set; }
public abstract int ID { get; internal set; }
public abstract StoreType Type { get; internal set; }
}

View File

@ -20,7 +20,7 @@ namespace Wabbajack.Lib
public abstract class ACompiler : ABatchProcessor
{
public string ModListName, ModListAuthor, ModListDescription, ModListWebsite;
public RelativePath ModListImage, ModListReadme;
public AbsolutePath ModListImage, ModListReadme;
public bool ReadmeIsWebsite;
protected Version WabbajackVersion;
@ -91,10 +91,10 @@ namespace Wabbajack.Lib
return id;
}
internal RelativePath IncludeFile(AbsolutePath data)
internal async Task<RelativePath> IncludeFile(AbsolutePath data)
{
var id = IncludeId();
data.Copy(ModListOutputFolder.Combine(id));
await data.CopyToAsync(ModListOutputFolder.Combine(id));
return id;
}
@ -124,57 +124,53 @@ namespace Wabbajack.Lib
Utils.Log($"Exporting ModList to {ModListOutputFile}");
// Modify readme and ModList image to relative paths if they exist
if (File.Exists(ModListImage))
if (ModListImage.Exists)
{
ModList.Image = "modlist-image.png";
ModList.Image = (RelativePath)"modlist-image.png";
}
if (File.Exists(ModListReadme))
if (ModListReadme.Exists)
{
var readme = new FileInfo(ModListReadme);
ModList.Readme = $"readme{readme.Extension}";
ModList.Readme = $"readme{ModListReadme.Extension}";
}
ModList.ReadmeIsWebsite = ReadmeIsWebsite;
using (var of = File.Create(Path.Combine(ModListOutputFolder, "modlist")))
using (var of = ModListOutputFolder.Combine("modlist").Create())
of.WriteAsMessagePack(ModList);
if (File.Exists(ModListOutputFile))
File.Delete(ModListOutputFile);
ModListOutputFile.Delete();
using (var fs = new FileStream(ModListOutputFile, FileMode.Create))
using (var fs = ModListOutputFile.Create())
{
using (var za = new ZipArchive(fs, ZipArchiveMode.Create))
{
Directory.EnumerateFiles(ModListOutputFolder, "*.*")
ModListOutputFolder.EnumerateFiles()
.DoProgress("Compressing ModList",
f =>
{
var ze = za.CreateEntry(Path.GetFileName(f));
using (var os = ze.Open())
using (var ins = File.OpenRead(f))
{
ins.CopyTo(os);
}
var ze = za.CreateEntry((string)f.FileName);
using var os = ze.Open();
using var ins = f.OpenRead();
ins.CopyTo(os);
});
// Copy in modimage
if (File.Exists(ModListImage))
if (ModListImage.Exists)
{
var ze = za.CreateEntry(ModList.Image);
var ze = za.CreateEntry((string)ModList.Image);
using (var os = ze.Open())
using (var ins = File.OpenRead(ModListImage))
using (var ins = ModListImage.OpenRead())
{
ins.CopyTo(os);
}
}
// Copy in readme
if (File.Exists(ModListReadme))
if (ModListReadme.Exists)
{
var ze = za.CreateEntry(ModList.Readme);
using (var os = ze.Open())
using (var ins = File.OpenRead(ModListReadme))
using (var ins = ModListReadme.OpenRead())
{
ins.CopyTo(os);
}
@ -185,7 +181,7 @@ namespace Wabbajack.Lib
Utils.Log("Exporting ModList metadata");
var metadata = new DownloadMetadata
{
Size = File.GetSize(ModListOutputFile),
Size = ModListOutputFile.Size,
Hash = ModListOutputFile.FileHash(),
NumberOfArchives = ModList.Archives.Count,
SizeOfArchives = ModList.Archives.Sum(a => a.Size),
@ -210,7 +206,7 @@ namespace Wabbajack.Lib
Info("Building a list of archives based on the files required");
var hashes = InstallDirectives.OfType<FromArchive>()
.Select(a => Hash.FromBase64(a.ArchiveHashPath[0]))
.Select(a => a.Hash)
.Distinct();
var archives = IndexedArchives.OrderByDescending(f => f.File.LastModified)

View File

@ -56,17 +56,18 @@ namespace Wabbajack.Lib
throw new Exception(msg);
}
public byte[] LoadBytesFromPath(string path)
public async Task<byte[]> LoadBytesFromPath(RelativePath path)
{
using (var fs = new FileStream(ModListArchive, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
using (var ms = new MemoryStream())
await using var fs = new FileStream(ModListArchive, FileMode.Open, FileAccess.Read, FileShare.Read);
using var ar = new ZipArchive(fs, ZipArchiveMode.Read);
await using var ms = new MemoryStream();
var entry = ar.GetEntry((string)path);
await using (var e = entry.Open())
{
var entry = ar.GetEntry(path);
using (var e = entry.Open())
e.CopyTo(ms);
return ms.ToArray();
await e.CopyToAsync(ms);
}
return ms.ToArray();
}
public static ModList LoadFromFile(string path)
@ -190,7 +191,7 @@ namespace Wabbajack.Lib
Status($"Patching {toPatch.To.FileName}");
// Read in the patch data
byte[] patchData = LoadBytesFromPath(toPatch.PatchID);
byte[] patchData = await LoadBytesFromPath(toPatch.PatchID);
var toFile = OutputFolder.Combine(toPatch.To);
var oldData = new MemoryStream(await toFile.ReadAllBytesAsync());

View File

@ -12,7 +12,7 @@ namespace Wabbajack.Lib.CompilationSteps.CompilationErrors
public class InvalidGameESMError : AErrorMessage
{
public Hash Hash { get; }
public string PathToFile { get; }
public AbsolutePath PathToFile { get; }
private readonly CleanedESM _esm;
public RelativePath GameFileName => _esm.To.FileName;
public override string ShortDescription
@ -30,7 +30,7 @@ the modlist expecting a different of the game than you currently have installed,
the game, and then attempting to re-install this modlist. Also verify that the version of the game you have installed matches the version expected by this modlist.";
}
public InvalidGameESMError(CleanedESM esm, Hash hash, string path)
public InvalidGameESMError(CleanedESM esm, Hash hash, AbsolutePath path)
{
Hash = hash;
PathToFile = path;

View File

@ -53,10 +53,10 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!Consts.SupportedBSAs.Contains(Path.GetExtension(source.Path).ToLower())) return null;
if (!Consts.SupportedBSAs.Contains(source.Path.Extension)) return null;
var defaultInclude = false;
if (source.Path.StartsWith(Consts.MO2ModFolderName))
if (source.Path.RelativeTo(_mo2Compiler.MO2Folder).InFolder(_mo2Compiler.MO2Folder.Combine(Consts.MO2ModFolderName)))
if (_includeDirectly.Any(path => source.Path.StartsWith(path)))
defaultInclude = true;
@ -66,7 +66,7 @@ namespace Wabbajack.Lib.CompilationSteps
var id = Guid.NewGuid().ToString();
var matches = await sourceFiles.PMap(_mo2Compiler.Queue, e => _mo2Compiler.RunStack(stack, new RawSourceFile(e, Path.Combine(Consts.BSACreationDir, id, e.Name))));
var matches = await sourceFiles.PMap(_mo2Compiler.Queue, e => _mo2Compiler.RunStack(stack, new RawSourceFile(e, Consts.BSACreationDir.Combine((RelativePath)id, e.Name.FileName))));
foreach (var match in matches)
@ -82,7 +82,7 @@ namespace Wabbajack.Lib.CompilationSteps
directive = new CreateBSA
{
To = source.Path,
TempID = id,
TempID = (RelativePath)id,
State = bsa.State,
FileStates = bsa.Files.Select(f => f.State).ToList()
};

View File

@ -22,7 +22,7 @@ namespace Wabbajack.Lib.CompilationSteps
.Where(line => line.StartsWith("+") || line.EndsWith("_separator"))
.Select(line => line.Substring(1))
.Concat(alwaysEnabled)
.Select(line => Path.Combine(Consts.MO2ModFolderName, line) + "\\")
.Select(line => Path.Combine((string)Consts.MO2ModFolderName, line) + "\\")
.ToList();
}

View File

@ -11,40 +11,41 @@ namespace Wabbajack.Lib.CompilationSteps
{
public class IncludePatches : ACompilationStep
{
private readonly Dictionary<string, IGrouping<string, VirtualFile>> _indexed;
private readonly Dictionary<RelativePath, IGrouping<RelativePath, VirtualFile>> _indexed;
private VirtualFile _bsa;
private Dictionary<string, VirtualFile> _indexedByName;
private Dictionary<RelativePath, VirtualFile> _indexedByName;
public IncludePatches(ACompiler compiler, VirtualFile constructingFromBSA = null) : base(compiler)
{
_bsa = constructingFromBSA;
_indexed = _compiler.IndexedFiles.Values
.SelectMany(f => f)
.GroupBy(f => Path.GetFileName(f.Name).ToLower())
.GroupBy(f => f.Name.FileName)
.ToDictionary(f => f.Key);
_indexedByName = _indexed.Values
.SelectMany(s => s)
.Where(f => f.IsNative)
.ToDictionary(f => Path.GetFileName(f.FullPath));
.ToDictionary(f => f.FullPath.FileName);
}
public override async ValueTask<Directive> Run(RawSourceFile source)
{
var name = Path.GetFileName(source.File.Name.ToLower());
string nameWithoutExt = name;
if (Path.GetExtension(name) == ".mohidden")
nameWithoutExt = Path.GetFileNameWithoutExtension(name);
var name = source.File.Name.FileName;
RelativePath nameWithoutExt = name;
if (name.Extension == Consts.MOHIDDEN)
nameWithoutExt = name.FileNameWithoutExtension;
if (!_indexed.TryGetValue(Path.GetFileName(name), out var choices))
_indexed.TryGetValue(Path.GetFileName(nameWithoutExt), out choices);
if (!_indexed.TryGetValue(name, out var choices))
_indexed.TryGetValue(nameWithoutExt, out choices);
dynamic mod_ini;
if (_bsa == null)
mod_ini = ((MO2Compiler)_compiler).ModMetas.FirstOrDefault(f => source.Path.StartsWith(f.Key)).Value;
else
{
var bsa_path = _bsa.FullPath.RelativeTo(((MO2Compiler)_compiler).MO2Folder);
mod_ini = ((MO2Compiler)_compiler).ModMetas.FirstOrDefault(f => bsa_path.StartsWith(f.Key)).Value;
var bsa_path = _bsa.FullPath.Paths.Last().RelativeTo(((MO2Compiler)_compiler).MO2Folder);
mod_ini = ((MO2Compiler)_compiler).ModMetas.FirstOrDefault(f => ((string)bsa_path).StartsWith(f.Key)).Value;
}
var installationFile = mod_ini?.General?.installationFile;
@ -55,7 +56,7 @@ namespace Wabbajack.Lib.CompilationSteps
if (choices != null)
{
found = choices.FirstOrDefault(
f => Path.GetFileName(f.FilesInFullPath.First().Name) == installationFile);
f => f.FilesInFullPath.First().Name.FileName == installationFile);
}
// Find based on file name only (not ext)
@ -67,11 +68,10 @@ namespace Wabbajack.Lib.CompilationSteps
}
// Find based on matchAll=<archivename> in [General] in meta.ini
var matchAllName = (string)mod_ini?.General?.matchAll;
var matchAllName = (RelativePath?)mod_ini?.General?.matchAll;
if (matchAllName != null)
{
matchAllName = matchAllName.Trim();
if (_indexedByName.TryGetValue(matchAllName, out var arch))
if (_indexedByName.TryGetValue(matchAllName.Value, out var arch))
{
// Just match some file in the archive based on the smallest delta difference
found = arch.ThisAndAllChildren
@ -92,7 +92,7 @@ namespace Wabbajack.Lib.CompilationSteps
Utils.TryGetPatch(found.Hash, source.File.Hash, out var data);
if (data != null)
e.PatchID = _compiler.IncludeFile(data);
e.PatchID = await _compiler.IncludeFile(data);
return e;
}

View File

@ -25,16 +25,16 @@ namespace Wabbajack.Lib.CompilationSteps
var isBanner = source.AbsolutePath == _compiler.ModListImage;
//var isReadme = source.AbsolutePath == ModListReadme;
var result = source.EvolveTo<PropertyFile>();
result.SourceDataID = await _compiler.IncludeFile(source.AbsolutePath.ReadAllBytesAsync());
result.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
if (isBanner)
{
result.Type = PropertyType.Banner;
_compiler.ModListImage = result.SourceDataID;
_compiler.ModListImage = result.SourceDataID.RelativeTo(_compiler.ModListOutputFolder);
}
else
{
result.Type = PropertyType.Readme;
_compiler.ModListReadme = result.SourceDataID;
_compiler.ModListReadme = result.SourceDataID.RelativeTo(_compiler.ModListOutputFolder);
}
return result;

View File

@ -20,12 +20,12 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!_regex.IsMatch(source.Path))
if (!_regex.IsMatch((string)source.Path))
return null;
try
{
var lines = File.ReadAllLines(source.AbsolutePath);
var lines = await source.AbsolutePath.ReadAllLinesAsync();
var id = 0;
lines.Where(l => l.StartsWith("itemID=")).Do(l => int.TryParse(l.Replace("itemID=", ""), out id));
if (id == 0)
@ -37,7 +37,7 @@ namespace Wabbajack.Lib.CompilationSteps
return null;
var fromSteam = source.EvolveTo<SteamMeta>();
fromSteam.SourceDataID = _compiler.IncludeFile(source.AbsolutePath);
fromSteam.SourceDataID = await _compiler.IncludeFile(source.AbsolutePath);
fromSteam.ItemID = item.ItemID;
fromSteam.Size = item.Size;
return fromSteam;

View File

@ -46,7 +46,7 @@ namespace Wabbajack.Lib.CompilationSteps
if (data == originalData)
return null;
var result = source.EvolveTo<RemappedInlineFile>();
result.SourceDataID = _compiler.IncludeFile(Encoding.UTF8.GetBytes(data));
result.SourceDataID = await _compiler.IncludeFile(Encoding.UTF8.GetBytes(data));
return result;
}

View File

@ -33,7 +33,7 @@ namespace Wabbajack.Lib.CompilationSteps
{
if (!source.Path.StartsWith(modpath)) continue;
var result = source.EvolveTo<InlineFile>();
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
result.SourceDataID = await _compiler.IncludeFile(source.AbsolutePath);
return result;
}

View File

@ -4,6 +4,7 @@ using System.Text;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using Newtonsoft.Json;
using Wabbajack.Common;
namespace Wabbajack.Lib.CompilationSteps
{
@ -22,12 +23,12 @@ namespace Wabbajack.Lib.CompilationSteps
{
if (_correctProfiles.Any(p => source.Path.StartsWith(p)))
{
var data = source.Path.EndsWith("\\modlist.txt")
? ReadAndCleanModlist(source.AbsolutePath)
: File.ReadAllBytes(source.AbsolutePath);
var data = source.Path.FileName == Consts.ModListTxt
? await ReadAndCleanModlist(source.AbsolutePath)
: await source.AbsolutePath.ReadAllBytesAsync();
var e = source.EvolveTo<InlineFile>();
e.SourceDataID = _compiler.IncludeFile(data);
e.SourceDataID = await _compiler.IncludeFile(data);
return e;
}
@ -39,12 +40,11 @@ namespace Wabbajack.Lib.CompilationSteps
return new State();
}
private static byte[] ReadAndCleanModlist(string absolutePath)
private static async Task<byte[]> ReadAndCleanModlist(AbsolutePath absolutePath)
{
var lines = File.ReadAllLines(absolutePath);
lines = (from line in lines
where !(line.StartsWith("-") && !line.EndsWith("_separator"))
select line).ToArray();
var lines = await absolutePath.ReadAllLinesAsync();
lines = lines.Where(line => !(line.StartsWith("-") && !line.EndsWith("_separator")))
.ToArray();
return Encoding.UTF8.GetBytes(string.Join("\r\n", lines));
}

View File

@ -14,22 +14,24 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
// * TODO I don't know what this does
/*
var l = new List<string> {"vortex.deployment.msgpack", "vortex.deployment.json"};
if (!l.Any(a => source.Path.Contains(a))) return null;
if (!l.Any(a => ((string)source.Path).Contains(a))) return null;
var inline = source.EvolveTo<InlineFile>();
inline.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
if (!source.Path.Contains("vortex.deployment.json"))
if (!((string)source.Path).Contains("vortex.deployment.json"))
return inline;
var path = source.Path;
if (!path.StartsWith(Consts.GameFolderFilesDir))
return inline;
*/
//path = ((string)path).Substring(Consts.GameFolderFilesDir.Length + 1);
//path = $"{Consts.ManualGameFilesDir}\\{path}";
//inline.To = path;
path = path.Substring(Consts.GameFolderFilesDir.Length + 1);
path = $"{Consts.ManualGameFilesDir}\\{path}";
inline.To = path;
return inline;
return null;
}
public override IState GetState()

View File

@ -73,7 +73,7 @@ namespace Wabbajack.Lib
/// Hash of the banner-image
/// </summary>
[Key(5)]
public string Image;
public RelativePath Image;
/// <summary>
/// The Mod Manager used to create the modlist
@ -229,7 +229,7 @@ namespace Wabbajack.Lib
public class CreateBSA : Directive
{
[Key(3)]
public string TempID { get; set; }
public RelativePath TempID { get; set; }
[Key(4)]
public ArchiveStateObject State { get; set; }
[Key(5)]
@ -246,7 +246,7 @@ namespace Wabbajack.Lib
/// The file to apply to the source file to patch it
/// </summary>
[Key(5)]
public string PatchID { get; set; }
public RelativePath PatchID { get; set; }
}
[MessagePackObject]
@ -262,7 +262,7 @@ namespace Wabbajack.Lib
public class MergedPatch : Directive
{
[Key(3)]
public string PatchID { get; set; }
public RelativePath PatchID { get; set; }
[Key(4)]
public List<SourcePatch> Sources { get; set; }
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using MessagePack;
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
namespace Wabbajack.Lib.Downloaders
@ -96,15 +97,12 @@ namespace Wabbajack.Lib.Downloaders
/// Downloads this file to the given destination location
/// </summary>
/// <param name="destination"></param>
public abstract Task<bool> Download(Archive a, string destination);
public abstract Task<bool> Download(Archive a, AbsolutePath destination);
public async Task<bool> Download(string destination)
public async Task<bool> Download(AbsolutePath destination)
{
var path = Path.GetDirectoryName(destination);
if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
Directory.CreateDirectory(path);
return await Download(new Archive {Name = Path.GetFileName(destination)}, destination);
destination.Parent.CreateDirectory();
return await Download(new Archive {Name = (string)destination.FileName}, destination);
}
/// <summary>

View File

@ -91,12 +91,12 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
await using var stream = await ResolveDownloadStream();
await using (var file = File.Open(destination, FileMode.Create))
await using (var file = destination.Create())
{
stream.CopyTo(file);
await stream.CopyToAsync(file);
}
return true;
}

View File

@ -66,7 +66,7 @@ namespace Wabbajack.Lib.Downloaders
public static async Task<BethesdaNetData> Login(Game game)
{
var metadata = game.MetaData();
var gamePath = Path.Combine(metadata.GameLocation(), metadata.MainExecutable);
var gamePath = metadata.GameLocation()?.Combine(metadata.MainExecutable);
var info = new ProcessStartInfo
{
FileName = @"Downloaders\BethesdaNet\bethnetlogin.exe",
@ -125,7 +125,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var (client, info, collected) = await ResolveDownloadInfo();
using var tf = new TempFile();
@ -151,16 +151,16 @@ namespace Wabbajack.Lib.Downloaders
}
}
file.Close();
await ConvertCKMToZip(file.Name, destination);
await ConvertCKMToZip((AbsolutePath)file.Name, destination);
return true;
}
private const uint CKM_Magic = 0x52415442; // BTAR
private async Task ConvertCKMToZip(string src, string dest)
private async Task ConvertCKMToZip(AbsolutePath src, AbsolutePath dest)
{
using var reader = new BinaryReader(File.OpenRead(src));
using var reader = new BinaryReader(src.OpenRead());
var magic = reader.ReadUInt32();
if (magic != CKM_Magic)
throw new InvalidDataException("Invalid magic format in CKM parsing");
@ -173,7 +173,7 @@ namespace Wabbajack.Lib.Downloaders
if (minorVersion < 2 || minorVersion > 4)
throw new InvalidDataException("Archive minor version is unknown. Should be 2, 3, or 4.");
await using var fos = File.Create(dest);
await using var fos = dest.Create();
using var archive = new ZipArchive(fos, ZipArchiveMode.Create);
while (reader.PeekChar() != -1)
{

View File

@ -86,7 +86,7 @@ namespace Wabbajack.Lib.Downloaders
.Do(t => Downloaders.First(d => d.GetType() == t).Prepare());
}
public static async Task<bool> DownloadWithPossibleUpgrade(Archive archive, string destination)
public static async Task<bool> DownloadWithPossibleUpgrade(Archive archive, AbsolutePath destination)
{
var success = await Download(archive, destination);
if (success)
@ -104,12 +104,12 @@ namespace Wabbajack.Lib.Downloaders
}
Utils.Log($"Upgrading {archive.Hash}");
var upgradePath = Path.Combine(Path.GetDirectoryName(destination), "_Upgrade_" + archive.Name);
var upgradePath = destination.Parent.Combine("_Upgrade_" + archive.Name);
var upgradeResult = await Download(upgrade, upgradePath);
if (!upgradeResult) return false;
var patchName = $"{archive.Hash.ToHex()}_{upgrade.Hash.ToHex()}";
var patchPath = Path.Combine(Path.GetDirectoryName(destination), "_Patch_" + patchName);
var patchPath = destination.Parent.Combine("_Patch_" + patchName);
var patchState = new Archive
{
@ -124,9 +124,9 @@ namespace Wabbajack.Lib.Downloaders
if (!patchResult) return false;
Utils.Status($"Applying Upgrade to {archive.Hash}");
await using (var patchStream = File.OpenRead(patchPath))
await using (var srcStream = File.OpenRead(upgradePath))
await using (var destStream = File.Create(destination))
await using (var patchStream = patchPath.OpenRead())
await using (var srcStream = upgradePath.OpenRead())
await using (var destStream = destination.Create())
{
OctoDiff.Apply(srcStream, patchStream, destStream);
}
@ -136,7 +136,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
private static async Task<bool> Download(Archive archive, string destination)
private static async Task<bool> Download(Archive archive, AbsolutePath destination)
{
try
{

View File

@ -25,12 +25,14 @@ namespace Wabbajack.Lib.Downloaders
if (game == null) return null;
var path = game.GameLocation();
var filePath = Path.Combine(path, gameFile);
var filePath = path?.Combine(gameFile);
if (!File.Exists(filePath))
if (!filePath?.Exists ?? false)
return null;
var hash = filePath.FileHashCached();
var fp = filePath.Value;
var hash = await fp.FileHashCachedAsync();
return new State
{
@ -53,7 +55,7 @@ namespace Wabbajack.Lib.Downloaders
public string GameVersion { get; set; }
internal string SourcePath => Path.Combine(Game.MetaData().GameLocation(), GameFile);
internal AbsolutePath SourcePath => Game.MetaData().GameLocation().Value.Combine(GameFile);
public override object[] PrimaryKey { get => new object[] {Game, GameVersion, GameFile}; }
@ -62,12 +64,12 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
using(var src = File.OpenRead(SourcePath))
using (var dest = File.Open(destination, System.IO.FileMode.Create))
using(var src = SourcePath.OpenRead())
using (var dest = destination.Create())
{
var size = new FileInfo(SourcePath).Length;
var size = SourcePath.Size;
src.CopyToWithStatus(size, dest, "Copying from Game folder");
}
return true;
@ -75,7 +77,7 @@ namespace Wabbajack.Lib.Downloaders
public override async Task<bool> Verify(Archive a)
{
return File.Exists(SourcePath) && SourcePath.FileHashCached() == Hash;
return SourcePath.Exists && await SourcePath.FileHashCachedAsync() == Hash;
}
public override IDownloader GetDownloader()

View File

@ -44,7 +44,7 @@ namespace Wabbajack.Lib.Downloaders
return whitelist.GoogleIDs.Contains(Id);
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var state = await ToHttpState();
return await state.Download(a, destination);

View File

@ -64,21 +64,19 @@ namespace Wabbajack.Lib.Downloaders
return whitelist.AllowedPrefixes.Any(p => Url.StartsWith(p));
}
public override Task<bool> Download(Archive a, string destination)
public override Task<bool> Download(Archive a, AbsolutePath destination)
{
return DoDownload(a, destination, true);
}
public async Task<bool> DoDownload(Archive a, string destination, bool download)
public async Task<bool> DoDownload(Archive a, AbsolutePath destination, bool download)
{
if (download)
{
var parent = Directory.GetParent(destination);
if (!Directory.Exists(parent.FullName))
Directory.CreateDirectory(parent.FullName);
destination.Parent.CreateDirectory();
}
using (var fs = download ? File.Open(destination, FileMode.Create) : null)
using (var fs = download ? destination.Create() : null)
{
var client = Client ?? new Common.Http.Client();
client.Headers.Add(("User-Agent", Consts.UserAgent));
@ -186,7 +184,7 @@ TOP:
public override async Task<bool> Verify(Archive a)
{
return await DoDownload(a, "", false);
return await DoDownload(a, ((RelativePath)"").RelativeToEntryPoint(), false);
}
public override IDownloader GetDownloader()

View File

@ -26,7 +26,7 @@ namespace Wabbajack.Lib.Downloaders
public class State : HTTPDownloader.State
{
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var client = new MegaApiClient();
Utils.Status("Logging into MEGA (as anonymous)");
@ -34,7 +34,7 @@ namespace Wabbajack.Lib.Downloaders
var fileLink = new Uri(Url);
var node = client.GetNodeFromLink(fileLink);
Utils.Status($"Downloading MEGA file: {a.Name}");
client.DownloadFile(fileLink, destination);
client.DownloadFile(fileLink, (string)destination);
return true;
}

View File

@ -85,7 +85,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var (uri, client) = await Utils.Log(await ManuallyDownloadFile.Create(this)).Task;
var state = new HTTPDownloader.State {Url = uri.ToString(), Client = client};

View File

@ -2,6 +2,7 @@
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.Lib.Validation;
using Wabbajack.Lib.WebAutomation;
@ -31,7 +32,7 @@ namespace Wabbajack.Lib.Downloaders
return whitelist.AllowedPrefixes.Any(p => Url.StartsWith(p));
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var result = await Resolve();
return await result.Download(a, destination);

View File

@ -46,7 +46,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var urls = await GetDownloadUrls();
Utils.Log($"Found {urls.Length} ModDB mirrors for {a.Name}");

View File

@ -152,7 +152,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
string url;
try

View File

@ -49,7 +49,7 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
var currentLib = Item.Game.Universe;

View File

@ -82,13 +82,13 @@ namespace Wabbajack.Lib.Downloaders
return true;
}
public override async Task<bool> Download(Archive a, string destination)
public override async Task<bool> Download(Archive a, AbsolutePath destination)
{
try
{
using var queue = new WorkQueue();
using var folder = new TempFolder();
Directory.CreateDirectory(Path.Combine(folder.Dir.FullName, "tracks"));
folder.Dir.Combine("tracks").CreateDirectory();
var client = new YoutubeClient(Common.Http.ClientFactory.Client);
var meta = await client.GetVideoAsync(Key);
var video = await client.GetVideoMediaStreamInfosAsync(Key);
@ -96,17 +96,17 @@ namespace Wabbajack.Lib.Downloaders
var stream = video.GetAll().OfType<AudioStreamInfo>().Where(f => f.AudioEncoding == AudioEncoding.Aac).OrderByDescending(a => a.Bitrate)
.ToArray().First();
var initialDownload = Path.Combine(folder.Dir.FullName, "initial_download");
var initialDownload = folder.Dir.Combine("initial_download");
var trackFolder = Path.Combine(folder.Dir.FullName, "tracks");
var trackFolder = folder.Dir.Combine("tracks");
await using (var fs = File.Create(initialDownload))
await using (var fs = initialDownload.Create())
{
await client.DownloadMediaStreamAsync(stream, fs, new Progress($"Downloading {a.Name}"),
CancellationToken.None);
}
File.Copy(initialDownload, @$"c:\tmp\{Path.GetFileName(destination)}.dest_stream");
initialDownload.CopyTo(destination.WithExtension(new Extension(".dest_stream")));
await Tracks.PMap(queue, async track =>
{
@ -114,15 +114,15 @@ namespace Wabbajack.Lib.Downloaders
await ExtractTrack(initialDownload, trackFolder, track);
});
await using var dest = File.Create(destination);
await using var dest = destination.Create();
using var ar = new ZipArchive(dest, ZipArchiveMode.Create);
foreach (var track in Directory.EnumerateFiles(trackFolder).OrderBy(e => e))
foreach (var track in trackFolder.EnumerateFiles().OrderBy(e => e))
{
Utils.Status($"Adding {Path.GetFileName(track)} to archive");
var entry = ar.CreateEntry(Path.Combine("Data", "tracks", track.RelativeTo(trackFolder)), CompressionLevel.NoCompression);
Utils.Status($"Adding {track.FileName} to archive");
var entry = ar.CreateEntry(Path.Combine("Data", "tracks", (string)track.RelativeTo(trackFolder)), CompressionLevel.NoCompression);
entry.LastWriteTime = meta.UploadDate;
await using var es = entry.Open();
await using var ins = File.OpenRead(track);
await using var ins = track.OpenRead();
await ins.CopyToAsync(es);
}
@ -137,7 +137,7 @@ namespace Wabbajack.Lib.Downloaders
private const string FFMpegPath = "Downloaders/Converters/ffmpeg.exe";
private const string xWMAEncodePath = "Downloaders/Converters/xWMAEncode.exe";
private async Task ExtractTrack(string source, string dest_folder, Track track)
private async Task ExtractTrack(AbsolutePath source, AbsolutePath dest_folder, Track track)
{
var info = new ProcessStartInfo
{

View File

@ -21,31 +21,25 @@ namespace Wabbajack.Lib.FileUploader
{
public static IObservable<bool> HaveAuthorAPIKey => Utils.HaveEncryptedJsonObservable("author-api-key.txt");
public static IObservable<string> AuthorAPIKey => HaveAuthorAPIKey.Where(h => h)
.Select(_ => File.ReadAllText(Path.Combine(Consts.LocalAppDataPath, "author-api-key.txt")));
public static string GetAPIKey()
public static async Task<string> GetAPIKey()
{
return File.ReadAllText(Path.Combine(Consts.LocalAppDataPath, "author-api-key.txt")).Trim();
return (await Consts.LocalAppDataPath.Combine("author-api-key.txt").ReadAllTextAsync()).Trim();
}
public static bool HasAPIKey => File.Exists(Path.Combine(Consts.LocalAppDataPath, "author-api-key.txt"));
public static readonly Uri UploadURL = new Uri("https://build.wabbajack.org/upload_file");
public static long BLOCK_SIZE = (long)1024 * 1024 * 2;
public static int MAX_CONNECTIONS = 8;
public static Task<string> UploadFile(WorkQueue queue, string filename, Action<double> progressFn)
public static Task<string> UploadFile(WorkQueue queue, AbsolutePath filename, Action<double> progressFn)
{
var tcs = new TaskCompletionSource<string>();
Task.Run(async () =>
{
var client = GetAuthorizedClient();
var fsize = new FileInfo(filename).Length;
var fsize = filename.Size;
var hash_task = filename.FileHashAsync();
var response = await client.PutAsync(UploadURL+$"/{Path.GetFileName(filename)}/start", new StringContent(""));
var response = await client.PutAsync(UploadURL+$"/{filename.FileName.ToString()}/start", new StringContent(""));
if (!response.IsSuccessStatusCode)
{
tcs.SetException(new Exception($"Start Error: {response.StatusCode} {response.ReasonPhrase}"));
@ -75,29 +69,27 @@ namespace Wabbajack.Lib.FileUploader
progressFn((double)sent / fsize);
int retries = 0;
using (var fs = File.OpenRead(filename))
{
fs.Position = block_offset;
var data = new byte[block_size];
await fs.ReadAsync(data, 0, data.Length);
await using var fs = filename.OpenRead();
fs.Position = block_offset;
var data = new byte[block_size];
await fs.ReadAsync(data, 0, data.Length);
var putResponse = await client.PutAsync(UploadURL + $"/{key}/data/{block_offset}",
new ByteArrayContent(data));
response = await client.PutAsync(UploadURL + $"/{key}/data/{block_offset}",
new ByteArrayContent(data));
if (!putResponse.IsSuccessStatusCode)
{
tcs.SetException(new Exception($"Put Error: {putResponse.StatusCode} {putResponse.ReasonPhrase}"));
return;
}
if (!response.IsSuccessStatusCode)
{
tcs.SetException(new Exception($"Put Error: {response.StatusCode} {response.ReasonPhrase}"));
return;
}
var val = long.Parse(await putResponse.Content.ReadAsStringAsync());
if (val != block_offset + data.Length)
{
tcs.SetResult($"Sync Error {val} vs {block_offset + data.Length}");
tcs.SetException(new Exception($"Sync Error {val} vs {block_offset + data.Length}"));
}
var val = long.Parse(await response.Content.ReadAsStringAsync());
if (val != block_offset + data.Length)
{
tcs.SetResult($"Sync Error {val} vs {block_offset + data.Length}");
tcs.SetException(new Exception($"Sync Error {val} vs {block_offset + data.Length}"));
}
});
}
@ -119,16 +111,16 @@ namespace Wabbajack.Lib.FileUploader
return tcs.Task;
}
public static Common.Http.Client GetAuthorizedClient()
public static async Task<Common.Http.Client> GetAuthorizedClient()
{
var client = new Common.Http.Client();
client.Headers.Add(("X-API-KEY", GetAPIKey()));
client.Headers.Add(("X-API-KEY", await GetAPIKey()));
return client;
}
public static async Task<string> RunJob(string jobtype)
{
var client = GetAuthorizedClient();
var client = await GetAuthorizedClient();
return await client.GetStringAsync($"https://{Consts.WabbajackCacheHostname}/jobs/enqueue_job/{jobtype}");
}
@ -173,17 +165,17 @@ namespace Wabbajack.Lib.FileUploader
public static async Task<string> GetServerLog()
{
return await GetAuthorizedClient().GetStringAsync($"https://{Consts.WabbajackCacheHostname}/heartbeat/logs");
return await (await GetAuthorizedClient()).GetStringAsync($"https://{Consts.WabbajackCacheHostname}/heartbeat/logs");
}
public static async Task<IEnumerable<string>> GetMyFiles()
{
return (await GetAuthorizedClient().GetStringAsync($"https://{Consts.WabbajackCacheHostname}/uploaded_files/list")).FromJSONString<string[]>();
return (await (await GetAuthorizedClient()).GetStringAsync($"https://{Consts.WabbajackCacheHostname}/uploaded_files/list")).FromJSONString<string[]>();
}
public static async Task<string> DeleteFile(string name)
{
var result = await GetAuthorizedClient()
var result = await (await GetAuthorizedClient())
.DeleteStringAsync($"https://{Consts.WabbajackCacheHostname}/uploaded_files/{name}");
return result;
}

View File

@ -30,9 +30,9 @@ namespace Wabbajack.Lib
public override ModManager ModManager => ModManager.MO2;
public string GameFolder { get; set; }
public AbsolutePath? GameFolder { get; set; }
public MO2Installer(string archive, ModList modList, string outputFolder, string downloadFolder, SystemParameters parameters)
public MO2Installer(string archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
: base(
archive: archive,
modList: modList,
@ -84,10 +84,10 @@ namespace Wabbajack.Lib
UpdateTracker.NextStep("Validating Modlist");
await ValidateModlist.RunValidation(ModList);
Directory.CreateDirectory(OutputFolder);
Directory.CreateDirectory(DownloadFolder);
OutputFolder.CreateDirectory();
DownloadFolder.CreateDirectory();
if (Directory.Exists(Path.Combine(OutputFolder, Consts.MO2ModFolderName)) && WarnOnOverwrite)
if (OutputFolder.Combine(Consts.MO2ModFolderName).IsDirectory && WarnOnOverwrite)
{
if ((await Utils.Log(new ConfirmUpdateOfExistingInstall { ModListName = ModList.Name, OutputFolder = OutputFolder }).Task) == ConfirmUpdateOfExistingInstall.Choice.Abort)
{
@ -152,7 +152,7 @@ namespace Wabbajack.Lib
await zEditIntegration.GenerateMerges(this);
UpdateTracker.NextStep("Set MO2 into portable");
ForcePortable();
await ForcePortable();
UpdateTracker.NextStep("Create Empty Output Mods");
CreateOutputMods();
@ -168,7 +168,11 @@ namespace Wabbajack.Lib
private void CreateOutputMods()
{
Directory.EnumerateFiles(Path.Combine(OutputFolder, "profiles"), "settings.ini", DirectoryEnumerationOptions.Recursive).Do(f =>
OutputFolder.Combine("profiles")
.EnumerateFiles(true)
.Where(f => f.FileName == Consts.SettingsIni)
.Do(f =>
{
var ini = f.LoadIniFile();
if (ini == null)
@ -189,23 +193,22 @@ namespace Wabbajack.Lib
data.Coll.Do(keyData =>
{
var v = keyData.Value;
var mod = Path.Combine(OutputFolder, Consts.MO2ModFolderName, v);
var mod = OutputFolder.Combine(Consts.MO2ModFolderName, (RelativePath)v);
if (!Directory.Exists(mod))
Directory.CreateDirectory(mod);
mod.CreateDirectory();
});
}
});
}
private void ForcePortable()
private async Task ForcePortable()
{
var path = Path.Combine(OutputFolder, "portable.txt");
if (File.Exists(path)) return;
var path = OutputFolder.Combine("portable.txt");
if (path.Exists) return;
try
{
File.WriteAllText(path, "Created by Wabbajack");
await path.WriteAllTextAsync("Created by Wabbajack");
}
catch (Exception e)
{
@ -217,12 +220,12 @@ namespace Wabbajack.Lib
{
await ModList.Directives
.OfType<ArchiveMeta>()
.PMap(Queue, directive =>
.PMap(Queue, async directive =>
{
Status($"Writing included .meta file {directive.To}");
var outPath = Path.Combine(DownloadFolder, directive.To);
if (File.Exists(outPath)) File.Delete(outPath);
File.WriteAllBytes(outPath, LoadBytesFromPath(directive.SourceDataID));
var outPath = DownloadFolder.Combine(directive.To);
if (outPath.IsFile) outPath.Delete();
await outPath.WriteAllBytesAsync(await LoadBytesFromPath(directive.SourceDataID));
});
}
@ -230,8 +233,8 @@ namespace Wabbajack.Lib
{
foreach (var esm in ModList.Directives.OfType<CleanedESM>().ToList())
{
var filename = Path.GetFileName(esm.To);
var gameFile = Path.Combine(GameFolder, "Data", filename);
var filename = esm.To.FileName;
var gameFile = GameFolder.Value.Combine((RelativePath)"Data", filename);
Utils.Log($"Validating {filename}");
var hash = gameFile.FileHash();
if (hash != esm.SourceESMHash)
@ -249,28 +252,28 @@ namespace Wabbajack.Lib
foreach (var bsa in bsas)
{
Status($"Building {bsa.To}");
var sourceDir = Path.Combine(OutputFolder, Consts.BSACreationDir, bsa.TempID);
var sourceDir = OutputFolder.Combine(Consts.BSACreationDir, bsa.TempID);
var bsaSize = bsa.FileStates.Select(state => File.GetSize(Path.Combine(sourceDir, state.Path))).Sum();
var bsaSize = bsa.FileStates.Select(state => sourceDir.Combine(state.Path).Size).Sum();
using (var a = bsa.State.MakeBuilder(bsaSize))
{
var streams = await bsa.FileStates.PMap(Queue, state =>
{
Status($"Adding {state.Path} to BSA");
var fs = File.OpenRead(Path.Combine(sourceDir, state.Path));
var fs = sourceDir.Combine(state.Path).OpenRead();
a.AddFile(state, fs);
return fs;
});
Info($"Writing {bsa.To}");
a.Build(Path.Combine(OutputFolder, bsa.To));
a.Build(OutputFolder.Combine(bsa.To));
streams.Do(s => s.Dispose());
}
}
var bsaDir = Path.Combine(OutputFolder, Consts.BSACreationDir);
if (Directory.Exists(bsaDir))
var bsaDir = OutputFolder.Combine(Consts.BSACreationDir);
if (bsaDir.Exists)
{
Info($"Removing temp folder {Consts.BSACreationDir}");
Utils.DeleteDirectory(bsaDir);
@ -282,40 +285,45 @@ namespace Wabbajack.Lib
Info("Writing inline files");
await ModList.Directives
.OfType<InlineFile>()
.PMap(Queue, directive =>
.PMap(Queue, async directive =>
{
Status($"Writing included file {directive.To}");
var outPath = Path.Combine(OutputFolder, directive.To);
if (File.Exists(outPath)) File.Delete(outPath);
if (directive is RemappedInlineFile)
WriteRemappedFile((RemappedInlineFile)directive);
else if (directive is CleanedESM)
GenerateCleanedESM((CleanedESM)directive);
else
File.WriteAllBytes(outPath, LoadBytesFromPath(directive.SourceDataID));
var outPath = OutputFolder.Combine(directive.To);
outPath.Delete();
switch (directive)
{
case RemappedInlineFile file:
await WriteRemappedFile(file);
break;
case CleanedESM esm:
await GenerateCleanedESM(esm);
break;
default:
await outPath.WriteAllBytesAsync(await LoadBytesFromPath(directive.SourceDataID));
break;
}
});
}
private void GenerateCleanedESM(CleanedESM directive)
private async Task GenerateCleanedESM(CleanedESM directive)
{
var filename = Path.GetFileName(directive.To);
var gameFile = Path.Combine(GameFolder, "Data", filename);
var filename = directive.To.FileName;
var gameFile = GameFolder.Value.Combine((RelativePath)"Data", filename);
Info($"Generating cleaned ESM for {filename}");
if (!File.Exists(gameFile)) throw new InvalidDataException($"Missing {filename} at {gameFile}");
if (!gameFile.Exists) throw new InvalidDataException($"Missing {filename} at {gameFile}");
Status($"Hashing game version of {filename}");
var sha = gameFile.FileHash();
var sha = await gameFile.FileHashAsync();
if (sha != directive.SourceESMHash)
throw new InvalidDataException(
$"Cannot patch {filename} from the game folder because the hashes do not match. Have you already cleaned the file?");
var patchData = LoadBytesFromPath(directive.SourceDataID);
var toFile = Path.Combine(OutputFolder, directive.To);
var patchData = await LoadBytesFromPath(directive.SourceDataID);
var toFile = OutputFolder.Combine(directive.To);
Status($"Patching {filename}");
using (var output = File.Open(toFile, FileMode.Create))
using (var input = File.OpenRead(gameFile))
{
Utils.ApplyPatch(input, () => new MemoryStream(patchData), output);
}
using var output = toFile.Create();
using var input = gameFile.OpenRead();
Utils.ApplyPatch(input, () => new MemoryStream(patchData), output);
}
private void SetScreenSizeInPrefs()
@ -363,23 +371,23 @@ namespace Wabbajack.Lib
}
}
private void WriteRemappedFile(RemappedInlineFile directive)
private async Task WriteRemappedFile(RemappedInlineFile directive)
{
var data = Encoding.UTF8.GetString(LoadBytesFromPath(directive.SourceDataID));
var data = Encoding.UTF8.GetString(await LoadBytesFromPath(directive.SourceDataID));
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.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("\\", "/"));
data = data.Replace(Consts.MO2_PATH_MAGIC_BACK, OutputFolder);
data = data.Replace(Consts.MO2_PATH_MAGIC_DOUBLE_BACK, OutputFolder.Replace("\\", "\\\\"));
data = data.Replace(Consts.MO2_PATH_MAGIC_FORWARD, OutputFolder.Replace("\\", "/"));
data = data.Replace(Consts.MO2_PATH_MAGIC_BACK, (string)OutputFolder);
data = data.Replace(Consts.MO2_PATH_MAGIC_DOUBLE_BACK, ((string)OutputFolder).Replace("\\", "\\\\"));
data = data.Replace(Consts.MO2_PATH_MAGIC_FORWARD, ((string)OutputFolder).Replace("\\", "/"));
data = data.Replace(Consts.DOWNLOAD_PATH_MAGIC_BACK, DownloadFolder);
data = data.Replace(Consts.DOWNLOAD_PATH_MAGIC_DOUBLE_BACK, DownloadFolder.Replace("\\", "\\\\"));
data = data.Replace(Consts.DOWNLOAD_PATH_MAGIC_FORWARD, DownloadFolder.Replace("\\", "/"));
data = data.Replace(Consts.DOWNLOAD_PATH_MAGIC_BACK, (string)DownloadFolder);
data = data.Replace(Consts.DOWNLOAD_PATH_MAGIC_DOUBLE_BACK, ((string)DownloadFolder).Replace("\\", "\\\\"));
data = data.Replace(Consts.DOWNLOAD_PATH_MAGIC_FORWARD, ((string)DownloadFolder).Replace("\\", "/"));
File.WriteAllText(Path.Combine(OutputFolder, directive.To), data);
await OutputFolder.Combine(directive.To).WriteAllTextAsync(data);
}
public static IErrorResponse CheckValidInstallPath(string path, string downloadFolder)

View File

@ -82,16 +82,6 @@ namespace Wabbajack.Lib.ModListRegistry
return metadata.OrderBy(m => (m.ValidationSummary?.HasFailures ?? false ? 1 : 0, m.Title)).ToList();
}
public bool NeedsDownload(string modlistPath)
{
if (!File.Exists(modlistPath)) return true;
if (DownloadMetadata?.Hash == null)
{
return true;
}
return DownloadMetadata.Hash != modlistPath.FileHashCached(true);
}
}
public class DownloadMetadata

View File

@ -9,7 +9,7 @@ namespace Wabbajack.Lib
{
public class ConfirmUpdateOfExistingInstall : ConfirmationIntervention
{
public string OutputFolder { get; set; }
public AbsolutePath OutputFolder { get; set; }
public string ModListName { get; set; }
public override string ShortDescription { get; } = "Do you want to overwrite existing files?";

View File

@ -47,11 +47,9 @@ namespace Wabbajack.Lib
private SteamGame _steamGame;
private bool _hasSteamWorkshopItems;
public override string VFSCacheName => Path.Combine(
Consts.LocalAppDataPath,
$"vfs_compile_cache-{StagingFolder?.StringSha256Hex() ?? "Unknown"}.bin");
public override AbsolutePath VFSCacheName => Consts.LocalAppDataPath.Combine($"vfs_compile_cache-{((string)StagingFolder)?.StringSha256Hex() ?? "Unknown"}.bin");
public VortexCompiler(Game game, string gamePath, string vortexFolder, string downloadsFolder, string stagingFolder, string outputFile)
public VortexCompiler(Game game, AbsolutePath gamePath, AbsolutePath vortexFolder, AbsolutePath downloadsFolder, AbsolutePath stagingFolder, AbsolutePath outputFile)
{
Game = game;
@ -65,7 +63,7 @@ namespace Wabbajack.Lib
if (string.IsNullOrEmpty(ModListName))
{
ModListName = $"Vortex ModList for {Game.ToString()}";
ModListOutputFile = $"{ModListName}{Consts.ModListExtension}";
ModListOutputFile = ((RelativePath)ModListName).RelativeToEntryPoint().WithExtension(Consts.ModListExtension);
}
GameName = Game.MetaData().NexusName;

View File

@ -20,7 +20,7 @@ namespace Wabbajack.Lib
public override ModManager ModManager => ModManager.Vortex;
public VortexInstaller(string archive, ModList modList, string outputFolder, string downloadFolder, SystemParameters parameters)
public VortexInstaller(string archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
: base(
archive: archive,
modList: modList,
@ -52,7 +52,7 @@ namespace Wabbajack.Lib
if (cancel.IsCancellationRequested) return false;
ConfigureProcessor(10, ConstructDynamicNumThreads(await RecommendQueueSize()));
Directory.CreateDirectory(DownloadFolder);
DownloadFolder.CreateDirectory();
if (cancel.IsCancellationRequested) return false;
UpdateTracker.NextStep("Hashing Archives");
@ -120,22 +120,22 @@ namespace Wabbajack.Lib
if (result != ConfirmationIntervention.Choice.Continue)
return;
var manualFilesDir = Path.Combine(OutputFolder, Consts.ManualGameFilesDir);
var manualFilesDir = OutputFolder.Combine(Consts.ManualGameFilesDir);
var gameFolder = GameInfo.GameLocation();
Info($"Copying files from {manualFilesDir} " +
$"to the game folder at {gameFolder}");
if (!Directory.Exists(manualFilesDir))
if (!manualFilesDir.Exists)
{
Info($"{manualFilesDir} does not exist!");
return;
}
await Directory.EnumerateDirectories(manualFilesDir).PMap(Queue, dir =>
/* TODO FIX THIS
await manualFilesDir.EnumerateFiles().PMap(Queue, dir =>
{
var dirInfo = new DirectoryInfo(dir);
dirInfo.GetDirectories("*", SearchOption.AllDirectories).Do(d =>
{
var destPath = d.FullName.Replace(manualFilesDir, gameFolder);
@ -157,6 +157,8 @@ namespace Wabbajack.Lib
}
});
});
*/
}
private async Task InstallSteamWorkshopItems()
@ -182,12 +184,12 @@ namespace Wabbajack.Lib
return;
await ModList.Directives.OfType<SteamMeta>()
.PMap(Queue, item =>
.PMap(Queue, async 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));
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");
@ -209,15 +211,15 @@ namespace Wabbajack.Lib
{
Info("Writing inline files");
await ModList.Directives.OfType<InlineFile>()
.PMap(Queue,directive =>
.PMap(Queue,async directive =>
{
if (directive.To.EndsWith(Consts.MetaFileExtension))
if (directive.To.Extension == Consts.MetaFileExtension)
return;
Info($"Writing included file {directive.To}");
var outPath = Path.Combine(OutputFolder, directive.To);
if(File.Exists(outPath)) File.Delete(outPath);
File.WriteAllBytes(outPath, LoadBytesFromPath(directive.SourceDataID));
var outPath = OutputFolder.Combine(directive.To);
outPath.Delete();
await outPath.WriteAllBytesAsync(await LoadBytesFromPath(directive.SourceDataID));
});
}
}