Only 246 errors left in Wabbajack.Lib

This commit is contained in:
Timothy Baldridge 2020-03-25 06:47:25 -06:00
parent 72d77bef1a
commit 035e376a09
30 changed files with 423 additions and 287 deletions

View File

@ -30,12 +30,14 @@ namespace Wabbajack.Common
public static readonly HashSet<Extension> SupportedBSAs = new[] {".bsa", ".ba2"}
.Select(s => new Extension(s)).ToHashSet();
public static HashSet<string> ConfigFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {".json", ".ini", ".yml", ".xml"};
public static HashSet<string> ESPFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".esp", ".esm", ".esl"};
public static HashSet<string> AssetFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {".dds", ".tga", ".nif", ".psc", ".pex"};
public static HashSet<Extension> ConfigFileExtensions = new[]{".json", ".ini", ".yml", ".xml"}.Select(s => new Extension(s)).ToHashSet();
public static HashSet<Extension> ESPFileExtensions = new []{ ".esp", ".esm", ".esl"}.Select(s => new Extension(s)).ToHashSet();
public static HashSet<Extension> AssetFileExtensions = new[] {".dds", ".tga", ".nif", ".psc", ".pex"}.Select(s => new Extension(s)).ToHashSet();
public static readonly Extension EXE = new Extension(".exe");
public static readonly Extension OMOD = new Extension(".omod");
public static readonly Extension ESM = new Extension(".esm");
public static readonly Extension ESP = new Extension(".esp");
public static string NexusCacheDirectory = "nexus_link_cache";
@ -59,7 +61,7 @@ namespace Wabbajack.Common
public static string AppName = "Wabbajack";
public static HashSet<string> GameESMs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
public static HashSet<RelativePath> GameESMs = new []
{
// Skyrim LE/SE
"Skyrim.esm",
@ -77,7 +79,7 @@ namespace Wabbajack.Common
"DLCNukaWorld.esm",
"DLCUltraHighResolution.esm"
};
}.Select(s => (RelativePath)s).ToHashSet();
public static string ModPermissionsURL = "https://raw.githubusercontent.com/wabbajack-tools/opt-out-lists/master/NexusModPermissions.yml";
public static string ServerWhitelistURL = "https://raw.githubusercontent.com/wabbajack-tools/opt-out-lists/master/ServerWhitelist.yml";
@ -93,11 +95,13 @@ namespace Wabbajack.Common
return headerString;
}
}
public static RelativePath MetaIni = new RelativePath("meta.ini");
public static string HashFileExtension => ".xxHash";
public static string MetaFileExtension => ".meta";
public static string ModListExtension = ".wabbajack";
public static string LocalAppDataPath => Path.Combine(KnownFolders.LocalAppData.Path, "Wabbajack");
public static Extension HashFileExtension = new Extension(".xxHash");
public static Extension MetaFileExtension = new Extension(".meta");
public static Extension ModListExtension = new Extension(".wabbajack");
public static AbsolutePath LocalAppDataPath => new AbsolutePath(Path.Combine(KnownFolders.LocalAppData.Path, "Wabbajack"));
public static string MetricsKeyHeader => "x-metrics-key";
public static string WabbajackCacheLocation = "http://build.wabbajack.org/nexus_api_cache/";
@ -107,13 +111,16 @@ namespace Wabbajack.Common
public static int MaxHTTPRetries = 4;
public const string MO2ModFolderName = "mods";
public static string PatchCacheFolder => Path.Combine(LocalAppDataPath, "patch_cache");
public static AbsolutePath PatchCacheFolder => LocalAppDataPath.Combine("patch_cache");
public static int MaxConnectionsPerServer = 4;
public static string LogsFolder = "logs";
public static AbsolutePath LogsFolder = ((RelativePath)"logs").RelativeToEntryPoint();
public static AbsolutePath EntryPoint = (AbsolutePath)(Assembly.GetEntryAssembly()?.Location ?? (string)((RelativePath)"Unknown").RelativeToWorkingDirectory());
public static AbsolutePath LogFile = LogsFolder.Combine(EntryPoint.FileNameWithoutExtension + ".current.log");
public static int MaxOldLogs = 50;
public static Extension BSA = new Extension(".BSA");
public static string SettingsFile => Path.Combine(LocalAppDataPath, "settings.json");
public static AbsolutePath SettingsFile => LocalAppDataPath.Combine("settings.json");
public static byte SettingsVersion => 1;
}
}

View File

