diff options
author | TSRBerry <20988865+TSRBerry@users.noreply.github.com> | 2023-02-15 23:36:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-15 22:36:35 +0000 |
commit | a5a9b9bc8b64184cd4c342dea39fed5c2c058a72 (patch) | |
tree | 654b13d7290a151534d73cf673750a74a737aa59 | |
parent | 17078ad929f9942d2b03ede00b30867aeab924de (diff) |
GUI: Small Updater refactor & Set correct permissions on Linux when extracting files (#4315)1.1.622
* ava: Refactor Updater.cs
Fix typos
Remove unused usings
Rename variables to follow naming scheme
* ava: Set file permissions when extracting update files
* gtk: Apply the same refactor to Updater.cs
* updater: Replace assert with if statement
* updater: Remove await usings again
-rw-r--r-- | Ryujinx.Ava/Modules/Updater/Updater.cs | 65 | ||||
-rw-r--r-- | Ryujinx/Modules/Updater/Updater.cs | 328 |
2 files changed, 178 insertions, 215 deletions
diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index 62dc1772..b476bb85 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -132,8 +132,8 @@ namespace Ryujinx.Modules } } - // If build not done, assume no new update are availaible. - if (_buildUrl == null) + // If build not done, assume no new update are available. + if (_buildUrl is null) { if (showVersionUpToDate) { @@ -240,13 +240,13 @@ namespace Ryujinx.Modules { HttpClient result = new(); - // Required by GitHub to interract with APIs. + // Required by GitHub to interact with APIs. result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); return result; } - public static async void UpdateRyujinx(Window parent, string downloadUrl) + private static async void UpdateRyujinx(Window parent, string downloadUrl) { _updateSuccessful = false; @@ -300,8 +300,6 @@ namespace Ryujinx.Modules ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); } - SetFileExecutable(ryuExe); - Process.Start(ryuExe, CommandLineState.Arguments); Environment.Exit(0); @@ -408,9 +406,9 @@ namespace Ryujinx.Modules Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - for (int j = 0; j < webClients.Count; j++) + foreach (WebClient webClient in webClients) { - webClients[j].CancelAsync(); + webClient.CancelAsync(); } DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); @@ -472,22 +470,6 @@ namespace Ryujinx.Modules worker.Start(); } - private static void SetFileExecutable(string path) - { - const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute | - UnixFileMode.UserWrite | - UnixFileMode.UserRead | - UnixFileMode.GroupRead | - UnixFileMode.GroupWrite | - UnixFileMode.OtherRead | - UnixFileMode.OtherWrite; - - if (!OperatingSystem.IsWindows() && File.Exists(path)) - { - File.SetUnixFileMode(path, ExecutableFileMode); - } - } - private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) { // Extract Update @@ -503,27 +485,30 @@ namespace Ryujinx.Modules await Task.Run(() => { TarEntry tarEntry; - while ((tarEntry = tarStream.GetNextEntry()) != null) - { - if (tarEntry.IsDirectory) continue; - string outPath = Path.Combine(UpdateDir, tarEntry.Name); + if (!OperatingSystem.IsWindows()) + { + while ((tarEntry = tarStream.GetNextEntry()) is not null) + { + if (tarEntry.IsDirectory) continue; - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + string outPath = Path.Combine(UpdateDir, tarEntry.Name); - using (FileStream outStream = File.OpenWrite(outPath)) - { - tarStream.CopyEntryContents(outStream); - } + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); + using (FileStream outStream = File.OpenWrite(outPath)) + { + tarStream.CopyEntryContents(outStream); + } - TarEntry entry = tarEntry; + File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - Dispatcher.UIThread.Post(() => - { - taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); - }); + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); + }); + } } }); @@ -603,8 +588,6 @@ namespace Ryujinx.Modules Directory.Delete(UpdateDir, true); - SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx")); - _updateSuccessful = true; taskDialog.Hide(); diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs index 2a25e78f..5ad5924e 100644 --- a/Ryujinx/Modules/Updater/Updater.cs +++ b/Ryujinx/Modules/Updater/Updater.cs @@ -9,6 +9,7 @@ using Ryujinx.Ui; using Ryujinx.Ui.Widgets; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -23,20 +24,20 @@ namespace Ryujinx.Modules { public static class Updater { + private const string GitHubApiURL = "https://api.github.com"; + private const int ConnectionCount = 4; + internal static bool Running; private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); - private static readonly int ConnectionCount = 4; private static string _buildVer; private static string _platformExt; private static string _buildUrl; private static long _buildSize; - private const string GitHubApiURL = "https://api.github.com"; - // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; @@ -44,7 +45,7 @@ namespace Ryujinx.Modules { HttpClient result = new HttpClient(); - // Required by GitHub to interract with APIs. + // Required by GitHub to interact with APIs. result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); return result; @@ -101,50 +102,48 @@ namespace Ryujinx.Modules // Get latest version number from GitHub API try { - using (HttpClient jsonClient = ConstructHttpClient()) - { - string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + using HttpClient jsonClient = ConstructHttpClient(); + string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + + // Fetch latest build information + string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); + JObject jsonRoot = JObject.Parse(fetchedJson); + JToken assets = jsonRoot["assets"]; - // Fetch latest build information - string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); - JObject jsonRoot = JObject.Parse(fetchedJson); - JToken assets = jsonRoot["assets"]; + _buildVer = (string)jsonRoot["name"]; - _buildVer = (string)jsonRoot["name"]; + foreach (JToken asset in assets) + { + string assetName = (string)asset["name"]; + string assetState = (string)asset["state"]; + string downloadURL = (string)asset["browser_download_url"]; - foreach (JToken asset in assets) + if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) { - string assetName = (string)asset["name"]; - string assetState = (string)asset["state"]; - string downloadURL = (string)asset["browser_download_url"]; + _buildUrl = downloadURL; - if (assetName.StartsWith("ryujinx") && assetName.EndsWith(_platformExt)) + if (assetState != "uploaded") { - _buildUrl = downloadURL; - - if (assetState != "uploaded") + if (showVersionUpToDate) { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; + GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } - break; + return; } + + break; } + } - if (_buildUrl == null) + if (_buildUrl == null) + { + if (showVersionUpToDate) { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; + GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); } + + return; } } catch (Exception exception) @@ -247,160 +246,142 @@ namespace Ryujinx.Modules for (int i = 0; i < ConnectionCount; i++) { - list.Add(new byte[0]); + list.Add(Array.Empty<byte>()); } for (int i = 0; i < ConnectionCount; i++) { #pragma warning disable SYSLIB0014 // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. - using (WebClient client = new WebClient()) + using WebClient client = new WebClient(); #pragma warning restore SYSLIB0014 + webClients.Add(client); + + if (i == ConnectionCount - 1) { - webClients.Add(client); + client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); + } + else + { + client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); + } - if (i == ConnectionCount - 1) - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); - } - else - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); - } + client.DownloadProgressChanged += (_, args) => + { + int index = (int)args.UserState; - client.DownloadProgressChanged += (_, args) => - { - int index = (int)args.UserState; + Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); + Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); + Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); - Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); - Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); + updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; + }; - updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; - }; + client.DownloadDataCompleted += (_, args) => + { + int index = (int)args.UserState; - client.DownloadDataCompleted += (_, args) => + if (args.Cancelled) { - int index = (int)args.UserState; + webClients[index].Dispose(); - if (args.Cancelled) - { - webClients[index].Dispose(); - - return; - } + return; + } - list[index] = args.Result; - Interlocked.Increment(ref completedRequests); + list[index] = args.Result; + Interlocked.Increment(ref completedRequests); - if (Equals(completedRequests, ConnectionCount)) + if (Equals(completedRequests, ConnectionCount)) + { + byte[] mergedFileBytes = new byte[_buildSize]; + for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) { - byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) - { - Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); - destinationOffset += list[connectionIndex].Length; - } + Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); + destinationOffset += list[connectionIndex].Length; + } - File.WriteAllBytes(updateFile, mergedFileBytes); + File.WriteAllBytes(updateFile, mergedFileBytes); - try - { - InstallUpdate(updateDialog, updateFile); - } - catch (Exception e) - { - Logger.Warning?.Print(LogClass.Application, e.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); + try + { + InstallUpdate(updateDialog, updateFile); + } + catch (Exception e) + { + Logger.Warning?.Print(LogClass.Application, e.Message); + Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - return; - } + return; } - }; + } + }; - try + try + { + client.DownloadDataAsync(new Uri(downloadUrl), i); + } + catch (WebException ex) + { + Logger.Warning?.Print(LogClass.Application, ex.Message); + Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); + + foreach (WebClient webClient in webClients) { - client.DownloadDataAsync(new Uri(downloadUrl), i); + webClient.CancelAsync(); } - catch (WebException ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - for (int j = 0; j < webClients.Count; j++) - { - webClients[j].CancelAsync(); - } - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); + DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - return; - } + return; } } } private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) { - using (HttpClient client = new HttpClient()) - { - // We do not want to timeout while downloading - client.Timeout = TimeSpan.FromDays(1); + using HttpClient client = new HttpClient(); + // We do not want to timeout while downloading + client.Timeout = TimeSpan.FromDays(1); - using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) - using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) + using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) + using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) + { + using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) { - using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) - { - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; + long totalBytes = response.Content.Headers.ContentLength.Value; + long byteWritten = 0; - byte[] buffer = new byte[32 * 1024]; + byte[] buffer = new byte[32 * 1024]; - while (true) - { - int readSize = remoteFileStream.Read(buffer); + while (true) + { + int readSize = remoteFileStream.Read(buffer); - if (readSize == 0) - { - break; - } + if (readSize == 0) + { + break; + } - byteWritten += readSize; + byteWritten += readSize; - updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; - updateFileStream.Write(buffer, 0, readSize); - } + updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; + updateFileStream.Write(buffer, 0, readSize); } } - - InstallUpdate(updateDialog, updateFile); } - } - private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)); - worker.Name = "Updater.SingleThreadWorker"; - worker.Start(); + InstallUpdate(updateDialog, updateFile); } - private static void SetFileExecutable(string path) + private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) { - const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute | - UnixFileMode.UserWrite | - UnixFileMode.UserRead | - UnixFileMode.GroupRead | - UnixFileMode.GroupWrite | - UnixFileMode.OtherRead | - UnixFileMode.OtherWrite; - - if (!OperatingSystem.IsWindows() && File.Exists(path)) + Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) { - File.SetUnixFileMode(path, ExecutableFileMode); - } + Name = "Updater.SingleThreadWorker" + }; + worker.Start(); } private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) @@ -411,15 +392,17 @@ namespace Ryujinx.Modules if (OperatingSystem.IsLinux()) { - using (Stream inStream = File.OpenRead(updateFile)) - using (Stream gzipStream = new GZipInputStream(inStream)) - using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) + using Stream inStream = File.OpenRead(updateFile); + using Stream gzipStream = new GZipInputStream(inStream); + using TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII); + updateDialog.ProgressBar.MaxValue = inStream.Length; + + await Task.Run(() => { - updateDialog.ProgressBar.MaxValue = inStream.Length; + TarEntry tarEntry; - await Task.Run(() => + if (!OperatingSystem.IsWindows()) { - TarEntry tarEntry; while ((tarEntry = tarStream.GetNextEntry()) != null) { if (tarEntry.IsDirectory) continue; @@ -433,6 +416,7 @@ namespace Ryujinx.Modules tarStream.CopyEntryContents(outStream); } + File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); TarEntry entry = tarEntry; @@ -442,43 +426,41 @@ namespace Ryujinx.Modules updateDialog.ProgressBar.Value += entry.Size; }); } - }); + } + }); - updateDialog.ProgressBar.Value = inStream.Length; - } + updateDialog.ProgressBar.Value = inStream.Length; } else { - using (Stream inStream = File.OpenRead(updateFile)) - using (ZipFile zipFile = new ZipFile(inStream)) - { - updateDialog.ProgressBar.MaxValue = zipFile.Count; + using Stream inStream = File.OpenRead(updateFile); + using ZipFile zipFile = new ZipFile(inStream); + updateDialog.ProgressBar.MaxValue = zipFile.Count; - await Task.Run(() => + await Task.Run(() => + { + foreach (ZipEntry zipEntry in zipFile) { - foreach (ZipEntry zipEntry in zipFile) - { - if (zipEntry.IsDirectory) continue; + if (zipEntry.IsDirectory) continue; - string outPath = Path.Combine(UpdateDir, zipEntry.Name); + string outPath = Path.Combine(UpdateDir, zipEntry.Name); - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } + using (Stream zipStream = zipFile.GetInputStream(zipEntry)) + using (FileStream outStream = File.OpenWrite(outPath)) + { + zipStream.CopyTo(outStream); + } - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value++; - }); - } - }); - } + Application.Invoke(delegate + { + updateDialog.ProgressBar.Value++; + }); + } + }); } // Delete downloaded zip @@ -522,8 +504,6 @@ namespace Ryujinx.Modules Directory.Delete(UpdateDir, true); - SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx")); - updateDialog.MainText.Text = "Update Complete!"; updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; updateDialog.Modal = true; @@ -640,4 +620,4 @@ namespace Ryujinx.Modules } } } -} +}
\ No newline at end of file |