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