mirror of https://github.com/PCSX2/pcsx2.git
MemoryCard: When converting a file to a folder, simulate the conversion process once before writing the data. This prevents half-converted/corrupted cards by ensuring we crash before any actual writes occur.
This commit is contained in:
parent
39e1de4d13
commit
d331d59a9f
|
@ -201,19 +201,29 @@ bool Dialogs::ConvertMemoryCardDialog::ConvertToFolder( const wxFileName& source
|
||||||
AppConfig::McdOptions config;
|
AppConfig::McdOptions config;
|
||||||
config.Enabled = true;
|
config.Enabled = true;
|
||||||
config.Type = MemoryCardType::MemoryCard_Folder;
|
config.Type = MemoryCardType::MemoryCard_Folder;
|
||||||
targetFolderMemoryCard.Open( targetPath.GetFullPath(), config, 0, false, L"" );
|
|
||||||
|
|
||||||
u32 adr = 0;
|
u32 adr = 0;
|
||||||
while ( !sourceFile.Eof() ) {
|
|
||||||
int size = sourceFile.Read( buffer, FolderMemoryCard::PageSizeRaw );
|
for ( int i = 0; i < 2; ++i ) {
|
||||||
if ( size > 0 ) {
|
// Before writing the data, we first simulate the entire process without actual writes to the file system.
|
||||||
targetFolderMemoryCard.Save( buffer, adr, size );
|
// This ensures that if we crash/fail due to a corrupted memory card file system or similar, we do so during
|
||||||
adr += size;
|
// 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();
|
sourceFile.Close();
|
||||||
targetFolderMemoryCard.Close();
|
|
||||||
|
|
||||||
if ( adr != FolderMemoryCard::TotalSizeRaw ) {
|
if ( adr != FolderMemoryCard::TotalSizeRaw ) {
|
||||||
// reset memory card metrics in superblock to the default 8MB, since the converted card was different
|
// reset memory card metrics in superblock to the default 8MB, since the converted card was different
|
||||||
|
|
|
@ -44,6 +44,7 @@ void FolderMemoryCard::InitializeInternalData() {
|
||||||
m_isEnabled = false;
|
m_isEnabled = false;
|
||||||
m_framesUntilFlush = 0;
|
m_framesUntilFlush = 0;
|
||||||
m_lastAccessedFile.Close();
|
m_lastAccessedFile.Close();
|
||||||
|
m_performFileWrites = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FolderMemoryCard::IsFormatted() const {
|
bool FolderMemoryCard::IsFormatted() const {
|
||||||
|
@ -52,11 +53,12 @@ bool FolderMemoryCard::IsFormatted() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderMemoryCard::Open( const bool enableFiltering, const wxString& filter ) {
|
void FolderMemoryCard::Open( const bool enableFiltering, const wxString& filter ) {
|
||||||
Open( g_Conf->FullpathToMcd( m_slot ), g_Conf->Mcd[m_slot], 0, enableFiltering, filter );
|
Open( g_Conf->FullpathToMcd( m_slot ), g_Conf->Mcd[m_slot], 0, enableFiltering, filter, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderMemoryCard::Open( const wxString& fullPath, const AppConfig::McdOptions& mcdOptions, const u32 sizeInClusters, const bool enableFiltering, const wxString& filter ) {
|
void FolderMemoryCard::Open( const wxString& fullPath, const AppConfig::McdOptions& mcdOptions, const u32 sizeInClusters, const bool enableFiltering, const wxString& filter, bool simulateFileWrites ) {
|
||||||
InitializeInternalData();
|
InitializeInternalData();
|
||||||
|
m_performFileWrites = !simulateFileWrites;
|
||||||
|
|
||||||
wxFileName configuredFileName( fullPath );
|
wxFileName configuredFileName( fullPath );
|
||||||
m_folderName = wxFileName( configuredFileName.GetFullPath() + L"/" );
|
m_folderName = wxFileName( configuredFileName.GetFullPath() + L"/" );
|
||||||
|
@ -74,7 +76,7 @@ void FolderMemoryCard::Open( const wxString& fullPath, const AppConfig::McdOptio
|
||||||
}
|
}
|
||||||
|
|
||||||
// if nothing exists at a valid location, create a directory for the memory card
|
// if nothing exists at a valid location, create a directory for the memory card
|
||||||
if ( !disabled && !m_folderName.DirExists() ) {
|
if ( !disabled && m_performFileWrites && !m_folderName.DirExists() ) {
|
||||||
if ( !m_folderName.Mkdir() ) {
|
if ( !m_folderName.Mkdir() ) {
|
||||||
str = L"[couldn't create folder]";
|
str = L"[couldn't create folder]";
|
||||||
disabled = true;
|
disabled = true;
|
||||||
|
@ -910,7 +912,7 @@ bool FolderMemoryCard::FlushBlock( const u32 block ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderMemoryCard::FlushSuperBlock() {
|
void FolderMemoryCard::FlushSuperBlock() {
|
||||||
if ( FlushBlock( 0 ) ) {
|
if ( FlushBlock( 0 ) && m_performFileWrites ) {
|
||||||
wxFileName superBlockFileName( m_folderName.GetPath(), L"_pcsx2_superblock" );
|
wxFileName superBlockFileName( m_folderName.GetPath(), L"_pcsx2_superblock" );
|
||||||
wxFFile superBlockFile( superBlockFileName.GetFullPath().c_str(), L"wb" );
|
wxFFile superBlockFile( superBlockFileName.GetFullPath().c_str(), L"wb" );
|
||||||
if ( superBlockFile.IsOpened() ) {
|
if ( superBlockFile.IsOpened() ) {
|
||||||
|
@ -946,21 +948,23 @@ void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remaini
|
||||||
const wxString subDirName = wxString::FromAscii( (const char*)cleanName );
|
const wxString subDirName = wxString::FromAscii( (const char*)cleanName );
|
||||||
const wxString subDirPath = dirPath + L"/" + subDirName;
|
const wxString subDirPath = dirPath + L"/" + subDirName;
|
||||||
|
|
||||||
// if this directory has nonstandard metadata, write that to the file system
|
if ( m_performFileWrites ) {
|
||||||
wxFileName metaFileName( m_folderName.GetFullPath() + subDirPath + L"/_pcsx2_meta_directory" );
|
// if this directory has nonstandard metadata, write that to the file system
|
||||||
if ( filenameCleaned || entry->entry.data.mode != MemoryCardFileEntry::DefaultDirMode || entry->entry.data.attr != 0 ) {
|
wxFileName metaFileName( m_folderName.GetFullPath() + subDirPath + L"/_pcsx2_meta_directory" );
|
||||||
if ( !metaFileName.DirExists() ) {
|
if ( filenameCleaned || entry->entry.data.mode != MemoryCardFileEntry::DefaultDirMode || entry->entry.data.attr != 0 ) {
|
||||||
metaFileName.Mkdir();
|
if ( !metaFileName.DirExists() ) {
|
||||||
}
|
metaFileName.Mkdir();
|
||||||
wxFFile metaFile( metaFileName.GetFullPath(), L"wb" );
|
}
|
||||||
if ( metaFile.IsOpened() ) {
|
wxFFile metaFile( metaFileName.GetFullPath(), L"wb" );
|
||||||
metaFile.Write( entry->entry.raw, sizeof( entry->entry.raw ) );
|
if ( metaFile.IsOpened() ) {
|
||||||
metaFile.Close();
|
metaFile.Write( entry->entry.raw, sizeof( entry->entry.raw ) );
|
||||||
}
|
metaFile.Close();
|
||||||
} else {
|
}
|
||||||
// if metadata is standard make sure to remove a possibly existing metadata file
|
} else {
|
||||||
if ( metaFileName.FileExists() ) {
|
// if metadata is standard make sure to remove a possibly existing metadata file
|
||||||
wxRemoveFile( metaFileName.GetFullPath() );
|
if ( metaFileName.FileExists() ) {
|
||||||
|
wxRemoveFile( metaFileName.GetFullPath() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,33 +1109,36 @@ bool FolderMemoryCard::WriteToFile( const u8* src, u32 adr, u32 dataLength ) {
|
||||||
if ( it != m_fileMetadataQuickAccess.end() ) {
|
if ( it != m_fileMetadataQuickAccess.end() ) {
|
||||||
const MemoryCardFileEntry* const entry = it->second.entry;
|
const MemoryCardFileEntry* const entry = it->second.entry;
|
||||||
const u32 clusterNumber = it->second.consecutiveCluster;
|
const u32 clusterNumber = it->second.consecutiveCluster;
|
||||||
wxFFile* file = m_lastAccessedFile.ReOpen( m_folderName, &it->second, L"r+b", true );
|
|
||||||
if ( file->IsOpened() ) {
|
if ( m_performFileWrites ) {
|
||||||
const u32 clusterOffset = ( page % 2 ) * PageSize + offset;
|
wxFFile* file = m_lastAccessedFile.ReOpen( m_folderName, &it->second, L"r+b", true );
|
||||||
const u32 fileSize = entry->entry.data.length;
|
if ( file->IsOpened() ) {
|
||||||
const u32 fileOffsetStart = std::min( clusterNumber * ClusterSize + clusterOffset, fileSize );
|
const u32 clusterOffset = ( page % 2 ) * PageSize + offset;
|
||||||
const u32 fileOffsetEnd = std::min( fileOffsetStart + dataLength, fileSize );
|
const u32 fileSize = entry->entry.data.length;
|
||||||
const u32 bytesToWrite = fileOffsetEnd - fileOffsetStart;
|
const u32 fileOffsetStart = std::min( clusterNumber * ClusterSize + clusterOffset, fileSize );
|
||||||
|
const u32 fileOffsetEnd = std::min( fileOffsetStart + dataLength, fileSize );
|
||||||
|
const u32 bytesToWrite = fileOffsetEnd - fileOffsetStart;
|
||||||
|
|
||||||
wxFileOffset actualFileSize = file->Length();
|
wxFileOffset actualFileSize = file->Length();
|
||||||
if ( actualFileSize < fileOffsetStart ) {
|
if ( actualFileSize < fileOffsetStart ) {
|
||||||
file->Seek( actualFileSize );
|
file->Seek( actualFileSize );
|
||||||
const u32 diff = fileOffsetStart - actualFileSize;
|
const u32 diff = fileOffsetStart - actualFileSize;
|
||||||
u8 temp = 0xFF;
|
u8 temp = 0xFF;
|
||||||
for ( u32 i = 0; i < diff; ++i ) {
|
for ( u32 i = 0; i < diff; ++i ) {
|
||||||
file->Write( &temp, 1 );
|
file->Write( &temp, 1 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const wxFileOffset fileOffset = file->Tell();
|
const wxFileOffset fileOffset = file->Tell();
|
||||||
if ( fileOffset != fileOffsetStart ) {
|
if ( fileOffset != fileOffsetStart ) {
|
||||||
file->Seek( fileOffsetStart );
|
file->Seek( fileOffsetStart );
|
||||||
|
}
|
||||||
|
if ( bytesToWrite > 0 ) {
|
||||||
|
file->Write( src, bytesToWrite );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if ( bytesToWrite > 0 ) {
|
|
||||||
file->Write( src, bytesToWrite );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -283,6 +283,9 @@ protected:
|
||||||
|
|
||||||
bool m_isEnabled;
|
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:
|
public:
|
||||||
FolderMemoryCard();
|
FolderMemoryCard();
|
||||||
virtual ~FolderMemoryCard() throw() {}
|
virtual ~FolderMemoryCard() throw() {}
|
||||||
|
@ -293,7 +296,7 @@ public:
|
||||||
// Initialize & Load Memory Card with values configured in the Memory Card Manager
|
// Initialize & Load Memory Card with values configured in the Memory Card Manager
|
||||||
void Open( const bool enableFiltering, const wxString& filter );
|
void Open( const bool enableFiltering, const wxString& filter );
|
||||||
// Initialize & Load Memory Card with provided custom values
|
// 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 );
|
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.
|
// Close the memory card and flush changes to the file system. Set flush to false to not store changes.
|
||||||
void Close( bool flush = true );
|
void Close( bool flush = true );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue