Merge pull request #11230 from AdmiralCurtiss/gci-filenames
GCMemcardDirectory: GCI filename cleanup and fixes.
This commit is contained in:
commit
d189c70d4d
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue