mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
MessagePack, and basic sanity test passes
This commit is contained in:
parent
3b895f4dbb
commit
d6123a7fb2
@ -37,6 +37,12 @@ namespace Wabbajack.Common.Http
|
|||||||
return await SendStringAsync(request);
|
return await SendStringAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetStringAsync(Uri url)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
return await SendStringAsync(request);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<string> DeleteStringAsync(string url)
|
public async Task<string> DeleteStringAsync(string url)
|
||||||
{
|
{
|
||||||
var request = new HttpRequestMessage(HttpMethod.Delete, url);
|
var request = new HttpRequestMessage(HttpMethod.Delete, url);
|
||||||
|
91
Wabbajack.Common/MessagePack.cs
Normal file
91
Wabbajack.Common/MessagePack.cs
Normal file
@ -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<IMessagePackFormatter>{new HashFormatter()},
|
||||||
|
new List<IFormatterResolver> {StandardResolver.Instance}
|
||||||
|
);
|
||||||
|
_messagePackOptions = MessagePackSerializerOptions.Standard
|
||||||
|
.WithResolver(_resolver)
|
||||||
|
.WithCompression(MessagePackCompression.Lz4BlockArray);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a object to this stream using MessagePack
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static async Task WriteAsMessagePackAsync<T>(this Stream stream, T obj)
|
||||||
|
{
|
||||||
|
await MessagePackSerializer.SerializeAsync(stream, obj, _messagePackOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a object to this stream using MessagePack
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void WriteAsMessagePack<T>(this Stream stream, T obj)
|
||||||
|
{
|
||||||
|
MessagePackSerializer.Serialize(stream, obj, _messagePackOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a object from this stream using MessagePack
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<T> ReadAsMessagePackAsync<T>(this Stream stream)
|
||||||
|
{
|
||||||
|
return await MessagePackSerializer.DeserializeAsync<T>(stream, _messagePackOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a object from this stream using MessagePack
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static T ReadAsMessagePack<T>(this Stream stream)
|
||||||
|
{
|
||||||
|
return MessagePackSerializer.Deserialize<T>(stream, _messagePackOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Formatters
|
||||||
|
|
||||||
|
public class HashFormatter : IMessagePackFormatter<Hash>
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
@ -6,7 +6,6 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -16,7 +15,6 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Alphaleonis.Win32.Filesystem;
|
using Alphaleonis.Win32.Filesystem;
|
||||||
using Ceras;
|
|
||||||
using ICSharpCode.SharpZipLib.BZip2;
|
using ICSharpCode.SharpZipLib.BZip2;
|
||||||
using IniParser;
|
using IniParser;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -52,6 +50,8 @@ namespace Wabbajack.Common
|
|||||||
|
|
||||||
static Utils()
|
static Utils()
|
||||||
{
|
{
|
||||||
|
MessagePackInit();
|
||||||
|
|
||||||
if (!Directory.Exists(Consts.LocalAppDataPath))
|
if (!Directory.Exists(Consts.LocalAppDataPath))
|
||||||
Directory.CreateDirectory(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)))));
|
return new DynamicIniData(new FileIniDataParser().ReadData(new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(file)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ToCERAS<T>(this T obj, string filename, SerializerConfig config)
|
|
||||||
{
|
|
||||||
byte[] final;
|
|
||||||
final = ToCERAS(obj, config);
|
|
||||||
File.WriteAllBytes(filename, final);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] ToCERAS<T>(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<T>(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<T>(m2.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ToJSON<T>(this T obj, string filename)
|
public static void ToJSON<T>(this T obj, string filename)
|
||||||
{
|
{
|
||||||
if (File.Exists(filename))
|
if (File.Exists(filename))
|
||||||
@ -388,18 +351,6 @@ namespace Wabbajack.Common
|
|||||||
JsonConvert.SerializeObject(obj, Formatting.Indented,
|
JsonConvert.SerializeObject(obj, Formatting.Indented,
|
||||||
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}));
|
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}));
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public static void ToBSON<T>(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)
|
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}")
|
Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {url}")
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
<Folder Include="Properties\" />
|
<Folder Include="Properties\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Ceras" Version="4.1.7" />
|
|
||||||
<PackageReference Include="Genbox.AlphaFS" Version="2.2.2.1" />
|
<PackageReference Include="Genbox.AlphaFS" Version="2.2.2.1" />
|
||||||
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
|
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
|
||||||
<PackageReference Include="MessagePack" Version="2.1.90" />
|
<PackageReference Include="MessagePack" Version="2.1.90" />
|
||||||
|
@ -9,6 +9,7 @@ using Wabbajack.Common;
|
|||||||
using Wabbajack.Lib.CompilationSteps;
|
using Wabbajack.Lib.CompilationSteps;
|
||||||
using Wabbajack.Lib.Downloaders;
|
using Wabbajack.Lib.Downloaders;
|
||||||
using Wabbajack.Lib.ModListRegistry;
|
using Wabbajack.Lib.ModListRegistry;
|
||||||
|
using Wabbajack.Lib.Validation;
|
||||||
using Wabbajack.VirtualFileSystem;
|
using Wabbajack.VirtualFileSystem;
|
||||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||||
using File = Alphaleonis.Win32.Filesystem.File;
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
@ -20,7 +21,7 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
public string ModListName, ModListAuthor, ModListDescription, ModListImage, ModListWebsite, ModListReadme;
|
public string ModListName, ModListAuthor, ModListDescription, ModListImage, ModListWebsite, ModListReadme;
|
||||||
public bool ReadmeIsWebsite;
|
public bool ReadmeIsWebsite;
|
||||||
public string WabbajackVersion;
|
protected Version WabbajackVersion;
|
||||||
|
|
||||||
public abstract string VFSCacheName { get; }
|
public abstract string VFSCacheName { get; }
|
||||||
//protected string VFSCacheName => Path.Combine(Consts.LocalAppDataPath, $"vfs_compile_cache.bin");
|
//protected string VFSCacheName => Path.Combine(Consts.LocalAppDataPath, $"vfs_compile_cache.bin");
|
||||||
@ -122,7 +123,8 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
ModList.ReadmeIsWebsite = ReadmeIsWebsite;
|
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))
|
if (File.Exists(ModListOutputFile))
|
||||||
File.Delete(ModListOutputFile);
|
File.Delete(ModListOutputFile);
|
||||||
|
@ -82,7 +82,7 @@ namespace Wabbajack.Lib
|
|||||||
return e.FromJSON<ModList>();
|
return e.FromJSON<ModList>();
|
||||||
}
|
}
|
||||||
using (var e = entry.Open())
|
using (var e = entry.Open())
|
||||||
return e.FromCERAS<ModList>(CerasConfig.Config);
|
return e.ReadAsMessagePack<ModList>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,8 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Ceras;
|
|
||||||
using Compression.BSA;
|
using Compression.BSA;
|
||||||
|
using MessagePack;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Lib.Downloaders;
|
using Wabbajack.Lib.Downloaders;
|
||||||
using Wabbajack.VirtualFileSystem;
|
using Wabbajack.VirtualFileSystem;
|
||||||
@ -38,99 +38,122 @@ namespace Wabbajack.Lib
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class ModList
|
public class ModList
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Archives required by this modlist
|
/// Archives required by this modlist
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
public List<Archive> Archives;
|
public List<Archive> Archives;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Mod Manager used to create the modlist
|
|
||||||
/// </summary>
|
|
||||||
public ModManager ModManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The game variant to which this game applies
|
|
||||||
/// </summary>
|
|
||||||
public Game GameType;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The build version of Wabbajack used when compiling the Modlist
|
|
||||||
/// </summary>
|
|
||||||
public string WabbajackVersion;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Install directives
|
|
||||||
/// </summary>
|
|
||||||
public List<Directive> Directives;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the ModList
|
|
||||||
/// </summary>
|
|
||||||
public string Name;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Author of the ModList
|
/// Author of the ModList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
public string Author;
|
public string Author;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Description of the ModList
|
/// Description of the ModList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(2)]
|
||||||
public string Description;
|
public string Description;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Install directives
|
||||||
|
/// </summary>
|
||||||
|
[Key(3)]
|
||||||
|
public List<Directive> Directives;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The game variant to which this game applies
|
||||||
|
/// </summary>
|
||||||
|
[Key(4)]
|
||||||
|
public Game GameType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hash of the banner-image
|
/// Hash of the banner-image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(5)]
|
||||||
public string Image;
|
public string Image;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Website of the ModList
|
/// The Mod Manager used to create the modlist
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Website;
|
[Key(6)]
|
||||||
|
public ModManager ModManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the ModList
|
||||||
|
/// </summary>
|
||||||
|
[Key(7)]
|
||||||
|
public string Name;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// readme path or website
|
/// readme path or website
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(8)]
|
||||||
public string Readme;
|
public string Readme;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of all the archives once they're downloaded
|
|
||||||
/// </summary>
|
|
||||||
public long DownloadSize => Archives.Sum(a => a.Size);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of all the files once they are installed (excluding downloaded archives)
|
|
||||||
/// </summary>
|
|
||||||
public long InstallSize => Directives.Sum(s => s.Size);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Estimate of the amount of space required in the VFS staging folders during installation
|
|
||||||
/// </summary>
|
|
||||||
public long ScratchSpaceSize => Archives.OrderByDescending(a => a.Size)
|
|
||||||
.Take(Environment.ProcessorCount)
|
|
||||||
.Sum(a => a.Size) * 2;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether readme is a website
|
/// Whether readme is a website
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(9)]
|
||||||
public bool ReadmeIsWebsite;
|
public bool ReadmeIsWebsite;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The build version of Wabbajack used when compiling the Modlist
|
||||||
|
/// </summary>
|
||||||
|
[Key(10)]
|
||||||
|
public Version WabbajackVersion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Website of the ModList
|
||||||
|
/// </summary>
|
||||||
|
[Key(11)]
|
||||||
|
public Uri Website;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of all the archives once they're downloaded
|
||||||
|
/// </summary>
|
||||||
|
[IgnoreMember]
|
||||||
|
public long DownloadSize => Archives.Sum(a => a.Size);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of all the files once they are installed (excluding downloaded archives)
|
||||||
|
/// </summary>
|
||||||
|
[IgnoreMember]
|
||||||
|
public long InstallSize => Directives.Sum(s => s.Size);
|
||||||
|
|
||||||
public ModList Clone()
|
public ModList Clone()
|
||||||
{
|
{
|
||||||
return new MemoryStream(this.ToCERAS(CerasConfig.Config)).FromCERAS<ModList>(CerasConfig.Config);
|
using var ms = new MemoryStream();
|
||||||
|
ms.WriteAsMessagePack(this);
|
||||||
|
ms.Position = 0;
|
||||||
|
return ms.ReadAsMessagePack<ModList>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// location the file will be copied to, relative to the install path.
|
/// location the file will be copied to, relative to the install path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string To;
|
[Key(2)]
|
||||||
public long Size;
|
public string To { get; set; }
|
||||||
public Hash Hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IgnoredDirectly : Directive
|
public class IgnoredDirectly : Directive
|
||||||
@ -142,17 +165,21 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class InlineFile : Directive
|
public class InlineFile : Directive
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data that will be written as-is to the destination location;
|
/// Data that will be written as-is to the destination location;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(3)]
|
||||||
public string SourceDataID;
|
public string SourceDataID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class ArchiveMeta : Directive
|
public class ArchiveMeta : Directive
|
||||||
{
|
{
|
||||||
public string SourceDataID;
|
[Key(3)]
|
||||||
|
public string SourceDataID { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PropertyType { Banner, Readme }
|
public enum PropertyType { Banner, Readme }
|
||||||
@ -173,82 +200,100 @@ namespace Wabbajack.Lib
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A file that has the game and MO2 folders remapped on installation
|
/// A file that has the game and MO2 folders remapped on installation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[MessagePackObject]
|
||||||
public class RemappedInlineFile : InlineFile
|
public class RemappedInlineFile : InlineFile
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class SteamMeta : ArchiveMeta
|
public class SteamMeta : ArchiveMeta
|
||||||
{
|
{
|
||||||
public int ItemID;
|
[Key(4)]
|
||||||
/// <summary>
|
public int ItemID { get; set; }
|
||||||
/// Size is in bytes
|
|
||||||
/// </summary>
|
|
||||||
public int Size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemberConfig(TargetMember.All)]
|
[MessagePackObject]
|
||||||
public class FromArchive : Directive
|
public class FromArchive : Directive
|
||||||
{
|
{
|
||||||
private string _fullPath;
|
private string _fullPath;
|
||||||
|
|
||||||
public string[] ArchiveHashPath;
|
[Key(3)]
|
||||||
|
public string[] ArchiveHashPath { get; set; }
|
||||||
|
|
||||||
[Exclude]
|
[IgnoreMember]
|
||||||
public VirtualFile FromFile;
|
public VirtualFile FromFile { get; set; }
|
||||||
|
|
||||||
[Exclude]
|
[IgnoreMember]
|
||||||
public string FullPath => _fullPath ??= string.Join("|", ArchiveHashPath);
|
public string FullPath => _fullPath ??= string.Join("|", ArchiveHashPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class CreateBSA : Directive
|
public class CreateBSA : Directive
|
||||||
{
|
{
|
||||||
public string TempID;
|
[Key(3)]
|
||||||
public uint Type;
|
public string TempID { get; set; }
|
||||||
|
[Key(4)]
|
||||||
public ArchiveStateObject State { get; set; }
|
public ArchiveStateObject State { get; set; }
|
||||||
|
[Key(5)]
|
||||||
public List<FileStateObject> FileStates { get; set; }
|
public List<FileStateObject> FileStates { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class PatchedFromArchive : FromArchive
|
public class PatchedFromArchive : FromArchive
|
||||||
{
|
{
|
||||||
|
[Key(4)]
|
||||||
|
public Hash FromHash { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The file to apply to the source file to patch it
|
/// The file to apply to the source file to patch it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PatchID;
|
[Key(5)]
|
||||||
|
public string PatchID { get; set; }
|
||||||
[Exclude]
|
|
||||||
public Hash FromHash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class SourcePatch
|
public class SourcePatch
|
||||||
{
|
{
|
||||||
public string RelativePath;
|
[Key(0)]
|
||||||
public Hash Hash;
|
public Hash Hash { get; set; }
|
||||||
|
[Key(1)]
|
||||||
|
public string RelativePath { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class MergedPatch : Directive
|
public class MergedPatch : Directive
|
||||||
{
|
{
|
||||||
public List<SourcePatch> Sources;
|
[Key(3)]
|
||||||
public string PatchID;
|
public string PatchID { get; set; }
|
||||||
|
[Key(4)]
|
||||||
|
public List<SourcePatch> Sources { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class Archive
|
public class Archive
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MurMur3 Hash of the archive
|
/// MurMur3 Hash of the archive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
public Hash Hash { get; set; }
|
public Hash Hash { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Meta INI for the downloaded archive
|
/// Meta INI for the downloaded archive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
public string Meta { get; set; }
|
public string Meta { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Human friendly name of this archive
|
/// Human friendly name of this archive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Key(2)]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[Key(3)]
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
|
|
||||||
|
[Key(4)]
|
||||||
public AbstractDownloadState State { get; set; }
|
public AbstractDownloadState State { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,15 +330,4 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
public string[] HashPath;
|
public string[] HashPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data found inside a BSA file in an archive
|
|
||||||
/// </summary>
|
|
||||||
public class BSAIndexedEntry : IndexedEntry
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// MurMur3 hash of the BSA this file comes from
|
|
||||||
/// </summary>
|
|
||||||
public string BSAHash;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,14 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Alphaleonis.Win32.Filesystem;
|
using Alphaleonis.Win32.Filesystem;
|
||||||
|
using MessagePack;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
|
|
||||||
namespace Wabbajack.Lib.Downloaders
|
namespace Wabbajack.Lib.Downloaders
|
||||||
{
|
{
|
||||||
public interface IMetaState
|
public interface IMetaState
|
||||||
{
|
{
|
||||||
string URL { get; }
|
Uri URL { get; }
|
||||||
string Name { get; set; }
|
string Name { get; set; }
|
||||||
string Author { get; set; }
|
string Author { get; set; }
|
||||||
string Version { get; set; }
|
string Version { get; set; }
|
||||||
@ -20,6 +21,22 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
Task<bool> LoadMetaData();
|
Task<bool> 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
|
public abstract class AbstractDownloadState
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -51,8 +68,10 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
TypeToName = NameToType.ToDictionary(k => k.Value, k => k.Key);
|
TypeToName = NameToType.ToDictionary(k => k.Value, k => k.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreMember]
|
||||||
public abstract object[] PrimaryKey { get; }
|
public abstract object[] PrimaryKey { get; }
|
||||||
|
|
||||||
|
[IgnoreMember]
|
||||||
public string PrimaryKeyString
|
public string PrimaryKeyString
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -204,7 +204,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from IMetaState
|
// 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 Name { get; set; }
|
||||||
public string Author { get; set; }
|
public string Author { get; set; }
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
@ -2,13 +2,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Reflection.Emit;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ceras;
|
|
||||||
using SharpCompress.Common;
|
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Lib.Exceptions;
|
using Wabbajack.Lib.Exceptions;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
@ -53,14 +49,12 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemberConfig(TargetMember.All)]
|
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
public List<string> Headers { get; set; }
|
public List<string> Headers { get; set; }
|
||||||
|
|
||||||
[Exclude]
|
|
||||||
public Common.Http.Client Client { get; set; }
|
public Common.Http.Client Client { get; set; }
|
||||||
|
|
||||||
public override object[] PrimaryKey { get => new object[] {Url};}
|
public override object[] PrimaryKey { get => new object[] {Url};}
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MessagePack;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Common.IO;
|
using Wabbajack.Common.IO;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
@ -69,10 +70,14 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public async Task Prepare()
|
public async Task Prepare()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MessagePackObject]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
|
[Key(0)]
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
[IgnoreMember]
|
||||||
public override object[] PrimaryKey { get => new object[] {Url}; }
|
public override object[] PrimaryKey { get => new object[] {Url}; }
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
using Ceras;
|
|
||||||
using MongoDB.Bson.Serialization.Attributes;
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Common.StatusFeed.Errors;
|
using Wabbajack.Common.StatusFeed.Errors;
|
||||||
using Wabbajack.Lib.NexusApi;
|
using Wabbajack.Lib.NexusApi;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
using Game = Wabbajack.Common.Game;
|
|
||||||
|
|
||||||
namespace Wabbajack.Lib.Downloaders
|
namespace Wabbajack.Lib.Downloaders
|
||||||
{
|
{
|
||||||
@ -126,7 +121,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[BsonIgnoreExtraElements]
|
[BsonIgnoreExtraElements]
|
||||||
public class State : AbstractDownloadState, IMetaState
|
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; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ namespace Wabbajack.Lib
|
|||||||
Description = ModListDescription ?? "",
|
Description = ModListDescription ?? "",
|
||||||
Readme = ModListReadme ?? "",
|
Readme = ModListReadme ?? "",
|
||||||
Image = ModListImage ?? "",
|
Image = ModListImage ?? "",
|
||||||
Website = ModListWebsite ?? ""
|
Website = ModListWebsite != null ? new Uri(ModListWebsite) : null
|
||||||
};
|
};
|
||||||
|
|
||||||
UpdateTracker.NextStep("Running Validation");
|
UpdateTracker.NextStep("Running Validation");
|
||||||
@ -472,7 +472,8 @@ namespace Wabbajack.Lib
|
|||||||
Status($"Patching {entry.To}");
|
Status($"Patching {entry.To}");
|
||||||
var srcFile = byPath[string.Join("|", entry.ArchiveHashPath.Skip(1))];
|
var srcFile = byPath[string.Join("|", entry.ArchiveHashPath.Skip(1))];
|
||||||
await using var srcStream = srcFile.OpenRead();
|
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 using var destStream = LoadDataForTo(entry.To, absolutePaths);
|
||||||
await Utils.CreatePatch(srcStream, srcFile.Hash, destStream, entry.Hash, outputStream);
|
await Utils.CreatePatch(srcStream, srcFile.Hash, destStream, entry.Hash, outputStream);
|
||||||
Info($"Patch size {outputStream.Length} for {entry.To}");
|
Info($"Patch size {outputStream.Length} for {entry.To}");
|
||||||
|
@ -237,7 +237,7 @@ namespace Wabbajack.Lib
|
|||||||
Description = ModListDescription ?? "",
|
Description = ModListDescription ?? "",
|
||||||
Readme = ModListReadme ?? "",
|
Readme = ModListReadme ?? "",
|
||||||
Image = ModListImage ?? "",
|
Image = ModListImage ?? "",
|
||||||
Website = ModListWebsite ?? "",
|
Website = ModListWebsite != null ? new Uri(ModListWebsite) : null,
|
||||||
Archives = SelectedArchives.ToList(),
|
Archives = SelectedArchives.ToList(),
|
||||||
ModManager = ModManager.Vortex,
|
ModManager = ModManager.Vortex,
|
||||||
Directives = InstallDirectives,
|
Directives = InstallDirectives,
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
<PackageReference Include="CefSharp.OffScreen">
|
<PackageReference Include="CefSharp.OffScreen">
|
||||||
<Version>79.1.350</Version>
|
<Version>79.1.350</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Ceras">
|
|
||||||
<Version>4.1.7</Version>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Fody">
|
<PackageReference Include="Fody">
|
||||||
<Version>6.1.1</Version>
|
<Version>6.1.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@ -30,6 +27,9 @@
|
|||||||
<PackageReference Include="MegaApiClient">
|
<PackageReference Include="MegaApiClient">
|
||||||
<Version>1.7.1</Version>
|
<Version>1.7.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="MessagePackAnalyzer">
|
||||||
|
<Version>2.1.90</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.CSharp">
|
<PackageReference Include="Microsoft.CSharp">
|
||||||
<Version>4.7.0</Version>
|
<Version>4.7.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -54,7 +54,7 @@ namespace Wabbajack
|
|||||||
Metadata = metadata;
|
Metadata = metadata;
|
||||||
Location = Path.Combine(Consts.ModListDownloadFolder, Metadata.Links.MachineURL + Consts.ModListExtension);
|
Location = Path.Combine(Consts.ModListDownloadFolder, Metadata.Links.MachineURL + Consts.ModListExtension);
|
||||||
IsBroken = metadata.ValidationSummary.HasFailures;
|
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<Unit, Unit>(
|
ExecuteCommand = ReactiveCommand.CreateFromObservable<Unit, Unit>(
|
||||||
canExecute: this.WhenAny(x => x.IsBroken).Select(x => !x),
|
canExecute: this.WhenAny(x => x.IsBroken).Select(x => !x),
|
||||||
execute: (unit) =>
|
execute: (unit) =>
|
||||||
|
@ -324,7 +324,7 @@ namespace Wabbajack
|
|||||||
return Unit.Default;
|
return Unit.Default;
|
||||||
},
|
},
|
||||||
canExecute: this.WhenAny(x => x.ModList.Website)
|
canExecute: this.WhenAny(x => x.ModList.Website)
|
||||||
.Select(x => x?.StartsWith("https://") ?? false)
|
.Select(x => x != null)
|
||||||
.ObserveOnGuiThread());
|
.ObserveOnGuiThread());
|
||||||
|
|
||||||
_progressTitle = this.WhenAnyValue(
|
_progressTitle = this.WhenAnyValue(
|
||||||
|
@ -20,7 +20,7 @@ namespace Wabbajack
|
|||||||
public string Readme => SourceModList?.Readme;
|
public string Readme => SourceModList?.Readme;
|
||||||
public string Author => SourceModList?.Author;
|
public string Author => SourceModList?.Author;
|
||||||
public string Description => SourceModList?.Description;
|
public string Description => SourceModList?.Description;
|
||||||
public string Website => SourceModList?.Website;
|
public Uri Website => SourceModList?.Website;
|
||||||
public ModManager ModManager => SourceModList?.ModManager ?? ModManager.MO2;
|
public ModManager ModManager => SourceModList?.ModManager ?? ModManager.MO2;
|
||||||
|
|
||||||
// Image isn't exposed as a direct property, but as an observable.
|
// Image isn't exposed as a direct property, but as an observable.
|
||||||
@ -96,7 +96,7 @@ namespace Wabbajack
|
|||||||
if (string.IsNullOrEmpty(Readme)) return;
|
if (string.IsNullOrEmpty(Readme)) return;
|
||||||
if (SourceModList.ReadmeIsWebsite)
|
if (SourceModList.ReadmeIsWebsite)
|
||||||
{
|
{
|
||||||
Utils.OpenWebsite(Readme);
|
Utils.OpenWebsite(new Uri(Readme));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -132,7 +132,7 @@ namespace Wabbajack
|
|||||||
.Select(x =>
|
.Select(x =>
|
||||||
{
|
{
|
||||||
var regex = new Regex("^(http|https):\\/\\/");
|
var regex = new Regex("^(http|https):\\/\\/");
|
||||||
return x != null && regex.Match(x).Success;
|
return x != null && regex.Match(x.ToString()).Success;
|
||||||
})
|
})
|
||||||
.ObserveOnGuiThread());
|
.ObserveOnGuiThread());
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
@ -17,17 +18,17 @@ namespace Wabbajack
|
|||||||
|
|
||||||
private void GitHub_Click(object sender, RoutedEventArgs e)
|
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)
|
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)
|
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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user