From 139e28988d19523533e66a07b5a16c922d56974f Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 9 May 2015 03:31:30 +0200 Subject: [PATCH] FolderMemoryCard: Store nonstandard file and folder metadata. Fixes issues with Star Ocean 3 battle trophies, and probably some other games. --- pcsx2/gui/MemoryCardFolder.cpp | 107 +++++++++++++++++++++++++++------ pcsx2/gui/MemoryCardFolder.h | 5 +- 2 files changed, 93 insertions(+), 19 deletions(-) diff --git a/pcsx2/gui/MemoryCardFolder.cpp b/pcsx2/gui/MemoryCardFolder.cpp index 5ac35e92d7..aead262ee9 100644 --- a/pcsx2/gui/MemoryCardFolder.cpp +++ b/pcsx2/gui/MemoryCardFolder.cpp @@ -273,14 +273,17 @@ bool FolderMemoryCard::AddFolder( MemoryCardFileEntry* const dirEntry, const wxS int entryNumber = 2; // include . and .. hasNext = dir.GetFirst( &fileName ); while ( hasNext ) { + if ( fileName.StartsWith( L"_pcsx2_" ) ) { + hasNext = dir.GetNext( &fileName ); + continue; + } + wxFileName fileInfo( dirPath, fileName ); bool isFile = wxFile::Exists( fileInfo.GetFullPath() ); if ( isFile ) { - if ( !fileName.StartsWith( L"_pcsx2_" ) ) { - if ( AddFile( dirEntry, dirPath, fileName ) ) { - ++entryNumber; - } + if ( AddFile( dirEntry, dirPath, fileName ) ) { + ++entryNumber; } } else { // make sure we have enough space on the memcard for the directory @@ -301,10 +304,21 @@ bool FolderMemoryCard::AddFolder( MemoryCardFileEntry* const dirEntry, const wxS // add entry for subdir in parent dir MemoryCardFileEntry* newDirEntry = AppendFileEntryToDir( dirEntry ); dirEntry->entry.data.length++; - newDirEntry->entry.data.mode = 0x8427; + + // set metadata + wxFileName metaFileName( dirPath, L"_pcsx2_meta_directory" ); + metaFileName.AppendDir( fileName ); + wxFFile metaFile; + if ( metaFileName.FileExists() && metaFile.Open( metaFileName.GetFullPath(), L"rb" ) ) { + metaFile.Read( &newDirEntry->entry.raw, 0x40 ); + metaFile.Close(); + } else { + newDirEntry->entry.data.mode = MemoryCardFileEntry::DefaultDirMode; + newDirEntry->entry.data.timeCreated.value = ConvertToMemoryCardTimestamp( creationTime ); + newDirEntry->entry.data.timeModified.value = ConvertToMemoryCardTimestamp( modificationTime ); + } + newDirEntry->entry.data.length = 2; - newDirEntry->entry.data.timeCreated.value = ConvertToMemoryCardTimestamp( creationTime ); - newDirEntry->entry.data.timeModified.value = ConvertToMemoryCardTimestamp( modificationTime ); strcpy( (char*)&newDirEntry->entry.data.name[0], fileName.mbc_str() ); // create new cluster for . and .. entries @@ -314,12 +328,12 @@ bool FolderMemoryCard::AddFolder( MemoryCardFileEntry* const dirEntry, const wxS MemoryCardFileEntryCluster* const subDirCluster = &m_fileEntryDict[newCluster]; memset( &subDirCluster->entries[0].entry.raw[0], 0x00, 0x200 ); - subDirCluster->entries[0].entry.data.mode = 0x8427; + subDirCluster->entries[0].entry.data.mode = MemoryCardFileEntry::DefaultDirMode; subDirCluster->entries[0].entry.data.dirEntry = entryNumber; subDirCluster->entries[0].entry.data.name[0] = '.'; memset( &subDirCluster->entries[1].entry.raw[0], 0x00, 0x200 ); - subDirCluster->entries[1].entry.data.mode = 0x8427; + subDirCluster->entries[1].entry.data.mode = MemoryCardFileEntry::DefaultDirMode; subDirCluster->entries[1].entry.data.name[0] = '.'; subDirCluster->entries[1].entry.data.name[1] = '.'; @@ -361,12 +375,22 @@ bool FolderMemoryCard::AddFile( MemoryCardFileEntry* const dirEntry, const wxStr wxDateTime creationTime, modificationTime; fileInfo.GetTimes( NULL, &modificationTime, &creationTime ); - // set file entry data + // set file entry metadata memset( &newFileEntry->entry.raw[0], 0x00, 0x200 ); - newFileEntry->entry.data.mode = 0x8497; + + wxFileName metaFileName( dirPath, fileName ); + metaFileName.AppendDir( L"_pcsx2_meta" ); + wxFFile metaFile; + if ( metaFileName.FileExists() && metaFile.Open( metaFileName.GetFullPath(), L"rb" ) ) { + metaFile.Read( &newFileEntry->entry.raw, 0x40 ); + metaFile.Close(); + } else { + newFileEntry->entry.data.mode = MemoryCardFileEntry::DefaultFileMode; + newFileEntry->entry.data.timeCreated.value = ConvertToMemoryCardTimestamp( creationTime ); + newFileEntry->entry.data.timeModified.value = ConvertToMemoryCardTimestamp( modificationTime ); + } + newFileEntry->entry.data.length = filesize; - newFileEntry->entry.data.timeCreated.value = ConvertToMemoryCardTimestamp( creationTime ); - newFileEntry->entry.data.timeModified.value = ConvertToMemoryCardTimestamp( modificationTime ); u32 fileDataStartingCluster = GetFreeDataCluster(); newFileEntry->entry.data.cluster = fileDataStartingCluster; strcpy( (char*)&newFileEntry->entry.data.name[0], fileName.mbc_str() ); @@ -721,7 +745,7 @@ void FolderMemoryCard::Flush( const u32 page ) { } } -void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remainingFiles ) { +void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remainingFiles, const wxString& dirPath ) { // flush the current cluster const u32 page = ( dirCluster + m_superBlock.data.alloc_offset ) * 2; Flush( page ); @@ -735,7 +759,28 @@ void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remaini if ( entry->IsValid() && entry->IsUsed() && entry->IsDir() ) { const u32 cluster = entry->entry.data.cluster; if ( cluster > 0 ) { - FlushFileEntries( cluster, entry->entry.data.length ); + const wxString subDirName = wxString::FromAscii( (const char*)entry->entry.data.name ); + const wxString subDirPath = dirPath + L"/" + subDirName; + + // if this directory has nonstandard metadata, write that to the file system + wxFileName metaFileName( folderName.GetFullPath() + subDirPath + L"/_pcsx2_meta_directory" ); + if ( 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, 0x40 ); + metaFile.Close(); + } + } else { + // if metadata is standard make sure to remove a possibly existing metadata file + if ( metaFileName.FileExists() ) { + wxRemoveFile( metaFileName.GetFullPath() ); + } + } + + FlushFileEntries( cluster, entry->entry.data.length, subDirPath ); } } } @@ -743,7 +788,7 @@ void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remaini // continue to the next cluster of this directory const u32 nextCluster = m_fat.data[0][0][dirCluster]; if ( nextCluster != 0xFFFFFFFF ) { - FlushFileEntries( nextCluster & 0x7FFFFFFF, remainingFiles - 2 ); + FlushFileEntries( nextCluster & 0x7FFFFFFF, remainingFiles - 2, dirPath ); } } @@ -824,9 +869,35 @@ bool FolderMemoryCard::WriteToFile( const u8* src, u32 adr, u32 dataLength ) { } file.Close(); - - return true; + } else { + return false; } + + // separately write metadata of file if it's nonstandard + fileName.AppendDir( L"_pcsx2_meta" ); + if ( entry->entry.data.mode != MemoryCardFileEntry::DefaultFileMode || entry->entry.data.attr != 0 ) { + if ( !fileName.DirExists() ) { + fileName.Mkdir(); + } + wxFFile metaFile( fileName.GetFullPath(), L"wb" ); + if ( metaFile.IsOpened() ) { + metaFile.Write( entry->entry.raw, 0x40 ); + metaFile.Close(); + } + } else { + // if metadata is standard remove metadata file if it exists + if ( fileName.FileExists() ) { + wxRemoveFile( fileName.GetFullPath() ); + + // and remove the metadata dir if it's now empty + wxDir metaDir( fileName.GetPath() ); + if ( metaDir.IsOpened() && !metaDir.HasFiles() ) { + wxRmdir( fileName.GetPath() ); + } + } + } + + return true; } return false; diff --git a/pcsx2/gui/MemoryCardFolder.h b/pcsx2/gui/MemoryCardFolder.h index dfdc769e47..2b53b60fb5 100644 --- a/pcsx2/gui/MemoryCardFolder.h +++ b/pcsx2/gui/MemoryCardFolder.h @@ -94,6 +94,9 @@ struct MemoryCardFileEntry { bool IsDir() { return !!( entry.data.mode & 0x0020 ); } bool IsUsed() { return !!( entry.data.mode & 0x8000 ); } bool IsValid() { return entry.data.mode != 0xFFFFFFFF; } + + static const u32 DefaultDirMode = 0x8427; + static const u32 DefaultFileMode = 0x8497; }; #pragma pack(pop) @@ -269,7 +272,7 @@ protected: void Flush( const u32 page ); // flush a directory's file entries and all its subdirectories to the internal data - void FlushFileEntries( const u32 dirCluster, const u32 remainingFiles ); + void FlushFileEntries( const u32 dirCluster, const u32 remainingFiles, const wxString& dirPath = L"" ); // write data as Save() normally would, but ignore the cache; used for flushing s32 WriteWithoutCache( const u8 *src, u32 adr, int size );