diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index bde01b243e..7c5b925f05 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -37,6 +37,7 @@ + @@ -58,6 +59,7 @@ + diff --git a/Ryujinx/Ui/GtkDialog.cs b/Ryujinx/Ui/GtkDialog.cs index b4e9fa1caa..4d43299bdf 100644 --- a/Ryujinx/Ui/GtkDialog.cs +++ b/Ryujinx/Ui/GtkDialog.cs @@ -1,5 +1,8 @@ using Gtk; using System.Reflection; +using Ryujinx.Updater.Parser; +using System.IO; +using System; namespace Ryujinx.Ui { @@ -29,5 +32,56 @@ namespace Ryujinx.Ui { CreateDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage); } + + internal static MessageDialog CreateAcceptDialog(string iconType, string acceptMessage) + { + MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null) + { + Title = "Ryujinx - Update", + Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Update.png"), + Text = "Would you like to update?", + SecondaryText = "Version " + acceptMessage + " is available.", + WindowPosition = WindowPosition.Center + }; + messageDialog.SetSizeRequest(100, 20); + return messageDialog; + } + + internal static MessageDialog CreateInfoDialog(string iconType, string titleMessage, string textMessage, string secText) + { + MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null) + { + Title = titleMessage, + Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets." + iconType +".png"), + Text = textMessage, + SecondaryText = secText, + WindowPosition = WindowPosition.Center + }; + messageDialog.SetSizeRequest(100, 20); + return messageDialog; + } + + internal static async System.Threading.Tasks.Task CreateProgressDialogAsync(bool isInstall, string iconType, string titleMessage, string textMessage, string secText) + { + MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.None, null) + { + Title = titleMessage, + Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets." + iconType + ".png"), + Text = textMessage, + SecondaryText = secText, + WindowPosition = WindowPosition.Center + }; + messageDialog.SetSizeRequest(100, 20); + if (isInstall == true) + { + await UpdateParser.ExtractPackageAsync(); + } + else + { + Uri URL = new Uri(UpdateParser._BuildArt); + UpdateParser._Package.DownloadFileAsync(URL, Path.Combine(UpdateParser._RyuDir, "Data", "Update", "RyujinxPackage.zip")); + } + return messageDialog; + } } } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 6c771bb96f..aae63d9cd2 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -896,16 +896,18 @@ namespace Ryujinx.Ui private void Update_Pressed(object sender, EventArgs args) { - string ryuUpdater = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "RyuUpdater.exe"); + Ryujinx.Updater.Parser.UpdateParser.BeginParse(); + return; + //string ryuUpdater = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "RyuUpdater.exe"); - try - { - Process.Start(new ProcessStartInfo(ryuUpdater, "/U") { UseShellExecute = true }); - } - catch(System.ComponentModel.Win32Exception) - { - GtkDialog.CreateErrorDialog("Update canceled by user or updater was not found"); - } + //try + //{ + // Process.Start(new ProcessStartInfo(ryuUpdater, "/U") { UseShellExecute = true }); + //} + //catch(System.ComponentModel.Win32Exception) + //{ + // GtkDialog.CreateErrorDialog("Update canceled by user or updater was not found"); + //} } private void About_Pressed(object sender, EventArgs args) diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index 8477f392a4..a2b1165071 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -323,7 +323,7 @@ True False - Check for updates to Ryujinx (requires Ryujinx Installer) + Check for updates to Ryujinx Check for Updates True diff --git a/Ryujinx/Ui/assets/Update.png b/Ryujinx/Ui/assets/Update.png new file mode 100644 index 0000000000..f9ec78dbb3 Binary files /dev/null and b/Ryujinx/Ui/assets/Update.png differ diff --git a/Ryujinx/Updater/Parser/UpdateParser.cs b/Ryujinx/Updater/Parser/UpdateParser.cs new file mode 100644 index 0000000000..ff6beefd75 --- /dev/null +++ b/Ryujinx/Updater/Parser/UpdateParser.cs @@ -0,0 +1,151 @@ +using Gtk; +using Newtonsoft.Json.Linq; +using Ryujinx.Common.Logging; +using Ryujinx.Ui; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Ryujinx.Updater.Parser +{ + public class UpdateParser + { + private static string _JobID; + private static string _BuildVer; + private static string _BuildURL = "https://ci.appveyor.com/api/projects/gdkchan/ryujinx/branch/master"; + public static string _BuildArt; + private static string _BuildCommit; + private static string _Branch; + private static string _PlatformExt; + public static string _RyuDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Ryujinx"); + public static WebClient _Package = new WebClient(); + private static string _URLStr; + public static int _PackageProgress; + public static double _Percentage; + public static void BeginParse() + { + try + { + //Detect current platform + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + _PlatformExt = "osx_x64.zip"; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + _PlatformExt = "win_x64.zip"; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + _PlatformExt = "linux_x64.tar.gz"; + + WebClient JSONClient = new WebClient(); + string FetchedJSON = JSONClient.DownloadString(_BuildURL); + var __JSONRoot = JObject.Parse(FetchedJSON); + var __Build = __JSONRoot["build"]; + string __Version = (string)__Build["version"]; + string __JobsID = (string)__Build["jobs"][0]["jobId"]; + string __Branch = (string)__Build["branch"]; + string __BuildCommit = (string)__Build["commitId"]; + _JobID = __JobsID; + _BuildVer = __Version; + _BuildArt = "https://ci.appveyor.com/api/buildjobs/" + _JobID + "/artifacts/ryujinx-" + _BuildVer + "-" + _PlatformExt; + _BuildCommit = __BuildCommit.Substring(0, 7); + _Branch = __Branch; + Logger.PrintInfo(LogClass.Application, "Fetched JSON and Parsed:" + Environment.NewLine + "MetaData: JobID(" + __JobsID + ") BuildVer(" + __Version + ")" + Environment.NewLine + "BuildURL(" + _BuildArt + ")"); + Logger.PrintInfo(LogClass.Application, "Commit-id: (" + _BuildCommit + ")" + " Branch: (" + _Branch + ")"); + + using (MessageDialog dialog = GtkDialog.CreateAcceptDialog("Update", _BuildVer)) + { + if (dialog.Run() == (int)ResponseType.Yes) + { + dialog.Dispose(); + GrabPackage(); + } + } + + } + catch (Exception ex) + { + Logger.PrintError(LogClass.Application, ex.Message); + GtkDialog.CreateErrorDialog("Update canceled by user or failed to grab or parse the information.\nPlease try at a later time, or report the error to our GitHub."); + return; + } + UpdateData data = new UpdateData() + { + JobID = _JobID, + BuildVer = _BuildVer, + BuildURL = _BuildURL, + BuildArt = _BuildArt, + BuildCommit = _BuildCommit, + Branch = _Branch + }; + } + + private static async void GrabPackage() + { + if (!Directory.Exists(Path.Combine(_RyuDir, "Data", "Update")) || !Directory.Exists(Path.Combine(_RyuDir, "Data"))) + { + Directory.CreateDirectory(Path.Combine(_RyuDir, "Data", "Update")); + Directory.CreateDirectory(Path.Combine(_RyuDir, "Data")); + } + + try + { + _Package.DownloadProgressChanged += new DownloadProgressChangedEventHandler(PackageDownloadProgress); + _Package.DownloadFileCompleted += new AsyncCompletedEventHandler(PackageDownloadedAsync); + using (MessageDialog dialog = await GtkDialog.CreateProgressDialogAsync(false, "Update", "Ryujinx - Update", "Downloading update " + _BuildVer + ", 0% complete...", "Please wait while we download the latest package")) + { + dialog.Run(); + } + } + catch (Exception ex) + { + Logger.PrintError(LogClass.Application, ex.InnerException.ToString()); + GtkDialog.CreateErrorDialog(ex.Message); + return; + } + } + private static async void PackageDownloadedAsync(object sender, AsyncCompletedEventArgs e) + { + if (e.Cancelled == true) + { + Logger.PrintError(LogClass.Application, "Package download failed or cancelled"); + return; + } + else + { + Logger.PrintWarning(LogClass.Application, "Package is now installing"); + using (MessageDialog dialog = await GtkDialog.CreateProgressDialogAsync(true, "Update", "Ryujinx - Update", "Installing update " + _BuildVer + "...", "Please wait while we install the latest package")) + { + dialog.Run(); + } + return; + } + } + + private static void PackageDownloadProgress(object sender, DownloadProgressChangedEventArgs e) + { + _Percentage = e.ProgressPercentage; + _PackageProgress = e.ProgressPercentage; + } + public static async Task ExtractPackageAsync() + { + try + { + //using (ZipFile Package = ZipFile.Read(Path.Combine(_RyuDir, "Data", "Update", "RyujinxPackage.zip"))) + //{ + // await Task.Run(() => Package.ExtractAll(_InstallDir, ExtractExistingFileAction.OverwriteSilently)); + //} + throw new Exception("This is a test exception.\nThis is the package extract thread."); + } + catch (Exception ex) + { + Logger.PrintError(LogClass.Application, "Package installation has failed\n" + ex.InnerException.ToString()); + GtkDialog.CreateErrorDialog("Package installation has failed\nCheck the log for more information."); + return; + } + } + } +} diff --git a/Ryujinx/Updater/UpdateData.cs b/Ryujinx/Updater/UpdateData.cs new file mode 100644 index 0000000000..497b09d2c3 --- /dev/null +++ b/Ryujinx/Updater/UpdateData.cs @@ -0,0 +1,29 @@ +/* ========================================= + * This service is responsible for parsing + * the appveyor json. + * + * ========================================= + * + * Strings and other variables: These are stored for the config page. + * + * JobID: String, stores the parsed JobID. + * BuildVer: String, stores the parsed BuildVersion. + * BuildURL: String, stores the BuildURL. + * BuildArt: String, stores the parsed build artifact (URL). + * BuildCommit: String, stores the parsed build commit; and is stored in a five character substring. + * Branch: String, stores the parsed branch for the build. + * ========================================= + */ + +namespace Ryujinx.Updater +{ + public struct UpdateData + { + public string JobID { get; set; } + public string BuildVer { get; set; } + public string BuildURL { get; set; } + public string BuildArt { get; set; } + public string BuildCommit { get; set; } + public string Branch { get; set; } + } +} \ No newline at end of file