From eb3823ac956180c31b911c4ac643fc38898764dd Mon Sep 17 00:00:00 2001 From: shawly Date: Sun, 13 Sep 2026 15:31:49 +0200 Subject: [PATCH] Memcard: Add ECC bytes when converting an ECC-less file card to a folder MMCE cards such as MemCard PRO and SD2PSX store pages without ECC, so the source is expanded to raw pages and rejected if it is not a whole number. --- pcsx2-qt/Settings/MemoryCardConvertWorker.cpp | 10 ++++++ pcsx2/SIO/Memcard/MemoryCardFile.cpp | 32 ++++++++++++++++++- pcsx2/SIO/Memcard/MemoryCardFile.h | 1 + 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/pcsx2-qt/Settings/MemoryCardConvertWorker.cpp b/pcsx2-qt/Settings/MemoryCardConvertWorker.cpp index 6cab5ea280..ad061049bf 100644 --- a/pcsx2-qt/Settings/MemoryCardConvertWorker.cpp +++ b/pcsx2-qt/Settings/MemoryCardConvertWorker.cpp @@ -118,6 +118,16 @@ bool MemoryCardConvertWorker::ConvertToFolder(const std::string& srcFileName, co } std::vector sourceBuffer = sourceBufferOpt.value(); + + // MemCard PRO, SD2PSX and other MMCE cards have no ECC bytes + FileMcd_InsertECC(sourceBuffer); + + if ((sourceBuffer.size() % FolderMemoryCard::PageSizeRaw) != 0) + { + Console.Error("%s(%s, %s, %d) File Memory Card is not a whole number of pages!", __FUNCTION__, srcFileName.c_str(), destFolderName.c_str(), type); + return false; + } + // Set progress bar to the literal number of bytes in the memcard. // Plus two because there is a lag period after the Save calls complete // where the progress bar stalls out; this lets us stop the progress bar diff --git a/pcsx2/SIO/Memcard/MemoryCardFile.cpp b/pcsx2/SIO/Memcard/MemoryCardFile.cpp index 3d9343d304..5cae5578a4 100644 --- a/pcsx2/SIO/Memcard/MemoryCardFile.cpp +++ b/pcsx2/SIO/Memcard/MemoryCardFile.cpp @@ -40,7 +40,7 @@ bool FileMcd_Open = false; // https://sourceforge.net/p/mymc-opl/code/ci/master/tree/ps2mc_ecc.py // Public domain license -static u32 CalculateECC(u8* buf) +static u32 CalculateECC(const u8* buf) { const u8 parity_table[256] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, @@ -889,6 +889,36 @@ bool FileMcd_IsMemoryCardFormatted(std::FILE* fp) std::memcmp(data, formatted_psx, sizeof(formatted_psx) - 1) == 0); } +static bool IsNoECCSize(s64 size) +{ + return (size == _8mb || size == _16mb || size == _32mb || size == _64mb); +} + +bool FileMcd_InsertECC(std::vector& buffer) +{ + if (!IsNoECCSize(static_cast(buffer.size()))) + return false; + + const size_t pages = buffer.size() / FolderMemoryCard::PageSize; + std::vector raw(pages * FolderMemoryCard::PageSizeRaw, 0xff); + + for (size_t page = 0; page < pages; page++) + { + const u8* src = &buffer[page * FolderMemoryCard::PageSize]; + u8* dst = &raw[page * FolderMemoryCard::PageSizeRaw]; + std::memcpy(dst, src, FolderMemoryCard::PageSize); + + for (int j = 0; j < 4; j++) + { + const u32 checksum = CalculateECC(&src[j * 128]); + std::memcpy(&dst[FolderMemoryCard::PageSize + (j * 3)], &checksum, 3); + } + } + + buffer = std::move(raw); + return true; +} + std::vector FileMcd_GetAvailableCards(bool include_in_use_cards) { std::vector files; diff --git a/pcsx2/SIO/Memcard/MemoryCardFile.h b/pcsx2/SIO/Memcard/MemoryCardFile.h index cf677a38c0..2148c0b60f 100644 --- a/pcsx2/SIO/Memcard/MemoryCardFile.h +++ b/pcsx2/SIO/Memcard/MemoryCardFile.h @@ -57,6 +57,7 @@ std::vector FileMcd_GetAvailableCards(bool include_in_use_card std::optional FileMcd_GetCardInfo(const std::string_view name); bool FileMcd_IsMemoryCardFormatted(const std::string& path); bool FileMcd_IsMemoryCardFormatted(std::FILE* fp); +bool FileMcd_InsertECC(std::vector& buffer); 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); \ No newline at end of file