Merge pull request #980 from AdmiralCurtiss/ff12mcdfix

FolderMemoryCard: Fix issue #976: Corrupted FF12 saves when overwriting data.
This commit is contained in:
refractionpcsx2 2015-12-13 00:08:42 +00:00
commit d3ec74be2b
2 changed files with 77 additions and 9 deletions

View File

@ -109,8 +109,8 @@ void FolderMemoryCard::Close( bool flush ) {
m_cache.clear(); m_cache.clear();
m_oldDataCache.clear(); m_oldDataCache.clear();
m_fileMetadataQuickAccess.clear();
m_lastAccessedFile.CloseAll(); m_lastAccessedFile.CloseAll();
m_fileMetadataQuickAccess.clear();
} }
void FolderMemoryCard::LoadMemoryCardData( const u32 sizeInClusters, const bool enableFiltering, const wxString& filter ) { void FolderMemoryCard::LoadMemoryCardData( const u32 sizeInClusters, const bool enableFiltering, const wxString& filter ) {
@ -142,6 +142,10 @@ void FolderMemoryCard::LoadMemoryCardData( const u32 sizeInClusters, const bool
CreateRootDir(); CreateRootDir();
MemoryCardFileEntry* const rootDirEntry = &m_fileEntryDict[m_superBlock.data.rootdir_cluster].entries[0]; MemoryCardFileEntry* const rootDirEntry = &m_fileEntryDict[m_superBlock.data.rootdir_cluster].entries[0];
AddFolder( rootDirEntry, m_folderName.GetPath(), nullptr, enableFiltering, filter ); AddFolder( rootDirEntry, m_folderName.GetPath(), nullptr, enableFiltering, filter );
#ifdef DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE
WriteToFile( m_folderName.GetFullPath().RemoveLast() + L"-debug_" + wxDateTime::Now().Format( L"%Y-%m-%d-%H-%M-%S" ) + L"_load.ps2" );
#endif
} }
} }
@ -842,6 +846,10 @@ void FolderMemoryCard::NextFrame() {
void FolderMemoryCard::Flush() { void FolderMemoryCard::Flush() {
if ( m_cache.empty() ) { return; } if ( m_cache.empty() ) { return; }
#ifdef DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE
WriteToFile( m_folderName.GetFullPath().RemoveLast() + L"-debug_" + wxDateTime::Now().Format( L"%Y-%m-%d-%H-%M-%S" ) + L"_pre-flush.ps2" );
#endif
Console.WriteLn( L"(FolderMcd) Writing data for slot %u to file system...", m_slot ); Console.WriteLn( L"(FolderMcd) Writing data for slot %u to file system...", m_slot );
const u64 timeFlushStart = wxGetLocalTimeMillis().GetValue(); const u64 timeFlushStart = wxGetLocalTimeMillis().GetValue();
@ -896,10 +904,15 @@ void FolderMemoryCard::Flush() {
} }
m_lastAccessedFile.FlushAll(); m_lastAccessedFile.FlushAll();
m_lastAccessedFile.ClearMetadataWriteState();
m_oldDataCache.clear(); m_oldDataCache.clear();
const u64 timeFlushEnd = wxGetLocalTimeMillis().GetValue(); const u64 timeFlushEnd = wxGetLocalTimeMillis().GetValue();
Console.WriteLn( L"(FolderMcd) Done! Took %u ms.", timeFlushEnd - timeFlushStart ); Console.WriteLn( L"(FolderMcd) Done! Took %u ms.", timeFlushEnd - timeFlushStart );
#ifdef DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE
WriteToFile( m_folderName.GetFullPath().RemoveLast() + L"-debug_" + wxDateTime::Now().Format( L"%Y-%m-%d-%H-%M-%S" ) + L"_post-flush.ps2" );
#endif
} }
bool FolderMemoryCard::FlushPage( const u32 page ) { bool FolderMemoryCard::FlushPage( const u32 page ) {
@ -1344,6 +1357,20 @@ void FolderMemoryCard::CalculateECC( u8* ecc, const u8* data ) {
return; return;
} }
void FolderMemoryCard::WriteToFile( const wxString& filename ) {
wxFFile targetFile( filename, L"wb" );
u8 buffer[FolderMemoryCard::PageSizeRaw];
u32 adr = 0;
while ( adr < GetSizeInClusters() * FolderMemoryCard::ClusterSizeRaw ) {
Read( buffer, adr, FolderMemoryCard::PageSizeRaw );
targetFile.Write( buffer, FolderMemoryCard::PageSizeRaw );
adr += FolderMemoryCard::PageSizeRaw;
}
targetFile.Close();
}
FileAccessHelper::FileAccessHelper() { FileAccessHelper::FileAccessHelper() {
m_files.clear(); m_files.clear();
@ -1370,7 +1397,13 @@ wxFFile* FileAccessHelper::Open( const wxFileName& folderName, MemoryCardFileMet
const MemoryCardFileEntry* const entry = fileRef->entry; const MemoryCardFileEntry* const entry = fileRef->entry;
wxFFile* file = new wxFFile( filename, L"r+b" ); wxFFile* file = new wxFFile( filename, L"r+b" );
m_files.emplace( entry, file );
std::string internalPath;
fileRef->GetInternalPath( &internalPath );
MemoryCardFileHandleStructure handleStruct;
handleStruct.fileHandle = file;
handleStruct.fileRef = fileRef;
m_files.emplace( internalPath, handleStruct );
if ( writeMetadata ) { if ( writeMetadata ) {
fn.AppendDir( L"_pcsx2_meta" ); fn.AppendDir( L"_pcsx2_meta" );
@ -1418,7 +1451,9 @@ void FileAccessHelper::WriteMetadata( bool metadataIsNonstandard, wxFileName& me
} }
wxFFile* FileAccessHelper::ReOpen( const wxFileName& folderName, MemoryCardFileMetadataReference* fileRef, bool writeMetadata ) { wxFFile* FileAccessHelper::ReOpen( const wxFileName& folderName, MemoryCardFileMetadataReference* fileRef, bool writeMetadata ) {
auto it = m_files.find( fileRef->entry ); std::string internalPath;
fileRef->GetInternalPath( &internalPath );
auto it = m_files.find( internalPath );
if ( it != m_files.end() ) { if ( it != m_files.end() ) {
// we already have a handle to this file // we already have a handle to this file
@ -1434,7 +1469,10 @@ wxFFile* FileAccessHelper::ReOpen( const wxFileName& folderName, MemoryCardFileM
} }
} }
return it->second; // update the fileRef in the map since it might have been modified or deleted
it->second.fileRef = fileRef;
return it->second.fileHandle;
} else { } else {
return this->Open( folderName, fileRef, writeMetadata ); return this->Open( folderName, fileRef, writeMetadata );
} }
@ -1458,9 +1496,9 @@ void FileAccessHelper::CloseMatching( const wxString& path ) {
fn.Normalize(); fn.Normalize();
wxString pathNormalized = fn.GetFullPath(); wxString pathNormalized = fn.GetFullPath();
for ( auto it = m_files.begin(); it != m_files.end(); ) { for ( auto it = m_files.begin(); it != m_files.end(); ) {
wxString openPath = it->second->GetName(); wxString openPath = it->second.fileHandle->GetName();
if ( openPath.StartsWith( pathNormalized ) ) { if ( openPath.StartsWith( pathNormalized ) ) {
CloseFileHandle( it->second, it->first ); CloseFileHandle( it->second.fileHandle, it->second.fileRef->entry );
it = m_files.erase( it ); it = m_files.erase( it );
} else { } else {
++it; ++it;
@ -1470,17 +1508,21 @@ void FileAccessHelper::CloseMatching( const wxString& path ) {
void FileAccessHelper::CloseAll() { void FileAccessHelper::CloseAll() {
for ( auto it = m_files.begin(); it != m_files.end(); ++it ) { for ( auto it = m_files.begin(); it != m_files.end(); ++it ) {
CloseFileHandle( it->second, it->first ); CloseFileHandle( it->second.fileHandle, it->second.fileRef->entry );
} }
m_files.clear(); m_files.clear();
} }
void FileAccessHelper::FlushAll() { void FileAccessHelper::FlushAll() {
for ( auto it = m_files.begin(); it != m_files.end(); ++it ) { for ( auto it = m_files.begin(); it != m_files.end(); ++it ) {
it->second->Flush(); it->second.fileHandle->Flush();
} }
} }
void FileAccessHelper::ClearMetadataWriteState() {
m_lastWrittenFileRef = nullptr;
}
bool FileAccessHelper::CleanMemcardFilename( char* name ) { bool FileAccessHelper::CleanMemcardFilename( char* name ) {
// invalid characters for filenames in the PS2 file system: { '/', '?', '*' } // invalid characters for filenames in the PS2 file system: { '/', '?', '*' }
// the following characters are valid in a PS2 memcard file system but invalid in Windows // the following characters are valid in a PS2 memcard file system but invalid in Windows
@ -1521,6 +1563,17 @@ bool MemoryCardFileMetadataReference::GetPath( wxFileName* fileName ) const {
return parentCleaned || localCleaned; return parentCleaned || localCleaned;
} }
void MemoryCardFileMetadataReference::GetInternalPath( std::string* fileName ) const {
if ( parent ) {
parent->GetInternalPath( fileName );
}
fileName->append( (const char*)entry->entry.data.name );
if ( entry->IsDir() ) {
fileName->append( "/" );
}
}
FolderMemoryCardAggregator::FolderMemoryCardAggregator() { FolderMemoryCardAggregator::FolderMemoryCardAggregator() {
for ( uint i = 0; i < TotalCardSlots; ++i ) { for ( uint i = 0; i < TotalCardSlots; ++i ) {

View File

@ -24,6 +24,8 @@
#include "PluginCallbacks.h" #include "PluginCallbacks.h"
#include "AppConfig.h" #include "AppConfig.h"
//#define DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Superblock Header Struct // Superblock Header Struct
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -195,6 +197,14 @@ struct MemoryCardFileMetadataReference {
// returns true if filename was modified and metadata containing the actual filename should be written // returns true if filename was modified and metadata containing the actual filename should be written
bool GetPath( wxFileName* fileName ) const; bool GetPath( wxFileName* fileName ) const;
// gives the internal memory card file system path, not to be used for writes to the host file system
void GetInternalPath( std::string* fileName ) const;
};
struct MemoryCardFileHandleStructure {
MemoryCardFileMetadataReference* fileRef;
wxFFile* fileHandle;
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -203,7 +213,7 @@ struct MemoryCardFileMetadataReference {
// Small helper class to keep memory card files opened between calls to Read()/Save() // Small helper class to keep memory card files opened between calls to Read()/Save()
class FileAccessHelper { class FileAccessHelper {
protected: protected:
std::map<const MemoryCardFileEntry* const, wxFFile*> m_files; std::map<std::string, MemoryCardFileHandleStructure> m_files;
MemoryCardFileMetadataReference* m_lastWrittenFileRef; // we remember this to reduce redundant metadata checks/writes MemoryCardFileMetadataReference* m_lastWrittenFileRef; // we remember this to reduce redundant metadata checks/writes
public: public:
@ -219,6 +229,9 @@ public:
// Flush the written data of all open files to the file system // Flush the written data of all open files to the file system
void FlushAll(); void FlushAll();
// Force metadata to be written on next file access, not sure if this is necessary but it can't hurt.
void ClearMetadataWriteState();
// removes characters from a PS2 file name that would be illegal in a Windows file system // removes characters from a PS2 file name that would be illegal in a Windows file system
// returns true if any changes were made // returns true if any changes were made
static bool CleanMemcardFilename( char* name ); static bool CleanMemcardFilename( char* name );
@ -348,6 +361,8 @@ public:
static void CalculateECC( u8* ecc, const u8* data ); static void CalculateECC( u8* ecc, const u8* data );
void WriteToFile( const wxString& filename );
protected: protected:
// initializes memory card data, as if it was fresh from the factory // initializes memory card data, as if it was fresh from the factory
void InitializeInternalData(); void InitializeInternalData();