Merge branch 'master' into win-clang-arm

This commit is contained in:
qurious-pixel 2025-12-05 18:39:46 -08:00 committed by GitHub
commit 0fbdfd9239
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 1008 additions and 366 deletions

View file

@ -6,7 +6,8 @@ export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew install -f --overwrite --quiet pipenv ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
brew install -f --overwrite --quiet pipenv googletest opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative
brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
# moltenvk based on commit for 1.4.0 release
@ -38,17 +39,14 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
# nested Qt 6.10.0 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}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
# archived Qt 6.7.3 URL workaround
sed -i '' "s/official_releases/archive/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
cd "/tmp/Qt"
"$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
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
"$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"
sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader"
"$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
cd "$WORKDIR"

View file

@ -12,7 +12,8 @@ brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
rm /usr/local/bin/{idle3.14,pip3.14,pydoc3.14,python3.14,python3.14-config} && \
rm /usr/local/bin/{idle3,pip3,pydoc3,python3,python3-config}
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative
arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
# moltenvk based on commit for 1.4.0 release
@ -38,16 +39,13 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
# nested Qt 6.10.0 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}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
# archived Qt 6.7.3 URL workaround
sed -i '' "s/official_releases/archive/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
cd "/tmp/Qt"
"/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"
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.0 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"
sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader"
"/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
cd "$WORKDIR"

View file

@ -17,6 +17,7 @@ cd bin
mkdir "rpcs3.app/Contents/lib/" || true
cp "$(realpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
cp "$(realpath /opt/homebrew/opt/gcc/lib/gcc/current/libgcc_s.1.1.dylib)" "rpcs3.app/Contents/Frameworks/libgcc_s.1.1.dylib"
cp "$(realpath /opt/homebrew/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib"
cp "$(realpath /opt/homebrew/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib"

View file

@ -16,8 +16,9 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
cd bin
mkdir "rpcs3.app/Contents/lib/"
cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "rpcs3.app/Contents/Frameworks/libunwind.1.dylib"
cp "$(realpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
cp "$(realpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib)" "rpcs3.app/Contents/Frameworks/libunwind.1.dylib"
cp "$(realpath /usr/local/opt/gcc/lib/gcc/current/libgcc_s.1.1.dylib)" "rpcs3.app/Contents/Frameworks/libgcc_s.1.1.dylib"
cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib"
cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib"

View file

@ -134,7 +134,7 @@ jobs:
runs-on: macos-14
env:
CCACHE_DIR: /tmp/ccache_dir
QT_VER: '6.7.3'
QT_VER: '6.10.1'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '21'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
@ -212,9 +212,9 @@ jobs:
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.10.0'
QT_VER: '6.10.1'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202510021201'
QT_DATE: '202511161843'
LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'

2
.gitmodules vendored
View file

@ -21,7 +21,7 @@
ignore = dirty
[submodule "3rdparty/hidapi"]
path = 3rdparty/hidapi/hidapi
url = ../../RPCS3/hidapi.git
url = ../../libusb/hidapi.git
branch = master
ignore = dirty
[submodule "3rdparty/pugixml"]

2
3rdparty/FAudio vendored

@ -1 +1 @@
Subproject commit 8de3616b5b204260fe639e76587731d8a73b8d2c
Subproject commit 4ea8afea6ba857c24e40877f487d000d559b196d

@ -1 +1 @@
Subproject commit f42423643ec9011c98cccc0bb790722bbbd3f30b
Subproject commit d6b2a974608dec3b76fb1e36c189f22b9cf3650c

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

@ -1 +1 @@
Subproject commit badbf8da4ee72b3ef599c721ffc9899e8d7c8d90
Subproject commit 7f3ae3d57459e59943a4ecfefc8f6277ec6bf540

View file

@ -8,37 +8,38 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
### 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)
**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)
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
with standalone **CMake** tool.
- [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.
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:
- 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)
**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):
- 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
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+
- [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.
- [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)
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**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)

View file

@ -201,7 +201,7 @@ QPushButton::disabled {
/* QSpinBox (Settings -> Emulator -> width/height) */
/* QDoubleSpinBox (Pads -> Mouse Acceleration -> x/y) */
QSpinBox, QDoubleSpinBox {
height: 0.1em;
height: 1.50em;
background-color: #b3ac98;
}
QSpinBox::disabled, QDoubleSpinBox::disabled {

View file

@ -1051,7 +1051,15 @@ error_code cellPadSetPortSetting(u32 port_no, u32 port_setting)
if (port_no >= CELL_PAD_MAX_PORT_NUM)
return CELL_OK;
config.port_setting[port_no] = port_setting;
if (port_setting & CELL_PAD_SETTING_PRESS_ON)
config.port_setting[port_no] |= CELL_PAD_SETTING_PRESS_ON;
else
config.port_setting[port_no] &= ~CELL_PAD_SETTING_PRESS_ON;
if (port_setting & CELL_PAD_SETTING_SENSOR_ON)
config.port_setting[port_no] |= CELL_PAD_SETTING_SENSOR_ON;
else
config.port_setting[port_no] &= ~CELL_PAD_SETTING_SENSOR_ON;
// can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD <- Update: seems to be just internal and ignored
@ -1123,7 +1131,7 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode)
if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED;
if (port_no >= CELL_PAD_MAX_PORT_NUM)
if (port_no >= CELL_MAX_PADS || mode > 1)
return CELL_PAD_ERROR_INVALID_PARAMETER;
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
@ -1157,7 +1165,7 @@ error_code cellPadSetSensorMode(u32 port_no, u32 mode)
if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED;
if (port_no >= CELL_MAX_PADS)
if (port_no >= CELL_MAX_PADS || mode > 1)
return CELL_PAD_ERROR_INVALID_PARAMETER;
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.

View file

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

View file

@ -720,9 +720,19 @@ void spu_cache::initialize(bool build_existing_cache)
}
// 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)
{
@ -3165,6 +3175,15 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break;
}
if (target >= SPU_LS_SIZE && target <= 0u - SPU_LS_SIZE)
{
if (g_spu_itype.decode(target) != spu_itype::UNK)
{
// End of jumptable: valid instruction
break;
}
}
if (target >= lsa && target < SPU_LS_SIZE)
{
// Possible jump table entry (absolute)
@ -4954,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 get_pc = SPU_LS_SIZE; // PC of GETLLAR
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_offs = reg_state_t::from_value(0); // Added value to ls
reg_state_t lsa{}; // state of LSA register on GETLLAR
@ -4999,7 +5019,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
ls_invalid = true;
ls_write |= write;
if (write)
if (ls_write)
{
return discard();
}
@ -6314,6 +6334,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break;
}
atomic16->rdatomic_pc = pos;
const auto it = atomic16_all.find(pos);
if (it == atomic16_all.end())
@ -7254,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)
{
if (!pattern.active)
if (!pattern.active || pattern.lsa_pc >= pattern.rdatomic_pc)
{
continue;
}
@ -7264,6 +7286,17 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
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
{
u64 data;
@ -7286,8 +7319,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
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);
add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data);
// 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);
continue;
}
@ -7354,16 +7387,35 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
value.reg2 = pattern.reg2;
}
bool allow_pattern = true;
if (g_cfg.core.spu_accurate_reservations)
{
// Because enabling it is a hack, as it turns out
// continue;
// The problem with PUTLLC16 optimization, that it is in theory correct at the bounds of the spu function.
// 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));
}
add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data);
if (allow_pattern)
{
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)"
, 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);
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, pattern_hash, +stats.nowrite, ++stats.single, +stats.all);
}
for (const auto& [read_pc, pattern] : rchcnt_loop_all)
@ -8426,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
// 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 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)
{

View file

@ -12,28 +12,45 @@ namespace rsx
namespace aligned_allocator
{
template <size_t Align>
requires (Align != 0) && ((Align & (Align - 1)) == 0)
size_t align_up(size_t size)
{
return (size + (Align - 1)) & ~(Align - 1);
}
template <size_t Align>
requires (Align != 0) && ((Align & (Align - 1)) == 0)
void* malloc(size_t size)
{
#ifdef _WIN32
#if defined(_WIN32)
return _aligned_malloc(size, Align);
#elif defined(__APPLE__)
constexpr size_t NativeAlign = std::max(Align, sizeof(void*));
return std::aligned_alloc(NativeAlign, align_up<NativeAlign>(size));
#else
return std::aligned_alloc(Align, size);
return std::aligned_alloc(Align, align_up<Align>(size));
#endif
}
template <size_t Align>
requires (Align != 0) && ((Align & (Align - 1)) == 0)
void* realloc(void* prev_ptr, [[maybe_unused]] size_t prev_size, size_t new_size)
{
if (prev_size >= new_size)
if (align_up<Align>(prev_size) >= new_size)
{
return prev_ptr;
}
ensure(reinterpret_cast<usz>(prev_ptr) % Align == 0, "Pointer not aligned to Align");
#ifdef _WIN32
#if defined(_WIN32)
return _aligned_realloc(prev_ptr, new_size, Align);
#else
void* ret = std::aligned_alloc(Align, new_size);
#if defined(__APPLE__)
constexpr size_t NativeAlign = std::max(Align, sizeof(void*));
void* ret = std::aligned_alloc(NativeAlign, align_up<NativeAlign>(new_size));
#else
void* ret = std::aligned_alloc(Align, align_up<Align>(new_size));
#endif
std::memcpy(ret, prev_ptr, std::min(prev_size, new_size));
std::free(prev_ptr);
return ret;

View file

@ -1,7 +1,6 @@
#include "stdafx.h"
#include "../overlay_manager.h"
#include "overlay_friends_list_dialog.h"
#include "Emu/System.h"
#include "Emu/NP/rpcn_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;
}
case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
close_dialog = true;
break;
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
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 "overlay_home_menu_message_box.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
namespace rsx
@ -77,7 +76,7 @@ namespace rsx
{
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)
{
m_on_accept();
@ -86,7 +85,7 @@ namespace rsx
}
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)
{
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
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);
}
@ -169,7 +169,7 @@ namespace rsx
}
case pad_button::circle:
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
if (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
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;
}

