Merge pull request #11230 from AdmiralCurtiss/gci-filenames

GCMemcardDirectory: GCI filename cleanup and fixes.
This commit is contained in:
Admiral H. Curtiss 2022-11-26 03:52:43 +01:00 committed by GitHub
commit d189c70d4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 150 deletions

View File

@ -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<u8> 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<const char*>(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<const char*>(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<const char*>(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<u8*>(this), 0xFF, DENTRY_SIZE);
}
std::string DEntry::GCI_FileName() const
{
std::string filename =
std::string(reinterpret_cast<const char*>(m_makercode.data()), m_makercode.size()) + '-' +
std::string(reinterpret_cast<const char*>(m_gamecode.data()), m_gamecode.size()) + '-' +
reinterpret_cast<const char*>(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<u8*>(this), 0xFF, BLOCK_SIZE);

View File

@ -231,6 +231,8 @@ struct Header
std::pair<u16, u16> 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<Header>);
@ -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<u8, 4> 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<u8> 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

View File

@ -9,6 +9,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <vector>
#include <fmt/format.h>
@ -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<const char*>(entry.m_makercode.data()),
entry.m_makercode.size());
const std::string_view gamecode(reinterpret_cast<const char*>(entry.m_gamecode.data()),
entry.m_gamecode.size());
const std::string_view filename(reinterpret_cast<const char*>(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<std::string> GCMemcardDirectory::GetFileNamesForGameID(const std::st
if (game_id.length() >= 4 && game_id != "00000000")
game_code = Common::swap32(reinterpret_cast<const u8*>(game_id.c_str()));
std::vector<std::string> loaded_saves;
std::vector<Memcard::DEntry> 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<std::string> 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<std::string> 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