mirror of https://github.com/PCSX2/pcsx2.git
FolderMemoryCard: Add support for deleting of files/folders.
We're not actually deleting files though, we just rename them to prepend _pcsx2_deleted_, in case something breaks or whatever, so the user can in an emergency just restore the save by removing that part of the filename.
This commit is contained in:
parent
798ec3eb9c
commit
8e92d25b75
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include "svnrev.h"
|
||||
|
||||
bool RemoveDirectory( const wxString& dirname );
|
||||
|
||||
FolderMemoryCard::FolderMemoryCard() {
|
||||
m_slot = 0;
|
||||
m_isEnabled = false;
|
||||
|
@ -805,16 +807,22 @@ s32 FolderMemoryCard::Save( const u8 *src, u32 adr, int size ) {
|
|||
void FolderMemoryCard::NextFrame() {
|
||||
if ( m_framesUntilFlush > 0 && --m_framesUntilFlush == 0 ) {
|
||||
Flush();
|
||||
m_lastAccessedFile.Close();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMemoryCard::Flush() {
|
||||
m_lastAccessedFile.Close();
|
||||
if ( m_cache.empty() ) { return; }
|
||||
|
||||
Console.WriteLn( L"(FolderMcd) Writing data for slot %u to file system...", m_slot );
|
||||
const u64 timeFlushStart = wxGetLocalTimeMillis().GetValue();
|
||||
|
||||
// Keep a copy of the old file entries so we can figure out which files and directories, if any, have been deleted from the memory card.
|
||||
std::vector<MemoryCardFileEntryTreeNode> oldFileEntryTree;
|
||||
if ( IsFormatted() ) {
|
||||
CopyEntryDictIntoTree( &oldFileEntryTree, m_superBlock.data.rootdir_cluster, m_fileEntryDict[m_superBlock.data.rootdir_cluster].entries[0].entry.data.length );
|
||||
}
|
||||
|
||||
// first write the superblock if necessary
|
||||
FlushBlock( 0 );
|
||||
if ( !IsFormatted() ) { return; }
|
||||
|
@ -851,6 +859,9 @@ void FolderMemoryCard::Flush() {
|
|||
// then all directory and file entries
|
||||
FlushFileEntries();
|
||||
|
||||
// Now we have the new file system, compare it to the old one and "delete" any files that were in it before but aren't anymore.
|
||||
FlushDeletedFiles( oldFileEntryTree );
|
||||
|
||||
// and finally, flush everything that hasn't been flushed yet
|
||||
for ( uint i = 0; i < pageCount; ++i ) {
|
||||
FlushPage( i );
|
||||
|
@ -944,6 +955,46 @@ void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remaini
|
|||
}
|
||||
}
|
||||
|
||||
void FolderMemoryCard::FlushDeletedFiles( const std::vector<MemoryCardFileEntryTreeNode>& oldFileEntries ) {
|
||||
const u32 newRootDirCluster = m_superBlock.data.rootdir_cluster;
|
||||
const u32 newFileCount = m_fileEntryDict[newRootDirCluster].entries[0].entry.data.length;
|
||||
wxString path = L"";
|
||||
FlushDeletedFiles( oldFileEntries, newRootDirCluster, newFileCount, path );
|
||||
}
|
||||
|
||||
void FolderMemoryCard::FlushDeletedFiles( const std::vector<MemoryCardFileEntryTreeNode>& oldFileEntries, const u32 newCluster, const u32 newFileCount, const wxString& dirPath ) {
|
||||
// go through all file entires of the current directory of the old data
|
||||
for ( auto it = oldFileEntries.cbegin(); it != oldFileEntries.cend(); ++it ) {
|
||||
const MemoryCardFileEntry* entry = &it->entry;
|
||||
if ( entry->IsValid() && entry->IsUsed() && !entry->IsDotDir() ) {
|
||||
// check if an equivalent entry exists in m_fileEntryDict
|
||||
const MemoryCardFileEntry* newEntry = FindEquivalent( entry, newCluster, newFileCount );
|
||||
if ( newEntry == nullptr ) {
|
||||
// file/dir doesn't exist anymore, remove!
|
||||
char cleanName[sizeof( entry->entry.data.name )];
|
||||
memcpy( cleanName, (const char*)entry->entry.data.name, sizeof( cleanName ) );
|
||||
FileAccessHelper::CleanMemcardFilename( cleanName );
|
||||
const wxString fileName = wxString::FromAscii( cleanName );
|
||||
const wxString filePath = m_folderName.GetFullPath() + dirPath + L"/" + fileName;
|
||||
const wxString newFilePath = m_folderName.GetFullPath() + dirPath + L"/_pcsx2_deleted_" + fileName;
|
||||
if ( wxFileName::DirExists( newFilePath ) ) {
|
||||
// wxRenameFile doesn't overwrite directories, so we have to remove the old one first
|
||||
RemoveDirectory( newFilePath );
|
||||
}
|
||||
wxRenameFile( filePath, newFilePath );
|
||||
} else if ( entry->IsDir() ) {
|
||||
// still exists and is a directory, recursive call for subdir
|
||||
char cleanName[sizeof( entry->entry.data.name )];
|
||||
memcpy( cleanName, (const char*)entry->entry.data.name, sizeof( cleanName ) );
|
||||
FileAccessHelper::CleanMemcardFilename( cleanName );
|
||||
const wxString subDirName = wxString::FromAscii( cleanName );
|
||||
const wxString subDirPath = dirPath + L"/" + subDirName;
|
||||
FlushDeletedFiles( it->subdir, newEntry->entry.data.cluster, newEntry->entry.data.length, subDirPath );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 FolderMemoryCard::WriteWithoutCache( const u8 *src, u32 adr, int size ) {
|
||||
const u32 block = adr / BlockSizeRaw;
|
||||
const u32 cluster = adr / ClusterSizeRaw;
|
||||
|
@ -1031,6 +1082,54 @@ bool FolderMemoryCard::WriteToFile( const u8* src, u32 adr, u32 dataLength ) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void FolderMemoryCard::CopyEntryDictIntoTree( std::vector<MemoryCardFileEntryTreeNode>* fileEntryTree, const u32 cluster, const u32 fileCount ) {
|
||||
const MemoryCardFileEntryCluster* entryCluster = &m_fileEntryDict[cluster];
|
||||
u32 fileCluster = cluster;
|
||||
|
||||
for ( size_t i = 0; i < fileCount; ++i ) {
|
||||
const MemoryCardFileEntry* entry = &entryCluster->entries[i % 2];
|
||||
|
||||
if ( entry->IsValid() && entry->IsUsed() ) {
|
||||
fileEntryTree->emplace_back( *entry );
|
||||
|
||||
if ( entry->IsDir() && !entry->IsDotDir() ) {
|
||||
MemoryCardFileEntryTreeNode* treeEntry = &fileEntryTree->back();
|
||||
CopyEntryDictIntoTree( &treeEntry->subdir, entry->entry.data.cluster, entry->entry.data.length );
|
||||
}
|
||||
}
|
||||
|
||||
if ( i % 2 == 1 ) {
|
||||
fileCluster = m_fat.data[0][0][fileCluster] & 0x7FFFFFFFu;
|
||||
if ( fileCluster == 0x7FFFFFFFu ) { return; }
|
||||
entryCluster = &m_fileEntryDict[fileCluster];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MemoryCardFileEntry* FolderMemoryCard::FindEquivalent( const MemoryCardFileEntry* searchEntry, const u32 cluster, const u32 fileCount ) {
|
||||
const MemoryCardFileEntryCluster* entryCluster = &m_fileEntryDict[cluster];
|
||||
u32 fileCluster = cluster;
|
||||
|
||||
for ( size_t i = 0; i < fileCount; ++i ) {
|
||||
const MemoryCardFileEntry* entry = &entryCluster->entries[i % 2];
|
||||
|
||||
if ( entry->IsValid() && entry->IsUsed() ) {
|
||||
if ( entry->IsFile() == searchEntry->IsFile() && entry->IsDir() == searchEntry->IsDir()
|
||||
&& strncmp( (const char*)searchEntry->entry.data.name, (const char*)entry->entry.data.name, sizeof( entry->entry.data.name ) ) == 0 ) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i % 2 == 1 ) {
|
||||
fileCluster = m_fat.data[0][0][fileCluster] & 0x7FFFFFFFu;
|
||||
if ( fileCluster == 0x7FFFFFFFu ) { return nullptr; }
|
||||
entryCluster = &m_fileEntryDict[fileCluster];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s32 FolderMemoryCard::EraseBlock( u32 adr ) {
|
||||
const u32 block = adr / BlockSizeRaw;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <wx/dir.h>
|
||||
#include <wx/ffile.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "PluginCallbacks.h"
|
||||
#include "AppConfig.h"
|
||||
|
@ -152,6 +153,13 @@ struct MemoryCardPage {
|
|||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct MemoryCardFileEntryTreeNode {
|
||||
MemoryCardFileEntry entry;
|
||||
std::vector<MemoryCardFileEntryTreeNode> subdir;
|
||||
|
||||
MemoryCardFileEntryTreeNode( const MemoryCardFileEntry& entry ) : entry(entry) {}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// MemoryCardFileMetadataReference
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -417,9 +425,24 @@ protected:
|
|||
// 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
|
||||
void FlushDeletedFiles( 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 FlushDeletedFiles( const std::vector<MemoryCardFileEntryTreeNode>& oldFileEntries, const u32 newCluster, const u32 newFileCount, const wxString& dirPath );
|
||||
|
||||
// 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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue