diff --git a/pcsx2/MemoryCardFile.cpp b/pcsx2/MemoryCardFile.cpp index 912f22e1bf..0c0353aa14 100644 --- a/pcsx2/MemoryCardFile.cpp +++ b/pcsx2/MemoryCardFile.cpp @@ -844,6 +844,24 @@ static bool IsMemoryCardFolder(const std::string& path) return FileSystem::FileExists(superblock_path.c_str()); } +static bool IsMemoryCardFormatted(const std::string& path) +{ + auto fp = FileSystem::OpenManagedCFile(path.c_str(), "rb"); + if (!fp) + return false; + + static const char formatted_psx[] = "MC"; + static const char formatted_string[] = "Sony PS2 Memory Card Format"; + static constexpr size_t read_length = sizeof(formatted_string) - 1; + + u8 data[read_length]; + if (std::fread(data, read_length, 1, fp.get()) != 1) + return false; + + return (std::memcmp(data, formatted_string, sizeof(formatted_string) - 1) == 0 || + std::memcmp(data, formatted_psx, sizeof(formatted_psx) - 1) == 0); +} + std::vector FileMcd_GetAvailableCards(bool include_in_use_cards) { std::vector files; @@ -876,16 +894,18 @@ std::vector FileMcd_GetAvailableCards(bool include_in_use_card if (!IsMemoryCardFolder(fd.FileName)) continue; - mcds.push_back({std::move(basename), std::move(fd.FileName), MemoryCardType::Folder, - MemoryCardFileType::Unknown, 0u}); + mcds.push_back({std::move(basename), std::move(fd.FileName), fd.ModificationTime, + MemoryCardType::Folder, MemoryCardFileType::Unknown, 0u, true}); } else { if (fd.Size < MCD_SIZE) continue; - mcds.push_back({std::move(basename), std::move(fd.FileName), MemoryCardType::File, - GetMemoryCardFileTypeFromSize(fd.Size), static_cast(fd.Size)}); + const bool formatted = IsMemoryCardFormatted(fd.FileName); + mcds.push_back({std::move(basename), std::move(fd.FileName), fd.ModificationTime, + MemoryCardType::File, GetMemoryCardFileTypeFromSize(fd.Size), + static_cast(fd.Size), formatted}); } } @@ -907,16 +927,18 @@ std::optional FileMcd_GetCardInfo(const std::string_view& name { if (IsMemoryCardFolder(path)) { - ret = {std::move(basename), std::move(path), MemoryCardType::Folder, - MemoryCardFileType::Unknown, 0u}; + ret = {std::move(basename), std::move(path), sd.ModificationTime, + MemoryCardType::Folder, MemoryCardFileType::Unknown, 0u, true}; } } else { if (sd.Size >= MCD_SIZE) { - ret = {std::move(basename), std::move(path), MemoryCardType::File, - GetMemoryCardFileTypeFromSize(sd.Size), static_cast(sd.Size)}; + const bool formatted = IsMemoryCardFormatted(path); + ret = {std::move(basename), std::move(path), sd.ModificationTime, + MemoryCardType::File, GetMemoryCardFileTypeFromSize(sd.Size), + static_cast(sd.Size), formatted}; } } @@ -1011,3 +1033,62 @@ bool FileMcd_CreateNewCard(const std::string_view& name, MemoryCardType type, Me return false; } + +bool FileMcd_RenameCard(const std::string_view& name, const std::string_view& new_name) +{ + const std::string name_path(Path::CombineStdString(EmuFolders::MemoryCards, name)); + const std::string new_name_path(Path::CombineStdString(EmuFolders::MemoryCards, new_name)); + + FILESYSTEM_STAT_DATA sd, new_sd; + if (!FileSystem::StatFile(name_path.c_str(), &sd) || FileSystem::StatFile(new_name_path.c_str(), &new_sd)) + { + Console.Error("(FileMcd) New name already exists, or old name does not"); + return false; + } + + Console.WriteLn("(FileMcd) Renaming memory card '%.*s' to '%.*s'", + static_cast(name.size()), name.data(), + static_cast(new_name.size()), new_name.data()); + + if (!FileSystem::RenamePath(name_path.c_str(), new_name_path.c_str())) + { + Console.Error("(FileMcd) Failed to rename '%s' to '%s'", name_path.c_str(), new_name_path.c_str()); + return false; + } + + return true; +} + +bool FileMcd_DeleteCard(const std::string_view& name) +{ + const std::string name_path(Path::CombineStdString(EmuFolders::MemoryCards, name)); + + FILESYSTEM_STAT_DATA sd; + if (!FileSystem::StatFile(name_path.c_str(), &sd)) + { + Console.Error("(FileMcd) Can't stat '%s' for deletion", name_path.c_str()); + return false; + } + + Console.WriteLn("(FileMcd) Deleting memory card '%.*s'", static_cast(name.size()), name.data()); + + if (sd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY) + { + // must be a folder memcard, so do a recursive delete (scary) + if (!FileSystem::RecursiveDeleteDirectory(name_path.c_str())) + { + Console.Error("(FileMcd) Failed to recursively delete '%s'", name_path.c_str()); + return false; + } + } + else + { + if (!FileSystem::DeleteFilePath(name_path.c_str())) + { + Console.Error("(FileMcd) Failed to delete file '%s'", name_path.c_str()); + return false; + } + } + + return true; +} diff --git a/pcsx2/MemoryCardFile.h b/pcsx2/MemoryCardFile.h index 4abba25ee1..79d909e6b4 100644 --- a/pcsx2/MemoryCardFile.h +++ b/pcsx2/MemoryCardFile.h @@ -15,6 +15,7 @@ #pragma once #include "Config.h" +#include #include #include #include @@ -31,9 +32,11 @@ struct AvailableMcdInfo { std::string name; std::string path; + std::time_t modified_time; MemoryCardType type; MemoryCardFileType file_type; u32 size; + bool formatted; }; extern uint FileMcd_GetMtapPort(uint slot); @@ -59,4 +62,6 @@ bool FileMcd_ReIndex(uint port, uint slot, const wxString& filter); std::vector FileMcd_GetAvailableCards(bool include_in_use_cards); std::optional FileMcd_GetCardInfo(const std::string_view& name); -bool FileMcd_CreateNewCard(const std::string_view& name, MemoryCardType type, MemoryCardFileType file_type); \ No newline at end of file +bool FileMcd_CreateNewCard(const std::string_view& name, MemoryCardType type, MemoryCardFileType file_type); +bool FileMcd_RenameCard(const std::string_view& name, const std::string_view& new_name); +bool FileMcd_DeleteCard(const std::string_view& name);