From 454cff052eaf725be83cd9f6352421e50922c619 Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Fri, 13 Sep 2019 22:35:42 -0600 Subject: [PATCH] code reformatting --- Compression.BSA.Test/App.config | 9 +- Compression.BSA.Test/Program.cs | 53 +-- .../Properties/AssemblyInfo.cs | 3 +- Compression.BSA/BSABuilder.cs | 345 +++++---------- Compression.BSA/BSAReader.cs | 272 ++++-------- Compression.BSA/Properties/AssemblyInfo.cs | 3 +- Compression.BSA/Utils.cs | 50 +-- Compression.BSA/packages.config | 1 + VirtualFileSystem.Test/App.config | 9 +- VirtualFileSystem.Test/Program.cs | 10 +- .../Properties/AssemblyInfo.cs | 3 +- VirtualFileSystem/Properties/AssemblyInfo.cs | 3 +- VirtualFileSystem/VirtualFileSystem.cs | 406 ++++++++---------- VirtualFileSystem/packages.config | 1 + Wabbajack.Common/BSDiff.cs | 290 ++++++------- Wabbajack.Common/ChildProcessTracker.cs | 98 +++-- Wabbajack.Common/Consts.cs | 34 +- Wabbajack.Common/DynamicIniData.cs | 25 +- Wabbajack.Common/FileExtractor.cs | 45 +- Wabbajack.Common/Properties/AssemblyInfo.cs | 3 +- Wabbajack.Common/SplittingStream.cs | 32 +- Wabbajack.Common/Utils.cs | 134 +++--- Wabbajack.Common/WorkQueue.cs | 50 ++- Wabbajack.Common/packages.config | 1 + Wabbajack/App.config | 7 +- Wabbajack/App.xaml | 2 +- Wabbajack/App.xaml.cs | 13 +- Wabbajack/AppState.cs | 378 ++++++++-------- Wabbajack/AutoScrollBehavior.cs | 70 ++- Wabbajack/Compiler.cs | 19 +- Wabbajack/Data.cs | 144 +++---- Wabbajack/Installer.cs | 268 ++++++------ Wabbajack/LambdaCommand.cs | 8 +- Wabbajack/MainWindow.xaml | 94 ++-- Wabbajack/MainWindow.xaml.cs | 43 +- Wabbajack/NexusAPI.cs | 152 +++---- Wabbajack/Properties/AssemblyInfo.cs | 12 +- Wabbajack/Properties/Settings.settings | 1 + Wabbajack/ReportBuilder.cs | 62 ++- .../Themes/LeftMarginMultiplierConverter.cs | 8 +- Wabbajack/Themes/TreeViewItemExtensions.cs | 22 +- Wabbajack/packages.config | 1 + 42 files changed, 1385 insertions(+), 1799 deletions(-) diff --git a/Compression.BSA.Test/App.config b/Compression.BSA.Test/App.config index 56efbc7b..d1d97acc 100644 --- a/Compression.BSA.Test/App.config +++ b/Compression.BSA.Test/App.config @@ -1,6 +1,7 @@ - + + - - - + + + \ No newline at end of file diff --git a/Compression.BSA.Test/Program.cs b/Compression.BSA.Test/Program.cs index f2a0ff47..24cfd2ff 100644 --- a/Compression.BSA.Test/Program.cs +++ b/Compression.BSA.Test/Program.cs @@ -1,33 +1,28 @@ -using ICSharpCode.SharpZipLib; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace Compression.BSA.Test { - class Program + internal class Program { - const string TestDir = @"D:\MO2 Instances\"; - const string TempDir = "c:\\tmp\\out"; - static void Main(string[] args) + private const string TestDir = @"D:\MO2 Instances\"; + private const string TempDir = "c:\\tmp\\out"; + + private static void Main(string[] args) { foreach (var bsa in Directory.EnumerateFiles(TestDir, "*.bsa", SearchOption.AllDirectories).Skip(0)) { Console.WriteLine($"From {bsa}"); Console.WriteLine("Cleaning Output Dir"); - if (Directory.Exists(TempDir)) - { - Directory.Delete(TempDir, true); - } + if (Directory.Exists(TempDir)) Directory.Delete(TempDir, true); Directory.CreateDirectory(TempDir); Console.WriteLine($"Reading {bsa}"); using (var a = new BSAReader(bsa)) { - Parallel.ForEach(a.Files, file => { var abs_name = Path.Combine(TempDir, file.Path); @@ -36,8 +31,11 @@ namespace Compression.BSA.Test Directory.CreateDirectory(Path.GetDirectoryName(abs_name)); using (var fs = File.OpenWrite(abs_name)) + { file.CopyDataTo(fs); - Equal((long)file.Size, new FileInfo(abs_name).Length); + } + + Equal(file.Size, new FileInfo(abs_name).Length); }); @@ -56,9 +54,8 @@ namespace Compression.BSA.Test { var entry = w.AddFile(file.Path, str, file.FlipCompression); } - }); - + w.Build("c:\\tmp\\tmp.bsa"); // Sanity Checks @@ -70,33 +67,31 @@ namespace Compression.BSA.Test Console.WriteLine($"{pair.ai.Path}, {pair.ai.Hash}, {pair.bi.Path}, {pair.bi.Hash}"); }*/ - foreach (var pair in Enumerable.Zip(a.Files, w.Files, (ai, bi) => (ai, bi))) + foreach (var pair in a.Files.Zip(w.Files, (ai, bi) => (ai, bi))) { Equal(pair.ai.Path, pair.bi.Path); Equal(pair.ai.Hash, pair.bi.Hash); } - } Console.WriteLine($"Verifying {bsa}"); using (var b = new BSAReader("c:\\tmp\\tmp.bsa")) { Console.WriteLine($"Performing A/B tests on {bsa}"); - Equal((uint)a.ArchiveFlags, (uint)b.ArchiveFlags); - Equal((uint)a.FileFlags, (uint)b.FileFlags); + Equal((uint) a.ArchiveFlags, (uint) b.ArchiveFlags); + Equal((uint) a.FileFlags, (uint) b.FileFlags); // Check same number of files Equal(a.Files.Count(), b.Files.Count()); - int idx = 0; - foreach (var pair in Enumerable.Zip(a.Files, b.Files, (ai, bi) => (ai, bi))) + var idx = 0; + foreach (var pair in a.Files.Zip(b.Files, (ai, bi) => (ai, bi))) { - idx ++; + idx++; //Console.WriteLine($" - {pair.ai.Path}"); Equal(pair.ai.Path, pair.bi.Path); Equal(pair.ai.Compressed, pair.bi.Compressed); Equal(pair.ai.Size, pair.bi.Size); Equal(pair.ai.GetData(), pair.bi.GetData()); - } } } @@ -109,7 +104,6 @@ namespace Compression.BSA.Test foreach (var itm in a) Equal(b.Contains(itm)); - } private static void Equal(bool v) @@ -161,16 +155,11 @@ namespace Compression.BSA.Test public static void Equal(byte[] a, byte[] b) { - if (a.Length != b.Length) - { - throw new InvalidDataException($"Byte array sizes are not equal"); - } + if (a.Length != b.Length) throw new InvalidDataException("Byte array sizes are not equal"); - for (var idx = 0; idx < a.Length; idx ++) - { + for (var idx = 0; idx < a.Length; idx++) if (a[idx] != b[idx]) throw new InvalidDataException($"Byte array contents not equal at {idx}"); - } } } -} +} \ No newline at end of file diff --git a/Compression.BSA.Test/Properties/AssemblyInfo.cs b/Compression.BSA.Test/Properties/AssemblyInfo.cs index e2c26383..e39e4f04 100644 --- a/Compression.BSA.Test/Properties/AssemblyInfo.cs +++ b/Compression.BSA.Test/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Compression.BSA/BSABuilder.cs b/Compression.BSA/BSABuilder.cs index 173c04d7..6d70f6f9 100644 --- a/Compression.BSA/BSABuilder.cs +++ b/Compression.BSA/BSABuilder.cs @@ -1,11 +1,11 @@ -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using K4os.Compression.LZ4; -using K4os.Compression.LZ4.Streams; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using K4os.Compression.LZ4; +using K4os.Compression.LZ4.Streams; using File = Alphaleonis.Win32.Filesystem.File; using Path = Alphaleonis.Win32.Filesystem.Path; @@ -13,18 +13,18 @@ namespace Compression.BSA { public class BSABuilder : IDisposable { - internal byte[] _fileId; - internal uint _version; - internal uint _offset; internal uint _archiveFlags; - internal uint _folderCount; internal uint _fileCount; - internal uint _totalFolderNameLength; - internal uint _totalFileNameLength; internal uint _fileFlags; + internal byte[] _fileId; private List _files = new List(); + internal uint _folderCount; internal List _folders = new List(); + internal uint _offset; + internal uint _totalFileNameLength; + internal uint _totalFolderNameLength; + internal uint _version; public BSABuilder() { @@ -32,59 +32,24 @@ namespace Compression.BSA _offset = 0x24; } - public IEnumerable Files - { - get - { - return _files; - } - } + public IEnumerable Files => _files; public ArchiveFlags ArchiveFlags { - get - { - return (ArchiveFlags)_archiveFlags; - } - set - { - _archiveFlags = (uint)value; - } + get => (ArchiveFlags) _archiveFlags; + set => _archiveFlags = (uint) value; } public FileFlags FileFlags { - get - { - return (FileFlags)_archiveFlags; - } - set - { - _archiveFlags = (uint)value; - } + get => (FileFlags) _archiveFlags; + set => _archiveFlags = (uint) value; } public VersionType HeaderType { - get - { - return (VersionType)_version; - } - set - { - _version = (uint)value; - } - } - - public FileEntry AddFile(string path, Stream src, bool flipCompression = false) - { - FileEntry r = new FileEntry(this, path, src, flipCompression); - - lock (this) - { - _files.Add(r); - } - return r; + get => (VersionType) _version; + set => _version = (uint) value; } public IEnumerable FolderNames @@ -92,40 +57,32 @@ namespace Compression.BSA get { return _files.Select(f => Path.GetDirectoryName(f.Path)) - .ToHashSet(); + .ToHashSet(); } } - public bool HasFolderNames + public bool HasFolderNames => (_archiveFlags & 0x1) > 0; + + public bool HasFileNames => (_archiveFlags & 0x2) > 0; + + public bool CompressedByDefault => (_archiveFlags & 0x4) > 0; + + public bool HasNameBlobs => (_archiveFlags & 0x100) > 0; + + public void Dispose() { - get - { - return (_archiveFlags & 0x1) > 0; - } } - public bool HasFileNames + public FileEntry AddFile(string path, Stream src, bool flipCompression = false) { - get - { - return (_archiveFlags & 0x2) > 0; - } - } + var r = new FileEntry(this, path, src, flipCompression); - public bool CompressedByDefault - { - get + lock (this) { - return (_archiveFlags & 0x4) > 0; + _files.Add(r); } - } - public bool HasNameBlobs - { - get - { - return (_archiveFlags & 0x100) > 0; - } + return r; } public void Build(string outputName) @@ -141,107 +98,82 @@ namespace Compression.BSA wtr.Write(_offset); wtr.Write(_archiveFlags); var folders = FolderNames.ToList(); - wtr.Write((uint)folders.Count); - wtr.Write((uint)_files.Count); - wtr.Write((uint)_folders.Select(f => f._nameBytes.Count() - 1).Sum()); // totalFolderNameLength + wtr.Write((uint) folders.Count); + wtr.Write((uint) _files.Count); + wtr.Write((uint) _folders.Select(f => f._nameBytes.Count() - 1).Sum()); // totalFolderNameLength var s = _files.Select(f => f._pathBytes.Count()).Sum(); - _totalFileNameLength = (uint)_files.Select(f => f._nameBytes.Count()).Sum(); + _totalFileNameLength = (uint) _files.Select(f => f._nameBytes.Count()).Sum(); wtr.Write(_totalFileNameLength); // totalFileNameLength wtr.Write(_fileFlags); - foreach (var folder in _folders) - { - folder.WriteFolderRecord(wtr); - } + foreach (var folder in _folders) folder.WriteFolderRecord(wtr); - foreach(var folder in _folders) + foreach (var folder in _folders) { if (HasFolderNames) wtr.Write(folder._nameBytes); - foreach (var file in folder._files) - { - file.WriteFileRecord(wtr); - } + foreach (var file in folder._files) file.WriteFileRecord(wtr); } - foreach(var file in _files) - { - wtr.Write(file._nameBytes); - } + foreach (var file in _files) wtr.Write(file._nameBytes); - foreach(var file in _files) - { - file.WriteData(wtr); - } - - + foreach (var file in _files) file.WriteData(wtr); } } public void RegenFolderRecords() { _folders = _files.GroupBy(f => Path.GetDirectoryName(f.Path.ToLowerInvariant())) - .Select(f => new FolderRecordBuilder(this, f.Key, f.ToList())) - .OrderBy(f => f._hash) - .ToList(); + .Select(f => new FolderRecordBuilder(this, f.Key, f.ToList())) + .OrderBy(f => f._hash) + .ToList(); var lnk = _files.Where(f => f.Path.EndsWith(".lnk")).FirstOrDefault(); foreach (var folder in _folders) - foreach (var file in folder._files) - file._folder = folder; + foreach (var file in folder._files) + file._folder = folder; _files = (from folder in _folders - from file in folder._files - orderby folder._hash, file._hash - select file).ToList(); - } - - public void Dispose() - { - + from file in folder._files + orderby folder._hash, file._hash + select file).ToList(); } } public class FolderRecordBuilder { - internal IEnumerable _files; - private string _name; internal BSABuilder _bsa; - internal ulong _hash; internal uint _fileCount; + internal IEnumerable _files; + internal ulong _hash; internal byte[] _nameBytes; - internal uint _recordSize; internal ulong _offset; + internal uint _recordSize; - public ulong Hash + public FolderRecordBuilder(BSABuilder bsa, string folderName, IEnumerable files) { - get - { - return _hash; - } + _files = files.OrderBy(f => f._hash); + Name = folderName.ToLowerInvariant(); + _bsa = bsa; + // Folders don't have extensions, so let's make sure we cut it out + _hash = Name.GetBSAHash(""); + _fileCount = (uint) files.Count(); + _nameBytes = folderName.ToBZString(_bsa.HeaderType); + _recordSize = sizeof(ulong) + sizeof(uint) + sizeof(uint); } - public string Name - { - get - { - return _name; - } - } + public ulong Hash => _hash; + + public string Name { get; } public ulong SelfSize { get { if (_bsa.HeaderType == VersionType.SSE) - { return sizeof(ulong) + sizeof(uint) + sizeof(uint) + sizeof(ulong); - } - else - { - return sizeof(ulong) + sizeof(uint) + sizeof(uint); - } + return sizeof(ulong) + sizeof(uint) + sizeof(uint); } } @@ -251,69 +183,56 @@ namespace Compression.BSA { ulong size = 0; if (_bsa.HasFolderNames) - size += (ulong)_nameBytes.Length; - size += (ulong)_files.Select(f => sizeof(ulong) + sizeof(uint) + sizeof(uint)).Sum(); + size += (ulong) _nameBytes.Length; + size += (ulong) _files.Select(f => sizeof(ulong) + sizeof(uint) + sizeof(uint)).Sum(); return size; } } - public FolderRecordBuilder(BSABuilder bsa, string folderName, IEnumerable files) - { - _files = files.OrderBy(f => f._hash); - _name = folderName.ToLowerInvariant(); - _bsa = bsa; - // Folders don't have extensions, so let's make sure we cut it out - _hash = _name.GetBSAHash(""); - _fileCount = (uint)files.Count(); - _nameBytes = folderName.ToBZString(_bsa.HeaderType); - _recordSize = sizeof(ulong) + sizeof(uint) + sizeof(uint); - } - public void WriteFolderRecord(BinaryWriter wtr) { var idx = _bsa._folders.IndexOf(this); - _offset = (ulong)wtr.BaseStream.Position; - _offset += (ulong)_bsa._folders.Skip((int)idx).Select(f => (long)f.SelfSize).Sum(); + _offset = (ulong) wtr.BaseStream.Position; + _offset += (ulong) _bsa._folders.Skip(idx).Select(f => (long) f.SelfSize).Sum(); _offset += _bsa._totalFileNameLength; - _offset += (ulong)_bsa._folders.Take((int)idx).Select(f => (long)f.FileRecordSize).Sum(); + _offset += (ulong) _bsa._folders.Take(idx).Select(f => (long) f.FileRecordSize).Sum(); - var sp = wtr.BaseStream.Position; + var sp = wtr.BaseStream.Position; wtr.Write(_hash); wtr.Write(_fileCount); if (_bsa.HeaderType == VersionType.SSE) { - wtr.Write((uint)0); // unk - wtr.Write((ulong)_offset); // offset + wtr.Write((uint) 0); // unk + wtr.Write(_offset); // offset } else if (_bsa.HeaderType == VersionType.FO3 || _bsa.HeaderType == VersionType.TES4) { - wtr.Write((uint)_offset); + wtr.Write((uint) _offset); } else { throw new NotImplementedException($"Cannot write to BSAs of type {_bsa.HeaderType}"); } } - } public class FileEntry { - internal FolderRecordBuilder _folder; internal BSABuilder _bsa; - internal string _path; - internal string _name; - internal string _filenameSource; internal Stream _bytesSource; + internal string _filenameSource; internal bool _flipCompression; + internal FolderRecordBuilder _folder; internal ulong _hash; + internal string _name; internal byte[] _nameBytes; - internal byte[] _pathBytes; - private byte[] _pathBSBytes; - internal byte[] _rawData; - internal int _originalSize; private long _offsetOffset; + internal int _originalSize; + internal string _path; + private readonly byte[] _pathBSBytes; + internal byte[] _pathBytes; + internal byte[] _rawData; public FileEntry(BSABuilder bsa, string path, Stream src, bool flipCompression) { @@ -333,16 +252,35 @@ namespace Compression.BSA if (Compressed) CompressData(); - } + public bool Compressed + { + get + { + if (_flipCompression) + return !_bsa.CompressedByDefault; + return _bsa.CompressedByDefault; + } + } + + public string Path => _path; + + public bool FlipCompression => _flipCompression; + + public ulong Hash => _hash; + + public FolderRecordBuilder Folder => _folder; + private void CompressData() { if (_bsa.HeaderType == VersionType.SSE) { var r = new MemoryStream(); - using (var w = LZ4Stream.Encode(r, new LZ4EncoderSettings() { CompressionLevel = LZ4Level.L10_OPT})) - (new MemoryStream(_rawData)).CopyTo(w); + using (var w = LZ4Stream.Encode(r, new LZ4EncoderSettings {CompressionLevel = LZ4Level.L10_OPT})) + { + new MemoryStream(_rawData).CopyTo(w); + } _rawData = r.ToArray(); } @@ -350,7 +288,9 @@ namespace Compression.BSA { var r = new MemoryStream(); using (var w = new DeflaterOutputStream(r)) - (new MemoryStream(_rawData)).CopyTo(w); + { + new MemoryStream(_rawData).CopyTo(w); + } _rawData = r.ToArray(); } @@ -360,85 +300,34 @@ namespace Compression.BSA } } - public bool Compressed - { - get - { - if (_flipCompression) - return !_bsa.CompressedByDefault; - else - return _bsa.CompressedByDefault; - } - } - - public string Path - { - get - { - return _path; - } - } - - public bool FlipCompression - { - get - { - return _flipCompression; - } - } - - public ulong Hash { get - { - return _hash; - } - } - - public FolderRecordBuilder Folder - { - get - { - return _folder; - } - } - internal void WriteFileRecord(BinaryWriter wtr) { wtr.Write(_hash); var size = _rawData.Length; - if (_bsa.HasNameBlobs) - { - size += _pathBSBytes.Length; - } - if (Compressed) - { - size += 4; - } + if (_bsa.HasNameBlobs) size += _pathBSBytes.Length; + if (Compressed) size += 4; if (_flipCompression) - wtr.Write((uint)size | (0x1 << 30)); + wtr.Write((uint) size | (0x1 << 30)); else - wtr.Write((uint)size); + wtr.Write((uint) size); _offsetOffset = wtr.BaseStream.Position; - wtr.Write((uint)0xDEADBEEF); + wtr.Write(0xDEADBEEF); } internal void WriteData(BinaryWriter wtr) { - uint offset = (uint)wtr.BaseStream.Position; + var offset = (uint) wtr.BaseStream.Position; wtr.BaseStream.Position = _offsetOffset; - wtr.Write((uint)offset); + wtr.Write(offset); wtr.BaseStream.Position = offset; - if (_bsa.HasNameBlobs) - { - wtr.Write(_pathBSBytes); - } + if (_bsa.HasNameBlobs) wtr.Write(_pathBSBytes); if (Compressed) { - - wtr.Write((uint)_originalSize); + wtr.Write((uint) _originalSize); wtr.Write(_rawData); } else @@ -447,4 +336,4 @@ namespace Compression.BSA } } } -} +} \ No newline at end of file diff --git a/Compression.BSA/BSAReader.cs b/Compression.BSA/BSAReader.cs index 8c1a654c..5e51cc58 100644 --- a/Compression.BSA/BSAReader.cs +++ b/Compression.BSA/BSAReader.cs @@ -1,5 +1,4 @@ - -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -15,7 +14,7 @@ namespace Compression.BSA FO3 = 0x68, // FO3, FNV, TES5 SSE = 0x69, FO4 = 0x01 - }; + } [Flags] public enum ArchiveFlags : uint @@ -49,104 +48,23 @@ namespace Compression.BSA public class BSAReader : IDisposable { - private Stream _stream; - private BinaryReader _rdr; - private string _magic; - private uint _version; - private uint _folderRecordOffset; private uint _archiveFlags; - private uint _folderCount; private uint _fileCount; - private uint _totalFolderNameLength; - private uint _totalFileNameLength; private uint _fileFlags; - private List _folders; internal string _fileName; - - public IEnumerable Files - { - get - { - foreach (var folder in _folders) - foreach (var file in folder._files) - yield return file; - } - } - - public VersionType HeaderType - { - get - { - return (VersionType)_version; - } - } - - public ArchiveFlags ArchiveFlags - { - get - { - return (ArchiveFlags)_archiveFlags; - } - } - - public FileFlags FileFlags - { - get - { - return (FileFlags)_archiveFlags; - } - } - - - public bool HasFolderNames - { - get - { - return (_archiveFlags & 0x1) > 0; - } - } - - public bool HasFileNames - { - get - { - return (_archiveFlags & 0x2) > 0; - } - } - - public bool CompressedByDefault - { - get - { - return (_archiveFlags & 0x4) > 0; - } - } - - public bool Bit9Set - { - get - { - return (_archiveFlags & 0x100) > 0; - } - } - - public bool HasNameBlobs - { - get - { - if (HeaderType == VersionType.FO3 || HeaderType == VersionType.SSE) - { - return (_archiveFlags & 0x100) > 0; - } - return false; - } - } + private uint _folderCount; + private uint _folderRecordOffset; + private List _folders; + private string _magic; + private readonly BinaryReader _rdr; + private readonly Stream _stream; + private uint _totalFileNameLength; + private uint _totalFolderNameLength; + private uint _version; public BSAReader(string filename) : this(File.OpenRead(filename)) { _fileName = filename; - - } public BSAReader(Stream stream) @@ -156,6 +74,40 @@ namespace Compression.BSA LoadHeaders(); } + public IEnumerable Files + { + get + { + foreach (var folder in _folders) + foreach (var file in folder._files) + yield return file; + } + } + + public VersionType HeaderType => (VersionType) _version; + + public ArchiveFlags ArchiveFlags => (ArchiveFlags) _archiveFlags; + + public FileFlags FileFlags => (FileFlags) _archiveFlags; + + + public bool HasFolderNames => (_archiveFlags & 0x1) > 0; + + public bool HasFileNames => (_archiveFlags & 0x2) > 0; + + public bool CompressedByDefault => (_archiveFlags & 0x4) > 0; + + public bool Bit9Set => (_archiveFlags & 0x100) > 0; + + public bool HasNameBlobs + { + get + { + if (HeaderType == VersionType.FO3 || HeaderType == VersionType.SSE) return (_archiveFlags & 0x100) > 0; + return false; + } + } + public void Dispose() { _stream.Close(); @@ -184,30 +136,28 @@ namespace Compression.BSA private void LoadFolderRecords() { _folders = new List(); - for (int idx = 0; idx < _folderCount; idx += 1) + for (var idx = 0; idx < _folderCount; idx += 1) _folders.Add(new FolderRecord(this, _rdr)); foreach (var folder in _folders) folder.LoadFileRecordBlock(this, _rdr); foreach (var folder in _folders) - foreach (var file in folder._files) - file.LoadFileRecord(this, folder, file, _rdr); - + foreach (var file in folder._files) + file.LoadFileRecord(this, folder, file, _rdr); } } public class FolderRecord { - private ulong _nameHash; - private uint _fileCount; - private uint _unk; - private ulong _offset; + private readonly uint _fileCount; internal List _files; + private ulong _offset; + private uint _unk; internal FolderRecord(BSAReader bsa, BinaryReader src) { - _nameHash = src.ReadUInt64(); + Hash = src.ReadUInt64(); _fileCount = src.ReadUInt32(); if (bsa.HeaderType == VersionType.SSE) { @@ -221,53 +171,38 @@ namespace Compression.BSA } public string Name { get; private set; } - public ulong Hash - { - get - { - return _nameHash; - } - } + + public ulong Hash { get; } internal void LoadFileRecordBlock(BSAReader bsa, BinaryReader src) { - if (bsa.HasFolderNames) - { - Name = src.ReadStringLen(bsa.HeaderType); - } + if (bsa.HasFolderNames) Name = src.ReadStringLen(bsa.HeaderType); _files = new List(); - for (int idx = 0; idx < _fileCount; idx += 1) - { - _files.Add(new FileRecord(bsa, this, src)); - } + for (var idx = 0; idx < _fileCount; idx += 1) _files.Add(new FileRecord(bsa, this, src)); } - } public class FileRecord { - private BSAReader _bsa; - private ulong _hash; - private bool _compressedFlag; - private uint _size; - private uint _offset; - private FolderRecord _folder; + private readonly BSAReader _bsa; + private readonly long _dataOffset; + private readonly uint _dataSize; private string _name; - private uint _originalSize; - private uint _dataSize; - private uint _onDiskSize; - private string _nameBlob; - private long _dataOffset; + private readonly string _nameBlob; + private readonly uint _offset; + private readonly uint _onDiskSize; + private readonly uint _originalSize; + private readonly uint _size; public FileRecord(BSAReader bsa, FolderRecord folderRecord, BinaryReader src) { _bsa = bsa; - _hash = src.ReadUInt64(); + Hash = src.ReadUInt64(); var size = src.ReadUInt32(); - _compressedFlag = (size & (0x1 << 30)) > 0; + FlipCompression = (size & (0x1 << 30)) > 0; - if (_compressedFlag) + if (FlipCompression) _size = size ^ (0x1 << 30); else _size = size; @@ -276,7 +211,7 @@ namespace Compression.BSA _size -= 4; _offset = src.ReadUInt32(); - _folder = folderRecord; + Folder = folderRecord; var old_pos = src.BaseStream.Position; @@ -285,11 +220,11 @@ namespace Compression.BSA if (bsa.HasNameBlobs) _nameBlob = src.ReadStringLenNoTerm(bsa.HeaderType); - + if (Compressed) _originalSize = src.ReadUInt32(); - _onDiskSize = (uint)(_size - (_nameBlob == null ? 0 : _nameBlob.Length + 1)); + _onDiskSize = (uint) (_size - (_nameBlob == null ? 0 : _nameBlob.Length + 1)); if (Compressed) { @@ -297,24 +232,21 @@ namespace Compression.BSA _onDiskSize -= 4; } else + { _dataSize = _onDiskSize; + } _dataOffset = src.BaseStream.Position; src.BaseStream.Position = old_pos; } - internal void LoadFileRecord(BSAReader bsaReader, FolderRecord folder, FileRecord file, BinaryReader rdr) - { - _name = rdr.ReadStringTerm(_bsa.HeaderType); - } - public string Path { get { - if (string.IsNullOrEmpty(_folder.Name)) return _name; - return _folder.Name + "\\" + _name; + if (string.IsNullOrEmpty(Folder.Name)) return _name; + return Folder.Name + "\\" + _name; } } @@ -322,36 +254,24 @@ namespace Compression.BSA { get { - if (_compressedFlag) return !_bsa.CompressedByDefault; + if (FlipCompression) return !_bsa.CompressedByDefault; return _bsa.CompressedByDefault; } } - public int Size + public int Size => (int) _dataSize; + + public ulong Hash { get; } + + public FolderRecord Folder { get; } + + public bool FlipCompression { get; } + + internal void LoadFileRecord(BSAReader bsaReader, FolderRecord folder, FileRecord file, BinaryReader rdr) { - get - { - return (int)_dataSize; - } + _name = rdr.ReadStringTerm(_bsa.HeaderType); } - public ulong Hash { - get - { - return _hash; - } - } - - public FolderRecord Folder - { - get - { - return _folder; - } - } - - public bool FlipCompression { get => _compressedFlag; } - public void CopyDataTo(Stream output) { using (var in_file = File.OpenRead(_bsa._fileName)) @@ -361,31 +281,25 @@ namespace Compression.BSA if (_bsa.HeaderType == VersionType.SSE) { - if (Compressed) { var r = LZ4Stream.Decode(rdr.BaseStream); - r.CopyToLimit(output, (int)_originalSize); - + r.CopyToLimit(output, (int) _originalSize); } else { - rdr.BaseStream.CopyToLimit(output, (int)_onDiskSize); + rdr.BaseStream.CopyToLimit(output, (int) _onDiskSize); } } else { - if (Compressed) - { using (var z = new InflaterInputStream(rdr.BaseStream)) - z.CopyToLimit(output, (int)_originalSize); - - } + { + z.CopyToLimit(output, (int) _originalSize); + } else - { - rdr.BaseStream.CopyToLimit(output, (int)_onDiskSize); - } + rdr.BaseStream.CopyToLimit(output, (int) _onDiskSize); } } } @@ -396,7 +310,5 @@ namespace Compression.BSA CopyDataTo(ms); return ms.ToArray(); } - - } -} +} \ No newline at end of file diff --git a/Compression.BSA/Properties/AssemblyInfo.cs b/Compression.BSA/Properties/AssemblyInfo.cs index 5d2029be..96c50d74 100644 --- a/Compression.BSA/Properties/AssemblyInfo.cs +++ b/Compression.BSA/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Compression.BSA/Utils.cs b/Compression.BSA/Utils.cs index ba480181..2017b21c 100644 --- a/Compression.BSA/Utils.cs +++ b/Compression.BSA/Utils.cs @@ -8,7 +8,8 @@ namespace Compression.BSA { internal static class Utils { - private static Encoding Windows1252 = Encoding.GetEncoding(1252); + private static readonly Encoding Windows1252 = Encoding.GetEncoding(1252); + private static Encoding GetEncoding(VersionType version) { if (version == VersionType.SSE) @@ -20,10 +21,8 @@ namespace Compression.BSA { var len = rdr.ReadByte(); if (len == 0) - { //rdr.ReadByte(); return ""; - } var bytes = rdr.ReadBytes(len - 1); rdr.ReadByte(); @@ -39,7 +38,7 @@ namespace Compression.BSA public static string ReadStringTerm(this BinaryReader rdr, VersionType version) { - List acc = new List(); + var acc = new List(); while (true) { var c = rdr.ReadByte(); @@ -48,12 +47,13 @@ namespace Compression.BSA acc.Add(c); } + return GetEncoding(version).GetString(acc.ToArray()); } /// - /// Returns bytes for a \0 terminated string + /// Returns bytes for a \0 terminated string /// /// /// @@ -62,12 +62,12 @@ namespace Compression.BSA var b = GetEncoding(version).GetBytes(val); var b2 = new byte[b.Length + 2]; b.CopyTo(b2, 1); - b2[0] = (byte)(b.Length + 1); + b2[0] = (byte) (b.Length + 1); return b2; } /// - /// Returns bytes for unterminated string with a count at the start + /// Returns bytes for unterminated string with a count at the start /// /// /// @@ -76,13 +76,13 @@ namespace Compression.BSA var b = Encoding.ASCII.GetBytes(val); var b2 = new byte[b.Length + 1]; b.CopyTo(b2, 1); - b2[0] = (byte)b.Length; + b2[0] = (byte) b.Length; return b2; } /// - /// Returns bytes for a \0 terminated string prefixed by a length + /// Returns bytes for a \0 terminated string prefixed by a length /// /// /// @@ -91,7 +91,7 @@ namespace Compression.BSA var b = GetEncoding(version).GetBytes(val); var b2 = new byte[b.Length + 1]; b.CopyTo(b2, 0); - b[0] = (byte)b.Length; + b[0] = (byte) b.Length; return b2; } @@ -111,10 +111,10 @@ namespace Compression.BSA var hashBytes = new[] { - (byte)(name.Length == 0 ? '\0' : name[name.Length - 1]), - (byte)(name.Length < 3 ? '\0' : name[name.Length - 2]), - (byte)name.Length, - (byte)name[0] + (byte) (name.Length == 0 ? '\0' : name[name.Length - 1]), + (byte) (name.Length < 3 ? '\0' : name[name.Length - 2]), + (byte) name.Length, + (byte) name[0] }; var hash1 = BitConverter.ToUInt32(hashBytes, 0); switch (ext) @@ -134,32 +134,26 @@ namespace Compression.BSA } uint hash2 = 0; - for (var i = 1; i < name.Length - 2; i++) - { - hash2 = hash2 * 0x1003f + (byte)name[i]; - } + for (var i = 1; i < name.Length - 2; i++) hash2 = hash2 * 0x1003f + (byte) name[i]; uint hash3 = 0; - for (var i = 0; i < ext.Length; i++) - { - hash3 = hash3 * 0x1003f + (byte)ext[i]; - } + for (var i = 0; i < ext.Length; i++) hash3 = hash3 * 0x1003f + (byte) ext[i]; - return (((ulong)(hash2 + hash3)) << 32) + hash1; + return ((ulong) (hash2 + hash3) << 32) + hash1; } public static void CopyToLimit(this Stream frm, Stream tw, int limit) { - byte[] buff = new byte[1024]; + var buff = new byte[1024]; while (limit > 0) { - int to_read = Math.Min(buff.Length, limit); - int read = frm.Read(buff, 0, to_read); + var to_read = Math.Min(buff.Length, limit); + var read = frm.Read(buff, 0, to_read); tw.Write(buff, 0, read); limit -= read; } + tw.Flush(); } } -} - +} \ No newline at end of file diff --git a/Compression.BSA/packages.config b/Compression.BSA/packages.config index 4d29cdb4..a47026cb 100644 --- a/Compression.BSA/packages.config +++ b/Compression.BSA/packages.config @@ -1,4 +1,5 @@  + diff --git a/VirtualFileSystem.Test/App.config b/VirtualFileSystem.Test/App.config index 56efbc7b..d1d97acc 100644 --- a/VirtualFileSystem.Test/App.config +++ b/VirtualFileSystem.Test/App.config @@ -1,6 +1,7 @@ - + + - - - + + + \ No newline at end of file diff --git a/VirtualFileSystem.Test/Program.cs b/VirtualFileSystem.Test/Program.cs index 6e624cd0..91978d3d 100644 --- a/VirtualFileSystem.Test/Program.cs +++ b/VirtualFileSystem.Test/Program.cs @@ -3,15 +3,15 @@ using Wabbajack.Common; namespace VirtualFileSystem.Test { - class Program + internal class Program { - static void Main(string[] args) + private static void Main(string[] args) { Utils.SetLoggerFn(s => Console.WriteLine(s)); Utils.SetStatusFn((s, i) => Console.WriteLine(s)); - WorkQueue.Init((a, b, c) => { return; }, - (a, b) => { return; }); + WorkQueue.Init((a, b, c) => { }, + (a, b) => { }); VFS.VirtualFileSystem.VFS.AddRoot(@"D:\tmp\Interesting NPCs SSE 3.42\Data"); } } -} +} \ No newline at end of file diff --git a/VirtualFileSystem.Test/Properties/AssemblyInfo.cs b/VirtualFileSystem.Test/Properties/AssemblyInfo.cs index 7746488b..8704d406 100644 --- a/VirtualFileSystem.Test/Properties/AssemblyInfo.cs +++ b/VirtualFileSystem.Test/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/VirtualFileSystem/Properties/AssemblyInfo.cs b/VirtualFileSystem/Properties/AssemblyInfo.cs index 3693b24e..8f051e94 100644 --- a/VirtualFileSystem/Properties/AssemblyInfo.cs +++ b/VirtualFileSystem/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/VirtualFileSystem/VirtualFileSystem.cs b/VirtualFileSystem/VirtualFileSystem.cs index 8540be1e..d07a917f 100644 --- a/VirtualFileSystem/VirtualFileSystem.cs +++ b/VirtualFileSystem/VirtualFileSystem.cs @@ -1,17 +1,13 @@ -using Compression.BSA; -using ICSharpCode.SharpZipLib.Zip; -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; -using System.Security.AccessControl; -using Alphaleonis.Win32.Filesystem; +using Compression.BSA; +using ICSharpCode.SharpZipLib.Zip; +using Newtonsoft.Json; using Wabbajack.Common; using Directory = Alphaleonis.Win32.Filesystem.Directory; -using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo; using File = Alphaleonis.Win32.Filesystem.File; using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo; using Path = Alphaleonis.Win32.Filesystem.Path; @@ -20,16 +16,12 @@ namespace VFS { public class VirtualFileSystem { - internal static string _stagedRoot; public static VirtualFileSystem VFS; - private Dictionary _files = new Dictionary(); private bool _disableDiskCache; + private Dictionary _files = new Dictionary(); private volatile bool _isSyncing; - public static string RootFolder { get; } - public Dictionary> HashIndex { get; private set; } - static VirtualFileSystem() { VFS = new VirtualFileSystem(); @@ -45,40 +37,38 @@ namespace VFS Directory.CreateDirectory(_stagedRoot); } + public VirtualFileSystem() + { + LoadFromDisk(); + } + + public static string RootFolder { get; } + public Dictionary> HashIndex { get; private set; } + + public VirtualFile this[string path] => Lookup(path); + private static void DeleteDirectory(string path, bool recursive = true) { if (recursive) { var subfolders = Directory.GetDirectories(path); - foreach (var s in subfolders) - { - DeleteDirectory(s, recursive); - } + foreach (var s in subfolders) DeleteDirectory(s, recursive); } + var files = Directory.GetFiles(path); foreach (var f in files) - { try { var attr = File.GetAttributes(f); if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) - { File.SetAttributes(f, attr ^ FileAttributes.ReadOnly); - } File.Delete(f); } catch (IOException) { } - } + Directory.Delete(path, true); - - - } - - public VirtualFileSystem () - { - LoadFromDisk(); } private void LoadFromDisk() @@ -96,7 +86,6 @@ namespace VFS { while (true) { - var fr = VirtualFile.Read(br); _files.Add(fr.FullPath, fr); } @@ -135,10 +124,7 @@ namespace VFS using (var bw = new BinaryWriter(fs)) { Utils.Log($"Syncing VFS to Disk: {_files.Count} entries"); - foreach (var f in _files.Values) - { - f.Write(bw); - } + foreach (var f in _files.Values) f.Write(bw); } if (File.Exists("vfs_cache.bin")) @@ -158,8 +144,8 @@ namespace VFS { var path = f.FullPath + "|"; return _files.Values - .Where(v => v.FullPath.StartsWith(path)) - .ToList(); + .Where(v => v.FullPath.StartsWith(path)) + .ToList(); } @@ -169,11 +155,9 @@ namespace VFS lock (this) { _files.Values - .Where(v => v.FullPath.StartsWith(path) || v.FullPath == f.FullPath) - .ToList() - .Do(r => { - _files.Remove(r.FullPath); - }); + .Where(v => v.FullPath.StartsWith(path) || v.FullPath == f.FullPath) + .ToList() + .Do(r => { _files.Remove(r.FullPath); }); } } @@ -198,7 +182,7 @@ namespace VFS } /// - /// Remove any orphaned files in the DB. + /// Remove any orphaned files in the DB. /// private void CleanDB() { @@ -206,51 +190,49 @@ namespace VFS lock (this) { _files.Values - .Where(f => - { - if (f.IsConcrete) - return !File.Exists(f.StagedPath); - if (f.Hash == null) - return true; - while (f.ParentPath != null) - { - if (Lookup(f.ParentPath) == null) - return true; - if (f.Hash == null) - return true; - f = Lookup(f.ParentPath); - } - return false; - }) - .ToList() - .Do(f => _files.Remove(f.FullPath)); + .Where(f => + { + if (f.IsConcrete) + return !File.Exists(f.StagedPath); + if (f.Hash == null) + return true; + while (f.ParentPath != null) + { + if (Lookup(f.ParentPath) == null) + return true; + if (f.Hash == null) + return true; + f = Lookup(f.ParentPath); + } + + return false; + }) + .ToList() + .Do(f => _files.Remove(f.FullPath)); } } public void BackfillMissing() { - lock(this) + lock (this) { _files.Values - .Select(f => f.ParentPath) - .Where(s => s != null) - .Where(s => !_files.ContainsKey(s)) - .ToHashSet() - .Do(s => - { - AddKnown(new VirtualFile() { Paths = s.Split('|') }); - }); + .Select(f => f.ParentPath) + .Where(s => s != null) + .Where(s => !_files.ContainsKey(s)) + .ToHashSet() + .Do(s => { AddKnown(new VirtualFile {Paths = s.Split('|')}); }); } } /// - /// Add a known file to the index, bit of a hack as we won't assume that all the fields for the archive are filled in. - /// you will need to manually update the SHA hash when you are done adding files, by calling `RefreshIndexes` + /// Add a known file to the index, bit of a hack as we won't assume that all the fields for the archive are filled in. + /// you will need to manually update the SHA hash when you are done adding files, by calling `RefreshIndexes` /// /// public void AddKnown(VirtualFile virtualFile) { - lock(this) + lock (this) { // We don't know enough about these files to be able to store them in the disk cache _disableDiskCache = true; @@ -259,8 +241,8 @@ namespace VFS } /// - /// Adds the root path to the filesystem. This may take quite some time as every file in the folder will be hashed, - /// and every archive examined. + /// Adds the root path to the filesystem. This may take quite some time as every file in the folder will be hashed, + /// and every archive examined. /// /// public void AddRoot(string path) @@ -273,11 +255,11 @@ namespace VFS public void RefreshIndexes() { Utils.Log("Building Hash Index"); - lock(this) + lock (this) { HashIndex = _files.Values - .GroupBy(f => f.Hash) - .ToDictionary(f => f.Key, f => (IEnumerable)f); + .GroupBy(f => f.Hash) + .ToDictionary(f => f.Key, f => (IEnumerable) f); } } @@ -291,25 +273,23 @@ namespace VFS private void UpdateFile(string f) { - TOP: + TOP: var lv = Lookup(f); if (lv == null) { Utils.Status($"Analyzing {f}"); - lv = new VirtualFile() + lv = new VirtualFile { - Paths = new string[] { f } + Paths = new[] {f} }; lv.Analyze(); Add(lv); - if (lv.IsArchive) - { - UpdateArchive(lv); - } + if (lv.IsArchive) UpdateArchive(lv); // Upsert after extraction incase extraction fails } + if (lv.IsOutdated) { Purge(lv); @@ -331,20 +311,21 @@ namespace VFS Utils.Status($"Updating Archive {Path.GetFileName(f.StagedPath)}"); var entries = Directory.EnumerateFiles(tmp_dir, "*", SearchOption.AllDirectories) - .Select(path => path.RelativeTo(tmp_dir)); + .Select(path => path.RelativeTo(tmp_dir)); - var new_files = entries.Select(e => { + var new_files = entries.Select(e => + { var new_path = new string[f.Paths.Length + 1]; f.Paths.CopyTo(new_path, 0); new_path[f.Paths.Length] = e; - var nf = new VirtualFile() + var nf = new VirtualFile { - Paths = new_path, + Paths = new_path }; nf._stagedPath = Path.Combine(tmp_dir, e); Add(nf); return nf; - }).ToList(); + }).ToList(); // Analyze them new_files.PMap(file => @@ -362,19 +343,18 @@ namespace VFS Utils.Status("Cleaning Directory"); DeleteDirectory(tmp_dir); - } public Action Stage(IEnumerable files) { var grouped = files.SelectMany(f => f.FilesInPath) - .Distinct() - .Where(f => f.ParentArchive != null) - .GroupBy(f => f.ParentArchive) - .OrderBy(f => f.Key == null ? 0 : f.Key.Paths.Length) - .ToList(); + .Distinct() + .Where(f => f.ParentArchive != null) + .GroupBy(f => f.ParentArchive) + .OrderBy(f => f.Key == null ? 0 : f.Key.Paths.Length) + .ToList(); - List Paths = new List(); + var Paths = new List(); foreach (var group in grouped) { @@ -383,7 +363,6 @@ namespace VFS Paths.Add(tmp_path); foreach (var file in group) file._stagedPath = Path.Combine(tmp_path, file.Paths[group.Key.Paths.Length]); - } return () => @@ -395,7 +374,6 @@ namespace VFS }; } - public StagingGroup StageWith(IEnumerable files) { @@ -404,42 +382,30 @@ namespace VFS return grp; } - public VirtualFile this[string path] - { - get - { - return Lookup(path); - } - } - internal List GetArchiveEntryNames(VirtualFile file) { if (!file.IsStaged) throw new InvalidDataException("File is not staged"); - if (file.Extension == ".bsa") { + if (file.Extension == ".bsa") using (var ar = new BSAReader(file.StagedPath)) { return ar.Files.Select(f => f.Path).ToList(); } - } + if (file.Extension == ".zip") - { using (var s = new ZipFile(File.OpenRead(file.StagedPath))) { s.IsStreamOwner = true; s.UseZip64 = UseZip64.On; if (s.OfType().FirstOrDefault(e => !e.CanDecompress) == null) - { return s.OfType() - .Where(f => f.IsFile) - .Select(f => f.Name.Replace('/', '\\')) - .ToList(); - } + .Where(f => f.IsFile) + .Select(f => f.Name.Replace('/', '\\')) + .ToList(); } - } - + /* using (var e = new ArchiveFile(file.StagedPath)) { @@ -448,11 +414,10 @@ namespace VFS .Select(f => f.FileName).ToList(); }*/ return null; - } /// - /// Given a path that starts with a HASH, return the Virtual file referenced + /// Given a path that starts with a HASH, return the Virtual file referenced /// /// /// @@ -461,14 +426,16 @@ namespace VFS if (archiveHashPath.Length == 1) return HashIndex[archiveHashPath[0]].First(); - var archive = HashIndex[archiveHashPath[0]].Where(a => a.IsArchive).OrderByDescending(a => a.LastModified).First(); - string fullPath = archive.FullPath + "|" + String.Join("|", archiveHashPath.Skip(1)); + var archive = HashIndex[archiveHashPath[0]].Where(a => a.IsArchive).OrderByDescending(a => a.LastModified) + .First(); + var fullPath = archive.FullPath + "|" + string.Join("|", archiveHashPath.Skip(1)); return Lookup(fullPath); } public IDictionary> GroupedByArchive() { - return _files.Values.GroupBy(f => f.TopLevelArchive).ToDictionary(f => f.Key, f => (IEnumerable)f); + return _files.Values.GroupBy(f => f.TopLevelArchive) + .ToDictionary(f => f.Key, f => (IEnumerable) f); } } @@ -492,64 +459,52 @@ namespace VFS [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class VirtualFile { + private string _fullPath; + + private bool? _isArchive; + + private string _parentPath; public string[] _paths; + + internal string _stagedPath; + [JsonProperty] public string[] Paths { - get - { - return _paths; - } + get => _paths; set { - for (int idx = 0; idx < value.Length; idx += 1) - value[idx] = String.Intern(value[idx]); + for (var idx = 0; idx < value.Length; idx += 1) + value[idx] = string.Intern(value[idx]); _paths = value; } } - [JsonProperty] - public string Hash { get; set; } - [JsonProperty] - public long Size { get; set; } - [JsonProperty] - public ulong LastModified { get; set; } + + [JsonProperty] public string Hash { get; set; } + + [JsonProperty] public long Size { get; set; } + + [JsonProperty] public ulong LastModified { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public bool? FinishedIndexing { get; set; } - private string _fullPath; - - public VirtualFile() - { - } - - internal string _stagedPath; - - public string FullPath { get { if (_fullPath != null) return _fullPath; - _fullPath = String.Join("|", Paths); + _fullPath = string.Join("|", Paths); return _fullPath; } } - public string Extension - { - get - { - return Path.GetExtension(Paths.Last()); - } - } - - + public string Extension => Path.GetExtension(Paths.Last()); /// - /// If this file is in an archive, return the Archive File, otherwise return null. + /// If this file is in an archive, return the Archive File, otherwise return null. /// public VirtualFile TopLevelArchive { @@ -569,14 +524,13 @@ namespace VFS } } - private bool? _isArchive; public bool IsArchive { get { if (_isArchive == null) _isArchive = FileExtractor.CanExtract(Extension); - return (bool)_isArchive; + return (bool) _isArchive; } } @@ -606,58 +560,10 @@ namespace VFS } } - public FileStream OpenRead() - { - if (!IsStaged) - throw new InvalidDataException("File is not staged, cannot open"); - return File.OpenRead(_stagedPath); - } - /// - /// Calculate the file's SHA, size and last modified + /// Returns true if this file always exists on-disk, and doesn't need to be staged. /// - internal void Analyze() - { - if (!IsStaged) - throw new InvalidDataException("Cannot analyze an unstaged file"); - - var fio = new FileInfo(StagedPath); - Size = fio.Length; - Hash = Utils.FileSHA256(StagedPath); - LastModified = fio.LastWriteTime.ToMilliseconds(); - - } - - - /// - /// Delete the temoporary file associated with this file - /// - internal void Unstage() - { - if (IsStaged && !IsConcrete) - { - File.Delete(_stagedPath); - _stagedPath = null; - } - } - - internal string GenerateStagedName() - { - if (_stagedPath != null) return _stagedPath; - _stagedPath = Path.Combine(VirtualFileSystem._stagedRoot, Guid.NewGuid().ToString() + Path.GetExtension(Paths.Last())); - return _stagedPath; - } - - /// - /// Returns true if this file always exists on-disk, and doesn't need to be staged. - /// - public bool IsConcrete - { - get - { - return Paths.Length == 1; - } - } + public bool IsConcrete => Paths.Length == 1; public bool IsOutdated { @@ -672,46 +578,81 @@ namespace VFS if (!FinishedIndexing ?? true) return true; } + return false; } - } - private string _parentPath; - public string ParentPath - { - get { - if (_parentPath == null && !IsConcrete) - _parentPath = String.Join("|", Paths.Take(Paths.Length - 1)); - return _parentPath; - } - } - - public IEnumerable FileInArchive + public string ParentPath { get { - return VirtualFileSystem.VFS.FilesInArchive(this); + if (_parentPath == null && !IsConcrete) + _parentPath = string.Join("|", Paths.Take(Paths.Length - 1)); + return _parentPath; } } + public IEnumerable FileInArchive => VirtualFileSystem.VFS.FilesInArchive(this); + + public IEnumerable FilesInPath + { + get + { + return Enumerable.Range(1, Paths.Length) + .Select(i => Paths.Take(i)) + .Select(path => VirtualFileSystem.VFS.Lookup(string.Join("|", path))); + } + } + + public FileStream OpenRead() + { + if (!IsStaged) + throw new InvalidDataException("File is not staged, cannot open"); + return File.OpenRead(_stagedPath); + } + + /// + /// Calculate the file's SHA, size and last modified + /// + internal void Analyze() + { + if (!IsStaged) + throw new InvalidDataException("Cannot analyze an unstaged file"); + + var fio = new FileInfo(StagedPath); + Size = fio.Length; + Hash = StagedPath.FileSHA256(); + LastModified = fio.LastWriteTime.ToMilliseconds(); + } + + + /// + /// Delete the temoporary file associated with this file + /// + internal void Unstage() + { + if (IsStaged && !IsConcrete) + { + File.Delete(_stagedPath); + _stagedPath = null; + } + } + + internal string GenerateStagedName() + { + if (_stagedPath != null) return _stagedPath; + _stagedPath = Path.Combine(VirtualFileSystem._stagedRoot, Guid.NewGuid() + Path.GetExtension(Paths.Last())); + return _stagedPath; + } + public string[] MakeRelativePaths() { - var path_copy = (string[])Paths.Clone(); + var path_copy = (string[]) Paths.Clone(); path_copy[0] = VirtualFileSystem.VFS.Lookup(Paths[0]).Hash; return path_copy; } - public IEnumerable FilesInPath - { - get { - return Enumerable.Range(1, Paths.Length) - .Select(i => Paths.Take(i)) - .Select(path => VirtualFileSystem.VFS.Lookup(String.Join("|", path))); - - } - } - public void Write(BinaryWriter bw) { bw.Write(FullPath); @@ -727,8 +668,8 @@ namespace VFS var full_path = rdr.ReadString(); vf.Paths = full_path.Split('|'); - for (int x = 0; x < vf.Paths.Length; x++) - vf.Paths[x] = String.Intern(vf.Paths[x]); + for (var x = 0; x < vf.Paths.Length; x++) + vf.Paths[x] = string.Intern(vf.Paths[x]); vf._fullPath = full_path; vf.Hash = rdr.ReadString(); @@ -738,9 +679,6 @@ namespace VFS vf.FinishedIndexing = rdr.ReadBoolean(); if (vf.FinishedIndexing == false) vf.FinishedIndexing = null; return vf; - } } - - -} +} \ No newline at end of file diff --git a/VirtualFileSystem/packages.config b/VirtualFileSystem/packages.config index a49a0ac7..cfd09870 100644 --- a/VirtualFileSystem/packages.config +++ b/VirtualFileSystem/packages.config @@ -1,4 +1,5 @@  + diff --git a/Wabbajack.Common/BSDiff.cs b/Wabbajack.Common/BSDiff.cs index 26c16b52..d0352127 100644 --- a/Wabbajack.Common/BSDiff.cs +++ b/Wabbajack.Common/BSDiff.cs @@ -1,8 +1,9 @@ -namespace Wabbajack.Common +using System; +using System.IO; +using ICSharpCode.SharpZipLib.BZip2; + +namespace Wabbajack.Common { - using ICSharpCode.SharpZipLib.BZip2; - using System; - using System.IO; /* The original bsdiff.c source code (http://www.daemonology.net/bsdiff/) is distributed under the following license: @@ -38,13 +39,16 @@ */ public class BSDiff { + private const long c_fileSignature = 0x3034464649445342L; + private const int c_headerSize = 32; + /// - /// Creates a binary patch (in bsdiff format) that can be used - /// (by ) to transform into . + /// Creates a binary patch (in bsdiff format) that can be used + /// (by ) to transform into . /// /// The original binary data. /// The new binary data. - /// A to which the patch will be written. + /// A to which the patch will be written. public static void Create(byte[] oldData, byte[] newData, Stream output) { // check arguments @@ -69,59 +73,57 @@ 32 ?? Bzip2ed ctrl block ?? ?? Bzip2ed diff block ?? ?? Bzip2ed extra block */ - byte[] header = new byte[c_headerSize]; + var header = new byte[c_headerSize]; WriteInt64(c_fileSignature, header, 0); // "BSDIFF40" WriteInt64(0, header, 8); WriteInt64(0, header, 16); WriteInt64(newData.Length, header, 24); - long startPosition = output.Position; + var startPosition = output.Position; output.Write(header, 0, header.Length); - int[] I = SuffixSort(oldData); + var I = SuffixSort(oldData); - byte[] db = new byte[newData.Length]; - byte[] eb = new byte[newData.Length]; + var db = new byte[newData.Length]; + var eb = new byte[newData.Length]; - int dblen = 0; - int eblen = 0; + var dblen = 0; + var eblen = 0; - using (BZip2OutputStream bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) + using (var bz2Stream = new BZip2OutputStream(output) {IsStreamOwner = false}) { // compute the differences, writing ctrl as we go - int scan = 0; - int pos = 0; - int len = 0; - int lastscan = 0; - int lastpos = 0; - int lastoffset = 0; + var scan = 0; + var pos = 0; + var len = 0; + var lastscan = 0; + var lastpos = 0; + var lastoffset = 0; while (scan < newData.Length) { - int oldscore = 0; + var oldscore = 0; - for (int scsc = scan += len; scan < newData.Length; scan++) + for (var scsc = scan += len; scan < newData.Length; scan++) { len = Search(I, oldData, newData, scan, 0, oldData.Length, out pos); for (; scsc < scan + len; scsc++) - { - if ((scsc + lastoffset < oldData.Length) && (oldData[scsc + lastoffset] == newData[scsc])) + if (scsc + lastoffset < oldData.Length && oldData[scsc + lastoffset] == newData[scsc]) oldscore++; - } - if ((len == oldscore && len != 0) || (len > oldscore + 8)) + if (len == oldscore && len != 0 || len > oldscore + 8) break; - if ((scan + lastoffset < oldData.Length) && (oldData[scan + lastoffset] == newData[scan])) + if (scan + lastoffset < oldData.Length && oldData[scan + lastoffset] == newData[scan]) oldscore--; } if (len != oldscore || scan == newData.Length) { - int s = 0; - int sf = 0; - int lenf = 0; - for (int i = 0; (lastscan + i < scan) && (lastpos + i < oldData.Length);) + var s = 0; + var sf = 0; + var lenf = 0; + for (var i = 0; lastscan + i < scan && lastpos + i < oldData.Length;) { if (oldData[lastpos + i] == newData[lastscan + i]) s++; @@ -133,12 +135,12 @@ } } - int lenb = 0; + var lenb = 0; if (scan < newData.Length) { s = 0; - int sb = 0; - for (int i = 1; (scan >= lastscan + i) && (pos >= i); i++) + var sb = 0; + for (var i = 1; scan >= lastscan + i && pos >= i; i++) { if (oldData[pos - i] == newData[scan - i]) s++; @@ -152,11 +154,11 @@ if (lastscan + lenf > scan - lenb) { - int overlap = (lastscan + lenf) - (scan - lenb); + var overlap = lastscan + lenf - (scan - lenb); s = 0; - int ss = 0; - int lens = 0; - for (int i = 0; i < overlap; i++) + var ss = 0; + var lens = 0; + for (var i = 0; i < overlap; i++) { if (newData[lastscan + lenf - overlap + i] == oldData[lastpos + lenf - overlap + i]) s++; @@ -173,22 +175,22 @@ lenb -= lens; } - for (int i = 0; i < lenf; i++) - db[dblen + i] = (byte)(newData[lastscan + i] - oldData[lastpos + i]); - for (int i = 0; i < (scan - lenb) - (lastscan + lenf); i++) + for (var i = 0; i < lenf; i++) + db[dblen + i] = (byte) (newData[lastscan + i] - oldData[lastpos + i]); + for (var i = 0; i < scan - lenb - (lastscan + lenf); i++) eb[eblen + i] = newData[lastscan + lenf + i]; dblen += lenf; - eblen += (scan - lenb) - (lastscan + lenf); + eblen += scan - lenb - (lastscan + lenf); - byte[] buf = new byte[8]; + var buf = new byte[8]; WriteInt64(lenf, buf, 0); bz2Stream.Write(buf, 0, 8); - WriteInt64((scan - lenb) - (lastscan + lenf), buf, 0); + WriteInt64(scan - lenb - (lastscan + lenf), buf, 0); bz2Stream.Write(buf, 0, 8); - WriteInt64((pos - lenb) - (lastpos + lenf), buf, 0); + WriteInt64(pos - lenb - (lastpos + lenf), buf, 0); bz2Stream.Write(buf, 0, 8); lastscan = scan - lenb; @@ -199,41 +201,44 @@ } // compute size of compressed ctrl data - long controlEndPosition = output.Position; + var controlEndPosition = output.Position; WriteInt64(controlEndPosition - startPosition - c_headerSize, header, 8); // write compressed diff data - using (BZip2OutputStream bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) + using (var bz2Stream = new BZip2OutputStream(output) {IsStreamOwner = false}) { bz2Stream.Write(db, 0, dblen); } // compute size of compressed diff data - long diffEndPosition = output.Position; + var diffEndPosition = output.Position; WriteInt64(diffEndPosition - controlEndPosition, header, 16); // write compressed extra data - using (BZip2OutputStream bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) + using (var bz2Stream = new BZip2OutputStream(output) {IsStreamOwner = false}) { bz2Stream.Write(eb, 0, eblen); } // seek to the beginning, write the header, then seek back to end - long endPosition = output.Position; + var endPosition = output.Position; output.Position = startPosition; output.Write(header, 0, header.Length); output.Position = endPosition; } /// - /// Applies a binary patch (in bsdiff format) to the data in - /// and writes the results of patching to . + /// Applies a binary patch (in bsdiff format) to the data in + /// and writes the results of patching to . /// - /// A containing the input data. - /// A func that can open a positioned at the start of the patch data. - /// This stream must support reading and seeking, and must allow multiple streams on - /// the patch to be opened concurrently. - /// A to which the patched data is written. + /// A containing the input data. + /// + /// A func that can open a positioned at the start of the patch data. + /// This stream must support reading and seeking, and must allow multiple streams + /// on + /// the patch to be opened concurrently. + /// + /// A to which the patched data is written. public static void Apply(Stream input, Func openPatchStream, Stream output) { // check arguments @@ -259,7 +264,7 @@ */ // read header long controlLength, diffLength, newSize; - using (Stream patchStream = openPatchStream()) + using (var patchStream = openPatchStream()) { // check patch stream capabilities if (!patchStream.CanRead) @@ -267,10 +272,10 @@ if (!patchStream.CanSeek) throw new ArgumentException("Patch stream must be seekable.", "openPatchStream"); - byte[] header = ReadExactly(patchStream, c_headerSize); + var header = ReadExactly(patchStream, c_headerSize); // check for appropriate magic - long signature = ReadInt64(header, 0); + var signature = ReadInt64(header, 0); if (signature != c_fileSignature) throw new InvalidOperationException("Corrupt patch."); @@ -284,13 +289,13 @@ // preallocate buffers for reading and writing const int c_bufferSize = 1048576; - byte[] newData = new byte[c_bufferSize]; - byte[] oldData = new byte[c_bufferSize]; + var newData = new byte[c_bufferSize]; + var oldData = new byte[c_bufferSize]; // prepare to read three parts of the patch in parallel - using (Stream compressedControlStream = openPatchStream()) - using (Stream compressedDiffStream = openPatchStream()) - using (Stream compressedExtraStream = openPatchStream()) + using (var compressedControlStream = openPatchStream()) + using (var compressedDiffStream = openPatchStream()) + using (var compressedExtraStream = openPatchStream()) { // seek to the start of each part compressedControlStream.Seek(c_headerSize, SeekOrigin.Current); @@ -298,19 +303,19 @@ compressedExtraStream.Seek(c_headerSize + controlLength + diffLength, SeekOrigin.Current); // decompress each part (to read it) - using (BZip2InputStream controlStream = new BZip2InputStream(compressedControlStream)) - using (BZip2InputStream diffStream = new BZip2InputStream(compressedDiffStream)) - using (BZip2InputStream extraStream = new BZip2InputStream(compressedExtraStream)) + using (var controlStream = new BZip2InputStream(compressedControlStream)) + using (var diffStream = new BZip2InputStream(compressedDiffStream)) + using (var extraStream = new BZip2InputStream(compressedExtraStream)) { - long[] control = new long[3]; - byte[] buffer = new byte[8]; + var control = new long[3]; + var buffer = new byte[8]; - int oldPosition = 0; - int newPosition = 0; + var oldPosition = 0; + var newPosition = 0; while (newPosition < newSize) { // read control data - for (int i = 0; i < 3; i++) + for (var i = 0; i < 3; i++) { ReadExactly(controlStream, buffer, 0, 8); control[i] = ReadInt64(buffer, 0); @@ -323,19 +328,20 @@ // seek old file to the position that the new data is diffed against input.Position = oldPosition; - int bytesToCopy = (int)control[0]; + var bytesToCopy = (int) control[0]; while (bytesToCopy > 0) { - int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); + var actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); // read diff string ReadExactly(diffStream, newData, 0, actualBytesToCopy); // add old data to diff string - int availableInputBytes = Math.Min(actualBytesToCopy, (int)(input.Length - input.Position)); + var availableInputBytes = + Math.Min(actualBytesToCopy, (int) (input.Length - input.Position)); ReadExactly(input, oldData, 0, availableInputBytes); - for (int index = 0; index < availableInputBytes; index++) + for (var index = 0; index < availableInputBytes; index++) newData[index] += oldData[index]; output.Write(newData, 0, actualBytesToCopy); @@ -351,10 +357,10 @@ throw new InvalidOperationException("Corrupt patch."); // read extra string - bytesToCopy = (int)control[1]; + bytesToCopy = (int) control[1]; while (bytesToCopy > 0) { - int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); + var actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); ReadExactly(extraStream, newData, 0, actualBytesToCopy); output.Write(newData, 0, actualBytesToCopy); @@ -364,7 +370,7 @@ } // adjust position - oldPosition = (int)(oldPosition + control[2]); + oldPosition = (int) (oldPosition + control[2]); } } } @@ -372,12 +378,13 @@ private static int CompareBytes(byte[] left, int leftOffset, byte[] right, int rightOffset) { - for (int index = 0; index < left.Length - leftOffset && index < right.Length - rightOffset; index++) + for (var index = 0; index < left.Length - leftOffset && index < right.Length - rightOffset; index++) { - int diff = left[index + leftOffset] - right[index + rightOffset]; + var diff = left[index + leftOffset] - right[index + rightOffset]; if (diff != 0) return diff; } + return 0; } @@ -385,38 +392,33 @@ { int i; for (i = 0; i < oldData.Length - oldOffset && i < newData.Length - newOffset; i++) - { if (oldData[i + oldOffset] != newData[i + newOffset]) break; - } return i; } - private static int Search(int[] I, byte[] oldData, byte[] newData, int newOffset, int start, int end, out int pos) + private static int Search(int[] I, byte[] oldData, byte[] newData, int newOffset, int start, int end, + out int pos) { if (end - start < 2) { - int startLength = MatchLength(oldData, I[start], newData, newOffset); - int endLength = MatchLength(oldData, I[end], newData, newOffset); + var startLength = MatchLength(oldData, I[start], newData, newOffset); + var endLength = MatchLength(oldData, I[end], newData, newOffset); if (startLength > endLength) { pos = I[start]; return startLength; } - else - { - pos = I[end]; - return endLength; - } - } - else - { - int midPoint = start + (end - start) / 2; - return CompareBytes(oldData, I[midPoint], newData, newOffset) < 0 ? - Search(I, oldData, newData, newOffset, midPoint, end, out pos) : - Search(I, oldData, newData, newOffset, start, midPoint, out pos); + + pos = I[end]; + return endLength; } + + var midPoint = start + (end - start) / 2; + return CompareBytes(oldData, I[midPoint], newData, newOffset) < 0 + ? Search(I, oldData, newData, newOffset, midPoint, end, out pos) + : Search(I, oldData, newData, newOffset, start, midPoint, out pos); } private static void Split(int[] I, int[] v, int start, int len, int h) @@ -424,24 +426,26 @@ if (len < 16) { int j; - for (int k = start; k < start + len; k += j) + for (var k = start; k < start + len; k += j) { j = 1; - int x = v[I[k] + h]; - for (int i = 1; k + i < start + len; i++) + var x = v[I[k] + h]; + for (var i = 1; k + i < start + len; i++) { if (v[I[k + i] + h] < x) { x = v[I[k + i] + h]; j = 0; } + if (v[I[k + i] + h] == x) { Swap(ref I[k + j], ref I[k + i]); j++; } } - for (int i = 0; i < j; i++) + + for (var i = 0; i < j; i++) v[I[k + i]] = k + j - 1; if (j == 1) I[k] = -1; @@ -456,33 +460,36 @@ y = v[I[start] + h]; z = v[I[k] + h]; if (len > 40) - { /* Big array: Pseudomedian of 9 */ + { + /* Big array: Pseudomedian of 9 */ tmp = len / 8; x = median3(x, v[I[j - tmp] + h], v[I[j + tmp] + h]); y = median3(y, v[I[start + tmp] + h], v[I[start + tmp + tmp] + h]); z = median3(z, v[I[k - tmp] + h], v[I[k - tmp - tmp] + h]); - }; /* Else medium array: Pseudomedian of 3 */ + } + + ; /* Else medium array: Pseudomedian of 3 */ x = median3(x, y, z); //int x = v[I[start + len / 2] + h]; - int jj = 0; - int kk = 0; - for (int i2 = start; i2 < start + len; i2++) + var jj = 0; + var kk = 0; + for (var i2 = start; i2 < start + len; i2++) { if (v[I[i2] + h] < x) jj++; if (v[I[i2] + h] == x) kk++; } + jj += start; kk += jj; - int i = start; + var i = start; j = 0; k = 0; while (i < jj) - { if (v[I[i] + h] < x) { i++; @@ -497,10 +504,8 @@ Swap(ref I[i], ref I[kk + k]); k++; } - } while (jj + j < kk) - { if (v[I[jj + j] + h] == x) { j++; @@ -510,7 +515,6 @@ Swap(ref I[jj + j], ref I[kk + k]); k++; } - } if (jj > start) Split(I, v, start, jj - start, h); @@ -527,37 +531,34 @@ private static int[] SuffixSort(byte[] oldData) { - int[] buckets = new int[256]; + var buckets = new int[256]; - foreach (byte oldByte in oldData) + foreach (var oldByte in oldData) buckets[oldByte]++; - for (int i = 1; i < 256; i++) + for (var i = 1; i < 256; i++) buckets[i] += buckets[i - 1]; - for (int i = 255; i > 0; i--) + for (var i = 255; i > 0; i--) buckets[i] = buckets[i - 1]; buckets[0] = 0; - int[] I = new int[oldData.Length + 1]; - for (int i = 0; i < oldData.Length; i++) + var I = new int[oldData.Length + 1]; + for (var i = 0; i < oldData.Length; i++) I[++buckets[oldData[i]]] = i; - int[] v = new int[oldData.Length + 1]; - for (int i = 0; i < oldData.Length; i++) + var v = new int[oldData.Length + 1]; + for (var i = 0; i < oldData.Length; i++) v[i] = buckets[oldData[i]]; - for (int i = 1; i < 256; i++) - { + for (var i = 1; i < 256; i++) if (buckets[i] == buckets[i - 1] + 1) I[buckets[i]] = -1; - } I[0] = -1; - for (int h = 1; I[0] != -(oldData.Length + 1); h += h) + for (var h = 1; I[0] != -(oldData.Length + 1); h += h) { - int len = 0; - int i = 0; + var len = 0; + var i = 0; while (i < oldData.Length + 1) - { if (I[i] < 0) { len -= I[i]; @@ -572,13 +573,12 @@ i += len; len = 0; } - } if (len != 0) I[i - len] = -len; } - for (int i = 0; i < oldData.Length + 1; i++) + for (var i = 0; i < oldData.Length + 1; i++) I[v[i]] = i; return I; @@ -586,7 +586,7 @@ private static void Swap(ref int first, ref int second) { - int temp = first; + var temp = first; first = second; second = temp; } @@ -595,7 +595,7 @@ { long value = buf[offset + 7] & 0x7F; - for (int index = 6; index >= 0; index--) + for (var index = 6; index >= 0; index--) { value *= 256; value += buf[offset + index]; @@ -609,11 +609,11 @@ private static void WriteInt64(long value, byte[] buf, int offset) { - long valueToWrite = value < 0 ? -value : value; + var valueToWrite = value < 0 ? -value : value; - for (int byteIndex = 0; byteIndex < 8; byteIndex++) + for (var byteIndex = 0; byteIndex < 8; byteIndex++) { - buf[offset + byteIndex] = unchecked((byte)valueToWrite); + buf[offset + byteIndex] = unchecked((byte) valueToWrite); valueToWrite >>= 8; } @@ -622,7 +622,7 @@ } /// - /// Reads exactly bytes from . + /// Reads exactly bytes from . /// /// The stream to read from. /// The count of bytes to read. @@ -631,14 +631,14 @@ { if (count < 0) throw new ArgumentOutOfRangeException("count"); - byte[] buffer = new byte[count]; + var buffer = new byte[count]; ReadExactly(stream, buffer, 0, count); return buffer; } /// - /// Reads exactly bytes from into - /// , starting at the byte given by . + /// Reads exactly bytes from into + /// , starting at the byte given by . /// /// The stream to read from. /// The buffer to read data into. @@ -659,7 +659,7 @@ while (count > 0) { // read data - int bytesRead = stream.Read(buffer, offset, count); + var bytesRead = stream.Read(buffer, offset, count); // check for failure to read if (bytesRead == 0) @@ -671,14 +671,10 @@ } } - const long c_fileSignature = 0x3034464649445342L; - const int c_headerSize = 32; - private static int median3(int a, int b, int c) { - return (((a) < (b)) ? ((b) < (c) ? (b) : ((a) < (c) ? (c) : (a))) : ((b) > (c) ? (b) : ((a) > (c) ? (c) : (a)))); + return a < b ? b < c ? b : a < c ? c : a : b > c ? b : a > c ? c : a; } - } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/ChildProcessTracker.cs b/Wabbajack.Common/ChildProcessTracker.cs index eafd51c8..18af5d98 100644 --- a/Wabbajack.Common/ChildProcessTracker.cs +++ b/Wabbajack.Common/ChildProcessTracker.cs @@ -1,36 +1,25 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Wabbajack.Common { /// - /// Allows processes to be automatically killed if this parent process unexpectedly quits. - /// This feature requires Windows 8 or greater. On Windows 7, nothing is done. - /// References: - /// https://stackoverflow.com/a/4657392/386091 - /// https://stackoverflow.com/a/9164742/386091 + /// Allows processes to be automatically killed if this parent process unexpectedly quits. + /// This feature requires Windows 8 or greater. On Windows 7, nothing is done. + /// + /// + /// References: + /// https://stackoverflow.com/a/4657392/386091 + /// https://stackoverflow.com/a/9164742/386091 + /// public static class ChildProcessTracker { - /// - /// Add the process to be tracked. If our current process is killed, the child processes - /// that we are tracking will be automatically killed, too. If the child process terminates - /// first, that's fine, too. - /// - public static void AddProcess(Process process) - { - if (s_jobHandle != IntPtr.Zero) - { - bool success = AssignProcessToJobObject(s_jobHandle, process.Handle); - if (!success && !process.HasExited) - throw new Win32Exception(); - } - } + // Windows will automatically close any open job handles when our process terminates. + // This can be verified by using SysInternals' Handle utility. When the job handle + // is closed, the child processes will be killed. + private static readonly IntPtr s_jobHandle; static ChildProcessTracker() { @@ -45,7 +34,7 @@ namespace Wabbajack.Common // The job name is optional (and can be null) but it helps with diagnostics. // If it's not null, it has to be unique. Use SysInternals' Handle command-line // utility: handle -a ChildProcessTracker - string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; + var jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); @@ -58,17 +47,15 @@ namespace Wabbajack.Common var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); extendedInfo.BasicLimitInformation = info; - int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); - IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); + var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); + var extendedInfoPtr = Marshal.AllocHGlobal(length); try { Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, - extendedInfoPtr, (uint)length)) - { + extendedInfoPtr, (uint) length)) throw new Win32Exception(); - } } finally { @@ -76,20 +63,31 @@ namespace Wabbajack.Common } } + /// + /// Add the process to be tracked. If our current process is killed, the child processes + /// that we are tracking will be automatically killed, too. If the child process terminates + /// first, that's fine, too. + /// + /// + public static void AddProcess(Process process) + { + if (s_jobHandle != IntPtr.Zero) + { + var success = AssignProcessToJobObject(s_jobHandle, process.Handle); + if (!success && !process.HasExited) + throw new Win32Exception(); + } + } + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); + private static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); [DllImport("kernel32.dll")] - static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, + private static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] - static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); - - // Windows will automatically close any open job handles when our process terminates. - // This can be verified by using SysInternals' Handle utility. When the job handle - // is closed, the child processes will be killed. - private static readonly IntPtr s_jobHandle; + private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); } public enum JobObjectInfoType @@ -106,15 +104,15 @@ namespace Wabbajack.Common [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_BASIC_LIMIT_INFORMATION { - public Int64 PerProcessUserTimeLimit; - public Int64 PerJobUserTimeLimit; + public long PerProcessUserTimeLimit; + public long PerJobUserTimeLimit; public JOBOBJECTLIMIT LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; - public UInt32 ActiveProcessLimit; - public Int64 Affinity; - public UInt32 PriorityClass; - public UInt32 SchedulingClass; + public uint ActiveProcessLimit; + public long Affinity; + public uint PriorityClass; + public uint SchedulingClass; } [Flags] @@ -126,12 +124,12 @@ namespace Wabbajack.Common [StructLayout(LayoutKind.Sequential)] public struct IO_COUNTERS { - public UInt64 ReadOperationCount; - public UInt64 WriteOperationCount; - public UInt64 OtherOperationCount; - public UInt64 ReadTransferCount; - public UInt64 WriteTransferCount; - public UInt64 OtherTransferCount; + public ulong ReadOperationCount; + public ulong WriteOperationCount; + public ulong OtherOperationCount; + public ulong ReadTransferCount; + public ulong WriteTransferCount; + public ulong OtherTransferCount; } [StructLayout(LayoutKind.Sequential)] @@ -144,4 +142,4 @@ namespace Wabbajack.Common public UIntPtr PeakProcessMemoryUsed; public UIntPtr PeakJobMemoryUsed; } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/Consts.cs b/Wabbajack.Common/Consts.cs index e5819c4c..0dfc206e 100644 --- a/Wabbajack.Common/Consts.cs +++ b/Wabbajack.Common/Consts.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace Wabbajack.Common { @@ -16,17 +13,8 @@ namespace Wabbajack.Common public static string BSACreationDir = "TEMP_BSA_FILES"; public static string MegaPrefix = "https://mega.nz/#!"; - public static HashSet SupportedArchives = new HashSet { ".zip", ".rar", ".7z", ".7zip" }; - public static HashSet SupportedBSAs = new HashSet { ".bsa" }; - - public static String UserAgent { - get - { - var platformType = Environment.Is64BitOperatingSystem ? "x64" : "x86"; - var headerString = $"{AppName}/{Assembly.GetEntryAssembly().GetName().Version} ({Environment.OSVersion.VersionString}; {platformType}) {RuntimeInformation.FrameworkDescription}"; - return headerString; - } - } + public static HashSet SupportedArchives = new HashSet {".zip", ".rar", ".7z", ".7zip"}; + public static HashSet SupportedBSAs = new HashSet {".bsa"}; public static HashSet ConfigFileExtensions = new HashSet {".json", ".ini", ".yml"}; @@ -48,9 +36,21 @@ namespace Wabbajack.Common public static string DOWNLOAD_PATH_MAGIC_FORWARD = "{--||DOWNLOAD_PATH_MAGIC_FORWARD||--}"; - public static String AppName = "Wabbajack"; + public static string AppName = "Wabbajack"; public static string HashCacheName = "Wabbajack.hash_cache"; - public static HashSet GameESMs = new HashSet() { "Skyrim.esm", "Update.esm", "Dawnguard.esm", "HearthFires.esm", "Dragonborn.esm" }; + public static HashSet GameESMs = new HashSet + {"Skyrim.esm", "Update.esm", "Dawnguard.esm", "HearthFires.esm", "Dragonborn.esm"}; + + public static string UserAgent + { + get + { + var platformType = Environment.Is64BitOperatingSystem ? "x64" : "x86"; + var headerString = + $"{AppName}/{Assembly.GetEntryAssembly().GetName().Version} ({Environment.OSVersion.VersionString}; {platformType}) {RuntimeInformation.FrameworkDescription}"; + return headerString; + } + } } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/DynamicIniData.cs b/Wabbajack.Common/DynamicIniData.cs index 094630e9..0d41edd1 100644 --- a/Wabbajack.Common/DynamicIniData.cs +++ b/Wabbajack.Common/DynamicIniData.cs @@ -1,16 +1,13 @@ -using IniParser; -using IniParser.Model; -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Text; +using System.Dynamic; using System.Text.RegularExpressions; +using IniParser; +using IniParser.Model; namespace Wabbajack.Common { public class DynamicIniData : DynamicObject { - private IniData value; + private readonly IniData value; public DynamicIniData(IniData value) // { @@ -35,24 +32,20 @@ namespace Wabbajack.Common } } - class SectionData : DynamicObject + internal class SectionData : DynamicObject { - private KeyDataCollection _coll; + private readonly KeyDataCollection _coll; public SectionData(KeyDataCollection coll) { - this._coll = coll; + _coll = coll; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = _coll[binder.Name]; - if (result is string) - { - result = Regex.Unescape(((string)result).Trim('"')); - } + if (result is string) result = Regex.Unescape(((string) result).Trim('"')); return true; } } - -} +} \ No newline at end of file diff --git a/Wabbajack.Common/FileExtractor.cs b/Wabbajack.Common/FileExtractor.cs index 77f097a2..9f4a2c56 100644 --- a/Wabbajack.Common/FileExtractor.cs +++ b/Wabbajack.Common/FileExtractor.cs @@ -1,16 +1,9 @@ -using Compression.BSA; -using ICSharpCode.SharpZipLib.Zip; -using System; -using System.Collections.Generic; +using System; using System.Diagnostics; -using System.Linq; -using System.Net; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; +using Compression.BSA; using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; namespace Wabbajack.Common { @@ -34,25 +27,15 @@ namespace Wabbajack.Common } } - public class Entry - { - public string Name; - public ulong Size; - } - public static void ExtractAll(string source, string dest) { try { if (source.EndsWith(".bsa")) - { ExtractAllWithBSA(source, dest); - } else - { ExtractAllWith7Zip(source, dest); - } } catch (Exception ex) { @@ -105,12 +88,12 @@ namespace Wabbajack.Common RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false, - CreateNoWindow = true, + CreateNoWindow = true }; var p = new Process { - StartInfo = info, + StartInfo = info }; p.Start(); @@ -119,7 +102,9 @@ namespace Wabbajack.Common { p.PriorityClass = ProcessPriorityClass.BelowNormal; } - catch (Exception) { } + catch (Exception) + { + } var name = Path.GetFileName(source); try @@ -129,18 +114,16 @@ namespace Wabbajack.Common var line = p.StandardOutput.ReadLine(); if (line == null) break; - int percent = 0; + var percent = 0; if (line.Length > 4 && line[3] == '%') { - Int32.TryParse(line.Substring(0, 3), out percent); + int.TryParse(line.Substring(0, 3), out percent); Utils.Status($"Extracting {name} - {line.Trim()}", percent); } - } } catch (Exception ex) { - } p.WaitForExit(); @@ -149,11 +132,10 @@ namespace Wabbajack.Common Utils.Log(p.StandardOutput.ReadToEnd()); Utils.Log($"Extraction error extracting {source}"); } - } /// - /// Returns true if the given extension type can be extracted + /// Returns true if the given extension type can be extracted /// /// /// @@ -162,5 +144,10 @@ namespace Wabbajack.Common return Consts.SupportedArchives.Contains(v) || v == ".bsa"; } + public class Entry + { + public string Name; + public ulong Size; + } } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/Properties/AssemblyInfo.cs b/Wabbajack.Common/Properties/AssemblyInfo.cs index fbfcdefc..ed598795 100644 --- a/Wabbajack.Common/Properties/AssemblyInfo.cs +++ b/Wabbajack.Common/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Wabbajack.Common/SplittingStream.cs b/Wabbajack.Common/SplittingStream.cs index 858988b8..f95b39bd 100644 --- a/Wabbajack.Common/SplittingStream.cs +++ b/Wabbajack.Common/SplittingStream.cs @@ -1,18 +1,22 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Wabbajack.Common { public class SplittingStream : Stream { - private Stream _a; - private Stream _b; - private bool _leave_a_open; - private bool _leave_b_open; + private readonly Stream _a; + private readonly Stream _b; + private readonly bool _leave_a_open; + private readonly bool _leave_b_open; + + public SplittingStream(Stream a, bool leave_a_open, Stream b, bool leave_b_open) + { + _a = a; + _b = b; + _leave_a_open = leave_a_open; + _leave_b_open = leave_b_open; + } public override bool CanRead => false; @@ -22,14 +26,10 @@ namespace Wabbajack.Common public override long Length => throw new NotImplementedException(); - public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public SplittingStream(Stream a, bool leave_a_open, Stream b, bool leave_b_open) + public override long Position { - _a = a; - _b = b; - _leave_a_open = leave_a_open; - _leave_b_open = leave_b_open; + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); } public override void Flush() @@ -68,4 +68,4 @@ namespace Wabbajack.Common } } } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/Utils.cs b/Wabbajack.Common/Utils.cs index 256aba13..74c23a5c 100644 --- a/Wabbajack.Common/Utils.cs +++ b/Wabbajack.Common/Utils.cs @@ -1,18 +1,17 @@ -using ICSharpCode.SharpZipLib.BZip2; -using IniParser; -using Newtonsoft.Json; -using Newtonsoft.Json.Bson; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net.Configuration; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; +using ICSharpCode.SharpZipLib.BZip2; +using IniParser; +using Newtonsoft.Json; +using Newtonsoft.Json.Bson; using File = Alphaleonis.Win32.Filesystem.File; using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo; using Path = Alphaleonis.Win32.Filesystem.Path; @@ -24,6 +23,8 @@ namespace Wabbajack.Common private static Action _loggerFn; private static Action _statusFn; + private static readonly string[] Suffix = {"B", "KB", "MB", "GB", "TB", "PB", "EB"}; //Longs run out around EB + public static void SetLoggerFn(Action f) { _loggerFn = f; @@ -43,10 +44,10 @@ namespace Wabbajack.Common { _statusFn?.Invoke(msg, progress); } - + /// - /// MurMur3 hashes the file pointed to by this string + /// MurMur3 hashes the file pointed to by this string /// /// /// @@ -56,10 +57,12 @@ namespace Wabbajack.Common using (var o = new CryptoStream(Stream.Null, sha, CryptoStreamMode.Write)) { using (var i = File.OpenRead(file)) + { i.CopyToWithStatus(new FileInfo(file).Length, o, $"Hashing {Path.GetFileName(file)}"); + } } - return sha.Hash.ToBase64(); + return sha.Hash.ToBase64(); } public static void CopyToWithStatus(this Stream istream, long maxSize, Stream ostream, string status) @@ -69,22 +72,21 @@ namespace Wabbajack.Common long total_read = 0; while (true) { - int read = istream.Read(buffer, 0, buffer.Length); + var read = istream.Read(buffer, 0, buffer.Length); if (read == 0) break; total_read += read; ostream.Write(buffer, 0, read); - Status(status, (int)(total_read * 100 / maxSize)); + Status(status, (int) (total_read * 100 / maxSize)); } } public static string SHA256(this byte[] data) { return new SHA256Managed().ComputeHash(data).ToBase64(); - } /// - /// Returns a Base64 encoding of these bytes + /// Returns a Base64 encoding of these bytes /// /// /// @@ -95,16 +97,13 @@ namespace Wabbajack.Common public static string ToHEX(this byte[] bytes) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < bytes.Length; i++) - { - builder.Append(bytes[i].ToString("x2")); - } + var builder = new StringBuilder(); + for (var i = 0; i < bytes.Length; i++) builder.Append(bytes[i].ToString("x2")); return builder.ToString(); } /// - /// Returns data from a base64 stream + /// Returns data from a base64 stream /// /// /// @@ -114,7 +113,7 @@ namespace Wabbajack.Common } /// - /// Executes the action for every item in coll + /// Executes the action for every item in coll /// /// /// @@ -126,7 +125,7 @@ namespace Wabbajack.Common public static void DoIndexed(this IEnumerable coll, Action f) { - int idx = 0; + var idx = 0; foreach (var i in coll) { f(idx, i); @@ -136,8 +135,8 @@ namespace Wabbajack.Common /// - /// Loads INI data from the given filename and returns a dynamic type that - /// can use . operators to navigate the INI. + /// Loads INI data from the given filename and returns a dynamic type that + /// can use . operators to navigate the INI. /// /// /// @@ -148,50 +147,57 @@ namespace Wabbajack.Common public static void ToJSON(this T obj, string filename) { - File.WriteAllText(filename, JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto})); + File.WriteAllText(filename, + JsonConvert.SerializeObject(obj, Formatting.Indented, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto})); } public static void ToBSON(this T obj, string filename) { - using(var fo = File.OpenWrite(filename)) - using(var br = new BsonDataWriter(fo)) + using (var fo = File.OpenWrite(filename)) + using (var br = new BsonDataWriter(fo)) { fo.SetLength(0); - var serializer = JsonSerializer.Create(new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }); + var serializer = JsonSerializer.Create(new JsonSerializerSettings + {TypeNameHandling = TypeNameHandling.Auto}); serializer.Serialize(br, obj); } } public static ulong ToMilliseconds(this DateTime date) { - return (ulong)(date - new DateTime(1970, 1, 1)).TotalMilliseconds; + return (ulong) (date - new DateTime(1970, 1, 1)).TotalMilliseconds; } public static string ToJSON(this T obj) { - return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }); + return JsonConvert.SerializeObject(obj, Formatting.Indented, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}); } public static T FromJSON(this string filename) { - return JsonConvert.DeserializeObject(File.ReadAllText(filename), new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }); + return JsonConvert.DeserializeObject(File.ReadAllText(filename), + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}); } public static T FromBSON(this string filename, bool root_is_array = false) { using (var fo = File.OpenRead(filename)) - using (var br = new BsonDataReader(fo, readRootValueAsArray: root_is_array, DateTimeKind.Local)) + using (var br = new BsonDataReader(fo, root_is_array, DateTimeKind.Local)) { - var serializer = JsonSerializer.Create(new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }); + var serializer = JsonSerializer.Create(new JsonSerializerSettings + {TypeNameHandling = TypeNameHandling.Auto}); return serializer.Deserialize(br); } - } public static T FromJSONString(this string data) { - return JsonConvert.DeserializeObject(data, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }); + return JsonConvert.DeserializeObject(data, + new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}); } + public static T FromJSON(this Stream data) { var s = Encoding.UTF8.GetString(data.ReadAll()); @@ -209,7 +215,7 @@ namespace Wabbajack.Common } /// - /// Returns the string compressed via BZip2 + /// Returns the string compressed via BZip2 /// /// /// @@ -220,14 +226,17 @@ namespace Wabbajack.Common using (var bz = new BZip2OutputStream(os)) { using (var bw = new BinaryWriter(bz)) + { bw.Write(data); + } } + return os.ToArray(); } } /// - /// Returns the string compressed via BZip2 + /// Returns the string compressed via BZip2 /// /// /// @@ -238,7 +247,9 @@ namespace Wabbajack.Common using (var bz = new BZip2InputStream(s)) { using (var bw = new BinaryReader(bz)) + { return bw.ReadString(); + } } } } @@ -258,11 +269,11 @@ namespace Wabbajack.Common Interlocked.Add(ref WorkQueue.MaxQueueSize, colllst.Count); //WorkQueue.CurrentQueueSize = 0; - int remaining_tasks = colllst.Count; + var remaining_tasks = colllst.Count; var tasks = coll.Select(i => { - TaskCompletionSource tc = new TaskCompletionSource(); + var tc = new TaskCompletionSource(); WorkQueue.QueueTask(() => { try @@ -273,6 +284,7 @@ namespace Wabbajack.Common { tc.SetException(ex); } + Interlocked.Increment(ref WorkQueue.CurrentQueueSize); Interlocked.Decrement(ref remaining_tasks); WorkQueue.ReportNow(); @@ -282,13 +294,9 @@ namespace Wabbajack.Common // To avoid thread starvation, we'll start to help out in the work queue if (WorkQueue.WorkerThread) - while(remaining_tasks > 0) - { - if(WorkQueue.Queue.TryTake(out var a, 500)) - { - a(); - } - } + while (remaining_tasks > 0) + if (WorkQueue.Queue.TryTake(out var a, 500)) + a(); if (WorkQueue.CurrentQueueSize == WorkQueue.MaxQueueSize) { @@ -307,14 +315,14 @@ namespace Wabbajack.Common public static void PMap(this IEnumerable coll, Action f) { - coll.PMap(i => + coll.PMap(i => { f(i); return false; }); } - public static void DoProgress(this IEnumerable coll, String msg, Action f) + public static void DoProgress(this IEnumerable coll, string msg, Action f) { var lst = coll.ToList(); lst.DoIndexed((idx, i) => @@ -335,6 +343,7 @@ namespace Wabbajack.Common result.Wait(); return result.Result; } + public static string GetStringSync(this HttpClient client, string url) { var result = client.GetStringAsync(url); @@ -360,15 +369,14 @@ namespace Wabbajack.Common public static string ExceptionToString(this Exception ex) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); while (ex != null) { sb.AppendLine(ex.Message); var st = new StackTrace(ex, true); foreach (var frame in st.GetFrames()) - { - sb.AppendLine($"{frame.GetFileName()}:{frame.GetMethod().Name}:{frame.GetFileLineNumber()}:{frame.GetFileColumnNumber()}"); - } + sb.AppendLine( + $"{frame.GetFileName()}:{frame.GetMethod().Name}:{frame.GetFileLineNumber()}:{frame.GetFileColumnNumber()}"); ex = ex.InnerException; } @@ -383,13 +391,13 @@ namespace Wabbajack.Common public static IEnumerable DistinctBy(this IEnumerable vs, Func select) { - HashSet set = new HashSet(); - foreach (var v in vs) { + var set = new HashSet(); + foreach (var v in vs) + { var key = select(v); if (set.Contains(key)) continue; yield return v; } - } public static T Last(this T[] a) @@ -401,19 +409,18 @@ namespace Wabbajack.Common public static V GetOrDefault(this IDictionary dict, K key) { - if (dict.TryGetValue(key, out V v)) return v; - return default(V); + if (dict.TryGetValue(key, out var v)) return v; + return default; } - private static string[] Suffix = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB public static string ToFileSizeString(this long byteCount) { if (byteCount == 0) return "0" + Suffix[0]; - long bytes = Math.Abs(byteCount); - int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); - double num = Math.Round(bytes / Math.Pow(1024, place), 1); - return (Math.Sign(byteCount) * num).ToString() + Suffix[place]; + var bytes = Math.Abs(byteCount); + var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); + var num = Math.Round(bytes / Math.Pow(1024, place), 1); + return Math.Sign(byteCount) * num + Suffix[place]; } public static void CreatePatch(byte[] a, byte[] b, Stream output) @@ -452,8 +459,9 @@ namespace Wabbajack.Common public static void TryGetPatch(string foundHash, string fileHash, out byte[] ePatch) { - var patch_name = Path.Combine("patch_cache", $"{foundHash.FromBase64().ToHEX()}_{fileHash.FromBase64().ToHEX()}.patch"); + var patch_name = Path.Combine("patch_cache", + $"{foundHash.FromBase64().ToHEX()}_{fileHash.FromBase64().ToHEX()}.patch"); ePatch = File.Exists(patch_name) ? File.ReadAllBytes(patch_name) : null; } } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/WorkQueue.cs b/Wabbajack.Common/WorkQueue.cs index 749abb95..fcb1b3e2 100644 --- a/Wabbajack.Common/WorkQueue.cs +++ b/Wabbajack.Common/WorkQueue.cs @@ -2,21 +2,26 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace Wabbajack.Common { public class WorkQueue { - internal static BlockingCollection Queue = new BlockingCollection(new ConcurrentStack()); + internal static BlockingCollection + Queue = new BlockingCollection(new ConcurrentStack()); - [ThreadStatic] - private static int CpuId; + [ThreadStatic] private static int CpuId; - [ThreadStatic] - internal static bool WorkerThread; + [ThreadStatic] internal static bool WorkerThread; + + public static int MaxQueueSize; + public static int CurrentQueueSize; + + public static Action ReportFunction { get; private set; } + public static Action ReportQueueSize { get; private set; } + public static int ThreadCount { get; private set; } + public static List Threads { get; private set; } public static void Init(Action report_function, Action report_queue_size) { @@ -29,15 +34,15 @@ namespace Wabbajack.Common private static void StartThreads() { Threads = Enumerable.Range(0, ThreadCount) - .Select(idx => - { - var thread = new Thread(() => ThreadBody(idx)); - thread.Priority = ThreadPriority.BelowNormal; - thread.IsBackground = true; - thread.Name = String.Format("Wabbajack_Worker_{0}", idx); - thread.Start(); - return thread; - }).ToList(); + .Select(idx => + { + var thread = new Thread(() => ThreadBody(idx)); + thread.Priority = ThreadPriority.BelowNormal; + thread.IsBackground = true; + thread.Name = string.Format("Wabbajack_Worker_{0}", idx); + thread.Start(); + return thread; + }).ToList(); } private static void ThreadBody(int idx) @@ -45,14 +50,14 @@ namespace Wabbajack.Common CpuId = idx; WorkerThread = true; - while(true) + while (true) { Report("Waiting", 0); var f = Queue.Take(); f(); } - } + public static void Report(string msg, int progress) { ReportFunction(CpuId, msg, progress); @@ -63,16 +68,9 @@ namespace Wabbajack.Common Queue.Add(a); } - public static Action ReportFunction { get; private set; } - public static Action ReportQueueSize { get; private set; } - public static int ThreadCount { get; private set; } - public static List Threads { get; private set; } - public static int MaxQueueSize; - public static int CurrentQueueSize; - internal static void ReportNow() { ReportQueueSize(MaxQueueSize, CurrentQueueSize); } } -} +} \ No newline at end of file diff --git a/Wabbajack.Common/packages.config b/Wabbajack.Common/packages.config index 38ced1b9..ae47d570 100644 --- a/Wabbajack.Common/packages.config +++ b/Wabbajack.Common/packages.config @@ -1,4 +1,5 @@  + diff --git a/Wabbajack/App.config b/Wabbajack/App.config index 851d9884..b0634730 100644 --- a/Wabbajack/App.config +++ b/Wabbajack/App.config @@ -1,8 +1,9 @@  + - - - + + + diff --git a/Wabbajack/App.xaml b/Wabbajack/App.xaml index e24a56eb..9dd43ac8 100644 --- a/Wabbajack/App.xaml +++ b/Wabbajack/App.xaml @@ -10,4 +10,4 @@ - + \ No newline at end of file diff --git a/Wabbajack/App.xaml.cs b/Wabbajack/App.xaml.cs index c3240ebd..8b504a42 100644 --- a/Wabbajack/App.xaml.cs +++ b/Wabbajack/App.xaml.cs @@ -1,18 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace Wabbajack { /// - /// Interaction logic for App.xaml + /// Interaction logic for App.xaml /// public partial class App : Application { - } -} +} \ No newline at end of file diff --git a/Wabbajack/AppState.cs b/Wabbajack/AppState.cs index f12834e5..ee261fa9 100644 --- a/Wabbajack/AppState.cs +++ b/Wabbajack/AppState.cs @@ -4,160 +4,47 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using System.Threading; using System.Windows; using System.Windows.Input; using System.Windows.Threading; +using Ookii.Dialogs.Wpf; using Wabbajack.Common; namespace Wabbajack { internal class AppState : INotifyPropertyChanged { - public class CPUStatus - { - public int Progress { get; internal set; } - public string Msg { get; internal set; } - public int ID { get; internal set; } - } + private ICommand _begin; - public volatile bool Dirty; + private ICommand _changeDownloadPath; - private Dispatcher dispatcher; + private ICommand _changePath; + private string _downloadLocation; - public event PropertyChangedEventHandler PropertyChanged; + private string _htmlReport; - public void OnPropertyChanged(string name) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); - } + private bool _ignoreMissingFiles; + private string _location; - public ObservableCollection Log { get; } - public ObservableCollection Status { get; } + private string _mo2Folder; private string _mode; - public string Mode - { - get - { - return _mode; - } - set - { - _mode = value; - OnPropertyChanged("Mode"); - } - } - - private bool _ignoreMissingFiles = false; - public bool IgnoreMissingFiles - { - get - { - return _ignoreMissingFiles; - } - set - { - if (value) - { - if (MessageBox.Show("Setting this value could result in broken installations. \n Are you sure you want to continue?", "Ignore Missing Files?", MessageBoxButton.OKCancel, MessageBoxImage.Warning) - == MessageBoxResult.OK) - { - _ignoreMissingFiles = value; - } - } - else - { - _ignoreMissingFiles = value; - } - OnPropertyChanged("IgnoreMissingFiles"); - } - } - - private string _mo2Folder; - private string _modListName; private ModList _modList; - private string _location; - private string _downloadLocation; - - public string ModListName - { - get - { - return _modListName; - } - set - { - _modListName = value; - OnPropertyChanged("ModListName"); - } - } - - public string Location - { - get - { - return _location; - } - set - { - _location = value; - OnPropertyChanged("Location"); - } - } - - public string DownloadLocation - { - get - { - return _downloadLocation; - } - set - { - _downloadLocation = value; - OnPropertyChanged("DownloadLocation"); - } - } - - private string _htmlReport; - public Visibility ShowReportButton => _htmlReport == null ? Visibility.Collapsed : Visibility.Visible; - - public string HTMLReport - { - get { return _htmlReport; } - set - { - _htmlReport = value; - OnPropertyChanged("HTMLReport"); - OnPropertyChanged("ShowReportButton"); - } - } + private string _modListName; private int _queueProgress; - public int QueueProgress - { - get - { - return _queueProgress; - } - set - { - if (value != _queueProgress) - { - _queueProgress = value; - OnPropertyChanged("QueueProgress"); - } - } - } + private ICommand _showReportCommand; + private readonly DateTime _startTime; - private List InternalStatus { get; } - public string LogFile { get; private set; } + public volatile bool Dirty; - public AppState(Dispatcher d, String mode) + private readonly Dispatcher dispatcher; + + public AppState(Dispatcher d, string mode) { if (Assembly.GetEntryAssembly().Location.ToLower().Contains("\\downloads\\")) { @@ -189,28 +76,164 @@ namespace Wabbajack th.Start(); } + public ObservableCollection Log { get; } + public ObservableCollection Status { get; } + + public string Mode + { + get => _mode; + set + { + _mode = value; + OnPropertyChanged("Mode"); + } + } + + public bool IgnoreMissingFiles + { + get => _ignoreMissingFiles; + set + { + if (value) + { + if (MessageBox.Show( + "Setting this value could result in broken installations. \n Are you sure you want to continue?", + "Ignore Missing Files?", MessageBoxButton.OKCancel, MessageBoxImage.Warning) + == MessageBoxResult.OK) + _ignoreMissingFiles = value; + } + else + { + _ignoreMissingFiles = value; + } + + OnPropertyChanged("IgnoreMissingFiles"); + } + } + + public string ModListName + { + get => _modListName; + set + { + _modListName = value; + OnPropertyChanged("ModListName"); + } + } + + public string Location + { + get => _location; + set + { + _location = value; + OnPropertyChanged("Location"); + } + } + + public string DownloadLocation + { + get => _downloadLocation; + set + { + _downloadLocation = value; + OnPropertyChanged("DownloadLocation"); + } + } + + public Visibility ShowReportButton => _htmlReport == null ? Visibility.Collapsed : Visibility.Visible; + + public string HTMLReport + { + get => _htmlReport; + set + { + _htmlReport = value; + OnPropertyChanged("HTMLReport"); + OnPropertyChanged("ShowReportButton"); + } + } + + public int QueueProgress + { + get => _queueProgress; + set + { + if (value != _queueProgress) + { + _queueProgress = value; + OnPropertyChanged("QueueProgress"); + } + } + } + + + private List InternalStatus { get; } + public string LogFile { get; } + + public ICommand ChangePath + { + get + { + if (_changePath == null) _changePath = new LambdaCommand(() => true, () => ExecuteChangePath()); + return _changePath; + } + } + + public ICommand ChangeDownloadPath + { + get + { + if (_changeDownloadPath == null) + _changeDownloadPath = new LambdaCommand(() => true, () => ExecuteChangeDownloadPath()); + return _changeDownloadPath; + } + } + + public ICommand Begin + { + get + { + if (_begin == null) _begin = new LambdaCommand(() => true, () => ExecuteBegin()); + return _begin; + } + } + + public ICommand ShowReportCommand + { + get + { + if (_showReportCommand == null) _showReportCommand = new LambdaCommand(() => true, () => ShowReport()); + return _showReportCommand; + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void OnPropertyChanged(string name) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + private void UpdateLoop() { while (true) { if (Dirty) - { lock (InternalStatus) { var data = InternalStatus.ToArray(); dispatcher.Invoke(() => { - for (int idx = 0; idx < data.Length; idx += 1) - { + for (var idx = 0; idx < data.Length; idx += 1) if (idx >= Status.Count) Status.Add(data[idx]); else if (Status[idx] != data[idx]) Status[idx] = data[idx]; - } }); Dirty = false; } - } + Thread.Sleep(1000); } } @@ -222,14 +245,14 @@ namespace Wabbajack ModListName = _modList.Name; HTMLReport = _modList.ReportHTML; Location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - } public void LogMsg(string msg) { msg = $"{(DateTime.Now - _startTime).TotalSeconds:0.##} - {msg}"; dispatcher.Invoke(() => Log.Add(msg)); - lock (dispatcher) { + lock (dispatcher) + { File.AppendAllText(LogFile, msg + "\r\n"); } } @@ -239,12 +262,9 @@ namespace Wabbajack lock (InternalStatus) { Dirty = true; - while (id >= InternalStatus.Count) - { - InternalStatus.Add(new CPUStatus()); - } + while (id >= InternalStatus.Count) InternalStatus.Add(new CPUStatus()); - InternalStatus[id] = new CPUStatus() { ID = id, Msg = msg, Progress = progress }; + InternalStatus[id] = new CPUStatus {ID = id, Msg = msg, Progress = progress}; } } @@ -256,37 +276,11 @@ namespace Wabbajack QueueProgress = total; } - private ICommand _changePath; - public ICommand ChangePath - { - get - { - if (_changePath == null) - { - _changePath = new LambdaCommand(() => true, () => this.ExecuteChangePath()); - } - return _changePath; - } - } - - private ICommand _changeDownloadPath; - public ICommand ChangeDownloadPath - { - get - { - if (_changeDownloadPath == null) - { - _changeDownloadPath = new LambdaCommand(() => true, () => this.ExecuteChangeDownloadPath()); - } - return _changeDownloadPath; - } - } - private void ExecuteChangePath() { if (Mode == "Installing") { - var ofd = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog(); + var ofd = new VistaFolderBrowserDialog(); ofd.Description = "Select Installation Directory"; ofd.UseDescriptionForTitle = true; if (ofd.ShowDialog() == true) @@ -298,7 +292,7 @@ namespace Wabbajack } else { - var fsd = new Ookii.Dialogs.Wpf.VistaOpenFileDialog(); + var fsd = new VistaOpenFileDialog(); fsd.Title = "Select a ModOrganizer modlist.txt file"; fsd.Filter = "modlist.txt|modlist.txt"; if (fsd.ShowDialog() == true) @@ -311,13 +305,10 @@ namespace Wabbajack private void ExecuteChangeDownloadPath() { - var ofd = new Ookii.Dialogs.Wpf.VistaFolderBrowserDialog(); + var ofd = new VistaFolderBrowserDialog(); ofd.Description = "Select a location for MO2 downloads"; ofd.UseDescriptionForTitle = true; - if (ofd.ShowDialog() == true) - { - DownloadLocation = ofd.SelectedPath; - } + if (ofd.ShowDialog() == true) DownloadLocation = ofd.SelectedPath; } private void ConfigureForBuild() @@ -337,34 +328,6 @@ namespace Wabbajack _mo2Folder = mo2folder; } - private ICommand _begin; - private DateTime _startTime; - - public ICommand Begin - { - get - { - if (_begin == null) - { - _begin = new LambdaCommand(() => true, () => this.ExecuteBegin()); - } - return _begin; - } - } - - private ICommand _showReportCommand; - public ICommand ShowReportCommand - { - get - { - if (_showReportCommand == null) - { - _showReportCommand = new LambdaCommand(() => true, () => this.ShowReport()); - } - return _showReportCommand; - } - } - private void ShowReport() { var file = Path.GetTempFileName() + ".html"; @@ -377,7 +340,7 @@ namespace Wabbajack { if (Mode == "Installing") { - var installer = new Installer(_modList, Location, msg => this.LogMsg(msg)); + var installer = new Installer(_modList, Location, msg => LogMsg(msg)); installer.IgnoreMissingFiles = IgnoreMissingFiles; installer.DownloadFolder = DownloadLocation; var th = new Thread(() => @@ -408,9 +371,7 @@ namespace Wabbajack { compiler.Compile(); if (compiler.ModList != null && compiler.ModList.ReportHTML != null) - { HTMLReport = compiler.ModList.ReportHTML; - } } catch (Exception ex) { @@ -424,5 +385,12 @@ namespace Wabbajack th.Start(); } } + + public class CPUStatus + { + public int Progress { get; internal set; } + public string Msg { get; internal set; } + public int ID { get; internal set; } + } } } \ No newline at end of file diff --git a/Wabbajack/AutoScrollBehavior.cs b/Wabbajack/AutoScrollBehavior.cs index 1c46151f..13c2bba3 100644 --- a/Wabbajack/AutoScrollBehavior.cs +++ b/Wabbajack/AutoScrollBehavior.cs @@ -2,28 +2,15 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace Wabbajack { - class AutoScrollBehavior + internal class AutoScrollBehavior { - static readonly Dictionary Associations = - new Dictionary(); - - public static bool GetScrollOnNewItem(DependencyObject obj) - { - return (bool)obj.GetValue(ScrollOnNewItemProperty); - } - - public static void SetScrollOnNewItem(DependencyObject obj, bool value) - { - obj.SetValue(ScrollOnNewItemProperty, value); - } + private static readonly Dictionary Associations = + new Dictionary(); public static readonly DependencyProperty ScrollOnNewItemProperty = DependencyProperty.RegisterAttached( @@ -32,13 +19,23 @@ namespace Wabbajack typeof(AutoScrollBehavior), new UIPropertyMetadata(false, OnScrollOnNewItemChanged)); + public static bool GetScrollOnNewItem(DependencyObject obj) + { + return (bool) obj.GetValue(ScrollOnNewItemProperty); + } + + public static void SetScrollOnNewItem(DependencyObject obj, bool value) + { + obj.SetValue(ScrollOnNewItemProperty, value); + } + public static void OnScrollOnNewItemChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { var listBox = d as ListBox; if (listBox == null) return; - bool oldValue = (bool)e.OldValue, newValue = (bool)e.NewValue; + bool oldValue = (bool) e.OldValue, newValue = (bool) e.NewValue; if (newValue == oldValue) return; if (newValue) { @@ -60,51 +57,39 @@ namespace Wabbajack private static void ListBox_ItemsSourceChanged(object sender, EventArgs e) { - var listBox = (ListBox)sender; + var listBox = (ListBox) sender; if (Associations.ContainsKey(listBox)) Associations[listBox].Dispose(); Associations[listBox] = new Capture(listBox); } - static void ListBox_Unloaded(object sender, RoutedEventArgs e) + private static void ListBox_Unloaded(object sender, RoutedEventArgs e) { - var listBox = (ListBox)sender; + var listBox = (ListBox) sender; if (Associations.ContainsKey(listBox)) Associations[listBox].Dispose(); listBox.Unloaded -= ListBox_Unloaded; } - static void ListBox_Loaded(object sender, RoutedEventArgs e) + private static void ListBox_Loaded(object sender, RoutedEventArgs e) { - var listBox = (ListBox)sender; + var listBox = (ListBox) sender; var incc = listBox.Items as INotifyCollectionChanged; if (incc == null) return; listBox.Loaded -= ListBox_Loaded; Associations[listBox] = new Capture(listBox); } - class Capture : IDisposable + private class Capture : IDisposable { - private readonly ListBox listBox; private readonly INotifyCollectionChanged incc; + private readonly ListBox listBox; public Capture(ListBox listBox) { this.listBox = listBox; incc = listBox.ItemsSource as INotifyCollectionChanged; - if (incc != null) - { - incc.CollectionChanged += incc_CollectionChanged; - } - } - - void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.Action == NotifyCollectionChangedAction.Add) - { - listBox.ScrollIntoView(e.NewItems[0]); - listBox.SelectedItem = e.NewItems[0]; - } + if (incc != null) incc.CollectionChanged += incc_CollectionChanged; } public void Dispose() @@ -112,6 +97,15 @@ namespace Wabbajack if (incc != null) incc.CollectionChanged -= incc_CollectionChanged; } + + private void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + listBox.ScrollIntoView(e.NewItems[0]); + listBox.SelectedItem = e.NewItems[0]; + } + } } } -} +} \ No newline at end of file diff --git a/Wabbajack/Compiler.cs b/Wabbajack/Compiler.cs index 7398ac75..b760ad80 100644 --- a/Wabbajack/Compiler.cs +++ b/Wabbajack/Compiler.cs @@ -11,12 +11,9 @@ using System.Text.RegularExpressions; using System.Web; using CommonMark; using Compression.BSA; -using ICSharpCode.SharpZipLib.BZip2; using K4os.Compression.LZ4; using K4os.Compression.LZ4.Streams; -using Microsoft.SqlServer.Server; using Newtonsoft.Json; -using SharpCompress.Compressors.BZip2; using VFS; using Wabbajack.Common; using static Wabbajack.NexusAPI; @@ -567,7 +564,11 @@ namespace Wabbajack private Func IgnoreWabbajackInstallCruft() { - var cruft_files = new HashSet {"7z.dll", "7z.exe", "vfs_staged_files\\", "nexus.key_cache", "patch_cache\\", Consts.NexusCacheDirectory + "\\"}; + var cruft_files = new HashSet + { + "7z.dll", "7z.exe", "vfs_staged_files\\", "nexus.key_cache", "patch_cache\\", + Consts.NexusCacheDirectory + "\\" + }; return source => { if (!cruft_files.Any(f => source.Path.StartsWith(f))) return null; @@ -882,8 +883,8 @@ namespace Wabbajack } /// - /// This matches files based purely on filename, and then creates a binary patch. - /// In practice this is fine, because a single file tends to only come from one archive. + /// This matches files based purely on filename, and then creates a binary patch. + /// In practice this is fine, because a single file tends to only come from one archive. /// /// private Func IncludePatches() @@ -906,7 +907,6 @@ namespace Wabbajack e.Hash = source.File.Hash; Utils.TryGetPatch(found.Hash, source.File.Hash, out e.Patch); return e; - }; } @@ -1120,8 +1120,11 @@ namespace Wabbajack //bw.Write(data); var formatter = new BinaryFormatter(); - using (var compressed = LZ4Stream.Encode(bw.BaseStream, new LZ4EncoderSettings { CompressionLevel = LZ4Level.L10_OPT }, true)) + using (var compressed = LZ4Stream.Encode(bw.BaseStream, + new LZ4EncoderSettings {CompressionLevel = LZ4Level.L10_OPT}, true)) + { formatter.Serialize(compressed, ModList); + } bw.Write(orig_pos); bw.Write(Encoding.ASCII.GetBytes(Consts.ModPackMagic)); diff --git a/Wabbajack/Data.cs b/Wabbajack/Data.cs index f60e660a..2d16b84d 100644 --- a/Wabbajack/Data.cs +++ b/Wabbajack/Data.cs @@ -1,37 +1,24 @@ -using Newtonsoft.Json; -using System; +using System; using System.Collections.Generic; +using Newtonsoft.Json; using VFS; namespace Wabbajack { - public class RawSourceFile + public class RawSourceFile { + public string Path; + public RawSourceFile(VirtualFile file) { File = file; } - public string AbsolutePath - { - get - { - return File.StagedPath; - } - } - - public string Path; + public string AbsolutePath => File.StagedPath; - public VirtualFile File { get; private set; } + public VirtualFile File { get; } - public string Hash - { - get - { - return File.Hash; - } - - } + public string Hash => File.Hash; public T EvolveTo() where T : Directive, new() { @@ -45,22 +32,22 @@ namespace Wabbajack public class ModList { /// - /// Name of the ModList - /// - public string Name; - - /// - /// Install directives - /// - public List Directives; - - /// - /// Archives required by this modlist + /// Archives required by this modlist /// public List Archives; /// - /// Content Report in HTML form + /// Install directives + /// + public List Directives; + + /// + /// Name of the ModList + /// + public string Name; + + /// + /// Content Report in HTML form /// public string ReportHTML; } @@ -69,7 +56,7 @@ namespace Wabbajack public class Directive { /// - /// location the file will be copied to, relative to the install path. + /// location the file will be copied to, relative to the install path. /// public string To; } @@ -83,14 +70,13 @@ namespace Wabbajack [Serializable] public class NoMatch : IgnoredDirectly { - } [Serializable] public class InlineFile : Directive { /// - /// Data that will be written as-is to the destination location; + /// Data that will be written as-is to the destination location; /// public string SourceData; } @@ -102,7 +88,7 @@ namespace Wabbajack } /// - /// A file that has the game and MO2 folders remapped on installation + /// A file that has the game and MO2 folders remapped on installation /// [Serializable] public class RemappedInlineFile : InlineFile @@ -112,25 +98,21 @@ namespace Wabbajack [Serializable] public class FromArchive : Directive { + private string _fullPath; + /// - /// MurMur3 hash of the archive this file comes from + /// MurMur3 hash of the archive this file comes from /// public string[] ArchiveHashPath; - [JsonIgnore] - [NonSerialized] - public VirtualFile FromFile; - - private string _fullPath = null; + [JsonIgnore] [NonSerialized] public VirtualFile FromFile; [JsonIgnore] public string FullPath { get { - if (_fullPath == null) { - _fullPath = String.Join("|", ArchiveHashPath); - } + if (_fullPath == null) _fullPath = string.Join("|", ArchiveHashPath); return _fullPath; } } @@ -139,11 +121,11 @@ namespace Wabbajack [Serializable] public class CreateBSA : Directive { - public string TempID; public string IsCompressed; - public uint Version; - public uint Type; public bool ShareData; + public string TempID; + public uint Type; + public uint Version; public uint FileFlags { get; set; } public bool Compress { get; set; } @@ -153,42 +135,44 @@ namespace Wabbajack [Serializable] public class PatchedFromArchive : FromArchive { + public string Hash; + /// - /// The file to apply to the source file to patch it + /// The file to apply to the source file to patch it /// public byte[] Patch; - public string Hash; } [Serializable] public class Archive { /// - /// MurMur3 Hash of the archive + /// MurMur3 Hash of the archive /// public string Hash; - /// - /// Human friendly name of this archive - /// - public string Name; /// Meta INI for the downloaded archive /// public string Meta; + /// + /// Human friendly name of this archive + /// + public string Name; + public long Size; } [Serializable] public class NexusMod : Archive { + public string Author; + public string FileID; public string GameName; public string ModID; - public string FileID; - public string Version; - public string UploaderProfile; public string UploadedBy; - public string Author; + public string UploaderProfile; + public string Version; } [Serializable] @@ -198,19 +182,19 @@ namespace Wabbajack } /// - /// URL that can be downloaded directly without any additional options + /// URL that can be downloaded directly without any additional options /// [Serializable] public class DirectURLArchive : Archive { - public string URL; - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public List Headers; + + public string URL; } /// - /// A URL that cannot be downloaded automatically and has to be downloaded by hand + /// A URL that cannot be downloaded automatically and has to be downloaded by hand /// [Serializable] public class ManualURLArchive : Archive @@ -219,7 +203,7 @@ namespace Wabbajack } /// - /// An archive that requires additional HTTP headers. + /// An archive that requires additional HTTP headers. /// [Serializable] public class DirectURLArchiveEx : DirectURLArchive @@ -228,7 +212,7 @@ namespace Wabbajack } /// - /// Archive that comes from MEGA + /// Archive that comes from MEGA /// [Serializable] public class MEGAArchive : DirectURLArchive @@ -236,7 +220,7 @@ namespace Wabbajack } /// - /// Archive that comes from MODDB + /// Archive that comes from MODDB /// [Serializable] public class MODDBArchive : DirectURLArchive @@ -244,7 +228,7 @@ namespace Wabbajack } /// - /// Archive that comes from MediaFire + /// Archive that comes from MediaFire /// [Serializable] public class MediaFireArchive : DirectURLArchive @@ -255,27 +239,29 @@ namespace Wabbajack public class IndexedArchive { public dynamic IniData; - public string Name; public string Meta; + public string Name; public VirtualFile File { get; internal set; } } /// - /// A archive entry + /// A archive entry /// [Serializable] public class IndexedEntry { /// - /// Path in the archive to this file - /// - public string Path; - /// - /// MurMur3 hash of this file + /// MurMur3 hash of this file /// public string Hash; + /// - /// Size of the file (uncompressed) + /// Path in the archive to this file + /// + public string Path; + + /// + /// Size of the file (uncompressed) /// public long Size; } @@ -287,14 +273,14 @@ namespace Wabbajack } /// - /// Data found inside a BSA file in an archive + /// Data found inside a BSA file in an archive /// [Serializable] public class BSAIndexedEntry : IndexedEntry { /// - /// MurMur3 hash of the BSA this file comes from + /// MurMur3 hash of the BSA this file comes from /// public string BSAHash; } -} +} \ No newline at end of file diff --git a/Wabbajack/Installer.cs b/Wabbajack/Installer.cs index 4b3ee9e3..669906a7 100644 --- a/Wabbajack/Installer.cs +++ b/Wabbajack/Installer.cs @@ -1,7 +1,4 @@ -using CG.Web.MegaApiClient; -using Compression.BSA; -using Ookii.Dialogs.Wpf; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,10 +8,11 @@ using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using System.Windows; -using K4os.Compression.LZ4.Encoders; +using CG.Web.MegaApiClient; +using Compression.BSA; using K4os.Compression.LZ4.Streams; +using Ookii.Dialogs.Wpf; using VFS; using Wabbajack.Common; @@ -24,14 +22,6 @@ namespace Wabbajack { private string _downloadsFolder; - public VirtualFileSystem VFS - { - get - { - return VirtualFileSystem.VFS; - } - } - public Installer(ModList mod_list, string output_folder, Action log_fn) { Outputfolder = output_folder; @@ -39,12 +29,16 @@ namespace Wabbajack Log_Fn = log_fn; } + public VirtualFileSystem VFS => VirtualFileSystem.VFS; + public string Outputfolder { get; } + public string DownloadFolder { get => _downloadsFolder ?? Path.Combine(Outputfolder, "downloads"); set => _downloadsFolder = value; } + public ModList ModList { get; } public Action Log_Fn { get; } public Dictionary HashedArchives { get; private set; } @@ -56,50 +50,49 @@ namespace Wabbajack public void Info(string msg, params object[] args) { if (args.Length > 0) - msg = String.Format(msg, args); + msg = string.Format(msg, args); Log_Fn(msg); } public void Status(string msg, params object[] args) { if (args.Length > 0) - msg = String.Format(msg, args); + msg = string.Format(msg, args); WorkQueue.Report(msg, 0); } public void Status(int progress, string msg, params object[] args) { if (args.Length > 0) - msg = String.Format(msg, args); + msg = string.Format(msg, args); WorkQueue.Report(msg, progress); } + private void Error(string msg, params object[] args) { if (args.Length > 0) - msg = String.Format(msg, args); + msg = string.Format(msg, args); Log_Fn(msg); throw new Exception(msg); } public void Install() { - Directory.CreateDirectory(Outputfolder); Directory.CreateDirectory(DownloadFolder); if (Directory.Exists(Path.Combine(Outputfolder, "mods"))) { - if (MessageBox.Show("There already appears to be a Mod Organize 2 install in this folder, are you sure you wish to continue" + - " with installation? If you do, you may render both your existing install and the new modlist inoperable.", - "Existing MO2 installation in install folder", - MessageBoxButton.YesNo, - MessageBoxImage.Exclamation) == MessageBoxResult.No) + if (MessageBox.Show( + "There already appears to be a Mod Organize 2 install in this folder, are you sure you wish to continue" + + " with installation? If you do, you may render both your existing install and the new modlist inoperable.", + "Existing MO2 installation in install folder", + MessageBoxButton.YesNo, + MessageBoxImage.Exclamation) == MessageBoxResult.No) Utils.Log("Existing installation at the request of the user, existing mods folder found."); - return; - - + return; } - + if (ModList.Directives.OfType().FirstOrDefault() != null || ModList.Directives.OfType().FirstOrDefault() != null) { @@ -125,13 +118,9 @@ namespace Wabbajack foreach (var a in missing) Info("Unable to download {0}", a.Name); if (IgnoreMissingFiles) - { Info("Missing some archives, but continuing anyways at the request of the user"); - } else - { Error("Cannot continue, was unable to download one or more archives"); - } } PrimeVFS(); @@ -179,7 +168,6 @@ namespace Wabbajack Utils.Log($"Endorsed {mod.GameName} - {mod.ModID} - Result: {er.message}"); }); Info("Done! You may now exit the application!"); - } private bool LocateGameFolder() @@ -192,38 +180,36 @@ namespace Wabbajack GameFolder = vf.SelectedPath; return true; } + return false; } /// - /// 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. + /// 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. /// private void PrimeVFS() { - HashedArchives.Do(a => VFS.AddKnown(new VirtualFile() + HashedArchives.Do(a => VFS.AddKnown(new VirtualFile { - Paths = new string[] { a.Value }, + Paths = new[] {a.Value}, Hash = a.Key })); VFS.RefreshIndexes(); ModList.Directives - .OfType() - .Do(f => - { - var updated_path = new string[f.ArchiveHashPath.Length]; - f.ArchiveHashPath.CopyTo(updated_path, 0); - updated_path[0] = VFS.HashIndex[updated_path[0]].Where(e => e.IsConcrete).First().FullPath; - VFS.AddKnown(new VirtualFile() { Paths = updated_path }); - }); + .OfType() + .Do(f => + { + var updated_path = new string[f.ArchiveHashPath.Length]; + f.ArchiveHashPath.CopyTo(updated_path, 0); + updated_path[0] = VFS.HashIndex[updated_path[0]].Where(e => e.IsConcrete).First().FullPath; + VFS.AddKnown(new VirtualFile {Paths = updated_path}); + }); VFS.BackfillMissing(); - - - } private void BuildBSAs() @@ -236,29 +222,29 @@ namespace Wabbajack Status($"Building {bsa.To}"); var source_dir = Path.Combine(Outputfolder, Consts.BSACreationDir, bsa.TempID); var source_files = Directory.EnumerateFiles(source_dir, "*", SearchOption.AllDirectories) - .Select(e => e.Substring(source_dir.Length + 1)) - .ToList(); + .Select(e => e.Substring(source_dir.Length + 1)) + .ToList(); - if(source_files.Count > 0) - using (var a = new BSABuilder()) - { - - //a.Create(Path.Combine(Outputfolder, bsa.To), (bsa_archive_type_t)bsa.Type, entries); - a.HeaderType = (VersionType)bsa.Type; - a.FileFlags = (FileFlags)bsa.FileFlags; - a.ArchiveFlags = (ArchiveFlags)bsa.ArchiveFlags; - - source_files.PMap(f => + if (source_files.Count > 0) + using (var a = new BSABuilder()) { - Status($"Adding {f} to BSA"); - using (var fs = File.OpenRead(Path.Combine(source_dir, f))) - a.AddFile(f, fs); - }); + //a.Create(Path.Combine(Outputfolder, bsa.To), (bsa_archive_type_t)bsa.Type, entries); + a.HeaderType = (VersionType) bsa.Type; + a.FileFlags = (FileFlags) bsa.FileFlags; + a.ArchiveFlags = (ArchiveFlags) bsa.ArchiveFlags; - Info($"Writing {bsa.To}"); - a.Build(Path.Combine(Outputfolder, bsa.To)); + source_files.PMap(f => + { + Status($"Adding {f} to BSA"); + using (var fs = File.OpenRead(Path.Combine(source_dir, f))) + { + a.AddFile(f, fs); + } + }); - } + Info($"Writing {bsa.To}"); + a.Build(Path.Combine(Outputfolder, bsa.To)); + } }); if (Directory.Exists(Consts.BSACreationDir)) @@ -266,32 +252,25 @@ namespace Wabbajack Info($"Removing temp folder {Consts.BSACreationDir}"); Directory.Delete(Path.Combine(Outputfolder, Consts.BSACreationDir), true); } - } private void InstallIncludedFiles() { Info("Writing inline files"); ModList.Directives - .OfType() - .PMap(directive => - { - Status("Writing included file {0}", directive.To); - var out_path = Path.Combine(Outputfolder, directive.To); - if (File.Exists(out_path)) File.Delete(out_path); - if (directive is RemappedInlineFile) - { - WriteRemappedFile((RemappedInlineFile)directive); - } - else if (directive is CleanedESM) - { - GenerateCleanedESM((CleanedESM)directive); - } - else - { - File.WriteAllBytes(out_path, directive.SourceData.FromBase64()); - } - }); + .OfType() + .PMap(directive => + { + Status("Writing included file {0}", directive.To); + var out_path = Path.Combine(Outputfolder, directive.To); + if (File.Exists(out_path)) File.Delete(out_path); + if (directive is RemappedInlineFile) + WriteRemappedFile((RemappedInlineFile) directive); + else if (directive is CleanedESM) + GenerateCleanedESM((CleanedESM) directive); + else + File.WriteAllBytes(out_path, directive.SourceData.FromBase64()); + }); } private void GenerateCleanedESM(CleanedESM directive) @@ -299,19 +278,18 @@ namespace Wabbajack var filename = Path.GetFileName(directive.To); var game_file = Path.Combine(GameFolder, "Data", filename); Info($"Generating cleaned ESM for {filename}"); - if (!File.Exists(game_file)) - { - throw new InvalidDataException($"Missing {filename} at {game_file}"); - } + if (!File.Exists(game_file)) throw new InvalidDataException($"Missing {filename} at {game_file}"); Status($"Hashing game version of {filename}"); - var sha = Utils.FileSHA256(game_file); + var sha = game_file.FileSHA256(); if (sha != directive.SourceESMHash) - throw new InvalidDataException($"Cannot patch {filename} from the game folder hashes don't match have you already cleaned the file?"); + throw new InvalidDataException( + $"Cannot patch {filename} from the game folder hashes don't match have you already cleaned the file?"); var patch_data = directive.SourceData.FromBase64(); var to_file = Path.Combine(Outputfolder, directive.To); Status($"Patching {filename}"); - using (var output = File.OpenWrite(to_file)) { + using (var output = File.OpenWrite(to_file)) + { BSDiff.Apply(File.OpenRead(game_file), () => new MemoryStream(patch_data), output); } } @@ -339,12 +317,13 @@ namespace Wabbajack { Info("Building Folder Structure"); ModList.Directives - .Select(d => Path.Combine(Outputfolder, Path.GetDirectoryName(d.To))) - .ToHashSet() - .Do(f => { - if (Directory.Exists(f)) return; - Directory.CreateDirectory(f); - }); + .Select(d => Path.Combine(Outputfolder, Path.GetDirectoryName(d.To))) + .ToHashSet() + .Do(f => + { + if (Directory.Exists(f)) return; + Directory.CreateDirectory(f); + }); } private void InstallArchives() @@ -352,17 +331,16 @@ namespace Wabbajack Info("Installing Archives"); Info("Grouping Install Files"); var grouped = ModList.Directives - .OfType() - .GroupBy(e => e.ArchiveHashPath[0]) - .ToDictionary(k => k.Key); + .OfType() + .GroupBy(e => e.ArchiveHashPath[0]) + .ToDictionary(k => k.Key); var archives = ModList.Archives - .Select(a => new { Archive = a, AbsolutePath = HashedArchives.GetOrDefault(a.Hash) }) - .Where(a => a.AbsolutePath != null) - .ToList(); + .Select(a => new {Archive = a, AbsolutePath = HashedArchives.GetOrDefault(a.Hash)}) + .Where(a => a.AbsolutePath != null) + .ToList(); Info("Installing Archives"); archives.PMap(a => InstallArchive(a.Archive, a.AbsolutePath, grouped[a.Archive.Hash])); - } private void InstallArchive(Archive archive, string absolutePath, IGrouping grouping) @@ -383,7 +361,7 @@ namespace Wabbajack vfiles.DoIndexed((idx, file) => { - Utils.Status($"Installing files", idx * 100 / vfiles.Count); + Utils.Status("Installing files", idx * 100 / vfiles.Count); File.Copy(file.FromFile.StagedPath, Path.Combine(Outputfolder, file.To)); }); @@ -392,7 +370,6 @@ namespace Wabbajack // Now patch all the files from this archive foreach (var to_patch in grouping.OfType()) - { using (var patch_stream = new MemoryStream()) { Status("Patching {0}", Path.GetFileName(to_patch.To)); @@ -401,7 +378,7 @@ namespace Wabbajack var patch_data = to_patch.Patch; var to_file = Path.Combine(Outputfolder, to_patch.To); - MemoryStream old_data = new MemoryStream(File.ReadAllBytes(to_file)); + var old_data = new MemoryStream(File.ReadAllBytes(to_file)); // Remove the file we're about to patch File.Delete(to_file); @@ -413,12 +390,10 @@ namespace Wabbajack } Status($"Verifying Patch {Path.GetFileName(to_patch.To)}"); - var result_sha = Utils.FileSHA256(to_file); + var result_sha = to_file.FileSHA256(); if (result_sha != to_patch.Hash) throw new InvalidDataException($"Invalid Hash for {to_patch.To} after patching"); } - - } } private void DownloadArchives() @@ -431,16 +406,17 @@ namespace Wabbajack var user_status = NexusAPI.GetUserStatus(NexusAPIKey); - if (!user_status.is_premium) { - Info($"Automated installs with Wabbajack requires a premium nexus account. {user_status.name} is not a premium account"); + if (!user_status.is_premium) + { + Info( + $"Automated installs with Wabbajack requires a premium nexus account. {user_status.name} is not a premium account"); return; } DownloadMissingArchives(missing); - return; } - private void DownloadMissingArchives(List missing, bool download=true) + private void DownloadMissingArchives(List missing, bool download = true) { missing.PMap(archive => { @@ -465,7 +441,7 @@ namespace Wabbajack string url; try { - url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey, !download); + url = NexusAPI.GetNexusDownloadLink(a, NexusAPIKey, !download); if (!download) return true; } catch (Exception ex) @@ -473,6 +449,7 @@ namespace Wabbajack Info($"{a.Name} - Error Getting Nexus Download URL - {ex.Message}"); return false; } + Info($"Downloading Nexus Archive - {archive.Name} - {a.GameName} - {a.ModID} - {a.FileID}"); DownloadURLDirect(archive, url); return true; @@ -484,7 +461,7 @@ namespace Wabbajack return DownloadModDBArchive(archive, (archive as MODDBArchive).URL, download); case MediaFireArchive a: return false; - //return DownloadMediaFireArchive(archive, a.URL, download); + //return DownloadMediaFireArchive(archive, a.URL, download); case DirectURLArchive a: return DownloadURLDirect(archive, a.URL, headers: a.Headers, download: download); } @@ -495,6 +472,7 @@ namespace Wabbajack Utils.Log(ex.ToString()); return false; } + return false; } @@ -529,7 +507,8 @@ namespace Wabbajack var result = client.GetStringSync(initial_url); var regex = new Regex("(?<=/uc\\?export=download&confirm=).*(?=;id=)"); var confirm = regex.Match(result); - return DownloadURLDirect(a, $"https://drive.google.com/uc?export=download&confirm={confirm}&id={a.Id}", client, download: download); + return DownloadURLDirect(a, $"https://drive.google.com/uc?export=download&confirm={confirm}&id={a.Id}", + client, download); } private bool DownloadModDBArchive(Archive archive, string url, bool download) @@ -541,7 +520,8 @@ namespace Wabbajack return DownloadURLDirect(archive, match.Value, download: download); } - private bool DownloadURLDirect(Archive archive, string url, HttpClient client = null, bool download = true, List headers = null) + private bool DownloadURLDirect(Archive archive, string url, HttpClient client = null, bool download = true, + List headers = null) { try { @@ -551,7 +531,7 @@ namespace Wabbajack client.DefaultRequestHeaders.Add("User-Agent", Consts.UserAgent); } - if (headers != null) { + if (headers != null) foreach (var header in headers) { var idx = header.IndexOf(':'); @@ -559,10 +539,9 @@ namespace Wabbajack var v = header.Substring(idx + 1); client.DefaultRequestHeaders.Add(k, v); } - } long total_read = 0; - int buffer_size = 1024 * 32; + var buffer_size = 1024 * 32; var response = client.GetSync(url); var stream = response.Content.ReadAsStreamAsync(); @@ -572,24 +551,26 @@ namespace Wabbajack } catch (Exception ex) { - }; + } + + ; if (stream.IsFaulted) { - Info($"While downloading {url} - {Utils.ExceptionToString(stream.Exception)}"); + Info($"While downloading {url} - {stream.Exception.ExceptionToString()}"); return false; } if (!download) return true; - string header_var = "1"; + var header_var = "1"; if (response.Content.Headers.Contains("Content-Length")) header_var = response.Content.Headers.GetValues("Content-Length").FirstOrDefault(); - long content_size = header_var != null ? long.Parse(header_var) : 1; + var content_size = header_var != null ? long.Parse(header_var) : 1; var output_path = Path.Combine(DownloadFolder, archive.Name); -; + ; using (var webs = stream.Result) using (var fs = File.OpenWrite(output_path)) @@ -599,13 +580,13 @@ namespace Wabbajack { var read = webs.Read(buffer, 0, buffer_size); if (read == 0) break; - Status((int)(total_read * 100 / content_size), "Downloading {0}", archive.Name); + Status((int) (total_read * 100 / content_size), "Downloading {0}", archive.Name); fs.Write(buffer, 0, read); total_read += read; - } } + Status("Hashing {0}", archive.Name); HashArchive(output_path); return true; @@ -620,12 +601,12 @@ namespace Wabbajack private void HashArchives() { HashedArchives = Directory.EnumerateFiles(DownloadFolder) - .Where(e => !e.EndsWith(".sha")) - .PMap(e => (HashArchive(e), e)) - .OrderByDescending(e => File.GetLastWriteTime(e.Item2)) - .GroupBy(e => e.Item1) - .Select(e => e.First()) - .ToDictionary(e => e.Item1, e => e.Item2); + .Where(e => !e.EndsWith(".sha")) + .PMap(e => (HashArchive(e), e)) + .OrderByDescending(e => File.GetLastWriteTime(e.Item2)) + .GroupBy(e => e.Item1) + .Select(e => e.First()) + .ToDictionary(e => e.Item1, e => e.Item2); } private string HashArchive(string e) @@ -635,7 +616,7 @@ namespace Wabbajack return File.ReadAllText(cache); Status("Hashing {0}", Path.GetFileName(e)); - File.WriteAllText(cache, Utils.FileSHA256(e)); + File.WriteAllText(cache, e.FileSHA256()); return HashArchive(e); } @@ -650,11 +631,7 @@ namespace Wabbajack { var bytes = br.ReadBytes(magic_bytes.Length); var magic = Encoding.ASCII.GetString(bytes); - if (magic != Consts.ModPackMagic) - { - - return null; - } + if (magic != Consts.ModPackMagic) return null; s.Position = s.Length - magic_bytes.Length - 8; var start_pos = br.ReadInt64(); @@ -665,11 +642,10 @@ namespace Wabbajack IFormatter formatter = new BinaryFormatter(); var list = formatter.Deserialize(dc); Utils.Log("Modlist loaded."); - return (ModList)list; + return (ModList) list; } } } } - } -} +} \ No newline at end of file diff --git a/Wabbajack/LambdaCommand.cs b/Wabbajack/LambdaCommand.cs index 1da24ef0..cd341bfa 100644 --- a/Wabbajack/LambdaCommand.cs +++ b/Wabbajack/LambdaCommand.cs @@ -5,10 +5,8 @@ namespace Wabbajack { internal class LambdaCommand : ICommand { - private Action _execute; - private Func _canExecute; - - public event EventHandler CanExecuteChanged; + private readonly Func _canExecute; + private readonly Action _execute; public LambdaCommand(Func canExecute, Action execute) { @@ -16,6 +14,8 @@ namespace Wabbajack _canExecute = canExecute; } + public event EventHandler CanExecuteChanged; + public bool CanExecute(object parameter) { return _canExecute(); diff --git a/Wabbajack/MainWindow.xaml b/Wabbajack/MainWindow.xaml index 0eab68aa..6aab176d 100644 --- a/Wabbajack/MainWindow.xaml +++ b/Wabbajack/MainWindow.xaml @@ -6,79 +6,81 @@ xmlns:local="clr-namespace:Wabbajack" mc:Ignorable="d" Title="Wabbajack" Height="800" Width="800" - Style="{StaticResource {x:Type Window}}" Icon="square_transparent_icon.ico" WindowStyle="ToolWindow" Closing="Window_Closing"> + Style="{StaticResource {x:Type Window}}" Icon="square_transparent_icon.ico" WindowStyle="ToolWindow" + Closing="Window_Closing"> - - - - - - - - - + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - - - - +