From d331d59a9f5aaa5e27435656f799cff162ba088c Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Mon, 20 Jul 2015 23:53:53 +0200 Subject: [PATCH] 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. --- pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp | 26 ++++-- pcsx2/gui/MemoryCardFolder.cpp | 91 ++++++++++--------- pcsx2/gui/MemoryCardFolder.h | 5 +- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp b/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp index e36641e236..acf13bb82c 100644 --- a/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp +++ b/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp @@ -201,19 +201,29 @@ bool Dialogs::ConvertMemoryCardDialog::ConvertToFolder( const wxFileName& source AppConfig::McdOptions config; config.Enabled = true; config.Type = MemoryCardType::MemoryCard_Folder; - targetFolderMemoryCard.Open( targetPath.GetFullPath(), config, 0, false, L"" ); - u32 adr = 0; - while ( !sourceFile.Eof() ) { - int size = sourceFile.Read( buffer, FolderMemoryCard::PageSizeRaw ); - if ( size > 0 ) { - targetFolderMemoryCard.Save( buffer, adr, size ); - adr += size; + + 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(); - targetFolderMemoryCard.Close(); if ( adr != FolderMemoryCard::TotalSizeRaw ) { // reset memory card metrics in superblock to the default 8MB, since the converted card was different diff --git a/pcsx2/gui/MemoryCardFolder.cpp b/pcsx2/gui/MemoryCardFolder.cpp index 0266b70fad..5b2c245ba9 100644 --- a/pcsx2/gui/MemoryCardFolder.cpp +++ b/pcsx2/gui/MemoryCardFolder.cpp @@ -44,6 +44,7 @@ void FolderMemoryCard::InitializeInternalData() { m_isEnabled = false; m_framesUntilFlush = 0; m_lastAccessedFile.Close(); + m_performFileWrites = true; } bool FolderMemoryCard::IsFormatted() const { @@ -52,11 +53,12 @@ bool FolderMemoryCard::IsFormatted() const { } 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(); + m_performFileWrites = !simulateFileWrites; wxFileName configuredFileName( fullPath ); 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 ( !disabled && !m_folderName.DirExists() ) { + if ( !disabled && m_performFileWrites && !m_folderName.DirExists() ) { if ( !m_folderName.Mkdir() ) { str = L"[couldn't create folder]"; disabled = true; @@ -910,7 +912,7 @@ bool FolderMemoryCard::FlushBlock( const u32 block ) { } void FolderMemoryCard::FlushSuperBlock() { - if ( FlushBlock( 0 ) ) { + if ( FlushBlock( 0 ) && m_performFileWrites ) { wxFileName superBlockFileName( m_folderName.GetPath(), L"_pcsx2_superblock" ); wxFFile superBlockFile( superBlockFileName.GetFullPath().c_str(), L"wb" ); 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 subDirPath = dirPath + L"/" + subDirName; - // if this directory has nonstandard metadata, write that to the file system - wxFileName metaFileName( m_folderName.GetFullPath() + subDirPath + L"/_pcsx2_meta_directory" ); - if ( filenameCleaned || entry->entry.data.mode != MemoryCardFileEntry::DefaultDirMode || entry->entry.data.attr != 0 ) { - if ( !metaFileName.DirExists() ) { - metaFileName.Mkdir(); - } - wxFFile metaFile( metaFileName.GetFullPath(), L"wb" ); - if ( metaFile.IsOpened() ) { - 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 - if ( metaFileName.FileExists() ) { - wxRemoveFile( metaFileName.GetFullPath() ); + if ( m_performFileWrites ) { + // if this directory has nonstandard metadata, write that to the file system + wxFileName metaFileName( m_folderName.GetFullPath() + subDirPath + L"/_pcsx2_meta_directory" ); + if ( filenameCleaned || entry->entry.data.mode != MemoryCardFileEntry::DefaultDirMode || entry->entry.data.attr != 0 ) { + if ( !metaFileName.DirExists() ) { + metaFileName.Mkdir(); + } + wxFFile metaFile( metaFileName.GetFullPath(), L"wb" ); + if ( metaFile.IsOpened() ) { + 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 + 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() ) { const MemoryCardFileEntry* const entry = it->second.entry; const u32 clusterNumber = it->second.consecutiveCluster; - wxFFile* file = m_lastAccessedFile.ReOpen( m_folderName, &it->second, L"r+b", true ); - if ( file->IsOpened() ) { - const u32 clusterOffset = ( page % 2 ) * PageSize + offset; - const u32 fileSize = entry->entry.data.length; - const u32 fileOffsetStart = std::min( clusterNumber * ClusterSize + clusterOffset, fileSize ); - const u32 fileOffsetEnd = std::min( fileOffsetStart + dataLength, fileSize ); - const u32 bytesToWrite = fileOffsetEnd - fileOffsetStart; + + if ( m_performFileWrites ) { + wxFFile* file = m_lastAccessedFile.ReOpen( m_folderName, &it->second, L"r+b", true ); + if ( file->IsOpened() ) { + const u32 clusterOffset = ( page % 2 ) * PageSize + offset; + const u32 fileSize = entry->entry.data.length; + 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(); - if ( actualFileSize < fileOffsetStart ) { - file->Seek( actualFileSize ); - const u32 diff = fileOffsetStart - actualFileSize; - u8 temp = 0xFF; - for ( u32 i = 0; i < diff; ++i ) { - file->Write( &temp, 1 ); + wxFileOffset actualFileSize = file->Length(); + if ( actualFileSize < fileOffsetStart ) { + file->Seek( actualFileSize ); + const u32 diff = fileOffsetStart - actualFileSize; + u8 temp = 0xFF; + for ( u32 i = 0; i < diff; ++i ) { + file->Write( &temp, 1 ); + } } - } - const wxFileOffset fileOffset = file->Tell(); - if ( fileOffset != fileOffsetStart ) { - file->Seek( fileOffsetStart ); + const wxFileOffset fileOffset = file->Tell(); + if ( fileOffset != 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; diff --git a/pcsx2/gui/MemoryCardFolder.h b/pcsx2/gui/MemoryCardFolder.h index 028a0bcd3c..421f088dfe 100644 --- a/pcsx2/gui/MemoryCardFolder.h +++ b/pcsx2/gui/MemoryCardFolder.h @@ -283,6 +283,9 @@ protected: 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() {} @@ -293,7 +296,7 @@ public: // 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 ); + 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 );