Appveyor Ryujinx Updater (#1403)
Co-authored-by: Xpl0itR <xpl0itr@outlook.com>
This commit is contained in:
parent
4f65043ad7
commit
a15459366e
@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 12;
|
||||
public const int CurrentVersion = 14;
|
||||
|
||||
public int Version { get; set; }
|
||||
|
||||
@ -118,6 +118,11 @@ namespace Ryujinx.Configuration
|
||||
/// </summary>
|
||||
public bool EnableDiscordIntegration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks for updates when Ryujinx starts when enabled
|
||||
/// </summary>
|
||||
public bool CheckUpdatesOnStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Vertical Sync
|
||||
/// </summary>
|
||||
|
@ -343,6 +343,11 @@ namespace Ryujinx.Configuration
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableDiscordIntegration { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks for updates when Ryujinx starts when enabled
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> CheckUpdatesOnStart { get; private set; }
|
||||
|
||||
private ConfigurationState()
|
||||
{
|
||||
Ui = new UiSection();
|
||||
@ -351,6 +356,7 @@ namespace Ryujinx.Configuration
|
||||
Graphics = new GraphicsSection();
|
||||
Hid = new HidSection();
|
||||
EnableDiscordIntegration = new ReactiveObject<bool>();
|
||||
CheckUpdatesOnStart = new ReactiveObject<bool>();
|
||||
}
|
||||
|
||||
public ConfigurationFileFormat ToFileFormat()
|
||||
@ -393,6 +399,7 @@ namespace Ryujinx.Configuration
|
||||
SystemTimeOffset = System.SystemTimeOffset,
|
||||
DockedMode = System.EnableDockedMode,
|
||||
EnableDiscordIntegration = EnableDiscordIntegration,
|
||||
CheckUpdatesOnStart = CheckUpdatesOnStart,
|
||||
EnableVsync = Graphics.EnableVsync,
|
||||
EnableMulticoreScheduling = System.EnableMulticoreScheduling,
|
||||
EnablePtc = System.EnablePtc,
|
||||
@ -452,6 +459,7 @@ namespace Ryujinx.Configuration
|
||||
System.SystemTimeOffset.Value = 0;
|
||||
System.EnableDockedMode.Value = false;
|
||||
EnableDiscordIntegration.Value = true;
|
||||
CheckUpdatesOnStart.Value = true;
|
||||
Graphics.EnableVsync.Value = true;
|
||||
System.EnableMulticoreScheduling.Value = true;
|
||||
System.EnablePtc.Value = false;
|
||||
@ -696,6 +704,15 @@ namespace Ryujinx.Configuration
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
if (configurationFileFormat.Version < 14)
|
||||
{
|
||||
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 14.");
|
||||
|
||||
configurationFileFormat.CheckUpdatesOnStart = true;
|
||||
|
||||
configurationFileUpdated = true;
|
||||
}
|
||||
|
||||
List<InputConfig> inputConfig = new List<InputConfig>();
|
||||
inputConfig.AddRange(configurationFileFormat.ControllerConfig);
|
||||
inputConfig.AddRange(configurationFileFormat.KeyboardConfig);
|
||||
@ -720,6 +737,7 @@ namespace Ryujinx.Configuration
|
||||
System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset;
|
||||
System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
|
||||
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
|
||||
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
|
||||
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
|
||||
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
|
||||
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
|
||||
|
@ -36,6 +36,7 @@ EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
appveyor.yml = appveyor.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": 11,
|
||||
"version": 14,
|
||||
"res_scale": 1,
|
||||
"res_scale_custom": 1,
|
||||
"max_anisotropy": -1,
|
||||
@ -19,6 +19,7 @@
|
||||
"system_time_offset": 0,
|
||||
"docked_mode": false,
|
||||
"enable_discord_integration": true,
|
||||
"check_updates_on_start": true,
|
||||
"enable_vsync": true,
|
||||
"enable_multicore_scheduling": true,
|
||||
"enable_ptc": false,
|
||||
|
@ -10,6 +10,7 @@ using Ryujinx.Ui.Diagnostic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx
|
||||
{
|
||||
@ -44,6 +45,9 @@ namespace Ryujinx
|
||||
}
|
||||
}
|
||||
|
||||
// Delete backup files after updating
|
||||
Task.Run(Updater.CleanupUpdate);
|
||||
|
||||
Toolkit.Init(new ToolkitOptions
|
||||
{
|
||||
Backend = PlatformBackend.PreferNative,
|
||||
@ -122,6 +126,11 @@ namespace Ryujinx
|
||||
mainWindow.LoadApplication(launchPath);
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||
{
|
||||
Updater.BeginParse(mainWindow, false);
|
||||
}
|
||||
|
||||
Application.Run();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
@ -42,6 +42,7 @@
|
||||
<None Remove="Ui\ProfileDialog.glade" />
|
||||
<None Remove="Ui\SettingsWindow.glade" />
|
||||
<None Remove="Ui\TitleUpdateWindow.glade" />
|
||||
<None Remove="Ui\UpdateDialog.glade" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -66,6 +67,7 @@
|
||||
<EmbeddedResource Include="Ui\SettingsWindow.glade" />
|
||||
<EmbeddedResource Include="Ui\DlcWindow.glade" />
|
||||
<EmbeddedResource Include="Ui\TitleUpdateWindow.glade" />
|
||||
<EmbeddedResource Include="Updater\UpdateDialog.glade" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -75,6 +77,7 @@
|
||||
<PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.3.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="OpenTK.NetStandard" Version="1.0.5.12" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -135,7 +135,7 @@ namespace Ryujinx.Ui
|
||||
{
|
||||
if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape))
|
||||
{
|
||||
if (GtkDialog.CreateExitDialog())
|
||||
if (GtkDialog.CreateChoiceDialog("Ryujinx - Exit", "Are you sure you want to stop emulation?", "All unsaved data will be lost!"))
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ namespace Ryujinx.Ui
|
||||
{
|
||||
internal class GtkDialog : MessageDialog
|
||||
{
|
||||
internal static bool _isExitDialogOpen = false;
|
||||
private static bool _isChoiceDialogOpen;
|
||||
|
||||
private GtkDialog(string title, string mainText, string secondaryText,
|
||||
MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok) : base(null, DialogFlags.Modal, messageType, buttonsType, null)
|
||||
private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok)
|
||||
: base(null, DialogFlags.Modal, messageType, buttonsType, null)
|
||||
{
|
||||
Title = title;
|
||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
||||
@ -45,19 +45,16 @@ namespace Ryujinx.Ui
|
||||
return new GtkDialog("Ryujinx - Confirmation", mainText, secondaryText, MessageType.Question, ButtonsType.YesNo);
|
||||
}
|
||||
|
||||
internal static bool CreateExitDialog()
|
||||
internal static bool CreateChoiceDialog(string title, string mainText, string secondaryText)
|
||||
{
|
||||
if (_isExitDialogOpen)
|
||||
{
|
||||
if (_isChoiceDialogOpen)
|
||||
return false;
|
||||
}
|
||||
|
||||
_isExitDialogOpen = true;
|
||||
ResponseType res = (ResponseType)new GtkDialog("Ryujinx - Exit", "Are you sure you want to stop emulation?",
|
||||
"All unsaved data will be lost", MessageType.Question, ButtonsType.YesNo).Run();
|
||||
_isExitDialogOpen = false;
|
||||
_isChoiceDialogOpen = true;
|
||||
ResponseType response = (ResponseType)new GtkDialog(title, mainText, secondaryText, MessageType.Question, ButtonsType.YesNo).Run();
|
||||
_isChoiceDialogOpen = false;
|
||||
|
||||
return res == ResponseType.Yes;
|
||||
return response == ResponseType.Yes;
|
||||
}
|
||||
}
|
||||
}
|
@ -49,36 +49,38 @@ namespace Ryujinx.Ui
|
||||
|
||||
#pragma warning disable CS0169, CS0649, IDE0044
|
||||
|
||||
[GUI] MenuBar _menuBar;
|
||||
[GUI] Box _footerBox;
|
||||
[GUI] Box _statusBar;
|
||||
[GUI] MenuItem _stopEmulation;
|
||||
[GUI] MenuItem _fullScreen;
|
||||
[GUI] CheckMenuItem _favToggle;
|
||||
[GUI] MenuItem _firmwareInstallDirectory;
|
||||
[GUI] MenuItem _firmwareInstallFile;
|
||||
[GUI] Label _hostStatus;
|
||||
[GUI] CheckMenuItem _iconToggle;
|
||||
[GUI] CheckMenuItem _developerToggle;
|
||||
[GUI] CheckMenuItem _appToggle;
|
||||
[GUI] CheckMenuItem _timePlayedToggle;
|
||||
[GUI] CheckMenuItem _versionToggle;
|
||||
[GUI] CheckMenuItem _lastPlayedToggle;
|
||||
[GUI] CheckMenuItem _fileExtToggle;
|
||||
[GUI] CheckMenuItem _pathToggle;
|
||||
[GUI] CheckMenuItem _fileSizeToggle;
|
||||
[GUI] Label _dockedMode;
|
||||
[GUI] Label _gameStatus;
|
||||
[GUI] TreeView _gameTable;
|
||||
[GUI] TreeSelection _gameTableSelection;
|
||||
[GUI] ScrolledWindow _gameTableWindow;
|
||||
[GUI] Label _gpuName;
|
||||
[GUI] Label _progressLabel;
|
||||
[GUI] Label _firmwareVersionLabel;
|
||||
[GUI] LevelBar _progressBar;
|
||||
[GUI] Box _viewBox;
|
||||
[GUI] Label _vSyncStatus;
|
||||
[GUI] Box _listStatusBox;
|
||||
[GUI] public MenuItem ExitMenuItem;
|
||||
[GUI] public MenuItem UpdateMenuItem;
|
||||
[GUI] MenuBar _menuBar;
|
||||
[GUI] Box _footerBox;
|
||||
[GUI] Box _statusBar;
|
||||
[GUI] MenuItem _stopEmulation;
|
||||
[GUI] MenuItem _fullScreen;
|
||||
[GUI] CheckMenuItem _favToggle;
|
||||
[GUI] MenuItem _firmwareInstallDirectory;
|
||||
[GUI] MenuItem _firmwareInstallFile;
|
||||
[GUI] Label _hostStatus;
|
||||
[GUI] CheckMenuItem _iconToggle;
|
||||
[GUI] CheckMenuItem _developerToggle;
|
||||
[GUI] CheckMenuItem _appToggle;
|
||||
[GUI] CheckMenuItem _timePlayedToggle;
|
||||
[GUI] CheckMenuItem _versionToggle;
|
||||
[GUI] CheckMenuItem _lastPlayedToggle;
|
||||
[GUI] CheckMenuItem _fileExtToggle;
|
||||
[GUI] CheckMenuItem _pathToggle;
|
||||
[GUI] CheckMenuItem _fileSizeToggle;
|
||||
[GUI] Label _dockedMode;
|
||||
[GUI] Label _gameStatus;
|
||||
[GUI] TreeView _gameTable;
|
||||
[GUI] TreeSelection _gameTableSelection;
|
||||
[GUI] ScrolledWindow _gameTableWindow;
|
||||
[GUI] Label _gpuName;
|
||||
[GUI] Label _progressLabel;
|
||||
[GUI] Label _firmwareVersionLabel;
|
||||
[GUI] LevelBar _progressBar;
|
||||
[GUI] Box _viewBox;
|
||||
[GUI] Label _vSyncStatus;
|
||||
[GUI] Box _listStatusBox;
|
||||
|
||||
#pragma warning restore CS0649, IDE0044, CS0169
|
||||
|
||||
@ -1163,15 +1165,9 @@ namespace Ryujinx.Ui
|
||||
|
||||
private void Update_Pressed(object sender, EventArgs args)
|
||||
{
|
||||
string ryuUpdater = System.IO.Path.Combine(AppDataManager.BaseDirPath, "RyuUpdater.exe");
|
||||
|
||||
try
|
||||
if (Updater.CanUpdate(true))
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(ryuUpdater, "/U") { UseShellExecute = true });
|
||||
}
|
||||
catch(System.ComponentModel.Win32Exception)
|
||||
{
|
||||
GtkDialog.CreateErrorDialog("Update canceled by user or updater was not found");
|
||||
Updater.BeginParse(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="Exit">
|
||||
<object class="GtkMenuItem" id="ExitMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Exit Ryujinx</property>
|
||||
@ -320,10 +320,10 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="CheckUpdates">
|
||||
<object class="GtkMenuItem" id="UpdateMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Check for updates to Ryujinx (requires Ryujinx Installer)</property>
|
||||
<property name="tooltip_text" translatable="yes">Check for updates to Ryujinx</property>
|
||||
<property name="label" translatable="yes">Check for Updates</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="Update_Pressed" swapped="no"/>
|
||||
|
@ -40,6 +40,7 @@ namespace Ryujinx.Ui
|
||||
[GUI] ComboBoxText _graphicsDebugLevel;
|
||||
[GUI] CheckButton _dockedModeToggle;
|
||||
[GUI] CheckButton _discordToggle;
|
||||
[GUI] CheckButton _checkUpdatesToggle;
|
||||
[GUI] CheckButton _vSyncToggle;
|
||||
[GUI] CheckButton _multiSchedToggle;
|
||||
[GUI] CheckButton _ptcToggle;
|
||||
@ -170,6 +171,11 @@ namespace Ryujinx.Ui
|
||||
_discordToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart)
|
||||
{
|
||||
_checkUpdatesToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Graphics.EnableVsync)
|
||||
{
|
||||
_vSyncToggle.Click();
|
||||
@ -519,6 +525,7 @@ namespace Ryujinx.Ui
|
||||
ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse<GraphicsDebugLevel>(_graphicsDebugLevel.ActiveId);
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
|
||||
ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
|
||||
ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active;
|
||||
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
|
||||
ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
|
||||
ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active;
|
||||
|
@ -121,7 +121,23 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="_checkUpdatesToggle">
|
||||
<property name="label" translatable="yes">Check for updates on launch</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
86
Ryujinx/Updater/UpdateDialog.cs
Normal file
86
Ryujinx/Updater/UpdateDialog.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using Gdk;
|
||||
using Gtk;
|
||||
using Mono.Unix;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Ui
|
||||
{
|
||||
public class UpdateDialog : Gtk.Window
|
||||
{
|
||||
#pragma warning disable CS0649, IDE0044
|
||||
[Builder.Object] public Label MainText;
|
||||
[Builder.Object] public Label SecondaryText;
|
||||
[Builder.Object] public LevelBar ProgressBar;
|
||||
[Builder.Object] public Button YesButton;
|
||||
[Builder.Object] public Button NoButton;
|
||||
#pragma warning restore CS0649, IDE0044
|
||||
|
||||
private readonly MainWindow _mainWindow;
|
||||
private readonly string _buildUrl;
|
||||
private bool _restartQuery;
|
||||
|
||||
public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { }
|
||||
|
||||
private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetObject("UpdateDialog").Handle)
|
||||
{
|
||||
builder.Autoconnect(this);
|
||||
|
||||
_mainWindow = mainWindow;
|
||||
_buildUrl = buildUrl;
|
||||
|
||||
MainText.Text = "Do you want to update Ryujinx to the latest version?";
|
||||
SecondaryText.Text = $"{Program.Version} -> {newVersion}";
|
||||
|
||||
ProgressBar.Hide();
|
||||
|
||||
YesButton.Pressed += YesButton_Pressed;
|
||||
NoButton.Pressed += NoButton_Pressed;
|
||||
}
|
||||
|
||||
private void YesButton_Pressed(object sender, EventArgs args)
|
||||
{
|
||||
if (_restartQuery)
|
||||
{
|
||||
string ryuName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Ryujinx.exe" : "Ryujinx";
|
||||
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
UnixFileInfo unixFileInfo = new UnixFileInfo(ryuExe);
|
||||
unixFileInfo.FileAccessPermissions |= FileAccessPermissions.UserExecute;
|
||||
}
|
||||
|
||||
Process.Start(ryuExe);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Window.Functions = _mainWindow.Window.Functions = WMFunction.All & WMFunction.Close;
|
||||
_mainWindow.ExitMenuItem.Sensitive = false;
|
||||
|
||||
YesButton.Hide();
|
||||
NoButton.Hide();
|
||||
ProgressBar.Show();
|
||||
|
||||
SecondaryText.Text = "";
|
||||
_restartQuery = true;
|
||||
|
||||
Updater.UpdateRyujinx(this, _buildUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void NoButton_Pressed(object sender, EventArgs args)
|
||||
{
|
||||
Updater.Running = false;
|
||||
_mainWindow.Window.Functions = WMFunction.All;
|
||||
|
||||
_mainWindow.ExitMenuItem.Sensitive = true;
|
||||
_mainWindow.UpdateMenuItem.Sensitive = true;
|
||||
|
||||
this.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
127
Ryujinx/Updater/UpdateDialog.glade
Normal file
127
Ryujinx/Updater/UpdateDialog.glade
Normal file
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkWindow" id="UpdateDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Ryujinx - Updater</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="default_width">400</property>
|
||||
<property name="default_height">130</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="MainBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="BodyBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="MainText">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
<attribute name="size" value="10000"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="SecondaryText">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLevelBar" id="ProgressBar">
|
||||
<property name="height_request">20</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="max_value">100</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="ButtonBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="YesButton">
|
||||
<property name="label" translatable="yes">Yes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="NoButton">
|
||||
<property name="label" translatable="yes">No</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
347
Ryujinx/Updater/Updater.cs
Normal file
347
Ryujinx/Updater/Updater.cs
Normal file
@ -0,0 +1,347 @@
|
||||
using Gtk;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using ICSharpCode.SharpZipLib.Tar;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx
|
||||
{
|
||||
public static class Updater
|
||||
{
|
||||
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 string _jobId;
|
||||
private static string _buildVer;
|
||||
private static string _platformExt;
|
||||
private static string _buildUrl;
|
||||
|
||||
private const string AppveyorApiUrl = "https://ci.appveyor.com/api";
|
||||
|
||||
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
|
||||
{
|
||||
if (Running) return;
|
||||
|
||||
Running = true;
|
||||
mainWindow.UpdateMenuItem.Sensitive = false;
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
Version newVersion;
|
||||
Version currentVersion;
|
||||
|
||||
try
|
||||
{
|
||||
currentVersion = Version.Parse(Program.Version);
|
||||
}
|
||||
catch
|
||||
{
|
||||
GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!");
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get latest version number from Appveyor
|
||||
try
|
||||
{
|
||||
using (WebClient jsonClient = new WebClient())
|
||||
{
|
||||
string fetchedJson = await jsonClient.DownloadStringTaskAsync($"{AppveyorApiUrl}/projects/gdkchan/ryujinx/branch/master");
|
||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
||||
JToken buildToken = jsonRoot["build"];
|
||||
|
||||
_jobId = (string)buildToken["jobs"][0]["jobId"];
|
||||
_buildVer = (string)buildToken["version"];
|
||||
_buildUrl = $"{AppveyorApiUrl}/buildjobs/{_jobId}/artifacts/ryujinx-{_buildVer}-{_platformExt}";
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||
GtkDialog.CreateErrorDialog("An error has occurred when trying to get release information from AppVeyor.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
newVersion = Version.Parse(_buildVer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from AppVeyor.", "Cancelling Update!");
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from AppVeyor!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (newVersion <= currentVersion)
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
GtkDialog.CreateInfoDialog("Ryujinx - Updater", "You are already using the most updated version of Ryujinx!", "");
|
||||
}
|
||||
|
||||
Running = false;
|
||||
mainWindow.UpdateMenuItem.Sensitive = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Show a message asking the user if they want to update
|
||||
UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl);
|
||||
updateDialog.Show();
|
||||
}
|
||||
|
||||
public static async Task UpdateRyujinx(UpdateDialog updateDialog, string downloadUrl)
|
||||
{
|
||||
// Empty update dir, although it shouldn't ever have anything inside it
|
||||
if (Directory.Exists(UpdateDir))
|
||||
{
|
||||
Directory.Delete(UpdateDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(UpdateDir);
|
||||
|
||||
string updateFile = Path.Combine(UpdateDir, "update.bin");
|
||||
|
||||
// Download the update .zip
|
||||
updateDialog.MainText.Text = "Downloading Update...";
|
||||
updateDialog.ProgressBar.Value = 0;
|
||||
updateDialog.ProgressBar.MaxValue = 100;
|
||||
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
client.DownloadProgressChanged += (_, args) =>
|
||||
{
|
||||
updateDialog.ProgressBar.Value = args.ProgressPercentage;
|
||||
};
|
||||
|
||||
await client.DownloadFileTaskAsync(downloadUrl, updateFile);
|
||||
}
|
||||
|
||||
// Extract Update
|
||||
updateDialog.MainText.Text = "Extracting Update...";
|
||||
updateDialog.ProgressBar.Value = 0;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
using (Stream inStream = File.OpenRead(updateFile))
|
||||
using (Stream gzipStream = new GZipInputStream(inStream))
|
||||
using (TarInputStream tarStream = new TarInputStream(gzipStream))
|
||||
{
|
||||
updateDialog.ProgressBar.MaxValue = inStream.Length;
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
TarEntry tarEntry;
|
||||
while ((tarEntry = tarStream.GetNextEntry()) != null)
|
||||
{
|
||||
if (tarEntry.IsDirectory) continue;
|
||||
|
||||
string outPath = Path.Combine(UpdateDir, tarEntry.Name);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
|
||||
using (FileStream outStream = File.OpenWrite(outPath))
|
||||
{
|
||||
tarStream.CopyEntryContents(outStream);
|
||||
}
|
||||
|
||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
||||
|
||||
TarEntry entry = tarEntry;
|
||||
|
||||
Application.Invoke(delegate
|
||||
{
|
||||
updateDialog.ProgressBar.Value += entry.Size;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
updateDialog.ProgressBar.Value = inStream.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream inStream = File.OpenRead(updateFile))
|
||||
using (ZipFile zipFile = new ZipFile(inStream))
|
||||
{
|
||||
updateDialog.ProgressBar.MaxValue = zipFile.Count;
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
foreach (ZipEntry zipEntry in zipFile)
|
||||
{
|
||||
if (zipEntry.IsDirectory) continue;
|
||||
|
||||
string outPath = Path.Combine(UpdateDir, zipEntry.Name);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||
|
||||
using (Stream zipStream = zipFile.GetInputStream(zipEntry))
|
||||
using (FileStream outStream = File.OpenWrite(outPath))
|
||||
{
|
||||
zipStream.CopyTo(outStream);
|
||||
}
|
||||
|
||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
|
||||
|
||||
Application.Invoke(delegate
|
||||
{
|
||||
updateDialog.ProgressBar.Value++;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Delete downloaded zip
|
||||
File.Delete(updateFile);
|
||||
|
||||
string[] allFiles = Directory.GetFiles(HomeDir, "*", SearchOption.AllDirectories);
|
||||
|
||||
updateDialog.MainText.Text = "Renaming Old Files...";
|
||||
updateDialog.ProgressBar.Value = 0;
|
||||
updateDialog.ProgressBar.MaxValue = allFiles.Length;
|
||||
|
||||
// Replace old files
|
||||
await Task.Run(() =>
|
||||
{
|
||||
foreach (string file in allFiles)
|
||||
{
|
||||
if (!Path.GetExtension(file).Equals(".log"))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Move(file, file + ".ryuold");
|
||||
|
||||
Application.Invoke(delegate
|
||||
{
|
||||
updateDialog.ProgressBar.Value++;
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, "Updater wasn't able to rename file: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Application.Invoke(delegate
|
||||
{
|
||||
updateDialog.MainText.Text = "Adding New Files...";
|
||||
updateDialog.ProgressBar.Value = 0;
|
||||
updateDialog.ProgressBar.MaxValue = Directory.GetFiles(UpdatePublishDir, "*", SearchOption.AllDirectories).Length;
|
||||
});
|
||||
|
||||
MoveAllFilesOver(UpdatePublishDir, HomeDir, updateDialog);
|
||||
});
|
||||
|
||||
Directory.Delete(UpdateDir, true);
|
||||
|
||||
updateDialog.MainText.Text = "Update Complete!";
|
||||
updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?";
|
||||
updateDialog.Modal = true;
|
||||
|
||||
updateDialog.ProgressBar.Hide();
|
||||
updateDialog.YesButton.Show();
|
||||
updateDialog.NoButton.Show();
|
||||
}
|
||||
|
||||
public static bool CanUpdate(bool showWarnings)
|
||||
{
|
||||
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
GtkDialog.CreateWarningDialog("You are not running a supported system architecture!", "(Only x64 systems are supported!)");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
GtkDialog.CreateWarningDialog("You are not connected to the Internet!", "Please verify that you have a working Internet connection!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Program.Version.Contains("dirty"))
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
GtkDialog.CreateWarningDialog("You Cannot update a Dirty build of Ryujinx!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void MoveAllFilesOver(string root, string dest, UpdateDialog dialog)
|
||||
{
|
||||
foreach (string directory in Directory.GetDirectories(root))
|
||||
{
|
||||
string dirName = Path.GetFileName(directory);
|
||||
|
||||
if (!Directory.Exists(Path.Combine(dest, dirName)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(dest, dirName));
|
||||
}
|
||||
|
||||
MoveAllFilesOver(directory, Path.Combine(dest, dirName), dialog);
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(root))
|
||||
{
|
||||
File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true);
|
||||
|
||||
Application.Invoke(delegate
|
||||
{
|
||||
dialog.ProgressBar.Value++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void CleanupUpdate()
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(HomeDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (Path.GetExtension(file).EndsWith(".ryuold"))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -719,7 +719,7 @@
|
||||
"type": "number",
|
||||
"title": "Custom Resolution Scale",
|
||||
"description": "A custom floating point scale applied to applicable render targets. Only active when Resolution Scale is -1.",
|
||||
"default": 1.0,
|
||||
"default": 1.0
|
||||
},
|
||||
"max_anisotropy": {
|
||||
"$id": "#/properties/max_anisotropy",
|
||||
@ -966,6 +966,17 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"check_updates_on_start": {
|
||||
"$id": "#/properties/check_updates_on_start",
|
||||
"type": "boolean",
|
||||
"title": "Checks for updates when ryujinx starts when enabled",
|
||||
"description": "Checks for updates when ryujinx starts when enabled",
|
||||
"default": true,
|
||||
"examples": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"enable_vsync": {
|
||||
"$id": "#/properties/enable_vsync",
|
||||
"type": "boolean",
|
||||
|
Loading…
Reference in New Issue
Block a user