diff --git a/Wabbajack.Common/Consts.cs b/Wabbajack.Common/Consts.cs index 62e4a975..bda50e65 100644 --- a/Wabbajack.Common/Consts.cs +++ b/Wabbajack.Common/Consts.cs @@ -59,6 +59,8 @@ namespace Wabbajack.Common public static string DOWNLOAD_PATH_MAGIC_BACK = "{--||DOWNLOAD_PATH_MAGIC_BACK||--}"; public static string DOWNLOAD_PATH_MAGIC_DOUBLE_BACK = "{--||DOWNLOAD_PATH_MAGIC_DOUBLE_BACK||--}"; public static string DOWNLOAD_PATH_MAGIC_FORWARD = "{--||DOWNLOAD_PATH_MAGIC_FORWARD||--}"; + + public static Uri GITHUB_REPO_RELEASES = new Uri("https://api.github.com/repos/wabbajack-tools/wabbajack/releases"); public static string AppName = "Wabbajack"; diff --git a/Wabbajack.Lib/Http/Client.cs b/Wabbajack.Lib/Http/Client.cs index 8cafb129..66e74d43 100644 --- a/Wabbajack.Lib/Http/Client.cs +++ b/Wabbajack.Lib/Http/Client.cs @@ -158,5 +158,19 @@ namespace Wabbajack.Lib.Http { Cookies.AddRange(cookies.Select(c => new Cookie {Domain = c.Domain, Name = c.Name, Value = c.Value, Path = c.Path})); } + + public void UseChromeUserAgent() + { + Headers.Add(("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36")); + } + + public async Task DownloadAsync(Uri url, AbsolutePath path) + { + using var response = await GetAsync(url); + await using var content = await response.Content.ReadAsStreamAsync(); + path.Parent.CreateDirectory(); + await using var of = await path.Create(); + await content.CopyToAsync(of); + } } } diff --git a/Wabbajack.Lib/LauncherUpdater.cs b/Wabbajack.Lib/LauncherUpdater.cs new file mode 100644 index 00000000..15ad6eb6 --- /dev/null +++ b/Wabbajack.Lib/LauncherUpdater.cs @@ -0,0 +1,114 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Wabbajack.Common; +using Wabbajack.Lib.Http; + +namespace Wabbajack.Lib +{ + public class LauncherUpdater + { + public static async Task Run() + { + var entryPoint = AbsolutePath.EntryPoint; + + // If we're not in a folder that looks like a version, abort + if (!Version.TryParse(entryPoint.FileName.ToString(), out var version)) + { + Utils.Log($"Not in a version folder, not attempting update. Got {entryPoint.Parent}"); + return; + } + + // If we're not in a folder that has Wabbajack.exe in the parent folder, abort + if (!entryPoint.Parent.Parent.Combine(Consts.AppName).WithExtension(new Extension(".exe")).IsFile) + { + Utils.Log("Parent folder does not contain launcher, not updating"); + return; + } + + var oldVersions = entryPoint.Parent + .EnumerateDirectories() + .Select(f => Version.TryParse(f.FileName.ToString(), out var ver) ? (ver, f) : default) + .Where(f => f != default) + .Where(f => f.ver < version) + .Select(f => f!) + .OrderByDescending(f => f) + .Skip(2) + .ToArray(); + + foreach (var (_, path) in oldVersions) + { + Utils.Log($"Deleting old Wabbajack version at: {path}"); + await path.DeleteDirectory(); + } + + var release = (await GetReleases()) + .Select(release => Version.TryParse(release.Tag, out version) ? (version, release) : default) + .Where(r => r != default) + .OrderByDescending(r => r.version) + .Select(r => + { + var (version, release) = r; + var asset = release.Assets.FirstOrDefault(a => a.Name == "Wabbajack.exe"); + return asset != default ? (version, release, asset) : default; + }) + .FirstOrDefault(); + + var launcherFolder = AbsolutePath.EntryPoint.Parent; + var exePath = launcherFolder.Combine("Wabbajack.exe"); + + var launcherVersion = FileVersionInfo.GetVersionInfo(exePath.ToString()); + + if (release != default && launcherVersion != null && release.version > Version.Parse(launcherVersion.FileVersion!)) + { + Utils.Log($"Updating Launcher from {launcherVersion} to {release.version}"); + var tempPath = launcherFolder.Combine("Wabbajack.exe.temp"); + var client = new Client(); + client.UseChromeUserAgent(); + await client.DownloadAsync(release.asset.BrowserDownloadUrl!, tempPath); + + if (tempPath.Size != release.asset.Size) + { + Utils.Log( + $"Downloaded launcher did not match expected size: {tempPath.Size} expected {release.asset.Size}"); + return; + } + + if (exePath.Exists) + await exePath.DeleteAsync(); + await tempPath.MoveToAsync(exePath); + + Utils.Log("Finished updating wabbajack"); + } + } + + private static async Task GetReleases() + { + Utils.Log("Getting new Wabbajack version list"); + var client = new Client(); + client.UseChromeUserAgent(); + return await client.GetJsonAsync(Consts.GITHUB_REPO_RELEASES.ToString()); + } + + + class Release + { + [JsonProperty("tag_name")] public string Tag { get; set; } = ""; + + [JsonProperty("assets")] public Asset[] Assets { get; set; } = Array.Empty(); + + } + + class Asset + { + [JsonProperty("browser_download_url")] + public Uri? BrowserDownloadUrl { get; set; } + + [JsonProperty("name")] public string Name { get; set; } = ""; + + [JsonProperty("size")] public long Size { get; set; } = 0; + } + } +} diff --git a/Wabbajack/App.xaml.cs b/Wabbajack/App.xaml.cs index 729690ce..4cb166b9 100644 --- a/Wabbajack/App.xaml.cs +++ b/Wabbajack/App.xaml.cs @@ -1,6 +1,7 @@ using System; using System.Windows; using Wabbajack.Common; +using Wabbajack.Lib; namespace Wabbajack { diff --git a/Wabbajack/Views/MainWindow.xaml.cs b/Wabbajack/Views/MainWindow.xaml.cs index a74772c7..a0418624 100644 --- a/Wabbajack/Views/MainWindow.xaml.cs +++ b/Wabbajack/Views/MainWindow.xaml.cs @@ -7,6 +7,7 @@ using MahApps.Metro.Controls; using Newtonsoft.Json; using Wabbajack.Common; using Wabbajack.Common.StoreHandlers; +using Wabbajack.Lib; using Wabbajack.Lib.LibCefHelpers; using Wabbajack.Util; using Application = System.Windows.Application; @@ -55,6 +56,8 @@ namespace Wabbajack Utils.Log("Pagefile below recommended! Consider increasing to 20000MB. A suboptimal pagefile can cause crashes and poor in-game performance."); Warmup(); + + var _ = LauncherUpdater.Run(); var (settings, loadedSettings) = MainSettings.TryLoadTypicalSettings().AsTask().Result; // Load settings