From d6123a7fb274047fe6302f9dddfd8587b216f201 Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Sun, 22 Mar 2020 15:55:31 -0600 Subject: [PATCH] MessagePack, and basic sanity test passes --- Wabbajack.Common/Http/Client.cs | 6 + Wabbajack.Common/MessagePack.cs | 91 ++++++++ Wabbajack.Common/Utils.cs | 55 +---- Wabbajack.Common/Wabbajack.Common.csproj | 1 - Wabbajack.Lib/ACompiler.cs | 6 +- Wabbajack.Lib/AInstaller.cs | 2 +- Wabbajack.Lib/CerasConfig.cs | 39 ---- Wabbajack.Lib/Data.cs | 200 ++++++++++-------- .../Downloaders/AbstractDownloadState.cs | 21 +- .../Downloaders/AbstractIPS4Downloader.cs | 2 +- Wabbajack.Lib/Downloaders/HTTPDownloader.cs | 6 - Wabbajack.Lib/Downloaders/ManualDownloader.cs | 7 +- Wabbajack.Lib/Downloaders/NexusDownloader.cs | 7 +- Wabbajack.Lib/MO2Compiler.cs | 5 +- Wabbajack.Lib/VortexCompiler.cs | 2 +- Wabbajack.Lib/Wabbajack.Lib.csproj | 6 +- .../View Models/Gallery/ModListMetadataVM.cs | 2 +- .../View Models/Installers/InstallerVM.cs | 2 +- Wabbajack/View Models/ModListVM.cs | 4 +- Wabbajack/View Models/SlideShow.cs | 2 +- Wabbajack/Views/LinksView.xaml.cs | 9 +- 21 files changed, 267 insertions(+), 208 deletions(-) create mode 100644 Wabbajack.Common/MessagePack.cs delete mode 100644 Wabbajack.Lib/CerasConfig.cs diff --git a/Wabbajack.Common/Http/Client.cs b/Wabbajack.Common/Http/Client.cs index c54073e4..c886c913 100644 --- a/Wabbajack.Common/Http/Client.cs +++ b/Wabbajack.Common/Http/Client.cs @@ -37,6 +37,12 @@ namespace Wabbajack.Common.Http return await SendStringAsync(request); } + public async Task GetStringAsync(Uri url) + { + var request = new HttpRequestMessage(HttpMethod.Get, url); + return await SendStringAsync(request); + } + public async Task DeleteStringAsync(string url) { var request = new HttpRequestMessage(HttpMethod.Delete, url); diff --git a/Wabbajack.Common/MessagePack.cs b/Wabbajack.Common/MessagePack.cs new file mode 100644 index 00000000..f3405d61 --- /dev/null +++ b/Wabbajack.Common/MessagePack.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using MessagePack; +using MessagePack.Formatters; +using MessagePack.Resolvers; + +namespace Wabbajack.Common +{ + public partial class Utils + { + private static MessagePackSerializerOptions _messagePackOptions; + private static IFormatterResolver _resolver; + + private static void MessagePackInit() + { + _resolver = CompositeResolver.Create( + new List{new HashFormatter()}, + new List {StandardResolver.Instance} + ); + _messagePackOptions = MessagePackSerializerOptions.Standard + .WithResolver(_resolver) + .WithCompression(MessagePackCompression.Lz4BlockArray); + + } + + /// + /// Writes a object to this stream using MessagePack + /// + /// + /// + /// + public static async Task WriteAsMessagePackAsync(this Stream stream, T obj) + { + await MessagePackSerializer.SerializeAsync(stream, obj, _messagePackOptions); + } + + /// + /// Writes a object to this stream using MessagePack + /// + /// + /// + /// + public static void WriteAsMessagePack(this Stream stream, T obj) + { + MessagePackSerializer.Serialize(stream, obj, _messagePackOptions); + } + + /// + /// Reads a object from this stream using MessagePack + /// + /// + /// + /// + public static async Task ReadAsMessagePackAsync(this Stream stream) + { + return await MessagePackSerializer.DeserializeAsync(stream, _messagePackOptions); + } + + + /// + /// Reads a object from this stream using MessagePack + /// + /// + /// + /// + public static T ReadAsMessagePack(this Stream stream) + { + return MessagePackSerializer.Deserialize(stream, _messagePackOptions); + } + + + } + + #region Formatters + + public class HashFormatter : IMessagePackFormatter + { + public void Serialize(ref MessagePackWriter writer, Hash value, MessagePackSerializerOptions options) + { + writer.WriteUInt64((ulong)value); + } + + public Hash Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + return new Hash(reader.ReadUInt64()); + } + } + + #endregion +} diff --git a/Wabbajack.Common/Utils.cs b/Wabbajack.Common/Utils.cs index c8887062..262b7c83 100644 --- a/Wabbajack.Common/Utils.cs +++ b/Wabbajack.Common/Utils.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Reflection; @@ -16,7 +15,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; -using Ceras; using ICSharpCode.SharpZipLib.BZip2; using IniParser; using Newtonsoft.Json; @@ -52,6 +50,8 @@ namespace Wabbajack.Common static Utils() { + MessagePackInit(); + if (!Directory.Exists(Consts.LocalAppDataPath)) Directory.CreateDirectory(Consts.LocalAppDataPath); @@ -343,43 +343,6 @@ namespace Wabbajack.Common return new DynamicIniData(new FileIniDataParser().ReadData(new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(file))))); } - public static void ToCERAS(this T obj, string filename, SerializerConfig config) - { - byte[] final; - final = ToCERAS(obj, config); - File.WriteAllBytes(filename, final); - } - - public static byte[] ToCERAS(this T obj, SerializerConfig config) - { - byte[] final; - var ceras = new CerasSerializer(config); - byte[] buffer = null; - ceras.Serialize(obj, ref buffer); - - using (var m1 = new MemoryStream(buffer)) - using (var m2 = new MemoryStream()) - { - BZip2.Compress(m1, m2, false, 9); - m2.Seek(0, SeekOrigin.Begin); - final = m2.ToArray(); - } - return final; - } - - public static T FromCERAS(this Stream data, SerializerConfig config) - { - var ceras = new CerasSerializer(config); - byte[] bytes = data.ReadAll(); - using (var m1 = new MemoryStream(bytes)) - using (var m2 = new MemoryStream()) - { - BZip2.Decompress(m1, m2, false); - m2.Seek(0, SeekOrigin.Begin); - return ceras.Deserialize(m2.ToArray()); - } - } - public static void ToJSON(this T obj, string filename) { if (File.Exists(filename)) @@ -388,18 +351,6 @@ namespace Wabbajack.Common JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto})); } - /* - public static void ToBSON(this T obj, string filename) - { - using (var fo = File.Open(filename, System.IO.FileMode.Create)) - using (var br = new BsonDataWriter(fo)) - { - fo.SetLength(0); - var serializer = JsonSerializer.Create(new JsonSerializerSettings - {TypeNameHandling = TypeNameHandling.Auto}); - serializer.Serialize(br, obj); - } - }*/ public static ulong ToMilliseconds(this DateTime date) { @@ -1179,7 +1130,7 @@ namespace Wabbajack.Common }); } - public static void OpenWebsite(string url) + public static void OpenWebsite(Uri url) { Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {url}") { diff --git a/Wabbajack.Common/Wabbajack.Common.csproj b/Wabbajack.Common/Wabbajack.Common.csproj index 39f626dd..74c4e330 100644 --- a/Wabbajack.Common/Wabbajack.Common.csproj +++ b/Wabbajack.Common/Wabbajack.Common.csproj @@ -26,7 +26,6 @@ - diff --git a/Wabbajack.Lib/ACompiler.cs b/Wabbajack.Lib/ACompiler.cs index cec70db9..81bf812d 100644 --- a/Wabbajack.Lib/ACompiler.cs +++ b/Wabbajack.Lib/ACompiler.cs @@ -9,6 +9,7 @@ using Wabbajack.Common; using Wabbajack.Lib.CompilationSteps; using Wabbajack.Lib.Downloaders; using Wabbajack.Lib.ModListRegistry; +using Wabbajack.Lib.Validation; using Wabbajack.VirtualFileSystem; using Directory = Alphaleonis.Win32.Filesystem.Directory; using File = Alphaleonis.Win32.Filesystem.File; @@ -20,7 +21,7 @@ namespace Wabbajack.Lib { public string ModListName, ModListAuthor, ModListDescription, ModListImage, ModListWebsite, ModListReadme; public bool ReadmeIsWebsite; - public string WabbajackVersion; + protected Version WabbajackVersion; public abstract string VFSCacheName { get; } //protected string VFSCacheName => Path.Combine(Consts.LocalAppDataPath, $"vfs_compile_cache.bin"); @@ -122,7 +123,8 @@ namespace Wabbajack.Lib ModList.ReadmeIsWebsite = ReadmeIsWebsite; - ModList.ToCERAS(Path.Combine(ModListOutputFolder, "modlist"), CerasConfig.Config); + using (var of = File.Create(Path.Combine(ModListOutputFolder, "modlist"))) + of.WriteAsMessagePack(ModList); if (File.Exists(ModListOutputFile)) File.Delete(ModListOutputFile); diff --git a/Wabbajack.Lib/AInstaller.cs b/Wabbajack.Lib/AInstaller.cs index efa6a42b..ee0e4c80 100644 --- a/Wabbajack.Lib/AInstaller.cs +++ b/Wabbajack.Lib/AInstaller.cs @@ -82,7 +82,7 @@ namespace Wabbajack.Lib return e.FromJSON(); } using (var e = entry.Open()) - return e.FromCERAS(CerasConfig.Config); + return e.ReadAsMessagePack(); } } diff --git a/Wabbajack.Lib/CerasConfig.cs b/Wabbajack.Lib/CerasConfig.cs deleted file mode 100644 index 8d1d7450..00000000 --- a/Wabbajack.Lib/CerasConfig.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Ceras; -using Compression.BSA; -using Wabbajack.Common; -using Wabbajack.Lib.Downloaders; -using Wabbajack.VirtualFileSystem; - -namespace Wabbajack.Lib -{ - public class CerasConfig - { - public static readonly SerializerConfig Config; - - static CerasConfig() - { - Config = new SerializerConfig - { - KnownTypes = - { - typeof(ModList), typeof(Game), typeof(Directive), typeof(IgnoredDirectly), - typeof(NoMatch), typeof(InlineFile), typeof(PropertyType), typeof(CleanedESM), - typeof(RemappedInlineFile), typeof(FromArchive), typeof(CreateBSA), typeof(PatchedFromArchive), - typeof(SourcePatch), typeof(MergedPatch), typeof(Archive), typeof(IndexedArchive), typeof(IndexedEntry), - typeof(IndexedArchiveEntry), typeof(BSAIndexedEntry), typeof(VirtualFile), - typeof(ArchiveStateObject), typeof(FileStateObject), typeof(IDownloader), - typeof(IUrlDownloader), typeof(AbstractDownloadState), typeof(ManualDownloader.State), - typeof(DropboxDownloader), typeof(GoogleDriveDownloader.State), typeof(HTTPDownloader.State), - typeof(MegaDownloader.State), typeof(ModDBDownloader.State), typeof(NexusDownloader.State), - typeof(BSAStateObject), typeof(BSAFileStateObject), typeof(BA2StateObject), typeof(BA2DX10EntryState), - typeof(BA2FileEntryState), typeof(MediaFireDownloader.State), typeof(ArchiveMeta), - typeof(PropertyFile), typeof(SteamMeta), typeof(SteamWorkshopDownloader), typeof(SteamWorkshopDownloader.State), - typeof(LoversLabDownloader.State), typeof(GameFileSourceDownloader.State), typeof(VectorPlexusDownloader.State), - typeof(DeadlyStreamDownloader.State), typeof(AFKModsDownloader.State), typeof(TESAllianceDownloader.State), - typeof(TES3ArchiveState), typeof(TES3FileState), typeof(BethesdaNetDownloader.State), typeof(YouTubeDownloader), typeof(IMetaState) - }, - }; - Config.VersionTolerance.Mode = VersionToleranceMode.Standard; - } - } -} diff --git a/Wabbajack.Lib/Data.cs b/Wabbajack.Lib/Data.cs index fbc42d68..a1f750c0 100644 --- a/Wabbajack.Lib/Data.cs +++ b/Wabbajack.Lib/Data.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Ceras; using Compression.BSA; +using MessagePack; using Wabbajack.Common; using Wabbajack.Lib.Downloaders; using Wabbajack.VirtualFileSystem; @@ -38,99 +38,122 @@ namespace Wabbajack.Lib } } + [MessagePackObject] public class ModList { /// /// Archives required by this modlist /// + [Key(0)] public List Archives; - /// - /// The Mod Manager used to create the modlist - /// - public ModManager ModManager; - - /// - /// The game variant to which this game applies - /// - public Game GameType; - - /// - /// The build version of Wabbajack used when compiling the Modlist - /// - public string WabbajackVersion; - - /// - /// Install directives - /// - public List Directives; - - /// - /// Name of the ModList - /// - public string Name; - /// /// Author of the ModList /// + [Key(1)] public string Author; /// /// Description of the ModList /// + [Key(2)] public string Description; + /// + /// Install directives + /// + [Key(3)] + public List Directives; + + /// + /// The game variant to which this game applies + /// + [Key(4)] + public Game GameType; + /// /// Hash of the banner-image /// + [Key(5)] public string Image; /// - /// Website of the ModList + /// The Mod Manager used to create the modlist /// - public string Website; + [Key(6)] + public ModManager ModManager; + + /// + /// Name of the ModList + /// + [Key(7)] + public string Name; /// /// readme path or website /// + [Key(8)] public string Readme; - /// - /// The size of all the archives once they're downloaded - /// - public long DownloadSize => Archives.Sum(a => a.Size); - - /// - /// The size of all the files once they are installed (excluding downloaded archives) - /// - public long InstallSize => Directives.Sum(s => s.Size); - - /// - /// Estimate of the amount of space required in the VFS staging folders during installation - /// - public long ScratchSpaceSize => Archives.OrderByDescending(a => a.Size) - .Take(Environment.ProcessorCount) - .Sum(a => a.Size) * 2; - /// /// Whether readme is a website /// + [Key(9)] public bool ReadmeIsWebsite; + /// + /// The build version of Wabbajack used when compiling the Modlist + /// + [Key(10)] + public Version WabbajackVersion; + + /// + /// Website of the ModList + /// + [Key(11)] + public Uri Website; + + /// + /// The size of all the archives once they're downloaded + /// + [IgnoreMember] + public long DownloadSize => Archives.Sum(a => a.Size); + + /// + /// The size of all the files once they are installed (excluding downloaded archives) + /// + [IgnoreMember] + public long InstallSize => Directives.Sum(s => s.Size); + public ModList Clone() { - return new MemoryStream(this.ToCERAS(CerasConfig.Config)).FromCERAS(CerasConfig.Config); + using var ms = new MemoryStream(); + ms.WriteAsMessagePack(this); + ms.Position = 0; + return ms.ReadAsMessagePack(); } } - public class Directive + [MessagePackObject] + [Union(0, typeof(ArchiveMeta))] + [Union(1, typeof(CreateBSA))] + [Union(2, typeof(FromArchive))] + [Union(3, typeof(MergedPatch))] + [Union(4, typeof(InlineFile))] + [Union(5, typeof(PatchedFromArchive))] + [Union(6, typeof(RemappedInlineFile))] + public abstract class Directive { + [Key(0)] + public Hash Hash { get; set; } + [Key(1)] + public long Size { get; set; } + /// /// location the file will be copied to, relative to the install path. /// - public string To; - public long Size; - public Hash Hash; + [Key(2)] + public string To { get; set; } } public class IgnoredDirectly : Directive @@ -142,17 +165,21 @@ namespace Wabbajack.Lib { } + [MessagePackObject] public class InlineFile : Directive { /// /// Data that will be written as-is to the destination location; /// + [Key(3)] public string SourceDataID; } + [MessagePackObject] public class ArchiveMeta : Directive { - public string SourceDataID; + [Key(3)] + public string SourceDataID { get; set; } } public enum PropertyType { Banner, Readme } @@ -173,82 +200,100 @@ namespace Wabbajack.Lib /// /// A file that has the game and MO2 folders remapped on installation /// + [MessagePackObject] public class RemappedInlineFile : InlineFile { } + [MessagePackObject] public class SteamMeta : ArchiveMeta { - public int ItemID; - /// - /// Size is in bytes - /// - public int Size; + [Key(4)] + public int ItemID { get; set; } } - [MemberConfig(TargetMember.All)] + [MessagePackObject] public class FromArchive : Directive { private string _fullPath; - public string[] ArchiveHashPath; + [Key(3)] + public string[] ArchiveHashPath { get; set; } - [Exclude] - public VirtualFile FromFile; + [IgnoreMember] + public VirtualFile FromFile { get; set; } - [Exclude] + [IgnoreMember] public string FullPath => _fullPath ??= string.Join("|", ArchiveHashPath); } + [MessagePackObject] public class CreateBSA : Directive { - public string TempID; - public uint Type; + [Key(3)] + public string TempID { get; set; } + [Key(4)] public ArchiveStateObject State { get; set; } + [Key(5)] public List FileStates { get; set; } } + [MessagePackObject] public class PatchedFromArchive : FromArchive { + [Key(4)] + public Hash FromHash { get; set; } + /// /// The file to apply to the source file to patch it /// - public string PatchID; - - [Exclude] - public Hash FromHash; + [Key(5)] + public string PatchID { get; set; } } + [MessagePackObject] public class SourcePatch { - public string RelativePath; - public Hash Hash; + [Key(0)] + public Hash Hash { get; set; } + [Key(1)] + public string RelativePath { get; set; } } + [MessagePackObject] public class MergedPatch : Directive { - public List Sources; - public string PatchID; + [Key(3)] + public string PatchID { get; set; } + [Key(4)] + public List Sources { get; set; } } + [MessagePackObject] public class Archive { /// /// MurMur3 Hash of the archive /// + [Key(0)] public Hash Hash { get; set; } /// - /// Meta INI for the downloaded archive + /// Meta INI for the downloaded archive /// + [Key(1)] public string Meta { get; set; } /// /// Human friendly name of this archive /// + [Key(2)] public string Name { get; set; } + [Key(3)] public long Size { get; set; } + + [Key(4)] public AbstractDownloadState State { get; set; } } @@ -285,15 +330,4 @@ namespace Wabbajack.Lib { public string[] HashPath; } - - /// - /// Data found inside a BSA file in an archive - /// - public class BSAIndexedEntry : IndexedEntry - { - /// - /// MurMur3 hash of the BSA this file comes from - /// - public string BSAHash; - } } diff --git a/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs b/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs index 91d2534c..ab3d30f3 100644 --- a/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs +++ b/Wabbajack.Lib/Downloaders/AbstractDownloadState.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; +using MessagePack; using Wabbajack.Lib.Validation; namespace Wabbajack.Lib.Downloaders { public interface IMetaState { - string URL { get; } + Uri URL { get; } string Name { get; set; } string Author { get; set; } string Version { get; set; } @@ -20,6 +21,22 @@ namespace Wabbajack.Lib.Downloaders Task LoadMetaData(); } + [MessagePackObject] + [Union(0, typeof(HTTPDownloader.State))] + [Union(1, typeof(GameFileSourceDownloader.State))] + [Union(2, typeof(GoogleDriveDownloader.State))] + [Union(3, typeof(LoversLabDownloader.State))] + [Union(4, typeof(ManualDownloader.State))] + [Union(5, typeof(MediaFireDownloader.State))] + [Union(6, typeof(MegaDownloader.State))] + [Union(7, typeof(ModDBDownloader.State))] + [Union(8, typeof(NexusDownloader.State))] + [Union(9, typeof(SteamWorkshopDownloader.State))] + [Union(10, typeof(VectorPlexusDownloader.State))] + [Union(11, typeof(AFKModsDownloader.State))] + [Union(12, typeof(TESAllianceDownloader.State))] + [Union(13, typeof(BethesdaNetDownloader.State))] + [Union(14, typeof(YouTubeDownloader.State))] public abstract class AbstractDownloadState { @@ -51,8 +68,10 @@ namespace Wabbajack.Lib.Downloaders TypeToName = NameToType.ToDictionary(k => k.Value, k => k.Key); } + [IgnoreMember] public abstract object[] PrimaryKey { get; } + [IgnoreMember] public string PrimaryKeyString { get diff --git a/Wabbajack.Lib/Downloaders/AbstractIPS4Downloader.cs b/Wabbajack.Lib/Downloaders/AbstractIPS4Downloader.cs index 4df8f011..50f8971f 100644 --- a/Wabbajack.Lib/Downloaders/AbstractIPS4Downloader.cs +++ b/Wabbajack.Lib/Downloaders/AbstractIPS4Downloader.cs @@ -204,7 +204,7 @@ namespace Wabbajack.Lib.Downloaders } // from IMetaState - public string URL => $"{Site}/files/file/{FileName}"; + public Uri URL => new Uri($"{Site}/files/file/{FileName}"); public string Name { get; set; } public string Author { get; set; } public string Version { get; set; } diff --git a/Wabbajack.Lib/Downloaders/HTTPDownloader.cs b/Wabbajack.Lib/Downloaders/HTTPDownloader.cs index fcebdc0a..b81dc19f 100644 --- a/Wabbajack.Lib/Downloaders/HTTPDownloader.cs +++ b/Wabbajack.Lib/Downloaders/HTTPDownloader.cs @@ -2,13 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Reflection.Emit; using System.Threading.Tasks; -using Ceras; -using SharpCompress.Common; using Wabbajack.Common; using Wabbajack.Lib.Exceptions; using Wabbajack.Lib.Validation; @@ -53,14 +49,12 @@ namespace Wabbajack.Lib.Downloaders { } - [MemberConfig(TargetMember.All)] public class State : AbstractDownloadState { public string Url { get; set; } public List Headers { get; set; } - [Exclude] public Common.Http.Client Client { get; set; } public override object[] PrimaryKey { get => new object[] {Url};} diff --git a/Wabbajack.Lib/Downloaders/ManualDownloader.cs b/Wabbajack.Lib/Downloaders/ManualDownloader.cs index fd74e334..8f8ef52e 100644 --- a/Wabbajack.Lib/Downloaders/ManualDownloader.cs +++ b/Wabbajack.Lib/Downloaders/ManualDownloader.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading.Tasks; +using MessagePack; using Wabbajack.Common; using Wabbajack.Common.IO; using Wabbajack.Lib.Validation; @@ -69,10 +70,14 @@ namespace Wabbajack.Lib.Downloaders public async Task Prepare() { } - + + [MessagePackObject] public class State : AbstractDownloadState { + [Key(0)] public string Url { get; set; } + + [IgnoreMember] public override object[] PrimaryKey { get => new object[] {Url}; } public override bool IsWhitelisted(ServerWhitelist whitelist) diff --git a/Wabbajack.Lib/Downloaders/NexusDownloader.cs b/Wabbajack.Lib/Downloaders/NexusDownloader.cs index 226ebb1e..074695a7 100644 --- a/Wabbajack.Lib/Downloaders/NexusDownloader.cs +++ b/Wabbajack.Lib/Downloaders/NexusDownloader.cs @@ -1,19 +1,14 @@ using System; -using System.ComponentModel; using System.Linq; using System.Reactive; using System.Reactive.Linq; -using System.Threading; using System.Threading.Tasks; -using System.Windows.Input; -using Ceras; using MongoDB.Bson.Serialization.Attributes; using ReactiveUI; using Wabbajack.Common; using Wabbajack.Common.StatusFeed.Errors; using Wabbajack.Lib.NexusApi; using Wabbajack.Lib.Validation; -using Game = Wabbajack.Common.Game; namespace Wabbajack.Lib.Downloaders { @@ -126,7 +121,7 @@ namespace Wabbajack.Lib.Downloaders [BsonIgnoreExtraElements] public class State : AbstractDownloadState, IMetaState { - public string URL => $"http://nexusmods.com/{NexusApiUtils.ConvertGameName(GameName)}/mods/{ModID}"; + public Uri URL => new Uri($"http://nexusmods.com/{NexusApiUtils.ConvertGameName(GameName)}/mods/{ModID}"); public string Name { get; set; } diff --git a/Wabbajack.Lib/MO2Compiler.cs b/Wabbajack.Lib/MO2Compiler.cs index 8ff47a1a..3483f3d8 100644 --- a/Wabbajack.Lib/MO2Compiler.cs +++ b/Wabbajack.Lib/MO2Compiler.cs @@ -313,7 +313,7 @@ namespace Wabbajack.Lib Description = ModListDescription ?? "", Readme = ModListReadme ?? "", Image = ModListImage ?? "", - Website = ModListWebsite ?? "" + Website = ModListWebsite != null ? new Uri(ModListWebsite) : null }; UpdateTracker.NextStep("Running Validation"); @@ -472,7 +472,8 @@ namespace Wabbajack.Lib Status($"Patching {entry.To}"); var srcFile = byPath[string.Join("|", entry.ArchiveHashPath.Skip(1))]; await using var srcStream = srcFile.OpenRead(); - await using var outputStream = IncludeFile(out entry.PatchID); + await using var outputStream = IncludeFile(out var id); + entry.PatchID = id; await using var destStream = LoadDataForTo(entry.To, absolutePaths); await Utils.CreatePatch(srcStream, srcFile.Hash, destStream, entry.Hash, outputStream); Info($"Patch size {outputStream.Length} for {entry.To}"); diff --git a/Wabbajack.Lib/VortexCompiler.cs b/Wabbajack.Lib/VortexCompiler.cs index b029542f..fa11b0c4 100644 --- a/Wabbajack.Lib/VortexCompiler.cs +++ b/Wabbajack.Lib/VortexCompiler.cs @@ -237,7 +237,7 @@ namespace Wabbajack.Lib Description = ModListDescription ?? "", Readme = ModListReadme ?? "", Image = ModListImage ?? "", - Website = ModListWebsite ?? "", + Website = ModListWebsite != null ? new Uri(ModListWebsite) : null, Archives = SelectedArchives.ToList(), ModManager = ModManager.Vortex, Directives = InstallDirectives, diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj index ce96fc8f..e5461e62 100644 --- a/Wabbajack.Lib/Wabbajack.Lib.csproj +++ b/Wabbajack.Lib/Wabbajack.Lib.csproj @@ -12,9 +12,6 @@ 79.1.350 - - 4.1.7 - 6.1.1 @@ -30,6 +27,9 @@ 1.7.1 + + 2.1.90 + 4.7.0 diff --git a/Wabbajack/View Models/Gallery/ModListMetadataVM.cs b/Wabbajack/View Models/Gallery/ModListMetadataVM.cs index 4d4dc7c7..44c31f65 100644 --- a/Wabbajack/View Models/Gallery/ModListMetadataVM.cs +++ b/Wabbajack/View Models/Gallery/ModListMetadataVM.cs @@ -54,7 +54,7 @@ namespace Wabbajack Metadata = metadata; Location = Path.Combine(Consts.ModListDownloadFolder, Metadata.Links.MachineURL + Consts.ModListExtension); IsBroken = metadata.ValidationSummary.HasFailures; - OpenWebsiteCommand = ReactiveCommand.Create(() => Utils.OpenWebsite($"https://www.wabbajack.org/modlist/{Metadata.Links.MachineURL}")); + OpenWebsiteCommand = ReactiveCommand.Create(() => Utils.OpenWebsite(new Uri($"https://www.wabbajack.org/modlist/{Metadata.Links.MachineURL}"))); ExecuteCommand = ReactiveCommand.CreateFromObservable( canExecute: this.WhenAny(x => x.IsBroken).Select(x => !x), execute: (unit) => diff --git a/Wabbajack/View Models/Installers/InstallerVM.cs b/Wabbajack/View Models/Installers/InstallerVM.cs index 9c8dceef..42742c89 100644 --- a/Wabbajack/View Models/Installers/InstallerVM.cs +++ b/Wabbajack/View Models/Installers/InstallerVM.cs @@ -324,7 +324,7 @@ namespace Wabbajack return Unit.Default; }, canExecute: this.WhenAny(x => x.ModList.Website) - .Select(x => x?.StartsWith("https://") ?? false) + .Select(x => x != null) .ObserveOnGuiThread()); _progressTitle = this.WhenAnyValue( diff --git a/Wabbajack/View Models/ModListVM.cs b/Wabbajack/View Models/ModListVM.cs index 14370c33..00cadcd6 100644 --- a/Wabbajack/View Models/ModListVM.cs +++ b/Wabbajack/View Models/ModListVM.cs @@ -20,7 +20,7 @@ namespace Wabbajack public string Readme => SourceModList?.Readme; public string Author => SourceModList?.Author; public string Description => SourceModList?.Description; - public string Website => SourceModList?.Website; + public Uri Website => SourceModList?.Website; public ModManager ModManager => SourceModList?.ModManager ?? ModManager.MO2; // Image isn't exposed as a direct property, but as an observable. @@ -96,7 +96,7 @@ namespace Wabbajack if (string.IsNullOrEmpty(Readme)) return; if (SourceModList.ReadmeIsWebsite) { - Utils.OpenWebsite(Readme); + Utils.OpenWebsite(new Uri(Readme)); } else { diff --git a/Wabbajack/View Models/SlideShow.cs b/Wabbajack/View Models/SlideShow.cs index 7f60f259..b1ebd7d1 100644 --- a/Wabbajack/View Models/SlideShow.cs +++ b/Wabbajack/View Models/SlideShow.cs @@ -132,7 +132,7 @@ namespace Wabbajack .Select(x => { var regex = new Regex("^(http|https):\\/\\/"); - return x != null && regex.Match(x).Success; + return x != null && regex.Match(x.ToString()).Success; }) .ObserveOnGuiThread()); diff --git a/Wabbajack/Views/LinksView.xaml.cs b/Wabbajack/Views/LinksView.xaml.cs index 16938432..0f31876c 100644 --- a/Wabbajack/Views/LinksView.xaml.cs +++ b/Wabbajack/Views/LinksView.xaml.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using System.Windows; using System.Windows.Controls; using Wabbajack.Common; @@ -17,17 +18,17 @@ namespace Wabbajack private void GitHub_Click(object sender, RoutedEventArgs e) { - Utils.OpenWebsite("https://github.com/wabbajack-tools/wabbajack"); + Utils.OpenWebsite(new Uri("https://github.com/wabbajack-tools/wabbajack")); } private void Discord_Click(object sender, RoutedEventArgs e) { - Utils.OpenWebsite("https://discord.gg/wabbajack"); + Utils.OpenWebsite(new Uri("https://discord.gg/wabbajack")); } private void Patreon_Click(object sender, RoutedEventArgs e) { - Utils.OpenWebsite("https://www.patreon.com/user?u=11907933"); + Utils.OpenWebsite(new Uri("https://www.patreon.com/user?u=11907933")); } } }