Files
archived-pcsx2/pcsx2/gui/Panels/MemoryCardListPanel.cpp
Gregory Hainaut 2ff43f2ed8 core: remove throw specifier on destructor
It is the 'default' on C++11
2017-05-13 10:38:35 +02:00

1245 lines
38 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "AppCoreThread.h"
#include "System.h"
#include "MemoryCardFile.h"
#include "ConfigurationPanels.h"
#include "MemoryCardPanels.h"
#include "Dialogs/ConfigurationDialog.h"
#include "Utilities/IniInterface.h"
#include "../../Sio.h"
#include <wx/filepicker.h>
#include <wx/ffile.h>
#include <wx/dir.h>
static wxDataFormat drag_drop_format(L"PCSX2McdDragDrop");
bool CopyDirectory( const wxString& from, const wxString& to );
bool RemoveDirectory( const wxString& dirname );
using namespace pxSizerFlags;
using namespace Panels;
static bool IsMcdFormatted( wxFFile& fhand )
{
static const char formatted_psx[] = "MC";
static const char formatted_string[] = "Sony PS2 Memory Card Format";
static const int fmtstrlen = sizeof( formatted_string )-1;
char dest[fmtstrlen];
fhand.Read( dest, fmtstrlen );
bool formatted = memcmp( formatted_string, dest, fmtstrlen ) == 0;
if(!formatted) formatted = memcmp( formatted_psx, dest, 2 ) == 0;
return formatted;
}
//sets IsPresent if the file is valid, and derived properties (filename, formatted, size, etc)
bool EnumerateMemoryCard( McdSlotItem& dest, const wxFileName& filename, const wxDirName basePath )
{
dest.IsFormatted = false;
dest.IsPresent = false;
dest.IsPSX = false;
dest.Type = MemoryCardType::MemoryCard_None;
const wxString fullpath( filename.GetFullPath() );
//DevCon.WriteLn( fullpath );
if ( filename.FileExists() ) {
// might be a memory card file
wxFFile mcdFile( fullpath );
if ( !mcdFile.IsOpened() ) { return false; } // wx should log the error for us.
wxFileOffset length = mcdFile.Length();
if( length < (1024*528) && length != 0x20000 )
{
Console.Warning( "... MemoryCard appears to be truncated. Ignoring." );
return false;
}
dest.SizeInMB = (uint)( length / ( 1024 * 528 * 2 ) );
if ( length == 0x20000 ) {
dest.IsPSX = true; // PSX memcard;
dest.SizeInMB = 1; // MegaBIT
}
dest.Type = MemoryCardType::MemoryCard_File;
dest.IsFormatted = IsMcdFormatted( mcdFile );
filename.GetTimes( NULL, &dest.DateModified, &dest.DateCreated );
} else if ( filename.DirExists() ) {
// might be a memory card folder
wxFileName superBlockFileName( fullpath, L"_pcsx2_superblock" );
if ( !superBlockFileName.FileExists() ) { return false; }
wxFFile mcdFile( superBlockFileName.GetFullPath() );
if ( !mcdFile.IsOpened() ) { return false; }
dest.SizeInMB = 0;
dest.Type = MemoryCardType::MemoryCard_Folder;
dest.IsFormatted = IsMcdFormatted( mcdFile );
superBlockFileName.GetTimes( NULL, &dest.DateModified, &dest.DateCreated );
} else {
// is neither
return false;
}
dest.IsPresent = true;
dest.Filename = filename;
if( filename.GetFullPath() == (basePath+filename.GetFullName()).GetFullPath() )
dest.Filename = filename.GetFullName();
return true;
}
//avih: unused
/*
static int EnumerateMemoryCards( McdList& dest, const wxArrayString& files )
{
int pushed = 0;
Console.WriteLn( Color_StrongBlue, "Enumerating memory cards..." );
for( size_t i=0; i<files.GetCount(); ++i )
{
ConsoleIndentScope con_indent;
McdSlotItem mcdItem;
if( EnumerateMemoryCard(mcdItem, files[i]) )
{
dest.push_back( mcdItem );
++pushed;
}
}
if( pushed > 0 )
Console.WriteLn( Color_StrongBlue, "Memory card Enumeration Complete." );
else
Console.WriteLn( Color_StrongBlue, "No valid memory card found." );
return pushed;
}
*/
// --------------------------------------------------------------------------------------
// McdListItem (implementations)
// --------------------------------------------------------------------------------------
bool McdSlotItem::IsMultitapSlot() const
{
return FileMcd_IsMultitapSlot(Slot);
}
uint McdSlotItem::GetMtapPort() const
{
return FileMcd_GetMtapPort(Slot);
}
uint McdSlotItem::GetMtapSlot() const
{
return FileMcd_GetMtapSlot(Slot);
}
// Compares two cards -- If this equality comparison is used on items where
// no filename is specified, then the check will include port and slot.
bool McdSlotItem::operator==( const McdSlotItem& right ) const
{
bool fileEqu;
if( Filename.GetFullName().IsEmpty() )
fileEqu = OpEqu(Slot);
else
fileEqu = OpEqu(Filename);
return fileEqu &&
OpEqu(IsPresent) && OpEqu(IsEnabled) &&
OpEqu(SizeInMB) && OpEqu(IsFormatted) &&
OpEqu(DateCreated) && OpEqu(DateModified);
}
bool McdSlotItem::operator!=( const McdSlotItem& right ) const
{
return operator==( right );
}
// =====================================================================================================
// BaseMcdListPanel (implementations)
// =====================================================================================================
Panels::BaseMcdListPanel::BaseMcdListPanel( wxWindow* parent )
: _parent( parent )
{
m_FolderPicker = new DirPickerPanel( this, FolderId_MemoryCards,
//_("memory card Search Path:"), // static box label
_("Select folder with PS2 memory cards") // dir picker popup label
);
m_listview = NULL;
s_leftside_buttons = NULL;
s_rightside_buttons = NULL;
m_btn_Refresh = new wxButton( this, wxID_ANY, _("Refresh list") );
Bind(wxEVT_BUTTON, &BaseMcdListPanel::OnRefreshSelections, this, m_btn_Refresh->GetId());
}
void Panels::BaseMcdListPanel::RefreshMcds() const
{
wxCommandEvent refit( wxEVT_BUTTON );
refit.SetId( m_btn_Refresh->GetId() );
GetEventHandler()->AddPendingEvent( refit );
}
void Panels::BaseMcdListPanel::CreateLayout()
{
//if( m_listview ) m_listview->SetMinSize( wxSize( 480, 140 ) );
wxFlexGridSizer* s_flex=new wxFlexGridSizer(3,1, 0, 0);
s_flex->AddGrowableCol(0);
s_flex->AddGrowableRow(1);
SetSizer(s_flex);
wxBoxSizer& s_buttons(*new wxBoxSizer( wxHORIZONTAL ));
s_leftside_buttons = new wxBoxSizer( wxHORIZONTAL );
s_rightside_buttons = new wxBoxSizer( wxHORIZONTAL );
s_buttons += s_leftside_buttons | pxAlignLeft;
s_buttons += pxStretchSpacer();
s_buttons += s_rightside_buttons | pxAlignRight;
if( m_FolderPicker ) *this += m_FolderPicker | pxExpand;
else *this += StdPadding;//we need the 'else' because we need these items to land into the proper rows of s_flex.
if( m_listview ) *this += m_listview | pxExpand;
else *this += StdPadding;
*this += s_buttons | pxExpand;
*s_leftside_buttons += m_btn_Refresh;
if (m_listview)
{
IniLoader loader;
ScopedIniGroup group( loader, L"MemoryCardListPanel" );
m_listview->LoadSaveColumns( loader );
}
}
void Panels::BaseMcdListPanel::Apply()
{
// Save column widths to the configuration file. Since these are used *only* by this
// dialog, we use a direct local ini save approach, instead of going through g_conf.
if (m_listview)
{
IniSaver saver;
ScopedIniGroup group( saver, L"MemoryCardListPanel" );
m_listview->LoadSaveColumns(saver);
}
}
void Panels::BaseMcdListPanel::AppStatusEvent_OnSettingsApplied()
{
if( (m_MultitapEnabled[0] != g_Conf->EmuOptions.MultitapPort0_Enabled) ||
(m_MultitapEnabled[1] != g_Conf->EmuOptions.MultitapPort1_Enabled) )
{
m_MultitapEnabled[0] = g_Conf->EmuOptions.MultitapPort0_Enabled;
m_MultitapEnabled[1] = g_Conf->EmuOptions.MultitapPort1_Enabled;
RefreshMcds();
}
}
class McdDropTarget : public wxDropTarget
{
protected:
BaseMcdListView* m_listview;
public:
McdDropTarget( BaseMcdListView* listview=NULL )
{
m_listview = listview;
SetDataObject(new wxCustomDataObject(drag_drop_format));
}
virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
{
int flags = 0;
int destViewIndex = m_listview->HitTest( wxPoint(x,y), flags);
if( wxNOT_FOUND == destViewIndex )
destViewIndex=-1;//non list item target is the filesystem placeholder.
if ( !GetData() )
return wxDragNone;
wxCustomDataObject *dobj = static_cast<wxCustomDataObject *>(GetDataObject());
if (dobj->GetDataSize() != sizeof(int))
return wxDragNone;
int sourceViewIndex;
dobj->GetDataHere(&sourceViewIndex);
wxDragResult result = OnDropMcd(
m_listview->GetMcdProvider().GetCardForViewIndex( sourceViewIndex ),
m_listview->GetMcdProvider().GetCardForViewIndex( destViewIndex ),
def
);
if( wxDragNone == result )
return wxDragNone;
m_listview->GetMcdProvider().RefreshMcds();
return result;
}
virtual wxDragResult OnDropMcd( McdSlotItem& src, McdSlotItem& dest, wxDragResult def )
{
if( src.Slot == dest.Slot ) return wxDragNone;
//if( !pxAssert( (src.Slot >= 0) && (dest.Slot >= 0) ) ) return wxDragNone;
const wxDirName basepath( m_listview->GetMcdProvider().GetMcdPath() );
if( wxDragCopy == def )
{
if( !m_listview->GetMcdProvider().UiDuplicateCard(src, dest) )
return wxDragNone;
}
else if( wxDragMove == def )
{ // source can only be an existing card.
// if dest is a ps2-port (empty or not) -> swap cards between ports.
// is dest is a non-ps2-port -> remove card from port.
// Note: For the sake of usability, automatically enable dest if a ps2-port.
if (src.IsPresent)
{
wxFileName tmpFilename = dest.Filename;
bool tmpPresent = dest.IsPresent;
if (src.Slot<0 && m_listview->GetMcdProvider().isFileAssignedToInternalSlot(src.Filename))
m_listview->GetMcdProvider().RemoveCardFromSlot(src.Filename);
dest.Filename = src.Filename;
dest.IsEnabled = dest.IsPresent? dest.IsEnabled:true;
dest.IsPresent = src.IsPresent;
if (dest.Slot>=0)
{//2 internal slots: swap
src.Filename = tmpFilename;
src.IsPresent = tmpPresent;
}
else
{//dest is at the filesystem (= remove card from slot)
src.Filename = L"";
src.IsPresent = false;
src.IsEnabled = false;
}
}
}
return def;
}
};
enum McdMenuId
{
McdMenuId_Create = 0x888,
//McdMenuId_Mount,
McdMenuId_Rename,
McdMenuId_RefreshList,
McdMenuId_AssignUnassign,
McdMenuId_Duplicate,
McdMenuId_Convert,
};
Panels::MemoryCardListPanel_Simple* g_uglyPanel=NULL;
void g_uglyFunc(){if (g_uglyPanel) g_uglyPanel->OnChangedListSelection();}
Panels::MemoryCardListPanel_Simple::~MemoryCardListPanel_Simple() {g_uglyPanel=NULL;}
Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent )
: _parent( parent )
{
m_MultitapEnabled[0] = false;
m_MultitapEnabled[1] = false;
m_listview = new MemoryCardListView_Simple(this);
m_listview->SetMinSize(wxSize(620, m_listview->GetCharHeight() * 13)); // 740 is nice for default font sizes
m_listview->SetDropTarget( new McdDropTarget(m_listview) );
//m_button_Mount = new wxButton(this, wxID_ANY, _("Enable port"));
m_button_AssignUnassign = new wxButton(this, wxID_ANY, _("Eject"));
m_button_Duplicate = new wxButton(this, wxID_ANY, _("Duplicate ..."));
m_button_Rename = new wxButton(this, wxID_ANY, _("Rename ..."));
m_button_Create = new wxButton(this, wxID_ANY, _("Create ..."));
m_button_Convert = new wxButton(this, wxID_ANY, _("Convert ..."));
// ------------------------------------
// Sizer / Layout Section
// ------------------------------------
CreateLayout();
*s_leftside_buttons += 20;
//*s_leftside_buttons += m_button_Mount;
//*s_leftside_buttons += 20;
*s_leftside_buttons += Label(_("Card: ")) | pxMiddle;
*s_leftside_buttons += m_button_AssignUnassign;
*s_leftside_buttons += 20;
*s_leftside_buttons += m_button_Duplicate;
*s_leftside_buttons += 2;
*s_leftside_buttons += m_button_Rename;
*s_leftside_buttons += 2;
*s_leftside_buttons += m_button_Create;
*s_leftside_buttons += 2;
*s_leftside_buttons += m_button_Convert;
SetSizerAndFit(GetSizer());
parent->SetWindowStyle(parent->GetWindowStyle() | wxRESIZE_BORDER);
Bind(wxEVT_LIST_BEGIN_DRAG, &MemoryCardListPanel_Simple::OnListDrag, this, m_listview->GetId());
Bind(wxEVT_LIST_ITEM_SELECTED, &MemoryCardListPanel_Simple::OnListSelectionChanged, this, m_listview->GetId());
Bind(wxEVT_LIST_ITEM_ACTIVATED, &MemoryCardListPanel_Simple::OnItemActivated, this, m_listview->GetId()); //enter or double click
//Deselected is not working for some reason (e.g. when clicking an empty row at the table?) - avih
// wxMSW bug for virtual listviews. Works fine on Linux: http://trac.wxwidgets.org/ticket/1919 - turtleli
Bind(wxEVT_LIST_ITEM_DESELECTED, &MemoryCardListPanel_Simple::OnListSelectionChanged, this, m_listview->GetId());
Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &MemoryCardListPanel_Simple::OnOpenItemContextMenu, this, m_listview->GetId());
//Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnMountCard, this, m_button_Mount->GetId());
Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnCreateOrDeleteCard, this, m_button_Create->GetId());
Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnConvertCard, this, m_button_Convert->GetId());
Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnRenameFile, this, m_button_Rename->GetId());
Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnDuplicateFile, this, m_button_Duplicate->GetId());
Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnAssignUnassignFile, this, m_button_AssignUnassign->GetId());
// Popup Menu Connections!
//Bind(McdMenuId_Mount, &MemoryCardListPanel_Simple::OnMountCard, this, McdMenuId_Mount);
Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnCreateOrDeleteCard, this, McdMenuId_Create);
Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnConvertCard, this, McdMenuId_Convert);
Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnRenameFile, this, McdMenuId_Rename);
Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnDuplicateFile, this, McdMenuId_Duplicate);
Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnAssignUnassignFile, this, McdMenuId_AssignUnassign);
Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnRefreshSelections, this, McdMenuId_RefreshList);
//because the wxEVT_LIST_ITEM_DESELECTED doesn't work (buttons stay enabled when clicking an empty area of the list),
// m_listview can send us an event that indicates a change at the list. Ugly, but works.
g_uglyPanel=this;
m_listview->setExternHandler(g_uglyFunc);
}
void Panels::MemoryCardListPanel_Simple::UpdateUI()
{
if( !m_listview ) return;
int sel = m_listview->GetFirstSelected();
m_button_Create->Enable();
if( wxNOT_FOUND == sel )
{
m_button_Create->SetLabel( _("Create ...") );
pxSetToolTip( m_button_Create, _("Create a new memory card." ));
// m_button_Mount->Disable();
m_button_Rename->Disable();
m_button_Duplicate->Disable();
m_button_AssignUnassign->Disable();
m_button_Convert->Disable();
return;
}
const McdSlotItem& card( GetCardForViewIndex(sel) );
m_button_Rename->Enable( card.IsPresent );
wxString renameTip = _("Rename this memory card ...");
pxSetToolTip( m_button_Rename, renameTip );
m_button_AssignUnassign->Enable( card.IsPresent );
m_button_AssignUnassign->SetLabel( card.Slot>=0 ? _("Eject") : _("Insert ...") );
wxString assignTip = (card.Slot>=0)?_("Eject the card from this port"):_("Insert this card to a port ...");
pxSetToolTip( m_button_AssignUnassign, assignTip );
m_button_Duplicate->Enable( card.IsPresent );
wxString dupTip = _("Create a duplicate of this memory card ...");
pxSetToolTip( m_button_Duplicate, dupTip );
m_button_Convert->Enable( card.IsPresent && card.IsFormatted && !card.IsPSX );
//m_button_Create->Enable( card.Slot>=0 || card.IsPresent);
m_button_Create->SetLabel( card.IsPresent ? _("Delete") : _("Create ...") );
if (card.IsPresent)
pxSetToolTip( m_button_Create, _("Permanently delete this memory card from disk (all contents are lost)"));
else if( card.Slot >= 0 )
pxSetToolTip( m_button_Create, _("Create a new memory card and assign it to this Port." ));
else
pxSetToolTip( m_button_Create, _("Create a new memory card." ));
/*
m_button_Mount->Enable( card.IsPresent && card.Slot>=0);
m_button_Mount->SetLabel( card.IsEnabled ? _("Disable Port") : _("Enable Port") );
pxSetToolTip( m_button_Mount,
card.IsEnabled
? _("Disable the selected PS2-Port (this memory card will be invisible to games/BIOS).")
: _("Enable the selected PS2-Port (games/BIOS will see this memory card).")
);
*/
}
void Panels::MemoryCardListPanel_Simple::Apply()
{
_parent::Apply();
int used=0;
Console.WriteLn( L"Apply Memory cards:" );
for( uint slot=0; slot<8; ++slot )
{
g_Conf->Mcd[slot].Type = m_Cards[slot].Type;
g_Conf->Mcd[slot].Enabled = m_Cards[slot].IsEnabled && m_Cards[slot].IsPresent;
if (m_Cards[slot].IsPresent)
g_Conf->Mcd[slot].Filename = m_Cards[slot].Filename;
else
g_Conf->Mcd[slot].Filename = L"";
if( g_Conf->Mcd[slot].Enabled )
{
used++;
Console.WriteLn( L"slot[%d]='%s'", slot, WX_STR(g_Conf->Mcd[slot].Filename.GetFullName()) );
}
}
if( !used )
Console.WriteLn( L"No active slots" );
SetForceMcdEjectTimeoutNow();
}
void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied()
{
for( uint slot=0; slot<8; ++slot )
{
m_Cards[slot].IsEnabled = g_Conf->Mcd[slot].Enabled;
m_Cards[slot].Filename = g_Conf->Mcd[slot].Filename;
//automatically create the enabled but non-existing file such that it can be managed (else will get created anyway on boot)
wxString targetFile = (GetMcdPath() + m_Cards[slot].Filename.GetFullName()).GetFullPath();
if ( m_Cards[slot].IsEnabled && !( wxFileExists( targetFile ) || wxDirExists( targetFile ) ) )
{
wxString errMsg;
if (isValidNewFilename(m_Cards[slot].Filename.GetFullName(), GetMcdPath(), errMsg, 5))
{
if ( !Dialogs::CreateMemoryCardDialog::CreateIt(targetFile, 8) )
Console.Error( L"Automatic createion of MCD '%s' failed. Hope for the best...", WX_STR(targetFile) );
else
Console.WriteLn( L"memcard created: '%s'.", WX_STR(targetFile) );
}
else
{
Console.Error( L"memcard was enabled but had an invalid file name. Aborting automatic creation. Hope for the best... (%s)", WX_STR(errMsg) );
}
}
if ( !m_Cards[slot].IsEnabled || !( wxFileExists( targetFile ) || wxDirExists( targetFile ) ) )
{
m_Cards[slot].IsEnabled = false;
m_Cards[slot].IsPresent = false;
m_Cards[slot].Filename = L"";
}
}
DoRefresh();
_parent::AppStatusEvent_OnSettingsApplied();
}
//BUG: the next function is never reached because, for some reason, IsoDropTarget::OnDropFiles is called instead.
// Interestingly, IsoDropTarget::OnDropFiles actually "detects" a memory card file as a valid Audio-CD ISO... - avih
bool Panels::MemoryCardListPanel_Simple::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames)
{
if( filenames.GetCount() == 1 && wxFileName(filenames[0]).IsDir() )
{
m_FolderPicker->SetPath( filenames[0] );
return true;
}
return false;
}
bool Panels::MemoryCardListPanel_Simple::ValidateEnumerationStatus()
{
if( m_listview ) m_listview->SetMcdProvider( NULL );
return false;
}
void Panels::MemoryCardListPanel_Simple::DoRefresh()
{
for( uint slot=0; slot<8; ++slot )
{
//if( FileMcd_IsMultitapSlot(slot) && !m_MultitapEnabled[FileMcd_GetMtapPort(slot)] )
// continue;
//wxFileName fullpath( m_FolderPicker->GetPath() + g_Conf->Mcd[slot].Filename.GetFullName() );
wxFileName fullpath = m_FolderPicker->GetPath() + m_Cards[slot].Filename.GetFullName();
EnumerateMemoryCard( m_Cards[slot], fullpath, m_FolderPicker->GetPath());
m_Cards[slot].Slot = slot;
}
ReadFilesAtMcdFolder();
if( m_listview ) m_listview->SetMcdProvider( this );
UpdateUI();
}
// =====================================================================================================
// MemoryCardListPanel_Simple (implementations)
// =====================================================================================================
void Panels::MemoryCardListPanel_Simple::UiCreateNewCard( McdSlotItem& card )
{//card can also be the filesystem placeholder. On that case, the changes
// made to it will be reverted on refresh, and we'll just have a new card at the folder.
if( card.IsPresent ){
Console.WriteLn("Error: Aborted: create mcd invoked but but a file is already associated.");
return;
}
Dialogs::CreateMemoryCardDialog dialog( this, m_FolderPicker->GetPath(), L"my memory card" );
wxWindowID result = dialog.ShowModal();
if (result != wxID_CANCEL)
{
card.IsEnabled = true;
card.Filename = dialog.result_createdMcdFilename;
card.IsPresent = true;
if ( card.Slot >= 0)
Console.WriteLn(L"setting new card to slot %u: '%s'", card.Slot, WX_STR(card.Filename.GetFullName()));
else
Console.WriteLn(L"Created a new unassigned card file: '%s'", WX_STR(card.Filename.GetFullName()) );
}
else
card.IsEnabled=false;
Apply();
RefreshSelections();
}
void Panels::MemoryCardListPanel_Simple::UiConvertCard( McdSlotItem& card ) {
if ( !card.IsPresent ) {
Console.WriteLn( "Error: Aborted: Convert mcd invoked but but a file is not associated." );
return;
}
AppConfig::McdOptions config;
config.Filename = card.Filename.GetFullName();
config.Enabled = card.IsEnabled;
config.Type = card.Type;
Dialogs::ConvertMemoryCardDialog dialog( this, m_FolderPicker->GetPath(), config );
wxWindowID result = dialog.ShowModal();
if ( result != wxID_CANCEL ) {
Apply();
RefreshSelections();
}
}
void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card )
{
if( !card.IsPresent ){
Console.WriteLn("Error: Aborted: delete mcd invoked but but a file is not associated.");
return;
}
bool result = true;
if( card.IsFormatted )
{
wxString content;
content.Printf(
pxE( L"You are about to delete the formatted memory card '%s'. All data on this card will be lost! Are you absolutely and quite positively sure?"
), WX_STR(card.Filename.GetFullName())
);
result = Msgbox::YesNo( content, _("Delete memory file?") );
}
if( result )
{
wxFileName fullpath( m_FolderPicker->GetPath() + card.Filename.GetFullName());
card.IsEnabled=false;
Apply();
if ( fullpath.FileExists() ) {
wxRemoveFile( fullpath.GetFullPath() );
} else {
RemoveDirectory( fullpath.GetFullPath() );
}
RefreshSelections();
}
}
bool Panels::MemoryCardListPanel_Simple::UiDuplicateCard(McdSlotItem& src, McdSlotItem& dest)
{
wxDirName basepath = GetMcdPath();
if( !src.IsPresent )
{
Msgbox::Alert(_("Failed: Can only duplicate an existing card."), _("Duplicate memory card"));
return false;
}
if ( dest.IsPresent && dest.Slot!=-1 )
{
wxString content;
content.Printf(
pxE( L"Failed: Duplicate is only allowed to an empty PS2-Port or to the file system." )
);
Msgbox::Alert( content, _("Duplicate memory card") );
return false;
}
while (1)
{
wxString newFilename=L"";
newFilename = wxGetTextFromUser(_("Select a name for the duplicate\n( '.ps2' will be added automatically)"), _("Duplicate memory card"));
if( newFilename==L"" )
{
//Msgbox::Alert( _("Duplicate canceled"), _("Duplicate memory card") );
return false;
}
newFilename += L".ps2";
//check that the name is valid for a new file
wxString errMsg;
if( !isValidNewFilename( newFilename, basepath, errMsg, 5 ) )
{
wxString message;
message.Printf(_("Failed: %s"), WX_STR(errMsg));
Msgbox::Alert( message, _("Duplicate memory card") );
continue;
}
dest.Filename = newFilename;
break;
}
wxFileName srcfile( basepath + src.Filename);
wxFileName destfile( basepath + dest.Filename);
ScopedBusyCursor doh( Cursor_ReallyBusy );
if( !( ( srcfile.FileExists() && wxCopyFile( srcfile.GetFullPath(), destfile.GetFullPath(), true ) )
|| ( !srcfile.FileExists() && CopyDirectory( srcfile.GetFullPath(), destfile.GetFullPath() ) ) ) )
{
wxString heading;
heading.Printf( pxE( L"Failed: Destination memory card '%s' is in use." ),
WX_STR(dest.Filename.GetFullName()), dest.Slot
);
wxString content;
Msgbox::Alert( heading + L"\n\n" + content, _("Copy failed!") );
return false;
}
// Destination memcard isEnabled state is the same now as the source's
wxString success;
success.Printf(_("Memory card '%s' duplicated to '%s'."),
WX_STR(src.Filename.GetFullName()),
WX_STR(dest.Filename.GetFullName())
);
Msgbox::Alert(success, _("Success"));
dest.IsPresent=true;
dest.IsEnabled = true;
Apply();
DoRefresh();
return true;
}
void Panels::MemoryCardListPanel_Simple::UiRenameCard( McdSlotItem& card )
{
if( !card.IsPresent ){
Console.WriteLn("Error: Aborted: Rename mcd invoked but no file is associated.");
return;
}
const wxDirName basepath( m_listview->GetMcdProvider().GetMcdPath() );
wxString newFilename;
while (1){
wxString title;
title.Printf(_("Select a new name for the memory card '%s'\n( '.ps2' will be added automatically)"),
WX_STR(card.Filename.GetFullName())
);
newFilename = wxGetTextFromUser(title, _("Rename memory card"));
if( newFilename==L"" )
return;
newFilename += L".ps2";
//check that the name is valid for a new file
wxString errMsg;
if( !isValidNewFilename( newFilename, basepath, errMsg, 5 ) )
{
wxString message;
message.Printf(_("Error (%s)"), WX_STR(errMsg));
Msgbox::Alert( message, _("Rename memory card") );
continue;
}
break;
}
bool origEnabled=card.IsEnabled;
card.IsEnabled=false;
Apply();
if( !wxRenameFile( (basepath + card.Filename).GetFullPath(), (basepath + wxFileName(newFilename)).GetFullPath(), false ) )
{
card.IsEnabled=origEnabled;
Apply();
Msgbox::Alert( _("Error: Rename could not be completed.\n"), _("Rename memory card") );
return;
}
card.Filename = newFilename;
card.IsEnabled=origEnabled;
Apply();
RefreshSelections();
}
void Panels::MemoryCardListPanel_Simple::OnCreateOrDeleteCard(wxCommandEvent& evt)
{
int selectedViewIndex = m_listview->GetFirstSelected();
if( wxNOT_FOUND == selectedViewIndex )
selectedViewIndex = -1;//get filesystem placeholder, just create a new card at the filesystem.
McdSlotItem& card( GetCardForViewIndex(selectedViewIndex) );
if( card.IsPresent )
UiDeleteCard( card );
else
UiCreateNewCard( card );
}
void Panels::MemoryCardListPanel_Simple::OnConvertCard(wxCommandEvent& evt) {
int selectedViewIndex = m_listview->GetFirstSelected();
if ( wxNOT_FOUND == selectedViewIndex ) {
return;
}
McdSlotItem& card( GetCardForViewIndex( selectedViewIndex ) );
if ( card.IsPresent ) {
UiConvertCard( card );
}
}
//enable/disapbe port
/*
void Panels::MemoryCardListPanel_Simple::OnMountCard(wxCommandEvent& evt)
{
evt.Skip();
const int selectedViewIndex = m_listview->GetFirstSelected();
if( wxNOT_FOUND == selectedViewIndex ) return;
McdSlotItem& card( GetCardForViewIndex(selectedViewIndex) );
card.IsEnabled = !card.IsEnabled;
m_listview->RefreshItem(selectedViewIndex);
UpdateUI();
}
*/
/*
//text dialog: can be used for rename: wxGetTextFromUser - avih
void Panels::MemoryCardListPanel_Simple::OnRelocateCard(wxCommandEvent& evt)
{
evt.Skip();
const int slot = m_listview->GetFirstSelected();
if( wxNOT_FOUND == slot ) return;
// Issue a popup to the user that allows them to pick a new .PS2 file to serve as
// the new host memorycard file for the slot. The dialog has a number of warnings
// present to reiterate that this is an advanced operation that PCSX2 may not
// support very well (ie, might be buggy).
m_listview->RefreshItem(slot);
UpdateUI();
}
*/
// enter/double-click
void Panels::MemoryCardListPanel_Simple::OnItemActivated(wxListEvent& evt)
{
const int viewIndex = m_listview->GetFirstSelected();
if( wxNOT_FOUND == viewIndex ) return;
McdSlotItem& card( GetCardForViewIndex(viewIndex) );
if ( card.IsPresent )
UiRenameCard( card );
else if (card.Slot>=0)
UiCreateNewCard( card );// IsPresent==false can only happen for an internal slot (vs filename on the HD), so a card can be created.
}
void Panels::MemoryCardListPanel_Simple::OnDuplicateFile(wxCommandEvent& evt)
{
const int viewIndex = m_listview->GetFirstSelected();
if( wxNOT_FOUND == viewIndex ) return;
McdSlotItem& card( GetCardForViewIndex(viewIndex) );
pxAssert( card.IsPresent );
McdSlotItem dummy;
UiDuplicateCard(card, dummy);
}
wxString GetPortName(int slotIndex)
{
if (slotIndex==0 || slotIndex==1)
return pxsFmt(wxString(L" ") + _("Port-%u / Multitap-%u--Port-1"), FileMcd_GetMtapPort(slotIndex)+1, FileMcd_GetMtapPort(slotIndex)+1);
return pxsFmt(wxString(L" ")+_(" Multitap-%u--Port-%u"), FileMcd_GetMtapPort(slotIndex)+1, FileMcd_GetMtapSlot(slotIndex)+1 );
}
void Panels::MemoryCardListPanel_Simple::UiAssignUnassignFile(McdSlotItem &card)
{
pxAssert( card.IsPresent );
if( card.Slot >=0 )
{//eject
card.IsEnabled = false;
card.IsPresent = false;
card.Filename = L"";
DoRefresh();
}
else
{//insert into a (UI) selected slot
wxArrayString selections;
int i;
for (i=0; i< GetNumVisibleInternalSlots(); i++)
{
McdSlotItem& selCard = GetCardForViewIndex(i);
wxString sel = GetPortName( selCard.Slot ) + L" ( ";
if (selCard.IsPresent)
sel += selCard.Filename.GetFullName();
else
sel += _("Empty");
sel += L" )";
selections.Add(sel);
}
wxString title;
title.Printf(_("Select a target port for '%s'"), WX_STR(card.Filename.GetFullName()));
int res=wxGetSingleChoiceIndex(title, _("Insert card"), selections, this);
if( res<0 )
return;
McdSlotItem& target = GetCardForViewIndex(res);
bool en = target.IsPresent? target.IsEnabled : true;
RemoveCardFromSlot( card.Filename );
target.Filename = card.Filename;
target.IsPresent = true;
target.IsEnabled = en;
DoRefresh();
}
}
void Panels::MemoryCardListPanel_Simple::OnAssignUnassignFile(wxCommandEvent& evt)
{
const int viewIndex = m_listview->GetFirstSelected();
if( wxNOT_FOUND == viewIndex ) return;
McdSlotItem& card( GetCardForViewIndex(viewIndex) );
UiAssignUnassignFile(card);
}
void Panels::MemoryCardListPanel_Simple::OnRenameFile(wxCommandEvent& evt)
{
const int viewIndex = m_listview->GetFirstSelected();
if( wxNOT_FOUND == viewIndex ) return;
McdSlotItem& card( GetCardForViewIndex(viewIndex) );
UiRenameCard( card );
}
void Panels::MemoryCardListPanel_Simple::OnListDrag(wxListEvent& evt)
{
int selectionViewIndex = m_listview->GetFirstSelected();
if( selectionViewIndex < 0 ) return;
wxCustomDataObject my_data(drag_drop_format);
my_data.SetData(sizeof(int), &selectionViewIndex);
wxDropSource dragSource( m_listview );
dragSource.SetData( my_data );
/*wxDragResult result = */dragSource.DoDragDrop( wxDrag_DefaultMove );
}
void Panels::MemoryCardListPanel_Simple::OnListSelectionChanged(wxListEvent& evt)
{
UpdateUI();
}
void Panels::MemoryCardListPanel_Simple::OnOpenItemContextMenu(wxListEvent& evt)
{
int idx = evt.GetIndex();
wxMenu* junk = new wxMenu();
if( idx != wxNOT_FOUND )
{
const McdSlotItem& card( GetCardForViewIndex(idx) );
if (card.IsPresent){
junk->Append( McdMenuId_AssignUnassign, card.Slot>=0?_("Eject card"):_("Insert card ...") );
junk->Append( McdMenuId_Duplicate, _("Duplicate card ...") );
junk->Append( McdMenuId_Rename, _("Rename card ...") );
junk->Append( McdMenuId_Create, _("Delete card") );
if (card.IsFormatted && !card.IsPSX) {
junk->Append( McdMenuId_Convert, _("Convert card") );
}
}
else
junk->Append( McdMenuId_Create, _("Create a new card ...") );
}
else
junk->Append( McdMenuId_Create, _("Create a new card ...") );
junk->AppendSeparator();
junk->Append( McdMenuId_RefreshList, _("Refresh List") );
PopupMenu( junk );
m_listview->RefreshItem( idx );
UpdateUI();
}
void Panels::MemoryCardListPanel_Simple::ReadFilesAtMcdFolder(){
//Dir enumeration/iteration code courtesy of cotton. - avih.
while( !m_allFilesystemCards.empty() )
m_allFilesystemCards.pop_back();
m_filesystemPlaceholderCard.Slot=-1;
m_filesystemPlaceholderCard.IsEnabled=false;
m_filesystemPlaceholderCard.IsPresent=false;
m_filesystemPlaceholderCard.Filename=L"";
wxArrayString memcardList;
wxString filename = m_FolderPicker->GetPath().ToString();
wxDir memcardDir( filename );
if ( memcardDir.IsOpened() ) {
// add memory card files
wxDir::GetAllFiles( filename, &memcardList, L"*.ps2", wxDIR_FILES );
wxDir::GetAllFiles( filename, &memcardList, L"*.mcd", wxDIR_FILES );
wxDir::GetAllFiles( filename, &memcardList, L"*.mcr", wxDIR_FILES );
// add memory card folders
wxString dirname;
if ( memcardDir.GetFirst( &dirname, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN ) ) {
do {
wxFileName superBlockFileName( wxFileName( filename, dirname ).GetFullPath(), L"_pcsx2_superblock" );
if ( superBlockFileName.FileExists() ) {
memcardList.Add( superBlockFileName.GetPath() );
}
} while ( memcardDir.GetNext( &dirname ) );
}
}
for(uint i = 0; i < memcardList.size(); i++) {
McdSlotItem currentCardFile;
bool isOk=EnumerateMemoryCard( currentCardFile, memcardList[i], m_FolderPicker->GetPath() );
if( isOk && !isFileAssignedAndVisibleOnList( currentCardFile.Filename ) )
{
currentCardFile.Slot = -1;
currentCardFile.IsEnabled = false;
m_allFilesystemCards.push_back(currentCardFile);
//DevCon.WriteLn(L"Enumerated file: '%s'", WX_STR(currentCardFile.Filename.GetFullName()) );
}
/*else
DevCon.WriteLn(L"MCD folder card file skipped: '%s'", WX_STR(memcardList[i]) );*/
}
}
bool Panels::MemoryCardListPanel_Simple::IsSlotVisible(int slotIndex) const
{
if ( slotIndex<0 || slotIndex>=8 ) return false;
if ( !m_MultitapEnabled[0] && 2<=slotIndex && slotIndex<=4) return false;
if ( !m_MultitapEnabled[1] && 5<=slotIndex && slotIndex<=7) return false;
return true;
}
//whether or not this filename appears on the ports at the list (takes into account MT enabled/disabled)
bool Panels::MemoryCardListPanel_Simple::isFileAssignedAndVisibleOnList(const wxFileName cardFile) const
{
int i;
for( i=0; i<8; i++)
if ( IsSlotVisible(i) && cardFile.GetFullName()==m_Cards[i].Filename.GetFullName() )
return true;
return false;
}
//whether or not this filename is assigned to a ports (regardless if MT enabled/disabled)
bool Panels::MemoryCardListPanel_Simple::isFileAssignedToInternalSlot(const wxFileName cardFile) const
{
int i;
for( i=0; i<8; i++)
if ( cardFile.GetFullName()==m_Cards[i].Filename.GetFullName() )
return true;
return false;
}
void Panels::MemoryCardListPanel_Simple::RemoveCardFromSlot(const wxFileName cardFile)
{
int i;
for( i=0; i<8; i++)
if ( cardFile.GetFullName()==m_Cards[i].Filename.GetFullName() )
{
m_Cards[i].Filename = L"";
m_Cards[i].IsPresent = false;
m_Cards[i].IsEnabled = false;
}
}
int Panels::MemoryCardListPanel_Simple::GetNumFilesVisibleAsFilesystem() const
{
return m_allFilesystemCards.size();
}
bool Panels::MemoryCardListPanel_Simple::IsNonEmptyFilesystemCards() const
{
return GetNumFilesVisibleAsFilesystem()>0;
}
McdSlotItem& Panels::MemoryCardListPanel_Simple::GetNthVisibleFilesystemCard(int n)
{
return m_allFilesystemCards.at(n);
}
int Panels::MemoryCardListPanel_Simple::GetNumVisibleInternalSlots() const
{
uint baselen = 2;
if( m_MultitapEnabled[0] ) baselen += 3;
if( m_MultitapEnabled[1] ) baselen += 3;
return baselen;
}
// Interface Implementation for IMcdList
int Panels::MemoryCardListPanel_Simple::GetLength() const
{
uint baselen = GetNumVisibleInternalSlots();
baselen++;//filesystem placeholder
baselen+= GetNumFilesVisibleAsFilesystem();
return baselen;
}
//Translates a list-view index (idx) to an internal memory card slot.
//This method effectively defines the arrangement of the card slots at the list view.
//The internal card slots array is fixed as sollows:
// slot 0: mcd1 (= MT1 slot 1)
// slot 1: mcd2 (= MT2 slot 1)
// slots 2,3,4: MT1 slots 2,3,4
// slots 5,6,7: MT2 slots 2,3,4
//
//however, the list-view doesn't show MT slots when this MT is disabled,
// so the view-index should "shift" to point at the real card slot.
//While we're at it, we can alternatively enforce any other arrangment of the view by
// using any other set of 'view-index-to-card-slot' translating that we'd like.
int Panels::MemoryCardListPanel_Simple::GetSlotIndexForViewIndex( int listViewIndex )
{
pxAssert( 0<=listViewIndex && listViewIndex<GetNumVisibleInternalSlots() );
int targetSlot=-1;
/*
//this list-view arrangement is mostly kept aligned with the internal slots indexes, and only takes care
// of the case where MT1 is disabled (hence the MT2 slots 2,3,4 "move backwards" 3 places on the view-index)
// However, this arrangement it's not very intuitive to use...
if (!m_MultitapEnabled[0] && listViewIndex>=2)
{
//we got an MT2 slot.
assert(listViewIndex < 5);
targetSlot = listViewIndex+3;
}
else
{
targetSlot=listViewIndex;//identical view-index and card slot.
}
*/
//This arrangement of list-view is as follows:
//mcd1(=MT1 port 1)
//[MT1 port 2,3,4 if MT1 is enabled]
//mcd2(=MT2 slot 1)
//[MT2 port 2,3,4 if MT2 is enabled]
if (m_MultitapEnabled[0]){
//MT1 enabled:
if (1<=listViewIndex && listViewIndex<=3){//MT1 ports 2/3/4 move one place backwards
targetSlot=listViewIndex+1;
}else if (listViewIndex==4){//mcd2 (=MT2 port 1) moves 3 places forward
targetSlot=1;
} else {//mcd1 keeps it's pos as first, MT2 ports keep their pos at the end of the list.
targetSlot=listViewIndex;
}
} else {
//MT1 disabled: mcd1 and mcd2 stay put, MT2 ports 2,3,4 come next (move backwards 3 places)
if (2<=listViewIndex && listViewIndex<=4)
targetSlot=listViewIndex+3;
else
targetSlot=listViewIndex;
}
pxAssert( 0<=targetSlot && targetSlot<=7 );
return targetSlot;
}
McdSlotItem& Panels::MemoryCardListPanel_Simple::GetCardForViewIndex( int idx )
{
pxAssert( -1<=idx && idx< GetNumVisibleInternalSlots()+1+GetNumFilesVisibleAsFilesystem() );
if( 0<=idx && idx<GetNumVisibleInternalSlots() )
return m_Cards[GetSlotIndexForViewIndex( idx )];
if( idx==-1 || idx == GetNumVisibleInternalSlots() )
return this->m_filesystemPlaceholderCard;
return this->m_allFilesystemCards.at( idx - GetNumVisibleInternalSlots() - 1 );
}