diff --git a/CHANGELOG.md b/CHANGELOG.md index f00af9ac..16abbce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ### Changelog +#### Version - 3.0.3.1 - 10/30/2022 +* Fix file verification issues for CreatedBSAs +* Fix files during verification where CreatedDate > LastModified + #### Version - 3.0.3.0 - 10/26/2022 * Verify hashes of all installed files * Verify contents of BSAs during installation diff --git a/Wabbajack.CLI/Verbs/VerifyModlistInstall.cs b/Wabbajack.CLI/Verbs/VerifyModlistInstall.cs index 173609ba..7a197fe0 100644 --- a/Wabbajack.CLI/Verbs/VerifyModlistInstall.cs +++ b/Wabbajack.CLI/Verbs/VerifyModlistInstall.cs @@ -54,61 +54,58 @@ public class VerifyModlistInstall _logger.LogInformation("Scanning files"); - var errors = await list.Directives.PMapAllBatchedAsync(_limiter, async directive => - { - if (directive is ArchiveMeta) - return null; - - if (directive is RemappedInlineFile) - return null; - - if (directive.To.InFolder(Consts.BSACreationDir)) - return null; - - var dest = directive.To.RelativeTo(installFolder); - if (!dest.FileExists()) + var errors = await list.Directives.PMapAllBatchedAsync(_limiter, async directive => { - return new Result + if (!(directive is CreateBSA || directive.IsDeterministic)) + return null; + + if (directive.To.InFolder(Consts.BSACreationDir)) + return null; + + var dest = directive.To.RelativeTo(installFolder); + if (!dest.FileExists()) { - Path = directive.To, - Message = $"File does not exist directive {directive.GetType()}" - }; - } + return new Result + { + Path = directive.To, + Message = $"File does not exist directive {directive.GetType()}" + }; + } + + if (Consts.KnownModifiedFiles.Contains(directive.To.FileName)) + return null; + + if (directive is CreateBSA bsa) + { + return await VerifyBSA(dest, bsa, byTo, token); + } + + if (dest.Size() != directive.Size) + { + return new Result + { + Path = directive.To, + Message = $"Sizes do not match got {dest.Size()} expected {directive.Size}" + }; + } + + if (directive.Size > (1024 * 1024 * 128)) + { + _logger.LogInformation("Hashing {Size} file at {Path}", directive.Size.ToFileSizeString(), + directive.To); + } + + var hash = await AbsolutePathExtensions.Hash(dest, token); + if (hash != directive.Hash) + { + return new Result + { + Path = directive.To, + Message = $"Hashes do not match, got {hash} expected {directive.Hash}" + }; + } - if (Consts.KnownModifiedFiles.Contains(directive.To.FileName)) return null; - - if (directive is CreateBSA bsa) - { - return await VerifyBSA(dest, bsa, byTo, token); - } - - if (dest.Size() != directive.Size) - { - return new Result - { - Path = directive.To, - Message = $"Sizes do not match got {dest.Size()} expected {directive.Size}" - }; - } - - if (directive.Size > (1024 * 1024 * 128)) - { - _logger.LogInformation("Hashing {Size} file at {Path}", directive.Size.ToFileSizeString(), - directive.To); - } - - var hash = await AbsolutePathExtensions.Hash(dest, token); - if (hash != directive.Hash) - { - return new Result - { - Path = directive.To, - Message = $"Hashes do not match, got {hash} expected {directive.Hash}" - }; - } - - return null; }).Where(r => r != null) .ToList(); @@ -150,6 +147,9 @@ public class VerifyModlistInstall var astate = bsa.FileStates.First(f => f.Path == state.Path); var srcDirective = byTo[Consts.BSACreationDir.Combine(bsa.TempID, astate.Path)]; + if (!srcDirective.IsDeterministic) + continue; + if (srcDirective.Hash != hash) { return new Result diff --git a/Wabbajack.DTOs/ModList/Directive.cs b/Wabbajack.DTOs/ModList/Directive.cs index 659b8125..9b13d3ec 100644 --- a/Wabbajack.DTOs/ModList/Directive.cs +++ b/Wabbajack.DTOs/ModList/Directive.cs @@ -1,3 +1,4 @@ +using System.Text.Json.Serialization; using Wabbajack.Hashing.xxHash64; using Wabbajack.Paths; @@ -12,4 +13,6 @@ public abstract class Directive /// location the file will be copied to, relative to the install path. /// public RelativePath To { get; set; } + + [JsonIgnore] public virtual bool IsDeterministic => true; } \ No newline at end of file diff --git a/Wabbajack.DTOs/ModList/Directives/ArchiveMeta.cs b/Wabbajack.DTOs/ModList/Directives/ArchiveMeta.cs index fe982eab..6dff60a6 100644 --- a/Wabbajack.DTOs/ModList/Directives/ArchiveMeta.cs +++ b/Wabbajack.DTOs/ModList/Directives/ArchiveMeta.cs @@ -8,4 +8,6 @@ namespace Wabbajack.DTOs.Directives; public class ArchiveMeta : Directive { public RelativePath SourceDataID { get; set; } + + public override bool IsDeterministic => false; } \ No newline at end of file diff --git a/Wabbajack.DTOs/ModList/Directives/CreateBSA.cs b/Wabbajack.DTOs/ModList/Directives/CreateBSA.cs index f1bd6839..3d9dc48d 100644 --- a/Wabbajack.DTOs/ModList/Directives/CreateBSA.cs +++ b/Wabbajack.DTOs/ModList/Directives/CreateBSA.cs @@ -13,4 +13,6 @@ public class CreateBSA : Directive public RelativePath TempID { get; set; } public IArchive State { get; set; } public AFile[] FileStates { get; set; } = Array.Empty(); + + public override bool IsDeterministic => false; } \ No newline at end of file diff --git a/Wabbajack.DTOs/ModList/Directives/RemappedInlineFile.cs b/Wabbajack.DTOs/ModList/Directives/RemappedInlineFile.cs index fbdc1b89..fea843d6 100644 --- a/Wabbajack.DTOs/ModList/Directives/RemappedInlineFile.cs +++ b/Wabbajack.DTOs/ModList/Directives/RemappedInlineFile.cs @@ -9,4 +9,5 @@ namespace Wabbajack.DTOs.Directives; [JsonAlias("RemappedInlineFile, Wabbajack.Lib")] public class RemappedInlineFile : InlineFile { + public override bool IsDeterministic => false; } \ No newline at end of file diff --git a/Wabbajack.DTOs/ModList/Directives/TransformedTexture.cs b/Wabbajack.DTOs/ModList/Directives/TransformedTexture.cs index dcf6eb83..15e9412b 100644 --- a/Wabbajack.DTOs/ModList/Directives/TransformedTexture.cs +++ b/Wabbajack.DTOs/ModList/Directives/TransformedTexture.cs @@ -11,4 +11,5 @@ public class TransformedTexture : FromArchive /// The file to apply to the source file to patch it /// public ImageState ImageState { get; set; } = new(); + public override bool IsDeterministic => false; } \ No newline at end of file diff --git a/Wabbajack.Installer/StandardInstaller.cs b/Wabbajack.Installer/StandardInstaller.cs index cd68f7fa..e1a87938 100644 --- a/Wabbajack.Installer/StandardInstaller.cs +++ b/Wabbajack.Installer/StandardInstaller.cs @@ -316,7 +316,7 @@ public class StandardInstaller : AInstaller var astate = bsa.FileStates.First(f => f.Path == state.Path); var srcDirective = indexedByDestination[Consts.BSACreationDir.Combine(bsa.TempID, astate.Path)]; //DX10Files are lossy - if (astate is not BA2DX10File) + if (astate is not BA2DX10File && srcDirective.IsDeterministic) ThrowOnNonMatchingHash(bsa, srcDirective, astate, hash); return (srcDirective, hash); }).ToHashSet(); diff --git a/Wabbajack.Paths.IO/AbsolutePathExtensions.cs b/Wabbajack.Paths.IO/AbsolutePathExtensions.cs index 81bd6848..266dd5ed 100644 --- a/Wabbajack.Paths.IO/AbsolutePathExtensions.cs +++ b/Wabbajack.Paths.IO/AbsolutePathExtensions.cs @@ -55,6 +55,11 @@ public static class AbsolutePathExtensions { return new FileInfo(file.ToNativePath()).LastWriteTimeUtc; } + + public static DateTime CreatedUtc(this AbsolutePath file) + { + return new FileInfo(file.ToNativePath()).CreationTimeUtc; + } public static DateTime LastModified(this AbsolutePath file) { diff --git a/Wabbajack.VFS/FileHashCache.cs b/Wabbajack.VFS/FileHashCache.cs index 64dfd90c..f9391b31 100644 --- a/Wabbajack.VFS/FileHashCache.cs +++ b/Wabbajack.VFS/FileHashCache.cs @@ -91,6 +91,10 @@ public class FileHashCache var result = await Get(file); if (result == default || result.Hash == default) return default; + + // Fix for strange issue where dates are messed up on some systems + if (file.LastModifiedUtc() < file.CreatedUtc()) + file.Touch(); if (result.LastModified == file.LastModifiedUtc().ToFileTimeUtc()) {