@ -139,13 +139,13 @@ namespace Wabbajack.Common
public static bool TryGetHashCache(AbsolutePath file, out Hash hash)
{
var hashFile = file + Consts.HashFileExtension;
var hashFile = file.WithExtension(Consts.HashFileExtension);
hash = Hash.Empty;
if (!File.Exists(hashFile)) return false;
if (!hashFile.IsFile) return false;
if (File.GetSize(hashFile) != 20) return false;
if (hashFile.Size != 20) return false;
using var fs = File.OpenRead(hashFile);
using var fs = hashFile.OpenRead();
using var br = new BinaryReader(fs);
var version = br.ReadUInt32();
if (version != HashCacheVersion) return false;
@ -160,7 +160,7 @@ namespace Wabbajack.Common
private const uint HashCacheVersion = 0x01;
private static void WriteHashCache(AbsolutePath file, Hash hash)
{
using var fs = File.Create(file + Consts.HashFileExtension);
using var fs = file.WithExtension(Consts.HashFileExtension).Create();
using var bw = new BinaryWriter(fs);
bw.Write(HashCacheVersion);
var lastModified = file.LastModifiedUtc.AsUnixTime();

View File

@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -16,13 +15,18 @@ namespace Wabbajack.Common
{
public interface IPath
{
/// <summary>
/// Get the final file name, for c:\bar\baz this is `baz` for c:\bar.zip this is `bar.zip`
/// for `bar.zip` this is `bar.zip`
/// </summary>
public RelativePath FileName { get; }
}
public struct AbsolutePath : IPath
{
#region ObjectEquality
bool Equals(AbsolutePath other)
private bool Equals(AbsolutePath other)
{
return _path == other._path;
}
@ -39,51 +43,57 @@ namespace Wabbajack.Common
return true;
}
if (obj.GetType() != this.GetType())
if (obj.GetType() != GetType())
{
return false;
}
return Equals((AbsolutePath) obj);
return Equals((AbsolutePath)obj);
}
#endregion
public override int GetHashCode()
{
return (_path != null ? _path.GetHashCode() : 0);
return _path != null ? _path.GetHashCode() : 0;
}
private readonly string _path;
private Extension _extension;
public AbsolutePath(string path)
{
_path = path.ToLowerInvariant().Replace("/", "\\").TrimEnd('\\');
_extension = new Extension(Path.GetExtension(_path));
Extension = new Extension(Path.GetExtension(_path));
ValidateAbsolutePath();
}
public AbsolutePath(string path, bool skipValidation)
{
_path = path.ToLowerInvariant().Replace("/", "\\").TrimEnd('\\');
_extension = Extension.FromPath(path);
if (!skipValidation)
Extension = Extension.FromPath(path);
if (!skipValidation)
{
ValidateAbsolutePath();
}
}
public AbsolutePath(AbsolutePath path)
{
_path = path._path;
_extension = path._extension;
Extension = path.Extension;
}
private void ValidateAbsolutePath()
{
if (Path.IsPathRooted(_path)) return;
throw new InvalidDataException($"Absolute path must be absolute");
if (Path.IsPathRooted(_path))
{
return;
}
throw new InvalidDataException("Absolute path must be absolute");
}
public Extension Extension => _extension;
public Extension Extension { get; }
public FileStream OpenRead()
{
@ -112,23 +122,22 @@ namespace Wabbajack.Common
public void DeleteDirectory()
{
if (IsDirectory)
Utils.DeleteDirectory(this);
if (IsDirectory)
{
Utils.DeleteDirectory(this);
}
}
public long Size => (new FileInfo(_path)).Length;
public long Size => new FileInfo(_path).Length;
public DateTime LastModified => File.GetLastWriteTime(_path);
public DateTime LastModifiedUtc => File.GetLastWriteTimeUtc(_path);
public AbsolutePath Parent => (AbsolutePath)Path.GetDirectoryName(_path);
public RelativePath FileName => (RelativePath)Path.GetFileName(_path);
public void Copy(AbsolutePath otherPath)
{
File.Copy(_path, otherPath._path);
}
public RelativePath FileNameWithoutExtension => (RelativePath)Path.GetFileNameWithoutExtension(_path);
/// <summary>
/// Moves this file to the specified location
/// Moves this file to the specified location
/// </summary>
/// <param name="otherPath"></param>
/// <param name="overwrite">Replace the destination file if it exists</param>
@ -139,11 +148,14 @@ namespace Wabbajack.Common
public RelativePath RelativeTo(AbsolutePath p)
{
if (_path.Substring(0, p._path.Length + 1) != p._path + "\\")
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));
}
public async Task<string> ReadAllTextAsync()
{
await using var fs = File.OpenRead(_path);
@ -151,7 +163,7 @@ namespace Wabbajack.Common
}
/// <summary>
/// Assuming the path is a folder, enumerate all the files in the folder
/// Assuming the path is a folder, enumerate all the files in the folder
/// </summary>
/// <param name="recursive">if true, also returns files in sub-folders</param>
/// <returns></returns>
@ -164,25 +176,27 @@ namespace Wabbajack.Common
#region Operators
public static explicit operator string(AbsolutePath path)
{
return path._path;
}
public static explicit operator AbsolutePath(string path)
{
return !Path.IsPathRooted(path) ? ((RelativePath)path).RelativeToEntryPoint() : new AbsolutePath(path);
}
public static bool operator ==(AbsolutePath a, AbsolutePath b)
{
return a._path == b._path;
}
public static bool operator !=(AbsolutePath a, AbsolutePath b)
{
return a._path != b._path;
}
#endregion
public void CreateDirectory()
@ -193,25 +207,117 @@ namespace Wabbajack.Common
public void Delete()
{
if (IsFile)
{
File.Delete(_path);
}
}
public bool InFolder(AbsolutePath gameFolder)
{
throw new NotImplementedException();
}
public async Task<byte[]> ReadAllBytesAsync()
{
await using var f = OpenRead();
return await f.ReadAllAsync();
}
public AbsolutePath WithExtension(Extension hashFileExtension)
{
return new AbsolutePath(_path + (string)Extension, true);
}
public AbsolutePath ReplaceExtension(Extension extension)
{
return new AbsolutePath(
Path.Combine(Path.GetDirectoryName(_path), Path.GetFileNameWithoutExtension(_path) + (string)extension),
true);
}
public AbsolutePath AppendToName(AbsolutePath bsa, string toAppend)
{
return new AbsolutePath(
Path.Combine(Path.GetDirectoryName(_path),
Path.GetFileNameWithoutExtension(_path) + toAppend + (string)Extension));
}
public AbsolutePath Combine(params RelativePath[] paths)
{
return new AbsolutePath(Path.Combine(paths.Select(s => (string)s).Cons(_path).ToArray()));
}
public AbsolutePath Combine(params string[] paths)
{
return new AbsolutePath(Path.Combine(paths.Cons(_path).ToArray()));
}
public IEnumerable<string> ReadAllLines()
{
return File.ReadAllLines(_path);
}
public void WriteAllBytes(byte[] data)
{
using var fs = Create();
fs.Write(data);
}
public async Task WriteAllBytesAsync(byte[] data)
{
await using var fs = Create();
await fs.WriteAsync(data);
}
public void AppendAllText(string text)
{
File.AppendAllText(_path, text);
}
public void CopyTo(AbsolutePath dest, bool useMove = false)
{
if (useMove)
{
File.Move(_path, dest._path);
}
else
{
File.Copy(_path, dest._path);
}
}
public async Task<IEnumerable<string>> ReadAllLinesAsync()
{
return (await ReadAllTextAsync()).Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
}
public byte[] ReadAllBytes()
{
return File.ReadAllBytes(_path);
}
}
public struct RelativePath : IPath, IEquatable<RelativePath>
{
private readonly string _path;
private Extension _extension;
public RelativePath(string path)
{
_path = path.ToLowerInvariant().Replace("/", "\\").Trim('\\');
_extension = new Extension(Path.GetExtension(path));
Extension = new Extension(Path.GetExtension(path));
Validate();
}
public override string ToString()
{
return _path;
}
public Extension Extension { get; }
public override int GetHashCode()
{
return (_path != null ? _path.GetHashCode() : 0);
return _path != null ? _path.GetHashCode() : 0;
}
public static RelativePath RandomFileName()
@ -222,7 +328,9 @@ namespace Wabbajack.Common
private void Validate()
{
if (Path.IsPathRooted(_path))
{
throw new InvalidDataException("Cannot create relative path from absolute path string");
}
}
public AbsolutePath RelativeTo(AbsolutePath abs)
@ -234,17 +342,17 @@ namespace Wabbajack.Common
{
return RelativeTo(((AbsolutePath)Assembly.GetEntryAssembly().Location).Parent);
}
public AbsolutePath RelativeToWorkingDirectory()
{
return RelativeTo((AbsolutePath)Directory.GetCurrentDirectory());
}
public static explicit operator string(RelativePath path)
{
return path._path;
}
public static explicit operator RelativePath(string path)
{
return new RelativePath(path);
@ -254,9 +362,9 @@ namespace Wabbajack.Common
{
return RelativeTo((AbsolutePath)Environment.SystemDirectory);
}
public RelativePath Parent => (RelativePath)Path.GetDirectoryName(_path);
public RelativePath FileName => new RelativePath(Path.GetFileName(_path));
public bool Equals(RelativePath other)
@ -268,16 +376,21 @@ namespace Wabbajack.Common
{
return obj is RelativePath other && Equals(other);
}
public static bool operator ==(RelativePath a, RelativePath b)
{
return a._path == b._path;
}
public static bool operator !=(RelativePath a, RelativePath b)
{
return !(a == b);
}
public bool StartsWith(string s)
{
return _path.StartsWith(s);
}
}
public static partial class Utils
@ -286,7 +399,7 @@ namespace Wabbajack.Common
{
return (RelativePath)str;
}
public static AbsolutePath RelativeTo(this string str, AbsolutePath path)
{
return ((RelativePath)str).RelativeTo(path);
@ -296,15 +409,20 @@ namespace Wabbajack.Common
{
wtr.Write(path is AbsolutePath);
if (path is AbsolutePath)
{
wtr.Write((AbsolutePath)path);
}
else
{
wtr.Write((RelativePath)path);
}
}
public static void Write(this BinaryWriter wtr, AbsolutePath path)
{
wtr.Write((string)path);
}
public static void Write(this BinaryWriter wtr, RelativePath path)
{
wtr.Write((string)path);
@ -313,7 +431,10 @@ namespace Wabbajack.Common
public static IPath ReadIPath(this BinaryReader rdr)
{
if (rdr.ReadBoolean())
{
return rdr.ReadAbsolutePath();
}
return rdr.ReadRelativePath();
}
@ -334,15 +455,15 @@ namespace Wabbajack.Common
newArr[arr.Length] = itm;
return newArr;
}
}
public struct Extension
{
public static Extension None = new Extension("", false);
public static Extension None = new Extension("", false);
#region ObjectEquality
bool Equals(Extension other)
private bool Equals(Extension other)
{
return _extension == other._extension;
}
@ -359,18 +480,19 @@ namespace Wabbajack.Common
return true;
}
if (obj.GetType() != this.GetType())
if (obj.GetType() != GetType())
{
return false;
}
return Equals((Extension) obj);
return Equals((Extension)obj);
}
public override int GetHashCode()
{
return (_extension != null ? _extension.GetHashCode() : 0);
return _extension != null ? _extension.GetHashCode() : 0;
}
#endregion
private readonly string _extension;
@ -390,39 +512,51 @@ namespace Wabbajack.Common
private Extension(string extension, bool validate)
{
_extension = string.Intern(extension);
if (validate) Validate();
if (validate)
{
Validate();
}
}
public Extension(Extension other)
{
_extension = other._extension;
}
private void Validate()
{
if (!_extension.StartsWith("."))
throw new InvalidDataException($"Extensions must start with '.'");
{
throw new InvalidDataException("Extensions must start with '.'");
}
}
public static explicit operator string(Extension path)
{
return path._extension;
}
public static explicit operator Extension(string path)
{
return new Extension(path);
}
public static bool operator ==(Extension a, Extension b)
{
// Super fast comparison because extensions are interned
if ((object)a == null && (object)b == null) return true;
if ((object)a == null || (object)b == null) return false;
if ((object)a == null && (object)b == null)
{
return true;
}
if ((object)a == null || (object)b == null)
{
return false;
}
return ReferenceEquals(a._extension, b._extension);
}
public static bool operator !=(Extension a, Extension b)
{
return !(a == b);
@ -445,7 +579,7 @@ namespace Wabbajack.Common
{
EMPTY_PATH = new RelativePath[0];
}
public HashRelativePath(Hash baseHash, params RelativePath[] paths)
{
BaseHash = baseHash;
@ -456,25 +590,31 @@ namespace Wabbajack.Common
{
return string.Join("|", Paths.Select(t => t.ToString()).Cons(BaseHash.ToString()));
}
public static bool operator ==(HashRelativePath a, HashRelativePath b)
{
if (a.BaseHash != b.BaseHash || a.Paths.Length == b.Paths.Length)
{
return false;
for (int idx = 0; idx < a.Paths.Length; idx += 1)
}
for (var idx = 0; idx < a.Paths.Length; idx += 1)
{
if (a.Paths[idx] != b.Paths[idx])
{
return false;
}
}
return true;
}
public static bool operator !=(HashRelativePath a, HashRelativePath b)
{
return !(a == b);
}
}
public struct FullPath : IEquatable<FullPath>
{
public AbsolutePath Base { get; }
@ -488,7 +628,9 @@ namespace Wabbajack.Common
Paths = paths;
_hash = Base.GetHashCode();
foreach (var itm in Paths)
{
_hash ^= itm.GetHashCode();
}
}
public override string ToString()
@ -504,15 +646,21 @@ namespace Wabbajack.Common
public static bool operator ==(FullPath a, FullPath b)
{
if (a.Base != b.Base || a.Paths.Length != b.Paths.Length)
{
return false;
for (int idx = 0; idx < a.Paths.Length; idx += 1)
}
for (var idx = 0; idx < a.Paths.Length; idx += 1)
{
if (a.Paths[idx] != b.Paths[idx])
{
return false;
}
}
return true;
}
public static bool operator !=(FullPath a, FullPath b)
{
return !(a == b);

View File

@ -38,8 +38,8 @@ namespace Wabbajack.Common
return processList.Where(process => process.ProcessName == "ModOrganizer").Any(process => Path.GetDirectoryName(process.MainModule?.FileName) == mo2Path);
}
public static string LogFile { get; }
public static string LogFolder { get; }
public static AbsolutePath LogFile { get; }
public static AbsolutePath LogFolder { get; }
public enum FileEventType
{
@ -52,34 +52,28 @@ namespace Wabbajack.Common
{
MessagePackInit();
if (!Directory.Exists(Consts.LocalAppDataPath))
Directory.CreateDirectory(Consts.LocalAppDataPath);
Consts.LocalAppDataPath.CreateDirectory();
Consts.LogsFolder.CreateDirectory();
if (!Directory.Exists(Consts.LogsFolder))
Directory.CreateDirectory(Consts.LogsFolder);
var programName = Assembly.GetEntryAssembly()?.Location ?? "Wabbajack";
LogFolder = Path.Combine(Path.GetDirectoryName(programName), Consts.LogsFolder);
LogFile = Path.Combine(Consts.LogsFolder, Path.GetFileNameWithoutExtension(programName) + ".current.log");
LogFolder = Consts.LogsFolder;
LogFile = Consts.LogFile;
_startTime = DateTime.Now;
if (LogFile.FileExists())
if (LogFile.Exists)
{
var newPath = Path.Combine(Consts.LogsFolder, Path.GetFileNameWithoutExtension(programName) + new FileInfo(LogFile).LastWriteTime.ToString(" yyyy-MM-dd HH_mm_ss") + ".log");
File.Move(LogFile, newPath, MoveOptions.ReplaceExisting);
var newPath = Consts.LogsFolder.Combine(Consts.EntryPoint.FileNameWithoutExtension + LogFile.LastModified.ToString(" yyyy-MM-dd HH_mm_ss") + ".log");
LogFile.MoveTo(newPath, true);
}
var logFiles = Directory.GetFiles(Consts.LogsFolder);
if (logFiles.Length >= Consts.MaxOldLogs)
var logFiles = Consts.LogsFolder.EnumerateFiles(false).ToList();
if (logFiles.Count >= Consts.MaxOldLogs)
{
Log($"Maximum amount of old logs reached ({logFiles.Length} >= {Consts.MaxOldLogs})");
Log($"Maximum amount of old logs reached ({logFiles.Count} >= {Consts.MaxOldLogs})");
var filesToDelete = logFiles
.Where(File.Exists)
.OrderBy(f =>
{
var fi = new FileInfo(f);
return fi.LastWriteTime;
}).Take(logFiles.Length - Consts.MaxOldLogs).ToList();
.Where(f => f.IsFile)
.OrderBy(f => f.LastModified)
.Take(logFiles.Count - Consts.MaxOldLogs)
.ToList();
Log($"Found {filesToDelete.Count} old log files to delete");
@ -89,7 +83,7 @@ namespace Wabbajack.Common
{
try
{
File.Delete(f);
f.Delete();
success++;
}
catch (Exception e)
@ -102,7 +96,7 @@ namespace Wabbajack.Common
Log($"Deleted {success} log files, failed to delete {failed} logs");
}
var watcher = new FileSystemWatcher(Consts.LocalAppDataPath);
var watcher = new FileSystemWatcher((string)Consts.LocalAppDataPath);
AppLocalEvents = Observable.Merge(Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => watcher.Changed += h, h => watcher.Changed -= h).Select(e => (FileEventType.Changed, e.EventArgs)),
Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => watcher.Created += h, h => watcher.Created -= h).Select(e => (FileEventType.Created, e.EventArgs)),
Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => watcher.Deleted += h, h => watcher.Deleted -= h).Select(e => (FileEventType.Deleted, e.EventArgs)))
@ -164,7 +158,7 @@ namespace Wabbajack.Common
{
lock (_lock)
{
File.AppendAllText(LogFile, $"{(DateTime.Now - _startTime).TotalSeconds:0.##} - {msg}\r\n");
LogFile.AppendAllText($"{(DateTime.Now - _startTime).TotalSeconds:0.##} - {msg}\r\n");
}
}
@ -328,9 +322,9 @@ namespace Wabbajack.Common
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public static dynamic LoadIniFile(this string file)
public static dynamic LoadIniFile(this AbsolutePath file)
{
return new DynamicIniData(new FileIniDataParser().ReadFile(file));
return new DynamicIniData(new FileIniDataParser().ReadFile((string)file));
}
/// <summary>
@ -749,18 +743,17 @@ namespace Wabbajack.Common
{
var dataA = a.xxHash().FromBase64().ToHex();
var dataB = b.xxHash().FromBase64().ToHex();
var cacheFile = Path.Combine(Consts.PatchCacheFolder, $"{dataA}_{dataB}.patch");
if (!Directory.Exists(Consts.PatchCacheFolder))
Directory.CreateDirectory(Consts.PatchCacheFolder);
var cacheFile = Consts.PatchCacheFolder.Combine($"{dataA}_{dataB}.patch");
Consts.PatchCacheFolder.CreateDirectory();
while (true)
{
if (File.Exists(cacheFile))
if (cacheFile.IsFile)
{
RETRY_OPEN:
try
{
await using var f = File.OpenRead(cacheFile);
await using var f = cacheFile.OpenRead();
await f.CopyToAsync(output);
}
catch (IOException)
@ -773,9 +766,9 @@ namespace Wabbajack.Common
}
else
{
var tmpName = Path.Combine(Consts.PatchCacheFolder, Guid.NewGuid() + ".tmp");
var tmpName = Consts.PatchCacheFolder.Combine(Guid.NewGuid() + ".tmp");
await using (var f = File.Open(tmpName, System.IO.FileMode.Create))
await using (var f = tmpName.Create())
{
Status("Creating Patch");
OctoDiff.Create(a, b, f);
@ -784,12 +777,11 @@ namespace Wabbajack.Common
RETRY:
try
{
File.Move(tmpName, cacheFile, MoveOptions.ReplaceExisting);
tmpName.MoveTo(cacheFile, true);
}
catch (UnauthorizedAccessException)
{
if (File.Exists(cacheFile))
if (cacheFile.IsFile)
continue;
await Task.Delay(1000);
goto RETRY;
@ -808,9 +800,9 @@ namespace Wabbajack.Common
await using var sigFile = new TempStream();
OctoDiff.Create(srcStream, destStream, sigFile, patchStream);
patchStream.Position = 0;
var tmpName = Path.Combine(Consts.PatchCacheFolder, Guid.NewGuid() + ".tmp");
var tmpName = Consts.PatchCacheFolder.Combine(Guid.NewGuid() + ".tmp");
await using (var f = File.Create(tmpName))
await using (var f = tmpName.Create())
{
await patchStream.CopyToAsync(f);
patchStream.Position = 0;
@ -818,26 +810,23 @@ namespace Wabbajack.Common
try
{
var cacheFile = Path.Combine(Consts.PatchCacheFolder, $"{srcHash.ToHex()}_{destHash.ToHex()}.patch");
if (!Directory.Exists(Consts.PatchCacheFolder))
Directory.CreateDirectory(Consts.PatchCacheFolder);
var cacheFile = Consts.PatchCacheFolder.Combine($"{srcHash.ToHex()}_{destHash.ToHex()}.patch");
Consts.PatchCacheFolder.CreateDirectory();
File.Move(tmpName, cacheFile, MoveOptions.ReplaceExisting);
tmpName.MoveTo(cacheFile, true);
}
catch (UnauthorizedAccessException)
{
if (File.Exists(tmpName))
File.Delete(tmpName);
tmpName.Delete();
}
}
public static bool TryGetPatch(Hash foundHash, Hash fileHash, out byte[] ePatch)
{
var patchName = Path.Combine(Consts.PatchCacheFolder,
$"{foundHash.ToHex()}_{fileHash.ToHex()}.patch");
if (File.Exists(patchName))
var patchName = Consts.PatchCacheFolder.Combine($"{foundHash.ToHex()}_{fileHash.ToHex()}.patch");
if (patchName.Exists)
{
ePatch = File.ReadAllBytes(patchName);
ePatch = patchName.ReadAllBytes();
return true;
}
@ -1084,42 +1073,35 @@ namespace Wabbajack.Common
public static void ToEcryptedData(this byte[] bytes, string key)
{
var encoded = ProtectedData.Protect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
Consts.LocalAppDataPath.CreateDirectory();
if (!Directory.Exists(Consts.LocalAppDataPath))
Directory.CreateDirectory(Consts.LocalAppDataPath);
var path = Path.Combine(Consts.LocalAppDataPath, key);
File.WriteAllBytes(path, encoded);
Consts.LocalAppDataPath.Combine(key).WriteAllBytes(bytes);
}
public static byte[] FromEncryptedData(string key)
{
var path = Path.Combine(Consts.LocalAppDataPath, key);
var bytes = File.ReadAllBytes(path);
var bytes = Consts.LocalAppDataPath.Combine(key).ReadAllBytes();
return ProtectedData.Unprotect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
}
public static bool HaveEncryptedJson(string key)
{
var path = Path.Combine(Consts.LocalAppDataPath, key);
return File.Exists(path);
return Consts.LocalAppDataPath.Combine(key).IsFile;
}
public static IObservable<(FileEventType, FileSystemEventArgs)> AppLocalEvents { get; }
public static IObservable<bool> HaveEncryptedJsonObservable(string key)
{
var path = Path.Combine(Consts.LocalAppDataPath, key).ToLower();
return AppLocalEvents.Where(t => t.Item2.FullPath.ToLower() == path)
.Select(_ => File.Exists(path))
.StartWith(File.Exists(path))
var path = Consts.LocalAppDataPath.Combine(key);
return AppLocalEvents.Where(t => (AbsolutePath)t.Item2.FullPath.ToLower() == path)
.Select(_ => path.Exists)
.StartWith(path.Exists)
.DistinctUntilChanged();
}
public static void DeleteEncryptedJson(string key)
{
var path = Path.Combine(Consts.LocalAppDataPath, key);
if (File.Exists(path))
File.Delete(path);
Consts.LocalAppDataPath.Combine(key).Delete();
}
public static void StartProcessFromFile(string file)

View File

@ -19,11 +19,12 @@ namespace Wabbajack.Lib
{
public abstract class ACompiler : ABatchProcessor
{
public string ModListName, ModListAuthor, ModListDescription, ModListImage, ModListWebsite, ModListReadme;
public string ModListName, ModListAuthor, ModListDescription, ModListWebsite;
public RelativePath ModListImage, ModListReadme;
public bool ReadmeIsWebsite;
protected Version WabbajackVersion;
public abstract string VFSCacheName { get; }
public abstract AbsolutePath VFSCacheName { get; }
//protected string VFSCacheName => Path.Combine(Consts.LocalAppDataPath, $"vfs_compile_cache.bin");
/// <summary>
/// A stream of tuples of ("Update Title", 0.25) which represent the name of the current task
@ -34,10 +35,10 @@ namespace Wabbajack.Lib
public abstract ModManager ModManager { get; }
public abstract string GamePath { get; }
public abstract AbsolutePath GamePath { get; }
public abstract string ModListOutputFolder { get; }
public abstract string ModListOutputFile { get; }
public abstract AbsolutePath ModListOutputFolder { get; }
public abstract AbsolutePath ModListOutputFile { get; }
public bool IgnoreMissingFiles { get; set; }
@ -65,23 +66,35 @@ namespace Wabbajack.Lib
throw new Exception(msg);
}
internal string IncludeFile(byte[] data)
internal RelativePath IncludeId()
{
var id = Guid.NewGuid().ToString();
File.WriteAllBytes(Path.Combine(ModListOutputFolder, id), data);
return RelativePath.RandomFileName();
}
internal async Task<RelativePath> IncludeFile(byte[] data)
{
var id = IncludeId();
await ModListOutputFolder.Combine(id).WriteAllBytesAsync(data);
return id;
}
internal FileStream IncludeFile(out string id)
internal FileStream IncludeFile(out RelativePath id)
{
id = Guid.NewGuid().ToString();
return File.Create(Path.Combine(ModListOutputFolder, id));
id = IncludeId();
return ModListOutputFolder.Combine(id).Create();
}
internal string IncludeFile(string data)
internal async Task<RelativePath> IncludeFile(string data)
{
var id = Guid.NewGuid().ToString();
File.WriteAllText(Path.Combine(ModListOutputFolder, id), data);
var id = IncludeId();
await ModListOutputFolder.Combine(id).WriteAllTextAsync(data);
return id;
}
internal RelativePath IncludeFile(AbsolutePath data)
{
var id = IncludeId();
data.Copy(ModListOutputFolder.Combine(id));
return id;
}

View File

@ -20,18 +20,18 @@ namespace Wabbajack.Lib
{
public bool IgnoreMissingFiles { get; internal set; } = false;
public string OutputFolder { get; private set; }
public string DownloadFolder { get; private set; }
public AbsolutePath OutputFolder { get; private set; }
public AbsolutePath DownloadFolder { get; private set; }
public abstract ModManager ModManager { get; }
public string ModListArchive { get; private set; }
public ModList ModList { get; private set; }
public Dictionary<Hash, string> HashedArchives { get; set; }
public Dictionary<Hash, AbsolutePath> HashedArchives { get; set; }
public SystemParameters SystemParameters { get; set; }
public AInstaller(string archive, ModList modList, string outputFolder, string downloadFolder, SystemParameters parameters)
public AInstaller(string archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
{
ModList = modList;
ModListArchive = archive;
@ -90,20 +90,9 @@ namespace Wabbajack.Lib
/// We don't want to make the installer index all the archives, that's just a waste of time, so instead
/// we'll pass just enough information to VFS to let it know about the files we have.
/// </summary>
public async Task PrimeVFS()
protected async Task PrimeVFS()
{
VFS.AddKnown(HashedArchives.Select(a => new KnownFile
{
Paths = new[] { a.Value },
Hash = a.Key
}));
VFS.AddKnown(
ModList.Directives
.OfType<FromArchive>()
.Select(f => new KnownFile { Paths = f.ArchiveHashPath, Hash = f.Hash}));
VFS.AddKnown(ModList.Directives.OfType<FromArchive>().Select(d => d.ArchiveHashPath), HashedArchives);
await VFS.BackfillMissing();
}
@ -111,13 +100,9 @@ namespace Wabbajack.Lib
{
Info("Building Folder Structure");
ModList.Directives
.Select(d => Path.Combine(OutputFolder, Path.GetDirectoryName(d.To)))
.Select(d => OutputFolder.Combine(d.To.Parent))
.Distinct()
.Do(f =>
{
if (Directory.Exists(f)) return;
Directory.CreateDirectory(f);
});
.Do(f => OutputFolder.CreateDirectory());
}
public async Task InstallArchives()
@ -126,8 +111,8 @@ namespace Wabbajack.Lib
Info("Grouping Install Files");
var grouped = ModList.Directives
.OfType<FromArchive>()
.GroupBy(e => e.ArchiveHashPath[0])
.ToDictionary(k => Hash.FromBase64(k.Key));
.GroupBy(e => e.ArchiveHashPath.BaseHash)
.ToDictionary(k => k.Key);
var archives = ModList.Archives
.Select(a => new { Archive = a, AbsolutePath = HashedArchives.GetOrDefault(a.Hash) })
.Where(a => a.AbsolutePath != null)
@ -137,7 +122,7 @@ namespace Wabbajack.Lib
await archives.PMap(Queue, UpdateTracker,a => InstallArchive(Queue, a.Archive, a.AbsolutePath, grouped[a.Archive.Hash]));
}
private async Task InstallArchive(WorkQueue queue, Archive archive, string absolutePath, IGrouping<string, FromArchive> grouping)
private async Task InstallArchive(WorkQueue queue, Archive archive, AbsolutePath absolutePath, IGrouping<Hash, FromArchive> grouping)
{
Status($"Extracting {archive.Name}");
@ -184,13 +169,12 @@ namespace Wabbajack.Lib
.PDoIndexed(queue, (idx, group) =>
{
Utils.Status("Installing files", Percent.FactoryPutInRange(idx, vFiles.Count));
var firstDest = Path.Combine(OutputFolder, group.First().To);
CopyFile(group.Key.StagedPath, firstDest, true);
var firstDest = OutputFolder.Combine(group.First().To);
group.Key.StagedPath.CopyTo(firstDest, true);
foreach (var copy in group.Skip(1))
{
var nextDest = Path.Combine(OutputFolder, copy.To);
CopyFile(firstDest, nextDest, false);
firstDest.CopyTo(OutputFolder.Combine(copy.To));
}
});
@ -203,25 +187,25 @@ namespace Wabbajack.Lib
.PMap(queue, async toPatch =>
{
await using var patchStream = new MemoryStream();
Status($"Patching {Path.GetFileName(toPatch.To)}");
Status($"Patching {toPatch.To.FileName}");
// Read in the patch data
byte[] patchData = LoadBytesFromPath(toPatch.PatchID);
var toFile = Path.Combine(OutputFolder, toPatch.To);
var oldData = new MemoryStream(File.ReadAllBytes(toFile));
var toFile = OutputFolder.Combine(toPatch.To);
var oldData = new MemoryStream(await toFile.ReadAllBytesAsync());
// Remove the file we're about to patch
File.Delete(toFile);
toFile.Delete();
// Patch it
await using (var outStream = File.Open(toFile, FileMode.Create))
await using (var outStream = toFile.Create())
{
Utils.ApplyPatch(oldData, () => new MemoryStream(patchData), outStream);
}
Status($"Verifying Patch {Path.GetFileName(toPatch.To)}");
var resultSha = toFile.FileHash();
Status($"Verifying Patch {toPatch.To.FileName}");
var resultSha = await toFile.FileHashAsync();
if (resultSha != toPatch.Hash)
throw new InvalidDataException($"Invalid Hash for {toPatch.To} after patching");
});

View File

@ -14,7 +14,7 @@ namespace Wabbajack.Lib.CompilationSteps.CompilationErrors
public Hash Hash { get; }
public string PathToFile { get; }
private readonly CleanedESM _esm;
public string GameFileName => Path.GetFileName(_esm.To);
public RelativePath GameFileName => _esm.To.FileName;
public override string ShortDescription
{
get =>
@ -24,7 +24,7 @@ namespace Wabbajack.Lib.CompilationSteps.CompilationErrors
public override string ExtendedDescription
{
get =>
$@"This modlist is setup to perform automatic cleaning of the stock game file {GameFileName} in order to perform this cleaning Wabbajack must first verify that the
$@"This modlist is setup to perform automatic cleaning of the stock game file {(string)GameFileName} in order to perform this cleaning Wabbajack must first verify that the
source file is in the correct state. It seems that the file in your game directory has a hash of {Hash} instead of the expect hash of {_esm.SourceESMHash}. This could be caused by
the modlist expecting a different of the game than you currently have installed, or perhaps you have already cleaned the file. You could attempt to fix this error by re-installing
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.";

View File

@ -16,7 +16,7 @@ namespace Wabbajack.Lib.CompilationSteps
if (!_compiler.IndexedFiles.TryGetValue(source.Hash, out var found)) return null;
var result = source.EvolveTo<FromArchive>();
var match = found.Where(f => Path.GetFileName(f.Name) == Path.GetFileName(source.Path))
var match = found.Where(f => f.Name.FileName == source.Path.FileName)
.OrderBy(f => f.NestingFactor)
.FirstOrDefault()
?? found.OrderBy(f => f.NestingFactor).FirstOrDefault();

View File

@ -18,7 +18,7 @@ namespace Wabbajack.Lib.CompilationSteps
var alwaysEnabled = _mo2Compiler.ModInis.Where(f => IsAlwaysEnabled(f.Value)).Select(f => f.Key).Distinct();
_allEnabledMods = _mo2Compiler.SelectedProfiles
.SelectMany(p => File.ReadAllLines(Path.Combine(_mo2Compiler.MO2Folder, "profiles", p, "modlist.txt")))
.SelectMany(p => _mo2Compiler.MO2Folder.Combine("profiles", p, "modlist.txt").ReadAllLines())
.Where(line => line.StartsWith("+") || line.EndsWith("_separator"))
.Select(line => line.Substring(1))
.Concat(alwaysEnabled)

View File

@ -17,7 +17,7 @@ namespace Wabbajack.Lib.CompilationSteps
var b = false;
_vortexCompiler.ActiveArchives.Do(a =>
{
if (source.Path.Contains(a)) b = true;
if (((string)source.Path).Contains(a)) b = true;
});
if (b) return null;
var r = source.EvolveTo<IgnoredDirectly>();

View File

@ -16,7 +16,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!source.Path.EndsWith(_postfix)) return null;
if (!((string)source.Path).EndsWith(_postfix)) return null;
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = _reason;
return result;

View File

@ -15,7 +15,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!source.Path.StartsWith(_startDir)) return null;
if (!((string)source.Path).StartsWith(_startDir)) return null;
var i = source.EvolveTo<IgnoredDirectly>();
i.Reason = "Default game file";
return i;

View File

@ -7,11 +7,11 @@ namespace Wabbajack.Lib.CompilationSteps
public class IgnoreGameFilesIfGameFolderFilesExist : ACompilationStep
{
private readonly bool _gameFolderFilesExists;
private readonly string _gameFolder;
private readonly AbsolutePath _gameFolder;
public IgnoreGameFilesIfGameFolderFilesExist(ACompiler compiler) : base(compiler)
{
_gameFolderFilesExists = Directory.Exists(Path.Combine(((MO2Compiler)compiler).MO2Folder, Consts.GameFolderFilesDir));
_gameFolderFilesExists = ((MO2Compiler)compiler).MO2Folder.Combine(Consts.GameFolderFilesDir).IsDirectory;
_gameFolder = compiler.GamePath;
}
@ -19,7 +19,7 @@ namespace Wabbajack.Lib.CompilationSteps
{
if (_gameFolderFilesExists)
{
if (source.AbsolutePath.IsInPath(_gameFolder))
if (source.AbsolutePath.InFolder(_gameFolder))
{
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = $"Ignoring game files because {Consts.GameFolderFilesDir} exists";

View File

@ -16,7 +16,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!source.Path.Contains(_pattern)) return null;
if (!((string)source.Path).Contains(_pattern)) return null;
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = _reason;
return result;

View File

@ -19,7 +19,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!_regex.IsMatch(source.Path)) return null;
if (!_regex.IsMatch((string)source.Path)) return null;
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = _reason;
return result;

View File

@ -16,14 +16,15 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (source.Path.StartsWith(_prefix))
if (!((string)source.Path).StartsWith(_prefix))
{
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = _reason;
return result;
return null;
}
return null;
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = _reason;
return result;
}
public override IState GetState()

View File

@ -15,7 +15,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (Path.GetDirectoryName(source.AbsolutePath) != _vortex.DownloadsFolder) return null;
if (source.AbsolutePath.Parent != _vortex.DownloadsFolder) return null;
var result = source.EvolveTo<IgnoredDirectly>();
result.Reason = "Ignored because it is a Vortex file";
return result;

View File

@ -13,7 +13,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
var inline = source.EvolveTo<InlineFile>();
inline.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
inline.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
return inline;
}

View File

@ -13,9 +13,9 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path))) return null;
if (!Consts.ConfigFileExtensions.Contains(source.Path.Extension)) return null;
var result = source.EvolveTo<InlineFile>();
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
result.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
return result;
}

View File

@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using Newtonsoft.Json;
using Wabbajack.Common;
namespace Wabbajack.Lib.CompilationSteps
{
@ -12,19 +13,16 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (Path.GetExtension(source.AbsolutePath) != ".esp" &&
Path.GetExtension(source.AbsolutePath) != ".esm") return null;
if (source.AbsolutePath.Extension != Consts.ESP &&
source.AbsolutePath.Extension != Consts.ESM) return null;
var bsa = Path.Combine(Path.GetDirectoryName(source.AbsolutePath),
Path.GetFileNameWithoutExtension(source.AbsolutePath) + ".bsa");
var bsaTextures = Path.Combine(Path.GetDirectoryName(source.AbsolutePath),
Path.GetFileNameWithoutExtension(source.AbsolutePath) + " - Textures.bsa");
var espSize = new FileInfo(source.AbsolutePath).Length;
var bsa = source.AbsolutePath.ReplaceExtension(Consts.BSA);
var bsaTextures = source.AbsolutePath.AppendToName(bsa, " - Textures");
if (espSize > 250 || !File.Exists(bsa) && !File.Exists(bsaTextures)) return null;
if (source.AbsolutePath.Size > 250 || !bsa.IsFile && !bsaTextures.IsFile) return null;
var inline = source.EvolveTo<InlineFile>();
inline.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
inline.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
return inline;
}

View File

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

View File

@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using Newtonsoft.Json;
using Wabbajack.Common;
namespace Wabbajack.Lib.CompilationSteps
{
@ -12,9 +13,9 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!source.Path.StartsWith("mods\\") || !source.Path.EndsWith("\\meta.ini")) return null;
if (!source.Path.StartsWith("mods\\") || source.Path.FileName != Consts.MetaIni) return null;
var e = source.EvolveTo<InlineFile>();
e.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
e.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
return e;
}

View File

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem;
using Newtonsoft.Json;
using Wabbajack.Common;
namespace Wabbajack.Lib.CompilationSteps
{
@ -15,16 +16,16 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
var files = new HashSet<string>
var files = new HashSet<AbsolutePath>
{
_compiler.ModListImage, _compiler.ModListReadme
};
if (!files.Any(f => source.AbsolutePath.Equals(f))) return null;
if (!File.Exists(source.AbsolutePath)) return null;
if (!source.AbsolutePath.Exists) return null;
var isBanner = source.AbsolutePath == _compiler.ModListImage;
//var isReadme = source.AbsolutePath == ModListReadme;
var result = source.EvolveTo<PropertyFile>();
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
result.SourceDataID = await _compiler.IncludeFile(source.AbsolutePath.ReadAllBytesAsync());
if (isBanner)
{
result.Type = PropertyType.Banner;

View File

@ -18,10 +18,10 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
if (!_regex.IsMatch(source.Path)) return null;
if (!_regex.IsMatch((string)source.Path)) return null;
var result = source.EvolveTo<InlineFile>();
result.SourceDataID = _compiler.IncludeFile(File.ReadAllBytes(source.AbsolutePath));
result.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
return result;
}

View File

@ -17,7 +17,7 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
return Consts.ConfigFileExtensions.Contains(Path.GetExtension(source.Path)) ? RemapFile(source) : null;
return Consts.ConfigFileExtensions.Contains(source.Path.Extension) ? await RemapFile(source) : null;
}
public override IState GetState()
@ -25,23 +25,23 @@ namespace Wabbajack.Lib.CompilationSteps
return new State();
}
private Directive RemapFile(RawSourceFile source)
private async Task<Directive> RemapFile(RawSourceFile source)
{
var data = File.ReadAllText(source.AbsolutePath);
var data = await source.AbsolutePath.ReadAllTextAsync();
var originalData = data;
data = data.Replace(_mo2Compiler.GamePath, Consts.GAME_PATH_MAGIC_BACK);
data = data.Replace(_mo2Compiler.GamePath.Replace("\\", "\\\\"), Consts.GAME_PATH_MAGIC_DOUBLE_BACK);
data = data.Replace(_mo2Compiler.GamePath.Replace("\\", "/"), Consts.GAME_PATH_MAGIC_FORWARD);
data = data.Replace((string)_mo2Compiler.GamePath, Consts.GAME_PATH_MAGIC_BACK);
data = data.Replace(((string)_mo2Compiler.GamePath).Replace("\\", "\\\\"), Consts.GAME_PATH_MAGIC_DOUBLE_BACK);
data = data.Replace(((string)_mo2Compiler.GamePath).Replace("\\", "/"), Consts.GAME_PATH_MAGIC_FORWARD);
data = data.Replace(_mo2Compiler.MO2Folder, Consts.MO2_PATH_MAGIC_BACK);
data = data.Replace(_mo2Compiler.MO2Folder.Replace("\\", "\\\\"), Consts.MO2_PATH_MAGIC_DOUBLE_BACK);
data = data.Replace(_mo2Compiler.MO2Folder.Replace("\\", "/"), Consts.MO2_PATH_MAGIC_FORWARD);
data = data.Replace((string)_mo2Compiler.MO2Folder, Consts.MO2_PATH_MAGIC_BACK);
data = data.Replace(((string)_mo2Compiler.MO2Folder).Replace("\\", "\\\\"), Consts.MO2_PATH_MAGIC_DOUBLE_BACK);
data = data.Replace(((string)_mo2Compiler.MO2Folder).Replace("\\", "/"), Consts.MO2_PATH_MAGIC_FORWARD);
data = data.Replace(_mo2Compiler.MO2DownloadsFolder, Consts.DOWNLOAD_PATH_MAGIC_BACK);
data = data.Replace(_mo2Compiler.MO2DownloadsFolder.Replace("\\", "\\\\"),
data = data.Replace((string)_mo2Compiler.MO2DownloadsFolder, Consts.DOWNLOAD_PATH_MAGIC_BACK);
data = data.Replace(((string)_mo2Compiler.MO2DownloadsFolder).Replace("\\", "\\\\"),
Consts.DOWNLOAD_PATH_MAGIC_DOUBLE_BACK);
data = data.Replace(_mo2Compiler.MO2DownloadsFolder.Replace("\\", "/"), Consts.DOWNLOAD_PATH_MAGIC_FORWARD);
data = data.Replace(((string)_mo2Compiler.MO2DownloadsFolder).Replace("\\", "/"), Consts.DOWNLOAD_PATH_MAGIC_FORWARD);
if (data == originalData)
return null;

View File

@ -18,10 +18,10 @@ namespace Wabbajack.Lib.CompilationSteps
public override async ValueTask<Directive> Run(RawSourceFile source)
{
var filename = Path.GetFileName(source.Path);
var gameFile = Path.Combine(_mo2Compiler.GamePath, "Data", filename);
var filename = source.Path.FileName;
var gameFile = _mo2Compiler.GamePath.Combine((RelativePath)"Data", filename);
if (!Consts.GameESMs.Contains(filename) || !source.Path.StartsWith("mods\\") ||
!File.Exists(gameFile)) return null;
!gameFile.Exists) return null;
Utils.Log(
$"An ESM named {filename} was found in a mod that shares a name with one of the core game ESMs, it is assumed this is a cleaned ESM and it will be binary patched");
@ -31,9 +31,9 @@ namespace Wabbajack.Lib.CompilationSteps
Utils.Status($"Generating patch of {filename}");
await using (var ms = new MemoryStream())
{
await Utils.CreatePatch(File.ReadAllBytes(gameFile), File.ReadAllBytes(source.AbsolutePath), ms);
await Utils.CreatePatch(await gameFile.ReadAllBytesAsync(), await source.AbsolutePath.ReadAllBytesAsync(), ms);
var data = ms.ToArray();
result.SourceDataID = _compiler.IncludeFile(data);
result.SourceDataID = await _compiler.IncludeFile(data);
Utils.Log($"Generated a {data.Length} byte patch for {filename}");
}

View File

@ -12,11 +12,9 @@ namespace Wabbajack.Lib
{
public class RawSourceFile
{
// ToDo
// Make readonly
public string Path;
public readonly RelativePath Path;
public RawSourceFile(VirtualFile file, string path)
public RawSourceFile(VirtualFile file, RelativePath path)
{
File = file;
Path = path;
@ -153,7 +151,7 @@ namespace Wabbajack.Lib
/// location the file will be copied to, relative to the install path.
/// </summary>
[Key(2)]
public string To { get; set; }
public RelativePath To { get; set; }
}
public class IgnoredDirectly : Directive
@ -172,14 +170,14 @@ namespace Wabbajack.Lib
/// Data that will be written as-is to the destination location;
/// </summary>
[Key(3)]
public string SourceDataID;
public RelativePath SourceDataID { get; set; }
}
[MessagePackObject]
public class ArchiveMeta : Directive
{
[Key(3)]
public string SourceDataID { get; set; }
public RelativePath SourceDataID { get; set; }
}
public enum PropertyType { Banner, Readme }
@ -218,7 +216,7 @@ namespace Wabbajack.Lib
private string _fullPath;
[Key(3)]
public string[] ArchiveHashPath { get; set; }
public HashRelativePath ArchiveHashPath { get; set; }
[IgnoreMember]
public VirtualFile FromFile { get; set; }
@ -257,7 +255,7 @@ namespace Wabbajack.Lib
[Key(0)]
public Hash Hash { get; set; }
[Key(1)]
public string RelativePath { get; set; }
public RelativePath RelativePath { get; set; }
}
[MessagePackObject]

View File

@ -26,41 +26,41 @@ namespace Wabbajack.Lib
public class MO2Compiler : ACompiler
{
private string _mo2DownloadsFolder;
private AbsolutePath _mo2DownloadsFolder;
public string MO2Folder;
public AbsolutePath MO2Folder;
public string MO2Profile { get; }
public Dictionary<string, dynamic> ModMetas { get; set; }
public override ModManager ModManager => ModManager.MO2;
public override string GamePath { get; }
public override AbsolutePath GamePath { get; }
public GameMetaData CompilingGame { get; set; }
public override string ModListOutputFolder => "output_folder";
public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint();
public override string ModListOutputFile { get; }
public override AbsolutePath ModListOutputFile { get; }
public override string VFSCacheName => Path.Combine(
Consts.LocalAppDataPath,
$"vfs_compile_cache-{Path.Combine(MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin");
public override AbsolutePath VFSCacheName =>
Consts.LocalAppDataPath.Combine(
$"vfs_compile_cache-{Path.Combine((string)MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin");
public MO2Compiler(string mo2Folder, string mo2Profile, string outputFile)
public MO2Compiler(AbsolutePath mo2Folder, string mo2Profile, AbsolutePath outputFile)
{
MO2Folder = mo2Folder;
MO2Profile = mo2Profile;
MO2Ini = Path.Combine(MO2Folder, "ModOrganizer.ini").LoadIniFile();
MO2Ini = MO2Folder.Combine("ModOrganizer.ini").LoadIniFile();
var mo2game = (string)MO2Ini.General.gameName;
CompilingGame = GameRegistry.Games.First(g => g.Value.MO2Name == mo2game).Value;
GamePath = ((string)MO2Ini.General.gamePath).Replace("\\\\", "\\");
GamePath = new AbsolutePath((string)MO2Ini.General.gamePath.Replace("\\\\", "\\"));
ModListOutputFile = outputFile;
}
public dynamic MO2Ini { get; }
public string MO2DownloadsFolder
public AbsolutePath MO2DownloadsFolder
{
get
{
@ -74,9 +74,9 @@ namespace Wabbajack.Lib
set => _mo2DownloadsFolder = value;
}
public static string GetTypicalDownloadsFolder(string mo2Folder) => Path.Combine(mo2Folder, "downloads");
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
public string MO2ProfileDir => Path.Combine(MO2Folder, "profiles", MO2Profile);
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
internal UserStatus User { get; private set; }
public ConcurrentBag<Directive> ExtraFiles { get; private set; }
@ -91,9 +91,9 @@ namespace Wabbajack.Lib
UpdateTracker.Reset();
UpdateTracker.NextStep("Gathering information");
Info("Looking for other profiles");
var otherProfilesPath = Path.Combine(MO2ProfileDir, "otherprofiles.txt");
var otherProfilesPath = MO2ProfileDir.Combine("otherprofiles.txt");
SelectedProfiles = new HashSet<string>();
if (File.Exists(otherProfilesPath)) SelectedProfiles = File.ReadAllLines(otherProfilesPath).ToHashSet();
if (otherProfilesPath.Exists) SelectedProfiles = (await otherProfilesPath.ReadAllLinesAsync()).ToHashSet();
SelectedProfiles.Add(MO2Profile);
Info("Using Profiles: " + string.Join(", ", SelectedProfiles.OrderBy(p => p)));

View File

@ -31,14 +31,14 @@ namespace Wabbajack.Lib
public Game Game { get; }
public string GameName { get; }
public string VortexFolder { get; set; }
public string StagingFolder { get; set; }
public string DownloadsFolder { get; set; }
public AbsolutePath VortexFolder { get; set; }
public AbsolutePath StagingFolder { get; set; }
public AbsolutePath DownloadsFolder { get; set; }
public override ModManager ModManager => ModManager.Vortex;
public override string GamePath { get; }
public override string ModListOutputFolder => "output_folder";
public override string ModListOutputFile { get; }
public override AbsolutePath GamePath { get; }
public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint();
public override AbsolutePath ModListOutputFile { get; }
public const string StagingMarkerName = "__vortex_staging_folder";
public const string DownloadMarkerName = "__vortex_downloads_folder";

View File

@ -233,9 +233,12 @@ namespace Wabbajack.VirtualFileSystem
#region KnownFiles
private List<HashRelativePath> _knownFiles = new List<HashRelativePath>();
public void AddKnown(IEnumerable<HashRelativePath> known)
private Dictionary<Hash, AbsolutePath> _knownArchives = new Dictionary<Hash, AbsolutePath>();
public void AddKnown(IEnumerable<HashRelativePath> known, Dictionary<Hash, AbsolutePath> archives)
{
_knownFiles.AddRange(known);
foreach (var (key, value) in archives)
_knownArchives.TryAdd(key, value);
}
public async Task BackfillMissing()