diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc index 9c66c9a75..18b8074ce 100644 --- a/src/core/memorycard.cc +++ b/src/core/memorycard.cc @@ -19,13 +19,313 @@ #include "core/memorycard.h" +#include + #include "core/sio.h" +#include "magic_enum/include/magic_enum/magic_enum_all.hpp" #include "support/sjis_conv.h" -void PCSX::MemoryCard::acknowledge() { m_sio->acknowledge(); } +void PCSX::MemoryCards::loadMcds(const CommandLine::args &args) { + auto &settings = g_emulator->settings; + + auto argPath1 = args.get("memcard1"); + auto argPath2 = args.get("memcard2"); + if (argPath1.has_value()) { + settings.get() = argPath1.value(); + } + if (argPath2.has_value()) { + settings.get() = argPath1.value(); + } + PCSX::u8string path1 = settings.get().string(); + PCSX::u8string path2 = settings.get().string(); + + loadMcd(path1, m_memoryCard[0].getMcdData()); + loadMcd(path2, m_memoryCard[1].getMcdData()); +} + +void PCSX::MemoryCards::getMcdBlockInfo(MemoryCard::Which mcd, int block, McdBlock &info) { + if (block < 1 || block > 15) { + throw std::runtime_error(_("Wrong block number")); + } + + uint16_t clut[16]; + + info.reset(); + info.number = block; + info.mcd = mcd; + + char *data = getMcdData(mcd); + uint8_t *ptr = reinterpret_cast(data) + block * c_blockSize + 2; + auto &ta = info.titleAscii; + auto &ts = info.titleSjis; + info.iconCount = std::max(1, *ptr & 0x3); + + ptr += 2; + int x = 0; + + for (int i = 0; i < 48; i++) { + uint8_t b = *ptr++; + ts += b; + uint16_t c = b; + if (b & 0x80) { + c <<= 8; + b = *ptr++; + ts += b; + c |= b; + } + + // Poor man's SJIS to ASCII conversion + if (c >= 0x8281 && c <= 0x829a) { + c = (c - 0x8281) + 'a'; + } else if (c >= 0x824f && c <= 0x827a) { + c = (c - 0x824f) + '0'; + } else if (c == 0x8140) { + c = ' '; + } else if (c == 0x8143) { + c = ','; + } else if (c == 0x8144) { + c = '.'; + } else if (c == 0x8146) { + c = ':'; + } else if (c == 0x8147) { + c = ';'; + } else if (c == 0x8148) { + c = '?'; + } else if (c == 0x8149) { + c = '!'; + } else if (c == 0x815e) { + c = '/'; + } else if (c == 0x8168) { + c = '"'; + } else if (c == 0x8169) { + c = '('; + } else if (c == 0x816a) { + c = ')'; + } else if (c == 0x816d) { + c = '['; + } else if (c == 0x816e) { + c = ']'; + } else if (c == 0x817c) { + c = '-'; + } else if (c > 0x7e) { + c = '?'; + } + + ta += c; + } + + info.titleUtf8 = Sjis::toUtf8(ts); + + // Read CLUT + ptr = reinterpret_cast(data) + block * c_blockSize + 0x60; + std::memcpy(clut, ptr, 16 * sizeof(uint16_t)); + + // Icons can have 1 to 3 frames of animation + for (uint32_t i = 0; i < info.iconCount; i++) { + uint16_t *icon = &info.icon[i * 16 * 16]; + ptr = reinterpret_cast(data) + block * c_blockSize + 128 + 128 * i; // icon data + + // Fetch each pixel, store it in the icon array in ABBBBBGGGGGRRRRR with the alpha bit set to 1 + for (x = 0; x < 16 * 16; x++) { + const uint8_t entry = (uint8_t)*ptr; + icon[x++] = clut[entry & 0xf] | (1 << 15); + icon[x] = clut[entry >> 4] | (1 << 15); + ptr++; + } + } + + // Parse directory frame info + const auto directoryFrame = (uint8_t *)data + block * c_sectorSize; + uint32_t allocState = 0; + allocState |= directoryFrame[0]; + allocState |= directoryFrame[1] << 8; + allocState |= directoryFrame[2] << 16; + allocState |= directoryFrame[3] << 24; + info.allocState = allocState; + + char tmp[17]; + memset(tmp, 0, sizeof(tmp)); + std::strncpy(tmp, (const char *)&directoryFrame[0xa], 12); + info.id = tmp; + memset(tmp, 0, sizeof(tmp)); + std::strncpy(tmp, (const char *)&directoryFrame[0x16], 16); + info.name = tmp; + + uint32_t fileSize = 0; + fileSize |= directoryFrame[4]; + fileSize |= directoryFrame[5] << 8; + fileSize |= directoryFrame[6] << 16; + fileSize |= directoryFrame[7] << 24; + info.fileSize = fileSize; + + uint16_t nextBlock = 0; + nextBlock |= directoryFrame[8]; + nextBlock |= directoryFrame[9] << 8; + info.nextBlock = nextBlock == 0xffff ? -1 : (nextBlock + 1); + + // Check if the block is marked as free in the directory frame and adjust the name/filename if so + if (info.isErased()) { + info.reset(); + info.allocState = 0xa0; + info.titleAscii = "Free Block"; + info.titleSjis = "Free Block"; + info.titleUtf8 = "Free Block"; + } +} + +char *PCSX::MemoryCards::getMcdData(MemoryCard::Which mcd) { + return m_memoryCard[magic_enum::enum_integer(mcd)].getMcdData(); +} + +// Erase a memory card block by clearing it with 0s +// mcd: The memory card we want to use (1 or 2) +void PCSX::MemoryCards::eraseMcdFile(const McdBlock &block) { + char *data = getMcdData(block.mcd); + + // Set the block data to 0 + const size_t offset = block.number * c_blockSize; + std::memset(data + offset, 0, c_blockSize); + + // Fix up the corresponding directory frame in block 0. + const auto frame = (uint8_t *)data + block.number * c_sectorSize; + frame[0] = 0xa0; // Code for a freshly formatted block + for (auto i = 1; i < 0x7f; i++) { // Zero the rest of the frame + frame[i] = 0; + } + frame[0x7f] = 0xa0; // xor checksum of frame + + if (block.isErased()) { + return; + } + + auto nextBlock = block.nextBlock; + if ((nextBlock >= 1) && (nextBlock <= 15)) { + McdBlock next; + getMcdBlockInfo(block.mcd, nextBlock, next); + eraseMcdFile(next); + } +} + +unsigned PCSX::MemoryCards::getFreeSpace(MemoryCard::Which mcd) { + unsigned count = 0; + for (int i = 1; i < 16; i++) { + McdBlock block; + getMcdBlockInfo(mcd, i, block); + if (block.isErased()) { + count++; + } + } + + return count; +} + +unsigned PCSX::MemoryCards::getFileBlockCount(McdBlock block) { + if (block.isErased()) { + return 0; + } + + std::bitset<16> walked; + unsigned count = 1; -uint8_t PCSX::MemoryCard::transceive(uint8_t value) { - uint8_t data_out = m_spdr; + while (true) { + if ((block.nextBlock < 1) || (block.nextBlock > 15)) { + return count; + } + if (walked.test(block.nextBlock)) { + return count; + } + walked.set(block.nextBlock); + getMcdBlockInfo(block.mcd, block.nextBlock, block); + count++; + } +} + +int PCSX::MemoryCards::findFirstFree(MemoryCard::Which mcd) { + McdBlock block; + for (int i = 1; i < 16; i++) { + getMcdBlockInfo(mcd, i, block); + if (block.isErased()) { + return i; + } + } + + return -1; +} + +bool PCSX::MemoryCards::copyMcdFile(McdBlock block) { + auto other = otherMcd(block); + if (getFreeSpace(other) < getFileBlockCount(block)) { + return false; + } + const auto data = getMcdData(block); + const auto otherData = getMcdData(other); + + std::bitset<16> walked; + int prevBlock = -1; + + while (true) { + int dstBlock = findFirstFree(other); + if (dstBlock < 1 || dstBlock > 16) { + throw std::runtime_error("Inconsistent memory card state"); + } + + // copy block data + size_t srcOffset = block.number * c_blockSize; + size_t dstOffset = dstBlock * c_blockSize; + std::memcpy(otherData + dstOffset, data + srcOffset, c_blockSize); + + // copy directory entry + srcOffset = block.number * c_sectorSize; + dstOffset = dstBlock * c_sectorSize; + std::memcpy(otherData + dstOffset, data + srcOffset, c_sectorSize); + + // Fix up the corresponding directory frame in block 0. + if (prevBlock != -1) { + const auto frame = reinterpret_cast(otherData) + prevBlock * c_sectorSize; + uint8_t crcFix = frame[8] ^ (dstBlock - 1); + frame[8] = dstBlock - 1; + frame[0x7f] ^= crcFix; + } + prevBlock = dstBlock; + if (block.nextBlock == -1) { + return true; + } + if ((block.nextBlock < 1) || (block.nextBlock > 15)) { + return false; + } + if (walked.test(block.nextBlock)) { + return false; + } + walked.set(block.nextBlock); + getMcdBlockInfo(block.mcd, block.nextBlock, block); + } +} + +// Back up the entire memory card to a file +// index: The memory card to back up (0-7) +bool PCSX::MemoryCards::saveMcd(MemoryCard::Which which) { + return saveMcd(getMcdPath(which), m_memoryCard[magic_enum::enum_integer(which)].getMcdData(), 0, c_cardSize); +} + +void PCSX::MemoryCards::resetCard(MemoryCard::Which which) { m_memoryCard[magic_enum::enum_integer(which)].reset(); } + +void PCSX::MemoryCards::setPocketstationEnabled(MemoryCard::Which which, bool enabled) { + m_memoryCard[magic_enum::enum_integer(which)].setPocketstationEnabled(enabled); +} + +void PCSX::MemoryCard::commit() { + for (int retry_count = 0; retry_count < 3; retry_count++) { + if (g_emulator->m_memoryCards->saveMcd(m_whichDevice)) { + m_savedToDisk = true; + break; + } else { + PCSX::g_system->printf(_("Failed to save card %d, attempt %d/3"), magic_enum::enum_integer(m_whichDevice) + 1, retry_count + 1); + } + } +} + +uint8_t PCSX::MemoryCard::transceive(uint8_t value, bool *ack) { + uint8_t dataOut = m_spdr; if (m_currentCommand == Commands::None || m_currentCommand == Commands::Access) { m_currentCommand = value; @@ -36,44 +336,44 @@ uint8_t PCSX::MemoryCard::transceive(uint8_t value) { case Commands::Access: // 81h // Incoming value is the device command m_spdr = m_directoryFlag; - acknowledge(); + *ack = true; break; // Read a sector case Commands::Read: // 52h - m_spdr = tickReadCommand(value); + m_spdr = tickReadCommand(value, ack); break; // Write a sector case Commands::Write: // 57h - m_spdr = tickWriteCommand(value); + m_spdr = tickWriteCommand(value, ack); break; // case Commands::PS_GetVersion: // 58h if (m_pocketstationEnabled) { - m_spdr = tickPS_GetVersion(value); + m_spdr = tickPS_GetVersion(value, ack); } break; // case Commands::PS_PrepFileExec: // 59h if (m_pocketstationEnabled) { - m_spdr = tickPS_PrepFileExec(value); + m_spdr = tickPS_PrepFileExec(value, ack); } break; // case Commands::PS_GetDirIndex: // 5Ah if (m_pocketstationEnabled) { - m_spdr = tickPS_GetDirIndex(value); + m_spdr = tickPS_GetDirIndex(value, ack); } break; // case Commands::PS_ExecCustom: // 5Dh if (m_pocketstationEnabled) { - m_spdr = tickPS_ExecCustom(value); + m_spdr = tickPS_ExecCustom(value, ack); } break; @@ -84,49 +384,49 @@ uint8_t PCSX::MemoryCard::transceive(uint8_t value) { break; } - return data_out; + return dataOut; } -uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value) { - uint8_t data_out = 0xFF; +inline uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value, bool *ack) { + uint8_t dataOut = 0xFF; switch (m_commandTicks) { case 0: - data_out = Responses::ID1; + dataOut = Responses::ID1; break; case 1: - data_out = Responses::ID2; + dataOut = Responses::ID2; break; case 2: - data_out = Responses::Dummy; + dataOut = Responses::Dummy; break; case 3: // MSB m_sector = (value << 8); - data_out = value; + dataOut = value; break; case 4: // LSB // Store lower 8 bits of sector m_sector |= value; m_dataOffset = m_sector * 128; - data_out = Responses::CommandAcknowledge1; + dataOut = Responses::CommandAcknowledge1; break; case 5: // 00h - data_out = Responses::CommandAcknowledge2; + dataOut = Responses::CommandAcknowledge2; break; case 6: // 00h // Confirm MSB - data_out = m_sector >> 8; + dataOut = m_sector >> 8; break; case 7: // 00h // Confirm LSB - data_out = (m_sector & 0xFF); + dataOut = (m_sector & 0xFF); m_checksumOut = (m_sector >> 8) ^ (m_sector & 0xff); break; @@ -134,12 +434,12 @@ uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value) { default: if (m_commandTicks >= 8 && m_commandTicks <= 135) { // Stay here for 128 bytes if (m_sector >= 1024) { - data_out = Responses::BadSector; + dataOut = Responses::BadSector; } else { - data_out = m_mcdData[m_dataOffset++]; + dataOut = m_mcdData[m_dataOffset++]; } - m_checksumOut ^= data_out; + m_checksumOut ^= dataOut; } else { // Send this till the spooky extra bytes go away return Responses::CommandAcknowledge1; @@ -147,22 +447,22 @@ uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value) { break; case 136: - data_out = m_checksumOut; + dataOut = m_checksumOut; break; case 137: - data_out = Responses::GoodReadWrite; + dataOut = Responses::GoodReadWrite; break; } m_commandTicks++; - acknowledge(); + *ack = true; - return data_out; + return dataOut; } -uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) { - uint8_t data_out = 0xFF; +inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) { + uint8_t dataOut = 0xFF; switch (m_commandTicks) { // Data is sent and received simultaneously, @@ -173,22 +473,22 @@ uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) { // Offset "Send" bytes noted from nocash's psx specs. case 0: // 57h - data_out = Responses::ID1; + dataOut = Responses::ID1; break; case 1: // 00h - data_out = Responses::ID2; + dataOut = Responses::ID2; break; case 2: // 00h - data_out = Responses::Dummy; + dataOut = Responses::Dummy; break; case 3: // MSB // Store upper 8 bits of sector m_sector = (value << 8); // Reply with (pre) - data_out = value; + dataOut = value; break; case 4: // LSB @@ -197,7 +497,7 @@ uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) { // m_dataOffset = (m_sector * 128); m_dataOffset = 0; m_checksumOut = (m_sector >> 8) ^ (m_sector & 0xFF); - data_out = value; + dataOut = value; break; // Cases 5 through 135 overloaded to default operator below @@ -211,7 +511,7 @@ uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) { m_checksumOut ^= value; // Reply with (pre) - data_out = value; + dataOut = value; m_dataOffset++; } else { // Send this till the spooky extra bytes go away @@ -233,116 +533,114 @@ uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) { m_commandTicks = 0xFF; return Responses::BadChecksum; } else { - data_out = Responses::CommandAcknowledge1; + dataOut = Responses::CommandAcknowledge1; } break; case 134: // 00h - data_out = Responses::CommandAcknowledge2; + dataOut = Responses::CommandAcknowledge2; break; case 135: // 00h m_directoryFlag = Flags::DirectoryRead; - data_out = Responses::GoodReadWrite; + dataOut = Responses::GoodReadWrite; memcpy(&m_mcdData[m_sector * 128], &m_tempBuffer, c_sectorSize); m_savedToDisk = false; + commit(); break; } m_commandTicks++; - acknowledge(); + *ack = true; - return data_out; + return dataOut; } -uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value) { - uint8_t data_out = Responses::IdleHighZ; +inline uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value, bool *ack) { + uint8_t dataOut = Responses::IdleHighZ; static constexpr uint8_t response_count = 19; static constexpr uint8_t responses[response_count] = {0x12, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x13, 0x11, 0x4F, 0x41, 0x20, 0x01, 0x99, 0x19, 0x27, 0x30, 0x09, 0x04}; if (m_commandTicks < response_count) { - data_out = responses[m_commandTicks]; + dataOut = responses[m_commandTicks]; // Don't ack the last byte if (m_commandTicks <= (response_count - 1)) { - acknowledge(); + *ack = true; } } m_commandTicks++; - return data_out; + return dataOut; } -uint8_t PCSX::MemoryCard::tickPS_ExecCustom(uint8_t value) { - uint8_t data_out = Responses::IdleHighZ; +inline uint8_t PCSX::MemoryCard::tickPS_ExecCustom(uint8_t value, bool *ack) { + uint8_t dataOut = Responses::IdleHighZ; switch (m_commandTicks) { case 0: // 5D - data_out = 0x03; + dataOut = 0x03; break; default: - data_out = 0x00; + dataOut = 0x00; break; } m_commandTicks++; - acknowledge(); + *ack = true; - return data_out; + return dataOut; } -uint8_t PCSX::MemoryCard::tickPS_PrepFileExec(uint8_t value) { - uint8_t data_out = Responses::IdleHighZ; +inline uint8_t PCSX::MemoryCard::tickPS_PrepFileExec(uint8_t value, bool *ack) { + uint8_t dataOut = Responses::IdleHighZ; switch (m_commandTicks) { case 0: // 59 - data_out = 0x06; + dataOut = 0x06; break; default: - data_out = 0x00; + dataOut = 0x00; break; } m_commandTicks++; - acknowledge(); + *ack = true; - return data_out; + return dataOut; } -uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value) { - uint8_t data_out = Responses::IdleHighZ; +inline uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value, bool *ack) { + uint8_t dataOut = Responses::IdleHighZ; static constexpr uint8_t response_count = 3; static constexpr uint8_t responses[response_count] = {0x02, 0x01, 0x01}; if (m_commandTicks < response_count) { - data_out = responses[m_commandTicks]; + dataOut = responses[m_commandTicks]; // Don't ack the last byte if (m_commandTicks <= (response_count - 1)) { - acknowledge(); + *ack = true; } } m_commandTicks++; - return data_out; + return dataOut; } // To-do: "All the code starting here is terrible and needs to be rewritten" -void PCSX::MemoryCard::loadMcd(PCSX::u8string mcd) { - char *data = m_mcdData; +bool PCSX::MemoryCards::loadMcd(PCSX::u8string mcd, char *data) { if (std::filesystem::path(mcd).is_relative()) { mcd = (g_system->getPersistentDir() / mcd).u8string(); } const char *fname = reinterpret_cast(mcd.c_str()); size_t bytesRead; - m_directoryFlag = Flags::DirectoryUnread; - FILE *f = fopen(fname, "rb"); if (f == nullptr) { PCSX::g_system->printf(_("The memory card %s doesn't exist - creating it\n"), fname); @@ -384,12 +682,14 @@ void PCSX::MemoryCard::loadMcd(PCSX::u8string mcd) { if (bytesRead != c_cardSize) { throw std::runtime_error(_("Error reading memory card.")); } else { - m_savedToDisk = true; + return true; } } + + return false; } -void PCSX::MemoryCard::saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size) { +bool PCSX::MemoryCards::saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size) { if (std::filesystem::path(mcd).is_relative()) { mcd = (g_system->getPersistentDir() / mcd).u8string(); } @@ -413,8 +713,9 @@ void PCSX::MemoryCard::saveMcd(PCSX::u8string mcd, const char *data, uint32_t ad fwrite(data + adr, 1, size, f); fclose(f); - m_savedToDisk = true; PCSX::g_system->printf(_("Saving memory card %s\n"), fname); + + return true; } else { // try to create it again if we can't open it f = fopen(fname, "wb"); @@ -423,9 +724,11 @@ void PCSX::MemoryCard::saveMcd(PCSX::u8string mcd, const char *data, uint32_t ad fclose(f); } } + + return false; } -void PCSX::MemoryCard::createMcd(PCSX::u8string mcd) { +void PCSX::MemoryCards::createMcd(PCSX::u8string mcd) { if (std::filesystem::path(mcd).is_relative()) { mcd = (g_system->getPersistentDir() / mcd).u8string(); } diff --git a/src/core/memorycard.h b/src/core/memorycard.h index 68881458d..660c7fe50 100644 --- a/src/core/memorycard.h +++ b/src/core/memorycard.h @@ -21,18 +21,27 @@ #include +#include "core/psxemulator.h" #include "core/sstate.h" +#include "magic_enum/include/magic_enum/magic_enum_all.hpp" namespace PCSX { class SIO; +/// +/// Implements a memory card for SIO +/// class MemoryCard { public: - MemoryCard() : m_sio(nullptr) { memset(m_mcdData, 0, c_cardSize); } - MemoryCard(SIO *parent) : m_sio(parent) { memset(m_mcdData, 0, c_cardSize); } + enum class Which : uint8_t { + One = 0, + Two = 1, + }; + + MemoryCard(Which which) : m_whichDevice(which) {} // Hardware events - void acknowledge(); + void deselect() { memset(&m_tempBuffer, 0, c_sectorSize); m_currentCommand = Commands::None; @@ -41,21 +50,16 @@ class MemoryCard { m_sector = 0; m_spdr = Responses::IdleHighZ; } + void reset() { + deselect(); + m_directoryFlag = Flags::DirectoryUnread; + } + + void setPocketstationEnabled(bool enabled) { m_pocketstationEnabled = enabled; }; // File system / data manipulation - void commit(const PCSX::u8string path) { - if (!m_savedToDisk) { - saveMcd(path); - } - } - void createMcd(PCSX::u8string mcd); - bool dataChanged() { return !m_savedToDisk; } - void disablePocketstation() { m_pocketstationEnabled = false; }; - void enablePocketstation() { m_pocketstationEnabled = true; }; + void commit(); char *getMcdData() { return m_mcdData; } - void loadMcd(PCSX::u8string mcd); - void saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size); - void saveMcd(PCSX::u8string mcd) { saveMcd(mcd, m_mcdData, 0, c_cardSize); } private: enum Commands : uint8_t { @@ -97,21 +101,21 @@ class MemoryCard { friend class SIO; friend SaveStates::SaveState SaveStates::constructSaveState(); - static constexpr size_t c_sectorSize = 8 * 16; - static constexpr size_t c_blockSize = 8192; - static constexpr size_t c_cardSize = 1024 * c_sectorSize; + static constexpr size_t c_sectorSize = 8 * 16; // 80h bytes per sector/frame + static constexpr size_t c_blockSize = c_sectorSize * 64; // 40h sectors per block + static constexpr size_t c_cardSize = c_blockSize * 16; // 16 blocks per frame(directory+15 saves); // State machine / handlers - uint8_t transceive(uint8_t value); - uint8_t tickReadCommand(uint8_t value); - uint8_t tickWriteCommand(uint8_t value); - uint8_t tickPS_GetDirIndex(uint8_t value); // 5Ah - uint8_t tickPS_GetVersion(uint8_t value); // 58h - uint8_t tickPS_PrepFileExec(uint8_t value); // 59h - uint8_t tickPS_ExecCustom(uint8_t value); // 5Dh - - char m_mcdData[c_cardSize]; - uint8_t m_tempBuffer[c_sectorSize]; + uint8_t transceive(uint8_t value, bool *ack); // * + uint8_t tickReadCommand(uint8_t value, bool *ack); // 52h + uint8_t tickWriteCommand(uint8_t value, bool *ack); // 57h + uint8_t tickPS_GetDirIndex(uint8_t value, bool *ack); // 5Ah + uint8_t tickPS_GetVersion(uint8_t value, bool *ack); // 58h + uint8_t tickPS_PrepFileExec(uint8_t value, bool *ack); // 59h + uint8_t tickPS_ExecCustom(uint8_t value, bool *ack); // 5Dh + + char m_mcdData[c_cardSize] = {0}; + uint8_t m_tempBuffer[c_blockSize] = {0}; bool m_savedToDisk = false; uint8_t m_checksumIn = 0, m_checksumOut = 0; @@ -128,7 +132,109 @@ class MemoryCard { bool m_pocketstationEnabled = false; uint16_t m_directoryIndex = 0; - SIO *m_sio; + SIO *m_sio = nullptr; + Which m_whichDevice = Which::One; +}; + +/// +/// Helper functions for MemoryCard class, gui, and filesystem +/// +class MemoryCards { + public: + void deselect() { + m_memoryCard[0].deselect(); + m_memoryCard[1].deselect(); + } + + void reset() { + m_memoryCard[0].reset(); + m_memoryCard[1].reset(); + } + + struct McdBlock { + McdBlock() { reset(); } + MemoryCard::Which mcd; + int number; + std::string titleAscii; + std::string titleSjis; + std::string titleUtf8; + std::string id; + std::string name; + uint32_t fileSize; + uint32_t iconCount; + uint16_t icon[16 * 16 * 3]; + uint32_t allocState; + int16_t nextBlock; + void reset() { + mcd = MemoryCard::Which::One; + number = 0; + titleAscii.clear(); + titleSjis.clear(); + titleUtf8.clear(); + id.clear(); + name.clear(); + fileSize = 0; + iconCount = 0; + memset(icon, 0, sizeof(icon)); + allocState = 0; + nextBlock = -1; + } + bool isErased() const { return (allocState & 0xa0) == 0xa0; } + bool isChained() const { return (allocState & ~1) == 0x52; } + }; + + static constexpr size_t c_sectorSize = 8 * 16; // 80h bytes per sector/frame + static constexpr size_t c_blockSize = c_sectorSize * 64; // 40h sectors per block + static constexpr size_t c_cardSize = c_blockSize * 16; // 16 blocks per frame(directory+15 saves) + + bool copyMcdFile(McdBlock block); + void eraseMcdFile(const McdBlock &block); + void eraseMcdFile(MemoryCard::Which mcd, int block) { + McdBlock info; + getMcdBlockInfo(mcd, block, info); + eraseMcdFile(info); + } + int findFirstFree(MemoryCard::Which mcd); + unsigned getFreeSpace(MemoryCard::Which mcd); + unsigned getFileBlockCount(McdBlock block); + void getMcdBlockInfo(MemoryCard::Which mcd, int block, McdBlock &info); + char *getMcdData(MemoryCard::Which mcd); + char *getMcdData(const McdBlock &block) { return getMcdData(block.mcd); } + + // File operations + void createMcd(PCSX::u8string mcd); + void loadMcds(const CommandLine::args &args); + bool saveMcd(MemoryCard::Which which); + + bool loadMcd(PCSX::u8string mcd, char *data); + bool saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size); + + static constexpr MemoryCard::Which otherMcd(MemoryCard::Which mcd) { + if ((mcd != MemoryCard::Which::One) && (mcd != MemoryCard::Which::Two)) + throw std::runtime_error("Bad memory card number"); + if (mcd == MemoryCard::Which::One) return MemoryCard::Which::Two; + return MemoryCard::Which::One; + } + + PCSX::u8string getMcdPath(MemoryCard::Which which) { + std::filesystem::path *paths[] = {&PCSX::g_emulator->settings.get().value, + &PCSX::g_emulator->settings.get().value}; + + PCSX::u8string thepath = paths[magic_enum::enum_integer(which)]->u8string(); + return thepath; + } + bool isCardInserted(MemoryCard::Which which) { + bool *const inserted_lut[] = {&PCSX::g_emulator->settings.get().value, + &PCSX::g_emulator->settings.get().value}; + + return *inserted_lut[magic_enum::enum_integer(which)]; + } + + static constexpr MemoryCard::Which otherMcd(const McdBlock &block) { return otherMcd(block.mcd); } + void resetCard(MemoryCard::Which which); + void setPocketstationEnabled(MemoryCard::Which which, bool enabled); + + MemoryCard m_memoryCard[2] = {MemoryCard(MemoryCard::Which::One), MemoryCard(MemoryCard::Which::Two)}; }; } // namespace PCSX diff --git a/src/core/pad.cc b/src/core/pad.cc index b8797e8b4..82849457d 100644 --- a/src/core/pad.cc +++ b/src/core/pad.cc @@ -45,8 +45,6 @@ class PadsImpl : public PCSX::Pads { PadsImpl(); void init() override; void shutdown() override; - uint8_t startPoll(Port port) override; - uint8_t poll(uint8_t value, Port port, uint32_t& padState) override; json getCfg() override; void setCfg(const json& j) override; @@ -63,10 +61,18 @@ class PadsImpl : public PCSX::Pads { if (pad > m_pads.size()) { return false; } else { - return m_pads[pad - 1].isControllerConnected(); + return m_pads[pad].isControllerConnected(); } } + void deselect() { + for (int i = 0; i < m_pads.size(); i++) { + m_pads[i].deselect(); + } + } + + uint8_t transceive(int index, uint8_t value, bool* ack) override { return m_pads[index].transceive(value, ack); } + private: PCSX::EventBus::Listener m_listener; // This is a list of all of the valid GLFW gamepad IDs that we have found querying GLFW. @@ -168,6 +174,12 @@ class PadsImpl : public PCSX::Pads { bool isControllerButtonPressed(int button, GLFWgamepadstate* state); bool isControllerConnected() { return m_settings.get(); } + void deselect() { + m_bufferIndex = 0; + m_padState = Pads::PAD_STATE_IDLE; + } + uint8_t transceive(uint8_t value, bool* ack); + json getCfg(); void setCfg(const json& j); void setDefaults(bool firstController); @@ -199,6 +211,13 @@ class PadsImpl : public PCSX::Pads { uint8_t m_stdpar[8] = {0x41, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; uint8_t m_mousepar[6] = {0x12, 0x5a, 0xff, 0xff, 0xff, 0xff}; uint8_t m_analogpar[8] = {0x73, 0x5a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + static constexpr size_t c_padBufferSize = 0x1010; + + uint8_t m_buffer[c_padBufferSize]; + uint32_t m_bufferIndex = 0; + uint32_t m_maxBufferIndex = 0; + uint32_t m_padState = Pads::PAD_STATE_IDLE; }; std::array m_pads; @@ -678,15 +697,49 @@ void PadsImpl::Pad::getButtons() { pad.buttonStatus = result ^ 0xffff; // Controls are inverted, so 0 = pressed } -uint8_t PadsImpl::startPoll(Port port) { - int index = magic_enum::enum_integer(port); - m_pads[index].getButtons(); - return m_pads[index].startPoll(); -} +uint8_t PadsImpl::Pad::transceive(uint8_t value, bool* ack) { + uint8_t dataOut = 0xff; + + switch (m_padState) { + case Pads::PAD_STATE_IDLE: // start pad + getButtons(); + m_buffer[0] = startPoll(); + m_maxBufferIndex = 2; + m_bufferIndex = 0; + m_padState = Pads::PAD_STATE_READ_COMMAND; + break; + + case Pads::PAD_STATE_READ_COMMAND: + m_padState = Pads::PAD_STATE_READ_DATA; + m_bufferIndex = 1; + m_buffer[m_bufferIndex] = poll(value, m_padState); + + if (!(m_buffer[m_bufferIndex] & 0x0f)) { + m_maxBufferIndex = 2 + 32; + } else { + m_maxBufferIndex = 2 + (m_buffer[m_bufferIndex] & 0x0f) * 2; + } + + break; + + case Pads::PAD_STATE_READ_DATA: + m_bufferIndex++; + m_buffer[m_bufferIndex] = poll(value, m_padState); + + if (m_bufferIndex == m_maxBufferIndex) { + m_padState = Pads::PAD_STATE_BAD_COMMAND; + } + break; + } + + dataOut = m_buffer[m_bufferIndex]; + + if (m_padState == Pads::PAD_STATE_BAD_COMMAND) { + } else { + *ack = true; + } -uint8_t PadsImpl::poll(uint8_t value, Port port, uint32_t& padState) { - int index = magic_enum::enum_integer(port); - return m_pads[index].poll(value, padState); + return dataOut; } uint8_t PadsImpl::Pad::poll(uint8_t value, uint32_t& padState) { diff --git a/src/core/pad.h b/src/core/pad.h index ba0801a7a..84a627578 100644 --- a/src/core/pad.h +++ b/src/core/pad.h @@ -29,6 +29,7 @@ using json = nlohmann::json; namespace PCSX { class GUI; +class SIO; class Pads { public: @@ -36,10 +37,17 @@ class Pads { virtual ~Pads() = default; + class InputDevice { + virtual void* getPadState() = 0; + virtual bool isButtonPressed(int button) = 0; + virtual void updateInput() = 0; + }; + virtual void init() = 0; virtual void shutdown() = 0; - virtual uint8_t startPoll(Port port) = 0; - virtual uint8_t poll(uint8_t value, Port port, uint32_t& padState) = 0; + + virtual void deselect() = 0; + virtual uint8_t transceive(int index, uint8_t value, bool* ack) = 0; virtual json getCfg() = 0; virtual void setCfg(const json& j) = 0; diff --git a/src/core/psxemulator.cc b/src/core/psxemulator.cc index 6276328f5..63531fdd0 100644 --- a/src/core/psxemulator.cc +++ b/src/core/psxemulator.cc @@ -66,6 +66,7 @@ PCSX::Emulator::Emulator() m_lua(new PCSX::Lua()), m_mdec(new PCSX::MDEC()), m_mem(new PCSX::Memory()), + m_memoryCards(new PCSX::MemoryCards()), m_pads(PCSX::Pads::factory()), m_patchManager(new PatchManager()), m_pioCart(new PCSX::PIOCart), diff --git a/src/core/psxemulator.h b/src/core/psxemulator.h index 8a4d0eb17..7a749c9a4 100644 --- a/src/core/psxemulator.h +++ b/src/core/psxemulator.h @@ -78,6 +78,7 @@ class HW; class Lua; class MDEC; class Memory; +class MemoryCards; class Pads; class PatchManager; class R3000Acpu; @@ -264,6 +265,7 @@ class Emulator { std::unique_ptr m_lua; std::unique_ptr m_mdec; std::unique_ptr m_mem; + std::unique_ptr m_memoryCards; std::unique_ptr m_pads; std::unique_ptr m_patchManager; std::unique_ptr m_pioCart; diff --git a/src/core/sio.cc b/src/core/sio.cc index d899dc6f8..9af91edd8 100644 --- a/src/core/sio.cc +++ b/src/core/sio.cc @@ -22,7 +22,6 @@ #include #include -#include #include #include "core/memorycard.h" @@ -44,9 +43,8 @@ void PCSX::SIO::acknowledge() { } void PCSX::SIO::init() { - reset(); - togglePocketstationMode(); g_emulator->m_pads->init(); + reset(); g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); } @@ -86,103 +84,72 @@ bool PCSX::SIO::isReceiveIRQReady() { bool PCSX::SIO::isTransmitReady() { const bool txEnabled = m_regs.control & ControlFlags::TX_ENABLE; const bool txFinished = m_regs.status & StatusFlags::TX_FINISHED; - const bool txDataNotEmpty = !(m_regs.status & StatusFlags::TX_DATACLEAR); + const bool txDataEmpty = m_regs.status & StatusFlags::TX_DATACLEAR; - return (txEnabled && txFinished && txDataNotEmpty); + return (txEnabled && txFinished && !txDataEmpty); } void PCSX::SIO::reset() { - m_rxFIFO.clear(); - m_padState = Pads::PAD_STATE_IDLE; - m_regs.status = StatusFlags::TX_DATACLEAR | StatusFlags::TX_FINISHED; m_regs.mode = 0; m_regs.control = 0; m_regs.baud = 0; - m_bufferIndex = 0; - m_memoryCard[0].deselect(); - m_memoryCard[1].deselect(); + + m_rxFIFO.clear(); m_currentDevice = DeviceType::None; + m_regs.status = StatusFlags::TX_DATACLEAR | StatusFlags::TX_FINISHED; + if (g_emulator) { + g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); + g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_SIO); + g_emulator->m_memoryCards->deselect(); + g_emulator->m_pads->deselect(); + } } -void PCSX::SIO::writePad(uint8_t value) { - switch (m_padState) { - case Pads::PAD_STATE_IDLE: // start pad - m_regs.status |= StatusFlags::RX_FIFONOTEMPTY; // Transfer is Ready - g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); +uint8_t PCSX::SIO::writeCard(uint8_t value) { + const int portIndex = ((m_regs.control & ControlFlags::WHICH_PORT) == SelectedPort::Port1) ? 0 : 1; + const bool isConnected = g_emulator->m_memoryCards->isCardInserted(magic_enum::enum_cast(portIndex).value()); - switch (m_regs.control & ControlFlags::WHICH_PORT) { - case SelectedPort::Port1: - if (!PCSX::g_emulator->m_pads->isPadConnected(1)) { - m_buffer[0] = 0xff; - return; - } - - m_buffer[0] = PCSX::g_emulator->m_pads->startPoll(Pads::Port::Port1); - break; - case SelectedPort::Port2: - if (!PCSX::g_emulator->m_pads->isPadConnected(2)) { - m_buffer[0] = 0xff; - return; - } - m_buffer[0] = PCSX::g_emulator->m_pads->startPoll(Pads::Port::Port2); - break; - } - - m_maxBufferIndex = 2; - m_bufferIndex = 0; - m_padState = Pads::PAD_STATE_READ_COMMAND; - break; + bool ack = false; + uint8_t rxBuffer = 0xff; - case Pads::PAD_STATE_READ_COMMAND: - m_padState = Pads::PAD_STATE_READ_DATA; - m_bufferIndex = 1; - switch (m_regs.control & ControlFlags::WHICH_PORT) { - case SelectedPort::Port1: - m_buffer[m_bufferIndex] = PCSX::g_emulator->m_pads->poll(value, Pads::Port::Port1, m_padState); - break; - case SelectedPort::Port2: - m_buffer[m_bufferIndex] = PCSX::g_emulator->m_pads->poll(value, Pads::Port::Port2, m_padState); - break; - } - - if (!(m_buffer[m_bufferIndex] & 0x0f)) { - m_maxBufferIndex = 2 + 32; - } else { - m_maxBufferIndex = 2 + (m_buffer[m_bufferIndex] & 0x0f) * 2; - } - break; + if (isConnected) { + rxBuffer = g_emulator->m_memoryCards->m_memoryCard[portIndex].transceive(m_regs.data, &ack); + } else { + m_currentDevice = DeviceType::Ignore; + } - case Pads::PAD_STATE_READ_DATA: - m_bufferIndex++; - switch (m_regs.control & ControlFlags::WHICH_PORT) { - case SelectedPort::Port1: - m_buffer[m_bufferIndex] = PCSX::g_emulator->m_pads->poll(value, Pads::Port::Port1, m_padState); - break; - case SelectedPort::Port2: - m_buffer[m_bufferIndex] = PCSX::g_emulator->m_pads->poll(value, Pads::Port::Port2, m_padState); - break; - } - - if (m_bufferIndex == m_maxBufferIndex) { - m_padState = Pads::PAD_STATE_IDLE; - m_currentDevice = DeviceType::Ignore; - return; - } - break; + if (ack) { + acknowledge(); + } + + return rxBuffer; +} + +uint8_t PCSX::SIO::writePad(uint8_t value) { + const int portIndex = ((m_regs.control & ControlFlags::WHICH_PORT) == SelectedPort::Port1) ? 0 : 1; + const bool isConnected = g_emulator->m_pads->isPadConnected(portIndex); + + bool ack = false; + uint8_t rxBuffer = 0xff; + + if (isConnected) { + rxBuffer = g_emulator->m_pads->transceive(portIndex, m_regs.data, &ack); + } else { + m_currentDevice = DeviceType::Ignore; } - if (m_padState == Pads::PAD_STATE_BAD_COMMAND) { - return; + if (ack) { + acknowledge(); } - acknowledge(); + return rxBuffer; } void PCSX::SIO::transmitData() { m_regs.status &= ~StatusFlags::TX_FINISHED; g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); - uint8_t m_rxBuffer = 0xff; + uint8_t rxBuffer = 0xff; if (m_currentDevice == DeviceType::None) { m_currentDevice = m_regs.data; @@ -190,58 +157,24 @@ void PCSX::SIO::transmitData() { switch (m_currentDevice) { case DeviceType::PAD: - // Pad Process events - writePad(m_regs.data); - m_rxBuffer = m_buffer[m_bufferIndex]; + rxBuffer = writePad(m_regs.data); break; case DeviceType::MemoryCard: - switch (m_regs.control & ControlFlags::WHICH_PORT) { - case SelectedPort::Port1: - if (PCSX::g_emulator->settings.get()) { - m_rxBuffer = m_memoryCard[0].transceive(m_regs.data); - if (m_memoryCard[0].dataChanged()) { - m_memoryCard[0].commit( - PCSX::g_emulator->settings.get().string().c_str()); - } - } else { - m_memoryCard[0].m_directoryFlag = MemoryCard::Flags::DirectoryUnread; - m_currentDevice = DeviceType::Ignore; - m_memoryCard[0].deselect(); - } - break; - - case SelectedPort::Port2: - if (PCSX::g_emulator->settings.get()) { - m_rxBuffer = m_memoryCard[1].transceive(m_regs.data); - if (m_memoryCard[1].dataChanged()) { - m_memoryCard[1].commit( - PCSX::g_emulator->settings.get().string().c_str()); - } - } else { - m_memoryCard[1].m_directoryFlag = MemoryCard::Flags::DirectoryUnread; - m_currentDevice = DeviceType::Ignore; - m_memoryCard[1].deselect(); - } - break; - } + rxBuffer = writeCard(m_regs.data); break; case DeviceType::Ignore: break; default: - m_currentDevice = DeviceType::None; - m_padState = Pads::PAD_STATE_IDLE; - m_memoryCard[0].deselect(); - m_memoryCard[1].deselect(); - break; + m_currentDevice = DeviceType::Ignore; } - m_rxFIFO.push(m_rxBuffer); + m_rxFIFO.push(rxBuffer); updateFIFOStatus(); - m_regs.data = m_rxBuffer; - g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(m_rxBuffer); + m_regs.data = rxBuffer; + g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(rxBuffer); if (isReceiveIRQReady() && !(m_regs.status & StatusFlags::IRQ)) { scheduleInterrupt(SIO_CYCLES); @@ -251,7 +184,7 @@ void PCSX::SIO::transmitData() { } void PCSX::SIO::write8(uint8_t value) { - SIO0_LOG("sio write8 %x (PAR:%x PAD:%x)\n", value, m_bufferIndex, m_padState); + SIO0_LOG("sio write8 %x\n", value); m_regs.data = value; m_regs.status &= ~StatusFlags::TX_DATACLEAR; @@ -269,24 +202,22 @@ void PCSX::SIO::writeMode16(uint16_t value) { m_regs.mode = value; } void PCSX::SIO::writeCtrl16(uint16_t value) { const bool deselected = (m_regs.control & ControlFlags::SELECT_ENABLE) && (!(value & ControlFlags::SELECT_ENABLE)); const bool selected = (!(m_regs.control & ControlFlags::SELECT_ENABLE)) && (value & ControlFlags::SELECT_ENABLE); - const bool portChanged = (m_regs.control & ControlFlags::WHICH_PORT) && (!(value & ControlFlags::WHICH_PORT)); - const bool wasReady = isTransmitReady(); + const bool port_changed = (m_regs.control & ControlFlags::WHICH_PORT) && (!(value & ControlFlags::WHICH_PORT)); + const bool was_ready = isTransmitReady(); m_regs.control = value; - SIO0_LOG("sio ctrlwrite16 %x (PAR:%x PAD:%x)\n", value, m_bufferIndex, m_padState); + SIO0_LOG("sio ctrlwrite16 %x\n", value); if (selected && (m_regs.control & ControlFlags::TX_IRQEN) && !(m_regs.status & StatusFlags::IRQ)) { scheduleInterrupt(SIO_CYCLES); } - if (deselected || portChanged) { + if (deselected || port_changed) { // Select line de-activated, reset state machines m_currentDevice = DeviceType::None; - m_padState = Pads::PAD_STATE_IDLE; - m_memoryCard[0].deselect(); - m_memoryCard[1].deselect(); - m_bufferIndex = 0; + g_emulator->m_memoryCards->deselect(); + g_emulator->m_pads->deselect(); } if (m_regs.control & ControlFlags::RESET_ERR) { @@ -300,15 +231,7 @@ void PCSX::SIO::writeCtrl16(uint16_t value) { } if (m_regs.control & ControlFlags::RESET) { - m_rxFIFO.clear(); - m_padState = Pads::PAD_STATE_IDLE; - m_memoryCard[0].deselect(); - m_memoryCard[1].deselect(); - m_bufferIndex = 0; - m_regs.status = StatusFlags::TX_DATACLEAR | StatusFlags::TX_FINISHED; - g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); - PCSX::g_emulator->m_cpu->m_regs.interrupt &= ~(1 << PCSX::PSXINT_SIO); - m_currentDevice = DeviceType::None; + reset(); } updateFIFOStatus(); @@ -318,7 +241,7 @@ void PCSX::SIO::writeCtrl16(uint16_t value) { g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status); } - if (wasReady == false && isTransmitReady()) { + if (was_ready == false && isTransmitReady()) { transmitData(); } } @@ -333,9 +256,7 @@ uint8_t PCSX::SIO::read8() { updateFIFOStatus(); } - SIO0_LOG("sio read8 ;ret = %x (I:%x ST:%x BUF:(%x %x %x))\n", ret, m_bufferIndex, m_regs.status, - m_buffer[m_bufferIndex > 0 ? m_bufferIndex - 1 : 0], m_buffer[m_bufferIndex], - m_buffer[m_bufferIndex < c_padBufferSize - 1 ? m_bufferIndex + 1 : c_padBufferSize - 1]); + SIO0_LOG("sio read8 ;ret = %x\n", ret); g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(ret); @@ -362,304 +283,6 @@ void PCSX::SIO::interrupt() { #endif } -void PCSX::SIO::getMcdBlockInfo(int mcd, int block, McdBlock &info) { - if (block < 1 || block > 15) { - throw std::runtime_error(_("Wrong block number")); - } - - uint16_t clut[16]; - - info.reset(); - info.number = block; - info.mcd = mcd; - - char *data = getMcdData(mcd); - uint8_t *ptr = reinterpret_cast(data) + block * c_blockSize + 2; - auto &ta = info.titleAscii; - auto &ts = info.titleSjis; - info.iconCount = std::max(1, *ptr & 0x3); - - ptr += 2; - int x = 0; - - for (int i = 0; i < 48; i++) { - uint8_t b = *ptr++; - ts += b; - uint16_t c = b; - if (b & 0x80) { - c <<= 8; - b = *ptr++; - ts += b; - c |= b; - } - - // Poor man's SJIS to ASCII conversion - if (c >= 0x8281 && c <= 0x829a) { - c = (c - 0x8281) + 'a'; - } else if (c >= 0x824f && c <= 0x827a) { - c = (c - 0x824f) + '0'; - } else if (c == 0x8140) { - c = ' '; - } else if (c == 0x8143) { - c = ','; - } else if (c == 0x8144) { - c = '.'; - } else if (c == 0x8146) { - c = ':'; - } else if (c == 0x8147) { - c = ';'; - } else if (c == 0x8148) { - c = '?'; - } else if (c == 0x8149) { - c = '!'; - } else if (c == 0x815e) { - c = '/'; - } else if (c == 0x8168) { - c = '"'; - } else if (c == 0x8169) { - c = '('; - } else if (c == 0x816a) { - c = ')'; - } else if (c == 0x816d) { - c = '['; - } else if (c == 0x816e) { - c = ']'; - } else if (c == 0x817c) { - c = '-'; - } else if (c > 0x7e) { - c = '?'; - } - - ta += c; - } - - info.titleUtf8 = Sjis::toUtf8(ts); - - // Read CLUT - ptr = reinterpret_cast(data) + block * c_blockSize + 0x60; - std::memcpy(clut, ptr, 16 * sizeof(uint16_t)); - - // Icons can have 1 to 3 frames of animation - for (uint32_t i = 0; i < info.iconCount; i++) { - uint16_t *icon = &info.icon[i * 16 * 16]; - ptr = reinterpret_cast(data) + block * c_blockSize + 128 + 128 * i; // icon data - - // Fetch each pixel, store it in the icon array in ABBBBBGGGGGRRRRR with the alpha bit set to 1 - for (x = 0; x < 16 * 16; x++) { - const uint8_t entry = (uint8_t)*ptr; - icon[x++] = clut[entry & 0xf] | (1 << 15); - icon[x] = clut[entry >> 4] | (1 << 15); - ptr++; - } - } - - // Parse directory frame info - const auto directoryFrame = (uint8_t *)data + block * c_sectorSize; - uint32_t allocState = 0; - allocState |= directoryFrame[0]; - allocState |= directoryFrame[1] << 8; - allocState |= directoryFrame[2] << 16; - allocState |= directoryFrame[3] << 24; - info.allocState = allocState; - - char tmp[17]; - memset(tmp, 0, sizeof(tmp)); - std::strncpy(tmp, (const char *)&directoryFrame[0xa], 12); - info.id = tmp; - memset(tmp, 0, sizeof(tmp)); - std::strncpy(tmp, (const char *)&directoryFrame[0x16], 16); - info.name = tmp; - - uint32_t fileSize = 0; - fileSize |= directoryFrame[4]; - fileSize |= directoryFrame[5] << 8; - fileSize |= directoryFrame[6] << 16; - fileSize |= directoryFrame[7] << 24; - info.fileSize = fileSize; - - uint16_t nextBlock = 0; - nextBlock |= directoryFrame[8]; - nextBlock |= directoryFrame[9] << 8; - info.nextBlock = nextBlock == 0xffff ? -1 : (nextBlock + 1); - - // Check if the block is marked as free in the directory frame and adjust the name/filename if so - if (info.isErased()) { - info.reset(); - info.allocState = 0xa0; - info.titleAscii = "Free Block"; - info.titleSjis = "Free Block"; - info.titleUtf8 = "Free Block"; - } -} - -char *PCSX::SIO::getMcdData(int mcd) { - switch (mcd) { - case 1: - return m_memoryCard[0].getMcdData(); - case 2: - return m_memoryCard[1].getMcdData(); - default: - throw std::runtime_error("Attempt to access invalid memory card"); - return nullptr; - break; - } -} - -// Erase a memory card block by clearing it with 0s -// mcd: The memory card we want to use (1 or 2) -void PCSX::SIO::eraseMcdFile(const McdBlock &block) { - char *data = getMcdData(block.mcd); - - // Set the block data to 0 - const size_t offset = block.number * c_blockSize; - std::memset(data + offset, 0, c_blockSize); - - // Fix up the corresponding directory frame in block 0. - const auto frame = (uint8_t *)data + block.number * c_sectorSize; - frame[0] = 0xa0; // Code for a freshly formatted block - for (auto i = 1; i < 0x7f; i++) { // Zero the rest of the frame - frame[i] = 0; - } - frame[0x7f] = 0xa0; // xor checksum of frame - - if (block.isErased()) { - return; - } - - auto nextBlock = block.nextBlock; - if ((nextBlock >= 1) && (nextBlock <= 15)) { - McdBlock next; - getMcdBlockInfo(block.mcd, nextBlock, next); - eraseMcdFile(next); - } -} - -unsigned PCSX::SIO::getFreeSpace(int mcd) { - unsigned count = 0; - for (int i = 1; i < 16; i++) { - McdBlock block; - getMcdBlockInfo(mcd, i, block); - if (block.isErased()) { - count++; - } - } - - return count; -} - -unsigned PCSX::SIO::getFileBlockCount(McdBlock block) { - if (block.isErased()) { - return 0; - } - - std::bitset<16> walked; - unsigned count = 1; - - while (true) { - if ((block.nextBlock < 1) || (block.nextBlock > 15)) { - return count; - } - if (walked.test(block.nextBlock)) { - return count; - } - walked.set(block.nextBlock); - getMcdBlockInfo(block.mcd, block.nextBlock, block); - count++; - } -} - -int PCSX::SIO::findFirstFree(int mcd) { - McdBlock block; - for (int i = 1; i < 16; i++) { - getMcdBlockInfo(mcd, i, block); - if (block.isErased()) { - return i; - } - } - - return -1; -} - -bool PCSX::SIO::copyMcdFile(McdBlock block) { - auto other = otherMcd(block); - if (getFreeSpace(other) < getFileBlockCount(block)) { - return false; - } - const auto data = getMcdData(block); - const auto otherData = getMcdData(other); - - std::bitset<16> walked; - int prevBlock = -1; - - while (true) { - int dstBlock = findFirstFree(other); - if (dstBlock < 1 || dstBlock > 16) { - throw std::runtime_error("Inconsistent memory card state"); - } - - // copy block data - size_t srcOffset = block.number * c_blockSize; - size_t dstOffset = dstBlock * c_blockSize; - std::memcpy(otherData + dstOffset, data + srcOffset, c_blockSize); - - // copy directory entry - srcOffset = block.number * c_sectorSize; - dstOffset = dstBlock * c_sectorSize; - std::memcpy(otherData + dstOffset, data + srcOffset, c_sectorSize); - - // Fix up the corresponding directory frame in block 0. - if (prevBlock != -1) { - const auto frame = reinterpret_cast(otherData) + prevBlock * c_sectorSize; - uint8_t crcFix = frame[8] ^ (dstBlock - 1); - frame[8] = dstBlock - 1; - frame[0x7f] ^= crcFix; - } - prevBlock = dstBlock; - if (block.nextBlock == -1) { - return true; - } - if ((block.nextBlock < 1) || (block.nextBlock > 15)) { - return false; - } - if (walked.test(block.nextBlock)) { - return false; - } - walked.set(block.nextBlock); - getMcdBlockInfo(block.mcd, block.nextBlock, block); - } -} - -// Back up the entire memory card to a file -// mcd: The memory card to back up (1 or 2) -void PCSX::SIO::saveMcd(int mcd) { - switch (mcd) { - case 1: { - const auto path = g_emulator->settings.get().string(); - m_memoryCard[0].saveMcd(path); - break; - } - case 2: { - const auto path = g_emulator->settings.get().string(); - m_memoryCard[1].saveMcd(path); - break; - } - } -} - -void PCSX::SIO::togglePocketstationMode() { - if (PCSX::g_emulator->settings.get()) { - m_memoryCard[0].enablePocketstation(); - } else { - m_memoryCard[0].disablePocketstation(); - } - - if (PCSX::g_emulator->settings.get()) { - m_memoryCard[1].enablePocketstation(); - } else { - m_memoryCard[1].disablePocketstation(); - } -} - void PCSX::SIO::updateFIFOStatus() { if (m_rxFIFO.size() > 0) { m_regs.status |= StatusFlags::RX_FIFONOTEMPTY; diff --git a/src/core/sio.h b/src/core/sio.h index 8071c4730..3e5dd6da7 100644 --- a/src/core/sio.h +++ b/src/core/sio.h @@ -40,44 +40,9 @@ struct SIORegisters { class SIO { public: - struct McdBlock { - McdBlock() { reset(); } - int mcd; - int number; - std::string titleAscii; - std::string titleSjis; - std::string titleUtf8; - std::string id; - std::string name; - uint32_t fileSize; - uint32_t iconCount; - uint16_t icon[16 * 16 * 3]; - uint32_t allocState; - int16_t nextBlock; - void reset() { - mcd = 0; - number = 0; - titleAscii.clear(); - titleSjis.clear(); - titleUtf8.clear(); - id.clear(); - name.clear(); - fileSize = 0; - iconCount = 0; - memset(icon, 0, sizeof(icon)); - allocState = 0; - nextBlock = -1; - } - bool isErased() const { return (allocState & 0xa0) == 0xa0; } - bool isChained() const { return (allocState & ~1) == 0x52; } - }; - - static constexpr size_t c_sectorSize = 8 * 16; // 80h bytes per sector/frame - static constexpr size_t c_blockSize = c_sectorSize * 64; // 40h sectors per block - static constexpr size_t c_cardSize = c_blockSize * 16; // 16 blocks per frame(directory+15 saves) - static constexpr size_t c_cardCount = 2; SIO() { reset(); } + ~SIO() {} void write8(uint8_t value); void writeStatus16(uint16_t value); @@ -96,36 +61,6 @@ class SIO { void interrupt(); void reset(); - bool copyMcdFile(McdBlock block); - void eraseMcdFile(const McdBlock &block); - void eraseMcdFile(int mcd, int block) { - McdBlock info; - getMcdBlockInfo(mcd, block, info); - eraseMcdFile(info); - } - int findFirstFree(int mcd); - unsigned getFreeSpace(int mcd); - unsigned getFileBlockCount(McdBlock block); - void getMcdBlockInfo(int mcd, int block, McdBlock &info); - char *getMcdData(int mcd); - char *getMcdData(const McdBlock &block) { return getMcdData(block.mcd); } - void loadMcd(const PCSX::u8string &path, int mcd) { - if (mcd > 0 && mcd <= c_cardCount) m_memoryCard[mcd - 1].loadMcd(path); - } - void loadMcds(const PCSX::u8string &mcd1, const PCSX::u8string &mcd2) { - m_memoryCard[0].loadMcd(mcd1); - m_memoryCard[1].loadMcd(mcd2); - } - void saveMcd(int mcd); - static constexpr int otherMcd(int mcd) { - if ((mcd != 1) && (mcd != 2)) throw std::runtime_error("Bad memory card number"); - if (mcd == 1) return 2; - return 1; - } - - void togglePocketstationMode(); - static constexpr int otherMcd(const McdBlock &block) { return otherMcd(block.mcd); } - private: struct StatusFlags { enum : uint16_t { @@ -163,13 +98,6 @@ class SIO { MCDST_CHANGED = 0x08, }; - struct PAD_Commands { - enum : uint8_t { - Read = 0x42, // Read Command - None = 0x00, // No command, idle state - Error = 0xFF // Bad command - }; - }; struct DeviceType { enum : uint8_t { None = 0x00, // No device selected yet @@ -226,7 +154,6 @@ class SIO { std::queue queue_; }; - friend MemoryCard; friend SaveStates::SaveState SaveStates::constructSaveState(); static constexpr size_t c_padBufferSize = 0x1010; @@ -235,15 +162,11 @@ class SIO { bool isTransmitReady(); static inline void scheduleInterrupt(uint64_t eCycle) { g_emulator->m_cpu->scheduleInterrupt(PSXINT_SIO, eCycle); -#if 0 -// Breaks Twisted Metal 2 intro - m_statusReg &= ~RX_FIFONOTEMPTY; - m_statusReg &= ~TX_DATACLEAR; -#endif } void transmitData(); void updateFIFOStatus(); - void writePad(uint8_t value); + uint8_t writeCard(uint8_t value); + uint8_t writePad(uint8_t value); SIORegisters m_regs = { .status = StatusFlags::TX_DATACLEAR | StatusFlags::TX_FINISHED, // Transfer Ready and the Buffer is Empty @@ -251,14 +174,6 @@ class SIO { uint8_t m_currentDevice = DeviceType::None; - // Pads - uint8_t m_buffer[c_padBufferSize]; - uint32_t m_bufferIndex; - uint32_t m_maxBufferIndex; - uint32_t m_padState; - - MemoryCard m_memoryCard[c_cardCount] = {this, this}; - FIFO m_rxFIFO; }; diff --git a/src/core/sstate.cc b/src/core/sstate.cc index 27107c2a8..f05041df4 100644 --- a/src/core/sstate.cc +++ b/src/core/sstate.cc @@ -81,31 +81,27 @@ PCSX::SaveStates::SaveState PCSX::SaveStates::constructSaveState() { GPU {}, SPU {}, SIO { - SIOBuffer { g_emulator->m_sio->m_buffer }, SIOStatusReg { g_emulator->m_sio->m_regs.status }, SIOModeReg { g_emulator->m_sio->m_regs.mode }, SIOCtrlReg { g_emulator->m_sio->m_regs.control }, SIOBaudReg { g_emulator->m_sio->m_regs.baud }, - SIOBufferMaxIndex { g_emulator->m_sio->m_maxBufferIndex }, - SIOBufferIndex { g_emulator->m_sio->m_bufferIndex }, - SIOPadState { g_emulator->m_sio->m_padState }, SIOCurrentDevice { g_emulator->m_sio->m_currentDevice }, - SIOMCD1TempBuffer { g_emulator->m_sio->m_memoryCard[0].m_tempBuffer }, - SIOMCD1DirectoryFlag { g_emulator->m_sio->m_memoryCard[0].m_directoryFlag }, - SIOMCD1ChecksumIn{ g_emulator->m_sio->m_memoryCard[0].m_checksumIn}, - SIOMCD1ChecksumOut{g_emulator->m_sio->m_memoryCard[0].m_checksumOut}, - SIOMCD1CommandTicks{g_emulator->m_sio->m_memoryCard[0].m_commandTicks}, - SIOMCD1CurrentCommand{g_emulator->m_sio->m_memoryCard[0].m_currentCommand}, - SIOMCD1Sector{g_emulator->m_sio->m_memoryCard[0].m_sector}, - SIOMCD1DataOffset{g_emulator->m_sio->m_memoryCard[0].m_dataOffset}, - SIOMCD2TempBuffer{g_emulator->m_sio->m_memoryCard[1].m_tempBuffer}, - SIOMCD2DirectoryFlag{g_emulator->m_sio->m_memoryCard[1].m_directoryFlag}, - SIOMCD2ChecksumIn{g_emulator->m_sio->m_memoryCard[1].m_checksumIn}, - SIOMCD2ChecksumOut{g_emulator->m_sio->m_memoryCard[1].m_checksumOut}, - SIOMCD2CommandTicks{g_emulator->m_sio->m_memoryCard[1].m_commandTicks}, - SIOMCD2CurrentCommand{g_emulator->m_sio->m_memoryCard[1].m_currentCommand}, - SIOMCD2Sector{g_emulator->m_sio->m_memoryCard[1].m_sector}, - SIOMCD2DataOffset{g_emulator->m_sio->m_memoryCard[1].m_dataOffset}, + SIOMCD1TempBuffer { g_emulator->m_memoryCards->m_memoryCard[0].m_tempBuffer }, + SIOMCD1DirectoryFlag { g_emulator->m_memoryCards->m_memoryCard[0].m_directoryFlag }, + SIOMCD1ChecksumIn{ g_emulator->m_memoryCards->m_memoryCard[0].m_checksumIn}, + SIOMCD1ChecksumOut{g_emulator->m_memoryCards->m_memoryCard[0].m_checksumOut}, + SIOMCD1CommandTicks{g_emulator->m_memoryCards->m_memoryCard[0].m_commandTicks}, + SIOMCD1CurrentCommand{g_emulator->m_memoryCards->m_memoryCard[0].m_currentCommand}, + SIOMCD1Sector{g_emulator->m_memoryCards->m_memoryCard[0].m_sector}, + SIOMCD1DataOffset{g_emulator->m_memoryCards->m_memoryCard[0].m_dataOffset}, + SIOMCD2TempBuffer{g_emulator->m_memoryCards->m_memoryCard[1].m_tempBuffer}, + SIOMCD2DirectoryFlag{g_emulator->m_memoryCards->m_memoryCard[1].m_directoryFlag}, + SIOMCD2ChecksumIn{g_emulator->m_memoryCards->m_memoryCard[1].m_checksumIn}, + SIOMCD2ChecksumOut{g_emulator->m_memoryCards->m_memoryCard[1].m_checksumOut}, + SIOMCD2CommandTicks{g_emulator->m_memoryCards->m_memoryCard[1].m_commandTicks}, + SIOMCD2CurrentCommand{g_emulator->m_memoryCards->m_memoryCard[1].m_currentCommand}, + SIOMCD2Sector{g_emulator->m_memoryCards->m_memoryCard[1].m_sector}, + SIOMCD2DataOffset{g_emulator->m_memoryCards->m_memoryCard[1].m_dataOffset}, }, CDRom { CDReg1Mode { g_emulator->m_cdrom->m_reg1Mode }, diff --git a/src/core/sstate.h b/src/core/sstate.h index 317b51685..e2744ea2f 100644 --- a/src/core/sstate.h +++ b/src/core/sstate.h @@ -136,18 +136,18 @@ typedef Protobuf::Message SPUField; -typedef Protobuf::FieldPtr, TYPESTRING("buffer"), 1> SIOBuffer; +// skip id 1 typedef Protobuf::FieldRef SIOStatusReg; typedef Protobuf::FieldRef SIOModeReg; typedef Protobuf::FieldRef SIOCtrlReg; typedef Protobuf::FieldRef SIOBaudReg; -typedef Protobuf::FieldRef SIOBufferMaxIndex; -typedef Protobuf::FieldRef SIOBufferIndex; +// skip id 6 +// skip id 7 // skip id 8 // skip id 9 // skip id 10 // skip id 11 -typedef Protobuf::FieldRef SIOPadState; +// skip id 12 // skip id 13 // skip id 14 typedef Protobuf::FieldRef SIOCurrentDevice; @@ -168,12 +168,11 @@ typedef Protobuf::FieldRef SIOMCD2Sector; typedef Protobuf::FieldRef SIOMCD2DataOffset; -typedef Protobuf::Message +typedef Protobuf::Message SIO; typedef Protobuf::MessageField SIOField; diff --git a/src/gui/widgets/memcard_manager.cc b/src/gui/widgets/memcard_manager.cc index 1cac21a9c..ec880dfe3 100644 --- a/src/gui/widgets/memcard_manager.cc +++ b/src/gui/widgets/memcard_manager.cc @@ -25,6 +25,7 @@ #include "core/system.h" #include "fmt/format.h" #include "gui/gui.h" +#include "magic_enum/include/magic_enum/magic_enum_all.hpp" #include "support/imgui-helpers.h" #include "support/uvfile.h" @@ -63,19 +64,19 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (ImGui::BeginMenu(_("File"))) { if (ImGui::MenuItem(_("Import file into memory card 1"))) { showImportMemoryCardDialog = true; - m_memoryCardImportExportIndex = 1; + m_memoryCardImportExportIndex = MemoryCard::Which::One; } if (ImGui::MenuItem(_("Import file into memory card 2"))) { showImportMemoryCardDialog = true; - m_memoryCardImportExportIndex = 2; + m_memoryCardImportExportIndex = MemoryCard::Which::Two; } if (ImGui::MenuItem(_("Export memory card 1 to file"))) { showExportMemoryCardDialog = true; - m_memoryCardImportExportIndex = 1; + m_memoryCardImportExportIndex = MemoryCard::Which::One; } if (ImGui::MenuItem(_("Export memory card 2 to file"))) { showExportMemoryCardDialog = true; - m_memoryCardImportExportIndex = 2; + m_memoryCardImportExportIndex = MemoryCard::Which::Two; } ImGui::EndMenu(); } @@ -89,8 +90,11 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (m_importMemoryCardDialog.draw()) { std::vector fileToOpen = m_importMemoryCardDialog.selected(); if (!fileToOpen.empty()) { - g_emulator->m_sio->loadMcd(fileToOpen[0], m_memoryCardImportExportIndex); - g_emulator->m_sio->saveMcd(m_memoryCardImportExportIndex); + g_emulator->m_memoryCards->loadMcd( + fileToOpen[0], + g_emulator->m_memoryCards->m_memoryCard[magic_enum::enum_integer(m_memoryCardImportExportIndex)] + .getMcdData()); + g_emulator->m_memoryCards->saveMcd(m_memoryCardImportExportIndex); clearUndoBuffer(); } } @@ -104,7 +108,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (!fileToOpen.empty()) { IO out = new UvFile(fileToOpen[0], FileOps::TRUNCATE); if (!out->failed()) { - const auto dataCard = g_emulator->m_sio->getMcdData(m_memoryCardImportExportIndex); + const auto dataCard = g_emulator->m_memoryCards->getMcdData(m_memoryCardImportExportIndex); Slice slice; slice.copy(dataCard, 128 * 1024); out->writeAt(std::move(slice), 0); @@ -120,23 +124,23 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { const bool wasLatest = isLatest; if (ImGui::SliderInt(_("Undo"), &m_undoIndex, 0, m_undo.size(), "")) { isLatest = m_undo.size() == m_undoIndex; - const auto dataCard1 = g_emulator->m_sio->getMcdData(1); - const auto dataCard2 = g_emulator->m_sio->getMcdData(2); + const auto dataCard1 = g_emulator->m_memoryCards->getMcdData(MemoryCard::Which::One); + const auto dataCard2 = g_emulator->m_memoryCards->getMcdData(MemoryCard::Which::Two); if (isLatest) { - std::memcpy(dataCard1, m_latest.get(), SIO::c_cardSize); - std::memcpy(dataCard2, m_latest.get() + SIO::c_cardSize, SIO::c_cardSize); + std::memcpy(dataCard1, m_latest.get(), MemoryCards::c_cardSize); + std::memcpy(dataCard2, m_latest.get() + MemoryCards::c_cardSize, MemoryCards::c_cardSize); } else { if (wasLatest) { - std::unique_ptr latest = std::make_unique(SIO::c_cardSize * 2); - std::memcpy(latest.get(), dataCard1, SIO::c_cardSize); - std::memcpy(latest.get() + SIO::c_cardSize, dataCard2, SIO::c_cardSize); + std::unique_ptr latest = std::make_unique(MemoryCards::c_cardSize * 2); + std::memcpy(latest.get(), dataCard1, MemoryCards::c_cardSize); + std::memcpy(latest.get() + MemoryCards::c_cardSize, dataCard2, MemoryCards::c_cardSize); m_latest.swap(latest); } - std::memcpy(dataCard1, m_undo[m_undoIndex].second.get(), SIO::c_cardSize); - std::memcpy(dataCard2, m_undo[m_undoIndex].second.get() + SIO::c_cardSize, SIO::c_cardSize); + std::memcpy(dataCard1, m_undo[m_undoIndex].second.get(), MemoryCards::c_cardSize); + std::memcpy(dataCard2, m_undo[m_undoIndex].second.get() + MemoryCards::c_cardSize, MemoryCards::c_cardSize); } - g_emulator->m_sio->saveMcd(1); - g_emulator->m_sio->saveMcd(2); + g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::One); + g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::Two); } ImGui::TextUnformatted(_("Undo version: ")); ImGui::SameLine(); @@ -164,7 +168,8 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (ImGui::Checkbox(_("Card 1 Pocketstation"), &g_emulator->settings.get().value)) { - g_emulator->m_sio->togglePocketstationMode(); + g_emulator->m_memoryCards->setPocketstationEnabled( + MemoryCard::Which::One, g_emulator->settings.get().value); changed = true; } ImGuiHelpers::ShowHelpMarker( @@ -172,7 +177,8 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { "allowing apps to be saved/exported.")); if (ImGui::Checkbox(_("Card 2 Pocketstation"), &g_emulator->settings.get().value)) { - g_emulator->m_sio->togglePocketstationMode(); + g_emulator->m_memoryCards->setPocketstationEnabled( + MemoryCard::Which::Two, g_emulator->settings.get().value); changed = true; } ImGuiHelpers::ShowHelpMarker( @@ -185,13 +191,13 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { initTextures(); } - static const auto draw = [this, gui](int card, int othercard) { + static const auto draw = [this, gui](MemoryCard::Which card, MemoryCard::Which othercard) { static constexpr ImGuiTableFlags flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_SizingStretchProp; - SIO::McdBlock block; // The current memory card block we're looking into + MemoryCards::McdBlock block; // The current memory card block we're looking into - unsigned otherFreeSpace = g_emulator->m_sio->getFreeSpace(othercard); + unsigned otherFreeSpace = g_emulator->m_memoryCards->getFreeSpace(othercard); if (ImGui::BeginTable("Memory card information", 6, flags)) { ImGui::TableSetupColumn(_("Block number")); @@ -203,8 +209,8 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { ImGui::TableHeadersRow(); for (auto i = 1; i < 16; i++) { - g_emulator->m_sio->getMcdBlockInfo(card, i, block); - unsigned size = g_emulator->m_sio->getFileBlockCount(block); + g_emulator->m_memoryCards->getMcdBlockInfo(card, i, block); + unsigned size = g_emulator->m_memoryCards->getFileBlockCount(block); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -243,11 +249,11 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { auto buttonName = fmt::format(f_("Erase##{}"), i); if (ImGui::SmallButton(buttonName.c_str())) { auto latest = getLatest(); - g_emulator->m_sio->eraseMcdFile(block); + g_emulator->m_memoryCards->eraseMcdFile(block); saveUndoBuffer(std::move(latest), fmt::format(f_("Erased file {}({}) off card {}"), block.number, gui->hasJapanese() ? block.titleUtf8 : block.titleAscii, block.mcd)); - g_emulator->m_sio->saveMcd(card); + g_emulator->m_memoryCards->saveMcd(card); } ImGui::SameLine(); @@ -255,7 +261,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (otherFreeSpace >= size) { if (ImGui::SmallButton(buttonName.c_str())) { auto latest = getLatest(); - bool success = g_emulator->m_sio->copyMcdFile(block); + bool success = g_emulator->m_memoryCards->copyMcdFile(block); if (!success) { gui->addNotification("Error while copying save file"); } else { @@ -263,7 +269,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { std::move(latest), fmt::format(f_("Copied file {}({}) from card {} to card {}"), block.number, gui->hasJapanese() ? block.titleUtf8 : block.titleAscii, card, othercard)); - g_emulator->m_sio->saveMcd(othercard); + g_emulator->m_memoryCards->saveMcd(othercard); } } } else { @@ -277,17 +283,17 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (otherFreeSpace >= size) { if (ImGui::SmallButton(buttonName.c_str())) { auto latest = getLatest(); - bool success = g_emulator->m_sio->copyMcdFile(block); + bool success = g_emulator->m_memoryCards->copyMcdFile(block); if (!success) { gui->addNotification("Error while copying save file"); } else { - g_emulator->m_sio->eraseMcdFile(block); + g_emulator->m_memoryCards->eraseMcdFile(block); saveUndoBuffer( std::move(latest), fmt::format(f_("Moved file {}({}) from card {} to card {}"), block.number, gui->hasJapanese() ? block.titleUtf8 : block.titleAscii, card, othercard)); - g_emulator->m_sio->saveMcd(1); - g_emulator->m_sio->saveMcd(2); + g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::One); + g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::Two); } } } else { @@ -314,11 +320,11 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { if (ImGui::BeginTabBar("Cards")) { if (ImGui::BeginTabItem(_("Memory Card 1"))) { - draw(1, 2); + draw(MemoryCard::Which::One, MemoryCard::Which::Two); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(_("Memory Card 2"))) { - draw(2, 1); + draw(MemoryCard::Which::Two, MemoryCard::Which::One); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -329,7 +335,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) { return changed; } -void PCSX::Widgets::MemcardManager::drawIcon(const PCSX::SIO::McdBlock& block) { +void PCSX::Widgets::MemcardManager::drawIcon(const PCSX::MemoryCards::McdBlock& block) { int currentFrame = 0; // 1st frame = 0, 2nd frame = 1, 3rd frame = 2 and so on const auto texture = m_iconTextures[block.number - 1]; glBindTexture(GL_TEXTURE_2D, texture); @@ -356,9 +362,9 @@ void PCSX::Widgets::MemcardManager::drawIcon(const PCSX::SIO::McdBlock& block) { } // Extract the pocketstation icon from the block indicated by blockNumber into the pixels array (In RGBA8888) -void PCSX::Widgets::MemcardManager::getPocketstationIcon(uint32_t* pixels, const SIO::McdBlock& block) { - const auto data = g_emulator->m_sio->getMcdData(block.mcd); - const auto titleFrame = data + block.number * PCSX::SIO::c_blockSize; +void PCSX::Widgets::MemcardManager::getPocketstationIcon(uint32_t* pixels, const MemoryCards::McdBlock& block) { + const auto data = g_emulator->m_memoryCards->getMcdData(block.mcd); + const auto titleFrame = data + block.number * PCSX::MemoryCards::c_blockSize; // Calculate icon offset using the header info documented here // https://psx-spx.consoledev.net/pocketstation/#pocketstation-file-headericons @@ -382,7 +388,7 @@ void PCSX::Widgets::MemcardManager::getPocketstationIcon(uint32_t* pixels, const } } -clip::image PCSX::Widgets::MemcardManager::getIconRGBA8888(const SIO::McdBlock& block) { +clip::image PCSX::Widgets::MemcardManager::getIconRGBA8888(const MemoryCards::McdBlock& block) { clip::image_spec spec; spec.bits_per_pixel = 32; spec.red_mask = 0xff; @@ -423,13 +429,13 @@ clip::image PCSX::Widgets::MemcardManager::getIconRGBA8888(const SIO::McdBlock& } } -void PCSX::Widgets::MemcardManager::exportPNG(const SIO::McdBlock& block) { +void PCSX::Widgets::MemcardManager::exportPNG(const MemoryCards::McdBlock& block) { const auto filename = fmt::format("icon{}.png", block.number); const auto pixels = getIconRGBA8888(block); pixels.export_to_png(filename); } -void PCSX::Widgets::MemcardManager::copyToClipboard(const SIO::McdBlock& block) { +void PCSX::Widgets::MemcardManager::copyToClipboard(const MemoryCards::McdBlock& block) { const auto pixels = getIconRGBA8888(block); clip::set_image(pixels); } diff --git a/src/gui/widgets/memcard_manager.h b/src/gui/widgets/memcard_manager.h index db74f0740..248ab8772 100644 --- a/src/gui/widgets/memcard_manager.h +++ b/src/gui/widgets/memcard_manager.h @@ -27,7 +27,7 @@ #include "GL/gl3w.h" #include "clip/clip.h" -#include "core/sio.h" +#include "core/memorycard.h" #include "gui/widgets/filedialog.h" #include "imgui.h" @@ -57,19 +57,20 @@ class MemcardManager { GLuint m_iconTextures[15] = {0}; - clip::image getIconRGBA8888(const SIO::McdBlock& block); + clip::image getIconRGBA8888(const MemoryCards::McdBlock& block); - void drawIcon(const SIO::McdBlock& block); - void exportPNG(const SIO::McdBlock& block); - void copyToClipboard(const SIO::McdBlock& block); - void getPocketstationIcon(uint32_t* pixels, const SIO::McdBlock& block); + void drawIcon(const MemoryCards::McdBlock& block); + void exportPNG(const MemoryCards::McdBlock& block); + void copyToClipboard(const MemoryCards::McdBlock& block); + void getPocketstationIcon(uint32_t* pixels, const MemoryCards::McdBlock& block); void saveUndoBuffer(std::unique_ptr&& tosave, const std::string& action); std::unique_ptr getLatest() { - std::unique_ptr data = std::make_unique(SIO::c_cardSize * 2); - std::memcpy(data.get(), g_emulator->m_sio->getMcdData(1), SIO::c_cardSize); - std::memcpy(data.get() + SIO::c_cardSize, g_emulator->m_sio->getMcdData(2), SIO::c_cardSize); + std::unique_ptr data = std::make_unique(MemoryCards::c_cardSize * 2); + std::memcpy(data.get(), g_emulator->m_memoryCards->getMcdData(MemoryCard::Which::One), MemoryCards::c_cardSize); + std::memcpy(data.get() + MemoryCards::c_cardSize, g_emulator->m_memoryCards->getMcdData(MemoryCard::Which::Two), + MemoryCards::c_cardSize); return data; } @@ -78,7 +79,7 @@ class MemcardManager { std::unique_ptr m_latest; Widgets::FileDialog<> m_importMemoryCardDialog; Widgets::FileDialog m_exportMemoryCardDialog; - unsigned m_memoryCardImportExportIndex = 0; + MemoryCard::Which m_memoryCardImportExportIndex = MemoryCard::Which::One; void clearUndoBuffer() { m_undo.clear(); diff --git a/src/main/main.cc b/src/main/main.cc index 36591940d..b96769f99 100644 --- a/src/main/main.cc +++ b/src/main/main.cc @@ -230,22 +230,9 @@ int pcsxMain(int argc, char **argv) { // to handle overrides properly. auto &emuSettings = emulator->settings; auto &debugSettings = emuSettings.get(); - if (emuSettings.get().empty()) { - emuSettings.get() = MAKEU8(u8"memcard1.mcd"); - } - - if (emuSettings.get().empty()) { - emuSettings.get() = MAKEU8(u8"memcard2.mcd"); - } - - auto argPath1 = args.get("memcard1"); - auto argPath2 = args.get("memcard2"); - if (argPath1.has_value()) emuSettings.get() = argPath1.value(); - if (argPath2.has_value()) emuSettings.get() = argPath1.value(); - PCSX::u8string path1 = emuSettings.get().string(); - PCSX::u8string path2 = emuSettings.get().string(); - emulator->m_sio->loadMcds(path1, path2); + PCSX::g_emulator->m_memoryCards->loadMcds(args); + auto biosCfg = args.get("bios"); if (biosCfg.has_value()) emuSettings.get() = biosCfg.value();