diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index 0d68d3ca02..f487d8cf38 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -721,7 +721,7 @@ void InitializeSIO(u8 value) wxTimeSpan delta = wxDateTime::UNow().Subtract( m_ForceEjection_minTriesTimestamp[port] ); if ( delta.GetMilliseconds() >= FORCED_MCD_EJECTION_MAX_MS_AFTER_MIN_TRIES ) { - Console.Warning( L"[%s] Auto-eject: Timeout reached after mcd was accessed %d times [port:%d, slot:%d]", GetTimeMsStr().c_str(), numTimesAccessed, port, slot); + DevCon.Warning( L"[%s] Auto-eject: Timeout reached after mcd was accessed %d times [port:%d, slot:%d]", GetTimeMsStr().c_str(), numTimesAccessed, port, slot); m_ForceEjectionTimeout[port] = 0; //Done. on next sio access the card will be seen as inserted. } } diff --git a/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp b/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp index f496953de7..9a8af7fd4a 100644 --- a/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp +++ b/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp @@ -37,7 +37,7 @@ wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, ui } */ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, const wxDirName& mcdpath, const wxString& suggested_mcdfileName) - : wxDialogWithHelpers( parent, _("Create a new memory card file") ) + : wxDialogWithHelpers( parent, _("Create a new memory card") ) , m_mcdpath( mcdpath ) , m_mcdfile( suggested_mcdfileName )//suggested_and_result_mcdfileName.IsEmpty() ? g_Conf->Mcd[slot].Filename.GetFullName() { @@ -68,7 +68,7 @@ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, const // s_padding += m_filepicker | StdExpand(); // else { - s_padding += Heading( _( "New memory card file:" ) ) | StdExpand(); + s_padding += Heading( _( "New memory card:" ) ) | StdExpand(); s_padding += Heading( wxString(_("At folder: ")) + (m_mcdpath + m_mcdfile).GetPath() ).Unwrapped() | StdExpand(); wxBoxSizer& s_filename( *new wxBoxSizer(wxHORIZONTAL) ); @@ -115,7 +115,7 @@ bool Dialogs::CreateMemoryCardDialog::CreateIt( const wxString& mcdFile, uint si u8 m_effeffs[528*16]; memset8<0xff>( m_effeffs ); - Console.WriteLn( L"(FileMcd) Creating new %uMB memory card file: '%s'", sizeInMB, mcdFile.c_str() ); + Console.WriteLn( L"(FileMcd) Creating new %uMB memory card: '%s'", sizeInMB, mcdFile.c_str() ); wxFFile fp( mcdFile, L"wb" ); if( !fp.IsOpened() ) return false; @@ -147,7 +147,7 @@ void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt ) { wxString message; message.Printf(_("Error (%s)"), errMsg.c_str()); - Msgbox::Alert( message, _("Create memory card file") ); + Msgbox::Alert( message, _("Create memory card") ); m_text_filenameInput->SetFocus(); m_text_filenameInput->SelectAll(); return; @@ -160,8 +160,8 @@ void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt ) ) ) { Msgbox::Alert( - _("Error: The memory card file could not be created."), - _("Create memory card file") + _("Error: The memory card could not be created."), + _("Create memory card") ); return; } diff --git a/pcsx2/gui/Dialogs/McdConfigDialog.cpp b/pcsx2/gui/Dialogs/McdConfigDialog.cpp index d305f510c0..532e4fc2e8 100644 --- a/pcsx2/gui/Dialogs/McdConfigDialog.cpp +++ b/pcsx2/gui/Dialogs/McdConfigDialog.cpp @@ -109,8 +109,8 @@ Dialogs::McdConfigDialog::McdConfigDialog( wxWindow* parent ) wxBoxSizer* s_top = new wxBoxSizer(wxVERTICAL); - wxString title=_("Drag card files in the list to swap or copy between ports."); - title+=_("\nNote: Rename/Copy/Create/Delete will NOT be reverted with 'Cancel'."); + wxString title=_("Drag cards to or from PS2-ports"); + title+=_("\nNote: Rename/Duplicate/Create/Delete will NOT be reverted with 'Cancel'."); *s_top += Heading(title) | StdExpand(); *s_top += StdPadding; diff --git a/pcsx2/gui/MemoryCardFile.cpp b/pcsx2/gui/MemoryCardFile.cpp index eb0056f3bb..fdbfd0bd1e 100644 --- a/pcsx2/gui/MemoryCardFile.cpp +++ b/pcsx2/gui/MemoryCardFile.cpp @@ -180,7 +180,7 @@ void FileMemoryCard::Open() if( !Create( str, 8 ) ) { Msgbox::Alert( - wxsFormat(_( "Could not create a memory card file: \n\n%s\n\n" ), str.c_str()) + + wxsFormat(_( "Could not create a memory card: \n\n%s\n\n" ), str.c_str()) + GetDisabledMessage( slot ) ); } @@ -198,7 +198,7 @@ void FileMemoryCard::Open() // Translation note: detailed description should mention that the memory card will be disabled // for the duration of this session. Msgbox::Alert( - wxsFormat(_( "Access denied to memory card file: \n\n%s\n\n" ), str.c_str()) + + wxsFormat(_( "Access denied to memory card: \n\n%s\n\n" ), str.c_str()) + GetDisabledMessage( slot ) ); } diff --git a/pcsx2/gui/Panels/MemoryCardListPanel.cpp b/pcsx2/gui/Panels/MemoryCardListPanel.cpp index f24cffc905..8a62228a26 100644 --- a/pcsx2/gui/Panels/MemoryCardListPanel.cpp +++ b/pcsx2/gui/Panels/MemoryCardListPanel.cpp @@ -264,7 +264,7 @@ public: if( !pxAssertDev( len == sizeof(u32), "Data length mismatch on memory card drag&drop operation." ) ) return false; m_viewIndex = *(u32*)buf; - return ( (uint)m_viewIndex < 8 ); // sanity check (unsigned, so that -1 also is invalid) :) + return true;//( (uint)m_viewIndex < 8 ); // sanity check (unsigned, so that -1 also is invalid) :) } // Must provide overloads to avoid hiding them (and warnings about it) @@ -284,6 +284,7 @@ public: } }; + class McdDropTarget : public wxDropTarget { protected: @@ -368,116 +369,44 @@ public: 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; + //if( !pxAssert( (src.Slot >= 0) && (dest.Slot >= 0) ) ) return wxDragNone; const wxDirName basepath( m_listview->GetMcdProvider().GetMcdPath() ); bool result = true; if( wxDragCopy == def ) { - // user is force invoking copy mode, which means we need to check the destination - // and prompt if it looks valuable (formatted). - if( !src.IsPresent ) - { - Msgbox::Alert(_("Failed. Can only copy an existing card file."), _("Copy memory card file")); + if( !m_listview->GetMcdProvider().UiDuplicateCard(src, dest) ) return wxDragNone; - } - - if ( !dest.IsPresent ) - { - while (1){ - wxString newFilename=L""; - newFilename = wxGetTextFromUser(_("Select a name for the new memory card file copy\n( '.ps2' will be added automatically)"), _("Copy memory card file")); - if( newFilename==L"" ) - { - Msgbox::Alert( _("Copy canceled"), _("Copy memory card file") ); - return wxDragNone; - } - 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)"), errMsg.c_str()); - Msgbox::Alert( message, _("Copy memory card file") ); - continue; - } - - dest.Filename = newFilename; - break; - } - - } - - wxFileName srcfile( basepath + src.Filename);//g_Conf->Mcd[src.Slot].Filename ); - wxFileName destfile( basepath + dest.Filename);//g_Conf->Mcd[dest.Slot].Filename ); - - if( dest.IsPresent && dest.IsFormatted ) - { - wxString content; - content.Printf( - pxE( "!Notice:Mcd:Overwrite", - L"This will copy the entire contents of memory card file '%s' [=slot %u] to the memory card file '%s' [=slot %u]. " - L"All previous data on memory card file '%s' will be lost. Are you sure?" ), - src.Filename.GetFullName().c_str(), src.Slot, - dest.Filename.GetFullName().c_str(), dest.Slot, - dest.Filename.GetFullName().c_str(), dest.Slot - ); - - result = Msgbox::YesNo( content, _("Overwrite memory card file?") ); - - if (!result) - return wxDragNone; - } - - ScopedBusyCursor doh( Cursor_ReallyBusy ); - if( !wxCopyFile( srcfile.GetFullPath(), destfile.GetFullPath(), true ) ) - { - wxString heading; - heading.Printf( pxE( "!Notice:Mcd:Copy Failed", - L"Error! Copy failed. Destination memory card file '%s' [=slot %u] is in use." ), - dest.Filename.GetFullName().c_str(), dest.Slot - ); - - wxString content; - - Msgbox::Alert( heading + L"\n\n" + content, _("Copy failed!") ); - return wxDragNone; - } - - // Destination memcard isEnabled state is the same now as the source's - wxString success; - success.Printf(_("Memory card file '%s' copied to '%s'.\n\nBoth card files are now identical."), - src.Filename.GetFullName().c_str(), - dest.Filename.GetFullName().c_str() - ); - Msgbox::Alert(success, _("Success")); - dest.IsPresent=true; - dest.IsEnabled = true;//src.IsEnabled; - m_listview->GetMcdProvider().PublicApply(); } else if( wxDragMove == def ) - {// move/swap files in ports - // Just swaps the assigned file names at the slots. + { // 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: each slot has 2 important properties: IsPresent (with Filename) and IsEnabled. - // For the sake of usability, when draggind src to dest, if src IsPresent, automatically enable dest. - // However, src slot keeps its old IsEnabled regardless of what happened. - if (src.IsPresent || dest.IsPresent) + // Note: For the sake of usability, automatically enable dest if a ps2-port. + if (src.IsPresent) { - //swap file names (along with 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( src.IsPresent ) - dest.IsEnabled = true; - src.Filename = tmpFilename; - src.IsPresent = tmpPresent; + 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; + } } } @@ -485,24 +414,23 @@ public: } }; + enum McdMenuId { McdMenuId_Create = 0x888, McdMenuId_Mount, McdMenuId_Rename, - McdMenuId_RefreshList + McdMenuId_RefreshList, + McdMenuId_AssignUnassign, + McdMenuId_Duplicate, }; -// ===================================================================================================== -// MemoryCardListPanel_Simple (implementations) -// ===================================================================================================== -/* some code from cotton to enumerate files at a folder: -[21:07] ScopedPtr memcardList(new wxArrayString()); -[21:07] wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), memcardList, L"*.ps2*", wxDIR_FILES); -[21:07] for(uint i = 0; i < memcardList->size(); i++) { -[21:07] DevCon.WriteLn(L"hey - " + memcardList[0][i]); -[21:07] } -*/ + +Panels::MemoryCardListPanel_Simple* g_uglyPanel=NULL; +void g_uglyFunc(){if (g_uglyPanel) g_uglyPanel->OnChangedListSelection();} + +Panels::MemoryCardListPanel_Simple::~MemoryCardListPanel_Simple() throw(){g_uglyPanel=NULL;} + Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent ) : _parent( parent ) { @@ -511,18 +439,16 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent m_listview = new MemoryCardListView_Simple(this); - // Fixme: Small problem: - // m_listview->GetMinWidth() can return -1 (On Win7 x64 + Aero for example) - if ( m_listview->GetMinWidth() <= 0 ) - m_listview->SetMinSize(wxSize(740, m_listview->GetCharHeight() * 14)); // 740 is nice for default font sizes - else - m_listview->SetMinSize(wxSize(m_listview->GetMinWidth(), m_listview->GetCharHeight() * 14)); + m_listview->SetMinSize(wxSize(700, m_listview->GetCharHeight() * 13)); // 740 is nice for default font sizes m_listview->SetDropTarget( new McdDropTarget(m_listview) ); - m_button_Create = new wxButton(this, wxID_ANY, _("Create card file")); m_button_Mount = new wxButton(this, wxID_ANY, _("Enable port")); - m_button_Rename = new wxButton(this, wxID_ANY, _("Rename card file")); + + 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 ...")); // ------------------------------------ // Sizer / Layout Section @@ -534,8 +460,13 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent *s_leftside_buttons += m_button_Mount; *s_leftside_buttons += 20; - *s_leftside_buttons += m_button_Rename; + *s_leftside_buttons += Text(_("Card: ")) | pxMiddle; + *s_leftside_buttons += m_button_AssignUnassign; *s_leftside_buttons += 2; + *s_leftside_buttons += m_button_Duplicate; + *s_leftside_buttons += 2; + *s_leftside_buttons += m_button_Rename; + *s_leftside_buttons += 8; *s_leftside_buttons += m_button_Create; SetSizerAndFit(GetSizer()); @@ -553,12 +484,22 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent Connect( m_button_Mount->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnMountCard)); Connect( m_button_Create->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnCreateOrDeleteCard)); Connect( m_button_Rename->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRenameFile)); + Connect( m_button_Duplicate->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnDuplicateFile)); + Connect( m_button_AssignUnassign->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnAssignUnassignFile)); // Popup Menu Connections! Connect( McdMenuId_Create, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnCreateOrDeleteCard) ); Connect( McdMenuId_Mount, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnMountCard) ); Connect( McdMenuId_Rename, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRenameFile) ); + Connect( McdMenuId_AssignUnassign, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnAssignUnassignFile) ); + Connect( McdMenuId_Duplicate, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnDuplicateFile) ); + Connect( McdMenuId_RefreshList, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRefreshSelections) ); + + //because the wxEVT_COMMAND_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() @@ -572,27 +513,36 @@ void Panels::MemoryCardListPanel_Simple::UpdateUI() m_button_Create->Disable(); m_button_Mount->Disable(); m_button_Rename->Disable(); + m_button_Duplicate->Disable(); + m_button_AssignUnassign->Disable(); return; } const McdSlotItem& card( GetCardForViewIndex(sel) ); m_button_Rename->Enable( card.IsPresent ); - wxString renameTip = _("Rename this memory card file."); - renameTip += wxString(L"\n") + _("Note: Port needs to be disabled first, and the change then needs to be applied." ); + wxString renameTip = _("Rename this memory card ..."); pxSetToolTip( m_button_Rename, renameTip ); - m_button_Create->Enable(); - m_button_Create->SetLabel( card.IsPresent ? _("Delete card file") : _("Create card file") ); - wxString deleteTip = _("Permanently delete this memory card file from disk (all contents are lost)"); - deleteTip += wxString(L"\n") + _("Note: Port needs to be disabled first, and the change then needs to be applied." ); + 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_Create->Enable(card.Slot>=0 || card.IsPresent); + m_button_Create->SetLabel( card.IsPresent ? _("Delete") : _("Create ...") ); + wxString deleteTip = _("Permanently delete this memory card from disk (all contents are lost)"); if (card.IsPresent) pxSetToolTip( m_button_Create, deleteTip); else - pxSetToolTip( m_button_Create, _("Create a new memory card file and assign it to the selected PS2-Port." )); + pxSetToolTip( m_button_Create, _("Create a new memory card and assign it to the selected PS2-Port." )); - m_button_Mount->Enable( card.IsPresent ); + 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 @@ -643,7 +593,7 @@ void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied() } else { - Console.Error( L"memcard was enabled but had an invalid file name. Aborting automatic creation. Hope for the best..." ); + Console.Error( L"memcard was enabled but had an invalid file name. Aborting automatic creation. Hope for the best... (%s)", errMsg.c_str() ); } } @@ -676,8 +626,8 @@ void Panels::MemoryCardListPanel_Simple::DoRefresh() { for( uint slot=0; slot<8; ++slot ) { - if( FileMcd_IsMultitapSlot(slot) && !m_MultitapEnabled[FileMcd_GetMtapPort(slot)] ) - continue; + //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(); @@ -686,10 +636,18 @@ void Panels::MemoryCardListPanel_Simple::DoRefresh() m_Cards[slot].Slot = slot; } + ReadFilesAtMcdFolder(); + + if( m_listview ) m_listview->SetMcdProvider( this ); UpdateUI(); } + +// ===================================================================================================== +// MemoryCardListPanel_Simple (implementations) +// ===================================================================================================== + void Panels::MemoryCardListPanel_Simple::UiCreateNewCard( McdSlotItem& card ) { if( card.IsPresent ){ @@ -725,7 +683,6 @@ void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card ) return; } - ScopedCoreThreadClose closed_core; bool result = true; if( card.IsFormatted ) @@ -733,26 +690,113 @@ void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card ) wxString content; content.Printf( pxE( "!Notice:Mcd:Delete", - L"You are about to delete the formatted memory card file '%s' [=slot %u]. " + L"You are about to delete the formatted memory card '%s'. " L"All data on this card will be lost! Are you absolutely and quite positively sure?" ), card.Filename.GetFullName().c_str() - , card.Slot ); - result = Msgbox::YesNo( content, _("Delete memory card file?") ); + result = Msgbox::YesNo( content, _("Delete memory file?") ); } if( result ) { - wxFileName fullpath( m_FolderPicker->GetPath() + card.Filename.GetFullName());//g_Conf->Mcd[GetSlotIndexForViewIndex( selectedViewIndex )].Filename.GetFullName() ); + ScopedCoreThreadClose closed_core; + + wxFileName fullpath( m_FolderPicker->GetPath() + card.Filename.GetFullName()); card.IsEnabled=false; Apply(); wxRemoveFile( fullpath.GetFullPath() ); + + RefreshSelections(); + closed_core.AllowResume(); } - RefreshSelections(); - closed_core.AllowResume(); +} + +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( "!Notice:Mcd:CantDuplicate", + 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"), errMsg.c_str()); + 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 ); + ScopedCoreThreadClose closed_core; + + if( !wxCopyFile( srcfile.GetFullPath(), destfile.GetFullPath(), true ) ) + { + wxString heading; + heading.Printf( pxE( "!Notice:Mcd:Copy Failed", + L"Failed: Destination memory card '%s' is in use." ), + dest.Filename.GetFullName().c_str(), dest.Slot + ); + + wxString content; + + Msgbox::Alert( heading + L"\n\n" + content, _("Copy failed!") ); + + closed_core.AllowResume(); + return false; + } + + // Destination memcard isEnabled state is the same now as the source's + wxString success; + success.Printf(_("Memory card '%s' duplicated to '%s'.\n\nBoth card files are now identical."), + src.Filename.GetFullName().c_str(), + dest.Filename.GetFullName().c_str() + ); + Msgbox::Alert(success, _("Success")); + dest.IsPresent=true; + dest.IsEnabled = true; + + Apply(); + DoRefresh(); + closed_core.AllowResume(); + return true; } void Panels::MemoryCardListPanel_Simple::UiRenameCard( McdSlotItem& card ) @@ -766,10 +810,10 @@ void Panels::MemoryCardListPanel_Simple::UiRenameCard( McdSlotItem& card ) wxString newFilename; while (1){ wxString title; - title.Printf(_("Select a new name for the memory card file '%s'\n( '.ps2' will be added automatically)"), + title.Printf(_("Select a new name for the memory card '%s'\n( '.ps2' will be added automatically)"), card.Filename.GetFullName().c_str() ); - newFilename = wxGetTextFromUser(title, _("Rename memory card file")); + newFilename = wxGetTextFromUser(title, _("Rename memory card")); if( newFilename==L"" ) return; @@ -781,7 +825,7 @@ void Panels::MemoryCardListPanel_Simple::UiRenameCard( McdSlotItem& card ) { wxString message; message.Printf(_("Error (%s)"), errMsg.c_str()); - Msgbox::Alert( message, _("Rename memory card file") ); + Msgbox::Alert( message, _("Rename memory card") ); continue; } @@ -797,7 +841,7 @@ void Panels::MemoryCardListPanel_Simple::UiRenameCard( McdSlotItem& card ) { card.IsEnabled=origEnabled; Apply(); - Msgbox::Alert( _("Error: Rename could not be completed.\n"), _("Rename memory card file") ); + Msgbox::Alert( _("Error: Rename could not be completed.\n"), _("Rename memory card") ); closed_core.AllowResume(); return; @@ -866,10 +910,79 @@ void Panels::MemoryCardListPanel_Simple::OnItemActivated(wxListEvent& evt) if ( card.IsPresent ) UiRenameCard( card ); - else + 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'"), card.Filename.GetFullName().c_str()); + 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(); @@ -908,13 +1021,21 @@ void Panels::MemoryCardListPanel_Simple::OnOpenItemContextMenu(wxListEvent& evt) const McdSlotItem& card( GetCardForViewIndex(idx) ); if (card.IsPresent){ - junk->Append( McdMenuId_Mount, card.IsEnabled ? _("Disable Port") : _("Enable Port") ); - junk->Append( McdMenuId_Rename, _("Rename card file...") ); + if (card.Slot>=0) + { + junk->Append( McdMenuId_Mount, card.IsEnabled ? _("Disable Port") : _("Enable Port") ); + junk->AppendSeparator(); + } + 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, card.IsPresent ? _("Delete card file") : _("Create a new card file...") ); - - junk->AppendSeparator(); + if ( card.IsPresent || card.Slot>=0 ) + { + junk->Append( McdMenuId_Create, card.IsPresent ? _("Delete card") : _("Create a new card ...") ); + junk->AppendSeparator(); + } } junk->Append( McdMenuId_RefreshList, _("Refresh List") ); @@ -924,8 +1045,94 @@ void Panels::MemoryCardListPanel_Simple::OnOpenItemContextMenu(wxListEvent& evt) UpdateUI(); } -// Interface Implementation for IMcdList -int Panels::MemoryCardListPanel_Simple::GetLength() const +void Panels::MemoryCardListPanel_Simple::ReadFilesAtMcdFolder(){ + //Dir enumeration/iteration code courtesy of cotton. - avih. + while( m_allFilesystemCards.size() ) + m_allFilesystemCards.pop_back(); + + m_filesystemPlaceholderCard.Slot=-1; + m_filesystemPlaceholderCard.IsEnabled=false; + m_filesystemPlaceholderCard.IsPresent=false; + m_filesystemPlaceholderCard.Filename=L""; + + + wxArrayString memcardList; + wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), &memcardList, L"*.ps2", wxDIR_FILES); + + 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'", currentCardFile.Filename.GetFullName().c_str() ); + } + else + DevCon.WriteLn(L"MCD folder card file skipped: '%s'", memcardList[i].c_str() ); + } +} + + +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; @@ -933,8 +1140,17 @@ int Panels::MemoryCardListPanel_Simple::GetLength() const 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 a memory card slot. + +//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) @@ -948,6 +1164,7 @@ int Panels::MemoryCardListPanel_Simple::GetLength() const // 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=0); + pxAssert( 0<=targetSlot && targetSlot<=7 ); return targetSlot; } McdSlotItem& Panels::MemoryCardListPanel_Simple::GetCardForViewIndex( int idx ) { - int slot = GetSlotIndexForViewIndex( idx ); - return m_Cards[slot]; + pxAssert( 0<=idx && idx< GetNumVisibleInternalSlots()+1+GetNumFilesVisibleAsFilesystem() ); + + if( 0<=idx && idxm_filesystemPlaceholderCard; + + return this->m_allFilesystemCards.at( idx - GetNumVisibleInternalSlots() - 1 ); } diff --git a/pcsx2/gui/Panels/MemoryCardListView.cpp b/pcsx2/gui/Panels/MemoryCardListView.cpp index 831d23bcd5..162d8c22fd 100644 --- a/pcsx2/gui/Panels/MemoryCardListView.cpp +++ b/pcsx2/gui/Panels/MemoryCardListView.cpp @@ -102,13 +102,13 @@ const ListViewColumnInfo& MemoryCardListView_Simple::GetDefaultColumnInfo( uint { static const ListViewColumnInfo columns[] = { - { _("PS2 Port") , 160 , wxLIST_FORMAT_LEFT }, + { _("PS2 Port") , 154 , wxLIST_FORMAT_LEFT }, { _("Port status") , 80 , wxLIST_FORMAT_LEFT }, - { _("File name") , 128 , wxLIST_FORMAT_LEFT }, - { _("File size") , 80 , wxLIST_FORMAT_LEFT }, + { _("File name") , 126 , wxLIST_FORMAT_LEFT }, + { _("File size") , 60 , wxLIST_FORMAT_LEFT }, { _("Formatted") , 80 , wxLIST_FORMAT_LEFT }, - { _("Last Modified"), 96 , wxLIST_FORMAT_LEFT }, - { _("Created on") , 96 , wxLIST_FORMAT_LEFT }, + { _("Last Modified"), 85 , wxLIST_FORMAT_LEFT }, + { _("Created on") , 85 , wxLIST_FORMAT_LEFT }, }; pxAssumeDev( idx < ArraySize(columns), "ListView column index is out of bounds." ); @@ -128,20 +128,27 @@ wxString MemoryCardListView_Simple::OnGetItemText(long item, long column) const { if( !m_CardProvider ) return _parent::OnGetItemText(item, column); const McdSlotItem& it( m_CardProvider->GetCardForViewIndex(item) ); - wxString prefix=L" "; + wxString prefix=L" "; switch( column ) { case McdColS_PortSlot: - if (!it.IsMultitapSlot()) - return pxsFmt(wxString(L" ") + _("Port-%u / Multitap-%u--Port-1"), it.GetMtapPort()+1, it.GetMtapPort()+1); - return pxsFmt(wxString(L" ") + _("Multitap-%u--Port-%u"), it.GetMtapPort()+1, it.GetMtapSlot()+1); + if (it.Slot>=0) + { + if( !it.IsMultitapSlot() ) + return pxsFmt(wxString(L" ") + _("Port-%u / Multitap-%u--Port-1"), it.GetMtapPort()+1, it.GetMtapPort()+1); + return pxsFmt(wxString(L" ")+_(" Multitap-%u--Port-%u"), it.GetMtapPort()+1, it.GetMtapSlot()+1); + } + + return L""; case McdColS_Status: { wxString res = prefix + (it.IsEnabled ? _("Enabled") : _("Disabled")); - if( !it.IsPresent ) + if( !it.IsPresent && it.Slot>=0 ) res = prefix + _("Empty"); + else if ( it.Slot == -1 ) + res= L""; return prefix + res; } @@ -152,7 +159,17 @@ wxString MemoryCardListView_Simple::OnGetItemText(long item, long column) const case McdColS_Filename: { - if (!it.IsPresent) return L""; + if (!it.IsPresent && it.Slot!=-1) return L""; + else if (!it.IsPresent && it.Slot==-1) + { + //UGLY #2: because this method must be const to be used at the list, + // it cannot access GetMcdProvider() which isn't (and shouldn't be) const. + //so.. a plain old cast does the trick. Hope it's not too bad. - avih. + if (((MemoryCardListView_Simple*)this)->GetMcdProvider().IsNonEmptyFilesystemCards()) + return _("[-- Unused cards --]"); + else + return _("[-- No unused cards --]"); + } wxDirName filepath( it.Filename.GetPath() ); @@ -194,7 +211,7 @@ wxListItemAttr* MemoryCardListView_Simple::OnGetItemAttr(long item) const m_ItemAttr = wxListItemAttr(); // Wipe it clean! - if( it.IsPresent && !it.IsEnabled) + if( it.Slot==-1 || it.IsPresent && !it.IsEnabled) m_ItemAttr.SetTextColour( *wxLIGHT_GREY ); /* if( m_TargetedItem == item ) diff --git a/pcsx2/gui/Panels/MemoryCardPanels.h b/pcsx2/gui/Panels/MemoryCardPanels.h index 6c061e005d..3e7b612b23 100644 --- a/pcsx2/gui/Panels/MemoryCardPanels.h +++ b/pcsx2/gui/Panels/MemoryCardPanels.h @@ -79,6 +79,11 @@ public: virtual int GetSlotIndexForViewIndex( int listViewIndex )=0; virtual wxDirName GetMcdPath() const=0; virtual void PublicApply() =0; + virtual bool isFileAssignedToInternalSlot(const wxFileName cardFile) const =0; + virtual void RemoveCardFromSlot(const wxFileName cardFile) =0; + virtual bool IsNonEmptyFilesystemCards() const =0; + virtual bool UiDuplicateCard( McdSlotItem& src, McdSlotItem& dest ) =0; + }; // -------------------------------------------------------------------------------------- @@ -95,10 +100,17 @@ protected: int m_TargetedItem; public: + void (*m_externHandler)(void); + void setExternHandler(void (*f)(void)){m_externHandler=f;}; + void OnChanged(wxEvent& evt){if (m_externHandler) m_externHandler(); evt.Skip();} + virtual ~BaseMcdListView() throw() { } BaseMcdListView( wxWindow* parent ) : _parent( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_VIRTUAL ) { + m_externHandler=NULL; + Connect( this->GetId(), wxEVT_LEFT_UP, wxEventHandler(BaseMcdListView::OnChanged)); + m_CardProvider = NULL; } @@ -198,11 +210,15 @@ namespace Panels // Doubles as Create and Delete buttons wxButton* m_button_Create; - // Doubles as Mount and Unmount buttons + // Doubles as Mount and Unmount buttons ("Enable"/"Disable" port) wxButton* m_button_Mount; + wxButton* m_button_AssignUnassign; //insert/eject card + wxButton* m_button_Duplicate; + + public: - virtual ~MemoryCardListPanel_Simple() throw() {} + virtual ~MemoryCardListPanel_Simple() throw(); MemoryCardListPanel_Simple( wxWindow* parent ); void UpdateUI(); @@ -213,12 +229,16 @@ namespace Panels virtual McdSlotItem& GetCardForViewIndex( int idx ); virtual int GetSlotIndexForViewIndex( int viewIndex ); virtual void PublicApply(){ Apply(); }; + void OnChangedListSelection(){ UpdateUI(); }; + virtual bool UiDuplicateCard( McdSlotItem& src, McdSlotItem& dest ); protected: void OnCreateOrDeleteCard(wxCommandEvent& evt); void OnMountCard(wxCommandEvent& evt); // void OnRelocateCard(wxCommandEvent& evt); void OnRenameFile(wxCommandEvent& evt); + void OnDuplicateFile(wxCommandEvent& evt); + void OnAssignUnassignFile(wxCommandEvent& evt); void OnListDrag(wxListEvent& evt); void OnListSelectionChanged(wxListEvent& evt); @@ -227,6 +247,19 @@ namespace Panels virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames); + std::vector m_allFilesystemCards; + McdSlotItem m_filesystemPlaceholderCard; + + virtual int GetNumFilesVisibleAsFilesystem() const; + virtual int GetNumVisibleInternalSlots() const; + virtual McdSlotItem& GetNthVisibleFilesystemCard(int n); + virtual bool IsSlotVisible(int slotIndex) const; + virtual void ReadFilesAtMcdFolder(); + virtual bool isFileAssignedAndVisibleOnList(const wxFileName cardFile) const; + virtual bool isFileAssignedToInternalSlot(const wxFileName cardFile) const; + virtual void RemoveCardFromSlot(const wxFileName cardFile); + virtual bool IsNonEmptyFilesystemCards() const; + virtual void Apply(); virtual void AppStatusEvent_OnSettingsApplied(); virtual void DoRefresh(); @@ -235,6 +268,8 @@ namespace Panels virtual void UiRenameCard( McdSlotItem& card ); virtual void UiCreateNewCard( McdSlotItem& card ); virtual void UiDeleteCard( McdSlotItem& card ); + virtual void UiAssignUnassignFile( McdSlotItem& card ); + }; // --------------------------------------------------------------------------------------