2022-08-22 22:21:23 +02:00
# include "gui/TitleManager.h"
# include "gui/helpers/wxCustomEvents.h"
# include "gui/helpers/wxCustomData.h"
# include "Cafe/TitleList/GameInfo.h"
# include "util/helpers/helpers.h"
# include "gui/helpers/wxHelpers.h"
2022-09-05 18:42:24 +02:00
# include "gui/wxHelper.h"
2022-08-22 22:21:23 +02:00
# include "gui/components/wxTitleManagerList.h"
# include "gui/components/wxDownloadManagerList.h"
# include "gui/GameUpdateWindow.h"
# include "gui/dialogs/SaveImport/SaveTransfer.h"
# include <wx/listbase.h>
# include <wx/sizer.h>
# include <wx/listctrl.h>
# include <wx/statline.h>
# include <wx/stattext.h>
# include <wx/textctrl.h>
# include <wx/statbmp.h>
# include <wx/panel.h>
# include <wx/bmpbuttn.h>
# include <wx/choice.h>
# include <wx/msgdlg.h>
# include <wx/filedlg.h>
# include <wx/combobox.h>
# include <wx/checkbox.h>
# include <pugixml.hpp>
# include <zip.h>
# include <wx/dirdlg.h>
# include <wx/notebook.h>
# include "config/ActiveSettings.h"
# include "gui/dialogs/SaveImport/SaveImportWindow.h"
# include "Cafe/Account/Account.h"
# include "Cemu/Tools/DownloadManager/DownloadManager.h"
# include "gui/CemuApp.h"
# include "Cafe/TitleList/TitleList.h"
2022-08-26 04:03:26 +02:00
# include "resource/embedded/resources.h"
2022-09-18 14:39:00 +02:00
2022-08-22 22:21:23 +02:00
# include "Cafe/TitleList/SaveList.h"
wxDEFINE_EVENT ( wxEVT_TITLE_FOUND , wxCommandEvent ) ;
wxDEFINE_EVENT ( wxEVT_TITLE_SEARCH_COMPLETE , wxCommandEvent ) ;
wxDEFINE_EVENT ( wxEVT_DL_TITLE_UPDATE , wxCommandEvent ) ;
wxDEFINE_EVENT ( wxEVT_DL_DISCONNECT_COMPLETE , wxCommandEvent ) ;
wxPanel * TitleManager : : CreateTitleManagerPage ( )
{
auto * panel = new wxPanel ( m_notebook ) ;
auto * sizer = new wxBoxSizer ( wxVERTICAL ) ;
{
auto * row = new wxFlexGridSizer ( 0 , 4 , 0 , 0 ) ;
row - > AddGrowableCol ( 1 ) ;
row - > Add ( new wxStaticText ( panel , wxID_ANY , _ ( " Filter " ) ) , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
m_filter = new wxTextCtrl ( panel , wxID_ANY ) ;
m_filter - > Bind ( wxEVT_TEXT , & TitleManager : : OnFilterChanged , this ) ;
row - > Add ( m_filter , 1 , wxALL | wxEXPAND , 5 ) ;
2022-09-18 14:39:00 +02:00
const wxImage refresh = wxBITMAP_PNG_FROM_DATA ( PNG_REFRESH ) . ConvertToImage ( ) ;
2022-08-22 22:21:23 +02:00
m_refresh_button = new wxBitmapButton ( panel , wxID_ANY , refresh . Scale ( 16 , 16 ) ) ;
m_refresh_button - > Disable ( ) ;
m_refresh_button - > Bind ( wxEVT_BUTTON , & TitleManager : : OnRefreshButton , this ) ;
m_refresh_button - > SetToolTip ( _ ( " Refresh " ) ) ;
row - > Add ( m_refresh_button , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
2022-09-18 14:39:00 +02:00
auto * help_button = new wxStaticBitmap ( panel , wxID_ANY , wxBITMAP_PNG_FROM_DATA ( PNG_HELP ) ) ;
2022-08-22 22:21:23 +02:00
help_button - > SetToolTip ( wxStringFormat2 ( _ ( " The following prefixes are supported: \n {0} \n {1} \n {2} \n {3} \n {4} " ) ,
" titleid: " , " name: " , " type: " , " version: " , " region: " ) ) ;
row - > Add ( help_button , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
sizer - > Add ( row , 0 , wxEXPAND , 5 ) ;
}
sizer - > Add ( new wxStaticLine ( panel , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxLI_HORIZONTAL ) , 0 , wxEXPAND | wxALL , 5 ) ;
m_title_list = new wxTitleManagerList ( panel ) ;
m_title_list - > SetSizeHints ( 800 , 600 ) ;
m_title_list - > Bind ( wxEVT_LIST_ITEM_SELECTED , & TitleManager : : OnTitleSelected , this ) ;
sizer - > Add ( m_title_list , 1 , wxALL | wxEXPAND , 5 ) ;
sizer - > Add ( new wxStaticLine ( panel , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxLI_HORIZONTAL ) , 0 , wxEXPAND | wxALL , 5 ) ;
{
auto * row = new wxFlexGridSizer ( 0 , 3 , 0 , 0 ) ;
row - > AddGrowableCol ( 2 ) ;
auto * install_button = new wxButton ( panel , wxID_ANY , _ ( " Install title " ) ) ;
install_button - > Bind ( wxEVT_BUTTON , & TitleManager : : OnInstallTitle , this ) ;
row - > Add ( install_button , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
row - > Add ( new wxStaticLine ( panel , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxLI_VERTICAL ) , 0 , wxEXPAND | wxALL , 5 ) ;
{
m_save_panel = new wxPanel ( panel ) ;
auto * save_sizer = new wxFlexGridSizer ( 0 , 7 , 0 , 0 ) ;
save_sizer - > Add ( new wxStaticText ( m_save_panel , wxID_ANY , _ ( " Account " ) ) , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
m_save_account_list = new wxChoice ( m_save_panel , wxID_ANY ) ;
m_save_account_list - > SetMinSize ( { 170 , - 1 } ) ;
m_save_account_list - > Bind ( wxEVT_CHOICE , & TitleManager : : OnSaveAccountSelected , this ) ;
save_sizer - > Add ( m_save_account_list , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
auto * save_open = new wxButton ( m_save_panel , wxID_ANY , _ ( " Open directory " ) ) ;
save_open - > Bind ( wxEVT_BUTTON , & TitleManager : : OnSaveOpenDirectory , this ) ;
save_open - > SetToolTip ( _ ( " Open the directory of the save entry " ) ) ;
save_open - > Disable ( ) ;
save_sizer - > Add ( save_open , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
auto * save_transfer = new wxButton ( m_save_panel , wxID_ANY , _ ( " Transfer " ) ) ;
save_transfer - > Bind ( wxEVT_BUTTON , & TitleManager : : OnSaveTransfer , this ) ;
save_transfer - > SetToolTip ( _ ( " Transfers the save entry to another persistent account id " ) ) ;
save_transfer - > Disable ( ) ;
save_sizer - > Add ( save_transfer , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
auto * save_delete = new wxButton ( m_save_panel , wxID_ANY , _ ( " Delete " ) ) ;
save_delete - > Bind ( wxEVT_BUTTON , & TitleManager : : OnSaveDelete , this ) ;
2022-09-03 22:55:58 +02:00
save_delete - > SetToolTip ( _ ( " Permanently delete the save entry " ) ) ;
2022-08-22 22:21:23 +02:00
save_delete - > Disable ( ) ;
save_sizer - > Add ( save_delete , 0 , wxALIGN_CENTER_VERTICAL | wxALL , 5 ) ;
m_save_import = new wxButton ( m_save_panel , wxID_ANY , _ ( " Import " ) ) ;
m_save_import - > Bind ( wxEVT_BUTTON , & TitleManager : : OnSaveImport , this ) ;
m_save_import - > SetToolTip ( _ ( " Imports a zipped save entry " ) ) ;
save_sizer - > Add ( m_save_import , 0 , wxALIGN_CENTER_VERTICAL | wxLEFT | wxTOP | wxBOTTOM , 5 ) ;
auto * export_bttn = new wxButton ( m_save_panel , wxID_ANY , _ ( " Export " ) ) ;
export_bttn - > Bind ( wxEVT_BUTTON , & TitleManager : : OnSaveExport , this ) ;
export_bttn - > SetToolTip ( _ ( " Exports the selected save entry as zip file " ) ) ;
export_bttn - > Disable ( ) ;
save_sizer - > Add ( export_bttn , 0 , wxALIGN_CENTER_VERTICAL | wxRIGHT | wxTOP | wxBOTTOM , 5 ) ;
m_save_panel - > SetSizerAndFit ( save_sizer ) ;
row - > Add ( m_save_panel , 1 , wxRESERVE_SPACE_EVEN_IF_HIDDEN | wxALIGN_CENTER_VERTICAL , 0 ) ;
m_save_panel - > Hide ( ) ; // hide by default
}
sizer - > Add ( row , 0 , wxEXPAND , 5 ) ;
}
panel - > SetSizerAndFit ( sizer ) ;
return panel ;
}
wxPanel * TitleManager : : CreateDownloadManagerPage ( )
{
auto * panel = new wxPanel ( m_notebook ) ;
auto * sizer = new wxBoxSizer ( wxVERTICAL ) ;
{
auto * row = new wxBoxSizer ( wxHORIZONTAL ) ;
m_account = new wxChoice ( panel , wxID_ANY ) ;
m_account - > SetMinSize ( { 250 , - 1 } ) ;
auto accounts = Account : : GetAccounts ( ) ;
if ( ! accounts . empty ( ) )
{
const auto id = GetConfig ( ) . account . m_persistent_id . GetValue ( ) ;
for ( const auto & a : accounts )
{
m_account - > Append ( a . ToString ( ) , ( void * ) static_cast < uintptr_t > ( a . GetPersistentId ( ) ) ) ;
if ( a . GetPersistentId ( ) = = id )
{
m_account - > SetSelection ( m_account - > GetCount ( ) - 1 ) ;
}
}
}
row - > Add ( m_account , 0 , wxALL , 5 ) ;
m_connect = new wxButton ( panel , wxID_ANY , _ ( " Connect " ) ) ;
m_connect - > Bind ( wxEVT_BUTTON , & TitleManager : : OnConnect , this ) ;
row - > Add ( m_connect , 0 , wxALL , 5 ) ;
sizer - > Add ( row , 0 , wxEXPAND , 5 ) ;
}
m_status_text = new wxStaticText ( panel , wxID_ANY , _ ( " Select an account and press Connect " ) ) ;
this - > Bind ( wxEVT_SET_TEXT , & TitleManager : : OnSetStatusText , this ) ;
sizer - > Add ( m_status_text , 0 , wxALL , 5 ) ;
sizer - > Add ( new wxStaticLine ( panel , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxLI_HORIZONTAL ) , 0 , wxEXPAND | wxALL , 5 ) ;
{
auto * row = new wxFlexGridSizer ( 0 , 3 , 0 , 0 ) ;
m_show_titles = new wxCheckBox ( panel , wxID_ANY , _ ( " Show available titles " ) ) ;
m_show_titles - > SetValue ( true ) ;
m_show_titles - > Enable ( false ) ;
row - > Add ( m_show_titles , 0 , wxALL , 5 ) ;
m_show_titles - > Bind ( wxEVT_CHECKBOX , & TitleManager : : OnDlFilterCheckbox , this ) ;
m_show_updates = new wxCheckBox ( panel , wxID_ANY , _ ( " Show available updates " ) ) ;
m_show_updates - > SetValue ( true ) ;
m_show_updates - > Enable ( false ) ;
row - > Add ( m_show_updates , 0 , wxALL , 5 ) ;
m_show_updates - > Bind ( wxEVT_CHECKBOX , & TitleManager : : OnDlFilterCheckbox , this ) ;
m_show_installed = new wxCheckBox ( panel , wxID_ANY , _ ( " Show installed " ) ) ;
m_show_installed - > SetValue ( true ) ;
m_show_installed - > Enable ( false ) ;
row - > Add ( m_show_installed , 0 , wxALL , 5 ) ;
m_show_installed - > Bind ( wxEVT_CHECKBOX , & TitleManager : : OnDlFilterCheckbox , this ) ;
sizer - > Add ( row , 0 , wxEXPAND , 5 ) ;
}
m_download_list = new wxDownloadManagerList ( panel ) ;
m_download_list - > SetSizeHints ( 800 , 600 ) ;
m_download_list - > Bind ( wxEVT_LIST_ITEM_SELECTED , & TitleManager : : OnTitleSelected , this ) ;
sizer - > Add ( m_download_list , 1 , wxALL | wxEXPAND , 5 ) ;
panel - > SetSizerAndFit ( sizer ) ;
return panel ;
}
TitleManager : : TitleManager ( wxWindow * parent , TitleManagerPage default_page )
2022-10-07 14:30:06 +02:00
: wxFrame ( parent , wxID_ANY , _ ( " Title Manager " ) , wxDefaultPosition , wxDefaultSize , wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL )
2022-08-22 22:21:23 +02:00
{
SetIcon ( wxICON ( X_BOX ) ) ;
auto * sizer = new wxBoxSizer ( wxVERTICAL ) ;
m_notebook = new wxNotebook ( this , wxID_ANY ) ;
m_notebook - > AddPage ( CreateTitleManagerPage ( ) , _ ( " Title Manager " ) , default_page = = TitleManagerPage : : TitleManager ) ;
m_notebook - > AddPage ( CreateDownloadManagerPage ( ) , _ ( " Download Manager " ) , default_page = = TitleManagerPage : : DownloadManager ) ;
sizer - > Add ( m_notebook , 1 , wxEXPAND ) ;
m_status_bar = CreateStatusBar ( 2 , wxSTB_SIZEGRIP ) ;
m_status_bar - > SetStatusText ( _ ( " Searching for titles... " ) ) ;
this - > SetSizerAndFit ( sizer ) ;
this - > Centre ( wxBOTH ) ;
this - > Bind ( wxEVT_SET_STATUS_BAR_TEXT , & TitleManager : : OnSetStatusBarText , this ) ;
this - > Bind ( wxEVT_TITLE_FOUND , & TitleManager : : OnTitleFound , this ) ;
this - > Bind ( wxEVT_TITLE_SEARCH_COMPLETE , & TitleManager : : OnTitleSearchComplete , this ) ;
this - > Bind ( wxEVT_DL_TITLE_UPDATE , & TitleManager : : OnDownloadableTitleUpdate , this ) ;
this - > Bind ( wxEVT_DL_DISCONNECT_COMPLETE , & TitleManager : : OnDisconnect , this ) ;
// TODO typing on title list should change filter text and filter!
// if download manager is already active then restore state
DownloadManager * dlMgr = DownloadManager : : GetInstance ( false ) ;
if ( dlMgr & & dlMgr - > IsConnected ( ) )
{
dlMgr - > setUserData ( this ) ;
dlMgr - > registerCallbacks (
TitleManager : : Callback_ConnectStatusUpdate ,
TitleManager : : Callback_AddDownloadableTitle ,
TitleManager : : Callback_RemoveDownloadableTitle ) ;
SetConnected ( true ) ;
}
m_callbackId = CafeTitleList : : RegisterCallback ( [ ] ( CafeTitleListCallbackEvent * evt , void * ctx ) { ( ( TitleManager * ) ctx ) - > HandleTitleListCallback ( evt ) ; } , this ) ;
}
TitleManager : : ~ TitleManager ( )
{
CafeTitleList : : UnregisterCallback ( m_callbackId ) ;
// unregister callbacks for download manager
DownloadManager * dlMgr = DownloadManager : : GetInstance ( false ) ;
if ( dlMgr )
{
dlMgr - > setUserData ( nullptr ) ;
dlMgr - > registerCallbacks (
nullptr ,
nullptr ,
nullptr ) ;
// if download manager is still downloading / installing then show a warning
if ( dlMgr - > hasActiveDownloads ( ) )
{
static bool s_showedBGDownloadWarning = false ;
if ( ! s_showedBGDownloadWarning )
{
wxMessageBox ( _ ( " Currently active downloads will continue in the background. " ) , _ ( " Information " ) , wxOK | wxCENTRE , this ) ;
s_showedBGDownloadWarning = true ;
}
}
}
m_running = false ;
}
void TitleManager : : SetFocusAndTab ( TitleManagerPage page )
{
m_notebook - > SetSelection ( ( int ) page ) ;
this - > SetFocus ( ) ;
}
void TitleManager : : SetDownloadStatusText ( const wxString & text )
{
auto * evt = new wxCommandEvent ( wxEVT_SET_TEXT ) ;
evt - > SetEventObject ( m_status_text ) ;
evt - > SetString ( text ) ;
wxQueueEvent ( this , evt ) ;
}
void TitleManager : : HandleTitleListCallback ( CafeTitleListCallbackEvent * evt )
{
if ( evt - > eventType = = CafeTitleListCallbackEvent : : TYPE : : SCAN_FINISHED )
{
auto * evt = new wxCommandEvent ( wxEVT_TITLE_SEARCH_COMPLETE ) ;
wxQueueEvent ( this , evt ) ;
}
}
void TitleManager : : OnTitleFound ( wxCommandEvent & event )
{
auto * obj = dynamic_cast < wxTitleManagerList : : TitleEntryData_t * > ( event . GetClientObject ( ) ) ;
wxASSERT ( obj ) ;
m_title_list - > AddTitle ( obj ) ;
}
void TitleManager : : OnTitleSearchComplete ( wxCommandEvent & event )
{
m_isScanning = false ;
if ( m_connectRequested )
{
InitiateConnect ( ) ;
m_connectRequested = false ;
}
// update status bar text
m_title_list - > SortEntries ( - 1 ) ;
2022-09-03 22:55:58 +02:00
m_status_bar - > SetStatusText ( wxStringFormat2 ( _ ( " Found {0} games, {1} updates, {2} DLCs and {3} save entries " ) ,
2022-08-22 22:21:23 +02:00
m_title_list - > GetCountByType ( wxTitleManagerList : : EntryType : : Base ) + m_title_list - > GetCountByType ( wxTitleManagerList : : EntryType : : System ) ,
m_title_list - > GetCountByType ( wxTitleManagerList : : EntryType : : Update ) ,
m_title_list - > GetCountByType ( wxTitleManagerList : : EntryType : : Dlc ) ,
m_title_list - > GetCountByType ( wxTitleManagerList : : EntryType : : Save )
) ) ;
// re-filter if any filter is set
const auto filter = m_filter - > GetValue ( ) ;
if ( ! filter . IsEmpty ( ) )
m_title_list - > Filter ( m_filter - > GetValue ( ) ) ;
m_title_list - > AutosizeColumns ( ) ;
m_refresh_button - > Enable ( ) ;
}
void TitleManager : : OnSetStatusBarText ( wxSetStatusBarTextEvent & event )
{
m_status_bar - > SetStatusText ( _ ( event . GetText ( ) ) , event . GetNumber ( ) ) ;
}
void TitleManager : : OnFilterChanged ( wxCommandEvent & event )
{
event . Skip ( ) ;
m_title_list - > Filter ( m_filter - > GetValue ( ) ) ;
}
void TitleManager : : OnRefreshButton ( wxCommandEvent & event )
{
m_refresh_button - > Disable ( ) ;
m_isScanning = true ;
// m_title_list->ClearItems(); -> Dont clear. Refresh() triggers incremental updates via notifications
m_status_bar - > SetStatusText ( _ ( " Searching for titles... " ) ) ;
CafeTitleList : : Refresh ( ) ;
}
void TitleManager : : OnInstallTitle ( wxCommandEvent & event )
{
wxFileDialog openFileDialog ( this , _ ( " Select title to install " ) , " " , " " , " meta.xml|meta.xml " , wxFD_OPEN | wxFD_FILE_MUST_EXIST ) ;
2023-06-06 14:17:41 +02:00
if ( openFileDialog . ShowModal ( ) = = wxID_CANCEL | | openFileDialog . GetPath ( ) . IsEmpty ( ) )
2022-08-22 22:21:23 +02:00
return ;
fs : : path filePath ( openFileDialog . GetPath ( ) . wc_str ( ) ) ;
try
{
filePath = filePath . parent_path ( ) ;
filePath = filePath . parent_path ( ) ;
GameUpdateWindow frame ( * this , filePath ) ;
const int updateResult = frame . ShowModal ( ) ;
if ( updateResult = = wxID_OK )
{
CafeTitleList : : AddTitleFromPath ( frame . GetTargetPath ( ) ) ;
}
else
{
if ( frame . GetExceptionMessage ( ) . empty ( ) )
wxMessageBox ( _ ( " Update installation has been canceled! " ) ) ;
else
{
throw std : : runtime_error ( frame . GetExceptionMessage ( ) ) ;
}
}
}
catch ( const AbortException & )
{
// ignored
}
catch ( const std : : exception & ex )
{
wxMessageBox ( ex . what ( ) , _ ( " Update error " ) ) ;
}
}
static void PopulateSavePersistentIds ( wxTitleManagerList : : TitleEntry & entry )
{
if ( ! entry . persistent_ids . empty ( ) )
return ;
cemu_assert ( entry . type = = wxTitleManagerList : : EntryType : : Save ) ;
SaveInfo saveInfo = CafeSaveList : : GetSaveByTitleId ( entry . title_id ) ;
if ( ! saveInfo . IsValid ( ) )
return ;
fs : : path savePath = saveInfo . GetPath ( ) ;
savePath / = " user " ;
std : : error_code ec ;
for ( auto it : fs : : directory_iterator ( savePath , ec ) )
{
if ( ! it . is_directory ( ec ) )
continue ;
2022-12-26 23:46:53 +01:00
if ( fs : : is_empty ( it . path ( ) ) )
continue ;
2022-08-22 22:21:23 +02:00
std : : string dirName = it . path ( ) . filename ( ) . string ( ) ;
2022-12-26 23:46:53 +01:00
if ( ! std : : regex_match ( dirName , std : : regex ( " [0-9a-fA-F]{8} " ) ) )
continue ;
entry . persistent_ids . emplace_back ( ConvertString < uint32 > ( dirName , 16 ) ) ;
2022-08-22 22:21:23 +02:00
}
}
void TitleManager : : OnTitleSelected ( wxListEvent & event )
{
event . Skip ( ) ;
const auto entry = m_title_list - > GetSelectedTitleEntry ( ) ;
if ( entry . has_value ( ) & & entry - > type = = wxTitleManagerList : : EntryType : : Save )
{
m_save_panel - > Show ( ) ;
m_save_account_list - > Clear ( ) ;
PopulateSavePersistentIds ( * entry ) ;
// an account must be selected before any of the control buttons can be used
for ( auto & & v : m_save_panel - > GetChildren ( ) )
{
if ( dynamic_cast < wxButton * > ( v ) & & v - > GetId ( ) ! = m_save_import - > GetId ( ) ) // import is always enabled
v - > Disable ( ) ;
}
const auto & accounts = Account : : GetAccounts ( ) ;
for ( const auto & id : entry - > persistent_ids )
{
const auto it = std : : find_if ( accounts . cbegin ( ) , accounts . cend ( ) , [ id ] ( const auto & acc ) { return acc . GetPersistentId ( ) = = id ; } ) ;
if ( it ! = accounts . cend ( ) )
{
m_save_account_list - > Append ( fmt : : format ( " {:x} ({}) " , id ,
boost : : nowide : : narrow ( it - > GetMiiName ( ) . data ( ) , it - > GetMiiName ( ) . size ( ) ) ) ,
( void * ) ( uintptr_t ) id ) ;
}
else
m_save_account_list - > Append ( fmt : : format ( " {:x} " , id ) , ( void * ) ( uintptr_t ) id ) ;
}
}
else
{
m_save_panel - > Hide ( ) ;
}
}
void TitleManager : : OnSaveOpenDirectory ( wxCommandEvent & event )
{
const auto selection = m_save_account_list - > GetSelection ( ) ;
if ( selection = = wxNOT_FOUND )
return ;
const auto entry = m_title_list - > GetSelectedTitleEntry ( ) ;
if ( ! entry . has_value ( ) )
return ;
const auto persistent_id = ( uint32 ) ( uintptr_t ) m_save_account_list - > GetClientData ( selection ) ;
const fs : : path target = ActiveSettings : : GetMlcPath ( " usr/save/{:08x}/{:08x}/user/{:08x} " , ( uint32 ) ( entry - > title_id > > 32 ) , ( uint32 ) ( entry - > title_id & 0xFFFFFFFF ) , persistent_id ) ;
if ( ! fs : : exists ( target ) | | ! fs : : is_directory ( target ) )
return ;
2022-09-09 23:48:52 +02:00
wxLaunchDefaultBrowser ( wxHelper : : FromUtf8 ( fmt : : format ( " file:{} " , _pathToUtf8 ( target ) ) ) ) ;
2022-08-22 22:21:23 +02:00
}
void TitleManager : : OnSaveDelete ( wxCommandEvent & event )
{
const auto selection_index = m_save_account_list - > GetSelection ( ) ;
if ( selection_index = = wxNOT_FOUND )
return ;
const auto selection = m_save_account_list - > GetStringSelection ( ) ;
if ( selection . IsEmpty ( ) )
return ;
const auto msg = wxStringFormat2 ( _ ( " Are you really sure that you want to delete the save entry for {} " ) , selection ) ;
const auto result = wxMessageBox ( msg , _ ( " Warning " ) , wxYES_NO | wxCENTRE | wxICON_EXCLAMATION , this ) ;
if ( result = = wxNO )
return ;
auto entry = m_title_list - > GetSelectedTitleEntry ( ) ;
if ( ! entry . has_value ( ) )
return ;
const auto persistent_id = ( uint32 ) ( uintptr_t ) m_save_account_list - > GetClientData ( selection_index ) ;
const fs : : path target = ActiveSettings : : GetMlcPath ( " usr/save/{:08x}/{:08x}/user/{:08x} " , ( uint32 ) ( entry - > title_id > > 32 ) , ( uint32 ) ( entry - > title_id & 0xFFFFFFFF ) , persistent_id ) ;
if ( ! fs : : exists ( target ) | | ! fs : : is_directory ( target ) )
return ;
// edit meta saveinfo.xml
bool meta_file_edited = false ;
const fs : : path saveinfo = ActiveSettings : : GetMlcPath ( " usr/save/{:08x}/{:08x}/meta/saveinfo.xml " , ( uint32 ) ( entry - > title_id > > 32 ) , ( uint32 ) ( entry - > title_id & 0xFFFFFFFF ) ) ;
if ( fs : : exists ( saveinfo ) | | fs : : is_regular_file ( saveinfo ) )
{
pugi : : xml_document doc ;
if ( doc . load_file ( saveinfo . c_str ( ) ) )
{
auto info_node = doc . child ( " info " ) ;
if ( info_node )
{
auto persistend_id_string = fmt : : format ( L " {:08x} " , persistent_id ) ;
const auto delete_entry = info_node . find_child ( [ & persistend_id_string ] ( const pugi : : xml_node & node )
{
return boost : : iequals ( node . attribute ( " persistentId " ) . as_string ( ) , persistend_id_string ) ;
} ) ;
if ( delete_entry )
{
info_node . remove_child ( delete_entry ) ;
meta_file_edited = doc . save_file ( saveinfo . c_str ( ) ) ;
}
}
}
}
if ( ! meta_file_edited )
2023-04-12 16:31:34 +02:00
cemuLog_log ( LogType : : Force , " TitleManager::OnSaveDelete: couldn't delete save entry in saveinfo.xml: {} " , _pathToUtf8 ( saveinfo ) ) ;
2022-08-22 22:21:23 +02:00
// remove from title entry
auto & persistent_ids = entry - > persistent_ids ;
persistent_ids . erase ( std : : remove ( persistent_ids . begin ( ) , persistent_ids . end ( ) , persistent_id ) , persistent_ids . end ( ) ) ;
std : : error_code ec ;
fs : : remove_all ( target , ec ) ;
if ( ec )
{
const auto error_msg = wxStringFormat2 ( _ ( " Error when trying to delete the save directory: \n {} " ) , GetSystemErrorMessage ( ec ) ) ;
wxMessageBox ( error_msg , _ ( " Error " ) , wxOK | wxCENTRE , this ) ;
return ;
}
m_save_account_list - > Delete ( selection_index ) ;
}
void TitleManager : : OnSaveTransfer ( wxCommandEvent & event )
{
const auto selection_index = m_save_account_list - > GetSelection ( ) ;
if ( selection_index = = wxNOT_FOUND )
return ;
const auto selection = m_save_account_list - > GetStringSelection ( ) ;
if ( selection . IsEmpty ( ) )
return ;
auto entry = m_title_list - > GetSelectedTitleEntry ( ) ;
if ( ! entry . has_value ( ) )
return ;
const auto persistent_id = ( uint32 ) ( uintptr_t ) m_save_account_list - > GetClientData ( selection_index ) ;
SaveTransfer transfer ( this , entry - > title_id , selection , persistent_id ) ;
if ( transfer . ShowModal ( ) = = wxCANCEL )
return ;
// remove old id entry
auto & persistent_ids = entry - > persistent_ids ;
persistent_ids . erase ( std : : remove ( persistent_ids . begin ( ) , persistent_ids . end ( ) , persistent_id ) , persistent_ids . end ( ) ) ;
// add new id if not added yet
const auto new_id = transfer . GetTargetPersistentId ( ) ;
if ( new_id ! = 0 & & std : : find ( persistent_ids . cbegin ( ) , persistent_ids . cend ( ) , new_id ) = = persistent_ids . cend ( ) )
{
persistent_ids . emplace_back ( new_id ) ;
const auto & account = Account : : GetAccount ( new_id ) ;
if ( account . GetPersistentId ( ) = = new_id )
m_save_account_list - > Append ( fmt : : format ( " {:x} ({}) " , new_id ,
boost : : nowide : : narrow ( account . GetMiiName ( ) . data ( ) , account . GetMiiName ( ) . size ( ) ) ) ,
( void * ) ( uintptr_t ) new_id ) ;
else
m_save_account_list - > Append ( fmt : : format ( " {:x} " , new_id ) , ( void * ) ( uintptr_t ) new_id ) ;
}
m_save_account_list - > Delete ( selection_index ) ;
}
void TitleManager : : OnSaveAccountSelected ( wxCommandEvent & event )
{
event . Skip ( ) ;
for ( auto & & v : m_save_panel - > GetChildren ( ) )
{
if ( ! v - > IsEnabled ( ) )
v - > Enable ( ) ;
}
}
void TitleManager : : OnSaveExport ( wxCommandEvent & event )
{
const auto selection_index = m_save_account_list - > GetSelection ( ) ;
if ( selection_index = = wxNOT_FOUND )
return ;
const auto selection = m_save_account_list - > GetStringSelection ( ) ;
if ( selection . IsEmpty ( ) )
return ;
const auto entry = m_title_list - > GetSelectedTitleEntry ( ) ;
if ( ! entry . has_value ( ) )
return ;
const auto persistent_id = ( uint32 ) ( uintptr_t ) m_save_account_list - > GetClientData ( selection_index ) ;
wxFileDialog path_dialog ( this , _ ( " Select a target file to export the save entry " ) , entry - > path . string ( ) , wxEmptyString , " Exported save entry (*.zip)|*.zip " , wxFD_SAVE | wxFD_OVERWRITE_PROMPT ) ;
2023-06-06 14:17:41 +02:00
if ( path_dialog . ShowModal ( ) ! = wxID_OK | | path_dialog . GetPath ( ) . IsEmpty ( ) )
2022-08-22 22:21:23 +02:00
return ;
const auto path = path_dialog . GetPath ( ) ;
int ze ;
auto * zip = zip_open ( path . ToUTF8 ( ) . data ( ) , ZIP_CREATE | ZIP_TRUNCATE , & ze ) ;
if ( ! zip )
{
zip_error_t ziperror ;
zip_error_init_with_code ( & ziperror , ze ) ;
const auto error_msg = wxStringFormat2 ( _ ( " Error when creating the zip for the save entry: \n {} " ) , zip_error_strerror ( & ziperror ) ) ;
wxMessageBox ( error_msg , _ ( " Error " ) , wxOK | wxCENTRE | wxICON_ERROR , this ) ;
return ;
}
// grab all files to zip
const auto savedir = fs : : path ( entry - > path ) . append ( fmt : : format ( " user/{:08x} " , persistent_id ) ) ;
const auto savedir_str = savedir . generic_u8string ( ) ;
for ( const auto & it : fs : : recursive_directory_iterator ( savedir ) )
{
if ( it . path ( ) = = " . " | | it . path ( ) = = " .. " )
continue ;
const auto entryname = it . path ( ) . generic_u8string ( ) ;
if ( fs : : is_directory ( it . path ( ) ) )
{
if ( zip_dir_add ( zip , ( const char * ) entryname . substr ( savedir_str . size ( ) + 1 ) . c_str ( ) , ZIP_FL_ENC_UTF_8 ) < 0 )
{
const auto error_msg = wxStringFormat2 ( _ ( " Error when trying to add a directory to the zip: \n {} " ) , zip_strerror ( zip ) ) ;
wxMessageBox ( error_msg , _ ( " Error " ) , wxOK | wxCENTRE | wxICON_ERROR , this ) ;
}
}
else
{
auto * source = zip_source_file ( zip , ( const char * ) entryname . c_str ( ) , 0 , 0 ) ;
if ( ! source )
{
const auto error_msg = wxStringFormat2 ( _ ( " Error when trying to add a file to the zip: \n {} " ) , zip_strerror ( zip ) ) ;
wxMessageBox ( error_msg , _ ( " Error " ) , wxOK | wxCENTRE | wxICON_ERROR , this ) ;
}
if ( zip_file_add ( zip , ( const char * ) entryname . substr ( savedir_str . size ( ) + 1 ) . c_str ( ) , source , ZIP_FL_ENC_UTF_8 ) < 0 )
{
const auto error_msg = wxStringFormat2 ( _ ( " Error when trying to add a file to the zip: \n {} " ) , zip_strerror ( zip ) ) ;
wxMessageBox ( error_msg , _ ( " Error " ) , wxOK | wxCENTRE | wxICON_ERROR , this ) ;
zip_source_free ( source ) ;
}
}
}
// add own metainfo like file and store title id for verification later
std : : string metacontent = fmt : : format ( " titleId = {:#016x} " , entry - > title_id ) ;
auto * metabuff = zip_source_buffer ( zip , metacontent . data ( ) , metacontent . size ( ) , 0 ) ;
if ( zip_file_add ( zip , " cemu_meta " , metabuff , ZIP_FL_ENC_UTF_8 ) < 0 )
{
2022-09-03 22:55:58 +02:00
const auto error_msg = wxStringFormat2 ( _ ( " Error when trying to add cemu_meta file to the zip: \n {} " ) , zip_strerror ( zip ) ) ;
2022-08-22 22:21:23 +02:00
wxMessageBox ( error_msg , _ ( " Error " ) , wxOK | wxCENTRE | wxICON_ERROR , this ) ;
zip_source_free ( metabuff ) ;
}
zip_close ( zip ) ;
}
void TitleManager : : OnSaveImport ( wxCommandEvent & event )
{
auto entry = m_title_list - > GetSelectedTitleEntry ( ) ;
if ( ! entry . has_value ( ) )
return ;
SaveImportWindow save_import ( this , entry - > title_id ) ;
if ( save_import . ShowModal ( ) = = wxCANCEL )
return ;
// add new id if not added yet
auto & persistent_ids = entry - > persistent_ids ;
const auto new_id = save_import . GetTargetPersistentId ( ) ;
if ( new_id ! = 0 & & std : : find ( persistent_ids . cbegin ( ) , persistent_ids . cend ( ) , new_id ) = = persistent_ids . cend ( ) )
{
persistent_ids . emplace_back ( new_id ) ;
const auto & account = Account : : GetAccount ( new_id ) ;
if ( account . GetPersistentId ( ) = = new_id )
m_save_account_list - > Append ( fmt : : format ( " {:x} ({}) " , new_id ,
boost : : nowide : : narrow ( account . GetMiiName ( ) . data ( ) , account . GetMiiName ( ) . size ( ) ) ) ,
( void * ) ( uintptr_t ) new_id ) ;
else
m_save_account_list - > Append ( fmt : : format ( " {:x} " , new_id ) , ( void * ) ( uintptr_t ) new_id ) ;
}
}
void TitleManager : : InitiateConnect ( )
{
// init connection to download manager if queued
uint32 persistentId = ( uint32 ) ( uintptr_t ) m_account - > GetClientData ( m_account - > GetSelection ( ) ) ;
auto & account = Account : : GetAccount ( persistentId ) ;
DownloadManager * dlMgr = DownloadManager : : GetInstance ( ) ;
dlMgr - > reset ( ) ;
m_download_list - > SetCurrentDownloadMgr ( dlMgr ) ;
std : : string deviceCertBase64 = NCrypto : : CertECC : : GetDeviceCertificate ( ) . encodeToBase64 ( ) ;
if ( ! NCrypto : : SEEPROM_IsPresent ( ) )
{
SetDownloadStatusText ( " Dumped online files not found " ) ;
return ;
}
SetDownloadStatusText ( " Connecting... " ) ;
// begin async connect
dlMgr - > setUserData ( this ) ;
dlMgr - > registerCallbacks (
TitleManager : : Callback_ConnectStatusUpdate ,
TitleManager : : Callback_AddDownloadableTitle ,
TitleManager : : Callback_RemoveDownloadableTitle ) ;
dlMgr - > connect ( account . GetAccountId ( ) , account . GetAccountPasswordCache ( ) , NCrypto : : SEEPROM_GetRegion ( ) , NCrypto : : GetCountryAsString ( account . GetCountry ( ) ) , NCrypto : : GetDeviceId ( ) , NCrypto : : GetSerial ( ) , deviceCertBase64 ) ;
}
void TitleManager : : OnConnect ( wxCommandEvent & event )
{
if ( ! m_isScanning )
{
InitiateConnect ( ) ;
SetConnected ( true ) ;
}
else
{
SetDownloadStatusText ( _ ( " Getting installed title information... " ) ) ;
SetConnected ( true ) ;
m_connectRequested = true ;
}
}
void TitleManager : : OnDlFilterCheckbox ( wxCommandEvent & event )
{
m_download_list - > Filter2 ( m_show_titles - > GetValue ( ) , m_show_updates - > GetValue ( ) , m_show_installed - > GetValue ( ) ) ;
m_download_list - > SortEntries ( ) ;
}
void TitleManager : : OnSetStatusText ( wxCommandEvent & event )
{
auto * text = wxDynamicCast ( event . GetEventObject ( ) , wxControl ) ;
wxASSERT ( text ) ;
text - > SetLabel ( event . GetString ( ) ) ;
}
void TitleManager : : OnDownloadableTitleUpdate ( wxCommandEvent & event )
{
auto * obj = dynamic_cast < wxDownloadManagerList : : TitleEntryData_t * > ( event . GetClientObject ( ) ) ;
auto entry = obj - > GetData ( ) ;
m_download_list - > AddOrUpdateTitle ( obj ) ;
}
void TitleManager : : OnDisconnect ( wxCommandEvent & event )
{
SetConnected ( false ) ;
}
void TitleManager : : SetConnected ( bool state )
{
m_account - > Enable ( ! state ) ;
m_connect - > Enable ( ! state ) ;
m_show_titles - > Enable ( state ) ;
m_show_updates - > Enable ( state ) ;
m_show_installed - > Enable ( state ) ;
m_download_list - > Enable ( state ) ;
}
void TitleManager : : Callback_ConnectStatusUpdate ( std : : string statusText , DLMGR_STATUS_CODE statusCode )
{
TitleManager * titleManager = static_cast < TitleManager * > ( DownloadManager : : GetInstance ( ) - > getUserData ( ) ) ;
titleManager - > SetDownloadStatusText ( statusText ) ;
if ( statusCode = = DLMGR_STATUS_CODE : : FAILED )
{
auto * evt = new wxCommandEvent ( wxEVT_DL_DISCONNECT_COMPLETE ) ;
wxQueueEvent ( titleManager , evt ) ; // this will set SetConnected() to false
return ;
}
}
void TitleManager : : Callback_AddDownloadableTitle ( const DlMgrTitleReport & titleInfo )
{
TitleManager * titleManager = static_cast < TitleManager * > ( DownloadManager : : GetInstance ( ) - > getUserData ( ) ) ;
wxDownloadManagerList : : EntryType type = wxDownloadManagerList : : EntryType : : Base ;
if ( ( ( titleInfo . titleId > > 32 ) & 0xF ) = = 0xE )
type = wxDownloadManagerList : : EntryType : : Update ;
else if ( ( ( titleInfo . titleId > > 32 ) & 0xF ) = = 0xC )
type = wxDownloadManagerList : : EntryType : : DLC ;
if ( titleInfo . status = = DlMgrTitleReport : : STATUS : : INSTALLABLE | | titleInfo . status = = DlMgrTitleReport : : STATUS : : INSTALLABLE_UNFINISHED | | titleInfo . status = = DlMgrTitleReport : : STATUS : : INSTALLABLE_UPDATE )
{
// installable title
wxDownloadManagerList : : TitleEntry titleEntry ( type , false , titleInfo . titleId , titleInfo . version , titleInfo . isPaused ) ;
titleEntry . name = wxString : : FromUTF8 ( titleInfo . name ) ;
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Available ;
titleEntry . progress = 0 ;
if ( titleInfo . status = = DlMgrTitleReport : : STATUS : : INSTALLABLE_UNFINISHED )
titleEntry . progress = 1 ;
if ( titleInfo . status = = DlMgrTitleReport : : STATUS : : INSTALLABLE_UPDATE )
titleEntry . progress = 2 ;
auto * evt = new wxCommandEvent ( wxEVT_DL_TITLE_UPDATE ) ;
evt - > SetClientObject ( new wxCustomData ( titleEntry ) ) ;
wxQueueEvent ( titleManager , evt ) ;
}
else
{
// package
wxDownloadManagerList : : TitleEntry titleEntry ( type , true , titleInfo . titleId , titleInfo . version , titleInfo . isPaused ) ;
titleEntry . name = wxString : : FromUTF8 ( titleInfo . name ) ;
titleEntry . progress = titleInfo . progress ;
titleEntry . progressMax = titleInfo . progressMax ;
switch ( titleInfo . status )
{
case DlMgrTitleReport : : STATUS : : INITIALIZING :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Initializing ;
break ;
case DlMgrTitleReport : : STATUS : : QUEUED :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Queued ;
break ;
case DlMgrTitleReport : : STATUS : : CHECKING :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Checking ;
break ;
case DlMgrTitleReport : : STATUS : : DOWNLOADING :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Downloading ;
titleEntry . progress = titleInfo . progress ;
break ;
case DlMgrTitleReport : : STATUS : : VERIFYING :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Verifying ;
break ;
case DlMgrTitleReport : : STATUS : : INSTALLING :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Installing ;
break ;
case DlMgrTitleReport : : STATUS : : INSTALLED :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Installed ;
break ;
case DlMgrTitleReport : : STATUS : : HAS_ERROR :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : Error ;
titleEntry . errorMsg = titleInfo . errorMsg ;
break ;
default :
titleEntry . status = wxDownloadManagerList : : TitleDownloadStatus : : None ;
break ;
}
auto * evt = new wxCommandEvent ( wxEVT_DL_TITLE_UPDATE ) ;
evt - > SetClientObject ( new wxCustomData ( titleEntry ) ) ;
wxQueueEvent ( titleManager , evt ) ;
}
}
void TitleManager : : Callback_RemoveDownloadableTitle ( uint64 titleId , uint16 version )
{
TitleManager * titleManager = static_cast < TitleManager * > ( DownloadManager : : GetInstance ( ) - > getUserData ( ) ) ;
assert_dbg ( ) ;
}