View file

@ -127,11 +127,11 @@ namespace rsx
{
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;
break;
case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
close_dialog = true;
break;
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
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)
break;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav");
play_sound(sound_effect::accept);
if (!get_current_selection().empty())
{
@ -95,7 +95,7 @@ namespace rsx
close_dialog = true;
break;
case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
close_dialog = true;
break;
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
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)
{
case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
close_dialog = true;
break;
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
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 "Emu/Cell/Modules/cellMusic.h"
#include "Emu/System.h"
#include "Emu/VFS.h"
#include "Utilities/StrUtil.h"
#include "Utilities/Thread.h"
@ -154,13 +153,13 @@ namespace rsx
return_code = m_list->get_selected_index();
m_stop_input_loop = true;
play_cursor_sound = false;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav");
play_sound(sound_effect::accept);
break;
case pad_button::circle:
return_code = selection_code::canceled;
m_stop_input_loop = true;
play_cursor_sound = false;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
break;
case pad_button::dpad_up:
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
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;
}
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_decide.wav");
play_sound(sound_effect::accept);
break;
}
case pad_button::circle:
@ -175,7 +175,7 @@ namespace rsx
return_code = CELL_MSGDIALOG_BUTTON_NO;
}
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
break;
}
default: return;
@ -238,10 +238,7 @@ namespace rsx
if (!type.se_mute_on)
{
if (type.se_normal)
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");
play_sound(type.se_normal ? sound_effect::dialog_ok : sound_effect::dialog_error);
}
set_text(text);

View file

@ -823,7 +823,7 @@ namespace rsx
}
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);
play_cursor_sound = false;
break;
@ -840,7 +840,7 @@ namespace rsx
}
case pad_button::cross:
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_oskenter.wav");
play_sound(sound_effect::osk_accept);
on_accept();
m_reset_pulse = true;
play_cursor_sound = false;
@ -848,7 +848,7 @@ namespace rsx
}
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);
play_cursor_sound = false;
break;
@ -890,7 +890,7 @@ namespace rsx
// 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))
{
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cursor.wav");
play_sound(sound_effect::cursor);
}
if (m_reset_pulse)

View file

@ -2,7 +2,6 @@
#include "overlay_save_dialog.h"
#include "overlay_video.h"
#include "Utilities/date_time.h"
#include "Emu/System.h"
namespace rsx
{
@ -133,11 +132,11 @@ namespace rsx
if (m_no_saves)
break;
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;
break;
case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
close_dialog = true;
break;
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
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 "overlay_trophy_notification.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/System.h"
namespace rsx
{
@ -70,7 +69,7 @@ namespace rsx
{
// First tick
creation_time_us = timestamp_us;
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_trophy.wav");
play_sound(sound_effect::trophy);
return;
}

View file

@ -113,11 +113,11 @@ namespace rsx
{
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;
break;
case pad_button::circle:
Emu.GetCallbacks().play_sound(fs::get_config_dir() + "sounds/snd_cancel.wav");
play_sound(sound_effect::cancel);
close_dialog = true;
break;
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
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
{
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;
u32 user_interface::alloc_thread_bit()

View file

@ -17,6 +17,21 @@ namespace rsx
{
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
enum status_bits : u32
{

View file

@ -3073,7 +3073,7 @@ namespace rsx
{
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);

View file

@ -840,6 +840,19 @@ bool Emulator::BootRsxCapture(const std::string& path)
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();
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::u32string(localized_string_id, const char*)> get_localized_u32string;
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&, 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

View file

@ -36,7 +36,7 @@ struct cfg_root : cfg::node
cfg::_int<0, 16> spu_delay_penalty{ this, "SPU delay penalty", 3 }; // Number of milliseconds to block a thread if a virtual 'core' isn't free
cfg::_bool spu_loop_detection{ this, "SPU loop detection", false }; // Try to detect wait loops and trigger thread yield
cfg::_int<1, 6> max_spurs_threads{ this, "Max SPURS Threads", 6, true }; // HACK. If less then 6, max number of running SPURS threads in each thread group.
cfg::_enum<spu_block_size_type> spu_block_size{ this, "SPU Block Size", spu_block_size_type::safe };
cfg::_enum<spu_block_size_type> spu_block_size{ this, "SPU Analyzer Block Size", spu_block_size_type::mega };
cfg::_bool spu_accurate_dma{ this, "Accurate SPU DMA", false };
cfg::_bool spu_accurate_reservations{ this, "Accurate SPU Reservations", true };
cfg::_bool accurate_cache_line_stores{ this, "Accurate Cache Line Stores", false };
@ -53,7 +53,7 @@ struct cfg_root : cfg::node
}
};
fifo_setting rsx_fifo_accuracy{this, "RSX FIFO Accuracy", rsx_fifo_mode::fast };
fifo_setting rsx_fifo_accuracy{this, "RSX FIFO Fetch Accuracy", rsx_fifo_mode::atomic };
cfg::_bool spu_verification{ this, "SPU Verification", true }; // Should be enabled
cfg::_bool spu_cache{ this, "SPU Cache", true };
cfg::_bool spu_prof{ this, "SPU Profiler", false };

View file

@ -266,14 +266,6 @@ void ds3_pad_handler::check_add_device(hid_device* hidDevice, hid_enumerated_dev
}
device->report_id = buf[0];
#elif defined (__APPLE__)
int res = hid_init_sixaxis_usb(hidDevice);
if (res < 0)
{
ds3_log.error("check_add_device: hid_init_sixaxis_usb failed! (result=%d, error=%s)", res, hid_error(hidDevice));
HidDevice::close(hidDevice);
return;
}
#endif
for (wchar_t ch : wide_serial)

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_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.display_sleep_control_supported = [](){ return false; };

View file

@ -435,6 +435,12 @@ QCoreApplication* create_application(std::span<char* const> qt_argv)
{
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0");
}
#elif __APPLE__
// set the QT_MTL_NO_TRANSACTION variable in order to prevent Qt GUI freeze
qputenv("QT_MTL_NO_TRANSACTION", "1");
// set the QT_MAC_NO_CONTAINER_LAYER variable in order to prevent swapchain crash
qputenv("QT_MAC_NO_CONTAINER_LAYER", "1");
#endif
bool use_high_dpi = true;

View file

@ -430,6 +430,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</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">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -718,6 +721,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</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">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -851,6 +857,7 @@
<ClCompile Include="rpcs3qt\screenshot_item.cpp" />
<ClCompile Include="rpcs3qt\screenshot_manager_dialog.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\settings.cpp" />
<ClCompile Include="rpcs3qt\shortcut_dialog.cpp" />
@ -1311,6 +1318,16 @@
<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>
</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">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>

View file

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

View file

@ -99,6 +99,7 @@ add_library(rpcs3_ui STATIC
shortcut_handler.cpp
shortcut_settings.cpp
skylander_dialog.cpp
sound_effect_manager_dialog.cpp
syntax_highlighter.cpp
system_cmd_dialog.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->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, spinbox->cleanText().toStdString());
SetSetting(type, fmt::format("%d", value));
});
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->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, spinbox->cleanText().toStdString());
SetSetting(type, fmt::format("%f", value));
});
connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]()
@ -975,9 +973,9 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
case emu_settings_type::SPUBlockSize:
switch (static_cast<spu_block_size_type>(index))
{
case spu_block_size_type::safe: return tr("Safe", "SPU block size");
case spu_block_size_type::mega: return tr("Mega", "SPU block size");
case spu_block_size_type::giga: return tr("Giga", "SPU block size");
case spu_block_size_type::safe: return tr("Safe", "SPU Analyzer Block Size");
case spu_block_size_type::mega: return tr("Mega", "SPU Analyzer Block Size");
case spu_block_size_type::giga: return tr("Giga", "SPU Analyzer Block Size");
}
break;
case emu_settings_type::ThreadSchedulerMode:
@ -1200,10 +1198,10 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
case emu_settings_type::FIFOAccuracy:
switch (static_cast<rsx_fifo_mode>(index))
{
case rsx_fifo_mode::fast: return tr("Fast", "RSX FIFO Accuracy");
case rsx_fifo_mode::atomic: return tr("Atomic", "RSX FIFO Accuracy");
case rsx_fifo_mode::atomic_ordered: return tr("Ordered & Atomic", "RSX FIFO Accuracy");
case rsx_fifo_mode::as_ps3: return tr("PS3", "RSX FIFO Accuracy");
case rsx_fifo_mode::fast: return tr("Fast", "RSX FIFO Fetch Accuracy");
case rsx_fifo_mode::atomic: return tr("Atomic", "RSX FIFO Fetch Accuracy");
case rsx_fifo_mode::atomic_ordered: return tr("Ordered & Atomic", "RSX FIFO Fetch Accuracy");
case rsx_fifo_mode::as_ps3: return tr("PS3", "RSX FIFO Fetch Accuracy");
}
break;
case emu_settings_type::PerfOverlayDetailLevel:

View file

