From 095c8843312cdc88a17d25dfa17eb50fabb15aab Mon Sep 17 00:00:00 2001 From: Ian Carpenter Date: Thu, 27 Mar 2025 15:39:43 -0400 Subject: [PATCH 1/6] unpkg: Restore support for PSP/Vita packages --- rpcs3/Crypto/unpkg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index 45fd0b9ee1..bc7481fcd6 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -528,7 +528,7 @@ bool package_reader::read_entries(std::vector& entries) entries.clear(); entries.resize(m_header.file_count + BUF_PADDING / sizeof(PKGEntry) + 1); - const usz read_size = decrypt(0, m_header.file_count * sizeof(PKGEntry), m_dec_key.data(), entries.data()); + const usz read_size = decrypt(0, m_header.file_count * sizeof(PKGEntry), m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA ? PKG_AES_KEY2 : m_dec_key.data(), entries.data()); if (read_size < m_header.file_count * sizeof(PKGEntry)) { From 949a80dc0a0298d1a468a37af906fd33f09b6a97 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 28 Mar 2025 15:29:29 +0100 Subject: [PATCH 2/6] Loader: deny boot if the firmware is too old --- rpcs3/Emu/System.cpp | 17 ++++++++++++++++- rpcs3/Emu/System.h | 1 + rpcs3/Emu/system_utils.cpp | 15 +++++++++++++++ rpcs3/Emu/system_utils.hpp | 2 ++ rpcs3/rpcs3qt/game_list_frame.cpp | 19 ++----------------- rpcs3/rpcs3qt/main_window.cpp | 3 +++ 6 files changed, 39 insertions(+), 18 deletions(-) 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: From a7df3cfb40d1a502090ca34e23b656527a0381b6 Mon Sep 17 00:00:00 2001 From: Ani Date: Fri, 28 Mar 2025 12:32:29 +0100 Subject: [PATCH 3/6] config: Set default Driver Wake-Up Delay to 0 --- rpcs3/Emu/system_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index c5aa64068e..7035fff5d0 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -172,7 +172,7 @@ struct cfg_root : cfg::node cfg::_int<1, 1024> min_scalable_dimension{ this, "Minimum Scalable Dimension", 16 }; cfg::_int<0, 16> shader_compiler_threads_count{ this, "Shader Compiler Threads", 0 }; cfg::_int<0, 30000000> driver_recovery_timeout{ this, "Driver Recovery Timeout", 1000000, true }; - cfg::uint<0, 16667> driver_wakeup_delay{ this, "Driver Wake-Up Delay", 1, true }; + cfg::uint<0, 16667> driver_wakeup_delay{ this, "Driver Wake-Up Delay", 0, true }; cfg::_int<1, 6000> vblank_rate{ this, "Vblank Rate", 60, true }; // Changing this from 60 may affect game speed in unexpected ways cfg::_bool vblank_ntsc{ this, "Vblank NTSC Fixup", false, true }; cfg::_bool decr_memory_layout{ this, "DECR memory layout", false}; // Force enable increased allowed main memory range as DECR console From 91b1ec6d5c057512842a8b5860c961c2fbadc936 Mon Sep 17 00:00:00 2001 From: elad335 <18193363+elad335@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:59:06 +0200 Subject: [PATCH 4/6] SPU LLVM: Dump some funcs --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 56 ++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index c158318a8d..0df3e3a854 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -1531,8 +1531,6 @@ public: return add_loc->compiled; } - std::string log; - bool add_to_file = false; if (auto& cache = g_fxo->get(); cache && g_cfg.core.spu_cache && !add_loc->cached.exchange(1)) @@ -1566,10 +1564,35 @@ public: m_pp_id = 0; + std::string function_log; + + this->dump(func, function_log); + bool to_log_func = false; + if (g_cfg.core.spu_debug && !add_loc->logged.exchange(1)) { - this->dump(func, log); - fs::write_file(m_spurt->get_cache_path() + "spu.log", fs::write + fs::append, log); + if (!fs::write_file(m_spurt->get_cache_path() + "spu.log", fs::write + fs::append, function_log)) + { + // Fallback: write to main log + to_log_func = true; + } + } + + for (u32 data : func.data) + { + const spu_opcode_t op{std::bit_cast>(data)}; + + const auto itype = g_spu_itype.decode(op.opcode); + + if (itype == spu_itype::RDCH && op.ra == SPU_RdDec) + { + to_log_func = true; + } + } + + if (to_log_func) + { + spu_log.notice("Function %s dump:\n%s", m_hash, function_log); } using namespace llvm; @@ -2715,11 +2738,13 @@ public: m_function_queue.clear(); m_function_table = nullptr; - raw_string_ostream out(log); + // Append for now + std::string& llvm_log = function_log; + raw_string_ostream out(llvm_log); if (g_cfg.core.spu_debug) { - fmt::append(log, "LLVM IR at 0x%x:\n", func.entry_point); + fmt::append(llvm_log, "LLVM IR at 0x%x:\n", func.entry_point); out << *_module; // print IR out << "\n\n"; } @@ -2727,11 +2752,11 @@ public: if (verifyModule(*_module, &out)) { out.flush(); - spu_log.error("LLVM: Verification failed at 0x%x:\n%s", func.entry_point, log); + spu_log.error("LLVM: Verification failed at 0x%x:\n%s", func.entry_point, llvm_log); if (g_cfg.core.spu_debug) { - fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::write + fs::append, log); + fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::write + fs::append, llvm_log); } if (auto& cache = g_fxo->get()) @@ -2786,7 +2811,7 @@ public: if (g_cfg.core.spu_debug) { out.flush(); - fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, log); + fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, llvm_log); } #if defined(__APPLE__) @@ -3182,13 +3207,12 @@ public: run_transforms(f); } - std::string log; - - raw_string_ostream out(log); + std::string llvm_log; + raw_string_ostream out(llvm_log); if (g_cfg.core.spu_debug) { - fmt::append(log, "LLVM IR (interpreter):\n"); + fmt::append(llvm_log, "LLVM IR (interpreter):\n"); out << *_module; // print IR out << "\n\n"; } @@ -3196,11 +3220,11 @@ public: if (verifyModule(*_module, &out)) { out.flush(); - spu_log.error("LLVM: Verification failed:\n%s", log); + spu_log.error("LLVM: Verification failed:\n%s", llvm_log); if (g_cfg.core.spu_debug) { - fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, log); + fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, llvm_log); } fmt::throw_exception("Compilation failed"); @@ -3235,7 +3259,7 @@ public: if (g_cfg.core.spu_debug) { out.flush(); - fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, log); + fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, llvm_log); } return spu_runtime::g_interpreter; From 047f71b434c5f039a3c070eaa931d47de80a1ddd Mon Sep 17 00:00:00 2001 From: elad335 <18193363+elad335@users.noreply.github.com> Date: Sun, 23 Mar 2025 06:58:35 +0200 Subject: [PATCH 5/6] PPU/cellSpurs: MGS4: Fix cellSpursAddUrgentCommand race condition cellSpursAddUrgentCommand searches in 4 slots for an empty slot to put the command at. At first, it seems to do so unordered. Meanwhile, on SPU side, it expects an order between all the commands because it pops them it in FIFO manner. Not keeping track of how many commands are queued in total. After second observation of cellSpursAddUrgentCommand, something odd comes takes places here. Usually, reservation loops are individual and are expected to be closed without any changes of the previous loop affected by the proceeding one. But in this case, after a single failure, the entire operayion is reset, a loop of 4 reservation operations suddenly is reset completely. This makes one wonder if it the HW expects sometjing else here, perhaps it caches the reservation internally here? After some adjustments to LDARX and STDCX to cache the reservation between succeeding loops, Metal Gear Solid 4 no longer freezes! --- rpcs3/Emu/Cell/PPUInterpreter.cpp | 2 ++ rpcs3/Emu/Cell/PPUThread.cpp | 24 ++++++++++++++++++++++-- rpcs3/Emu/Cell/PPUThread.h | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index 690581acaa..4f75cd634a 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -484,6 +484,7 @@ auto ppu_feed_data(ppu_thread& ppu, u64 addr) { // Reservation was lost ppu.raddr = 0; + ppu.res_cached = 0; } } else @@ -503,6 +504,7 @@ auto ppu_feed_data(ppu_thread& ppu, u64 addr) if (std::memcmp(buffer + offs, src, size)) { ppu.raddr = 0; + ppu.res_cached = 0; } } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 90241016e6..bdd6eeae84 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3103,7 +3103,18 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) ppu.last_faddr = 0; } - ppu.rtime = vm::reservation_acquire(addr) & -128; + const u32 res_cached = ppu.res_cached; + + if ((addr & -128) == (res_cached & -128)) + { + // Reload "cached" reservation of previous succeeded conditional store + // This seems like a hardware feature according to cellSpursAddUrgentCommand function + ppu.rtime -= 128; + } + else + { + ppu.rtime = vm::reservation_acquire(addr) & -128; + } be_t rdata; @@ -3376,7 +3387,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) } // Test if store address is on the same aligned 8-bytes memory as load - if (const u32 raddr = std::exchange(ppu.raddr, 0); raddr / 8 != addr / 8) + if (const u32 raddr = ppu.raddr; raddr / 8 != addr / 8) { // If not and it is on the same aligned 128-byte memory, proceed only if 128-byte reservations are enabled // In realhw the store address can be at any address of the 128-byte cache line @@ -3389,12 +3400,16 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) data += 0; } + ppu.raddr = 0; + ppu.res_cached = 0; return false; } } if (old_data != data || rtime != (res & -128)) { + ppu.raddr = 0; + ppu.res_cached = 0; return false; } @@ -3650,6 +3665,9 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) } ppu.last_faddr = 0; + ppu.res_cached = ppu.raddr; + ppu.rtime += 128; + ppu.raddr = 0; return true; } @@ -3669,6 +3687,8 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) ppu.res_notify = 0; } + ppu.raddr = 0; + ppu.res_cached = 0; return false; } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 0e4ec1bb1a..322cc13ebe 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -264,6 +264,7 @@ public: u64 rtime{0}; alignas(64) std::byte rdata[128]{}; // Reservation data bool use_full_rdata{}; + u32 res_cached{0}; // Reservation "cached" addresss u32 res_notify{0}; u64 res_notify_time{0}; From 9c99e75939af118cddcf4c0fc5c45c295cbf38ee Mon Sep 17 00:00:00 2001 From: elad335 <18193363+elad335@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:11:22 +0300 Subject: [PATCH 6/6] SPU Debug: WrDec and LSA view Report the last written value to WrDec. --- rpcs3/Emu/Cell/SPUThread.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index faf78babb3..6a8c3aeceb 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1204,18 +1204,30 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const std::vector gpr_saved(128); be_t rdata_saved[32]{}; + be_t lsa_saved[32]{}; + spu_mfc_cmd mfc_regs_saved{}; u32 saved_pc = umax; + const u8* lsa_state_ptr = nullptr; + + const u8* lsa_ptr = _ptr(ch_mfc_cmd.lsa); // Load PC, GPRs and reservation data atomically // We may not load the entire context atomically, but there is importance their state being intact for debugging do { saved_pc = pc; + + // Account for list transfer: record EAL instead + mfc_regs_saved = ch_mfc_cmd; + lsa_state_ptr = _ptr(mfc_regs_saved.eal < SPU_LS_SIZE && mfc_regs_saved.eal % 8 == 0 ? mfc_regs_saved.eal : mfc_regs_saved.lsa); + std::memcpy(gpr_saved.data(), gpr.data(), sizeof(v128) * gpr.size()); std::memcpy(rdata_saved, rdata, sizeof(rdata)); + std::memcpy(lsa_saved, lsa_state_ptr, std::min(128, SPU_LS_SIZE - (lsa_state_ptr - _ptr(0)))); atomic_fence_acquire(); } - while (saved_pc != pc || std::memcmp(rdata_saved, rdata, sizeof(rdata)) != 0 || std::memcmp(gpr_saved.data(), gpr.data(), sizeof(v128) * gpr.size()) != 0); + while (saved_pc != pc || std::memcmp(rdata_saved, rdata, sizeof(rdata)) != 0 || std::memcmp(gpr_saved.data(), gpr.data(), sizeof(v128) * gpr.size()) != 0 + || std::memcmp(&mfc_regs_saved, &ch_mfc_cmd, sizeof(mfc_regs_saved)) != 0 || std::memcmp(lsa_saved, lsa_state_ptr, std::min(128, SPU_LS_SIZE - (lsa_state_ptr - _ptr(0)))) != 0); for (u32 i = 0; i < 128; i++, ret += '\n') { @@ -1350,6 +1362,7 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const fmt::append(ret, "SNR config: 0x%llx\n", snr_config); fmt::append(ret, "SNR1: %s\n", ch_snr1); fmt::append(ret, "SNR2: %s\n", ch_snr2); + fmt::append(ret, "Last WrDec: %-9d (0x%08x) (%s)\n", ch_dec_value, ch_dec_value, is_dec_frozen ? "suspend" : "running"); if (get_type() != spu_type::threaded) { @@ -1388,6 +1401,14 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const fmt::append(ret, "[0x%02x] %08x %08x %08x %08x\n", i * sizeof(rdata_saved[0]) , rdata_saved[i + 0], rdata_saved[i + 1], rdata_saved[i + 2], rdata_saved[i + 3]); } + + fmt::append(ret, "\nLSA Data:\n"); + + for (usz i = 0; i < std::size(lsa_saved); i += 4) + { + fmt::append(ret, "[0x%02x] %08x %08x %08x %08x\n", i * sizeof(lsa_saved[0]) + , lsa_saved[i + 0], lsa_saved[i + 1], lsa_saved[i + 2], lsa_saved[i + 3]); + } } std::string spu_thread::dump_callstack() const