diff --git a/Wabbajack.Common/Consts.cs b/Wabbajack.Common/Consts.cs
index 12142972..56ab64c6 100644
--- a/Wabbajack.Common/Consts.cs
+++ b/Wabbajack.Common/Consts.cs
@@ -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;
     }
 }
diff --git a/Wabbajack.Common/Hash.cs b/Wabbajack.Common/Hash.cs
index aa44138b..2350e037 100644
--- a/Wabbajack.Common/Hash.cs
+++ b/Wabbajack.Common/Hash.cs
@@ -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();
diff --git a/Wabbajack.Common/Paths.cs b/Wabbajack.Common/Paths.cs
index f92c1613..0f37b193 100644
--- a/Wabbajack.Common/Paths.cs
+++ b/Wabbajack.Common/Paths.cs
@@ -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);
diff --git a/Wabbajack.Common/Utils.cs b/Wabbajack.Common/Utils.cs
index 931f3797..4cfa9093 100644
--- a/Wabbajack.Common/Utils.cs
+++ b/Wabbajack.Common/Utils.cs
@@ -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)
diff --git a/Wabbajack.Lib/ACompiler.cs b/Wabbajack.Lib/ACompiler.cs
index 81bf812d..e6927b18 100644
--- a/Wabbajack.Lib/ACompiler.cs
+++ b/Wabbajack.Lib/ACompiler.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/AInstaller.cs b/Wabbajack.Lib/AInstaller.cs
index ee0e4c80..978cc727 100644
--- a/Wabbajack.Lib/AInstaller.cs
+++ b/Wabbajack.Lib/AInstaller.cs
@@ -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");
                 });
diff --git a/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs b/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs
index 4bb367c3..3a362505 100644
--- a/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs
+++ b/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs
@@ -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.";
diff --git a/Wabbajack.Lib/CompilationSteps/DirectMatch.cs b/Wabbajack.Lib/CompilationSteps/DirectMatch.cs
index 73cc5368..807184f4 100644
--- a/Wabbajack.Lib/CompilationSteps/DirectMatch.cs
+++ b/Wabbajack.Lib/CompilationSteps/DirectMatch.cs
@@ -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();
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs b/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs
index 7cb8f350..7c049cab 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreDisabledMods.cs
@@ -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)
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreDisabledVortexMods.cs b/Wabbajack.Lib/CompilationSteps/IgnoreDisabledVortexMods.cs
index a2c0ad8d..4694905a 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreDisabledVortexMods.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreDisabledVortexMods.cs
@@ -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>();
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreEndsWith.cs b/Wabbajack.Lib/CompilationSteps/IgnoreEndsWith.cs
index 4b0b6582..adec2b87 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreEndsWith.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreEndsWith.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreGameFiles.cs b/Wabbajack.Lib/CompilationSteps/IgnoreGameFiles.cs
index 5911b24f..30889a77 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreGameFiles.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreGameFiles.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreGameFilesIfGameFolderFilesExist.cs b/Wabbajack.Lib/CompilationSteps/IgnoreGameFilesIfGameFolderFilesExist.cs
index c2dc1cdb..420a17a2 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreGameFilesIfGameFolderFilesExist.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreGameFilesIfGameFolderFilesExist.cs
@@ -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";
diff --git a/Wabbajack.Lib/CompilationSteps/IgnorePathContains.cs b/Wabbajack.Lib/CompilationSteps/IgnorePathContains.cs
index feef6d93..25094e16 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnorePathContains.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnorePathContains.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreRegex.cs b/Wabbajack.Lib/CompilationSteps/IgnoreRegex.cs
index bb00fd6b..7c004d2a 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreRegex.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreRegex.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreStartsWith.cs b/Wabbajack.Lib/CompilationSteps/IgnoreStartsWith.cs
index b8ce4b99..ce345a53 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreStartsWith.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreStartsWith.cs
@@ -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()
diff --git a/Wabbajack.Lib/CompilationSteps/IgnoreVortex.cs b/Wabbajack.Lib/CompilationSteps/IgnoreVortex.cs
index e468d158..b3282001 100644
--- a/Wabbajack.Lib/CompilationSteps/IgnoreVortex.cs
+++ b/Wabbajack.Lib/CompilationSteps/IgnoreVortex.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeAll.cs b/Wabbajack.Lib/CompilationSteps/IncludeAll.cs
index a307aed6..9f836158 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeAll.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeAll.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeAllConfigs.cs b/Wabbajack.Lib/CompilationSteps/IncludeAllConfigs.cs
index 31539ffe..10ae1e26 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeAllConfigs.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeAllConfigs.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeDummyESPs.cs b/Wabbajack.Lib/CompilationSteps/IncludeDummyESPs.cs
index e416f772..8a420488 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeDummyESPs.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeDummyESPs.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeLOOTFiles.cs b/Wabbajack.Lib/CompilationSteps/IncludeLOOTFiles.cs
index d12ad53d..7bc05446 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeLOOTFiles.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeLOOTFiles.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeModIniData.cs b/Wabbajack.Lib/CompilationSteps/IncludeModIniData.cs
index 01f4499a..c5d3614a 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeModIniData.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeModIniData.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/CompilationSteps/IncludePropertyFiles.cs b/Wabbajack.Lib/CompilationSteps/IncludePropertyFiles.cs
index 63ca4457..034050f1 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludePropertyFiles.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludePropertyFiles.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeRegex.cs b/Wabbajack.Lib/CompilationSteps/IncludeRegex.cs
index c7c4cfa7..a7482285 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeRegex.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeRegex.cs
@@ -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;
         }
 
diff --git a/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs b/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs
index 6fc7363e..4e28bff5 100644
--- a/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs
+++ b/Wabbajack.Lib/CompilationSteps/IncludeStubbedConfigfiles.cs
@@ -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;
diff --git a/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs b/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs
index 81dd5b69..e71dfc0c 100644
--- a/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs
+++ b/Wabbajack.Lib/CompilationSteps/PatchStockESMs.cs
@@ -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}");
             }
 
diff --git a/Wabbajack.Lib/Data.cs b/Wabbajack.Lib/Data.cs
index 96449de4..1ceb4ab7 100644
--- a/Wabbajack.Lib/Data.cs
+++ b/Wabbajack.Lib/Data.cs
@@ -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]
diff --git a/Wabbajack.Lib/MO2Compiler.cs b/Wabbajack.Lib/MO2Compiler.cs
index 3483f3d8..8c07eede 100644
--- a/Wabbajack.Lib/MO2Compiler.cs
+++ b/Wabbajack.Lib/MO2Compiler.cs
@@ -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)));
diff --git a/Wabbajack.Lib/VortexCompiler.cs b/Wabbajack.Lib/VortexCompiler.cs
index fa11b0c4..4c375120 100644
--- a/Wabbajack.Lib/VortexCompiler.cs
+++ b/Wabbajack.Lib/VortexCompiler.cs
@@ -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";
diff --git a/Wabbajack.VirtualFileSystem/Context.cs b/Wabbajack.VirtualFileSystem/Context.cs
index fadc1c52..6da9785f 100644
--- a/Wabbajack.VirtualFileSystem/Context.cs
+++ b/Wabbajack.VirtualFileSystem/Context.cs
@@ -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()