Merge branch 'master' into nastys-patch-17

This commit is contained in:
nastys 2025-12-01 18:15:09 +01:00 committed by GitHub
commit b53e8e17ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 784 additions and 207 deletions

View file

@ -38,7 +38,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
git clone https://github.com/engnr/qt-downloader.git git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
# nested Qt 6.10.0 URL workaround # nested Qt 6.10.1 URL workaround
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
# archived Qt 6.7.3 URL workaround # archived Qt 6.7.3 URL workaround
@ -47,7 +47,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
"$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml "$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml
"$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache "$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.0 workaround # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.1 workaround
"$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" "$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
fi fi

View file

@ -38,7 +38,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
git clone https://github.com/engnr/qt-downloader.git git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
# nested Qt 6.10.0 URL workaround # nested Qt 6.10.1 URL workaround
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
# archived Qt 6.7.3 URL workaround # archived Qt 6.7.3 URL workaround
@ -46,7 +46,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
cd "/tmp/Qt" cd "/tmp/Qt"
"/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run pip3 install py7zr requests semantic_version lxml "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run pip3 install py7zr requests semantic_version lxml
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.0 workaround # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.1 workaround
"/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
fi fi

View file

@ -212,9 +212,9 @@ jobs:
env: env:
COMPILER: msvc COMPILER: msvc
QT_VER_MAIN: '6' QT_VER_MAIN: '6'
QT_VER: '6.10.0' QT_VER: '6.10.1'
QT_VER_MSVC: 'msvc2022' QT_VER_MSVC: 'msvc2022'
QT_DATE: '202510021201' QT_DATE: '202511161843'
LLVM_VER: '19.1.7' LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0' VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'

@ -1 +1 @@
Subproject commit 2b978915d82377df13fcbb1fb56660195ded868a Subproject commit 49363adcfaf098748d7a4c8c624ad8c45a8c3a86

View file

