2019-09-20 01:59:48 +02:00
using DiscordRPC ;
2019-09-02 18:03:57 +02:00
using Gtk ;
2019-11-29 05:32:51 +01:00
using JsonPrettyPrinterPlus ;
2019-09-02 18:03:57 +02:00
using Ryujinx.Audio ;
using Ryujinx.Common.Logging ;
using Ryujinx.Graphics.Gal.OpenGL ;
2019-11-29 05:32:51 +01:00
using Ryujinx.Graphics.Gal ;
using Ryujinx.HLE.FileSystem ;
2019-09-02 18:03:57 +02:00
using Ryujinx.Profiler ;
using System ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
using System.Text ;
2019-11-29 05:32:51 +01:00
using System.Threading.Tasks ;
2019-09-02 18:03:57 +02:00
using System.Threading ;
2019-11-29 05:32:51 +01:00
using Utf8Json ;
using Utf8Json.Resolvers ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
using GUI = Gtk . Builder . ObjectAttribute ;
namespace Ryujinx.Ui
2019-09-02 18:03:57 +02:00
{
public class MainWindow : Window
{
2019-11-29 05:32:51 +01:00
private static HLE . Switch _device ;
2019-09-02 18:03:57 +02:00
private static IGalRenderer _renderer ;
private static IAalOutput _audioOut ;
2019-11-29 05:32:51 +01:00
private static GlScreen _screen ;
2019-09-02 18:03:57 +02:00
private static ListStore _tableStore ;
2019-11-29 05:32:51 +01:00
private static bool _updatingGameTable ;
private static bool _gameLoaded ;
private static bool _ending ;
private static TreeViewColumn _favColumn ;
private static TreeViewColumn _appColumn ;
private static TreeViewColumn _devColumn ;
private static TreeViewColumn _versionColumn ;
private static TreeViewColumn _timePlayedColumn ;
private static TreeViewColumn _lastPlayedColumn ;
private static TreeViewColumn _fileExtColumn ;
private static TreeViewColumn _fileSizeColumn ;
private static TreeViewColumn _pathColumn ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
private static TreeView _treeView ;
2019-09-02 18:03:57 +02:00
public static bool DiscordIntegrationEnabled { get ; set ; }
public static DiscordRpcClient DiscordClient ;
public static RichPresence DiscordPresence ;
2019-11-29 05:32:51 +01:00
#pragma warning disable CS0649
#pragma warning disable IDE0044
2019-09-02 18:03:57 +02:00
[GUI] Window _mainWin ;
[GUI] CheckMenuItem _fullScreen ;
[GUI] MenuItem _stopEmulation ;
2019-11-29 05:32:51 +01:00
[GUI] CheckMenuItem _favToggle ;
2019-09-02 18:03:57 +02:00
[GUI] CheckMenuItem _iconToggle ;
2019-11-29 05:32:51 +01:00
[GUI] CheckMenuItem _appToggle ;
2019-09-02 18:03:57 +02:00
[GUI] CheckMenuItem _developerToggle ;
[GUI] CheckMenuItem _versionToggle ;
[GUI] CheckMenuItem _timePlayedToggle ;
[GUI] CheckMenuItem _lastPlayedToggle ;
[GUI] CheckMenuItem _fileExtToggle ;
[GUI] CheckMenuItem _fileSizeToggle ;
[GUI] CheckMenuItem _pathToggle ;
[GUI] TreeView _gameTable ;
2019-11-29 05:32:51 +01:00
[GUI] Label _progressLabel ;
[GUI] LevelBar _progressBar ;
#pragma warning restore CS0649
#pragma warning restore IDE0044
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
public MainWindow ( ) : this ( new Builder ( "Ryujinx.Ui.MainWindow.glade" ) ) { }
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
private MainWindow ( Builder builder ) : base ( builder . GetObject ( "_mainWin" ) . Handle )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
builder . Autoconnect ( this ) ;
DeleteEvent + = Window_Close ;
ApplicationLibrary . ApplicationAdded + = Application_Added ;
2019-09-02 18:03:57 +02:00
_renderer = new OglRenderer ( ) ;
_audioOut = InitializeAudioEngine ( ) ;
_device = new HLE . Switch ( _renderer , _audioOut ) ;
2019-11-29 05:32:51 +01:00
_treeView = _gameTable ;
2019-09-02 18:03:57 +02:00
Configuration . Load ( System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
Configuration . InitialConfigure ( _device ) ;
ApplyTheme ( ) ;
if ( DiscordIntegrationEnabled )
{
DiscordClient = new DiscordRpcClient ( "568815339807309834" ) ;
DiscordPresence = new RichPresence
{
Assets = new Assets
{
LargeImageKey = "ryujinx" ,
LargeImageText = "Ryujinx is an emulator for the Nintendo Switch"
} ,
Details = "Main Menu" ,
State = "Idling" ,
Timestamps = new Timestamps ( DateTime . UtcNow )
} ;
DiscordClient . Initialize ( ) ;
DiscordClient . SetPresence ( DiscordPresence ) ;
}
2019-11-29 05:32:51 +01:00
_mainWin . Icon = new Gdk . Pixbuf ( Assembly . GetExecutingAssembly ( ) , "Ryujinx.Ui.assets.Icon.png" ) ;
2019-09-02 18:03:57 +02:00
_stopEmulation . Sensitive = false ;
2019-11-29 05:32:51 +01:00
if ( SwitchSettings . SwitchConfig . GuiColumns . FavColumn ) { _favToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . IconColumn ) { _iconToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . AppColumn ) { _appToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . DevColumn ) { _developerToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . VersionColumn ) { _versionToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . TimePlayedColumn ) { _timePlayedToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . LastPlayedColumn ) { _lastPlayedToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . FileExtColumn ) { _fileExtToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . FileSizeColumn ) { _fileSizeToggle . Active = true ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . PathColumn ) { _pathToggle . Active = true ; }
_gameTable . Model = _tableStore = new ListStore (
typeof ( bool ) ,
typeof ( Gdk . Pixbuf ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ) ;
_tableStore . SetSortFunc ( 5 , TimePlayedSort ) ;
_tableStore . SetSortFunc ( 6 , LastPlayedSort ) ;
_tableStore . SetSortFunc ( 8 , FileSizeSort ) ;
_tableStore . SetSortColumnId ( 0 , SortType . Descending ) ;
UpdateColumns ( ) ;
#pragma warning disable CS4014
UpdateGameTable ( ) ;
#pragma warning restore CS4014
}
internal static void ApplyTheme ( )
{
if ( ! SwitchSettings . SwitchConfig . EnableCustomTheme )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
return ;
}
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
if ( File . Exists ( SwitchSettings . SwitchConfig . CustomThemePath ) & & ( System . IO . Path . GetExtension ( SwitchSettings . SwitchConfig . CustomThemePath ) = = ".css" ) )
{
CssProvider cssProvider = new CssProvider ( ) ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
cssProvider . LoadFromPath ( SwitchSettings . SwitchConfig . CustomThemePath ) ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
StyleContext . AddProviderForScreen ( Gdk . Screen . Default , cssProvider , 800 ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
else
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
Logger . PrintWarning ( LogClass . Application , $"The \" custom_theme_path \ " section in \"Config.json\" contains an invalid path: \"{SwitchSettings.SwitchConfig.CustomThemePath}\"." ) ;
}
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void UpdateColumns ( )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
foreach ( TreeViewColumn column in _gameTable . Columns )
{
_gameTable . RemoveColumn ( column ) ;
}
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
CellRendererToggle favToggle = new CellRendererToggle ( ) ;
favToggle . Toggled + = FavToggle_Toggled ;
if ( SwitchSettings . SwitchConfig . GuiColumns . FavColumn ) { _gameTable . AppendColumn ( "Fav" , favToggle , "active" , 0 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . IconColumn ) { _gameTable . AppendColumn ( "Icon" , new CellRendererPixbuf ( ) , "pixbuf" , 1 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . AppColumn ) { _gameTable . AppendColumn ( "Application" , new CellRendererText ( ) , "text" , 2 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . DevColumn ) { _gameTable . AppendColumn ( "Developer" , new CellRendererText ( ) , "text" , 3 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . VersionColumn ) { _gameTable . AppendColumn ( "Version" , new CellRendererText ( ) , "text" , 4 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . TimePlayedColumn ) { _gameTable . AppendColumn ( "Time Played" , new CellRendererText ( ) , "text" , 5 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . LastPlayedColumn ) { _gameTable . AppendColumn ( "Last Played" , new CellRendererText ( ) , "text" , 6 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . FileExtColumn ) { _gameTable . AppendColumn ( "File Ext" , new CellRendererText ( ) , "text" , 7 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . FileSizeColumn ) { _gameTable . AppendColumn ( "File Size" , new CellRendererText ( ) , "text" , 8 ) ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . PathColumn ) { _gameTable . AppendColumn ( "Path" , new CellRendererText ( ) , "text" , 9 ) ; }
foreach ( TreeViewColumn column in _gameTable . Columns )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
if ( column . Title = = "Fav" ) { _favColumn = column ; }
else if ( column . Title = = "Application" ) { _appColumn = column ; }
else if ( column . Title = = "Developer" ) { _devColumn = column ; }
else if ( column . Title = = "Version" ) { _versionColumn = column ; }
else if ( column . Title = = "Time Played" ) { _timePlayedColumn = column ; }
else if ( column . Title = = "Last Played" ) { _lastPlayedColumn = column ; }
else if ( column . Title = = "File Ext" ) { _fileExtColumn = column ; }
else if ( column . Title = = "File Size" ) { _fileSizeColumn = column ; }
else if ( column . Title = = "Path" ) { _pathColumn = column ; }
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
if ( SwitchSettings . SwitchConfig . GuiColumns . FavColumn ) { _favColumn . SortColumnId = 0 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . IconColumn ) { _appColumn . SortColumnId = 2 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . AppColumn ) { _devColumn . SortColumnId = 3 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . DevColumn ) { _versionColumn . SortColumnId = 4 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . TimePlayedColumn ) { _timePlayedColumn . SortColumnId = 5 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . LastPlayedColumn ) { _lastPlayedColumn . SortColumnId = 6 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . FileExtColumn ) { _fileExtColumn . SortColumnId = 7 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . FileSizeColumn ) { _fileSizeColumn . SortColumnId = 8 ; }
if ( SwitchSettings . SwitchConfig . GuiColumns . PathColumn ) { _pathColumn . SortColumnId = 9 ; }
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
internal static async Task UpdateGameTable ( )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
if ( _updatingGameTable )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
return ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
_updatingGameTable = true ;
_tableStore . Clear ( ) ;
await Task . Run ( ( ) = > ApplicationLibrary . LoadApplications ( SwitchSettings . SwitchConfig . GameDirs , _device . System . KeySet , _device . System . State . DesiredTitleLanguage ) ) ;
_updatingGameTable = false ;
2019-09-02 18:03:57 +02:00
}
2019-09-08 03:59:41 +02:00
internal void LoadApplication ( string path )
2019-09-02 18:03:57 +02:00
{
if ( _gameLoaded )
{
2019-11-29 05:32:51 +01:00
GtkDialog . CreateErrorDialog ( "A game has already been loaded. Please close the emulator and try again" ) ;
2019-09-02 18:03:57 +02:00
}
else
{
2019-09-20 01:59:48 +02:00
Logger . RestartTime ( ) ;
2019-09-02 18:03:57 +02:00
if ( Directory . Exists ( path ) )
{
string [ ] romFsFiles = Directory . GetFiles ( path , "*.istorage" ) ;
if ( romFsFiles . Length = = 0 )
{
romFsFiles = Directory . GetFiles ( path , "*.romfs" ) ;
}
if ( romFsFiles . Length > 0 )
{
Logger . PrintInfo ( LogClass . Application , "Loading as cart with RomFS." ) ;
_device . LoadCart ( path , romFsFiles [ 0 ] ) ;
}
else
{
Logger . PrintInfo ( LogClass . Application , "Loading as cart WITHOUT RomFS." ) ;
_device . LoadCart ( path ) ;
}
}
else if ( File . Exists ( path ) )
{
switch ( System . IO . Path . GetExtension ( path ) . ToLowerInvariant ( ) )
{
case ".xci" :
Logger . PrintInfo ( LogClass . Application , "Loading as XCI." ) ;
_device . LoadXci ( path ) ;
break ;
case ".nca" :
Logger . PrintInfo ( LogClass . Application , "Loading as NCA." ) ;
_device . LoadNca ( path ) ;
break ;
case ".nsp" :
case ".pfs0" :
Logger . PrintInfo ( LogClass . Application , "Loading as NSP." ) ;
_device . LoadNsp ( path ) ;
break ;
default :
Logger . PrintInfo ( LogClass . Application , "Loading as homebrew." ) ;
try
{
_device . LoadProgram ( path ) ;
}
catch ( ArgumentOutOfRangeException )
{
2019-09-20 01:59:48 +02:00
Logger . PrintError ( LogClass . Application , "The file which you have specified is unsupported by Ryujinx." ) ;
2019-09-02 18:03:57 +02:00
}
break ;
}
}
else
{
2019-09-20 01:59:48 +02:00
Logger . PrintWarning ( LogClass . Application , "Please specify a valid XCI/NCA/NSP/PFS0/NRO file." ) ;
2019-09-02 18:03:57 +02:00
End ( ) ;
}
2019-11-29 05:32:51 +01:00
#if MACOS_BUILD
CreateGameWindow ( ) ;
#else
new Thread ( CreateGameWindow ) . Start ( ) ;
#endif
2019-09-02 18:03:57 +02:00
_gameLoaded = true ;
_stopEmulation . Sensitive = true ;
if ( DiscordIntegrationEnabled )
{
2019-11-29 05:32:51 +01:00
if ( File . ReadAllLines ( System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "RPsupported.dat" ) ) . Contains ( _device . System . TitleId ) )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
DiscordPresence . Assets . LargeImageKey = _device . System . TitleId ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
string state = _device . System . TitleId ;
2019-09-02 18:03:57 +02:00
if ( state = = null )
{
state = "Ryujinx" ;
}
else
{
state = state . ToUpper ( ) ;
}
string details = "Idling" ;
if ( _device . System . TitleName ! = null )
{
details = $"Playing {_device.System.TitleName}" ;
}
DiscordPresence . Details = details ;
DiscordPresence . State = state ;
DiscordPresence . Assets . LargeImageText = _device . System . TitleName ;
DiscordPresence . Assets . SmallImageKey = "ryujinx" ;
DiscordPresence . Assets . SmallImageText = "Ryujinx is an emulator for the Nintendo Switch" ;
DiscordPresence . Timestamps = new Timestamps ( DateTime . UtcNow ) ;
DiscordClient . SetPresence ( DiscordPresence ) ;
}
2019-11-29 05:32:51 +01:00
string metadataFolder = System . IO . Path . Combine ( new VirtualFileSystem ( ) . GetBasePath ( ) , "games" , _device . System . TitleId , "gui" ) ;
string metadataFile = System . IO . Path . Combine ( metadataFolder , "metadata.json" ) ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
IJsonFormatterResolver resolver = CompositeResolver . Create ( new [ ] { StandardResolver . AllowPrivateSnakeCase } ) ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
ApplicationMetadata appMetadata ;
if ( ! File . Exists ( metadataFile ) )
{
Directory . CreateDirectory ( metadataFolder ) ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
appMetadata = new ApplicationMetadata
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
Favorite = false ,
TimePlayed = 0 ,
LastPlayed = "Never"
} ;
byte [ ] data = JsonSerializer . Serialize ( appMetadata , resolver ) ;
File . WriteAllText ( metadataFile , Encoding . UTF8 . GetString ( data , 0 , data . Length ) . PrettyPrintJson ( ) ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
using ( Stream stream = File . OpenRead ( metadataFile ) )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
appMetadata = JsonSerializer . Deserialize < ApplicationMetadata > ( stream , resolver ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
appMetadata . LastPlayed = DateTime . UtcNow . ToString ( ) ;
byte [ ] saveData = JsonSerializer . Serialize ( appMetadata , resolver ) ;
File . WriteAllText ( metadataFile , Encoding . UTF8 . GetString ( saveData , 0 , saveData . Length ) . PrettyPrintJson ( ) ) ;
2019-09-02 18:03:57 +02:00
}
}
private static void CreateGameWindow ( )
{
Configuration . ConfigureHid ( _device , SwitchSettings . SwitchConfig ) ;
2019-11-29 05:32:51 +01:00
using ( _screen = new GlScreen ( _device , _renderer ) )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
_screen . MainLoop ( ) ;
2019-09-02 18:03:57 +02:00
End ( ) ;
}
}
private static void End ( )
{
2019-11-29 05:32:51 +01:00
if ( _ending )
{
return ;
}
_ending = true ;
2019-09-02 18:03:57 +02:00
if ( _gameLoaded )
{
2019-11-29 05:32:51 +01:00
string metadataFolder = System . IO . Path . Combine ( new VirtualFileSystem ( ) . GetBasePath ( ) , "games" , _device . System . TitleId , "gui" ) ;
string metadataFile = System . IO . Path . Combine ( metadataFolder , "metadata.json" ) ;
IJsonFormatterResolver resolver = CompositeResolver . Create ( new [ ] { StandardResolver . AllowPrivateSnakeCase } ) ;
ApplicationMetadata appMetadata ;
if ( ! File . Exists ( metadataFile ) )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
Directory . CreateDirectory ( metadataFolder ) ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
appMetadata = new ApplicationMetadata
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
Favorite = false ,
TimePlayed = 0 ,
LastPlayed = "Never"
} ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
byte [ ] data = JsonSerializer . Serialize ( appMetadata , resolver ) ;
File . WriteAllText ( metadataFile , Encoding . UTF8 . GetString ( data , 0 , data . Length ) . PrettyPrintJson ( ) ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
using ( Stream stream = File . OpenRead ( metadataFile ) )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
appMetadata = JsonSerializer . Deserialize < ApplicationMetadata > ( stream , resolver ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
DateTime lastPlayedDateTime = DateTime . Parse ( appMetadata . LastPlayed ) ;
double sessionTimePlayed = DateTime . UtcNow . Subtract ( lastPlayedDateTime ) . TotalSeconds ;
appMetadata . TimePlayed + = Math . Round ( sessionTimePlayed , MidpointRounding . AwayFromZero ) ;
byte [ ] saveData = JsonSerializer . Serialize ( appMetadata , resolver ) ;
File . WriteAllText ( metadataFile , Encoding . UTF8 . GetString ( saveData , 0 , saveData . Length ) . PrettyPrintJson ( ) ) ;
2019-09-02 18:03:57 +02:00
}
Profile . FinishProfiling ( ) ;
_device . Dispose ( ) ;
_audioOut . Dispose ( ) ;
2019-09-20 01:59:48 +02:00
DiscordClient ? . Dispose ( ) ;
2019-09-02 18:03:57 +02:00
Logger . Shutdown ( ) ;
Environment . Exit ( 0 ) ;
}
/// <summary>
/// Picks an <see cref="IAalOutput"/> audio output renderer supported on this machine
/// </summary>
/// <returns>An <see cref="IAalOutput"/> supported by this machine</returns>
private static IAalOutput InitializeAudioEngine ( )
{
if ( SoundIoAudioOut . IsSupported )
{
return new SoundIoAudioOut ( ) ;
}
else if ( OpenALAudioOut . IsSupported )
{
return new OpenALAudioOut ( ) ;
}
else
{
return new DummyAudioOut ( ) ;
}
}
//Events
2019-11-29 05:32:51 +01:00
private void Application_Added ( object sender , ApplicationAddedEventArgs e )
{
Application . Invoke ( delegate
{
_tableStore . AppendValues (
e . AppData . Favorite ,
new Gdk . Pixbuf ( e . AppData . Icon , 75 , 75 ) ,
$"{e.AppData.TitleName}\n{e.AppData.TitleId.ToUpper()}" ,
e . AppData . Developer ,
e . AppData . Version ,
e . AppData . TimePlayed ,
e . AppData . LastPlayed ,
e . AppData . FileExtension ,
e . AppData . FileSize ,
e . AppData . Path ) ;
_progressLabel . Text = $"{e.NumAppsLoaded}/{e.NumAppsFound} Games Loaded" ;
_progressBar . Value = ( float ) e . NumAppsLoaded / e . NumAppsFound ;
} ) ;
}
private void FavToggle_Toggled ( object sender , ToggledArgs args )
{
_tableStore . GetIter ( out TreeIter treeIter , new TreePath ( args . Path ) ) ;
string titleId = _tableStore . GetValue ( treeIter , 2 ) . ToString ( ) . Split ( "\n" ) [ 1 ] . ToLower ( ) ;
string metadataPath = System . IO . Path . Combine ( new VirtualFileSystem ( ) . GetBasePath ( ) , "games" , titleId , "gui" , "metadata.json" ) ;
IJsonFormatterResolver resolver = CompositeResolver . Create ( new [ ] { StandardResolver . AllowPrivateSnakeCase } ) ;
ApplicationMetadata appMetadata ;
using ( Stream stream = File . OpenRead ( metadataPath ) )
{
appMetadata = JsonSerializer . Deserialize < ApplicationMetadata > ( stream , resolver ) ;
}
if ( ( bool ) _tableStore . GetValue ( treeIter , 0 ) )
{
_tableStore . SetValue ( treeIter , 0 , false ) ;
appMetadata . Favorite = false ;
}
else
{
_tableStore . SetValue ( treeIter , 0 , true ) ;
appMetadata . Favorite = true ;
}
byte [ ] saveData = JsonSerializer . Serialize ( appMetadata , resolver ) ;
File . WriteAllText ( metadataPath , Encoding . UTF8 . GetString ( saveData , 0 , saveData . Length ) . PrettyPrintJson ( ) ) ;
}
private void Row_Activated ( object sender , RowActivatedArgs args )
2019-09-02 18:03:57 +02:00
{
_tableStore . GetIter ( out TreeIter treeIter , new TreePath ( args . Path . ToString ( ) ) ) ;
2019-11-29 05:32:51 +01:00
string path = ( string ) _tableStore . GetValue ( treeIter , 9 ) ;
2019-09-02 18:03:57 +02:00
LoadApplication ( path ) ;
}
2019-11-29 05:32:51 +01:00
private void Load_Application_File ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
FileChooserDialog fileChooser = new FileChooserDialog ( "Choose the file to open" , this , FileChooserAction . Open , "Cancel" , ResponseType . Cancel , "Open" , ResponseType . Accept ) ;
fileChooser . Filter = new FileFilter ( ) ;
fileChooser . Filter . AddPattern ( "*.nsp" ) ;
fileChooser . Filter . AddPattern ( "*.pfs0" ) ;
fileChooser . Filter . AddPattern ( "*.xci" ) ;
fileChooser . Filter . AddPattern ( "*.nca" ) ;
fileChooser . Filter . AddPattern ( "*.nro" ) ;
fileChooser . Filter . AddPattern ( "*.nso" ) ;
if ( fileChooser . Run ( ) = = ( int ) ResponseType . Accept )
{
LoadApplication ( fileChooser . Filename ) ;
}
2019-11-29 05:32:51 +01:00
fileChooser . Dispose ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Load_Application_Folder ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
FileChooserDialog fileChooser = new FileChooserDialog ( "Choose the folder to open" , this , FileChooserAction . SelectFolder , "Cancel" , ResponseType . Cancel , "Open" , ResponseType . Accept ) ;
if ( fileChooser . Run ( ) = = ( int ) ResponseType . Accept )
{
LoadApplication ( fileChooser . Filename ) ;
}
2019-11-29 05:32:51 +01:00
fileChooser . Dispose ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Open_Ryu_Folder ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
Process . Start ( new ProcessStartInfo ( )
{
2019-11-29 05:32:51 +01:00
FileName = new VirtualFileSystem ( ) . GetBasePath ( ) ,
2019-09-02 18:03:57 +02:00
UseShellExecute = true ,
Verb = "open"
} ) ;
}
2019-11-29 05:32:51 +01:00
private void Exit_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
_screen ? . Exit ( ) ;
2019-09-02 18:03:57 +02:00
End ( ) ;
}
2019-11-29 05:32:51 +01:00
private void Window_Close ( object sender , DeleteEventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
_screen ? . Exit ( ) ;
2019-09-02 18:03:57 +02:00
End ( ) ;
}
2019-11-29 05:32:51 +01:00
private void StopEmulation_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
// TODO: Write logic to kill running game
2019-11-29 05:32:51 +01:00
_gameLoaded = false ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void FullScreen_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
if ( _fullScreen . Active )
{
Fullscreen ( ) ;
}
else
{
Unfullscreen ( ) ;
}
}
2019-11-29 05:32:51 +01:00
private void Settings_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
SwitchSettings settingsWin = new SwitchSettings ( _device ) ;
settingsWin . Show ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Update_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
string ryuUpdater = System . IO . Path . Combine ( new VirtualFileSystem ( ) . GetBasePath ( ) , "RyuUpdater.exe" ) ;
2019-09-02 18:03:57 +02:00
try
{
Process . Start ( new ProcessStartInfo ( ryuUpdater , "/U" ) { UseShellExecute = true } ) ;
}
catch ( System . ComponentModel . Win32Exception )
{
2019-11-29 05:32:51 +01:00
GtkDialog . CreateErrorDialog ( "Update canceled by user or updater was not found" ) ;
2019-09-02 18:03:57 +02:00
}
}
2019-11-29 05:32:51 +01:00
private void About_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
AboutWindow aboutWin = new AboutWindow ( ) ;
aboutWin . Show ( ) ;
}
private void Fav_Toggled ( object sender , EventArgs args )
{
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
updatedColumns . FavColumn = _favToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Icon_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . IconColumn = _iconToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Title_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . AppColumn = _appToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Developer_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . DevColumn = _developerToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Version_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . VersionColumn = _versionToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void TimePlayed_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . TimePlayedColumn = _timePlayedToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void LastPlayed_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . LastPlayedColumn = _lastPlayedToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void FileExt_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . FileExtColumn = _fileExtToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void FileSize_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . FileSizeColumn = _fileSizeToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Path_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
GuiColumns updatedColumns = SwitchSettings . SwitchConfig . GuiColumns ;
updatedColumns . PathColumn = _pathToggle . Active ;
SwitchSettings . SwitchConfig . GuiColumns = updatedColumns ;
2019-09-02 18:03:57 +02:00
Configuration . SaveConfig ( SwitchSettings . SwitchConfig , System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Config.json" ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
}
private void RefreshList_Pressed ( object sender , ButtonReleaseEventArgs args )
{
#pragma warning disable CS4014
UpdateGameTable ( ) ;
#pragma warning restore CS4014
}
private static int TimePlayedSort ( ITreeModel model , TreeIter a , TreeIter b )
{
string aValue = model . GetValue ( a , 5 ) . ToString ( ) ;
string bValue = model . GetValue ( b , 5 ) . ToString ( ) ;
if ( aValue . Length > 4 & & aValue . Substring ( aValue . Length - 4 ) = = "mins" )
{
aValue = ( float . Parse ( aValue . Substring ( 0 , aValue . Length - 5 ) ) * 60 ) . ToString ( ) ;
}
else if ( aValue . Length > 3 & & aValue . Substring ( aValue . Length - 3 ) = = "hrs" )
{
aValue = ( float . Parse ( aValue . Substring ( 0 , aValue . Length - 4 ) ) * 3600 ) . ToString ( ) ;
}
else if ( aValue . Length > 4 & & aValue . Substring ( aValue . Length - 4 ) = = "days" )
{
aValue = ( float . Parse ( aValue . Substring ( 0 , aValue . Length - 5 ) ) * 86400 ) . ToString ( ) ;
}
else
{
aValue = aValue . Substring ( 0 , aValue . Length - 1 ) ;
}
if ( bValue . Length > 4 & & bValue . Substring ( bValue . Length - 4 ) = = "mins" )
{
bValue = ( float . Parse ( bValue . Substring ( 0 , bValue . Length - 5 ) ) * 60 ) . ToString ( ) ;
}
else if ( bValue . Length > 3 & & bValue . Substring ( bValue . Length - 3 ) = = "hrs" )
{
bValue = ( float . Parse ( bValue . Substring ( 0 , bValue . Length - 4 ) ) * 3600 ) . ToString ( ) ;
}
else if ( bValue . Length > 4 & & bValue . Substring ( bValue . Length - 4 ) = = "days" )
{
bValue = ( float . Parse ( bValue . Substring ( 0 , bValue . Length - 5 ) ) * 86400 ) . ToString ( ) ;
}
else
{
bValue = bValue . Substring ( 0 , bValue . Length - 1 ) ;
}
if ( float . Parse ( aValue ) > float . Parse ( bValue ) )
{
return - 1 ;
}
else if ( float . Parse ( bValue ) > float . Parse ( aValue ) )
{
return 1 ;
}
else
{
return 0 ;
}
}
private static int LastPlayedSort ( ITreeModel model , TreeIter a , TreeIter b )
{
string aValue = model . GetValue ( a , 6 ) . ToString ( ) ;
string bValue = model . GetValue ( b , 6 ) . ToString ( ) ;
if ( aValue = = "Never" )
{
aValue = DateTime . UnixEpoch . ToString ( ) ;
}
if ( bValue = = "Never" )
{
bValue = DateTime . UnixEpoch . ToString ( ) ;
}
return DateTime . Compare ( DateTime . Parse ( bValue ) , DateTime . Parse ( aValue ) ) ;
}
private static int FileSizeSort ( ITreeModel model , TreeIter a , TreeIter b )
{
string aValue = model . GetValue ( a , 8 ) . ToString ( ) ;
string bValue = model . GetValue ( b , 8 ) . ToString ( ) ;
if ( aValue . Substring ( aValue . Length - 2 ) = = "GB" )
{
aValue = ( float . Parse ( aValue [ 0. . ^ 2 ] ) * 1024 ) . ToString ( ) ;
}
else
{
aValue = aValue [ 0. . ^ 2 ] ;
}
if ( bValue . Substring ( bValue . Length - 2 ) = = "GB" )
{
bValue = ( float . Parse ( bValue [ 0. . ^ 2 ] ) * 1024 ) . ToString ( ) ;
}
else
{
bValue = bValue [ 0. . ^ 2 ] ;
}
if ( float . Parse ( aValue ) > float . Parse ( bValue ) )
{
return - 1 ;
}
else if ( float . Parse ( bValue ) > float . Parse ( aValue ) )
{
return 1 ;
}
else
{
return 0 ;
}
2019-09-02 18:03:57 +02:00
}
}
}