diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp index 7a0a4f12e7..5c86b06464 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp @@ -180,6 +180,11 @@ s32 CEXIMemoryCard::ReadFromMemcard(u32 memcard_offset, s32 length, u8* dest_add return m_memory_card->Read(memcard_offset, length, dest_address); } +void CEXIMemoryCard::DisableWrites() +{ + m_allow_writes = false; +} + void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) { const std::string& game_id = SConfig::GetInstance().GetGameID(); @@ -297,7 +302,8 @@ void CEXIMemoryCard::SetCS(int cs) case Command::SectorErase: if (m_position > 2) { - m_memory_card->ClearBlock(m_address & (m_memory_card_size - 1)); + if (m_allow_writes) + m_memory_card->ClearBlock(m_address & (m_memory_card_size - 1)); m_status |= MC_STATUS_BUSY; m_status &= ~MC_STATUS_READY; @@ -310,7 +316,8 @@ void CEXIMemoryCard::SetCS(int cs) case Command::ChipErase: if (m_position > 2) { - m_memory_card->ClearAll(); + if (m_allow_writes) + m_memory_card->ClearAll(); m_status &= ~MC_STATUS_BUSY; } break; @@ -324,7 +331,8 @@ void CEXIMemoryCard::SetCS(int cs) while (count--) { - m_memory_card->Write(m_address, 1, &(m_programming_buffer[i++])); + if (m_allow_writes) + m_memory_card->Write(m_address, 1, &(m_programming_buffer[i++])); i &= 127; m_address = (m_address & ~0x1FF) | ((m_address + 1) & 0x1FF); } @@ -550,12 +558,16 @@ void CEXIMemoryCard::DMARead(u32 addr, u32 size) // write all at once instead of single byte at a time as done by IEXIDevice::DMAWrite void CEXIMemoryCard::DMAWrite(u32 addr, u32 size) { - auto& memory = m_system.GetMemory(); - m_memory_card->Write(m_address, size, memory.GetPointerForRange(addr, size)); + if (m_allow_writes) + { + auto& memory = m_system.GetMemory(); + m_memory_card->Write(m_address, size, memory.GetPointerForRange(addr, size)); + } if (((m_address + size) % Memcard::BLOCK_SIZE) == 0) { - INFO_LOG_FMT(EXPANSIONINTERFACE, "writing to block: {:x}", m_address / Memcard::BLOCK_SIZE); + INFO_LOG_FMT(EXPANSIONINTERFACE, "{}writing to block: {:x}", m_allow_writes ? "" : "fake ", + m_address / Memcard::BLOCK_SIZE); } // Schedule transfer complete later based on write speed diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h index 04f11b52f8..7c6ef9c1a5 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h @@ -66,6 +66,13 @@ public: s32 ReadFromMemcard(u32 memcard_offset, s32 length, u8* dest_address) const; + // After this call all writes to the card are disabled. + // This is used to have a 'grace period' after loading a savestate where the emulated software + // still sees the memory card but can't write to it anymore. + // It is expected that this device is destroyed and possibly recreated soon (within a few emulated + // frames) after this method has been called. + void DisableWrites(); + private: void SetupGciFolder(const Memcard::HeaderData& header_data); void SetupRawMemcard(u16 size_mb); @@ -121,6 +128,7 @@ private: unsigned int m_address; u32 m_memory_card_size; std::unique_ptr m_memory_card; + bool m_allow_writes = true; protected: void TransferByte(u8& byte) override;