From 2132e005c32e137f8723abb68b314f57e02eb2e7 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Wed, 19 Jan 2022 03:05:05 +0100 Subject: [PATCH 1/4] Core: Add config setting for base GCI folder path. --- Source/Core/Core/Config/MainSettings.cpp | 71 ++++++++++ Source/Core/Core/Config/MainSettings.h | 7 + .../Core/ConfigLoaders/IsSettingSaveable.cpp | 2 + .../Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp | 16 +-- Source/Core/Core/NetPlayServer.cpp | 6 +- Source/Core/DolphinQt/GameList/GameList.cpp | 12 +- .../Core/DolphinQt/Settings/GameCubePane.cpp | 132 ++++++++++++++++++ Source/Core/DolphinQt/Settings/GameCubePane.h | 6 + 8 files changed, 232 insertions(+), 20 deletions(-) diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 08b3d586f1..091fd3504f 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -76,6 +76,18 @@ const Info& GetInfoForAGPCartPath(ExpansionInterface::Slot slot) }; return *infos[slot]; } +const Info MAIN_GCI_FOLDER_A_PATH{{System::Main, "Core", "GCIFolderAPath"}, ""}; +const Info MAIN_GCI_FOLDER_B_PATH{{System::Main, "Core", "GCIFolderBPath"}, ""}; +const Info& GetInfoForGCIPath(ExpansionInterface::Slot slot) +{ + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); + static constexpr Common::EnumMap*, ExpansionInterface::MAX_MEMCARD_SLOT> + infos{ + &MAIN_GCI_FOLDER_A_PATH, + &MAIN_GCI_FOLDER_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{ @@ -650,4 +662,63 @@ bool IsDefaultMemcardPathConfigured(ExpansionInterface::Slot slot) { return Config::Get(GetInfoForMemcardPath(slot)).empty(); } + +std::string GetGCIFolderPath(ExpansionInterface::Slot slot, std::optional region) +{ + return GetGCIFolderPath(Config::Get(GetInfoForGCIPath(slot)), slot, region); +} + +std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::Slot slot, + std::optional region) +{ + if (configured_folder.empty()) + { + const auto region_dir = Config::GetDirectoryForRegion( + Config::ToGameCubeRegion(region ? *region : Config::Get(Config::MAIN_FALLBACK_REGION))); + const bool is_slot_a = slot == ExpansionInterface::Slot::A; + return fmt::format("{}{}/Card {}", File::GetUserPath(D_GCUSER_IDX), region_dir, + is_slot_a ? 'A' : 'B'); + } + + // Custom path is expected to be stored in the form of + // "/path/to/folder/{region_code}" + // with an arbitrary but supported region code. + // Try to extract and replace that region code. + // If there's no region code just insert one at the end. + + UnifyPathSeparators(configured_folder); + while (StringEndsWith(configured_folder, "/")) + configured_folder.pop_back(); + + constexpr std::string_view us_region = "/" USA_DIR; + constexpr std::string_view jp_region = "/" JAP_DIR; + constexpr std::string_view eu_region = "/" EUR_DIR; + std::string_view base_path = configured_folder; + std::optional path_region = std::nullopt; + if (StringEndsWith(base_path, us_region)) + { + base_path = base_path.substr(0, base_path.size() - us_region.size()); + path_region = DiscIO::Region::NTSC_U; + } + else if (StringEndsWith(base_path, jp_region)) + { + base_path = base_path.substr(0, base_path.size() - jp_region.size()); + path_region = DiscIO::Region::NTSC_J; + } + else if (StringEndsWith(base_path, eu_region)) + { + base_path = base_path.substr(0, base_path.size() - eu_region.size()); + path_region = DiscIO::Region::PAL; + } + + const DiscIO::Region used_region = + region ? *region : (path_region ? *path_region : Config::Get(Config::MAIN_FALLBACK_REGION)); + return fmt::format("{}/{}", base_path, + Config::GetDirectoryForRegion(Config::ToGameCubeRegion(used_region))); +} + +bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot) +{ + return Config::Get(GetInfoForGCIPath(slot)).empty(); +} } // namespace Config diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 4aab4dac1c..f30ef02a38 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -75,6 +75,9 @@ 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; +extern const Info MAIN_GCI_FOLDER_B_PATH; +const Info& GetInfoForGCIPath(ExpansionInterface::Slot slot); extern const Info MAIN_GCI_FOLDER_A_PATH_OVERRIDE; extern const Info MAIN_GCI_FOLDER_B_PATH_OVERRIDE; const Info& GetInfoForGCIPathOverride(ExpansionInterface::Slot slot); @@ -352,4 +355,8 @@ std::string GetMemcardPath(ExpansionInterface::Slot slot, std::optional region, u16 size_mb = 0x80); bool IsDefaultMemcardPathConfigured(ExpansionInterface::Slot slot); +std::string GetGCIFolderPath(ExpansionInterface::Slot slot, std::optional region); +std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::Slot slot, + std::optional region); +bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot); } // namespace Config diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index 97fda95b3b..bd7f28fe3c 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -131,6 +131,8 @@ bool IsSettingSaveable(const Config::Location& config_location) &Config::MAIN_SYNC_GPU_MIN_DISTANCE.GetLocation(), &Config::MAIN_SYNC_GPU_OVERCLOCK.GetLocation(), &Config::MAIN_OVERRIDE_BOOT_IOS.GetLocation(), + &Config::MAIN_GCI_FOLDER_A_PATH.GetLocation(), + &Config::MAIN_GCI_FOLDER_B_PATH.GetLocation(), // UI.General diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp index 2aa5c1bbb5..c054c47404 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceMemoryCard.cpp @@ -154,20 +154,20 @@ CEXIMemoryCard::GetGCIFolderPath(Slot card_slot, AllowMovieFolder allow_movie_fo if (!path_override.empty()) return {std::move(path_override), false}; - std::string path = File::GetUserPath(D_GCUSER_IDX); - const bool use_movie_folder = allow_movie_folder == AllowMovieFolder::Yes && Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsUsingMemcard(card_slot) && Movie::IsStartingFromClearSave(); - if (use_movie_folder) - path += "Movie" DIR_SEP; - const DiscIO::Region region = Config::ToGameCubeRegion(SConfig::GetInstance().m_region); - path = path + Config::GetDirectoryForRegion(region) + DIR_SEP + - fmt::format("Card {}", s_card_short_names[card_slot]); - return {std::move(path), !use_movie_folder}; + if (use_movie_folder) + { + return {fmt::format("{}{}/Movie/Card {}", File::GetUserPath(D_GCUSER_IDX), + Config::GetDirectoryForRegion(region), s_card_short_names[card_slot]), + false}; + } + + return {Config::GetGCIFolderPath(card_slot, region), true}; } void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data) diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 18076ada29..ed2110ad16 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -1686,7 +1686,8 @@ bool NetPlayServer::SyncSaveData(const SaveSyncInfo& sync_info) return true; const auto game_region = sync_info.game->GetRegion(); - const std::string region = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(game_region)); + const auto gamecube_region = Config::ToGameCubeRegion(game_region); + const std::string region = Config::GetDirectoryForRegion(gamecube_region); for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { @@ -1723,8 +1724,7 @@ bool NetPlayServer::SyncSaveData(const SaveSyncInfo& sync_info) 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'); + const std::string path = Config::GetGCIFolderPath(slot, gamecube_region); sf::Packet pac; pac << MessageID::SyncSaveData; diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp index db86079825..69afc13a74 100644 --- a/Source/Core/DolphinQt/GameList/GameList.cpp +++ b/Source/Core/DolphinQt/GameList/GameList.cpp @@ -716,16 +716,10 @@ void GameList::OpenGCSaveFolder() { case ExpansionInterface::EXIDeviceType::MemoryCardFolder: { - std::string path = fmt::format("{}/{}/{}", File::GetUserPath(D_GCUSER_IDX), - Config::GetDirectoryForRegion(game->GetRegion()), - slot == Slot::A ? "Card A" : "Card B"); - std::string override_path = Config::Get(Config::GetInfoForGCIPathOverride(slot)); - - if (!override_path.empty()) - path = override_path; - - QDir dir(QString::fromStdString(path)); + QDir dir(QString::fromStdString(override_path.empty() ? + Config::GetGCIFolderPath(slot, game->GetRegion()) : + override_path)); if (!dir.entryList({QStringLiteral("%1-%2-*.gci") .arg(QString::fromStdString(game->GetMakerID())) diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index cc2b8fbe5f..b4e02c8305 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -114,6 +114,12 @@ void GameCubePane::CreateWidgets() m_agp_paths[slot] = new QLineEdit(); m_agp_path_layouts[slot]->addWidget(m_agp_path_labels[slot]); m_agp_path_layouts[slot]->addWidget(m_agp_paths[slot]); + + m_gci_path_layouts[slot] = new QHBoxLayout(); + m_gci_path_labels[slot] = new QLabel(tr("GCI Folder Path:")); + m_gci_paths[slot] = new QLineEdit(); + m_gci_path_layouts[slot]->addWidget(m_gci_path_labels[slot]); + m_gci_path_layouts[slot]->addWidget(m_gci_paths[slot]); } // Add slot devices @@ -155,6 +161,9 @@ void GameCubePane::CreateWidgets() ++row; device_layout->addLayout(m_agp_path_layouts[ExpansionInterface::Slot::A], row, 0, 1, 3); + ++row; + device_layout->addLayout(m_gci_path_layouts[ExpansionInterface::Slot::A], row, 0, 1, 3); + ++row; device_layout->addWidget(new QLabel(tr("Slot B:")), row, 0); device_layout->addWidget(m_slot_combos[ExpansionInterface::Slot::B], row, 1); @@ -166,6 +175,9 @@ void GameCubePane::CreateWidgets() ++row; device_layout->addLayout(m_agp_path_layouts[ExpansionInterface::Slot::B], row, 0, 1, 3); + ++row; + device_layout->addLayout(m_gci_path_layouts[ExpansionInterface::Slot::B], row, 0, 1, 3); + ++row; device_layout->addWidget(new QLabel(tr("SP1:")), row, 0); device_layout->addWidget(m_slot_combos[ExpansionInterface::Slot::SP1], row, 1); @@ -249,6 +261,11 @@ void GameCubePane::ConnectWidgets() }); connect(m_agp_paths[slot], &QLineEdit::editingFinished, [this, slot] { SetAGPRom(slot, m_agp_paths[slot]->text()); }); + connect(m_gci_paths[slot], &QLineEdit::editingFinished, [this, slot] { + // revert path change on failure + if (!SetGCIFolder(slot, m_gci_paths[slot]->text())) + LoadSettings(); + }); } #ifdef HAS_LIBMGBA @@ -302,14 +319,19 @@ void GameCubePane::UpdateButton(ExpansionInterface::Slot slot) case ExpansionInterface::Slot::B: { has_config = (device == ExpansionInterface::EXIDeviceType::MemoryCard || + device == ExpansionInterface::EXIDeviceType::MemoryCardFolder || device == ExpansionInterface::EXIDeviceType::AGP || device == ExpansionInterface::EXIDeviceType::Microphone); const bool hide_memory_card = device != ExpansionInterface::EXIDeviceType::MemoryCard || Config::IsDefaultMemcardPathConfigured(slot); + const bool hide_gci_path = device != ExpansionInterface::EXIDeviceType::MemoryCardFolder || + Config::IsDefaultGCIFolderPathConfigured(slot); m_memcard_path_labels[slot]->setHidden(hide_memory_card); m_memcard_paths[slot]->setHidden(hide_memory_card); m_agp_path_labels[slot]->setHidden(device != ExpansionInterface::EXIDeviceType::AGP); m_agp_paths[slot]->setHidden(device != ExpansionInterface::EXIDeviceType::AGP); + m_gci_path_labels[slot]->setHidden(hide_gci_path); + m_gci_paths[slot]->setHidden(hide_gci_path); break; } case ExpansionInterface::Slot::SP1: @@ -332,6 +354,9 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot) case ExpansionInterface::EXIDeviceType::MemoryCard: BrowseMemcard(slot); return; + case ExpansionInterface::EXIDeviceType::MemoryCardFolder: + BrowseGCIFolder(slot); + return; case ExpansionInterface::EXIDeviceType::AGP: BrowseAGPRom(slot); return; @@ -462,6 +487,111 @@ bool GameCubePane::SetMemcard(ExpansionInterface::Slot slot, const QString& file return true; } +void GameCubePane::BrowseGCIFolder(ExpansionInterface::Slot slot) +{ + ASSERT(ExpansionInterface::IsMemcardSlot(slot)); + + const QString path = DolphinFileDialog::getExistingDirectory( + this, tr("Choose the GCI base folder"), + QString::fromStdString(File::GetUserPath(D_GCUSER_IDX))); + + if (!path.isEmpty()) + SetGCIFolder(slot, path); +} + +bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& path) +{ + if (path.isEmpty()) + { + ModalMessageBox::critical(this, tr("Error"), tr("Cannot set GCI folder to an empty path.")); + return false; + } + + std::string raw_path = + WithUnifiedPathSeparators(QFileInfo(path).absoluteFilePath().toStdString()); + while (StringEndsWith(raw_path, "/")) + raw_path.pop_back(); + + // The user might be attempting to reset this path to its default, check for this. + const std::string default_jp_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_J); + const std::string default_us_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_U); + const std::string default_eu_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::PAL); + const bool is_default_path = + raw_path == default_jp_path || raw_path == default_us_path || raw_path == default_eu_path; + + bool path_changed; + if (is_default_path) + { + // Reset to default. + // Note that this does not need to check if the same card is in the other slot, because that's + // impossible given our constraints for folder names. + raw_path = ""; + path_changed = !Config::IsDefaultGCIFolderPathConfigured(slot); + } + else + { + // Figure out if the user selected a folder that ends in a valid region specifier. + const std::string jp_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_J); + const std::string us_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_U); + const std::string eu_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::PAL); + const bool raw_path_valid = raw_path == jp_path || raw_path == us_path || raw_path == eu_path; + + if (!raw_path_valid) + { + // TODO: We could try to autodetect the card region here and offer automatic renaming. + ModalMessageBox::critical( + this, tr("Error"), + tr("The folder %1 does not conform to Dolphin's region code format " + "for GCI folders. Please rename this folder to either %2, %3, or " + "%4, matching the region of the save files that are in it.") + .arg(QString::fromStdString(PathToFileName(raw_path))) + .arg(QString::fromStdString(PathToFileName(us_path))) + .arg(QString::fromStdString(PathToFileName(eu_path))) + .arg(QString::fromStdString(PathToFileName(jp_path)))); + return false; + } + + // Check if the other slot has the same folder configured and refuse to use this folder if so. + // The EU path is compared here, but it doesn't actually matter which one we compare since they + // follow a known pattern, so if the EU path matches the other match too and vice-versa. + for (ExpansionInterface::Slot other_slot : ExpansionInterface::MEMCARD_SLOTS) + { + if (other_slot == slot) + continue; + + const std::string other_eu_path = Config::GetGCIFolderPath(other_slot, DiscIO::Region::PAL); + if (eu_path == other_eu_path) + { + ModalMessageBox::critical( + this, tr("Error"), + tr("The same folder can't be used in multiple slots; it is already used by %1.") + .arg(QString::fromStdString(fmt::to_string(other_slot)))); + return false; + } + } + + path_changed = eu_path != Config::GetGCIFolderPath(slot, DiscIO::Region::PAL); + } + + Config::SetBase(Config::GetInfoForGCIPath(slot), raw_path); + + if (Core::IsRunning()) + { + // If emulation is running and the new card is different from the old one, notify the system to + // eject the old and insert the new card. + // TODO: This should probably be done by a config change callback instead. + if (path_changed) + { + // 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::MemoryCardFolder); + } + } + + LoadSettings(); + return true; +} + void GameCubePane::BrowseAGPRom(ExpansionInterface::Slot slot) { ASSERT(ExpansionInterface::IsMemcardSlot(slot)); @@ -576,6 +706,8 @@ void GameCubePane::LoadSettings() ->setText(QString::fromStdString(Config::GetMemcardPath(slot, std::nullopt))); SignalBlocking(m_agp_paths[slot]) ->setText(QString::fromStdString(Config::Get(Config::GetInfoForAGPCartPath(slot)))); + SignalBlocking(m_gci_paths[slot]) + ->setText(QString::fromStdString(Config::GetGCIFolderPath(slot, std::nullopt))); } #ifdef HAS_LIBMGBA diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.h b/Source/Core/DolphinQt/Settings/GameCubePane.h index 4ed1934e1c..fd3dd556fa 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.h +++ b/Source/Core/DolphinQt/Settings/GameCubePane.h @@ -42,6 +42,8 @@ private: void BrowseMemcard(ExpansionInterface::Slot slot); bool SetMemcard(ExpansionInterface::Slot slot, const QString& filename); + void BrowseGCIFolder(ExpansionInterface::Slot slot); + bool SetGCIFolder(ExpansionInterface::Slot slot, const QString& path); void BrowseAGPRom(ExpansionInterface::Slot slot); void SetAGPRom(ExpansionInterface::Slot slot, const QString& filename); void BrowseGBABios(); @@ -63,6 +65,10 @@ private: Common::EnumMap m_agp_path_labels; Common::EnumMap m_agp_paths; + Common::EnumMap m_gci_path_layouts; + Common::EnumMap m_gci_path_labels; + Common::EnumMap m_gci_paths; + QCheckBox* m_gba_threads; QCheckBox* m_gba_save_rom_path; QPushButton* m_gba_browse_bios; From 86d01c33997a313cc37213ffe183ddf9c0b513aa Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Mon, 7 Nov 2022 05:33:47 +0100 Subject: [PATCH 2/4] Config: Add option to use JPN as the Japanese region directory in GetDirectoryForRegion(). See https://bugs.dolphin-emu.org/issues/13076 for motivation for this. --- Source/Core/Common/CommonPaths.h | 2 ++ Source/Core/Core/Config/MainSettings.cpp | 7 ++++--- Source/Core/Core/Config/MainSettings.h | 10 +++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index cb8aacddf8..187231f743 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -28,9 +28,11 @@ #endif // Dirs in both User and Sys +// Legacy setups used /JAP/ while newer setups use /JPN/ by default. #define EUR_DIR "EUR" #define USA_DIR "USA" #define JAP_DIR "JAP" +#define JPN_DIR "JPN" // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) #define GC_USER_DIR "GC" diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 091fd3504f..4a8ae957f0 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -561,7 +561,7 @@ DiscIO::Region ToGameCubeRegion(DiscIO::Region region) return DiscIO::Region::NTSC_J; } -const char* GetDirectoryForRegion(DiscIO::Region region) +const char* GetDirectoryForRegion(DiscIO::Region region, RegionDirectoryStyle style) { if (region == DiscIO::Region::Unknown) region = ToGameCubeRegion(Config::Get(Config::MAIN_FALLBACK_REGION)); @@ -569,7 +569,7 @@ const char* GetDirectoryForRegion(DiscIO::Region region) switch (region) { case DiscIO::Region::NTSC_J: - return JAP_DIR; + return style == RegionDirectoryStyle::Legacy ? JAP_DIR : JPN_DIR; case DiscIO::Region::NTSC_U: return USA_DIR; @@ -578,8 +578,9 @@ const char* GetDirectoryForRegion(DiscIO::Region region) return EUR_DIR; case DiscIO::Region::NTSC_K: + // See ToGameCubeRegion ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region"); - return JAP_DIR; // See ToGameCubeRegion + return style == RegionDirectoryStyle::Legacy ? JAP_DIR : JPN_DIR; default: ASSERT_MSG(BOOT, false, "Default case should not be reached"); diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index f30ef02a38..0730681f2d 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -344,8 +344,16 @@ void SetUSBDeviceWhitelist(const std::set>& devices); // Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions DiscIO::Region ToGameCubeRegion(DiscIO::Region region); + // The region argument must be valid for GameCube (i.e. must not be NTSC-K) -const char* GetDirectoryForRegion(DiscIO::Region region); +enum class RegionDirectoryStyle +{ + Legacy, + Modern, +}; +const char* GetDirectoryForRegion(DiscIO::Region region, + RegionDirectoryStyle style = RegionDirectoryStyle::Legacy); + std::string GetBootROMPath(const std::string& region_directory); // Builds the memory card according to the configuration with the given region and size. If the // given region is std::nullopt, the region in the configured path is used if there is one, or the From c18d6c3deb9de9f20132a75a09d9ee68ef567ec1 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Mon, 7 Nov 2022 05:34:12 +0100 Subject: [PATCH 3/4] Config: Use JPN for custom GCI folder paths. --- Source/Core/Core/Config/MainSettings.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 4a8ae957f0..c88849a795 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -692,7 +692,7 @@ std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface:: configured_folder.pop_back(); constexpr std::string_view us_region = "/" USA_DIR; - constexpr std::string_view jp_region = "/" JAP_DIR; + constexpr std::string_view jp_region = "/" JPN_DIR; constexpr std::string_view eu_region = "/" EUR_DIR; std::string_view base_path = configured_folder; std::optional path_region = std::nullopt; @@ -715,7 +715,8 @@ std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface:: const DiscIO::Region used_region = region ? *region : (path_region ? *path_region : Config::Get(Config::MAIN_FALLBACK_REGION)); return fmt::format("{}/{}", base_path, - Config::GetDirectoryForRegion(Config::ToGameCubeRegion(used_region))); + Config::GetDirectoryForRegion(Config::ToGameCubeRegion(used_region), + Config::RegionDirectoryStyle::Modern)); } bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot) From 81557f611a5a3cd6ba96727ec0f583929de9f03c Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Mon, 7 Nov 2022 05:59:39 +0100 Subject: [PATCH 4/4] Qt/GameCubePane: Display a warning if a GCI folder override is set. --- .../Core/DolphinQt/Settings/GameCubePane.cpp | 23 ++++++++++++++++--- Source/Core/DolphinQt/Settings/GameCubePane.h | 4 +++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index b4e02c8305..1454a907f4 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -115,11 +115,19 @@ void GameCubePane::CreateWidgets() m_agp_path_layouts[slot]->addWidget(m_agp_path_labels[slot]); m_agp_path_layouts[slot]->addWidget(m_agp_paths[slot]); - m_gci_path_layouts[slot] = new QHBoxLayout(); + m_gci_path_layouts[slot] = new QVBoxLayout(); m_gci_path_labels[slot] = new QLabel(tr("GCI Folder Path:")); + m_gci_override_labels[slot] = + new QLabel(tr("Warning: A GCI folder override path is currently configured for this slot. " + "Adjusting the GCI path here will have no effect.")); + m_gci_override_labels[slot]->setHidden(true); + m_gci_override_labels[slot]->setWordWrap(true); m_gci_paths[slot] = new QLineEdit(); - m_gci_path_layouts[slot]->addWidget(m_gci_path_labels[slot]); - m_gci_path_layouts[slot]->addWidget(m_gci_paths[slot]); + auto* hlayout = new QHBoxLayout(); + hlayout->addWidget(m_gci_path_labels[slot]); + hlayout->addWidget(m_gci_paths[slot]); + m_gci_path_layouts[slot]->addWidget(m_gci_override_labels[slot]); + m_gci_path_layouts[slot]->addLayout(hlayout); } // Add slot devices @@ -332,6 +340,15 @@ void GameCubePane::UpdateButton(ExpansionInterface::Slot slot) m_agp_paths[slot]->setHidden(device != ExpansionInterface::EXIDeviceType::AGP); m_gci_path_labels[slot]->setHidden(hide_gci_path); m_gci_paths[slot]->setHidden(hide_gci_path); + + // In the years before we introduced the GCI folder configuration paths it has become somewhat + // popular to use the GCI override path instead. Check if this is the case and display a warning + // if it is set, so users aren't confused why configuring the normal GCI path doesn't do + // anything. + m_gci_override_labels[slot]->setHidden( + device != ExpansionInterface::EXIDeviceType::MemoryCardFolder || + Config::Get(Config::GetInfoForGCIPathOverride(slot)).empty()); + break; } case ExpansionInterface::Slot::SP1: diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.h b/Source/Core/DolphinQt/Settings/GameCubePane.h index fd3dd556fa..35862e2f57 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.h +++ b/Source/Core/DolphinQt/Settings/GameCubePane.h @@ -19,6 +19,7 @@ class QLabel; class QLineEdit; class QPushButton; class QString; +class QVBoxLayout; class GameCubePane : public QWidget { @@ -65,8 +66,9 @@ private: Common::EnumMap m_agp_path_labels; Common::EnumMap m_agp_paths; - Common::EnumMap m_gci_path_layouts; + Common::EnumMap m_gci_path_layouts; Common::EnumMap m_gci_path_labels; + Common::EnumMap m_gci_override_labels; Common::EnumMap m_gci_paths; QCheckBox* m_gba_threads;