diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 2a4915db45..781018d2c6 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -97,6 +97,7 @@ set(SRCS ActionReplay.cpp
HW/EXI_DeviceGecko.cpp
HW/EXI_DeviceIPL.cpp
HW/EXI_DeviceMemoryCard.cpp
+ HW/EXI_DeviceMemoryCardRaw.cpp
HW/EXI_DeviceMic.cpp
HW/GCMemcard.cpp
HW/GCPad.cpp
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 0ea27a1b7e..7d6f97ab55 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -135,6 +135,7 @@
+
@@ -331,6 +332,7 @@
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 469b7783be..d615957892 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -702,6 +702,9 @@
PowerPC\JitILCommon
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
@@ -1207,6 +1210,9 @@
PowerPC\JitILCommon
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
index 0f587dd50d..e9fe240f7d 100644
--- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
@@ -13,6 +13,7 @@
#include "Core/HW/EXI.h"
#include "Core/HW/EXI_Device.h"
#include "Core/HW/EXI_DeviceMemoryCard.h"
+#include "Core/HW/EXI_DeviceMemoryCardRaw.h"
#include "Core/HW/GCMemcard.h"
#include "Core/HW/Sram.h"
@@ -25,13 +26,14 @@
#define SIZE_TO_Mb (1024 * 8 * 16)
#define MC_HDR_SIZE 0xA000
+
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
{
// note that userdata is forbidden to be a pointer, due to the implementation of EventDoState
int card_index = (int)userdata;
CEXIMemoryCard* pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index);
- if (pThis)
- pThis->Flush();
+ if (pThis && pThis->memorycard)
+ pThis->memorycard->Flush();
}
void CEXIMemoryCard::CmdDoneCallback(u64 userdata, int cyclesLate)
@@ -46,13 +48,13 @@ CEXIMemoryCard::CEXIMemoryCard(const int index)
: card_index(index)
, m_bDirty(false)
{
- m_strFilename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA : SConfig::GetInstance().m_strMemoryCardB;
+ std::string filename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA : SConfig::GetInstance().m_strMemoryCardB;
if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard() && Movie::IsStartingFromClearSave())
- m_strFilename = File::GetUserPath(D_GCUSER_IDX) + "Movie.raw";
+ filename = File::GetUserPath(D_GCUSER_IDX) + "Movie.raw";
// we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential
- et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback);
- et_cmd_done = CoreTiming::RegisterEvent((card_index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback);
+ et_this_card = CoreTiming::RegisterEvent((index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback);
+ et_cmd_done = CoreTiming::RegisterEvent((index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback);
interruptSwitch = 0;
m_bInterruptSet = 0;
@@ -81,110 +83,25 @@ CEXIMemoryCard::CEXIMemoryCard(const int index)
bool useMC251;
IniFile gameIni = Core::g_CoreStartupParameter.LoadGameIni();
gameIni.GetOrCreateSection("Core")->Get("MemoryCard251", &useMC251, false);
- nintendo_card_id = MemCard2043Mb;
+ u16 sizeMb = MemCard2043Mb;
if (useMC251)
{
- nintendo_card_id = MemCard251Mb;
- m_strFilename.insert(m_strFilename.find_last_of("."), ".251");
+ sizeMb = MemCard251Mb;
+ filename.insert(filename.find_last_of("."), ".251");
}
card_id = 0xc221; // It's a Nintendo brand memcard
-
- File::IOFile pFile(m_strFilename, "rb");
- if (pFile)
- {
- // Measure size of the memcard file.
- memory_card_size = (int)pFile.GetSize();
- nintendo_card_id = memory_card_size / SIZE_TO_Mb;
- memory_card_content = new u8[memory_card_size];
- memset(memory_card_content, 0xFF, memory_card_size);
-
- INFO_LOG(EXPANSIONINTERFACE, "Reading memory card %s", m_strFilename.c_str());
- pFile.ReadBytes(memory_card_content, memory_card_size);
-
- }
- else
- {
- // Create a new memcard
- memory_card_size = nintendo_card_id * SIZE_TO_Mb;
-
- memory_card_content = new u8[memory_card_size];
- GCMemcard::Format(memory_card_content, m_strFilename.find(".JAP.raw") != std::string::npos, nintendo_card_id);
- memset(memory_card_content+MC_HDR_SIZE, 0xFF, memory_card_size-MC_HDR_SIZE);
- WARN_LOG(EXPANSIONINTERFACE, "No memory card found. Will create a new one.");
- }
- SetCardFlashID(memory_card_content, card_index);
-}
-
-void innerFlush(FlushData* data)
-{
- File::IOFile pFile(data->filename, "r+b");
- if (!pFile)
- {
- std::string dir;
- SplitPath(data->filename, &dir, nullptr, nullptr);
- if (!File::IsDirectory(dir))
- File::CreateFullPath(dir);
- pFile.Open(data->filename, "wb");
- }
-
- if (!pFile) // Note - pFile changed inside above if
- {
- PanicAlertT("Could not write memory card file %s.\n\n"
- "Are you running Dolphin from a CD/DVD, or is the save file maybe write protected?\n\n"
- "Are you receiving this after moving the emulator directory?\nIf so, then you may "
- "need to re-specify your memory card location in the options.", data->filename.c_str());
- return;
- }
-
- pFile.WriteBytes(data->memcardContent, data->memcardSize);
-
- if (!data->bExiting)
- Core::DisplayMessage(StringFromFormat("Wrote memory card %c contents to %s",
- data->memcardIndex ? 'B' : 'A', data->filename.c_str()).c_str(), 4000);
- return;
-}
-
-// Flush memory card contents to disc
-void CEXIMemoryCard::Flush(bool exiting)
-{
- if (!m_bDirty)
- return;
-
- if (!Core::g_CoreStartupParameter.bEnableMemcardSaving)
- return;
-
- if (flushThread.joinable())
- {
- flushThread.join();
- }
-
- if (!exiting)
- Core::DisplayMessage(StringFromFormat("Writing to memory card %c", card_index ? 'B' : 'A'), 1000);
-
- flushData.filename = m_strFilename;
- flushData.memcardContent = memory_card_content;
- flushData.memcardIndex = card_index;
- flushData.memcardSize = memory_card_size;
- flushData.bExiting = exiting;
-
- flushThread = std::thread(innerFlush, &flushData);
- if (exiting)
- flushThread.join();
-
- m_bDirty = false;
+ memorycard = new MemoryCard(filename, card_index, sizeMb);
+ memory_card_size = memorycard->GetCardId() * SIZE_TO_Mb;
+ u8 header[20] = { 0 };
+ memorycard->Read(0, 20, header);
+ SetCardFlashID(header, card_index);
}
CEXIMemoryCard::~CEXIMemoryCard()
{
CoreTiming::RemoveEvent(et_this_card);
- Flush(true);
- delete[] memory_card_content;
- memory_card_content = nullptr;
-
- if (flushThread.joinable())
- {
- flushThread.join();
- }
+ memorycard->Flush(true);
+ delete memorycard;
}
bool CEXIMemoryCard::IsPresent()
@@ -210,10 +127,7 @@ void CEXIMemoryCard::CmdDoneLater(u64 cycles)
void CEXIMemoryCard::SetCS(int cs)
{
// So that memory card won't be invalidated during flushing
- if (flushThread.joinable())
- {
- flushThread.join();
- }
+ memorycard->joinThread();
if (cs) // not-selected to selected
{
@@ -226,7 +140,7 @@ void CEXIMemoryCard::SetCS(int cs)
case cmdSectorErase:
if (m_uPosition > 2)
{
- memset(memory_card_content + (address & (memory_card_size-1)), 0xFF, 0x2000);
+ memorycard->ClearBlock(address & (memory_card_size - 1));
status |= MC_STATUS_BUSY;
status &= ~MC_STATUS_READY;
@@ -239,7 +153,8 @@ void CEXIMemoryCard::SetCS(int cs)
case cmdChipErase:
if (m_uPosition > 2)
{
- memset(memory_card_content, 0xFF, memory_card_size);
+ // TODO: Investigate on HW, I (LPFaint99) believe that this only erases the system area (Blocks 0-4)
+ memorycard->ClearAll();
status &= ~MC_STATUS_BUSY;
m_bDirty = true;
}
@@ -254,7 +169,7 @@ void CEXIMemoryCard::SetCS(int cs)
while (count--)
{
- memory_card_content[address] = programming_buffer[i++];
+ memorycard->Write(address, 1, &(programming_buffer[i++]));
i &= 127;
address = (address & ~0x1FF) | ((address+1) & 0x1FF);
}
@@ -336,7 +251,7 @@ void CEXIMemoryCard::TransferByte(u8 &byte)
if (m_uPosition == 1)
byte = 0x80; // dummy cycle
else
- byte = (u8)(nintendo_card_id >> (24-(((m_uPosition-2) & 3) * 8)));
+ byte = (u8)(memorycard->GetCardId() >> (24 - (((m_uPosition - 2) & 3) * 8)));
break;
case cmdReadArray:
@@ -358,7 +273,7 @@ void CEXIMemoryCard::TransferByte(u8 &byte)
}
if (m_uPosition > 1) // not specified for 1..8, anyway
{
- byte = memory_card_content[address & (memory_card_size-1)];
+ memorycard->Read(address & (memory_card_size - 1), 1, &byte);
// after 9 bytes, we start incrementing the address,
// but only the sector offset - the pointer wraps around
if (m_uPosition >= 9)
@@ -441,10 +356,7 @@ void CEXIMemoryCard::PauseAndLock(bool doLock, bool unpauseOnUnlock)
{
// we don't exactly have anything to pause,
// but let's make sure the flush thread isn't running.
- if (flushThread.joinable())
- {
- flushThread.join();
- }
+ memorycard->joinThread();
}
}
@@ -466,11 +378,7 @@ void CEXIMemoryCard::DoState(PointerWrap &p)
p.Do(programming_buffer);
p.Do(m_bDirty);
p.Do(address);
-
- p.Do(nintendo_card_id);
- p.Do(card_id);
- p.Do(memory_card_size);
- p.DoArray(memory_card_content, memory_card_size);
+ memorycard->DoState(p);
p.Do(card_index);
}
}
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h
index 60aed6556c..5d45d91027 100644
--- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h
@@ -3,18 +3,7 @@
// Refer to the license.txt file included.
#pragma once
-
-#include "Common/Thread.h"
-
-// Data structure to be passed to the flushing thread.
-struct FlushData
-{
- bool bExiting;
- std::string filename;
- u8 *memcardContent;
- int memcardSize, memcardIndex;
-};
-
+class MemoryCardBase;
class CEXIMemoryCard : public IEXIDevice
{
public:
@@ -63,7 +52,6 @@ private:
cmdChipErase = 0xF4,
};
- std::string m_strFilename;
int card_index;
int et_this_card, et_cmd_done;
//! memory card state
@@ -77,13 +65,10 @@ private:
u8 programming_buffer[128];
bool m_bDirty;
//! memory card parameters
- unsigned int nintendo_card_id, card_id;
+ unsigned int card_id;
unsigned int address;
- int memory_card_size; //! in bytes, must be power of 2.
- u8 *memory_card_content;
-
- FlushData flushData;
- std::thread flushThread;
+ u32 memory_card_size;
+ MemoryCardBase * memorycard;
protected:
virtual void TransferByte(u8 &byte) override;
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCardRaw.cpp b/Source/Core/Core/HW/EXI_DeviceMemoryCardRaw.cpp
new file mode 100644
index 0000000000..aff083093c
--- /dev/null
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCardRaw.cpp
@@ -0,0 +1,160 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+#include "Core/Core.h"
+#include "Core/HW/EXI_DeviceMemoryCardRaw.h"
+#include "Core/HW/GCMemcard.h"
+#define SIZE_TO_Mb (1024 * 8 * 16)
+#define MC_HDR_SIZE 0xA000
+
+void innerFlush(FlushData* data)
+{
+ File::IOFile pFile(data->filename, "r+b");
+ if (!pFile)
+ {
+ std::string dir;
+ SplitPath(data->filename, &dir, nullptr, nullptr);
+ if (!File::IsDirectory(dir))
+ File::CreateFullPath(dir);
+ pFile.Open(data->filename, "wb");
+ }
+
+ if (!pFile) // Note - pFile changed inside above if
+ {
+ PanicAlertT("Could not write memory card file %s.\n\n"
+ "Are you running Dolphin from a CD/DVD, or is the save file maybe write protected?\n\n"
+ "Are you receiving this after moving the emulator directory?\nIf so, then you may "
+ "need to re-specify your memory card location in the options.", data->filename.c_str());
+ return;
+ }
+
+ pFile.WriteBytes(data->memcardContent, data->memcardSize);
+
+ if (!data->bExiting)
+ Core::DisplayMessage(StringFromFormat("Wrote memory card %c contents to %s",
+ data->memcardIndex ? 'B' : 'A', data->filename.c_str()).c_str(), 4000);
+ return;
+}
+
+MemoryCard::MemoryCard(std::string filename, int _card_index, u16 sizeMb) : MemoryCardBase(_card_index, sizeMb), m_strFilename(filename), m_bDirty(false)
+{
+
+ std::string ;
+ File::IOFile pFile(m_strFilename, "rb");
+ if (pFile)
+ {
+ // Measure size of the memcard file.
+ memory_card_size = (int)pFile.GetSize();
+ nintendo_card_id = memory_card_size / SIZE_TO_Mb;
+ memory_card_content = new u8[memory_card_size];
+ memset(memory_card_content, 0xFF, memory_card_size);
+
+ INFO_LOG(EXPANSIONINTERFACE, "Reading memory card %s", m_strFilename.c_str());
+ pFile.ReadBytes(memory_card_content, memory_card_size);
+
+ }
+ else
+ {
+ // Create a new 128Mb memcard
+ nintendo_card_id = sizeMb;
+ memory_card_size = sizeMb * SIZE_TO_Mb;
+
+ memory_card_content = new u8[memory_card_size];
+ GCMemcard::Format(memory_card_content, m_strFilename.find(".JAP.raw") != std::string::npos, sizeMb);
+ memset(memory_card_content + MC_HDR_SIZE, 0xFF, memory_card_size - MC_HDR_SIZE);
+
+ WARN_LOG(EXPANSIONINTERFACE, "No memory card found. Will create a new one.");
+ }
+}
+
+void MemoryCard::joinThread()
+{
+ if (flushThread.joinable())
+ {
+ flushThread.join();
+ }
+}
+
+// Flush memory card contents to disc
+void MemoryCard::Flush(bool exiting)
+{
+ if (!m_bDirty)
+ return;
+
+ if (!Core::g_CoreStartupParameter.bEnableMemcardSaving)
+ return;
+
+ if (flushThread.joinable())
+ {
+ flushThread.join();
+ }
+
+ if (!exiting)
+ Core::DisplayMessage(StringFromFormat("Writing to memory card %c", card_index ? 'B' : 'A'), 1000);
+
+ flushData.filename = m_strFilename;
+ flushData.memcardContent = memory_card_content;
+ flushData.memcardIndex = card_index;
+ flushData.memcardSize = memory_card_size;
+ flushData.bExiting = exiting;
+
+ flushThread = std::thread(innerFlush, &flushData);
+ if (exiting)
+ flushThread.join();
+
+ m_bDirty = false;
+}
+
+s32 MemoryCard::Read(u32 srcaddress, s32 length, u8* destaddress)
+{
+ if (!memory_card_content)
+ return -1;
+ if (srcaddress > (memory_card_size - 1))
+ {
+ PanicAlertT("MemoryCard: Read called with invalid source address, %x", srcaddress);
+ return -1;
+ }
+
+ memcpy(destaddress, &(memory_card_content[srcaddress]), length);
+ return length;
+}
+
+s32 MemoryCard::Write(u32 destaddress, s32 length, u8* srcaddress)
+{
+ if (!memory_card_content)
+ return -1;
+
+ if (destaddress > (memory_card_size - 1))
+ {
+ PanicAlertT("MemoryCard: Write called with invalid destination address, %x", destaddress);
+ return -1;
+ }
+
+ m_bDirty = true;
+ memcpy(&(memory_card_content[destaddress]), srcaddress, length);
+ return length;
+}
+
+void MemoryCard::ClearBlock(u32 address)
+{
+ if (address & (BLOCK_SIZE - 1) || address > (memory_card_size - 1))
+ PanicAlertT("MemoryCard: ClearBlock called on invalid address %x", address);
+ else
+ {
+ m_bDirty = true;
+ memset(memory_card_content + address, 0xFF, BLOCK_SIZE);
+ }
+}
+
+void MemoryCard::ClearAll()
+{
+ m_bDirty = true;
+ memset(memory_card_content, 0xFF, memory_card_size);
+}
+
+void MemoryCard::DoState(PointerWrap &p)
+{
+ p.Do(card_index);
+ p.Do(memory_card_size);
+ p.DoArray(memory_card_content, memory_card_size);
+}
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCardRaw.h b/Source/Core/Core/HW/EXI_DeviceMemoryCardRaw.h
new file mode 100644
index 0000000000..ea1b22e2f3
--- /dev/null
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCardRaw.h
@@ -0,0 +1,58 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "Common/ChunkFile.h"
+#include "Common/Thread.h"
+#include "Core/HW/GCMemcard.h"
+
+// Data structure to be passed to the flushing thread.
+struct FlushData
+{
+ bool bExiting;
+ std::string filename;
+ u8 *memcardContent;
+ int memcardSize, memcardIndex;
+};
+
+class MemoryCardBase
+{
+public:
+ MemoryCardBase(int _card_index = 0, int sizeMb = MemCard2043Mb) :card_index(_card_index), nintendo_card_id(sizeMb) { ; }
+ virtual void Flush(bool exiting = false) = 0;
+ virtual s32 Read(u32 address, s32 length, u8* destaddress) = 0;
+ virtual s32 Write(u32 destaddress, s32 length, u8* srcaddress) = 0;
+ virtual void ClearBlock(u32 address) = 0;
+ virtual void ClearAll() = 0;
+ virtual void DoState(PointerWrap &p) = 0;
+ virtual void joinThread() {};
+ u32 GetCardId() { return nintendo_card_id; }
+protected:
+ int card_index;
+ u16 nintendo_card_id;
+ u32 memory_card_size;
+};
+
+class MemoryCard : public MemoryCardBase
+{
+public:
+ MemoryCard(std::string filename, int _card_index, u16 sizeMb = MemCard2043Mb);
+ ~MemoryCard() { Flush(true); }
+ void Flush(bool exiting = false) override;
+
+ s32 Read(u32 address, s32 length, u8* destaddress) override;
+ s32 Write(u32 destaddress, s32 length, u8* srcaddress) override;
+ void ClearBlock(u32 address) override;
+ void ClearAll() override;
+ void DoState(PointerWrap &p) override;
+ void joinThread() override;
+private:
+ u8 *memory_card_content;
+ bool m_bDirty;
+ std::string m_strFilename;
+
+ FlushData flushData;
+ std::thread flushThread;
+};