diff --git a/Wabbajack.Common.Test/PathTests.cs b/Wabbajack.Common.Test/PathTests.cs new file mode 100644 index 00000000..89bac5e1 --- /dev/null +++ b/Wabbajack.Common.Test/PathTests.cs @@ -0,0 +1,28 @@ +using Xunit; + +namespace Wabbajack.Common.Test +{ + public class PathTests + { + [Fact] + public void CanDeleteReadOnlyFile() + { + var tempFile = new TempFile(); + tempFile.Path.WriteAllText("Test"); + tempFile.Path.SetReadOnly(true); + + tempFile.Path.Delete(); + } + + [Fact] + public void CanMoveReadOnlyFiles() + { + var tempFile = new TempFile(); + var tempFile2 = new TempFile(); + tempFile.Path.WriteAllText("Test"); + tempFile.Path.SetReadOnly(true); + + tempFile.Path.MoveTo(tempFile2.Path); + } + } +} diff --git a/Wabbajack.Common/Paths.cs b/Wabbajack.Common/Paths.cs index c3400ef0..2d786dbf 100644 --- a/Wabbajack.Common/Paths.cs +++ b/Wabbajack.Common/Paths.cs @@ -146,6 +146,11 @@ namespace Wabbajack.Common } } + public void SetReadOnly(bool val) + { + IsReadOnly = true; + } + /// /// Returns the full path the folder that contains Wabbajack.Common. This will almost always be /// where all the binaries for the project reside. @@ -263,10 +268,11 @@ namespace Wabbajack.Common public void Delete() { - if (IsFile) - { - File.Delete(_path); - } + if (!IsFile) return; + + if (IsReadOnly) IsReadOnly = false; + + File.Delete(_path); } public bool InFolder(AbsolutePath folder) diff --git a/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs b/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs index e8f918ed..586ecb00 100644 --- a/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs +++ b/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs @@ -13,7 +13,7 @@ namespace Wabbajack.Lib.CompilationSteps { public class DeconstructBSAs : ACompilationStep { - private readonly IEnumerable _includeDirectly; + private readonly IEnumerable _includeDirectly; private readonly Func> _microstack; private readonly Func> _microstackWithInclude; private readonly MO2Compiler _mo2Compiler; @@ -24,11 +24,11 @@ namespace Wabbajack.Lib.CompilationSteps _includeDirectly = _mo2Compiler.ModInis.Where(kv => { var general = kv.Value.General; - if (general.notes != null && general.notes.Contains(Consts.WABBAJACK_INCLUDE)) return true; - if (general.comments != null && general.comments.Contains(Consts.WABBAJACK_INCLUDE)) return true; + if (general.notes != null && (general.notes.Contains(Consts.WABBAJACK_INCLUDE) || general.notes.Contains(Consts.WABBAJACK_NOMATCH_INCLUDE))) return true; + if (general.comments != null && (general.notes.Contains(Consts.WABBAJACK_INCLUDE) || general.notes.Contains(Consts.WABBAJACK_NOMATCH_INCLUDE))) return true; return false; }) - .Select(kv => $"mods\\{kv.Key}\\") + .Select(kv => kv.Key.RelativeTo(_mo2Compiler.MO2Folder)) .ToList(); _microstack = bsa => new List @@ -77,6 +77,13 @@ namespace Wabbajack.Lib.CompilationSteps var id = Guid.NewGuid().ToString(); + + Func? _cleanup = null; + if (defaultInclude) + { + _cleanup = await source.File.Context.Stage(source.File.Children); + } + var matches = await sourceFiles.PMap(_mo2Compiler.Queue, e => _mo2Compiler.RunStack(stack, new RawSourceFile(e, Consts.BSACreationDir.Combine((RelativePath)id, (RelativePath)e.Name)))); @@ -99,6 +106,8 @@ namespace Wabbajack.Lib.CompilationSteps }; } + if (_cleanup != null) + await _cleanup(); return directive; } diff --git a/Wabbajack.Lib/CompilationSteps/IncludeAll.cs b/Wabbajack.Lib/CompilationSteps/IncludeAll.cs index db63b34b..e837f75a 100644 --- a/Wabbajack.Lib/CompilationSteps/IncludeAll.cs +++ b/Wabbajack.Lib/CompilationSteps/IncludeAll.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; using Newtonsoft.Json; +using Wabbajack.Common; namespace Wabbajack.Lib.CompilationSteps { @@ -13,7 +14,8 @@ namespace Wabbajack.Lib.CompilationSteps public override async ValueTask Run(RawSourceFile source) { var inline = source.EvolveTo(); - inline.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync()); + await using var file = source.File.StagedFile.OpenRead(); + inline.SourceDataID = await _compiler.IncludeFile(await file.ReadAllAsync()); return inline; } diff --git a/Wabbajack.Test/SanityTests.cs b/Wabbajack.Test/SanityTests.cs index d5e123e0..f1b58015 100644 --- a/Wabbajack.Test/SanityTests.cs +++ b/Wabbajack.Test/SanityTests.cs @@ -295,6 +295,53 @@ namespace Wabbajack.Test } + [Fact] + public async Task CanNoMatchIncludeFilesFromBSAs() + { + var profile = utils.AddProfile(); + var mod = utils.AddMod(); + var file = utils.AddModFile(mod, @"baz.bsa", 10); + + await file.Parent.Combine("meta.ini").WriteAllLinesAsync(new[] + { + "[General]", + "notes= asdf WABBAJACK_NOMATCH_INCLUDE asdfa" + }); + + await utils.Configure(); + + + using var tempFile = new TempFile(); + var bsaState = new BSAStateObject + { + Magic = "BSA\0", Version = 0x69, ArchiveFlags = 0x107, FileFlags = 0x0, + }; + + + var tempFileData = utils.RandomData(1024); + + await using (var bsa = bsaState.MakeBuilder(1024 * 1024)) + { + await bsa.AddFile(new BSAFileStateObject + { + Path = (RelativePath)@"matching_file.bin", Index = 0, FlipCompression = false + }, new MemoryStream(tempFileData)); + await bsa.AddFile( + new BSAFileStateObject() + { + Path = (RelativePath)@"unmatching_file.bin", Index = 1, FlipCompression = false + }, new MemoryStream(utils.RandomData(1024))); + await bsa.Build(file); + } + + var archive = utils.AddManualDownload( + new Dictionary { { "/stuff/matching_file_data.bin", tempFileData } }); + + await CompileAndInstall(profile); + utils.VerifyInstalledFile(mod, @"baz.bsa"); + + } + [Fact] public async Task CanInstallFilesFromBSAAndBSA() { diff --git a/Wabbajack/App.xaml.cs b/Wabbajack/App.xaml.cs index 033b3fa5..4c542895 100644 --- a/Wabbajack/App.xaml.cs +++ b/Wabbajack/App.xaml.cs @@ -21,7 +21,6 @@ namespace Wabbajack public App() { TempFolder.EnsureInited(); - RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; CLIOld.ParseOptions(Environment.GetCommandLineArgs()); if (CLIArguments.Help) CLIOld.DisplayHelpText();