memory_card.cpp (12646B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "memory_card.h" 5 #include "host.h" 6 #include "system.h" 7 8 #include "util/imgui_manager.h" 9 #include "util/state_wrapper.h" 10 11 #include "common/bitutils.h" 12 #include "common/error.h" 13 #include "common/file_system.h" 14 #include "common/log.h" 15 #include "common/path.h" 16 #include "common/string_util.h" 17 18 #include "IconsFontAwesome5.h" 19 20 Log_SetChannel(MemoryCard); 21 22 MemoryCard::MemoryCard() 23 : m_save_event( 24 "Memory Card Host Flush", GetSaveDelayInTicks(), GetSaveDelayInTicks(), 25 [](void* param, TickCount ticks, TickCount ticks_late) { static_cast<MemoryCard*>(param)->SaveIfChanged(true); }, 26 this) 27 { 28 m_FLAG.no_write_yet = true; 29 } 30 31 MemoryCard::~MemoryCard() 32 { 33 SaveIfChanged(false); 34 } 35 36 TickCount MemoryCard::GetSaveDelayInTicks() 37 { 38 return System::GetTicksPerSecond() * SAVE_DELAY_IN_SECONDS; 39 } 40 41 void MemoryCard::Reset() 42 { 43 ResetTransferState(); 44 SaveIfChanged(true); 45 m_FLAG.no_write_yet = true; 46 } 47 48 bool MemoryCard::DoState(StateWrapper& sw) 49 { 50 if (sw.IsReading()) 51 SaveIfChanged(true); 52 53 sw.Do(&m_state); 54 sw.Do(&m_FLAG.bits); 55 sw.Do(&m_address); 56 sw.Do(&m_sector_offset); 57 sw.Do(&m_checksum); 58 sw.Do(&m_last_byte); 59 sw.Do(&m_data); 60 sw.Do(&m_changed); 61 62 return !sw.HasError(); 63 } 64 65 void MemoryCard::CopyState(const MemoryCard* src) 66 { 67 DebugAssert(m_data == src->m_data); 68 69 m_state = src->m_state; 70 m_FLAG.bits = src->m_FLAG.bits; 71 m_address = src->m_address; 72 m_sector_offset = src->m_sector_offset; 73 m_checksum = src->m_checksum; 74 m_last_byte = src->m_last_byte; 75 m_changed = src->m_changed; 76 } 77 78 void MemoryCard::ResetTransferState() 79 { 80 m_state = State::Idle; 81 m_address = 0; 82 m_sector_offset = 0; 83 m_checksum = 0; 84 m_last_byte = 0; 85 } 86 87 bool MemoryCard::Transfer(const u8 data_in, u8* data_out) 88 { 89 bool ack = false; 90 #ifdef _DEBUG 91 const State old_state = m_state; 92 #endif 93 94 switch (m_state) 95 { 96 97 #define FIXED_REPLY_STATE(state, reply, ack_value, next_state) \ 98 case state: \ 99 { \ 100 *data_out = reply; \ 101 ack = ack_value; \ 102 m_state = next_state; \ 103 } \ 104 break; 105 106 #define ADDRESS_STATE_MSB(state, next_state) \ 107 case state: \ 108 { \ 109 *data_out = 0x00; \ 110 ack = true; \ 111 m_address = ((m_address & u16(0x00FF)) | (ZeroExtend16(data_in) << 8)) & 0x3FF; \ 112 m_state = next_state; \ 113 } \ 114 break; 115 116 #define ADDRESS_STATE_LSB(state, next_state) \ 117 case state: \ 118 { \ 119 *data_out = m_last_byte; \ 120 ack = true; \ 121 m_address = ((m_address & u16(0xFF00)) | ZeroExtend16(data_in)) & 0x3FF; \ 122 m_sector_offset = 0; \ 123 m_state = next_state; \ 124 } \ 125 break; 126 127 // read state 128 129 FIXED_REPLY_STATE(State::ReadCardID1, 0x5A, true, State::ReadCardID2); 130 FIXED_REPLY_STATE(State::ReadCardID2, 0x5D, true, State::ReadAddressMSB); 131 ADDRESS_STATE_MSB(State::ReadAddressMSB, State::ReadAddressLSB); 132 ADDRESS_STATE_LSB(State::ReadAddressLSB, State::ReadACK1); 133 FIXED_REPLY_STATE(State::ReadACK1, 0x5C, true, State::ReadACK2); 134 FIXED_REPLY_STATE(State::ReadACK2, 0x5D, true, State::ReadConfirmAddressMSB); 135 FIXED_REPLY_STATE(State::ReadConfirmAddressMSB, Truncate8(m_address >> 8), true, State::ReadConfirmAddressLSB); 136 FIXED_REPLY_STATE(State::ReadConfirmAddressLSB, Truncate8(m_address), true, State::ReadData); 137 138 case State::ReadData: 139 { 140 const u8 bits = m_data[ZeroExtend32(m_address) * MemoryCardImage::FRAME_SIZE + m_sector_offset]; 141 if (m_sector_offset == 0) 142 { 143 DEV_LOG("Reading memory card sector {}", m_address); 144 m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ bits; 145 } 146 else 147 { 148 m_checksum ^= bits; 149 } 150 151 *data_out = bits; 152 ack = true; 153 154 m_sector_offset++; 155 if (m_sector_offset == MemoryCardImage::FRAME_SIZE) 156 { 157 m_state = State::ReadChecksum; 158 m_sector_offset = 0; 159 } 160 } 161 break; 162 163 FIXED_REPLY_STATE(State::ReadChecksum, m_checksum, true, State::ReadEnd); 164 FIXED_REPLY_STATE(State::ReadEnd, 0x47, true, State::Idle); 165 166 // write state 167 168 FIXED_REPLY_STATE(State::WriteCardID1, 0x5A, true, State::WriteCardID2); 169 FIXED_REPLY_STATE(State::WriteCardID2, 0x5D, true, State::WriteAddressMSB); 170 ADDRESS_STATE_MSB(State::WriteAddressMSB, State::WriteAddressLSB); 171 ADDRESS_STATE_LSB(State::WriteAddressLSB, State::WriteData); 172 173 case State::WriteData: 174 { 175 if (m_sector_offset == 0) 176 { 177 INFO_LOG("Writing memory card sector {}", m_address); 178 m_checksum = Truncate8(m_address >> 8) ^ Truncate8(m_address) ^ data_in; 179 m_FLAG.no_write_yet = false; 180 } 181 else 182 { 183 m_checksum ^= data_in; 184 } 185 186 const u32 offset = ZeroExtend32(m_address) * MemoryCardImage::FRAME_SIZE + m_sector_offset; 187 m_changed |= (m_data[offset] != data_in); 188 m_data[offset] = data_in; 189 190 *data_out = m_last_byte; 191 ack = true; 192 193 m_sector_offset++; 194 if (m_sector_offset == MemoryCardImage::FRAME_SIZE) 195 { 196 m_state = State::WriteChecksum; 197 m_sector_offset = 0; 198 if (m_changed) 199 QueueFileSave(); 200 } 201 } 202 break; 203 204 FIXED_REPLY_STATE(State::WriteChecksum, m_checksum, true, State::WriteACK1); 205 FIXED_REPLY_STATE(State::WriteACK1, 0x5C, true, State::WriteACK2); 206 FIXED_REPLY_STATE(State::WriteACK2, 0x5D, true, State::WriteEnd); 207 FIXED_REPLY_STATE(State::WriteEnd, 0x47, false, State::Idle); 208 209 // TODO: This really needs a proper buffer system... 210 FIXED_REPLY_STATE(State::GetIDCardID1, 0x5A, true, State::GetIDCardID2); 211 FIXED_REPLY_STATE(State::GetIDCardID2, 0x5D, true, State::GetIDACK1); 212 FIXED_REPLY_STATE(State::GetIDACK1, 0x5C, true, State::GetIDACK2); 213 FIXED_REPLY_STATE(State::GetIDACK2, 0x5D, true, State::GetID1); 214 FIXED_REPLY_STATE(State::GetID1, 0x04, true, State::GetID2); 215 FIXED_REPLY_STATE(State::GetID2, 0x00, true, State::GetID3); 216 FIXED_REPLY_STATE(State::GetID3, 0x00, true, State::GetID4); 217 FIXED_REPLY_STATE(State::GetID4, 0x80, true, State::Command); 218 219 // new command 220 case State::Idle: 221 { 222 // select device 223 if (data_in == 0x81) 224 { 225 *data_out = 0xFF; 226 ack = true; 227 m_state = State::Command; 228 } 229 } 230 break; 231 232 case State::Command: 233 { 234 switch (data_in) 235 { 236 case 0x52: // read data 237 { 238 *data_out = m_FLAG.bits; 239 ack = true; 240 m_state = State::ReadCardID1; 241 } 242 break; 243 244 case 0x57: // write data 245 { 246 *data_out = m_FLAG.bits; 247 ack = true; 248 m_state = State::WriteCardID1; 249 } 250 break; 251 252 case 0x53: // get id 253 { 254 *data_out = m_FLAG.bits; 255 ack = true; 256 m_state = State::GetIDCardID1; 257 } 258 break; 259 260 default: 261 [[unlikely]] 262 { 263 ERROR_LOG("Invalid command 0x{:02X}", data_in); 264 *data_out = m_FLAG.bits; 265 ack = false; 266 m_state = State::Idle; 267 } 268 } 269 } 270 break; 271 272 default: 273 UnreachableCode(); 274 break; 275 } 276 277 DEBUG_LOG("Transfer, old_state={}, new_state={}, data_in=0x{:02X}, data_out=0x{:02X}, ack={}", 278 static_cast<u32>(old_state), static_cast<u32>(m_state), data_in, *data_out, ack ? "true" : "false"); 279 m_last_byte = data_in; 280 return ack; 281 } 282 283 bool MemoryCard::IsOrWasRecentlyWriting() const 284 { 285 return (m_state == State::WriteData || m_save_event.IsActive()); 286 } 287 288 std::unique_ptr<MemoryCard> MemoryCard::Create() 289 { 290 std::unique_ptr<MemoryCard> mc = std::make_unique<MemoryCard>(); 291 mc->Format(); 292 return mc; 293 } 294 295 std::unique_ptr<MemoryCard> MemoryCard::Open(std::string_view filename) 296 { 297 std::unique_ptr<MemoryCard> mc = std::make_unique<MemoryCard>(); 298 mc->m_filename = filename; 299 300 Error error; 301 if (!FileSystem::FileExists(mc->m_filename.c_str())) [[unlikely]] 302 { 303 Host::AddIconOSDMessage(fmt::format("memory_card_{}", filename), ICON_FA_SD_CARD, 304 fmt::format(TRANSLATE_FS("OSDMessage", "Memory card '{}' does not exist, creating."), 305 Path::GetFileName(filename), Host::OSD_INFO_DURATION)); 306 mc->Format(); 307 mc->SaveIfChanged(true); 308 } 309 else if (!MemoryCardImage::LoadFromFile(&mc->m_data, mc->m_filename.c_str(), &error)) [[unlikely]] 310 { 311 Host::AddIconOSDMessage(fmt::format("memory_card_{}", filename), ICON_FA_SD_CARD, 312 fmt::format(TRANSLATE_FS("OSDMessage", "Memory card '{}' could not be read: {}"), 313 Path::GetFileName(filename), Host::OSD_INFO_DURATION)); 314 mc->Format(); 315 } 316 317 return mc; 318 } 319 320 void MemoryCard::Format() 321 { 322 MemoryCardImage::Format(&m_data); 323 m_changed = true; 324 } 325 326 bool MemoryCard::SaveIfChanged(bool display_osd_message) 327 { 328 m_save_event.Deactivate(); 329 330 if (!m_changed) 331 return true; 332 333 m_changed = false; 334 335 if (m_filename.empty()) 336 return false; 337 338 std::string osd_key; 339 std::string display_name; 340 if (display_osd_message) 341 { 342 osd_key = fmt::format("memory_card_save_{}", m_filename); 343 display_name = FileSystem::GetDisplayNameFromPath(m_filename); 344 } 345 346 INFO_LOG("Saving memory card to {}...", Path::GetFileTitle(m_filename)); 347 348 Error error; 349 if (!MemoryCardImage::SaveToFile(m_data, m_filename.c_str(), &error)) 350 { 351 if (display_osd_message) 352 { 353 Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_SD_CARD, 354 fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save memory card to '{}': {}"), 355 Path::GetFileName(display_name), error.GetDescription()), 356 Host::OSD_ERROR_DURATION); 357 } 358 359 return false; 360 } 361 362 if (display_osd_message) 363 { 364 Host::AddIconOSDMessage( 365 std::move(osd_key), ICON_FA_SD_CARD, 366 fmt::format(TRANSLATE_FS("OSDMessage", "Saved memory card to '{}'."), Path::GetFileName(display_name)), 367 Host::OSD_QUICK_DURATION); 368 } 369 370 return true; 371 } 372 373 void MemoryCard::QueueFileSave() 374 { 375 // skip if the event is already pending, or we don't have a backing file 376 if (m_save_event.IsActive() || m_filename.empty()) 377 return; 378 379 // save in one second, that should be long enough for everything to finish writing 380 m_save_event.Schedule(GetSaveDelayInTicks()); 381 }