diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 9a958ce20a..17d1b1c40e 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -272,6 +272,7 @@ set(pcsx2GuiSources gui/Panels/ThemeSelectorPanel.cpp gui/Dialogs/BaseConfigurationDialog.cpp gui/Dialogs/ConfirmationDialogs.cpp + gui/Dialogs/ConvertMemoryCardDialog.cpp gui/Dialogs/CreateMemoryCardDialog.cpp gui/Dialogs/FirstTimeWizard.cpp gui/Dialogs/GameDatabaseDialog.cpp diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.h b/pcsx2/gui/Dialogs/ConfigurationDialog.h index 8dbe07a880..faaf3cc304 100644 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.h +++ b/pcsx2/gui/Dialogs/ConfigurationDialog.h @@ -237,4 +237,26 @@ namespace Dialogs void CreateControls(); void OnOk_Click( wxCommandEvent& evt ); }; + + // -------------------------------------------------------------------------------------- + // ConvertMemoryCardDialog + // -------------------------------------------------------------------------------------- + class ConvertMemoryCardDialog : public wxDialogWithHelpers + { + protected: + wxDirName m_mcdPath; + wxString m_mcdSourceFilename; + wxTextCtrl* m_text_filenameInput; + pxRadioPanel* m_radio_CardType; + + public: + virtual ~ConvertMemoryCardDialog() throw() {} + ConvertMemoryCardDialog( wxWindow* parent, const wxDirName& mcdPath, const AppConfig::McdOptions& mcdSourceConfig ); + + protected: + void CreateControls( const MemoryCardType sourceType ); + void OnOk_Click( wxCommandEvent& evt ); + bool ConvertToFile( const wxFileName& sourcePath, const wxFileName& targetPath ); + bool ConvertToFolder( const wxFileName& sourcePath, const wxFileName& targetPath ); + }; } diff --git a/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp b/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp new file mode 100644 index 0000000000..9c2766794a --- /dev/null +++ b/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp @@ -0,0 +1,197 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2015 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 . + */ + +#include "PrecompiledHeader.h" +#include "ConfigurationDialog.h" +#include "System.h" + +#include "MemoryCardFile.h" +#include "MemoryCardFolder.h" +#include + +Dialogs::ConvertMemoryCardDialog::ConvertMemoryCardDialog( wxWindow* parent, const wxDirName& mcdPath, const AppConfig::McdOptions& mcdSourceConfig ) + : wxDialogWithHelpers( parent, _( "Convert a memory card to a different format" ) ) + , m_mcdPath( mcdPath ) + , m_mcdSourceFilename( mcdSourceConfig.Filename.GetFullName() ) +{ + SetMinWidth( 472 ); + + CreateControls( mcdSourceConfig.Type ); + + if ( m_radio_CardType ) m_radio_CardType->Realize(); + + wxBoxSizer& s_buttons( *new wxBoxSizer( wxHORIZONTAL ) ); + s_buttons += new wxButton( this, wxID_OK, _( "Convert" ) ) | pxProportion( 2 ); + s_buttons += pxStretchSpacer( 3 ); + s_buttons += new wxButton( this, wxID_CANCEL ) | pxProportion( 2 ); + + wxBoxSizer& s_padding( *new wxBoxSizer( wxVERTICAL ) ); + + s_padding += Heading( wxString( _( "Convert: " ) ) + ( mcdPath + m_mcdSourceFilename ).GetFullPath() ).Unwrapped() | pxSizerFlags::StdExpand(); + + wxBoxSizer& s_filename( *new wxBoxSizer( wxHORIZONTAL ) ); + s_filename += Heading( _( "To: " ) ).SetMinWidth( 50 ); + m_text_filenameInput->SetMinSize( wxSize( 250, 20 ) ); + m_text_filenameInput->SetValue( wxFileName( m_mcdSourceFilename ).GetName() + L"_converted" ); + s_filename += m_text_filenameInput; + s_filename += Heading( L".ps2" ); + + s_padding += s_filename | wxALIGN_LEFT; + + s_padding += m_radio_CardType | pxSizerFlags::StdExpand(); + + s_padding += Heading( pxE( L"WARNING: Converting a memory card may take several minutes! Please do not close the emulator during the conversion process, even if the emulator is no longer responding to input." ) ); + + s_padding += 12; + s_padding += s_buttons | pxSizerFlags::StdCenter(); + + *this += s_padding | pxSizerFlags::StdExpand(); + + Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConvertMemoryCardDialog::OnOk_Click ) ); + Connect( m_text_filenameInput->GetId(), wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( ConvertMemoryCardDialog::OnOk_Click ) ); + + m_text_filenameInput->SetFocus(); + m_text_filenameInput->SelectAll(); +} + +void Dialogs::ConvertMemoryCardDialog::CreateControls( const MemoryCardType sourceType ) { + m_text_filenameInput = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); + + RadioPanelItem toFile = RadioPanelItem( _( "File" ), pxE( L"Convert this memory card to a regular 8 MB .ps2 file. Please note that the resulting file may not actually contain all saves, depending on how many are in the source memory card." ) ) + .SetInt( MemoryCardType::MemoryCard_File ); + RadioPanelItem toFolder = RadioPanelItem( _( "Folder" ), _( "Convert this memory card to a folder of individual saves." ) ) + .SetInt( MemoryCardType::MemoryCard_Folder ); + + const RadioPanelItem tblForFile[] = { toFolder }; + const RadioPanelItem tblForFolder[] = { toFile }; + + switch ( sourceType ) { + case MemoryCardType::MemoryCard_File: + m_radio_CardType = new pxRadioPanel( this, tblForFile ); + break; + case MemoryCardType::MemoryCard_Folder: + m_radio_CardType = new pxRadioPanel( this, tblForFolder ); + break; + default: + Console.Error( L"Memory Card Conversion: Invalid source type!" ); + return; + } + + m_radio_CardType->SetDefaultItem( 0 ); +} + +void Dialogs::ConvertMemoryCardDialog::OnOk_Click( wxCommandEvent& evt ) { + wxString composedName = m_text_filenameInput->GetValue().Trim() + L".ps2"; + + wxString errMsg; + if ( !isValidNewFilename( composedName, m_mcdPath, errMsg, 5 ) ) { + wxString message; + message.Printf( _( "Error (%s)" ), errMsg.c_str() ); + Msgbox::Alert( message, _( "Convert memory card" ) ); + m_text_filenameInput->SetFocus(); + m_text_filenameInput->SelectAll(); + return; + } + + bool success = false; + + wxFileName sourcePath = ( m_mcdPath + m_mcdSourceFilename ); + wxFileName targetPath = ( m_mcdPath + composedName ); + if ( m_radio_CardType ) { + MemoryCardType targetType = (MemoryCardType)m_radio_CardType->SelectedItem().SomeInt; + + switch ( targetType ) { + case MemoryCardType::MemoryCard_File: + success = ConvertToFile( sourcePath, targetPath ); + break; + case MemoryCardType::MemoryCard_Folder: + success = ConvertToFolder( sourcePath, targetPath ); + break; + } + } + + if ( !success ) { + Msgbox::Alert( _( "Memory Card conversion failed for unknown reasons." ), _( "Convert memory card" ) ); + return; + } + + EndModal( wxID_OK ); +} + +bool Dialogs::ConvertMemoryCardDialog::ConvertToFile( const wxFileName& sourcePath, const wxFileName& targetPath ) { + // Conversion method: Open FolderMcd as usual, then read the raw data from it and write it to a file stream + + wxFFile targetFile( targetPath.GetFullPath(), L"wb" ); + if ( !targetFile.IsOpened() ) { + return false; + } + + FolderMemoryCard sourceFolderMemoryCard; + AppConfig::McdOptions config; + config.Enabled = true; + config.Type = MemoryCardType::MemoryCard_Folder; + sourceFolderMemoryCard.Open( sourcePath.GetFullPath(), config, false, L"" ); + + u8 buffer[FolderMemoryCard::PageSizeRaw]; + u32 adr = 0; + while ( adr < FolderMemoryCard::TotalSizeRaw ) { + sourceFolderMemoryCard.Read( buffer, adr, FolderMemoryCard::PageSizeRaw ); + targetFile.Write( buffer, FolderMemoryCard::PageSizeRaw ); + adr += FolderMemoryCard::PageSizeRaw; + } + + targetFile.Close(); + sourceFolderMemoryCard.Close(); + + return true; +} + +bool Dialogs::ConvertMemoryCardDialog::ConvertToFolder( const wxFileName& sourcePath, const wxFileName& targetPath ) { + // Conversion method: Read all pages of the FileMcd into a FolderMcd, then just write that out with the regular methods + // TODO: Test if >8MB files don't super fuck up something + + wxFFile sourceFile( sourcePath.GetFullPath(), L"rb" ); + if ( !sourceFile.IsOpened() ) { + return false; + } + + u8 buffer[FolderMemoryCard::PageSizeRaw]; + FolderMemoryCard targetFolderMemoryCard; + AppConfig::McdOptions config; + config.Enabled = true; + config.Type = MemoryCardType::MemoryCard_Folder; + targetFolderMemoryCard.Open( targetPath.GetFullPath(), config, false, L"" ); + + u32 adr = 0; + while ( !sourceFile.Eof() ) { + int size = sourceFile.Read( buffer, FolderMemoryCard::PageSizeRaw ); + if ( size > 0 ) { + targetFolderMemoryCard.Save( buffer, adr, size ); + adr += size; + } + } + + sourceFile.Close(); + targetFolderMemoryCard.Close(); + + if ( adr != FolderMemoryCard::TotalSizeRaw ) { + // reset memory card metrics in superblock to the default 8MB, since the converted card was different + targetFolderMemoryCard.Open( targetPath.GetFullPath(), config, true, L"" ); + targetFolderMemoryCard.SetSizeInMB( 8 ); + targetFolderMemoryCard.Close(); + } + + return true; +} diff --git a/pcsx2/gui/Panels/MemoryCardListPanel.cpp b/pcsx2/gui/Panels/MemoryCardListPanel.cpp index db0ed221cd..d391088bce 100644 --- a/pcsx2/gui/Panels/MemoryCardListPanel.cpp +++ b/pcsx2/gui/Panels/MemoryCardListPanel.cpp @@ -466,6 +466,7 @@ enum McdMenuId McdMenuId_RefreshList, McdMenuId_AssignUnassign, McdMenuId_Duplicate, + McdMenuId_Convert, }; @@ -492,6 +493,7 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent 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 @@ -511,6 +513,8 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent *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); @@ -526,12 +530,14 @@ 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_Convert->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnConvertCard)); 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_Convert, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnConvertCard) ); //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) ); @@ -562,6 +568,7 @@ void Panels::MemoryCardListPanel_Simple::UpdateUI() m_button_Rename->Disable(); m_button_Duplicate->Disable(); m_button_AssignUnassign->Disable(); + m_button_Convert->Disable(); return; } @@ -581,6 +588,8 @@ void Panels::MemoryCardListPanel_Simple::UpdateUI() 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 ...") ); @@ -747,6 +756,29 @@ void Panels::MemoryCardListPanel_Simple::UiCreateNewCard( McdSlotItem& card ) closed_core.AllowResume(); } +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; + } + + ScopedCoreThreadClose closed_core; + + 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(); + } + + closed_core.AllowResume(); +} + bool CopyDirectory( const wxString& from, const wxString& to ) { wxDir src( from ); if ( !src.IsOpened() ) { @@ -1009,6 +1041,18 @@ void Panels::MemoryCardListPanel_Simple::OnCreateOrDeleteCard(wxCommandEvent& ev 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) @@ -1168,6 +1212,9 @@ void Panels::MemoryCardListPanel_Simple::OnOpenItemContextMenu(wxListEvent& evt) 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 ...") ); diff --git a/pcsx2/gui/Panels/MemoryCardPanels.h b/pcsx2/gui/Panels/MemoryCardPanels.h index a58646c1ae..59eb6e2529 100644 --- a/pcsx2/gui/Panels/MemoryCardPanels.h +++ b/pcsx2/gui/Panels/MemoryCardPanels.h @@ -212,6 +212,8 @@ namespace Panels // Doubles as Create and Delete buttons wxButton* m_button_Create; + + wxButton* m_button_Convert; // Doubles as Mount and Unmount buttons ("Enable"/"Disable" port) // wxButton* m_button_Mount; @@ -237,6 +239,7 @@ namespace Panels protected: void OnCreateOrDeleteCard(wxCommandEvent& evt); + void OnConvertCard(wxCommandEvent& evt); void OnMountCard(wxCommandEvent& evt); // void OnRelocateCard(wxCommandEvent& evt); void OnRenameFile(wxCommandEvent& evt); @@ -270,6 +273,7 @@ namespace Panels virtual void UiRenameCard( McdSlotItem& card ); virtual void UiCreateNewCard( McdSlotItem& card ); + virtual void UiConvertCard( McdSlotItem& card ); virtual void UiDeleteCard( McdSlotItem& card ); virtual void UiAssignUnassignFile( McdSlotItem& card ); diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj index 1e469df160..bd6037fcd6 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj @@ -646,6 +646,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters index 80e1532c21..c0ec451fab 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2012.vcxproj.filters @@ -665,6 +665,9 @@ AppHost\Dialogs + + AppHost\Dialogs + AppHost\Dialogs diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj index 5d3c284c3c..d9fcf128b5 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj @@ -646,6 +646,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters index 065beb5dad..de618d1a6c 100644 --- a/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2_vs2013.vcxproj.filters @@ -665,6 +665,9 @@ AppHost\Dialogs + + AppHost\Dialogs + AppHost\Dialogs