@ -235,11 +235,11 @@ inline static const std::map<emu_settings_type, cfg_location> settings_location
{ emu_settings_type::AccurateSpuDMA, { "Core", "Accurate SPU DMA"}},
{ emu_settings_type::AccurateClineStores, { "Core", "Accurate Cache Line Stores"}},
{ emu_settings_type::AccurateRSXAccess, { "Core", "Accurate RSX reservation access"}},
{ emu_settings_type::FIFOAccuracy, { "Core", "RSX FIFO Accuracy"}},
{ emu_settings_type::FIFOAccuracy, { "Core", "RSX FIFO Fetch Accuracy"}},
{ emu_settings_type::XFloatAccuracy, { "Core", "XFloat Accuracy"}},
{ emu_settings_type::MFCCommandsShuffling, { "Core", "MFC Commands Shuffling Limit"}},
{ emu_settings_type::SetDAZandFTZ, { "Core", "Set DAZ and FTZ"}},
{ emu_settings_type::SPUBlockSize, { "Core", "SPU Block Size"}},
{ emu_settings_type::SPUBlockSize, { "Core", "SPU Analyzer Block Size"}},
{ emu_settings_type::SPUCache, { "Core", "SPU Cache"}},
{ emu_settings_type::DebugConsoleMode, { "Core", "Debug Console Mode"}},
{ emu_settings_type::MaxSPURSThreads, { "Core", "Max SPURS Threads"}},

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);
bool is_dirty = false;
for (int col = 0; col < actions.count(); ++col)
for (auto& [col, action] : actions)
{
const bool is_hidden = !get_visibility(col);
actions[col]->setChecked(!is_hidden);
action->setChecked(!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(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)
{
QMenu* configure = new QMenu(this);
configure->addActions(actions);
for (auto& [col, action] : actions)
{
configure->addAction(action);
}
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
{
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;
}
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;
}
}
@ -114,7 +117,8 @@ void game_list::fix_narrow_columns()
void game_list::mousePressEvent(QMouseEvent* event)
{
if (QTableWidgetItem* item = itemAt(event->pos()); !item || !item->data(Qt::UserRole).isValid())
// Handle deselction when clicking on empty space in the table
if (!itemAt(event->pos()))
{
clearSelection();
setCurrentItem(nullptr); // Needed for currentItemChanged

View file

@ -23,8 +23,8 @@ class game_list : public QTableWidget, public game_list_base
public:
game_list();
void sync_header_actions(QList<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 sync_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_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

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);
// 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));
m_columnActs.append(new QAction(action_text, this));
const int column = static_cast<int>(col);
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::name, tr("Name"), tr("Show Names"));
add_column(gui::game_list_columns::serial, tr("Serial"), tr("Show Serials"));
add_column(gui::game_list_columns::firmware, tr("Firmware"), tr("Show Firmwares"));
add_column(gui::game_list_columns::version, tr("Version"), tr("Show Versions"));
add_column(gui::game_list_columns::category, tr("Category"), tr("Show Categories"));
add_column(gui::game_list_columns::path, tr("Path"), tr("Show Paths"));
add_column(gui::game_list_columns::move, tr("PlayStation Move"), tr("Show PlayStation Move"));
add_column(gui::game_list_columns::resolution, tr("Supported Resolutions"), tr("Show Supported Resolutions"));
add_column(gui::game_list_columns::sound, tr("Sound Formats"), tr("Show Sound Formats"));
add_column(gui::game_list_columns::parental, tr("Parental Level"), tr("Show Parental Levels"));
add_column(gui::game_list_columns::last_play, tr("Last Played"), tr("Show Last Played"));
add_column(gui::game_list_columns::playtime, tr("Time Played"), tr("Show Time Played"));
add_column(gui::game_list_columns::compat, tr("Compatibility"), tr("Show Compatibility"));
add_column(gui::game_list_columns::dir_size, tr("Space On Disk"), tr("Show Space On Disk"));
add_column(gui::game_list_columns::icon);
add_column(gui::game_list_columns::name);
add_column(gui::game_list_columns::serial);
add_column(gui::game_list_columns::firmware);
add_column(gui::game_list_columns::version);
add_column(gui::game_list_columns::category);
add_column(gui::game_list_columns::path);
add_column(gui::game_list_columns::move);
add_column(gui::game_list_columns::resolution);
add_column(gui::game_list_columns::sound);
add_column(gui::game_list_columns::parental);
add_column(gui::game_list_columns::last_play);
add_column(gui::game_list_columns::playtime);
add_column(gui::game_list_columns::compat);
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->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_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, 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_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()
@ -238,6 +239,54 @@ game_list_frame::~game_list_frame()
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)
{
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);
}
// 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();
// 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))
{
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
@ -959,9 +1023,9 @@ void game_list_frame::ToggleCategoryFilter(const QStringList& categories, bool s
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_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;
game_info gameinfo;
@ -2704,7 +2768,7 @@ void game_list_frame::ShowCustomConfigIcon(const game_info& game)
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 = gui_settings::SizeFromSlider(slider_pos);
@ -2712,7 +2776,7 @@ void game_list_frame::ResizeIcons(const int& slider_pos)
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_refresh_watcher, false);
@ -2746,7 +2810,7 @@ void game_list_frame::SetShowHidden(bool 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_is_list_layout = is_list;

View file

@ -48,10 +48,10 @@ public:
void SaveSettings();
/** 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 */
void RepaintIcons(const bool& from_settings = false);
void RepaintIcons(bool from_settings = false);
void SetShowHidden(bool show);
@ -70,7 +70,7 @@ public Q_SLOTS:
void BatchRemoveCustomConfigurations();
void BatchRemoveCustomPadConfigurations();
void BatchRemoveShaderCaches();
void SetListMode(const bool& is_list);
void SetListMode(bool is_list);
void SetSearchText(const QString& text);
void SetShowCompatibilityInGrid(bool show);
void SetPreferGameDataIcons(bool enabled);
@ -83,7 +83,7 @@ private Q_SLOTS:
void OnRefreshFinished();
void OnCompatFinished();
void OnColClicked(int col);
void ShowContextMenu(const QPoint &pos);
void ShowContextMenu(const QPoint& pos);
void doubleClickedSlot(QTableWidgetItem* item);
void doubleClickedSlot(const game_info& game);
void ItemSelectionChangedSlot();
@ -91,7 +91,7 @@ Q_SIGNALS:
void GameListFrameClosed();
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 RequestIconSizeChange(const int& val);
void RequestIconSizeChange(int val);
void NotifyEmuSettingsChange();
void FocusToSearchBar();
void Refreshed();
@ -127,6 +127,9 @@ protected:
private:
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);
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_compatibility* m_game_compat = nullptr;
progress_dialog* m_progress_dialog = nullptr;
QList<QAction*> m_columnActs;
std::map<int, QAction*> m_column_acts;
Qt::SortOrder m_col_sort_order{};
int m_sort_column{};
bool m_initial_refresh_done = false;

View file

@ -447,7 +447,7 @@ void gs_frame::toggle_recording()
// Play a sound
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
{
@ -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))
{
Emu.GetCallbacks().play_sound(sound_path);
Emu.GetCallbacks().play_sound(sound_path, std::nullopt);
}
else
{

View file

@ -743,9 +743,9 @@ void gui_application::InitializeCallbacks()
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))
{
@ -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.
std::unique_ptr<QSoundEffect> sound_effect = std::make_unique<QSoundEffect>();
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();
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.

View file

@ -44,6 +44,7 @@
#include "vfs_tool_dialog.h"
#include "welcome_dialog.h"
#include "music_player_dialog.h"
#include "sound_effect_manager_dialog.h"
#include <thread>
#include <unordered_set>
@ -229,42 +230,32 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
// RPCS3 Updater
QMenu* download_menu = new QMenu(tr("Update Available!"));
QAction* download_action = new QAction(tr("Download Update"), download_menu);
connect(download_action, &QAction::triggered, this, [this]
connect(ui->actionDownload_Update, &QAction::triggered, this, [this]
{
m_updater.update(false);
});
download_menu->addAction(download_action);
#ifdef _WIN32
// Use a menu at the top right corner to indicate the new version.
QMenuBar *corner_bar = new QMenuBar(ui->menuBar);
m_download_menu_action = corner_bar->addMenu(download_menu);
// Some distros just can't handle corner widgets at the moment.
QMenuBar* corner_bar = new QMenuBar(ui->menuBar);
corner_bar->addMenu(ui->menuUpdate_Available);
ui->menuBar->setCornerWidget(corner_bar);
ui->menuBar->cornerWidget()->setVisible(false);
#else
// Append a menu to the right of the regular menus to indicate the new version.
// Some distros just can't handle corner widgets at the moment.
m_download_menu_action = ui->menuBar->addMenu(download_menu);
#endif
ensure(m_download_menu_action);
m_download_menu_action->setVisible(false);
ui->menuBar->removeAction(ui->menuUpdate_Available->menuAction());
connect(&m_updater, &update_manager::signal_update_available, this, [this](bool update_available)
{
if (m_download_menu_action)
{
m_download_menu_action->setVisible(update_available);
}
if (ui->menuBar && ui->menuBar->cornerWidget())
{
ui->menuBar->cornerWidget()->setVisible(update_available);
}
ui->menuBar->cornerWidget()->setVisible(update_available);
});
#else
ui->menuUpdate_Available->menuAction()->setVisible(false);
connect(&m_updater, &update_manager::signal_update_available, this, [this](bool update_available)
{
ui->menuUpdate_Available->menuAction()->setVisible(update_available);
});
#endif
#ifdef RPCS3_UPDATE_SUPPORTED
if (const auto update_value = m_gui_settings->GetValue(gui::m_check_upd_start).toString(); update_value != gui::update_off)
@ -1932,9 +1923,11 @@ void main_window::OnEmuRun(bool /*start_playtime*/)
EnableMenus(true);
update_gui_pad_thread();
m_system_state = system_state::running;
}
void main_window::OnEmuResume() const
void main_window::OnEmuResume()
{
const QString title = GetCurrentTitle();
const QString restart_tooltip = tr("Restart %0").arg(title);
@ -1947,9 +1940,11 @@ void main_window::OnEmuResume() const
ui->toolbar_start->setText(tr("Pause"));
ui->toolbar_start->setToolTip(pause_tooltip);
ui->toolbar_stop->setToolTip(stop_tooltip);
m_system_state = system_state::starting; // Let's just use this state to distinguish between resumed and running
}
void main_window::OnEmuPause() const
void main_window::OnEmuPause()
{
const QString title = GetCurrentTitle();
const QString resume_tooltip = tr("Resume %0").arg(title);
@ -1965,6 +1960,8 @@ void main_window::OnEmuPause() const
{
m_game_list_frame->Refresh();
}
m_system_state = system_state::paused;
}
void main_window::OnEmuStop()
@ -2025,9 +2022,11 @@ void main_window::OnEmuStop()
}
update_gui_pad_thread();
m_system_state = system_state::stopped;
}
void main_window::OnEmuReady() const
void main_window::OnEmuReady()
{
const QString title = GetCurrentTitle();
const QString play_tooltip = tr("Play %0").arg(title);
@ -2053,6 +2052,8 @@ void main_window::OnEmuReady() const
ui->removeAllCachesAct->setEnabled(false);
ui->removeSavestatesAct->setEnabled(false);
ui->cleanUpGameListAct->setEnabled(false);
m_system_state = system_state::ready;
}
void main_window::EnableMenus(bool enabled) const
@ -2339,10 +2340,27 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString
ui->retranslateUi(this);
// Refresh game list first to prevent localization mismatches in further Refresh calls
if (m_game_list_frame)
{
m_game_list_frame->Refresh(true);
}
// Update menu bar size (needed if the corner widget changes its size)
ui->menuBar->adjustSize();
// Update toolbar elements
switch (m_system_state)
{
case system_state::running: OnEmuRun(false); break;
case system_state::stopped: OnEmuStop(); break;
case system_state::paused: OnEmuPause(); break;
case system_state::starting: OnEmuResume(); break;
case system_state::ready: OnEmuReady(); break;
default: break;
}
Q_EMIT RequestDialogRepaint();
}
void main_window::ShowTitleBars(bool show) const
@ -3053,6 +3071,12 @@ void main_window::CreateConnects()
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]
{
cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings);
@ -3336,7 +3360,7 @@ void main_window::CreateConnects()
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(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;
m_save_slider_pos = true;

View file

@ -11,6 +11,7 @@
#include "settings.h"
#include "shortcut_handler.h"
#include "Emu/config_mode.h"
#include "Emu/System.h"
#include <memory>
@ -88,9 +89,9 @@ Q_SIGNALS:
public Q_SLOTS:
void OnEmuStop();
void OnEmuRun(bool start_playtime);
void OnEmuResume() const;
void OnEmuPause() const;
void OnEmuReady() const;
void OnEmuResume();
void OnEmuPause();
void OnEmuReady();
void OnEnableDiscEject(bool enabled) const;
void OnEnableDiscInsert(bool enabled) const;
void OnAddBreakpoint(u32 addr) const;
@ -196,9 +197,10 @@ private:
std::shared_ptr<persistent_settings> m_persistent_settings;
update_manager m_updater;
QAction* m_download_menu_action = nullptr;
shortcut_handler* m_shortcut_handler = nullptr;
std::unique_ptr<gui_pad_thread> m_gui_pad_thread;
system_state m_system_state = system_state::stopped;
};

