diff --git a/Source/Core/Common/EnumFormatter.h b/Source/Core/Common/EnumFormatter.h index 26cc910438..9530013768 100644 --- a/Source/Core/Common/EnumFormatter.h +++ b/Source/Core/Common/EnumFormatter.h @@ -95,7 +95,6 @@ protected: constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {} -private: const array_type m_names; char format_type = 'u'; }; diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index 1a16b7e95a..7ca13a7729 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -156,12 +156,13 @@ bool BootCore(std::unique_ptr boot, const WindowSystemInfo& wsi) // Movie settings if (Movie::IsPlayingInput() && Movie::IsConfigSaved()) { - for (int i = 0; i < 2; ++i) + for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { - if (Movie::IsUsingMemcard(i) && Movie::IsStartingFromClearSave() && !StartUp.bWii) + if (Movie::IsUsingMemcard(slot) && Movie::IsStartingFromClearSave() && !StartUp.bWii) { const auto raw_path = - File::GetUserPath(D_GCUSER_IDX) + fmt::format("Movie{}.raw", (i == 0) ? 'A' : 'B'); + File::GetUserPath(D_GCUSER_IDX) + + fmt::format("Movie{}.raw", slot == ExpansionInterface::Slot::A ? 'A' : 'B'); if (File::Exists(raw_path)) File::Delete(raw_path); diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index e2b73ce846..650592f1da 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -8,13 +8,16 @@ #include #include "AudioCommon/AudioCommon.h" +#include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/Config/Config.h" +#include "Common/EnumMap.h" #include "Common/Logging/Log.h" #include "Common/MathUtil.h" #include "Common/StringUtil.h" #include "Common/Version.h" #include "Core/Config/DefaultLocale.h" +#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_Device.h" #include "Core/HW/Memmap.h" #include "Core/HW/SI/SI_Device.h" @@ -48,28 +51,60 @@ const Info MAIN_AUDIO_STRETCH{{System::Main, "Core", "AudioStretch"}, fals const Info MAIN_AUDIO_STRETCH_LATENCY{{System::Main, "Core", "AudioStretchMaxLatency"}, 80}; const Info MAIN_MEMCARD_A_PATH{{System::Main, "Core", "MemcardAPath"}, ""}; const Info MAIN_MEMCARD_B_PATH{{System::Main, "Core", "MemcardBPath"}, ""}; +const Info& GetInfoForMemcardPath(ExpansionInterface::Slot slot) +{ + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); + static constexpr Common::EnumMap*, ExpansionInterface::MAX_MEMCARD_SLOT> + infos{ + &MAIN_MEMCARD_A_PATH, + &MAIN_MEMCARD_B_PATH, + }; + return *infos[slot]; +} const Info MAIN_AGP_CART_A_PATH{{System::Main, "Core", "AgpCartAPath"}, ""}; const Info MAIN_AGP_CART_B_PATH{{System::Main, "Core", "AgpCartBPath"}, ""}; +const Info& GetInfoForAGPCartPath(ExpansionInterface::Slot slot) +{ + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); + static constexpr Common::EnumMap*, ExpansionInterface::MAX_MEMCARD_SLOT> + infos{ + &MAIN_AGP_CART_A_PATH, + &MAIN_AGP_CART_B_PATH, + }; + return *infos[slot]; +} const Info MAIN_GCI_FOLDER_A_PATH_OVERRIDE{ {System::Main, "Core", "GCIFolderAPathOverride"}, ""}; const Info MAIN_GCI_FOLDER_B_PATH_OVERRIDE{ {System::Main, "Core", "GCIFolderBPathOverride"}, ""}; - -const Info MAIN_SLOT_A{ - {System::Main, "Core", "SlotA"}, ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER}; -const Info MAIN_SLOT_B{{System::Main, "Core", "SlotB"}, - ExpansionInterface::EXIDEVICE_NONE}; -const Info MAIN_SERIAL_PORT_1{ - {System::Main, "Core", "SerialPort1"}, ExpansionInterface::EXIDEVICE_NONE}; - -const Info& GetInfoForEXIDevice(int channel) +const Info& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot) { - static constexpr std::array*, 3> infos{ - &MAIN_SLOT_A, - &MAIN_SLOT_B, - &MAIN_SERIAL_PORT_1, - }; - return *infos[channel]; + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); + static constexpr Common::EnumMap*, ExpansionInterface::MAX_MEMCARD_SLOT> + infos{ + &MAIN_GCI_FOLDER_A_PATH_OVERRIDE, + &MAIN_GCI_FOLDER_B_PATH_OVERRIDE, + }; + return *infos[slot]; +} + +const Info MAIN_SLOT_A{ + {System::Main, "Core", "SlotA"}, ExpansionInterface::EXIDeviceType::MemoryCardFolder}; +const Info MAIN_SLOT_B{{System::Main, "Core", "SlotB"}, + ExpansionInterface::EXIDeviceType::None}; +const Info MAIN_SERIAL_PORT_1{ + {System::Main, "Core", "SerialPort1"}, ExpansionInterface::EXIDeviceType::None}; + +const Info& GetInfoForEXIDevice(ExpansionInterface::Slot slot) +{ + static constexpr Common::EnumMap*, + ExpansionInterface::MAX_SLOT> + infos{ + &MAIN_SLOT_A, + &MAIN_SLOT_B, + &MAIN_SERIAL_PORT_1, + }; + return *infos[slot]; } const Info MAIN_BBA_MAC{{System::Main, "Core", "BBA_MAC"}, ""}; diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 5ee87fcf72..8a2b8be500 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -33,8 +33,9 @@ enum class DPL2Quality; namespace ExpansionInterface { -enum TEXIDevices : int; -} +enum class EXIDeviceType : int; +enum class Slot : int; +} // namespace ExpansionInterface namespace SerialInterface { @@ -65,14 +66,17 @@ extern const Info MAIN_AUDIO_STRETCH; extern const Info MAIN_AUDIO_STRETCH_LATENCY; extern const Info MAIN_MEMCARD_A_PATH; extern const Info MAIN_MEMCARD_B_PATH; +const Info& GetInfoForMemcardPath(ExpansionInterface::Slot slot); extern const Info MAIN_AGP_CART_A_PATH; extern const Info MAIN_AGP_CART_B_PATH; +const Info& GetInfoForAGPCartPath(ExpansionInterface::Slot slot); extern const Info MAIN_GCI_FOLDER_A_PATH_OVERRIDE; extern const Info MAIN_GCI_FOLDER_B_PATH_OVERRIDE; -extern const Info MAIN_SLOT_A; -extern const Info MAIN_SLOT_B; -extern const Info MAIN_SERIAL_PORT_1; -const Info& GetInfoForEXIDevice(int channel); +const Info& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot); +extern const Info MAIN_SLOT_A; +extern const Info MAIN_SLOT_B; +extern const Info MAIN_SERIAL_PORT_1; +const Info& GetInfoForEXIDevice(ExpansionInterface::Slot slot); extern const Info MAIN_BBA_MAC; extern const Info MAIN_BBA_XLINK_IP; extern const Info MAIN_BBA_XLINK_CHAT_OSD; diff --git a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp index cd6f6d8829..92dd1a9cfc 100644 --- a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp +++ b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp @@ -15,6 +15,7 @@ #include "Core/Config/MainSettings.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/Config/SessionSettings.h" +#include "Core/HW/EXI/EXI.h" #include "Core/NetPlayProto.h" namespace ConfigLoaders @@ -37,9 +38,8 @@ public: layer->Set(Config::MAIN_DSP_HLE, m_settings.m_DSPHLE); layer->Set(Config::MAIN_OVERCLOCK_ENABLE, m_settings.m_OCEnable); layer->Set(Config::MAIN_OVERCLOCK, m_settings.m_OCFactor); - layer->Set(Config::MAIN_SLOT_A, m_settings.m_EXIDevice[0]); - layer->Set(Config::MAIN_SLOT_B, m_settings.m_EXIDevice[1]); - layer->Set(Config::MAIN_SERIAL_PORT_1, m_settings.m_EXIDevice[2]); + for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS) + layer->Set(Config::GetInfoForEXIDevice(slot), m_settings.m_EXIDevice[slot]); layer->Set(Config::SESSION_SAVE_DATA_WRITABLE, m_settings.m_WriteToMemcard); layer->Set(Config::MAIN_RAM_OVERRIDE_ENABLE, m_settings.m_RAMOverrideEnable); layer->Set(Config::MAIN_MEM1_SIZE, m_settings.m_Mem1Size); diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 9125eabcb3..bb0b165249 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -25,26 +25,11 @@ struct Partition; class Volume; } // namespace DiscIO -namespace ExpansionInterface -{ -enum TEXIDevices : int; -} // namespace ExpansionInterface - namespace IOS::ES { class TMDReader; } // namespace IOS::ES -namespace PowerPC -{ -enum class CPUCore; -} // namespace PowerPC - -namespace SerialInterface -{ -enum SIDevices : int; -} // namespace SerialInterface - struct BootParameters; struct SConfig diff --git a/Source/Core/Core/HW/EXI/EXI.cpp b/Source/Core/Core/HW/EXI/EXI.cpp index d7fc90a25e..e10403e495 100644 --- a/Source/Core/Core/HW/EXI/EXI.cpp +++ b/Source/Core/Core/HW/EXI/EXI.cpp @@ -39,32 +39,70 @@ static void UpdateInterruptsCallback(u64 userdata, s64 cycles_late); namespace { -void AddMemoryCards(int i) +void AddMemoryCard(Slot slot) { - TEXIDevices memorycard_device; + EXIDeviceType memorycard_device; if (Movie::IsPlayingInput() && Movie::IsConfigSaved()) { - if (Movie::IsUsingMemcard(i)) + if (Movie::IsUsingMemcard(slot)) { - if (Config::Get(Config::GetInfoForEXIDevice(i)) == EXIDEVICE_MEMORYCARDFOLDER) - memorycard_device = EXIDEVICE_MEMORYCARDFOLDER; - else - memorycard_device = EXIDEVICE_MEMORYCARD; + memorycard_device = Config::Get(Config::GetInfoForEXIDevice(slot)); + if (memorycard_device != EXIDeviceType::MemoryCardFolder && + memorycard_device != EXIDeviceType::MemoryCard) + { + PanicAlertFmtT( + "The movie indicates that a memory card should be inserted into {0:n}, but one is not " + "currently inserted (instead, {1} is inserted). For the movie to sync properly, " + "please change the selected device to Memory Card or GCI Folder.", + slot, Common::GetStringT(fmt::format("{:n}", memorycard_device).c_str())); + } } else { - memorycard_device = EXIDEVICE_NONE; + memorycard_device = EXIDeviceType::None; } } else { - memorycard_device = Config::Get(Config::GetInfoForEXIDevice(i)); + memorycard_device = Config::Get(Config::GetInfoForEXIDevice(slot)); } - g_Channels[i]->AddDevice(memorycard_device, 0); + g_Channels[SlotToEXIChannel(slot)]->AddDevice(memorycard_device, SlotToEXIDevice(slot)); } } // namespace +u8 SlotToEXIChannel(Slot slot) +{ + switch (slot) + { + case Slot::A: + return 0; + case Slot::B: + return 1; + case Slot::SP1: + return 0; + default: + PanicAlertFmt("Unhandled slot {}", slot); + return 0; + } +} + +u8 SlotToEXIDevice(Slot slot) +{ + switch (slot) + { + case Slot::A: + return 0; + case Slot::B: + return 0; + case Slot::SP1: + return 2; + default: + PanicAlertFmt("Unhandled slot {}", slot); + return 0; + } +} + void Init() { if (!g_SRAM_netplay_initialized) @@ -98,12 +136,13 @@ void Init() } } - for (int i = 0; i < MAX_MEMORYCARD_SLOTS; i++) - AddMemoryCards(i); + for (Slot slot : MEMCARD_SLOTS) + AddMemoryCard(slot); - g_Channels[0]->AddDevice(EXIDEVICE_MASKROM, 1); - g_Channels[0]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_1), 2); - g_Channels[2]->AddDevice(EXIDEVICE_AD16, 0); + g_Channels[0]->AddDevice(EXIDeviceType::MaskROM, 1); + g_Channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_1), + SlotToEXIDevice(Slot::SP1)); + g_Channels[2]->AddDevice(EXIDeviceType::AD16, 0); changeDevice = CoreTiming::RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback); updateInterrupts = CoreTiming::RegisterEvent("EXIUpdateInterrupts", UpdateInterruptsCallback); @@ -149,15 +188,20 @@ static void ChangeDeviceCallback(u64 userdata, s64 cyclesLate) u8 type = (u8)(userdata >> 16); u8 num = (u8)userdata; - g_Channels.at(channel)->AddDevice((TEXIDevices)type, num); + g_Channels.at(channel)->AddDevice(static_cast(type), num); } -void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num, +void ChangeDevice(Slot slot, EXIDeviceType device_type, CoreTiming::FromThread from_thread) +{ + ChangeDevice(SlotToEXIChannel(slot), SlotToEXIDevice(slot), device_type, from_thread); +} + +void ChangeDevice(u8 channel, u8 device_num, EXIDeviceType device_type, CoreTiming::FromThread from_thread) { // Let the hardware see no device for 1 second CoreTiming::ScheduleEvent(0, changeDevice, - ((u64)channel << 32) | ((u64)EXIDEVICE_NONE << 16) | device_num, + ((u64)channel << 32) | ((u64)EXIDeviceType::None << 16) | device_num, from_thread); CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), changeDevice, ((u64)channel << 32) | ((u64)device_type << 16) | device_num, @@ -169,15 +213,9 @@ CEXIChannel* GetChannel(u32 index) return g_Channels.at(index).get(); } -IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex) +IEXIDevice* GetDevice(Slot slot) { - for (auto& channel : g_Channels) - { - IEXIDevice* device = channel->FindDevice(device_type, customIndex); - if (device) - return device; - } - return nullptr; + return g_Channels.at(SlotToEXIChannel(slot))->GetDevice(1 << SlotToEXIDevice(slot)); } void UpdateInterrupts() diff --git a/Source/Core/Core/HW/EXI/EXI.h b/Source/Core/Core/HW/EXI/EXI.h index f902c6e735..f466b0428b 100644 --- a/Source/Core/Core/HW/EXI/EXI.h +++ b/Source/Core/Core/HW/EXI/EXI.h @@ -3,7 +3,10 @@ #pragma once +#include + #include "Common/CommonTypes.h" +#include "Common/EnumFormatter.h" #include "Core/CoreTiming.h" class PointerWrap; @@ -21,14 +24,31 @@ namespace ExpansionInterface { class CEXIChannel; class IEXIDevice; -enum TEXIDevices : int; +enum class EXIDeviceType : int; enum { - MAX_MEMORYCARD_SLOTS = 2, MAX_EXI_CHANNELS = 3 }; +enum class Slot : int +{ + A, + B, + SP1, +}; +static constexpr auto SLOTS = {Slot::A, Slot::B, Slot::SP1}; +static constexpr auto MAX_SLOT = Slot::SP1; +static constexpr auto MEMCARD_SLOTS = {Slot::A, Slot::B}; +static constexpr auto MAX_MEMCARD_SLOT = Slot::B; +constexpr bool IsMemcardSlot(Slot slot) +{ + return slot == Slot::A || slot == Slot::B; +} + +u8 SlotToEXIChannel(Slot slot); +u8 SlotToEXIDevice(Slot slot); + void Init(); void Shutdown(); void DoState(PointerWrap& p); @@ -39,11 +59,18 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base); void UpdateInterrupts(); void ScheduleUpdateInterrupts(CoreTiming::FromThread from, int cycles_late); -void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num, +void ChangeDevice(Slot slot, EXIDeviceType device_type, + CoreTiming::FromThread from_thread = CoreTiming::FromThread::NON_CPU); +void ChangeDevice(u8 channel, u8 device_num, EXIDeviceType device_type, CoreTiming::FromThread from_thread = CoreTiming::FromThread::NON_CPU); CEXIChannel* GetChannel(u32 index); - -IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex = -1); +IEXIDevice* GetDevice(Slot slot); } // namespace ExpansionInterface + +template <> +struct fmt::formatter : EnumFormatter +{ + constexpr formatter() : EnumFormatter({"Slot A", "Slot B", "Serial Port 1"}) {} +}; diff --git a/Source/Core/Core/HW/EXI/EXI_Channel.cpp b/Source/Core/Core/HW/EXI/EXI_Channel.cpp index b8d46133fe..ccc85a42aa 100644 --- a/Source/Core/Core/HW/EXI/EXI_Channel.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Channel.cpp @@ -34,7 +34,7 @@ CEXIChannel::CEXIChannel(u32 channel_id, const Memcard::HeaderData& memcard_head m_status.CHIP_SELECT = 1; for (auto& device : m_devices) - device = EXIDevice_Create(EXIDEVICE_NONE, m_channel_id, m_memcard_header_data); + device = EXIDevice_Create(EXIDeviceType::None, m_channel_id, m_memcard_header_data); } CEXIChannel::~CEXIChannel() @@ -168,7 +168,7 @@ void CEXIChannel::RemoveDevices() device.reset(nullptr); } -void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num) +void CEXIChannel::AddDevice(const EXIDeviceType device_type, const int device_num) { AddDevice(EXIDevice_Create(device_type, m_channel_id, m_memcard_header_data), device_num); } @@ -245,7 +245,7 @@ void CEXIChannel::DoState(PointerWrap& p) for (int device_index = 0; device_index < NUM_DEVICES; ++device_index) { std::unique_ptr& device = m_devices[device_index]; - TEXIDevices type = device->m_device_type; + EXIDeviceType type = device->m_device_type; p.Do(type); if (type == device->m_device_type) @@ -260,7 +260,7 @@ void CEXIChannel::DoState(PointerWrap& p) AddDevice(std::move(save_device), device_index, false); } - if (type == EXIDEVICE_MEMORYCARDFOLDER && old_header_data != m_memcard_header_data && + if (type == EXIDeviceType::MemoryCardFolder && old_header_data != m_memcard_header_data && !Movie::IsMovieActive()) { // We have loaded a savestate that has a GCI folder memcard that is different to the virtual @@ -277,8 +277,8 @@ void CEXIChannel::DoState(PointerWrap& p) // notify_presence_changed flag set to true? Not sure how software behaves if the previous and // the new device type are identical in this case. I assume there is a reason we have this // grace period when switching in the GUI. - AddDevice(EXIDEVICE_NONE, device_index); - ExpansionInterface::ChangeDevice(m_channel_id, EXIDEVICE_MEMORYCARDFOLDER, device_index, + AddDevice(EXIDeviceType::None, device_index); + ExpansionInterface::ChangeDevice(m_channel_id, device_index, EXIDeviceType::MemoryCardFolder, CoreTiming::FromThread::CPU); } } @@ -294,15 +294,4 @@ void CEXIChannel::SetEXIINT(bool exiint) { m_status.EXIINT = !!exiint; } - -IEXIDevice* CEXIChannel::FindDevice(TEXIDevices device_type, int custom_index) -{ - for (auto& sup : m_devices) - { - IEXIDevice* device = sup->FindDevice(device_type, custom_index); - if (device) - return device; - } - return nullptr; -} } // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_Channel.h b/Source/Core/Core/HW/EXI/EXI_Channel.h index ce727b3008..96596a4db6 100644 --- a/Source/Core/Core/HW/EXI/EXI_Channel.h +++ b/Source/Core/Core/HW/EXI/EXI_Channel.h @@ -20,7 +20,7 @@ class Mapping; namespace ExpansionInterface { class IEXIDevice; -enum TEXIDevices : int; +enum class EXIDeviceType : int; class CEXIChannel { @@ -30,13 +30,12 @@ public: // get device IEXIDevice* GetDevice(u8 chip_select); - IEXIDevice* FindDevice(TEXIDevices device_type, int custom_index = -1); void RegisterMMIO(MMIO::Mapping* mmio, u32 base); void SendTransferComplete(); - void AddDevice(TEXIDevices device_type, int device_num); + void AddDevice(EXIDeviceType device_type, int device_num); void AddDevice(std::unique_ptr device, int device_num, bool notify_presence_changed = true); diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index 854a549f4f..e43d47a8b3 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -64,11 +64,6 @@ void IEXIDevice::DMARead(u32 address, u32 size) } } -IEXIDevice* IEXIDevice::FindDevice(TEXIDevices device_type, int custom_index) -{ - return (device_type == m_device_type) ? this : nullptr; -} - bool IEXIDevice::UseDelayedTransferCompletion() const { return false; @@ -101,60 +96,63 @@ void IEXIDevice::TransferByte(u8& byte) } // F A C T O R Y -std::unique_ptr EXIDevice_Create(const TEXIDevices device_type, const int channel_num, +std::unique_ptr EXIDevice_Create(const EXIDeviceType device_type, const int channel_num, const Memcard::HeaderData& memcard_header_data) { std::unique_ptr result; + // XXX This computation isn't necessarily right (it holds for A/B, but not SP1) + // However, the devices that care about slots currently only go in A/B. + const Slot slot = static_cast(channel_num); switch (device_type) { - case EXIDEVICE_DUMMY: + case EXIDeviceType::Dummy: result = std::make_unique("Dummy"); break; - case EXIDEVICE_MEMORYCARD: - case EXIDEVICE_MEMORYCARDFOLDER: + case EXIDeviceType::MemoryCard: + case EXIDeviceType::MemoryCardFolder: { - bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER); - result = std::make_unique(channel_num, gci_folder, memcard_header_data); + bool gci_folder = (device_type == EXIDeviceType::MemoryCardFolder); + result = std::make_unique(slot, gci_folder, memcard_header_data); break; } - case EXIDEVICE_MASKROM: + case EXIDeviceType::MaskROM: result = std::make_unique(); break; - case EXIDEVICE_AD16: + case EXIDeviceType::AD16: result = std::make_unique(); break; - case EXIDEVICE_MIC: + case EXIDeviceType::Microphone: result = std::make_unique(channel_num); break; - case EXIDEVICE_ETH: + case EXIDeviceType::Ethernet: result = std::make_unique(BBADeviceType::TAP); break; #if defined(__APPLE__) - case EXIDEVICE_ETHTAPSERVER: + case EXIDeviceType::EthernetTapServer: result = std::make_unique(BBADeviceType::TAPSERVER); break; #endif - case EXIDEVICE_ETHXLINK: + case EXIDeviceType::EthernetXLink: result = std::make_unique(BBADeviceType::XLINK); break; - case EXIDEVICE_GECKO: + case EXIDeviceType::Gecko: result = std::make_unique(); break; - case EXIDEVICE_AGP: - result = std::make_unique(channel_num); + case EXIDeviceType::AGP: + result = std::make_unique(slot); break; - case EXIDEVICE_AM_BASEBOARD: - case EXIDEVICE_NONE: + case EXIDeviceType::AMBaseboard: + case EXIDeviceType::None: default: result = std::make_unique(); break; diff --git a/Source/Core/Core/HW/EXI/EXI_Device.h b/Source/Core/Core/HW/EXI/EXI_Device.h index 2f0d831af4..232f260166 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.h +++ b/Source/Core/Core/HW/EXI/EXI_Device.h @@ -4,7 +4,10 @@ #pragma once #include + +#include "Common/Common.h" #include "Common/CommonTypes.h" +#include "Common/EnumFormatter.h" class PointerWrap; @@ -15,26 +18,26 @@ struct HeaderData; namespace ExpansionInterface { -enum TEXIDevices : int +enum class EXIDeviceType : int { - EXIDEVICE_DUMMY, - EXIDEVICE_MEMORYCARD, - EXIDEVICE_MASKROM, - EXIDEVICE_AD16, - EXIDEVICE_MIC, - EXIDEVICE_ETH, + Dummy, + MemoryCard, + MaskROM, + AD16, + Microphone, + Ethernet, // Was used for Triforce in the past, but the implementation is no longer in Dolphin. // It's kept here so that values below will stay constant. - EXIDEVICE_AM_BASEBOARD, - EXIDEVICE_GECKO, + AMBaseboard, + Gecko, // Only used when creating a device by EXIDevice_Create. - // Converted to EXIDEVICE_MEMORYCARD internally. - EXIDEVICE_MEMORYCARDFOLDER, - EXIDEVICE_AGP, - EXIDEVICE_ETHXLINK, + // Converted to MemoryCard internally. + MemoryCardFolder, + AGP, + EthernetXLink, // Only used on Apple devices. - EXIDEVICE_ETHTAPSERVER, - EXIDEVICE_NONE = 0xFF + EthernetTapServer, + None = 0xFF }; class IEXIDevice @@ -51,8 +54,6 @@ public: virtual void DMAWrite(u32 address, u32 size); virtual void DMARead(u32 address, u32 size); - virtual IEXIDevice* FindDevice(TEXIDevices device_type, int custom_index = -1); - virtual bool UseDelayedTransferCompletion() const; virtual bool IsPresent() const; virtual void SetCS(int cs); @@ -65,13 +66,60 @@ public: // For savestates. storing it here seemed cleaner than requiring each implementation to report its // type. I know this class is set up like an interface, but no code requires it to be strictly // such. - TEXIDevices m_device_type = TEXIDevices::EXIDEVICE_NONE; + EXIDeviceType m_device_type = EXIDeviceType::None; private: // Byte transfer function for this device virtual void TransferByte(u8& byte); }; -std::unique_ptr EXIDevice_Create(TEXIDevices device_type, int channel_num, +std::unique_ptr EXIDevice_Create(EXIDeviceType device_type, int channel_num, const Memcard::HeaderData& memcard_header_data); } // namespace ExpansionInterface + +template <> +struct fmt::formatter + : EnumFormatter +{ + static constexpr array_type names = { + _trans("Dummy"), + _trans("Memory Card"), + _trans("Mask ROM"), + // i18n: A mysterious debugging/diagnostics peripheral for the GameCube. + _trans("AD16"), + _trans("Microphone"), + _trans("Broadband Adapter (TAP)"), + _trans("Triforce AM Baseboard"), + _trans("USB Gecko"), + _trans("GCI Folder"), + _trans("Advance Game Port"), + _trans("Broadband Adapter (XLink Kai)"), + _trans("Broadband Adapter (tapserver)"), + }; + + constexpr formatter() : EnumFormatter(names) {} + + template + auto format(const ExpansionInterface::EXIDeviceType& e, FormatContext& ctx) + { + if (e != ExpansionInterface::EXIDeviceType::None) + { + return EnumFormatter::format(e, ctx); + } + else + { + // Special-case None since it has a fixed ID (0xff) that is much larger than the rest; we + // don't need 200 nullptr entries in names. We also want to format it specially in the UI. + switch (format_type) + { + default: + case 'u': + return fmt::format_to(ctx.out(), "None"); + case 's': + return fmt::format_to(ctx.out(), "0xffu /* None */"); + case 'n': + return fmt::format_to(ctx.out(), _trans("")); + } + } + } +}; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceAGP.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceAGP.cpp index c0a5f843a4..8b2ae443d2 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceAGP.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceAGP.cpp @@ -8,18 +8,21 @@ #include #include +#include "Common/Assert.h" #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/IOFile.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" #include "Core/Config/MainSettings.h" +#include "Core/HW/EXI/EXI.h" namespace ExpansionInterface { -CEXIAgp::CEXIAgp(int index) +CEXIAgp::CEXIAgp(Slot slot) { - m_slot = index; + ASSERT(IsMemcardSlot(slot)); + m_slot = slot; // Create the ROM m_rom_size = 0; @@ -35,9 +38,7 @@ CEXIAgp::~CEXIAgp() std::string filename; std::string ext; std::string gbapath; - SplitPath(m_slot == 0 ? Config::Get(Config::MAIN_AGP_CART_A_PATH) : - Config::Get(Config::MAIN_AGP_CART_B_PATH), - &path, &filename, &ext); + SplitPath(Config::Get(Config::GetInfoForAGPCartPath(m_slot)), &path, &filename, &ext); gbapath = path + filename; SaveFileFromEEPROM(gbapath + ".sav"); @@ -75,9 +76,7 @@ void CEXIAgp::LoadRom() std::string path; std::string filename; std::string ext; - SplitPath(m_slot == 0 ? Config::Get(Config::MAIN_AGP_CART_A_PATH) : - Config::Get(Config::MAIN_AGP_CART_B_PATH), - &path, &filename, &ext); + SplitPath(Config::Get(Config::GetInfoForAGPCartPath(m_slot)), &path, &filename, &ext); const std::string gbapath = path + filename; LoadFileToROM(gbapath + ext); INFO_LOG_FMT(EXPANSIONINTERFACE, "Loaded GBA rom: {} card: {}", gbapath, m_slot); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceAGP.h b/Source/Core/Core/HW/EXI/EXI_DeviceAGP.h index ca2d21f1a4..65fa55f1c9 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceAGP.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceAGP.h @@ -12,10 +12,12 @@ class PointerWrap; namespace ExpansionInterface { +enum class Slot : int; + class CEXIAgp : public IEXIDevice { public: - CEXIAgp(const int index); + CEXIAgp(const Slot slot); virtual ~CEXIAgp() override; bool IsPresent() const override { return true; } void ImmWrite(u32 _uData, u32 _uSize) override; @@ -31,7 +33,7 @@ private: EE_READ_TRUE = 0xB, }; - int m_slot; + Slot m_slot; //! ROM u32 m_rom_size = 0; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp index f905b8bce4..3d11c3de00 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp @@ -16,6 +16,7 @@ #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Common/EnumMap.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" @@ -48,25 +49,24 @@ namespace ExpansionInterface static const u32 MC_TRANSFER_RATE_READ = 512 * 1024; static const auto MC_TRANSFER_RATE_WRITE = static_cast(96.125f * 1024.0f); -static std::array s_et_cmd_done; -static std::array s_et_transfer_complete; +static Common::EnumMap s_et_cmd_done; +static Common::EnumMap s_et_transfer_complete; +static Common::EnumMap s_card_short_names{'A', 'B'}; -// Takes care of the nasty recovery of the 'this' pointer from card_index, +// Takes care of the nasty recovery of the 'this' pointer from card_slot, // stored in the userdata parameter of the CoreTiming event. void CEXIMemoryCard::EventCompleteFindInstance(u64 userdata, std::function callback) { - int card_index = (int)userdata; - auto* self = static_cast( - ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index)); - if (self == nullptr) + Slot card_slot = static_cast(userdata); + IEXIDevice* self = ExpansionInterface::GetDevice(card_slot); + if (self != nullptr) { - self = static_cast( - ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARDFOLDER, card_index)); - } - if (self) - { - callback(self); + if (self->m_device_type == EXIDeviceType::MemoryCard || + self->m_device_type == EXIDeviceType::MemoryCardFolder) + { + callback(static_cast(self)); + } } } @@ -83,19 +83,15 @@ void CEXIMemoryCard::TransferCompleteCallback(u64 userdata, s64) void CEXIMemoryCard::Init() { - static constexpr char DONE_PREFIX[] = "memcardDone"; - static constexpr char TRANSFER_COMPLETE_PREFIX[] = "memcardTransferComplete"; - static_assert(s_et_cmd_done.size() == s_et_transfer_complete.size(), "Event array size differs"); - for (unsigned int i = 0; i < s_et_cmd_done.size(); ++i) + static_assert(s_et_cmd_done.size() == MEMCARD_SLOTS.size(), "Event array size differs"); + for (Slot slot : MEMCARD_SLOTS) { - std::string name = DONE_PREFIX; - name += static_cast('A' + i); - s_et_cmd_done[i] = CoreTiming::RegisterEvent(name, CmdDoneCallback); - - name = TRANSFER_COMPLETE_PREFIX; - name += static_cast('A' + i); - s_et_transfer_complete[i] = CoreTiming::RegisterEvent(name, TransferCompleteCallback); + s_et_cmd_done[slot] = CoreTiming::RegisterEvent( + fmt::format("memcardDone{}", s_card_short_names[slot]), CmdDoneCallback); + s_et_transfer_complete[slot] = CoreTiming::RegisterEvent( + fmt::format("memcardTransferComplete{}", s_card_short_names[slot]), + TransferCompleteCallback); } } @@ -105,12 +101,12 @@ void CEXIMemoryCard::Shutdown() s_et_transfer_complete.fill(nullptr); } -CEXIMemoryCard::CEXIMemoryCard(const int index, bool gci_folder, +CEXIMemoryCard::CEXIMemoryCard(const Slot slot, bool gci_folder, const Memcard::HeaderData& header_data) - : m_card_index(index) + : m_card_slot(slot) { - ASSERT_MSG(EXPANSIONINTERFACE, static_cast(index) < s_et_cmd_done.size(), - "Trying to create invalid memory card index {}.", index); + ASSERT_MSG(EXPANSIONINTERFACE, IsMemcardSlot(slot), "Trying to create invalid memory card in {}.", + slot); // NOTE: When loading a save state, DMA completion callbacks (s_et_transfer_complete) and such // may have been restored, we need to anticipate those arriving. @@ -145,15 +141,13 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gci_folder, m_memory_card_size = m_memory_card->GetCardId() * SIZE_TO_Mb; std::array header{}; m_memory_card->Read(0, static_cast(header.size()), header.data()); - SetCardFlashID(header.data(), m_card_index); + SetCardFlashID(header.data(), m_card_slot); } std::pair -CEXIMemoryCard::GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_folder) +CEXIMemoryCard::GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder) { - std::string path_override = - Config::Get(card_index == 0 ? Config::MAIN_GCI_FOLDER_A_PATH_OVERRIDE : - Config::MAIN_GCI_FOLDER_B_PATH_OVERRIDE); + std::string path_override = Config::Get(Config::GetInfoForGCIPathOverride(card_slot)); if (!path_override.empty()) return {std::move(path_override), false}; @@ -162,7 +156,7 @@ CEXIMemoryCard::GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_fo const bool use_movie_folder = allow_movie_folder == AllowMovieFolder::Yes && Movie::IsPlayingInput() && Movie::IsConfigSaved() && - Movie::IsUsingMemcard(card_index) && + Movie::IsUsingMemcard(card_slot) && Movie::IsStartingFromClearSave(); if (use_movie_folder) @@ -170,7 +164,7 @@ CEXIMemoryCard::GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_fo const DiscIO::Region region = SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region); path = path + SConfig::GetDirectoryForRegion(region) + DIR_SEP + - fmt::format("Card {}", char('A' + card_index)); + fmt::format("Card {}", s_card_short_names[card_slot]); return {std::move(path), !use_movie_folder}; } @@ -186,7 +180,7 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) // TODO(C++20): Use structured bindings when we can use C++20 and refer to structured bindings // in lambda captures - const auto folder_path_pair = GetGCIFolderPath(m_card_index, AllowMovieFolder::Yes); + const auto folder_path_pair = GetGCIFolderPath(m_card_slot, AllowMovieFolder::Yes); const std::string& dir_path = folder_path_pair.first; const bool migrate = folder_path_pair.second; @@ -194,7 +188,7 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) if (!file_info.Exists()) { if (migrate) // first use of memcard folder, migrate automatically - MigrateFromMemcardFile(dir_path + DIR_SEP, m_card_index); + MigrateFromMemcardFile(dir_path + DIR_SEP, m_card_slot); else File::CreateFullPath(dir_path + DIR_SEP); } @@ -204,7 +198,7 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) { PanicAlertFmtT("{0} was not a directory, moved to *.original", dir_path); if (migrate) - MigrateFromMemcardFile(dir_path + DIR_SEP, m_card_index); + MigrateFromMemcardFile(dir_path + DIR_SEP, m_card_slot); else File::CreateFullPath(dir_path + DIR_SEP); } @@ -218,22 +212,23 @@ void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) } } - m_memory_card = std::make_unique(dir_path + DIR_SEP, m_card_index, - header_data, current_game_id); + m_memory_card = std::make_unique(dir_path + DIR_SEP, m_card_slot, header_data, + current_game_id); } void CEXIMemoryCard::SetupRawMemcard(u16 size_mb) { - const bool is_slot_a = m_card_index == 0; - std::string filename = is_slot_a ? Config::Get(Config::MAIN_MEMCARD_A_PATH) : - Config::Get(Config::MAIN_MEMCARD_B_PATH); - if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(m_card_index) && + std::string filename = Config::Get(Config::GetInfoForMemcardPath(m_card_slot)); + if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(m_card_slot) && Movie::IsStartingFromClearSave()) - filename = File::GetUserPath(D_GCUSER_IDX) + fmt::format("Movie{}.raw", is_slot_a ? 'A' : 'B'); + { + filename = File::GetUserPath(D_GCUSER_IDX) + + fmt::format("Movie{}.raw", s_card_short_names[m_card_slot]); + } const std::string region_dir = SConfig::GetDirectoryForRegion(SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region)); - MemoryCard::CheckPath(filename, region_dir, is_slot_a); + MemoryCard::CheckPath(filename, region_dir, m_card_slot); if (size_mb < Memcard::MBIT_SIZE_MEMORY_CARD_2043) { @@ -241,13 +236,13 @@ void CEXIMemoryCard::SetupRawMemcard(u16 size_mb) fmt::format(".{}", Memcard::MbitToFreeBlocks(size_mb))); } - m_memory_card = std::make_unique(filename, m_card_index, size_mb); + m_memory_card = std::make_unique(filename, m_card_slot, size_mb); } CEXIMemoryCard::~CEXIMemoryCard() { - CoreTiming::RemoveEvent(s_et_cmd_done[m_card_index]); - CoreTiming::RemoveEvent(s_et_transfer_complete[m_card_index]); + CoreTiming::RemoveEvent(s_et_cmd_done[m_card_slot]); + CoreTiming::RemoveEvent(s_et_transfer_complete[m_card_slot]); } bool CEXIMemoryCard::UseDelayedTransferCompletion() const @@ -272,13 +267,14 @@ void CEXIMemoryCard::CmdDone() void CEXIMemoryCard::TransferComplete() { // Transfer complete, send interrupt - ExpansionInterface::GetChannel(m_card_index)->SendTransferComplete(); + ExpansionInterface::GetChannel(ExpansionInterface::SlotToEXIChannel(m_card_slot)) + ->SendTransferComplete(); } void CEXIMemoryCard::CmdDoneLater(u64 cycles) { - CoreTiming::RemoveEvent(s_et_cmd_done[m_card_index]); - CoreTiming::ScheduleEvent(cycles, s_et_cmd_done[m_card_index], m_card_index); + CoreTiming::RemoveEvent(s_et_cmd_done[m_card_slot]); + CoreTiming::ScheduleEvent(cycles, s_et_cmd_done[m_card_slot], static_cast(m_card_slot)); } void CEXIMemoryCard::SetCS(int cs) @@ -522,19 +518,10 @@ void CEXIMemoryCard::DoState(PointerWrap& p) p.Do(m_programming_buffer); p.Do(m_address); m_memory_card->DoState(p); - p.Do(m_card_index); + p.Do(m_card_slot); } } -IEXIDevice* CEXIMemoryCard::FindDevice(TEXIDevices device_type, int custom_index) -{ - if (device_type != m_device_type) - return nullptr; - if (custom_index != m_card_index) - return nullptr; - return this; -} - // DMA reads are preceded by all of the necessary setup via IMMRead // read all at once instead of single byte at a time as done by IEXIDevice::DMARead void CEXIMemoryCard::DMARead(u32 addr, u32 size) @@ -548,7 +535,7 @@ void CEXIMemoryCard::DMARead(u32 addr, u32 size) // Schedule transfer complete later based on read speed CoreTiming::ScheduleEvent(size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ), - s_et_transfer_complete[m_card_index], m_card_index); + s_et_transfer_complete[m_card_slot], static_cast(m_card_slot)); } // DMA write are preceded by all of the necessary setup via IMMWrite @@ -564,6 +551,6 @@ void CEXIMemoryCard::DMAWrite(u32 addr, u32 size) // Schedule transfer complete later based on write speed CoreTiming::ScheduleEvent(size * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE), - s_et_transfer_complete[m_card_index], m_card_index); + s_et_transfer_complete[m_card_slot], static_cast(m_card_slot)); } } // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h index 01fc30a4eb..c472448d09 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.h @@ -21,6 +21,8 @@ struct HeaderData; namespace ExpansionInterface { +enum class Slot : int; + enum class AllowMovieFolder { Yes, @@ -30,14 +32,13 @@ enum class AllowMovieFolder class CEXIMemoryCard : public IEXIDevice { public: - CEXIMemoryCard(int index, bool gci_folder, const Memcard::HeaderData& header_data); + CEXIMemoryCard(Slot slot, bool gci_folder, const Memcard::HeaderData& header_data); ~CEXIMemoryCard() override; void SetCS(int cs) override; bool IsInterruptSet() override; bool UseDelayedTransferCompletion() const override; bool IsPresent() const override; void DoState(PointerWrap& p) override; - IEXIDevice* FindDevice(TEXIDevices device_type, int custom_index) override; void DMARead(u32 addr, u32 size) override; void DMAWrite(u32 addr, u32 size) override; @@ -48,7 +49,7 @@ public: static void Shutdown(); static std::pair - GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_folder); + GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_folder); private: void SetupGciFolder(const Memcard::HeaderData& header_data); @@ -90,7 +91,7 @@ private: ChipErase = 0xF4, }; - int m_card_index; + Slot m_card_slot; //! memory card state // STATE_TO_SAVE diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardBase.h b/Source/Core/Core/HW/GCMemcard/GCMemcardBase.h index 6990148587..5805b90560 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardBase.h +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardBase.h @@ -12,8 +12,9 @@ class PointerWrap; class MemoryCardBase { public: - explicit MemoryCardBase(int card_index = 0, int size_mbits = Memcard::MBIT_SIZE_MEMORY_CARD_2043) - : m_card_index(card_index), m_nintendo_card_id(size_mbits) + explicit MemoryCardBase(ExpansionInterface::Slot card_slot, + int size_mbits = Memcard::MBIT_SIZE_MEMORY_CARD_2043) + : m_card_slot(card_slot), m_nintendo_card_id(size_mbits) { } virtual ~MemoryCardBase() = default; @@ -25,6 +26,6 @@ public: u32 GetCardId() const { return m_nintendo_card_id; } protected: - int m_card_index; + ExpansionInterface::Slot m_card_slot; u16 m_nintendo_card_id; }; diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp index b907e0b698..b65d7f0d03 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.cpp @@ -152,7 +152,7 @@ std::vector GCMemcardDirectory::GetFileNamesForGameID(const std::st return filenames; } -GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, +GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, ExpansionInterface::Slot slot, const Memcard::HeaderData& header_data, u32 game_id) : MemoryCardBase(slot, header_data.m_size_mb), m_game_id(game_id), m_last_block(-1), m_hdr(header_data), m_bat1(header_data.m_size_mb), m_saves(0), m_save_directory(directory), @@ -240,7 +240,7 @@ void GCMemcardDirectory::FlushThread() return; } - Common::SetCurrentThreadName(fmt::format("Memcard {} flushing thread", m_card_index).c_str()); + Common::SetCurrentThreadName(fmt::format("Memcard {} flushing thread", m_card_slot).c_str()); constexpr std::chrono::seconds flush_interval{1}; while (true) @@ -705,11 +705,10 @@ void GCMemcardDirectory::DoState(PointerWrap& p) } } -void MigrateFromMemcardFile(const std::string& directory_name, int card_index) +void MigrateFromMemcardFile(const std::string& directory_name, ExpansionInterface::Slot card_slot) { File::CreateFullPath(directory_name); - std::string ini_memcard = (card_index == 0) ? Config::Get(Config::MAIN_MEMCARD_A_PATH) : - Config::Get(Config::MAIN_MEMCARD_B_PATH); + std::string ini_memcard = Config::Get(Config::GetInfoForMemcardPath(card_slot)); if (File::Exists(ini_memcard)) { auto [error_code, memcard] = Memcard::GCMemcard::Open(ini_memcard.c_str()); diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h index b146b9681f..fa9ae44110 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardDirectory.h @@ -15,13 +15,13 @@ // Uncomment this to write the system data of the memorycard from directory to disc //#define _WRITE_MC_HEADER 1 -void MigrateFromMemcardFile(const std::string& directory_name, int card_index); +void MigrateFromMemcardFile(const std::string& directory_name, ExpansionInterface::Slot card_slot); class GCMemcardDirectory : public MemoryCardBase { public: - GCMemcardDirectory(const std::string& directory, int slot, const Memcard::HeaderData& header_data, - u32 game_id); + GCMemcardDirectory(const std::string& directory, ExpansionInterface::Slot slot, + const Memcard::HeaderData& header_data, u32 game_id); ~GCMemcardDirectory(); GCMemcardDirectory(const GCMemcardDirectory&) = delete; diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp index 5a6d25324a..923f88bc69 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.cpp @@ -26,6 +26,7 @@ #include "Core/Config/SessionSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" +#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/GCMemcard/GCMemcard.h" #include "Core/HW/Sram.h" @@ -33,8 +34,9 @@ #define SIZE_TO_Mb (1024 * 8 * 16) #define MC_HDR_SIZE 0xA000 -MemoryCard::MemoryCard(const std::string& filename, int card_index, u16 size_mbits) - : MemoryCardBase(card_index, size_mbits), m_filename(filename) +MemoryCard::MemoryCard(const std::string& filename, ExpansionInterface::Slot card_slot, + u16 size_mbits) + : MemoryCardBase(card_slot, size_mbits), m_filename(filename) { File::IOFile file(m_filename, "rb"); if (file) @@ -88,13 +90,15 @@ MemoryCard::~MemoryCard() } } -void MemoryCard::CheckPath(std::string& memcardPath, const std::string& gameRegion, bool isSlotA) +void MemoryCard::CheckPath(std::string& memcardPath, const std::string& gameRegion, + ExpansionInterface::Slot card_slot) { + bool is_slot_a = card_slot == ExpansionInterface::Slot::A; std::string ext("." + gameRegion + ".raw"); if (memcardPath.empty()) { // Use default memcard path if there is no user defined name - std::string defaultFilename = isSlotA ? GC_MEMCARDA : GC_MEMCARDB; + std::string defaultFilename = is_slot_a ? GC_MEMCARDA : GC_MEMCARDB; memcardPath = File::GetUserPath(D_GCUSER_IDX) + defaultFilename + ext; } else @@ -118,7 +122,7 @@ void MemoryCard::CheckPath(std::string& memcardPath, const std::string& gameRegi "Slot {1} path was changed to\n" "{2}\n" "Would you like to copy the old file to this new location?\n", - isSlotA ? 'A' : 'B', isSlotA ? 'A' : 'B', filename)) + is_slot_a ? 'A' : 'B', is_slot_a ? 'A' : 'B', filename)) { if (!File::Copy(oldFilename, filename)) PanicAlertFmtT("Copy failed"); @@ -142,7 +146,7 @@ void MemoryCard::FlushThread() return; } - Common::SetCurrentThreadName(fmt::format("Memcard {} flushing thread", m_card_index).c_str()); + Common::SetCurrentThreadName(fmt::format("Memcard {} flushing thread", m_card_slot).c_str()); const auto flush_interval = std::chrono::seconds(15); @@ -199,9 +203,10 @@ void MemoryCard::FlushThread() if (do_exit) return; - Core::DisplayMessage( - fmt::format("Wrote memory card {} contents to {}", m_card_index ? 'B' : 'A', m_filename), - 4000); + Core::DisplayMessage(fmt::format("Wrote memory card {} contents to {}", + m_card_slot == ExpansionInterface::Slot::A ? 'A' : 'B', + m_filename), + 4000); } } @@ -265,7 +270,7 @@ void MemoryCard::ClearAll() void MemoryCard::DoState(PointerWrap& p) { - p.Do(m_card_index); + p.Do(m_card_slot); p.Do(m_memory_card_size); p.DoArray(&m_memcard_data[0], m_memory_card_size); } diff --git a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h index a8624b64eb..b8486c13a3 100644 --- a/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h +++ b/Source/Core/Core/HW/GCMemcard/GCMemcardRaw.h @@ -17,10 +17,11 @@ class PointerWrap; class MemoryCard : public MemoryCardBase { public: - MemoryCard(const std::string& filename, int card_index, + MemoryCard(const std::string& filename, ExpansionInterface::Slot card_slot, u16 size_mbits = Memcard::MBIT_SIZE_MEMORY_CARD_2043); ~MemoryCard(); - static void CheckPath(std::string& memcardPath, const std::string& gameRegion, bool isSlotA); + static void CheckPath(std::string& memcardPath, const std::string& gameRegion, + ExpansionInterface::Slot slot); void FlushThread(); void MakeDirty(); diff --git a/Source/Core/Core/HW/Sram.cpp b/Source/Core/Core/HW/Sram.cpp index 16d28c85a2..1f8b29116a 100644 --- a/Source/Core/Core/HW/Sram.cpp +++ b/Source/Core/Core/HW/Sram.cpp @@ -6,8 +6,11 @@ #include "Common/CommonTypes.h" #include "Common/IOFile.h" #include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" #include "Common/Swap.h" + #include "Core/ConfigManager.h" +#include "Core/HW/EXI/EXI.h" // English // This is just a template. Most/all fields are updated with sane(r) values at runtime. @@ -72,8 +75,22 @@ void InitSRAM() } } -void SetCardFlashID(const u8* buffer, u8 card_index) +void SetCardFlashID(const u8* buffer, ExpansionInterface::Slot card_slot) { + u8 card_index; + switch (card_slot) + { + case ExpansionInterface::Slot::A: + card_index = 0; + break; + case ExpansionInterface::Slot::B: + card_index = 1; + break; + default: + PanicAlertFmt("Invalid memcard slot {}", card_slot); + return; + } + u64 rand = Common::swap64(&buffer[12]); u8 csum = 0; for (int i = 0; i < 12; i++) diff --git a/Source/Core/Core/HW/Sram.h b/Source/Core/Core/HW/Sram.h index 7b99f17141..92262ca331 100644 --- a/Source/Core/Core/HW/Sram.h +++ b/Source/Core/Core/HW/Sram.h @@ -34,9 +34,15 @@ distribution. #pragma once #include + #include "Common/CommonTypes.h" #include "Common/Swap.h" +namespace ExpansionInterface +{ +enum class Slot : int; +}; + using CardFlashId = std::array; #pragma pack(push, 1) @@ -128,7 +134,7 @@ static_assert(sizeof(Sram) == 0x44); #pragma pack(pop) void InitSRAM(); -void SetCardFlashID(const u8* buffer, u8 card_index); +void SetCardFlashID(const u8* buffer, ExpansionInterface::Slot card_slot); void FixSRAMChecksums(); extern Sram g_SRAM; diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index 78f36941c9..9b1c83ff89 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -43,6 +43,7 @@ #include "Core/DSP/DSPCore.h" #include "Core/HW/CPU.h" #include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/EXI/EXI_DeviceMemoryCard.h" #include "Core/HW/ProcessorInterface.h" @@ -433,9 +434,17 @@ bool IsStartingFromClearSave() return s_bClearSave; } -bool IsUsingMemcard(int memcard) +bool IsUsingMemcard(ExpansionInterface::Slot slot) { - return (s_memcards & (1 << memcard)) != 0; + switch (slot) + { + case ExpansionInterface::Slot::A: + return (s_memcards & 1) != 0; + case ExpansionInterface::Slot::B: + return (s_memcards & 2) != 0; + default: + return false; + } } bool IsNetPlayRecording() @@ -1438,12 +1447,13 @@ void SetGraphicsConfig() // NOTE: EmuThread / Host Thread void GetSettings() { - const ExpansionInterface::TEXIDevices slot_a_type = Config::Get(Config::MAIN_SLOT_A); - const ExpansionInterface::TEXIDevices slot_b_type = Config::Get(Config::MAIN_SLOT_B); - const bool slot_a_has_raw_memcard = slot_a_type == ExpansionInterface::EXIDEVICE_MEMORYCARD; - const bool slot_a_has_gci_folder = slot_a_type == ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER; - const bool slot_b_has_raw_memcard = slot_b_type == ExpansionInterface::EXIDEVICE_MEMORYCARD; - const bool slot_b_has_gci_folder = slot_b_type == ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER; + using ExpansionInterface::EXIDeviceType; + const EXIDeviceType slot_a_type = Config::Get(Config::MAIN_SLOT_A); + const EXIDeviceType slot_b_type = Config::Get(Config::MAIN_SLOT_B); + const bool slot_a_has_raw_memcard = slot_a_type == EXIDeviceType::MemoryCard; + const bool slot_a_has_gci_folder = slot_a_type == EXIDeviceType::MemoryCardFolder; + const bool slot_b_has_raw_memcard = slot_b_type == EXIDeviceType::MemoryCard; + const bool slot_b_has_gci_folder = slot_b_type == EXIDeviceType::MemoryCardFolder; s_bSaveConfig = true; s_bNetPlay = NetPlay::IsNetPlayRunning(); @@ -1455,9 +1465,9 @@ void GetSettings() } else { - const auto gci_folder_has_saves = [](int card_index) { + const auto gci_folder_has_saves = [](ExpansionInterface::Slot card_slot) { const auto [path, migrate] = ExpansionInterface::CEXIMemoryCard::GetGCIFolderPath( - card_index, ExpansionInterface::AllowMovieFolder::No); + card_slot, ExpansionInterface::AllowMovieFolder::No); const u64 number_of_saves = File::ScanDirectoryTree(path, false).size; return number_of_saves > 0; }; @@ -1465,8 +1475,8 @@ void GetSettings() s_bClearSave = !(slot_a_has_raw_memcard && File::Exists(Config::Get(Config::MAIN_MEMCARD_A_PATH))) && !(slot_b_has_raw_memcard && File::Exists(Config::Get(Config::MAIN_MEMCARD_B_PATH))) && - !(slot_a_has_gci_folder && gci_folder_has_saves(0)) && - !(slot_b_has_gci_folder && gci_folder_has_saves(1)); + !(slot_a_has_gci_folder && gci_folder_has_saves(ExpansionInterface::Slot::A)) && + !(slot_b_has_gci_folder && gci_folder_has_saves(ExpansionInterface::Slot::B)); } s_memcards |= (slot_a_has_raw_memcard || slot_a_has_gci_folder) << 0; s_memcards |= (slot_b_has_raw_memcard || slot_b_has_gci_folder) << 1; diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h index be95ebca69..a774f7ad4e 100644 --- a/Source/Core/Core/Movie.h +++ b/Source/Core/Core/Movie.h @@ -17,6 +17,11 @@ struct BootParameters; struct GCPadStatus; class PointerWrap; +namespace ExpansionInterface +{ +enum class Slot : int; +} + namespace WiimoteCommon { class DataReportBuilder; @@ -166,7 +171,7 @@ void SetReset(bool reset); bool IsConfigSaved(); bool IsStartingFromClearSave(); -bool IsUsingMemcard(int memcard); +bool IsUsingMemcard(ExpansionInterface::Slot slot); void SetGraphicsConfig(); bool IsNetPlayRecording(); diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index dc2a56efdc..02bdcd7219 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -39,6 +39,7 @@ #include "Core/Config/SessionSettings.h" #include "Core/ConfigManager.h" #include "Core/GeckoCode.h" +#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #ifdef HAS_LIBMGBA #include "Core/HW/GBACore.h" @@ -818,8 +819,8 @@ void NetPlayClient::OnStartGame(sf::Packet& packet) packet >> m_net_settings.m_OCEnable; packet >> m_net_settings.m_OCFactor; - for (auto& device : m_net_settings.m_EXIDevice) - packet >> device; + for (auto slot : ExpansionInterface::SLOTS) + packet >> m_net_settings.m_EXIDevice[slot]; for (u32& value : m_net_settings.m_SYSCONFSettings) packet >> value; diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index 5c5db0da6b..ccb147b256 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -8,7 +8,9 @@ #include #include "Common/CommonTypes.h" +#include "Common/EnumMap.h" #include "Core/Config/SYSCONFSettings.h" +#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_Device.h" namespace DiscIO @@ -44,7 +46,7 @@ struct NetSettings bool m_CopyWiiSave = false; bool m_OCEnable = false; float m_OCFactor = 0; - std::array m_EXIDevice{}; + Common::EnumMap m_EXIDevice{}; std::array m_SYSCONFSettings{}; diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 6b00929373..73132a4c7d 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -40,6 +40,8 @@ #include "Core/ConfigManager.h" #include "Core/GeckoCode.h" #include "Core/GeckoCodeConfig.h" +#include "Core/HW/EXI/EXI.h" +#include "Core/HW/EXI/EXI_Device.h" #ifdef HAS_LIBMGBA #include "Core/HW/GBACore.h" #endif @@ -1306,10 +1308,21 @@ bool NetPlayServer::SetupNetSettings() settings.m_CopyWiiSave = Config::Get(Config::NETPLAY_LOAD_WII_SAVE); settings.m_OCEnable = Config::Get(Config::MAIN_OVERCLOCK_ENABLE); settings.m_OCFactor = Config::Get(Config::MAIN_OVERCLOCK); - settings.m_EXIDevice[0] = Config::Get(Config::MAIN_SLOT_A); - settings.m_EXIDevice[1] = Config::Get(Config::MAIN_SLOT_B); - // There's no way the BBA is going to sync, disable it - settings.m_EXIDevice[2] = ExpansionInterface::EXIDEVICE_NONE; + + for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS) + { + ExpansionInterface::EXIDeviceType device; + if (slot == ExpansionInterface::Slot::SP1) + { + // There's no way the BBA is going to sync, disable it + device = ExpansionInterface::EXIDeviceType::None; + } + else + { + device = Config::Get(Config::GetInfoForEXIDevice(slot)); + } + settings.m_EXIDevice[slot] = device; + } for (size_t i = 0; i < Config::SYSCONF_SETTINGS.size(); ++i) { @@ -1494,8 +1507,8 @@ bool NetPlayServer::StartGame() spac << m_settings.m_OCEnable; spac << m_settings.m_OCFactor; - for (auto& device : m_settings.m_EXIDevice) - spac << device; + for (auto slot : ExpansionInterface::SLOTS) + spac << static_cast(m_settings.m_EXIDevice[slot]); for (u32 value : m_settings.m_SYSCONFSettings) spac << value; @@ -1592,12 +1605,11 @@ bool NetPlayServer::SyncSaveData() u8 save_count = 0; - constexpr int exi_device_count = 2; - for (int i = 0; i < exi_device_count; ++i) + for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { - if (m_settings.m_EXIDevice[i] == ExpansionInterface::EXIDEVICE_MEMORYCARD || - Config::Get(Config::GetInfoForEXIDevice(i)) == - ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER) + if (m_settings.m_EXIDevice[slot] == ExpansionInterface::EXIDeviceType::MemoryCard || + Config::Get(Config::GetInfoForEXIDevice(slot)) == + ExpansionInterface::EXIDeviceType::MemoryCardFolder) { save_count++; } @@ -1652,16 +1664,15 @@ bool NetPlayServer::SyncSaveData() const std::string region = SConfig::GetDirectoryForRegion(SConfig::ToGameCubeRegion(game->GetRegion())); - for (int i = 0; i < exi_device_count; ++i) + for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { - const bool is_slot_a = i == 0; + const bool is_slot_a = slot == ExpansionInterface::Slot::A; - if (m_settings.m_EXIDevice[i] == ExpansionInterface::EXIDEVICE_MEMORYCARD) + if (m_settings.m_EXIDevice[slot] == ExpansionInterface::EXIDeviceType::MemoryCard) { - std::string path = is_slot_a ? Config::Get(Config::MAIN_MEMCARD_A_PATH) : - Config::Get(Config::MAIN_MEMCARD_B_PATH); + std::string path = Config::Get(Config::GetInfoForMemcardPath(slot)); - MemoryCard::CheckPath(path, region, is_slot_a); + MemoryCard::CheckPath(path, region, slot); int size_override; IniFile gameIni = SConfig::LoadGameIni(game->GetGameID(), game->GetRevision()); @@ -1693,8 +1704,8 @@ bool NetPlayServer::SyncSaveData() SendChunkedToClients(std::move(pac), 1, fmt::format("Memory Card {} Synchronization", is_slot_a ? 'A' : 'B')); } - else if (Config::Get(Config::GetInfoForEXIDevice(i)) == - ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER) + else if (Config::Get(Config::GetInfoForEXIDevice(slot)) == + ExpansionInterface::EXIDeviceType::MemoryCardFolder) { const std::string path = File::GetUserPath(D_GCUSER_IDX) + region + DIR_SEP + fmt::format("Card {}", is_slot_a ? 'A' : 'B'); diff --git a/Source/Core/DolphinQt/GCMemcardManager.cpp b/Source/Core/DolphinQt/GCMemcardManager.cpp index 56f0c8ec93..31a9d16dae 100644 --- a/Source/Core/DolphinQt/GCMemcardManager.cpp +++ b/Source/Core/DolphinQt/GCMemcardManager.cpp @@ -42,6 +42,8 @@ #include "DolphinQt/QtUtils/DolphinFileDialog.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" +using namespace ExpansionInterface; + constexpr int ROW_HEIGHT = 36; constexpr int COLUMN_WIDTH_FILENAME = 100; constexpr int COLUMN_WIDTH_BANNER = Memcard::MEMORY_CARD_BANNER_WIDTH + 6; @@ -55,6 +57,14 @@ constexpr int COLUMN_INDEX_ICON = 3; constexpr int COLUMN_INDEX_BLOCKS = 4; constexpr int COLUMN_COUNT = 5; +namespace +{ +Slot OtherSlot(Slot slot) +{ + return slot == Slot::A ? Slot::B : Slot::A; +} +}; // namespace + struct GCMemcardManager::IconAnimationData { // the individual frames @@ -70,7 +80,7 @@ GCMemcardManager::GCMemcardManager(QWidget* parent) : QDialog(parent) CreateWidgets(); ConnectWidgets(); - SetActiveSlot(0); + SetActiveSlot(Slot::A); UpdateActions(); m_timer = new QTimer(this); @@ -117,52 +127,54 @@ void GCMemcardManager::CreateWidgets() auto* layout = new QGridLayout; - for (int i = 0; i < SLOT_COUNT; i++) + for (Slot slot : MEMCARD_SLOTS) { - m_slot_group[i] = new QGroupBox(i == 0 ? tr("Slot A") : tr("Slot B")); - m_slot_file_edit[i] = new QLineEdit; - m_slot_open_button[i] = new QPushButton(tr("&Open...")); - m_slot_create_button[i] = new QPushButton(tr("&Create...")); - m_slot_table[i] = new QTableWidget; - m_slot_table[i]->setTabKeyNavigation(false); - m_slot_stat_label[i] = new QLabel; + m_slot_group[slot] = new QGroupBox(slot == Slot::A ? tr("Slot A") : tr("Slot B")); + m_slot_file_edit[slot] = new QLineEdit; + m_slot_open_button[slot] = new QPushButton(tr("&Open...")); + m_slot_create_button[slot] = new QPushButton(tr("&Create...")); + m_slot_table[slot] = new QTableWidget; + m_slot_table[slot]->setTabKeyNavigation(false); + m_slot_stat_label[slot] = new QLabel; - m_slot_table[i]->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_slot_table[i]->setSelectionBehavior(QAbstractItemView::SelectRows); - m_slot_table[i]->setSortingEnabled(true); - m_slot_table[i]->horizontalHeader()->setHighlightSections(false); - m_slot_table[i]->horizontalHeader()->setMinimumSectionSize(0); - m_slot_table[i]->horizontalHeader()->setSortIndicatorShown(true); - m_slot_table[i]->setColumnCount(COLUMN_COUNT); - m_slot_table[i]->setHorizontalHeaderItem(COLUMN_INDEX_FILENAME, - new QTableWidgetItem(tr("Filename"))); - m_slot_table[i]->setHorizontalHeaderItem(COLUMN_INDEX_BANNER, - new QTableWidgetItem(tr("Banner"))); - m_slot_table[i]->setHorizontalHeaderItem(COLUMN_INDEX_TEXT, new QTableWidgetItem(tr("Title"))); - m_slot_table[i]->setHorizontalHeaderItem(COLUMN_INDEX_ICON, new QTableWidgetItem(tr("Icon"))); - m_slot_table[i]->setHorizontalHeaderItem(COLUMN_INDEX_BLOCKS, - new QTableWidgetItem(tr("Blocks"))); - m_slot_table[i]->setColumnWidth(COLUMN_INDEX_FILENAME, COLUMN_WIDTH_FILENAME); - m_slot_table[i]->setColumnWidth(COLUMN_INDEX_BANNER, COLUMN_WIDTH_BANNER); - m_slot_table[i]->setColumnWidth(COLUMN_INDEX_TEXT, COLUMN_WIDTH_TEXT); - m_slot_table[i]->setColumnWidth(COLUMN_INDEX_ICON, COLUMN_WIDTH_ICON); - m_slot_table[i]->setColumnWidth(COLUMN_INDEX_BLOCKS, COLUMN_WIDTH_BLOCKS); - m_slot_table[i]->verticalHeader()->setDefaultSectionSize(ROW_HEIGHT); - m_slot_table[i]->verticalHeader()->hide(); - m_slot_table[i]->setShowGrid(false); + m_slot_table[slot]->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_slot_table[slot]->setSelectionBehavior(QAbstractItemView::SelectRows); + m_slot_table[slot]->setSortingEnabled(true); + m_slot_table[slot]->horizontalHeader()->setHighlightSections(false); + m_slot_table[slot]->horizontalHeader()->setMinimumSectionSize(0); + m_slot_table[slot]->horizontalHeader()->setSortIndicatorShown(true); + m_slot_table[slot]->setColumnCount(COLUMN_COUNT); + m_slot_table[slot]->setHorizontalHeaderItem(COLUMN_INDEX_FILENAME, + new QTableWidgetItem(tr("Filename"))); + m_slot_table[slot]->setHorizontalHeaderItem(COLUMN_INDEX_BANNER, + new QTableWidgetItem(tr("Banner"))); + m_slot_table[slot]->setHorizontalHeaderItem(COLUMN_INDEX_TEXT, + new QTableWidgetItem(tr("Title"))); + m_slot_table[slot]->setHorizontalHeaderItem(COLUMN_INDEX_ICON, + new QTableWidgetItem(tr("Icon"))); + m_slot_table[slot]->setHorizontalHeaderItem(COLUMN_INDEX_BLOCKS, + new QTableWidgetItem(tr("Blocks"))); + m_slot_table[slot]->setColumnWidth(COLUMN_INDEX_FILENAME, COLUMN_WIDTH_FILENAME); + m_slot_table[slot]->setColumnWidth(COLUMN_INDEX_BANNER, COLUMN_WIDTH_BANNER); + m_slot_table[slot]->setColumnWidth(COLUMN_INDEX_TEXT, COLUMN_WIDTH_TEXT); + m_slot_table[slot]->setColumnWidth(COLUMN_INDEX_ICON, COLUMN_WIDTH_ICON); + m_slot_table[slot]->setColumnWidth(COLUMN_INDEX_BLOCKS, COLUMN_WIDTH_BLOCKS); + m_slot_table[slot]->verticalHeader()->setDefaultSectionSize(ROW_HEIGHT); + m_slot_table[slot]->verticalHeader()->hide(); + m_slot_table[slot]->setShowGrid(false); auto* slot_layout = new QGridLayout; - m_slot_group[i]->setLayout(slot_layout); + m_slot_group[slot]->setLayout(slot_layout); - slot_layout->addWidget(m_slot_file_edit[i], 0, 0); - slot_layout->addWidget(m_slot_open_button[i], 0, 1); - slot_layout->addWidget(m_slot_create_button[i], 0, 2); - slot_layout->addWidget(m_slot_table[i], 1, 0, 1, 3); - slot_layout->addWidget(m_slot_stat_label[i], 2, 0); + slot_layout->addWidget(m_slot_file_edit[slot], 0, 0); + slot_layout->addWidget(m_slot_open_button[slot], 0, 1); + slot_layout->addWidget(m_slot_create_button[slot], 0, 2); + slot_layout->addWidget(m_slot_table[slot], 1, 0, 1, 3); + slot_layout->addWidget(m_slot_stat_label[slot], 2, 0); - layout->addWidget(m_slot_group[i], 0, i * 2, 8, 1); + layout->addWidget(m_slot_group[slot], 0, slot == Slot::A ? 0 : 2, 8, 1); - UpdateSlotTable(i); + UpdateSlotTable(slot); } layout->addWidget(m_select_button, 1, 1); @@ -179,7 +191,8 @@ void GCMemcardManager::CreateWidgets() void GCMemcardManager::ConnectWidgets() { connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(m_select_button, &QPushButton::clicked, [this] { SetActiveSlot(!m_active_slot); }); + connect(m_select_button, &QPushButton::clicked, + [this] { SetActiveSlot(OtherSlot(m_active_slot)); }); connect(m_export_gci_action, &QAction::triggered, [this] { ExportFiles(Memcard::SavefileFormat::GCI); }); connect(m_export_gcs_action, &QAction::triggered, @@ -191,7 +204,7 @@ void GCMemcardManager::ConnectWidgets() connect(m_copy_button, &QPushButton::clicked, this, &GCMemcardManager::CopyFiles); connect(m_fix_checksums_button, &QPushButton::clicked, this, &GCMemcardManager::FixChecksums); - for (int slot = 0; slot < SLOT_COUNT; slot++) + for (Slot slot : MEMCARD_SLOTS) { connect(m_slot_file_edit[slot], &QLineEdit::textChanged, [this, slot](const QString& path) { SetSlotFile(slot, path); }); @@ -206,27 +219,26 @@ void GCMemcardManager::ConnectWidgets() void GCMemcardManager::LoadDefaultMemcards() { - for (int i = 0; i < SLOT_COUNT; i++) + for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { - if (Config::Get(i == 0 ? Config::MAIN_SLOT_A : Config::MAIN_SLOT_B) != - ExpansionInterface::EXIDEVICE_MEMORYCARD) + if (Config::Get(Config::GetInfoForEXIDevice(slot)) != + ExpansionInterface::EXIDeviceType::MemoryCard) { continue; } - const QString path = QString::fromStdString( - Config::Get(i == 0 ? Config::MAIN_MEMCARD_A_PATH : Config::MAIN_MEMCARD_B_PATH)); - SetSlotFile(i, path); + const QString path = QString::fromStdString(Config::Get(Config::GetInfoForMemcardPath(slot))); + SetSlotFile(slot, path); } } -void GCMemcardManager::SetActiveSlot(int slot) +void GCMemcardManager::SetActiveSlot(Slot slot) { - for (int i = 0; i < SLOT_COUNT; i++) - m_slot_table[i]->setEnabled(i == slot); + for (Slot slot2 : MEMCARD_SLOTS) + m_slot_table[slot2]->setEnabled(slot2 == slot); - m_select_button->setText(slot == 0 ? tr("Switch to B") : tr("Switch to A")); - m_copy_button->setText(slot == 0 ? tr("Copy to B") : tr("Copy to A")); + m_select_button->setText(slot == Slot::A ? tr("Switch to B") : tr("Switch to A")); + m_copy_button->setText(slot == Slot::A ? tr("Copy to B") : tr("Copy to A")); m_active_slot = slot; @@ -234,7 +246,7 @@ void GCMemcardManager::SetActiveSlot(int slot) UpdateActions(); } -void GCMemcardManager::UpdateSlotTable(int slot) +void GCMemcardManager::UpdateSlotTable(Slot slot) { m_slot_active_icons[slot].clear(); @@ -307,7 +319,7 @@ void GCMemcardManager::UpdateActions() auto selection = m_slot_table[m_active_slot]->selectedItems(); bool have_selection = selection.count(); bool have_memcard = m_slot_memcard[m_active_slot] != nullptr; - bool have_memcard_other = m_slot_memcard[!m_active_slot] != nullptr; + bool have_memcard_other = m_slot_memcard[OtherSlot(m_active_slot)] != nullptr; m_copy_button->setEnabled(have_selection && have_memcard_other); m_export_button->setEnabled(have_selection); @@ -316,7 +328,7 @@ void GCMemcardManager::UpdateActions() m_fix_checksums_button->setEnabled(have_memcard); } -void GCMemcardManager::SetSlotFile(int slot, QString path) +void GCMemcardManager::SetSlotFile(Slot slot, QString path) { auto [error_code, memcard] = Memcard::GCMemcard::Open(path.toStdString()); @@ -337,14 +349,15 @@ void GCMemcardManager::SetSlotFile(int slot, QString path) UpdateActions(); } -void GCMemcardManager::SetSlotFileInteractive(int slot) +void GCMemcardManager::SetSlotFileInteractive(Slot slot) { - QString path = QDir::toNativeSeparators(DolphinFileDialog::getOpenFileName( - this, - slot == 0 ? tr("Set memory card file for Slot A") : tr("Set memory card file for Slot B"), - QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)), - QStringLiteral("%1 (*.raw *.gcp);;%2 (*)") - .arg(tr("GameCube Memory Cards"), tr("All Files")))); + QString path = QDir::toNativeSeparators( + DolphinFileDialog::getOpenFileName(this, + slot == Slot::A ? tr("Set memory card file for Slot A") : + tr("Set memory card file for Slot B"), + QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)), + QStringLiteral("%1 (*.raw *.gcp);;%2 (*)") + .arg(tr("GameCube Memory Cards"), tr("All Files")))); if (!path.isEmpty()) m_slot_file_edit[slot]->setText(path); } @@ -487,7 +500,7 @@ void GCMemcardManager::ExportFiles(Memcard::SavefileFormat format) } } -void GCMemcardManager::ImportFiles(int slot, const std::vector& savefiles) +void GCMemcardManager::ImportFiles(Slot slot, const std::vector& savefiles) { auto& card = m_slot_memcard[slot]; if (!card) @@ -611,7 +624,7 @@ void GCMemcardManager::CopyFiles() if (!source_card) return; - auto& target_card = m_slot_memcard[!m_active_slot]; + auto& target_card = m_slot_memcard[OtherSlot(m_active_slot)]; if (!target_card) return; @@ -627,7 +640,7 @@ void GCMemcardManager::CopyFiles() return; } - ImportFiles(!m_active_slot, savefiles); + ImportFiles(OtherSlot(m_active_slot), savefiles); } void GCMemcardManager::DeleteFiles() @@ -677,7 +690,7 @@ void GCMemcardManager::FixChecksums() } } -void GCMemcardManager::CreateNewCard(int slot) +void GCMemcardManager::CreateNewCard(Slot slot) { GCMemcardCreateNewDialog dialog(this); if (dialog.exec() == QDialog::Accepted) @@ -687,7 +700,7 @@ void GCMemcardManager::CreateNewCard(int slot) void GCMemcardManager::DrawIcons() { const int column = COLUMN_INDEX_ICON; - for (int slot = 0; slot < SLOT_COUNT; slot++) + for (Slot slot : MEMCARD_SLOTS) { QTableWidget* table = m_slot_table[slot]; const int row_count = table->rowCount(); @@ -737,7 +750,7 @@ void GCMemcardManager::DrawIcons() ++m_current_frame; } -QPixmap GCMemcardManager::GetBannerFromSaveFile(int file_index, int slot) +QPixmap GCMemcardManager::GetBannerFromSaveFile(int file_index, Slot slot) { auto& memcard = m_slot_memcard[slot]; @@ -753,7 +766,7 @@ QPixmap GCMemcardManager::GetBannerFromSaveFile(int file_index, int slot) return QPixmap::fromImage(image); } -GCMemcardManager::IconAnimationData GCMemcardManager::GetIconFromSaveFile(int file_index, int slot) +GCMemcardManager::IconAnimationData GCMemcardManager::GetIconFromSaveFile(int file_index, Slot slot) { auto& memcard = m_slot_memcard[slot]; diff --git a/Source/Core/DolphinQt/GCMemcardManager.h b/Source/Core/DolphinQt/GCMemcardManager.h index 4baff2b9f3..838a7750d7 100644 --- a/Source/Core/DolphinQt/GCMemcardManager.h +++ b/Source/Core/DolphinQt/GCMemcardManager.h @@ -12,6 +12,8 @@ #include #include "Common/CommonTypes.h" +#include "Common/EnumMap.h" +#include "Core/HW/EXI/EXI.h" namespace Memcard { @@ -53,26 +55,26 @@ private: void LoadDefaultMemcards(); void UpdateActions(); - void UpdateSlotTable(int slot); - void SetSlotFile(int slot, QString path); - void SetSlotFileInteractive(int slot); - void SetActiveSlot(int slot); + void UpdateSlotTable(ExpansionInterface::Slot slot); + void SetSlotFile(ExpansionInterface::Slot slot, QString path); + void SetSlotFileInteractive(ExpansionInterface::Slot slot); + void SetActiveSlot(ExpansionInterface::Slot slot); std::vector GetSelectedFileIndices(); - void ImportFiles(int slot, const std::vector& savefiles); + void ImportFiles(ExpansionInterface::Slot slot, const std::vector& savefiles); void CopyFiles(); void ImportFile(); void DeleteFiles(); void ExportFiles(Memcard::SavefileFormat format); void FixChecksums(); - void CreateNewCard(int slot); + void CreateNewCard(ExpansionInterface::Slot slot); void DrawIcons(); - QPixmap GetBannerFromSaveFile(int file_index, int slot); + QPixmap GetBannerFromSaveFile(int file_index, ExpansionInterface::Slot slot); - IconAnimationData GetIconFromSaveFile(int file_index, int slot); + IconAnimationData GetIconFromSaveFile(int file_index, ExpansionInterface::Slot slot); // Actions QPushButton* m_select_button; @@ -87,17 +89,18 @@ private: QPushButton* m_fix_checksums_button; // Slots - static constexpr int SLOT_COUNT = 2; - std::array, SLOT_COUNT> m_slot_active_icons; - std::array, SLOT_COUNT> m_slot_memcard; - std::array m_slot_group; - std::array m_slot_file_edit; - std::array m_slot_open_button; - std::array m_slot_create_button; - std::array m_slot_table; - std::array m_slot_stat_label; + Common::EnumMap, ExpansionInterface::MAX_MEMCARD_SLOT> + m_slot_active_icons; + Common::EnumMap, ExpansionInterface::MAX_MEMCARD_SLOT> + m_slot_memcard; + Common::EnumMap m_slot_group; + Common::EnumMap m_slot_file_edit; + Common::EnumMap m_slot_open_button; + Common::EnumMap m_slot_create_button; + Common::EnumMap m_slot_table; + Common::EnumMap m_slot_stat_label; - int m_active_slot; + ExpansionInterface::Slot m_active_slot; u64 m_current_frame = 0; QDialogButtonBox* m_button_box; diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp index 2fd137055f..cc5b1ebb43 100644 --- a/Source/Core/DolphinQt/GameList/GameList.cpp +++ b/Source/Core/DolphinQt/GameList/GameList.cpp @@ -47,6 +47,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_Device.h" #include "Core/HW/WiiSave.h" #include "Core/WiiUtils.h" @@ -661,21 +662,22 @@ void GameList::OpenGCSaveFolder() bool found = false; - for (int i = 0; i < 2; i++) + using ExpansionInterface::Slot; + + for (Slot slot : ExpansionInterface::MEMCARD_SLOTS) { QUrl url; - const ExpansionInterface::TEXIDevices current_exi_device = - Config::Get(Config::GetInfoForEXIDevice(i)); + const ExpansionInterface::EXIDeviceType current_exi_device = + Config::Get(Config::GetInfoForEXIDevice(slot)); switch (current_exi_device) { - case ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER: + case ExpansionInterface::EXIDeviceType::MemoryCardFolder: { std::string path = StringFromFormat("%s/%s/%s", File::GetUserPath(D_GCUSER_IDX).c_str(), SConfig::GetDirectoryForRegion(game->GetRegion()), - i == 0 ? "Card A" : "Card B"); + slot == Slot::A ? "Card A" : "Card B"); - std::string override_path = i == 0 ? Config::Get(Config::MAIN_GCI_FOLDER_A_PATH_OVERRIDE) : - Config::Get(Config::MAIN_GCI_FOLDER_B_PATH_OVERRIDE); + std::string override_path = Config::Get(Config::GetInfoForGCIPathOverride(slot)); if (!override_path.empty()) path = override_path; @@ -691,10 +693,9 @@ void GameList::OpenGCSaveFolder() } break; } - case ExpansionInterface::EXIDEVICE_MEMORYCARD: + case ExpansionInterface::EXIDeviceType::MemoryCard: { - std::string memcard_path = i == 0 ? Config::Get(Config::MAIN_MEMCARD_A_PATH) : - Config::Get(Config::MAIN_MEMCARD_B_PATH); + std::string memcard_path = Config::Get(Config::GetInfoForMemcardPath(slot)); std::string memcard_dir; diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index 77b3b86b0c..6a8c813d17 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -16,8 +16,10 @@ #include #include +#include #include +#include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/Config/Config.h" #include "Common/FileUtil.h" @@ -54,6 +56,8 @@ GameCubePane::GameCubePane() void GameCubePane::CreateWidgets() { + using ExpansionInterface::EXIDeviceType; + QVBoxLayout* layout = new QVBoxLayout(this); // IPL Settings @@ -86,52 +90,49 @@ void GameCubePane::CreateWidgets() QGridLayout* device_layout = new QGridLayout(device_box); device_box->setLayout(device_layout); - for (int i = 0; i < SLOT_COUNT; i++) + for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS) { - m_slot_combos[i] = new QComboBox(device_box); - m_slot_combos[i]->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - m_slot_buttons[i] = new QPushButton(tr("..."), device_box); - m_slot_buttons[i]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_slot_combos[slot] = new QComboBox(device_box); + m_slot_combos[slot]->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + m_slot_buttons[slot] = new QPushButton(tr("..."), device_box); + m_slot_buttons[slot]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } // Add slot devices - for (const auto& entry : - {std::make_pair(tr(""), ExpansionInterface::EXIDEVICE_NONE), - std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY), - std::make_pair(tr("Memory Card"), ExpansionInterface::EXIDEVICE_MEMORYCARD), - std::make_pair(tr("GCI Folder"), ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER), - std::make_pair(tr("USB Gecko"), ExpansionInterface::EXIDEVICE_GECKO), - std::make_pair(tr("Advance Game Port"), ExpansionInterface::EXIDEVICE_AGP), - std::make_pair(tr("Microphone"), ExpansionInterface::EXIDEVICE_MIC)}) + for (const auto device : {EXIDeviceType::None, EXIDeviceType::Dummy, EXIDeviceType::MemoryCard, + EXIDeviceType::MemoryCardFolder, EXIDeviceType::Gecko, + EXIDeviceType::AGP, EXIDeviceType::Microphone}) { - m_slot_combos[0]->addItem(entry.first, entry.second); - m_slot_combos[1]->addItem(entry.first, entry.second); + const QString name = tr(fmt::format("{:n}", device).c_str()); + const int value = static_cast(device); + m_slot_combos[ExpansionInterface::Slot::A]->addItem(name, value); + m_slot_combos[ExpansionInterface::Slot::B]->addItem(name, value); } // Add SP1 devices - std::vector> sp1Entries{ - std::make_pair(tr(""), ExpansionInterface::EXIDEVICE_NONE), - std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY), - std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH), - std::make_pair(tr("Broadband Adapter (XLink Kai)"), ExpansionInterface::EXIDEVICE_ETHXLINK)}; -#if defined(__APPLE__) - sp1Entries.emplace_back(std::make_pair(tr("Broadband Adapter (tapserver)"), - ExpansionInterface::EXIDEVICE_ETHTAPSERVER)); + for (const auto device : { + EXIDeviceType::None, + EXIDeviceType::Dummy, + EXIDeviceType::Ethernet, + EXIDeviceType::EthernetXLink, +#ifdef __APPLE__ + EXIDeviceType::EthernetTapServer, #endif - for (const auto& entry : sp1Entries) + }) { - m_slot_combos[2]->addItem(entry.first, entry.second); + m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()), + static_cast(device)); } device_layout->addWidget(new QLabel(tr("Slot A:")), 0, 0); - device_layout->addWidget(m_slot_combos[0], 0, 1); - device_layout->addWidget(m_slot_buttons[0], 0, 2); + device_layout->addWidget(m_slot_combos[ExpansionInterface::Slot::A], 0, 1); + device_layout->addWidget(m_slot_buttons[ExpansionInterface::Slot::A], 0, 2); device_layout->addWidget(new QLabel(tr("Slot B:")), 1, 0); - device_layout->addWidget(m_slot_combos[1], 1, 1); - device_layout->addWidget(m_slot_buttons[1], 1, 2); + device_layout->addWidget(m_slot_combos[ExpansionInterface::Slot::B], 1, 1); + device_layout->addWidget(m_slot_buttons[ExpansionInterface::Slot::B], 1, 2); device_layout->addWidget(new QLabel(tr("SP1:")), 2, 0); - device_layout->addWidget(m_slot_combos[2], 2, 1); - device_layout->addWidget(m_slot_buttons[2], 2, 2); + device_layout->addWidget(m_slot_combos[ExpansionInterface::Slot::SP1], 2, 1); + device_layout->addWidget(m_slot_buttons[ExpansionInterface::Slot::SP1], 2, 2); #ifdef HAS_LIBMGBA // GBA Settings @@ -192,13 +193,13 @@ void GameCubePane::ConnectWidgets() &GameCubePane::SaveSettings); // Device Settings - for (int i = 0; i < SLOT_COUNT; i++) + for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS) { - connect(m_slot_combos[i], qOverload(&QComboBox::currentIndexChanged), this, - [this, i] { UpdateButton(i); }); - connect(m_slot_combos[i], qOverload(&QComboBox::currentIndexChanged), this, + connect(m_slot_combos[slot], qOverload(&QComboBox::currentIndexChanged), this, + [this, slot] { UpdateButton(slot); }); + connect(m_slot_combos[slot], qOverload(&QComboBox::currentIndexChanged), this, &GameCubePane::SaveSettings); - connect(m_slot_buttons[i], &QPushButton::clicked, [this, i] { OnConfigPressed(i); }); + connect(m_slot_buttons[slot], &QPushButton::clicked, [this, slot] { OnConfigPressed(slot); }); } #ifdef HAS_LIBMGBA @@ -240,62 +241,69 @@ void GameCubePane::OnEmulationStateChanged() #endif } -void GameCubePane::UpdateButton(int slot) +void GameCubePane::UpdateButton(ExpansionInterface::Slot slot) { - const auto value = m_slot_combos[slot]->currentData().toInt(); + const auto device = + static_cast(m_slot_combos[slot]->currentData().toInt()); bool has_config = false; switch (slot) { - case SLOT_A_INDEX: - case SLOT_B_INDEX: - has_config = - (value == ExpansionInterface::EXIDEVICE_MEMORYCARD || - value == ExpansionInterface::EXIDEVICE_AGP || value == ExpansionInterface::EXIDEVICE_MIC); + case ExpansionInterface::Slot::A: + case ExpansionInterface::Slot::B: + has_config = (device == ExpansionInterface::EXIDeviceType::MemoryCard || + device == ExpansionInterface::EXIDeviceType::AGP || + device == ExpansionInterface::EXIDeviceType::Microphone); break; - case SLOT_SP1_INDEX: - has_config = (value == ExpansionInterface::EXIDEVICE_ETH || - value == ExpansionInterface::EXIDEVICE_ETHXLINK); + case ExpansionInterface::Slot::SP1: + has_config = (device == ExpansionInterface::EXIDeviceType::Ethernet || + device == ExpansionInterface::EXIDeviceType::EthernetXLink); break; } m_slot_buttons[slot]->setEnabled(has_config); } -void GameCubePane::OnConfigPressed(int slot) +void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot) { - QString filter; - bool memcard = false; + const ExpansionInterface::EXIDeviceType device = + static_cast(m_slot_combos[slot]->currentData().toInt()); - switch (m_slot_combos[slot]->currentData().toInt()) + switch (device) { - case ExpansionInterface::EXIDEVICE_MEMORYCARD: - filter = tr("GameCube Memory Cards (*.raw *.gcp)"); - memcard = true; - break; - case ExpansionInterface::EXIDEVICE_AGP: - filter = tr("Game Boy Advance Carts (*.gba)"); - break; - case ExpansionInterface::EXIDEVICE_MIC: - MappingWindow(this, MappingWindow::Type::MAPPING_GC_MICROPHONE, slot).exec(); + case ExpansionInterface::EXIDeviceType::MemoryCard: + BrowseMemcard(slot); return; - case ExpansionInterface::EXIDEVICE_ETH: + case ExpansionInterface::EXIDeviceType::AGP: + BrowseAGPRom(slot); + return; + case ExpansionInterface::EXIDeviceType::Microphone: + // TODO: convert MappingWindow to use Slot? + MappingWindow(this, MappingWindow::Type::MAPPING_GC_MICROPHONE, static_cast(slot)).exec(); + return; + case ExpansionInterface::EXIDeviceType::Ethernet: { BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::Ethernet).exec(); return; } - case ExpansionInterface::EXIDEVICE_ETHXLINK: + case ExpansionInterface::EXIDeviceType::EthernetXLink: { BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::XLinkKai).exec(); return; } default: - qFatal("unknown settings pressed"); + PanicAlertFmt("Unknown settings pressed for {}", device); + return; } +} + +void GameCubePane::BrowseMemcard(ExpansionInterface::Slot slot) +{ + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); QString filename = DolphinFileDialog::getSaveFileName( this, tr("Choose a file to open"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)), - filter, 0, QFileDialog::DontConfirmOverwrite); + tr("GameCube Memory Cards (*.raw *.gcp)"), 0, QFileDialog::DontConfirmOverwrite); if (filename.isEmpty()) return; @@ -303,90 +311,85 @@ void GameCubePane::OnConfigPressed(int slot) QString path_abs = QFileInfo(filename).absoluteFilePath(); // Memcard validity checks - if (memcard) + if (File::Exists(filename.toStdString())) { - if (File::Exists(filename.toStdString())) - { - auto [error_code, mc] = Memcard::GCMemcard::Open(filename.toStdString()); + auto [error_code, mc] = Memcard::GCMemcard::Open(filename.toStdString()); - if (error_code.HasCriticalErrors() || !mc || !mc->IsValid()) + if (error_code.HasCriticalErrors() || !mc || !mc->IsValid()) + { + ModalMessageBox::critical( + this, tr("Error"), + tr("The file\n%1\nis either corrupted or not a GameCube memory card file.\n%2") + .arg(filename) + .arg(GCMemcardManager::GetErrorMessagesForErrorCode(error_code))); + return; + } + } + + for (ExpansionInterface::Slot other_slot : ExpansionInterface::MEMCARD_SLOTS) + { + if (other_slot == slot) + continue; + + bool other_slot_memcard = m_slot_combos[other_slot]->currentData().toInt() == + static_cast(ExpansionInterface::EXIDeviceType::MemoryCard); + if (other_slot_memcard) + { + QString path_other = + QFileInfo(QString::fromStdString(Config::Get(Config::GetInfoForMemcardPath(other_slot)))) + .absoluteFilePath(); + + if (path_abs == path_other) { ModalMessageBox::critical( this, tr("Error"), - tr("The file\n%1\nis either corrupted or not a GameCube memory card file.\n%2") - .arg(filename) - .arg(GCMemcardManager::GetErrorMessagesForErrorCode(error_code))); - return; - } - } - - bool other_slot_memcard = - m_slot_combos[slot == SLOT_A_INDEX ? SLOT_B_INDEX : SLOT_A_INDEX]->currentData().toInt() == - ExpansionInterface::EXIDEVICE_MEMORYCARD; - if (other_slot_memcard) - { - QString path_b = - QFileInfo(QString::fromStdString(slot == 0 ? Config::Get(Config::MAIN_MEMCARD_B_PATH) : - Config::Get(Config::MAIN_MEMCARD_A_PATH))) - .absoluteFilePath(); - - if (path_abs == path_b) - { - ModalMessageBox::critical(this, tr("Error"), - tr("The same file can't be used in both slots.")); + tr("The same file can't be used in multiple slots; it is already used by %1.") + .arg(QString::fromStdString(fmt::to_string(other_slot)))); return; } } } - QString path_old; - if (memcard) - { - path_old = - QFileInfo(QString::fromStdString(slot == 0 ? Config::Get(Config::MAIN_MEMCARD_A_PATH) : - Config::Get(Config::MAIN_MEMCARD_B_PATH))) - .absoluteFilePath(); - } - else - { - path_old = - QFileInfo(QString::fromStdString(slot == 0 ? Config::Get(Config::MAIN_AGP_CART_A_PATH) : - Config::Get(Config::MAIN_AGP_CART_B_PATH))) - .absoluteFilePath(); - } + QString path_old = + QFileInfo(QString::fromStdString(Config::Get(Config::GetInfoForMemcardPath(slot)))) + .absoluteFilePath(); - if (memcard) - { - if (slot == SLOT_A_INDEX) - { - Config::SetBase(Config::MAIN_MEMCARD_A_PATH, path_abs.toStdString()); - } - else - { - Config::SetBase(Config::MAIN_MEMCARD_B_PATH, path_abs.toStdString()); - } - } - else - { - if (slot == SLOT_A_INDEX) - { - Config::SetBase(Config::MAIN_AGP_CART_A_PATH, path_abs.toStdString()); - } - else - { - Config::SetBase(Config::MAIN_AGP_CART_B_PATH, path_abs.toStdString()); - } - } + Config::SetBase(Config::GetInfoForMemcardPath(slot), path_abs.toStdString()); if (Core::IsRunning() && path_abs != path_old) { - ExpansionInterface::ChangeDevice( - // SlotB is on channel 1, slotA and SP1 are on 0 - slot, - // The device enum to change to - memcard ? ExpansionInterface::EXIDEVICE_MEMORYCARD : ExpansionInterface::EXIDEVICE_AGP, - // SP1 is device 2, slots are device 0 - 0); + // ChangeDevice unplugs the device for 1 second, which means that games should notice that + // the path has changed and thus the memory card contents have changed + ExpansionInterface::ChangeDevice(slot, ExpansionInterface::EXIDeviceType::MemoryCard); + } +} + +void GameCubePane::BrowseAGPRom(ExpansionInterface::Slot slot) +{ + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); + + QString filename = DolphinFileDialog::getSaveFileName( + this, tr("Choose a file to open"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)), + tr("Game Boy Advance Carts (*.gba)"), 0, QFileDialog::DontConfirmOverwrite); + + if (filename.isEmpty()) + return; + + QString path_abs = QFileInfo(filename).absoluteFilePath(); + + QString path_old = + QFileInfo(QString::fromStdString(Config::Get(Config::GetInfoForAGPCartPath(slot)))) + .absoluteFilePath(); + + Config::SetBase(Config::GetInfoForAGPCartPath(slot), path_abs.toStdString()); + + if (Core::IsRunning() && path_abs != path_old) + { + // ChangeDevice unplugs the device for 1 second. For an actual AGP, you can remove the + // cartridge without unplugging it, and it's not clear if the AGP software actually notices + // that it's been unplugged or the cartridge has changed, but this was done for memcards so + // we might as well do it for the AGP too. + ExpansionInterface::ChangeDevice(slot, ExpansionInterface::EXIDeviceType::AGP); } } @@ -455,12 +458,14 @@ void GameCubePane::LoadSettings() m_skip_main_menu->setToolTip(have_menu ? QString{} : tr("Put IPL ROMs in User/GC/.")); // Device Settings - for (int i = 0; i < SLOT_COUNT; i++) + for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS) { - QSignalBlocker blocker(m_slot_combos[i]); - const ExpansionInterface::TEXIDevices exi_device = Config::Get(Config::GetInfoForEXIDevice(i)); - m_slot_combos[i]->setCurrentIndex(m_slot_combos[i]->findData(exi_device)); - UpdateButton(i); + QSignalBlocker blocker(m_slot_combos[slot]); + const ExpansionInterface::EXIDeviceType exi_device = + Config::Get(Config::GetInfoForEXIDevice(slot)); + m_slot_combos[slot]->setCurrentIndex( + m_slot_combos[slot]->findData(static_cast(exi_device))); + UpdateButton(slot); } #ifdef HAS_LIBMGBA @@ -483,24 +488,19 @@ void GameCubePane::SaveSettings() Config::SetBaseOrCurrent(Config::MAIN_GC_LANGUAGE, m_language_combo->currentData().toInt()); // Device Settings - for (int i = 0; i < SLOT_COUNT; i++) + for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS) { - const auto dev = ExpansionInterface::TEXIDevices(m_slot_combos[i]->currentData().toInt()); - const ExpansionInterface::TEXIDevices current_exi_device = - Config::Get(Config::GetInfoForEXIDevice(i)); + const auto dev = + static_cast(m_slot_combos[slot]->currentData().toInt()); + const ExpansionInterface::EXIDeviceType current_exi_device = + Config::Get(Config::GetInfoForEXIDevice(slot)); if (Core::IsRunning() && current_exi_device != dev) { - ExpansionInterface::ChangeDevice( - // SlotB is on channel 1, slotA and SP1 are on 0 - (i == 1) ? 1 : 0, - // The device enum to change to - dev, - // SP1 is device 2, slots are device 0 - (i == 2) ? 2 : 0); + ExpansionInterface::ChangeDevice(slot, dev); } - Config::SetBaseOrCurrent(Config::GetInfoForEXIDevice(i), dev); + Config::SetBaseOrCurrent(Config::GetInfoForEXIDevice(slot), dev); } #ifdef HAS_LIBMGBA diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.h b/Source/Core/DolphinQt/Settings/GameCubePane.h index ea945cfc1d..2bffc02bde 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.h +++ b/Source/Core/DolphinQt/Settings/GameCubePane.h @@ -9,6 +9,9 @@ #include +#include "Common/EnumMap.h" +#include "Core/HW/EXI/EXI.h" + class QCheckBox; class QComboBox; class QLineEdit; @@ -31,9 +34,11 @@ private: void OnEmulationStateChanged(); - void UpdateButton(int slot); - void OnConfigPressed(int slot); + void UpdateButton(ExpansionInterface::Slot slot); + void OnConfigPressed(ExpansionInterface::Slot slot); + void BrowseMemcard(ExpansionInterface::Slot slot); + void BrowseAGPRom(ExpansionInterface::Slot slot); void BrowseGBABios(); void BrowseGBARom(size_t index); void SaveRomPathChanged(); @@ -42,8 +47,8 @@ private: QCheckBox* m_skip_main_menu; QComboBox* m_language_combo; - QPushButton* m_slot_buttons[3]; - QComboBox* m_slot_combos[3]; + Common::EnumMap m_slot_buttons; + Common::EnumMap m_slot_combos; QCheckBox* m_gba_threads; QCheckBox* m_gba_save_rom_path;