diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index fba13005e..f72b38c8b 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -12,6 +12,7 @@ HostInterface::~HostInterface() = default; bool HostInterface::InitializeSystem(const char* filename, const char* exp1_filename) { Settings settings; + settings.memory_card_a_filename = "memory_card_a.mcd"; m_system = std::make_unique(this, settings); if (!m_system->Initialize()) diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index 2aec2c2a3..3b900478c 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -1,12 +1,17 @@ #include "memory_card.h" +#include "YBaseLib/AutoReleasePtr.h" +#include "YBaseLib/ByteStream.h" +#include "YBaseLib/FileSystem.h" #include "YBaseLib/Log.h" #include "common/state_wrapper.h" +#include "host_interface.h" +#include "system.h" +#include Log_SetChannel(MemoryCard); -MemoryCard::MemoryCard() +MemoryCard::MemoryCard(System* system) : m_system(system) { m_FLAG.no_write_yet = true; - Format(); } MemoryCard::~MemoryCard() = default; @@ -24,6 +29,7 @@ bool MemoryCard::DoState(StateWrapper& sw) sw.Do(&m_checksum); sw.Do(&m_last_byte); sw.Do(&m_data); + sw.Do(&m_changed); return !sw.HasError(); } @@ -35,6 +41,7 @@ void MemoryCard::ResetTransferState() m_sector_offset = 0; m_checksum = 0; m_last_byte = 0; + m_changed = false; } bool MemoryCard::Transfer(const u8 data_in, u8* data_out) @@ -134,7 +141,12 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) m_checksum ^= data_in; } - m_data[ZeroExtend32(m_address) * SECTOR_SIZE + m_sector_offset] = data_in; + const u32 offset = ZeroExtend32(m_address) * SECTOR_SIZE + m_sector_offset; + if (m_data[offset] != data_in) + m_changed = true; + + m_data[offset] = data_in; + *data_out = m_last_byte; ack = true; @@ -143,6 +155,11 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) { m_state = State::WriteChecksum; m_sector_offset = 0; + if (m_changed) + { + m_changed = false; + SaveToFile(); + } } } break; @@ -213,9 +230,24 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out) return ack; } -std::shared_ptr MemoryCard::Create() +std::shared_ptr MemoryCard::Create(System* system) { - return std::make_shared(); + auto mc = std::make_shared(system); + mc->Format(); + return mc; +} + +std::shared_ptr MemoryCard::Open(System* system, std::string_view filename) +{ + auto mc = std::make_shared(system); + mc->m_filename = filename; + if (!mc->LoadFromFile()) + { + Log_ErrorPrintf("Memory card at '%s' could not be read, formatting."); + mc->Format(); + } + + return mc; } u8 MemoryCard::ChecksumFrame(const u8* fptr) @@ -285,3 +317,46 @@ u8* MemoryCard::GetSectorPtr(u32 sector) Assert(sector < NUM_SECTORS); return &m_data[sector * SECTOR_SIZE]; } + +bool MemoryCard::LoadFromFile() +{ + AutoReleasePtr stream = + FileSystem::OpenFile(m_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (!stream) + return false; + + const size_t num_read = stream->Read(m_data.data(), SECTOR_SIZE * NUM_SECTORS); + if (num_read != (SECTOR_SIZE * NUM_SECTORS)) + { + Log_ErrorPrintf("Only read %zu of %u sectors from '%s'", num_read / SECTOR_SIZE, NUM_SECTORS, m_filename.c_str()); + return false; + } + + return true; +} + +bool MemoryCard::SaveToFile() +{ + if (m_filename.empty()) + return false; + + AutoReleasePtr stream = + FileSystem::OpenFile(m_filename.c_str(), BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_TRUNCATE | BYTESTREAM_OPEN_WRITE | + BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); + if (!stream) + { + Log_ErrorPrintf("Failed to open '%s' for writing.", m_filename.c_str()); + return false; + } + + if (!stream->Write2(m_data.data(), SECTOR_SIZE * NUM_SECTORS) || !stream->Commit()) + { + Log_ErrorPrintf("Failed to write sectors to '%s'", m_filename.c_str()); + stream->Discard(); + return false; + } + + Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str()); + m_system->GetHostInterface()->AddOSDMessage(SmallString::FromFormat("Saved memory card to '%s'", m_filename.c_str())); + return true; +} \ No newline at end of file diff --git a/src/core/memory_card.h b/src/core/memory_card.h index 1645f76b4..9cbd0096a 100644 --- a/src/core/memory_card.h +++ b/src/core/memory_card.h @@ -1,8 +1,12 @@ #pragma once #include "common/bitfield.h" #include "pad_device.h" -#include #include +#include +#include +#include + +class System; class MemoryCard final : public PadDevice { @@ -14,10 +18,11 @@ public: NUM_SECTORS = DATA_SIZE / SECTOR_SIZE }; - MemoryCard(); + MemoryCard(System* system); ~MemoryCard() override; - static std::shared_ptr Create(); + static std::shared_ptr Create(System* system); + static std::shared_ptr Open(System* system, std::string_view filename); void Reset() override; bool DoState(StateWrapper& sw) override; @@ -70,11 +75,19 @@ private: u8* GetSectorPtr(u32 sector); + bool LoadFromFile(); + bool SaveToFile(); + + System* m_system; + State m_state = State::Idle; u16 m_address = 0; u8 m_sector_offset = 0; u8 m_checksum = 0; u8 m_last_byte = 0; + bool m_changed = false; std::array m_data{}; + + std::string m_filename; }; diff --git a/src/core/pad.cpp b/src/core/pad.cpp index 30b03e651..eac92a26f 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -1,7 +1,9 @@ #include "pad.h" #include "YBaseLib/Log.h" #include "common/state_wrapper.h" +#include "host_interface.h" #include "interrupt_controller.h" +#include "memory_card.h" #include "pad_device.h" #include "system.h" Log_SetChannel(Pad); @@ -46,6 +48,28 @@ bool Pad::DoState(StateWrapper& sw) return false; } + bool card_present = static_cast(m_memory_cards[i]); + sw.Do(&card_present); + + if (card_present && !m_memory_cards[i]) + { + const TinyString message = TinyString::FromFormat( + "Memory card %c present in save state but not in system. Creating temporary card.", 'A' + i); + m_system->GetHostInterface()->AddOSDMessage(message); + Log_WarningPrint(message); + + m_memory_cards[i] = MemoryCard::Create(m_system); + } + else if (!card_present && m_memory_cards[i]) + { + const TinyString message = + TinyString::FromFormat("Memory card %u present system but not save state. Removing card.", 'A' + i); + m_system->GetHostInterface()->AddOSDMessage(message); + Log_WarningPrint(message); + + m_memory_cards[i].reset(); + } + if (m_memory_cards[i]) { if (!sw.DoMarker("MemoryCard") || !m_memory_cards[i]->DoState(sw)) diff --git a/src/core/pad.h b/src/core/pad.h index 289442fb4..36cc0050b 100644 --- a/src/core/pad.h +++ b/src/core/pad.h @@ -10,6 +10,7 @@ class StateWrapper; class System; class InterruptController; class PadDevice; +class MemoryCard; class Pad { @@ -24,8 +25,8 @@ public: PadDevice* GetController(u32 slot) { return m_controllers[slot].get(); } void SetController(u32 slot, std::shared_ptr dev) { m_controllers[slot] = dev; } - PadDevice* GetMemoryCard(u32 slot) { return m_memory_cards[slot].get(); } - void SetMemoryCard(u32 slot, std::shared_ptr dev) { m_memory_cards[slot] = dev; } + MemoryCard* GetMemoryCard(u32 slot) { return m_memory_cards[slot].get(); } + void SetMemoryCard(u32 slot, std::shared_ptr dev) { m_memory_cards[slot] = dev; } u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); @@ -118,5 +119,5 @@ private: InlineFIFOQueue m_TX_FIFO; std::array, NUM_SLOTS> m_controllers; - std::array, NUM_SLOTS> m_memory_cards; + std::array, NUM_SLOTS> m_memory_cards; }; diff --git a/src/core/settings.h b/src/core/settings.h index baab818f7..455d7e678 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -32,4 +32,7 @@ struct Settings } debugging; // TODO: Controllers, memory cards, etc. + + std::string memory_card_a_filename; + std::string memory_card_b_filename; }; diff --git a/src/core/system.cpp b/src/core/system.cpp index c66b6608d..f38851019 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -9,6 +9,7 @@ #include "gpu.h" #include "interrupt_controller.h" #include "mdec.h" +#include "memory_card.h" #include "pad.h" #include "pad_device.h" #include "spu.h" @@ -98,6 +99,8 @@ bool System::Initialize() if (!m_mdec->Initialize(this, m_dma.get())) return false; + UpdateMemoryCards(); + return true; } @@ -361,9 +364,24 @@ void System::SetController(u32 slot, std::shared_ptr dev) m_pad->SetController(slot, std::move(dev)); } -void System::SetMemoryCard(u32 slot, std::shared_ptr dev) +void System::UpdateMemoryCards() { - m_pad->SetMemoryCard(slot, std::move(dev)); + m_pad->SetMemoryCard(0, nullptr); + m_pad->SetMemoryCard(1, nullptr); + + if (!m_settings.memory_card_a_filename.empty()) + { + std::shared_ptr card = MemoryCard::Open(this, m_settings.memory_card_a_filename); + if (card) + m_pad->SetMemoryCard(0, std::move(card)); + } + + if (!m_settings.memory_card_b_filename.empty()) + { + std::shared_ptr card = MemoryCard::Open(this, m_settings.memory_card_b_filename); + if (card) + m_pad->SetMemoryCard(1, std::move(card)); + } } bool System::HasMedia() const diff --git a/src/core/system.h b/src/core/system.h index 24df00672..39b2fc5d4 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -71,7 +71,7 @@ public: void StallCPU(TickCount ticks); void SetController(u32 slot, std::shared_ptr dev); - void SetMemoryCard(u32 slot, std::shared_ptr dev); + void UpdateMemoryCards(); bool HasMedia() const; bool InsertMedia(const char* path); diff --git a/src/duckstation/sdl_interface.cpp b/src/duckstation/sdl_interface.cpp index da7010381..c7294c6ea 100644 --- a/src/duckstation/sdl_interface.cpp +++ b/src/duckstation/sdl_interface.cpp @@ -271,9 +271,6 @@ void SDLInterface::ConnectDevices() { m_controller = DigitalController::Create(); m_system->SetController(0, m_controller); - - m_memory_card = MemoryCard::Create(); - m_system->SetMemoryCard(0, m_memory_card); } std::unique_ptr SDLInterface::Create(const char* filename /* = nullptr */, diff --git a/src/duckstation/sdl_interface.h b/src/duckstation/sdl_interface.h index f20738816..0d644eeed 100644 --- a/src/duckstation/sdl_interface.h +++ b/src/duckstation/sdl_interface.h @@ -111,7 +111,6 @@ private: std::map m_sdl_controllers; std::shared_ptr m_controller; - std::shared_ptr m_memory_card; float m_vps = 0.0f; float m_fps = 0.0f;