View file

@ -306,6 +306,7 @@
<addaction name="actionManage_Cheats"/>
<addaction name="actionManage_Game_Patches"/>
<addaction name="actionManage_Screenshots"/>
<addaction name="actionManage_SoundEffects"/>
</widget>
<widget class="QMenu" name="menuUtilities">
<property name="title">
@ -411,6 +412,12 @@
<addaction name="aboutAct"/>
<addaction name="aboutQtAct"/>
</widget>
<widget class="QMenu" name="menuUpdate_Available">
<property name="title">
<string>Update Available!</string>
</property>
<addaction name="actionDownload_Update"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEmulation"/>
<addaction name="menuConfiguration"/>
@ -418,6 +425,7 @@
<addaction name="menuUtilities"/>
<addaction name="menuView"/>
<addaction name="menuHelp"/>
<addaction name="menuUpdate_Available"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="sizePolicy">
@ -1442,6 +1450,16 @@
<string>Music Player</string>
</property>
</action>
<action name="actionManage_SoundEffects">
<property name="text">
<string>Sound Effects</string>
</property>
</action>
<action name="actionDownload_Update">
<property name="text">
<string>Download Update</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View file

@ -432,9 +432,9 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDis
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();
});

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);
});
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);
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());
}
});
connect(ui->configurable_spin_box, QOverload<int>::of(&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_spin_box, &QSpinBox::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::clicked, [this](QAbstractButton* button)
{

View file

@ -71,14 +71,10 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame)
// Flip image if necessary
if (flip_horizontally || flip_vertically)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
Qt::Orientations orientation {};
orientation.setFlag(Qt::Orientation::Horizontal, flip_horizontally);
orientation.setFlag(Qt::Orientation::Vertical, flip_vertically);
image.flip(orientation);
#else
image.mirror(flip_horizontally, flip_vertically);
#endif
}
if (image.format() != QImage::Format_RGBA8888)

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);
mouse_acceleration_spin_box->setRange(0.1, 10.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;
config.set(std::clamp(value * 100.0, config.min, config.max));

View file

@ -32,7 +32,7 @@
LOG_CHANNEL(gui_log, "GUI");
enum SaveColumns
enum class SaveColumns
{
Icon = 0,
Name = 1,
@ -60,30 +60,26 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr<gui_settings> gui_setti
setMinimumSize(QSize(400, 400));
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
m_list = new game_list();
m_list->setItemDelegate(new game_list_delegate(m_list));
m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
m_list->setSelectionBehavior(QAbstractItemView::SelectRows);
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->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
m_list->verticalScrollBar()->setSingleStep(20);
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()->setStretchLastSection(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
const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt();
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]()
{
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)
{
return;
@ -176,12 +172,12 @@ void save_manager_dialog::Init()
connect(m_list, &QTableWidget::customContextMenuRequested, this, &save_manager_dialog::ShowContextMenu);
connect(m_list, &QTableWidget::cellChanged, [&](int row, int col)
{
if (col != SaveColumns::Note)
if (col != static_cast<int>(SaveColumns::Note))
{
return;
}
QTableWidgetItem* user_item = m_list->item(row, SaveColumns::Name);
QTableWidgetItem* text_item = m_list->item(row, SaveColumns::Note);
QTableWidgetItem* user_item = m_list->item(row, static_cast<int>(SaveColumns::Name));
QTableWidgetItem* text_item = m_list->item(row, static_cast<int>(SaveColumns::Note));
if (!user_item || !text_item)
{
return;
@ -196,7 +192,7 @@ void save_manager_dialog::Init()
connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails);
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->image_change_callback();
@ -205,6 +201,20 @@ void save_manager_dialog::Init()
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.
*/
@ -300,6 +310,15 @@ void save_manager_dialog::UpdateList()
m_list->clearContents();
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();
if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool())
@ -358,20 +377,20 @@ void save_manager_dialog::UpdateList()
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);
titleItem->setData(Qt::UserRole, i); // For sorting to work properly
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));
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);
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();
noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable);
@ -379,7 +398,7 @@ void save_manager_dialog::UpdateList()
{
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
@ -422,7 +441,7 @@ void save_manager_dialog::UpdateIcons()
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(Qt::DecorationRole, placeholder);
@ -430,14 +449,14 @@ void save_manager_dialog::UpdateIcons()
}
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 std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index);
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)
{
@ -448,12 +467,12 @@ void save_manager_dialog::UpdateIcons()
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())
{
// 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 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.
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 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(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)
{
return;
@ -655,8 +674,8 @@ void save_manager_dialog::UpdateDetails()
WaitForRepaintThreads(false);
const int row = m_list->currentRow();
QTableWidgetItem* item = m_list->item(row, SaveColumns::Name);
movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, SaveColumns::Icon));
QTableWidgetItem* item = m_list->item(row, static_cast<int>(SaveColumns::Name));
movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, static_cast<int>(SaveColumns::Icon)));
if (!item || !icon_item)
{
@ -692,7 +711,7 @@ void save_manager_dialog::WaitForRepaintThreads(bool abort)
{
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);
}
@ -706,7 +725,7 @@ void save_manager_dialog::text_changed(const QString& text)
if (text.isEmpty())
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);

