mirror of
https://github.com/libretro/pcsx2.git
synced 2024-12-12 03:56:26 +00:00
Merge pull request #359 from AdmiralCurtiss/memcard-folder-cache
Memory Card as folder support by AdmiralCurtiss
This commit is contained in:
commit
539a1767a3
File diff suppressed because it is too large
Load Diff
@ -1104,7 +1104,14 @@ typedef struct _PS2E_ComponentAPI_Mcd
|
||||
|
||||
u64 (PS2E_CALLBACK* McdGetCRC)( PS2E_THISPTR thisptr, uint port, uint slot );
|
||||
|
||||
void* reserved[8];
|
||||
// McdNextFrame
|
||||
// Inform the memory card that a frame of emulation time has passed.
|
||||
// Used by the FolderMemoryCard to find a good time to flush written data to the host file system.
|
||||
void (PS2E_CALLBACK* McdNextFrame)( PS2E_THISPTR thisptr, uint port, uint slot );
|
||||
|
||||
void (PS2E_CALLBACK* McdReIndex)( PS2E_THISPTR thisptr, uint port, uint slot, const wxString& filter );
|
||||
|
||||
void* reserved[6];
|
||||
|
||||
} PS2E_ComponentAPI_Mcd;
|
||||
|
||||
|
@ -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
|
||||
@ -296,6 +297,7 @@ set(pcsx2GuiSources
|
||||
gui/MainFrame.cpp
|
||||
gui/MainMenuClicks.cpp
|
||||
gui/MemoryCardFile.cpp
|
||||
gui/MemoryCardFolder.cpp
|
||||
gui/Panels/BaseApplicableConfigPanel.cpp
|
||||
gui/Panels/MemoryCardListPanel.cpp
|
||||
gui/MessageBoxes.cpp
|
||||
@ -348,6 +350,7 @@ set(pcsx2GuiHeaders
|
||||
gui/IsoDropTarget.h
|
||||
gui/MainFrame.h
|
||||
gui/MemoryCardFile.h
|
||||
gui/MemoryCardFolder.h
|
||||
gui/MSWstuff.h
|
||||
gui/Panels/ConfigurationPanels.h
|
||||
gui/Panels/LogOptionsPanels.h
|
||||
|
@ -441,6 +441,7 @@ struct Pcsx2Config
|
||||
BackupSavestate :1,
|
||||
// enables simulated ejection of memory cards when loading savestates
|
||||
McdEnableEjection :1,
|
||||
McdFolderAutoManage :1,
|
||||
|
||||
MultitapPort0_Enabled:1,
|
||||
MultitapPort1_Enabled:1,
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
#include "ps2/HwInternal.h"
|
||||
|
||||
#include "Sio.h"
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
extern u8 psxhblankgate;
|
||||
@ -428,6 +430,10 @@ static __fi void VSyncEnd(u32 sCycle)
|
||||
hwIntcIrq(INTC_VBLANK_E); // HW Irq
|
||||
psxVBlankEnd(); // psxCounters vBlank End
|
||||
if (gates) rcntEndGate(true, sCycle); // Counters End Gate Code
|
||||
|
||||
// FolderMemoryCard needs information on how much time has passed since the last write
|
||||
sioNextFrame();
|
||||
|
||||
frameLimit(); // limit FPS
|
||||
|
||||
//Do this here, breaks Dynasty Warriors otherwise.
|
||||
|
@ -398,6 +398,7 @@ Pcsx2Config::Pcsx2Config()
|
||||
bitset = 0;
|
||||
// Set defaults for fresh installs / reset settings
|
||||
McdEnableEjection = true;
|
||||
McdFolderAutoManage = true;
|
||||
EnablePatches = true;
|
||||
BackupSavestate = true;
|
||||
}
|
||||
@ -417,6 +418,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
|
||||
|
||||
IniBitBool( BackupSavestate );
|
||||
IniBitBool( McdEnableEjection );
|
||||
IniBitBool( McdFolderAutoManage );
|
||||
IniBitBool( MultitapPort0_Enabled );
|
||||
IniBitBool( MultitapPort1_Enabled );
|
||||
|
||||
|
@ -66,6 +66,13 @@ u64 SysPluginBindings::McdGetCRC( uint port, uint slot )
|
||||
return Mcd->McdGetCRC( (PS2E_THISPTR) Mcd, port, slot );
|
||||
}
|
||||
|
||||
void SysPluginBindings::McdNextFrame( uint port, uint slot ) {
|
||||
Mcd->McdNextFrame( (PS2E_THISPTR) Mcd, port, slot );
|
||||
}
|
||||
|
||||
void SysPluginBindings::McdReIndex( uint port, uint slot, const wxString& filter ) {
|
||||
Mcd->McdReIndex( (PS2E_THISPTR) Mcd, port, slot, filter );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Yay, order of this array shouldn't be important. :)
|
||||
|
@ -241,6 +241,8 @@ public:
|
||||
void McdSave( uint port, uint slot, const u8 *src, u32 adr, int size );
|
||||
void McdEraseBlock( uint port, uint slot, u32 adr );
|
||||
u64 McdGetCRC( uint port, uint slot );
|
||||
void McdNextFrame( uint port, uint slot );
|
||||
void McdReIndex( uint port, uint slot, const wxString& filter );
|
||||
|
||||
friend class SysCorePlugins;
|
||||
};
|
||||
|
@ -60,6 +60,13 @@ void SetForceMcdEjectTimeoutNow()
|
||||
mcds[port][slot].ForceEjection_Timeout = FORCED_MCD_EJECTION_MAX_TRIES;
|
||||
}
|
||||
|
||||
void ClearMcdEjectTimeoutNow()
|
||||
{
|
||||
for( u8 port=0; port<2; ++port )
|
||||
for( u8 slot=0; slot<4; ++slot )
|
||||
mcds[port][slot].ForceEjection_Timeout = 0;
|
||||
}
|
||||
|
||||
// SIO Inline'd IRQs : Calls the SIO interrupt handlers directly instead of
|
||||
// feeding them through the IOP's branch test. (see SIO.H for details)
|
||||
|
||||
@ -871,6 +878,28 @@ void SIODMAWrite(u8 value)
|
||||
sioWrite8inl(value);
|
||||
}
|
||||
|
||||
void sioNextFrame() {
|
||||
for ( uint port = 0; port < 2; ++port ) {
|
||||
for ( uint slot = 0; slot < 4; ++slot ) {
|
||||
mcds[port][slot].NextFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to figure out when a new game boots, so that memory cards can re-index themselves and only load data relevant to that game.
|
||||
wxString SioCurrentGameSerial = L"";
|
||||
void sioSetGameSerial( const wxString& serial ) {
|
||||
if ( serial == SioCurrentGameSerial ) { return; }
|
||||
SioCurrentGameSerial = serial;
|
||||
|
||||
for ( uint port = 0; port < 2; ++port ) {
|
||||
for ( uint slot = 0; slot < 4; ++slot ) {
|
||||
mcds[port][slot].ReIndex( serial );
|
||||
}
|
||||
}
|
||||
SetForceMcdEjectTimeoutNow();
|
||||
}
|
||||
|
||||
void SaveStateBase::sioFreeze()
|
||||
{
|
||||
// CRCs for memory cards.
|
||||
|
13
pcsx2/Sio.h
13
pcsx2/Sio.h
@ -19,6 +19,8 @@
|
||||
// Games are highly unlikely to need timed IRQ's for PAD and MemoryCard handling anyway (rama).
|
||||
#define SIO_INLINE_IRQS
|
||||
|
||||
#include "MemoryCardFile.h"
|
||||
|
||||
struct _mcd
|
||||
{
|
||||
u8 term; // terminator value;
|
||||
@ -80,6 +82,14 @@ struct _mcd
|
||||
{
|
||||
return SysPlugins.McdGetCRC(port, slot);
|
||||
}
|
||||
|
||||
void NextFrame() {
|
||||
SysPlugins.McdNextFrame( port, slot );
|
||||
}
|
||||
|
||||
void ReIndex(const wxString& filter = L"") {
|
||||
SysPlugins.McdReIndex( port, slot, filter );
|
||||
}
|
||||
};
|
||||
|
||||
struct _sio
|
||||
@ -117,3 +127,6 @@ extern void sioWriteCtrl16(u16 value);
|
||||
extern void sioInterrupt();
|
||||
extern void InitializeSIO(u8 value);
|
||||
extern void SetForceMcdEjectTimeoutNow();
|
||||
extern void ClearMcdEjectTimeoutNow();
|
||||
extern void sioNextFrame();
|
||||
extern void sioSetGameSerial(const wxString& serial);
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "AsciiFile.h"
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
void AsciiFile::Printf( const char* fmt, ... )
|
||||
{
|
||||
va_list list;
|
||||
@ -25,3 +28,70 @@ void AsciiFile::Printf( const char* fmt, ... )
|
||||
va_end( list );
|
||||
Write( ascii, strlen(ascii) );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "Elfheader.h"
|
||||
#include "Patch.h"
|
||||
#include "R5900Exceptions.h"
|
||||
#include "Sio.h"
|
||||
|
||||
__aligned16 SysMtgsThread mtgsThread;
|
||||
__aligned16 AppCoreThread CoreThread;
|
||||
@ -344,6 +345,7 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
|
||||
|
||||
wxString gameName;
|
||||
wxString gameCompat;
|
||||
wxString gameMemCardFilter;
|
||||
|
||||
int numberLoadedCheats;
|
||||
int numberLoadedWideScreenPatches;
|
||||
@ -368,6 +370,7 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
|
||||
gameName = game.getString("Name");
|
||||
gameName += L" (" + game.getString("Region") + L")";
|
||||
gameCompat = L" [Status = "+compatToStringWX(compat)+L"]";
|
||||
gameMemCardFilter = game.getString("MemCardFilter");
|
||||
}
|
||||
|
||||
if (EmuConfig.EnablePatches) {
|
||||
@ -382,6 +385,12 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameMemCardFilter.IsEmpty()) {
|
||||
sioSetGameSerial(gameMemCardFilter);
|
||||
} else {
|
||||
sioSetGameSerial(curGameKey);
|
||||
}
|
||||
|
||||
if (gameName.IsEmpty() && gameSerial.IsEmpty() && gameCRC.IsEmpty())
|
||||
{
|
||||
// if all these conditions are met, it should mean that we're currently running BIOS code.
|
||||
@ -513,6 +522,7 @@ void AppCoreThread::GameStartingInThread()
|
||||
m_ExecMode = ExecMode_Paused;
|
||||
OnResumeReady();
|
||||
_reset_stuff_as_needed();
|
||||
ClearMcdEjectTimeoutNow(); // probably safe to do this when a game boots, eliminates annoying prompts
|
||||
m_ExecMode = ExecMode_Opened;
|
||||
|
||||
_parent::GameStartingInThread();
|
||||
|
@ -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, const u32 sizeInMB );
|
||||
bool ConvertToFolder( const wxFileName& sourcePath, const wxFileName& targetPath );
|
||||
};
|
||||
}
|
||||
|
236
pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp
Normal file
236
pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "ConfigurationDialog.h"
|
||||
#include "System.h"
|
||||
|
||||
#include "MemoryCardFile.h"
|
||||
#include "MemoryCardFolder.h"
|
||||
#include <wx/ffile.h>
|
||||
|
||||
enum MemoryCardConversionType {
|
||||
MemoryCardConversion_File_8MB,
|
||||
MemoryCardConversion_File_16MB,
|
||||
MemoryCardConversion_File_32MB,
|
||||
MemoryCardConversion_File_64MB,
|
||||
MemoryCardConversion_Folder,
|
||||
MemoryCardConversion_MaxCount
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
if ( mcdSourceConfig.Type != MemoryCardType::MemoryCard_File ) {
|
||||
s_padding += Heading( pxE( L"Please note that the resulting file may not actually contain all saves, depending on how many are in the source memory card." ) );
|
||||
}
|
||||
s_padding += Heading( pxE( L"WARNING: Converting a memory card may take a while! 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 toFile8MB = RadioPanelItem( _( "8MB File" ), pxE( L"Convert this memory card to a standard 8 MB Memory Card .ps2 file." ) )
|
||||
.SetInt( MemoryCardConversionType::MemoryCardConversion_File_8MB );
|
||||
RadioPanelItem toFile16MB = RadioPanelItem( _( "16MB File" ), pxE( L"Convert this memory card to a 16 MB Memory Card .ps2 file." ) )
|
||||
.SetInt( MemoryCardConversionType::MemoryCardConversion_File_16MB );
|
||||
RadioPanelItem toFile32MB = RadioPanelItem( _( "32MB File" ), pxE( L"Convert this memory card to a 32 MB Memory Card .ps2 file." ) )
|
||||
.SetInt( MemoryCardConversionType::MemoryCardConversion_File_32MB );
|
||||
RadioPanelItem toFile64MB = RadioPanelItem( _( "64MB File" ), pxE( L"Convert this memory card to a 64 MB Memory Card .ps2 file." ) )
|
||||
.SetInt( MemoryCardConversionType::MemoryCardConversion_File_64MB );
|
||||
RadioPanelItem toFolder = RadioPanelItem( _( "Folder" ), _( "Convert this memory card to a folder of individual saves." ) )
|
||||
.SetInt( MemoryCardConversionType::MemoryCardConversion_Folder );
|
||||
|
||||
const RadioPanelItem tblForFile[] = { toFolder };
|
||||
const RadioPanelItem tblForFolder[] = { toFile8MB, toFile16MB, toFile32MB, toFile64MB };
|
||||
|
||||
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 ) {
|
||||
MemoryCardConversionType targetType = (MemoryCardConversionType)m_radio_CardType->SelectedItem().SomeInt;
|
||||
|
||||
switch ( targetType ) {
|
||||
case MemoryCardConversionType::MemoryCardConversion_File_8MB:
|
||||
success = ConvertToFile( sourcePath, targetPath, 8 );
|
||||
break;
|
||||
case MemoryCardConversionType::MemoryCardConversion_File_16MB:
|
||||
success = ConvertToFile( sourcePath, targetPath, 16 );
|
||||
break;
|
||||
case MemoryCardConversionType::MemoryCardConversion_File_32MB:
|
||||
success = ConvertToFile( sourcePath, targetPath, 32 );
|
||||
break;
|
||||
case MemoryCardConversionType::MemoryCardConversion_File_64MB:
|
||||
success = ConvertToFile( sourcePath, targetPath, 64 );
|
||||
break;
|
||||
case MemoryCardConversionType::MemoryCardConversion_Folder:
|
||||
success = ConvertToFolder( sourcePath, targetPath );
|
||||
break;
|
||||
default:
|
||||
Msgbox::Alert( _( "This target type is not supported!" ), _( "Convert memory card" ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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, const u32 sizeInMB ) {
|
||||
// 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, ( sizeInMB * 1024 * 1024 ) / FolderMemoryCard::ClusterSize, false, L"" );
|
||||
|
||||
u8 buffer[FolderMemoryCard::PageSizeRaw];
|
||||
u32 adr = 0;
|
||||
while ( adr < sourceFolderMemoryCard.GetSizeInClusters() * FolderMemoryCard::ClusterSizeRaw ) {
|
||||
sourceFolderMemoryCard.Read( buffer, adr, FolderMemoryCard::PageSizeRaw );
|
||||
targetFile.Write( buffer, FolderMemoryCard::PageSizeRaw );
|
||||
adr += FolderMemoryCard::PageSizeRaw;
|
||||
}
|
||||
|
||||
targetFile.Close();
|
||||
sourceFolderMemoryCard.Close( false );
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
u32 adr = 0;
|
||||
|
||||
for ( int i = 0; i < 2; ++i ) {
|
||||
// Before writing the data, we first simulate the entire process without actual writes to the file system.
|
||||
// This ensures that if we crash/fail due to a corrupted memory card file system or similar, we do so during
|
||||
// the simulation run, and don't actually write out any partial data to the host file system.
|
||||
bool simulateWrites = i == 0;
|
||||
targetFolderMemoryCard.Open( targetPath.GetFullPath(), config, 0, false, L"", simulateWrites );
|
||||
|
||||
adr = 0;
|
||||
sourceFile.Seek( 0 );
|
||||
while ( !sourceFile.Eof() ) {
|
||||
int size = sourceFile.Read( buffer, FolderMemoryCard::PageSizeRaw );
|
||||
if ( size > 0 ) {
|
||||
targetFolderMemoryCard.Save( buffer, adr, size );
|
||||
adr += size;
|
||||
}
|
||||
}
|
||||
|
||||
targetFolderMemoryCard.Close();
|
||||
}
|
||||
|
||||
sourceFile.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, 0, true, L"" );
|
||||
targetFolderMemoryCard.SetSizeInMB( 8 );
|
||||
targetFolderMemoryCard.Close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -153,17 +153,31 @@ void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt )
|
||||
return;
|
||||
}
|
||||
|
||||
wxString fullPath=(m_mcdpath + composedName).GetFullPath();
|
||||
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;
|
||||
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;
|
||||
@ -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 );
|
||||
|
@ -40,6 +40,12 @@ Panels::McdConfigPanel_Toggles::McdConfigPanel_Toggles(wxWindow *parent)
|
||||
)
|
||||
);
|
||||
|
||||
m_folderAutoIndex = new pxCheckBox( this,
|
||||
_( "Automatically manage saves based on running game" ),
|
||||
pxE( L"(Folder type only) Re-index memory card content every time the running software changes. This prevents the memory card from running out of space for saves."
|
||||
)
|
||||
);
|
||||
|
||||
//m_check_SavestateBackup = new pxCheckBox( this, pxsFmt(_("Backup existing Savestate when creating a new one")) );
|
||||
/*
|
||||
for( uint i=0; i<2; ++i )
|
||||
@ -64,6 +70,7 @@ Panels::McdConfigPanel_Toggles::McdConfigPanel_Toggles(wxWindow *parent)
|
||||
*this += new wxStaticLine( this ) | StdExpand();
|
||||
|
||||
*this += m_check_Ejection;
|
||||
*this += m_folderAutoIndex;
|
||||
}
|
||||
|
||||
void Panels::McdConfigPanel_Toggles::Apply()
|
||||
@ -73,6 +80,7 @@ void Panels::McdConfigPanel_Toggles::Apply()
|
||||
|
||||
//g_Conf->EmuOptions.BackupSavestate = m_check_SavestateBackup->GetValue();
|
||||
g_Conf->EmuOptions.McdEnableEjection = m_check_Ejection->GetValue();
|
||||
g_Conf->EmuOptions.McdFolderAutoManage = m_folderAutoIndex->GetValue();
|
||||
}
|
||||
|
||||
void Panels::McdConfigPanel_Toggles::AppStatusEvent_OnSettingsApplied()
|
||||
@ -82,6 +90,7 @@ void Panels::McdConfigPanel_Toggles::AppStatusEvent_OnSettingsApplied()
|
||||
|
||||
//m_check_SavestateBackup ->SetValue( g_Conf->EmuOptions.BackupSavestate );
|
||||
m_check_Ejection ->SetValue( g_Conf->EmuOptions.McdEnableEjection );
|
||||
m_folderAutoIndex ->SetValue( g_Conf->EmuOptions.McdFolderAutoManage );
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Utilities/SafeArray.inl"
|
||||
#include <wx/file.h>
|
||||
|
||||
#include "MemoryCardFile.h"
|
||||
#include <wx/dir.h>
|
||||
#include <wx/stopwatch.h>
|
||||
|
||||
// IMPORTANT! If this gets a macro redefinition error it means PluginCallbacks.h is included
|
||||
// in a global-scope header, and that's a BAD THING. Include it only into modules that need
|
||||
@ -26,12 +26,16 @@
|
||||
struct Component_FileMcd;
|
||||
#define PS2E_THISPTR Component_FileMcd*
|
||||
|
||||
#include "MemoryCardFile.h"
|
||||
#include "MemoryCardFolder.h"
|
||||
|
||||
#include "System.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
#include "svnrev.h"
|
||||
|
||||
#include <wx/ffile.h>
|
||||
#include <map>
|
||||
|
||||
static const int MCD_SIZE = 1024 * 8 * 16; // Legacy PSX card default size
|
||||
|
||||
@ -70,7 +74,7 @@ public:
|
||||
s32 Save ( uint slot, const u8 *src, u32 adr, int size );
|
||||
s32 EraseBlock ( uint slot, u32 adr );
|
||||
u64 GetCRC ( uint slot );
|
||||
|
||||
|
||||
protected:
|
||||
bool Seek( wxFFile& f, u32 adr );
|
||||
bool Create( const wxString& mcdFile, uint sizeInMB );
|
||||
@ -167,7 +171,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();
|
||||
@ -403,8 +412,9 @@ u64 FileMemoryCard::GetCRC( uint slot )
|
||||
|
||||
struct Component_FileMcd
|
||||
{
|
||||
PS2E_ComponentAPI_Mcd api; // callbacks the plugin provides back to the emulator
|
||||
FileMemoryCard impl; // class-based implementations we refer to when API is invoked
|
||||
PS2E_ComponentAPI_Mcd api; // callbacks the plugin provides back to the emulator
|
||||
FileMemoryCard impl; // class-based implementations we refer to when API is invoked
|
||||
FolderMemoryCardAggregator implFolder;
|
||||
|
||||
Component_FileMcd();
|
||||
};
|
||||
@ -419,46 +429,135 @@ 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.SetFiltering( g_Conf->EmuOptions.McdFolderAutoManage );
|
||||
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 ) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2E_CALLBACK FileMcd_ReIndex( PS2E_THISPTR thisptr, uint port, uint slot, const wxString& filter ) {
|
||||
const uint combinedSlot = FileMcd_ConvertToSlot( port, slot );
|
||||
switch ( g_Conf->Mcd[combinedSlot].Type ) {
|
||||
//case MemoryCardType::MemoryCard_File:
|
||||
// thisptr->impl.ReIndex( combinedSlot, filter );
|
||||
// break;
|
||||
case MemoryCardType::MemoryCard_Folder:
|
||||
thisptr->implFolder.ReIndex( combinedSlot, g_Conf->EmuOptions.McdFolderAutoManage, filter );
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Component_FileMcd::Component_FileMcd()
|
||||
@ -475,6 +574,8 @@ Component_FileMcd::Component_FileMcd()
|
||||
api.McdSave = FileMcd_Save;
|
||||
api.McdEraseBlock = FileMcd_EraseBlock;
|
||||
api.McdGetCRC = FileMcd_GetCRC;
|
||||
api.McdNextFrame = FileMcd_NextFrame;
|
||||
api.McdReIndex = FileMcd_ReIndex;
|
||||
}
|
||||
|
||||
|
||||
@ -547,32 +648,6 @@ extern "C" const PS2E_LibraryAPI* FileMcd_InitAPI( const PS2E_EmulatorInfo* emui
|
||||
return &FileMcd_Library;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Currently Unused Superblock Header Struct
|
||||
// --------------------------------------------------------------------------------------
|
||||
// (provided for reference purposes)
|
||||
|
||||
struct superblock
|
||||
{
|
||||
char magic[28]; // 0x00
|
||||
char version[12]; // 0x1c
|
||||
u16 page_len; // 0x28
|
||||
u16 pages_per_cluster; // 0x2a
|
||||
u16 pages_per_block; // 0x2c
|
||||
u16 unused; // 0x2e
|
||||
u32 clusters_per_card; // 0x30
|
||||
u32 alloc_offset; // 0x34
|
||||
u32 alloc_end; // 0x38
|
||||
u32 rootdir_cluster; // 0x3c
|
||||
u32 backup_block1; // 0x40
|
||||
u32 backup_block2; // 0x44
|
||||
u32 ifc_list[32]; // 0x50
|
||||
u32 bad_block_list[32]; // 0xd0
|
||||
u8 card_type; // 0x150
|
||||
u8 card_flags; // 0x151
|
||||
};
|
||||
|
||||
|
||||
//Tests if a string is a valid name for a new file within a specified directory.
|
||||
//returns true if:
|
||||
// - the file name has a minimum length of minNumCharacters chars (default is 5 chars: at least 1 char + '.' + 3-chars extension)
|
||||
@ -597,6 +672,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() ))
|
||||
|
1487
pcsx2/gui/MemoryCardFolder.cpp
Normal file
1487
pcsx2/gui/MemoryCardFolder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
522
pcsx2/gui/MemoryCardFolder.h
Normal file
522
pcsx2/gui/MemoryCardFolder.h
Normal file
@ -0,0 +1,522 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/file.h>
|
||||
#include <wx/dir.h>
|
||||
#include <wx/ffile.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "PluginCallbacks.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Superblock Header Struct
|
||||
// --------------------------------------------------------------------------------------
|
||||
#pragma pack(push, 1)
|
||||
struct superblock {
|
||||
char magic[28]; // 0x00
|
||||
char version[12]; // 0x1c
|
||||
u16 page_len; // 0x28
|
||||
u16 pages_per_cluster; // 0x2a
|
||||
u16 pages_per_block; // 0x2c
|
||||
u16 unused; // 0x2e
|
||||
u32 clusters_per_card; // 0x30
|
||||
u32 alloc_offset; // 0x34
|
||||
u32 alloc_end; // 0x38
|
||||
u32 rootdir_cluster; // 0x3c
|
||||
u32 backup_block1; // 0x40
|
||||
u32 backup_block2; // 0x44
|
||||
u64 padding0x48; // 0x48
|
||||
u32 ifc_list[32]; // 0x50
|
||||
u32 bad_block_list[32]; // 0xd0
|
||||
u8 card_type; // 0x150
|
||||
u8 card_flags; // 0x151
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MemoryCardFileEntryDateTime {
|
||||
u8 unused;
|
||||
u8 second;
|
||||
u8 minute;
|
||||
u8 hour;
|
||||
u8 day;
|
||||
u8 month;
|
||||
u16 year;
|
||||
|
||||
static MemoryCardFileEntryDateTime FromWxDateTime( const wxDateTime& time ) {
|
||||
MemoryCardFileEntryDateTime t;
|
||||
|
||||
if ( time.IsValid() ) {
|
||||
wxDateTime::Tm tm = time.GetTm( wxDateTime::GMT9 );
|
||||
|
||||
t.unused = 0;
|
||||
t.second = tm.sec;
|
||||
t.minute = tm.min;
|
||||
t.hour = tm.hour;
|
||||
t.day = tm.mday;
|
||||
t.month = tm.mon + 1;
|
||||
t.year = tm.year;
|
||||
} else {
|
||||
t.unused = 0;
|
||||
t.second = 0;
|
||||
t.minute = 0;
|
||||
t.hour = 0;
|
||||
t.day = 0;
|
||||
t.month = 0;
|
||||
t.year = 0;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool operator==( const MemoryCardFileEntryDateTime& other ) const {
|
||||
return unused == other.unused && second == other.second && minute == other.minute && hour == other.hour
|
||||
&& day == other.day && month == other.month && year == other.year;
|
||||
}
|
||||
bool operator!=( const MemoryCardFileEntryDateTime& other ) const {
|
||||
return !( *this == other );
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MemoryCardFileEntry
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Structure for directory and file relationships as stored on memory cards
|
||||
#pragma pack(push, 1)
|
||||
struct MemoryCardFileEntry {
|
||||
enum MemoryCardFileModeFlags {
|
||||
Mode_Read = 0x0001,
|
||||
Mode_Write = 0x0002,
|
||||
Mode_Execute = 0x0004,
|
||||
Mode_CopyProtected = 0x0008,
|
||||
Mode_File = 0x0010,
|
||||
Mode_Directory = 0x0020,
|
||||
Mode_Unknown0x0040 = 0x0040,
|
||||
Mode_Unknown0x0080 = 0x0080,
|
||||
Mode_Unknown0x0100 = 0x0100,
|
||||
Mode_Unknown0x0200 = 0x0200,
|
||||
Mode_Unknown0x0400 = 0x0400, // Maybe Mode_PS2_Save or something along those lines?
|
||||
Mode_PocketStation = 0x0800,
|
||||
Mode_PSX = 0x1000,
|
||||
Mode_Unknown0x2000 = 0x2000, // Supposedly Mode_Hidden but files still show up in the PS2 browser with this set
|
||||
Mode_Unknown0x4000 = 0x4000,
|
||||
Mode_Used = 0x8000
|
||||
};
|
||||
|
||||
union {
|
||||
struct MemoryCardFileEntryData {
|
||||
u32 mode;
|
||||
u32 length; // number of bytes for file, number of files for dir
|
||||
MemoryCardFileEntryDateTime timeCreated;
|
||||
u32 cluster; // cluster the start of referred file or folder can be found in
|
||||
u32 dirEntry; // parent directory entry number, only used if "." entry of subdir
|
||||
MemoryCardFileEntryDateTime timeModified;
|
||||
u32 attr;
|
||||
u8 padding[0x1C];
|
||||
u8 name[0x20];
|
||||
u8 unused[0x1A0];
|
||||
} data;
|
||||
u8 raw[0x200];
|
||||
} entry;
|
||||
|
||||
bool IsFile() const { return !!( entry.data.mode & Mode_File ); }
|
||||
bool IsDir() const { return !!( entry.data.mode & Mode_Directory ); }
|
||||
bool IsUsed() const { return !!( entry.data.mode & Mode_Used ); }
|
||||
bool IsValid() const { return entry.data.mode != 0xFFFFFFFF; }
|
||||
// checks if we're either "." or ".."
|
||||
bool IsDotDir() const { return entry.data.name[0] == '.' && ( entry.data.name[1] == '\0' || ( entry.data.name[1] == '.' && entry.data.name[2] == '\0' ) ); }
|
||||
|
||||
static const u32 DefaultDirMode = Mode_Read | Mode_Write | Mode_Execute | Mode_Directory | Mode_Unknown0x0400 | Mode_Used;
|
||||
static const u32 DefaultFileMode = Mode_Read | Mode_Write | Mode_Execute | Mode_File | Mode_Unknown0x0080 | Mode_Unknown0x0400 | Mode_Used;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MemoryCardFileEntryCluster {
|
||||
MemoryCardFileEntry entries[2];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct MemoryCardPage {
|
||||
static const int PageSize = 0x200;
|
||||
u8 raw[PageSize];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct MemoryCardFileEntryTreeNode {
|
||||
MemoryCardFileEntry entry;
|
||||
std::vector<MemoryCardFileEntryTreeNode> subdir;
|
||||
|
||||
MemoryCardFileEntryTreeNode( const MemoryCardFileEntry& entry ) : entry(entry) {}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MemoryCardFileMetadataReference
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Helper structure to quickly access file entries from any file data FAT cluster
|
||||
struct MemoryCardFileMetadataReference {
|
||||
MemoryCardFileMetadataReference* parent;
|
||||
MemoryCardFileEntry* entry;
|
||||
u32 consecutiveCluster;
|
||||
|
||||
// returns true if filename was modified and metadata containing the actual filename should be written
|
||||
bool GetPath( wxFileName* fileName ) const;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FileAccessHelper
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Small helper class to keep memory card files opened between calls to Read()/Save()
|
||||
class FileAccessHelper {
|
||||
protected:
|
||||
wxFFile* m_file;
|
||||
const MemoryCardFileEntry* m_entry;
|
||||
wxString m_mode;
|
||||
|
||||
public:
|
||||
FileAccessHelper();
|
||||
~FileAccessHelper();
|
||||
|
||||
// Get an already opened file if possible, or open a new one and remember it
|
||||
wxFFile* ReOpen( const wxFileName& folderName, MemoryCardFileMetadataReference* fileRef, const wxString& mode, bool writeMetadata = false );
|
||||
// Close an open file, if any
|
||||
void Close();
|
||||
|
||||
// removes characters from a PS2 file name that would be illegal in a Windows file system
|
||||
// returns true if any changes were made
|
||||
static bool CleanMemcardFilename( char* name );
|
||||
|
||||
protected:
|
||||
// Open a new file and remember it for later
|
||||
wxFFile* Open( const wxFileName& folderName, MemoryCardFileMetadataReference* fileRef, const wxString& mode, bool writeMetadata = false );
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FolderMemoryCard
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Fakes a memory card using a regular folder/file structure in the host file system
|
||||
class FolderMemoryCard {
|
||||
public:
|
||||
// a few constants so we could in theory change the memory card size without too much effort
|
||||
static const int IndirectFatClusterCount = 1; // should be 32 but only 1 is ever used
|
||||
static const int PageSize = MemoryCardPage::PageSize;
|
||||
static const int ClusterSize = PageSize * 2;
|
||||
static const int BlockSize = ClusterSize * 8;
|
||||
static const int EccSize = 0x10;
|
||||
static const int PageSizeRaw = PageSize + EccSize;
|
||||
static const int ClusterSizeRaw = PageSizeRaw * 2;
|
||||
static const int BlockSizeRaw = ClusterSizeRaw * 8;
|
||||
static const int TotalPages = 0x4000;
|
||||
static const int TotalClusters = TotalPages / 2;
|
||||
static const int TotalBlocks = TotalClusters / 8;
|
||||
static const int TotalSizeRaw = TotalPages * PageSizeRaw;
|
||||
|
||||
static const u32 IndirectFatUnused = 0xFFFFFFFFu;
|
||||
static const u32 LastDataCluster = 0x7FFFFFFFu;
|
||||
static const u32 NextDataClusterMask = 0x7FFFFFFFu;
|
||||
static const u32 DataClusterInUseMask = 0x80000000u;
|
||||
|
||||
static const int FramesAfterWriteUntilFlush = 60;
|
||||
|
||||
protected:
|
||||
union superBlockUnion {
|
||||
superblock data;
|
||||
u8 raw[BlockSize];
|
||||
} m_superBlock;
|
||||
union indirectFatUnion {
|
||||
u32 data[IndirectFatClusterCount][ClusterSize / 4];
|
||||
u8 raw[IndirectFatClusterCount][ClusterSize];
|
||||
} m_indirectFat;
|
||||
union fatUnion {
|
||||
u32 data[IndirectFatClusterCount][ClusterSize / 4][ClusterSize / 4];
|
||||
u8 raw[IndirectFatClusterCount][ClusterSize / 4][ClusterSize];
|
||||
} m_fat;
|
||||
u8 m_backupBlock1[BlockSize];
|
||||
union backupBlock2Union {
|
||||
u32 programmedBlock;
|
||||
u8 raw[BlockSize];
|
||||
} m_backupBlock2;
|
||||
|
||||
// stores directory and file metadata
|
||||
std::map<u32, MemoryCardFileEntryCluster> m_fileEntryDict;
|
||||
// quick-access map of related file entry metadata for each memory card FAT cluster that contains file data
|
||||
std::map<u32, MemoryCardFileMetadataReference> m_fileMetadataQuickAccess;
|
||||
|
||||
// holds a copy of modified pages of the memory card before they're flushed to the file system
|
||||
std::map<u32, MemoryCardPage> m_cache;
|
||||
// contains the state of how the data looked before the first write to it
|
||||
// used to reduce the amount of disk I/O by not re-writing unchanged data that just happened to be
|
||||
// touched in memory due to how actual physical memory cards have to erase and rewrite in blocks
|
||||
std::map<u32, MemoryCardPage> m_oldDataCache;
|
||||
// if > 0, the amount of frames until data is flushed to the file system
|
||||
// reset to FramesAfterWriteUntilFlush on each write
|
||||
int m_framesUntilFlush;
|
||||
// used to figure out if contents were changed for savestate-related purposes, see GetCRC()
|
||||
u64 m_timeLastWritten;
|
||||
|
||||
// remembers and keeps the last accessed file open for further access
|
||||
FileAccessHelper m_lastAccessedFile;
|
||||
|
||||
// path to the folder that contains the files of this memory card
|
||||
wxFileName m_folderName;
|
||||
|
||||
// PS2 memory card slot this card is inserted into
|
||||
uint m_slot;
|
||||
|
||||
bool m_isEnabled;
|
||||
|
||||
// if set to false, nothing is actually written to the file system while flushing, and data is discarded instead
|
||||
bool m_performFileWrites;
|
||||
|
||||
public:
|
||||
FolderMemoryCard();
|
||||
virtual ~FolderMemoryCard() throw() {}
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
// Initialize & Load Memory Card with values configured in the Memory Card Manager
|
||||
void Open( const bool enableFiltering, const wxString& filter );
|
||||
// Initialize & Load Memory Card with provided custom values
|
||||
void Open( const wxString& fullPath, const AppConfig::McdOptions& mcdOptions, const u32 sizeInClusters, const bool enableFiltering, const wxString& filter, bool simulateFileWrites = false );
|
||||
// Close the memory card and flush changes to the file system. Set flush to false to not store changes.
|
||||
void Close( bool flush = true );
|
||||
|
||||
s32 IsPresent() const;
|
||||
void GetSizeInfo( PS2E_McdSizeInfo& outways ) const;
|
||||
bool IsPSX() const;
|
||||
s32 Read( u8 *dest, u32 adr, int size );
|
||||
s32 Save( const u8 *src, u32 adr, int size );
|
||||
s32 EraseBlock( u32 adr );
|
||||
u64 GetCRC() const;
|
||||
|
||||
void SetSlot( uint slot );
|
||||
|
||||
u32 GetSizeInClusters() const;
|
||||
|
||||
// WARNING: The intended use-case for this is resetting back to 8MB if a differently-sized superblock was loaded
|
||||
// setting to a different size is untested and will probably not work correctly
|
||||
void SetSizeInClusters( u32 clusters );
|
||||
// see SetSizeInClusters()
|
||||
void SetSizeInMB( u32 megaBytes );
|
||||
|
||||
// called once per frame, used for flushing data after FramesAfterWriteUntilFlush frames of no writes
|
||||
void NextFrame();
|
||||
|
||||
static void CalculateECC( u8* ecc, const u8* data );
|
||||
|
||||
protected:
|
||||
// initializes memory card data, as if it was fresh from the factory
|
||||
void InitializeInternalData();
|
||||
|
||||
bool IsFormatted() const;
|
||||
|
||||
// returns the in-memory address of data the given memory card adr corresponds to
|
||||
// returns nullptr if adr corresponds to a folder or file entry
|
||||
u8* GetSystemBlockPointer( const u32 adr );
|
||||
|
||||
// returns in-memory address of file or directory metadata searchCluster corresponds to
|
||||
// returns nullptr if searchCluster contains something else
|
||||
// - searchCluster: the cluster that is being accessed, relative to alloc_offset in the superblock
|
||||
// - entryNumber: page of cluster
|
||||
// - offset: offset of page
|
||||
u8* GetFileEntryPointer( const u32 searchCluster, const u32 entryNumber, const u32 offset );
|
||||
|
||||
// used by GetFileEntryPointer to find the correct cluster
|
||||
// returns nullptr if searchCluster is not a file or directory metadata cluster
|
||||
// - currentCluster: the cluster we're currently traversing
|
||||
// - searchCluster: the cluster we want
|
||||
// - fileCount: the number of files left in the directory currently traversed
|
||||
MemoryCardFileEntryCluster* GetFileEntryCluster( const u32 currentCluster, const u32 searchCluster, const u32 fileCount );
|
||||
|
||||
// returns file entry of the file at the given searchCluster
|
||||
// the passed fileName will be filled with a path to the file being accessed
|
||||
// returns nullptr if searchCluster contains no file
|
||||
// call by passing:
|
||||
// - currentCluster: the root directory cluster as indicated in the superblock
|
||||
// - searchCluster: the cluster that is being accessed, relative to alloc_offset in the superblock
|
||||
// - fileName: wxFileName of the root directory of the memory card folder in the host file system (filename part doesn't matter)
|
||||
// - originalDirCount: the point in fileName where to insert the found folder path, usually fileName->GetDirCount()
|
||||
// - outClusterNumber: the cluster's sequential number of the file will be written to this pointer,
|
||||
// which can be used to calculate the in-file offset of the address being accessed
|
||||
MemoryCardFileEntry* GetFileEntryFromFileDataCluster( const u32 currentCluster, const u32 searchCluster, wxFileName* fileName, const size_t originalDirCount, u32* outClusterNumber );
|
||||
|
||||
|
||||
// loads files and folders from the host file system if a superblock exists in the root directory
|
||||
// - sizeInClusters: total memory card size in clusters, 0 for default
|
||||
// - enableFiltering: if set to true, only folders whose name contain the filter string are loaded
|
||||
// - filter: can include multiple filters by separating them with "/"
|
||||
void LoadMemoryCardData( const u32 sizeInClusters, const bool enableFiltering, const wxString& filter );
|
||||
|
||||
// creates the FAT and indirect FAT
|
||||
void CreateFat();
|
||||
|
||||
// creates file entries for the root directory
|
||||
void CreateRootDir();
|
||||
|
||||
|
||||
// returns the system cluster past the highest used one (will be the lowest free one under normal use)
|
||||
// this is used for creating the FAT, don't call otherwise unless you know exactly what you're doing
|
||||
u32 GetFreeSystemCluster() const;
|
||||
|
||||
// returns the total amount of data clusters available on the memory card, both used and unused
|
||||
u32 GetAmountDataClusters() const;
|
||||
|
||||
// returns the lowest unused data cluster, relative to alloc_offset in the superblock
|
||||
// returns 0xFFFFFFFFu when the memory card is full
|
||||
u32 GetFreeDataCluster() const;
|
||||
|
||||
// returns the amount of unused data clusters
|
||||
u32 GetAmountFreeDataClusters() const;
|
||||
|
||||
// returns the final cluster of the file or directory which is (partially) stored in the given cluster
|
||||
u32 GetLastClusterOfData( const u32 cluster ) const;
|
||||
|
||||
|
||||
// creates and returns a new file entry in the given directory entry, ready to be filled
|
||||
// returns nullptr when the memory card is full
|
||||
MemoryCardFileEntry* AppendFileEntryToDir( const MemoryCardFileEntry* const dirEntry );
|
||||
|
||||
// adds a folder in the host file system to the memory card, including all files and subdirectories
|
||||
// - dirEntry: the entry of the directory in the parent directory, or the root "." entry
|
||||
// - dirPath: the full path to the directory in the host file system
|
||||
// - parent: pointer to the parent dir's quick-access reference element
|
||||
// - enableFiltering and filter: filter loaded contents, see LoadMemoryCardData()
|
||||
bool AddFolder( MemoryCardFileEntry* const dirEntry, const wxString& dirPath, MemoryCardFileMetadataReference* parent = nullptr, const bool enableFiltering = false, const wxString& filter = L"" );
|
||||
|
||||
// adds a file in the host file sytem to the memory card
|
||||
// - dirEntry: the entry of the directory in the parent directory, or the root "." entry
|
||||
// - dirPath: the full path to the directory containing the file in the host file system
|
||||
// - fileName: the name of the file, without path
|
||||
// - parent: pointer to the parent dir's quick-access reference element
|
||||
bool AddFile( MemoryCardFileEntry* const dirEntry, const wxString& dirPath, const wxString& fileName, MemoryCardFileMetadataReference* parent = nullptr );
|
||||
|
||||
// calculates the amount of clusters a directory would use up if put into a memory card
|
||||
u32 CalculateRequiredClustersOfDirectory( const wxString& dirPath ) const;
|
||||
|
||||
|
||||
// adds a file to the quick-access dictionary, so it can be accessed more efficiently (ie, without searching through the entire file system) later
|
||||
void AddFileEntryToMetadataQuickAccess( MemoryCardFileEntry* const entry, MemoryCardFileMetadataReference* const parent );
|
||||
|
||||
// creates a reference to a directory entry, so it can be passed as parent to other files/directories
|
||||
MemoryCardFileMetadataReference* AddDirEntryToMetadataQuickAccess( MemoryCardFileEntry* const entry, MemoryCardFileMetadataReference* const parent );
|
||||
|
||||
|
||||
// read data from the memory card, ignoring the cache
|
||||
// do NOT attempt to read ECC with this method, it will not work
|
||||
void ReadDataWithoutCache( u8* const dest, const u32 adr, const u32 dataLength );
|
||||
|
||||
|
||||
bool ReadFromFile( u8 *dest, u32 adr, u32 dataLength );
|
||||
bool WriteToFile( const u8* src, u32 adr, u32 dataLength );
|
||||
|
||||
|
||||
// flush the whole cache to the internal data and/or host file system
|
||||
void Flush();
|
||||
|
||||
// flush a single page of the cache to the internal data and/or host file system
|
||||
bool FlushPage( const u32 page );
|
||||
|
||||
// flush a memory card cluster of the cache to the internal data and/or host file system
|
||||
bool FlushCluster( const u32 cluster );
|
||||
|
||||
// flush a whole memory card block of the cache to the internal data and/or host file system
|
||||
bool FlushBlock( const u32 block );
|
||||
|
||||
// flush the superblock to the internal data and/or host file system
|
||||
void FlushSuperBlock();
|
||||
|
||||
// flush all directory and file entries to the internal data
|
||||
void FlushFileEntries();
|
||||
|
||||
// flush a directory's file entries and all its subdirectories to the internal data
|
||||
void FlushFileEntries( const u32 dirCluster, const u32 remainingFiles, const wxString& dirPath = L"", MemoryCardFileMetadataReference* parent = nullptr );
|
||||
|
||||
// "delete" (prepend '_pcsx2_deleted_' to) any files that exist in oldFileEntries but no longer exist in m_fileEntryDict
|
||||
// also calls RemoveUnchangedDataFromCache() since both operate on comparing with the old file entires
|
||||
void FlushDeletedFilesAndRemoveUnchangedDataFromCache( const std::vector<MemoryCardFileEntryTreeNode>& oldFileEntries );
|
||||
|
||||
// recursive worker method of the above
|
||||
// - newCluster: Current directory dotdir cluster of the new entries.
|
||||
// - newFileCount: Number of file entries in the new directory.
|
||||
// - dirPath: Path to the current directory relative to the root of the memcard. Must be identical for both entries.
|
||||
void FlushDeletedFilesAndRemoveUnchangedDataFromCache( const std::vector<MemoryCardFileEntryTreeNode>& oldFileEntries, const u32 newCluster, const u32 newFileCount, const wxString& dirPath );
|
||||
|
||||
// try and remove unchanged data from m_cache
|
||||
// oldEntry and newEntry should be equivalent entries found by FindEquivalent()
|
||||
void RemoveUnchangedDataFromCache( const MemoryCardFileEntry* const oldEntry, const MemoryCardFileEntry* const newEntry );
|
||||
|
||||
// write data as Save() normally would, but ignore the cache; used for flushing
|
||||
s32 WriteWithoutCache( const u8 *src, u32 adr, int size );
|
||||
|
||||
// copies the contents of m_fileEntryDict into the tree structure fileEntryTree
|
||||
void CopyEntryDictIntoTree( std::vector<MemoryCardFileEntryTreeNode>* fileEntryTree, const u32 cluster, const u32 fileCount );
|
||||
|
||||
// find equivalent (same name and type) of searchEntry in m_fileEntryDict in the directory indicated by cluster
|
||||
const MemoryCardFileEntry* FindEquivalent( const MemoryCardFileEntry* searchEntry, const u32 cluster, const u32 fileCount );
|
||||
|
||||
void SetTimeLastReadToNow();
|
||||
void SetTimeLastWrittenToNow();
|
||||
|
||||
|
||||
wxString GetDisabledMessage( uint slot ) const {
|
||||
return wxsFormat( pxE( L"The PS2-slot %d has been automatically disabled. You can correct the problem\nand re-enable it at any time using Config:Memory cards from the main menu."
|
||||
), slot//TODO: translate internal slot index to human-readable slot description
|
||||
);
|
||||
}
|
||||
wxString GetCardFullMessage( const wxString& filePath ) const {
|
||||
return wxsFormat( pxE( L"(FolderMcd) Memory Card is full, could not add: %s" ), WX_STR( filePath ) );
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// FolderMemoryCardAggregator
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Forwards the API's requests for specific memory card slots to the correct FolderMemoryCard.
|
||||
class FolderMemoryCardAggregator {
|
||||
protected:
|
||||
static const int TotalCardSlots = 8;
|
||||
FolderMemoryCard m_cards[TotalCardSlots];
|
||||
|
||||
// stores the specifics of the current filtering settings, so they can be
|
||||
// re-applied automatically when memory cards are reloaded
|
||||
bool m_enableFiltering = true;
|
||||
wxString m_lastKnownFilter = L"";
|
||||
|
||||
public:
|
||||
FolderMemoryCardAggregator();
|
||||
virtual ~FolderMemoryCardAggregator() throw( ) {}
|
||||
|
||||
void Open();
|
||||
void Close();
|
||||
|
||||
void SetFiltering( const bool enableFiltering );
|
||||
|
||||
s32 IsPresent( uint slot );
|
||||
void GetSizeInfo( uint slot, PS2E_McdSizeInfo& outways );
|
||||
bool IsPSX( uint slot );
|
||||
s32 Read( uint slot, u8 *dest, u32 adr, int size );
|
||||
s32 Save( uint slot, const u8 *src, u32 adr, int size );
|
||||
s32 EraseBlock( uint slot, u32 adr );
|
||||
u64 GetCRC( uint slot );
|
||||
void NextFrame( uint slot );
|
||||
void ReIndex( uint slot, const bool enableFiltering, const wxString& filter );
|
||||
};
|
@ -30,6 +30,9 @@
|
||||
#include <wx/dir.h>
|
||||
|
||||
|
||||
bool CopyDirectory( const wxString& from, const wxString& to );
|
||||
bool RemoveDirectory( const wxString& dirname );
|
||||
|
||||
using namespace pxSizerFlags;
|
||||
using namespace Panels;
|
||||
|
||||
@ -54,19 +57,48 @@ bool EnumerateMemoryCard( McdSlotItem& dest, const wxFileName& filename, const w
|
||||
{
|
||||
dest.IsFormatted = false;
|
||||
dest.IsPresent = false;
|
||||
dest.IsPSX = false;
|
||||
dest.Type = MemoryCardType::MemoryCard_None;
|
||||
|
||||
const wxString fullpath( filename.GetFullPath() );
|
||||
if( !filename.FileExists() ) return false;
|
||||
|
||||
//DevCon.WriteLn( fullpath );
|
||||
wxFFile mcdFile( fullpath );
|
||||
if( !mcdFile.IsOpened() ) return false; // wx should log the error for us.
|
||||
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();
|
||||
wxFileOffset length = mcdFile.Length();
|
||||
|
||||
if( length < (1024*528) && length != 0x20000 )
|
||||
{
|
||||
Console.Warning( "... MemoryCard appears to be truncated. Ignoring." );
|
||||
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;
|
||||
}
|
||||
|
||||
@ -74,18 +106,7 @@ bool EnumerateMemoryCard( McdSlotItem& dest, const wxFileName& filename, const w
|
||||
dest.Filename = filename;
|
||||
if( filename.GetFullPath() == (basePath+filename.GetFullName()).GetFullPath() )
|
||||
dest.Filename = filename.GetFullName();
|
||||
|
||||
dest.SizeInMB = (uint)(length / (1024 * 528 * 2));
|
||||
|
||||
if(length == 0x20000)
|
||||
{
|
||||
dest.IsPSX = true; // PSX memcard;
|
||||
dest.SizeInMB = 1; // MegaBIT
|
||||
}
|
||||
|
||||
dest.IsFormatted = IsMcdFormatted( mcdFile );
|
||||
filename.GetTimes( NULL, &dest.DateModified, &dest.DateCreated );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -448,6 +469,7 @@ enum McdMenuId
|
||||
McdMenuId_RefreshList,
|
||||
McdMenuId_AssignUnassign,
|
||||
McdMenuId_Duplicate,
|
||||
McdMenuId_Convert,
|
||||
};
|
||||
|
||||
|
||||
@ -474,6 +496,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
|
||||
@ -493,6 +516,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);
|
||||
@ -508,12 +533,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) );
|
||||
@ -544,6 +571,7 @@ void Panels::MemoryCardListPanel_Simple::UpdateUI()
|
||||
m_button_Rename->Disable();
|
||||
m_button_Duplicate->Disable();
|
||||
m_button_AssignUnassign->Disable();
|
||||
m_button_Convert->Disable();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -563,6 +591,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 ...") );
|
||||
|
||||
@ -595,6 +625,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 +654,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 +670,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 +759,28 @@ 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();
|
||||
}
|
||||
|
||||
void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card )
|
||||
{
|
||||
@ -757,7 +810,12 @@ void Panels::MemoryCardListPanel_Simple::UiDeleteCard( McdSlotItem& card )
|
||||
|
||||
card.IsEnabled=false;
|
||||
Apply();
|
||||
wxRemoveFile( fullpath.GetFullPath() );
|
||||
|
||||
if ( fullpath.FileExists() ) {
|
||||
wxRemoveFile( fullpath.GetFullPath() );
|
||||
} else {
|
||||
RemoveDirectory( fullpath.GetFullPath() );
|
||||
}
|
||||
|
||||
RefreshSelections();
|
||||
closed_core.AllowResume();
|
||||
@ -817,7 +875,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." ),
|
||||
@ -918,6 +977,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)
|
||||
@ -1077,6 +1148,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 ...") );
|
||||
@ -1106,9 +1180,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;
|
||||
|
@ -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() );
|
||||
|
@ -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
|
||||
@ -211,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;
|
||||
@ -236,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);
|
||||
@ -269,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 );
|
||||
|
||||
@ -284,6 +289,7 @@ namespace Panels
|
||||
protected:
|
||||
//pxCheckBox* m_check_Multitap[2];
|
||||
pxCheckBox* m_check_Ejection;
|
||||
pxCheckBox* m_folderAutoIndex;
|
||||
|
||||
//moved to the system menu, just below "Save State"
|
||||
//pxCheckBox* m_check_SavestateBackup;
|
||||
|
@ -636,6 +636,7 @@
|
||||
<ClCompile Include="..\..\gui\MainFrame.cpp" />
|
||||
<ClCompile Include="..\..\gui\MainMenuClicks.cpp" />
|
||||
<ClCompile Include="..\..\gui\MemoryCardFile.cpp" />
|
||||
<ClCompile Include="..\..\gui\MemoryCardFolder.cpp" />
|
||||
<ClCompile Include="..\..\gui\MessageBoxes.cpp" />
|
||||
<ClCompile Include="..\..\gui\MSWstuff.cpp" />
|
||||
<ClCompile Include="..\..\gui\pxLogTextCtrl.cpp" />
|
||||
@ -645,6 +646,7 @@
|
||||
<ClCompile Include="..\..\gui\Dialogs\AssertionDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\BaseConfigurationDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConfirmationDialogs.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConvertMemoryCardDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\CreateMemoryCardDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\FirstTimeWizard.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\ImportSettingsDialog.cpp" />
|
||||
|
@ -635,6 +635,9 @@
|
||||
<ClCompile Include="..\..\gui\MemoryCardFile.cpp">
|
||||
<Filter>AppHost</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\MemoryCardFolder.cpp">
|
||||
<Filter>AppHost</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\MessageBoxes.cpp">
|
||||
<Filter>AppHost</Filter>
|
||||
</ClCompile>
|
||||
@ -662,6 +665,9 @@
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConfirmationDialogs.cpp">
|
||||
<Filter>AppHost\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConvertMemoryCardDialog.cpp">
|
||||
<Filter>AppHost\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Dialogs\CreateMemoryCardDialog.cpp">
|
||||
<Filter>AppHost\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
|
@ -636,6 +636,7 @@
|
||||
<ClCompile Include="..\..\gui\MainFrame.cpp" />
|
||||
<ClCompile Include="..\..\gui\MainMenuClicks.cpp" />
|
||||
<ClCompile Include="..\..\gui\MemoryCardFile.cpp" />
|
||||
<ClCompile Include="..\..\gui\MemoryCardFolder.cpp" />
|
||||
<ClCompile Include="..\..\gui\MessageBoxes.cpp" />
|
||||
<ClCompile Include="..\..\gui\MSWstuff.cpp" />
|
||||
<ClCompile Include="..\..\gui\pxLogTextCtrl.cpp" />
|
||||
@ -645,6 +646,7 @@
|
||||
<ClCompile Include="..\..\gui\Dialogs\AssertionDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\BaseConfigurationDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConfirmationDialogs.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConvertMemoryCardDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\CreateMemoryCardDialog.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\FirstTimeWizard.cpp" />
|
||||
<ClCompile Include="..\..\gui\Dialogs\ImportSettingsDialog.cpp" />
|
||||
|
@ -635,6 +635,9 @@
|
||||
<ClCompile Include="..\..\gui\MemoryCardFile.cpp">
|
||||
<Filter>AppHost</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\MemoryCardFolder.cpp">
|
||||
<Filter>AppHost</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\MessageBoxes.cpp">
|
||||
<Filter>AppHost</Filter>
|
||||
</ClCompile>
|
||||
@ -662,6 +665,9 @@
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConfirmationDialogs.cpp">
|
||||
<Filter>AppHost\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Dialogs\ConvertMemoryCardDialog.cpp">
|
||||
<Filter>AppHost\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\gui\Dialogs\CreateMemoryCardDialog.cpp">
|
||||
<Filter>AppHost\Dialogs</Filter>
|
||||
</ClCompile>
|
||||
|
Loading…
Reference in New Issue
Block a user