Merge pull request #5089 from lioncash/gcmemcard
GCMemcard: Minor cleanup
This commit is contained in:
commit
54e32fd91e
|
@ -70,22 +70,22 @@ enum
|
||||||
class MemoryCardBase
|
class MemoryCardBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit MemoryCardBase(int _card_index = 0, int sizeMb = MemCard2043Mb)
|
explicit MemoryCardBase(int card_index = 0, int size_mbits = MemCard2043Mb)
|
||||||
: card_index(_card_index), nintendo_card_id(sizeMb)
|
: m_card_index(card_index), m_nintendo_card_id(size_mbits)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
virtual ~MemoryCardBase() {}
|
virtual ~MemoryCardBase() {}
|
||||||
virtual s32 Read(u32 address, s32 length, u8* destaddress) = 0;
|
virtual s32 Read(u32 src_address, s32 length, u8* dest_address) = 0;
|
||||||
virtual s32 Write(u32 destaddress, s32 length, const u8* srcaddress) = 0;
|
virtual s32 Write(u32 dest_address, s32 length, const u8* src_address) = 0;
|
||||||
virtual void ClearBlock(u32 address) = 0;
|
virtual void ClearBlock(u32 address) = 0;
|
||||||
virtual void ClearAll() = 0;
|
virtual void ClearAll() = 0;
|
||||||
virtual void DoState(PointerWrap& p) = 0;
|
virtual void DoState(PointerWrap& p) = 0;
|
||||||
u32 GetCardId() const { return nintendo_card_id; }
|
u32 GetCardId() const { return m_nintendo_card_id; }
|
||||||
bool IsAddressInBounds(u32 address) const { return address <= (memory_card_size - 1); }
|
bool IsAddressInBounds(u32 address) const { return address <= (m_memory_card_size - 1); }
|
||||||
protected:
|
protected:
|
||||||
int card_index;
|
int m_card_index;
|
||||||
u16 nintendo_card_id;
|
u16 m_nintendo_card_id;
|
||||||
u32 memory_card_size;
|
u32 m_memory_card_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GCMBlock
|
struct GCMBlock
|
||||||
|
|
|
@ -25,18 +25,18 @@
|
||||||
const int NO_INDEX = -1;
|
const int NO_INDEX = -1;
|
||||||
static const char* MC_HDR = "MC_SYSTEM_AREA";
|
static const char* MC_HDR = "MC_SYSTEM_AREA";
|
||||||
|
|
||||||
int GCMemcardDirectory::LoadGCI(const std::string& fileName, DiscIO::Region card_region,
|
int GCMemcardDirectory::LoadGCI(const std::string& file_name, DiscIO::Region card_region,
|
||||||
bool currentGameOnly)
|
bool current_game_only)
|
||||||
{
|
{
|
||||||
File::IOFile gcifile(fileName, "rb");
|
File::IOFile gci_file(file_name, "rb");
|
||||||
if (gcifile)
|
if (gci_file)
|
||||||
{
|
{
|
||||||
GCIFile gci;
|
GCIFile gci;
|
||||||
gci.m_filename = fileName;
|
gci.m_filename = file_name;
|
||||||
gci.m_dirty = false;
|
gci.m_dirty = false;
|
||||||
if (!gcifile.ReadBytes(&(gci.m_gci_header), DENTRY_SIZE))
|
if (!gci_file.ReadBytes(&(gci.m_gci_header), DENTRY_SIZE))
|
||||||
{
|
{
|
||||||
ERROR_LOG(EXPANSIONINTERFACE, "%s failed to read header", fileName.c_str());
|
ERROR_LOG(EXPANSIONINTERFACE, "%s failed to read header", file_name.c_str());
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& fileName, DiscIO::Region card
|
||||||
{
|
{
|
||||||
PanicAlertT(
|
PanicAlertT(
|
||||||
"GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
|
"GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
|
||||||
fileName.c_str());
|
file_name.c_str());
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,19 +65,19 @@ int GCMemcardDirectory::LoadGCI(const std::string& fileName, DiscIO::Region card
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 numBlocks = BE16(gci.m_gci_header.BlockCount);
|
u16 num_blocks = BE16(gci.m_gci_header.BlockCount);
|
||||||
// largest number of free blocks on a memory card
|
// largest number of free blocks on a memory card
|
||||||
// in reality, there are not likely any valid gci files > 251 blocks
|
// in reality, there are not likely any valid gci files > 251 blocks
|
||||||
if (numBlocks > 2043)
|
if (num_blocks > 2043)
|
||||||
{
|
{
|
||||||
PanicAlertT(
|
PanicAlertT(
|
||||||
"%s\nwas not loaded because it is an invalid GCI.\n Number of blocks claimed to be %u",
|
"%s\nwas not loaded because it is an invalid GCI.\n Number of blocks claimed to be %u",
|
||||||
gci.m_filename.c_str(), numBlocks);
|
gci.m_filename.c_str(), num_blocks);
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 size = numBlocks * BLOCK_SIZE;
|
u32 size = num_blocks * BLOCK_SIZE;
|
||||||
u64 file_size = gcifile.GetSize();
|
u64 file_size = gci_file.GetSize();
|
||||||
if (file_size != size + DENTRY_SIZE)
|
if (file_size != size + DENTRY_SIZE)
|
||||||
{
|
{
|
||||||
PanicAlertT("%s\nwas not loaded because it is an invalid GCI.\n File size (0x%" PRIx64
|
PanicAlertT("%s\nwas not loaded because it is an invalid GCI.\n File size (0x%" PRIx64
|
||||||
|
@ -86,33 +86,33 @@ int GCMemcardDirectory::LoadGCI(const std::string& fileName, DiscIO::Region card
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_GameId == BE32(gci.m_gci_header.Gamecode))
|
if (m_game_id == BE32(gci.m_gci_header.Gamecode))
|
||||||
{
|
{
|
||||||
gci.LoadSaveBlocks();
|
gci.LoadSaveBlocks();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (currentGameOnly)
|
if (current_game_only)
|
||||||
{
|
{
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
int totalBlocks = BE16(m_hdr.SizeMb) * MBIT_TO_BLOCKS - MC_FST_BLOCKS;
|
int total_blocks = BE16(m_hdr.SizeMb) * MBIT_TO_BLOCKS - MC_FST_BLOCKS;
|
||||||
int freeBlocks = BE16(m_bat1.FreeBlocks);
|
int free_blocks = BE16(m_bat1.FreeBlocks);
|
||||||
if (totalBlocks > freeBlocks * 10)
|
if (total_blocks > free_blocks * 10)
|
||||||
{
|
{
|
||||||
PanicAlertT("%s\nwas not loaded because there is less than 10%% free blocks available on "
|
PanicAlertT("%s\nwas not loaded because there is less than 10%% free blocks available on "
|
||||||
"the memory card\n"
|
"the memory card\n"
|
||||||
"Total Blocks: %d; Free Blocks: %d",
|
"Total Blocks: %d; Free Blocks: %d",
|
||||||
gci.m_filename.c_str(), totalBlocks, freeBlocks);
|
gci.m_filename.c_str(), total_blocks, free_blocks);
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u16 first_block = m_bat1.AssignBlocksContiguous(numBlocks);
|
u16 first_block = m_bat1.AssignBlocksContiguous(num_blocks);
|
||||||
if (first_block == 0xFFFF)
|
if (first_block == 0xFFFF)
|
||||||
{
|
{
|
||||||
PanicAlertT(
|
PanicAlertT(
|
||||||
"%s\nwas not loaded because there are not enough free blocks on the virtual memory card",
|
"%s\nwas not loaded because there are not enough free blocks on the virtual memory card",
|
||||||
fileName.c_str());
|
file_name.c_str());
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
*(u16*)&gci.m_gci_header.FirstBlock = first_block;
|
*(u16*)&gci.m_gci_header.FirstBlock = first_block;
|
||||||
|
@ -131,22 +131,22 @@ int GCMemcardDirectory::LoadGCI(const std::string& fileName, DiscIO::Region card
|
||||||
return NO_INDEX;
|
return NO_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 sizeMb,
|
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits,
|
||||||
bool shift_jis, DiscIO::Region card_region, int gameId)
|
bool shift_jis, DiscIO::Region card_region, int game_id)
|
||||||
: MemoryCardBase(slot, sizeMb), m_GameId(gameId), m_LastBlock(-1),
|
: MemoryCardBase(slot, size_mbits), m_game_id(game_id), m_last_block(-1),
|
||||||
m_hdr(slot, sizeMb, shift_jis), m_bat1(sizeMb), m_saves(0), m_SaveDirectory(directory),
|
m_hdr(slot, size_mbits, shift_jis), m_bat1(size_mbits), m_saves(0),
|
||||||
m_exiting(false)
|
m_save_directory(directory), m_exiting(false)
|
||||||
{
|
{
|
||||||
// Use existing header data if available
|
// Use existing header data if available
|
||||||
if (File::Exists(m_SaveDirectory + MC_HDR))
|
if (File::Exists(m_save_directory + MC_HDR))
|
||||||
{
|
{
|
||||||
File::IOFile hdrfile((m_SaveDirectory + MC_HDR), "rb");
|
File::IOFile hdr_file((m_save_directory + MC_HDR), "rb");
|
||||||
hdrfile.ReadBytes(&m_hdr, BLOCK_SIZE);
|
hdr_file.ReadBytes(&m_hdr, BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> rFilenames = DoFileSearch({".gci"}, {m_SaveDirectory});
|
std::vector<std::string> filenames = DoFileSearch({".gci"}, {m_save_directory});
|
||||||
|
|
||||||
if (rFilenames.size() > 112)
|
if (filenames.size() > 112)
|
||||||
{
|
{
|
||||||
Core::DisplayMessage("Warning: There are more than 112 save files on this memory card.\n"
|
Core::DisplayMessage("Warning: There are more than 112 save files on this memory card.\n"
|
||||||
" Only loading the first 112 in the folder, unless the game ID is the "
|
" Only loading the first 112 in the folder, unless the game ID is the "
|
||||||
|
@ -154,16 +154,16 @@ GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u
|
||||||
4000);
|
4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string& gciFile : rFilenames)
|
for (const std::string& gci_file : filenames)
|
||||||
{
|
{
|
||||||
if (m_saves.size() == DIRLEN)
|
if (m_saves.size() == DIRLEN)
|
||||||
{
|
{
|
||||||
PanicAlertT(
|
PanicAlertT(
|
||||||
"There are too many GCI files in the folder\n%s.\nOnly the first 127 will be available",
|
"There are too many GCI files in the folder\n%s.\nOnly the first 127 will be available",
|
||||||
m_SaveDirectory.c_str());
|
m_save_directory.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int index = LoadGCI(gciFile, card_region, m_saves.size() > 112);
|
int index = LoadGCI(gci_file, card_region, m_saves.size() > 112);
|
||||||
if (index != NO_INDEX)
|
if (index != NO_INDEX)
|
||||||
{
|
{
|
||||||
m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName());
|
m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName());
|
||||||
|
@ -185,7 +185,8 @@ void GCMemcardDirectory::FlushThread()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::SetCurrentThreadName(StringFromFormat("Memcard %d flushing thread", card_index).c_str());
|
Common::SetCurrentThreadName(
|
||||||
|
StringFromFormat("Memcard %d flushing thread", m_card_index).c_str());
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -214,10 +215,10 @@ GCMemcardDirectory::~GCMemcardDirectory()
|
||||||
FlushToFile();
|
FlushToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 GCMemcardDirectory::Read(u32 address, s32 length, u8* destaddress)
|
s32 GCMemcardDirectory::Read(u32 src_address, s32 length, u8* dest_address)
|
||||||
{
|
{
|
||||||
s32 block = address / BLOCK_SIZE;
|
s32 block = src_address / BLOCK_SIZE;
|
||||||
u32 offset = address % BLOCK_SIZE;
|
u32 offset = src_address % BLOCK_SIZE;
|
||||||
s32 extra = 0; // used for read calls that are across multiple blocks
|
s32 extra = 0; // used for read calls that are across multiple blocks
|
||||||
|
|
||||||
if (offset + length > BLOCK_SIZE)
|
if (offset + length > BLOCK_SIZE)
|
||||||
|
@ -226,58 +227,58 @@ s32 GCMemcardDirectory::Read(u32 address, s32 length, u8* destaddress)
|
||||||
length -= extra;
|
length -= extra;
|
||||||
|
|
||||||
// verify that we haven't calculated a length beyond BLOCK_SIZE
|
// verify that we haven't calculated a length beyond BLOCK_SIZE
|
||||||
_dbg_assert_msg_(EXPANSIONINTERFACE, (address + length) % BLOCK_SIZE == 0,
|
_dbg_assert_msg_(EXPANSIONINTERFACE, (src_address + length) % BLOCK_SIZE == 0,
|
||||||
"Memcard directory Read Logic Error");
|
"Memcard directory Read Logic Error");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LastBlock != block)
|
if (m_last_block != block)
|
||||||
{
|
{
|
||||||
switch (block)
|
switch (block)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_hdr;
|
m_last_block_address = (u8*)&m_hdr;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_LastBlock = -1;
|
m_last_block = -1;
|
||||||
m_LastBlockAddress = (u8*)&m_dir1;
|
m_last_block_address = (u8*)&m_dir1;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_LastBlock = -1;
|
m_last_block = -1;
|
||||||
m_LastBlockAddress = (u8*)&m_dir2;
|
m_last_block_address = (u8*)&m_dir2;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_bat1;
|
m_last_block_address = (u8*)&m_bat1;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_bat2;
|
m_last_block_address = (u8*)&m_bat2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
m_LastBlock = SaveAreaRW(block);
|
m_last_block = SaveAreaRW(block);
|
||||||
|
|
||||||
if (m_LastBlock == -1)
|
if (m_last_block == -1)
|
||||||
{
|
{
|
||||||
memset(destaddress, 0xFF, length);
|
memset(dest_address, 0xFF, length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(destaddress, m_LastBlockAddress + offset, length);
|
memcpy(dest_address, m_last_block_address + offset, length);
|
||||||
if (extra)
|
if (extra)
|
||||||
extra = Read(address + length, extra, destaddress + length);
|
extra = Read(src_address + length, extra, dest_address + length);
|
||||||
return length + extra;
|
return length + extra;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 GCMemcardDirectory::Write(u32 destaddress, s32 length, const u8* srcaddress)
|
s32 GCMemcardDirectory::Write(u32 dest_address, s32 length, const u8* src_address)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_write_mutex);
|
std::unique_lock<std::mutex> l(m_write_mutex);
|
||||||
if (length != 0x80)
|
if (length != 0x80)
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "Writing to 0x%x. Length: 0x%x", destaddress, length);
|
INFO_LOG(EXPANSIONINTERFACE, "Writing to 0x%x. Length: 0x%x", dest_address, length);
|
||||||
s32 block = destaddress / BLOCK_SIZE;
|
s32 block = dest_address / BLOCK_SIZE;
|
||||||
u32 offset = destaddress % BLOCK_SIZE;
|
u32 offset = dest_address % BLOCK_SIZE;
|
||||||
s32 extra = 0; // used for write calls that are across multiple blocks
|
s32 extra = 0; // used for write calls that are across multiple blocks
|
||||||
|
|
||||||
if (offset + length > BLOCK_SIZE)
|
if (offset + length > BLOCK_SIZE)
|
||||||
|
@ -286,42 +287,42 @@ s32 GCMemcardDirectory::Write(u32 destaddress, s32 length, const u8* srcaddress)
|
||||||
length -= extra;
|
length -= extra;
|
||||||
|
|
||||||
// verify that we haven't calculated a length beyond BLOCK_SIZE
|
// verify that we haven't calculated a length beyond BLOCK_SIZE
|
||||||
_dbg_assert_msg_(EXPANSIONINTERFACE, (destaddress + length) % BLOCK_SIZE == 0,
|
_dbg_assert_msg_(EXPANSIONINTERFACE, (dest_address + length) % BLOCK_SIZE == 0,
|
||||||
"Memcard directory Write Logic Error");
|
"Memcard directory Write Logic Error");
|
||||||
}
|
}
|
||||||
if (m_LastBlock != block)
|
if (m_last_block != block)
|
||||||
{
|
{
|
||||||
switch (block)
|
switch (block)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_hdr;
|
m_last_block_address = (u8*)&m_hdr;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
m_LastBlock = -1;
|
m_last_block = -1;
|
||||||
s32 bytes_written = 0;
|
s32 bytes_written = 0;
|
||||||
while (length > 0)
|
while (length > 0)
|
||||||
{
|
{
|
||||||
s32 to_write = std::min<s32>(DENTRY_SIZE, length);
|
s32 to_write = std::min<s32>(DENTRY_SIZE, length);
|
||||||
bytes_written +=
|
bytes_written +=
|
||||||
DirectoryWrite(destaddress + bytes_written, to_write, srcaddress + bytes_written);
|
DirectoryWrite(dest_address + bytes_written, to_write, src_address + bytes_written);
|
||||||
length -= to_write;
|
length -= to_write;
|
||||||
}
|
}
|
||||||
return bytes_written;
|
return bytes_written;
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_bat1;
|
m_last_block_address = (u8*)&m_bat1;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_bat2;
|
m_last_block_address = (u8*)&m_bat2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
m_LastBlock = SaveAreaRW(block, true);
|
m_last_block = SaveAreaRW(block, true);
|
||||||
if (m_LastBlock == -1)
|
if (m_last_block == -1)
|
||||||
{
|
{
|
||||||
PanicAlertT("Report: GCIFolder Writing to unallocated block 0x%x", block);
|
PanicAlertT("Report: GCIFolder Writing to unallocated block 0x%x", block);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -329,11 +330,11 @@ s32 GCMemcardDirectory::Write(u32 destaddress, s32 length, const u8* srcaddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(m_LastBlockAddress + offset, srcaddress, length);
|
memcpy(m_last_block_address + offset, src_address, length);
|
||||||
|
|
||||||
l.unlock();
|
l.unlock();
|
||||||
if (extra)
|
if (extra)
|
||||||
extra = Write(destaddress + length, extra, srcaddress + length);
|
extra = Write(dest_address + length, extra, src_address + length);
|
||||||
if (offset + length == BLOCK_SIZE)
|
if (offset + length == BLOCK_SIZE)
|
||||||
m_flush_trigger.Set();
|
m_flush_trigger.Set();
|
||||||
return length + extra;
|
return length + extra;
|
||||||
|
@ -352,31 +353,31 @@ void GCMemcardDirectory::ClearBlock(u32 address)
|
||||||
switch (block)
|
switch (block)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_hdr;
|
m_last_block_address = (u8*)&m_hdr;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_LastBlock = -1;
|
m_last_block = -1;
|
||||||
m_LastBlockAddress = (u8*)&m_dir1;
|
m_last_block_address = (u8*)&m_dir1;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_LastBlock = -1;
|
m_last_block = -1;
|
||||||
m_LastBlockAddress = (u8*)&m_dir2;
|
m_last_block_address = (u8*)&m_dir2;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_bat1;
|
m_last_block_address = (u8*)&m_bat1;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = (u8*)&m_bat2;
|
m_last_block_address = (u8*)&m_bat2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
m_LastBlock = SaveAreaRW(block, true);
|
m_last_block = SaveAreaRW(block, true);
|
||||||
if (m_LastBlock == -1)
|
if (m_last_block == -1)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
((GCMBlock*)m_LastBlockAddress)->Erase();
|
((GCMBlock*)m_last_block_address)->Erase();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void GCMemcardDirectory::SyncSaves()
|
inline void GCMemcardDirectory::SyncSaves()
|
||||||
|
@ -405,11 +406,11 @@ inline void GCMemcardDirectory::SyncSaves()
|
||||||
{
|
{
|
||||||
m_saves[i].m_dirty = true;
|
m_saves[i].m_dirty = true;
|
||||||
u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
|
u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
|
||||||
u32 newGameCode = BE32(current->Dir[i].Gamecode);
|
u32 new_gamecode = BE32(current->Dir[i].Gamecode);
|
||||||
u32 old_start = BE16(m_saves[i].m_gci_header.FirstBlock);
|
u32 old_start = BE16(m_saves[i].m_gci_header.FirstBlock);
|
||||||
u32 new_start = BE16(current->Dir[i].FirstBlock);
|
u32 new_start = BE16(current->Dir[i].FirstBlock);
|
||||||
|
|
||||||
if ((gamecode != 0xFFFFFFFF) && (gamecode != newGameCode))
|
if ((gamecode != 0xFFFFFFFF) && (gamecode != new_gamecode))
|
||||||
{
|
{
|
||||||
PanicAlertT("Game overwrote with another games save. Data corruption ahead 0x%x, 0x%x",
|
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.Gamecode), BE32(current->Dir[i].Gamecode));
|
||||||
|
@ -467,19 +468,19 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
|
||||||
m_saves[i].m_dirty = true;
|
m_saves[i].m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_LastBlock = block;
|
m_last_block = block;
|
||||||
m_LastBlockAddress = m_saves[i].m_save_data[idx].block;
|
m_last_block_address = m_saves[i].m_save_data[idx].block;
|
||||||
return m_LastBlock;
|
return m_last_block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 GCMemcardDirectory::DirectoryWrite(u32 destaddress, u32 length, const u8* srcaddress)
|
s32 GCMemcardDirectory::DirectoryWrite(u32 dest_address, u32 length, const u8* src_address)
|
||||||
{
|
{
|
||||||
u32 block = destaddress / BLOCK_SIZE;
|
u32 block = dest_address / BLOCK_SIZE;
|
||||||
u32 offset = destaddress % BLOCK_SIZE;
|
u32 offset = dest_address % BLOCK_SIZE;
|
||||||
Directory* dest = (block == 1) ? &m_dir1 : &m_dir2;
|
Directory* dest = (block == 1) ? &m_dir1 : &m_dir2;
|
||||||
u16 Dnum = offset / DENTRY_SIZE;
|
u16 Dnum = offset / DENTRY_SIZE;
|
||||||
|
|
||||||
|
@ -489,27 +490,27 @@ s32 GCMemcardDirectory::DirectoryWrite(u32 destaddress, u32 length, const u8* sr
|
||||||
// needed to update the update ctr, checksums
|
// needed to update the update ctr, checksums
|
||||||
// could check for writes to the 6 important bytes but doubtful that it improves performance
|
// could check for writes to the 6 important bytes but doubtful that it improves performance
|
||||||
// noticably
|
// noticably
|
||||||
memcpy((u8*)(dest) + offset, srcaddress, length);
|
memcpy((u8*)(dest) + offset, src_address, length);
|
||||||
SyncSaves();
|
SyncSaves();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memcpy((u8*)(dest) + offset, srcaddress, length);
|
memcpy((u8*)(dest) + offset, src_address, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GCMemcardDirectory::SetUsedBlocks(int saveIndex)
|
bool GCMemcardDirectory::SetUsedBlocks(int save_index)
|
||||||
{
|
{
|
||||||
BlockAlloc* currentBat;
|
BlockAlloc* current_bat;
|
||||||
if (BE16(m_bat2.UpdateCounter) > BE16(m_bat1.UpdateCounter))
|
if (BE16(m_bat2.UpdateCounter) > BE16(m_bat1.UpdateCounter))
|
||||||
currentBat = &m_bat2;
|
current_bat = &m_bat2;
|
||||||
else
|
else
|
||||||
currentBat = &m_bat1;
|
current_bat = &m_bat1;
|
||||||
|
|
||||||
u16 block = BE16(m_saves[saveIndex].m_gci_header.FirstBlock);
|
u16 block = BE16(m_saves[save_index].m_gci_header.FirstBlock);
|
||||||
while (block != 0xFFFF)
|
while (block != 0xFFFF)
|
||||||
{
|
{
|
||||||
m_saves[saveIndex].m_used_blocks.push_back(block);
|
m_saves[save_index].m_used_blocks.push_back(block);
|
||||||
block = currentBat->GetNextBlock(block);
|
block = current_bat->GetNextBlock(block);
|
||||||
if (block == 0)
|
if (block == 0)
|
||||||
{
|
{
|
||||||
PanicAlertT("BAT incorrect. Dolphin will now exit");
|
PanicAlertT("BAT incorrect. Dolphin will now exit");
|
||||||
|
@ -517,13 +518,13 @@ bool GCMemcardDirectory::SetUsedBlocks(int saveIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 num_blocks = BE16(m_saves[saveIndex].m_gci_header.BlockCount);
|
u16 num_blocks = BE16(m_saves[save_index].m_gci_header.BlockCount);
|
||||||
u16 blocksFromBat = (u16)m_saves[saveIndex].m_used_blocks.size();
|
u16 blocks_from_bat = (u16)m_saves[save_index].m_used_blocks.size();
|
||||||
if (blocksFromBat != num_blocks)
|
if (blocks_from_bat != num_blocks)
|
||||||
{
|
{
|
||||||
PanicAlertT("Warning: Number of blocks indicated by the BAT (%u) does not match that of the "
|
PanicAlertT("Warning: Number of blocks indicated by the BAT (%u) does not match that of the "
|
||||||
"loaded file header (%u)",
|
"loaded file header (%u)",
|
||||||
blocksFromBat, num_blocks);
|
blocks_from_bat, num_blocks);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,27 +554,27 @@ void GCMemcardDirectory::FlushToFile()
|
||||||
}
|
}
|
||||||
if (m_saves[i].m_filename.empty())
|
if (m_saves[i].m_filename.empty())
|
||||||
{
|
{
|
||||||
std::string defaultSaveName = m_SaveDirectory + m_saves[i].m_gci_header.GCI_FileName();
|
std::string default_save_name = m_save_directory + m_saves[i].m_gci_header.GCI_FileName();
|
||||||
|
|
||||||
// Check to see if another file is using the same name
|
// Check to see if another file is using the same name
|
||||||
// This seems unlikely except in the case of file corruption
|
// This seems unlikely except in the case of file corruption
|
||||||
// otherwise what user would name another file this way?
|
// otherwise what user would name another file this way?
|
||||||
for (int j = 0; File::Exists(defaultSaveName) && j < 10; ++j)
|
for (int j = 0; File::Exists(default_save_name) && j < 10; ++j)
|
||||||
{
|
{
|
||||||
defaultSaveName.insert(defaultSaveName.end() - 4, '0');
|
default_save_name.insert(default_save_name.end() - 4, '0');
|
||||||
}
|
}
|
||||||
if (File::Exists(defaultSaveName))
|
if (File::Exists(default_save_name))
|
||||||
PanicAlertT("Failed to find new filename.\n%s\n will be overwritten",
|
PanicAlertT("Failed to find new filename.\n%s\n will be overwritten",
|
||||||
defaultSaveName.c_str());
|
default_save_name.c_str());
|
||||||
m_saves[i].m_filename = defaultSaveName;
|
m_saves[i].m_filename = default_save_name;
|
||||||
}
|
}
|
||||||
File::IOFile GCI(m_saves[i].m_filename, "wb");
|
File::IOFile gci(m_saves[i].m_filename, "wb");
|
||||||
if (GCI)
|
if (gci)
|
||||||
{
|
{
|
||||||
GCI.WriteBytes(&m_saves[i].m_gci_header, DENTRY_SIZE);
|
gci.WriteBytes(&m_saves[i].m_gci_header, DENTRY_SIZE);
|
||||||
GCI.WriteBytes(m_saves[i].m_save_data.data(), BLOCK_SIZE * m_saves[i].m_save_data.size());
|
gci.WriteBytes(m_saves[i].m_save_data.data(), BLOCK_SIZE * m_saves[i].m_save_data.size());
|
||||||
|
|
||||||
if (GCI.IsGood())
|
if (gci.IsGood())
|
||||||
{
|
{
|
||||||
Core::DisplayMessage(
|
Core::DisplayMessage(
|
||||||
StringFromFormat("Wrote save contents to %s", m_saves[i].m_filename.c_str()), 4000);
|
StringFromFormat("Wrote save contents to %s", m_saves[i].m_filename.c_str()), 4000);
|
||||||
|
@ -592,11 +593,11 @@ void GCMemcardDirectory::FlushToFile()
|
||||||
else if (m_saves[i].m_filename.length() != 0)
|
else if (m_saves[i].m_filename.length() != 0)
|
||||||
{
|
{
|
||||||
m_saves[i].m_dirty = false;
|
m_saves[i].m_dirty = false;
|
||||||
std::string& oldname = m_saves[i].m_filename;
|
std::string& old_name = m_saves[i].m_filename;
|
||||||
std::string deletedname = oldname + ".deleted";
|
std::string deleted_name = old_name + ".deleted";
|
||||||
if (File::Exists(deletedname))
|
if (File::Exists(deleted_name))
|
||||||
File::Delete(deletedname);
|
File::Delete(deleted_name);
|
||||||
File::Rename(oldname, deletedname);
|
File::Rename(old_name, deleted_name);
|
||||||
m_saves[i].m_filename.clear();
|
m_saves[i].m_filename.clear();
|
||||||
m_saves[i].m_save_data.clear();
|
m_saves[i].m_save_data.clear();
|
||||||
m_saves[i].m_used_blocks.clear();
|
m_saves[i].m_used_blocks.clear();
|
||||||
|
@ -609,7 +610,7 @@ void GCMemcardDirectory::FlushToFile()
|
||||||
// this ensures that the save data for all of the current games gci files are stored in the
|
// this ensures that the save data for all of the current games gci files are stored in the
|
||||||
// savestate
|
// savestate
|
||||||
u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
|
u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
|
||||||
if (gamecode != m_GameId && gamecode != 0xFFFFFFFF && m_saves[i].m_save_data.size())
|
if (gamecode != m_game_id && gamecode != 0xFFFFFFFF && m_saves[i].m_save_data.size())
|
||||||
{
|
{
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "Flushing savedata to disk for %s",
|
INFO_LOG(EXPANSIONINTERFACE, "Flushing savedata to disk for %s",
|
||||||
m_saves[i].m_filename.c_str());
|
m_saves[i].m_filename.c_str());
|
||||||
|
@ -619,7 +620,7 @@ void GCMemcardDirectory::FlushToFile()
|
||||||
#if _WRITE_MC_HEADER
|
#if _WRITE_MC_HEADER
|
||||||
u8 mc[BLOCK_SIZE * MC_FST_BLOCKS];
|
u8 mc[BLOCK_SIZE * MC_FST_BLOCKS];
|
||||||
Read(0, BLOCK_SIZE * MC_FST_BLOCKS, mc);
|
Read(0, BLOCK_SIZE * MC_FST_BLOCKS, mc);
|
||||||
File::IOFile hdrfile(m_SaveDirectory + MC_HDR, "wb");
|
File::IOFile hdrfile(m_save_directory + MC_HDR, "wb");
|
||||||
hdrfile.WriteBytes(mc, BLOCK_SIZE * MC_FST_BLOCKS);
|
hdrfile.WriteBytes(mc, BLOCK_SIZE * MC_FST_BLOCKS);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -627,17 +628,17 @@ void GCMemcardDirectory::FlushToFile()
|
||||||
void GCMemcardDirectory::DoState(PointerWrap& p)
|
void GCMemcardDirectory::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_write_mutex);
|
std::unique_lock<std::mutex> l(m_write_mutex);
|
||||||
m_LastBlock = -1;
|
m_last_block = -1;
|
||||||
m_LastBlockAddress = nullptr;
|
m_last_block_address = nullptr;
|
||||||
p.Do(m_SaveDirectory);
|
p.Do(m_save_directory);
|
||||||
p.DoPOD<Header>(m_hdr);
|
p.DoPOD<Header>(m_hdr);
|
||||||
p.DoPOD<Directory>(m_dir1);
|
p.DoPOD<Directory>(m_dir1);
|
||||||
p.DoPOD<Directory>(m_dir2);
|
p.DoPOD<Directory>(m_dir2);
|
||||||
p.DoPOD<BlockAlloc>(m_bat1);
|
p.DoPOD<BlockAlloc>(m_bat1);
|
||||||
p.DoPOD<BlockAlloc>(m_bat2);
|
p.DoPOD<BlockAlloc>(m_bat2);
|
||||||
int numSaves = (int)m_saves.size();
|
int num_saves = (int)m_saves.size();
|
||||||
p.Do(numSaves);
|
p.Do(num_saves);
|
||||||
m_saves.resize(numSaves);
|
m_saves.resize(num_saves);
|
||||||
for (auto itr = m_saves.begin(); itr != m_saves.end(); ++itr)
|
for (auto itr = m_saves.begin(); itr != m_saves.end(); ++itr)
|
||||||
{
|
{
|
||||||
itr->DoState(p);
|
itr->DoState(p);
|
||||||
|
@ -651,15 +652,15 @@ bool GCIFile::LoadSaveBlocks()
|
||||||
if (m_filename.empty())
|
if (m_filename.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
File::IOFile savefile(m_filename, "rb");
|
File::IOFile save_file(m_filename, "rb");
|
||||||
if (!savefile)
|
if (!save_file)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str());
|
INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str());
|
||||||
savefile.Seek(DENTRY_SIZE, SEEK_SET);
|
save_file.Seek(DENTRY_SIZE, SEEK_SET);
|
||||||
u16 num_blocks = BE16(m_gci_header.BlockCount);
|
u16 num_blocks = BE16(m_gci_header.BlockCount);
|
||||||
m_save_data.resize(num_blocks);
|
m_save_data.resize(num_blocks);
|
||||||
if (!savefile.ReadBytes(m_save_data.data(), num_blocks * BLOCK_SIZE))
|
if (!save_file.ReadBytes(m_save_data.data(), num_blocks * BLOCK_SIZE))
|
||||||
{
|
{
|
||||||
PanicAlertT("Failed to read data from GCI file %s", m_filename.c_str());
|
PanicAlertT("Failed to read data from GCI file %s", m_filename.c_str());
|
||||||
m_save_data.clear();
|
m_save_data.clear();
|
||||||
|
@ -669,11 +670,11 @@ bool GCIFile::LoadSaveBlocks()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GCIFile::UsesBlock(u16 blocknum)
|
int GCIFile::UsesBlock(u16 block_num)
|
||||||
{
|
{
|
||||||
for (u16 i = 0; i < m_used_blocks.size(); ++i)
|
for (u16 i = 0; i < m_used_blocks.size(); ++i)
|
||||||
{
|
{
|
||||||
if (m_used_blocks[i] == blocknum)
|
if (m_used_blocks[i] == block_num)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -684,9 +685,9 @@ void GCIFile::DoState(PointerWrap& p)
|
||||||
p.DoPOD<DEntry>(m_gci_header);
|
p.DoPOD<DEntry>(m_gci_header);
|
||||||
p.Do(m_dirty);
|
p.Do(m_dirty);
|
||||||
p.Do(m_filename);
|
p.Do(m_filename);
|
||||||
int numBlocks = (int)m_save_data.size();
|
int num_blocks = (int)m_save_data.size();
|
||||||
p.Do(numBlocks);
|
p.Do(num_blocks);
|
||||||
m_save_data.resize(numBlocks);
|
m_save_data.resize(num_blocks);
|
||||||
for (auto itr = m_save_data.begin(); itr != m_save_data.end(); ++itr)
|
for (auto itr = m_save_data.begin(); itr != m_save_data.end(); ++itr)
|
||||||
{
|
{
|
||||||
p.DoPOD<GCMBlock>(*itr);
|
p.DoPOD<GCMBlock>(*itr);
|
||||||
|
@ -694,9 +695,9 @@ void GCIFile::DoState(PointerWrap& p)
|
||||||
p.Do(m_used_blocks);
|
p.Do(m_used_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MigrateFromMemcardFile(const std::string& strDirectoryName, int card_index)
|
void MigrateFromMemcardFile(const std::string& directory_name, int card_index)
|
||||||
{
|
{
|
||||||
File::CreateFullPath(strDirectoryName);
|
File::CreateFullPath(directory_name);
|
||||||
std::string ini_memcard = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA :
|
std::string ini_memcard = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA :
|
||||||
SConfig::GetInstance().m_strMemoryCardB;
|
SConfig::GetInstance().m_strMemoryCardB;
|
||||||
if (File::Exists(ini_memcard))
|
if (File::Exists(ini_memcard))
|
||||||
|
@ -706,7 +707,7 @@ void MigrateFromMemcardFile(const std::string& strDirectoryName, int card_index)
|
||||||
{
|
{
|
||||||
for (u8 i = 0; i < DIRLEN; i++)
|
for (u8 i = 0; i < DIRLEN; i++)
|
||||||
{
|
{
|
||||||
memcard.ExportGci(i, "", strDirectoryName);
|
memcard.ExportGci(i, "", directory_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,33 +17,33 @@
|
||||||
|
|
||||||
// Uncomment this to write the system data of the memorycard from directory to disc
|
// Uncomment this to write the system data of the memorycard from directory to disc
|
||||||
//#define _WRITE_MC_HEADER 1
|
//#define _WRITE_MC_HEADER 1
|
||||||
void MigrateFromMemcardFile(const std::string& strDirectoryName, int card_index);
|
void MigrateFromMemcardFile(const std::string& directory_name, int card_index);
|
||||||
|
|
||||||
class GCMemcardDirectory : public MemoryCardBase, NonCopyable
|
class GCMemcardDirectory : public MemoryCardBase, NonCopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GCMemcardDirectory(const std::string& directory, int slot, u16 sizeMb, bool shift_jis,
|
GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits, bool shift_jis,
|
||||||
DiscIO::Region card_region, int gameId);
|
DiscIO::Region card_region, int game_id);
|
||||||
~GCMemcardDirectory();
|
~GCMemcardDirectory();
|
||||||
void FlushToFile();
|
void FlushToFile();
|
||||||
void FlushThread();
|
void FlushThread();
|
||||||
s32 Read(u32 address, s32 length, u8* destaddress) override;
|
s32 Read(u32 src_address, s32 length, u8* dest_address) override;
|
||||||
s32 Write(u32 destaddress, s32 length, const u8* srcaddress) override;
|
s32 Write(u32 dest_address, s32 length, const u8* src_address) override;
|
||||||
void ClearBlock(u32 address) override;
|
void ClearBlock(u32 address) override;
|
||||||
void ClearAll() override {}
|
void ClearAll() override {}
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int LoadGCI(const std::string& fileName, DiscIO::Region card_region, bool currentGameOnly);
|
int LoadGCI(const std::string& file_name, DiscIO::Region card_region, bool current_game_only);
|
||||||
inline s32 SaveAreaRW(u32 block, bool writing = false);
|
inline s32 SaveAreaRW(u32 block, bool writing = false);
|
||||||
// s32 DirectoryRead(u32 offset, u32 length, u8* destaddress);
|
// s32 DirectoryRead(u32 offset, u32 length, u8* dest_address);
|
||||||
s32 DirectoryWrite(u32 destaddress, u32 length, const u8* srcaddress);
|
s32 DirectoryWrite(u32 dest_address, u32 length, const u8* src_address);
|
||||||
inline void SyncSaves();
|
inline void SyncSaves();
|
||||||
bool SetUsedBlocks(int saveIndex);
|
bool SetUsedBlocks(int save_index);
|
||||||
|
|
||||||
u32 m_GameId;
|
u32 m_game_id;
|
||||||
s32 m_LastBlock;
|
s32 m_last_block;
|
||||||
u8* m_LastBlockAddress;
|
u8* m_last_block_address;
|
||||||
|
|
||||||
Header m_hdr;
|
Header m_hdr;
|
||||||
Directory m_dir1, m_dir2;
|
Directory m_dir1, m_dir2;
|
||||||
|
@ -51,7 +51,7 @@ private:
|
||||||
std::vector<GCIFile> m_saves;
|
std::vector<GCIFile> m_saves;
|
||||||
|
|
||||||
std::vector<std::string> m_loaded_saves;
|
std::vector<std::string> m_loaded_saves;
|
||||||
std::string m_SaveDirectory;
|
std::string m_save_directory;
|
||||||
const std::chrono::seconds flush_interval = std::chrono::seconds(1);
|
const std::chrono::seconds flush_interval = std::chrono::seconds(1);
|
||||||
Common::Event m_flush_trigger;
|
Common::Event m_flush_trigger;
|
||||||
std::mutex m_write_mutex;
|
std::mutex m_write_mutex;
|
||||||
|
|
|
@ -22,38 +22,39 @@
|
||||||
#define SIZE_TO_Mb (1024 * 8 * 16)
|
#define SIZE_TO_Mb (1024 * 8 * 16)
|
||||||
#define MC_HDR_SIZE 0xA000
|
#define MC_HDR_SIZE 0xA000
|
||||||
|
|
||||||
MemoryCard::MemoryCard(const std::string& filename, int _card_index, u16 sizeMb)
|
MemoryCard::MemoryCard(const std::string& filename, int card_index, u16 size_mbits)
|
||||||
: MemoryCardBase(_card_index, sizeMb), m_filename(filename)
|
: MemoryCardBase(card_index, size_mbits), m_filename(filename)
|
||||||
{
|
{
|
||||||
File::IOFile pFile(m_filename, "rb");
|
File::IOFile file(m_filename, "rb");
|
||||||
if (pFile)
|
if (file)
|
||||||
{
|
{
|
||||||
// Measure size of the existing memcard file.
|
// Measure size of the existing memcard file.
|
||||||
memory_card_size = (u32)pFile.GetSize();
|
m_memory_card_size = (u32)file.GetSize();
|
||||||
nintendo_card_id = memory_card_size / SIZE_TO_Mb;
|
m_nintendo_card_id = m_memory_card_size / SIZE_TO_Mb;
|
||||||
m_memcard_data = std::make_unique<u8[]>(memory_card_size);
|
m_memcard_data = std::make_unique<u8[]>(m_memory_card_size);
|
||||||
memset(&m_memcard_data[0], 0xFF, memory_card_size);
|
memset(&m_memcard_data[0], 0xFF, m_memory_card_size);
|
||||||
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "Reading memory card %s", m_filename.c_str());
|
INFO_LOG(EXPANSIONINTERFACE, "Reading memory card %s", m_filename.c_str());
|
||||||
pFile.ReadBytes(&m_memcard_data[0], memory_card_size);
|
file.ReadBytes(&m_memcard_data[0], m_memory_card_size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create a new 128Mb memcard
|
// Create a new 128Mb memcard
|
||||||
nintendo_card_id = sizeMb;
|
m_nintendo_card_id = size_mbits;
|
||||||
memory_card_size = sizeMb * SIZE_TO_Mb;
|
m_memory_card_size = size_mbits * SIZE_TO_Mb;
|
||||||
|
|
||||||
m_memcard_data = std::make_unique<u8[]>(memory_card_size);
|
m_memcard_data = std::make_unique<u8[]>(m_memory_card_size);
|
||||||
// Fills in MC_HDR_SIZE bytes
|
// Fills in MC_HDR_SIZE bytes
|
||||||
GCMemcard::Format(&m_memcard_data[0], m_filename.find(".JAP.raw") != std::string::npos, sizeMb);
|
GCMemcard::Format(&m_memcard_data[0], m_filename.find(".JAP.raw") != std::string::npos,
|
||||||
memset(&m_memcard_data[MC_HDR_SIZE], 0xFF, memory_card_size - MC_HDR_SIZE);
|
size_mbits);
|
||||||
|
memset(&m_memcard_data[MC_HDR_SIZE], 0xFF, m_memory_card_size - MC_HDR_SIZE);
|
||||||
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "No memory card found. A new one was created instead.");
|
INFO_LOG(EXPANSIONINTERFACE, "No memory card found. A new one was created instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class members (including inherited ones) have now been initialized, so
|
// Class members (including inherited ones) have now been initialized, so
|
||||||
// it's safe to startup the flush thread (which reads them).
|
// it's safe to startup the flush thread (which reads them).
|
||||||
m_flush_buffer = std::make_unique<u8[]>(memory_card_size);
|
m_flush_buffer = std::make_unique<u8[]>(m_memory_card_size);
|
||||||
m_flush_thread = std::thread(&MemoryCard::FlushThread, this);
|
m_flush_thread = std::thread(&MemoryCard::FlushThread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +75,8 @@ void MemoryCard::FlushThread()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::SetCurrentThreadName(StringFromFormat("Memcard %d flushing thread", card_index).c_str());
|
Common::SetCurrentThreadName(
|
||||||
|
StringFromFormat("Memcard %d flushing thread", m_card_index).c_str());
|
||||||
|
|
||||||
const auto flush_interval = std::chrono::seconds(15);
|
const auto flush_interval = std::chrono::seconds(15);
|
||||||
|
|
||||||
|
@ -94,9 +96,9 @@ void MemoryCard::FlushThread()
|
||||||
|
|
||||||
// Opening the file is purposefully done each iteration to ensure the
|
// Opening the file is purposefully done each iteration to ensure the
|
||||||
// file doesn't disappear out from under us after the first check.
|
// file doesn't disappear out from under us after the first check.
|
||||||
File::IOFile pFile(m_filename, "r+b");
|
File::IOFile file(m_filename, "r+b");
|
||||||
|
|
||||||
if (!pFile)
|
if (!file)
|
||||||
{
|
{
|
||||||
std::string dir;
|
std::string dir;
|
||||||
SplitPath(m_filename, &dir, nullptr, nullptr);
|
SplitPath(m_filename, &dir, nullptr, nullptr);
|
||||||
|
@ -104,11 +106,11 @@ void MemoryCard::FlushThread()
|
||||||
{
|
{
|
||||||
File::CreateFullPath(dir);
|
File::CreateFullPath(dir);
|
||||||
}
|
}
|
||||||
pFile.Open(m_filename, "wb");
|
file.Open(m_filename, "wb");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note - pFile may have changed above, after ctor
|
// Note - file may have changed above, after ctor
|
||||||
if (!pFile)
|
if (!file)
|
||||||
{
|
{
|
||||||
PanicAlertT(
|
PanicAlertT(
|
||||||
"Could not write memory card file %s.\n\n"
|
"Could not write memory card file %s.\n\n"
|
||||||
|
@ -124,14 +126,14 @@ void MemoryCard::FlushThread()
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_flush_mutex);
|
std::unique_lock<std::mutex> l(m_flush_mutex);
|
||||||
memcpy(&m_flush_buffer[0], &m_memcard_data[0], memory_card_size);
|
memcpy(&m_flush_buffer[0], &m_memcard_data[0], m_memory_card_size);
|
||||||
}
|
}
|
||||||
pFile.WriteBytes(&m_flush_buffer[0], memory_card_size);
|
file.WriteBytes(&m_flush_buffer[0], m_memory_card_size);
|
||||||
|
|
||||||
if (!do_exit)
|
if (!do_exit)
|
||||||
{
|
{
|
||||||
Core::DisplayMessage(StringFromFormat("Wrote memory card %c contents to %s",
|
Core::DisplayMessage(StringFromFormat("Wrote memory card %c contents to %s",
|
||||||
card_index ? 'B' : 'A', m_filename.c_str())
|
m_card_index ? 'B' : 'A', m_filename.c_str())
|
||||||
.c_str(),
|
.c_str(),
|
||||||
4000);
|
4000);
|
||||||
}
|
}
|
||||||
|
@ -147,29 +149,29 @@ void MemoryCard::MakeDirty()
|
||||||
m_dirty.Set();
|
m_dirty.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 MemoryCard::Read(u32 srcaddress, s32 length, u8* destaddress)
|
s32 MemoryCard::Read(u32 src_address, s32 length, u8* dest_address)
|
||||||
{
|
{
|
||||||
if (!IsAddressInBounds(srcaddress))
|
if (!IsAddressInBounds(src_address))
|
||||||
{
|
{
|
||||||
PanicAlertT("MemoryCard: Read called with invalid source address (0x%x)", srcaddress);
|
PanicAlertT("MemoryCard: Read called with invalid source address (0x%x)", src_address);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(destaddress, &m_memcard_data[srcaddress], length);
|
memcpy(dest_address, &m_memcard_data[src_address], length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 MemoryCard::Write(u32 destaddress, s32 length, const u8* srcaddress)
|
s32 MemoryCard::Write(u32 dest_address, s32 length, const u8* src_address)
|
||||||
{
|
{
|
||||||
if (!IsAddressInBounds(destaddress))
|
if (!IsAddressInBounds(dest_address))
|
||||||
{
|
{
|
||||||
PanicAlertT("MemoryCard: Write called with invalid destination address (0x%x)", destaddress);
|
PanicAlertT("MemoryCard: Write called with invalid destination address (0x%x)", dest_address);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_flush_mutex);
|
std::unique_lock<std::mutex> l(m_flush_mutex);
|
||||||
memcpy(&m_memcard_data[destaddress], srcaddress, length);
|
memcpy(&m_memcard_data[dest_address], src_address, length);
|
||||||
}
|
}
|
||||||
MakeDirty();
|
MakeDirty();
|
||||||
return length;
|
return length;
|
||||||
|
@ -194,14 +196,14 @@ void MemoryCard::ClearAll()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(m_flush_mutex);
|
std::unique_lock<std::mutex> l(m_flush_mutex);
|
||||||
memset(&m_memcard_data[0], 0xFF, memory_card_size);
|
memset(&m_memcard_data[0], 0xFF, m_memory_card_size);
|
||||||
}
|
}
|
||||||
MakeDirty();
|
MakeDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryCard::DoState(PointerWrap& p)
|
void MemoryCard::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(card_index);
|
p.Do(m_card_index);
|
||||||
p.Do(memory_card_size);
|
p.Do(m_memory_card_size);
|
||||||
p.DoArray(&m_memcard_data[0], memory_card_size);
|
p.DoArray(&m_memcard_data[0], m_memory_card_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ class PointerWrap;
|
||||||
class MemoryCard : public MemoryCardBase
|
class MemoryCard : public MemoryCardBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MemoryCard(const std::string& filename, int _card_index, u16 sizeMb = MemCard2043Mb);
|
MemoryCard(const std::string& filename, int card_index, u16 size_mbits = MemCard2043Mb);
|
||||||
~MemoryCard();
|
~MemoryCard();
|
||||||
void FlushThread();
|
void FlushThread();
|
||||||
void MakeDirty();
|
void MakeDirty();
|
||||||
|
|
||||||
s32 Read(u32 address, s32 length, u8* destaddress) override;
|
s32 Read(u32 src_address, s32 length, u8* dest_address) override;
|
||||||
s32 Write(u32 destaddress, s32 length, const u8* srcaddress) override;
|
s32 Write(u32 dest_address, s32 length, const u8* src_address) override;
|
||||||
void ClearBlock(u32 address) override;
|
void ClearBlock(u32 address) override;
|
||||||
void ClearAll() override;
|
void ClearAll() override;
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
Loading…
Reference in New Issue