View file

@ -39,7 +39,6 @@ Q_SIGNALS:
void IconReady(int index, const QPixmap& new_icon);
private:
void Init();
void UpdateList();
void UpdateIcons();
void ShowContextMenu(const QPoint& pos);
@ -49,6 +48,8 @@ private:
std::vector<SaveDataEntry> GetSaveEntries(const std::string& base_dir);
QString get_header_text(int col) const;
game_list* m_list = nullptr;
std::string m_dir;
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->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));
m_game_column_acts.append(new QAction(action_text, this));
const int column = static_cast<int>(col);
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::name, tr("Game"), tr("Show Games"));
add_game_column(gui::savestate_game_list_columns::savestates, tr("Savestates"), tr("Show Savestates"));
add_game_column(gui::savestate_game_list_columns::icon);
add_game_column(gui::savestate_game_list_columns::name);
add_game_column(gui::savestate_game_list_columns::savestates);
// Savestate Table
m_savestate_table = new game_list();
@ -84,6 +85,7 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
m_savestate_table->horizontalScrollBar()->setSingleStep(20);
m_savestate_table->setItemDelegate(new table_item_delegate(m_savestate_table, false));
m_savestate_table->setSelectionBehavior(QAbstractItemView::SelectRows);
m_savestate_table->setSelectionMode(QAbstractItemView::SingleSelection);
m_savestate_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_savestate_table->setColumnCount(static_cast<int>(gui::savestate_list_columns::count));
m_savestate_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
@ -94,16 +96,17 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
m_savestate_table->setAlternatingRowColors(true);
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));
m_savestate_column_acts.append(new QAction(action_text, this));
const int column = static_cast<int>(col);
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::compatible, tr("Compatible"), tr("Show Compatible"));
add_savestate_column(gui::savestate_list_columns::date, tr("Created"), tr("Show Created"));
add_savestate_column(gui::savestate_list_columns::path, tr("Path"), tr("Show Paths"));
add_savestate_column(gui::savestate_list_columns::name);
add_savestate_column(gui::savestate_list_columns::compatible);
add_savestate_column(gui::savestate_list_columns::date);
add_savestate_column(gui::savestate_list_columns::path);
m_splitter = new QSplitter();
m_splitter->addWidget(m_game_table);
@ -220,6 +223,56 @@ savestate_manager_dialog::~savestate_manager_dialog()
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)
{
ensure(!!game_savestates);
@ -625,6 +678,21 @@ void savestate_manager_dialog::PopulateGameTable()
m_game_table->clearContents();
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->blockSignals(true);
@ -681,6 +749,21 @@ void savestate_manager_dialog::PopulateSavestateTable()
m_savestate_table->setRowCount(static_cast<int>(savestates.size()));
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++)
{
const savestate_data& savestate = savestates[i];

View file

@ -64,6 +64,12 @@ private:
void closeEvent(QCloseEvent *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::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_game_table; //! UI element to display games.
QList<QAction*> m_savestate_column_acts;
QList<QAction*> m_game_column_acts;
std::map<int, QAction*> m_savestate_column_acts;
std::map<int, QAction*> m_game_column_acts;
int m_game_icon_size_index = 25;
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

@ -91,7 +91,7 @@ public:
const QString xfloat = tr("Control accuracy to SPU float vectors processing.\nFixes bugs in various games at the cost of performance.\nThis setting is only applied when SPU Decoder is set to Dynamic or LLVM.");
const QString enable_thread_scheduler = tr("Control how RPCS3 utilizes the threads of your system.\nEach option heavily depends on the game and on your CPU. It's recommended to try each option to find out which performs the best.\nChanging the thread scheduler is not supported on CPUs with less than 12 threads.");
const QString spu_loop_detection = tr("Try to detect loop conditions in SPU kernels and use them as scheduling hints.\nImproves performance and reduces CPU usage.\nMay cause severe audio stuttering in rare cases.");
const QString spu_block_size = tr("This option controls the SPU analyser, particularly the size of compiled units. The Mega and Giga modes may improve performance by tying smaller units together, decreasing the number of compiled units but increasing their size.\nUse the Safe mode for maximum compatibility.");
const QString spu_block_size = tr("This option controls the SPU analyser, particularly the size of compiled units. The Mega and Giga modes may improve performance by tying smaller units together, decreasing the number of compiled units but increasing their size.\nUse the Safe mode for maximum compatibility at the cost of lower performance.");
const QString preferred_spu_threads = tr("Some SPU stages are sensitive to race conditions and allowing a limited number at a time helps alleviate performance stalls.\nSetting this to a smaller value might improve performance and reduce stuttering in some games.\nLeave this on auto if performance is negatively affected when setting a small value.");
const QString max_cpu_preempt = tr("Reduces CPU usage and power consumption, improving battery life on mobile devices. (0 means disabled)\nHigher values cause a more pronounced effect, but may cause audio or performance issues. A value of 50 or less is recommended.\nThis option forces an FPS limit because it's active when framerate is stable.\nThe lighter the game is on the hardware, the more power is saved by it. (until the preemption count barrier is reached)");

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->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));
m_game_column_acts.append(new QAction(action_text, this));
const int column = static_cast<int>(col);
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::name, tr("Game"), tr("Show Games"));
add_game_column(gui::trophy_game_list_columns::progress, tr("Progress"), tr("Show Progress"));
add_game_column(gui::trophy_game_list_columns::trophies, tr("Trophies"), tr("Show Trophies"));
add_game_column(gui::trophy_game_list_columns::icon);
add_game_column(gui::trophy_game_list_columns::name);
add_game_column(gui::trophy_game_list_columns::progress);
add_game_column(gui::trophy_game_list_columns::trophies);
// Trophy Table
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->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));
m_trophy_column_acts.append(new QAction(action_text, this));
const int column = static_cast<int>(col);
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::name, tr("Name"), tr("Show Names"));
add_trophy_column(gui::trophy_list_columns::description, tr("Description"), tr("Show Descriptions"));
add_trophy_column(gui::trophy_list_columns::type, tr("Type"), tr("Show Types"));
add_trophy_column(gui::trophy_list_columns::is_unlocked, tr("Status"), tr("Show Status"));
add_trophy_column(gui::trophy_list_columns::id, tr("ID"), tr("Show IDs"));
add_trophy_column(gui::trophy_list_columns::platinum_link, tr("Platinum Relevant"), tr("Show Platinum Relevant"));
add_trophy_column(gui::trophy_list_columns::time_unlocked, tr("Time Unlocked"), tr("Show Time Unlocked"));
add_trophy_column(gui::trophy_list_columns::icon);
add_trophy_column(gui::trophy_list_columns::name);
add_trophy_column(gui::trophy_list_columns::description);
add_trophy_column(gui::trophy_list_columns::type);
add_trophy_column(gui::trophy_list_columns::is_unlocked);
add_trophy_column(gui::trophy_list_columns::id);
add_trophy_column(gui::trophy_list_columns::platinum_link);
add_trophy_column(gui::trophy_list_columns::time_unlocked);
m_splitter = new QSplitter();
m_splitter->addWidget(m_game_table);
@ -406,6 +408,66 @@ trophy_manager_dialog::~trophy_manager_dialog()
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)
{
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->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->blockSignals(true);
@ -1126,6 +1203,21 @@ void trophy_manager_dialog::PopulateTrophyTable()
m_trophy_table->setRowCount(all_trophies);
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);
placeholder.fill(Qt::transparent);

