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)) { 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}; 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; 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 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_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 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: