From 7b30e76f1799f86c40fcd58efdc8cd716fb87d20 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Sun, 23 Jun 2024 11:50:10 -0500
Subject: [PATCH 1/8] Initial commit of sio overhaul changes

---
 src/core/memorycard.cc             | 379 ++++++++++++++++++++--
 src/core/memorycard.h              | 194 +++++++++--
 src/core/pad.cc                    |  94 +++++-
 src/core/pad.h                     |  12 +-
 src/core/psxemulator.cc            |   1 +
 src/core/psxemulator.h             |  28 +-
 src/core/sio.cc                    | 502 ++++-------------------------
 src/core/sio.h                     |  91 +-----
 src/core/sstate.cc                 |  40 +--
 src/core/sstate.h                  |  19 +-
 src/gui/widgets/memcard_manager.cc |  83 ++---
 src/gui/widgets/memcard_manager.h  |  19 +-
 src/main/main.cc                   |  17 +-
 13 files changed, 783 insertions(+), 696 deletions(-)

diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc
index 9c66c9a75..b9eb2262e 100644
--- a/src/core/memorycard.cc
+++ b/src/core/memorycard.cc
@@ -19,12 +19,330 @@
 
 #include "core/memorycard.h"
 
+#include <bitset>
+
 #include "core/sio.h"
 #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;
+    const char *card_ids[] = {"1", "2", "1b", "1c", "1d", "2b", "2c", "2d"};
+
+    std::filesystem::path *card_paths[] = {
+        &settings.get<PCSX::Emulator::SettingMcd1>().value,  &settings.get<PCSX::Emulator::SettingMcd2>().value,
+        &settings.get<PCSX::Emulator::SettingMcd1B>().value, &settings.get<PCSX::Emulator::SettingMcd1C>().value,
+        &settings.get<PCSX::Emulator::SettingMcd1D>().value, &settings.get<PCSX::Emulator::SettingMcd2B>().value,
+        &settings.get<PCSX::Emulator::SettingMcd2C>().value, &settings.get<PCSX::Emulator::SettingMcd2D>().value,
+    };
+
+    for (int i = 0; i < 8; i++) {
+        auto argPath = args.get<std::string>(fmt::format("memcard{}", card_ids[i]));
+        if (argPath.has_value()) {
+            *card_paths[i] = argPath.value();
+        }
+
+        if (card_paths[i]->u8string().empty()) {
+            std::string path = std::format("memcard{}.mcd", card_ids[i]);
+            *card_paths[i] = path;
+        }
+
+        if (i >= m_memoryCard.size()) {
+            continue;
+        }
+
+        loadMcd(card_paths[i]->u8string(), m_memoryCard[i].getMcdData());
+    }
+}
+
+void PCSX::MemoryCards::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<uint8_t *>(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;
+    }
 
