Folder memcards: Write metadata for directories

This commit is contained in:
Silent 2020-11-22 00:17:02 +01:00 committed by lightningterror
parent a6cec6a04e
commit f8f783737d
2 changed files with 54 additions and 30 deletions

View File

@ -345,9 +345,8 @@ bool FolderMemoryCard::AddFolder( MemoryCardFileEntry* const dirEntry, const wxS
for ( const auto& file : GetOrderedFiles( dirPath ) ) { for ( const auto& file : GetOrderedFiles( dirPath ) ) {
wxFileName fileInfo( dirPath, file.m_fileName ); wxFileName fileInfo( dirPath, file.m_fileName );
bool isFile = wxFile::Exists( fileInfo.GetFullPath() );
if ( isFile ) { if ( file.m_isFile ) {
// don't load files in the root dir if we're filtering; no official software stores files there // don't load files in the root dir if we're filtering; no official software stores files there
if ( enableFiltering && parent == nullptr ) { if ( enableFiltering && parent == nullptr ) {
continue; continue;
@ -371,11 +370,9 @@ bool FolderMemoryCard::AddFolder( MemoryCardFileEntry* const dirEntry, const wxS
} }
// is a subdirectory // is a subdirectory
wxDateTime creationTime, modificationTime;
fileInfo.AppendDir( fileInfo.GetFullName() ); fileInfo.AppendDir( fileInfo.GetFullName() );
fileInfo.SetName( L"" ); fileInfo.SetName( L"" );
fileInfo.ClearExt(); fileInfo.ClearExt();
fileInfo.GetTimes( NULL, &modificationTime, &creationTime );
// add entry for subdir in parent dir // add entry for subdir in parent dir
MemoryCardFileEntry* newDirEntry = AppendFileEntryToDir( dirEntry ); MemoryCardFileEntry* newDirEntry = AppendFileEntryToDir( dirEntry );
@ -393,8 +390,8 @@ bool FolderMemoryCard::AddFolder( MemoryCardFileEntry* const dirEntry, const wxS
} }
} else { } else {
newDirEntry->entry.data.mode = MemoryCardFileEntry::DefaultDirMode; newDirEntry->entry.data.mode = MemoryCardFileEntry::DefaultDirMode;
newDirEntry->entry.data.timeCreated = MemoryCardFileEntryDateTime::FromWxDateTime( creationTime ); newDirEntry->entry.data.timeCreated = MemoryCardFileEntryDateTime::FromTime( file.m_timeCreated );
newDirEntry->entry.data.timeModified = MemoryCardFileEntryDateTime::FromWxDateTime( modificationTime ); newDirEntry->entry.data.timeModified = MemoryCardFileEntryDateTime::FromTime( file.m_timeModified );
strcpy( reinterpret_cast<char*>(newDirEntry->entry.data.name), file.m_fileName.mbc_str() ); strcpy( reinterpret_cast<char*>(newDirEntry->entry.data.name), file.m_fileName.mbc_str() );
} }
@ -998,11 +995,12 @@ void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remaini
if ( m_performFileWrites ) { if ( m_performFileWrites ) {
// if this directory has nonstandard metadata, write that to the file system // if this directory has nonstandard metadata, write that to the file system
wxFileName metaFileName( m_folderName.GetFullPath() + subDirPath + L"/_pcsx2_meta_directory" ); wxFileName metaFileName( m_folderName.GetFullPath() + subDirPath, L"_pcsx2_meta_directory" );
if ( !metaFileName.DirExists() ) {
metaFileName.Mkdir();
}
if ( filenameCleaned || entry->entry.data.mode != MemoryCardFileEntry::DefaultDirMode || entry->entry.data.attr != 0 ) { if ( filenameCleaned || entry->entry.data.mode != MemoryCardFileEntry::DefaultDirMode || entry->entry.data.attr != 0 ) {
if ( !metaFileName.DirExists() ) {
metaFileName.Mkdir();
}
wxFFile metaFile( metaFileName.GetFullPath(), L"wb" ); wxFFile metaFile( metaFileName.GetFullPath(), L"wb" );
if ( metaFile.IsOpened() ) { if ( metaFile.IsOpened() ) {
metaFile.Write( entry->entry.raw, sizeof( entry->entry.raw ) ); metaFile.Write( entry->entry.raw, sizeof( entry->entry.raw ) );
@ -1013,6 +1011,20 @@ void FolderMemoryCard::FlushFileEntries( const u32 dirCluster, const u32 remaini
wxRemoveFile( metaFileName.GetFullPath() ); wxRemoveFile( metaFileName.GetFullPath() );
} }
} }
// write the directory index
metaFileName.SetName( L"_pcsx2_index" );
YAML::Node index = LoadYAMLFromFile( metaFileName.GetFullPath() );
YAML::Node entryNode = index[ "%ROOT" ];
entryNode["timeCreated"] = entry->entry.data.timeCreated.ToTime();
entryNode["timeModified"] = entry->entry.data.timeModified.ToTime();
// Write out the changes
wxFFile indexFile;
if ( indexFile.Open( metaFileName.GetFullPath(), L"w" ) ) {
indexFile.Write( YAML::Dump( index ) );
}
} }
MemoryCardFileMetadataReference* dirRef = AddDirEntryToMetadataQuickAccess( entry, parent ); MemoryCardFileMetadataReference* dirRef = AddDirEntryToMetadataQuickAccess( entry, parent );
@ -1335,14 +1347,17 @@ std::vector<FolderMemoryCard::EnumeratedFileEntry> FolderMemoryCard::GetOrderedF
wxDir dir( dirPath ); wxDir dir( dirPath );
if ( dir.IsOpened() ) { if ( dir.IsOpened() ) {
YAML::Node index = LoadYAMLFromFile( wxFileName( dirPath, "_pcsx2_index" ).GetFullPath() ); const YAML::Node index = LoadYAMLFromFile( wxFileName( dirPath, "_pcsx2_index" ).GetFullPath() );
// We must be able to support legacy folder memcards without the index file, so for those // We must be able to support legacy folder memcards without the index file, so for those
// track an order variable and make it negative - this way new files get their order preserved // track an order variable and make it negative - this way new files get their order preserved
// and old files are listed first. // and old files are listed first.
// In the YAML File order is stored as an unsigned int, so use a signed int64_t to accommodate for // In the YAML File order is stored as an unsigned int, so use a signed int64_t to accommodate for
// all possible values without cutting them off // all possible values without cutting them off
std::map<int64_t, EnumeratedFileEntry> sortContainer; // Also exploit the fact pairs sort lexicographically to ensure directories are listed first
// (since they don't carry their own order in the index file)
std::map< std::pair<bool, int64_t>, EnumeratedFileEntry > sortContainer;
int64_t orderForDirectories = 1;
int64_t orderForLegacyFiles = -1; int64_t orderForLegacyFiles = -1;
wxString fileName; wxString fileName;
@ -1353,26 +1368,34 @@ std::vector<FolderMemoryCard::EnumeratedFileEntry> FolderMemoryCard::GetOrderedF
continue; continue;
} }
const wxFileName fileInfo( dirPath, fileName ); wxFileName fileInfo( dirPath, fileName );
try { if ( wxFile::Exists( fileInfo.GetFullPath() ) ) {
if ( wxFile::Exists( fileInfo.GetFullPath() ) ) {
const YAML::Node& node = index[ fileName.ToStdString() ];
EnumeratedFileEntry entry { fileName, node["timeCreated"].as<time_t>(), node["timeModified"].as<time_t>() };
sortContainer.try_emplace( node["order"].as<unsigned int>(), std::move(entry) );
}
else {
// TODO: Implement directories, for now force it to use the fallback implementation
throw YAML::InvalidNode( fileName.ToStdString() );
}
}
catch ( YAML::Exception& /*e*/ ) {
// File doesn't exist in index or it's corrupted - fall back to file-based timestamps and a custom order
wxDateTime creationTime, modificationTime; wxDateTime creationTime, modificationTime;
fileInfo.GetTimes( nullptr, &modificationTime, &creationTime ); fileInfo.GetTimes( nullptr, &modificationTime, &creationTime );
EnumeratedFileEntry entry { fileName, creationTime.GetTicks(), modificationTime.GetTicks() }; const YAML::Node& node = index[ fileName.ToStdString() ];
sortContainer.try_emplace( orderForLegacyFiles--, std::move(entry) );
// orderForLegacyFiles will decrement even if it ends up being unused, but that's fine
auto key = std::make_pair( true, node["order"].as<unsigned int>( orderForLegacyFiles-- ) );
EnumeratedFileEntry entry { fileName, node["timeCreated"].as<time_t>( creationTime.GetTicks() ),
node["timeModified"].as<time_t>( modificationTime.GetTicks() ), true };
sortContainer.try_emplace( std::move(key), std::move(entry) );
}
else {
fileInfo.AppendDir( fileInfo.GetFullName() );
fileInfo.SetName( L"" );
wxDateTime creationTime, modificationTime;
fileInfo.GetTimes( nullptr, &modificationTime, &creationTime );
const YAML::Node indexForDirectory = LoadYAMLFromFile( wxFileName( fileInfo.GetFullPath(), "_pcsx2_index" ).GetFullPath() );
const YAML::Node& node = indexForDirectory[ "%ROOT" ];
// orderForDirectories will increment even if it ends up being unused, but that's fine
auto key = std::make_pair( false, orderForDirectories++ );
EnumeratedFileEntry entry { fileName, node["timeCreated"].as<time_t>( creationTime.GetTicks() ),
node["timeModified"].as<time_t>( modificationTime.GetTicks() ), false };
sortContainer.try_emplace( std::move(key), std::move(entry) );
} }
hasNext = dir.GetNext( &fileName ); hasNext = dir.GetNext( &fileName );
@ -1542,7 +1565,7 @@ void FileAccessHelper::WriteIndex( wxFileName folderName, const MemoryCardFileMe
// Newly added file - figure out the sort order as the entry should be added to the end of the list // Newly added file - figure out the sort order as the entry should be added to the end of the list
unsigned int order = 0; unsigned int order = 0;
for ( const auto& node : index ) { for ( const auto& node : index ) {
order = std::max( order, node.second["order"].as<unsigned int>() ); order = std::max( order, node.second["order"].as<unsigned int>(0) );
} }
entryNode["order"] = order + 1; entryNode["order"] = order + 1;

View File

@ -390,6 +390,7 @@ protected:
wxString m_fileName; // TODO: Replace with std::string wxString m_fileName; // TODO: Replace with std::string
time_t m_timeCreated; time_t m_timeCreated;
time_t m_timeModified; time_t m_timeModified;
bool m_isFile;
}; };
// initializes memory card data, as if it was fresh from the factory // initializes memory card data, as if it was fresh from the factory