summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorLioncash <mathew1800@gmail.com>2014-07-15 12:09:56 -0400
committerLioncash <mathew1800@gmail.com>2014-07-15 12:09:56 -0400
commit01cd90deef0ebeb1b8960dbdb2952d143f34a60a (patch)
tree0e4dde7a07f5efe1d33eac6c529aad6de1ff8e62 /Source/Core
parent0720a36c999c5c796f32fdecde389451e743e7e4 (diff)
parent198d137ca0575bc6c334b89547e5e63536ee9f14 (diff)
Merge pull request #544 from LPFaint99/memcard
GCI Folder fix compatibility with games that relocate the save after first use
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp24
-rw-r--r--Source/Core/Core/HW/GCMemcard.h26
-rw-r--r--Source/Core/Core/HW/GCMemcardDirectory.cpp78
-rw-r--r--Source/Core/Core/HW/GCMemcardDirectory.h4
4 files changed, 96 insertions, 36 deletions
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
index dd0b2a3e5b..bdd31551ab 100644
--- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
@@ -257,11 +257,6 @@ void CEXIMemoryCard::SetCS(int cs)
CmdDoneLater(5000);
}
-
- // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
- // But first we unschedule already scheduled flushes - no point in flushing once per page for a large write.
- CoreTiming::RemoveEvent(et_this_card);
- CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)card_index);
break;
}
}
@@ -478,6 +473,10 @@ IEXIDevice* CEXIMemoryCard::FindDevice(TEXIDevices device_type, int customIndex)
void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize)
{
memorycard->Read(address, _uSize, Memory::GetPointer(_uAddr));
+#ifdef _DEBUG
+ if ((address + _uSize) % BLOCK_SIZE == 0)
+ INFO_LOG(EXPANSIONINTERFACE, "reading from block: %x", address / BLOCK_SIZE);
+#endif
}
// DMA write are preceded by all of the necessary setup via IMMWrite
@@ -485,4 +484,19 @@ void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize)
void CEXIMemoryCard::DMAWrite(u32 _uAddr, u32 _uSize)
{
memorycard->Write(address, _uSize, Memory::GetPointer(_uAddr));
+
+ // At the end of writing to a block flush to disk
+ // memory card blocks are always(?) written as a whole,
+ // but the dma calls are by page size (0x200) at a time
+ // just in case this is the last block that the game will be writing for a while
+ if (((address + _uSize) % BLOCK_SIZE) == 0)
+ {
+ INFO_LOG(EXPANSIONINTERFACE, "writing to block: %x", address / BLOCK_SIZE);
+ // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
+ // But first we unschedule already scheduled flushes - no point in flushing once per page for a large write
+ // Scheduling event is mainly for raw memory cards as the flush the whole 16MB to disk
+ // Flushing the gci folder is free in comparison
+ CoreTiming::RemoveEvent(et_this_card);
+ CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)card_index);
+ }
}
diff --git a/Source/Core/Core/HW/GCMemcard.h b/Source/Core/Core/HW/GCMemcard.h
index 69213983ec..9993e6b35d 100644
--- a/Source/Core/Core/HW/GCMemcard.h
+++ b/Source/Core/Core/HW/GCMemcard.h
@@ -4,10 +4,12 @@
#pragma once
+#include <algorithm>
#include <string>
#include "Common/Common.h"
#include "Common/CommonPaths.h"
+#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Core/HW/EXI_DeviceIPL.h"
@@ -156,9 +158,29 @@ struct DEntry
DEntry() { memset(this, 0xFF, DENTRY_SIZE); }
std::string GCI_FileName() const
{
- return std::string((char *)Makercode, 2) + '-' + std::string((char *)Gamecode, 4) + '-' + (char *)Filename +
- ".gci";
+ std::string filename = std::string((char *)Makercode, 2) + '-' + std::string((char *)Gamecode, 4) + '-' + (char *)Filename +
+ ".gci";
+ static Common::replace_v replacements;
+ if (replacements.size() == 0)
+ {
+ Common::ReadReplacements(replacements);
+ // Cannot add \r to replacements file due to it being a line ending char
+ // / might be ok, but we need to verify that this is only used on filenames
+ // as it is a dir_sep
+ replacements.push_back(std::make_pair('\r', std::string("__0d__")));
+ replacements.push_back(std::make_pair('/', std::string("__2f__")));
+
+ }
+
+ // Replaces chars that FAT32 can't support with strings defined in /sys/replace
+ for (auto& replacement : replacements)
+ {
+ for (size_t j = 0; (j = filename.find(replacement.first, j)) != filename.npos; ++j)
+ filename.replace(j, 1, replacement.second);
+ }
+ return filename;
}
+
u8 Gamecode[4]; //0x00 0x04 Gamecode
u8 Makercode[2]; //0x04 0x02 Makercode
u8 Unused1; //0x06 0x01 reserved/unused (always 0xff, has no effect)
diff --git a/Source/Core/Core/HW/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcardDirectory.cpp
index 13fec8b185..2f3492bc0b 100644
--- a/Source/Core/Core/HW/GCMemcardDirectory.cpp
+++ b/Source/Core/Core/HW/GCMemcardDirectory.cpp
@@ -11,7 +11,8 @@
const int NO_INDEX = -1;
static const char *MC_HDR = "MC_SYSTEM_AREA";
-int GCMemcardDirectory::LoadGCI(std::string fileName, int region)
+
+int GCMemcardDirectory::LoadGCI(std::string fileName, DiscIO::IVolume::ECountry card_region)
{
File::IOFile gcifile(fileName, "rb");
if (gcifile)
@@ -25,38 +26,33 @@ int GCMemcardDirectory::LoadGCI(std::string fileName, int region)
return NO_INDEX;
}
+ DiscIO::IVolume::ECountry gci_region;
// check region
switch (gci.m_gci_header.Gamecode[3])
{
case 'J':
- if (region != DiscIO::IVolume::COUNTRY_JAPAN)
- {
- PanicAlertT("GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
- fileName.c_str());
- return NO_INDEX;
- }
+ gci_region = DiscIO::IVolume::COUNTRY_JAPAN;
break;
case 'E':
- if (region != DiscIO::IVolume::COUNTRY_USA)
- {
- PanicAlertT("GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
- fileName.c_str());
- return NO_INDEX;
- }
+ gci_region = DiscIO::IVolume::COUNTRY_USA;
break;
case 'C':
// Used by Datel Action Replay Save
+ // can be on any regions card
+ gci_region = card_region;
break;
default:
- if (region != DiscIO::IVolume::COUNTRY_EUROPE)
- {
- PanicAlertT("GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
- fileName.c_str());
- return NO_INDEX;
- }
+ gci_region = DiscIO::IVolume::COUNTRY_EUROPE;
break;
}
+ if (gci_region != card_region)
+ {
+ PanicAlertT("GCI save file was not loaded because it is the wrong region for this memory card:\n%s",
+ fileName.c_str());
+ return NO_INDEX;
+ }
+
std::string gci_filename = gci.m_gci_header.GCI_FileName();
for (u16 i = 0; i < m_loaded_saves.size(); ++i)
{
@@ -117,7 +113,7 @@ int GCMemcardDirectory::LoadGCI(std::string fileName, int region)
return NO_INDEX;
}
-GCMemcardDirectory::GCMemcardDirectory(std::string directory, int slot, u16 sizeMb, bool ascii, int region, int gameId)
+GCMemcardDirectory::GCMemcardDirectory(std::string directory, int slot, u16 sizeMb, bool ascii, DiscIO::IVolume::ECountry card_region, int gameId)
: MemoryCardBase(slot, sizeMb)
, m_GameId(gameId)
, m_LastBlock(-1)
@@ -148,7 +144,7 @@ GCMemcardDirectory::GCMemcardDirectory(std::string directory, int slot, u16 size
m_SaveDirectory.c_str());
break;
}
- int index = LoadGCI(FST_Temp.children[j].physicalName, region);
+ int index = LoadGCI(FST_Temp.children[j].physicalName, card_region);
if (index != NO_INDEX)
{
m_loaded_saves.push_back(m_saves.at(index).m_gci_header.GCI_FileName());
@@ -222,7 +218,7 @@ s32 GCMemcardDirectory::Read(u32 address, s32 length, u8 *destaddress)
s32 GCMemcardDirectory::Write(u32 destaddress, s32 length, u8 *srcaddress)
{
if (length != 0x80)
- ERROR_LOG(EXPANSIONINTERFACE, "WRITING TO %x, len %x", destaddress, length);
+ INFO_LOG(EXPANSIONINTERFACE, "WRITING TO %x, len %x", destaddress, length);
s32 block = destaddress / BLOCK_SIZE;
u32 offset = destaddress % BLOCK_SIZE;
s32 extra = 0; // used for write calls that are across multiple blocks
@@ -292,6 +288,7 @@ void GCMemcardDirectory::ClearBlock(u32 address)
}
u32 block = address / BLOCK_SIZE;
+ INFO_LOG(EXPANSIONINTERFACE, "clearing block %d", block);
switch (block)
{
case 0:
@@ -333,8 +330,9 @@ inline void GCMemcardDirectory::SyncSaves()
for (u32 i = 0; i < DIRLEN; ++i)
{
- if (*(u32 *)&(current->Dir[i]) != 0xFFFFFFFF)
+ if (BE32(current->Dir[i].Gamecode) != 0xFFFFFFFF)
{
+ INFO_LOG(EXPANSIONINTERFACE, "Syncing Save %x", *(u32 *)&(current->Dir[i].Gamecode));
bool added = false;
while (i >= m_saves.size())
{
@@ -346,12 +344,38 @@ inline void GCMemcardDirectory::SyncSaves()
if (added || memcmp((u8 *)&(m_saves[i].m_gci_header), (u8 *)&(current->Dir[i]), DENTRY_SIZE))
{
m_saves[i].m_dirty = true;
+ u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
+ u32 newGameCode = BE32(current->Dir[i].Gamecode);
+ u32 old_start = BE16(m_saves[i].m_gci_header.FirstBlock);
+ u32 new_start = BE16(current->Dir[i].FirstBlock);
+
+ if ((gamecode != 0xFFFFFFFF)
+ && (gamecode != newGameCode))
+ {
+ PanicAlertT("Game overwrote with another games save, data corruption ahead %x, %x ",
+ BE32(m_saves[i].m_gci_header.Gamecode), BE32(current->Dir[i].Gamecode));
+ }
memcpy((u8 *)&(m_saves[i].m_gci_header), (u8 *)&(current->Dir[i]), DENTRY_SIZE);
+ if (old_start != new_start)
+ {
+ INFO_LOG(EXPANSIONINTERFACE, "Save moved from %x to %x", old_start, new_start);
+ m_saves[i].m_used_blocks.clear();
+ m_saves[i].m_save_data.clear();
+ }
+ if (m_saves[i].m_used_blocks.size() == 0)
+ {
+ SetUsedBlocks(i);
+ }
}
}
else if ((i < m_saves.size()) && (*(u32 *)&(m_saves[i].m_gci_header) != 0xFFFFFFFF))
{
- *(u32 *)&(m_saves[i].m_gci_header.Gamecode) = 0xFFFFFFF;
+ INFO_LOG(EXPANSIONINTERFACE, "Clearing and/or Deleting Save %x", BE32(m_saves[i].m_gci_header.Gamecode));
+ *(u32 *)&(m_saves[i].m_gci_header.Gamecode) = 0xFFFFFFFF;
+ m_saves[i].m_save_data.clear();
+ m_saves[i].m_used_blocks.clear();
+ m_saves[i].m_dirty = true;
+
}
}
}
@@ -434,10 +458,10 @@ bool GCMemcardDirectory::SetUsedBlocks(int saveIndex)
}
u16 num_blocks = BE16(m_saves[saveIndex].m_gci_header.BlockCount);
-
- if (m_saves[saveIndex].m_used_blocks.size() != num_blocks)
+ u16 blocksFromBat = (u16)m_saves[saveIndex].m_used_blocks.size();
+ if (blocksFromBat != num_blocks)
{
- PanicAlertT("Warning BAT number of blocks does not match file header");
+ PanicAlertT("Warning BAT number of blocks %d does not match file header loaded %d", blocksFromBat, num_blocks);
return false;
}
diff --git a/Source/Core/Core/HW/GCMemcardDirectory.h b/Source/Core/Core/HW/GCMemcardDirectory.h
index add9cf6e4b..463a60f439 100644
--- a/Source/Core/Core/HW/GCMemcardDirectory.h
+++ b/Source/Core/Core/HW/GCMemcardDirectory.h
@@ -14,7 +14,7 @@ class GCMemcardDirectory : public MemoryCardBase, NonCopyable
{
public:
GCMemcardDirectory(std::string directory, int slot = 0, u16 sizeMb = MemCard2043Mb, bool ascii = true,
- int region = 0, int gameId = 0);
+ DiscIO::IVolume::ECountry card_region = DiscIO::IVolume::COUNTRY_EUROPE, int gameId = 0);
~GCMemcardDirectory() { Flush(true); }
void Flush(bool exiting = false) override;
@@ -25,7 +25,7 @@ public:
void DoState(PointerWrap &p) override;
private:
- int LoadGCI(std::string fileName, int region);
+ int LoadGCI(std::string fileName, DiscIO::IVolume::ECountry card_region);
inline s32 SaveAreaRW(u32 block, bool writing = false);
// s32 DirectoryRead(u32 offset, u32 length, u8* destaddress);
s32 DirectoryWrite(u32 destaddress, u32 length, u8 *srcaddress);