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