diff options
Diffstat (limited to 'Ryujinx.Ava/Modules/Updater/Updater.cs')
| -rw-r--r-- | Ryujinx.Ava/Modules/Updater/Updater.cs | 229 |
1 files changed, 136 insertions, 93 deletions
diff --git a/Ryujinx.Ava/Modules/Updater/Updater.cs b/Ryujinx.Ava/Modules/Updater/Updater.cs index 511e273e..e89abd1d 100644 --- a/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -21,6 +21,7 @@ using System.Net.Http; using System.Net.NetworkInformation; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -57,7 +58,7 @@ namespace Ryujinx.Modules // Detect current platform if (OperatingSystem.IsMacOS()) { - _platformExt = "osx_x64.zip"; + _platformExt = "macos_universal.app.tar.gz"; } else if (OperatingSystem.IsWindows()) { @@ -286,22 +287,40 @@ namespace Ryujinx.Modules if (_updateSuccessful) { - var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], - LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], - LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); + bool shouldRestart = true; + + if (!OperatingSystem.IsMacOS()) + { + shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater], + LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage], + LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]); + } if (shouldRestart) { + List<string> arguments = CommandLineState.Arguments.ToList(); string ryuName = Path.GetFileName(Environment.ProcessPath); - string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); + string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; + string executablePath = Path.Combine(executableDirectory, ryuName); - if (!Path.Exists(ryuExe)) + if (!Path.Exists(executablePath)) { - ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); + executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); } - Process.Start(ryuExe, CommandLineState.Arguments); + // On macOS we perform the update at relaunch. + if (OperatingSystem.IsMacOS()) + { + string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); + string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); + string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); + string currentPid = Process.GetCurrentProcess().Id.ToString(); + executablePath = "/bin/bash"; + arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid }); + } + + Process.Start(executablePath, arguments); Environment.Exit(0); } } @@ -381,6 +400,15 @@ namespace Ryujinx.Modules File.WriteAllBytes(updateFile, mergedFileBytes); + // On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution. + if (OperatingSystem.IsMacOS()) + { + using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile })) + { + xattrProcess.WaitForExit(); + } + } + try { InstallUpdate(taskDialog, updateFile); @@ -470,87 +498,98 @@ namespace Ryujinx.Modules worker.Start(); } - private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath) { - // Extract Update - taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting]; - taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); + using Stream inStream = File.OpenRead(archivePath); + using GZipInputStream gzipStream = new(inStream); + using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); - if (OperatingSystem.IsLinux()) - { - using Stream inStream = File.OpenRead(updateFile); - using GZipInputStream gzipStream = new(inStream); - using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); + TarEntry tarEntry; - await Task.Run(() => + while ((tarEntry = tarStream.GetNextEntry()) is not null) + { + if (tarEntry.IsDirectory) { - TarEntry tarEntry; - - if (!OperatingSystem.IsWindows()) - { - while ((tarEntry = tarStream.GetNextEntry()) is not null) - { - if (tarEntry.IsDirectory) continue; - - string outPath = Path.Combine(UpdateDir, tarEntry.Name); + continue; + } - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name); - using (FileStream outStream = File.OpenWrite(outPath)) - { - tarStream.CopyEntryContents(outStream); - } + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); + using (FileStream outStream = File.OpenWrite(outPath)) + { + tarStream.CopyEntryContents(outStream); + } - Dispatcher.UIThread.Post(() => - { - if (tarEntry is null) - { - return; - } + File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); - }); - } + Dispatcher.UIThread.Post(() => + { + if (tarEntry is null) + { + return; } - }); - taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal); + taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal); + }); } - else - { - using Stream inStream = File.OpenRead(updateFile); - using ZipFile zipFile = new(inStream); + } - await Task.Run(() => - { - double count = 0; - foreach (ZipEntry zipEntry in zipFile) - { - count++; - if (zipEntry.IsDirectory) continue; + private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath) + { + using Stream inStream = File.OpenRead(archivePath); + using ZipFile zipFile = new(inStream); - string outPath = Path.Combine(UpdateDir, zipEntry.Name); + double count = 0; + foreach (ZipEntry zipEntry in zipFile) + { + count++; + if (zipEntry.IsDirectory) continue; - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name); - using (Stream zipStream = zipFile.GetInputStream(zipEntry)) - using (FileStream outStream = File.OpenWrite(outPath)) - { - zipStream.CopyTo(outStream); - } + Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); + using (Stream zipStream = zipFile.GetInputStream(zipEntry)) + using (FileStream outStream = File.OpenWrite(outPath)) + { + zipStream.CopyTo(outStream); + } - Dispatcher.UIThread.Post(() => - { - taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); - }); - } + File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); + + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal); }); } + } + + private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) + { + // Extract Update + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting]; + taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); + + await Task.Run(() => + { + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + ExtractTarGzipFile(taskDialog, updateFile, UpdateDir); + } + else if (OperatingSystem.IsWindows()) + { + ExtractZipFile(taskDialog, updateFile, UpdateDir); + } + else + { + throw new NotSupportedException(); + } + }); // Delete downloaded zip File.Delete(updateFile); @@ -560,38 +599,42 @@ namespace Ryujinx.Modules taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming]; taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); - // Replace old files - await Task.Run(() => + // NOTE: On macOS, replacement is delayed to the restart phase. + if (!OperatingSystem.IsMacOS()) { - double count = 0; - foreach (string file in allFiles) + // Replace old files + await Task.Run(() => { - count++; - try + double count = 0; + foreach (string file in allFiles) { - File.Move(file, file + ".ryuold"); + count++; + try + { + File.Move(file, file + ".ryuold"); - Dispatcher.UIThread.Post(() => + Dispatcher.UIThread.Post(() => + { + taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal); + }); + } + catch { - taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal); - }); + Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file)); + } } - catch + + Dispatcher.UIThread.Post(() => { - Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file)); - } - } + taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles]; + taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); + }); - Dispatcher.UIThread.Post(() => - { - taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles]; - taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); + MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); }); - MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); - }); - - Directory.Delete(UpdateDir, true); + Directory.Delete(UpdateDir, true); + } _updateSuccessful = true; @@ -601,7 +644,7 @@ namespace Ryujinx.Modules public static bool CanUpdate(bool showWarnings) { #if !DISABLE_UPDATER - if (RuntimeInformation.OSArchitecture != Architecture.X64) + if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS()) { if (showWarnings) { @@ -674,7 +717,7 @@ namespace Ryujinx.Modules #endif } - // NOTE: This method should always reflect the latest build layout.s + // NOTE: This method should always reflect the latest build layout. private static IEnumerable<string> EnumerateFilesToDelete() { var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. |
