2020-11-17 16:40:19 -05:00
using ARMeilleure.Translation ;
2020-06-16 14:28:02 -04:00
using ARMeilleure.Translation.PTC ;
2019-09-02 12:03:57 -04:00
using Gtk ;
2020-03-25 13:09:38 -04:00
using LibHac.Common ;
using LibHac.Ns ;
2019-09-02 12:03:57 -04:00
using Ryujinx.Audio ;
2020-08-30 12:51:53 -04:00
using Ryujinx.Common.Configuration ;
2019-09-02 12:03:57 -04:00
using Ryujinx.Common.Logging ;
2020-08-17 21:49:37 -04:00
using Ryujinx.Common.System ;
2020-01-05 06:49:44 -05:00
using Ryujinx.Configuration ;
2020-01-21 17:23:11 -05:00
using Ryujinx.Graphics.GAL ;
2019-10-13 02:02:07 -04:00
using Ryujinx.Graphics.OpenGL ;
2020-01-05 06:49:44 -05:00
using Ryujinx.HLE.FileSystem ;
2020-01-21 17:23:11 -05:00
using Ryujinx.HLE.FileSystem.Content ;
2020-09-20 23:45:30 -04:00
using Ryujinx.HLE.HOS ;
2020-09-01 05:09:42 -04:00
using Ryujinx.Ui.Diagnostic ;
2019-09-02 12:03:57 -04:00
using System ;
2020-01-05 06:49:44 -05:00
using System.Diagnostics ;
2019-09-02 12:03:57 -04:00
using System.IO ;
using System.Reflection ;
using System.Threading ;
2019-12-21 14:52:31 -05:00
using System.Threading.Tasks ;
2019-09-02 12:03:57 -04:00
2019-11-28 23:32:51 -05:00
using GUI = Gtk . Builder . ObjectAttribute ;
namespace Ryujinx.Ui
2019-09-02 12:03:57 -04:00
{
public class MainWindow : Window
{
2020-01-21 17:23:11 -05:00
private static VirtualFileSystem _virtualFileSystem ;
private static ContentManager _contentManager ;
2020-09-20 23:45:30 -04:00
private static UserChannelPersistence _userChannelPersistence ;
2019-09-02 12:03:57 -04:00
2020-08-17 21:49:37 -04:00
private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution ;
2020-01-21 17:23:11 -05:00
private static HLE . Switch _emulationContext ;
2019-09-02 12:03:57 -04:00
2020-05-02 22:00:53 -04:00
private static GlRenderer _glWidget ;
2020-08-02 21:30:58 -04:00
private static GtkHostUiHandler _uiHandler ;
2019-09-02 12:03:57 -04:00
2020-09-20 23:45:30 -04:00
public static GlRenderer GlWidget = > _glWidget ;
2020-02-13 12:43:29 -05:00
private static AutoResetEvent _deviceExitStatus = new AutoResetEvent ( false ) ;
2020-02-06 06:38:24 -05:00
2019-09-02 12:03:57 -04:00
private static ListStore _tableStore ;
2019-11-28 23:32:51 -05:00
private static bool _updatingGameTable ;
private static bool _gameLoaded ;
2020-09-20 23:45:30 -04:00
private static string _gamePath ;
2019-11-28 23:32:51 -05:00
private static bool _ending ;
2020-05-02 22:00:53 -04:00
2020-04-20 17:59:59 -04:00
#pragma warning disable CS0169 , CS0649 , IDE0044
2020-02-11 19:56:19 -05:00
2020-09-29 16:05:25 -04:00
[GUI] public MenuItem ExitMenuItem ;
[GUI] public MenuItem UpdateMenuItem ;
[GUI] MenuBar _menuBar ;
[GUI] Box _footerBox ;
[GUI] Box _statusBar ;
[GUI] MenuItem _stopEmulation ;
[GUI] MenuItem _fullScreen ;
2020-12-01 17:02:27 -05:00
[GUI] CheckMenuItem _startFullScreen ;
2020-09-29 16:05:25 -04:00
[GUI] CheckMenuItem _favToggle ;
[GUI] MenuItem _firmwareInstallDirectory ;
[GUI] MenuItem _firmwareInstallFile ;
2020-10-13 16:54:42 -04:00
[GUI] Label _fifoStatus ;
2020-09-29 16:05:25 -04:00
[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 ;
2020-12-15 21:19:07 -05:00
[GUI] Label _aspectRatio ;
2020-09-29 16:05:25 -04:00
[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 ;
2020-02-11 19:56:19 -05:00
2020-04-20 17:59:59 -04:00
#pragma warning restore CS0649 , IDE0044 , CS0169
2019-09-02 12:03:57 -04:00
2019-11-28 23:32:51 -05:00
public MainWindow ( ) : this ( new Builder ( "Ryujinx.Ui.MainWindow.glade" ) ) { }
2019-09-02 12:03:57 -04:00
2019-11-28 23:32:51 -05:00
private MainWindow ( Builder builder ) : base ( builder . GetObject ( "_mainWin" ) . Handle )
2019-09-02 12:03:57 -04:00
{
2019-11-28 23:32:51 -05:00
builder . Autoconnect ( this ) ;
2020-03-30 18:10:13 -04:00
int monitorWidth = Display . PrimaryMonitor . Geometry . Width * Display . PrimaryMonitor . ScaleFactor ;
int monitorHeight = Display . PrimaryMonitor . Geometry . Height * Display . PrimaryMonitor . ScaleFactor ;
this . DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280 ;
this . DefaultHeight = monitorHeight < 760 ? monitorHeight : 760 ;
2020-07-23 09:12:19 -04:00
this . WindowStateEvent + = MainWindow_WindowStateEvent ;
2020-03-06 21:40:06 -05:00
this . DeleteEvent + = Window_Close ;
_fullScreen . Activated + = FullScreen_Toggled ;
2019-11-28 23:32:51 -05:00
2020-05-02 22:00:53 -04:00
this . Icon = new Gdk . Pixbuf ( Assembly . GetExecutingAssembly ( ) , "Ryujinx.Ui.assets.Icon.png" ) ;
this . Title = $"Ryujinx {Program.Version}" ;
2020-01-31 13:21:46 -05:00
ApplicationLibrary . ApplicationAdded + = Application_Added ;
ApplicationLibrary . ApplicationCountUpdated + = ApplicationCount_Updated ;
2020-05-02 22:00:53 -04:00
GlRenderer . StatusUpdatedEvent + = Update_StatusBar ;
2019-11-28 23:32:51 -05:00
2019-12-21 21:49:51 -05:00
_gameTable . ButtonReleaseEvent + = Row_Clicked ;
2020-01-21 17:23:11 -05:00
// First we check that a migration isn't needed. (because VirtualFileSystem will create the new directory otherwise)
2020-01-05 06:49:44 -05:00
bool continueWithStartup = Migration . PromptIfMigrationNeededForStartup ( this , out bool migrationNeeded ) ;
2019-10-13 02:02:07 -04:00
if ( ! continueWithStartup )
2020-01-05 06:49:44 -05:00
{
2020-01-21 17:23:11 -05:00
End ( null ) ;
2020-01-05 06:49:44 -05:00
}
2020-11-18 19:34:28 -05:00
_virtualFileSystem = VirtualFileSystem . CreateInstance ( ) ;
2020-09-20 23:45:30 -04:00
_userChannelPersistence = new UserChannelPersistence ( ) ;
2020-11-18 19:34:28 -05:00
_contentManager = new ContentManager ( _virtualFileSystem ) ;
2019-09-02 12:03:57 -04:00
2020-01-05 06:49:44 -05:00
if ( migrationNeeded )
{
2020-01-21 17:23:11 -05:00
bool migrationSuccessful = Migration . DoMigrationForStartup ( this , _virtualFileSystem ) ;
2020-01-05 06:49:44 -05:00
if ( ! migrationSuccessful )
{
2020-01-21 17:23:11 -05:00
End ( null ) ;
2020-01-05 06:49:44 -05:00
}
}
2020-01-21 17:23:11 -05:00
// Make sure that everything is loaded.
_virtualFileSystem . Reload ( ) ;
2019-09-02 12:03:57 -04:00
ApplyTheme ( ) ;
2020-12-01 17:02:27 -05:00
if ( ConfigurationState . Instance . Ui . StartFullscreen )
{
_startFullScreen . Active = true ;
}
2019-09-02 12:03:57 -04:00
_stopEmulation . Sensitive = false ;
2019-12-21 14:52:31 -05:00
if ( ConfigurationState . Instance . Ui . GuiColumns . FavColumn ) _favToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . IconColumn ) _iconToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . AppColumn ) _appToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . DevColumn ) _developerToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . VersionColumn ) _versionToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . TimePlayedColumn ) _timePlayedToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . LastPlayedColumn ) _lastPlayedToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileExtColumn ) _fileExtToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileSizeColumn ) _fileSizeToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . PathColumn ) _pathToggle . Active = true ;
2019-11-28 23:32:51 -05:00
_gameTable . Model = _tableStore = new ListStore (
2019-12-21 14:52:31 -05:00
typeof ( bool ) ,
typeof ( Gdk . Pixbuf ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
2020-03-25 13:09:38 -04:00
typeof ( string ) ,
typeof ( BlitStruct < ApplicationControlProperty > ) ) ;
2019-12-21 14:52:31 -05:00
2019-11-28 23:32:51 -05:00
_tableStore . SetSortFunc ( 5 , TimePlayedSort ) ;
_tableStore . SetSortFunc ( 6 , LastPlayedSort ) ;
_tableStore . SetSortFunc ( 8 , FileSizeSort ) ;
2020-06-26 06:30:16 -04:00
int columnId = ConfigurationState . Instance . Ui . ColumnSort . SortColumnId ;
bool ascending = ConfigurationState . Instance . Ui . ColumnSort . SortAscending ;
_tableStore . SetSortColumnId ( columnId , ascending ? SortType . Ascending : SortType . Descending ) ;
2019-11-28 23:32:51 -05:00
2020-04-25 09:02:44 -04:00
_gameTable . EnableSearch = true ;
_gameTable . SearchColumn = 2 ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
UpdateGameTable ( ) ;
2020-01-11 21:10:55 -05:00
2020-07-03 19:29:36 -04:00
ConfigurationState . Instance . Ui . GameDirs . Event + = ( sender , args ) = >
{
if ( args . OldValue ! = args . NewValue )
{
UpdateGameTable ( ) ;
}
} ;
2020-01-11 21:10:55 -05:00
Task . Run ( RefreshFirmwareLabel ) ;
2020-02-11 19:56:19 -05:00
2020-03-06 21:40:06 -05:00
_statusBar . Hide ( ) ;
2020-08-02 21:30:58 -04:00
_uiHandler = new GtkHostUiHandler ( this ) ;
2020-09-20 23:45:30 -04:00
_gamePath = null ;
2019-11-28 23:32:51 -05:00
}
2020-07-23 09:12:19 -04:00
private void MainWindow_WindowStateEvent ( object o , WindowStateEventArgs args )
{
_fullScreen . Label = args . Event . NewWindowState . HasFlag ( Gdk . WindowState . Fullscreen ) ? "Exit Fullscreen" : "Enter Fullscreen" ;
}
2019-11-28 23:32:51 -05:00
internal static void ApplyTheme ( )
{
2019-12-21 14:52:31 -05:00
if ( ! ConfigurationState . Instance . Ui . EnableCustomTheme )
2019-09-02 12:03:57 -04:00
{
2019-11-28 23:32:51 -05:00
return ;
}
2019-09-02 12:03:57 -04:00
2019-12-21 14:52:31 -05:00
if ( File . Exists ( ConfigurationState . Instance . Ui . CustomThemePath ) & & ( System . IO . Path . GetExtension ( ConfigurationState . Instance . Ui . CustomThemePath ) = = ".css" ) )
2019-11-28 23:32:51 -05:00
{
CssProvider cssProvider = new CssProvider ( ) ;
2019-09-02 12:03:57 -04:00
2019-12-21 14:52:31 -05:00
cssProvider . LoadFromPath ( ConfigurationState . Instance . Ui . CustomThemePath ) ;
2019-09-02 12:03:57 -04:00
2019-11-28 23:32:51 -05:00
StyleContext . AddProviderForScreen ( Gdk . Screen . Default , cssProvider , 800 ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
else
2019-09-02 12:03:57 -04:00
{
2020-08-03 19:32:53 -04:00
Logger . Warning ? . Print ( LogClass . Application , $"The \" custom_theme_path \ " section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\"." ) ;
2019-11-28 23:32:51 -05:00
}
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void UpdateColumns ( )
2019-09-02 12:03:57 -04:00
{
2019-11-28 23:32:51 -05:00
foreach ( TreeViewColumn column in _gameTable . Columns )
{
_gameTable . RemoveColumn ( column ) ;
}
2019-09-02 12:03:57 -04:00
2019-11-28 23:32:51 -05:00
CellRendererToggle favToggle = new CellRendererToggle ( ) ;
favToggle . Toggled + = FavToggle_Toggled ;
2019-12-21 14:52:31 -05:00
if ( ConfigurationState . Instance . Ui . GuiColumns . FavColumn ) _gameTable . AppendColumn ( "Fav" , favToggle , "active" , 0 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . IconColumn ) _gameTable . AppendColumn ( "Icon" , new CellRendererPixbuf ( ) , "pixbuf" , 1 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . AppColumn ) _gameTable . AppendColumn ( "Application" , new CellRendererText ( ) , "text" , 2 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . DevColumn ) _gameTable . AppendColumn ( "Developer" , new CellRendererText ( ) , "text" , 3 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . VersionColumn ) _gameTable . AppendColumn ( "Version" , new CellRendererText ( ) , "text" , 4 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . TimePlayedColumn ) _gameTable . AppendColumn ( "Time Played" , new CellRendererText ( ) , "text" , 5 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . LastPlayedColumn ) _gameTable . AppendColumn ( "Last Played" , new CellRendererText ( ) , "text" , 6 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileExtColumn ) _gameTable . AppendColumn ( "File Ext" , new CellRendererText ( ) , "text" , 7 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileSizeColumn ) _gameTable . AppendColumn ( "File Size" , new CellRendererText ( ) , "text" , 8 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . PathColumn ) _gameTable . AppendColumn ( "Path" , new CellRendererText ( ) , "text" , 9 ) ;
2019-11-28 23:32:51 -05:00
foreach ( TreeViewColumn column in _gameTable . Columns )
2019-09-02 12:03:57 -04:00
{
2020-06-26 06:30:16 -04:00
switch ( column . Title )
{
case "Fav" :
column . SortColumnId = 0 ;
column . Clicked + = Column_Clicked ;
break ;
case "Application" :
column . SortColumnId = 2 ;
column . Clicked + = Column_Clicked ;
break ;
case "Developer" :
column . SortColumnId = 3 ;
column . Clicked + = Column_Clicked ;
break ;
case "Version" :
column . SortColumnId = 4 ;
column . Clicked + = Column_Clicked ;
break ;
case "Time Played" :
column . SortColumnId = 5 ;
column . Clicked + = Column_Clicked ;
break ;
case "Last Played" :
column . SortColumnId = 6 ;
column . Clicked + = Column_Clicked ;
break ;
case "File Ext" :
column . SortColumnId = 7 ;
column . Clicked + = Column_Clicked ;
break ;
case "File Size" :
column . SortColumnId = 8 ;
column . Clicked + = Column_Clicked ;
break ;
case "Path" :
column . SortColumnId = 9 ;
column . Clicked + = Column_Clicked ;
break ;
}
2019-09-02 12:03:57 -04:00
}
2019-12-21 14:52:31 -05:00
}
private HLE . Switch InitializeSwitchInstance ( )
{
2020-01-21 17:23:11 -05:00
_virtualFileSystem . Reload ( ) ;
2020-09-20 23:45:30 -04:00
HLE . Switch instance = new HLE . Switch ( _virtualFileSystem , _contentManager , _userChannelPersistence , InitializeRenderer ( ) , InitializeAudioEngine ( ) )
2020-08-02 21:30:58 -04:00
{
UiHandler = _uiHandler
} ;
2019-12-21 14:52:31 -05:00
instance . Initialize ( ) ;
return instance ;
2019-09-02 12:03:57 -04:00
}
2020-01-31 13:21:46 -05:00
internal static void UpdateGameTable ( )
2019-09-02 12:03:57 -04:00
{
2020-07-03 19:29:36 -04:00
if ( _updatingGameTable | | _gameLoaded )
2019-09-02 12:03:57 -04:00
{
2019-11-28 23:32:51 -05:00
return ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
_updatingGameTable = true ;
_tableStore . Clear ( ) ;
2020-01-31 13:21:46 -05:00
Thread applicationLibraryThread = new Thread ( ( ) = >
{
ApplicationLibrary . LoadApplications ( ConfigurationState . Instance . Ui . GameDirs ,
_virtualFileSystem , ConfigurationState . Instance . System . Language ) ;
2019-11-28 23:32:51 -05:00
2020-01-31 13:21:46 -05:00
_updatingGameTable = false ;
} ) ;
applicationLibraryThread . Name = "GUI.ApplicationLibraryThread" ;
applicationLibraryThread . IsBackground = true ;
applicationLibraryThread . Start ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-09-07 21:59:41 -04:00
internal void LoadApplication ( string path )
2019-09-02 12:03:57 -04:00
{
if ( _gameLoaded )
{
2020-05-02 22:00:53 -04:00
GtkDialog . CreateInfoDialog ( "Ryujinx" , "A game has already been loaded" , "Please close it first and try again." ) ;
2019-09-02 12:03:57 -04:00
}
else
{
2020-11-15 14:56:44 -05:00
#if ! DEBUG
2020-03-30 17:39:46 -04:00
if ( ConfigurationState . Instance . Logger . EnableDebug . Value )
{
MessageDialog debugWarningDialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Warning , ButtonsType . YesNo , null )
{
Title = "Ryujinx - Warning" ,
Text = "You have debug logging enabled, which is designed to be used by developers only." ,
SecondaryText = "For optimal performance, it's recommended to disable debug logging. Would you like to disable debug logging now?"
} ;
if ( debugWarningDialog . Run ( ) = = ( int ) ResponseType . Yes )
{
ConfigurationState . Instance . Logger . EnableDebug . Value = false ;
SaveConfig ( ) ;
}
debugWarningDialog . Dispose ( ) ;
}
if ( ! string . IsNullOrWhiteSpace ( ConfigurationState . Instance . Graphics . ShadersDumpPath . Value ) )
{
MessageDialog shadersDumpWarningDialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Warning , ButtonsType . YesNo , null )
{
Title = "Ryujinx - Warning" ,
Text = "You have shader dumping enabled, which is designed to be used by developers only." ,
SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?"
} ;
if ( shadersDumpWarningDialog . Run ( ) = = ( int ) ResponseType . Yes )
{
ConfigurationState . Instance . Graphics . ShadersDumpPath . Value = "" ;
SaveConfig ( ) ;
}
shadersDumpWarningDialog . Dispose ( ) ;
}
2020-11-15 14:56:44 -05:00
#endif
2019-09-19 19:59:48 -04:00
Logger . RestartTime ( ) ;
2020-01-21 17:23:11 -05:00
HLE . Switch device = InitializeSwitchInstance ( ) ;
2020-07-06 22:41:07 -04:00
UpdateGraphicsConfig ( ) ;
2019-12-21 14:52:31 -05:00
2020-09-01 05:09:42 -04:00
SystemVersion firmwareVersion = _contentManager . GetCurrentFirmwareVersion ( ) ;
bool isDirectory = Directory . Exists ( path ) ;
if ( ! SetupValidator . CanStartApplication ( _contentManager , path , out UserError userError ) )
{
if ( SetupValidator . CanFixStartApplication ( _contentManager , path , userError , out firmwareVersion ) )
{
if ( userError = = UserError . NoFirmware )
{
MessageDialog shouldInstallFirmwareDialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Info , ButtonsType . YesNo , null )
{
Title = "Ryujinx - Info" ,
Text = "No Firmware Installed" ,
SecondaryText = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})"
} ;
if ( shouldInstallFirmwareDialog . Run ( ) ! = ( int ) ResponseType . Yes )
{
shouldInstallFirmwareDialog . Dispose ( ) ;
UserErrorDialog . CreateUserErrorDialog ( userError ) ;
device . Dispose ( ) ;
return ;
}
else
{
shouldInstallFirmwareDialog . Dispose ( ) ;
}
}
if ( ! SetupValidator . TryFixStartApplication ( _contentManager , path , userError , out _ ) )
{
UserErrorDialog . CreateUserErrorDialog ( userError ) ;
device . Dispose ( ) ;
return ;
}
// Tell the user that we installed a firmware for them.
if ( userError = = UserError . NoFirmware )
{
firmwareVersion = _contentManager . GetCurrentFirmwareVersion ( ) ;
RefreshFirmwareLabel ( ) ;
GtkDialog . CreateInfoDialog ( "Ryujinx - Info" , $"Firmware {firmwareVersion.VersionString} was installed" ,
$"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start." ) ;
}
}
else
{
UserErrorDialog . CreateUserErrorDialog ( userError ) ;
device . Dispose ( ) ;
return ;
}
}
Logger . Notice . Print ( LogClass . Application , $"Using Firmware Version: {firmwareVersion?.VersionString}" ) ;
2020-05-05 07:51:04 -04:00
2019-09-02 12:03:57 -04:00
if ( Directory . Exists ( path ) )
{
string [ ] romFsFiles = Directory . GetFiles ( path , "*.istorage" ) ;
if ( romFsFiles . Length = = 0 )
{
romFsFiles = Directory . GetFiles ( path , "*.romfs" ) ;
}
if ( romFsFiles . Length > 0 )
{
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , "Loading as cart with RomFS." ) ;
2020-01-21 17:23:11 -05:00
device . LoadCart ( path , romFsFiles [ 0 ] ) ;
2019-09-02 12:03:57 -04:00
}
else
{
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , "Loading as cart WITHOUT RomFS." ) ;
2020-01-21 17:23:11 -05:00
device . LoadCart ( path ) ;
2019-09-02 12:03:57 -04:00
}
}
else if ( File . Exists ( path ) )
{
switch ( System . IO . Path . GetExtension ( path ) . ToLowerInvariant ( ) )
{
case ".xci" :
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , "Loading as XCI." ) ;
2020-01-21 17:23:11 -05:00
device . LoadXci ( path ) ;
2019-09-02 12:03:57 -04:00
break ;
case ".nca" :
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , "Loading as NCA." ) ;
2020-01-21 17:23:11 -05:00
device . LoadNca ( path ) ;
2019-09-02 12:03:57 -04:00
break ;
case ".nsp" :
case ".pfs0" :
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , "Loading as NSP." ) ;
2020-01-21 17:23:11 -05:00
device . LoadNsp ( path ) ;
2019-09-02 12:03:57 -04:00
break ;
default :
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , "Loading as homebrew." ) ;
2019-09-02 12:03:57 -04:00
try
{
2020-01-21 17:23:11 -05:00
device . LoadProgram ( path ) ;
2019-09-02 12:03:57 -04:00
}
catch ( ArgumentOutOfRangeException )
{
2020-08-03 19:32:53 -04:00
Logger . Error ? . Print ( LogClass . Application , "The file which you have specified is unsupported by Ryujinx." ) ;
2019-09-02 12:03:57 -04:00
}
break ;
}
}
else
{
2020-08-03 19:32:53 -04:00
Logger . Warning ? . Print ( LogClass . Application , "Please specify a valid XCI/NCA/NSP/PFS0/NRO file." ) ;
2020-03-28 23:25:54 -04:00
device . Dispose ( ) ;
return ;
2019-09-02 12:03:57 -04:00
}
2020-01-21 17:23:11 -05:00
_emulationContext = device ;
2020-09-20 23:45:30 -04:00
_gamePath = path ;
2020-01-21 17:23:11 -05:00
2020-02-13 12:43:29 -05:00
_deviceExitStatus . Reset ( ) ;
2020-02-06 06:38:24 -05:00
2020-11-17 16:40:19 -05:00
Translator . IsReadyForTranslation . Reset ( ) ;
#if MACOS_BUILD
CreateGameWindow ( device ) ;
#else
Thread windowThread = new Thread ( ( ) = >
{
CreateGameWindow ( device ) ;
} )
{
Name = "GUI.WindowThread"
} ;
windowThread . Start ( ) ;
#endif
2019-09-02 12:03:57 -04:00
_gameLoaded = true ;
_stopEmulation . Sensitive = true ;
2020-01-11 21:10:55 -05:00
_firmwareInstallFile . Sensitive = false ;
_firmwareInstallDirectory . Sensitive = false ;
2020-05-15 02:16:46 -04:00
DiscordIntegrationModule . SwitchToPlayingState ( device . Application . TitleIdText , device . Application . TitleName ) ;
2019-09-02 12:03:57 -04:00
2020-05-15 02:16:46 -04:00
ApplicationLibrary . LoadAndSaveMetaData ( device . Application . TitleIdText , appMetadata = >
2019-09-02 12:03:57 -04:00
{
2020-01-11 22:01:04 -05:00
appMetadata . LastPlayed = DateTime . UtcNow . ToString ( ) ;
} ) ;
2019-09-02 12:03:57 -04:00
}
}
2020-01-21 17:23:11 -05:00
private void CreateGameWindow ( HLE . Switch device )
2019-09-02 12:03:57 -04:00
{
2020-08-17 21:49:37 -04:00
if ( Environment . OSVersion . Platform = = PlatformID . Win32NT )
{
_windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution ( 1 ) ;
}
2020-11-17 16:40:19 -05:00
_glWidget = new GlRenderer ( _emulationContext , ConfigurationState . Instance . Logger . GraphicsDebugLevel ) ;
2020-02-11 19:56:19 -05:00
Application . Invoke ( delegate
2019-09-02 12:03:57 -04:00
{
2020-02-11 19:56:19 -05:00
_viewBox . Remove ( _gameTableWindow ) ;
2020-05-02 22:00:53 -04:00
_glWidget . Expand = true ;
_viewBox . Child = _glWidget ;
2020-02-11 19:56:19 -05:00
2020-05-02 22:00:53 -04:00
_glWidget . ShowAll ( ) ;
2020-03-06 21:40:06 -05:00
EditFooterForGameRender ( ) ;
2020-07-23 09:12:19 -04:00
if ( this . Window . State . HasFlag ( Gdk . WindowState . Fullscreen ) )
{
ToggleExtraWidgets ( false ) ;
}
2020-12-01 17:02:27 -05:00
else if ( ConfigurationState . Instance . Ui . StartFullscreen . Value )
{
FullScreen_Toggled ( null , null ) ;
}
2020-02-11 19:56:19 -05:00
} ) ;
2020-05-02 22:00:53 -04:00
_glWidget . WaitEvent . WaitOne ( ) ;
2020-02-11 19:56:19 -05:00
2020-05-02 22:00:53 -04:00
_glWidget . Start ( ) ;
2020-02-11 19:56:19 -05:00
2020-06-16 14:28:02 -04:00
Ptc . Close ( ) ;
PtcProfiler . Stop ( ) ;
2020-02-13 12:43:29 -05:00
device . Dispose ( ) ;
_deviceExitStatus . Set ( ) ;
// NOTE: Everything that is here will not be executed when you close the UI.
2020-02-11 19:56:19 -05:00
Application . Invoke ( delegate
{
2020-07-23 09:12:19 -04:00
if ( this . Window . State . HasFlag ( Gdk . WindowState . Fullscreen ) )
{
ToggleExtraWidgets ( true ) ;
}
2020-05-02 22:00:53 -04:00
_viewBox . Remove ( _glWidget ) ;
_glWidget . Exit ( ) ;
2020-02-11 19:56:19 -05:00
2020-05-02 22:00:53 -04:00
if ( _glWidget . Window ! = this . Window & & _glWidget . Window ! = null )
2020-02-11 19:56:19 -05:00
{
2020-05-02 22:00:53 -04:00
_glWidget . Window . Dispose ( ) ;
2020-02-11 19:56:19 -05:00
}
2020-05-02 22:00:53 -04:00
_glWidget . Dispose ( ) ;
2020-08-17 21:49:37 -04:00
_windowsMultimediaTimerResolution ? . Dispose ( ) ;
_windowsMultimediaTimerResolution = null ;
2020-02-13 12:43:29 -05:00
2020-02-11 19:56:19 -05:00
_viewBox . Add ( _gameTableWindow ) ;
_gameTableWindow . Expand = true ;
2020-02-14 05:33:22 -05:00
this . Window . Title = $"Ryujinx {Program.Version}" ;
2020-02-11 19:56:19 -05:00
2020-02-13 12:43:29 -05:00
_emulationContext = null ;
_gameLoaded = false ;
2020-05-02 22:00:53 -04:00
_glWidget = null ;
2020-02-13 12:43:29 -05:00
DiscordIntegrationModule . SwitchToMainMenu ( ) ;
RecreateFooterForMenu ( ) ;
2020-02-11 19:56:19 -05:00
UpdateColumns ( ) ;
UpdateGameTable ( ) ;
Task . Run ( RefreshFirmwareLabel ) ;
2020-09-20 23:45:30 -04:00
Task . Run ( HandleRelaunch ) ;
2020-01-21 17:23:11 -05:00
_stopEmulation . Sensitive = false ;
_firmwareInstallFile . Sensitive = true ;
_firmwareInstallDirectory . Sensitive = true ;
} ) ;
2020-02-13 12:43:29 -05:00
}
private void RecreateFooterForMenu ( )
{
2020-03-06 21:40:06 -05:00
_listStatusBox . Show ( ) ;
_statusBar . Hide ( ) ;
2020-02-13 12:43:29 -05:00
}
2020-02-11 19:56:19 -05:00
2020-03-06 21:40:06 -05:00
private void EditFooterForGameRender ( )
2020-02-13 12:43:29 -05:00
{
2020-03-06 21:40:06 -05:00
_listStatusBox . Hide ( ) ;
_statusBar . Show ( ) ;
2020-02-11 19:56:19 -05:00
}
public void ToggleExtraWidgets ( bool show )
{
2020-05-02 22:00:53 -04:00
if ( _glWidget ! = null )
2020-02-11 19:56:19 -05:00
{
if ( show )
{
_menuBar . ShowAll ( ) ;
2020-03-06 21:40:06 -05:00
_footerBox . Show ( ) ;
_statusBar . Show ( ) ;
2020-02-11 19:56:19 -05:00
}
else
{
_menuBar . Hide ( ) ;
_footerBox . Hide ( ) ;
}
}
2020-01-21 17:23:11 -05:00
}
private static void UpdateGameMetadata ( string titleId )
{
if ( _gameLoaded )
{
ApplicationLibrary . LoadAndSaveMetaData ( titleId , appMetadata = >
{
DateTime lastPlayedDateTime = DateTime . Parse ( appMetadata . LastPlayed ) ;
double sessionTimePlayed = DateTime . UtcNow . Subtract ( lastPlayedDateTime ) . TotalSeconds ;
2019-09-02 12:03:57 -04:00
2020-01-21 17:23:11 -05:00
appMetadata . TimePlayed + = Math . Round ( sessionTimePlayed , MidpointRounding . AwayFromZero ) ;
} ) ;
2019-09-02 12:03:57 -04:00
}
}
2020-07-06 22:41:07 -04:00
public static void UpdateGraphicsConfig ( )
{
2020-12-15 21:19:07 -05:00
int resScale = ConfigurationState . Instance . Graphics . ResScale ;
2020-07-06 22:41:07 -04:00
float resScaleCustom = ConfigurationState . Instance . Graphics . ResScaleCustom ;
2020-12-15 21:19:07 -05:00
Graphics . Gpu . GraphicsConfig . ResScale = ( resScale = = - 1 ) ? resScaleCustom : resScale ;
Graphics . Gpu . GraphicsConfig . MaxAnisotropy = ConfigurationState . Instance . Graphics . MaxAnisotropy ;
Graphics . Gpu . GraphicsConfig . ShadersDumpPath = ConfigurationState . Instance . Graphics . ShadersDumpPath ;
2020-11-12 18:15:34 -05:00
Graphics . Gpu . GraphicsConfig . EnableShaderCache = ConfigurationState . Instance . Graphics . EnableShaderCache ;
2020-07-06 22:41:07 -04:00
}
2020-05-02 22:00:53 -04:00
public static void SaveConfig ( )
{
2020-05-03 06:08:21 -04:00
ConfigurationState . Instance . ToFileFormat ( ) . SaveConfig ( Program . ConfigurationPath ) ;
2020-05-02 22:00:53 -04:00
}
2020-01-21 17:23:11 -05:00
private void End ( HLE . Switch device )
2019-09-02 12:03:57 -04:00
{
2019-11-28 23:32:51 -05:00
if ( _ending )
{
return ;
}
_ending = true ;
2020-01-21 17:23:11 -05:00
if ( device ! = null )
2019-09-02 12:03:57 -04:00
{
2020-05-15 02:16:46 -04:00
UpdateGameMetadata ( device . Application . TitleIdText ) ;
2020-02-06 06:38:24 -05:00
2020-05-02 22:00:53 -04:00
if ( _glWidget ! = null )
2020-02-06 06:38:24 -05:00
{
2020-02-13 12:43:29 -05:00
// We tell the widget that we are exiting
2020-05-02 22:00:53 -04:00
_glWidget . Exit ( ) ;
2020-02-13 12:43:29 -05:00
// Wait for the other thread to dispose the HLE context before exiting.
_deviceExitStatus . WaitOne ( ) ;
2020-02-06 06:38:24 -05:00
}
2019-09-02 12:03:57 -04:00
}
2020-01-21 17:23:11 -05:00
Dispose ( ) ;
DiscordIntegrationModule . Exit ( ) ;
2020-06-16 14:28:02 -04:00
Ptc . Dispose ( ) ;
PtcProfiler . Dispose ( ) ;
2020-08-03 19:32:53 -04:00
Logger . Shutdown ( ) ;
2020-01-21 17:23:11 -05:00
Application . Quit ( ) ;
}
private static IRenderer InitializeRenderer ( )
{
return new Renderer ( ) ;
2019-09-02 12:03:57 -04:00
}
private static IAalOutput InitializeAudioEngine ( )
{
2020-07-03 19:16:49 -04:00
if ( ConfigurationState . Instance . System . AudioBackend . Value = = AudioBackend . SoundIo )
2019-09-02 12:03:57 -04:00
{
2020-07-03 19:16:49 -04:00
if ( SoundIoAudioOut . IsSupported )
{
return new SoundIoAudioOut ( ) ;
}
else
{
2020-08-03 19:32:53 -04:00
Logger . Warning ? . Print ( LogClass . Audio , "SoundIO is not supported, falling back to dummy audio out." ) ;
2020-07-03 19:16:49 -04:00
}
2019-09-02 12:03:57 -04:00
}
2020-07-03 19:16:49 -04:00
else if ( ConfigurationState . Instance . System . AudioBackend . Value = = AudioBackend . OpenAl )
2019-09-02 12:03:57 -04:00
{
2020-07-03 19:16:49 -04:00
if ( OpenALAudioOut . IsSupported )
{
return new OpenALAudioOut ( ) ;
}
else
{
2020-08-03 19:32:53 -04:00
Logger . Warning ? . Print ( LogClass . Audio , "OpenAL is not supported, trying to fall back to SoundIO." ) ;
2020-07-03 20:38:39 -04:00
if ( SoundIoAudioOut . IsSupported )
{
2020-08-03 19:32:53 -04:00
Logger . Warning ? . Print ( LogClass . Audio , "Found SoundIO, changing configuration." ) ;
2020-07-03 20:38:39 -04:00
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . SoundIo ;
SaveConfig ( ) ;
return new SoundIoAudioOut ( ) ;
}
else
{
2020-08-03 19:32:53 -04:00
Logger . Warning ? . Print ( LogClass . Audio , "SoundIO is not supported, falling back to dummy audio out." ) ;
2020-07-03 20:38:39 -04:00
}
2020-07-03 19:16:49 -04:00
}
2019-09-02 12:03:57 -04:00
}
2020-07-03 19:16:49 -04:00
return new DummyAudioOut ( ) ;
2019-09-02 12:03:57 -04:00
}
//Events
2019-12-21 21:49:51 -05:00
private void Application_Added ( object sender , ApplicationAddedEventArgs args )
2019-11-28 23:32:51 -05:00
{
Application . Invoke ( delegate
{
_tableStore . AppendValues (
2019-12-21 21:49:51 -05:00
args . AppData . Favorite ,
new Gdk . Pixbuf ( args . AppData . Icon , 75 , 75 ) ,
$"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}" ,
args . AppData . Developer ,
args . AppData . Version ,
args . AppData . TimePlayed ,
args . AppData . LastPlayed ,
args . AppData . FileExtension ,
args . AppData . FileSize ,
2020-03-25 13:09:38 -04:00
args . AppData . Path ,
args . AppData . ControlHolder ) ;
2020-01-31 13:21:46 -05:00
} ) ;
}
2019-12-21 21:49:51 -05:00
2020-01-31 13:21:46 -05:00
private void ApplicationCount_Updated ( object sender , ApplicationCountUpdatedEventArgs args )
{
Application . Invoke ( delegate
{
2019-12-21 21:49:51 -05:00
_progressLabel . Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded" ;
2020-01-31 13:21:46 -05:00
float barValue = 0 ;
if ( args . NumAppsFound ! = 0 )
{
barValue = ( float ) args . NumAppsLoaded / args . NumAppsFound ;
}
_progressBar . Value = barValue ;
2020-06-26 06:30:16 -04:00
if ( args . NumAppsLoaded = = args . NumAppsFound ) // Reset the vertical scrollbar to the top when titles finish loading
{
_gameTableWindow . Vadjustment . Value = 0 ;
}
2019-11-28 23:32:51 -05:00
} ) ;
}
2020-03-06 21:40:06 -05:00
private void Update_StatusBar ( object sender , StatusUpdatedEventArgs args )
{
Application . Invoke ( delegate
{
2020-12-15 21:19:07 -05:00
_gameStatus . Text = args . GameStatus ;
_fifoStatus . Text = args . FifoStatus ;
_gpuName . Text = args . GpuName ;
_dockedMode . Text = args . DockedMode ;
_aspectRatio . Text = args . AspectRatio ;
2020-03-06 21:40:06 -05:00
if ( args . VSyncEnabled )
{
_vSyncStatus . Attributes = new Pango . AttrList ( ) ;
_vSyncStatus . Attributes . Insert ( new Pango . AttrForeground ( 11822 , 60138 , 51657 ) ) ;
}
else
{
_vSyncStatus . Attributes = new Pango . AttrList ( ) ;
_vSyncStatus . Attributes . Insert ( new Pango . AttrForeground ( ushort . MaxValue , 17733 , 21588 ) ) ;
}
} ) ;
}
2019-11-28 23:32:51 -05:00
private void FavToggle_Toggled ( object sender , ToggledArgs args )
{
_tableStore . GetIter ( out TreeIter treeIter , new TreePath ( args . Path ) ) ;
2020-01-11 22:01:04 -05:00
string titleId = _tableStore . GetValue ( treeIter , 2 ) . ToString ( ) . Split ( "\n" ) [ 1 ] . ToLower ( ) ;
2019-11-28 23:32:51 -05:00
2020-01-11 22:01:04 -05:00
bool newToggleValue = ! ( bool ) _tableStore . GetValue ( treeIter , 0 ) ;
2019-10-13 02:02:07 -04:00
2020-01-11 22:01:04 -05:00
_tableStore . SetValue ( treeIter , 0 , newToggleValue ) ;
2019-11-28 23:32:51 -05:00
2020-01-11 22:01:04 -05:00
ApplicationLibrary . LoadAndSaveMetaData ( titleId , appMetadata = >
2019-11-28 23:32:51 -05:00
{
2020-01-11 22:01:04 -05:00
appMetadata . Favorite = newToggleValue ;
} ) ;
2019-11-28 23:32:51 -05:00
}
2020-06-26 06:30:16 -04:00
private void Column_Clicked ( object sender , EventArgs args )
{
TreeViewColumn column = ( TreeViewColumn ) sender ;
ConfigurationState . Instance . Ui . ColumnSort . SortColumnId . Value = column . SortColumnId ;
ConfigurationState . Instance . Ui . ColumnSort . SortAscending . Value = column . SortOrder = = SortType . Ascending ;
SaveConfig ( ) ;
}
2019-11-28 23:32:51 -05:00
private void Row_Activated ( object sender , RowActivatedArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 21:49:51 -05:00
_gameTableSelection . GetSelected ( out TreeIter treeIter ) ;
2019-11-28 23:32:51 -05:00
string path = ( string ) _tableStore . GetValue ( treeIter , 9 ) ;
2019-09-02 12:03:57 -04:00
LoadApplication ( path ) ;
}
2020-11-18 19:34:28 -05:00
private void VSyncStatus_Clicked ( object sender , ButtonReleaseEventArgs args )
{
_emulationContext . EnableDeviceVsync = ! _emulationContext . EnableDeviceVsync ;
}
private void DockedMode_Clicked ( object sender , ButtonReleaseEventArgs args )
{
ConfigurationState . Instance . System . EnableDockedMode . Value = ! ConfigurationState . Instance . System . EnableDockedMode . Value ;
}
2020-12-15 21:19:07 -05:00
private void AspectRatio_Clicked ( object sender , ButtonReleaseEventArgs args )
{
AspectRatio aspectRatio = ConfigurationState . Instance . Graphics . AspectRatio . Value ;
ConfigurationState . Instance . Graphics . AspectRatio . Value = ( ( int ) aspectRatio + 1 ) > Enum . GetNames ( typeof ( AspectRatio ) ) . Length - 1 ? AspectRatio . Fixed4x3 : aspectRatio + 1 ;
}
2019-12-21 21:49:51 -05:00
private void Row_Clicked ( object sender , ButtonReleaseEventArgs args )
{
if ( args . Event . Button ! = 3 ) return ;
_gameTableSelection . GetSelected ( out TreeIter treeIter ) ;
if ( treeIter . UserData = = IntPtr . Zero ) return ;
2020-03-25 13:09:38 -04:00
BlitStruct < ApplicationControlProperty > controlData = ( BlitStruct < ApplicationControlProperty > ) _tableStore . GetValue ( treeIter , 10 ) ;
GameTableContextMenu contextMenu = new GameTableContextMenu ( _tableStore , controlData , treeIter , _virtualFileSystem ) ;
2019-12-21 21:49:51 -05:00
contextMenu . ShowAll ( ) ;
contextMenu . PopupAtPointer ( null ) ;
}
2019-11-28 23:32:51 -05:00
private void Load_Application_File ( object sender , EventArgs args )
2019-09-02 12:03:57 -04: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-28 23:32:51 -05:00
fileChooser . Dispose ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Load_Application_Folder ( object sender , EventArgs args )
2019-09-02 12:03:57 -04: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-28 23:32:51 -05:00
fileChooser . Dispose ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Open_Ryu_Folder ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-09-20 00:31:05 -04:00
Process . Start ( new ProcessStartInfo
2019-09-02 12:03:57 -04:00
{
2020-08-30 12:51:53 -04:00
FileName = AppDataManager . BaseDirPath ,
2019-09-02 12:03:57 -04:00
UseShellExecute = true ,
Verb = "open"
} ) ;
}
2020-09-20 00:31:05 -04:00
private void OpenLogsFolder_Pressed ( object sender , EventArgs args )
{
string logPath = System . IO . Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Logs" ) ;
DirectoryInfo directory = new DirectoryInfo ( logPath ) ;
directory . Create ( ) ;
Process . Start ( new ProcessStartInfo
{
FileName = logPath ,
UseShellExecute = true ,
Verb = "open"
} ) ;
}
2019-11-28 23:32:51 -05:00
private void Exit_Pressed ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-10-09 19:06:48 -04:00
if ( ! _gameLoaded | | GtkDialog . CreateExitDialog ( ) )
{
End ( _emulationContext ) ;
}
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Window_Close ( object sender , DeleteEventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-10-09 19:06:48 -04:00
if ( ! _gameLoaded | | GtkDialog . CreateExitDialog ( ) )
{
End ( _emulationContext ) ;
}
else
{
args . RetVal = true ;
}
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void StopEmulation_Pressed ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-05-02 22:00:53 -04:00
_glWidget ? . Exit ( ) ;
2019-09-02 12:03:57 -04:00
}
2020-01-11 21:10:55 -05:00
private void Installer_File_Pressed ( object o , EventArgs args )
{
FileChooserDialog fileChooser = new FileChooserDialog ( "Choose the firmware file to open" ,
this ,
FileChooserAction . Open ,
"Cancel" ,
ResponseType . Cancel ,
"Open" ,
ResponseType . Accept ) ;
fileChooser . Filter = new FileFilter ( ) ;
fileChooser . Filter . AddPattern ( "*.zip" ) ;
fileChooser . Filter . AddPattern ( "*.xci" ) ;
HandleInstallerDialog ( fileChooser ) ;
}
private void Installer_Directory_Pressed ( object o , EventArgs args )
{
FileChooserDialog directoryChooser = new FileChooserDialog ( "Choose the firmware directory to open" ,
this ,
FileChooserAction . SelectFolder ,
"Cancel" ,
ResponseType . Cancel ,
"Open" ,
ResponseType . Accept ) ;
HandleInstallerDialog ( directoryChooser ) ;
}
private void RefreshFirmwareLabel ( )
{
2020-05-02 22:00:53 -04:00
SystemVersion currentFirmware = _contentManager . GetCurrentFirmwareVersion ( ) ;
2020-01-11 21:10:55 -05:00
GLib . Idle . Add ( new GLib . IdleHandler ( ( ) = >
{
_firmwareVersionLabel . Text = currentFirmware ! = null ? currentFirmware . VersionString : "0.0.0" ;
return false ;
} ) ) ;
}
2020-09-20 23:45:30 -04:00
private void HandleRelaunch ( )
{
2020-10-09 18:52:11 -04:00
if ( _userChannelPersistence . PreviousIndex ! = - 1 & & _userChannelPersistence . ShouldRestart )
2020-09-20 23:45:30 -04:00
{
2020-10-09 18:52:11 -04:00
_userChannelPersistence . ShouldRestart = false ;
2020-09-20 23:45:30 -04:00
LoadApplication ( _gamePath ) ;
}
else
{
// otherwise, clear state.
_userChannelPersistence = new UserChannelPersistence ( ) ;
_gamePath = null ;
}
}
2020-01-11 21:10:55 -05:00
private void HandleInstallerDialog ( FileChooserDialog fileChooser )
{
if ( fileChooser . Run ( ) = = ( int ) ResponseType . Accept )
{
MessageDialog dialog = null ;
try
{
string filename = fileChooser . Filename ;
fileChooser . Dispose ( ) ;
2020-05-02 22:00:53 -04:00
SystemVersion firmwareVersion = _contentManager . VerifyFirmwarePackage ( filename ) ;
2020-01-11 21:10:55 -05:00
if ( firmwareVersion = = null )
{
dialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Info , ButtonsType . Ok , false , "" ) ;
dialog . Text = "Firmware not found." ;
dialog . SecondaryText = $"A valid system firmware was not found in {filename}." ;
2020-08-03 19:32:53 -04:00
Logger . Error ? . Print ( LogClass . Application , $"A valid system firmware was not found in {filename}." ) ;
2020-01-11 21:10:55 -05:00
dialog . Run ( ) ;
dialog . Hide ( ) ;
dialog . Dispose ( ) ;
return ;
}
2020-05-02 22:00:53 -04:00
SystemVersion currentVersion = _contentManager . GetCurrentFirmwareVersion ( ) ;
2020-01-11 21:10:55 -05:00
string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed." ;
if ( currentVersion ! = null )
{
dialogMessage + = $"This will replace the current system version {currentVersion.VersionString}. " ;
}
dialogMessage + = "Do you want to continue?" ;
dialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Question , ButtonsType . YesNo , false , "" ) ;
dialog . Text = $"Install Firmware {firmwareVersion.VersionString}" ;
dialog . SecondaryText = dialogMessage ;
int response = dialog . Run ( ) ;
dialog . Dispose ( ) ;
dialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Info , ButtonsType . None , false , "" ) ;
dialog . Text = $"Install Firmware {firmwareVersion.VersionString}" ;
dialog . SecondaryText = "Installing firmware..." ;
if ( response = = ( int ) ResponseType . Yes )
{
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , $"Installing firmware {firmwareVersion.VersionString}" ) ;
2020-01-11 21:10:55 -05:00
Thread thread = new Thread ( ( ) = >
{
GLib . Idle . Add ( new GLib . IdleHandler ( ( ) = >
{
dialog . Run ( ) ;
return false ;
} ) ) ;
try
{
2020-01-21 17:23:11 -05:00
_contentManager . InstallFirmware ( filename ) ;
2020-01-11 21:10:55 -05:00
GLib . Idle . Add ( new GLib . IdleHandler ( ( ) = >
{
dialog . Dispose ( ) ;
dialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Info , ButtonsType . Ok , false , "" ) ;
dialog . Text = $"Install Firmware {firmwareVersion.VersionString}" ;
dialog . SecondaryText = $"System version {firmwareVersion.VersionString} successfully installed." ;
2020-08-03 19:32:53 -04:00
Logger . Info ? . Print ( LogClass . Application , $"System version {firmwareVersion.VersionString} successfully installed." ) ;
2020-01-11 21:10:55 -05:00
dialog . Run ( ) ;
dialog . Dispose ( ) ;
return false ;
} ) ) ;
}
catch ( Exception ex )
{
GLib . Idle . Add ( new GLib . IdleHandler ( ( ) = >
{
dialog . Dispose ( ) ;
dialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Info , ButtonsType . Ok , false , "" ) ;
dialog . Text = $"Install Firmware {firmwareVersion.VersionString} Failed." ;
dialog . SecondaryText = $"An error occured while installing system version {firmwareVersion.VersionString}." +
" Please check logs for more info." ;
2020-08-03 19:32:53 -04:00
Logger . Error ? . Print ( LogClass . Application , ex . Message ) ;
2020-01-11 21:10:55 -05:00
dialog . Run ( ) ;
dialog . Dispose ( ) ;
return false ;
} ) ) ;
}
finally
{
RefreshFirmwareLabel ( ) ;
}
} ) ;
2020-01-12 19:21:54 -05:00
thread . Name = "GUI.FirmwareInstallerThread" ;
2020-01-11 21:10:55 -05:00
thread . Start ( ) ;
}
else
{
dialog . Dispose ( ) ;
}
}
catch ( Exception ex )
{
if ( dialog ! = null )
{
dialog . Dispose ( ) ;
}
dialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Info , ButtonsType . Ok , false , "" ) ;
dialog . Text = "Parsing Firmware Failed." ;
dialog . SecondaryText = "An error occured while parsing firmware. Please check the logs for more info." ;
2020-08-03 19:32:53 -04:00
Logger . Error ? . Print ( LogClass . Application , ex . Message ) ;
2020-01-11 21:10:55 -05:00
dialog . Run ( ) ;
dialog . Dispose ( ) ;
}
}
else
{
fileChooser . Dispose ( ) ;
}
}
2020-12-01 17:02:27 -05:00
private void FullScreen_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-02-11 19:56:19 -05:00
bool fullScreenToggled = this . Window . State . HasFlag ( Gdk . WindowState . Fullscreen ) ;
if ( ! fullScreenToggled )
2019-09-02 12:03:57 -04:00
{
Fullscreen ( ) ;
2020-02-11 19:56:19 -05:00
ToggleExtraWidgets ( false ) ;
2019-09-02 12:03:57 -04:00
}
else
{
Unfullscreen ( ) ;
2020-02-11 19:56:19 -05:00
ToggleExtraWidgets ( true ) ;
2019-09-02 12:03:57 -04:00
}
}
2020-12-01 17:02:27 -05:00
private void StartFullScreen_Toggled ( object sender , EventArgs args )
{
ConfigurationState . Instance . Ui . StartFullscreen . Value = _startFullScreen . Active ;
SaveConfig ( ) ;
}
2019-11-28 23:32:51 -05:00
private void Settings_Pressed ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-05-02 22:00:53 -04:00
SettingsWindow settingsWin = new SettingsWindow ( _virtualFileSystem , _contentManager ) ;
2019-11-28 23:32:51 -05:00
settingsWin . Show ( ) ;
2019-09-02 12:03:57 -04:00
}
2020-12-15 19:41:42 -05:00
private void Simulate_WakeUp_Message_Pressed ( object sender , EventArgs args )
{
_emulationContext . System . SimulateWakeUpMessage ( ) ;
}
2019-11-28 23:32:51 -05:00
private void Update_Pressed ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2020-09-29 16:05:25 -04:00
if ( Updater . CanUpdate ( true ) )
2019-09-02 12:03:57 -04:00
{
2020-11-27 12:57:20 -05:00
_ = Updater . BeginParse ( this , true ) ;
2019-09-02 12:03:57 -04:00
}
}
2019-11-28 23:32:51 -05:00
private void About_Pressed ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-11-28 23:32:51 -05:00
AboutWindow aboutWin = new AboutWindow ( ) ;
aboutWin . Show ( ) ;
}
private void Fav_Toggled ( object sender , EventArgs args )
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . FavColumn . Value = _favToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Icon_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . IconColumn . Value = _iconToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Title_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . AppColumn . Value = _appToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Developer_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . DevColumn . Value = _developerToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Version_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . VersionColumn . Value = _versionToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void TimePlayed_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . TimePlayedColumn . Value = _timePlayedToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void LastPlayed_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . LastPlayedColumn . Value = _lastPlayedToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void FileExt_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . FileExtColumn . Value = _fileExtToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void FileSize_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . FileSizeColumn . Value = _fileSizeToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
2019-09-02 12:03:57 -04:00
}
2019-11-28 23:32:51 -05:00
private void Path_Toggled ( object sender , EventArgs args )
2019-09-02 12:03:57 -04:00
{
2019-12-21 14:52:31 -05:00
ConfigurationState . Instance . Ui . GuiColumns . PathColumn . Value = _pathToggle . Active ;
2019-11-28 23:32:51 -05:00
2019-12-21 14:52:31 -05:00
SaveConfig ( ) ;
2019-11-28 23:32:51 -05:00
UpdateColumns ( ) ;
}
private void RefreshList_Pressed ( object sender , ButtonReleaseEventArgs args )
{
UpdateGameTable ( ) ;
}
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 12:03:57 -04:00
}
}
}