View file

@ -83,6 +83,12 @@ private:
static QDateTime TickToDateTime(u64 tick);
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::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_game_table; //! UI element to display games.
QList<QAction*> m_trophy_column_acts;
QList<QAction*> m_game_column_acts;
std::map<int, QAction*> m_trophy_column_acts;
std::map<int, QAction*> m_game_column_acts;
bool m_show_hidden_trophies = false;
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->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;

View file

@ -56,8 +56,7 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut
{
update_log.notice("Checking for updates: automatic=%d, check_only=%d, auto_accept=%d", automatic, check_only, auto_accept);
m_update_message.clear();
m_changelog.clear();
m_update_info = {};
if (automatic)
{
@ -103,7 +102,7 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut
}
}
Q_EMIT signal_update_available(result_json && !m_update_message.isEmpty());
Q_EMIT signal_update_available(result_json && m_update_info.update_found);
});
const utils::OS_version os = utils::get_OS_version();
@ -121,7 +120,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
const QJsonObject json_data = QJsonDocument::fromJson(data).object();
const int return_code = json_data["return_code"].toInt(-255);
bool hash_found = true;
m_update_info.hash_found = true;
if (return_code < 0)
{
@ -143,7 +142,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
// If a user clicks "Check for Updates" with a custom build ask him if he's sure he wants to update to latest version
if (!automatic && return_code == -1)
{
hash_found = false;
m_update_info.hash_found = false;
}
else
{
@ -187,7 +186,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
check_json(latest["version"].isString(), "Node 'latest_build: version' not found or not a string") &&
check_json(latest["datetime"].isString(), "Node 'latest_build: datetime' not found or not a string")
) ||
(hash_found && !(
(m_update_info.hash_found && !(
check_json(current.isObject(), "JSON doesn't contain current_build section") &&
check_json(current["version"].isString(), "Node 'current_build: datetime' not found or not a string") &&
check_json(current["datetime"].isString(), "Node 'current_build: version' not found or not a string")
@ -196,7 +195,7 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
return false;
}
if (hash_found && return_code == 0)
if (m_update_info.hash_found && return_code == 0)
{
update_log.success("RPCS3 is up to date!");
m_downloader->close_progress_dialog();
@ -210,59 +209,26 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
// Calculate how old the build is
const QString date_fmt = QStringLiteral("yyyy-MM-dd hh:mm:ss");
const QDateTime cur_date = hash_found ? QDateTime::fromString(current["datetime"].toString(), date_fmt) : QDateTime::currentDateTimeUtc();
const QDateTime cur_date = m_update_info.hash_found ? QDateTime::fromString(current["datetime"].toString(), date_fmt) : QDateTime::currentDateTimeUtc();
const QDateTime lts_date = QDateTime::fromString(latest["datetime"].toString(), date_fmt);
const QString cur_str = cur_date.toString(date_fmt);
const QString lts_str = lts_date.toString(date_fmt);
m_update_info.update_found = true;
m_update_info.cur_date = cur_date.toString(date_fmt);
m_update_info.lts_date = lts_date.toString(date_fmt);
m_update_info.diff_msec = cur_date.msecsTo(lts_date);
m_update_info.new_version = latest["version"].toString();
const qint64 diff_msec = cur_date.msecsTo(lts_date);
update_log.notice("Current: %s, latest: %s, difference: %lld ms", cur_str, lts_str, diff_msec);
const Localized localized;
const QString new_version = latest["version"].toString();
m_new_version = new_version.toStdString();
const QString support_message = tr("<br>You can empower our project at <a href=\"https://rpcs3.net/patreon\">RPCS3 Patreon</a>.<br>");
if (hash_found)
if (m_update_info.hash_found)
{
const QString old_version = current["version"].toString();
m_old_version = old_version.toStdString();
if (diff_msec < 0)
{
// This usually means that the current version was marked as broken and won't be shipped anymore, so we need to downgrade to avoid certain bugs.
m_update_message = tr("A better version of RPCS3 is available!<br><br>Current version: %0 (%1)<br>Better version: %2 (%3)<br>%4<br>Do you want to update?")
.arg(old_version)
.arg(cur_str)
.arg(new_version)
.arg(lts_str)
.arg(support_message);
}
else
{
m_update_message = tr("A new version of RPCS3 is available!<br><br>Current version: %0 (%1)<br>Latest version: %2 (%3)<br>Your version is %4 behind.<br>%5<br>Do you want to update?")
.arg(old_version)
.arg(cur_str)
.arg(new_version)
.arg(lts_str)
.arg(localized.GetVerboseTimeByMs(diff_msec, true))
.arg(support_message);
}
m_update_info.old_version = current["version"].toString();
}
else
{
m_old_version = fmt::format("%s-%s-%s", rpcs3::get_full_branch(), rpcs3::get_branch(), rpcs3::get_version().to_string());
m_update_message = tr("You're currently using a custom or PR build.<br><br>Latest version: %0 (%1)<br>The latest version is %2 old.<br>%3<br>Do you want to update to the latest official RPCS3 version?")
.arg(new_version)
.arg(lts_str)
.arg(localized.GetVerboseTimeByMs(std::abs(diff_msec), true))
.arg(support_message);
m_update_info.old_version = QString::fromStdString(fmt::format("%s-%s-%s", rpcs3::get_full_branch(), rpcs3::get_branch(), rpcs3::get_version().to_string()));
}
update_log.notice("Current: %s, latest: %s, difference: %lld ms", m_update_info.cur_date, m_update_info.lts_date, m_update_info.diff_msec);
m_request_url = latest[os]["download"].toString().toStdString();
m_expected_hash = latest[os]["checksum"].toString().toStdString();
m_expected_size = latest[os]["size"].toInt();
@ -277,9 +243,9 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
if (!auto_accept)
{
if (automatic && m_gui_settings->GetValue(gui::ib_skip_version).toString() == new_version)
if (automatic && m_gui_settings->GetValue(gui::ib_skip_version).toString() == m_update_info.new_version)
{
update_log.notice("Skipping automatic update notification for version '%s' due to user preference", new_version);
update_log.notice("Skipping automatic update notification for version '%s' due to user preference", m_update_info.new_version);
m_downloader->close_progress_dialog();
return true;
}
@ -300,7 +266,6 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
}
else
{
entry.version = tr("N/A");
update_log.notice("JSON changelog entry does not contain a version string.");
}
@ -310,11 +275,10 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce
}
else
{
entry.title = tr("N/A");
update_log.notice("JSON changelog entry does not contain a title string.");
}
m_changelog.push_back(entry);
m_update_info.changelog.push_back(std::move(entry));
}
else
{
@ -351,25 +315,61 @@ void update_manager::update(bool auto_accept)
if (!auto_accept)
{
if (m_update_message.isEmpty())
if (!m_update_info.update_found)
{
// This can happen if we abort the check_for_updates download. Just check again in this case.
update_log.notice("Aborting update: Update message is empty. Trying again...");
update_log.notice("Aborting update: Update not found. Trying again...");
m_downloader->close_progress_dialog();
check_for_updates(false, false, false, m_parent);
return;
}
const Localized localized;
const QString support_message = tr("<br>You can empower our project at <a href=\"https://rpcs3.net/patreon\">RPCS3 Patreon</a>.<br>");
QString update_message;
if (m_update_info.hash_found)
{
if (m_update_info.diff_msec < 0)
{
// This usually means that the current version was marked as broken and won't be shipped anymore, so we need to downgrade to avoid certain bugs.
update_message = tr("A better version of RPCS3 is available!<br><br>Current version: %0 (%1)<br>Better version: %2 (%3)<br>%4<br>Do you want to update?")
.arg(m_update_info.old_version)
.arg(m_update_info.cur_date)
.arg(m_update_info.new_version)
.arg(m_update_info.lts_date)
.arg(support_message);
}
else
{
update_message = tr("A new version of RPCS3 is available!<br><br>Current version: %0 (%1)<br>Latest version: %2 (%3)<br>Your version is %4 behind.<br>%5<br>Do you want to update?")
.arg(m_update_info.old_version)
.arg(m_update_info.cur_date)
.arg(m_update_info.new_version)
.arg(m_update_info.lts_date)
.arg(localized.GetVerboseTimeByMs(m_update_info.diff_msec, true))
.arg(support_message);
}
}
else
{
update_message = tr("You're currently using a custom or PR build.<br><br>Latest version: %0 (%1)<br>The latest version is %2 old.<br>%3<br>Do you want to update to the latest official RPCS3 version?")
.arg(m_update_info.new_version)
.arg(m_update_info.lts_date)
.arg(localized.GetVerboseTimeByMs(std::abs(m_update_info.diff_msec), true))
.arg(support_message);
}
QString changelog_content;
for (const changelog_data& entry : m_changelog)
for (const changelog_data& entry : m_update_info.changelog)
{
if (!changelog_content.isEmpty())
changelog_content.append('\n');
changelog_content.append(tr("• %0: %1").arg(entry.version, entry.title));
changelog_content.append(tr("• %0: %1").arg(entry.version.isEmpty() ? tr("N/A") : entry.version, entry.title.isEmpty() ? tr("N/A") : entry.title));
}
QMessageBox mb(QMessageBox::Icon::Question, tr("Update Available"), m_update_message, QMessageBox::Yes | QMessageBox::No, m_downloader->get_progress_dialog() ? m_downloader->get_progress_dialog() : m_parent);
QMessageBox mb(QMessageBox::Icon::Question, tr("Update Available"), update_message, QMessageBox::Yes | QMessageBox::No, m_downloader->get_progress_dialog() ? m_downloader->get_progress_dialog() : m_parent);
mb.setTextFormat(Qt::RichText);
mb.setCheckBox(new QCheckBox(tr("Don't show again for this version")));
@ -380,16 +380,16 @@ void update_manager::update(bool auto_accept)
// Smartass hack to make the unresizeable message box wide enough for the changelog
const int changelog_width = QLabel(changelog_content).sizeHint().width();
if (QLabel(m_update_message).sizeHint().width() < changelog_width)
if (QLabel(update_message).sizeHint().width() < changelog_width)
{
m_update_message += " &nbsp;";
while (QLabel(m_update_message).sizeHint().width() < changelog_width)
update_message += " &nbsp;";
while (QLabel(update_message).sizeHint().width() < changelog_width)
{
m_update_message += "&nbsp;";
update_message += "&nbsp;";
}
}
mb.setText(m_update_message);
mb.setText(update_message);
}
update_log.notice("Asking user for permission to update...");
@ -400,8 +400,8 @@ void update_manager::update(bool auto_accept)
if (mb.checkBox()->isChecked())
{
update_log.notice("User requested to skip further automatic update notifications for version '%s'", m_new_version);
m_gui_settings->SetValue(gui::ib_skip_version, QString::fromStdString(m_new_version));
update_log.notice("User requested to skip further automatic update notifications for version '%s'", m_update_info.new_version);
m_gui_settings->SetValue(gui::ib_skip_version, m_update_info.new_version);
}
m_downloader->close_progress_dialog();
@ -751,7 +751,7 @@ bool update_manager::handle_rpcs3(const QByteArray& data, bool auto_accept)
if (fs::file update_file{fs::get_config_dir() + "update_history.log", fs::create + fs::write + fs::append})
{
const std::string update_time = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss").toStdString();
const std::string entry = fmt::format("%s: Updated from \"%s\" to \"%s\"", update_time, m_old_version, m_new_version);
const std::string entry = fmt::format("%s: Updated from \"%s\" to \"%s\"", update_time, m_update_info.old_version, m_update_info.new_version);
update_file.write(fmt::format("%s\n", entry));
update_log.notice("Added entry '%s' to update_history.log", entry);
}

View file

@ -19,20 +19,28 @@ private:
std::shared_ptr<gui_settings> m_gui_settings;
// This message is empty if there is no download available
QString m_update_message;
struct changelog_data
{
QString version;
QString title;
};
std::vector<changelog_data> m_changelog;
struct update_info
{
bool update_found = false;
bool hash_found = false;
qint64 diff_msec = 0;
QString cur_date;
QString lts_date;
QString old_version;
QString new_version;
std::vector<changelog_data> changelog;
};
update_info m_update_info {};
std::string m_request_url;
std::string m_expected_hash;
std::string m_old_version;
std::string m_new_version;
u64 m_expected_size = 0;
bool handle_json(bool automatic, bool check_only, bool auto_accept, const QByteArray& data);