diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index cee6d624de..d8016d8c13 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -6,7 +6,7 @@ QT_HOST="http://qt.mirror.constant.com/" QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g") QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]') -QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." +QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}_${QT_VER_MSVC}_64/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" QT_SUFFIX="-Windows-Windows_11_24H2-${QT_VER_MSVC_UP}-Windows-Windows_11_24H2-X86_64.7z" QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}" diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 740e844d04..f474643e09 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -30,23 +30,23 @@ jobs: matrix: include: - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy:1.10" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: clang UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy:1.10" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: gcc - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: clang UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: gcc name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} @@ -134,7 +134,7 @@ jobs: runs-on: macos-14 env: CCACHE_DIR: /tmp/ccache_dir - QT_VER: '6.10.2' + QT_VER: '6.11.0' QT_VER_MAIN: '6' LLVM_COMPILER_VER: '21' RELEASE_MESSAGE: ../GitHubReleaseMessage.txt @@ -213,9 +213,9 @@ jobs: env: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.10.2' + QT_VER: '6.11.0' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202601261212' + QT_DATE: '202603180535' LLVM_VER: '19.1.7' VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index c3e304954a..d5515b5b8b 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d +Subproject commit d5515b5b8be3901aac04e5bd8bd5c89f287bcd33 diff --git a/3rdparty/wolfssl/wolfssl b/3rdparty/wolfssl/wolfssl index b077c81eb6..922d04b356 160000 --- a/3rdparty/wolfssl/wolfssl +++ b/3rdparty/wolfssl/wolfssl @@ -1 +1 @@ -Subproject commit b077c81eb635392e694ccedbab8b644297ec0285 +Subproject commit 922d04b3568c6428a9fb905ddee3ef5a68db3108 diff --git a/BUILDING.md b/BUILDING.md index 597621e810..c1774908fd 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -20,26 +20,26 @@ The following tools are required to build RPCS3 on Windows 10 or later: with standalone **CMake** tool. - [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -- [Qt 6.10.2](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.11.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) - [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. `\6.10.2\msvc2022_64\` +- add and set the `QTDIR` environment variable, e.g. `\6.11.0\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. `\6.10.2\msvc2022_64\` +- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.11.0\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.2](https://www.qt.io/download-qt-installer) +- [Qt 6.11.0](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) @@ -123,7 +123,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. `\6.10.2\msvc2022_64`, version will fill in automatically +2) add the path to your Qt installation with compiler e.g. `\6.11.0\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**) diff --git a/Utilities/Config.h b/Utilities/Config.h index ca9af028a5..4c79cbf31c 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -393,7 +393,7 @@ namespace cfg void set(const s64& value) { - ensure(value >= Min && value <= Max); + if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max); m_value = static_cast(value); } @@ -484,7 +484,7 @@ namespace cfg void set(const f64& value) { - ensure(value >= Min && value <= Max); + if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max); m_value = static_cast(value); } @@ -571,7 +571,7 @@ namespace cfg void set(const u64& value) { - ensure(value >= Min && value <= Max); + if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max); m_value = static_cast(value); } diff --git a/buildfiles/cmake/FindWolfSSL.cmake b/buildfiles/cmake/FindWolfSSL.cmake index d2e30be60b..35f316837c 100644 --- a/buildfiles/cmake/FindWolfSSL.cmake +++ b/buildfiles/cmake/FindWolfSSL.cmake @@ -1,4 +1,3 @@ -set(WOLFSSL_LIBRARY ON) -set(WOLFSSL_INCLUDE_DIR ON) -set(WOLFSSL_LIBRARIES wolfssl) +set(WOLFSSL_LIBRARY wolfssl) +set(WOLFSSL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/wolfssl) set(WOLFSSL_FOUND TRUE) diff --git a/buildfiles/cmake/FindZLIB.cmake b/buildfiles/cmake/FindZLIB.cmake index 0a29abafa9..ff5869a5f7 100644 --- a/buildfiles/cmake/FindZLIB.cmake +++ b/buildfiles/cmake/FindZLIB.cmake @@ -3,9 +3,9 @@ if(USE_SYSTEM_ZLIB) find_package(ZLIB) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) else() - add_library(ZLIB::ZLIB INTERFACE IMPORTED) + add_library(ZLIB::ZLIB STATIC IMPORTED) set_target_properties(ZLIB::ZLIB PROPERTIES - INTERFACE_LINK_LIBRARIES zlibstatic + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/libzlibstatic.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/zlib/zlib;${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib") set(ZLIB_FOUND TRUE) endif() diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 0062bc2825..f5d91cc519 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -1286,7 +1286,7 @@ extern bool ppu_patch(u32 addr, u32 value) { if (addr % 4) { - ppu_log.fatal("Patch failed at 0x%x: unanligned memory address.", addr); + ppu_log.fatal("Patch failed at 0x%x: unaligned memory address.", addr); return false; } diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 7511b2ae7b..948c6e8b14 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -2323,7 +2323,7 @@ public: llvm::Value* loop_dictator_after_adjustment{}; spu_opcode_t reg_target{}; - reg_target.rt = reduced_loop_info->cond_val_register_idx; + reg_target.rt = static_cast(reduced_loop_info->cond_val_register_idx); if (reg_target.rt != reduced_loop_info->cond_val_register_idx) { @@ -2375,7 +2375,7 @@ public: else { spu_opcode_t reg_incr{}; - reg_incr.rt = reduced_loop_info->cond_val_incr; + reg_incr.rt = static_cast(reduced_loop_info->cond_val_incr); if (reg_incr.rt != reduced_loop_info->cond_val_incr) { @@ -2425,7 +2425,7 @@ public: else { spu_opcode_t reg_target2{}; - reg_target2.rt = reduced_loop_info->cond_val_register_argument_idx; + reg_target2.rt = static_cast(reduced_loop_info->cond_val_register_argument_idx); if (reg_target2.rt != reduced_loop_info->cond_val_register_argument_idx) { diff --git a/rpcs3/Emu/Io/recording_config.h b/rpcs3/Emu/Io/recording_config.h index 127d24015f..ef73149f5e 100644 --- a/rpcs3/Emu/Io/recording_config.h +++ b/rpcs3/Emu/Io/recording_config.h @@ -13,13 +13,13 @@ struct cfg_recording final : cfg::node node_video(cfg::node* _this) : cfg::node(_this, "Video") {} cfg::uint<0, 60> framerate{this, "Framerate", 30}; - cfg::uint<0, 7680> width{this, "Width", 1280}; - cfg::uint<0, 4320> height{this, "Height", 720}; + cfg::uint<640, 7680> width{this, "Width", 1280}; + cfg::uint<360, 4320> height{this, "Height", 720}; cfg::uint<0, 192> pixel_format{this, "AVPixelFormat", 0}; // AVPixelFormat::AV_PIX_FMT_YUV420P cfg::uint<0, 0xFFFF> video_codec{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4 - cfg::uint<0, 25000000> video_bps{this, "Video Bitrate", 4000000}; - cfg::uint<0, 5> max_b_frames{this, "Max B-Frames", 2}; - cfg::uint<0, 20> gop_size{this, "Group of Pictures Size", 12}; + cfg::uint<1'000'000, 60'000'000> video_bps{this, "Video Bitrate", 4'000'000}; + cfg::uint<0, 3> max_b_frames{this, "Max B-Frames", 2}; + cfg::uint<1, 120> gop_size{this, "Group of Pictures Size", 30}; } video{ this }; @@ -28,7 +28,7 @@ struct cfg_recording final : cfg::node node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {} cfg::uint<0x10000, 0x17000> audio_codec{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC - cfg::uint<0, 25000000> audio_bps{this, "Audio Bitrate", 320000}; + cfg::uint<64'000, 320'000> audio_bps{this, "Audio Bitrate", 192'000}; } audio{ this }; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp index 93148d655c..5208efd747 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp @@ -431,25 +431,25 @@ namespace rsx m_is_compiled = false; } - void overlay_element::set_text(const std::string& text) + void overlay_element::set_text(std::string_view text) { std::u32string new_text = utf8_to_u32string(text); const bool is_dirty = this->text != new_text; - this->text = std::move(new_text); if (is_dirty) { + this->text = std::move(new_text); m_is_compiled = false; } } - void overlay_element::set_unicode_text(const std::u32string& text) + void overlay_element::set_unicode_text(std::u32string_view text) { const bool is_dirty = this->text != text; - this->text = text; if (is_dirty) { + this->text = text; m_is_compiled = false; } } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.h b/rpcs3/Emu/RSX/Overlays/overlay_controls.h index 04b6820287..8fa835f595 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.h @@ -242,8 +242,8 @@ namespace rsx // NOTE: Functions as a simple position offset. Top left corner is the anchor. virtual void set_margin(u16 left, u16 top); virtual void set_margin(u16 margin); - virtual void set_text(const std::string& text); - virtual void set_unicode_text(const std::u32string& text); + virtual void set_text(std::string_view text); + virtual void set_unicode_text(std::u32string_view text); void set_text(localized_string_id id); void set_text(const localized_string& container); virtual void set_font(const char* font_name, u16 font_size); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp index ba3d138f96..4fad1f65e9 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp @@ -95,12 +95,12 @@ namespace rsx } } - void edit_text::set_text(const std::string& text) + void edit_text::set_text(std::string_view text) { set_unicode_text(utf8_to_u32string(text)); } - void edit_text::set_unicode_text(const std::u32string& text) + void edit_text::set_unicode_text(std::u32string_view text) { value = text; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp index 624580e4f4..7d4f6d93b5 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp @@ -26,8 +26,8 @@ namespace rsx using label::label; - void set_text(const std::string& text) override; - void set_unicode_text(const std::u32string& text) override; + void set_text(std::string_view text) override; + void set_unicode_text(std::u32string_view text) override; void set_placeholder(const std::u32string& placeholder_text); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp index 304e54de1e..1c49401d02 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp @@ -90,7 +90,7 @@ namespace rsx return m_loc_id == id; } - bool message_item::text_matches(const std::u32string& text) const + bool message_item::text_matches(std::u32string_view text) const { return m_text.text == text; } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.h b/rpcs3/Emu/RSX/Overlays/overlay_message.h index 219103e843..e8cb1a3285 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.h @@ -31,7 +31,7 @@ namespace rsx compiled_resource& get_compiled() override; bool id_matches(localized_string_id id) const; - bool text_matches(const std::u32string& text) const; + bool text_matches(std::u32string_view text) const; void set_label_text(const std::string& text); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp b/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp index e62097c71c..635833c9d8 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp @@ -948,7 +948,7 @@ namespace rsx perf_overlay->set_update_interval(perf_settings.update_interval); perf_overlay->set_font(perf_settings.font); perf_overlay->set_font_size(perf_settings.font_size); - perf_overlay->set_margins(perf_settings.margin_x, perf_settings.margin_y, perf_settings.center_x.get(), perf_settings.center_y.get()); + perf_overlay->set_margins(static_cast(perf_settings.margin_x.get()), static_cast(perf_settings.margin_y.get()), perf_settings.center_x.get(), perf_settings.center_y.get()); perf_overlay->use_window_space = perf_settings.perf_overlay_use_window_space.get(); perf_overlay->set_opacity(perf_settings.opacity / 100.f); perf_overlay->set_body_colors(perf_settings.color_body, perf_settings.background_body); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp index 860f54544a..b9140f2a69 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp @@ -58,7 +58,7 @@ namespace rsx set_pos(x + dx, y + dy); } - void progress_bar::set_text(const std::string& str) + void progress_bar::set_text(std::string_view str) { text_view.set_text(str); text_view.align_text(text_align::center); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp index 6ed6b73c77..d622796dae 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp @@ -24,7 +24,7 @@ namespace rsx void set_pos(s16 _x, s16 _y) override; void set_size(u16 _w, u16 _h) override; void translate(s16 dx, s16 dy) override; - void set_text(const std::string& str) override; + void set_text(std::string_view str) override; compiled_resource& get_compiled() override; }; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp b/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp index 18eac492e9..047ca6fff3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp @@ -60,7 +60,7 @@ static auto s_ascii_lowering_map = []() }(); template -void process_multibyte(const std::string& s, F&& func) +void process_multibyte(std::string_view s, F&& func) { const usz end = s.length(); for (usz index = 0; index < end; ++index) @@ -110,7 +110,7 @@ void process_multibyte(const std::string& s, F&& func) } } -std::string utf8_to_ascii8(const std::string& utf8_string) +std::string utf8_to_ascii8(std::string_view utf8_string) { std::string out; out.reserve(utf8_string.length()); @@ -135,7 +135,7 @@ std::string utf8_to_ascii8(const std::string& utf8_string) return out; } -std::string utf16_to_ascii8(const std::u16string& utf16_string) +std::string utf16_to_ascii8(std::u16string_view utf16_string) { // Strip extended codes, map to '#' instead (placeholder) std::string out; @@ -152,7 +152,7 @@ std::string utf16_to_ascii8(const std::u16string& utf16_string) return out; } -std::u16string ascii8_to_utf16(const std::string& ascii_string) +std::u16string ascii8_to_utf16(std::string_view ascii_string) { std::u16string out; out.reserve(ascii_string.length()); @@ -168,7 +168,7 @@ std::u16string ascii8_to_utf16(const std::string& ascii_string) return out; } -std::u32string utf8_to_u32string(const std::string& utf8_string) +std::u32string utf8_to_u32string(std::string_view utf8_string) { std::u32string result; result.reserve(utf8_string.size()); @@ -181,7 +181,7 @@ std::u32string utf8_to_u32string(const std::string& utf8_string) return result; } -std::u16string u32string_to_utf16(const std::u32string& utf32_string) +std::u16string u32string_to_utf16(std::u32string_view utf32_string) { std::u16string result; result.reserve(utf32_string.size()); @@ -194,7 +194,7 @@ std::u16string u32string_to_utf16(const std::u32string& utf32_string) return result; } -std::u32string utf16_to_u32string(const std::u16string& utf16_string) +std::u32string utf16_to_u32string(std::u16string_view utf16_string) { std::u32string result; result.reserve(utf16_string.size()); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_utils.h b/rpcs3/Emu/RSX/Overlays/overlay_utils.h index aaa70f09a3..ab3ec0894d 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_utils.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_utils.h @@ -218,9 +218,9 @@ void operator < (const vector3_base& lhs, T rhs) using vector3i = vector3_base; using vector3f = vector3_base; -std::string utf8_to_ascii8(const std::string& utf8_string); -std::string utf16_to_ascii8(const std::u16string& utf16_string); -std::u16string ascii8_to_utf16(const std::string& ascii_string); -std::u32string utf8_to_u32string(const std::string& utf8_string); -std::u16string u32string_to_utf16(const std::u32string& utf32_string); -std::u32string utf16_to_u32string(const std::u16string& utf16_string); +std::string utf8_to_ascii8(std::string_view utf8_string); +std::string utf16_to_ascii8(std::u16string_view utf16_string); +std::u16string ascii8_to_utf16(std::string_view ascii_string); +std::u32string utf8_to_u32string(std::string_view utf8_string); +std::u16string u32string_to_utf16(std::u32string_view utf32_string); +std::u32string utf16_to_u32string(std::u16string_view utf16_string); diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 5158ce9a29..3392b9693b 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1868,6 +1868,7 @@ namespace rsx } default: rsx_log.fatal("Unhandled framebuffer option changed 0x%x", opt); + break; } } diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 4fdb5e47f3..da7251e291 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2837,7 +2837,7 @@ bool Emulator::Pause(bool freeze_emulation, bool show_resume_message) auto msg_ref = std::make_shared>(1); // No timeout - rsx::overlays::queue_message(status == system_state::paused ? localized_string_id::EMULATION_PAUSED_RESUME_WITH_START : localized_string_id::EMULATION_FROZEN, -1, msg_ref); + rsx::overlays::queue_message(status == system_state::paused ? localized_string_id::EMULATION_PAUSED_RESUME_WITH_START : localized_string_id::EMULATION_FROZEN, umax, msg_ref); m_pause_msgs_refs.emplace_back(msg_ref); auto refresh_l = [this, msg_ref, status]() @@ -3160,7 +3160,11 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta if (async_op) { - std::thread{perform_kill}.detach(); + std::thread{[perform_kill]() + { + thread_base::set_name("Perform Kill"); + perform_kill(); + }}.detach(); } else { diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index 135a61c491..5d9240254d 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -49,7 +49,7 @@ void headless_application::InitializeCallbacks() { EmuCallbacks callbacks = CreateCallbacks(); - callbacks.try_to_quit = [this](bool force_quit, std::function on_exit) -> bool + callbacks.try_to_quit = [](bool force_quit, std::function on_exit) -> bool { if (force_quit) { diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 86e3c47171..921394e311 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -69,6 +69,7 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti #include "rpcs3_version.h" #include "Emu/System.h" #include "Emu/system_utils.hpp" +#include "Emu/RSX/Overlays/overlay_message.h" #include #include @@ -312,7 +313,8 @@ public: { if (msg == logs::level::fatal || (msg == logs::level::always && m_log_always)) { - std::string _msg = "RPCS3: "; + static const std::string rpcs3_prefix = "RPCS3: "; + std::string _msg = rpcs3_prefix; if (!prefix.empty()) { @@ -351,7 +353,11 @@ public: #endif if (msg == logs::level::fatal) { + std::string overlay_msg = "Fatal error: " + _msg.substr(rpcs3_prefix.size()); + fmt::trim_back(overlay_msg, " \t\n"); + // Pause emulation if fatal error encountered + rsx::overlays::queue_message(overlay_msg, umax); Emu.Pause(true); } } diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 5401efb323..d25ca3905c 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -386,6 +386,9 @@ true + + true + true @@ -689,6 +692,9 @@ true + + true + true @@ -868,6 +874,7 @@ + @@ -1157,6 +1164,7 @@ + @@ -1624,6 +1632,16 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-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" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-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" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-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" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + @@ -2196,6 +2214,16 @@ .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) Uic%27ing %(Identity)... diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index d9ebc34ef6..28af783415 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -291,6 +291,12 @@ Generated Files\Release + + Generated Files\Debug + + + Generated Files\Release + Generated Files\Debug @@ -441,6 +447,9 @@ Gui\settings + + Gui\settings + Gui\log @@ -1304,6 +1313,9 @@ Generated Files + + Generated Files + Generated Files @@ -1555,6 +1567,9 @@ Form Files + + Form Files + Form Files @@ -1597,6 +1612,9 @@ Gui\settings + + Gui\settings + Gui\log diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 5c330c7ae4..b0a75d9bfa 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -83,6 +83,7 @@ add_library(rpcs3_ui STATIC qt_video_source.cpp raw_mouse_settings_dialog.cpp register_editor_dialog.cpp + recording_settings_dialog.cpp recvmessage_dialog_frame.cpp render_creator.cpp rpcn_settings_dialog.cpp @@ -135,6 +136,7 @@ add_library(rpcs3_ui STATIC patch_creator_dialog.ui patch_manager_dialog.ui ps_move_tracker_dialog.ui + recording_settings_dialog.ui settings_dialog.ui shortcut_dialog.ui welcome_dialog.ui diff --git a/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp b/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp index 26cbfc96b7..4eaadb9e7e 100644 --- a/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp @@ -181,7 +181,7 @@ void auto_pause_settings_dialog::ShowContextMenu(const QPoint &pos) OnEntryConfig(idx, true); }); connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove); - connect(config, &QAction::triggered, this, [=, this]() {OnEntryConfig(row, false); }); + connect(config, &QAction::triggered, this, [=]() {OnEntryConfig(row, false); }); myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos)); } diff --git a/rpcs3/rpcs3qt/clans_settings_dialog.cpp b/rpcs3/rpcs3qt/clans_settings_dialog.cpp index 9745d11294..0b26b791ee 100644 --- a/rpcs3/rpcs3qt/clans_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/clans_settings_dialog.cpp @@ -66,7 +66,7 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent) g_cfg_clans.save(); }); - connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [this](int index) + connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [](int index) { if (index < 0) return; diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index 852449c0af..9a5038fcc1 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -8,6 +8,7 @@ #include "progress_dialog.h" #include "util/logs.hpp" +#include "Utilities/Thread.h" #include @@ -82,6 +83,8 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p m_thread = QThread::create([this] { + thread_base::set_name("Downloader"); + // Reset error buffer before we call curl_easy_perform m_curl->reset_error_buffer(); diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 30860e10f5..0ec017a02c 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -207,7 +207,7 @@ bool emu_settings::ValidateSettings(bool cleanup) bool is_clean = true; std::function&, cfg::_base*)> search_level; - search_level = [&search_level, &is_clean, &cleanup, this](int level, YAML::Node& yml_node, std::vector& keys, cfg::_base* cfg_base) + search_level = [&search_level, &is_clean, &cleanup](int level, YAML::Node& yml_node, std::vector& keys, cfg::_base* cfg_base) { if (!yml_node || !yml_node.IsMap()) { diff --git a/rpcs3/rpcs3qt/find_dialog.cpp b/rpcs3/rpcs3qt/find_dialog.cpp index 25d9442f2b..756e355e4f 100644 --- a/rpcs3/rpcs3qt/find_dialog.cpp +++ b/rpcs3/rpcs3qt/find_dialog.cpp @@ -15,7 +15,7 @@ find_dialog::find_dialog(QPlainTextEdit* edit, QWidget *parent, Qt::WindowFlags QCheckBox* cb_case_sensitive = new QCheckBox(tr("Case sensitive")); cb_case_sensitive->setChecked(m_case_sensitive); - connect(cb_case_sensitive, &QCheckBox::toggled, this, [=](bool checked) + connect(cb_case_sensitive, &QCheckBox::toggled, this, [this](bool checked) { m_case_sensitive = checked; }); diff --git a/rpcs3/rpcs3qt/game_list_actions.cpp b/rpcs3/rpcs3qt/game_list_actions.cpp index 5ff938e990..25b9f06b96 100644 --- a/rpcs3/rpcs3qt/game_list_actions.cpp +++ b/rpcs3/rpcs3qt/game_list_actions.cpp @@ -908,7 +908,7 @@ void game_list_actions::BatchActionBySerials(progress_dialog* pdlg, const std::s const int serials_size = ::narrow(serials.size()); - *iterate_over_serial = [=, this, index_ptr = index](int index) + *iterate_over_serial = [=, index_ptr = index](int index) { if (index == serials_size) { diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp index 94fa571f7e..903fdf016a 100644 --- a/rpcs3/rpcs3qt/game_list_grid.cpp +++ b/rpcs3/rpcs3qt/game_list_grid.cpp @@ -21,7 +21,7 @@ game_list_grid::game_list_grid() Q_EMIT IconReady(game, item); }; - connect(this, &game_list_grid::IconReady, this, [this](const game_info& game, const movie_item_base* item) + connect(this, &game_list_grid::IconReady, this, [](const game_info& game, const movie_item_base* item) { if (game && item && game->item == item) item->image_change_callback(); }, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case... diff --git a/rpcs3/rpcs3qt/game_list_table.cpp b/rpcs3/rpcs3qt/game_list_table.cpp index 53d0754bce..3d40467bb1 100644 --- a/rpcs3/rpcs3qt/game_list_table.cpp +++ b/rpcs3/rpcs3qt/game_list_table.cpp @@ -54,7 +54,7 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptritem == item) item->image_change_callback(); }); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 2dab09d87f..bcc148d9a6 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -793,7 +793,7 @@ f64 gs_frame::client_display_rate() { f64 rate = 20.; // Minimum is 20 - Emu.BlockingCallFromMainThread([this, &rate]() + Emu.BlockingCallFromMainThread([&rate]() { const QList screens = QGuiApplication::screens(); diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 606a081fff..8379e2f5e0 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -737,8 +737,8 @@ void gui_application::InitializeCallbacks() callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; callbacks.get_osk_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; callbacks.get_save_dialog = []() -> std::unique_ptr { return std::make_unique(); }; - callbacks.get_sendmessage_dialog = [this]() -> std::shared_ptr { return std::make_shared(); }; - callbacks.get_recvmessage_dialog = [this]() -> std::shared_ptr { return std::make_shared(); }; + callbacks.get_sendmessage_dialog = []() -> std::shared_ptr { return std::make_shared(); }; + callbacks.get_recvmessage_dialog = []() -> std::shared_ptr { return std::make_shared(); }; callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr { return std::make_unique(m_game_window); }; callbacks.on_run = [this](bool start_playtime) { OnEmulatorRun(start_playtime); }; @@ -839,7 +839,7 @@ void gui_application::InitializeCallbacks() }; } - callbacks.on_emulation_stop_no_response = [this](std::shared_ptr> closed_successfully, int seconds_waiting_already) + callbacks.on_emulation_stop_no_response = [](std::shared_ptr> closed_successfully, int seconds_waiting_already) { const std::string terminate_message = tr("Stopping emulator took too long." "\nSome thread has probably deadlocked. Aborting.").toStdString(); @@ -849,7 +849,7 @@ void gui_application::InitializeCallbacks() report_fatal_error(terminate_message); } - Emu.CallFromMainThread([this, closed_successfully, seconds_waiting_already, terminate_message] + Emu.CallFromMainThread([closed_successfully, seconds_waiting_already, terminate_message] { const auto seconds = std::make_shared(seconds_waiting_already); diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 68c6f45e2b..926949fb58 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -316,7 +316,7 @@ void kernel_explorer::update() add_solid_node(find_node(root, additional_nodes::process_info), QString::fromStdString(fmt::format("Process Info, Sdk Version: 0x%08x, PPC SEG: %#x, SFO Category: %s (Fake: %s)", g_ps3_process_info.sdk_ver, g_ps3_process_info.ppc_seg, Emu.GetCat(), Emu.GetFakeCat()))); - auto display_program_segments = [this](QTreeWidgetItem* tree, const ppu_module& m) + auto display_program_segments = [](QTreeWidgetItem* tree, const ppu_module& m) { for (usz i = 0; i < m.segs.size(); i++) { @@ -661,7 +661,7 @@ void kernel_explorer::update() const s32 prio = ppu.prio.load().prio; std::string prio_text = fmt::format("%4d", prio); prio_text = fmt::replace_all(prio_text, " ", " "); - + ppu_threads.emplace_back(prio, fmt::format(u8"PPU 0x%07x: PRIO: %s, “%s”Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, prio_text, *ppu.ppu_tname.load(), ppu.joiner.load(), status, ppu.state.load() , ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time))); }, idm::unlocked); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index a82fe5b091..130434f0c2 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -46,6 +46,7 @@ #include "welcome_dialog.h" #include "music_player_dialog.h" #include "sound_effect_manager_dialog.h" +#include "recording_settings_dialog.h" #include #include @@ -1734,7 +1735,7 @@ void main_window::DecryptSPRXLibraries() dlg->set_button_enabled(QDialogButtonBox::StandardButton::Ok, text.size() - (text.indexOf('x') + 1) == 32); }); - connect(dlg, &QDialog::accepted, this, [this, iterate, dlg, mod_index, decrypter, repeat_count]() + connect(dlg, &QDialog::accepted, this, [iterate, dlg, mod_index, decrypter, repeat_count]() { std::string text = dlg->get_input_text().toStdString(); @@ -3120,16 +3121,22 @@ void main_window::CreateConnects() connect(ui->actionManage_Screenshots, &QAction::triggered, this, [this] { - screenshot_manager_dialog* screenshot_manager = new screenshot_manager_dialog(); + screenshot_manager_dialog* screenshot_manager = new screenshot_manager_dialog(m_game_list_frame ? m_game_list_frame->GetGameInfo() : std::vector{}); screenshot_manager->show(); }); - connect(ui->actionManage_SoundEffects, &QAction::triggered, this, [this] + connect(ui->actionManage_SoundEffects, &QAction::triggered, this, [] { sound_effect_manager_dialog* dlg = new sound_effect_manager_dialog(); dlg->show(); }); + connect(ui->actionRecording, &QAction::triggered, this, [this] + { + recording_settings_dialog* dlg = new recording_settings_dialog(this); + dlg->open(); + }); + connect(ui->toolsCgDisasmAct, &QAction::triggered, this, [this] { cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings); @@ -3376,7 +3383,7 @@ void main_window::CreateConnects() welcome->open(); }); - connect(ui->supportAct, &QAction::triggered, this, [this] + connect(ui->supportAct, &QAction::triggered, this, [] { QDesktopServices::openUrl(QUrl("https://rpcs3.net/patreon")); }); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 8fcf4ac610..7a7c965ce8 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -290,6 +290,7 @@ + @@ -1536,6 +1537,11 @@ Play Hover Music + + + Recording + + diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index bcaa6537ef..0c779a9558 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -504,6 +504,8 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr + +#ifdef _MSC_VER +#pragma warning(push, 0) +#else +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wextra" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +} +#ifdef _MSC_VER +#pragma warning(pop) +#else +#pragma GCC diagnostic pop +#endif + +LOG_CHANNEL(cfg_log, "CFG"); + +static std::vector get_video_codecs(const AVOutputFormat* fmt) +{ + std::vector codecs; + + void* opaque = nullptr; + while (const AVCodec* codec = av_codec_iterate(&opaque)) + { + if (!codec->pix_fmts) + continue; + + if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) + continue; + + if (codec->type != AVMediaType::AVMEDIA_TYPE_VIDEO) + continue; + + switch (codec->id) + { + case AV_CODEC_ID_H264: + case AV_CODEC_ID_HEVC: + case AV_CODEC_ID_MPEG4: + case AV_CODEC_ID_AV1: + break; + default: + continue; + } + + if (!av_codec_is_encoder(codec)) + continue; + + if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1) + continue; + + codecs.push_back(codec); + } + + return codecs; +} + +static std::vector get_audio_codecs(const AVOutputFormat* fmt) +{ + std::vector codecs; + + void* opaque = nullptr; + while (const AVCodec* codec = av_codec_iterate(&opaque)) + { + if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) + continue; + + if (codec->type != AVMediaType::AVMEDIA_TYPE_AUDIO) + continue; + + if (!av_codec_is_encoder(codec)) + continue; + + if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1) + continue; + + codecs.push_back(codec); + } + + return codecs; +} + +recording_settings_dialog::recording_settings_dialog(QWidget* parent) + : QDialog(parent), ui(new Ui::recording_settings_dialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + if (!g_cfg_recording.load()) + { + cfg_log.notice("Could not load recording config. Using defaults."); + } + + ui->combo_presets->addItem(tr("720p 30fps"), static_cast(quality_preset::_720p_30)); + ui->combo_presets->addItem(tr("720p 60fps"), static_cast(quality_preset::_720p_60)); + ui->combo_presets->addItem(tr("1080p 30fps"), static_cast(quality_preset::_1080p_30)); + ui->combo_presets->addItem(tr("1080p 60fps"), static_cast(quality_preset::_1080p_60)); + ui->combo_presets->addItem(tr("1440p 30fps"), static_cast(quality_preset::_1440p_30)); + ui->combo_presets->addItem(tr("1440p 60fps"), static_cast(quality_preset::_1440p_60)); + ui->combo_presets->addItem(tr("2160p 30fps"), static_cast(quality_preset::_2160p_30)); + ui->combo_presets->addItem(tr("2160p 60fps"), static_cast(quality_preset::_2160p_60)); + ui->combo_presets->addItem(tr("Custom"), static_cast(quality_preset::custom)); + connect(ui->combo_presets, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_presets->itemData(index); + if (var.canConvert()) + { + const quality_preset preset = static_cast(var.toInt()); + select_preset(preset, g_cfg_recording); + update_ui(); + } + }); + + ui->combo_resolution->addItem("360p", QVariant::fromValue(QPair(640, 360))); + ui->combo_resolution->addItem("480p", QVariant::fromValue(QPair(854, 480))); + ui->combo_resolution->addItem("720p", QVariant::fromValue(QPair(1280, 720))); + ui->combo_resolution->addItem("1080p", QVariant::fromValue(QPair(1920, 1080))); + ui->combo_resolution->addItem("1440p", QVariant::fromValue(QPair(2560, 1440))); + ui->combo_resolution->addItem("2160p", QVariant::fromValue(QPair(3840, 2160))); + connect(ui->combo_resolution, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_resolution->itemData(index); + if (var.canConvert>()) + { + const QPair size = var.value>(); + g_cfg_recording.video.width.set(size.first); + g_cfg_recording.video.height.set(size.second); + update_preset(); + } + }); + + const AVOutputFormat* fmt = av_guess_format("mp4", nullptr, nullptr); + m_video_codecs = get_video_codecs(fmt); + m_audio_codecs = get_audio_codecs(fmt); + + for (const AVCodec* codec : m_video_codecs) + { + if (!codec) continue; + + const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id); + ui->combo_video_codec->addItem(QString::fromStdString(name), static_cast(codec->id)); + } + + for (const AVCodec* codec : m_audio_codecs) + { + if (!codec) continue; + + const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id); + ui->combo_audio_codec->addItem(QString::fromStdString(name), static_cast(codec->id)); + } + + connect(ui->combo_video_codec, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_video_codec->itemData(index); + if (var.canConvert()) + { + const int codec_id = var.toInt(); + g_cfg_recording.video.video_codec.set(codec_id); + update_preset(); + } + }); + + connect(ui->combo_audio_codec, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_audio_codec->itemData(index); + if (var.canConvert()) + { + const int codec_id = var.toInt(); + g_cfg_recording.audio.audio_codec.set(codec_id); + update_preset(); + } + }); + + ui->combo_framerate->addItem("30", 30); + ui->combo_framerate->addItem("60", 60); + connect(ui->combo_framerate, &QComboBox::currentIndexChanged, this, [this](int index) + { + const QVariant var = ui->combo_framerate->itemData(index); + if (var.canConvert()) + { + const int fps = var.toInt(); + g_cfg_recording.video.framerate.set(fps); + update_preset(); + } + }); + + ui->spinbox_video_bitrate->setSingleStep(1); + ui->spinbox_video_bitrate->setMinimum(g_cfg_recording.video.video_bps.min); + ui->spinbox_video_bitrate->setMaximum(g_cfg_recording.video.video_bps.max); + connect(ui->spinbox_video_bitrate, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.video.video_bps.set(value); + update_preset(); + }); + + ui->spinbox_audio_bitrate->setSingleStep(1); + ui->spinbox_audio_bitrate->setMinimum(g_cfg_recording.audio.audio_bps.min); + ui->spinbox_audio_bitrate->setMaximum(g_cfg_recording.audio.audio_bps.max); + connect(ui->spinbox_audio_bitrate, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.audio.audio_bps.set(value); + update_preset(); + }); + + ui->spinbox_gop_size->setSingleStep(1); + ui->spinbox_gop_size->setMinimum(g_cfg_recording.video.gop_size.min); + ui->spinbox_gop_size->setMaximum(g_cfg_recording.video.gop_size.max); + connect(ui->spinbox_gop_size, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.video.gop_size.set(value); + update_preset(); + }); + + ui->spinbox_max_b_frames->setSingleStep(1); + ui->spinbox_max_b_frames->setMinimum(g_cfg_recording.video.max_b_frames.min); + ui->spinbox_max_b_frames->setMaximum(g_cfg_recording.video.max_b_frames.max); + connect(ui->spinbox_max_b_frames, &QSpinBox::valueChanged, this, [this](int value) + { + g_cfg_recording.video.max_b_frames.set(value); + update_preset(); + }); + + connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) + { + if (button == ui->buttonBox->button(QDialogButtonBox::Save)) + { + g_cfg_recording.save(); + accept(); + } + else if (button == ui->buttonBox->button(QDialogButtonBox::Cancel)) + { + reject(); + } + else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) + { + g_cfg_recording.from_default(); + update_ui(); + update_preset(); + } + }); + + connect(this, &QDialog::rejected, this, []() + { + if (!g_cfg_recording.load()) + { + cfg_log.notice("Could not load recording config. Using defaults."); + } + }); + + update_ui(); + update_preset(); +} + +recording_settings_dialog::~recording_settings_dialog() +{ +} + +void recording_settings_dialog::update_preset() +{ + const quality_preset preset = current_preset(); + ui->combo_presets->setCurrentIndex(ui->combo_presets->findData(static_cast(preset))); +} + +void recording_settings_dialog::update_ui() +{ + ui->combo_resolution->blockSignals(true); + ui->combo_framerate->blockSignals(true); + ui->combo_video_codec->blockSignals(true); + ui->combo_audio_codec->blockSignals(true); + ui->spinbox_video_bitrate->blockSignals(true); + ui->spinbox_audio_bitrate->blockSignals(true); + ui->spinbox_gop_size->blockSignals(true); + ui->spinbox_max_b_frames->blockSignals(true); + + ui->combo_resolution->setCurrentIndex(ui->combo_resolution->findData(QVariant::fromValue(QPair(g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get())))); + ui->combo_framerate->setCurrentIndex(ui->combo_framerate->findData(static_cast(g_cfg_recording.video.framerate.get()))); + ui->combo_video_codec->setCurrentIndex(ui->combo_video_codec->findData(static_cast(g_cfg_recording.video.video_codec.get()))); + ui->combo_audio_codec->setCurrentIndex(ui->combo_audio_codec->findData(static_cast(g_cfg_recording.audio.audio_codec.get()))); + ui->spinbox_video_bitrate->setValue(g_cfg_recording.video.video_bps); + ui->spinbox_audio_bitrate->setValue(g_cfg_recording.audio.audio_bps); + ui->spinbox_gop_size->setValue(g_cfg_recording.video.gop_size); + ui->spinbox_max_b_frames->setValue(g_cfg_recording.video.max_b_frames); + + ui->combo_resolution->blockSignals(false); + ui->combo_framerate->blockSignals(false); + ui->combo_video_codec->blockSignals(false); + ui->combo_audio_codec->blockSignals(false); + ui->spinbox_video_bitrate->blockSignals(false); + ui->spinbox_audio_bitrate->blockSignals(false); + ui->spinbox_gop_size->blockSignals(false); + ui->spinbox_max_b_frames->blockSignals(false); + + const auto get_codec_name = [](const std::vector& codecs, u32 id) + { + for (const AVCodec* codec : codecs) + { + if (codec && codec->id == static_cast(id)) + { + const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id); + return name; + } + } + return std::string(); + }; + + ui->label_info_keys->setText( + tr("Resolution:") + "\n" + + tr("Framerate:") + "\n" + + tr("Video Codec:") + "\n" + + tr("Video Bitrate:") + "\n" + + tr("Audio Codec:") + "\n" + + tr("Audio Bitrate:") + "\n" + + tr("Gop-Size:") + "\n" + + tr("Max B-Frames:") + ); + + ui->label_info_values->setText(QString::fromStdString( + fmt::format("%d x %d\n%d fps\n%s\n%d\n%s\n%d\n%d\n%d", + g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get(), + g_cfg_recording.video.framerate.get(), + get_codec_name(m_video_codecs, g_cfg_recording.video.video_codec.get()), + g_cfg_recording.video.video_bps.get(), + get_codec_name(m_audio_codecs, g_cfg_recording.audio.audio_codec.get()), + g_cfg_recording.audio.audio_bps.get(), + g_cfg_recording.video.gop_size.get(), + g_cfg_recording.video.max_b_frames.get() + ) + )); +} + +void recording_settings_dialog::select_preset(quality_preset preset, cfg_recording& cfg) +{ + if (preset == quality_preset::custom) + { + return; + } + + cfg.audio.audio_codec.set(static_cast(AVCodecID::AV_CODEC_ID_AAC)); + cfg.audio.audio_bps.set(192'000); // 192 kbps + + cfg.video.video_codec.set(static_cast(AVCodecID::AV_CODEC_ID_MPEG4)); + cfg.video.pixel_format.set(static_cast(::AV_PIX_FMT_YUV420P)); + + switch (preset) + { + case quality_preset::_720p_30: + case quality_preset::_720p_60: + cfg.video.width.set(1280); + cfg.video.height.set(720); + break; + case quality_preset::_1080p_30: + case quality_preset::_1080p_60: + cfg.video.width.set(1920); + cfg.video.height.set(1080); + break; + case quality_preset::_1440p_30: + case quality_preset::_1440p_60: + cfg.video.width.set(2560); + cfg.video.height.set(1440); + break; + case quality_preset::_2160p_30: + case quality_preset::_2160p_60: + cfg.video.width.set(3840); + cfg.video.height.set(2160); + break; + case quality_preset::custom: + break; + } + + switch (preset) + { + case quality_preset::_720p_30: + case quality_preset::_1080p_30: + case quality_preset::_1440p_30: + case quality_preset::_2160p_30: + cfg.video.framerate.set(30); + break; + case quality_preset::_720p_60: + case quality_preset::_1080p_60: + case quality_preset::_1440p_60: + case quality_preset::_2160p_60: + cfg.video.framerate.set(60); + break; + case quality_preset::custom: + break; + } + + switch (preset) + { + case quality_preset::_720p_30: + cfg.video.video_bps.set(4'000'000); + break; + case quality_preset::_720p_60: + cfg.video.video_bps.set(6'000'000); + break; + case quality_preset::_1080p_30: + cfg.video.video_bps.set(8'000'000); + break; + case quality_preset::_1080p_60: + cfg.video.video_bps.set(12'000'000); + break; + case quality_preset::_1440p_30: + cfg.video.video_bps.set(16'000'000); + break; + case quality_preset::_1440p_60: + cfg.video.video_bps.set(24'000'000); + break; + case quality_preset::_2160p_30: + cfg.video.video_bps.set(40'000'000); + break; + case quality_preset::_2160p_60: + cfg.video.video_bps.set(60'000'000); + break; + case quality_preset::custom: + break; + } + + cfg.video.gop_size.set(cfg.video.framerate.get()); + cfg.video.max_b_frames.set(2); +} + +recording_settings_dialog::quality_preset recording_settings_dialog::current_preset() +{ + for (u32 i = 0; i < static_cast(quality_preset::custom); i++) + { + const quality_preset preset = static_cast(i); + + cfg_recording cfg; + select_preset(preset, cfg); + + if (g_cfg_recording.video.framerate.get() == cfg.video.framerate.get() && + g_cfg_recording.video.width.get() == cfg.video.width.get() && + g_cfg_recording.video.height.get() == cfg.video.height.get() && + g_cfg_recording.video.pixel_format.get() == cfg.video.pixel_format.get() && + g_cfg_recording.video.video_codec.get() == cfg.video.video_codec.get() && + g_cfg_recording.video.video_bps.get() == cfg.video.video_bps.get() && + g_cfg_recording.video.max_b_frames.get() == cfg.video.max_b_frames.get() && + g_cfg_recording.video.gop_size.get() == cfg.video.gop_size.get() && + g_cfg_recording.audio.audio_codec.get() == cfg.audio.audio_codec.get() && + g_cfg_recording.audio.audio_bps.get() == cfg.audio.audio_bps.get()) + { + return preset; + } + } + + return quality_preset::custom; +} diff --git a/rpcs3/rpcs3qt/recording_settings_dialog.h b/rpcs3/rpcs3qt/recording_settings_dialog.h new file mode 100644 index 0000000000..3a17d7d8a8 --- /dev/null +++ b/rpcs3/rpcs3qt/recording_settings_dialog.h @@ -0,0 +1,47 @@ +#pragma once + +#include "util/types.hpp" +#include "Emu/Io/recording_config.h" + +#include + +namespace Ui +{ + class recording_settings_dialog; +} + +struct AVCodec; + +class recording_settings_dialog : public QDialog +{ + Q_OBJECT + +public: + recording_settings_dialog(QWidget* parent = nullptr); + virtual ~recording_settings_dialog(); + +private: + enum class quality_preset + { + _720p_30, + _720p_60, + _1080p_30, + _1080p_60, + _1440p_30, + _1440p_60, + _2160p_30, + _2160p_60, + custom + }; + + void update_preset(); + void update_ui(); + + static void select_preset(quality_preset preset, cfg_recording& cfg); + static quality_preset current_preset(); + + Ui::recording_settings_dialog* ui; + + std::vector m_video_codecs; + std::vector m_audio_codecs; +}; diff --git a/rpcs3/rpcs3qt/recording_settings_dialog.ui b/rpcs3/rpcs3qt/recording_settings_dialog.ui new file mode 100644 index 0000000000..9d11e1299a --- /dev/null +++ b/rpcs3/rpcs3qt/recording_settings_dialog.ui @@ -0,0 +1,273 @@ + + + recording_settings_dialog + + + + 0 + 0 + 692 + 734 + + + + Recording Settings + + + + + + 0 + + + + Presets + + + + + + Preset + + + + + + + + + + + + Info + + + + + + Keys + + + + + + + Values + + + + + + + Qt::Orientation::Horizontal + + + + 0 + 0 + + + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 0 + + + + + + + + + Advanced + + + + + + Video + + + + + + Codec + + + + + + + + + + + + Resolution + + + + + + + + + + + + Framerate + + + + + + + + + + + + Bitrate + + + + + + + + + + + + Group of Pictures Size + + + + + + + + + + + + Max. B-Frames + + + + + + + + + + + + + + + Audio + + + + + + Codec + + + + + + + + + + + + Bitrate + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 0 + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save + + + + + + + + + buttonBox + accepted() + recording_settings_dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + recording_settings_dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp index 2898c9b170..dbeca1698f 100644 --- a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp @@ -575,7 +575,7 @@ void savestate_manager_dialog::ShowGameTableContextMenu(const QPoint& pos) if (!name.isEmpty()) { QAction* copy_name = new QAction(tr("&Copy Name"), menu); - connect(copy_name, &QAction::triggered, this, [this, name]() + connect(copy_name, &QAction::triggered, this, [name]() { QApplication::clipboard()->setText(name); }); diff --git a/rpcs3/rpcs3qt/screenshot_item.cpp b/rpcs3/rpcs3qt/screenshot_item.cpp index dbfd96d44d..2897cf968c 100644 --- a/rpcs3/rpcs3qt/screenshot_item.cpp +++ b/rpcs3/rpcs3qt/screenshot_item.cpp @@ -1,26 +1,40 @@ #include "screenshot_item.h" #include "qt_utils.h" +#include "Utilities/Thread.h" #include -screenshot_item::screenshot_item(QWidget* parent) +screenshot_item::screenshot_item(QWidget* parent, QSize icon_size, const QString& icon_path, const QPixmap& placeholder) : flow_widget_item(parent) + , m_icon_path(icon_path) + , m_icon_size(icon_size) { + setToolTip(icon_path); + cb_on_first_visibility = [this]() { m_thread.reset(QThread::create([this]() { - const QPixmap pixmap = gui::utils::get_aligned_pixmap(icon_path, icon_size, 1.0, Qt::SmoothTransformation, gui::utils::align_h::center, gui::utils::align_v::center); + thread_base::set_name("Screenshot item"); + + const QPixmap src_icon = QPixmap(m_icon_path); + if (src_icon.isNull()) return; + + const QPixmap pixmap = gui::utils::get_aligned_pixmap(src_icon, m_icon_size, 1.0, Qt::SmoothTransformation, gui::utils::align_h::center, gui::utils::align_v::center); Q_EMIT signal_icon_update(pixmap); })); m_thread->start(); }; - label = new QLabel(this); + m_label = new QLabel(this); + m_label->setPixmap(placeholder); + QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(label); + layout->addWidget(m_label); setLayout(layout); + + connect(this, &screenshot_item::signal_icon_update, this, &screenshot_item::update_icon, Qt::ConnectionType::QueuedConnection); } screenshot_item::~screenshot_item() @@ -30,3 +44,23 @@ screenshot_item::~screenshot_item() m_thread->wait(); } } + +void screenshot_item::update_icon(const QPixmap& pixmap) +{ + if (m_label) + { + m_label->setPixmap(pixmap); + } +} + +void screenshot_item::mouseDoubleClickEvent(QMouseEvent* ev) +{ + flow_widget_item::mouseDoubleClickEvent(ev); + + if (!ev) return; + + if (ev->button() == Qt::LeftButton) + { + Q_EMIT signal_icon_preview(m_icon_path); + } +} diff --git a/rpcs3/rpcs3qt/screenshot_item.h b/rpcs3/rpcs3qt/screenshot_item.h index 36f0232b1f..df223783f3 100644 --- a/rpcs3/rpcs3qt/screenshot_item.h +++ b/rpcs3/rpcs3qt/screenshot_item.h @@ -3,22 +3,29 @@ #include "flow_widget_item.h" #include #include +#include class screenshot_item : public flow_widget_item { Q_OBJECT public: - screenshot_item(QWidget* parent); + screenshot_item(QWidget* parent, QSize icon_size, const QString& icon_path, const QPixmap& placeholder); virtual ~screenshot_item(); - QString icon_path; - QSize icon_size; - QLabel* label{}; - private: + QLabel* m_label{}; + QString m_icon_path; + QSize m_icon_size; std::unique_ptr m_thread; +protected: + void mouseDoubleClickEvent(QMouseEvent* ev) override; + Q_SIGNALS: void signal_icon_update(const QPixmap& pixmap); + void signal_icon_preview(const QString& path); + +public Q_SLOTS: + void update_icon(const QPixmap& pixmap); }; diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index 3825dfd747..d99ce35605 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -10,13 +10,15 @@ #include #include #include +#include #include #include #include LOG_CHANNEL(gui_log, "GUI"); -screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(parent) +screenshot_manager_dialog::screenshot_manager_dialog(const std::vector& games, QWidget* parent) + : QDialog(parent), m_games(games) { setWindowTitle(tr("Screenshots")); setAttribute(Qt::WA_DeleteOnClose); @@ -28,11 +30,50 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog( m_placeholder = QPixmap(m_icon_size); m_placeholder.fill(Qt::gray); - connect(this, &screenshot_manager_dialog::signal_icon_preview, this, &screenshot_manager_dialog::show_preview); - connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry); + connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry, Qt::ConnectionType::QueuedConnection); - QVBoxLayout* layout = new QVBoxLayout; + m_combo_sort_filter = new QComboBox(); + m_combo_sort_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_combo_sort_filter->addItem(tr("Sort by Game"), static_cast(sort_filter::game)); + m_combo_sort_filter->addItem(tr("Sort by Date"), static_cast(sort_filter::date)); + connect(m_combo_sort_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); }); + + m_combo_type_filter = new QComboBox(); + m_combo_type_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_combo_type_filter->addItem(tr("All Screenshots"), static_cast(type_filter::all)); + m_combo_type_filter->addItem(tr("RPCS3 Screenshots"), static_cast(type_filter::rpcs3)); + m_combo_type_filter->addItem(tr("Cell Screenshots"), static_cast(type_filter::cell)); + connect(m_combo_type_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); }); + + m_combo_game_filter = new QComboBox(); + m_combo_game_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents); + m_combo_game_filter->addItem(tr("All Games"), QString()); + connect(m_combo_game_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); }); + + QHBoxLayout* sort_layout = new QHBoxLayout(); + sort_layout->addWidget(m_combo_sort_filter); + QGroupBox* gb_sort = new QGroupBox(tr("Sort")); + gb_sort->setLayout(sort_layout); + + QHBoxLayout* type_layout = new QHBoxLayout(); + type_layout->addWidget(m_combo_type_filter); + QGroupBox* gb_type = new QGroupBox(tr("Filter Type")); + gb_type->setLayout(type_layout); + + QHBoxLayout* game_layout = new QHBoxLayout(); + game_layout->addWidget(m_combo_game_filter); + QGroupBox* gb_game = new QGroupBox(tr("Filter Game")); + gb_game->setLayout(game_layout); + + QHBoxLayout* top_layout = new QHBoxLayout(); + top_layout->addWidget(gb_sort); + top_layout->addWidget(gb_type); + top_layout->addWidget(gb_game); + top_layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); + + QVBoxLayout* layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); + layout->addLayout(top_layout); layout->addWidget(m_flow_widget); setLayout(layout); @@ -47,14 +88,8 @@ screenshot_manager_dialog::~screenshot_manager_dialog() void screenshot_manager_dialog::add_entry(const QString& path) { - screenshot_item* item = new screenshot_item(m_flow_widget); - ensure(item->label); - item->setToolTip(path); - item->installEventFilter(this); - item->label->setPixmap(m_placeholder); - item->icon_path = path; - item->icon_size = m_icon_size; - connect(item, &screenshot_item::signal_icon_update, this, &screenshot_manager_dialog::update_icon); + screenshot_item* item = new screenshot_item(m_flow_widget, m_icon_size, path, m_placeholder); + connect(item, &screenshot_item::signal_icon_preview, this, &screenshot_manager_dialog::show_preview); m_flow_widget->add_widget(item); } @@ -65,28 +100,74 @@ void screenshot_manager_dialog::show_preview(const QString& path) preview->show(); } -void screenshot_manager_dialog::update_icon(const QPixmap& pixmap) -{ - if (screenshot_item* item = static_cast(QObject::sender())) - { - if (item->label) - { - item->label->setPixmap(pixmap); - } - } -} - void screenshot_manager_dialog::reload() { m_abort_parsing = true; + m_parsing_watcher.disconnect(); gui::utils::stop_future_watcher(m_parsing_watcher, true); - const std::string screenshot_path_qt = fs::get_config_dir() + "screenshots/"; - const std::string screenshot_path_cell = rpcs3::utils::get_hdd0_dir() + "/photo/"; + const type_filter t_filter = static_cast(m_combo_type_filter->currentData().toInt()); + const sort_filter s_filter = static_cast(m_combo_sort_filter->currentData().toInt()); + const QString game_filter = m_combo_game_filter->currentData().toString(); + + const std::string screenshot_path_rpcs3 = fs::get_config_dir() + "screenshots/"; + const std::string screenshot_path_cell = rpcs3::utils::get_hdd0_dir() + "/photo/"; + + std::vector folders; + switch (t_filter) + { + case type_filter::all: + folders.push_back(screenshot_path_rpcs3); + folders.push_back(screenshot_path_cell); + break; + case type_filter::rpcs3: + folders.push_back(screenshot_path_rpcs3); + break; + case type_filter::cell: + folders.push_back(screenshot_path_cell); + break; + } m_flow_widget->clear(); + m_game_folders.clear(); m_abort_parsing = false; - m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, screenshot_path_qt, screenshot_path_cell](int index) + + connect(&m_parsing_watcher, &QFutureWatcher::finished, this, [this]() + { + std::vector> games; + for (const auto& [dirname, paths] : m_game_folders) + { + const std::string serial = dirname.toStdString(); + std::string text = serial; + for (const auto& game : m_games) + { + if (game && game->info.serial == serial) + { + text = fmt::format("%s (%s)", game->info.name, serial); + break; + } + } + games.push_back(std::pair(dirname, QString::fromStdString(text))); + } + + std::sort(games.begin(), games.end(), [](const std::pair& l, const std::pair& r) + { + return l.second < r.second; + }); + + const QString old_filter = m_combo_game_filter->currentData().toString(); + m_combo_game_filter->blockSignals(true); + m_combo_game_filter->clear(); + m_combo_game_filter->addItem(tr("All Games"), QString()); + for (const auto& [dirname, text] : games) + { + m_combo_game_filter->addItem(text, dirname); + } + m_combo_game_filter->setCurrentIndex(m_combo_game_filter->findData(old_filter)); + m_combo_game_filter->blockSignals(false); + }); + + m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, folders, game_filter, s_filter](int index) { if (index != 0) { @@ -95,26 +176,68 @@ void screenshot_manager_dialog::reload() const QStringList filter{ QStringLiteral("*.png") }; - for (const std::string& path : { screenshot_path_qt, screenshot_path_cell }) + for (const std::string& folder : folders) { if (m_abort_parsing) { return; } - if (path.empty()) + if (folder.empty()) { gui_log.error("Screenshot manager: Trying to load screenshots from empty path!"); continue; } - QDirIterator dir_iter(QString::fromStdString(path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + QDirIterator dir_iter(QString::fromStdString(folder), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (dir_iter.hasNext() && !m_abort_parsing) { - Q_EMIT signal_entry_parsed(dir_iter.next()); + QFileInfo info(dir_iter.next()); + const QString dirname = info.dir().dirName(); + m_game_folders[dirname].push_back(std::move(info)); } } + + switch (s_filter) + { + case sort_filter::game: + { + for (const auto& [dirname, infos] : m_game_folders) + { + if (game_filter.isEmpty() || game_filter == dirname) + { + for (const QFileInfo& info : infos) + { + Q_EMIT signal_entry_parsed(info.filePath()); + } + } + } + break; + } + case sort_filter::date: + { + std::vector sorted_infos; + for (const auto& [dirname, infos] : m_game_folders) + { + if (game_filter.isEmpty() || game_filter == dirname) + { + sorted_infos.insert(sorted_infos.end(), infos.begin(), infos.end()); + } + } + + std::sort(sorted_infos.begin(), sorted_infos.end(), [](const QFileInfo& a, const QFileInfo& b) + { + return a.lastModified() < b.lastModified(); + }); + + for (const QFileInfo& info : sorted_infos) + { + Q_EMIT signal_entry_parsed(info.filePath()); + } + break; + } + } })); } @@ -123,17 +246,3 @@ void screenshot_manager_dialog::showEvent(QShowEvent* event) QDialog::showEvent(event); reload(); } - -bool screenshot_manager_dialog::eventFilter(QObject* watched, QEvent* event) -{ - if (event && event->type() == QEvent::MouseButtonDblClick && static_cast(event)->button() == Qt::LeftButton) - { - if (screenshot_item* item = static_cast(watched)) - { - Q_EMIT signal_icon_preview(item->icon_path); - return true; - } - } - - return false; -} diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.h b/rpcs3/rpcs3qt/screenshot_manager_dialog.h index ab4d7c13a8..a236eb5e0d 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.h +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.h @@ -1,30 +1,29 @@ #pragma once #include "flow_widget.h" +#include "gui_game_info.h" +#include #include +#include #include #include #include -#include +#include #include +#include +#include class screenshot_manager_dialog : public QDialog { Q_OBJECT public: - screenshot_manager_dialog(QWidget* parent = nullptr); + screenshot_manager_dialog(const std::vector& games, QWidget* parent = nullptr); ~screenshot_manager_dialog(); - bool eventFilter(QObject* watched, QEvent* event) override; - Q_SIGNALS: void signal_entry_parsed(const QString& path); - void signal_icon_preview(const QString& path); - -public Q_SLOTS: - void update_icon(const QPixmap& pixmap); private Q_SLOTS: void add_entry(const QString& path); @@ -36,11 +35,28 @@ protected: private: void reload(); + enum class type_filter + { + all, + rpcs3, + cell + }; + + enum class sort_filter + { + game, + date + }; + + std::vector m_games; bool m_abort_parsing = false; const std::array m_parsing_threads{0}; QFutureWatcher m_parsing_watcher; flow_widget* m_flow_widget = nullptr; - + QComboBox* m_combo_sort_filter = nullptr; + QComboBox* m_combo_game_filter = nullptr; + QComboBox* m_combo_type_filter = nullptr; QSize m_icon_size; QPixmap m_placeholder; + std::map> m_game_folders; }; diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index c95247748a..f4fcee4985 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -858,7 +858,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->vulkansched->setEnabled(is_vulkan); }; - const auto apply_fsr_specific_options = [r_creator, this]() + const auto apply_fsr_specific_options = [this]() { const auto [text, value] = get_data(ui->outputScalingMode, ui->outputScalingMode->currentIndex()); const bool fsr_selected = static_cast(value) == output_scaling_mode::fsr; @@ -2095,7 +2095,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std connect(ui->edit_button_game_window_title_format, &QAbstractButton::clicked, [get_game_window_title, set_game_window_title, this]() { - auto get_game_window_title_label = [get_game_window_title, set_game_window_title, this](const QString& format) + auto get_game_window_title_label = [get_game_window_title](const QString& format) { const QString game_window_title = get_game_window_title(format); diff --git a/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp b/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp index cfd93246eb..3f7aeccce0 100644 --- a/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/sound_effect_manager_dialog.cpp @@ -41,7 +41,7 @@ sound_effect_manager_dialog::sound_effect_manager_dialog(QWidget* parent) } QPushButton* button = new QPushButton("", this); - connect(button, &QAbstractButton::clicked, this, [this, button, sound, name]() + connect(button, &QAbstractButton::clicked, this, [this, sound, name]() { const std::string path = rsx::overlays::get_sound_filepath(sound); if (fs::is_file(path)) diff --git a/rpcs3/rpcs3qt/steam_utils.cpp b/rpcs3/rpcs3qt/steam_utils.cpp index 2642e25b37..e5aedf9e20 100644 --- a/rpcs3/rpcs3qt/steam_utils.cpp +++ b/rpcs3/rpcs3qt/steam_utils.cpp @@ -71,6 +71,8 @@ namespace gui::utils } const std::vector data = vdf.to_vector(); + vdf.close(); + usz last_pos = 0; usz pos = 0; @@ -507,9 +509,17 @@ namespace gui::utils sys_log.success("Removed steam shortcut(s) for '%s'", entry.app_name); } + update_steam_input_config(user_dir); + return true; } + bool steam_shortcut::steam_installed() + { + const std::string path = get_steam_path(); + return !path.empty() && fs::is_dir(path); + } + u32 steam_shortcut::crc32(const std::string& data) { u32 crc = 0xFFFFFFFF; @@ -527,12 +537,6 @@ namespace gui::utils return ~crc; } - bool steam_shortcut::steam_installed() - { - const std::string path = get_steam_path(); - return !path.empty() && fs::is_dir(path); - } - u32 steam_shortcut::steam_appid(const std::string& exe, const std::string& name) { return crc32(exe + name) | 0x80000000; @@ -667,8 +671,177 @@ namespace gui::utils return str; } + void steam_shortcut::update_steam_input_config(const std::string& user_dir) + { + if (m_entries_to_add.empty() && m_entries_to_remove.empty()) + { + return; + } + + const std::string vdf_path = user_dir + "localconfig.vdf"; + const std::string backup_path = fs::get_config_dir() + "/localconfig.vdf.backup"; + + if (fs::is_file(vdf_path) && !fs::copy_file(vdf_path, backup_path, true)) + { + sys_log.error("Failed to backup steam localconfig file '%s'", vdf_path); + return; + } + + fs::file vdf(vdf_path); + if (!vdf) + { + sys_log.error("update_steam_input_config: Failed to open steam localconfig file '%s': %s", vdf_path, fs::g_tls_error); + return; + } + + std::string content = vdf.to_string(); + vdf.close(); + + static const std::string app_section_start = "\n\t\"apps\"\n\t{"; + static const std::string app_section_end = "\n\t}\n"; + static const std::string entry_section_end = "\n\t\t}"; + + bool nothing_to_remove = m_entries_to_remove.empty(); + + usz app_pos = content.rfind(app_section_start); + if (app_pos == umax) + { + if (!nothing_to_remove) + { + // We don't have to remove anything because this section did not exist + sys_log.notice("update_steam_input_config: Could not find \"apps\" section. No need to remove anything."); + nothing_to_remove = true; + } + + if (m_entries_to_add.empty()) + { + return; // Nothing to do anyway + } + + const usz insert_pos = content.rfind("\n}"); + if (insert_pos == umax) + { + sys_log.error("update_steam_input_config: Could not find main section end"); + return; + } + + sys_log.notice("update_steam_input_config: Inserting missing \"apps\" section"); + content.insert(insert_pos, fmt::format("%s\n\t}", app_section_start)); + + app_pos = content.rfind(app_section_start); + ensure(app_pos != umax); + } + + const usz search_start = app_pos + app_section_start.size(); + + const usz insert_pos = content.find(app_section_end, search_start); + if (insert_pos == umax) + { + sys_log.error("update_steam_input_config: Could not find apps section end"); + return; + } + + const auto appid_string = [](s32 signed_appid) + { + return fmt::format("\n\t\t\"%d\"\n", signed_appid); + }; + + const auto find_entry = [&content, &appid_string, search_start, insert_pos](s32 signed_appid) -> usz + { + const usz pos = content.find(appid_string(signed_appid), search_start); + if (pos >= insert_pos) return umax; + return pos; + }; + + bool dirty = false; + + for (const shortcut_entry& entry : m_entries_to_remove) + { + constexpr bool removal_diabled = true; // Disabled for now. Steam doesn't seem to remove the entries either + if constexpr (removal_diabled) + { + break; + } + + if (nothing_to_remove) + { + break; + } + + const s32 signed_appid = static_cast(entry.appid); + const usz pos = find_entry(signed_appid); + + if (pos == umax) + { + // does not exist, do nothing + sys_log.notice("update_steam_input_config: Entry for '%s' with appid '%d' does not exist. Skipping removal", entry.app_name, signed_appid); + continue; + } + + // Find the opening brace of this entry + const usz pos_brace_open = content.find('{', pos); + if (pos_brace_open == umax || pos_brace_open >= insert_pos) + { + sys_log.error("update_steam_input_config: Can't find opening brace for entry for '%s' with appid '%d'.", entry.app_name, signed_appid); + continue; + } + + // Find the closing brace + const usz pos_brace_close = content.find(entry_section_end, pos_brace_open); + if (pos_brace_close == umax || pos_brace_close >= insert_pos) + { + sys_log.error("update_steam_input_config: Can't find closing brace for entry for '%s' with appid '%d'.", entry.app_name, signed_appid); + continue; + } + + // Include the closing brace line + const usz erase_end = pos_brace_close + entry_section_end.size(); + + // Erase the whole block + content.erase(pos, erase_end - pos); + + sys_log.notice("update_steam_input_config: Removed '%s' with appid '%d'", entry.app_name, signed_appid); + + dirty = true; + } + + for (const shortcut_entry& entry : m_entries_to_add) + { + const s32 signed_appid = static_cast(entry.appid); + const usz pos = find_entry(signed_appid); + + if (pos != umax) + { + // already exists, do nothing + sys_log.notice("update_steam_input_config: Entry for '%s' with appid '%d' already exists", entry.app_name, signed_appid); + continue; + } + + sys_log.notice("update_steam_input_config: Inserting '%s' with appid '%d'", entry.app_name, signed_appid); + content.insert(insert_pos, fmt::format( + "%s" + "\t\t{\n" + "\t\t\t\"UseSteamControllerConfig\"\t\t\"0\"\n" + //"\t\t\t\"SteamControllerRumble\"\t\t\"-1\"\n" + //"\t\t\t\"SteamControllerRumbleIntensity\"\t\t\"320\"\n" + "\t\t}", appid_string(signed_appid))); + + dirty = true; + } + + if (dirty && !fs::write_file(vdf_path, fs::rewrite, content)) + { + sys_log.error("Failed to update steam localconfig '%s': '%s'", vdf_path, fs::g_tls_error); + + if (!fs::copy_file(backup_path, vdf_path, true)) + { + sys_log.error("Failed to restore steam localconfig backup: '%s'", fs::g_tls_error); + } + } + } + #ifdef _WIN32 - std::string get_registry_string(const wchar_t* key, const wchar_t* name) + static std::string get_registry_string(const wchar_t* key, const wchar_t* name) { HKEY hkey = NULL; LSTATUS status = RegOpenKeyW(HKEY_CURRENT_USER, key, &hkey); @@ -832,6 +1005,7 @@ namespace gui::utils // } const std::string content = vdf.to_string(); + vdf.close(); usz user_count = 0; diff --git a/rpcs3/rpcs3qt/steam_utils.h b/rpcs3/rpcs3qt/steam_utils.h index 369ee084a2..deb7c0dad7 100644 --- a/rpcs3/rpcs3qt/steam_utils.h +++ b/rpcs3/rpcs3qt/steam_utils.h @@ -103,6 +103,8 @@ namespace gui::utils bool parse_file(const std::string& path); + void update_steam_input_config(const std::string& user_dir); + static u32 crc32(const std::string& data); static u32 steam_appid(const std::string& exe, const std::string& name); @@ -115,7 +117,7 @@ namespace gui::utils static std::string steamid64_to_32(const std::string& steam_id); static std::string get_steam_path(); static std::string get_last_active_steam_user(const std::string& steam_path); - + static std::string get_steam_banner_path(steam_banner banner, const std::string& grid_dir, u32 appid); static void create_steam_banner(steam_banner banner, const std::string& src_path, const QPixmap& src_icon, const std::string& grid_dir, const shortcut_entry& entry); diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index 51ef2d2e9f..11046f2a70 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -392,7 +392,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr gui_s m_trophy_table->create_header_actions(m_trophy_column_acts, [this](int col) { return m_gui_settings->GetTrophylistColVisibility(static_cast(col)); }, [this](int col, bool visible) { m_gui_settings->SetTrophylistColVisibility(static_cast(col), visible); }); - + m_game_table->create_header_actions(m_game_column_acts, [this](int col) { return m_gui_settings->GetTrophyGamelistColVisibility(static_cast(col)); }, [this](int col, bool visible) { m_gui_settings->SetTrophyGamelistColVisibility(static_cast(col), visible); }); @@ -651,7 +651,7 @@ void trophy_manager_dialog::ResizeGameIcons() const int trophy_index = item->data(GameUserRole::GameIndex).toInt(); QString trophy_icon_path = QString::fromStdString(m_trophies_db[trophy_index]->path); - item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, trophy_index, cancel = item->icon_loading_aborted(), dpr](int index) + item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, cancel = item->icon_loading_aborted(), dpr](int index) { if (cancel && cancel->load()) { @@ -897,7 +897,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos) if (!name.isEmpty() && !desc.isEmpty()) { QAction* copy_both = new QAction(tr("&Copy Name + Description"), copy_menu); - connect(copy_both, &QAction::triggered, this, [this, name, desc]() + connect(copy_both, &QAction::triggered, this, [name, desc]() { QApplication::clipboard()->setText(name % QStringLiteral("\n\n") % desc); }); @@ -907,7 +907,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos) if (!name.isEmpty()) { QAction* copy_name = new QAction(tr("&Copy Name"), copy_menu); - connect(copy_name, &QAction::triggered, this, [this, name]() + connect(copy_name, &QAction::triggered, this, [name]() { QApplication::clipboard()->setText(name); }); @@ -917,7 +917,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos) if (!desc.isEmpty()) { QAction* copy_desc = new QAction(tr("&Copy Description"), copy_menu); - connect(copy_desc, &QAction::triggered, this, [this, desc]() + connect(copy_desc, &QAction::triggered, this, [desc]() { QApplication::clipboard()->setText(desc); }); @@ -1038,7 +1038,7 @@ void trophy_manager_dialog::ShowGameTableContextMenu(const QPoint& pos) if (!name.isEmpty()) { QAction* copy_name = new QAction(tr("&Copy Name"), menu); - connect(copy_name, &QAction::triggered, this, [this, name]() + connect(copy_name, &QAction::triggered, this, [name]() { QApplication::clipboard()->setText(name); }); diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index 5bba4497d6..5482b8401e 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #if defined(_WIN32) || defined(__APPLE__) #include <7z.h> diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp index f5ceac8aee..44aceff70a 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp @@ -81,7 +81,7 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin item->setCheckState(Qt::CheckState::Checked); }); - connect(m_dir_list, &QListWidget::currentRowChanged, this, [this, button_remove_dir](int row) + connect(m_dir_list, &QListWidget::currentRowChanged, this, [button_remove_dir](int row) { button_remove_dir->setEnabled(row > 0); }); diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index 81c0b3d31a..16b40d7e52 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -743,7 +743,26 @@ std::pair utils::get_memory_usage() status.dwLength = sizeof(status); ::GlobalMemoryStatusEx(&status); return { status.ullTotalPhys, status.ullTotalPhys - status.ullAvailPhys }; +#elif __linux__ + std::ifstream proc("/proc/meminfo"); + std::string line; + uint64_t mem_total = get_total_memory(); + uint64_t mem_available = 0; + while (std::getline(proc, line)) + { + if (line.rfind("MemTotal:", 0) == 0 && line.find("kB") != std::string::npos) + { + mem_total = std::stoull(line.substr(line.find_first_of("0123456789"))) * 1024; + } + else if (line.rfind("MemAvailable:", 0) == 0 && line.find("kB") != std::string::npos) + { + mem_available = std::stoull(line.substr(line.find_first_of("0123456789"))) * 1024; + break; + } + } + + return { mem_total, mem_total - mem_available }; #else // TODO return { get_total_memory(), 0 };