diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp index e6b6383de5..d603e1075e 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp +++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp @@ -304,7 +304,7 @@ void GCMemcard::UpdateBat(const BlockAlloc& bat) bool GCMemcard::IsShiftJIS() const { - return m_header_block.m_data.m_encoding != 0; + return m_header_block.IsShiftJIS(); } bool GCMemcard::Save() @@ -420,51 +420,6 @@ std::optional GCMemcard::TitlePresent(const DEntry& d) const return std::nullopt; } -bool GCMemcard::GCI_FileName(u8 index, std::string& filename) const -{ - if (!m_valid || index >= DIRLEN || - GetActiveDirectory().m_dir_entries[index].m_gamecode == DEntry::UNINITIALIZED_GAMECODE) - return false; - - filename = GetActiveDirectory().m_dir_entries[index].GCI_FileName(); - return true; -} - -std::string GCMemcard::DEntry_GameCode(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - return std::string( - reinterpret_cast(GetActiveDirectory().m_dir_entries[index].m_gamecode.data()), - GetActiveDirectory().m_dir_entries[index].m_gamecode.size()); -} - -std::string GCMemcard::DEntry_Makercode(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - return std::string( - reinterpret_cast(GetActiveDirectory().m_dir_entries[index].m_makercode.data()), - GetActiveDirectory().m_dir_entries[index].m_makercode.size()); -} - -std::string GCMemcard::DEntry_BIFlags(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - std::string flags; - int x = GetActiveDirectory().m_dir_entries[index].m_banner_and_icon_flags; - for (int i = 0; i < 8; i++) - { - flags.push_back((x & 0x80) ? '1' : '0'); - x = x << 1; - } - return flags; -} - bool GCMemcard::DEntry_IsPingPong(u8 index) const { if (!m_valid || index >= DIRLEN) @@ -474,81 +429,6 @@ bool GCMemcard::DEntry_IsPingPong(u8 index) const return (flags & 0b0000'0100) != 0; } -std::string GCMemcard::DEntry_FileName(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - return std::string( - reinterpret_cast(GetActiveDirectory().m_dir_entries[index].m_filename.data()), - GetActiveDirectory().m_dir_entries[index].m_filename.size()); -} - -u32 GCMemcard::DEntry_ModTime(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return 0xFFFFFFFF; - - return GetActiveDirectory().m_dir_entries[index].m_modification_time; -} - -u32 GCMemcard::DEntry_ImageOffset(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return 0xFFFFFFFF; - - return GetActiveDirectory().m_dir_entries[index].m_image_offset; -} - -std::string GCMemcard::DEntry_IconFmt(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - u16 x = GetActiveDirectory().m_dir_entries[index].m_icon_format; - std::string format; - for (size_t i = 0; i < 16; ++i) - { - format.push_back(Common::ExtractBit(x, 15 - i) ? '1' : '0'); - } - return format; -} - -std::string GCMemcard::DEntry_AnimSpeed(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - u16 x = GetActiveDirectory().m_dir_entries[index].m_animation_speed; - std::string speed; - for (size_t i = 0; i < 16; ++i) - { - speed.push_back(Common::ExtractBit(x, 15 - i) ? '1' : '0'); - } - return speed; -} - -std::string GCMemcard::DEntry_Permissions(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return ""; - - u8 Permissions = GetActiveDirectory().m_dir_entries[index].m_file_permissions; - std::string permissionsString; - permissionsString.push_back((Permissions & 16) ? 'x' : 'M'); - permissionsString.push_back((Permissions & 8) ? 'x' : 'C'); - permissionsString.push_back((Permissions & 4) ? 'P' : 'x'); - return permissionsString; -} - -u8 GCMemcard::DEntry_CopyCounter(u8 index) const -{ - if (!m_valid || index >= DIRLEN) - return 0xFF; - - return GetActiveDirectory().m_dir_entries[index].m_copy_counter; -} - u16 GCMemcard::DEntry_FirstBlock(u8 index) const { if (!m_valid || index >= DIRLEN) @@ -1394,15 +1274,6 @@ DEntry::DEntry() memset(reinterpret_cast(this), 0xFF, DENTRY_SIZE); } -std::string DEntry::GCI_FileName() const -{ - std::string filename = - std::string(reinterpret_cast(m_makercode.data()), m_makercode.size()) + '-' + - std::string(reinterpret_cast(m_gamecode.data()), m_gamecode.size()) + '-' + - reinterpret_cast(m_filename.data()) + ".gci"; - return Common::EscapeFileName(filename); -} - void Header::FixChecksums() { std::tie(m_checksum, m_checksum_inv) = CalculateChecksums(); @@ -1444,6 +1315,11 @@ GCMemcardErrorCode Header::CheckForErrors(u16 card_size_mbits) const return error_code; } +bool Header::IsShiftJIS() const +{ + return m_data.m_encoding != 0; +} + Directory::Directory() { memset(reinterpret_cast(this), 0xFF, BLOCK_SIZE); diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.h b/Source/Core/Core/HW/GCMemcard/GCMemcard.h index 6b82d162d2..80a2b3eea4 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcard.h +++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.h @@ -231,6 +231,8 @@ struct Header std::pair CalculateChecksums() const; GCMemcardErrorCode CheckForErrors(u16 card_size_mbits) const; + + bool IsShiftJIS() const; }; static_assert(sizeof(Header) == BLOCK_SIZE); static_assert(std::is_trivially_copyable_v
); @@ -239,9 +241,6 @@ struct DEntry { DEntry(); - // TODO: This probably shouldn't be here at all? - std::string GCI_FileName() const; - static constexpr std::array UNINITIALIZED_GAMECODE{{0xFF, 0xFF, 0xFF, 0xFF}}; // 4 bytes at 0x00: Gamecode @@ -453,19 +452,8 @@ public: // with that identity exists in this card. std::optional TitlePresent(const DEntry& d) const; - bool GCI_FileName(u8 index, std::string& filename) const; // DEntry functions, all take u8 index < DIRLEN (127) - std::string DEntry_GameCode(u8 index) const; - std::string DEntry_Makercode(u8 index) const; - std::string DEntry_BIFlags(u8 index) const; bool DEntry_IsPingPong(u8 index) const; - std::string DEntry_FileName(u8 index) const; - u32 DEntry_ModTime(u8 index) const; - u32 DEntry_ImageOffset(u8 index) const; - std::string DEntry_IconFmt(u8 index) const; - std::string DEntry_AnimSpeed(u8 index) const; - std::string DEntry_Permissions(u8 index) const; - u8 DEntry_CopyCounter(u8 index) const; // get first block for file u16 DEntry_FirstBlock(u8 index) const; // get file length in blocks diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp index cbfbc200f3..304c7922d9 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -39,12 +40,34 @@ static const char* MC_HDR = "MC_SYSTEM_AREA"; +static std::string GenerateDefaultGCIFilename(const Memcard::DEntry& entry, + bool card_encoding_is_shift_jis) +{ + const auto string_decoder = card_encoding_is_shift_jis ? SHIFTJISToUTF8 : CP1252ToUTF8; + const auto strip_null = [](const std::string_view& s) { + auto offset = s.find('\0'); + if (offset == std::string_view::npos) + return s; + return s.substr(0, offset); + }; + + const std::string_view makercode(reinterpret_cast(entry.m_makercode.data()), + entry.m_makercode.size()); + const std::string_view gamecode(reinterpret_cast(entry.m_gamecode.data()), + entry.m_gamecode.size()); + const std::string_view filename(reinterpret_cast(entry.m_filename.data()), + entry.m_filename.size()); + return Common::EscapeFileName(fmt::format("{}-{}-{}.gci", strip_null(string_decoder(makercode)), + strip_null(string_decoder(gamecode)), + strip_null(string_decoder(filename)))); +} + bool GCMemcardDirectory::LoadGCI(Memcard::GCIFile gci) { // check if any already loaded file has the same internal name as the new file for (const Memcard::GCIFile& already_loaded_gci : m_saves) { - if (gci.m_gci_header.GCI_FileName() == already_loaded_gci.m_gci_header.GCI_FileName()) + if (HasSameIdentity(gci.m_gci_header, already_loaded_gci.m_gci_header)) { ERROR_LOG_FMT(EXPANSIONINTERFACE, "{}\nwas not loaded because it has the same internal filename as previously " @@ -110,7 +133,7 @@ std::vector GCMemcardDirectory::GetFileNamesForGameID(const std::st if (game_id.length() >= 4 && game_id != "00000000") game_code = Common::swap32(reinterpret_cast(game_id.c_str())); - std::vector loaded_saves; + std::vector loaded_saves; for (const std::string& file_name : Common::DoFileSearch({directory}, {".gci"})) { File::IOFile gci_file(file_name, "rb"); @@ -123,8 +146,11 @@ std::vector GCMemcardDirectory::GetFileNamesForGameID(const std::st if (!gci_file.ReadBytes(&gci.m_gci_header, Memcard::DENTRY_SIZE)) continue; - const std::string gci_filename = gci.m_gci_header.GCI_FileName(); - if (std::find(loaded_saves.begin(), loaded_saves.end(), gci_filename) != loaded_saves.end()) + const auto same_identity_save_it = std::find_if( + loaded_saves.begin(), loaded_saves.end(), [&gci](const Memcard::DEntry& entry) { + return Memcard::HasSameIdentity(gci.m_gci_header, entry); + }); + if (same_identity_save_it != loaded_saves.end()) continue; const u16 num_blocks = gci.m_gci_header.m_block_count; @@ -144,7 +170,7 @@ std::vector GCMemcardDirectory::GetFileNamesForGameID(const std::st if (game_code == Common::swap32(gci.m_gci_header.m_gamecode.data())) { - loaded_saves.push_back(gci_filename); + loaded_saves.push_back(gci.m_gci_header); filenames.push_back(file_name); } } @@ -614,7 +640,8 @@ void GCMemcardDirectory::FlushToFile() } if (save.m_filename.empty()) { - std::string default_save_name = m_save_directory + save.m_gci_header.GCI_FileName(); + std::string default_save_name = + m_save_directory + GenerateDefaultGCIFilename(save.m_gci_header, m_hdr.IsShiftJIS()); // Check to see if another file is using the same name // This seems unlikely except in the case of file corruption