move memorycard handling to its own class

This commit is contained in:
LPFaint99 2014-03-28 22:46:54 -07:00
parent 3d66e859d4
commit 404e9ce3ee
7 changed files with 258 additions and 138 deletions

View File

@ -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

View File

@ -135,6 +135,7 @@
<ClCompile Include="HW\EXI_DeviceGecko.cpp" />
<ClCompile Include="HW\EXI_DeviceIPL.cpp" />
<ClCompile Include="HW\EXI_DeviceMemoryCard.cpp" />
<ClCompile Include="HW\EXI_DeviceMemoryCardRaw.cpp" />
<ClCompile Include="HW\EXI_DeviceMic.cpp" />
<ClCompile Include="HW\GCMemcard.cpp" />
<ClCompile Include="HW\GCPad.cpp" />
@ -331,6 +332,7 @@
<ClInclude Include="HW\EXI_DeviceGecko.h" />
<ClInclude Include="HW\EXI_DeviceIPL.h" />
<ClInclude Include="HW\EXI_DeviceMemoryCard.h" />
<ClInclude Include="HW\EXI_DeviceMemoryCardRaw.h" />
<ClInclude Include="HW\EXI_DeviceMic.h" />
<ClInclude Include="HW\GCMemcard.h" />
<ClInclude Include="HW\GCPad.h" />

View File

@ -702,6 +702,9 @@
<ClCompile Include="PowerPC\JitILCommon\IR.cpp">
<Filter>PowerPC\JitILCommon</Filter>
</ClCompile>
<ClCompile Include="HW\EXI_DeviceMemoryCardRaw.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BootManager.h" />
@ -1207,6 +1210,9 @@
<ClInclude Include="PowerPC\JitILCommon\IR.h">
<Filter>PowerPC\JitILCommon</Filter>
</ClInclude>
<ClInclude Include="HW\EXI_DeviceMemoryCardRaw.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};