From 3033914e2dbb12fc58b4f66c4b0c15cb65478a84 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Jul 2023 11:35:53 +0300 Subject: [PATCH 01/13] Game List: Implement Remove HDD1 Cache option --- rpcs3/rpcs3qt/game_list_frame.cpp | 77 ++++++++++++++++++++++++++++++- rpcs3/rpcs3qt/game_list_frame.h | 1 + 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 76ec618526..0879fb3a44 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1141,7 +1141,10 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) } }); } - if (fs::is_dir(cache_base_dir)) + + const bool has_cache_dir = fs::is_dir(cache_base_dir); + + if (has_cache_dir) { remove_menu->addSeparator(); QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache")); @@ -1159,8 +1162,33 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { RemoveSPUCache(cache_base_dir, true); }); + } + + bool has_hdd1_cache = false; + const std::string hdd1 = rpcs3::utils::get_hdd1_dir() + "/caches/"; + + for (const auto& entry : fs::dir(hdd1)) + { + if (entry.is_directory && entry.name.starts_with(current_game.serial)) + { + has_hdd1_cache = true; + break; + } + } + + if (has_hdd1_cache) + { + QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache")); + connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1, serial = current_game.serial]() + { + RemoveHDD1Cache(hdd1, serial, true); + }); + } + + if (has_cache_dir || has_hdd1_cache) + { QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches")); - connect(remove_all_caches, &QAction::triggered, [this, cache_base_dir]() + connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1]() { if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes) return; @@ -1169,8 +1197,11 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) game_list_log.success("Removed cache directory: '%s'", cache_base_dir); else game_list_log.error("Could not remove cache directory: '%s' (%s)", cache_base_dir, fs::g_tls_error); + + RemoveHDD1Cache(hdd1, current_game.serial); }); } + menu.addSeparator(); QAction* open_game_folder = menu.addAction(tr("&Open Install Folder")); if (gameinfo->hasCustomConfig) @@ -1812,6 +1843,48 @@ bool game_list_frame::RemoveSPUCache(const std::string& base_dir, bool is_intera return success; } +void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive) +{ + if (!fs::is_dir(base_dir)) + return; + + if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove HDD1 cache?")) != QMessageBox::Yes) + return; + + u32 dirs_removed = 0; + u32 dirs_total = 0; + + const QString q_base_dir = qstr(base_dir); + + const QStringList filter{ qstr(title_id + "_*") }; + + QDirIterator dir_iter(q_base_dir, filter, QDir::Dirs | QDir::NoDotAndDotDot); + + while (dir_iter.hasNext()) + { + const QString filepath = dir_iter.next(); + + if (fs::remove_all(filepath.toStdString())) + { + ++dirs_removed; + game_list_log.notice("Removed HDD1 cache directory: %s", filepath); + } + else + { + game_list_log.warning("Could not remove HDD1 cache directory: %s", filepath); + } + + ++dirs_total; + } + + const bool success = dirs_removed == dirs_total; + + if (success) + game_list_log.success("Removed HDD1 cache in %s (%s)", base_dir, title_id); + else + game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id); +} + void game_list_frame::BatchCreatePPUCaches() { const std::string vsh_path = g_cfg_vfs.get_dev_flash() + "vsh/module/"; diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 50a18282e5..582d407a52 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -107,6 +107,7 @@ private: bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false); bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); + void RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive = false); static bool CreatePPUCache(const std::string& path, const std::string& serial = {}); static bool CreatePPUCache(const game_info& game); From afdfefc9adc9a1df6351ebaa70be8ac4f4ebd2e5 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Jul 2023 15:20:36 +0300 Subject: [PATCH 02/13] Game List: Fix allowance of caches/games to be removed while the game is running! --- rpcs3/rpcs3qt/game_list_frame.cpp | 37 ++++++++++++++++++++++++++++++- rpcs3/rpcs3qt/main_window.cpp | 11 +++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 0879fb3a44..b6e7fd2ffe 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1010,7 +1010,12 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) // Make Actions QMenu menu; - const bool is_current_running_game = (Emu.IsRunning() || Emu.IsPaused()) && current_game.serial == Emu.GetTitleID(); + static const auto is_game_running = [](const std::string& serial) + { + return Emu.GetStatus(false) != system_state::stopped && serial == Emu.GetTitleID(); + }; + + const bool is_current_running_game = is_game_running(current_game.serial); QAction* boot = new QAction(gameinfo->hasCustomConfig ? (is_current_running_game @@ -1119,6 +1124,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) menu.addSeparator(); QMenu* remove_menu = menu.addMenu(tr("&Remove")); QAction* remove_game = remove_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category)); + remove_game->setEnabled(!is_current_running_game); if (gameinfo->hasCustomConfig) { QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration")); @@ -1148,16 +1154,19 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { remove_menu->addSeparator(); QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache")); + remove_shaders_cache->setEnabled(!is_current_running_game); connect(remove_shaders_cache, &QAction::triggered, [this, cache_base_dir]() { RemoveShadersCache(cache_base_dir, true); }); QAction* remove_ppu_cache = remove_menu->addAction(tr("&Remove PPU Cache")); + remove_ppu_cache->setEnabled(!is_current_running_game); connect(remove_ppu_cache, &QAction::triggered, [this, cache_base_dir]() { RemovePPUCache(cache_base_dir, true); }); QAction* remove_spu_cache = remove_menu->addAction(tr("&Remove SPU Cache")); + remove_spu_cache->setEnabled(!is_current_running_game); connect(remove_spu_cache, &QAction::triggered, [this, cache_base_dir]() { RemoveSPUCache(cache_base_dir, true); @@ -1179,6 +1188,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) if (has_hdd1_cache) { QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache")); + remove_hdd1_cache->setEnabled(!is_current_running_game); connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1, serial = current_game.serial]() { RemoveHDD1Cache(hdd1, serial, true); @@ -1188,8 +1198,12 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) if (has_cache_dir || has_hdd1_cache) { QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches")); + remove_all_caches->setEnabled(!is_current_running_game); connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1]() { + if (is_game_running(current_game.serial)) + return; + if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes) return; @@ -1435,6 +1449,12 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) return; } + if (is_game_running(current_game.serial)) + { + QMessageBox::critical(this, tr("Cannot Remove Game"), tr("The PS3 application is still running, it cannot be removed!")); + return; + } + QString size_information; if (current_game.size_on_disk != umax) @@ -1976,6 +1996,11 @@ void game_list_frame::BatchCreatePPUCaches() void game_list_frame::BatchRemovePPUCaches() { + if (Emu.GetStatus(false) != system_state::stopped) + { + return; + } + std::set serials; for (const auto& game : m_game_data) { @@ -2017,6 +2042,11 @@ void game_list_frame::BatchRemovePPUCaches() void game_list_frame::BatchRemoveSPUCaches() { + if (Emu.GetStatus(false) != system_state::stopped) + { + return; + } + std::set serials; for (const auto& game : m_game_data) { @@ -2148,6 +2178,11 @@ void game_list_frame::BatchRemoveCustomPadConfigurations() void game_list_frame::BatchRemoveShaderCaches() { + if (Emu.GetStatus(false) != system_state::stopped) + { + return; + } + std::set serials; for (const auto& game : m_game_data) { diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index a2025de926..ba884712e2 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1909,6 +1909,12 @@ void main_window::OnEmuStop() m_thumb_restart->setEnabled(true); #endif } + + ui->batchRemovePPUCachesAct->setEnabled(true); + ui->batchRemoveSPUCachesAct->setEnabled(true); + ui->batchRemoveShaderCachesAct->setEnabled(true); + ui->removeDiskCacheAct->setEnabled(true); + ui->actionManage_Users->setEnabled(true); ui->confCamerasAct->setEnabled(true); @@ -1951,6 +1957,11 @@ void main_window::OnEmuReady() const ui->actionManage_Users->setEnabled(false); ui->confCamerasAct->setEnabled(false); + + ui->batchRemovePPUCachesAct->setEnabled(false); + ui->batchRemoveSPUCachesAct->setEnabled(false); + ui->batchRemoveShaderCachesAct->setEnabled(false); + ui->removeDiskCacheAct->setEnabled(false); } void main_window::EnableMenus(bool enabled) const From e303cc536cbc120d4a80c639d37d33c8f17c6f34 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Jul 2023 15:56:15 +0300 Subject: [PATCH 03/13] Qt: Fix GetBootConfirmation() --- rpcs3/rpcs3qt/gui_settings.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rpcs3/rpcs3qt/gui_settings.cpp b/rpcs3/rpcs3qt/gui_settings.cpp index adaa2bad82..d89ef43157 100644 --- a/rpcs3/rpcs3qt/gui_settings.cpp +++ b/rpcs3/rpcs3qt/gui_settings.cpp @@ -9,6 +9,8 @@ #include #include +#include + LOG_CHANNEL(cfg_log, "CFG"); namespace gui @@ -188,6 +190,12 @@ void gui_settings::ShowInfoBox(const QString& title, const QString& text, const bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry) { + while (Emu.GetStatus(false) == system_state::stopping) + { + QCoreApplication::processEvents(); + std::this_thread::sleep_for(16ms); + } + if (!Emu.IsStopped()) { QString title = tr("Close Running Game?"); From 9ff91c29f2804eed20ee383fc4dec412a33bcc38 Mon Sep 17 00:00:00 2001 From: Eladash Date: Fri, 21 Jul 2023 18:23:09 +0300 Subject: [PATCH 04/13] Invalidate Savestates --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 12 +----------- rpcs3/Emu/Cell/PPUThread.cpp | 16 +--------------- rpcs3/Emu/Cell/lv2/sys_net.cpp | 5 +---- rpcs3/Emu/Cell/lv2/sys_prx.cpp | 18 ++---------------- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 14 ++------------ rpcs3/Emu/RSX/RSXThread.cpp | 9 +++------ rpcs3/Emu/savestate_utils.cpp | 14 +++++++------- 7 files changed, 17 insertions(+), 71 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index f789c0c0f6..7b295e2d0e 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -335,17 +335,7 @@ public: [[maybe_unused]] const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellGem); ar(attribute, vc_attribute, status_flags, enable_pitch_correction, inertial_counter, controllers - , connected_controllers, update_started, camera_frame, memory_ptr); - - if (version == 1 && !ar.is_writing()) - { - u32 ts = ar; - start_timestamp_us = ts; - } - else - { - ar(start_timestamp_us); - } + , connected_controllers, update_started, camera_frame, memory_ptr, start_timestamp_us); } gem_config_data(utils::serial& ar) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index b86cde77b9..b7e5fa0dde 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2030,21 +2030,7 @@ void ppu_thread::serialize_common(utils::serial& ar) { [[maybe_unused]] const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), ppu); - ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj); - - if (ar.is_writing()) - { - ar(prio.load().all); - } - else if (version < 2) - { - prio.raw().all = 0; - prio.raw().prio = ar.operator s32(); - } - else - { - ar(prio.raw().all); - } + ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio.raw().all); ar(optional_savestate_state, vr); diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 9a547a7a0a..387cb3f623 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -259,10 +259,7 @@ lv2_socket::lv2_socket(utils::serial& ar, lv2_socket_type _type) const s32 version = GET_SERIALIZATION_VERSION(lv2_net); - if (version >= 2) - { - ar(so_rcvtimeo, so_sendtimeo); - } + ar(so_rcvtimeo, so_sendtimeo); lv2_id = idm::last_id(); diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index bfbf8c5fa1..4cde5d52e9 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -312,11 +312,7 @@ std::shared_ptr lv2_prx::load(utils::serial& ar) { std::basic_string loaded_flags, external_flags; - if (version >= 4) - { - ar(loaded_flags); - ar(external_flags); - } + ar(loaded_flags, external_flags); fs::file file{path.substr(0, path.size() - (offset ? fmt::format("_x%x", offset).size() : 0))}; @@ -328,21 +324,11 @@ std::shared_ptr lv2_prx::load(utils::serial& ar) prx->m_loaded_flags = std::move(loaded_flags); prx->m_external_loaded_flags = std::move(external_flags); - if (version == 2 && (state == PRX_STATE_STARTED || state == PRX_STATE_STARTING)) - { - prx->load_exports(); - } - - if (version >= 4 && state <= PRX_STATE_STARTED) + if (state <= PRX_STATE_STARTED) { prx->restore_exports(); } - if (version == 1) - { - prx->load_exports(); - } - ensure(prx); } else diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index d3cb822239..0ff409e1ae 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -215,14 +215,7 @@ lv2_spu_group::lv2_spu_group(utils::serial& ar) noexcept { std::common_type_t prio{}; - if (GET_SERIALIZATION_VERSION(spu) < 3) - { - prio.prio = ar.operator s32(); - } - else - { - ar(prio.all); - } + ar(prio.all); return prio; }()) @@ -387,10 +380,7 @@ struct spu_limits_t spu_limits_t(utils::serial& ar) noexcept { - if (GET_SERIALIZATION_VERSION(spu) >= 2) - { - ar(max_raw, max_spu); - } + ar(max_raw, max_spu); } void save(utils::serial& ar) diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 0b6b8efe9b..6f82e38a39 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -506,13 +506,10 @@ namespace rsx ar(u32{0}); } } - else if (version > 1) + else if (u32 count = ar) { - if (u32 count = ar) - { - restore_fifo_count = count; - ar(restore_fifo_cmd); - } + restore_fifo_count = count; + ar(restore_fifo_cmd); } } diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 54175aba6a..370fa57bf9 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -35,20 +35,20 @@ static std::array s_serial_versions; return ::s_serial_versions[identifier].current_version;\ } -SERIALIZATION_VER(global_version, 0, 12) // For stuff not listed here -SERIALIZATION_VER(ppu, 1, 1, 2 /*thread sleep queue order*/) -SERIALIZATION_VER(spu, 2, 1, 2 /*spu_limits_t ctor*/, 3 /*thread sleep queue order*/) +SERIALIZATION_VER(global_version, 0, 13) // For stuff not listed here +SERIALIZATION_VER(ppu, 1, 1) +SERIALIZATION_VER(spu, 2, 1) SERIALIZATION_VER(lv2_sync, 3, 1) SERIALIZATION_VER(lv2_vm, 4, 1) -SERIALIZATION_VER(lv2_net, 5, 1, 2/*RECV/SEND timeout*/) +SERIALIZATION_VER(lv2_net, 5, 1) SERIALIZATION_VER(lv2_fs, 6, 1) -SERIALIZATION_VER(lv2_prx_overlay, 7, 1, 2/*PRX dynamic exports*/, 4/*Conditionally Loaded Local Exports*/) +SERIALIZATION_VER(lv2_prx_overlay, 7, 1) SERIALIZATION_VER(lv2_memory, 8, 1) SERIALIZATION_VER(lv2_config, 9, 1) namespace rsx { - SERIALIZATION_VER(rsx, 10, 1, 2) + SERIALIZATION_VER(rsx, 10, 1) } namespace np @@ -65,7 +65,7 @@ SERIALIZATION_VER(sceNp, 11) SERIALIZATION_VER(cellVdec, 12, 1) SERIALIZATION_VER(cellAudio, 13, 1) SERIALIZATION_VER(cellCamera, 14, 1) -SERIALIZATION_VER(cellGem, 15, 1, 2/*frame_timestamp u32->u64*/) +SERIALIZATION_VER(cellGem, 15, 1) SERIALIZATION_VER(sceNpTrophy, 16, 1) SERIALIZATION_VER(cellMusic, 17, 1) SERIALIZATION_VER(cellVoice, 18, 1) From c44cddabfa79007f3b674be4b7b4adb3b00e5317 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 22 Jul 2023 06:08:35 +0300 Subject: [PATCH 05/13] CPUThread.cpp: Fix use of cpu_counter::add This also fixes a crash when saving savestate because main thread uses cpu_counter::suspend_all which adds cuncurrency. --- rpcs3/Emu/CPU/CPUThread.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index d0cf1e100e..f214026470 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -789,6 +789,19 @@ bool cpu_thread::check_state() noexcept return store; } + if (s_tls_thread_slot == umax) + { + if (cpu_flag::wait - state) + { + // Force wait flag (must be set during ownership of s_cpu_lock), this makes the atomic op fail as a side effect + state += cpu_flag::wait; + store = true; + } + + // Restore thread in the suspend list + cpu_counter::add(this); + } + if (flags & cpu_flag::wait) { flags -= cpu_flag::wait; @@ -843,12 +856,6 @@ bool cpu_thread::check_state() noexcept if (escape) { - if (s_tls_thread_slot == umax && !retval) - { - // Restore thread in the suspend list - cpu_counter::add(this); - } - if (cpu_can_stop && state0 & cpu_flag::pending) { // Execute pending work From 4647f3046ef9ef9d88eaa46d8f5c783d2bbe2852 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 22 Jul 2023 08:43:18 +0300 Subject: [PATCH 06/13] Qt: Minor logging change --- rpcs3/rpcs3qt/main_window.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index ba884712e2..fa7ea4108e 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -182,15 +182,19 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) connect(m_thumb_stop, &QWinThumbnailToolButton::clicked, this, []() { - gui_log.notice("User clicked stop button on thumbnail toolbar"); + gui_log.notice("User clicked the stop button on thumbnail toolbar"); Emu.GracefulShutdown(false, true); }); connect(m_thumb_restart, &QWinThumbnailToolButton::clicked, this, []() { - gui_log.notice("User clicked restart button on thumbnail toolbar"); + gui_log.notice("User clicked the restart button on thumbnail toolbar"); Emu.Restart(); }); - connect(m_thumb_playPause, &QWinThumbnailToolButton::clicked, this, &main_window::OnPlayOrPause); + connect(m_thumb_playPause, &QWinThumbnailToolButton::clicked, this, [this]() + { + gui_log.notice("User clicked the playPause button on thumbnail toolbar"); + OnPlayOrPause(); + }); #endif // RPCS3 Updater From 9fc5f6271b59ff6efe3d02f32d2af73feebb8564 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 22 Jul 2023 11:30:50 +0300 Subject: [PATCH 07/13] Update SPU reservation notifier mask --- rpcs3/Emu/CPU/CPUThread.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_spu.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index f214026470..dd26620ab1 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -1004,7 +1004,7 @@ cpu_thread& cpu_thread::operator=(thread_state) { if (u32 resv = atomic_storage::load(thread->raddr)) { - vm::reservation_notifier(resv).notify_one(); + vm::reservation_notifier(resv).notify_all(-128); } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 0ff409e1ae..7ccbc6acb1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -1397,7 +1397,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (prev_resv && prev_resv != resv) { // Batch reservation notifications if possible - vm::reservation_notifier(prev_resv).notify_all(); + vm::reservation_notifier(prev_resv).notify_all(-128); } prev_resv = resv; @@ -1407,7 +1407,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (prev_resv) { - vm::reservation_notifier(prev_resv).notify_all(); + vm::reservation_notifier(prev_resv).notify_all(-128); } group->exit_status = value; From 99671b754f9f57ddcf133840b2ddc9659a89bdd2 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 22 Jul 2023 12:03:45 +0300 Subject: [PATCH 08/13] Add ppu_module::get_ref() to ease debugging --- rpcs3/Emu/Cell/PPUAnalyser.cpp | 12 ++++++------ rpcs3/Emu/Cell/PPUAnalyser.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 4efe2a5291..ebda9c2580 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -78,7 +78,7 @@ void ppu_module::validate(u32 reloc) if (size && size != funcs[index].size) { - if (size + 4 != funcs[index].size || *ensure(get_ptr(addr + size)) != ppu_instructions::NOP()) + if (size + 4 != funcs[index].size || get_ref(addr + size) != ppu_instructions::NOP()) { ppu_validator.error("%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", path, found, funcs[index].size, addr, size); } @@ -733,7 +733,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Register TOC from entry point if (entry && !lib_toc) { - lib_toc = *ensure(get_ptr(entry)) ? *ensure(get_ptr(entry + 4)) : *ensure(get_ptr(entry + 20)); + lib_toc = get_ref(entry) ? get_ref(entry + 4) : get_ref(entry + 20); } // Secondary attempt @@ -1425,7 +1425,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b for (vm::cptr _ptr = vm::cast(block.first); _ptr.addr() < block.first + block.second;) { const u32 iaddr = _ptr.addr(); - const ppu_opcode_t op{*ensure(get_ptr(_ptr++))}; + const ppu_opcode_t op{get_ref(_ptr++)}; const ppu_itype::type type = s_ppu_itype.decode(op.opcode); if (type == ppu_itype::B || type == ppu_itype::BC) @@ -1499,7 +1499,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b for (vm::cptr _ptr = vm::cast(start); _ptr.addr() < next;) { const u32 addr = _ptr.addr(); - const ppu_opcode_t op{*ensure(get_ptr(_ptr++))}; + const ppu_opcode_t op{get_ref(_ptr++)}; const ppu_itype::type type = s_ppu_itype.decode(op.opcode); if (type == ppu_itype::UNK) @@ -1641,7 +1641,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b continue; } - const u32 target = *ensure(get_ptr(rel.addr)); + const u32 target = get_ref(rel.addr); if (target % 4 || target < start || target >= end) { @@ -1718,7 +1718,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b for (; i_pos < lim; i_pos += 4) { - const u32 opc = *ensure(get_ptr(i_pos)); + const u32 opc = get_ref(i_pos); switch (auto type = s_ppu_itype.decode(opc)) { diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 8c5330c2b3..d7e34e76a0 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -148,6 +148,40 @@ struct ppu_module constexpr usz size_element = std::is_void_v ? 0 : sizeof(std::conditional_t, char, T>); return get_ptr(addr.addr(), u32{size_element}); } + + template + to_be_t& get_ref(u32 addr, + u32 line = __builtin_LINE(), + u32 col = __builtin_COLUMN(), + const char* file = __builtin_FILE(), + const char* func = __builtin_FUNCTION()) const + { + constexpr usz size_element = std::is_void_v ? 0 : sizeof(std::conditional_t, char, T>); + if (auto ptr = get_ptr(addr, u32{size_element})) + { + return *ptr; + } + + fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", addr, src_loc{line, col, file, func}); + return *std::add_pointer_t>{}; + } + + template requires requires (const U& obj) { +obj.size() * 0; } + to_be_t& get_ref(U&& addr, + u32 line = __builtin_LINE(), + u32 col = __builtin_COLUMN(), + const char* file = __builtin_FILE(), + const char* func = __builtin_FUNCTION()) const + { + constexpr usz size_element = std::is_void_v ? 0 : sizeof(std::conditional_t, char, T>); + if (auto ptr = get_ptr(addr.addr(), u32{size_element})) + { + return *ptr; + } + + fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", addr.addr(), src_loc{line, col, file, func}); + return *std::add_pointer_t>{}; + } }; struct main_ppu_module : public ppu_module From 3bbc2c1f4cc634968ec557a8069c4d46cabd93f7 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 22 Jul 2023 16:06:51 +0300 Subject: [PATCH 09/13] PPU Analyzer: Fixup get_ptr() --- rpcs3/Emu/Cell/PPUAnalyser.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index d7e34e76a0..cde7fb7949 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -6,6 +6,7 @@ #include #include "util/types.hpp" #include "util/endian.hpp" +#include "util/asm.hpp" #include "util/to_endian.hpp" #include "Utilities/bit_set.h" @@ -127,7 +128,7 @@ struct ppu_module const u32 seg_size = seg.size; const u32 seg_addr = seg.addr; - if (seg_size >= std::max(size_bytes, 1) && addr <= seg_addr + seg_size - size_bytes) + if (seg_size >= std::max(size_bytes, 1) && addr <= utils::align(seg_addr + seg_size, 0x10000) - size_bytes) { return reinterpret_cast*>(static_cast(seg.ptr) + (addr - seg_addr)); } From 12fe55a2585be7abf0ec09dacd845354b8f73c29 Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 22 Jul 2023 17:46:29 +0300 Subject: [PATCH 10/13] Thread.cpp: Always print SPU thread name in access violation Suppress GUI log window prefix removal. --- Utilities/Thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 1fa5c301fd..3ab1566884 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1635,7 +1635,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe if (!g_tls_access_violation_recovered) { vm_log.notice("\n%s", dump_useful_thread_info()); - vm_log.error("Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); + vm_log.error("[%s] Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", cpu->get_name(), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); } // TODO: From fce8e0fef0787bc50c24cb7533a98fe4815de1ce Mon Sep 17 00:00:00 2001 From: Eladash Date: Sun, 23 Jul 2023 10:48:17 +0300 Subject: [PATCH 11/13] Savestates: Facilitate multi slots --- rpcs3/Emu/System.cpp | 13 +++++++++---- rpcs3/Emu/savestate_utils.cpp | 20 ++++++++++++++++++-- rpcs3/rpcs3qt/game_list_frame.cpp | 4 +++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 5674623bcc..c005e67371 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -82,7 +82,7 @@ extern std::pair, CellError> ppu_load_overlay(const extern bool ppu_load_rel_exec(const ppu_rel_object&); extern bool is_savestate_version_compatible(const std::vector>& data, bool is_boot_check); extern std::vector> read_used_savestate_versions(); -std::string get_savestate_path(std::string_view title_id, std::string_view boot_path); +std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id); extern void send_close_home_menu_cmds(); @@ -492,7 +492,7 @@ void Emulator::Init() make_path_verbose(fs::get_cache_dir() + "shaderlog/", false); make_path_verbose(fs::get_cache_dir() + "spu_progs/", false); - make_path_verbose(fs::get_cache_dir() + "/savestates/", false); + make_path_verbose(fs::get_parent_dir(get_savestate_file("NO_ID", "/NO_FILE", -1, -1)), false); make_path_verbose(fs::get_config_dir() + "captures/", false); make_path_verbose(fs::get_config_dir() + "sounds/", false); make_path_verbose(patch_engine::get_patches_path(), false); @@ -2180,7 +2180,7 @@ void Emulator::FixGuestTime() // Mark a known savestate location and the one we try to boot (in case we boot a moved/copied savestate) if (g_cfg.savestate.suspend_emu) { - for (std::string old_path : std::initializer_list{m_ar ? m_path_old : "", m_title_id.empty() ? "" : get_savestate_path(m_title_id, m_path_old)}) + for (std::string old_path : std::initializer_list{m_ar ? m_path_old : "", m_title_id.empty() ? "" : get_savestate_file(m_title_id, m_path_old, 0, 0)}) { if (old_path.empty()) { @@ -2841,7 +2841,12 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) if (savestate) { - const std::string path = get_savestate_path(m_title_id, m_path); + const std::string path = get_savestate_file(m_title_id, m_path, 0, 0); + + if (!fs::create_path(fs::get_parent_dir(path))) + { + sys_log.error("Failed to create savestate directory! (path='%s', %s)", fs::get_parent_dir(path), fs::g_tls_error); + } fs::pending_file file(path); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 370fa57bf9..e76bff203f 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -146,9 +146,25 @@ bool is_savestate_version_compatible(const std::vector>& dat return ok; } -std::string get_savestate_path(std::string_view title_id, std::string_view boot_path) +std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id) { - return fs::get_cache_dir() + "/savestates/" + std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id} + ".SAVESTAT"; + const std::string title = std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id}; + + if (abs_id == -1 && rel_id == -1) + { + // Return directory + return fs::get_cache_dir() + "/savestates/" + title + "/"; + } + + ensure(rel_id < 0 || abs_id >= 0, "Unimplemented!"); + + const std::string save_id = fmt::format("%d", abs_id); + + // Make sure that savestate file with higher IDs are placed at the bottom of "by name" file ordering in directory view by adding a single character prefix which tells the ID length + // While not needing to keep a 59 chars long suffix at all times for this purpose + const char prefix = ::at32("0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"sv, save_id.size()); + + return fs::get_cache_dir() + "/savestates/" + title + "/" + title + '_' + prefix + '_' + save_id + ".SAVESTAT"; } bool is_savestate_compatible(const fs::file& file) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index b6e7fd2ffe..4acf9649da 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -50,6 +50,8 @@ LOG_CHANNEL(sys_log, "SYS"); extern atomic_t g_system_progress_canceled; +std::string get_savestate_file(std::string_view title_id, std::string_view boot_pat, s64 abs_id, s64 rel_id); + inline std::string sstr(const QString& _in) { return _in.toStdString(); } game_list_frame::game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent) @@ -1078,7 +1080,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) extern bool is_savestate_compatible(const fs::file& file); - if (const std::string sstate = fs::get_cache_dir() + "/savestates/" + current_game.serial + ".SAVESTAT"; is_savestate_compatible(fs::file(sstate))) + if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(fs::file(sstate))) { QAction* boot_state = menu.addAction(is_current_running_game ? tr("&Reboot with savestate") From a975b4937a37e4aab0c0a8f9f797791ec8a8522e Mon Sep 17 00:00:00 2001 From: brian218 Date: Mon, 24 Jul 2023 01:49:00 +0800 Subject: [PATCH 12/13] USIO: Added support for Tekken Tag Tournament 2 --- rpcs3/Emu/Io/usio.cpp | 258 ++++++++++++++++++++++++------------- rpcs3/Emu/Io/usio.h | 3 +- rpcs3/Emu/Io/usio_config.h | 46 ++++--- 3 files changed, 196 insertions(+), 111 deletions(-) diff --git a/rpcs3/Emu/Io/usio.cpp b/rpcs3/Emu/Io/usio.cpp index 80d64c4ac5..287ad3b718 100644 --- a/rpcs3/Emu/Io/usio.cpp +++ b/rpcs3/Emu/Io/usio.cpp @@ -1,5 +1,4 @@ // v406 USIO emulator -// Responses may be specific to Taiko no Tatsujin #include "stdafx.h" #include "usio.h" @@ -18,18 +17,21 @@ void fmt_class_string::format(std::string& out, u64 arg) { case usio_btn::test: return "Test"; case usio_btn::coin: return "Coin"; - case usio_btn::enter: return "Enter"; + case usio_btn::service: return "Service"; + case usio_btn::enter: return "Enter/Start"; case usio_btn::up: return "Up"; case usio_btn::down: return "Down"; - case usio_btn::service: return "Service"; - case usio_btn::strong_hit_side_left: return "Strong Hit Side Left"; - case usio_btn::strong_hit_side_right: return "Strong Hit Side Right"; - case usio_btn::strong_hit_center_left: return "Strong Hit Center Left"; - case usio_btn::strong_hit_center_right: return "Strong Hit Center Right"; - case usio_btn::small_hit_side_left: return "Small Hit Side Left"; - case usio_btn::small_hit_side_right: return "Small Hit Side Right"; - case usio_btn::small_hit_center_left: return "Small Hit Center Left"; - case usio_btn::small_hit_center_right: return "Small Hit Center Right"; + case usio_btn::left: return "Left"; + case usio_btn::right: return "Right"; + case usio_btn::taiko_hit_side_left: return "Taiko Hit Side Left"; + case usio_btn::taiko_hit_side_right: return "Taiko Hit Side Right"; + case usio_btn::taiko_hit_center_left: return "Taiko Hit Center Left"; + case usio_btn::taiko_hit_center_right: return "Taiko Hit Center Right"; + case usio_btn::tekken_button1: return "Tekken Button 1"; + case usio_btn::tekken_button2: return "Tekken Button 2"; + case usio_btn::tekken_button3: return "Tekken Button 3"; + case usio_btn::tekken_button4: return "Tekken Button 4"; + case usio_btn::tekken_button5: return "Tekken Button 5"; case usio_btn::count: return "Count"; } @@ -47,11 +49,11 @@ struct usio_memory void init() { backup_memory.clear(); - backup_memory.resize(chip_size * chip_count); + backup_memory.resize(page_size * page_count); } - static constexpr usz chip_size = 0x10000; - static constexpr usz chip_count = 0x10; + static constexpr usz page_size = 0x10000; + static constexpr usz page_count = 0x10; }; usb_device_usio::usb_device_usio(const std::array& location) @@ -179,15 +181,14 @@ void usb_device_usio::save_backup() usio_backup_file.trunc(file_size); } -void usb_device_usio::translate_input() +void usb_device_usio::translate_input_taiko() { std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_current_handler(); - std::vector input_buf = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - constexpr le_t c_small_hit = 0x4D0; - constexpr le_t c_big_hit = 0x1800; - le_t test_keys = 0x0000; + std::vector input_buf(96); + constexpr le_t c_hit = 0x1800; + le_t digital_input = 0; auto translate_from_pad = [&](usz pad_number, usz player) { @@ -202,7 +203,7 @@ void usb_device_usio::translate_input() return; } - const usz offset = (player * 8ULL); + const usz offset = player * 8ULL; const auto& cfg = ::at32(g_cfg_usio.players, pad_number); cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed) @@ -221,89 +222,160 @@ void usb_device_usio::translate_input() coin_counter++; coin_key_pressed = pressed; break; + case usio_btn::service: + if (player == 0 && pressed) + digital_input |= 0x4000; + break; case usio_btn::enter: if (player == 0 && pressed) - test_keys |= 0x200; // Enter + digital_input |= 0x200; break; case usio_btn::up: if (player == 0 && pressed) - test_keys |= 0x2000; // Up + digital_input |= 0x2000; break; case usio_btn::down: if (player == 0 && pressed) - test_keys |= 0x1000; // Down + digital_input |= 0x1000; break; - case usio_btn::service: - if (player == 0 && pressed) - test_keys |= 0x4000; // Service - break; - case usio_btn::strong_hit_side_left: - // Strong hit side left + case usio_btn::taiko_hit_side_left: if (pressed) - std::memcpy(input_buf.data() + 32 + offset, &c_big_hit, sizeof(u16)); + std::memcpy(input_buf.data() + 32 + offset, &c_hit, sizeof(u16)); break; - case usio_btn::strong_hit_center_right: - // Strong hit center right + case usio_btn::taiko_hit_center_right: if (pressed) - std::memcpy(input_buf.data() + 36 + offset, &c_big_hit, sizeof(u16)); + std::memcpy(input_buf.data() + 36 + offset, &c_hit, sizeof(u16)); break; - case usio_btn::strong_hit_side_right: - // Strong hit side right + case usio_btn::taiko_hit_side_right: if (pressed) - std::memcpy(input_buf.data() + 38 + offset, &c_big_hit, sizeof(u16)); + std::memcpy(input_buf.data() + 38 + offset, &c_hit, sizeof(u16)); break; - case usio_btn::strong_hit_center_left: - // Strong hit center left + case usio_btn::taiko_hit_center_left: if (pressed) - std::memcpy(input_buf.data() + 34 + offset, &c_big_hit, sizeof(u16)); + std::memcpy(input_buf.data() + 34 + offset, &c_hit, sizeof(u16)); break; - case usio_btn::small_hit_center_left: - // Small hit center left - if (pressed) - std::memcpy(input_buf.data() + 34 + offset, &c_small_hit, sizeof(u16)); - break; - case usio_btn::small_hit_center_right: - // Small hit center right - if (pressed) - std::memcpy(input_buf.data() + 36 + offset, &c_small_hit, sizeof(u16)); - break; - case usio_btn::small_hit_side_left: - // Small hit side left - if (pressed) - std::memcpy(input_buf.data() + 32 + offset, &c_small_hit, sizeof(u16)); - break; - case usio_btn::small_hit_side_right: - // Small hit side right - if (pressed) - std::memcpy(input_buf.data() + 38 + offset, &c_small_hit, sizeof(u16)); - break; - case usio_btn::count: + default: break; } }); }; for (usz i = 0; i < g_cfg_usio.players.size(); i++) - { translate_from_pad(i, i); - } - test_keys |= test_on ? 0x80 : 0x00; - std::memcpy(input_buf.data(), &test_keys, sizeof(u16)); + digital_input |= test_on ? 0x80 : 0x00; + std::memcpy(input_buf.data(), &digital_input, sizeof(u16)); std::memcpy(input_buf.data() + 16, &coin_counter, sizeof(u16)); response = std::move(input_buf); } +void usb_device_usio::translate_input_tekken() +{ + std::lock_guard lock(pad::g_pad_mutex); + const auto handler = pad::get_current_handler(); + + std::vector input_buf(256); + le_t digital_input = 0; + + auto translate_from_pad = [&](usz pad_number, usz player) + { + if (!is_input_allowed()) + { + return; + } + + const auto& pad = ::at32(handler->GetPads(), pad_number); + if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + { + return; + } + + const usz shift = player * 24ULL; + + const auto& cfg = ::at32(g_cfg_usio.players, pad_number); + cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed) + { + switch (btn) + { + case usio_btn::test: + if (player != 0) + break; + if (pressed && !test_key_pressed) // Solve the need to hold the Test key + test_on = !test_on; + test_key_pressed = pressed; + break; + case usio_btn::coin: + if (player != 0) + break; + if (pressed && !coin_key_pressed) // Ensure only one coin is inserted each time the Coin key is pressed + coin_counter++; + coin_key_pressed = pressed; + break; + case usio_btn::service: + if (player == 0 && pressed) + digital_input |= 0x4000; + break; + case usio_btn::enter: + if (pressed) + digital_input |= 0x800000ULL << shift; + break; + case usio_btn::up: + if (pressed) + digital_input |= 0x200000ULL << shift; + break; + case usio_btn::down: + if ( pressed) + digital_input |= 0x100000ULL << shift; + break; + case usio_btn::left: + if (pressed) + digital_input |= 0x80000ULL << shift; + break; + case usio_btn::right: + if (pressed) + digital_input |= 0x40000ULL << shift; + break; + case usio_btn::tekken_button1: + if (pressed) + digital_input |= 0x20000ULL << shift; + break; + case usio_btn::tekken_button2: + if (pressed) + digital_input |= 0x10000ULL << shift; + break; + case usio_btn::tekken_button3: + if (pressed) + digital_input |= 0x40000000ULL << shift; + break; + case usio_btn::tekken_button4: + if (pressed) + digital_input |= 0x20000000ULL << shift; + break; + case usio_btn::tekken_button5: + if (pressed) + digital_input |= 0x80000000ULL << shift; + break; + default: + break; + } + }); + }; + + for (usz i = 0; i < g_cfg_usio.players.size(); i++) + translate_from_pad(i, i); + + digital_input |= test_on ? 0x80 : 0x00; + std::memcpy(input_buf.data() + 128, &digital_input, sizeof(u64)); + std::memcpy(input_buf.data() + 128 + 16, &coin_counter, sizeof(u16)); + + input_buf[2] = 0b00010000; // DIP Switches, 8 in total + + response = std::move(input_buf); +} + void usb_device_usio::usio_write(u8 channel, u16 reg, std::vector& data) { - auto write_memory = [&](std::vector& memory) - { - auto size = memory.size(); - memory = std::move(data); - memory.resize(size); - }; - const auto get_u16 = [&](std::string_view usio_func) -> u16 { if (data.size() != 2) @@ -351,27 +423,27 @@ void usb_device_usio::usio_write(u8 channel, u16 reg, std::vector& data) } default: { - //usio_log.error("Unhandled channel 0 register write: 0x%04X", reg); + usio_log.trace("Unhandled channel 0 register write(reg: 0x%04X, size: 0x%04X, data: %s)", reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size())); break; } } } else if (channel >= 2) { - const u8 chip = channel - 2; - usio_log.trace("Usio write of sram(chip: %d, addr: 0x%04X)", chip, reg); + const u8 page = channel - 2; + usio_log.trace("Usio write of sram(page: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s)", page, reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size())); auto& memory = g_fxo->get().backup_memory; const usz addr_end = reg + data.size(); - if (data.size() > 0 && chip < usio_memory::chip_count && addr_end <= usio_memory::chip_size) - std::memcpy(&memory[usio_memory::chip_size * chip + reg], data.data(), data.size()); + if (data.size() > 0 && page < usio_memory::page_count && addr_end <= usio_memory::page_size) + std::memcpy(&memory[usio_memory::page_size * page + reg], data.data(), data.size()); else - usio_log.error("Usio sram invalid write operation(chip: %d, addr: 0x%04X, size: %x)", chip, reg, data.size()); + usio_log.error("Usio sram invalid write operation(page: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s)", page, reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size())); } else { // Channel 1 is the endpoint for firmware update. // We are not using any firmware since this is emulation. - usio_log.warning("Unsupported write operation(channel: 0x%02X, addr: 0x%04X)", channel, reg); + usio_log.trace("Unsupported write operation(channel: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s)", channel, reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size())); } } @@ -401,50 +473,56 @@ void usb_device_usio::usio_read(u8 channel, u16 reg, u16 size) // No data returned break; } + case 0x1000: + { + // Often called, gets input from usio for Tekken + translate_input_tekken(); + break; + } case 0x1080: { - // Often called, gets input from usio - translate_input(); + // Often called, gets input from usio for Taiko + translate_input_taiko(); break; } case 0x1800: { // Firmware // "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG." - response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x75, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; break; } case 0x1880: { // Seems to contain a few extra bytes of info in addition to the firmware string // Firmware - // "NBGI2;USIO01;Ver1.00;JPN,Multipurpose with PPG." - response = {0x4E, 0x42, 0x47, 0x49, 0x32, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x08, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + // "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG." + response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x75, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; break; } default: { - usio_log.error("Unhandled channel 0 register read: 0x%04X", reg); + usio_log.trace("Unhandled channel 0 register read(reg: 0x%04X, size: 0x%04X)", reg, size); break; } } } else if (channel >= 2) { - const u8 chip = channel - 2; - usio_log.trace("Usio read of sram(chip: %d, addr: 0x%04X)", chip, reg); + const u8 page = channel - 2; + usio_log.trace("Usio read of sram(page: 0x%02X, addr: 0x%04X, size: 0x%04X)", page, reg, size); auto& memory = g_fxo->get().backup_memory; const usz addr_end = reg + size; - if (size > 0 && chip < usio_memory::chip_count && addr_end <= usio_memory::chip_size) - response.insert(response.end(), memory.begin() + (usio_memory::chip_size * chip + reg), memory.begin() + (usio_memory::chip_size * chip + addr_end)); + if (size > 0 && page < usio_memory::page_count && addr_end <= usio_memory::page_size) + response.insert(response.end(), memory.begin() + (usio_memory::page_size * page + reg), memory.begin() + (usio_memory::page_size * page + addr_end)); else - usio_log.error("Usio sram invalid read operation(chip: %d, addr: 0x%04X, size: %x)", chip, reg, size); + usio_log.error("Usio sram invalid read operation(page: 0x%02X, addr: 0x%04X, size: 0x%04X)", page, reg, size); } else { // Channel 1 is the endpoint for firmware update. // We are not using any firmware since this is emulation. - usio_log.warning("Unsupported read operation(channel: 0x%02X, addr: 0x%04X)", channel, reg); + usio_log.trace("Unsupported read operation(channel: 0x%02X, addr: 0x%04X, size: 0x%04X)", channel, reg, size); } response.resize(size); // Always resize the response vector to the given size diff --git a/rpcs3/Emu/Io/usio.h b/rpcs3/Emu/Io/usio.h index d0a1fb2588..98543fe122 100644 --- a/rpcs3/Emu/Io/usio.h +++ b/rpcs3/Emu/Io/usio.h @@ -15,7 +15,8 @@ public: private: void load_backup(); void save_backup(); - void translate_input(); + void translate_input_taiko(); + void translate_input_tekken(); void usio_write(u8 channel, u16 reg, std::vector& data); void usio_read(u8 channel, u16 reg, u16 size); diff --git a/rpcs3/Emu/Io/usio_config.h b/rpcs3/Emu/Io/usio_config.h index 66aa8e2caa..91e5f74934 100644 --- a/rpcs3/Emu/Io/usio_config.h +++ b/rpcs3/Emu/Io/usio_config.h @@ -8,18 +8,21 @@ enum class usio_btn { test, coin, + service, enter, up, down, - service, - strong_hit_side_left, - strong_hit_side_right, - strong_hit_center_left, - strong_hit_center_right, - small_hit_side_left, - small_hit_side_right, - small_hit_center_left, - small_hit_center_right, + left, + right, + taiko_hit_side_left, + taiko_hit_side_right, + taiko_hit_center_left, + taiko_hit_center_right, + tekken_button1, + tekken_button2, + tekken_button3, + tekken_button4, + tekken_button5, count }; @@ -29,19 +32,22 @@ struct cfg_usio final : public emulated_pad_config cfg_usio(node* owner, const std::string& name) : emulated_pad_config(owner, name) {} cfg_pad_btn test{ this, "Test", usio_btn::test, pad_button::select }; - cfg_pad_btn coin{ this, "Coin", usio_btn::coin, pad_button::dpad_left }; - cfg_pad_btn enter{ this, "Enter", usio_btn::enter, pad_button::start }; + cfg_pad_btn coin{ this, "Coin", usio_btn::coin, pad_button::L3 }; + cfg_pad_btn service{this, "Service", usio_btn::service, pad_button::R3}; + cfg_pad_btn enter{ this, "Enter/Start", usio_btn::enter, pad_button::start }; cfg_pad_btn up{ this, "Up", usio_btn::up, pad_button::dpad_up }; cfg_pad_btn down{ this, "Down", usio_btn::down, pad_button::dpad_down }; - cfg_pad_btn service{ this, "Service", usio_btn::service, pad_button::dpad_right }; - cfg_pad_btn strong_hit_side_left{ this, "Strong Hit Side Left", usio_btn::strong_hit_side_left, pad_button::square }; - cfg_pad_btn strong_hit_side_right{ this, "Strong Hit Side Right", usio_btn::strong_hit_side_right, pad_button::circle }; - cfg_pad_btn strong_hit_center_left{ this, "Strong Hit Center Left", usio_btn::strong_hit_center_left, pad_button::triangle }; - cfg_pad_btn strong_hit_center_right{ this, "Strong Hit Center Right", usio_btn::strong_hit_center_right, pad_button::cross }; - cfg_pad_btn small_hit_side_left{ this, "Small Hit Side Left", usio_btn::small_hit_side_left, pad_button::L2 }; - cfg_pad_btn small_hit_side_right{ this, "Small Hit Side Right", usio_btn::small_hit_side_right, pad_button::R2 }; - cfg_pad_btn small_hit_center_left{ this, "Small Hit Center Left", usio_btn::small_hit_center_left, pad_button::L1 }; - cfg_pad_btn small_hit_center_right{ this, "Small Hit Center Right", usio_btn::small_hit_center_right, pad_button::R1 }; + cfg_pad_btn left{this, "Left", usio_btn::left, pad_button::dpad_left}; + cfg_pad_btn right{this, "Right", usio_btn::right, pad_button::dpad_right}; + cfg_pad_btn taiko_hit_side_left{ this, "Taiko Hit Side Left", usio_btn::taiko_hit_side_left, pad_button::square }; + cfg_pad_btn taiko_hit_side_right{ this, "Taiko Hit Side Right", usio_btn::taiko_hit_side_right, pad_button::circle }; + cfg_pad_btn taiko_hit_center_left{ this, "Taiko Hit Center Left", usio_btn::taiko_hit_center_left, pad_button::triangle }; + cfg_pad_btn taiko_hit_center_right{ this, "Taiko Hit Center Right", usio_btn::taiko_hit_center_right, pad_button::cross }; + cfg_pad_btn tekken_button1{this, "Tekken Button 1", usio_btn::tekken_button1, pad_button::square}; + cfg_pad_btn tekken_button2{this, "Tekken Button 2", usio_btn::tekken_button2, pad_button::triangle}; + cfg_pad_btn tekken_button3{this, "Tekken Button 3", usio_btn::tekken_button3, pad_button::cross}; + cfg_pad_btn tekken_button4{this, "Tekken Button 4", usio_btn::tekken_button4, pad_button::circle}; + cfg_pad_btn tekken_button5{this, "Tekken Button 5", usio_btn::tekken_button5, pad_button::R1}; }; struct cfg_usios final : public emulated_pads_config From 7cb2d3f143f120d7a2e9e73c22368c6d46ab84f8 Mon Sep 17 00:00:00 2001 From: oltolm Date: Mon, 24 Jul 2023 06:30:21 +0200 Subject: [PATCH 13/13] opengl: fix Qt warnings (#14249) --- rpcs3/rpcs3qt/gl_gs_frame.cpp | 12 ++++++++++-- rpcs3/util/vm_native.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/rpcs3/rpcs3qt/gl_gs_frame.cpp b/rpcs3/rpcs3qt/gl_gs_frame.cpp index 549ab10449..671075cefc 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.cpp +++ b/rpcs3/rpcs3qt/gl_gs_frame.cpp @@ -1,5 +1,6 @@ #include "gl_gs_frame.h" +#include "Emu/System.h" #include "Emu/system_config.h" #include @@ -31,7 +32,11 @@ draw_context_t gl_gs_frame::make_context() { auto surface = new QOffscreenSurface(); surface->setFormat(m_format); - surface->create(); + // Workaround for the Qt warning: "Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures." + Emu.BlockingCallFromMainThread([&]() + { + surface->create(); + }); // Share resources with the first created context context->handle->setShareContext(m_primary_context->handle); @@ -117,5 +122,8 @@ void gl_gs_frame::flip(draw_context_t context, bool skip_frame) const auto gl_ctx = static_cast(context); - gl_ctx->handle->swapBuffers(gl_ctx->surface); + if (auto window = dynamic_cast(gl_ctx->surface); window && window->isExposed()) + { + gl_ctx->handle->swapBuffers(gl_ctx->surface); + } } diff --git a/rpcs3/util/vm_native.cpp b/rpcs3/util/vm_native.cpp index 4056adf438..14bff5cdb4 100644 --- a/rpcs3/util/vm_native.cpp +++ b/rpcs3/util/vm_native.cpp @@ -52,7 +52,7 @@ namespace utils #ifdef MAP_NORESERVE constexpr int c_map_noreserve = MAP_NORESERVE; #else - constexpr int c_map_noreserve = 0; + [[maybe_unused]] constexpr int c_map_noreserve = 0; #endif #ifdef MADV_FREE @@ -66,7 +66,7 @@ namespace utils #ifdef MADV_HUGEPAGE constexpr int c_madv_hugepage = MADV_HUGEPAGE; #else - constexpr int c_madv_hugepage = 0; + [[maybe_unused]] constexpr int c_madv_hugepage = 0; #endif #if defined(MADV_DONTDUMP) && defined(MADV_DODUMP) @@ -76,8 +76,8 @@ namespace utils constexpr int c_madv_no_dump = MADV_NOCORE; constexpr int c_madv_dump = MADV_CORE; #else - constexpr int c_madv_no_dump = 0; - constexpr int c_madv_dump = 0; + [[maybe_unused]] constexpr int c_madv_no_dump = 0; + [[maybe_unused]] constexpr int c_madv_dump = 0; #endif #if defined(MFD_HUGETLB) && defined(MFD_HUGE_2MB)