diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index bce15c443e..77f98c4798 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -137,6 +137,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case game_boot_result::decryption_error: return "Failed to decrypt content"; case game_boot_result::file_creation_error: return "Could not create important files"; case game_boot_result::firmware_missing: return "Firmware is missing"; + case game_boot_result::firmware_version: return "Firmware is too old"; case game_boot_result::unsupported_disc_type: return "This disc type is not supported yet"; case game_boot_result::savestate_corrupted: return "Savestate data is corrupted or it's not an RPCS3 savestate"; case game_boot_result::savestate_version_unsupported: return "Savestate versioning data differs from your RPCS3 build.\nTry to use an older or newer RPCS3 build.\nEspecially if you know the build that created the savestate."; @@ -1965,7 +1966,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, // Initialize performance monitor g_fxo->init>(); - // Set title to actual disc title if necessary const std::string disc_sfo_dir = vfs::get("/dev_bdvd/PS3_GAME/PARAM.SFO"); const auto disc_psf_obj = psf::load_object(disc_sfo_dir); @@ -2100,6 +2100,21 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, } } + // Check firmware version + if (const std::string_view game_fw_version = psf::get_string(_psf, "PS3_SYSTEM_VER", ""); !game_fw_version.empty()) + { + if (const std::string fw_version = utils::get_firmware_version(); fw_version.empty()) + { + sys_log.warning("Firmware not installed. Skipping required firmware check. (title_id='%s', game_fw='%s')", m_title_id, game_fw_version); + } + else if (rpcs3::utils::version_is_bigger(game_fw_version, fw_version, m_title_id, true)) + { + sys_log.error("The game's required firmware version is higher than the installed firmware's version. (title_id='%s', game_fw='%s', fw='%s')", m_title_id, game_fw_version, fw_version); + return game_boot_result::firmware_version; + } + } + + // Set title to actual disc title if necessary if (!disc_psf_obj.empty()) { const auto bdvd_title = psf::get_string(disc_psf_obj, "TITLE"); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 738f127922..2d9b2d954b 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -51,6 +51,7 @@ enum class game_boot_result : u32 decryption_error, file_creation_error, firmware_missing, + firmware_version, unsupported_disc_type, savestate_corrupted, savestate_version_unsupported, diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index 8455a85fd2..ba98a44795 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -418,4 +418,19 @@ namespace rpcs3::utils return {}; } + + bool version_is_bigger(std::string_view v0, std::string_view v1, std::string_view serial, bool is_fw) + { + std::add_pointer_t ev0, ev1; + const double ver0 = std::strtod(v0.data(), &ev0); + const double ver1 = std::strtod(v1.data(), &ev1); + + if (v0.data() + v0.size() == ev0 && v1.data() + v1.size() == ev1) + { + return ver0 > ver1; + } + + sys_log.error("Failed to compare the %s numbers for title ID %s: '%s'-'%s'", is_fw ? "firmware version" : "version", serial, v0, v1); + return false; + } } diff --git a/rpcs3/Emu/system_utils.hpp b/rpcs3/Emu/system_utils.hpp index d7af2aa5eb..30ccb0add0 100644 --- a/rpcs3/Emu/system_utils.hpp +++ b/rpcs3/Emu/system_utils.hpp @@ -42,4 +42,6 @@ namespace rpcs3::utils std::string get_custom_input_config_path(const std::string& title_id); std::string get_game_content_path(game_content_type type); + + bool version_is_bigger(std::string_view v0, std::string_view v1, std::string_view serial, bool is_fw); } diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 3bf33f2a8c..5f9267abce 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -864,30 +864,15 @@ void game_list_frame::OnRefreshFinished() if (entry->info.serial != other->info.serial) continue; // The patch is game data and must have the same serial and an app version - static constexpr auto version_is_bigger = [](const std::string& v0, const std::string& v1, const std::string& serial, bool is_fw) - { - std::add_pointer_t ev0, ev1; - const double ver0 = std::strtod(v0.c_str(), &ev0); - const double ver1 = std::strtod(v1.c_str(), &ev1); - - if (v0.c_str() + v0.size() == ev0 && v1.c_str() + v1.size() == ev1) - { - return ver0 > ver1; - } - - game_list_log.error("Failed to update the displayed %s numbers for title ID %s\n'%s'-'%s'", is_fw ? "firmware version" : "version", serial, v0, v1); - return false; - }; - if (other->info.app_ver != cat_unknown_localized) { // Update the app version if it's higher than the disc's version (old games may not have an app version) - if (entry->info.app_ver == cat_unknown_localized || version_is_bigger(other->info.app_ver, entry->info.app_ver, entry->info.serial, true)) + if (entry->info.app_ver == cat_unknown_localized || rpcs3::utils::version_is_bigger(other->info.app_ver, entry->info.app_ver, entry->info.serial, false)) { entry->info.app_ver = other->info.app_ver; } // Update the firmware version if possible and if it's higher than the disc's version - if (other->info.fw != cat_unknown_localized && version_is_bigger(other->info.fw, entry->info.fw, entry->info.serial, false)) + if (other->info.fw != cat_unknown_localized && rpcs3::utils::version_is_bigger(other->info.fw, entry->info.fw, entry->info.serial, true)) { entry->info.fw = other->info.fw; } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index eecb3be62e..5e3d973ce8 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -538,6 +538,9 @@ void main_window::show_boot_error(game_boot_result status) case game_boot_result::still_running: message = tr("A game or PS3 application is still running or has yet to be fully stopped."); break; + case game_boot_result::firmware_version: + message = tr("The game or PS3 application needs a more recent firmware version."); + break; case game_boot_result::firmware_missing: // Handled elsewhere case game_boot_result::already_added: // Handled elsewhere case game_boot_result::currently_restricted: