2020-09-29 20:05:25 +00:00
using Gtk ;
using ICSharpCode.SharpZipLib.GZip ;
using ICSharpCode.SharpZipLib.Tar ;
using ICSharpCode.SharpZipLib.Zip ;
2022-01-22 16:56:09 +00:00
using Ryujinx.Common ;
2020-09-29 20:05:25 +00:00
using Ryujinx.Common.Logging ;
2023-03-21 22:41:19 +00:00
using Ryujinx.Common.Utilities ;
2020-09-29 20:05:25 +00:00
using Ryujinx.Ui ;
2023-03-21 22:41:19 +00:00
using Ryujinx.Ui.Common.Models.Github ;
2021-01-08 08:14:13 +00:00
using Ryujinx.Ui.Widgets ;
2020-09-29 20:05:25 +00:00
using System ;
2021-02-22 20:48:45 +00:00
using System.Collections.Generic ;
2020-09-29 20:05:25 +00:00
using System.IO ;
2021-02-23 15:19:02 +00:00
using System.Linq ;
2020-09-29 20:05:25 +00:00
using System.Net ;
2021-11-28 20:24:17 +00:00
using System.Net.Http ;
2020-09-29 20:05:25 +00:00
using System.Net.NetworkInformation ;
using System.Runtime.InteropServices ;
2020-11-27 17:57:20 +00:00
using System.Text ;
2021-02-22 20:48:45 +00:00
using System.Threading ;
2020-09-29 20:05:25 +00:00
using System.Threading.Tasks ;
2021-01-08 08:14:13 +00:00
namespace Ryujinx.Modules
2020-09-29 20:05:25 +00:00
{
public static class Updater
{
2023-02-15 22:36:35 +00:00
private const string GitHubApiURL = "https://api.github.com" ;
private const int ConnectionCount = 4 ;
2020-09-29 20:05:25 +00:00
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 _buildVer ;
private static string _platformExt ;
private static string _buildUrl ;
2021-02-22 20:48:45 +00:00
private static long _buildSize ;
2022-01-22 16:56:09 +00:00
2023-03-21 22:41:19 +00:00
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new ( JsonHelper . GetDefaultSerializerOptions ( ) ) ;
2021-02-23 15:19:02 +00:00
// 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" } ;
2022-01-22 16:56:09 +00:00
private static HttpClient ConstructHttpClient ( )
{
HttpClient result = new HttpClient ( ) ;
2023-02-15 22:36:35 +00:00
// Required by GitHub to interact with APIs.
2022-01-22 16:56:09 +00:00
result . DefaultRequestHeaders . Add ( "User-Agent" , "Ryujinx-Updater/1.0.0" ) ;
return result ;
}
2020-09-29 20:05:25 +00:00
public static async Task BeginParse ( MainWindow mainWindow , bool showVersionUpToDate )
{
if ( Running ) return ;
Running = true ;
mainWindow . UpdateMenuItem . Sensitive = false ;
2021-02-22 20:48:45 +00:00
int artifactIndex = - 1 ;
2020-09-29 20:05:25 +00:00
// Detect current platform
2021-12-04 23:02:30 +00:00
if ( OperatingSystem . IsMacOS ( ) )
2020-09-29 20:05:25 +00:00
{
2021-02-22 20:48:45 +00:00
_platformExt = "osx_x64.zip" ;
artifactIndex = 1 ;
2020-09-29 20:05:25 +00:00
}
2021-12-04 23:02:30 +00:00
else if ( OperatingSystem . IsWindows ( ) )
2020-09-29 20:05:25 +00:00
{
2021-02-22 20:48:45 +00:00
_platformExt = "win_x64.zip" ;
artifactIndex = 2 ;
2020-09-29 20:05:25 +00:00
}
2021-12-04 23:02:30 +00:00
else if ( OperatingSystem . IsLinux ( ) )
2020-09-29 20:05:25 +00:00
{
2021-02-22 20:48:45 +00:00
_platformExt = "linux_x64.tar.gz" ;
artifactIndex = 0 ;
}
if ( artifactIndex = = - 1 )
{
GtkDialog . CreateErrorDialog ( "Your platform is not supported!" ) ;
return ;
2020-09-29 20:05:25 +00:00
}
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 ;
}
2022-01-22 16:56:09 +00:00
// Get latest version number from GitHub API
2020-09-29 20:05:25 +00:00
try
{
2023-02-15 22:36:35 +00:00
using HttpClient jsonClient = ConstructHttpClient ( ) ;
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest" ;
// Fetch latest build information
string fetchedJson = await jsonClient . GetStringAsync ( buildInfoURL ) ;
2023-03-21 22:41:19 +00:00
var fetched = JsonHelper . Deserialize ( fetchedJson , SerializerContext . GithubReleasesJsonResponse ) ;
_buildVer = fetched . Name ;
2022-01-22 16:56:09 +00:00
2023-03-21 22:41:19 +00:00
foreach ( var asset in fetched . Assets )
2023-02-15 22:36:35 +00:00
{
2023-03-21 22:41:19 +00:00
if ( asset . Name . StartsWith ( "ryujinx" ) & & asset . Name . EndsWith ( _platformExt ) )
2022-01-22 16:56:09 +00:00
{
2023-03-21 22:41:19 +00:00
_buildUrl = asset . BrowserDownloadUrl ;
2020-09-29 20:05:25 +00:00
2023-03-21 22:41:19 +00:00
if ( asset . State ! = "uploaded" )
2022-01-22 16:56:09 +00:00
{
2023-02-15 22:36:35 +00:00
if ( showVersionUpToDate )
2022-01-22 16:56:09 +00:00
{
2023-02-15 22:36:35 +00:00
GtkDialog . CreateUpdaterInfoDialog ( "You are already using the latest version of Ryujinx!" , "" ) ;
2022-01-22 16:56:09 +00:00
}
2023-02-15 22:36:35 +00:00
return ;
2022-01-22 16:56:09 +00:00
}
2023-02-15 22:36:35 +00:00
break ;
2022-01-22 16:56:09 +00:00
}
2023-02-15 22:36:35 +00:00
}
2020-09-29 20:52:41 +00:00
2023-02-15 22:36:35 +00:00
if ( _buildUrl = = null )
{
if ( showVersionUpToDate )
2020-09-29 20:52:41 +00:00
{
2023-02-15 22:36:35 +00:00
GtkDialog . CreateUpdaterInfoDialog ( "You are already using the latest version of Ryujinx!" , "" ) ;
2020-09-29 20:52:41 +00:00
}
2023-02-15 22:36:35 +00:00
return ;
2020-09-29 20:05:25 +00:00
}
}
catch ( Exception exception )
{
Logger . Error ? . Print ( LogClass . Application , exception . Message ) ;
2022-01-22 16:56:09 +00:00
GtkDialog . CreateErrorDialog ( "An error occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes." ) ;
2020-09-29 20:05:25 +00:00
return ;
}
try
{
newVersion = Version . Parse ( _buildVer ) ;
}
catch
{
2022-01-22 16:56:09 +00:00
GtkDialog . CreateWarningDialog ( "Failed to convert the received Ryujinx version from GitHub Release." , "Cancelling Update!" ) ;
Logger . Error ? . Print ( LogClass . Application , "Failed to convert the received Ryujinx version from GitHub Release!" ) ;
2020-09-29 20:05:25 +00:00
return ;
}
if ( newVersion < = currentVersion )
{
if ( showVersionUpToDate )
{
2021-02-28 10:33:36 +00:00
GtkDialog . CreateUpdaterInfoDialog ( "You are already using the latest version of Ryujinx!" , "" ) ;
2020-09-29 20:05:25 +00:00
}
Running = false ;
mainWindow . UpdateMenuItem . Sensitive = true ;
return ;
}
2021-02-22 20:48:45 +00:00
// Fetch build size information to learn chunk sizes.
2022-01-22 16:56:09 +00:00
using ( HttpClient buildSizeClient = ConstructHttpClient ( ) )
2021-11-28 20:24:17 +00:00
{
2021-02-22 20:48:45 +00:00
try
{
2021-11-28 20:24:17 +00:00
buildSizeClient . DefaultRequestHeaders . Add ( "Range" , "bytes=0-0" ) ;
HttpResponseMessage message = await buildSizeClient . GetAsync ( new Uri ( _buildUrl ) , HttpCompletionOption . ResponseHeadersRead ) ;
2021-02-22 20:48:45 +00:00
2021-11-28 20:24:17 +00:00
_buildSize = message . Content . Headers . ContentRange . Length . Value ;
2021-02-22 20:48:45 +00:00
}
catch ( Exception ex )
{
Logger . Warning ? . Print ( LogClass . Application , ex . Message ) ;
2021-08-20 21:48:00 +00:00
Logger . Warning ? . Print ( LogClass . Application , "Couldn't determine build size for update, using single-threaded updater" ) ;
2021-02-22 20:48:45 +00:00
_buildSize = - 1 ;
}
}
2020-09-29 20:05:25 +00:00
// Show a message asking the user if they want to update
UpdateDialog updateDialog = new UpdateDialog ( mainWindow , newVersion , _buildUrl ) ;
updateDialog . Show ( ) ;
}
2021-02-22 20:48:45 +00:00
public static void UpdateRyujinx ( UpdateDialog updateDialog , string downloadUrl )
2020-09-29 20:05:25 +00:00
{
// 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 ;
2021-02-22 20:48:45 +00:00
if ( _buildSize > = 0 )
{
DoUpdateWithMultipleThreads ( updateDialog , downloadUrl , updateFile ) ;
}
else
{
DoUpdateWithSingleThread ( updateDialog , downloadUrl , updateFile ) ;
}
}
private static void DoUpdateWithMultipleThreads ( UpdateDialog updateDialog , string downloadUrl , string updateFile )
{
// Multi-Threaded Updater
long chunkSize = _buildSize / ConnectionCount ;
long remainderChunk = _buildSize % ConnectionCount ;
int completedRequests = 0 ;
int totalProgressPercentage = 0 ;
int [ ] progressPercentage = new int [ ConnectionCount ] ;
List < byte [ ] > list = new List < byte [ ] > ( ConnectionCount ) ;
List < WebClient > webClients = new List < WebClient > ( ConnectionCount ) ;
for ( int i = 0 ; i < ConnectionCount ; i + + )
{
2023-02-15 22:36:35 +00:00
list . Add ( Array . Empty < byte > ( ) ) ;
2021-02-22 20:48:45 +00:00
}
for ( int i = 0 ; i < ConnectionCount ; i + + )
{
2021-11-28 20:24:17 +00:00
#pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
2023-02-15 22:36:35 +00:00
using WebClient client = new WebClient ( ) ;
2021-11-28 20:24:17 +00:00
#pragma warning restore SYSLIB0014
2023-02-15 22:36:35 +00:00
webClients . Add ( client ) ;
if ( i = = ConnectionCount - 1 )
2021-02-22 20:48:45 +00:00
{
2023-02-15 22:36:35 +00:00
client . Headers . Add ( "Range" , $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}" ) ;
}
else
{
client . Headers . Add ( "Range" , $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}" ) ;
}
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
client . DownloadProgressChanged + = ( _ , args ) = >
{
int index = ( int ) args . UserState ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
Interlocked . Add ( ref totalProgressPercentage , - 1 * progressPercentage [ index ] ) ;
Interlocked . Exchange ( ref progressPercentage [ index ] , args . ProgressPercentage ) ;
Interlocked . Add ( ref totalProgressPercentage , args . ProgressPercentage ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
updateDialog . ProgressBar . Value = totalProgressPercentage / ConnectionCount ;
} ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
client . DownloadDataCompleted + = ( _ , args ) = >
{
int index = ( int ) args . UserState ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
if ( args . Cancelled )
2021-02-22 20:48:45 +00:00
{
2023-02-15 22:36:35 +00:00
webClients [ index ] . Dispose ( ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
return ;
}
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
list [ index ] = args . Result ;
Interlocked . Increment ( ref completedRequests ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
if ( Equals ( completedRequests , ConnectionCount ) )
{
byte [ ] mergedFileBytes = new byte [ _buildSize ] ;
for ( int connectionIndex = 0 , destinationOffset = 0 ; connectionIndex < ConnectionCount ; connectionIndex + + )
2021-02-22 20:48:45 +00:00
{
2023-02-15 22:36:35 +00:00
Array . Copy ( list [ connectionIndex ] , 0 , mergedFileBytes , destinationOffset , list [ connectionIndex ] . Length ) ;
destinationOffset + = list [ connectionIndex ] . Length ;
}
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
File . WriteAllBytes ( updateFile , mergedFileBytes ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
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." ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
DoUpdateWithSingleThread ( updateDialog , downloadUrl , updateFile ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
return ;
2021-02-22 20:48:45 +00:00
}
2023-02-15 22:36:35 +00:00
}
} ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
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 )
2021-02-22 20:48:45 +00:00
{
2023-02-15 22:36:35 +00:00
webClient . CancelAsync ( ) ;
2021-02-22 20:48:45 +00:00
}
2023-02-15 22:36:35 +00:00
DoUpdateWithSingleThread ( updateDialog , downloadUrl , updateFile ) ;
2021-02-22 20:48:45 +00:00
2023-02-15 22:36:35 +00:00
return ;
2021-02-22 20:48:45 +00:00
}
}
}
2021-11-28 20:24:17 +00:00
private static void DoUpdateWithSingleThreadWorker ( UpdateDialog updateDialog , string downloadUrl , string updateFile )
2021-02-22 20:48:45 +00:00
{
2023-02-15 22:36:35 +00:00
using HttpClient client = new HttpClient ( ) ;
// We do not want to timeout while downloading
client . Timeout = TimeSpan . FromDays ( 1 ) ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
using ( HttpResponseMessage response = client . GetAsync ( downloadUrl , HttpCompletionOption . ResponseHeadersRead ) . Result )
using ( Stream remoteFileStream = response . Content . ReadAsStreamAsync ( ) . Result )
{
using ( Stream updateFileStream = File . Open ( updateFile , FileMode . Create ) )
2021-02-22 20:48:45 +00:00
{
2023-02-15 22:36:35 +00:00
long totalBytes = response . Content . Headers . ContentLength . Value ;
long byteWritten = 0 ;
2021-11-28 20:24:17 +00:00
2023-02-15 22:36:35 +00:00
byte [ ] buffer = new byte [ 32 * 1024 ] ;
2021-11-28 20:24:17 +00:00
2023-02-15 22:36:35 +00:00
while ( true )
{
int readSize = remoteFileStream . Read ( buffer ) ;
2021-11-28 20:24:17 +00:00
2023-02-15 22:36:35 +00:00
if ( readSize = = 0 )
{
break ;
}
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
byteWritten + = readSize ;
2021-11-28 20:24:17 +00:00
2023-02-15 22:36:35 +00:00
updateDialog . ProgressBar . Value = ( ( double ) byteWritten / totalBytes ) * 100 ;
updateFileStream . Write ( buffer , 0 , readSize ) ;
2021-11-28 20:24:17 +00:00
}
}
2021-02-22 20:48:45 +00:00
}
2021-11-28 20:24:17 +00:00
2023-02-15 22:36:35 +00:00
InstallUpdate ( updateDialog , updateFile ) ;
2021-11-28 20:24:17 +00:00
}
2023-02-15 22:36:35 +00:00
private static void DoUpdateWithSingleThread ( UpdateDialog updateDialog , string downloadUrl , string updateFile )
2021-02-24 23:35:48 +00:00
{
2023-02-15 22:36:35 +00:00
Thread worker = new Thread ( ( ) = > DoUpdateWithSingleThreadWorker ( updateDialog , downloadUrl , updateFile ) )
2021-02-24 23:35:48 +00:00
{
2023-02-15 22:36:35 +00:00
Name = "Updater.SingleThreadWorker"
} ;
worker . Start ( ) ;
2021-02-24 23:35:48 +00:00
}
2021-02-22 20:48:45 +00:00
private static async void InstallUpdate ( UpdateDialog updateDialog , string updateFile )
{
2020-09-29 20:05:25 +00:00
// Extract Update
updateDialog . MainText . Text = "Extracting Update..." ;
updateDialog . ProgressBar . Value = 0 ;
2021-12-04 23:02:30 +00:00
if ( OperatingSystem . IsLinux ( ) )
2020-09-29 20:05:25 +00:00
{
2023-02-15 22:36:35 +00:00
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 ( ( ) = >
2020-09-29 20:05:25 +00:00
{
2023-02-15 22:36:35 +00:00
TarEntry tarEntry ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
if ( ! OperatingSystem . IsWindows ( ) )
2020-09-29 20:05:25 +00:00
{
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 ) ;
}
2023-02-15 22:36:35 +00:00
File . SetUnixFileMode ( outPath , ( UnixFileMode ) tarEntry . TarHeader . Mode ) ;
2020-09-29 20:05:25 +00:00
File . SetLastWriteTime ( outPath , DateTime . SpecifyKind ( tarEntry . ModTime , DateTimeKind . Utc ) ) ;
TarEntry entry = tarEntry ;
Application . Invoke ( delegate
{
updateDialog . ProgressBar . Value + = entry . Size ;
} ) ;
}
2023-02-15 22:36:35 +00:00
}
} ) ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
updateDialog . ProgressBar . Value = inStream . Length ;
2020-09-29 20:05:25 +00:00
}
else
{
2023-02-15 22:36:35 +00:00
using Stream inStream = File . OpenRead ( updateFile ) ;
using ZipFile zipFile = new ZipFile ( inStream ) ;
updateDialog . ProgressBar . MaxValue = zipFile . Count ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
await Task . Run ( ( ) = >
{
foreach ( ZipEntry zipEntry in zipFile )
2020-09-29 20:05:25 +00:00
{
2023-02-15 22:36:35 +00:00
if ( zipEntry . IsDirectory ) continue ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
string outPath = Path . Combine ( UpdateDir , zipEntry . Name ) ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
Directory . CreateDirectory ( Path . GetDirectoryName ( outPath ) ) ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
using ( Stream zipStream = zipFile . GetInputStream ( zipEntry ) )
using ( FileStream outStream = File . OpenWrite ( outPath ) )
{
zipStream . CopyTo ( outStream ) ;
}
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
File . SetLastWriteTime ( outPath , DateTime . SpecifyKind ( zipEntry . DateTime , DateTimeKind . Utc ) ) ;
2020-09-29 20:05:25 +00:00
2023-02-15 22:36:35 +00:00
Application . Invoke ( delegate
{
updateDialog . ProgressBar . Value + + ;
} ) ;
}
} ) ;
2020-09-29 20:05:25 +00:00
}
// Delete downloaded zip
File . Delete ( updateFile ) ;
2021-02-23 15:19:02 +00:00
List < string > allFiles = EnumerateFilesToDelete ( ) . ToList ( ) ;
2020-09-29 20:05:25 +00:00
updateDialog . MainText . Text = "Renaming Old Files..." ;
updateDialog . ProgressBar . Value = 0 ;
2021-02-23 15:19:02 +00:00
updateDialog . ProgressBar . MaxValue = allFiles . Count ;
2020-09-29 20:05:25 +00:00
// Replace old files
await Task . Run ( ( ) = >
{
foreach ( string file in allFiles )
{
2021-02-23 15:19:02 +00:00
try
2020-09-29 20:05:25 +00:00
{
2021-02-23 15:19:02 +00:00
File . Move ( file , file + ".ryuold" ) ;
2020-09-29 20:05:25 +00:00
2021-02-23 15:19:02 +00:00
Application . Invoke ( delegate
2020-09-29 20:05:25 +00:00
{
2021-02-23 15:19:02 +00:00
updateDialog . ProgressBar . Value + + ;
} ) ;
}
catch
{
2021-02-28 10:33:36 +00:00
Logger . Warning ? . Print ( LogClass . Application , "Updater was unable to rename file: " + file ) ;
2020-09-29 20:05:25 +00:00
}
}
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 )
{
2021-02-28 10:56:15 +00:00
#if ! DISABLE_UPDATER
2020-09-29 20:05:25 +00:00
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 ;
}
2023-01-07 08:06:13 +00:00
if ( Program . Version . Contains ( "dirty" ) | | ! ReleaseInformation . IsValid ( ) )
2020-09-29 20:05:25 +00:00
{
if ( showWarnings )
{
2021-02-28 10:56:15 +00:00
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." ) ;
2020-09-29 20:05:25 +00:00
}
return false ;
}
return true ;
2021-02-28 10:56:15 +00:00
#else
if ( showWarnings )
{
2023-01-07 08:06:13 +00:00
if ( ReleaseInformation . IsFlatHubBuild ( ) )
2022-03-04 17:03:16 +00:00
{
GtkDialog . CreateWarningDialog ( "Updater Disabled!" , "Please update Ryujinx via FlatHub." ) ;
}
else
{
GtkDialog . CreateWarningDialog ( "Updater Disabled!" , "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version." ) ;
}
2021-02-28 10:56:15 +00:00
}
return false ;
#endif
2020-09-29 20:05:25 +00:00
}
2021-02-23 15:19:02 +00:00
// 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.
2021-12-04 23:02:30 +00:00
if ( OperatingSystem . IsWindows ( ) )
2021-02-23 15:19:02 +00:00
{
foreach ( string dir in WindowsDependencyDirs )
{
string dirPath = Path . Combine ( HomeDir , dir ) ;
if ( Directory . Exists ( dirPath ) )
{
files = files . Concat ( Directory . EnumerateFiles ( dirPath , "*" , SearchOption . AllDirectories ) ) ;
}
}
}
return files ;
}
2020-09-29 20:05:25 +00:00
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 ( )
{
2021-02-23 15:19:02 +00:00
foreach ( string file in EnumerateFilesToDelete ( ) )
2020-09-29 20:05:25 +00:00
{
if ( Path . GetExtension ( file ) . EndsWith ( ".ryuold" ) )
{
File . Delete ( file ) ;
}
}
}
}
2023-02-15 22:36:35 +00:00
}