diff --git a/pcsx2/Hotkeys.cpp b/pcsx2/Hotkeys.cpp index 8a74ac57f5..38fdc870cd 100644 --- a/pcsx2/Hotkeys.cpp +++ b/pcsx2/Hotkeys.cpp @@ -11,6 +11,7 @@ #include "Recording/InputRecording.h" #include "SPU2/spu2.h" #include "VMManager.h" +#include "SIO/Memcard/MemoryCardFile.h" #include "common/Assertions.h" #include "common/FileSystem.h" @@ -223,6 +224,11 @@ DEFINE_HOTKEY("InputRecToggleMode", TRANSLATE_NOOP("Hotkeys", "System"), if (!pressed && VMManager::HasValidVM()) g_InputRecording.getControls().toggleRecordMode(); }) +DEFINE_HOTKEY("SwapMemCards", TRANSLATE_NOOP("Hotkeys", "System"), + TRANSLATE_NOOP("Hotkeys", "Swap Memory Cards"), [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + FileMcd_Swap(); + }) DEFINE_HOTKEY("PreviousSaveStateSlot", TRANSLATE_NOOP("Hotkeys", "Save States"), TRANSLATE_NOOP("Hotkeys", "Select Previous Save Slot"), [](s32 pressed) { diff --git a/pcsx2/SIO/Memcard/MemoryCardFile.cpp b/pcsx2/SIO/Memcard/MemoryCardFile.cpp index fc76c1416d..e340f1f34d 100644 --- a/pcsx2/SIO/Memcard/MemoryCardFile.cpp +++ b/pcsx2/SIO/Memcard/MemoryCardFile.cpp @@ -5,6 +5,7 @@ #include "SIO/Memcard/MemoryCardFolder.h" #include "SIO/Sio.h" +#include #include "common/Assertions.h" #include "common/Console.h" @@ -18,6 +19,7 @@ #include "Config.h" #include "Host.h" +#include "VMManager.h" #include "IconsPromptFont.h" #include "fmt/format.h" @@ -636,6 +638,68 @@ void FileMcd_Reopen(std::string new_serial) FileMcd_EmuOpen(); } +static bool FileMcd_IsAutoEjecting() +{ + for (size_t port = 0; port < SIO::PORTS; ++port) + { + for (size_t slot = 0; slot < SIO::SLOTS; ++slot) + { + if (mcds[port][slot].autoEjectTicks > 0) + { + return true; // Auto-eject is active + } + } + } + return false; // No auto-eject active +} + +void FileMcd_Swap() +{ + //Assert that this is called on the CPU thread + pxAssert(Host::RunOnCPUThread([]() { return true; }, true) && "FileMcd_Swap must be called under Host::RunOnCPUThread"); + + if (MemcardBusy::IsBusy()) + { + Host::AddIconOSDMessage("MemoryCardSwap_Busy", ICON_PF_MEMORY_CARD, TRANSLATE_SV("MemoryCardSwap_Busy", "Memory cards are busy. Can't swap right now.")); + return; + } + + // Check if auto-eject is active + if (FileMcd_IsAutoEjecting()) + { + Host::AddIconOSDMessage("MemoryCardSwap_AutoEject", ICON_PF_MEMORY_CARD, TRANSLATE_SV("MemoryCardSwap_AutoEject", "Memory cards are being auto-ejected. Can't swap right now.")); + return; + } + + const std::string card1Filename = Host::GetStringSettingValue("MemoryCards", "Slot1_Filename"); + const std::string card2Filename = Host::GetStringSettingValue("MemoryCards", "Slot2_Filename"); + + // Copy each McdOptions to local memory + Pcsx2Config::McdOptions firstSlot = EmuConfig.Mcd[0]; + Pcsx2Config::McdOptions secondSlot = EmuConfig.Mcd[1]; + + if (!firstSlot.Enabled || !secondSlot.Enabled || card1Filename.empty() || card2Filename.empty()) + { + Host::AddIconOSDMessage("MemoryCardSwap_EmptySlot", ICON_PF_MEMORY_CARD, TRANSLATE_SV("MemoryCard_EmptySlot", "Both slots must have a card selected to swap.")); + return; + } + + // Swap them + Host::SetBaseStringSettingValue("MemoryCards", "Slot1_Filename", card2Filename.c_str()); + Host::SetBaseStringSettingValue("MemoryCards", "Slot2_Filename", card1Filename.c_str()); + Host::CommitBaseSettingChanges(); + VMManager::ApplySettings(); + EmuConfig.Mcd[0] = secondSlot; + EmuConfig.Mcd[1] = firstSlot; + + // Reopen them + FileMcd_EmuClose(); + FileMcd_SetType(); + FileMcd_EmuOpen(); + AutoEject::SetAll(); + Host::AddIconOSDMessage("MemoryCardSwap", ICON_PF_MEMORY_CARD, fmt::format(TRANSLATE_FS("MemoryCardSwap", "Memory Cards have been swapped.\nSlot 1: {}\nSlot 2: {}"), EmuConfig.Mcd[0].Filename, EmuConfig.Mcd[1].Filename), Host::OSD_INFO_DURATION); +} + s32 FileMcd_IsPresent(uint port, uint slot) { const uint combinedSlot = FileMcd_ConvertToSlot(port, slot); diff --git a/pcsx2/SIO/Memcard/MemoryCardFile.h b/pcsx2/SIO/Memcard/MemoryCardFile.h index 08419b9007..44d40619c7 100644 --- a/pcsx2/SIO/Memcard/MemoryCardFile.h +++ b/pcsx2/SIO/Memcard/MemoryCardFile.h @@ -42,6 +42,7 @@ void FileMcd_EmuOpen(); void FileMcd_EmuClose(); void FileMcd_CancelEject(); void FileMcd_Reopen(std::string new_serial); +void FileMcd_Swap(); s32 FileMcd_IsPresent(uint port, uint slot); void FileMcd_GetSizeInfo(uint port, uint slot, McdSizeInfo* outways); bool FileMcd_IsPSX(uint port, uint slot);