MemoryCard: Add support for folder memcards in GUI and make both implementation function side-by-side.

This commit is contained in:
Admiral H. Curtiss 2015-05-09 03:22:17 +02:00
parent 1d46800888
commit f15c07653c
9 changed files with 281 additions and 61 deletions

View File

@ -431,10 +431,8 @@ static __fi void VSyncEnd(u32 sCycle)
psxVBlankEnd(); // psxCounters vBlank End
if (gates) rcntEndGate(true, sCycle); // Counters End Gate Code
#ifdef MEMORYCARD_USE_FOLDER
// FolderMemoryCard needs information on how much time has passed since the last write
sioNextFrame();
#endif
frameLimit(); // limit FPS

View File

@ -611,6 +611,10 @@ void AppConfig::LoadSaveMemcards( IniInterface& ini )
Mcd[slot].Enabled, Mcd[slot].Enabled );
ini.Entry( pxsFmt( L"Slot%u_Filename", slot+1 ),
Mcd[slot].Filename, Mcd[slot].Filename );
int type = (int)Mcd[slot].Type;
ini.Entry( pxsFmt( L"Slot%u_Type", slot + 1 ),
type, (int)MemoryCardType::MemoryCard_File );
Mcd[slot].Type = (MemoryCardType)type;
}
for( uint slot=2; slot<8; ++slot )
@ -622,6 +626,10 @@ void AppConfig::LoadSaveMemcards( IniInterface& ini )
Mcd[slot].Enabled, Mcd[slot].Enabled );
ini.Entry( pxsFmt( L"Multitap%u_Slot%u_Filename", mtport, mtslot ),
Mcd[slot].Filename, Mcd[slot].Filename );
int type = (int)Mcd[slot].Type;
ini.Entry( pxsFmt( L"Multitap%u_Slot%u_Type", mtport, mtslot ),
type, (int)MemoryCardType::MemoryCard_File );
Mcd[slot].Type = (MemoryCardType)type;
}
}

View File

@ -98,6 +98,14 @@ enum AspectRatioType
AspectRatio_MaxCount
};
enum MemoryCardType
{
MemoryCard_None,
MemoryCard_File,
MemoryCard_Folder,
MemoryCard_MaxCount
};
// =====================================================================================================
// Pcsx2 Application Configuration.
// =====================================================================================================
@ -182,6 +190,7 @@ public:
{
wxFileName Filename; // user-configured location of this memory card
bool Enabled; // memory card enabled (if false, memcard will not show up in-game)
MemoryCardType Type; // the memory card implementation that should be used
};
// ------------------------------------------------------------------------

View File

@ -154,17 +154,31 @@ void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt )
}
wxString fullPath = ( m_mcdpath + composedName ).GetFullPath();
if ( m_radio_CardSize && m_radio_CardSize->SelectedItem().SomeInt == 0 ) {
// user selected to create a folder memory card
if ( !wxFileName::Mkdir( fullPath ) ) {
Msgbox::Alert(
_( "Error: The directory for the memory card could not be created." ),
_( "Create memory card" )
);
} else {
// also create an empty superblock so we can recognize memory card folders based on if they have a superblock
wxFFile superblock( wxFileName( fullPath, L"_pcsx2_superblock" ).GetFullPath(), L"wb" );
superblock.Close();
}
} else {
// otherwise create a file
if ( !CreateIt(
fullPath,
m_radio_CardSize ? m_radio_CardSize->SelectedItem().SomeInt : 8
) )
{
) ) {
Msgbox::Alert(
_( "Error: The memory card could not be created." ),
_( "Create memory card" )
);
return;
}
}
result_createdMcdFilename = composedName;
EndModal( wxID_OK );
@ -205,7 +219,11 @@ void Dialogs::CreateMemoryCardDialog::CreateControls()
RadioPanelItem(_("64 MB"), _("Low compatibility warning: Yes it's very big, but may not work with many games."))
. SetToolTip(_t("Use at your own risk. Erratic memory card behavior is possible (though unlikely)."))
. SetInt(64)
. SetInt(64),
RadioPanelItem(_("Folder [experimental]"), _("Store memory card contents in the host filesystem instead of a file."))
. SetToolTip(_t("Automatically manages memory card contents so that the console only sees files related to the currently running software. Allows you to drag-and-drop files in and out of the memory card with your standard file explorer. This is still experimental, so use at your own risk!"))
. SetInt(0)
};
m_radio_CardSize = new pxRadioPanel( this, tbl_CardSizes );

View File

@ -75,6 +75,8 @@ public:
s32 EraseBlock ( uint slot, u32 adr );
u64 GetCRC ( uint slot );
void NextFrame( uint slot );
protected:
bool Seek( wxFFile& f, u32 adr );
bool Create( const wxString& mcdFile, uint sizeInMB );
@ -171,7 +173,12 @@ void FileMemoryCard::Open()
cont = true;
}
Console.WriteLn( cont ? Color_Gray : Color_Green, L"McdSlot %u: " + str, slot );
if ( g_Conf->Mcd[slot].Type != MemoryCardType::MemoryCard_File ) {
str = L"[is not memcard file]";
cont = true;
}
Console.WriteLn( cont ? Color_Gray : Color_Green, L"McdSlot %u [File]: " + str, slot );
if( cont ) continue;
const wxULongLong fsz = fname.GetSize();
@ -401,6 +408,11 @@ u64 FileMemoryCard::GetCRC( uint slot )
return retval;
}
void FileMemoryCard::NextFrame( uint slot )
{
}
// --------------------------------------------------------------------------------------
// MemoryCard Component API Bindings
// --------------------------------------------------------------------------------------
@ -408,11 +420,8 @@ u64 FileMemoryCard::GetCRC( uint slot )
struct Component_FileMcd
{
PS2E_ComponentAPI_Mcd api; // callbacks the plugin provides back to the emulator
#ifdef MEMORYCARD_USE_FOLDER
FolderMemoryCardAggregator impl;
#else
FileMemoryCard impl; // class-based implementations we refer to when API is invoked
#endif
FolderMemoryCardAggregator implFolder;
Component_FileMcd();
};
@ -427,52 +436,120 @@ uint FileMcd_ConvertToSlot( uint port, uint slot )
static void PS2E_CALLBACK FileMcd_EmuOpen( PS2E_THISPTR thisptr, const PS2E_SessionInfo *session )
{
thisptr->impl.Open();
thisptr->implFolder.Open();
}
static void PS2E_CALLBACK FileMcd_EmuClose( PS2E_THISPTR thisptr )
{
thisptr->implFolder.Close();
thisptr->impl.Close();
}
static s32 PS2E_CALLBACK FileMcd_IsPresent( PS2E_THISPTR thisptr, uint port, uint slot )
{
return thisptr->impl.IsPresent( FileMcd_ConvertToSlot( port, slot ) );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
return thisptr->impl.IsPresent( combinedSlot );
case MemoryCardType::MemoryCard_Folder:
return thisptr->implFolder.IsPresent( combinedSlot );
default:
return false;
}
}
static void PS2E_CALLBACK FileMcd_GetSizeInfo( PS2E_THISPTR thisptr, uint port, uint slot, PS2E_McdSizeInfo* outways )
{
thisptr->impl.GetSizeInfo( FileMcd_ConvertToSlot( port, slot ), *outways );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
thisptr->impl.GetSizeInfo( combinedSlot, *outways );
break;
case MemoryCardType::MemoryCard_Folder:
thisptr->implFolder.GetSizeInfo( combinedSlot, *outways );
break;
default:
return;
}
}
static bool PS2E_CALLBACK FileMcd_IsPSX( PS2E_THISPTR thisptr, uint port, uint slot )
{
return thisptr->impl.IsPSX( FileMcd_ConvertToSlot( port, slot ) );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
return thisptr->impl.IsPSX( combinedSlot );
case MemoryCardType::MemoryCard_Folder:
return thisptr->implFolder.IsPSX( combinedSlot );
default:
return false;
}
}
static s32 PS2E_CALLBACK FileMcd_Read( PS2E_THISPTR thisptr, uint port, uint slot, u8 *dest, u32 adr, int size )
{
return thisptr->impl.Read( FileMcd_ConvertToSlot( port, slot ), dest, adr, size );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
return thisptr->impl.Read( combinedSlot, dest, adr, size );
case MemoryCardType::MemoryCard_Folder:
return thisptr->implFolder.Read( combinedSlot, dest, adr, size );
default:
return 0;
}
}
static s32 PS2E_CALLBACK FileMcd_Save( PS2E_THISPTR thisptr, uint port, uint slot, const u8 *src, u32 adr, int size )
{
return thisptr->impl.Save( FileMcd_ConvertToSlot( port, slot ), src, adr, size );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
return thisptr->impl.Save( combinedSlot, src, adr, size );
case MemoryCardType::MemoryCard_Folder:
return thisptr->implFolder.Save( combinedSlot, src, adr, size );
default:
return 0;
}
}
static s32 PS2E_CALLBACK FileMcd_EraseBlock( PS2E_THISPTR thisptr, uint port, uint slot, u32 adr )
{
return thisptr->impl.EraseBlock( FileMcd_ConvertToSlot( port, slot ), adr );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
return thisptr->impl.EraseBlock( combinedSlot, adr );
case MemoryCardType::MemoryCard_Folder:
return thisptr->implFolder.EraseBlock( combinedSlot, adr );
default:
return 0;
}
}
static u64 PS2E_CALLBACK FileMcd_GetCRC( PS2E_THISPTR thisptr, uint port, uint slot )
{
return thisptr->impl.GetCRC( FileMcd_ConvertToSlot( port, slot ) );
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
return thisptr->impl.GetCRC( combinedSlot );
case MemoryCardType::MemoryCard_Folder:
return thisptr->implFolder.GetCRC( combinedSlot );
default:
return 0;
}
}
static void PS2E_CALLBACK FileMcd_NextFrame( PS2E_THISPTR thisptr, uint port, uint slot ) {
#ifdef MEMORYCARD_USE_FOLDER
thisptr->impl.NextFrame( FileMcd_ConvertToSlot( port, slot ) );
#endif
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
switch ( g_Conf->Mcd[combinedSlot].Type ) {
case MemoryCardType::MemoryCard_File:
thisptr->impl.NextFrame( combinedSlot );
break;
case MemoryCardType::MemoryCard_Folder:
thisptr->implFolder.NextFrame( combinedSlot );
break;
default:
return;
}
}
Component_FileMcd::Component_FileMcd()
@ -586,6 +663,11 @@ bool isValidNewFilename( wxString filenameStringToTest, wxDirName atBasePath, wx
out_errorMessage = _("File name already exists");
return false;
}
if ( wxDirExists( (atBasePath + wxFileName(filenameStringToTest)).GetFullPath() ))
{
out_errorMessage = _( "File name already exists" );
return false;
}
wxFile fp;
if( !fp.Create( (atBasePath + wxFileName(filenameStringToTest)).GetFullPath() ))

