summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2019-01-16 21:56:40 +0100
committerGitHub <noreply@github.com>2019-01-16 21:56:40 +0100
commitfd3ef7ebc53d1b86737dc7b2557cd469b47d342e (patch)
tree4ba56497dfa5d33ec872584521d37e4ef5df0f1f /Source
parent8d601b1466769aa613a92775aff21a3045370e0b (diff)
parent04bcc64e82269be2814ac84c7c9ea3238682b8dc (diff)
Merge pull request #7616 from AdmiralCurtiss/memcard-cleanup
Clean up GCMemcard a bit.
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Core/HW/GCMemcard/GCMemcard.cpp584
-rw-r--r--Source/Core/Core/HW/GCMemcard/GCMemcard.h275
-rw-r--r--Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp59
-rw-r--r--Source/Core/DolphinQt/GCMemcardManager.cpp8
4 files changed, 503 insertions, 423 deletions
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp
index 28e7225845..cd723de7fb 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.cpp
@@ -9,6 +9,7 @@
#include <cstring>
#include <vector>
+#include "Common/BitUtils.h"
#include "Common/ColorUtil.h"
#include "Common/CommonFuncs.h"
#include "Common/CommonPaths.h"
@@ -19,6 +20,9 @@
#include "Common/StringUtil.h"
#include "Common/Swap.h"
+// TODO: Remove when on C++17 everywhere.
+constexpr std::array<u8, 4> DEntry::UNINITIALIZED_GAMECODE;
+
static void ByteSwap(u8* valueA, u8* valueB)
{
u8 tmp = *valueA;
@@ -27,11 +31,11 @@ static void ByteSwap(u8* valueA, u8* valueB)
}
GCMemcard::GCMemcard(const std::string& filename, bool forceCreation, bool shift_jis)
- : m_valid(false), m_fileName(filename)
+ : m_valid(false), m_filename(filename)
{
// Currently there is a string freeze. instead of adding a new message about needing r/w
// open file read only, if write is denied the error will be reported at that point
- File::IOFile mcdFile(m_fileName, "rb");
+ File::IOFile mcdFile(m_filename, "rb");
if (!mcdFile.IsOpen())
{
if (!forceCreation)
@@ -70,8 +74,8 @@ GCMemcard::GCMemcard(const std::string& filename, bool forceCreation, bool shift
return;
}
- m_sizeMb = (u16)((size / BLOCK_SIZE) / MBIT_TO_BLOCKS);
- switch (m_sizeMb)
+ m_size_mb = (u16)((size / BLOCK_SIZE) / MBIT_TO_BLOCKS);
+ switch (m_size_mb)
{
case MemCard59Mb:
case MemCard123Mb:
@@ -88,38 +92,38 @@ GCMemcard::GCMemcard(const std::string& filename, bool forceCreation, bool shift
}
mcdFile.Seek(0, SEEK_SET);
- if (!mcdFile.ReadBytes(&hdr, BLOCK_SIZE))
+ if (!mcdFile.ReadBytes(&m_header_block, BLOCK_SIZE))
{
PanicAlertT("Failed to read header correctly\n(0x0000-0x1FFF)");
return;
}
- if (m_sizeMb != BE16(hdr.SizeMb))
+ if (m_size_mb != m_header_block.m_size_mb)
{
PanicAlertT("Memory card file size does not match the header size");
return;
}
- if (!mcdFile.ReadBytes(&dir, BLOCK_SIZE))
+ if (!mcdFile.ReadBytes(&m_directory_blocks[0], BLOCK_SIZE))
{
- PanicAlertT("Failed to read directory correctly\n(0x2000-0x3FFF)");
+ PanicAlertT("Failed to read 1st directory block correctly\n(0x2000-0x3FFF)");
return;
}
- if (!mcdFile.ReadBytes(&dir_backup, BLOCK_SIZE))
+ if (!mcdFile.ReadBytes(&m_directory_blocks[1], BLOCK_SIZE))
{
- PanicAlertT("Failed to read directory backup correctly\n(0x4000-0x5FFF)");
+ PanicAlertT("Failed to read 2nd directory block correctly\n(0x4000-0x5FFF)");
return;
}
- if (!mcdFile.ReadBytes(&bat, BLOCK_SIZE))
+ if (!mcdFile.ReadBytes(&m_bat_blocks[0], BLOCK_SIZE))
{
- PanicAlertT("Failed to read block allocation table correctly\n(0x6000-0x7FFF)");
+ PanicAlertT("Failed to read 1st block allocation table block correctly\n(0x6000-0x7FFF)");
return;
}
- if (!mcdFile.ReadBytes(&bat_backup, BLOCK_SIZE))
+ if (!mcdFile.ReadBytes(&m_bat_blocks[1], BLOCK_SIZE))
{
- PanicAlertT("Failed to read block allocation table backup correctly\n(0x8000-0x9FFF)");
+ PanicAlertT("Failed to read 2nd block allocation table block correctly\n(0x8000-0x9FFF)");
return;
}
@@ -133,64 +137,62 @@ GCMemcard::GCMemcard(const std::string& filename, bool forceCreation, bool shift
return;
}
- if (csums & 0x2) // directory checksum error!
+ if (csums & 0x2) // 1st directory block checksum error!
{
if (csums & 0x4)
{
- // backup is also wrong!
- PanicAlertT("Directory checksum and directory backup checksum failed");
+ // 2nd block is also wrong!
+ PanicAlertT("Both directory block checksums are invalid");
return;
}
else
{
- // backup is correct, restore
- dir = dir_backup;
- bat = bat_backup;
+ // FIXME: This is probably incorrect behavior, confirm what actually happens on hardware here.
+ // The currently active directory block and currently active BAT block don't necessarily have
+ // to correlate.
+
+ // 2nd block is correct, restore
+ m_directory_blocks[0] = m_directory_blocks[1];
+ m_bat_blocks[0] = m_bat_blocks[1];
// update checksums
csums = TestChecksums();
}
}
- if (csums & 0x8) // BAT checksum error!
+ if (csums & 0x8) // 1st BAT checksum error!
{
if (csums & 0x10)
{
- // backup is also wrong!
- PanicAlertT("Block Allocation Table checksum failed");
+ // 2nd BAT is also wrong!
+ PanicAlertT("Both Block Allocation Table block checksums are invalid");
return;
}
else
{
- // backup is correct, restore
- dir = dir_backup;
- bat = bat_backup;
+ // FIXME: Same as above, this feels incorrect.
+
+ // 2nd block is correct, restore
+ m_directory_blocks[0] = m_directory_blocks[1];
+ m_bat_blocks[0] = m_bat_blocks[1];
// update checksums
csums = TestChecksums();
}
- // It seems that the backup having a larger counter doesn't necessarily mean
- // the backup should be copied?
- // }
- //
- // if (BE16(dir_backup.UpdateCounter) > BE16(dir.UpdateCounter)) //check if the backup is newer
- // {
- // dir = dir_backup;
- // bat = bat_backup; // needed?
}
mcdFile.Seek(0xa000, SEEK_SET);
- maxBlock = (u32)m_sizeMb * MBIT_TO_BLOCKS;
- mc_data_blocks.reserve(maxBlock - MC_FST_BLOCKS);
+ m_size_blocks = (u32)m_size_mb * MBIT_TO_BLOCKS;
+ m_data_blocks.reserve(m_size_blocks - MC_FST_BLOCKS);
m_valid = true;
- for (u32 i = MC_FST_BLOCKS; i < maxBlock; ++i)
+ for (u32 i = MC_FST_BLOCKS; i < m_size_blocks; ++i)
{
GCMBlock b;
- if (mcdFile.ReadBytes(b.block, BLOCK_SIZE))
+ if (mcdFile.ReadBytes(b.m_block.data(), b.m_block.size()))
{
- mc_data_blocks.push_back(b);
+ m_data_blocks.push_back(b);
}
else
{
@@ -204,51 +206,66 @@ GCMemcard::GCMemcard(const std::string& filename, bool forceCreation, bool shift
mcdFile.Close();
- InitDirBatPointers();
+ InitActiveDirBat();
}
-void GCMemcard::InitDirBatPointers()
+void GCMemcard::InitActiveDirBat()
{
- if (BE16(dir.UpdateCounter) > (BE16(dir_backup.UpdateCounter)))
- {
- CurrentDir = &dir;
- PreviousDir = &dir_backup;
- }
+ if (m_directory_blocks[0].m_update_counter > m_directory_blocks[1].m_update_counter)
+ m_active_directory = 0;
else
- {
- CurrentDir = &dir_backup;
- PreviousDir = &dir;
- }
- if (BE16(bat.UpdateCounter) > BE16(bat_backup.UpdateCounter))
- {
- CurrentBat = &bat;
- PreviousBat = &bat_backup;
- }
+ m_active_directory = 1;
+
+ if (m_bat_blocks[0].m_update_counter > m_bat_blocks[1].m_update_counter)
+ m_active_bat = 0;
else
- {
- CurrentBat = &bat_backup;
- PreviousBat = &bat;
- }
+ m_active_bat = 1;
+}
+
+const Directory& GCMemcard::GetActiveDirectory() const
+{
+ return m_directory_blocks[m_active_directory];
+}
+
+const BlockAlloc& GCMemcard::GetActiveBat() const
+{
+ return m_bat_blocks[m_active_bat];
+}
+
+void GCMemcard::UpdateDirectory(const Directory& directory)
+{
+ // overwrite inactive dir with given data, then set active dir to written block
+ int new_directory_index = m_active_directory == 0 ? 1 : 0;
+ m_directory_blocks[new_directory_index] = directory;
+ m_active_directory = new_directory_index;
+}
+
+void GCMemcard::UpdateBat(const BlockAlloc& bat)
+{
+ // overwrite inactive BAT with given data, then set active BAT to written block
+ int new_bat_index = m_active_bat == 0 ? 1 : 0;
+ m_bat_blocks[new_bat_index] = bat;
+ m_active_bat = new_bat_index;
}
bool GCMemcard::IsShiftJIS() const
{
- return hdr.Encoding != 0;
+ return m_header_block.m_encoding != 0;
}
bool GCMemcard::Save()
{
- File::IOFile mcdFile(m_fileName, "wb");
+ File::IOFile mcdFile(m_filename, "wb");
mcdFile.Seek(0, SEEK_SET);
- mcdFile.WriteBytes(&hdr, BLOCK_SIZE);
- mcdFile.WriteBytes(&dir, BLOCK_SIZE);
- mcdFile.WriteBytes(&dir_backup, BLOCK_SIZE);
- mcdFile.WriteBytes(&bat, BLOCK_SIZE);
- mcdFile.WriteBytes(&bat_backup, BLOCK_SIZE);
- for (unsigned int i = 0; i < maxBlock - MC_FST_BLOCKS; ++i)
+ mcdFile.WriteBytes(&m_header_block, BLOCK_SIZE);
+ mcdFile.WriteBytes(&m_directory_blocks[0], BLOCK_SIZE);
+ mcdFile.WriteBytes(&m_directory_blocks[1], BLOCK_SIZE);
+ mcdFile.WriteBytes(&m_bat_blocks[0], BLOCK_SIZE);
+ mcdFile.WriteBytes(&m_bat_blocks[1], BLOCK_SIZE);
+ for (unsigned int i = 0; i < m_size_blocks - MC_FST_BLOCKS; ++i)
{
- mcdFile.WriteBytes(mc_data_blocks[i].block, BLOCK_SIZE);
+ mcdFile.WriteBytes(m_data_blocks[i].m_block.data(), m_data_blocks[i].m_block.size());
}
return mcdFile.Close();
@@ -282,24 +299,26 @@ u32 GCMemcard::TestChecksums() const
u32 results = 0;
- calc_checksumsBE((u16*)&hdr, 0xFE, &csum, &csum_inv);
- if ((hdr.Checksum != csum) || (hdr.Checksum_Inv != csum_inv))
+ calc_checksumsBE((u16*)&m_header_block, 0xFE, &csum, &csum_inv);
+ if ((m_header_block.m_checksum != csum) || (m_header_block.m_checksum_inv != csum_inv))
results |= 1;
- calc_checksumsBE((u16*)&dir, 0xFFE, &csum, &csum_inv);
- if ((dir.Checksum != csum) || (dir.Checksum_Inv != csum_inv))
+ calc_checksumsBE((u16*)&m_directory_blocks[0], 0xFFE, &csum, &csum_inv);
+ if ((m_directory_blocks[0].m_checksum != csum) ||
+ (m_directory_blocks[0].m_checksum_inv != csum_inv))
results |= 2;
- calc_checksumsBE((u16*)&dir_backup, 0xFFE, &csum, &csum_inv);
- if ((dir_backup.Checksum != csum) || (dir_backup.Checksum_Inv != csum_inv))
+ calc_checksumsBE((u16*)&m_directory_blocks[1], 0xFFE, &csum, &csum_inv);
+ if ((m_directory_blocks[1].m_checksum != csum) ||
+ (m_directory_blocks[1].m_checksum_inv != csum_inv))
results |= 4;
- calc_checksumsBE((u16*)(((u8*)&bat) + 4), 0xFFE, &csum, &csum_inv);
- if ((bat.Checksum != csum) || (bat.Checksum_Inv != csum_inv))
+ calc_checksumsBE((u16*)(((u8*)&m_bat_blocks[0]) + 4), 0xFFE, &csum, &csum_inv);
+ if ((m_bat_blocks[0].m_checksum != csum) || (m_bat_blocks[0].m_checksum_inv != csum_inv))
results |= 8;
- calc_checksumsBE((u16*)(((u8*)&bat_backup) + 4), 0xFFE, &csum, &csum_inv);
- if ((bat_backup.Checksum != csum) || (bat_backup.Checksum_Inv != csum_inv))
+ calc_checksumsBE((u16*)(((u8*)&m_bat_blocks[1]) + 4), 0xFFE, &csum, &csum_inv);
+ if ((m_bat_blocks[1].m_checksum != csum) || (m_bat_blocks[1].m_checksum_inv != csum_inv))
results |= 16;
return results;
@@ -310,11 +329,16 @@ bool GCMemcard::FixChecksums()
if (!m_valid)
return false;
- calc_checksumsBE((u16*)&hdr, 0xFE, &hdr.Checksum, &hdr.Checksum_Inv);
- calc_checksumsBE((u16*)&dir, 0xFFE, &dir.Checksum, &dir.Checksum_Inv);
- calc_checksumsBE((u16*)&dir_backup, 0xFFE, &dir_backup.Checksum, &dir_backup.Checksum_Inv);
- calc_checksumsBE((u16*)&bat + 2, 0xFFE, &bat.Checksum, &bat.Checksum_Inv);
- calc_checksumsBE((u16*)&bat_backup + 2, 0xFFE, &bat_backup.Checksum, &bat_backup.Checksum_Inv);
+ calc_checksumsBE((u16*)&m_header_block, 0xFE, &m_header_block.m_checksum,
+ &m_header_block.m_checksum_inv);
+ calc_checksumsBE((u16*)&m_directory_blocks[0], 0xFFE, &m_directory_blocks[0].m_checksum,
+ &m_directory_blocks[0].m_checksum_inv);
+ calc_checksumsBE((u16*)&m_directory_blocks[1], 0xFFE, &m_directory_blocks[1].m_checksum,
+ &m_directory_blocks[1].m_checksum_inv);
+ calc_checksumsBE((u16*)&m_bat_blocks[0] + 2, 0xFFE, &m_bat_blocks[0].m_checksum,
+ &m_bat_blocks[0].m_checksum_inv);
+ calc_checksumsBE((u16*)&m_bat_blocks[1] + 2, 0xFFE, &m_bat_blocks[1].m_checksum,
+ &m_bat_blocks[1].m_checksum_inv);
return true;
}
@@ -327,7 +351,7 @@ u8 GCMemcard::GetNumFiles() const
u8 j = 0;
for (int i = 0; i < DIRLEN; i++)
{
- if (BE32(CurrentDir->Dir[i].Gamecode) != 0xFFFFFFFF)
+ if (GetActiveDirectory().m_dir_entries[i].m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
j++;
}
return j;
@@ -340,7 +364,7 @@ u8 GCMemcard::GetFileIndex(u8 fileNumber) const
u8 j = 0;
for (u8 i = 0; i < DIRLEN; i++)
{
- if (BE32(CurrentDir->Dir[i].Gamecode) != 0xFFFFFFFF)
+ if (GetActiveDirectory().m_dir_entries[i].m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
{
if (j == fileNumber)
{
@@ -358,7 +382,7 @@ u16 GCMemcard::GetFreeBlocks() const
if (!m_valid)
return 0;
- return BE16(CurrentBat->FreeBlocks);
+ return GetActiveBat().m_free_blocks;
}
u8 GCMemcard::TitlePresent(const DEntry& d) const
@@ -369,8 +393,8 @@ u8 GCMemcard::TitlePresent(const DEntry& d) const
u8 i = 0;
while (i < DIRLEN)
{
- if ((BE32(CurrentDir->Dir[i].Gamecode) == BE32(d.Gamecode)) &&
- (!memcmp(CurrentDir->Dir[i].Filename, d.Filename, 32)))
+ if (GetActiveDirectory().m_dir_entries[i].m_gamecode == d.m_gamecode &&
+ GetActiveDirectory().m_dir_entries[i].m_filename == d.m_filename)
{
break;
}
@@ -381,10 +405,11 @@ u8 GCMemcard::TitlePresent(const DEntry& d) const
bool GCMemcard::GCI_FileName(u8 index, std::string& filename) const
{
- if (!m_valid || index >= DIRLEN || (BE32(CurrentDir->Dir[index].Gamecode) == 0xFFFFFFFF))
+ if (!m_valid || index >= DIRLEN ||
+ GetActiveDirectory().m_dir_entries[index].m_gamecode == DEntry::UNINITIALIZED_GAMECODE)
return false;
- filename = CurrentDir->Dir[index].GCI_FileName();
+ filename = GetActiveDirectory().m_dir_entries[index].GCI_FileName();
return true;
}
@@ -396,7 +421,9 @@ std::string GCMemcard::DEntry_GameCode(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- return std::string((const char*)CurrentDir->Dir[index].Gamecode, 4);
+ 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
@@ -404,7 +431,9 @@ std::string GCMemcard::DEntry_Makercode(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- return std::string((const char*)CurrentDir->Dir[index].Makercode, 2);
+ 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
@@ -413,7 +442,7 @@ std::string GCMemcard::DEntry_BIFlags(u8 index) const
return "";
std::string flags;
- int x = CurrentDir->Dir[index].BIFlags;
+ 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');
@@ -427,7 +456,9 @@ std::string GCMemcard::DEntry_FileName(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- return std::string((const char*)CurrentDir->Dir[index].Filename, DENTRY_STRLEN);
+ 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
@@ -435,7 +466,7 @@ u32 GCMemcard::DEntry_ModTime(u8 index) const
if (!m_valid || index >= DIRLEN)
return 0xFFFFFFFF;
- return BE32(CurrentDir->Dir[index].ModTime);
+ return GetActiveDirectory().m_dir_entries[index].m_modification_time;
}
u32 GCMemcard::DEntry_ImageOffset(u8 index) const
@@ -443,7 +474,7 @@ u32 GCMemcard::DEntry_ImageOffset(u8 index) const
if (!m_valid || index >= DIRLEN)
return 0xFFFFFFFF;
- return BE32(CurrentDir->Dir[index].ImageOffset);
+ return GetActiveDirectory().m_dir_entries[index].m_image_offset;
}
std::string GCMemcard::DEntry_IconFmt(u8 index) const
@@ -451,14 +482,11 @@ std::string GCMemcard::DEntry_IconFmt(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- int x = CurrentDir->Dir[index].IconFmt[0];
+ u16 x = GetActiveDirectory().m_dir_entries[index].m_icon_format;
std::string format;
- for (int i = 0; i < 16; i++)
+ for (size_t i = 0; i < 16; ++i)
{
- if (i == 8)
- x = CurrentDir->Dir[index].IconFmt[1];
- format.push_back((x & 0x80) ? '1' : '0');
- x = x << 1;
+ format.push_back(Common::ExtractBit(x, 15 - i) ? '1' : '0');
}
return format;
}
@@ -468,14 +496,11 @@ std::string GCMemcard::DEntry_AnimSpeed(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- int x = CurrentDir->Dir[index].AnimSpeed[0];
+ u16 x = GetActiveDirectory().m_dir_entries[index].m_animation_speed;
std::string speed;
- for (int i = 0; i < 16; i++)
+ for (size_t i = 0; i < 16; ++i)
{
- if (i == 8)
- x = CurrentDir->Dir[index].AnimSpeed[1];
- speed.push_back((x & 0x80) ? '1' : '0');
- x = x << 1;
+ speed.push_back(Common::ExtractBit(x, 15 - i) ? '1' : '0');
}
return speed;
}
@@ -485,7 +510,7 @@ std::string GCMemcard::DEntry_Permissions(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- u8 Permissions = CurrentDir->Dir[index].Permissions;
+ 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');
@@ -498,7 +523,7 @@ u8 GCMemcard::DEntry_CopyCounter(u8 index) const
if (!m_valid || index >= DIRLEN)
return 0xFF;
- return CurrentDir->Dir[index].CopyCounter;
+ return GetActiveDirectory().m_dir_entries[index].m_copy_counter;
}
u16 GCMemcard::DEntry_FirstBlock(u8 index) const
@@ -506,8 +531,8 @@ u16 GCMemcard::DEntry_FirstBlock(u8 index) const
if (!m_valid || index >= DIRLEN)
return 0xFFFF;
- u16 block = BE16(CurrentDir->Dir[index].FirstBlock);
- if (block > (u16)maxBlock)
+ u16 block = GetActiveDirectory().m_dir_entries[index].m_first_block;
+ if (block > (u16)m_size_blocks)
return 0xFFFF;
return block;
}
@@ -517,8 +542,8 @@ u16 GCMemcard::DEntry_BlockCount(u8 index) const
if (!m_valid || index >= DIRLEN)
return 0xFFFF;
- u16 blocks = BE16(CurrentDir->Dir[index].BlockCount);
- if (blocks > (u16)maxBlock)
+ u16 blocks = GetActiveDirectory().m_dir_entries[index].m_block_count;
+ if (blocks > (u16)m_size_blocks)
return 0xFFFF;
return blocks;
}
@@ -528,7 +553,7 @@ u32 GCMemcard::DEntry_CommentsAddress(u8 index) const
if (!m_valid || index >= DIRLEN)
return 0xFFFF;
- return BE32(CurrentDir->Dir[index].CommentsAddr);
+ return GetActiveDirectory().m_dir_entries[index].m_comments_address;
}
std::string GCMemcard::GetSaveComment1(u8 index) const
@@ -536,13 +561,14 @@ std::string GCMemcard::GetSaveComment1(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- u32 Comment1 = BE32(CurrentDir->Dir[index].CommentsAddr);
- u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock) - MC_FST_BLOCKS;
- if ((DataBlock > maxBlock) || (Comment1 == 0xFFFFFFFF))
+ u32 Comment1 = GetActiveDirectory().m_dir_entries[index].m_comments_address;
+ u32 DataBlock = GetActiveDirectory().m_dir_entries[index].m_first_block - MC_FST_BLOCKS;
+ if ((DataBlock > m_size_blocks) || (Comment1 == 0xFFFFFFFF))
{
return "";
}
- return std::string((const char*)mc_data_blocks[DataBlock].block + Comment1, DENTRY_STRLEN);
+ return std::string((const char*)m_data_blocks[DataBlock].m_block.data() + Comment1,
+ DENTRY_STRLEN);
}
std::string GCMemcard::GetSaveComment2(u8 index) const
@@ -550,23 +576,23 @@ std::string GCMemcard::GetSaveComment2(u8 index) const
if (!m_valid || index >= DIRLEN)
return "";
- u32 Comment1 = BE32(CurrentDir->Dir[index].CommentsAddr);
+ u32 Comment1 = GetActiveDirectory().m_dir_entries[index].m_comments_address;
u32 Comment2 = Comment1 + DENTRY_STRLEN;
- u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock) - MC_FST_BLOCKS;
- if ((DataBlock > maxBlock) || (Comment1 == 0xFFFFFFFF))
+ u32 DataBlock = GetActiveDirectory().m_dir_entries[index].m_first_block - MC_FST_BLOCKS;
+ if ((DataBlock > m_size_blocks) || (Comment1 == 0xFFFFFFFF))
{
return "";
}
- return std::string((const char*)mc_data_blocks[DataBlock].block + Comment2, DENTRY_STRLEN);
+ return std::string((const char*)m_data_blocks[DataBlock].m_block.data() + Comment2,
+ DENTRY_STRLEN);
}
-bool GCMemcard::GetDEntry(u8 index, DEntry& dest) const
+std::optional<DEntry> GCMemcard::GetDEntry(u8 index) const
{
if (!m_valid || index >= DIRLEN)
- return false;
+ return std::nullopt;
- dest = CurrentDir->Dir[index];
- return true;
+ return GetActiveDirectory().m_dir_entries[index];
}
u16 BlockAlloc::GetNextBlock(u16 Block) const
@@ -574,23 +600,23 @@ u16 BlockAlloc::GetNextBlock(u16 Block) const
if ((Block < MC_FST_BLOCKS) || (Block > 4091))
return 0;
- return Common::swap16(Map[Block - MC_FST_BLOCKS]);
+ return m_map[Block - MC_FST_BLOCKS];
}
// Parameters and return value are expected as memory card block index,
// not BAT index; that is, block 5 is the first file data block.
u16 BlockAlloc::NextFreeBlock(u16 MaxBlock, u16 StartingBlock) const
{
- if (FreeBlocks)
+ if (m_free_blocks > 0)
{
StartingBlock = MathUtil::Clamp<u16>(StartingBlock, MC_FST_BLOCKS, BAT_SIZE + MC_FST_BLOCKS);
MaxBlock = MathUtil::Clamp<u16>(MaxBlock, MC_FST_BLOCKS, BAT_SIZE + MC_FST_BLOCKS);
for (u16 i = StartingBlock; i < MaxBlock; ++i)
- if (Map[i - MC_FST_BLOCKS] == 0)
+ if (m_map[i - MC_FST_BLOCKS] == 0)
return i;
for (u16 i = MC_FST_BLOCKS; i < StartingBlock; ++i)
- if (Map[i - MC_FST_BLOCKS] == 0)
+ if (m_map[i - MC_FST_BLOCKS] == 0)
return i;
}
return 0xFFFF;
@@ -612,8 +638,8 @@ bool BlockAlloc::ClearBlocks(u16 FirstBlock, u16 BlockCount)
return false;
}
for (unsigned int i = 0; i < length; ++i)
- Map[blocks.at(i) - MC_FST_BLOCKS] = 0;
- FreeBlocks = BE16(BE16(FreeBlocks) + BlockCount);
+ m_map[blocks.at(i) - MC_FST_BLOCKS] = 0;
+ m_free_blocks = m_free_blocks + BlockCount;
return true;
}
@@ -627,7 +653,7 @@ u32 GCMemcard::GetSaveData(u8 index, std::vector<GCMBlock>& Blocks) const
u16 block = DEntry_FirstBlock(index);
u16 BlockCount = DEntry_BlockCount(index);
- // u16 memcardSize = BE16(hdr.SizeMb) * MBIT_TO_BLOCKS;
+ // u16 memcardSize = BE16(hdr.m_size_mb) * MBIT_TO_BLOCKS;
if ((block == 0xFFFF) || (BlockCount == 0xFFFF))
{
@@ -639,8 +665,8 @@ u32 GCMemcard::GetSaveData(u8 index, std::vector<GCMBlock>& Blocks) const
{
if ((!nextBlock) || (nextBlock == 0xFFFF))
return FAIL;
- Blocks.push_back(mc_data_blocks[nextBlock - MC_FST_BLOCKS]);
- nextBlock = CurrentBat->GetNextBlock(nextBlock);
+ Blocks.push_back(m_data_blocks[nextBlock - MC_FST_BLOCKS]);
+ nextBlock = GetActiveBat().GetNextBlock(nextBlock);
}
return SUCCESS;
}
@@ -655,7 +681,7 @@ u32 GCMemcard::ImportFile(const DEntry& direntry, std::vector<GCMBlock>& saveBlo
{
return OUTOFDIRENTRIES;
}
- if (BE16(CurrentBat->FreeBlocks) < BE16(direntry.BlockCount))
+ if (GetActiveBat().m_free_blocks < direntry.m_block_count)
{
return OUTOFBLOCKS;
}
@@ -665,70 +691,51 @@ u32 GCMemcard::ImportFile(const DEntry& direntry, std::vector<GCMBlock>& saveBlo
}
// find first free data block
- u16 firstBlock = CurrentBat->NextFreeBlock(maxBlock, BE16(CurrentBat->LastAllocated));
+ u16 firstBlock =
+ GetActiveBat().NextFreeBlock(m_size_blocks, GetActiveBat().m_last_allocated_block);
if (firstBlock == 0xFFFF)
return OUTOFBLOCKS;
- Directory UpdatedDir = *CurrentDir;
+ Directory UpdatedDir = GetActiveDirectory();
// find first free dir entry
for (int i = 0; i < DIRLEN; i++)
{
- if (BE32(UpdatedDir.Dir[i].Gamecode) == 0xFFFFFFFF)
+ if (UpdatedDir.m_dir_entries[i].m_gamecode == DEntry::UNINITIALIZED_GAMECODE)
{
- UpdatedDir.Dir[i] = direntry;
- *(u16*)&UpdatedDir.Dir[i].FirstBlock = BE16(firstBlock);
- UpdatedDir.Dir[i].CopyCounter = UpdatedDir.Dir[i].CopyCounter + 1;
+ UpdatedDir.m_dir_entries[i] = direntry;
+ UpdatedDir.m_dir_entries[i].m_first_block = firstBlock;
+ UpdatedDir.m_dir_entries[i].m_copy_counter = UpdatedDir.m_dir_entries[i].m_copy_counter + 1;
break;
}
}
- UpdatedDir.UpdateCounter = BE16(BE16(UpdatedDir.UpdateCounter) + 1);
- *PreviousDir = UpdatedDir;
- if (PreviousDir == &dir)
- {
- CurrentDir = &dir;
- PreviousDir = &dir_backup;
- }
- else
- {
- CurrentDir = &dir_backup;
- PreviousDir = &dir;
- }
+ UpdatedDir.m_update_counter = UpdatedDir.m_update_counter + 1;
+ UpdateDirectory(UpdatedDir);
- int fileBlocks = BE16(direntry.BlockCount);
+ int fileBlocks = direntry.m_block_count;
- FZEROGX_MakeSaveGameValid(hdr, direntry, saveBlocks);
- PSO_MakeSaveGameValid(hdr, direntry, saveBlocks);
+ FZEROGX_MakeSaveGameValid(m_header_block, direntry, saveBlocks);
+ PSO_MakeSaveGameValid(m_header_block, direntry, saveBlocks);
- BlockAlloc UpdatedBat = *CurrentBat;
+ BlockAlloc UpdatedBat = GetActiveBat();
u16 nextBlock;
// keep assuming no freespace fragmentation, and copy over all the data
for (int i = 0; i < fileBlocks; ++i)
{
if (firstBlock == 0xFFFF)
PanicAlert("Fatal Error");
- mc_data_blocks[firstBlock - MC_FST_BLOCKS] = saveBlocks[i];
+ m_data_blocks[firstBlock - MC_FST_BLOCKS] = saveBlocks[i];
if (i == fileBlocks - 1)
nextBlock = 0xFFFF;
else
- nextBlock = UpdatedBat.NextFreeBlock(maxBlock, firstBlock + 1);
- UpdatedBat.Map[firstBlock - MC_FST_BLOCKS] = BE16(nextBlock);
- UpdatedBat.LastAllocated = BE16(firstBlock);
+ nextBlock = UpdatedBat.NextFreeBlock(m_size_blocks, firstBlock + 1);
+ UpdatedBat.m_map[firstBlock - MC_FST_BLOCKS] = nextBlock;
+ UpdatedBat.m_last_allocated_block = firstBlock;
firstBlock = nextBlock;
}
- UpdatedBat.FreeBlocks = BE16(BE16(UpdatedBat.FreeBlocks) - fileBlocks);
- UpdatedBat.UpdateCounter = BE16(BE16(UpdatedBat.UpdateCounter) + 1);
- *PreviousBat = UpdatedBat;
- if (PreviousBat == &bat)
- {
- CurrentBat = &bat;
- PreviousBat = &bat_backup;
- }
- else
- {
- CurrentBat = &bat_backup;
- PreviousBat = &bat;
- }
+ UpdatedBat.m_free_blocks = UpdatedBat.m_free_blocks - fileBlocks;
+ UpdatedBat.m_update_counter = UpdatedBat.m_update_counter + 1;
+ UpdateBat(UpdatedBat);
FixChecksums();
@@ -742,60 +749,24 @@ u32 GCMemcard::RemoveFile(u8 index) // index in the directory array
if (index >= DIRLEN)
return DELETE_FAIL;
- u16 startingblock = BE16(CurrentDir->Dir[index].FirstBlock);
- u16 numberofblocks = BE16(CurrentDir->Dir[index].BlockCount);
+ u16 startingblock = GetActiveDirectory().m_dir_entries[index].m_first_block;
+ u16 numberofblocks = GetActiveDirectory().m_dir_entries[index].m_block_count;
- BlockAlloc UpdatedBat = *CurrentBat;
+ BlockAlloc UpdatedBat = GetActiveBat();
if (!UpdatedBat.ClearBlocks(startingblock, numberofblocks))
return DELETE_FAIL;
- UpdatedBat.UpdateCounter = BE16(BE16(UpdatedBat.UpdateCounter) + 1);
- *PreviousBat = UpdatedBat;
- if (PreviousBat == &bat)
- {
- CurrentBat = &bat;
- PreviousBat = &bat_backup;
- }
- else
- {
- CurrentBat = &bat_backup;
- PreviousBat = &bat;
- }
+ UpdatedBat.m_update_counter = UpdatedBat.m_update_counter + 1;
+ UpdateBat(UpdatedBat);
- Directory UpdatedDir = *CurrentDir;
- /*
- // TODO: determine when this is used, even on the same memory card I have seen
- // both update to broken file, and not updated
- *(u32*)&UpdatedDir.Dir[index].Gamecode = 0;
- *(u16*)&UpdatedDir.Dir[index].Makercode = 0;
- memset(UpdatedDir.Dir[index].Filename, 0, 0x20);
- strcpy((char*)UpdatedDir.Dir[index].Filename, "Broken File000");
- *(u16*)&UpdatedDir.UpdateCounter = BE16(BE16(UpdatedDir.UpdateCounter) + 1);
-
- *PreviousDir = UpdatedDir;
- if (PreviousDir == &dir )
- {
- CurrentDir = &dir;
- PreviousDir = &dir_backup;
- }
- else
- {
- CurrentDir = &dir_backup;
- PreviousDir = &dir;
- }
- */
- memset(&(UpdatedDir.Dir[index]), 0xFF, DENTRY_SIZE);
- UpdatedDir.UpdateCounter = BE16(BE16(UpdatedDir.UpdateCounter) + 1);
- *PreviousDir = UpdatedDir;
- if (PreviousDir == &dir)
- {
- CurrentDir = &dir;
- PreviousDir = &dir_backup;
- }
- else
- {
- CurrentDir = &dir_backup;
- PreviousDir = &dir;
- }
+ Directory UpdatedDir = GetActiveDirectory();
+
+ // TODO: Deleting a file via the GC BIOS sometimes leaves behind an extra updated directory block
+ // here that has an empty file with the filename "Broken File000" where the actual deleted file
+ // was. Determine when exactly this happens and if this is neccessary for anything.
+
+ memset(&(UpdatedDir.m_dir_entries[index]), 0xFF, DENTRY_SIZE);
+ UpdatedDir.m_update_counter = UpdatedDir.m_update_counter + 1;
+ UpdateDirectory(UpdatedDir);
FixChecksums();
@@ -807,8 +778,8 @@ u32 GCMemcard::CopyFrom(const GCMemcard& source, u8 index)
if (!m_valid || !source.m_valid)
return NOMEMCARD;
- DEntry tempDEntry;
- if (!source.GetDEntry(index, tempDEntry))
+ std::optional<DEntry> tempDEntry = source.GetDEntry(index);
+ if (!tempDEntry)
return NOMEMCARD;
u32 size = source.DEntry_BlockCount(index);
@@ -825,7 +796,7 @@ u32 GCMemcard::CopyFrom(const GCMemcard& source, u8 index)
return NOMEMCARD;
default:
FixChecksums();
- return ImportFile(tempDEntry, saveData);
+ return ImportFile(*tempDEntry, saveData);
}
}
@@ -882,19 +853,19 @@ u32 GCMemcard::ImportGciInternal(File::IOFile&& gci, const std::string& inputFil
Gcs_SavConvert(tempDEntry, offset, length);
- if (length != BE16(tempDEntry.BlockCount) * BLOCK_SIZE)
+ if (length != tempDEntry.m_block_count * BLOCK_SIZE)
return LENGTHFAIL;
if (gci.Tell() != offset + DENTRY_SIZE) // Verify correct file position
return OPENFAIL;
- u32 size = BE16((tempDEntry.BlockCount));
+ u32 size = tempDEntry.m_block_count;
std::vector<GCMBlock> saveData;
saveData.reserve(size);
for (unsigned int i = 0; i < size; ++i)
{
GCMBlock b;
- gci.ReadBytes(b.block, BLOCK_SIZE);
+ gci.ReadBytes(b.m_block.data(), b.m_block.size());
saveData.push_back(b);
}
u32 ret;
@@ -910,12 +881,12 @@ u32 GCMemcard::ImportGciInternal(File::IOFile&& gci, const std::string& inputFil
if (!gci2.WriteBytes(&tempDEntry, DENTRY_SIZE))
completeWrite = false;
- int fileBlocks = BE16(tempDEntry.BlockCount);
+ int fileBlocks = tempDEntry.m_block_count;
gci2.Seek(DENTRY_SIZE, SEEK_SET);
for (int i = 0; i < fileBlocks; ++i)
{
- if (!gci2.WriteBytes(saveData[i].block, BLOCK_SIZE))
+ if (!gci2.WriteBytes(saveData[i].m_block.data(), saveData[i].m_block.size()))
completeWrite = false;
}
@@ -980,14 +951,12 @@ u32 GCMemcard::ExportGci(u8 index, const std::string& fileName, const std::strin
break;
}
- DEntry tempDEntry;
- if (!GetDEntry(index, tempDEntry))
- {
+ std::optional<DEntry> tempDEntry = GetDEntry(index);
+ if (!tempDEntry)
return NOMEMCARD;
- }
- Gcs_SavConvert(tempDEntry, offset);
- gci.WriteBytes(&tempDEntry, DENTRY_SIZE);
+ Gcs_SavConvert(*tempDEntry, offset);
+ gci.WriteBytes(&tempDEntry.value(), DENTRY_SIZE);
u32 size = DEntry_BlockCount(index);
if (size == 0xFFFF)
@@ -1008,7 +977,7 @@ u32 GCMemcard::ExportGci(u8 index, const std::string& fileName, const std::strin
gci.Seek(DENTRY_SIZE + offset, SEEK_SET);
for (unsigned int i = 0; i < size; ++i)
{
- gci.WriteBytes(saveData[i].block, BLOCK_SIZE);
+ gci.WriteBytes(saveData[i].m_block.data(), saveData[i].m_block.size());
}
if (gci.IsGood())
@@ -1028,7 +997,7 @@ void GCMemcard::Gcs_SavConvert(DEntry& tempDEntry, int saveType, int length)
// It is stored only within the corresponding GSV file.
// If the GCS file is added without using the GameSaves software,
// the value stored is always "1"
- *(u16*)&tempDEntry.BlockCount = BE16(length / BLOCK_SIZE);
+ tempDEntry.m_block_count = length / BLOCK_SIZE;
}
break;
case SAV:
@@ -1036,18 +1005,39 @@ void GCMemcard::Gcs_SavConvert(DEntry& tempDEntry, int saveType, int length)
// 0x2C and 0x2D, 0x2E and 0x2F, 0x30 and 0x31, 0x32 and 0x33,
// 0x34 and 0x35, 0x36 and 0x37, 0x38 and 0x39, 0x3A and 0x3B,
// 0x3C and 0x3D,0x3E and 0x3F.
- // It seems that sav files also swap the BIFlags...
- ByteSwap(&tempDEntry.Unused1, &tempDEntry.BIFlags);
- ArrayByteSwap((tempDEntry.ImageOffset));
- ArrayByteSwap(&(tempDEntry.ImageOffset[2]));
- ArrayByteSwap((tempDEntry.IconFmt));
- ArrayByteSwap((tempDEntry.AnimSpeed));
- ByteSwap(&tempDEntry.Permissions, &tempDEntry.CopyCounter);
- ArrayByteSwap((tempDEntry.FirstBlock));
- ArrayByteSwap((tempDEntry.BlockCount));
- ArrayByteSwap((tempDEntry.Unused2));
- ArrayByteSwap((tempDEntry.CommentsAddr));
- ArrayByteSwap(&(tempDEntry.CommentsAddr[2]));
+ // It seems that sav files also swap the banner/icon flags...
+ ByteSwap(&tempDEntry.m_unused_1, &tempDEntry.m_banner_and_icon_flags);
+
+ std::array<u8, 4> tmp;
+ memcpy(tmp.data(), &tempDEntry.m_image_offset, 4);
+ ByteSwap(&tmp[0], &tmp[1]);
+ ByteSwap(&tmp[2], &tmp[3]);
+ memcpy(&tempDEntry.m_image_offset, tmp.data(), 4);
+
+ memcpy(tmp.data(), &tempDEntry.m_icon_format, 2);
+ ByteSwap(&tmp[0], &tmp[1]);
+ memcpy(&tempDEntry.m_icon_format, tmp.data(), 2);
+
+ memcpy(tmp.data(), &tempDEntry.m_animation_speed, 2);
+ ByteSwap(&tmp[0], &tmp[1]);
+ memcpy(&tempDEntry.m_animation_speed, tmp.data(), 2);
+
+ ByteSwap(&tempDEntry.m_file_permissions, &tempDEntry.m_copy_counter);
+
+ memcpy(tmp.data(), &tempDEntry.m_first_block, 2);
+ ByteSwap(&tmp[0], &tmp[1]);
+ memcpy(&tempDEntry.m_first_block, tmp.data(), 2);
+
+ memcpy(tmp.data(), &tempDEntry.m_block_count, 2);
+ ByteSwap(&tmp[0], &tmp[1]);
+ memcpy(&tempDEntry.m_block_count, tmp.data(), 2);
+
+ ByteSwap(&tempDEntry.m_unused_2[0], &tempDEntry.m_unused_2[1]);
+
+ memcpy(tmp.data(), &tempDEntry.m_comments_address, 4);
+ ByteSwap(&tmp[0], &tmp[1]);
+ ByteSwap(&tmp[2], &tmp[3]);
+ memcpy(&tempDEntry.m_comments_address, tmp.data(), 4);
break;
default:
break;
@@ -1059,7 +1049,7 @@ bool GCMemcard::ReadBannerRGBA8(u8 index, u32* buffer) const
if (!m_valid || index >= DIRLEN)
return false;
- int flags = CurrentDir->Dir[index].BIFlags;
+ int flags = GetActiveDirectory().m_dir_entries[index].m_banner_and_icon_flags;
// Timesplitters 2 is the only game that I see this in
// May be a hack
if (flags == 0xFB)
@@ -1070,10 +1060,10 @@ bool GCMemcard::ReadBannerRGBA8(u8 index, u32* buffer) const
if (bnrFormat == 0)
return false;
- u32 DataOffset = BE32(CurrentDir->Dir[index].ImageOffset);
- u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock) - MC_FST_BLOCKS;
+ u32 DataOffset = GetActiveDirectory().m_dir_entries[index].m_image_offset;
+ u32 DataBlock = GetActiveDirectory().m_dir_entries[index].m_first_block - MC_FST_BLOCKS;
- if ((DataBlock > maxBlock) || (DataOffset == 0xFFFFFFFF))
+ if ((DataBlock > m_size_blocks) || (DataOffset == 0xFFFFFFFF))
{
return false;
}
@@ -1082,14 +1072,14 @@ bool GCMemcard::ReadBannerRGBA8(u8 index, u32* buffer) const
if (bnrFormat & 1)
{
- u8* pxdata = (u8*)(mc_data_blocks[DataBlock].block + DataOffset);
- u16* paldata = (u16*)(mc_data_blocks[DataBlock].block + DataOffset + pixels);
+ u8* pxdata = (u8*)(m_data_blocks[DataBlock].m_block.data() + DataOffset);
+ u16* paldata = (u16*)(m_data_blocks[DataBlock].m_block.data() + DataOffset + pixels);
Common::DecodeCI8Image(buffer, pxdata, paldata, 96, 32);
}
else
{
- u16* pxdata = (u16*)(mc_data_blocks[DataBlock].block + DataOffset);
+ u16* pxdata = (u16*)(m_data_blocks[DataBlock].m_block.data() + DataOffset);
Common::Decode5A3Image(buffer, pxdata, 96, 32);
}
@@ -1105,10 +1095,10 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8* delays) const
// Sonic Heroes it the only game I have seen that tries to use a CI8 and RGB5A3 icon
// int fmtCheck = 0;
- int formats = BE16(CurrentDir->Dir[index].IconFmt);
- int fdelays = BE16(CurrentDir->Dir[index].AnimSpeed);
+ int formats = GetActiveDirectory().m_dir_entries[index].m_icon_format;
+ int fdelays = GetActiveDirectory().m_dir_entries[index].m_animation_speed;
- int flags = CurrentDir->Dir[index].BIFlags;
+ int flags = GetActiveDirectory().m_dir_entries[index].m_banner_and_icon_flags;
// Timesplitters 2 and 3 is the only game that I see this in
// May be a hack
// if (flags == 0xFB) flags = ~flags;
@@ -1117,15 +1107,15 @@ u32 GCMemcard::ReadAnimRGBA8(u8 index, u32* buffer, u8* delays) const
int bnrFormat = (flags & 3);
- u32 DataOffset = BE32(CurrentDir->Dir[index].ImageOffset);
- u32 DataBlock = BE16(CurrentDir->Dir[index].FirstBlock) - MC_FST_BLOCKS;
+ u32 DataOffset = GetActiveDirectory().m_dir_entries[index].m_image_offset;
+ u32 DataBlock = GetActiveDirectory().m_dir_entries[index].m_first_block - MC_FST_BLOCKS;
- if ((DataBlock > maxBlock) || (DataOffset == 0xFFFFFFFF))
+ if ((DataBlock > m_size_blocks) || (DataOffset == 0xFFFFFFFF))
{
return 0;
}
- u8* animData = (u8*)(mc_data_blocks[DataBlock].block + DataOffset);
+ u8* animData = (u8*)(m_data_blocks[DataBlock].m_block.data() + DataOffset);
switch (bnrFormat)
{
@@ -1249,22 +1239,22 @@ bool GCMemcard::Format(u8* card_data, bool shift_jis, u16 SizeMb)
bool GCMemcard::Format(bool shift_jis, u16 SizeMb)
{
- memset(&hdr, 0xFF, BLOCK_SIZE);
- memset(&dir, 0xFF, BLOCK_SIZE);
- memset(&dir_backup, 0xFF, BLOCK_SIZE);
- memset(&bat, 0, BLOCK_SIZE);
- memset(&bat_backup, 0, BLOCK_SIZE);
-
- hdr = Header(SLOT_A, SizeMb, shift_jis);
- dir = dir_backup = Directory();
- bat = bat_backup = BlockAlloc(SizeMb);
-
- m_sizeMb = SizeMb;
- maxBlock = (u32)m_sizeMb * MBIT_TO_BLOCKS;
- mc_data_blocks.clear();
- mc_data_blocks.resize(maxBlock - MC_FST_BLOCKS);
-
- InitDirBatPointers();
+ memset(&m_header_block, 0xFF, BLOCK_SIZE);
+ memset(&m_directory_blocks[0], 0xFF, BLOCK_SIZE);
+ memset(&m_directory_blocks[1], 0xFF, BLOCK_SIZE);
+ memset(&m_bat_blocks[0], 0, BLOCK_SIZE);
+ memset(&m_bat_blocks[1], 0, BLOCK_SIZE);
+
+ m_header_block = Header(SLOT_A, SizeMb, shift_jis);
+ m_directory_blocks[0] = m_directory_blocks[1] = Directory();
+ m_bat_blocks[0] = m_bat_blocks[1] = BlockAlloc(SizeMb);
+
+ m_size_mb = SizeMb;
+ m_size_blocks = (u32)m_size_mb * MBIT_TO_BLOCKS;
+ m_data_blocks.clear();
+ m_data_blocks.resize(m_size_blocks - MC_FST_BLOCKS);
+
+ InitActiveDirBat();
m_valid = true;
return Save();
@@ -1290,22 +1280,22 @@ s32 GCMemcard::FZEROGX_MakeSaveGameValid(const Header& cardheader, const DEntry&
int block = 0;
// check for F-Zero GX system file
- if (strcmp(reinterpret_cast<const char*>(direntry.Filename), "f_zero.dat") != 0)
+ if (strcmp(reinterpret_cast<const char*>(direntry.m_filename.data()), "f_zero.dat") != 0)
return 0;
// get encrypted destination memory card serial numbers
cardheader.CARD_GetSerialNo(&serial1, &serial2);
// set new serial numbers
- *(u16*)&FileBuffer[1].block[0x0066] = BE16(BE32(serial1) >> 16);
- *(u16*)&FileBuffer[3].block[0x1580] = BE16(BE32(serial2) >> 16);
- *(u16*)&FileBuffer[1].block[0x0060] = BE16(BE32(serial1) & 0xFFFF);
- *(u16*)&FileBuffer[1].block[0x0200] = BE16(BE32(serial2) & 0xFFFF);
+ *(u16*)&FileBuffer[1].m_block[0x0066] = BE16(BE32(serial1) >> 16);
+ *(u16*)&FileBuffer[3].m_block[0x1580] = BE16(BE32(serial2) >> 16);
+ *(u16*)&FileBuffer[1].m_block[0x0060] = BE16(BE32(serial1) & 0xFFFF);
+ *(u16*)&FileBuffer[1].m_block[0x0200] = BE16(BE32(serial2) & 0xFFFF);
// calc 16-bit checksum
for (i = 0x02; i < 0x8000; i++)
{
- chksum ^= (FileBuffer[block].block[i - (block * 0x2000)] & 0xFF);
+ chksum ^= (FileBuffer[block].m_block[i - (block * 0x2000)] & 0xFF);
for (j = 8; j > 0; j--)
{
if (chksum & 1)
@@ -1318,7 +1308,7 @@ s32 GCMemcard::FZEROGX_MakeSaveGameValid(const Header& cardheader, const DEntry&
}
// set new checksum
- *(u16*)&FileBuffer[0].block[0x00] = BE16(~chksum);
+ *(u16*)&FileBuffer[0].m_block[0x00] = BE16(~chksum);
return 1;
}
@@ -1344,10 +1334,10 @@ s32 GCMemcard::PSO_MakeSaveGameValid(const Header& cardheader, const DEntry& dir
u32 pso3offset = 0x00;
// check for PSO1&2 system file
- if (strcmp(reinterpret_cast<const char*>(direntry.Filename), "PSO_SYSTEM") != 0)
+ if (strcmp(reinterpret_cast<const char*>(direntry.m_filename.data()), "PSO_SYSTEM") != 0)
{
// check for PSO3 system file
- if (strcmp(reinterpret_cast<const char*>(direntry.Filename), "PSO3_SYSTEM") == 0)
+ if (strcmp(reinterpret_cast<const char*>(direntry.m_filename.data()), "PSO3_SYSTEM") == 0)
{
// PSO3 data block size adjustment
pso3offset = 0x10;
@@ -1363,8 +1353,8 @@ s32 GCMemcard::PSO_MakeSaveGameValid(const Header& cardheader, const DEntry& dir
cardheader.CARD_GetSerialNo(&serial1, &serial2);
// set new serial numbers
- *(u32*)&FileBuffer[1].block[0x0158] = serial1;
- *(u32*)&FileBuffer[1].block[0x015C] = serial2;
+ *(u32*)&FileBuffer[1].m_block[0x0158] = serial1;
+ *(u32*)&FileBuffer[1].m_block[0x015C] = serial2;
// generate crc32 LUT
for (i = 0; i < 256; i++)
@@ -1387,11 +1377,11 @@ s32 GCMemcard::PSO_MakeSaveGameValid(const Header& cardheader, const DEntry& dir
// calc 32-bit checksum
for (i = 0x004C; i < 0x0164 + pso3offset; i++)
{
- chksum = ((chksum >> 8) & 0xFFFFFF) ^ crc32LUT[(chksum ^ FileBuffer[1].block[i]) & 0xFF];
+ chksum = ((chksum >> 8) & 0xFFFFFF) ^ crc32LUT[(chksum ^ FileBuffer[1].m_block[i]) & 0xFF];
}
// set new checksum
- *(u32*)&FileBuffer[1].block[0x0048] = BE32(chksum ^ 0xFFFFFFFF);
+ *(u32*)&FileBuffer[1].m_block[0x0048] = BE32(chksum ^ 0xFFFFFFFF);
return 1;
}
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcard.h b/Source/Core/Core/HW/GCMemcard/GCMemcard.h
index 6dfd50e8f8..662257715d 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcard.h
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcard.h
@@ -5,6 +5,7 @@
#pragma once
#include <algorithm>
+#include <array>
#include <string>
#include <vector>
@@ -92,42 +93,68 @@ protected:
struct GCMBlock
{
GCMBlock() { Erase(); }
- void Erase() { memset(block, 0xFF, BLOCK_SIZE); }
- u8 block[BLOCK_SIZE];
+ void Erase() { memset(m_block.data(), 0xFF, m_block.size()); }
+ std::array<u8, BLOCK_SIZE> m_block;
};
void calc_checksumsBE(const u16* buf, u32 length, u16* csum, u16* inv_csum);
#pragma pack(push, 1)
-struct Header // Offset Size Description
+struct Header
{
- // Serial in libogc
- u8 serial[12]; // 0x0000 12 ?
- u64 formatTime; // 0x000c 8 Time of format (OSTime value)
- u32 SramBias; // 0x0014 4 SRAM bias at time of format
- u32 SramLang; // 0x0018 4 SRAM language
- u8 Unk2[4]; // 0x001c 4 ? almost always 0
- // end Serial in libogc
- u8 deviceID[2]; // 0x0020 2 0 if formated in slot A 1 if formated in slot B
- u8 SizeMb[2]; // 0x0022 2 Size of memcard in Mbits
- u16 Encoding; // 0x0024 2 Encoding (Windows-1252 or Shift JIS)
- u8 Unused1[468]; // 0x0026 468 Unused (0xff)
- u16 UpdateCounter; // 0x01fa 2 Update Counter (?, probably unused)
- u16 Checksum; // 0x01fc 2 Additive Checksum
- u16 Checksum_Inv; // 0x01fe 2 Inverse Checksum
- u8 Unused2[7680]; // 0x0200 0x1e00 Unused (0xff)
+ // NOTE: libogc refers to 'Serial' as the first 0x20 bytes of the header,
+ // so the data from m_serial until m_unknown_2 (inclusive)
+
+ // 12 bytes at 0x0000
+ std::array<u8, 12> m_serial;
+
+ // 8 bytes at 0x000c: Time of format (OSTime value)
+ Common::BigEndianValue<u64> m_format_time;
+
+ // 4 bytes at 0x0014; SRAM bias at time of format
+ u32 m_sram_bias;
+
+ // 4 bytes at 0x0018: SRAM language
+ Common::BigEndianValue<u32> m_sram_language;
+
+ // 4 bytes at 0x001c: ? almost always 0
+ std::array<u8, 4> m_unknown_2;
+
+ // 2 bytes at 0x0020: 0 if formated in slot A, 1 if formated in slot B
+ Common::BigEndianValue<u16> m_device_id;
+
+ // 2 bytes at 0x0022: Size of memcard in Mbits
+ Common::BigEndianValue<u16> m_size_mb;
+
+ // 2 bytes at 0x0024: Encoding (Windows-1252 or Shift JIS)
+ Common::BigEndianValue<u16> m_encoding;
+
+ // 468 bytes at 0x0026: Unused (0xff)
+ std::array<u8, 468> m_unused_1;
+
+ // 2 bytes at 0x01fa: Update Counter (?, probably unused)
+ u16 m_update_counter;
+
+ // 2 bytes at 0x01fc: Additive Checksum
+ u16 m_checksum;
+
+ // 2 bytes at 0x01fe: Inverse Checksum
+ u16 m_checksum_inv;
+
+ // 0x1e00 bytes at 0x0200: Unused (0xff)
+ std::array<u8, 7680> m_unused_2;
void CARD_GetSerialNo(u32* serial1, u32* serial2) const
{
- u32 _serial[8];
+ u32 serial[8];
for (int i = 0; i < 8; i++)
{
- memcpy(&_serial[i], (u8*)this + (i * 4), 4);
+ memcpy(&serial[i], (u8*)this + (i * 4), 4);
}
- *serial1 = _serial[0] ^ _serial[2] ^ _serial[4] ^ _serial[6];
- *serial2 = _serial[1] ^ _serial[3] ^ _serial[5] ^ _serial[7];
+ *serial1 = serial[0] ^ serial[2] ^ serial[4] ^ serial[6];
+ *serial2 = serial[1] ^ serial[3] ^ serial[5] ^ serial[7];
}
// Nintendo format algorithm.
@@ -136,41 +163,53 @@ struct Header // Offset Size Description
explicit Header(int slot = 0, u16 sizeMb = MemCard2043Mb, bool shift_jis = false)
{
memset(this, 0xFF, BLOCK_SIZE);
- *(u16*)SizeMb = BE16(sizeMb);
- Encoding = BE16(shift_jis ? 1 : 0);
+ m_size_mb = sizeMb;
+ m_encoding = shift_jis ? 1 : 0;
u64 rand = Common::Timer::GetLocalTimeSinceJan1970() - ExpansionInterface::CEXIIPL::GC_EPOCH;
- formatTime = Common::swap64(rand);
+ m_format_time = rand;
for (int i = 0; i < 12; i++)
{
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
- serial[i] = (u8)(g_SRAM.settings_ex.flash_id[slot][i] + (u32)rand);
+ m_serial[i] = (u8)(g_SRAM.settings_ex.flash_id[slot][i] + (u32)rand);
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
rand &= (u64)0x0000000000007fffULL;
}
- SramBias = g_SRAM.settings.rtc_bias;
- SramLang = BE32(g_SRAM.settings.language);
- // TODO: determine the purpose of Unk2 1 works for slot A, 0 works for both slot A and slot B
- *(u32*)&Unk2 = 0; // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
- *(u16*)&deviceID = 0;
- calc_checksumsBE((u16*)this, 0xFE, &Checksum, &Checksum_Inv);
+ m_sram_bias = g_SRAM.settings.rtc_bias;
+ m_sram_language = static_cast<u32>(g_SRAM.settings.language);
+ // TODO: determine the purpose of m_unknown_2
+ // 1 works for slot A, 0 works for both slot A and slot B
+ memset(m_unknown_2.data(), 0,
+ m_unknown_2.size()); // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
+ m_device_id = 0;
+ calc_checksumsBE((u16*)this, 0xFE, &m_checksum, &m_checksum_inv);
}
};
+static_assert(sizeof(Header) == BLOCK_SIZE);
struct DEntry
{
- static const u8 DENTRY_SIZE = 0x40;
DEntry() { memset(this, 0xFF, DENTRY_SIZE); }
std::string GCI_FileName() const
{
- std::string filename = std::string((char*)Makercode, 2) + '-' +
- std::string((char*)Gamecode, 4) + '-' + (char*)Filename + ".gci";
+ 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);
}
- u8 Gamecode[4]; // 0x00 0x04 Gamecode
- u8 Makercode[2]; // 0x04 0x02 Makercode
- u8 Unused1; // 0x06 0x01 reserved/unused (always 0xff, has no effect)
- u8 BIFlags; // 0x07 0x01 banner gfx format and icon animation (Image Key)
+ static constexpr std::array<u8, 4> UNINITIALIZED_GAMECODE{{0xFF, 0xFF, 0xFF, 0xFF}};
+
+ // 4 bytes at 0x00: Gamecode
+ std::array<u8, 4> m_gamecode;
+
+ // 2 bytes at 0x04: Makercode
+ std::array<u8, 2> m_makercode;
+
+ // 1 byte at 0x06: reserved/unused (always 0xff, has no effect)
+ u8 m_unused_1;
+
+ // 1 byte at 0x07: banner gfx format and icon animation (Image Key)
// Bit(s) Description
// 2 Icon Animation 0: forward 1: ping-pong
// 1 [--0: No Banner 1: Banner present--] WRONG! YAGCD LIES!
@@ -180,98 +219,133 @@ struct DEntry
// 01 CI8 banner
// 10 RGB5A3 banner
// 11 ? maybe ==00? Time Splitters 2 and 3 have it and don't have banner
- //
- u8 Filename[DENTRY_STRLEN]; // 0x08 0x20 Filename
- u8 ModTime[4]; // 0x28 0x04 Time of file's last modification in seconds since 12am,
- // January 1st, 2000
- u8 ImageOffset[4]; // 0x2c 0x04 image data offset
- u8 IconFmt[2]; // 0x30 0x02 icon gfx format (2bits per icon)
+ u8 m_banner_and_icon_flags;
+
+ // 0x20 bytes at 0x08: Filename
+ std::array<u8, DENTRY_STRLEN> m_filename;
+
+ // 4 bytes at 0x28: Time of file's last modification in seconds since 12am, January 1st, 2000
+ Common::BigEndianValue<u32> m_modification_time;
+
+ // 4 bytes at 0x2c: image data offset
+ Common::BigEndianValue<u32> m_image_offset;
+
+ // 2 bytes at 0x30: icon gfx format (2bits per icon)
// Bits Description
// 00 No icon
// 01 CI8 with a shared color palette after the last frame
// 10 RGB5A3
// 11 CI8 with a unique color palette after itself
- //
- u8 AnimSpeed[2]; // 0x32 0x02 Animation speed (2bits per icon) (*1)
+ Common::BigEndianValue<u16> m_icon_format;
+
+ // 2 bytes at 0x32: Animation speed (2bits per icon)
// Bits Description
// 00 No icon
// 01 Icon lasts for 4 frames
// 10 Icon lasts for 8 frames
// 11 Icon lasts for 12 frames
- //
- u8 Permissions; // 0x34 0x01 File-permissions
+ Common::BigEndianValue<u16> m_animation_speed;
+
+ // 1 byte at 0x34: File-permissions
// Bit Permission Description
// 4 no move File cannot be moved by the IPL
// 3 no copy File cannot be copied by the IPL
// 2 public Can be read by any game
- //
- u8 CopyCounter; // 0x35 0x01 Copy counter (*2)
- u8 FirstBlock[2]; // 0x36 0x02 Block no of first block of file (0 == offset 0)
- u8 BlockCount[2]; // 0x38 0x02 File-length (number of blocks in file)
- u8 Unused2[2]; // 0x3a 0x02 Reserved/unused (always 0xffff, has no effect)
- u8 CommentsAddr[4]; // 0x3c 0x04 Address of the two comments within the file data (*3)
+ u8 m_file_permissions;
+
+ // 1 byte at 0x35: Copy counter
+ u8 m_copy_counter;
+
+ // 2 bytes at 0x36: Block number of first block of file (0 == offset 0)
+ Common::BigEndianValue<u16> m_first_block;
+
+ // 2 bytes at 0x38: File-length (number of blocks in file)
+ Common::BigEndianValue<u16> m_block_count;
+
+ // 2 bytes at 0x3a: Reserved/unused (always 0xffff, has no effect)
+ std::array<u8, 2> m_unused_2;
+
+ // 4 bytes at 0x3c: Address of the two comments within the file data
+ Common::BigEndianValue<u32> m_comments_address;
};
+static_assert(sizeof(DEntry) == DENTRY_SIZE);
struct Directory
{
- DEntry Dir[DIRLEN]; // 0x0000 Directory Entries (max 127)
- u8 Padding[0x3a];
- u16 UpdateCounter; // 0x1ffa 2 Update Counter
- u16 Checksum; // 0x1ffc 2 Additive Checksum
- u16 Checksum_Inv; // 0x1ffe 2 Inverse Checksum
+ std::array<DEntry, DIRLEN> m_dir_entries; // 0x0000 Directory Entries (max 127)
+ std::array<u8, 0x3a> m_padding;
+ Common::BigEndianValue<u16> m_update_counter; // 0x1ffa 2 Update Counter
+ u16 m_checksum; // 0x1ffc 2 Additive Checksum
+ u16 m_checksum_inv; // 0x1ffe 2 Inverse Checksum
Directory()
{
memset(this, 0xFF, BLOCK_SIZE);
- UpdateCounter = 0;
- Checksum = BE16(0xF003);
- Checksum_Inv = 0;
+ m_update_counter = 0;
+ m_checksum = BE16(0xF003);
+ m_checksum_inv = 0;
}
void Replace(DEntry d, int idx)
{
- Dir[idx] = d;
+ m_dir_entries[idx] = d;
fixChecksums();
}
- void fixChecksums() { calc_checksumsBE((u16*)this, 0xFFE, &Checksum, &Checksum_Inv); }
+ void fixChecksums() { calc_checksumsBE((u16*)this, 0xFFE, &m_checksum, &m_checksum_inv); }
};
+static_assert(sizeof(Directory) == BLOCK_SIZE);
struct BlockAlloc
{
- u16 Checksum; // 0x0000 2 Additive Checksum
- u16 Checksum_Inv; // 0x0002 2 Inverse Checksum
- u16 UpdateCounter; // 0x0004 2 Update Counter
- u16 FreeBlocks; // 0x0006 2 Free Blocks
- u16 LastAllocated; // 0x0008 2 Last allocated Block
- u16 Map[BAT_SIZE]; // 0x000a 0x1ff8 Map of allocated Blocks
+ // 2 bytes at 0x0000: Additive Checksum
+ u16 m_checksum;
+
+ // 2 bytes at 0x0002: Inverse Checksum
+ u16 m_checksum_inv;
+
+ // 2 bytes at 0x0004: Update Counter
+ Common::BigEndianValue<u16> m_update_counter;
+
+ // 2 bytes at 0x0006: Free Blocks
+ Common::BigEndianValue<u16> m_free_blocks;
+
+ // 2 bytes at 0x0008: Last allocated Block
+ Common::BigEndianValue<u16> m_last_allocated_block;
+
+ // 0x1ff8 bytes at 0x000a: Map of allocated Blocks
+ std::array<Common::BigEndianValue<u16>, BAT_SIZE> m_map;
+
u16 GetNextBlock(u16 Block) const;
u16 NextFreeBlock(u16 MaxBlock, u16 StartingBlock = MC_FST_BLOCKS) const;
bool ClearBlocks(u16 StartingBlock, u16 Length);
- void fixChecksums() { calc_checksumsBE((u16*)&UpdateCounter, 0xFFE, &Checksum, &Checksum_Inv); }
+ void fixChecksums()
+ {
+ calc_checksumsBE((u16*)&m_update_counter, 0xFFE, &m_checksum, &m_checksum_inv);
+ }
explicit BlockAlloc(u16 sizeMb = MemCard2043Mb)
{
memset(this, 0, BLOCK_SIZE);
- // UpdateCounter = 0;
- FreeBlocks = BE16((sizeMb * MBIT_TO_BLOCKS) - MC_FST_BLOCKS);
- LastAllocated = BE16(4);
+ m_free_blocks = (sizeMb * MBIT_TO_BLOCKS) - MC_FST_BLOCKS;
+ m_last_allocated_block = 4;
fixChecksums();
}
u16 AssignBlocksContiguous(u16 length)
{
- u16 starting = BE16(LastAllocated) + 1;
- if (length > BE16(FreeBlocks))
+ u16 starting = m_last_allocated_block + 1;
+ if (length > m_free_blocks)
return 0xFFFF;
u16 current = starting;
while ((current - starting + 1) < length)
{
- Map[current - 5] = BE16(current + 1);
+ m_map[current - 5] = current + 1;
current++;
}
- Map[current - 5] = 0xFFFF;
- LastAllocated = BE16(current);
- FreeBlocks = BE16(BE16(FreeBlocks) - length);
+ m_map[current - 5] = 0xFFFF;
+ m_last_allocated_block = current;
+ m_free_blocks = m_free_blocks - length;
fixChecksums();
- return BE16(starting);
+ return starting;
}
};
+static_assert(sizeof(BlockAlloc) == BLOCK_SIZE);
#pragma pack(pop)
class GCIFile
@@ -280,9 +354,11 @@ public:
bool LoadSaveBlocks();
bool HasCopyProtection() const
{
- if ((strcmp((char*)m_gci_header.Filename, "PSO_SYSTEM") == 0) ||
- (strcmp((char*)m_gci_header.Filename, "PSO3_SYSTEM") == 0) ||
- (strcmp((char*)m_gci_header.Filename, "f_zero.dat") == 0))
+ if ((strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO_SYSTEM") ==
+ 0) ||
+ (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO3_SYSTEM") ==
+ 0) ||
+ (strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "f_zero.dat") == 0))
return true;
return false;
}
@@ -300,20 +376,28 @@ class GCMemcard
{
private:
bool m_valid;
- std::string m_fileName;
+ std::string m_filename;
- u32 maxBlock;
- u16 m_sizeMb;
+ u32 m_size_blocks;
+ u16 m_size_mb;
- Header hdr;
- Directory dir, dir_backup, *CurrentDir, *PreviousDir;
- BlockAlloc bat, bat_backup, *CurrentBat, *PreviousBat;
+ Header m_header_block;
+ std::array<Directory, 2> m_directory_blocks;
+ std::array<BlockAlloc, 2> m_bat_blocks;
+ std::vector<GCMBlock> m_data_blocks;
- std::vector<GCMBlock> mc_data_blocks;
+ int m_active_directory;
+ int m_active_bat;
u32 ImportGciInternal(File::IOFile&& gci, const std::string& inputFile,
const std::string& outputFile);
- void InitDirBatPointers();
+ void InitActiveDirBat();
+
+ const Directory& GetActiveDirectory() const;
+ const BlockAlloc& GetActiveBat() const;
+
+ void UpdateDirectory(const Directory& directory);
+ void UpdateBat(const BlockAlloc& bat);
public:
explicit GCMemcard(const std::string& fileName, bool forceCreation = false,
@@ -366,8 +450,9 @@ public:
u32 DEntry_CommentsAddress(u8 index) const;
std::string GetSaveComment1(u8 index) const;
std::string GetSaveComment2(u8 index) const;
- // Copies a DEntry from u8 index to DEntry& data
- bool GetDEntry(u8 index, DEntry& dest) const;
+
+ // Fetches a DEntry from the given file index.
+ std::optional<DEntry> GetDEntry(u8 index) const;
u32 GetSaveData(u8 index, std::vector<GCMBlock>& saveBlocks) const;
diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
index 476aae5e3c..9d4aa83f44 100644
--- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
+++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp
@@ -58,7 +58,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
}
}
- u16 num_blocks = BE16(gci.m_gci_header.BlockCount);
+ u16 num_blocks = gci.m_gci_header.m_block_count;
// largest number of free blocks on a memory card
// in reality, there are not likely any valid gci files > 251 blocks
if (num_blocks > 2043)
@@ -79,7 +79,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
return NO_INDEX;
}
- if (m_game_id == BE32(gci.m_gci_header.Gamecode))
+ if (m_game_id == BE32(gci.m_gci_header.m_gamecode.data()))
{
gci.LoadSaveBlocks();
}
@@ -89,8 +89,8 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
{
return NO_INDEX;
}
- int total_blocks = BE16(m_hdr.SizeMb) * MBIT_TO_BLOCKS - MC_FST_BLOCKS;
- int free_blocks = BE16(m_bat1.FreeBlocks);
+ int total_blocks = m_hdr.m_size_mb * MBIT_TO_BLOCKS - MC_FST_BLOCKS;
+ int free_blocks = m_bat1.m_free_blocks;
if (total_blocks > free_blocks * 10)
{
PanicAlertT("%s\nwas not loaded because there is less than 10%% free blocks available on "
@@ -108,7 +108,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
file_name.c_str());
return NO_INDEX;
}
- *(u16*)&gci.m_gci_header.FirstBlock = first_block;
+ gci.m_gci_header.m_first_block = first_block;
if (gci.HasCopyProtection() && gci.LoadSaveBlocks())
{
GCMemcard::PSO_MakeSaveGameValid(m_hdr, gci.m_gci_header, gci.m_save_data);
@@ -151,7 +151,7 @@ std::vector<std::string> GCMemcardDirectory::GetFileNamesForGameID(const std::st
if (std::find(loaded_saves.begin(), loaded_saves.end(), gci_filename) != loaded_saves.end())
continue;
- const u16 num_blocks = BE16(gci.m_gci_header.BlockCount);
+ const u16 num_blocks = gci.m_gci_header.m_block_count;
// largest number of free blocks on a memory card
// in reality, there are not likely any valid gci files > 251 blocks
if (num_blocks > 2043)
@@ -166,7 +166,7 @@ std::vector<std::string> GCMemcardDirectory::GetFileNamesForGameID(const std::st
// card (see above method), but since we're only loading the saves for one GameID here, we're
// definitely not going to run out of space.
- if (game_code == BE32(gci.m_gci_header.Gamecode))
+ if (game_code == BE32(gci.m_gci_header.m_gamecode.data()))
{
loaded_saves.push_back(gci_filename);
filenames.push_back(file_name);
@@ -429,16 +429,17 @@ inline void GCMemcardDirectory::SyncSaves()
{
Directory* current = &m_dir2;
- if (BE16(m_dir1.UpdateCounter) > BE16(m_dir2.UpdateCounter))
+ if (m_dir1.m_update_counter > m_dir2.m_update_counter)
{
current = &m_dir1;
}
for (u32 i = 0; i < DIRLEN; ++i)
{
- if (BE32(current->Dir[i].Gamecode) != 0xFFFFFFFF)
+ if (current->m_dir_entries[i].m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
{
- INFO_LOG(EXPANSIONINTERFACE, "Syncing save 0x%x", *(u32*)&(current->Dir[i].Gamecode));
+ INFO_LOG(EXPANSIONINTERFACE, "Syncing save 0x%x",
+ BE32(current->m_dir_entries[i].m_gamecode.data()));
bool added = false;
while (i >= m_saves.size())
{
@@ -447,20 +448,22 @@ inline void GCMemcardDirectory::SyncSaves()
added = true;
}
- if (added || memcmp((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->Dir[i]), DENTRY_SIZE))
+ if (added ||
+ memcmp((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->m_dir_entries[i]), DENTRY_SIZE))
{
m_saves[i].m_dirty = true;
- u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
- u32 new_gamecode = BE32(current->Dir[i].Gamecode);
- u32 old_start = BE16(m_saves[i].m_gci_header.FirstBlock);
- u32 new_start = BE16(current->Dir[i].FirstBlock);
+ u32 gamecode = BE32(m_saves[i].m_gci_header.m_gamecode.data());
+ u32 new_gamecode = BE32(current->m_dir_entries[i].m_gamecode.data());
+ u32 old_start = m_saves[i].m_gci_header.m_first_block;
+ u32 new_start = current->m_dir_entries[i].m_first_block;
if ((gamecode != 0xFFFFFFFF) && (gamecode != new_gamecode))
{
PanicAlertT("Game overwrote with another games save. Data corruption ahead 0x%x, 0x%x",
- BE32(m_saves[i].m_gci_header.Gamecode), BE32(current->Dir[i].Gamecode));
+ BE32(m_saves[i].m_gci_header.m_gamecode.data()),
+ BE32(current->m_dir_entries[i].m_gamecode.data()));
}
- memcpy((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->Dir[i]), DENTRY_SIZE);
+ memcpy((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->m_dir_entries[i]), DENTRY_SIZE);
if (old_start != new_start)
{
INFO_LOG(EXPANSIONINTERFACE, "Save moved from 0x%x to 0x%x", old_start, new_start);
@@ -476,8 +479,8 @@ inline void GCMemcardDirectory::SyncSaves()
else if ((i < m_saves.size()) && (*(u32*)&(m_saves[i].m_gci_header) != 0xFFFFFFFF))
{
INFO_LOG(EXPANSIONINTERFACE, "Clearing and/or deleting save 0x%x",
- BE32(m_saves[i].m_gci_header.Gamecode));
- *(u32*)&(m_saves[i].m_gci_header.Gamecode) = 0xFFFFFFFF;
+ BE32(m_saves[i].m_gci_header.m_gamecode.data()));
+ m_saves[i].m_gci_header.m_gamecode = DEntry::UNINITIALIZED_GAMECODE;
m_saves[i].m_save_data.clear();
m_saves[i].m_used_blocks.clear();
m_saves[i].m_dirty = true;
@@ -488,7 +491,7 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
{
for (u16 i = 0; i < m_saves.size(); ++i)
{
- if (BE32(m_saves[i].m_gci_header.Gamecode) != 0xFFFFFFFF)
+ if (m_saves[i].m_gci_header.m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
{
if (m_saves[i].m_used_blocks.size() == 0)
{
@@ -500,7 +503,7 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
{
if (!m_saves[i].LoadSaveBlocks())
{
- int num_blocks = BE16(m_saves[i].m_gci_header.BlockCount);
+ int num_blocks = m_saves[i].m_gci_header.m_block_count;
while (num_blocks)
{
m_saves[i].m_save_data.emplace_back();
@@ -514,7 +517,7 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
}
m_last_block = block;
- m_last_block_address = m_saves[i].m_save_data[idx].block;
+ m_last_block_address = m_saves[i].m_save_data[idx].m_block.data();
return m_last_block;
}
}
@@ -546,12 +549,12 @@ s32 GCMemcardDirectory::DirectoryWrite(u32 dest_address, u32 length, const u8* s
bool GCMemcardDirectory::SetUsedBlocks(int save_index)
{
BlockAlloc* current_bat;
- if (BE16(m_bat2.UpdateCounter) > BE16(m_bat1.UpdateCounter))
+ if (m_bat2.m_update_counter > m_bat1.m_update_counter)
current_bat = &m_bat2;
else
current_bat = &m_bat1;
- u16 block = BE16(m_saves[save_index].m_gci_header.FirstBlock);
+ u16 block = m_saves[save_index].m_gci_header.m_first_block;
while (block != 0xFFFF)
{
m_saves[save_index].m_used_blocks.push_back(block);
@@ -563,7 +566,7 @@ bool GCMemcardDirectory::SetUsedBlocks(int save_index)
}
}
- u16 num_blocks = BE16(m_saves[save_index].m_gci_header.BlockCount);
+ u16 num_blocks = m_saves[save_index].m_gci_header.m_block_count;
u16 blocks_from_bat = (u16)m_saves[save_index].m_used_blocks.size();
if (blocks_from_bat != num_blocks)
{
@@ -585,7 +588,7 @@ void GCMemcardDirectory::FlushToFile()
{
if (m_saves[i].m_dirty)
{
- if (BE32(m_saves[i].m_gci_header.Gamecode) != 0xFFFFFFFF)
+ if (m_saves[i].m_gci_header.m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
{
m_saves[i].m_dirty = false;
if (m_saves[i].m_save_data.size() == 0)
@@ -654,7 +657,7 @@ void GCMemcardDirectory::FlushToFile()
// simultaneously
// this ensures that the save data for all of the current games gci files are stored in the
// savestate
- u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
+ u32 gamecode = BE32(m_saves[i].m_gci_header.m_gamecode.data());
if (gamecode != m_game_id && gamecode != 0xFFFFFFFF && m_saves[i].m_save_data.size())
{
INFO_LOG(EXPANSIONINTERFACE, "Flushing savedata to disk for %s",
@@ -703,7 +706,7 @@ bool GCIFile::LoadSaveBlocks()
INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str());
save_file.Seek(DENTRY_SIZE, SEEK_SET);
- u16 num_blocks = BE16(m_gci_header.BlockCount);
+ u16 num_blocks = m_gci_header.m_block_count;
m_save_data.resize(num_blocks);
if (!save_file.ReadBytes(m_save_data.data(), num_blocks * BLOCK_SIZE))
{
diff --git a/Source/Core/DolphinQt/GCMemcardManager.cpp b/Source/Core/DolphinQt/GCMemcardManager.cpp
index e57ee0574e..a74a5dc01e 100644
--- a/Source/Core/DolphinQt/GCMemcardManager.cpp
+++ b/Source/Core/DolphinQt/GCMemcardManager.cpp
@@ -199,10 +199,12 @@ void GCMemcardManager::UpdateSlotTable(int slot)
auto* icon = new QTableWidgetItem;
icon->setData(Qt::DecorationRole, frames[0]);
- DEntry d;
- memcard->GetDEntry(file_index, d);
+ std::optional<DEntry> entry = memcard->GetDEntry(file_index);
- const auto speed = ((d.AnimSpeed[0] & 1) << 2) + (d.AnimSpeed[1] & 1);
+ // TODO: This is wrong, the animation speed is not static and is already correctly calculated in
+ // GetIconFromSaveFile(), just not returned
+ const u16 animation_speed = entry ? entry->m_animation_speed : 1;
+ const auto speed = (((animation_speed >> 8) & 1) << 2) + (animation_speed & 1);
m_slot_active_icons[slot].push_back({speed, frames});