diff --git a/src/core/bios.cpp b/src/core/bios.cpp index 3264b07a5..73591c6a9 100644 --- a/src/core/bios.cpp +++ b/src/core/bios.cpp @@ -4,6 +4,7 @@ #include "common/log.h" #include "common/md5_digest.h" #include "cpu_disasm.h" +#include #include Log_SetChannel(BIOS); @@ -36,16 +37,28 @@ std::string Hash::ToString() const return str; } -static constexpr Hash SCPH_1000_HASH = MakeHashFromString("239665b1a3dade1b5a52c06338011044"); -static constexpr Hash SCPH_1001_HASH = MakeHashFromString("924e392ed05558ffdb115408c263dccf"); -static constexpr Hash SCPH_1002_HASH = MakeHashFromString("54847e693405ffeb0359c6287434cbef"); -static constexpr Hash SCPH_3000_HASH = MakeHashFromString("849515939161e62f6b866f6853006780"); -static constexpr Hash SCPH_5500_HASH = MakeHashFromString("8dd7d5296a650fac7319bce665a6a53c"); -static constexpr Hash SCPH_5501_HASH = MakeHashFromString("490f666e1afb15b7362b406ed1cea246"); -static constexpr Hash SCPH_5502_HASH = MakeHashFromString("32736f17079d0b2b7024407c39bd3050"); -static constexpr Hash SCPH_7001_HASH = MakeHashFromString("1e68c231d0896b7eadcad1d7d8e76129"); -static constexpr Hash SCPH_7002_HASH = MakeHashFromString("b9d9a0286c33dc6b7237bb13cd46fdee"); -static constexpr Hash SCPH_POPS660_HASH = MakeHashFromString("c53ca5908936d412331790f4426c6c33"); +static constexpr std::array s_image_infos = {{ + {"SCPH-1000, DTL-H1000 (v1.0 J)", ConsoleRegion::NTSC_J, MakeHashFromString("239665b1a3dade1b5a52c06338011044")}, + {"SCPH-1001, 5003, DTL-H1201, H3001 (v2.2 12-04-95 A)", ConsoleRegion::NTSC_U, + MakeHashFromString("924e392ed05558ffdb115408c263dccf")}, + {"SCPH-1002, DTL-H1002 (v2.0 05-10-95 E)", ConsoleRegion::PAL, + MakeHashFromString("54847e693405ffeb0359c6287434cbef")}, + {"SCPH-3000, DTL-H1000H (v1.1 01-22-95)", ConsoleRegion::NTSC_J, + MakeHashFromString("849515939161e62f6b866f6853006780")}, + {"SCPH-5000, DTL-H1200, H3000 (v2.2 12-04-95 J)", ConsoleRegion::NTSC_J, + MakeHashFromString("8dd7d5296a650fac7319bce665a6a53c")}, + {"SCPH-5501, 5503, 7003 (v3.0 11-18-96 A)", ConsoleRegion::NTSC_U, + MakeHashFromString("490f666e1afb15b7362b406ed1cea246")}, + {"SCPH-5502, 5552 (v3.0 01-06-97 E)", ConsoleRegion::PAL, MakeHashFromString("32736f17079d0b2b7024407c39bd3050")}, + {"SCPH-7000, 7500, 9000 (v4.0 08-18-97 J)", ConsoleRegion::NTSC_J, + MakeHashFromString("8e4c14f567745eff2f0408c8129f72a6")}, + {"SCPH-7000W (v4.1 11-14-97 A)", ConsoleRegion::NTSC_J, MakeHashFromString("b84be139db3ee6cbd075630aa20a6553")}, + {"SCPH-7001, 7501, 7503, 9001, 9003, 9903 (v4.1 12-16-97 A)", ConsoleRegion::NTSC_U, + MakeHashFromString("1e68c231d0896b7eadcad1d7d8e76129")}, + {"SCPH-7002, 7502, 9002 (v4.1 12-16-97 E)", ConsoleRegion::PAL, + MakeHashFromString("b9d9a0286c33dc6b7237bb13cd46fdee")}, + {"POPS-660 (PSP)", ConsoleRegion::Auto, MakeHashFromString("c53ca5908936d412331790f4426c6c33")}, +}}; Hash GetHash(const Image& image) { @@ -56,14 +69,13 @@ Hash GetHash(const Image& image) return hash; } -std::optional LoadImageFromFile(std::string_view filename) +std::optional LoadImageFromFile(const char* filename) { Image ret(BIOS_SIZE); - std::string filename_str(filename); - auto fp = FileSystem::OpenManagedCFile(filename_str.c_str(), "rb"); + auto fp = FileSystem::OpenManagedCFile(filename, "rb"); if (!fp) { - Log_ErrorPrintf("Failed to open BIOS image '%s', errno=%d", filename_str.c_str(), errno); + Log_ErrorPrintf("Failed to open BIOS image '%s', errno=%d", filename, errno); return std::nullopt; } @@ -73,21 +85,20 @@ std::optional LoadImageFromFile(std::string_view filename) if (size != BIOS_SIZE) { - Log_ErrorPrintf("BIOS image '%s' mismatch, expecting %u bytes, got %u bytes", filename_str.c_str(), BIOS_SIZE, - size); + Log_ErrorPrintf("BIOS image '%s' mismatch, expecting %u bytes, got %u bytes", filename, BIOS_SIZE, size); return std::nullopt; } if (std::fread(ret.data(), 1, ret.size(), fp.get()) != ret.size()) { - Log_ErrorPrintf("Failed to read BIOS image '%s'", filename_str.c_str()); + Log_ErrorPrintf("Failed to read BIOS image '%s'", filename); return std::nullopt; } return ret; } -std::optional GetHashForFile(const std::string_view filename) +std::optional GetHashForFile(const char* filename) { auto image = LoadImageFromFile(filename); if (!image) @@ -96,23 +107,24 @@ std::optional GetHashForFile(const std::string_view filename) return GetHash(*image); } +const ImageInfo* GetImageInfoForHash(const Hash& hash) +{ + for (const ImageInfo& ii : s_image_infos) + { + if (ii.hash == hash) + return ⅈ + } + + return nullptr; +} + bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash) { - switch (region) - { - case ConsoleRegion::NTSC_J: - return (hash == SCPH_1000_HASH || hash == SCPH_3000_HASH || hash == SCPH_5500_HASH || hash == SCPH_POPS660_HASH); + const ImageInfo* ii = GetImageInfoForHash(hash); + if (!ii) + return false; - case ConsoleRegion::NTSC_U: - return (hash == SCPH_1001_HASH || hash == SCPH_5501_HASH || hash == SCPH_7001_HASH || hash == SCPH_POPS660_HASH); - - case ConsoleRegion::PAL: - return (hash == SCPH_1002_HASH || hash == SCPH_5502_HASH || hash == SCPH_7002_HASH || hash == SCPH_POPS660_HASH); - - case ConsoleRegion::Auto: - default: - return false; - } + return (ii->region == ConsoleRegion::Auto || ii->region == region); } void PatchBIOS(Image& bios, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/) @@ -135,9 +147,8 @@ void PatchBIOS(Image& bios, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFF bool PatchBIOSEnableTTY(Image& image, const Hash& hash) { - if (hash != SCPH_1000_HASH && hash != SCPH_1001_HASH && hash != SCPH_1002_HASH && hash != SCPH_3000_HASH && - hash != SCPH_5500_HASH && hash != SCPH_5501_HASH && hash != SCPH_5502_HASH && hash != SCPH_7001_HASH && - hash != SCPH_7002_HASH && hash != SCPH_POPS660_HASH) + const ImageInfo* ii = GetImageInfoForHash(hash); + if (!ii) { Log_WarningPrintf("Incompatible version for TTY patch: %s", hash.ToString().c_str()); return false; @@ -151,9 +162,8 @@ bool PatchBIOSEnableTTY(Image& image, const Hash& hash) bool PatchBIOSFastBoot(Image& image, const Hash& hash) { - if (hash != SCPH_1000_HASH && hash != SCPH_1001_HASH && hash != SCPH_1002_HASH && hash != SCPH_3000_HASH && - hash != SCPH_5500_HASH && hash != SCPH_5501_HASH && hash != SCPH_5502_HASH && hash != SCPH_7001_HASH && - hash != SCPH_7002_HASH && hash != SCPH_POPS660_HASH) + const ImageInfo* ii = GetImageInfoForHash(hash); + if (!ii) { Log_WarningPrintf("Incompatible version for fast-boot patch: %s", hash.ToString().c_str()); return false; diff --git a/src/core/bios.h b/src/core/bios.h index 271dd126d..417c95df5 100644 --- a/src/core/bios.h +++ b/src/core/bios.h @@ -24,6 +24,13 @@ struct Hash std::string ToString() const; }; +struct ImageInfo +{ + const char* description; + ConsoleRegion region; + Hash hash; +}; + #pragma pack(push, 1) struct PSEXEHeader { @@ -46,9 +53,10 @@ static_assert(sizeof(PSEXEHeader) == 0x800); #pragma pack(pop) Hash GetHash(const Image& image); -std::optional LoadImageFromFile(std::string_view filename); -std::optional GetHashForFile(std::string_view filename); +std::optional LoadImageFromFile(const char* filename); +std::optional GetHashForFile(const char* filename); +const ImageInfo* GetImageInfoForHash(const Hash& hash); bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash); void PatchBIOS(Image& image, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF)); diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index b583a181b..b497bd039 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -201,84 +201,132 @@ void HostInterface::AddFormattedOSDMessage(float duration, const char* format, . std::optional> HostInterface::GetBIOSImage(ConsoleRegion region) { - // Try the other default filenames in the directory of the configured BIOS. -#define TRY_FILENAME(filename) \ - do \ - { \ - String try_filename = filename; \ - std::optional found_image = BIOS::LoadImageFromFile(try_filename.GetCharArray()); \ - if (found_image) \ - { \ - BIOS::Hash found_hash = BIOS::GetHash(*found_image); \ - Log_DevPrintf("Hash for BIOS '%s': %s", try_filename.GetCharArray(), found_hash.ToString().c_str()); \ - if (BIOS::IsValidHashForRegion(region, found_hash)) \ - { \ - Log_InfoPrintf("Using BIOS from '%s' for region '%s'", try_filename.GetCharArray(), \ - Settings::GetConsoleRegionName(region)); \ - return found_image; \ - } \ - } \ - } while (0) - - // Try the configured image. - TRY_FILENAME(g_settings.bios_path.c_str()); - - // Try searching in the same folder for other region's images. + const std::string* bios_path; switch (region) { case ConsoleRegion::NTSC_J: - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph3000.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-11j.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph1000.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-10j.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5500.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-30j.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7000.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7500.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9000.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-40j.bin", false, false)); - break; - - case ConsoleRegion::NTSC_U: - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph1001.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-22a.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5501.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5503.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7003.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-30a.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7001.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7501.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7503.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9001.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9003.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9903.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-41a.bin", false, false)); + bios_path = &g_settings.bios_path_ntsc_j; break; case ConsoleRegion::PAL: - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph1002.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-21e.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5502.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5552.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-30e.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7002.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7502.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9002.bin", false, false)); - TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-41e.bin", false, false)); + bios_path = &g_settings.bios_path_pal; break; + case ConsoleRegion::NTSC_U: default: + bios_path = &g_settings.bios_path_ntsc_u; break; } -#undef RELATIVE_PATH -#undef TRY_FILENAME + if (bios_path->empty()) + { + // auto-detect + return FindBIOSImageInDirectory(region, GetUserDirectoryRelativePath("bios").c_str()); + } - // Fall back to the default image. - Log_WarningPrintf("No suitable BIOS image for region %s could be located, using configured image '%s'. This may " - "result in instability.", - Settings::GetConsoleRegionName(region), g_settings.bios_path.c_str()); - return BIOS::LoadImageFromFile(g_settings.bios_path); + // try the configured path + std::optional image = BIOS::LoadImageFromFile( + GetUserDirectoryRelativePath("bios" FS_OSPATH_SEPARATOR_STR "%s", bios_path->c_str()).c_str()); + if (!image.has_value()) + { + g_host_interface->ReportFormattedError( + g_host_interface->TranslateString("HostInterface", "Failed to load configured BIOS file '%s'"), + bios_path->c_str()); + return std::nullopt; + } + + BIOS::Hash found_hash = BIOS::GetHash(*image); + Log_DevPrintf("Hash for BIOS '%s': %s", bios_path->c_str(), found_hash.ToString().c_str()); + + if (!BIOS::IsValidHashForRegion(region, found_hash)) + Log_WarningPrintf("Hash for BIOS '%s' does not match region. This may cause issues.", bios_path->c_str()); + + return image; +} + +std::optional> HostInterface::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory) +{ + Log_InfoPrintf("Searching for a %s BIOS in '%s'...", Settings::GetConsoleRegionDisplayName(region), directory); + + FileSystem::FindResultsArray results; + FileSystem::FindFiles( + directory, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results); + + std::string fallback_path; + std::optional fallback_image; + + for (const FILESYSTEM_FIND_DATA& fd : results) + { + if (fd.Size != BIOS::BIOS_SIZE) + { + Log_WarningPrintf("Skipping '%s': incorrect size", fd.FileName.c_str()); + continue; + } + + std::string full_path( + StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", directory, fd.FileName.c_str())); + + std::optional found_image = BIOS::LoadImageFromFile(full_path.c_str()); + if (!found_image) + continue; + + BIOS::Hash found_hash = BIOS::GetHash(*found_image); + Log_DevPrintf("Hash for BIOS '%s': %s", fd.FileName.c_str(), found_hash.ToString().c_str()); + + if (BIOS::IsValidHashForRegion(region, found_hash)) + { + Log_InfoPrintf("Using BIOS '%s'", fd.FileName.c_str()); + return found_image; + } + + fallback_path = std::move(full_path); + fallback_image = std::move(found_image); + } + + if (!fallback_image.has_value()) + { + g_host_interface->ReportFormattedError( + g_host_interface->TranslateString("HostInterface", "No BIOS image found for %s region"), + Settings::GetConsoleRegionDisplayName(region)); + return std::nullopt; + } + + Log_WarningPrintf("Falling back to possibly-incompatible image '%s'", fallback_path.c_str()); + return fallback_image; +} + +std::vector> +HostInterface::FindBIOSImagesInDirectory(const char* directory) +{ + std::vector> results; + + FileSystem::FindResultsArray files; + FileSystem::FindFiles(directory, "*", + FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &files); + + for (FILESYSTEM_FIND_DATA& fd : files) + { + if (fd.Size != BIOS::BIOS_SIZE) + continue; + + std::string full_path( + StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", directory, fd.FileName.c_str())); + + std::optional found_image = BIOS::LoadImageFromFile(full_path.c_str()); + if (!found_image) + continue; + + BIOS::Hash found_hash = BIOS::GetHash(*found_image); + const BIOS::ImageInfo* ii = BIOS::GetImageInfoForHash(found_hash); + results.emplace_back(std::move(fd.FileName), ii); + } + + return results; +} + +std::vector> HostInterface::FindBIOSImagesInUserDirectory() +{ + return FindBIOSImagesInDirectory(GetUserDirectoryRelativePath("bios").c_str()); } bool HostInterface::LoadState(const char* filename) @@ -410,7 +458,9 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si) si.SetBoolValue("Audio", "Sync", true); si.SetBoolValue("Audio", "DumpOnBoot", false); - si.SetStringValue("BIOS", "Path", "bios" FS_OSPATH_SEPARATOR_STR "scph1001.bin"); + si.SetStringValue("BIOS", "PathNTSCU", ""); + si.SetStringValue("BIOS", "PathNTSCJ", ""); + si.SetStringValue("BIOS", "PathPAL", ""); si.SetBoolValue("BIOS", "PatchTTYEnable", false); si.SetBoolValue("BIOS", "PatchFastBoot", false); diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 596ffaf75..5073dc979 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -22,6 +22,11 @@ class GameList; struct SystemBootParameters; +namespace BIOS +{ +struct ImageInfo; +} + class HostInterface { public: @@ -116,6 +121,14 @@ public: /// Loads the BIOS image for the specified region. std::optional> GetBIOSImage(ConsoleRegion region); + /// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first + /// 512KB BIOS image will be used. + std::optional> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory); + + /// Returns a list of filenames and descriptions for BIOS images in a directory. + std::vector> FindBIOSImagesInDirectory(const char* directory); + std::vector> FindBIOSImagesInUserDirectory(); + virtual void OnRunningGameChanged(); virtual void OnSystemPerformanceCountersUpdated(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 01e846dac..6622525c7 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -157,7 +157,9 @@ void Settings::Load(SettingsInterface& si) gpu_fifo_size = static_cast(si.GetIntValue("Hacks", "GPUFIFOSize", DEFAULT_GPU_FIFO_SIZE)); gpu_max_run_ahead = si.GetIntValue("Hacks", "GPUMaxRunAhead", DEFAULT_GPU_MAX_RUN_AHEAD); - bios_path = si.GetStringValue("BIOS", "Path", "bios" FS_OSPATH_SEPARATOR_STR "scph1001.bin"); + bios_path_ntsc_u = si.GetStringValue("BIOS", "PathNTSCU", ""); + bios_path_ntsc_j = si.GetStringValue("BIOS", "PathNTSCJ", ""); + bios_path_pal = si.GetStringValue("BIOS", "PathPAL", ""); bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", false); bios_patch_fast_boot = si.GetBoolValue("BIOS", "PatchFastBoot", false); @@ -271,7 +273,9 @@ void Settings::Save(SettingsInterface& si) const si.SetIntValue("Hacks", "GPUFIFOSize", gpu_fifo_size); si.SetIntValue("Hacks", "GPUMaxRunAhead", gpu_max_run_ahead); - si.SetStringValue("BIOS", "Path", bios_path.c_str()); + si.SetStringValue("BIOS", "PathNTSCJ", bios_path_ntsc_j.c_str()); + si.SetStringValue("BIOS", "PathNTSCU", bios_path_ntsc_u.c_str()); + si.SetStringValue("BIOS", "PathPAL", bios_path_pal.c_str()); si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable); si.SetBoolValue("BIOS", "PatchFastBoot", bios_patch_fast_boot); diff --git a/src/core/settings.h b/src/core/settings.h index 08373fd1c..199f677c6 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -147,7 +147,9 @@ struct Settings // TODO: Controllers, memory cards, etc. - std::string bios_path; + std::string bios_path_ntsc_j; + std::string bios_path_ntsc_u; + std::string bios_path_pal; bool bios_patch_tty_enable = false; bool bios_patch_fast_boot = false; diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 38d9dfd1e..48ba53975 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -15,6 +15,9 @@ set(SRCS autoupdaterdialog.cpp autoupdaterdialog.h autoupdaterdialog.ui + biossettingswidget.cpp + biossettingswidget.h + biossettingswidget.ui consolesettingswidget.cpp consolesettingswidget.h consolesettingswidget.ui diff --git a/src/duckstation-qt/biossettingswidget.cpp b/src/duckstation-qt/biossettingswidget.cpp new file mode 100644 index 000000000..5da0d57f1 --- /dev/null +++ b/src/duckstation-qt/biossettingswidget.cpp @@ -0,0 +1,123 @@ +#include "biossettingswidget.h" +#include "core/bios.h" +#include "qthostinterface.h" +#include "settingsdialog.h" +#include "settingwidgetbinder.h" +#include +#include + +static void populateDropDownForRegion(ConsoleRegion region, QComboBox* cb, + std::vector>& images) +{ + cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), + QT_TRANSLATE_NOOP("BIOSSettingsWidget", "Auto-Detect")); + + std::sort(images.begin(), images.end(), [region](const auto& left, const auto& right) { + const bool left_region_match = (left.second && left.second->region == region); + const bool right_region_match = (right.second && right.second->region == region); + if (left_region_match && !right_region_match) + return true; + else if (right_region_match && left_region_match) + return false; + + return left.first < right.first; + }); + + for (const auto& [name, info] : images) + { + QIcon icon; + if (info) + { + switch (info->region) + { + case ConsoleRegion::NTSC_J: + icon = QIcon(QStringLiteral(":/icons/flag-jp.png")); + break; + case ConsoleRegion::PAL: + icon = QIcon(QStringLiteral(":/icons/flag-eu.png")); + break; + case ConsoleRegion::NTSC_U: + icon = QIcon(QStringLiteral(":/icons/flag-uc.png")); + break; + default: + icon = QIcon(QStringLiteral(":/icons/applications-other.png")); + break; + } + } + else + { + icon = QIcon(QStringLiteral(":/icons/applications-other.png")); + } + + QString name_str(QString::fromStdString(name)); + cb->addItem(icon, + QStringLiteral("%1 (%2)") + .arg(info ? QString(info->description) : qApp->translate("BIOSSettingsWidget", "Unknown")) + .arg(name_str), + QVariant(name_str)); + } +} + +static void setDropDownValue(QComboBox* cb, const std::string& name) +{ + QSignalBlocker sb(cb); + + if (name.empty()) + { + cb->setCurrentIndex(0); + return; + } + + QString qname(QString::fromStdString(name)); + for (int i = 1; i < cb->count(); i++) + { + if (cb->itemData(i) == qname) + { + cb->setCurrentIndex(i); + return; + } + } + + cb->addItem(qname, QVariant(qname)); + cb->setCurrentIndex(cb->count() - 1); +} + +BIOSSettingsWidget::BIOSSettingsWidget(QtHostInterface* host_interface, QWidget* parent, SettingsDialog* dialog) + : QWidget(parent), m_host_interface(host_interface) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableTTYOutput, "BIOS", "PatchTTYEnable"); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.fastBoot, "BIOS", "PatchFastBoot"); + + dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"), + tr("Patches the BIOS to skip the console's boot animation. Does not work with all games, " + "but usually safe to enabled.")); + + auto images = m_host_interface->FindBIOSImagesInUserDirectory(); + populateDropDownForRegion(ConsoleRegion::NTSC_J, m_ui.imageNTSCJ, images); + populateDropDownForRegion(ConsoleRegion::NTSC_U, m_ui.imageNTSCU, images); + populateDropDownForRegion(ConsoleRegion::PAL, m_ui.imagePAL, images); + + setDropDownValue(m_ui.imageNTSCJ, m_host_interface->GetStringSettingValue("BIOS", "PathNTSCJ", "")); + setDropDownValue(m_ui.imageNTSCU, m_host_interface->GetStringSettingValue("BIOS", "PathNTSCU", "")); + setDropDownValue(m_ui.imagePAL, m_host_interface->GetStringSettingValue("BIOS", "PathPAL", "")); + + connect(m_ui.imageNTSCJ, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { + m_host_interface->SetStringSettingValue("BIOS", "PathNTSCJ", + m_ui.imageNTSCJ->itemData(index).toString().toStdString().c_str()); + m_host_interface->applySettings(); + }); + connect(m_ui.imageNTSCU, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { + m_host_interface->SetStringSettingValue("BIOS", "PathNTSCU", + m_ui.imageNTSCU->itemData(index).toString().toStdString().c_str()); + m_host_interface->applySettings(); + }); + connect(m_ui.imagePAL, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { + m_host_interface->SetStringSettingValue("BIOS", "PathPAL", + m_ui.imagePAL->itemData(index).toString().toStdString().c_str()); + m_host_interface->applySettings(); + }); +} + +BIOSSettingsWidget::~BIOSSettingsWidget() = default; diff --git a/src/duckstation-qt/biossettingswidget.h b/src/duckstation-qt/biossettingswidget.h new file mode 100644 index 000000000..7e324f40e --- /dev/null +++ b/src/duckstation-qt/biossettingswidget.h @@ -0,0 +1,22 @@ +#pragma once +#include "core/types.h" +#include + +#include "ui_biossettingswidget.h" + +class QtHostInterface; +class SettingsDialog; + +class BIOSSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit BIOSSettingsWidget(QtHostInterface* host_interface, QWidget* parent, SettingsDialog* dialog); + ~BIOSSettingsWidget(); + +private: + Ui::BIOSSettingsWidget m_ui; + + QtHostInterface* m_host_interface; +}; diff --git a/src/duckstation-qt/biossettingswidget.ui b/src/duckstation-qt/biossettingswidget.ui new file mode 100644 index 000000000..e89b3298b --- /dev/null +++ b/src/duckstation-qt/biossettingswidget.ui @@ -0,0 +1,129 @@ + + + BIOSSettingsWidget + + + + 0 + 0 + 472 + 259 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + BIOS Selection + + + + + + NTSC-J (Japan): + + + + + + + + 0 + 0 + + + + + + + + NTSC-U/C (US/Canada): + + + + + + + + 0 + 0 + + + + + + + + PAL (Europe, Australia): + + + + + + + + 0 + 0 + + + + + + + + + + + Options and Patches + + + + + + Fast Boot + + + + + + + Enable TTY Output + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp index 8d9e75ae4..ec0033712 100644 --- a/src/duckstation-qt/consolesettingswidget.cpp +++ b/src/duckstation-qt/consolesettingswidget.cpp @@ -1,9 +1,6 @@ #include "consolesettingswidget.h" #include "settingsdialog.h" #include "settingwidgetbinder.h" -#include - -static constexpr char BIOS_IMAGE_FILTER[] = "Binary Images (*.bin);;All Files (*.*)"; ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QWidget* parent, SettingsDialog* dialog) : QWidget(parent), m_host_interface(host_interface) @@ -25,9 +22,6 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.region, "Console", "Region", &Settings::ParseConsoleRegionName, &Settings::GetConsoleRegionName, Settings::DEFAULT_CONSOLE_REGION); - SettingWidgetBinder::BindWidgetToStringSetting(m_host_interface, m_ui.biosPath, "BIOS", "Path"); - SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableTTYOutput, "BIOS", "PatchTTYEnable"); - SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.fastBoot, "BIOS", "PatchFastBoot"); SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU", "ExecutionMode", &Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName, Settings::DEFAULT_CPU_EXECUTION_MODE); @@ -36,12 +30,6 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.cdromLoadImageToRAM, "CDROM", "LoadImageToRAM", false); - connect(m_ui.biosPathBrowse, &QPushButton::pressed, this, &ConsoleSettingsWidget::onBrowseBIOSPathButtonClicked); - - dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"), - tr("Patches the BIOS to skip the console's boot animation. Does not work with all games, " - "but usually safe to enabled.")); - dialog->registerWidgetHelp( m_ui.cdromLoadImageToRAM, tr("Preload Image to RAM"), tr("Unchecked"), tr("Loads the game image into RAM. Useful for network paths that may become unreliable during gameplay. In some " @@ -49,16 +37,3 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW } ConsoleSettingsWidget::~ConsoleSettingsWidget() = default; - -void ConsoleSettingsWidget::onBrowseBIOSPathButtonClicked() -{ - QString path = QDir::toNativeSeparators( - QFileDialog::getOpenFileName(this, tr("Select BIOS Image"), QString(), tr(BIOS_IMAGE_FILTER))); - if (path.isEmpty()) - return; - - m_ui.biosPath->setText(path); - - m_host_interface->SetStringSettingValue("BIOS", "Path", path.toUtf8().constData()); - m_host_interface->applySettings(); -} diff --git a/src/duckstation-qt/consolesettingswidget.h b/src/duckstation-qt/consolesettingswidget.h index ac640747e..17d498681 100644 --- a/src/duckstation-qt/consolesettingswidget.h +++ b/src/duckstation-qt/consolesettingswidget.h @@ -15,9 +15,6 @@ public: explicit ConsoleSettingsWidget(QtHostInterface* host_interface, QWidget* parent, SettingsDialog* dialog); ~ConsoleSettingsWidget(); -private Q_SLOTS: - void onBrowseBIOSPathButtonClicked(); - private: Ui::ConsoleSettingsWidget m_ui; diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui index 7852c65ea..99c3ce0bd 100644 --- a/src/duckstation-qt/consolesettingswidget.ui +++ b/src/duckstation-qt/consolesettingswidget.ui @@ -7,7 +7,7 @@ 0 0 502 - 358 + 255 @@ -42,41 +42,6 @@ - - - - BIOS Image Path: - - - - - - - Fast Boot - - - - - - - Enable TTY Output - - - - - - - - - - - - ... - - - - - @@ -145,7 +110,7 @@ - + diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index cfdf8fd73..a9b79b173 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -39,6 +39,7 @@ + @@ -68,6 +69,7 @@ + @@ -124,6 +126,9 @@ Document + + Document + Document @@ -171,6 +176,7 @@ + diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index af32b1eb2..94d71d2e8 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -646,6 +646,8 @@ void MainWindow::connectSignals() connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::Count); }); connect(m_ui.actionGeneralSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::GeneralSettings); }); + connect(m_ui.actionBIOSSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::BIOSSettings); }); connect(m_ui.actionConsoleSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::ConsoleSettings); }); connect(m_ui.actionGameListSettings, &QAction::triggered, diff --git a/src/duckstation-qt/mainwindow.ui b/src/duckstation-qt/mainwindow.ui index 2814523d8..939af6210 100644 --- a/src/duckstation-qt/mainwindow.ui +++ b/src/duckstation-qt/mainwindow.ui @@ -115,6 +115,7 @@ + @@ -322,6 +323,15 @@ E&xit + + + + :/icons/media-flash-2.png:/icons/media-flash-2.png + + + B&IOS Settings... + + diff --git a/src/duckstation-qt/resources/icons/media-flash-2.png b/src/duckstation-qt/resources/icons/media-flash-2.png new file mode 100644 index 000000000..d10f04749 Binary files /dev/null and b/src/duckstation-qt/resources/icons/media-flash-2.png differ diff --git a/src/duckstation-qt/resources/icons/media-flash-2@2x.png b/src/duckstation-qt/resources/icons/media-flash-2@2x.png new file mode 100644 index 000000000..d3d855301 Binary files /dev/null and b/src/duckstation-qt/resources/icons/media-flash-2@2x.png differ diff --git a/src/duckstation-qt/resources/resources.qrc b/src/duckstation-qt/resources/resources.qrc index d45321cce..72a268891 100644 --- a/src/duckstation-qt/resources/resources.qrc +++ b/src/duckstation-qt/resources/resources.qrc @@ -59,6 +59,8 @@ icons/list-remove@2x.png icons/media-flash.png icons/media-flash@2x.png + icons/media-flash-2.png + icons/media-flash-2@2x.png icons/media-flash-24.png icons/media-flash-24@2x.png icons/media-optical.png diff --git a/src/duckstation-qt/settingsdialog.cpp b/src/duckstation-qt/settingsdialog.cpp index c594738f4..bbd89d051 100644 --- a/src/duckstation-qt/settingsdialog.cpp +++ b/src/duckstation-qt/settingsdialog.cpp @@ -1,6 +1,7 @@ #include "settingsdialog.h" #include "advancedsettingswidget.h" #include "audiosettingswidget.h" +#include "biossettingswidget.h" #include "consolesettingswidget.h" #include "controllersettingswidget.h" #include "displaysettingswidget.h" @@ -24,6 +25,7 @@ SettingsDialog::SettingsDialog(QtHostInterface* host_interface, QWidget* parent setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); m_general_settings = new GeneralSettingsWidget(host_interface, m_ui.settingsContainer, this); + m_bios_settings = new BIOSSettingsWidget(host_interface, m_ui.settingsContainer, this); m_console_settings = new ConsoleSettingsWidget(host_interface, m_ui.settingsContainer, this); m_game_list_settings = new GameListSettingsWidget(host_interface, m_ui.settingsContainer); m_hotkey_settings = new HotkeySettingsWidget(host_interface, m_ui.settingsContainer); @@ -36,6 +38,7 @@ SettingsDialog::SettingsDialog(QtHostInterface* host_interface, QWidget* parent m_advanced_settings = new AdvancedSettingsWidget(host_interface, m_ui.settingsContainer, this); m_ui.settingsContainer->insertWidget(static_cast(Category::GeneralSettings), m_general_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::BIOSSettings), m_bios_settings); m_ui.settingsContainer->insertWidget(static_cast(Category::ConsoleSettings), m_console_settings); m_ui.settingsContainer->insertWidget(static_cast(Category::GameListSettings), m_game_list_settings); m_ui.settingsContainer->insertWidget(static_cast(Category::HotkeySettings), m_hotkey_settings); diff --git a/src/duckstation-qt/settingsdialog.h b/src/duckstation-qt/settingsdialog.h index e01d8b08f..cdd0aea92 100644 --- a/src/duckstation-qt/settingsdialog.h +++ b/src/duckstation-qt/settingsdialog.h @@ -8,6 +8,7 @@ class QtHostInterface; class GeneralSettingsWidget; +class BIOSSettingsWidget; class GameListSettingsWidget; class HotkeySettingsWidget; class ConsoleSettingsWidget; @@ -27,6 +28,7 @@ public: enum class Category { GeneralSettings, + BIOSSettings, ConsoleSettings, GameListSettings, HotkeySettings, @@ -44,6 +46,7 @@ public: ~SettingsDialog(); GeneralSettingsWidget* getGeneralSettingsWidget() const { return m_general_settings; } + BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; } ConsoleSettingsWidget* getConsoleSettingsWidget() const { return m_console_settings; } GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; } HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; } @@ -72,6 +75,7 @@ private: QtHostInterface* m_host_interface; GeneralSettingsWidget* m_general_settings = nullptr; + BIOSSettingsWidget* m_bios_settings = nullptr; ConsoleSettingsWidget* m_console_settings = nullptr; GameListSettingsWidget* m_game_list_settings = nullptr; HotkeySettingsWidget* m_hotkey_settings = nullptr; @@ -79,7 +83,7 @@ private: MemoryCardSettingsWidget* m_memory_card_settings = nullptr; DisplaySettingsWidget* m_display_settings = nullptr; EnhancementSettingsWidget* m_enhancement_settings = nullptr; - PostProcessingSettingsWidget *m_post_processing_settings = nullptr; + PostProcessingSettingsWidget* m_post_processing_settings = nullptr; AudioSettingsWidget* m_audio_settings = nullptr; AdvancedSettingsWidget* m_advanced_settings = nullptr; diff --git a/src/duckstation-qt/settingsdialog.ui b/src/duckstation-qt/settingsdialog.ui index 7bb715045..3593bd586 100644 --- a/src/duckstation-qt/settingsdialog.ui +++ b/src/duckstation-qt/settingsdialog.ui @@ -67,6 +67,15 @@ :/icons/applications-system.png:/icons/applications-system.png + + + BIOS Settings + + + + :/icons/media-flash-2.png:/icons/media-flash-2.png + + Console Settings diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index 2a05fe629..21e18c6ca 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -1182,10 +1182,6 @@ void SDLHostInterface::DrawSettingsWindow() settings_changed = true; } - ImGui::Text("BIOS Path:"); - ImGui::SameLine(indent); - settings_changed |= DrawFileChooser("##bios_path", &m_settings_copy.bios_path); - settings_changed |= ImGui::Checkbox("Enable TTY Output", &m_settings_copy.bios_patch_tty_enable); settings_changed |= ImGui::Checkbox("Fast Boot", &m_settings_copy.bios_patch_fast_boot); }