View File

@ -15,9 +15,6 @@
#pragma once
// define this to use the FolderMemoryCard implementation instead of the regular FileMemoryCard one
//#define MEMORYCARD_USE_FOLDER
// NOTICE! This file is intended as a temporary placebo only, until such time that the
// memorycard system is properly extracted into a plugin system (which would make it a
// separate project file).

View File

@ -54,13 +54,14 @@ bool EnumerateMemoryCard( McdSlotItem& dest, const wxFileName& filename, const w
{
dest.IsFormatted = false;
dest.IsPresent = false;
dest.Type = MemoryCardType::MemoryCard_None;
const wxString fullpath( filename.GetFullPath() );
if( !filename.FileExists() ) return false;
//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.
if ( !mcdFile.IsOpened() ) { return false; } // wx should log the error for us.
wxFileOffset length = mcdFile.Length();
@ -70,21 +71,37 @@ bool EnumerateMemoryCard( McdSlotItem& dest, const wxFileName& filename, const w
return false;
}
dest.IsPresent = true;
dest.Filename = filename;
if( filename.GetFullPath() == (basePath+filename.GetFullName()).GetFullPath() )
dest.Filename = filename.GetFullName();
dest.SizeInMB = (uint)( length / ( 1024 * 528 * 2 ) );
if(length == 0x20000)
{
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;
}
@ -595,6 +612,7 @@ void Panels::MemoryCardListPanel_Simple::Apply()
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;
@ -623,7 +641,7 @@ void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied()
//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 ) )
if ( m_Cards[slot].IsEnabled && !( wxFileExists( targetFile ) || wxDirExists( targetFile ) ) )
{
wxString errMsg;
if (isValidNewFilename(m_Cards[slot].Filename.GetFullName(), GetMcdPath(), errMsg, 5))
@ -639,7 +657,7 @@ void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied()
}
}
if ( !m_Cards[slot].IsEnabled || !wxFileExists( targetFile ) )
if ( !m_Cards[slot].IsEnabled || !( wxFileExists( targetFile ) || wxDirExists( targetFile ) ) )
{
m_Cards[slot].IsEnabled = false;
m_Cards[slot].IsPresent = false;
@ -728,6 +746,72 @@ void Panels::MemoryCardListPanel_Simple::UiCreateNewCard( McdSlotItem& card )
closed_core.AllowResume();
}
bool CopyDirectory( const wxString& from, const wxString& to ) {
wxDir src( from );
if ( !src.IsOpened() ) {
return false;
}
wxMkdir( to );
wxDir dst( to );
if ( !dst.IsOpened() ) {
return false;
}
wxString filename;
// copy directories
if ( src.GetFirst( &filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN ) ) {
do {
if ( !CopyDirectory( wxFileName( from, filename ).GetFullPath(), wxFileName( to, filename ).GetFullPath() ) ) {
return false;
}
} while ( src.GetNext( &filename ) );
}
// copy files
if ( src.GetFirst( &filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN ) ) {
do {
if ( !wxCopyFile( wxFileName( from, filename ).GetFullPath(), wxFileName( to, filename ).GetFullPath() ) ) {
return false;
}
} while ( src.GetNext( &filename ) );
}
return true;
}
bool RemoveDirectory( const wxString& dirname ) {
{
wxDir dir( dirname );
if ( !dir.IsOpened() ) {
return false;
}
wxString filename;
// delete subdirs recursively
if ( dir.GetFirst( &filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN ) ) {
do {
if ( !RemoveDirectory( wxFileName( dirname, filename ).GetFullPath() ) ) {
return false;
}
} while ( dir.GetNext( &filename ) );
}
// delete files
if ( dir.GetFirst( &filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN ) ) {
do {
if ( !wxRemoveFile( wxFileName( dirname, filename ).GetFullPath() ) ) {
return false;
}
} while ( dir.GetNext( &filename ) );
}
}
// oddly enough this has different results compared to the more sensible dirname.Rmdir(), don't change!
return wxFileName::Rmdir( dirname );
}
void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card )
{
@ -757,7 +841,12 @@ void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card )
card.IsEnabled=false;
Apply();
if ( fullpath.FileExists() ) {
wxRemoveFile( fullpath.GetFullPath() );
} else {
RemoveDirectory( fullpath.GetFullPath() );
}
RefreshSelections();
closed_core.AllowResume();
@ -817,7 +906,8 @@ bool Panels::MemoryCardListPanel_Simple::UiDuplicateCard(McdSlotItem& src, McdSl
ScopedBusyCursor doh( Cursor_ReallyBusy );
ScopedCoreThreadClose closed_core;
if( !wxCopyFile( srcfile.GetFullPath(), destfile.GetFullPath(), true ) )
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." ),
@ -1106,9 +1196,26 @@ void Panels::MemoryCardListPanel_Simple::ReadFilesAtMcdFolder(){
wxArrayString memcardList;
wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), &memcardList, L"*.ps2", wxDIR_FILES);
wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), &memcardList, L"*.mcd", wxDIR_FILES);
wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), &memcardList, L"*.mcr", wxDIR_FILES);
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;

View File

@ -156,7 +156,7 @@ wxString MemoryCardListView_Simple::OnGetItemText(long item, long column) const
return prefix + res;
}
*/
case McdColS_Size: return prefix + ( !it.IsPresent ? L"" : (it.IsPSX? pxsFmt( L"%u MBit", it.SizeInMB ) : pxsFmt( L"%u MiB", it.SizeInMB )) );
case McdColS_Size: return prefix + ( !it.IsPresent ? L"" : (it.IsPSX? pxsFmt( L"%u MBit", it.SizeInMB ) : ( it.SizeInMB > 0 ? pxsFmt( L"%u MiB", it.SizeInMB ) : L"Auto" ) ) );
case McdColS_Formatted: return prefix + ( !it.IsPresent ? L"" : ( it.IsFormatted ? _("Yes") : _("No")) );
case McdColS_Type: return prefix + ( !it.IsPresent ? L"" : ( it.IsPSX? _("PSX") : _("PS2")) );
case McdColS_DateModified: return prefix + ( !it.IsPresent ? L"" : it.DateModified.FormatDate() );

View File

@ -40,6 +40,7 @@ struct McdSlotItem
{
int Slot; //0-7: internal slot. -1: unrelated to an internal slot (the rest of the files at the folder).
bool IsPresent; //Whether or not a file is associated with this item (true/false when 0<=Slot<=7. Always true when Slot==-1)
MemoryCardType Type; //The implementation used for this memory card
//Only meaningful when IsPresent==true (a file exists for this item):
wxFileName Filename; // full pathname