-uint8_t PCSX::MemoryCard::transceive(uint8_t value) {
+    info.titleUtf8 = Sjis::toUtf8(ts);
+
+    // Read CLUT
+    ptr = reinterpret_cast<uint8_t *>(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<uint8_t *>(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(int mcd) {
+    const int index = mcd - 1;
+    if (index < 0 || index >= m_memoryCard.size()) {
+        throw std::runtime_error("Attempt to access invalid memory card");
+        return nullptr;
+    } else {
+        return m_memoryCard[index].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(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::MemoryCards::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::MemoryCards::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::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<uint8_t *>(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(int index) {
+    return saveMcd(getMcdPath(index), m_memoryCard[index].getMcdData(), 0, c_cardSize);
+}
+
+void PCSX::MemoryCards::resetCard(int index) {
+        m_memoryCard[index].reset();
+}
+
+void PCSX::MemoryCards::setPocketstationEnabled(int index, bool enabled) {
+    m_memoryCard[index].setPocketstationEnabled(enabled);
+}
+
+void PCSX::MemoryCard::commit() {
+    for (int retry_count = 0; retry_count < 3; retry_count++) {
+        if (g_emulator->m_memoryCards->saveMcd(m_deviceIndex)) {
+            m_savedToDisk = true;
+            break;
+        } else {
+            PCSX::g_system->printf(_("Failed to save card %d, attempt %d/3"), m_deviceIndex + 1, retry_count + 1);
+        }
+    }
+}
+
+uint8_t PCSX::MemoryCard::transceive(uint8_t value, bool *ack) {
     uint8_t data_out = m_spdr;
 
     if (m_currentCommand == Commands::None || m_currentCommand == Commands::Access) {
@@ -36,44 +354,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;
 
@@ -87,7 +405,7 @@ uint8_t PCSX::MemoryCard::transceive(uint8_t value) {
     return data_out;
 }
 
-uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value) {
+inline uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value, bool *ack) {
     uint8_t data_out = 0xFF;
 
     switch (m_commandTicks) {
@@ -156,12 +474,12 @@ uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value) {
     }
 
     m_commandTicks++;
-    acknowledge();
+    *ack = true;
 
     return data_out;
 }
 
-uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) {
+inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
     uint8_t data_out = 0xFF;
 
     switch (m_commandTicks) {
@@ -246,16 +564,17 @@ uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value) {
             data_out = 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;
 }
 
-uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value) {
+inline uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value, bool *ack) {
     uint8_t data_out = 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,
@@ -266,7 +585,7 @@ uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value) {
 
         // Don't ack the last byte
         if (m_commandTicks <= (response_count - 1)) {
-            acknowledge();
+            *ack = true;
         }
     }
 
@@ -275,7 +594,7 @@ uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value) {
     return data_out;
 }
 
-uint8_t PCSX::MemoryCard::tickPS_ExecCustom(uint8_t value) {
+inline uint8_t PCSX::MemoryCard::tickPS_ExecCustom(uint8_t value, bool *ack) {
     uint8_t data_out = Responses::IdleHighZ;
 
     switch (m_commandTicks) {
@@ -289,12 +608,12 @@ uint8_t PCSX::MemoryCard::tickPS_ExecCustom(uint8_t value) {
     }
 
     m_commandTicks++;
-    acknowledge();
+    *ack = true;
 
     return data_out;
 }
 
-uint8_t PCSX::MemoryCard::tickPS_PrepFileExec(uint8_t value) {
+inline uint8_t PCSX::MemoryCard::tickPS_PrepFileExec(uint8_t value, bool *ack) {
     uint8_t data_out = Responses::IdleHighZ;
 
     switch (m_commandTicks) {
@@ -308,12 +627,12 @@ uint8_t PCSX::MemoryCard::tickPS_PrepFileExec(uint8_t value) {
     }
 
     m_commandTicks++;
-    acknowledge();
+    *ack = true;
 
     return data_out;
 }
 
-uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value) {
+inline uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value, bool *ack) {
     uint8_t data_out = Responses::IdleHighZ;
     static constexpr uint8_t response_count = 3;
     static constexpr uint8_t responses[response_count] = {0x02, 0x01, 0x01};
@@ -323,7 +642,7 @@ uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value) {
 
         // Don't ack the last byte
         if (m_commandTicks <= (response_count - 1)) {
-            acknowledge();
+            *ack = true;
         }
     }
 
@@ -333,15 +652,14 @@ uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value) {
 }
 
 // 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<const char *>(mcd.c_str());
     size_t bytesRead;
 
-    m_directoryFlag = Flags::DirectoryUnread;
+    bool result = false;
 
     FILE *f = fopen(fname, "rb");
     if (f == nullptr) {
@@ -384,17 +702,20 @@ void PCSX::MemoryCard::loadMcd(PCSX::u8string mcd) {
         if (bytesRead != c_cardSize) {
             throw std::runtime_error(_("Error reading memory card."));
         } else {
-            m_savedToDisk = true;
+            result = true;
         }
     }
+
+    return result;
 }
 
-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();
     }
     const char *fname = reinterpret_cast<const char *>(mcd.c_str());
     FILE *f = fopen(fname, "r+b");
+    bool result = false;
 
     if (f != nullptr) {
         struct stat buf;
@@ -413,7 +734,7 @@ void PCSX::MemoryCard::saveMcd(PCSX::u8string mcd, const char *data, uint32_t ad
 
         fwrite(data + adr, 1, size, f);
         fclose(f);
-        m_savedToDisk = true;
+        result = true;
         PCSX::g_system->printf(_("Saving memory card %s\n"), fname);
     } else {
         // try to create it again if we can't open it
@@ -423,9 +744,11 @@ void PCSX::MemoryCard::saveMcd(PCSX::u8string mcd, const char *data, uint32_t ad
             fclose(f);
         }
     }
+
+    return result;
 }
 
-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..7bb9f887e 100644
--- a/src/core/memorycard.h
+++ b/src/core/memorycard.h
@@ -21,18 +21,33 @@
 
 #include <stdint.h>
 
+#include "core/psxemulator.h"
 #include "core/sstate.h"
 
 namespace PCSX {
 class SIO;
+class Memorycards;
 
+/// <summary>
+/// Implements a memory card for SIO
+/// </summary>
 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); }
+    // MemoryCard() { init(); }
+    MemoryCard(uint8_t device_index) : m_deviceIndex(device_index) { init(); }
+
+    ~MemoryCard(){};
 
     // Hardware events
-    void acknowledge();
+    void init() {
+        if (m_mcdData) {
+            memset(m_mcdData, 0, c_cardSize);
+        }
+
+        if (m_tempBuffer) {
+            memset(m_tempBuffer, 0, c_blockSize);
+        }
+    }
     void deselect() {
         memset(&m_tempBuffer, 0, c_sectorSize);
         m_currentCommand = Commands::None;
@@ -41,21 +56,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 {
@@ -102,16 +112,16 @@ class MemoryCard {
     static constexpr size_t c_cardSize = 1024 * c_sectorSize;
 
     // 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 = new char[c_cardSize];
+    uint8_t m_tempBuffer[c_blockSize];
     bool m_savedToDisk = false;
 
     uint8_t m_checksumIn = 0, m_checksumOut = 0;
@@ -128,7 +138,139 @@ class MemoryCard {
     bool m_pocketstationEnabled = false;
     uint16_t m_directoryIndex = 0;
 
-    SIO *m_sio;
+    SIO *m_sio = nullptr;
+    uint8_t m_deviceIndex = 0;
+};
+
+/// <summary>
+/// Helper functions for MemoryCard class, gui, and filesystem
+/// </summary>
+class MemoryCards {
+  public:
+    MemoryCards() {
+        for (int i = 0; i < c_cardCount; i++) {
+            MemoryCard card = MemoryCard(i);
+            m_memoryCard.push_back(card);
+        }
+    }
+    ~MemoryCards() {
+        if (m_memoryCard.size() > 0) {
+            m_memoryCard.clear();
+        }
+    }
+
+    void deselect() {
+        for (int i = 0; i < m_memoryCard.size(); i++) {
+            m_memoryCard[i].deselect();
+        }
+    }
+
+    void init() {
+        // setPocketstationEnabled();
+    }
+
+    void reset() {
+        for (int i = 0; i < m_memoryCard.size(); i++) {
+            m_memoryCard[i].reset();
+        }
+    }
+
+    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)
+
+    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); }
+
+    // File operations
+    void createMcd(PCSX::u8string mcd);
+    void loadMcds(const CommandLine::args &args);
+    bool saveMcd(int card_index);
+
+    bool loadMcd(PCSX::u8string mcd, char *data);
+    bool saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size);
+    // void saveMcd(const PCSX::u8string path) { saveMcd(path, m_mcdData, 0, c_cardSize); }
+    static constexpr int otherMcd(int mcd) {
+        if ((mcd != 0) && (mcd != 1)) throw std::runtime_error("Bad memory card number");
+        if (mcd == 0) return 1;
+        return 0;
+    }
+
+    PCSX::u8string getMcdPath(int index) {
+        std::filesystem::path *paths[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1B>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1C>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1D>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2B>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2C>().value,
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2D>().value};
+
+        PCSX::u8string thepath = paths[index]->u8string();
+        return thepath;
+    }
+    bool isCardInserted(int index) {
+        bool *const inserted_lut[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1Inserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2Inserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1BInserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1CInserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1DInserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2BInserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2CInserted>().value,
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2DInserted>().value};
+
+        return *inserted_lut[index];
+    }
+
+    static constexpr int otherMcd(const McdBlock &block) { return otherMcd(block.mcd); }
+    void resetCard(int index);
+    void setPocketstationEnabled(int index, bool enabled);
+
+    static constexpr size_t c_cardCount = 8;
+    std::vector<MemoryCard> m_memoryCard;
 };
 
 }  // namespace PCSX
diff --git a/src/core/pad.cc b/src/core/pad.cc
index 1d8d2ffe7..a8b61368c 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.
@@ -160,6 +166,7 @@ class PadsImpl : public PCSX::Pads {
     };
 
     struct Pad {
+        Pad(uint8_t device_index) : m_deviceIndex(device_index) {}
         uint8_t startPoll();
         uint8_t read();
         uint8_t poll(uint8_t value, uint32_t& padState);
@@ -168,6 +175,12 @@ class PadsImpl : public PCSX::Pads {
         bool isControllerButtonPressed(int button, GLFWgamepadstate* state);
         bool isControllerConnected() { return m_settings.get<SettingConnected>(); }
 
+        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,9 +212,18 @@ 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;
+
+        uint8_t m_deviceIndex = 0;
     };
 
-    std::array<Pad, 2> m_pads;
+    std::array<Pad, 2> m_pads = {Pad(0), Pad(1)};
     unsigned m_selectedPadForConfig = 0;
 };
 
@@ -596,12 +618,12 @@ void PadsImpl::Pad::getButtons() {
 
     if ((m_padID >= GLFW_JOYSTICK_1) && (m_padID <= GLFW_JOYSTICK_LAST)) {
         hasPad = glfwGetGamepadState(m_padID, &state);
-        if (!hasPad) {
+            if (!hasPad) {
             const char* guid = glfwGetJoystickGUID(m_padID);
-            PCSX::g_system->printf("Gamepad error: GUID %s likely has no database mapping, disabling pad\n", guid);
-            m_padID = -1;
+                PCSX::g_system->printf("Gamepad error: GUID %s likely has no database mapping, disabling pad\n", guid);
+                m_padID = -1;
+            }
         }
-    }
 
     if (!hasPad) {
         if (inputType == InputType::Auto) {
@@ -678,15 +700,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 data_out = 0xff;
 
-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);
+    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;
+    }
+
+    data_out = m_buffer[m_bufferIndex];
+
+    if (m_padState == Pads::PAD_STATE_BAD_COMMAND) {
+    } else {
+        *ack = true;
+    }
+
+    return data_out;
 }
 
 uint8_t PadsImpl::Pad::poll(uint8_t value, uint32_t& padState) {
@@ -918,6 +974,12 @@ bool PadsImpl::configure(PCSX::GUI* gui) {
     PCSX::ImGuiHelpers::ShowHelpMarker(
         _("When enabled, pressing CTRL and ALT will toggle the setting above, raw input"));
 
+    changed |= ImGui::Checkbox(_("Port 1 multi-tap connected"),
+                               &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingPort1Multitap>().value);
+
+    changed |= ImGui::Checkbox(_("Port 2 multi-tap connected"),
+                               &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingPort2Multitap>().value);
+
     if (ImGui::BeginCombo(_("Pad"), c_padNames[m_selectedPadForConfig]())) {
         for (unsigned i = 0; i < 2; i++) {
             if (ImGui::Selectable(c_padNames[i](), m_selectedPadForConfig == i)) {
diff --git a/src/core/pad.h b/src/core/pad.h
index ba0801a7a..0fa4188b0 100644
--- a/src/core/pad.h
+++ b/src/core/pad.h
@@ -29,17 +29,23 @@ using json = nlohmann::json;
 namespace PCSX {
 
 class GUI;
+class SIO;
 
 class Pads {
   public:
     enum class Port { Port1 = 0, Port2 };
 
-    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 7092665fe..3e107fbcf 100644
--- a/src/core/psxemulator.cc
+++ b/src/core/psxemulator.cc
@@ -65,6 +65,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_pioCart(new PCSX::PIOCart),
       m_sio(new PCSX::SIO()),
diff --git a/src/core/psxemulator.h b/src/core/psxemulator.h
index 381ecd2a7..9a4cbda8f 100644
--- a/src/core/psxemulator.h
+++ b/src/core/psxemulator.h
@@ -75,6 +75,7 @@ class HW;
 class Lua;
 class MDEC;
 class Memory;
+class MemoryCards;
 class Pads;
 class R3000Acpu;
 class SIO;
@@ -185,6 +186,26 @@ class Emulator {
     typedef SettingPath<TYPESTRING("EXP1Filepath")> SettingEXP1Filepath;
     typedef SettingPath<TYPESTRING("EXP1BrowsePath")> SettingEXP1BrowsePath;
     typedef Setting<bool, TYPESTRING("PIOConnected")> SettingPIOConnected;
+    typedef Setting<bool, TYPESTRING("Mcd1BInserted"), false> SettingMcd1BInserted;
+    typedef Setting<bool, TYPESTRING("Mcd1CInserted"), false> SettingMcd1CInserted;
+    typedef Setting<bool, TYPESTRING("Mcd1DInserted"), false> SettingMcd1DInserted;
+    typedef Setting<bool, TYPESTRING("Mcd2BInserted"), false> SettingMcd2BInserted;
+    typedef Setting<bool, TYPESTRING("Mcd2CInserted"), false> SettingMcd2CInserted;
+    typedef Setting<bool, TYPESTRING("Mcd8Inserted"), false> SettingMcd2DInserted;
+    typedef Setting<bool, TYPESTRING("Mcd1BPocketstation"), false> SettingMcd1BPocketstation;
+    typedef Setting<bool, TYPESTRING("Mcd1CPocketstation"), false> SettingMcd1CPocketstation;
+    typedef Setting<bool, TYPESTRING("Mcd1DPocketstation"), false> SettingMcd1DPocketstation;
+    typedef Setting<bool, TYPESTRING("Mcd2BPocketstation"), false> SettingMcd2BPocketstation;
+    typedef Setting<bool, TYPESTRING("Mcd2CPocketstation"), false> SettingMcd2CPocketstation;
+    typedef Setting<bool, TYPESTRING("Mcd2DPocketstation"), false> SettingMcd2DPocketstation;
+    typedef Setting<bool, TYPESTRING("Port1Multitap"), false> SettingPort1Multitap;
+    typedef Setting<bool, TYPESTRING("Port2Multitap"), false> SettingPort2Multitap;
+    typedef SettingPath<TYPESTRING("Mcd1B")> SettingMcd1B;
+    typedef SettingPath<TYPESTRING("Mcd1C")> SettingMcd1C;
+    typedef SettingPath<TYPESTRING("Mcd1D")> SettingMcd1D;
+    typedef SettingPath<TYPESTRING("Mcd2B")> SettingMcd2B;
+    typedef SettingPath<TYPESTRING("Mcd2C")> SettingMcd2C;
+    typedef SettingPath<TYPESTRING("Mcd2D")> SettingMcd2D;
 
     Settings<SettingMcd1, SettingMcd2, SettingBios, SettingPpfDir, SettingPsxExe, SettingXa, SettingSpuIrq,
              SettingBnWMdec, SettingScaler, SettingAutoVideo, SettingVideo, SettingFastBoot, SettingDebugSettings,
@@ -193,7 +214,11 @@ class Emulator {
              SettingGLErrorReportingSeverity, SettingFullCaching, SettingHardwareRenderer, SettingShownAutoUpdateConfig,
              SettingAutoUpdate, SettingMSAA, SettingLinearFiltering, SettingKioskMode, SettingMcd1Pocketstation,
              SettingMcd2Pocketstation, SettingBiosBrowsePath, SettingEXP1Filepath, SettingEXP1BrowsePath,
-             SettingPIOConnected>
+             SettingPIOConnected, SettingMcd1BInserted, SettingMcd1CInserted, SettingMcd1DInserted,
+             SettingMcd2BInserted, SettingMcd2CInserted, SettingMcd2DInserted, SettingMcd1BPocketstation,
+             SettingMcd1CPocketstation, SettingMcd1DPocketstation, SettingMcd2BPocketstation, SettingMcd2CPocketstation,
+             SettingMcd2DPocketstation, SettingPort1Multitap, SettingPort2Multitap, SettingMcd1B, SettingMcd1C,
+             SettingMcd1D, SettingMcd2B, SettingMcd2C, SettingMcd2D>
         settings;
     class PcsxConfig {
       public:
@@ -248,6 +273,7 @@ class Emulator {
     std::unique_ptr<Lua> m_lua;
     std::unique_ptr<MDEC> m_mdec;
     std::unique_ptr<Memory> m_mem;
+    std::unique_ptr<MemoryCards> m_memoryCards;
     std::unique_ptr<Pads> m_pads;
     std::unique_ptr<PIOCart> m_pioCart;
     std::unique_ptr<R3000Acpu> m_cpu;
diff --git a/src/core/sio.cc b/src/core/sio.cc
index d899dc6f8..68a86a75b 100644
--- a/src/core/sio.cc
+++ b/src/core/sio.cc
@@ -22,7 +22,6 @@
 #include <sys/stat.h>
 
 #include <algorithm>
-#include <bitset>
 #include <stdexcept>
 
 #include "core/memorycard.h"
@@ -44,9 +43,9 @@ void PCSX::SIO::acknowledge() {
 }
 
 void PCSX::SIO::init() {
-    reset();
-    togglePocketstationMode();
+    g_emulator->m_memoryCards->init();
     g_emulator->m_pads->init();
+    reset();
     g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status);
 }
 
@@ -84,105 +83,74 @@ 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 tx_enabled = m_regs.control & ControlFlags::TX_ENABLE;
+    const bool tx_finished = m_regs.status & StatusFlags::TX_FINISHED;
+    const bool tx_data_empty = m_regs.status & StatusFlags::TX_DATACLEAR;
 
-    return (txEnabled && txFinished && txDataNotEmpty);
+    return (tx_enabled && tx_finished && !tx_data_empty);
 }
 
 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 port_index = ((m_regs.control & ControlFlags::WHICH_PORT) == SelectedPort::Port1) ? 0 : 1;
+    const bool is_connected = g_emulator->m_memoryCards->isCardInserted(port_index);
 
-            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 rx_buffer = 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 (is_connected) {
+        rx_buffer = g_emulator->m_memoryCards->m_memoryCard[port_index].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 rx_buffer;
+}
+
+uint8_t PCSX::SIO::writePad(uint8_t value) {
+    const int port_index = ((m_regs.control & ControlFlags::WHICH_PORT) == SelectedPort::Port1) ? 0 : 1;
+    const bool is_connected = g_emulator->m_pads->isPadConnected(port_index);
+
+    bool ack = false;
+    uint8_t rx_buffer = 0xff;
+
+    if (is_connected) {
+        rx_buffer = g_emulator->m_pads->transceive(port_index, m_regs.data, &ack);
+    } else {
+        m_currentDevice = DeviceType::Ignore;
     }
 
-    if (m_padState == Pads::PAD_STATE_BAD_COMMAND) {
-        return;
+    if (ack) {
+        acknowledge();
     }
 
-    acknowledge();
+    return rx_buffer;
 }
 
 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 rx_buffer = 0xff;
 
     if (m_currentDevice == DeviceType::None) {
         m_currentDevice = m_regs.data;
@@ -190,58 +158,24 @@ void PCSX::SIO::transmitData() {
 
     switch (m_currentDevice) {
         case DeviceType::PAD:
-            // Pad Process events
-            writePad(m_regs.data);
-            m_rxBuffer = m_buffer[m_bufferIndex];
+            rx_buffer = writePad(m_regs.data);
             break;
 
         case DeviceType::MemoryCard:
-            switch (m_regs.control & ControlFlags::WHICH_PORT) {
-                case SelectedPort::Port1:
-                    if (PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1Inserted>()) {
-                        m_rxBuffer = m_memoryCard[0].transceive(m_regs.data);
-                        if (m_memoryCard[0].dataChanged()) {
-                            m_memoryCard[0].commit(
-                                PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1>().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<PCSX::Emulator::SettingMcd2Inserted>()) {
-                        m_rxBuffer = m_memoryCard[1].transceive(m_regs.data);
-                        if (m_memoryCard[1].dataChanged()) {
-                            m_memoryCard[1].commit(
-                                PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2>().string().c_str());
-                        }
-                    } else {
-                        m_memoryCard[1].m_directoryFlag = MemoryCard::Flags::DirectoryUnread;
-                        m_currentDevice = DeviceType::Ignore;
-                        m_memoryCard[1].deselect();
-                    }
-                    break;
-            }
+            rx_buffer = 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(rx_buffer);
     updateFIFOStatus();
-    m_regs.data = m_rxBuffer;
-    g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(m_rxBuffer);
+    m_regs.data = rx_buffer;
+    g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(rx_buffer);
 
     if (isReceiveIRQReady() && !(m_regs.status & StatusFlags::IRQ)) {
         scheduleInterrupt(SIO_CYCLES);
@@ -251,7 +185,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 +203,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 +232,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 +242,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 +257,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 +284,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<uint8_t *>(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<uint8_t *>(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<uint8_t *>(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<uint8_t *>(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<Emulator::SettingMcd1>().string();
-            m_memoryCard[0].saveMcd(path);
-            break;
-        }
-        case 2: {
-            const auto path = g_emulator->settings.get<Emulator::SettingMcd2>().string();
-            m_memoryCard[1].saveMcd(path);
-            break;
-        }
-    }
-}
-
-void PCSX::SIO::togglePocketstationMode() {
-    if (PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1Pocketstation>()) {
-        m_memoryCard[0].enablePocketstation();
-    } else {
-        m_memoryCard[0].disablePocketstation();
-    }
-
-    if (PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2Pocketstation>()) {
-        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 586e2b43b..2ef8f747b 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<T> 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(uint32_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<uint8_t, 8> m_rxFIFO;
 };
 
diff --git a/src/core/sstate.cc b/src/core/sstate.cc
index 6aef5dfb1..a99f5875c 100644
--- a/src/core/sstate.cc
+++ b/src/core/sstate.cc
@@ -81,31 +81,31 @@ PCSX::SaveStates::SaveState PCSX::SaveStates::constructSaveState() {
         GPU {},
         SPU {},
         SIO {
-            SIOBuffer { g_emulator->m_sio->m_buffer },
+            //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 },
+            //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 910f2e1b3..8eca6327d 100644
--- a/src/core/sstate.h
+++ b/src/core/sstate.h
@@ -136,18 +136,18 @@ typedef Protobuf::Message<TYPESTRING("SPU"), SPURam, SPUPorts, XAField, SPUIrq,
     SPU;
 typedef Protobuf::MessageField<SPU, TYPESTRING("spu"), 6> SPUField;
 
-typedef Protobuf::FieldPtr<Protobuf::FixedBytes<0x1010>, TYPESTRING("buffer"), 1> SIOBuffer;
+// skip id 1
 typedef Protobuf::FieldRef<Protobuf::UInt32, TYPESTRING("status_reg"), 2> SIOStatusReg;
 typedef Protobuf::FieldRef<Protobuf::UInt16, TYPESTRING("mode_reg"), 3> SIOModeReg;
 typedef Protobuf::FieldRef<Protobuf::UInt16, TYPESTRING("ctrl_reg"), 4> SIOCtrlReg;
 typedef Protobuf::FieldRef<Protobuf::UInt16, TYPESTRING("baud_reg"), 5> SIOBaudReg;
-typedef Protobuf::FieldRef<Protobuf::UInt32, TYPESTRING("buffer_max_index"), 6> SIOBufferMaxIndex;
-typedef Protobuf::FieldRef<Protobuf::UInt32, TYPESTRING("buffer_index"), 7> SIOBufferIndex;
+// skip id 6
+// skip id 7
 // skip id 8
 // skip id 9
 // skip id 10
 // skip id 11
-typedef Protobuf::FieldRef<Protobuf::UInt32, TYPESTRING("pad_state"), 12> SIOPadState;
+// skip id 12
 // skip id 13
 // skip id 14
 typedef Protobuf::FieldRef<Protobuf::UInt8, TYPESTRING("currentdevice"), 15> SIOCurrentDevice;
@@ -168,12 +168,11 @@ typedef Protobuf::FieldRef<Protobuf::UInt8, TYPESTRING("mcd2_currentcommand"), 2
 typedef Protobuf::FieldRef<Protobuf::UInt16, TYPESTRING("mcd2_sector"), 30> SIOMCD2Sector;
 typedef Protobuf::FieldRef<Protobuf::UInt32, TYPESTRING("mcd2_dataoffset"), 31> SIOMCD2DataOffset;
 
-typedef Protobuf::Message<TYPESTRING("SIO"), SIOBuffer, SIOStatusReg, SIOModeReg, SIOCtrlReg, SIOBaudReg,
-                          SIOBufferMaxIndex, SIOBufferIndex, SIOPadState, SIOCurrentDevice, SIOMCD1TempBuffer,
-                          SIOMCD1DirectoryFlag, SIOMCD1ChecksumIn, SIOMCD1ChecksumOut, SIOMCD1CommandTicks,
-                          SIOMCD1CurrentCommand, SIOMCD1Sector, SIOMCD1DataOffset, SIOMCD2TempBuffer,
-                          SIOMCD2DirectoryFlag, SIOMCD2ChecksumIn, SIOMCD2ChecksumOut, SIOMCD2CommandTicks,
-                          SIOMCD2CurrentCommand, SIOMCD2Sector, SIOMCD2DataOffset>
+typedef Protobuf::Message<TYPESTRING("SIO"), SIOStatusReg, SIOModeReg, SIOCtrlReg, SIOBaudReg, SIOCurrentDevice,
+                          SIOMCD1TempBuffer, SIOMCD1DirectoryFlag, SIOMCD1ChecksumIn, SIOMCD1ChecksumOut,
+                          SIOMCD1CommandTicks, SIOMCD1CurrentCommand, SIOMCD1Sector, SIOMCD1DataOffset,
+                          SIOMCD2TempBuffer, SIOMCD2DirectoryFlag, SIOMCD2ChecksumIn, SIOMCD2ChecksumOut,
+                          SIOMCD2CommandTicks, SIOMCD2CurrentCommand, SIOMCD2Sector, SIOMCD2DataOffset>
     SIO;
 typedef Protobuf::MessageField<SIO, TYPESTRING("sio"), 7> SIOField;
 
diff --git a/src/gui/widgets/memcard_manager.cc b/src/gui/widgets/memcard_manager.cc
index 9db4264a3..9e8465231 100644
--- a/src/gui/widgets/memcard_manager.cc
+++ b/src/gui/widgets/memcard_manager.cc
@@ -63,19 +63,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 = 0;
             }
             if (ImGui::MenuItem(_("Import file into memory card 2"))) {
                 showImportMemoryCardDialog = true;
-                m_memoryCardImportExportIndex = 2;
+                m_memoryCardImportExportIndex = 1;
             }
             if (ImGui::MenuItem(_("Export memory card 1 to file"))) {
                 showExportMemoryCardDialog = true;
-                m_memoryCardImportExportIndex = 1;
+                m_memoryCardImportExportIndex = 0;
             }
             if (ImGui::MenuItem(_("Export memory card 2 to file"))) {
                 showExportMemoryCardDialog = true;
-                m_memoryCardImportExportIndex = 2;
+                m_memoryCardImportExportIndex = 1;
             }
             ImGui::EndMenu();
         }
@@ -89,8 +89,9 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
     if (m_importMemoryCardDialog.draw()) {
         std::vector<PCSX::u8string> 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[m_memoryCardImportExportIndex].getMcdData());
+            g_emulator->m_memoryCards->saveMcd(m_memoryCardImportExportIndex);
             clearUndoBuffer();
         }
     }
@@ -104,7 +105,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
         if (!fileToOpen.empty()) {
             IO<File> 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 +121,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(0);
+        const auto dataCard2 = g_emulator->m_memoryCards->getMcdData(1);
         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<uint8_t[]> latest = std::make_unique<uint8_t[]>(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<uint8_t[]> latest = std::make_unique<uint8_t[]>(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(0);
+        g_emulator->m_memoryCards->saveMcd(1);
     }
     ImGui::TextUnformatted(_("Undo version: "));
     ImGui::SameLine();
@@ -164,7 +165,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
 
     if (ImGui::Checkbox(_("Card 1 Pocketstation"),
                         &g_emulator->settings.get<Emulator::SettingMcd1Pocketstation>().value)) {
-        g_emulator->m_sio->togglePocketstationMode();
+        g_emulator->m_memoryCards->setPocketstationEnabled(0, g_emulator->settings.get<Emulator::SettingMcd1Pocketstation>().value);
         changed = true;
     }
     ImGuiHelpers::ShowHelpMarker(
@@ -172,7 +173,7 @@ 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<Emulator::SettingMcd2Pocketstation>().value)) {
-        g_emulator->m_sio->togglePocketstationMode();
+        g_emulator->m_memoryCards->setPocketstationEnabled(1, g_emulator->settings.get<Emulator::SettingMcd2Pocketstation>().value);
         changed = true;
     }
     ImGuiHelpers::ShowHelpMarker(
@@ -189,9 +190,9 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
         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 +204,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 +244,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 +256,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 +264,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 +278,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(0);
+                            g_emulator->m_memoryCards->saveMcd(1);
                         }
                     }
                 } else {
@@ -314,11 +315,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(0, 1);
             ImGui::EndTabItem();
         }
         if (ImGui::BeginTabItem(_("Memory Card 2"))) {
-            draw(2, 1);
+            draw(1, 0);
             ImGui::EndTabItem();
         }
         ImGui::EndTabBar();
@@ -329,7 +330,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 +357,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 +383,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 +424,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 dff8f4f60..341f8592b 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"
 
@@ -54,19 +54,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<uint8_t[]>&& tosave, const std::string& action);
 
     std::unique_ptr<uint8_t[]> getLatest() {
-        std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(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<uint8_t[]> data = std::make_unique<uint8_t[]>(MemoryCards::c_cardSize * 2);
+        std::memcpy(data.get(), g_emulator->m_memoryCards->getMcdData(0), MemoryCards::c_cardSize);
+        std::memcpy(data.get() + MemoryCards::c_cardSize, g_emulator->m_memoryCards->getMcdData(1),
+                    MemoryCards::c_cardSize);
 
         return data;
     }
diff --git a/src/main/main.cc b/src/main/main.cc
index 1764157c1..12a14ed0d 100644
--- a/src/main/main.cc
+++ b/src/main/main.cc
@@ -226,22 +226,9 @@ int pcsxMain(int argc, char **argv) {
         // to handle overrides properly.
         auto &emuSettings = emulator->settings;
         auto &debugSettings = emuSettings.get<PCSX::Emulator::SettingDebugSettings>();
-        if (emuSettings.get<PCSX::Emulator::SettingMcd1>().empty()) {
-            emuSettings.get<PCSX::Emulator::SettingMcd1>() = MAKEU8(u8"memcard1.mcd");
-        }
-
-        if (emuSettings.get<PCSX::Emulator::SettingMcd2>().empty()) {
-            emuSettings.get<PCSX::Emulator::SettingMcd2>() = MAKEU8(u8"memcard2.mcd");
-        }
-
-        auto argPath1 = args.get<std::string>("memcard1");
-        auto argPath2 = args.get<std::string>("memcard2");
-        if (argPath1.has_value()) emuSettings.get<PCSX::Emulator::SettingMcd1>() = argPath1.value();
-        if (argPath2.has_value()) emuSettings.get<PCSX::Emulator::SettingMcd2>() = argPath1.value();
-        PCSX::u8string path1 = emuSettings.get<PCSX::Emulator::SettingMcd1>().string();
-        PCSX::u8string path2 = emuSettings.get<PCSX::Emulator::SettingMcd2>().string();
 
-        emulator->m_sio->loadMcds(path1, path2);
+        PCSX::g_emulator->m_memoryCards->loadMcds(args);
+        
         auto biosCfg = args.get<std::string>("bios");
         if (biosCfg.has_value()) emuSettings.get<PCSX::Emulator::SettingBios>() = biosCfg.value();
 

From f93553c48c0f20494ef6a72c5d151468f2ded21e Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Sun, 4 Aug 2024 19:48:46 -0500
Subject: [PATCH 2/8] Cleanup

---
 src/core/memorycard.cc | 32 ++++++--------------
 src/core/memorycard.h  | 67 +++++++++++-------------------------------
 src/core/pad.cc        | 19 ++++--------
 src/core/psxemulator.h | 26 +---------------
 src/core/sio.cc        |  1 -
 src/core/sstate.cc     |  4 ---
 6 files changed, 32 insertions(+), 117 deletions(-)

diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc
index b9eb2262e..6c0e48738 100644
--- a/src/core/memorycard.cc
+++ b/src/core/memorycard.cc
@@ -26,16 +26,13 @@
 
 void PCSX::MemoryCards::loadMcds(const CommandLine::args &args) {
     auto &settings = g_emulator->settings;
-    const char *card_ids[] = {"1", "2", "1b", "1c", "1d", "2b", "2c", "2d"};
+    const char *card_ids[] = {"1", "2"};
 
     std::filesystem::path *card_paths[] = {
-        &settings.get<PCSX::Emulator::SettingMcd1>().value,  &settings.get<PCSX::Emulator::SettingMcd2>().value,
-        &settings.get<PCSX::Emulator::SettingMcd1B>().value, &settings.get<PCSX::Emulator::SettingMcd1C>().value,
-        &settings.get<PCSX::Emulator::SettingMcd1D>().value, &settings.get<PCSX::Emulator::SettingMcd2B>().value,
-        &settings.get<PCSX::Emulator::SettingMcd2C>().value, &settings.get<PCSX::Emulator::SettingMcd2D>().value,
+        &settings.get<PCSX::Emulator::SettingMcd1>().value,  &settings.get<PCSX::Emulator::SettingMcd2>().value
     };
 
-    for (int i = 0; i < 8; i++) {
+    for (int i = 0; i < 2; i++) {
         auto argPath = args.get<std::string>(fmt::format("memcard{}", card_ids[i]));
         if (argPath.has_value()) {
             *card_paths[i] = argPath.value();
@@ -46,10 +43,6 @@ void PCSX::MemoryCards::loadMcds(const CommandLine::args &args) {
             *card_paths[i] = path;
         }
 
-        if (i >= m_memoryCard.size()) {
-            continue;
-        }
-
         loadMcd(card_paths[i]->u8string(), m_memoryCard[i].getMcdData());
     }
 }
@@ -185,12 +178,7 @@ void PCSX::MemoryCards::getMcdBlockInfo(int mcd, int block, McdBlock &info) {
 }
 
 char *PCSX::MemoryCards::getMcdData(int mcd) {
-    const int index = mcd - 1;
-    if (index < 0 || index >= m_memoryCard.size()) {
-        throw std::runtime_error("Attempt to access invalid memory card");
-        return nullptr;
-    } else {
-        return m_memoryCard[index].getMcdData();
+        return m_memoryCard[mcd].getMcdData();
 }
 
 // Erase a memory card block by clearing it with 0s
@@ -659,8 +647,6 @@ bool PCSX::MemoryCards::loadMcd(PCSX::u8string mcd, char *data) {
     const char *fname = reinterpret_cast<const char *>(mcd.c_str());
     size_t bytesRead;
 
-    bool result = false;
-
     FILE *f = fopen(fname, "rb");
     if (f == nullptr) {
         PCSX::g_system->printf(_("The memory card %s doesn't exist - creating it\n"), fname);
@@ -702,11 +688,11 @@ bool PCSX::MemoryCards::loadMcd(PCSX::u8string mcd, char *data) {
         if (bytesRead != c_cardSize) {
             throw std::runtime_error(_("Error reading memory card."));
         } else {
-            result = true;
+            return true;
         }
     }
 
-    return result;
+    return false;
 }
 
 bool PCSX::MemoryCards::saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size) {
@@ -715,7 +701,6 @@ bool PCSX::MemoryCards::saveMcd(PCSX::u8string mcd, const char *data, uint32_t a
     }
     const char *fname = reinterpret_cast<const char *>(mcd.c_str());
     FILE *f = fopen(fname, "r+b");
-    bool result = false;
 
     if (f != nullptr) {
         struct stat buf;
@@ -734,8 +719,9 @@ bool PCSX::MemoryCards::saveMcd(PCSX::u8string mcd, const char *data, uint32_t a
 
         fwrite(data + adr, 1, size, f);
         fclose(f);
-        result = 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");
@@ -745,7 +731,7 @@ bool PCSX::MemoryCards::saveMcd(PCSX::u8string mcd, const char *data, uint32_t a
         }
     }
 
-    return result;
+    return false;
 }
 
 void PCSX::MemoryCards::createMcd(PCSX::u8string mcd) {
diff --git a/src/core/memorycard.h b/src/core/memorycard.h
index 7bb9f887e..2a3eef0b8 100644
--- a/src/core/memorycard.h
+++ b/src/core/memorycard.h
@@ -33,13 +33,7 @@ class Memorycards;
 /// </summary>
 class MemoryCard {
   public:
-    // MemoryCard() { init(); }
-    MemoryCard(uint8_t device_index) : m_deviceIndex(device_index) { init(); }
-
-    ~MemoryCard(){};
-
-    // Hardware events
-    void init() {
+    MemoryCard(uint8_t device_index) : m_deviceIndex(device_index) {
         if (m_mcdData) {
             memset(m_mcdData, 0, c_cardSize);
         }
@@ -48,6 +42,10 @@ class MemoryCard {
             memset(m_tempBuffer, 0, c_blockSize);
         }
     }
+    ~MemoryCard(){};
+
+    // Hardware events
+
     void deselect() {
         memset(&m_tempBuffer, 0, c_sectorSize);
         m_currentCommand = Commands::None;
@@ -107,9 +105,9 @@ 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, bool *ack);           // *
@@ -120,7 +118,7 @@ class MemoryCard {
     uint8_t tickPS_PrepFileExec(uint8_t value, bool *ack);  // 59h
     uint8_t tickPS_ExecCustom(uint8_t value, bool *ack);    // 5Dh
 
-    char *m_mcdData = new char[c_cardSize];
+    char m_mcdData[c_cardSize];
     uint8_t m_tempBuffer[c_blockSize];
     bool m_savedToDisk = false;
 
@@ -147,32 +145,14 @@ class MemoryCard {
 /// </summary>
 class MemoryCards {
   public:
-    MemoryCards() {
-        for (int i = 0; i < c_cardCount; i++) {
-            MemoryCard card = MemoryCard(i);
-            m_memoryCard.push_back(card);
-        }
-    }
-    ~MemoryCards() {
-        if (m_memoryCard.size() > 0) {
-            m_memoryCard.clear();
-        }
-    }
-
     void deselect() {
-        for (int i = 0; i < m_memoryCard.size(); i++) {
-            m_memoryCard[i].deselect();
-        }
-    }
-
-    void init() {
-        // setPocketstationEnabled();
+        m_memoryCard[0].deselect();
+        m_memoryCard[1].deselect();
     }
 
     void reset() {
-        for (int i = 0; i < m_memoryCard.size(); i++) {
-            m_memoryCard[i].reset();
-        }
+        m_memoryCard[0].reset();
+        m_memoryCard[1].reset();
     }
 
     struct McdBlock {
@@ -232,7 +212,7 @@ class MemoryCards {
 
     bool loadMcd(PCSX::u8string mcd, char *data);
     bool saveMcd(PCSX::u8string mcd, const char *data, uint32_t adr, size_t size);
-    // void saveMcd(const PCSX::u8string path) { saveMcd(path, m_mcdData, 0, c_cardSize); }
+
     static constexpr int otherMcd(int mcd) {
         if ((mcd != 0) && (mcd != 1)) throw std::runtime_error("Bad memory card number");
         if (mcd == 0) return 1;
@@ -241,26 +221,14 @@ class MemoryCards {
 
     PCSX::u8string getMcdPath(int index) {
         std::filesystem::path *paths[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1B>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1C>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1D>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2B>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2C>().value,
-                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2D>().value};
+                                          &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2>().value};
 
         PCSX::u8string thepath = paths[index]->u8string();
         return thepath;
     }
     bool isCardInserted(int index) {
         bool *const inserted_lut[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1Inserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2Inserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1BInserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1CInserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1DInserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2BInserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2CInserted>().value,
-                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2DInserted>().value};
+                                      &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2Inserted>().value};
 
         return *inserted_lut[index];
     }
@@ -269,8 +237,7 @@ class MemoryCards {
     void resetCard(int index);
     void setPocketstationEnabled(int index, bool enabled);
 
-    static constexpr size_t c_cardCount = 8;
-    std::vector<MemoryCard> m_memoryCard;
+    MemoryCard m_memoryCard[2] = {MemoryCard(0), MemoryCard(1)};
 };
 
 }  // namespace PCSX
diff --git a/src/core/pad.cc b/src/core/pad.cc
index 8cb61f59b..a03cafede 100644
--- a/src/core/pad.cc
+++ b/src/core/pad.cc
@@ -166,7 +166,6 @@ class PadsImpl : public PCSX::Pads {
     };
 
     struct Pad {
-        Pad(uint8_t device_index) : m_deviceIndex(device_index) {}
         uint8_t startPoll();
         uint8_t read();
         uint8_t poll(uint8_t value, uint32_t& padState);
@@ -219,11 +218,9 @@ class PadsImpl : public PCSX::Pads {
         uint32_t m_bufferIndex = 0;
         uint32_t m_maxBufferIndex = 0;
         uint32_t m_padState = Pads::PAD_STATE_IDLE;
-
-        uint8_t m_deviceIndex = 0;
     };
 
-    std::array<Pad, 2> m_pads = {Pad(0), Pad(1)};
+    std::array<Pad, 2> m_pads;
     unsigned m_selectedPadForConfig = 0;
 };
 
@@ -618,12 +615,12 @@ void PadsImpl::Pad::getButtons() {
 
     if ((m_padID >= GLFW_JOYSTICK_1) && (m_padID <= GLFW_JOYSTICK_LAST)) {
         hasPad = glfwGetGamepadState(m_padID, &state);
-            if (!hasPad) {
+        if (!hasPad) {
             const char* guid = glfwGetJoystickGUID(m_padID);
-                PCSX::g_system->printf("Gamepad error: GUID %s likely has no database mapping, disabling pad\n", guid);
-                m_padID = -1;
-            }
+            PCSX::g_system->printf("Gamepad error: GUID %s likely has no database mapping, disabling pad\n", guid);
+            m_padID = -1;
         }
+    }
 
     if (!hasPad) {
         if (inputType == InputType::Auto) {
@@ -974,12 +971,6 @@ bool PadsImpl::configure(PCSX::GUI* gui) {
     PCSX::ImGuiHelpers::ShowHelpMarker(
         _("When enabled, pressing CTRL and ALT will toggle the setting above, raw input"));
 
-    changed |= ImGui::Checkbox(_("Port 1 multi-tap connected"),
-                               &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingPort1Multitap>().value);
-
-    changed |= ImGui::Checkbox(_("Port 2 multi-tap connected"),
-                               &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingPort2Multitap>().value);
-
     if (ImGui::BeginCombo(_("Pad"), c_padNames[m_selectedPadForConfig]())) {
         for (unsigned i = 0; i < 2; i++) {
             if (ImGui::Selectable(c_padNames[i](), m_selectedPadForConfig == i)) {
diff --git a/src/core/psxemulator.h b/src/core/psxemulator.h
index 9a4cbda8f..fbf2f77f2 100644
--- a/src/core/psxemulator.h
+++ b/src/core/psxemulator.h
@@ -186,26 +186,6 @@ class Emulator {
     typedef SettingPath<TYPESTRING("EXP1Filepath")> SettingEXP1Filepath;
     typedef SettingPath<TYPESTRING("EXP1BrowsePath")> SettingEXP1BrowsePath;
     typedef Setting<bool, TYPESTRING("PIOConnected")> SettingPIOConnected;
-    typedef Setting<bool, TYPESTRING("Mcd1BInserted"), false> SettingMcd1BInserted;
-    typedef Setting<bool, TYPESTRING("Mcd1CInserted"), false> SettingMcd1CInserted;
-    typedef Setting<bool, TYPESTRING("Mcd1DInserted"), false> SettingMcd1DInserted;
-    typedef Setting<bool, TYPESTRING("Mcd2BInserted"), false> SettingMcd2BInserted;
-    typedef Setting<bool, TYPESTRING("Mcd2CInserted"), false> SettingMcd2CInserted;
-    typedef Setting<bool, TYPESTRING("Mcd8Inserted"), false> SettingMcd2DInserted;
-    typedef Setting<bool, TYPESTRING("Mcd1BPocketstation"), false> SettingMcd1BPocketstation;
-    typedef Setting<bool, TYPESTRING("Mcd1CPocketstation"), false> SettingMcd1CPocketstation;
-    typedef Setting<bool, TYPESTRING("Mcd1DPocketstation"), false> SettingMcd1DPocketstation;
-    typedef Setting<bool, TYPESTRING("Mcd2BPocketstation"), false> SettingMcd2BPocketstation;
-    typedef Setting<bool, TYPESTRING("Mcd2CPocketstation"), false> SettingMcd2CPocketstation;
-    typedef Setting<bool, TYPESTRING("Mcd2DPocketstation"), false> SettingMcd2DPocketstation;
-    typedef Setting<bool, TYPESTRING("Port1Multitap"), false> SettingPort1Multitap;
-    typedef Setting<bool, TYPESTRING("Port2Multitap"), false> SettingPort2Multitap;
-    typedef SettingPath<TYPESTRING("Mcd1B")> SettingMcd1B;
-    typedef SettingPath<TYPESTRING("Mcd1C")> SettingMcd1C;
-    typedef SettingPath<TYPESTRING("Mcd1D")> SettingMcd1D;
-    typedef SettingPath<TYPESTRING("Mcd2B")> SettingMcd2B;
-    typedef SettingPath<TYPESTRING("Mcd2C")> SettingMcd2C;
-    typedef SettingPath<TYPESTRING("Mcd2D")> SettingMcd2D;
 
     Settings<SettingMcd1, SettingMcd2, SettingBios, SettingPpfDir, SettingPsxExe, SettingXa, SettingSpuIrq,
              SettingBnWMdec, SettingScaler, SettingAutoVideo, SettingVideo, SettingFastBoot, SettingDebugSettings,
@@ -214,11 +194,7 @@ class Emulator {
              SettingGLErrorReportingSeverity, SettingFullCaching, SettingHardwareRenderer, SettingShownAutoUpdateConfig,
              SettingAutoUpdate, SettingMSAA, SettingLinearFiltering, SettingKioskMode, SettingMcd1Pocketstation,
              SettingMcd2Pocketstation, SettingBiosBrowsePath, SettingEXP1Filepath, SettingEXP1BrowsePath,
-             SettingPIOConnected, SettingMcd1BInserted, SettingMcd1CInserted, SettingMcd1DInserted,
-             SettingMcd2BInserted, SettingMcd2CInserted, SettingMcd2DInserted, SettingMcd1BPocketstation,
-             SettingMcd1CPocketstation, SettingMcd1DPocketstation, SettingMcd2BPocketstation, SettingMcd2CPocketstation,
-             SettingMcd2DPocketstation, SettingPort1Multitap, SettingPort2Multitap, SettingMcd1B, SettingMcd1C,
-             SettingMcd1D, SettingMcd2B, SettingMcd2C, SettingMcd2D>
+             SettingPIOConnected>
         settings;
     class PcsxConfig {
       public:
diff --git a/src/core/sio.cc b/src/core/sio.cc
index 68a86a75b..796a2ef45 100644
--- a/src/core/sio.cc
+++ b/src/core/sio.cc
@@ -43,7 +43,6 @@ void PCSX::SIO::acknowledge() {
 }
 
 void PCSX::SIO::init() {
-    g_emulator->m_memoryCards->init();
     g_emulator->m_pads->init();
     reset();
     g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status);
diff --git a/src/core/sstate.cc b/src/core/sstate.cc
index a99f5875c..46be9f88a 100644
--- a/src/core/sstate.cc
+++ b/src/core/sstate.cc
@@ -81,14 +81,10 @@ 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_memoryCards->m_memoryCard[0].m_tempBuffer },
             SIOMCD1DirectoryFlag { g_emulator->m_memoryCards->m_memoryCard[0].m_directoryFlag },

From ae695c867e81c2a2ee8c15bd7343fdac6050e117 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Sun, 4 Aug 2024 20:16:07 -0500
Subject: [PATCH 3/8] Remove redundant code

---
 src/core/memorycard.h | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/src/core/memorycard.h b/src/core/memorycard.h
index 2a3eef0b8..6ef4fe933 100644
--- a/src/core/memorycard.h
+++ b/src/core/memorycard.h
@@ -33,16 +33,7 @@ class Memorycards;
 /// </summary>
 class MemoryCard {
   public:
-    MemoryCard(uint8_t device_index) : m_deviceIndex(device_index) {
-        if (m_mcdData) {
-            memset(m_mcdData, 0, c_cardSize);
-        }
-
-        if (m_tempBuffer) {
-            memset(m_tempBuffer, 0, c_blockSize);
-        }
-    }
-    ~MemoryCard(){};
+    MemoryCard(uint8_t device_index) : m_deviceIndex(device_index){}
 
     // Hardware events
 
@@ -118,8 +109,8 @@ class MemoryCard {
     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];
-    uint8_t m_tempBuffer[c_blockSize];
+    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;

From 704c614eccd67bcd325e599a62c43377d8e87390 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Fri, 9 Aug 2024 12:32:14 -0500
Subject: [PATCH 4/8] Fix linux build

---
 src/core/memorycard.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc
index 6c0e48738..7bf09553e 100644
--- a/src/core/memorycard.cc
+++ b/src/core/memorycard.cc
@@ -20,6 +20,7 @@
 #include "core/memorycard.h"
 
 #include <bitset>
+#include <format>
 
 #include "core/sio.h"
 #include "support/sjis_conv.h"

From d2df71f0278dc7847466883f9b38a81f83255be8 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Fri, 9 Aug 2024 13:37:05 -0500
Subject: [PATCH 5/8] Actually fix linux/cross builds

---
 src/core/memorycard.cc | 29 ++++++++++++-----------------
 1 file changed, 12 insertions(+), 17 deletions(-)

diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc
index 7bf09553e..1f410409c 100644
--- a/src/core/memorycard.cc
+++ b/src/core/memorycard.cc
@@ -27,25 +27,20 @@
 
 void PCSX::MemoryCards::loadMcds(const CommandLine::args &args) {
     auto &settings = g_emulator->settings;
-    const char *card_ids[] = {"1", "2"};
 
-    std::filesystem::path *card_paths[] = {
-        &settings.get<PCSX::Emulator::SettingMcd1>().value,  &settings.get<PCSX::Emulator::SettingMcd2>().value
-    };
-
-    for (int i = 0; i < 2; i++) {
-        auto argPath = args.get<std::string>(fmt::format("memcard{}", card_ids[i]));
-        if (argPath.has_value()) {
-            *card_paths[i] = argPath.value();
-        }
-
-        if (card_paths[i]->u8string().empty()) {
-            std::string path = std::format("memcard{}.mcd", card_ids[i]);
-            *card_paths[i] = path;
-        }
-
-        loadMcd(card_paths[i]->u8string(), m_memoryCard[i].getMcdData());
+    auto argPath1 = args.get<std::string>("memcard1");
+    auto argPath2 = args.get<std::string>("memcard2");
+    if (argPath1.has_value()) {
+        settings.get<PCSX::Emulator::SettingMcd1>() = argPath1.value();
+    }
+    if (argPath2.has_value()) {
+        settings.get<PCSX::Emulator::SettingMcd2>() = argPath1.value();
     }
+    PCSX::u8string path1 = settings.get<PCSX::Emulator::SettingMcd1>().string();
+    PCSX::u8string path2 = settings.get<PCSX::Emulator::SettingMcd2>().string();
+
+    loadMcd(path1, m_memoryCard[0].getMcdData());
+    loadMcd(path2, m_memoryCard[1].getMcdData());
 }
 
 void PCSX::MemoryCards::getMcdBlockInfo(int mcd, int block, McdBlock &info) {

From 416fc9f264ba8c9faa9d7ff69b4c4559e7950be7 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Fri, 9 Aug 2024 13:37:36 -0500
Subject: [PATCH 6/8] Derp

---
 src/core/memorycard.cc | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc
index 1f410409c..b7e695560 100644
--- a/src/core/memorycard.cc
+++ b/src/core/memorycard.cc
@@ -20,7 +20,6 @@
 #include "core/memorycard.h"
 
 #include <bitset>
-#include <format>
 
 #include "core/sio.h"
 #include "support/sjis_conv.h"

From 9c85a6fc586036088beeaf33a8b931603f543050 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Fri, 9 Aug 2024 19:09:10 -0500
Subject: [PATCH 7/8] Also derp?

---
 src/core/pad.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/core/pad.h b/src/core/pad.h
index 0fa4188b0..84a627578 100644
--- a/src/core/pad.h
+++ b/src/core/pad.h
@@ -35,6 +35,8 @@ class Pads {
   public:
     enum class Port { Port1 = 0, Port2 };
 
+    virtual ~Pads() = default;
+
     class InputDevice {
         virtual void* getPadState() = 0;
         virtual bool isButtonPressed(int button) = 0;

From 5eb1c3c86f748e31ea42333c09522636d79bc972 Mon Sep 17 00:00:00 2001
From: John Baumann <johnbaumann@live.com>
Date: Wed, 11 Dec 2024 19:01:09 -0600
Subject: [PATCH 8/8] Fix local var naming style, add memory card selector enum

---
 src/core/memorycard.cc             | 113 ++++++++++++++---------------
 src/core/memorycard.h              |  52 +++++++------
 src/core/pad.cc                    |   6 +-
 src/core/sio.cc                    |  44 +++++------
 src/gui/widgets/memcard_manager.cc |  37 ++++++----
 src/gui/widgets/memcard_manager.h  |   6 +-
 6 files changed, 134 insertions(+), 124 deletions(-)

diff --git a/src/core/memorycard.cc b/src/core/memorycard.cc
index b7e695560..18b8074ce 100644
--- a/src/core/memorycard.cc
+++ b/src/core/memorycard.cc
@@ -22,6 +22,7 @@
 #include <bitset>
 
 #include "core/sio.h"
+#include "magic_enum/include/magic_enum/magic_enum_all.hpp"
 #include "support/sjis_conv.h"
 
 void PCSX::MemoryCards::loadMcds(const CommandLine::args &args) {
@@ -42,7 +43,7 @@ void PCSX::MemoryCards::loadMcds(const CommandLine::args &args) {
     loadMcd(path2, m_memoryCard[1].getMcdData());
 }
 
-void PCSX::MemoryCards::getMcdBlockInfo(int mcd, int block, McdBlock &info) {
+void PCSX::MemoryCards::getMcdBlockInfo(MemoryCard::Which mcd, int block, McdBlock &info) {
     if (block < 1 || block > 15) {
         throw std::runtime_error(_("Wrong block number"));
     }
@@ -172,8 +173,8 @@ void PCSX::MemoryCards::getMcdBlockInfo(int mcd, int block, McdBlock &info) {
     }
 }
 
-char *PCSX::MemoryCards::getMcdData(int mcd) {
-        return m_memoryCard[mcd].getMcdData();
+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
@@ -205,7 +206,7 @@ void PCSX::MemoryCards::eraseMcdFile(const McdBlock &block) {
     }
 }
 
-unsigned PCSX::MemoryCards::getFreeSpace(int mcd) {
+unsigned PCSX::MemoryCards::getFreeSpace(MemoryCard::Which mcd) {
     unsigned count = 0;
     for (int i = 1; i < 16; i++) {
         McdBlock block;
@@ -239,7 +240,7 @@ unsigned PCSX::MemoryCards::getFileBlockCount(McdBlock block) {
     }
 }
 
-int PCSX::MemoryCards::findFirstFree(int mcd) {
+int PCSX::MemoryCards::findFirstFree(MemoryCard::Which mcd) {
     McdBlock block;
     for (int i = 1; i < 16; i++) {
         getMcdBlockInfo(mcd, i, block);
@@ -302,31 +303,29 @@ bool PCSX::MemoryCards::copyMcdFile(McdBlock block) {
 
 // Back up the entire memory card to a file
 // index: The memory card to back up (0-7)
-bool PCSX::MemoryCards::saveMcd(int index) {
-    return saveMcd(getMcdPath(index), m_memoryCard[index].getMcdData(), 0, c_cardSize);
+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(int index) {
-        m_memoryCard[index].reset();
-}
+void PCSX::MemoryCards::resetCard(MemoryCard::Which which) { m_memoryCard[magic_enum::enum_integer(which)].reset(); }
 
-void PCSX::MemoryCards::setPocketstationEnabled(int index, bool enabled) {
-    m_memoryCard[index].setPocketstationEnabled(enabled);
+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_deviceIndex)) {
+        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"), m_deviceIndex + 1, retry_count + 1);
+            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 data_out = m_spdr;
+    uint8_t dataOut = m_spdr;
 
     if (m_currentCommand == Commands::None || m_currentCommand == Commands::Access) {
         m_currentCommand = value;
@@ -385,49 +384,49 @@ uint8_t PCSX::MemoryCard::transceive(uint8_t value, bool *ack) {
             break;
     }
 
-    return data_out;
+    return dataOut;
 }
 
 inline uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value, bool *ack) {
-    uint8_t data_out = 0xFF;
+    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;
 
@@ -435,12 +434,12 @@ inline uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value, bool *ack) {
         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;
@@ -448,22 +447,22 @@ inline uint8_t PCSX::MemoryCard::tickReadCommand(uint8_t value, bool *ack) {
             break;
 
         case 136:
-            data_out = m_checksumOut;
+            dataOut = m_checksumOut;
             break;
 
         case 137:
-            data_out = Responses::GoodReadWrite;
+            dataOut = Responses::GoodReadWrite;
             break;
     }
 
     m_commandTicks++;
     *ack = true;
 
-    return data_out;
+    return dataOut;
 }
 
 inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
-    uint8_t data_out = 0xFF;
+    uint8_t dataOut = 0xFF;
 
     switch (m_commandTicks) {
             // Data is sent and received simultaneously,
@@ -474,22 +473,22 @@ inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
             // 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
@@ -498,7 +497,7 @@ inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
             // 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
@@ -512,7 +511,7 @@ inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
                 m_checksumOut ^= value;
 
                 // Reply with (pre)
-                data_out = value;
+                dataOut = value;
                 m_dataOffset++;
             } else {
                 // Send this till the spooky extra bytes go away
@@ -534,17 +533,17 @@ inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
                 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();
@@ -554,17 +553,17 @@ inline uint8_t PCSX::MemoryCard::tickWriteCommand(uint8_t value, bool *ack) {
     m_commandTicks++;
     *ack = true;
 
-    return data_out;
+    return dataOut;
 }
 
 inline uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value, bool *ack) {
-    uint8_t data_out = Responses::IdleHighZ;
+    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)) {
@@ -574,54 +573,54 @@ inline uint8_t PCSX::MemoryCard::tickPS_GetDirIndex(uint8_t value, bool *ack) {
 
     m_commandTicks++;
 
-    return data_out;
+    return dataOut;
 }
 
 inline uint8_t PCSX::MemoryCard::tickPS_ExecCustom(uint8_t value, bool *ack) {
-    uint8_t data_out = Responses::IdleHighZ;
+    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++;
     *ack = true;
 
-    return data_out;
+    return dataOut;
 }
 
 inline uint8_t PCSX::MemoryCard::tickPS_PrepFileExec(uint8_t value, bool *ack) {
-    uint8_t data_out = Responses::IdleHighZ;
+    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++;
     *ack = true;
 
-    return data_out;
+    return dataOut;
 }
 
 inline uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value, bool *ack) {
-    uint8_t data_out = Responses::IdleHighZ;
+    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)) {
@@ -631,7 +630,7 @@ inline uint8_t PCSX::MemoryCard::tickPS_GetVersion(uint8_t value, bool *ack) {
 
     m_commandTicks++;
 
-    return data_out;
+    return dataOut;
 }
 
 // To-do: "All the code starting here is terrible and needs to be rewritten"
@@ -715,7 +714,7 @@ bool PCSX::MemoryCards::saveMcd(PCSX::u8string mcd, const char *data, uint32_t a
         fwrite(data + adr, 1, size, f);
         fclose(f);
         PCSX::g_system->printf(_("Saving memory card %s\n"), fname);
-        
+
         return true;
     } else {
         // try to create it again if we can't open it
diff --git a/src/core/memorycard.h b/src/core/memorycard.h
index 6ef4fe933..660c7fe50 100644
--- a/src/core/memorycard.h
+++ b/src/core/memorycard.h
@@ -23,17 +23,22 @@
 
 #include "core/psxemulator.h"
 #include "core/sstate.h"
+#include "magic_enum/include/magic_enum/magic_enum_all.hpp"
 
 namespace PCSX {
 class SIO;
-class Memorycards;
 
 /// <summary>
 /// Implements a memory card for SIO
 /// </summary>
 class MemoryCard {
   public:
-    MemoryCard(uint8_t device_index) : m_deviceIndex(device_index){}
+    enum class Which : uint8_t {
+        One = 0,
+        Two = 1,
+    };
+
+    MemoryCard(Which which) : m_whichDevice(which) {}
 
     // Hardware events
 
@@ -128,7 +133,7 @@ class MemoryCard {
     uint16_t m_directoryIndex = 0;
 
     SIO *m_sio = nullptr;
-    uint8_t m_deviceIndex = 0;
+    Which m_whichDevice = Which::One;
 };
 
 /// <summary>
@@ -148,7 +153,7 @@ class MemoryCards {
 
     struct McdBlock {
         McdBlock() { reset(); }
-        int mcd;
+        MemoryCard::Which mcd;
         int number;
         std::string titleAscii;
         std::string titleSjis;
@@ -161,7 +166,7 @@ class MemoryCards {
         uint32_t allocState;
         int16_t nextBlock;
         void reset() {
-            mcd = 0;
+            mcd = MemoryCard::Which::One;
             number = 0;
             titleAscii.clear();
             titleSjis.clear();
@@ -184,51 +189,52 @@ class MemoryCards {
 
     bool copyMcdFile(McdBlock block);
     void eraseMcdFile(const McdBlock &block);
-    void eraseMcdFile(int mcd, int block) {
+    void eraseMcdFile(MemoryCard::Which mcd, int block) {
         McdBlock info;
         getMcdBlockInfo(mcd, block, info);
         eraseMcdFile(info);
     }
-    int findFirstFree(int mcd);
-    unsigned getFreeSpace(int mcd);
+    int findFirstFree(MemoryCard::Which mcd);
+    unsigned getFreeSpace(MemoryCard::Which mcd);
     unsigned getFileBlockCount(McdBlock block);
-    void getMcdBlockInfo(int mcd, int block, McdBlock &info);
-    char *getMcdData(int mcd);
+    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(int card_index);
+    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 int otherMcd(int mcd) {
-        if ((mcd != 0) && (mcd != 1)) throw std::runtime_error("Bad memory card number");
-        if (mcd == 0) return 1;
-        return 0;
+    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(int index) {
+    PCSX::u8string getMcdPath(MemoryCard::Which which) {
         std::filesystem::path *paths[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1>().value,
                                           &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2>().value};
 
-        PCSX::u8string thepath = paths[index]->u8string();
+        PCSX::u8string thepath = paths[magic_enum::enum_integer(which)]->u8string();
         return thepath;
     }
-    bool isCardInserted(int index) {
+    bool isCardInserted(MemoryCard::Which which) {
         bool *const inserted_lut[] = {&PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd1Inserted>().value,
                                       &PCSX::g_emulator->settings.get<PCSX::Emulator::SettingMcd2Inserted>().value};
 
-        return *inserted_lut[index];
+        return *inserted_lut[magic_enum::enum_integer(which)];
     }
 
-    static constexpr int otherMcd(const McdBlock &block) { return otherMcd(block.mcd); }
-    void resetCard(int index);
-    void setPocketstationEnabled(int index, bool enabled);
+    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(0), MemoryCard(1)};
+    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 a03cafede..82849457d 100644
--- a/src/core/pad.cc
+++ b/src/core/pad.cc
@@ -698,7 +698,7 @@ void PadsImpl::Pad::getButtons() {
 }
 
 uint8_t PadsImpl::Pad::transceive(uint8_t value, bool* ack) {
-    uint8_t data_out = 0xff;
+    uint8_t dataOut = 0xff;
 
     switch (m_padState) {
         case Pads::PAD_STATE_IDLE:  // start pad
@@ -732,14 +732,14 @@ uint8_t PadsImpl::Pad::transceive(uint8_t value, bool* ack) {
             break;
     }
 
-    data_out = m_buffer[m_bufferIndex];
+    dataOut = m_buffer[m_bufferIndex];
 
     if (m_padState == Pads::PAD_STATE_BAD_COMMAND) {
     } else {
         *ack = true;
     }
 
-    return data_out;
+    return dataOut;
 }
 
 uint8_t PadsImpl::Pad::poll(uint8_t value, uint32_t& padState) {
diff --git a/src/core/sio.cc b/src/core/sio.cc
index 796a2ef45..9af91edd8 100644
--- a/src/core/sio.cc
+++ b/src/core/sio.cc
@@ -82,11 +82,11 @@ bool PCSX::SIO::isReceiveIRQReady() {
 }
 
 bool PCSX::SIO::isTransmitReady() {
-    const bool tx_enabled = m_regs.control & ControlFlags::TX_ENABLE;
-    const bool tx_finished = m_regs.status & StatusFlags::TX_FINISHED;
-    const bool tx_data_empty = m_regs.status & StatusFlags::TX_DATACLEAR;
+    const bool txEnabled = m_regs.control & ControlFlags::TX_ENABLE;
+    const bool txFinished = m_regs.status & StatusFlags::TX_FINISHED;
+    const bool txDataEmpty = m_regs.status & StatusFlags::TX_DATACLEAR;
 
-    return (tx_enabled && tx_finished && !tx_data_empty);
+    return (txEnabled && txFinished && !txDataEmpty);
 }
 
 void PCSX::SIO::reset() {
@@ -106,14 +106,14 @@ void PCSX::SIO::reset() {
 }
 
 uint8_t PCSX::SIO::writeCard(uint8_t value) {
-    const int port_index = ((m_regs.control & ControlFlags::WHICH_PORT) == SelectedPort::Port1) ? 0 : 1;
-    const bool is_connected = g_emulator->m_memoryCards->isCardInserted(port_index);
+    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<MemoryCard::Which>(portIndex).value());
 
     bool ack = false;
-    uint8_t rx_buffer = 0xff;
+    uint8_t rxBuffer = 0xff;
 
-    if (is_connected) {
-        rx_buffer = g_emulator->m_memoryCards->m_memoryCard[port_index].transceive(m_regs.data, &ack);
+    if (isConnected) {
+        rxBuffer = g_emulator->m_memoryCards->m_memoryCard[portIndex].transceive(m_regs.data, &ack);
     } else {
         m_currentDevice = DeviceType::Ignore;
     }
@@ -122,18 +122,18 @@ uint8_t PCSX::SIO::writeCard(uint8_t value) {
         acknowledge();
     }
 
-    return rx_buffer;
+    return rxBuffer;
 }
 
 uint8_t PCSX::SIO::writePad(uint8_t value) {
-    const int port_index = ((m_regs.control & ControlFlags::WHICH_PORT) == SelectedPort::Port1) ? 0 : 1;
-    const bool is_connected = g_emulator->m_pads->isPadConnected(port_index);
+    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 rx_buffer = 0xff;
+    uint8_t rxBuffer = 0xff;
 
-    if (is_connected) {
-        rx_buffer = g_emulator->m_pads->transceive(port_index, m_regs.data, &ack);
+    if (isConnected) {
+        rxBuffer = g_emulator->m_pads->transceive(portIndex, m_regs.data, &ack);
     } else {
         m_currentDevice = DeviceType::Ignore;
     }
@@ -142,14 +142,14 @@ uint8_t PCSX::SIO::writePad(uint8_t value) {
         acknowledge();
     }
 
-    return rx_buffer;
+    return rxBuffer;
 }
 
 void PCSX::SIO::transmitData() {
     m_regs.status &= ~StatusFlags::TX_FINISHED;
     g_emulator->m_mem->writeHardwareRegister<0x1044>(m_regs.status);
 
-    uint8_t rx_buffer = 0xff;
+    uint8_t rxBuffer = 0xff;
 
     if (m_currentDevice == DeviceType::None) {
         m_currentDevice = m_regs.data;
@@ -157,11 +157,11 @@ void PCSX::SIO::transmitData() {
 
     switch (m_currentDevice) {
         case DeviceType::PAD:
-            rx_buffer = writePad(m_regs.data);
+            rxBuffer = writePad(m_regs.data);
             break;
 
         case DeviceType::MemoryCard:
-            rx_buffer = writeCard(m_regs.data);
+            rxBuffer = writeCard(m_regs.data);
             break;
 
         case DeviceType::Ignore:
@@ -171,10 +171,10 @@ void PCSX::SIO::transmitData() {
             m_currentDevice = DeviceType::Ignore;
     }
 
-    m_rxFIFO.push(rx_buffer);
+    m_rxFIFO.push(rxBuffer);
     updateFIFOStatus();
-    m_regs.data = rx_buffer;
-    g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(rx_buffer);
+    m_regs.data = rxBuffer;
+    g_emulator->m_mem->writeHardwareRegister<0x1040, uint8_t>(rxBuffer);
 
     if (isReceiveIRQReady() && !(m_regs.status & StatusFlags::IRQ)) {
         scheduleInterrupt(SIO_CYCLES);
diff --git a/src/gui/widgets/memcard_manager.cc b/src/gui/widgets/memcard_manager.cc
index b01de2c4d..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 = 0;
+                m_memoryCardImportExportIndex = MemoryCard::Which::One;
             }
             if (ImGui::MenuItem(_("Import file into memory card 2"))) {
                 showImportMemoryCardDialog = true;
-                m_memoryCardImportExportIndex = 1;
+                m_memoryCardImportExportIndex = MemoryCard::Which::Two;
             }
             if (ImGui::MenuItem(_("Export memory card 1 to file"))) {
                 showExportMemoryCardDialog = true;
-                m_memoryCardImportExportIndex = 0;
+                m_memoryCardImportExportIndex = MemoryCard::Which::One;
             }
             if (ImGui::MenuItem(_("Export memory card 2 to file"))) {
                 showExportMemoryCardDialog = true;
-                m_memoryCardImportExportIndex = 1;
+                m_memoryCardImportExportIndex = MemoryCard::Which::Two;
             }
             ImGui::EndMenu();
         }
@@ -90,7 +91,9 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
         std::vector<PCSX::u8string> fileToOpen = m_importMemoryCardDialog.selected();
         if (!fileToOpen.empty()) {
             g_emulator->m_memoryCards->loadMcd(
-                fileToOpen[0], g_emulator->m_memoryCards->m_memoryCard[m_memoryCardImportExportIndex].getMcdData());
+                fileToOpen[0],
+                g_emulator->m_memoryCards->m_memoryCard[magic_enum::enum_integer(m_memoryCardImportExportIndex)]
+                    .getMcdData());
             g_emulator->m_memoryCards->saveMcd(m_memoryCardImportExportIndex);
             clearUndoBuffer();
         }
@@ -121,8 +124,8 @@ 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_memoryCards->getMcdData(0);
-        const auto dataCard2 = g_emulator->m_memoryCards->getMcdData(1);
+        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(), MemoryCards::c_cardSize);
             std::memcpy(dataCard2, m_latest.get() + MemoryCards::c_cardSize, MemoryCards::c_cardSize);
@@ -136,8 +139,8 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
             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_memoryCards->saveMcd(0);
-        g_emulator->m_memoryCards->saveMcd(1);
+        g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::One);
+        g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::Two);
     }
     ImGui::TextUnformatted(_("Undo version: "));
     ImGui::SameLine();
@@ -165,7 +168,8 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
 
     if (ImGui::Checkbox(_("Card 1 Pocketstation"),
                         &g_emulator->settings.get<Emulator::SettingMcd1Pocketstation>().value)) {
-        g_emulator->m_memoryCards->setPocketstationEnabled(0, g_emulator->settings.get<Emulator::SettingMcd1Pocketstation>().value);
+        g_emulator->m_memoryCards->setPocketstationEnabled(
+            MemoryCard::Which::One, g_emulator->settings.get<Emulator::SettingMcd1Pocketstation>().value);
         changed = true;
     }
     ImGuiHelpers::ShowHelpMarker(
@@ -173,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<Emulator::SettingMcd2Pocketstation>().value)) {
-        g_emulator->m_memoryCards->setPocketstationEnabled(1, g_emulator->settings.get<Emulator::SettingMcd2Pocketstation>().value);
+        g_emulator->m_memoryCards->setPocketstationEnabled(
+            MemoryCard::Which::Two, g_emulator->settings.get<Emulator::SettingMcd2Pocketstation>().value);
         changed = true;
     }
     ImGuiHelpers::ShowHelpMarker(
@@ -186,7 +191,7 @@ 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;
@@ -287,8 +292,8 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
                                 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_memoryCards->saveMcd(0);
-                            g_emulator->m_memoryCards->saveMcd(1);
+                            g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::One);
+                            g_emulator->m_memoryCards->saveMcd(MemoryCard::Which::Two);
                         }
                     }
                 } else {
@@ -315,11 +320,11 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
 
     if (ImGui::BeginTabBar("Cards")) {
         if (ImGui::BeginTabItem(_("Memory Card 1"))) {
-            draw(0, 1);
+            draw(MemoryCard::Which::One, MemoryCard::Which::Two);
             ImGui::EndTabItem();
         }
         if (ImGui::BeginTabItem(_("Memory Card 2"))) {
-            draw(1, 0);
+            draw(MemoryCard::Which::Two, MemoryCard::Which::One);
             ImGui::EndTabItem();
         }
         ImGui::EndTabBar();
diff --git a/src/gui/widgets/memcard_manager.h b/src/gui/widgets/memcard_manager.h
index ef0e620e6..248ab8772 100644
--- a/src/gui/widgets/memcard_manager.h
+++ b/src/gui/widgets/memcard_manager.h
@@ -68,8 +68,8 @@ class MemcardManager {
 
     std::unique_ptr<uint8_t[]> getLatest() {
         std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(MemoryCards::c_cardSize * 2);
-        std::memcpy(data.get(), g_emulator->m_memoryCards->getMcdData(0), MemoryCards::c_cardSize);
-        std::memcpy(data.get() + MemoryCards::c_cardSize, g_emulator->m_memoryCards->getMcdData(1),
+        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;
@@ -79,7 +79,7 @@ class MemcardManager {
     std::unique_ptr<uint8_t[]> m_latest;
     Widgets::FileDialog<> m_importMemoryCardDialog;
     Widgets::FileDialog<FileDialogMode::Save> m_exportMemoryCardDialog;
-    unsigned m_memoryCardImportExportIndex = 0;
+    MemoryCard::Which m_memoryCardImportExportIndex = MemoryCard::Which::One;
 
     void clearUndoBuffer() {
         m_undo.clear();