@ -8,37 +8,38 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
### Windows 10 or later ### Windows 10 or later
The following tools are required to build RPCS3 on Windows 10 or later: The following tools are required to build RPCS3 on Windows 10 or later:
- [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) - [Visual Studio 2022/2026](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community)
- **Optional** - [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH) - **Optional** - [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH)
**NOTES:** **NOTES:**
- **Visual Studio 2022** integrates **CMake 3.29+** and it also supports both the `sln` solution (`.sln`, `.vcxproj`) and `CMake` solution (`CMakeLists.txt`, `CMakePresets.json`). - **Visual Studio 2026** needs at least **CMake 4.2.0+**.
- **Visual Studio 2022/2026** integrates **CMake 3.29+** and it also supports both the `sln` solution (`.sln`, `.vcxproj`) and `CMake` solution (`CMakeLists.txt`, `CMakePresets.json`).
See sections [Building with Visual Studio sln solution](#building-with-visual-studio-sln-solution) and [Building with Visual Studio CMake solution](#building-with-visual-studio-cmake-solution) See sections [Building with Visual Studio sln solution](#building-with-visual-studio-sln-solution) and [Building with Visual Studio CMake solution](#building-with-visual-studio-cmake-solution)
on how to build the project with **Visual Studio**. on how to build the project with **Visual Studio**.
- Install and use this standalone **CMake** tool just in case of your preference. See section [Building with standalone CMake tool](#building-with-standalone-cmake-tool) on how to build the project - Install and use this standalone **CMake** tool just in case of your preference. See section [Building with standalone CMake tool](#building-with-standalone-cmake-tool) on how to build the project
with standalone **CMake** tool. with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH) - [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
- [Qt 6.10.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) - [Qt 6.10.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode. The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs: In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.0\msvc2022_64\` - add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.1\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022) - or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead. **NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool): In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.0\msvc2022_64\` - add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.1\msvc2022_64\`
### Linux ### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager: These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+ - Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/) - [CMake 3.28.0+](https://www.cmake.org/download/)
- [Qt 6.10.0](https://www.qt.io/download-qt-installer) - [Qt 6.10.1](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) - [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@ -121,7 +122,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used) ##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions` 1) go to `Extensions->Qt VS Tools->Qt Versions`
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.0\msvc2022_64`, version will fill in automatically 2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.1\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**) 3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**) 4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)

View file

@ -827,14 +827,14 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_intrp_func_t ptr =
return; return;
} }
size = utils::align<u32>(size + addr % 4, 4);
addr &= -4;
if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm) if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm)
{ {
return; return;
} }
size = utils::align<u32>(size + addr % 4, 4);
addr &= -4;
// Initialize interpreter cache // Initialize interpreter cache
while (size) while (size)
{ {

View file

@ -720,9 +720,19 @@ void spu_cache::initialize(bool build_existing_cache)
} }
// SPU cache file (version + block size type) // SPU cache file (version + block size type)
const std::string loc = ppu_cache + "spu-" + fmt::to_lower(g_cfg.core.spu_block_size.to_string()) + "-v1-tane.dat"; const std::string filename = "spu-" + fmt::to_lower(g_cfg.core.spu_block_size.to_string()) + "-v1-tane.dat";
const std::string loc = ppu_cache + filename;
const std::string loc_debug = fs::get_cache_dir() + "DEBUG/" + filename;
spu_cache cache(loc); bool is_debug = false;
if (fs::is_file(loc_debug))
{
spu_log.success("SPU Cache override applied!");
is_debug = true;
}
spu_cache cache(is_debug ? loc_debug : loc);
if (!cache) if (!cache)
{ {
@ -4963,6 +4973,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
u32 lsa_last_pc = SPU_LS_SIZE; // PC of first LSA write u32 lsa_last_pc = SPU_LS_SIZE; // PC of first LSA write
u32 get_pc = SPU_LS_SIZE; // PC of GETLLAR u32 get_pc = SPU_LS_SIZE; // PC of GETLLAR
u32 put_pc = SPU_LS_SIZE; // PC of PUTLLC u32 put_pc = SPU_LS_SIZE; // PC of PUTLLC
u32 rdatomic_pc = SPU_LS_SIZE; // PC of last RdAtomcStat read
reg_state_t ls{}; // state of LS load/store address register reg_state_t ls{}; // state of LS load/store address register
reg_state_t ls_offs = reg_state_t::from_value(0); // Added value to ls reg_state_t ls_offs = reg_state_t::from_value(0); // Added value to ls
reg_state_t lsa{}; // state of LSA register on GETLLAR reg_state_t lsa{}; // state of LSA register on GETLLAR
@ -5008,7 +5019,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
ls_invalid = true; ls_invalid = true;
ls_write |= write; ls_write |= write;
if (write) if (ls_write)
{ {
return discard(); return discard();
} }
@ -6323,6 +6334,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
atomic16->rdatomic_pc = pos;
const auto it = atomic16_all.find(pos); const auto it = atomic16_all.find(pos);
if (it == atomic16_all.end()) if (it == atomic16_all.end())
@ -7263,7 +7276,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (const auto& [pc_commited, pattern] : atomic16_all) for (const auto& [pc_commited, pattern] : atomic16_all)
{ {
if (!pattern.active) if (!pattern.active || pattern.lsa_pc >= pattern.rdatomic_pc)
{ {
continue; continue;
} }
@ -7273,6 +7286,17 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
continue; continue;
} }
std::string pattern_hash;
{
sha1_context ctx;
u8 output[20]{};
sha1_starts(&ctx);
sha1_update(&ctx, reinterpret_cast<const u8*>(result.data.data()) + (pattern.lsa_pc - result.lower_bound), pattern.rdatomic_pc - pattern.lsa_pc);
sha1_finish(&ctx, output);
fmt::append(pattern_hash, "%s", fmt::base57(output));
}
union putllc16_or_0_info union putllc16_or_0_info
{ {
u64 data; u64 data;
@ -7295,8 +7319,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
value.required_pc = pattern.required_pc; value.required_pc = pattern.required_pc;
} }
spu_log.success("PUTLLC0 Pattern Detected! (put_pc=0x%x, %s) (putllc0=%d, putllc16+0=%d, all=%d)", pattern.put_pc, func_hash, ++stats.nowrite, ++stats.single, +stats.all); // spu_log.success("PUTLLC0 Pattern Detected! (put_pc=0x%x, %s) (putllc0=%d, putllc16+0=%d, all=%d)", pattern.put_pc, func_hash, ++stats.nowrite, ++stats.single, +stats.all);
add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data); // add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data);
continue; continue;
} }
@ -7363,16 +7387,35 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
value.reg2 = pattern.reg2; value.reg2 = pattern.reg2;
} }
bool allow_pattern = true;
if (g_cfg.core.spu_accurate_reservations) if (g_cfg.core.spu_accurate_reservations)
{ {
// Because enabling it is a hack, as it turns out // The problem with PUTLLC16 optimization, that it is in theory correct at the bounds of the spu function.
// continue; // But if the SPU code reuses the cache line data observed, it is not truly atomic.
// So we may enable it only for known cases where SPU atomic data is not used after the function leaves.
// So the two options are:
// 1. Atomic compare exchange 16 bytes operation. (rest of data is not read) -> good for RPCS3 to optimize.
// 2. Fetch 128 bytes (read them later), modify only 16 bytes. -> Bad for RPCS3 to optimize.
// This difference cannot be known at analyzer time but from observing callers.
static constexpr std::initializer_list<std::string_view> allowed_patterns =
{
"620oYSe8uQqq9eTkhWfMqoEXX0us"sv, // CellSpurs JobChain acquire pattern
};
allow_pattern = std::any_of(allowed_patterns.begin(), allowed_patterns.end(), FN(pattern_hash == x));
} }
if (allow_pattern)
{
add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data); add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data);
}
spu_log.success("PUTLLC16 Pattern Detected! (mem_count=%d, put_pc=0x%x, pc_rel=%d, offset=0x%x, const=%u, two_regs=%d, reg=%u, runtime=%d, 0x%x-%s) (putllc0=%d, putllc16+0=%d, all=%d)" spu_log.success("PUTLLC16 Pattern Detected! (mem_count=%d, put_pc=0x%x, pc_rel=%d, offset=0x%x, const=%u, two_regs=%d, reg=%u, runtime=%d, 0x%x-%s, pattern-hash=%s) (putllc0=%d, putllc16+0=%d, all=%d)"
, pattern.mem_count, pattern.put_pc, value.type == v_relative, value.off18, value.type == v_const, value.type == v_reg2, value.reg, value.runtime16_select, entry_point, func_hash, +stats.nowrite, ++stats.single, +stats.all); , pattern.mem_count, pattern.put_pc, value.type == v_relative, value.off18, value.type == v_const, value.type == v_reg2, value.reg, value.runtime16_select, entry_point, func_hash, pattern_hash, +stats.nowrite, ++stats.single, +stats.all);
} }
for (const auto& [read_pc, pattern] : rchcnt_loop_all) for (const auto& [read_pc, pattern] : rchcnt_loop_all)
@ -8435,8 +8478,9 @@ std::array<reg_state_t, s_reg_max>& block_reg_info::evaluate_start_state(const s
{ {
// TODO: The true maximum occurence count need to depend on the amount of branching-outs passed through // TODO: The true maximum occurence count need to depend on the amount of branching-outs passed through
// Currently allow 2 for short-term code and 1 for long-term code // Currently allow 2 for short-term code and 1 for long-term code
// Ignore large jumptables as well
const bool loop_terminator_detected = std::count(been_there.begin(), been_there.end(), prev_pc) >= (qi < 20 ? 2u : 1u); const bool loop_terminator_detected = std::count(been_there.begin(), been_there.end(), prev_pc) >= (qi < 20 ? 2u : 1u);
const bool avoid_extensive_analysis = qi >= (extensive_evaluation ? 22 : 16); const bool avoid_extensive_analysis = qi >= (extensive_evaluation ? 22 : 16) || it->state_prev.size() >= 8;
if (!loop_terminator_detected && !avoid_extensive_analysis) if (!loop_terminator_detected && !avoid_extensive_analysis)
{ {

View file

@ -1,7 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "../overlay_manager.h" #include "../overlay_manager.h"
#include "overlay_friends_list_dialog.h" #include "overlay_friends_list_dialog.h"
#include "Emu/System.h"
#include "Emu/NP/rpcn_config.h" #include "Emu/NP/rpcn_config.h"
#include "Emu/vfs_config.h" #include "Emu/vfs_config.h"
@ -306,11 +305,11 @@ namespace rsx
} }
} }
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
return; return;
} }
case pad_button::circle: case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::square: case pad_button::square:
@ -359,7 +358,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default) else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -1,6 +1,5 @@
#include "stdafx.h" #include "stdafx.h"
#include "overlay_home_menu_message_box.h" #include "overlay_home_menu_message_box.h"
#include "Emu/System.h"
#include "Emu/system_config.h" #include "Emu/system_config.h"
namespace rsx namespace rsx
@ -77,7 +76,7 @@ namespace rsx
{ {
case pad_button::cross: case pad_button::cross:
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
if (m_on_accept) if (m_on_accept)
{ {
m_on_accept(); m_on_accept();
@ -86,7 +85,7 @@ namespace rsx
} }
case pad_button::circle: case pad_button::circle:
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
if (m_on_cancel) if (m_on_cancel)
{ {
m_on_cancel(); m_on_cancel();

View file

@ -160,7 +160,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
if (!is_auto_repeat || auto_repeat_interval_ms >= user_interface::m_auto_repeat_ms_interval_default) if (!is_auto_repeat || auto_repeat_interval_ms >= user_interface::m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
} }
return func(button_press); return func(button_press);
} }
@ -169,7 +169,7 @@ namespace rsx
} }
case pad_button::circle: case pad_button::circle:
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
if (parent) if (parent)
{ {
set_current_page(parent); set_current_page(parent);
@ -244,7 +244,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
if (!is_auto_repeat || auto_repeat_interval_ms >= user_interface::m_auto_repeat_ms_interval_default) if (!is_auto_repeat || auto_repeat_interval_ms >= user_interface::m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
return page_navigation::stay; return page_navigation::stay;
} }

View file

@ -127,11 +127,11 @@ namespace rsx
{ {
return_code = selection_code::error; return_code = selection_code::error;
} }
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::circle: case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::dpad_up: case pad_button::dpad_up:
@ -167,7 +167,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default) else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -82,7 +82,7 @@ namespace rsx
if (m_list->m_items.empty() || is_auto_repeat) if (m_list->m_items.empty() || is_auto_repeat)
break; break;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
if (!get_current_selection().empty()) if (!get_current_selection().empty())
{ {
@ -95,7 +95,7 @@ namespace rsx
close_dialog = true; close_dialog = true;
break; break;
case pad_button::circle: case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::dpad_up: case pad_button::dpad_up:
@ -131,7 +131,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default) else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -132,7 +132,7 @@ namespace rsx
switch (button_press) switch (button_press)
{ {
case pad_button::circle: case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::square: case pad_button::square:
@ -172,7 +172,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default) else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -4,7 +4,6 @@
#include "overlay_media_list_dialog.h" #include "overlay_media_list_dialog.h"
#include "Emu/Cell/Modules/cellMusic.h" #include "Emu/Cell/Modules/cellMusic.h"
#include "Emu/System.h"
#include "Emu/VFS.h" #include "Emu/VFS.h"
#include "Utilities/StrUtil.h" #include "Utilities/StrUtil.h"
#include "Utilities/Thread.h" #include "Utilities/Thread.h"
@ -154,13 +153,13 @@ namespace rsx
return_code = m_list->get_selected_index(); return_code = m_list->get_selected_index();
m_stop_input_loop = true; m_stop_input_loop = true;
play_cursor_sound = false; play_cursor_sound = false;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
break; break;
case pad_button::circle: case pad_button::circle:
return_code = selection_code::canceled; return_code = selection_code::canceled;
m_stop_input_loop = true; m_stop_input_loop = true;
play_cursor_sound = false; play_cursor_sound = false;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
break; break;
case pad_button::dpad_up: case pad_button::dpad_up:
m_list->select_previous(); m_list->select_previous();
@ -182,7 +181,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
if (play_cursor_sound && (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)) if (play_cursor_sound && (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default))
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -155,7 +155,7 @@ namespace rsx
return_code = CELL_MSGDIALOG_BUTTON_YES; return_code = CELL_MSGDIALOG_BUTTON_YES;
} }
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
break; break;
} }
case pad_button::circle: case pad_button::circle:
@ -175,7 +175,7 @@ namespace rsx
return_code = CELL_MSGDIALOG_BUTTON_NO; return_code = CELL_MSGDIALOG_BUTTON_NO;
} }
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
break; break;
} }
default: return; default: return;
@ -238,10 +238,7 @@ namespace rsx
if (!type.se_mute_on) if (!type.se_mute_on)
{ {
if (type.se_normal) play_sound(type.se_normal ? sound_effect::dialog_ok : sound_effect::dialog_error);
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_system_ok.wav");
else
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_system_ng.wav");
} }
set_text(text); set_text(text);

View file

@ -823,7 +823,7 @@ namespace rsx
} }
case pad_button::start: case pad_button::start:
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskenter.wav"); play_sound(sound_effect::osk_accept);
Close(CELL_OSKDIALOG_CLOSE_CONFIRM); Close(CELL_OSKDIALOG_CLOSE_CONFIRM);
play_cursor_sound = false; play_cursor_sound = false;
break; break;
@ -840,7 +840,7 @@ namespace rsx
} }
case pad_button::cross: case pad_button::cross:
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskenter.wav"); play_sound(sound_effect::osk_accept);
on_accept(); on_accept();
m_reset_pulse = true; m_reset_pulse = true;
play_cursor_sound = false; play_cursor_sound = false;
@ -848,7 +848,7 @@ namespace rsx
} }
case pad_button::circle: case pad_button::circle:
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskcancel.wav"); play_sound(sound_effect::osk_cancel);
Close(CELL_OSKDIALOG_CLOSE_CANCEL); Close(CELL_OSKDIALOG_CLOSE_CANCEL);
play_cursor_sound = false; play_cursor_sound = false;
break; break;
@ -890,7 +890,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
if (play_cursor_sound && (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)) if (play_cursor_sound && (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default))
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
if (m_reset_pulse) if (m_reset_pulse)

View file

@ -2,7 +2,6 @@
#include "overlay_save_dialog.h" #include "overlay_save_dialog.h"
#include "overlay_video.h" #include "overlay_video.h"
#include "Utilities/date_time.h" #include "Utilities/date_time.h"
#include "Emu/System.h"
namespace rsx namespace rsx
{ {
@ -133,11 +132,11 @@ namespace rsx
if (m_no_saves) if (m_no_saves)
break; break;
return_code = m_list->get_selected_index(); return_code = m_list->get_selected_index();
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::circle: case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::dpad_up: case pad_button::dpad_up:
@ -173,7 +172,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default) else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -1,7 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "overlay_trophy_notification.h" #include "overlay_trophy_notification.h"
#include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/ErrorCodes.h"
#include "Emu/System.h"
namespace rsx namespace rsx
{ {
@ -70,7 +69,7 @@ namespace rsx
{ {
// First tick // First tick
creation_time_us = timestamp_us; creation_time_us = timestamp_us;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_trophy.wav"); play_sound(sound_effect::trophy);
return; return;
} }

View file

@ -113,11 +113,11 @@ namespace rsx
{ {
return_code = selection_code::error; return_code = selection_code::error;
} }
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav"); play_sound(sound_effect::accept);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::circle: case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav"); play_sound(sound_effect::cancel);
close_dialog = true; close_dialog = true;
break; break;
case pad_button::dpad_up: case pad_button::dpad_up:
@ -153,7 +153,7 @@ namespace rsx
// Play a sound unless this is a fast auto repeat which would induce a nasty noise // Play a sound unless this is a fast auto repeat which would induce a nasty noise
else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default) else if (!is_auto_repeat || m_auto_repeat_ms_interval >= m_auto_repeat_ms_interval_default)
{ {
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav"); play_sound(sound_effect::cursor);
} }
} }

View file

@ -15,6 +15,33 @@ namespace rsx
{ {
namespace overlays namespace overlays
{ {
std::string get_sound_filepath(sound_effect sound)
{
const auto get_sound_filename = [sound]()
{
switch (sound)
{
case sound_effect::cursor: return "snd_cursor"sv;
case sound_effect::accept: return "snd_decide"sv;
case sound_effect::cancel: return "snd_cancel"sv;
case sound_effect::osk_accept: return "snd_oskenter"sv;
case sound_effect::osk_cancel: return "snd_oskcancel"sv;
case sound_effect::dialog_ok: return "snd_system_ok"sv;
case sound_effect::dialog_error: return "snd_system_ng"sv;
case sound_effect::trophy: return "snd_trophy"sv;
}
fmt::throw_exception("Unreachable (sound=%d)", static_cast<u32>(sound));
};
return fmt::format("%ssounds/%s.wav", fs::get_config_dir(), get_sound_filename());
}
void play_sound(sound_effect sound, std::optional<f32> volume)
{
Emu.GetCallbacks().play_sound(get_sound_filepath(sound), volume);
}
thread_local DECLARE(user_interface::g_thread_bit) = 0; thread_local DECLARE(user_interface::g_thread_bit) = 0;
u32 user_interface::alloc_thread_bit() u32 user_interface::alloc_thread_bit()

View file

@ -17,6 +17,21 @@ namespace rsx
{ {
namespace overlays namespace overlays
{ {
enum class sound_effect
{
cursor,
accept,
cancel,
osk_accept,
osk_cancel,
dialog_ok,
dialog_error,
trophy,
};
std::string get_sound_filepath(sound_effect sound);
void play_sound(sound_effect sound, std::optional<f32> volume = std::nullopt);
// Bitfield of UI signals to overlay manager // Bitfield of UI signals to overlay manager
enum status_bits : u32 enum status_bits : u32
{ {

View file

@ -3073,7 +3073,7 @@ namespace rsx
{ {
capture_current_frame = false; capture_current_frame = false;
std::string file_path = fs::get_config_dir() + "captures/" + Emu.GetTitleID() + "_" + date_time::current_time_narrow() + "_capture.rrc.gz"; const std::string file_path = fs::get_config_dir() + "captures/" + (Emu.GetTitleID().empty() ? Emu.GetTitle() : Emu.GetTitleID()) + "_" + date_time::current_time_narrow() + "_capture.rrc.gz";
fs::pending_file temp(file_path); fs::pending_file temp(file_path);

View file

@ -840,6 +840,19 @@ bool Emulator::BootRsxCapture(const std::string& path)
return false; return false;
} }
m_path.clear();
m_path_old.clear();
m_path_original.clear();
m_title_id.clear();
m_title.clear();
m_localized_title.clear();
m_app_version.clear();
m_hash.clear();
m_cat.clear();
m_dir.clear();
m_sfo_dir.clear();
m_ar.reset();
Init(); Init();
g_cfg.video.disable_on_disk_shader_cache.set(true); g_cfg.video.disable_on_disk_shader_cache.set(true);

View file

@ -101,7 +101,7 @@ struct EmuCallbacks
std::function<std::string(localized_string_id, const char*)> get_localized_string; std::function<std::string(localized_string_id, const char*)> get_localized_string;
std::function<std::u32string(localized_string_id, const char*)> get_localized_u32string; std::function<std::u32string(localized_string_id, const char*)> get_localized_u32string;
std::function<std::string(const cfg::_base*, u32)> get_localized_setting; std::function<std::string(const cfg::_base*, u32)> get_localized_setting;
std::function<void(const std::string&)> play_sound; std::function<void(const std::string&, std::optional<f32>)> play_sound;
std::function<bool(const std::string&, std::string&, s32&, s32&, s32&)> get_image_info; // (filename, sub_type, width, height, CellSearchOrientation) std::function<bool(const std::string&, std::string&, s32&, s32&, s32&)> get_image_info; // (filename, sub_type, width, height, CellSearchOrientation)
std::function<bool(const std::string&, s32, s32, s32&, s32&, u8*, bool)> get_scaled_image; // (filename, target_width, target_height, width, height, dst, force_fit) std::function<bool(const std::string&, s32, s32, s32&, s32&, u8*, bool)> get_scaled_image; // (filename, target_width, target_height, width, height, dst, force_fit)
std::string(*resolve_path)(std::string_view) = [](std::string_view arg){ return std::string{arg}; }; // Resolve path using Qt std::string(*resolve_path)(std::string_view) = [](std::string_view arg){ return std::string{arg}; }; // Resolve path using Qt

View file

@ -169,7 +169,7 @@ void headless_application::InitializeCallbacks()
callbacks.get_localized_u32string = [](localized_string_id, const char*) -> std::u32string { return {}; }; callbacks.get_localized_u32string = [](localized_string_id, const char*) -> std::u32string { return {}; };
callbacks.get_localized_setting = [](const cfg::_base*, u32) -> std::string { return {}; }; callbacks.get_localized_setting = [](const cfg::_base*, u32) -> std::string { return {}; };
callbacks.play_sound = [](const std::string&){}; callbacks.play_sound = [](const std::string&, std::optional<f32>){};
callbacks.add_breakpoint = [](u32 /*addr*/){}; callbacks.add_breakpoint = [](u32 /*addr*/){};
callbacks.display_sleep_control_supported = [](){ return false; }; callbacks.display_sleep_control_supported = [](){ return false; };

View file

@ -430,6 +430,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_preview.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_sound_effect_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_sendmessage_dialog_frame.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_sendmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -718,6 +721,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_preview.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_sound_effect_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_sendmessage_dialog_frame.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_sendmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -851,6 +857,7 @@
<ClCompile Include="rpcs3qt\screenshot_item.cpp" /> <ClCompile Include="rpcs3qt\screenshot_item.cpp" />
<ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp" /> <ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_preview.cpp" /> <ClCompile Include="rpcs3qt\screenshot_preview.cpp" />
<ClCompile Include="rpcs3qt\sound_effect_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp" /> <ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\settings.cpp" /> <ClCompile Include="rpcs3qt\settings.cpp" />
<ClCompile Include="rpcs3qt\shortcut_dialog.cpp" /> <ClCompile Include="rpcs3qt\shortcut_dialog.cpp" />
@ -1311,6 +1318,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
</CustomBuild> </CustomBuild>
<CustomBuild Include="rpcs3qt\sound_effect_manager_dialog.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<CustomBuild Include="rpcs3qt\fatal_error_dialog.h"> <CustomBuild Include="rpcs3qt\fatal_error_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>

View file

@ -205,6 +205,9 @@
<Filter Include="Gui\widgets"> <Filter Include="Gui\widgets">
<UniqueIdentifier>{149c596b-83e7-43f8-b5db-6108694434ef}</UniqueIdentifier> <UniqueIdentifier>{149c596b-83e7-43f8-b5db-6108694434ef}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Gui\sound effect manager">
<UniqueIdentifier>{640b7d83-1522-4384-8bf7-a4fbd5873ae7}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
@ -738,6 +741,15 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_manager_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_manager_dialog.cpp">
<Filter>Generated Files\Release</Filter> <Filter>Generated Files\Release</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="rpcs3qt\sound_effect_manager_dialog.cpp">
<Filter>Gui\sound effect manager</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_sound_effect_manager_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_sound_effect_manager_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\screenshot_preview.cpp"> <ClCompile Include="rpcs3qt\screenshot_preview.cpp">
<Filter>Gui\screenshot manager</Filter> <Filter>Gui\screenshot manager</Filter>
</ClCompile> </ClCompile>
@ -1627,6 +1639,9 @@
<CustomBuild Include="rpcs3qt\screenshot_preview.h"> <CustomBuild Include="rpcs3qt\screenshot_preview.h">
<Filter>Gui\screenshot manager</Filter> <Filter>Gui\screenshot manager</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="rpcs3qt\sound_effect_manager_dialog.h">
<Filter>Gui\sound effect manager</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\dimensions_dialog.h"> <CustomBuild Include="rpcs3qt\dimensions_dialog.h">
<Filter>Gui\dimensions</Filter> <Filter>Gui\dimensions</Filter>
</CustomBuild> </CustomBuild>

View file

@ -99,6 +99,7 @@ add_library(rpcs3_ui STATIC
shortcut_handler.cpp shortcut_handler.cpp
shortcut_settings.cpp shortcut_settings.cpp
skylander_dialog.cpp skylander_dialog.cpp
sound_effect_manager_dialog.cpp
syntax_highlighter.cpp syntax_highlighter.cpp
system_cmd_dialog.cpp system_cmd_dialog.cpp
table_item_delegate.cpp table_item_delegate.cpp

View file

@ -668,10 +668,9 @@ void emu_settings::EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, con
spinbox->setRange(min, max); spinbox->setRange(min, max);
spinbox->setValue(val); spinbox->setValue(val);
connect(spinbox, &QSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) connect(spinbox, &QSpinBox::valueChanged, this, [type, this](int value)
{ {
if (!spinbox) return; SetSetting(type, fmt::format("%d", value));
SetSetting(type, spinbox->cleanText().toStdString());
}); });
connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]()
@ -724,10 +723,9 @@ void emu_settings::EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_ty
spinbox->setRange(min, max); spinbox->setRange(min, max);
spinbox->setValue(val); spinbox->setValue(val);
connect(spinbox, &QDoubleSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) connect(spinbox, &QDoubleSpinBox::valueChanged, this, [type, this](double value)
{ {
if (!spinbox) return; SetSetting(type, fmt::format("%f", value));
SetSetting(type, spinbox->cleanText().toStdString());
}); });
connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]()

View file

@ -14,16 +14,16 @@ game_list::game_list() : QTableWidget(), game_list_base()
}; };
} }
void game_list::sync_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility) void game_list::sync_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility)
{ {
ensure(get_visibility); ensure(get_visibility);
bool is_dirty = false; bool is_dirty = false;
for (int col = 0; col < actions.count(); ++col) for (auto& [col, action] : actions)
{ {
const bool is_hidden = !get_visibility(col); const bool is_hidden = !get_visibility(col);
actions[col]->setChecked(!is_hidden); action->setChecked(!is_hidden);
if (isColumnHidden(col) != is_hidden) if (isColumnHidden(col) != is_hidden)
{ {
@ -38,7 +38,7 @@ void game_list::sync_header_actions(QList<QAction*>& actions, std::function<bool
} }
} }
void game_list::create_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility) void game_list::create_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility)
{ {
ensure(get_visibility); ensure(get_visibility);
ensure(set_visibility); ensure(set_visibility);
@ -48,27 +48,30 @@ void game_list::create_header_actions(QList<QAction*>& actions, std::function<bo
connect(horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this, &actions](const QPoint& pos) connect(horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this, &actions](const QPoint& pos)
{ {
QMenu* configure = new QMenu(this); QMenu* configure = new QMenu(this);
configure->addActions(actions); for (auto& [col, action] : actions)
{
configure->addAction(action);
}
configure->exec(horizontalHeader()->viewport()->mapToGlobal(pos)); configure->exec(horizontalHeader()->viewport()->mapToGlobal(pos));
}); });
for (int col = 0; col < actions.count(); ++col) for (auto& [col, action] : actions)
{ {
actions[col]->setCheckable(true); action->setCheckable(true);
connect(actions[col], &QAction::triggered, this, [this, &actions, get_visibility, set_visibility, col](bool checked) connect(action, &QAction::triggered, this, [this, &actions, get_visibility, set_visibility, col](bool checked)
{ {
if (!checked) // be sure to have at least one column left so you can call the context menu at all time if (!checked) // be sure to have at least one column left so you can call the context menu at all time
{ {
int c = 0; int c = 0;
for (int i = 0; i < actions.count(); ++i) for (auto& [col, action] : actions)
{ {
if (get_visibility(i) && ++c > 1) if (get_visibility(col) && ++c > 1)
break; break;
} }
if (c < 2) if (c < 2)
{ {
actions[col]->setChecked(true); // re-enable the checkbox if we don't change the actual state ::at32(actions, col)->setChecked(true); // re-enable the checkbox if we don't change the actual state
return; return;
} }
} }

View file

@ -23,8 +23,8 @@ class game_list : public QTableWidget, public game_list_base
public: public:
game_list(); game_list();
void sync_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility); void sync_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility);
void create_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility); void create_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility);
void clear_list() override; // Use this instead of clearContents void clear_list() override; // Use this instead of clearContents

View file

@ -100,27 +100,28 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
m_game_dock->setCentralWidget(m_central_widget); m_game_dock->setCentralWidget(m_central_widget);
// Actions regarding showing/hiding columns // Actions regarding showing/hiding columns
auto add_column = [this](gui::game_list_columns col, const QString& header_text, const QString& action_text) const auto add_column = [this](gui::game_list_columns col)
{ {
m_game_list->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_columnActs.append(new QAction(action_text, this)); m_game_list->setHorizontalHeaderItem(column, new QTableWidgetItem(get_header_text(column)));
m_column_acts[column] = new QAction(get_action_text(column), this);
}; };
add_column(gui::game_list_columns::icon, tr("Icon"), tr("Show Icons")); add_column(gui::game_list_columns::icon);
add_column(gui::game_list_columns::name, tr("Name"), tr("Show Names")); add_column(gui::game_list_columns::name);
add_column(gui::game_list_columns::serial, tr("Serial"), tr("Show Serials")); add_column(gui::game_list_columns::serial);
add_column(gui::game_list_columns::firmware, tr("Firmware"), tr("Show Firmwares")); add_column(gui::game_list_columns::firmware);
add_column(gui::game_list_columns::version, tr("Version"), tr("Show Versions")); add_column(gui::game_list_columns::version);
add_column(gui::game_list_columns::category, tr("Category"), tr("Show Categories")); add_column(gui::game_list_columns::category);
add_column(gui::game_list_columns::path, tr("Path"), tr("Show Paths")); add_column(gui::game_list_columns::path);
add_column(gui::game_list_columns::move, tr("PlayStation Move"), tr("Show PlayStation Move")); add_column(gui::game_list_columns::move);
add_column(gui::game_list_columns::resolution, tr("Supported Resolutions"), tr("Show Supported Resolutions")); add_column(gui::game_list_columns::resolution);
add_column(gui::game_list_columns::sound, tr("Sound Formats"), tr("Show Sound Formats")); add_column(gui::game_list_columns::sound);
add_column(gui::game_list_columns::parental, tr("Parental Level"), tr("Show Parental Levels")); add_column(gui::game_list_columns::parental);
add_column(gui::game_list_columns::last_play, tr("Last Played"), tr("Show Last Played")); add_column(gui::game_list_columns::last_play);
add_column(gui::game_list_columns::playtime, tr("Time Played"), tr("Show Time Played")); add_column(gui::game_list_columns::playtime);
add_column(gui::game_list_columns::compat, tr("Compatibility"), tr("Show Compatibility")); add_column(gui::game_list_columns::compat);
add_column(gui::game_list_columns::dir_size, tr("Space On Disk"), tr("Show Space On Disk")); add_column(gui::game_list_columns::dir_size);
m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
m_progress_dialog->setMinimumDuration(200); // Only show the progress dialog after some time has passed m_progress_dialog->setMinimumDuration(200); // Only show the progress dialog after some time has passed
@ -211,7 +212,7 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
m_game_list->create_header_actions(m_columnActs, m_game_list->create_header_actions(m_column_acts,
[this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); }, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); },
[this](int col, bool visible) { m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), visible); }); [this](int col, bool visible) { m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), visible); });
} }
@ -227,7 +228,7 @@ void game_list_frame::LoadSettings()
m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool(); m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool();
m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool(); m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool();
m_game_list->sync_header_actions(m_columnActs, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); }); m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); });
} }
game_list_frame::~game_list_frame() game_list_frame::~game_list_frame()
@ -238,6 +239,54 @@ game_list_frame::~game_list_frame()
gui::utils::stop_future_watcher(m_refresh_watcher, true); gui::utils::stop_future_watcher(m_refresh_watcher, true);
} }
QString game_list_frame::get_header_text(int col) const
{
switch (static_cast<gui::game_list_columns>(col))
{
case gui::game_list_columns::icon: return tr("Icon");
case gui::game_list_columns::name: return tr("Name");
case gui::game_list_columns::serial: return tr("Serial");
case gui::game_list_columns::firmware: return tr("Firmware");
case gui::game_list_columns::version: return tr("Version");
case gui::game_list_columns::category: return tr("Category");
case gui::game_list_columns::path: return tr("Path");
case gui::game_list_columns::move: return tr("PlayStation Move");
case gui::game_list_columns::resolution: return tr("Supported Resolutions");
case gui::game_list_columns::sound: return tr("Sound Formats");
case gui::game_list_columns::parental: return tr("Parental Level");
case gui::game_list_columns::last_play: return tr("Last Played");
case gui::game_list_columns::playtime: return tr("Time Played");
case gui::game_list_columns::compat: return tr("Compatibility");
case gui::game_list_columns::dir_size: return tr("Space On Disk");
case gui::game_list_columns::count: break;
}
return {};
}
QString game_list_frame::get_action_text(int col) const
{
switch (static_cast<gui::game_list_columns>(col))
{
case gui::game_list_columns::icon: return tr("Show Icons");
case gui::game_list_columns::name: return tr("Show Names");
case gui::game_list_columns::serial: return tr("Show Serials");
case gui::game_list_columns::firmware: return tr("Show Firmwares");
case gui::game_list_columns::version: return tr("Show Versions");
case gui::game_list_columns::category: return tr("Show Categories");
case gui::game_list_columns::path: return tr("Show Paths");
case gui::game_list_columns::move: return tr("Show PlayStation Move");
case gui::game_list_columns::resolution: return tr("Show Supported Resolutions");
case gui::game_list_columns::sound: return tr("Show Sound Formats");
case gui::game_list_columns::parental: return tr("Show Parental Levels");
case gui::game_list_columns::last_play: return tr("Show Last Played");
case gui::game_list_columns::playtime: return tr("Show Time Played");
case gui::game_list_columns::compat: return tr("Show Compatibility");
case gui::game_list_columns::dir_size: return tr("Show Space On Disk");
case gui::game_list_columns::count: break;
}
return {};
}
void game_list_frame::OnColClicked(int col) void game_list_frame::OnColClicked(int col)
{ {
if (col == static_cast<int>(gui::game_list_columns::icon)) return; // Don't "sort" icons. if (col == static_cast<int>(gui::game_list_columns::icon)) return; // Don't "sort" icons.
@ -417,6 +466,21 @@ void game_list_frame::Refresh(const bool from_drive, const std::vector<std::stri
m_progress_dialog->SetValue(0); m_progress_dialog->SetValue(0);
} }
// Update headers
for (int col = 0; col < m_game_list->horizontalHeader()->count(); col++)
{
if (auto item = m_game_list->horizontalHeaderItem(col))
{
item->setText(get_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_column_acts)
{
action->setText(get_action_text(col));
}
const std::string games_dir = rpcs3::utils::get_games_dir(); const std::string games_dir = rpcs3::utils::get_games_dir();
// Remove the specified and detected serials (title id) belonging to "games_dir" folder only from the game list in memory // Remove the specified and detected serials (title id) belonging to "games_dir" folder only from the game list in memory
@ -920,7 +984,7 @@ void game_list_frame::OnRefreshFinished()
if (!std::exchange(m_initial_refresh_done, true)) if (!std::exchange(m_initial_refresh_done, true))
{ {
m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray()); m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray());
m_game_list->sync_header_actions(m_columnActs, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); }); m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); });
} }
// Emit signal and remove slots // Emit signal and remove slots
@ -959,9 +1023,9 @@ void game_list_frame::ToggleCategoryFilter(const QStringList& categories, bool s
void game_list_frame::SaveSettings() void game_list_frame::SaveSettings()
{ {
for (int col = 0; col < m_columnActs.count(); ++col) for (const auto& [col, action] : m_column_acts)
{ {
m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), m_columnActs[col]->isChecked()); m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), action->isChecked());
} }
m_gui_settings->SetValue(gui::gl_sortCol, m_sort_column, false); m_gui_settings->SetValue(gui::gl_sortCol, m_sort_column, false);
m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false); m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false);
@ -1121,7 +1185,7 @@ void game_list_frame::CreateShortcuts(const std::vector<game_info>& games, const
} }
} }
void game_list_frame::ShowContextMenu(const QPoint &pos) void game_list_frame::ShowContextMenu(const QPoint& pos)
{ {
QPoint global_pos; QPoint global_pos;
game_info gameinfo; game_info gameinfo;
@ -2704,7 +2768,7 @@ void game_list_frame::ShowCustomConfigIcon(const game_info& game)
RepaintIcons(); RepaintIcons();
} }
void game_list_frame::ResizeIcons(const int& slider_pos) void game_list_frame::ResizeIcons(int slider_pos)
{ {
m_icon_size_index = slider_pos; m_icon_size_index = slider_pos;
m_icon_size = gui_settings::SizeFromSlider(slider_pos); m_icon_size = gui_settings::SizeFromSlider(slider_pos);
@ -2712,7 +2776,7 @@ void game_list_frame::ResizeIcons(const int& slider_pos)
RepaintIcons(); RepaintIcons();
} }
void game_list_frame::RepaintIcons(const bool& from_settings) void game_list_frame::RepaintIcons(bool from_settings)
{ {
gui::utils::stop_future_watcher(m_parsing_watcher, false); gui::utils::stop_future_watcher(m_parsing_watcher, false);
gui::utils::stop_future_watcher(m_refresh_watcher, false); gui::utils::stop_future_watcher(m_refresh_watcher, false);
@ -2746,7 +2810,7 @@ void game_list_frame::SetShowHidden(bool show)
m_show_hidden = show; m_show_hidden = show;
} }
void game_list_frame::SetListMode(const bool& is_list) void game_list_frame::SetListMode(bool is_list)
{ {
m_old_layout_is_list = m_is_list_layout; m_old_layout_is_list = m_is_list_layout;
m_is_list_layout = is_list; m_is_list_layout = is_list;

View file

@ -48,10 +48,10 @@ public:
void SaveSettings(); void SaveSettings();
/** Resize Gamelist Icons to size given by slider position */ /** Resize Gamelist Icons to size given by slider position */
void ResizeIcons(const int& slider_pos); void ResizeIcons(int slider_pos);
/** Repaint Gamelist Icons with new background color */ /** Repaint Gamelist Icons with new background color */
void RepaintIcons(const bool& from_settings = false); void RepaintIcons(bool from_settings = false);
void SetShowHidden(bool show); void SetShowHidden(bool show);
@ -70,7 +70,7 @@ public Q_SLOTS:
void BatchRemoveCustomConfigurations(); void BatchRemoveCustomConfigurations();
void BatchRemoveCustomPadConfigurations(); void BatchRemoveCustomPadConfigurations();
void BatchRemoveShaderCaches(); void BatchRemoveShaderCaches();
void SetListMode(const bool& is_list); void SetListMode(bool is_list);
void SetSearchText(const QString& text); void SetSearchText(const QString& text);
void SetShowCompatibilityInGrid(bool show); void SetShowCompatibilityInGrid(bool show);
void SetPreferGameDataIcons(bool enabled); void SetPreferGameDataIcons(bool enabled);
@ -83,7 +83,7 @@ private Q_SLOTS:
void OnRefreshFinished(); void OnRefreshFinished();
void OnCompatFinished(); void OnCompatFinished();
void OnColClicked(int col); void OnColClicked(int col);
void ShowContextMenu(const QPoint &pos); void ShowContextMenu(const QPoint& pos);
void doubleClickedSlot(QTableWidgetItem* item); void doubleClickedSlot(QTableWidgetItem* item);
void doubleClickedSlot(const game_info& game); void doubleClickedSlot(const game_info& game);
void ItemSelectionChangedSlot(); void ItemSelectionChangedSlot();
@ -91,7 +91,7 @@ Q_SIGNALS:
void GameListFrameClosed(); void GameListFrameClosed();
void NotifyGameSelection(const game_info& game); void NotifyGameSelection(const game_info& game);
void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = ""); void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = "");
void RequestIconSizeChange(const int& val); void RequestIconSizeChange(int val);
void NotifyEmuSettingsChange(); void NotifyEmuSettingsChange();
void FocusToSearchBar(); void FocusToSearchBar();
void Refreshed(); void Refreshed();
@ -127,6 +127,9 @@ protected:
private: private:
void push_path(const std::string& path, std::vector<std::string>& legit_paths); void push_path(const std::string& path, std::vector<std::string>& legit_paths);
QString get_header_text(int col) const;
QString get_action_text(int col) const;
void ShowCustomConfigIcon(const game_info& game); void ShowCustomConfigIcon(const game_info& game);
bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const; bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const;
@ -165,7 +168,7 @@ private:
game_list_table* m_game_list = nullptr; game_list_table* m_game_list = nullptr;
game_compatibility* m_game_compat = nullptr; game_compatibility* m_game_compat = nullptr;
progress_dialog* m_progress_dialog = nullptr; progress_dialog* m_progress_dialog = nullptr;
QList<QAction*> m_columnActs; std::map<int, QAction*> m_column_acts;
Qt::SortOrder m_col_sort_order{}; Qt::SortOrder m_col_sort_order{};
int m_sort_column{}; int m_sort_column{};
bool m_initial_refresh_done = false; bool m_initial_refresh_done = false;

View file

@ -447,7 +447,7 @@ void gs_frame::toggle_recording()
// Play a sound // Play a sound
if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_recording.wav"; fs::is_file(sound_path)) if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_recording.wav"; fs::is_file(sound_path))
{ {
Emu.GetCallbacks().play_sound(sound_path); Emu.GetCallbacks().play_sound(sound_path, std::nullopt);
} }
else else
{ {
@ -1070,7 +1070,7 @@ void gs_frame::take_screenshot(std::vector<u8>&& data, u32 sshot_width, u32 ssho
{ {
if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_screenshot.wav"; fs::is_file(sound_path)) if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_screenshot.wav"; fs::is_file(sound_path))
{ {
Emu.GetCallbacks().play_sound(sound_path); Emu.GetCallbacks().play_sound(sound_path, std::nullopt);
} }
else else
{ {

View file

@ -743,9 +743,9 @@ void gui_application::InitializeCallbacks()
return m_emu_settings->GetLocalizedSetting(node, enum_index); return m_emu_settings->GetLocalizedSetting(node, enum_index);
}; };
callbacks.play_sound = [this](const std::string& path) callbacks.play_sound = [this](const std::string& path, std::optional<f32> volume)
{ {
Emu.CallFromMainThread([this, path]() Emu.CallFromMainThread([this, path, volume]()
{ {
if (fs::is_file(path)) if (fs::is_file(path))
{ {
@ -758,12 +758,12 @@ void gui_application::InitializeCallbacks()
// Create a new sound effect. Re-using the same object seems to be broken for some users starting with Qt 6.6.3. // Create a new sound effect. Re-using the same object seems to be broken for some users starting with Qt 6.6.3.
std::unique_ptr<QSoundEffect> sound_effect = std::make_unique<QSoundEffect>(); std::unique_ptr<QSoundEffect> sound_effect = std::make_unique<QSoundEffect>();
sound_effect->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); sound_effect->setSource(QUrl::fromLocalFile(QString::fromStdString(path)));
sound_effect->setVolume(audio::get_volume()); sound_effect->setVolume(volume ? *volume : audio::get_volume());
sound_effect->play(); sound_effect->play();
m_sound_effects.push_back(std::move(sound_effect)); m_sound_effects.push_back(std::move(sound_effect));
} }
}); }, nullptr, false);
}; };
if (m_show_gui) // If this is false, we already have a fallback in the main_application. if (m_show_gui) // If this is false, we already have a fallback in the main_application.

View file

@ -44,6 +44,7 @@
#include "vfs_tool_dialog.h" #include "vfs_tool_dialog.h"
#include "welcome_dialog.h" #include "welcome_dialog.h"
#include "music_player_dialog.h" #include "music_player_dialog.h"
#include "sound_effect_manager_dialog.h"
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
@ -2343,6 +2344,8 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString
{ {
m_game_list_frame->Refresh(true); m_game_list_frame->Refresh(true);
} }
Q_EMIT RequestDialogRepaint();
} }
void main_window::ShowTitleBars(bool show) const void main_window::ShowTitleBars(bool show) const
@ -3053,6 +3056,12 @@ void main_window::CreateConnects()
screenshot_manager->show(); screenshot_manager->show();
}); });
connect(ui->actionManage_SoundEffects, &QAction::triggered, this, [this]
{
sound_effect_manager_dialog* dlg = new sound_effect_manager_dialog();
dlg->show();
});
connect(ui->toolsCgDisasmAct, &QAction::triggered, this, [this] connect(ui->toolsCgDisasmAct, &QAction::triggered, this, [this]
{ {
cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings); cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings);
@ -3336,7 +3345,7 @@ void main_window::CreateConnects()
connect(ui->showCustomIconsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetShowCustomIcons); connect(ui->showCustomIconsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetShowCustomIcons);
connect(ui->playHoverGifsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPlayHoverGifs); connect(ui->playHoverGifsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPlayHoverGifs);
connect(m_game_list_frame, &game_list_frame::RequestIconSizeChange, this, [this](const int& val) connect(m_game_list_frame, &game_list_frame::RequestIconSizeChange, this, [this](int val)
{ {
const int idx = ui->sizeSlider->value() + val; const int idx = ui->sizeSlider->value() + val;
m_save_slider_pos = true; m_save_slider_pos = true;

View file

@ -306,6 +306,7 @@
<addaction name="actionManage_Cheats"/> <addaction name="actionManage_Cheats"/>
<addaction name="actionManage_Game_Patches"/> <addaction name="actionManage_Game_Patches"/>
<addaction name="actionManage_Screenshots"/> <addaction name="actionManage_Screenshots"/>
<addaction name="actionManage_SoundEffects"/>
</widget> </widget>
<widget class="QMenu" name="menuUtilities"> <widget class="QMenu" name="menuUtilities">
<property name="title"> <property name="title">
@ -1442,6 +1443,11 @@
<string>Music Player</string> <string>Music Player</string>
</property> </property>
</action> </action>
<action name="actionManage_SoundEffects">
<property name="text">
<string>Sound Effects</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View file

@ -432,9 +432,9 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDis
scroll(0); // Refresh scroll(0); // Refresh
}); });
connect(sb_words, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [=, this]() connect(sb_words, &QSpinBox::valueChanged, this, [this](int value)
{ {
m_colcount = 1 << sb_words->value(); m_colcount = 1 << value;
ShowMemory(); ShowMemory();
}); });

View file

@ -105,7 +105,7 @@ pad_motion_settings_dialog::pad_motion_settings_dialog(QDialog* parent, std::sha
m_config_entries[i]->mirrored.set(state != Qt::Unchecked); m_config_entries[i]->mirrored.set(state != Qt::Unchecked);
}); });
connect(m_shifts[i], QOverload<int>::of(&QSpinBox::valueChanged), this, [this, i](int value) connect(m_shifts[i], &QSpinBox::valueChanged, this, [this, i](int value)
{ {
std::lock_guard lock(m_config_mutex); std::lock_guard lock(m_config_mutex);
m_config_entries[i]->shift.set(value); m_config_entries[i]->shift.set(value);

View file

@ -123,8 +123,8 @@ patch_manager_dialog::patch_manager_dialog(std::shared_ptr<gui_settings> gui_set
handle_config_value_changed(ui->configurable_combo_box->itemData(index).toDouble()); handle_config_value_changed(ui->configurable_combo_box->itemData(index).toDouble());
} }
}); });
connect(ui->configurable_spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, &patch_manager_dialog::handle_config_value_changed); connect(ui->configurable_spin_box, &QSpinBox::valueChanged, this, &patch_manager_dialog::handle_config_value_changed);
connect(ui->configurable_double_spin_box, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &patch_manager_dialog::handle_config_value_changed); connect(ui->configurable_double_spin_box, &QDoubleSpinBox::valueChanged, this, &patch_manager_dialog::handle_config_value_changed);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
{ {

View file

@ -281,7 +281,7 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs)
m_accel_spin_boxes.push_back(mouse_acceleration_spin_box); m_accel_spin_boxes.push_back(mouse_acceleration_spin_box);
mouse_acceleration_spin_box->setRange(0.1, 10.0); mouse_acceleration_spin_box->setRange(0.1, 10.0);
mouse_acceleration_spin_box->setValue(config->mouse_acceleration.get() / 100.0); mouse_acceleration_spin_box->setValue(config->mouse_acceleration.get() / 100.0);
connect(mouse_acceleration_spin_box, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, [player](double value) connect(mouse_acceleration_spin_box, &QDoubleSpinBox::valueChanged, this, [player](double value)
{ {
auto& config = ::at32(g_cfg_raw_mouse.players, player)->mouse_acceleration; auto& config = ::at32(g_cfg_raw_mouse.players, player)->mouse_acceleration;
config.set(std::clamp(value * 100.0, config.min, config.max)); config.set(std::clamp(value * 100.0, config.min, config.max));

View file

@ -32,7 +32,7 @@
LOG_CHANNEL(gui_log, "GUI"); LOG_CHANNEL(gui_log, "GUI");
enum SaveColumns enum class SaveColumns
{ {
Icon = 0, Icon = 0,
Name = 1, Name = 1,
@ -60,30 +60,26 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr<gui_settings> gui_setti
setMinimumSize(QSize(400, 400)); setMinimumSize(QSize(400, 400));
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
Init();
}
/*
* Future proofing. Makes it easier in future if I add ability to change directories
*/
void save_manager_dialog::Init()
{
// Table // Table
m_list = new game_list(); m_list = new game_list();
m_list->setItemDelegate(new game_list_delegate(m_list)); m_list->setItemDelegate(new game_list_delegate(m_list));
m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
m_list->setSelectionBehavior(QAbstractItemView::SelectRows); m_list->setSelectionBehavior(QAbstractItemView::SelectRows);
m_list->setContextMenuPolicy(Qt::CustomContextMenu); m_list->setContextMenuPolicy(Qt::CustomContextMenu);
m_list->setColumnCount(SaveColumns::Count); m_list->setColumnCount(static_cast<int>(SaveColumns::Count));
m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
m_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); m_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
m_list->verticalScrollBar()->setSingleStep(20); m_list->verticalScrollBar()->setSingleStep(20);
m_list->horizontalScrollBar()->setSingleStep(10); m_list->horizontalScrollBar()->setSingleStep(10);
m_list->setHorizontalHeaderLabels(QStringList() << tr("Icon") << tr("Title & Subtitle") << tr("Last Modified") << tr("Save ID") << tr("Notes"));
m_list->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); m_list->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
m_list->horizontalHeader()->setStretchLastSection(true); m_list->horizontalHeader()->setStretchLastSection(true);
m_list->setMouseTracking(true); m_list->setMouseTracking(true);
for (int column = 0; column < static_cast<int>(SaveColumns::Count); column++)
{
m_list->setHorizontalHeaderItem(column, new QTableWidgetItem(get_header_text(column)));
}
// Bottom bar // Bottom bar
const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt(); const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt();
m_icon_size = QSize(icon_size, icon_size * 176 / 320); m_icon_size = QSize(icon_size, icon_size * 176 / 320);
@ -162,7 +158,7 @@ void save_manager_dialog::Init()
connect(m_button_folder, &QAbstractButton::clicked, [this]() connect(m_button_folder, &QAbstractButton::clicked, [this]()
{ {
const int idx = m_list->currentRow(); const int idx = m_list->currentRow();
QTableWidgetItem* item = m_list->item(idx, SaveColumns::Name); QTableWidgetItem* item = m_list->item(idx, static_cast<int>(SaveColumns::Name));
if (!item) if (!item)
{ {
return; return;
@ -176,12 +172,12 @@ void save_manager_dialog::Init()
connect(m_list, &QTableWidget::customContextMenuRequested, this, &save_manager_dialog::ShowContextMenu); connect(m_list, &QTableWidget::customContextMenuRequested, this, &save_manager_dialog::ShowContextMenu);
connect(m_list, &QTableWidget::cellChanged, [&](int row, int col) connect(m_list, &QTableWidget::cellChanged, [&](int row, int col)
{ {
if (col != SaveColumns::Note) if (col != static_cast<int>(SaveColumns::Note))
{ {
return; return;
} }
QTableWidgetItem* user_item = m_list->item(row, SaveColumns::Name); QTableWidgetItem* user_item = m_list->item(row, static_cast<int>(SaveColumns::Name));
QTableWidgetItem* text_item = m_list->item(row, SaveColumns::Note); QTableWidgetItem* text_item = m_list->item(row, static_cast<int>(SaveColumns::Note));
if (!user_item || !text_item) if (!user_item || !text_item)
{ {
return; return;
@ -196,7 +192,7 @@ void save_manager_dialog::Init()
connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails); connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails);
connect(this, &save_manager_dialog::IconReady, this, [this](int index, const QPixmap& pixmap) connect(this, &save_manager_dialog::IconReady, this, [this](int index, const QPixmap& pixmap)
{ {
if (movie_item* item = static_cast<movie_item*>(m_list->item(index, SaveColumns::Icon))) if (movie_item* item = static_cast<movie_item*>(m_list->item(index, static_cast<int>(SaveColumns::Icon))))
{ {
item->setData(SaveUserRole::PixmapScaled, pixmap); item->setData(SaveUserRole::PixmapScaled, pixmap);
item->image_change_callback(); item->image_change_callback();
@ -205,6 +201,20 @@ void save_manager_dialog::Init()
connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed); connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed);
} }
QString save_manager_dialog::get_header_text(int col) const
{
switch (static_cast<SaveColumns>(col))
{
case SaveColumns::Icon: return tr("Icon");
case SaveColumns::Name: return tr("Title & Subtitle");
case SaveColumns::Time: return tr("Last Modified");
case SaveColumns::Dir: return tr("Save ID");
case SaveColumns::Note: return tr("Notes");
case SaveColumns::Count: break;
}
return {};
}
/** /**
* This certainly isn't ideal for this code, as it essentially copies cellSaveData. But, I have no other choice without adding public methods to cellSaveData. * This certainly isn't ideal for this code, as it essentially copies cellSaveData. But, I have no other choice without adding public methods to cellSaveData.
*/ */
@ -300,6 +310,15 @@ void save_manager_dialog::UpdateList()
m_list->clearContents(); m_list->clearContents();
m_list->setRowCount(static_cast<int>(m_save_entries.size())); m_list->setRowCount(static_cast<int>(m_save_entries.size()));
// Update headers
for (int col = 0; col < m_list->horizontalHeader()->count(); col++)
{
if (auto item = m_list->horizontalHeaderItem(col))
{
item->setText(get_header_text(col));
}
}
const QVariantMap notes = m_persistent_settings->GetValue(gui::persistent::save_notes).toMap(); const QVariantMap notes = m_persistent_settings->GetValue(gui::persistent::save_notes).toMap();
if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool())
@ -358,20 +377,20 @@ void save_manager_dialog::UpdateList()
icon_item->stop_movie(); icon_item->stop_movie();
} }
}); });
m_list->setItem(i, SaveColumns::Icon, icon_item); m_list->setItem(i, static_cast<int>(SaveColumns::Icon), icon_item);
custom_table_widget_item* titleItem = new custom_table_widget_item(title); custom_table_widget_item* titleItem = new custom_table_widget_item(title);
titleItem->setData(Qt::UserRole, i); // For sorting to work properly titleItem->setData(Qt::UserRole, i); // For sorting to work properly
titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable); titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable);
m_list->setItem(i, SaveColumns::Name, titleItem); m_list->setItem(i, static_cast<int>(SaveColumns::Name), titleItem);
custom_table_widget_item* timeItem = new custom_table_widget_item(gui::utils::format_timestamp(entry.mtime)); custom_table_widget_item* timeItem = new custom_table_widget_item(gui::utils::format_timestamp(entry.mtime));
timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable); timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable);
m_list->setItem(i, SaveColumns::Time, timeItem); m_list->setItem(i, static_cast<int>(SaveColumns::Time), timeItem);
custom_table_widget_item* dirNameItem = new custom_table_widget_item(dir_name); custom_table_widget_item* dirNameItem = new custom_table_widget_item(dir_name);
dirNameItem->setFlags(dirNameItem->flags() & ~Qt::ItemIsEditable); dirNameItem->setFlags(dirNameItem->flags() & ~Qt::ItemIsEditable);
m_list->setItem(i, SaveColumns::Dir, dirNameItem); m_list->setItem(i, static_cast<int>(SaveColumns::Dir), dirNameItem);
custom_table_widget_item* noteItem = new custom_table_widget_item(); custom_table_widget_item* noteItem = new custom_table_widget_item();
noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable); noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable);
@ -379,7 +398,7 @@ void save_manager_dialog::UpdateList()
{ {
noteItem->setText(notes[dir_name].toString()); noteItem->setText(notes[dir_name].toString());
} }
m_list->setItem(i, SaveColumns::Note, noteItem); m_list->setItem(i, static_cast<int>(SaveColumns::Note), noteItem);
} }
m_list->setSortingEnabled(true); // Enable sorting only after using setItem calls m_list->setSortingEnabled(true); // Enable sorting only after using setItem calls
@ -422,7 +441,7 @@ void save_manager_dialog::UpdateIcons()
for (int i = 0; i < m_list->rowCount(); ++i) for (int i = 0; i < m_list->rowCount(); ++i)
{ {
if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon))) if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, static_cast<int>(SaveColumns::Icon))))
{ {
icon_item->setData(SaveUserRole::PixmapScaled, placeholder); icon_item->setData(SaveUserRole::PixmapScaled, placeholder);
icon_item->setData(Qt::DecorationRole, placeholder); icon_item->setData(Qt::DecorationRole, placeholder);
@ -430,14 +449,14 @@ void save_manager_dialog::UpdateIcons()
} }
m_list->resizeRowsToContents(); m_list->resizeRowsToContents();
m_list->resizeColumnToContents(SaveColumns::Icon); m_list->resizeColumnToContents(static_cast<int>(SaveColumns::Icon));
const s32 language_index = gui_application::get_language_id(); const s32 language_index = gui_application::get_language_id();
const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index);
for (int i = 0; i < m_list->rowCount(); ++i) for (int i = 0; i < m_list->rowCount(); ++i)
{ {
if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon))) if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, static_cast<int>(SaveColumns::Icon))))
{ {
icon_item->set_icon_load_func([this, cancel = icon_item->icon_loading_aborted(), dpr, localized_icon](int index) icon_item->set_icon_load_func([this, cancel = icon_item->icon_loading_aborted(), dpr, localized_icon](int index)
{ {
@ -448,12 +467,12 @@ void save_manager_dialog::UpdateIcons()
QPixmap icon; QPixmap icon;
if (movie_item* item = static_cast<movie_item*>(m_list->item(index, SaveColumns::Icon))) if (movie_item* item = static_cast<movie_item*>(m_list->item(index, static_cast<int>(SaveColumns::Icon))))
{ {
if (!item->data(SaveUserRole::PixmapLoaded).toBool()) if (!item->data(SaveUserRole::PixmapLoaded).toBool())
{ {
// Load game icon // Load game icon
if (QTableWidgetItem* user_item = m_list->item(index, SaveColumns::Name)) if (QTableWidgetItem* user_item = m_list->item(index, static_cast<int>(SaveColumns::Name)))
{ {
const int idx_real = user_item->data(Qt::UserRole).toInt(); const int idx_real = user_item->data(Qt::UserRole).toInt();
const SaveDataEntry& entry = ::at32(m_save_entries, idx_real); const SaveDataEntry& entry = ::at32(m_save_entries, idx_real);
@ -534,7 +553,7 @@ void save_manager_dialog::OnSort(int logicalIndex)
// Remove a save file, need to be confirmed. // Remove a save file, need to be confirmed.
void save_manager_dialog::OnEntryRemove(int row, bool user_interaction) void save_manager_dialog::OnEntryRemove(int row, bool user_interaction)
{ {
if (QTableWidgetItem* item = m_list->item(row, SaveColumns::Name)) if (QTableWidgetItem* item = m_list->item(row, static_cast<int>(SaveColumns::Name)))
{ {
const int idx_real = item->data(Qt::UserRole).toInt(); const int idx_real = item->data(Qt::UserRole).toInt();
const SaveDataEntry& entry = ::at32(m_save_entries, idx_real); const SaveDataEntry& entry = ::at32(m_save_entries, idx_real);
@ -599,7 +618,7 @@ void save_manager_dialog::ShowContextMenu(const QPoint& pos)
connect(removeAct, &QAction::triggered, this, &save_manager_dialog::OnEntriesRemove); // entriesremove handles case of one as well connect(removeAct, &QAction::triggered, this, &save_manager_dialog::OnEntriesRemove); // entriesremove handles case of one as well
connect(showDirAct, &QAction::triggered, [this, idx]() connect(showDirAct, &QAction::triggered, [this, idx]()
{ {
QTableWidgetItem* item = m_list->item(idx, SaveColumns::Name); QTableWidgetItem* item = m_list->item(idx, static_cast<int>(SaveColumns::Name));
if (!item) if (!item)
{ {
return; return;
@ -655,8 +674,8 @@ void save_manager_dialog::UpdateDetails()
WaitForRepaintThreads(false); WaitForRepaintThreads(false);
const int row = m_list->currentRow(); const int row = m_list->currentRow();
QTableWidgetItem* item = m_list->item(row, SaveColumns::Name); QTableWidgetItem* item = m_list->item(row, static_cast<int>(SaveColumns::Name));
movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, SaveColumns::Icon)); movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, static_cast<int>(SaveColumns::Icon)));
if (!item || !icon_item) if (!item || !icon_item)
{ {
@ -692,7 +711,7 @@ void save_manager_dialog::WaitForRepaintThreads(bool abort)
{ {
for (int i = 0; i < m_list->rowCount(); i++) for (int i = 0; i < m_list->rowCount(); i++)
{ {
if (movie_item* item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon))) if (movie_item* item = static_cast<movie_item*>(m_list->item(i, static_cast<int>(SaveColumns::Icon))))
{ {
item->wait_for_icon_loading(abort); item->wait_for_icon_loading(abort);
} }
@ -706,7 +725,7 @@ void save_manager_dialog::text_changed(const QString& text)
if (text.isEmpty()) if (text.isEmpty())
return true; return true;
for (int col = SaveColumns::Name; col < SaveColumns::Count; col++) for (int col = static_cast<int>(SaveColumns::Name); col < static_cast<int>(SaveColumns::Count); col++)
{ {
const QTableWidgetItem* item = m_list->item(row, col); const QTableWidgetItem* item = m_list->item(row, col);

View file

@ -39,7 +39,6 @@ Q_SIGNALS:
void IconReady(int index, const QPixmap& new_icon); void IconReady(int index, const QPixmap& new_icon);
private: private:
void Init();
void UpdateList(); void UpdateList();
void UpdateIcons(); void UpdateIcons();
void ShowContextMenu(const QPoint& pos); void ShowContextMenu(const QPoint& pos);
@ -49,6 +48,8 @@ private:
std::vector<SaveDataEntry> GetSaveEntries(const std::string& base_dir); std::vector<SaveDataEntry> GetSaveEntries(const std::string& base_dir);
QString get_header_text(int col) const;
game_list* m_list = nullptr; game_list* m_list = nullptr;
std::string m_dir; std::string m_dir;
std::vector<SaveDataEntry> m_save_entries; std::vector<SaveDataEntry> m_save_entries;

View file

@ -63,15 +63,16 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
m_game_table->setAlternatingRowColors(true); m_game_table->setAlternatingRowColors(true);
m_game_table->installEventFilter(this); m_game_table->installEventFilter(this);
auto add_game_column = [this](gui::savestate_game_list_columns col, const QString& header_text, const QString& action_text) const auto add_game_column = [this](gui::savestate_game_list_columns col)
{ {
m_game_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_game_column_acts.append(new QAction(action_text, this)); m_game_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_gamelist_header_text(column)));
m_game_column_acts[column] = new QAction(get_gamelist_action_text(column), this);
}; };
add_game_column(gui::savestate_game_list_columns::icon, tr("Icon"), tr("Show Icons")); add_game_column(gui::savestate_game_list_columns::icon);
add_game_column(gui::savestate_game_list_columns::name, tr("Game"), tr("Show Games")); add_game_column(gui::savestate_game_list_columns::name);
add_game_column(gui::savestate_game_list_columns::savestates, tr("Savestates"), tr("Show Savestates")); add_game_column(gui::savestate_game_list_columns::savestates);
// Savestate Table // Savestate Table
m_savestate_table = new game_list(); m_savestate_table = new game_list();
@ -94,16 +95,17 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
m_savestate_table->setAlternatingRowColors(true); m_savestate_table->setAlternatingRowColors(true);
m_savestate_table->installEventFilter(this); m_savestate_table->installEventFilter(this);
auto add_savestate_column = [this](gui::savestate_list_columns col, const QString& header_text, const QString& action_text) const auto add_savestate_column = [this](gui::savestate_list_columns col)
{ {
m_savestate_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_savestate_column_acts.append(new QAction(action_text, this)); m_savestate_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_savestate_header_text(column)));
m_savestate_column_acts[column] = new QAction(get_savestate_action_text(column), this);
}; };
add_savestate_column(gui::savestate_list_columns::name, tr("Name"), tr("Show Names")); add_savestate_column(gui::savestate_list_columns::name);
add_savestate_column(gui::savestate_list_columns::compatible, tr("Compatible"), tr("Show Compatible")); add_savestate_column(gui::savestate_list_columns::compatible);
add_savestate_column(gui::savestate_list_columns::date, tr("Created"), tr("Show Created")); add_savestate_column(gui::savestate_list_columns::date);
add_savestate_column(gui::savestate_list_columns::path, tr("Path"), tr("Show Paths")); add_savestate_column(gui::savestate_list_columns::path);
m_splitter = new QSplitter(); m_splitter = new QSplitter();
m_splitter->addWidget(m_game_table); m_splitter->addWidget(m_game_table);
@ -220,6 +222,56 @@ savestate_manager_dialog::~savestate_manager_dialog()
WaitAndAbortGameRepaintThreads(); WaitAndAbortGameRepaintThreads();
} }
QString savestate_manager_dialog::get_savestate_header_text(int col) const
{
switch (static_cast<gui::savestate_list_columns>(col))
{
case gui::savestate_list_columns::name: return tr("Name");
case gui::savestate_list_columns::compatible: return tr("Compatible");
case gui::savestate_list_columns::date: return tr("Created");
case gui::savestate_list_columns::path: return tr("Path");
case gui::savestate_list_columns::count: break;
}
return {};
}
QString savestate_manager_dialog::get_savestate_action_text(int col) const
{
switch (static_cast<gui::savestate_list_columns>(col))
{
case gui::savestate_list_columns::name: return tr("Show Names");
case gui::savestate_list_columns::compatible: return tr("Show Compatible");
case gui::savestate_list_columns::date: return tr("Show Created");
case gui::savestate_list_columns::path: return tr("Show Paths");
case gui::savestate_list_columns::count: break;
}
return {};
}
QString savestate_manager_dialog::get_gamelist_header_text(int col) const
{
switch (static_cast<gui::savestate_game_list_columns>(col))
{
case gui::savestate_game_list_columns::icon: return tr("Icon");
case gui::savestate_game_list_columns::name: return tr("Game");
case gui::savestate_game_list_columns::savestates: return tr("Savestates");
case gui::savestate_game_list_columns::count: break;
}
return {};
}
QString savestate_manager_dialog::get_gamelist_action_text(int col) const
{
switch (static_cast<gui::savestate_game_list_columns>(col))
{
case gui::savestate_game_list_columns::icon: return tr("Show Icons");
case gui::savestate_game_list_columns::name: return tr("Show Games");
case gui::savestate_game_list_columns::savestates: return tr("Show Savestates");
case gui::savestate_game_list_columns::count: break;
}
return {};
}
bool savestate_manager_dialog::LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates) bool savestate_manager_dialog::LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates)
{ {
ensure(!!game_savestates); ensure(!!game_savestates);
@ -625,6 +677,21 @@ void savestate_manager_dialog::PopulateGameTable()
m_game_table->clearContents(); m_game_table->clearContents();
m_game_table->setRowCount(static_cast<int>(m_savestate_db.size())); m_game_table->setRowCount(static_cast<int>(m_savestate_db.size()));
// Update headers
for (int col = 0; col < m_game_table->horizontalHeader()->count(); col++)
{
if (auto item = m_game_table->horizontalHeaderItem(col))
{
item->setText(get_gamelist_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_game_column_acts)
{
action->setText(get_gamelist_action_text(col));
}
m_game_combo->clear(); m_game_combo->clear();
m_game_combo->blockSignals(true); m_game_combo->blockSignals(true);
@ -681,6 +748,21 @@ void savestate_manager_dialog::PopulateSavestateTable()
m_savestate_table->setRowCount(static_cast<int>(savestates.size())); m_savestate_table->setRowCount(static_cast<int>(savestates.size()));
m_savestate_table->setSortingEnabled(false); // Disable sorting before using setItem calls m_savestate_table->setSortingEnabled(false); // Disable sorting before using setItem calls
// Update headers
for (int col = 0; col < m_savestate_table->horizontalHeader()->count(); col++)
{
if (auto item = m_savestate_table->horizontalHeaderItem(col))
{
item->setText(get_savestate_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_savestate_column_acts)
{
action->setText(get_savestate_action_text(col));
}
for (int i = 0; i < static_cast<int>(savestates.size()); i++) for (int i = 0; i < static_cast<int>(savestates.size()); i++)
{ {
const savestate_data& savestate = savestates[i]; const savestate_data& savestate = savestates[i];

View file

@ -64,6 +64,12 @@ private:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
QString get_savestate_header_text(int col) const;
QString get_savestate_action_text(int col) const;
QString get_gamelist_header_text(int col) const;
QString get_gamelist_action_text(int col) const;
std::shared_ptr<gui_settings> m_gui_settings; std::shared_ptr<gui_settings> m_gui_settings;
std::vector<game_info> m_game_info; std::vector<game_info> m_game_info;
@ -74,8 +80,8 @@ private:
game_list* m_savestate_table; //! UI element to display savestate stuff. game_list* m_savestate_table; //! UI element to display savestate stuff.
game_list* m_game_table; //! UI element to display games. game_list* m_game_table; //! UI element to display games.
QList<QAction*> m_savestate_column_acts; std::map<int, QAction*> m_savestate_column_acts;
QList<QAction*> m_game_column_acts; std::map<int, QAction*> m_game_column_acts;
int m_game_icon_size_index = 25; int m_game_icon_size_index = 25;
QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index); QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index);

View file

@ -0,0 +1,137 @@
#include "stdafx.h"
#include "sound_effect_manager_dialog.h"
#include <QApplication>
#include <QLabel>
#include <QGroupBox>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QScreen>
#include <QStyle>
#include <QMessageBox>
LOG_CHANNEL(gui_log, "GUI");
sound_effect_manager_dialog::sound_effect_manager_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("Sound Effects"));
setAttribute(Qt::WA_DeleteOnClose);
QLabel* description = new QLabel(tr("You can import sound effects for the RPCS3 overlays here.\nThe file format is .wav and you should try to make the sounds as short as possible."), this);
QVBoxLayout* main_layout = new QVBoxLayout(this);
main_layout->addWidget(description);
const auto add_sound_widget = [this, main_layout](rsx::overlays::sound_effect sound)
{
ensure(!m_widgets.contains(sound));
QString name;
switch (sound)
{
case rsx::overlays::sound_effect::cursor: name = tr("Cursor"); break;
case rsx::overlays::sound_effect::accept: name = tr("Accept"); break;
case rsx::overlays::sound_effect::cancel: name = tr("Cancel"); break;
case rsx::overlays::sound_effect::osk_accept: name = tr("Onscreen keyboard accept"); break;
case rsx::overlays::sound_effect::osk_cancel: name = tr("Onscreen keyboard cancel"); break;
case rsx::overlays::sound_effect::dialog_ok: name = tr("Dialog popup"); break;
case rsx::overlays::sound_effect::dialog_error: name = tr("Error dialog popup"); break;
case rsx::overlays::sound_effect::trophy: name = tr("Trophy popup"); break;
}
QPushButton* button = new QPushButton("", this);
connect(button, &QAbstractButton::clicked, this, [this, button, sound, name]()
{
const std::string path = rsx::overlays::get_sound_filepath(sound);
if (fs::is_file(path))
{
if (QMessageBox::question(this, tr("Remove sound effect?"), tr("Do you really want to remove the '%0' sound effect.").arg(name)) == QMessageBox::Yes)
{
if (!fs::remove_file(path))
{
gui_log.error("Failed to remove sound effect file '%s': %s", path, fs::g_tls_error);
}
update_widgets();
}
}
else
{
const QString src_path = QFileDialog::getOpenFileName(this, tr("Select Audio File to Import"), "", tr("WAV (*.wav);;"));
if (!src_path.isEmpty())
{
if (!fs::copy_file(src_path.toStdString(), path, true))
{
gui_log.error("Failed to import sound effect file '%s' to '%s': %s", src_path, path, fs::g_tls_error);
}
update_widgets();
}
}
});
QPushButton* play_button = new QPushButton(this);
play_button->setIcon(QApplication::style()->standardIcon(QStyle::SP_MediaPlay));
play_button->setIconSize(QSize(16, 16));
play_button->setFixedSize(24, 24);
connect(play_button, &QAbstractButton::clicked, this, [sound]()
{
rsx::overlays::play_sound(sound, 1.0f);
});
QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(button);
layout->addWidget(play_button);
layout->addStretch(1);
QGroupBox* gb = new QGroupBox(name, this);
gb->setLayout(layout);
main_layout->addWidget(gb);
m_widgets[sound] = {
.button = button,
.play_button = play_button
};
};
add_sound_widget(rsx::overlays::sound_effect::cursor);
add_sound_widget(rsx::overlays::sound_effect::accept);
add_sound_widget(rsx::overlays::sound_effect::cancel);
add_sound_widget(rsx::overlays::sound_effect::osk_accept);
add_sound_widget(rsx::overlays::sound_effect::osk_cancel);
add_sound_widget(rsx::overlays::sound_effect::dialog_ok);
add_sound_widget(rsx::overlays::sound_effect::dialog_error);
add_sound_widget(rsx::overlays::sound_effect::trophy);
setLayout(main_layout);
update_widgets();
resize(sizeHint());
}
sound_effect_manager_dialog::~sound_effect_manager_dialog()
{
}
void sound_effect_manager_dialog::update_widgets()
{
for (auto& [sound, widget] : m_widgets)
{
const bool file_exists = fs::is_file(rsx::overlays::get_sound_filepath(sound));
widget.play_button->setEnabled(file_exists);
if (file_exists)
{
widget.button->setText(tr("Remove"));
widget.button->setIcon(QApplication::style()->standardIcon(QStyle::SP_TrashIcon));
widget.button->setIconSize(QSize(16, 16));
}
else
{
widget.button->setText(tr("Import"));
widget.button->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogOpenButton));
widget.button->setIconSize(QSize(16, 16));
}
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "Emu/RSX/Overlays/overlays.h"
#include <QDialog>
#include <QPushButton>
class sound_effect_manager_dialog : public QDialog
{
Q_OBJECT
public:
explicit sound_effect_manager_dialog(QWidget* parent = nullptr);
~sound_effect_manager_dialog();
private:
void update_widgets();
struct widget
{
QPushButton* button = nullptr;
QPushButton* play_button = nullptr;
};
std::map<rsx::overlays::sound_effect, widget> m_widgets;
};

View file

@ -100,16 +100,17 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_game_table->setAlternatingRowColors(true); m_game_table->setAlternatingRowColors(true);
m_game_table->installEventFilter(this); m_game_table->installEventFilter(this);
auto add_game_column = [this](gui::trophy_game_list_columns col, const QString& header_text, const QString& action_text) const auto add_game_column = [this](gui::trophy_game_list_columns col)
{ {
m_game_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_game_column_acts.append(new QAction(action_text, this)); m_game_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_gamelist_header_text(column)));
m_game_column_acts[column] = new QAction(get_gamelist_action_text(column), this);
}; };
add_game_column(gui::trophy_game_list_columns::icon, tr("Icon"), tr("Show Icons")); add_game_column(gui::trophy_game_list_columns::icon);
add_game_column(gui::trophy_game_list_columns::name, tr("Game"), tr("Show Games")); add_game_column(gui::trophy_game_list_columns::name);
add_game_column(gui::trophy_game_list_columns::progress, tr("Progress"), tr("Show Progress")); add_game_column(gui::trophy_game_list_columns::progress);
add_game_column(gui::trophy_game_list_columns::trophies, tr("Trophies"), tr("Show Trophies")); add_game_column(gui::trophy_game_list_columns::trophies);
// Trophy Table // Trophy Table
m_trophy_table = new game_list(); m_trophy_table = new game_list();
@ -133,20 +134,21 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_trophy_table->setAlternatingRowColors(true); m_trophy_table->setAlternatingRowColors(true);
m_trophy_table->installEventFilter(this); m_trophy_table->installEventFilter(this);
auto add_trophy_column = [this](gui::trophy_list_columns col, const QString& header_text, const QString& action_text) const auto add_trophy_column = [this](gui::trophy_list_columns col)
{ {
m_trophy_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_trophy_column_acts.append(new QAction(action_text, this)); m_trophy_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_trophy_header_text(column)));
m_trophy_column_acts[column] = new QAction(get_trophy_action_text(column), this);
}; };
add_trophy_column(gui::trophy_list_columns::icon, tr("Icon"), tr("Show Icons")); add_trophy_column(gui::trophy_list_columns::icon);
add_trophy_column(gui::trophy_list_columns::name, tr("Name"), tr("Show Names")); add_trophy_column(gui::trophy_list_columns::name);
add_trophy_column(gui::trophy_list_columns::description, tr("Description"), tr("Show Descriptions")); add_trophy_column(gui::trophy_list_columns::description);
add_trophy_column(gui::trophy_list_columns::type, tr("Type"), tr("Show Types")); add_trophy_column(gui::trophy_list_columns::type);
add_trophy_column(gui::trophy_list_columns::is_unlocked, tr("Status"), tr("Show Status")); add_trophy_column(gui::trophy_list_columns::is_unlocked);
add_trophy_column(gui::trophy_list_columns::id, tr("ID"), tr("Show IDs")); add_trophy_column(gui::trophy_list_columns::id);
add_trophy_column(gui::trophy_list_columns::platinum_link, tr("Platinum Relevant"), tr("Show Platinum Relevant")); add_trophy_column(gui::trophy_list_columns::platinum_link);
add_trophy_column(gui::trophy_list_columns::time_unlocked, tr("Time Unlocked"), tr("Show Time Unlocked")); add_trophy_column(gui::trophy_list_columns::time_unlocked);
m_splitter = new QSplitter(); m_splitter = new QSplitter();
m_splitter->addWidget(m_game_table); m_splitter->addWidget(m_game_table);
@ -406,6 +408,66 @@ trophy_manager_dialog::~trophy_manager_dialog()
WaitAndAbortTrophyRepaintThreads(); WaitAndAbortTrophyRepaintThreads();
} }
QString trophy_manager_dialog::get_trophy_header_text(int col) const
{
switch (static_cast<gui::trophy_list_columns>(col))
{
case gui::trophy_list_columns::icon: return tr("Icon");
case gui::trophy_list_columns::name: return tr("Name");
case gui::trophy_list_columns::description: return tr("Description");
case gui::trophy_list_columns::type: return tr("Type");
case gui::trophy_list_columns::is_unlocked: return tr("Status");
case gui::trophy_list_columns::id: return tr("ID");
case gui::trophy_list_columns::platinum_link: return tr("Platinum Relevant");
case gui::trophy_list_columns::time_unlocked: return tr("Time Unlocked");
case gui::trophy_list_columns::count: break;
}
return {};
}
QString trophy_manager_dialog::get_trophy_action_text(int col) const
{
switch (static_cast<gui::trophy_list_columns>(col))
{
case gui::trophy_list_columns::icon: return tr("Show Icons");
case gui::trophy_list_columns::name: return tr("Show Names");
case gui::trophy_list_columns::description: return tr("Show Descriptions");
case gui::trophy_list_columns::type: return tr("Show Types");
case gui::trophy_list_columns::is_unlocked: return tr("Show Status");
case gui::trophy_list_columns::id: return tr("Show IDs");
case gui::trophy_list_columns::platinum_link: return tr("Show Platinum Relevant");
case gui::trophy_list_columns::time_unlocked: return tr("Show Time Unlocked");
case gui::trophy_list_columns::count: break;
}
return {};
}
QString trophy_manager_dialog::get_gamelist_header_text(int col) const
{
switch (static_cast<gui::trophy_game_list_columns>(col))
{
case gui::trophy_game_list_columns::icon: return tr("Icon");
case gui::trophy_game_list_columns::name: return tr("Game");
case gui::trophy_game_list_columns::progress: return tr("Progress");
case gui::trophy_game_list_columns::trophies: return tr("Trophies");
case gui::trophy_game_list_columns::count: break;
}
return {};
}
QString trophy_manager_dialog::get_gamelist_action_text(int col) const
{
switch (static_cast<gui::trophy_game_list_columns>(col))
{
case gui::trophy_game_list_columns::icon: return tr("Show Icons");
case gui::trophy_game_list_columns::name: return tr("Show Games");
case gui::trophy_game_list_columns::progress: return tr("Show Progress");
case gui::trophy_game_list_columns::trophies: return tr("Show Trophies");
case gui::trophy_game_list_columns::count: break;
}
return {};
}
bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name)
{ {
const std::string trophy_path = m_trophy_dir + trop_name; const std::string trophy_path = m_trophy_dir + trop_name;
@ -1062,6 +1124,21 @@ void trophy_manager_dialog::PopulateGameTable()
m_game_table->clearContents(); m_game_table->clearContents();
m_game_table->setRowCount(static_cast<int>(m_trophies_db.size())); m_game_table->setRowCount(static_cast<int>(m_trophies_db.size()));
// Update headers
for (int col = 0; col < m_game_table->horizontalHeader()->count(); col++)
{
if (auto item = m_game_table->horizontalHeaderItem(col))
{
item->setText(get_gamelist_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_game_column_acts)
{
action->setText(get_gamelist_action_text(col));
}
m_game_combo->clear(); m_game_combo->clear();
m_game_combo->blockSignals(true); m_game_combo->blockSignals(true);
@ -1126,6 +1203,21 @@ void trophy_manager_dialog::PopulateTrophyTable()
m_trophy_table->setRowCount(all_trophies); m_trophy_table->setRowCount(all_trophies);
m_trophy_table->setSortingEnabled(false); // Disable sorting before using setItem calls m_trophy_table->setSortingEnabled(false); // Disable sorting before using setItem calls
// Update headers
for (int col = 0; col < m_trophy_table->horizontalHeader()->count(); col++)
{
if (auto item = m_trophy_table->horizontalHeaderItem(col))
{
item->setText(get_trophy_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_trophy_column_acts)
{
action->setText(get_trophy_action_text(col));
}
QPixmap placeholder(m_icon_height, m_icon_height); QPixmap placeholder(m_icon_height, m_icon_height);
placeholder.fill(Qt::transparent); placeholder.fill(Qt::transparent);

View file

@ -83,6 +83,12 @@ private:
static QDateTime TickToDateTime(u64 tick); static QDateTime TickToDateTime(u64 tick);
static u64 DateTimeToTick(QDateTime date_time); static u64 DateTimeToTick(QDateTime date_time);
QString get_trophy_header_text(int col) const;
QString get_trophy_action_text(int col) const;
QString get_gamelist_header_text(int col) const;
QString get_gamelist_action_text(int col) const;
std::shared_ptr<gui_settings> m_gui_settings; std::shared_ptr<gui_settings> m_gui_settings;
std::vector<std::unique_ptr<GameTrophiesData>> m_trophies_db; //! Holds all the trophy information. std::vector<std::unique_ptr<GameTrophiesData>> m_trophies_db; //! Holds all the trophy information.
@ -93,8 +99,8 @@ private:
game_list* m_trophy_table; //! UI element to display trophy stuff. game_list* m_trophy_table; //! UI element to display trophy stuff.
game_list* m_game_table; //! UI element to display games. game_list* m_game_table; //! UI element to display games.
QList<QAction*> m_trophy_column_acts; std::map<int, QAction*> m_trophy_column_acts;
QList<QAction*> m_game_column_acts; std::map<int, QAction*> m_game_column_acts;
bool m_show_hidden_trophies = false; bool m_show_hidden_trophies = false;
bool m_show_unlocked_trophies = true; bool m_show_unlocked_trophies = true;

View file

@ -31,7 +31,7 @@ s32 trophy_notification_helper::ShowTrophyNotification(const SceNpTrophyDetails&
trophy_notification->move(m_game_window->mapToGlobal(QPoint(0, 0))); trophy_notification->move(m_game_window->mapToGlobal(QPoint(0, 0)));
trophy_notification->show(); trophy_notification->show();
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_trophy.wav"); Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_trophy.wav", std::nullopt);
}); });
return 0; return 0;